diff options
Diffstat (limited to 'src/backend/utils')
-rw-r--r-- | src/backend/utils/adt/ruleutils.c | 1 | ||||
-rw-r--r-- | src/backend/utils/cache/plancache.c | 34 | ||||
-rw-r--r-- | src/backend/utils/misc/Makefile | 4 | ||||
-rw-r--r-- | src/backend/utils/misc/queryenvironment.c | 144 | ||||
-rw-r--r-- | src/backend/utils/sort/tuplestore.c | 17 |
5 files changed, 184 insertions, 16 deletions
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index c2681ced2af..0c1a201ecb5 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -6710,6 +6710,7 @@ get_name_for_var_field(Var *var, int fieldno, { case RTE_RELATION: case RTE_VALUES: + case RTE_NAMEDTUPLESTORE: /* * This case should not occur: a column of a table or values list diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c index 043085d3a76..a116d5ed63e 100644 --- a/src/backend/utils/cache/plancache.c +++ b/src/backend/utils/cache/plancache.c @@ -88,10 +88,11 @@ static CachedPlanSource *first_saved_plan = NULL; static void ReleaseGenericPlan(CachedPlanSource *plansource); -static List *RevalidateCachedQuery(CachedPlanSource *plansource); +static List *RevalidateCachedQuery(CachedPlanSource *plansource, + QueryEnvironment *queryEnv); static bool CheckCachedPlan(CachedPlanSource *plansource); static CachedPlan *BuildCachedPlan(CachedPlanSource *plansource, List *qlist, - ParamListInfo boundParams); + ParamListInfo boundParams, QueryEnvironment *queryEnv); static bool choose_custom_plan(CachedPlanSource *plansource, ParamListInfo boundParams); static double cached_plan_cost(CachedPlan *plan, bool include_planner); @@ -150,7 +151,8 @@ InitPlanCache(void) CachedPlanSource * CreateCachedPlan(RawStmt *raw_parse_tree, const char *query_string, - const char *commandTag) + const char *commandTag, + QueryEnvironment *queryEnv) { CachedPlanSource *plansource; MemoryContext source_context; @@ -553,7 +555,8 @@ ReleaseGenericPlan(CachedPlanSource *plansource) * a tree copying step in a subsequent BuildCachedPlan call.) */ static List * -RevalidateCachedQuery(CachedPlanSource *plansource) +RevalidateCachedQuery(CachedPlanSource *plansource, + QueryEnvironment *queryEnv) { bool snapshot_set; RawStmt *rawtree; @@ -685,12 +688,14 @@ RevalidateCachedQuery(CachedPlanSource *plansource) tlist = pg_analyze_and_rewrite_params(rawtree, plansource->query_string, plansource->parserSetup, - plansource->parserSetupArg); + plansource->parserSetupArg, + queryEnv); else tlist = pg_analyze_and_rewrite(rawtree, plansource->query_string, plansource->param_types, - plansource->num_params); + plansource->num_params, + queryEnv); /* Release snapshot if we got one */ if (snapshot_set) @@ -875,7 +880,7 @@ CheckCachedPlan(CachedPlanSource *plansource) */ static CachedPlan * BuildCachedPlan(CachedPlanSource *plansource, List *qlist, - ParamListInfo boundParams) + ParamListInfo boundParams, QueryEnvironment *queryEnv) { CachedPlan *plan; List *plist; @@ -899,7 +904,7 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist, * safety, let's treat it as real and redo the RevalidateCachedQuery call. */ if (!plansource->is_valid) - qlist = RevalidateCachedQuery(plansource); + qlist = RevalidateCachedQuery(plansource, queryEnv); /* * If we don't already have a copy of the querytree list that can be @@ -1129,7 +1134,7 @@ cached_plan_cost(CachedPlan *plan, bool include_planner) */ CachedPlan * GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams, - bool useResOwner) + bool useResOwner, QueryEnvironment *queryEnv) { CachedPlan *plan = NULL; List *qlist; @@ -1143,7 +1148,7 @@ GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams, elog(ERROR, "cannot apply ResourceOwner to non-saved cached plan"); /* Make sure the querytree list is valid and we have parse-time locks */ - qlist = RevalidateCachedQuery(plansource); + qlist = RevalidateCachedQuery(plansource, queryEnv); /* Decide whether to use a custom plan */ customplan = choose_custom_plan(plansource, boundParams); @@ -1159,7 +1164,7 @@ GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams, else { /* Build a new generic plan */ - plan = BuildCachedPlan(plansource, qlist, NULL); + plan = BuildCachedPlan(plansource, qlist, NULL, queryEnv); /* Just make real sure plansource->gplan is clear */ ReleaseGenericPlan(plansource); /* Link the new generic plan into the plansource */ @@ -1204,7 +1209,7 @@ GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams, if (customplan) { /* Build a custom plan */ - plan = BuildCachedPlan(plansource, qlist, boundParams); + plan = BuildCachedPlan(plansource, qlist, boundParams, queryEnv); /* Accumulate total costs of custom plans, but 'ware overflow */ if (plansource->num_custom_plans < INT_MAX) { @@ -1418,7 +1423,8 @@ CachedPlanIsValid(CachedPlanSource *plansource) * within the cached plan, and may disappear next time the plan is updated. */ List * -CachedPlanGetTargetList(CachedPlanSource *plansource) +CachedPlanGetTargetList(CachedPlanSource *plansource, + QueryEnvironment *queryEnv) { Query *pstmt; @@ -1434,7 +1440,7 @@ CachedPlanGetTargetList(CachedPlanSource *plansource) return NIL; /* Make sure the querytree list is valid and we have parse-time locks */ - RevalidateCachedQuery(plansource); + RevalidateCachedQuery(plansource, queryEnv); /* Get the primary statement and find out what it returns */ pstmt = QueryListGetPrimaryStmt(plansource->query_list); diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile index 45cdf76ec2c..a53fcdf1889 100644 --- a/src/backend/utils/misc/Makefile +++ b/src/backend/utils/misc/Makefile @@ -15,8 +15,8 @@ include $(top_builddir)/src/Makefile.global override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS) OBJS = backend_random.o guc.o help_config.o pg_config.o pg_controldata.o \ - pg_rusage.o ps_status.o rls.o sampling.o superuser.o timeout.o \ - tzparser.o + pg_rusage.o ps_status.o queryenvironment.o rls.o sampling.o \ + superuser.o timeout.o tzparser.o # This location might depend on the installation directories. Therefore # we can't substitute it into pg_config.h. diff --git a/src/backend/utils/misc/queryenvironment.c b/src/backend/utils/misc/queryenvironment.c new file mode 100644 index 00000000000..a0b10d402bd --- /dev/null +++ b/src/backend/utils/misc/queryenvironment.c @@ -0,0 +1,144 @@ +/*------------------------------------------------------------------------- + * + * queryenvironment.c + * Query environment, to store context-specific values like ephemeral named + * relations. Initial use is for named tuplestores for delta information + * from "normal" relations. + * + * The initial implementation uses a list because the number of such relations + * in any one context is expected to be very small. If that becomes a + * performance problem, the implementation can be changed with no other impact + * on callers, since this is an opaque structure. This is the reason to + * require a create function. + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/backend/utils/misc/queryenvironment.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/heapam.h" +#include "utils/queryenvironment.h" +#include "utils/rel.h" + +/* + * Private state of a query environment. + */ +struct QueryEnvironment +{ + List *namedRelList; +}; + + +QueryEnvironment * +create_queryEnv() +{ + return (QueryEnvironment *) palloc0(sizeof(QueryEnvironment)); +} + +EphemeralNamedRelationMetadata +get_visible_ENR_metadata(QueryEnvironment *queryEnv, const char *refname) +{ + EphemeralNamedRelation enr; + + Assert(refname != NULL); + + if (queryEnv == NULL) + return NULL; + + enr = get_ENR(queryEnv, refname); + + if (enr) + return &(enr->md); + + return NULL; +} + +/* + * Register a named relation for use in the given environment. + * + * If this is intended exclusively for planning purposes, the tstate field can + * be left NULL; + */ +void +register_ENR(QueryEnvironment *queryEnv, EphemeralNamedRelation enr) +{ + Assert(enr != NULL); + Assert(get_ENR(queryEnv, enr->md.name) == NULL); + + queryEnv->namedRelList = lappend(queryEnv->namedRelList, enr); +} + +/* + * Unregister an ephemeral relation by name. This will probably be a rarely + * used function, but seems like it should be provided "just in case". + */ +void +unregister_ENR(QueryEnvironment *queryEnv, const char *name) +{ + EphemeralNamedRelation match; + + match = get_ENR(queryEnv, name); + if (match) + queryEnv->namedRelList = list_delete(queryEnv->namedRelList, match); +} + +/* + * This returns an ENR if there is a name match in the given collection. It + * must quietly return NULL if no match is found. + */ +EphemeralNamedRelation +get_ENR(QueryEnvironment *queryEnv, const char *name) +{ + ListCell *lc; + + Assert(name != NULL); + + if (queryEnv == NULL) + return NULL; + + foreach(lc, queryEnv->namedRelList) + { + EphemeralNamedRelation enr = (EphemeralNamedRelation) lfirst(lc); + + if (strcmp(enr->md.name, name) == 0) + return enr; + } + + return NULL; +} + +/* + * Gets the TupleDesc for a Ephemeral Named Relation, based on which field was + * filled. + * + * When the TupleDesc is based on a relation from the catalogs, we count on + * that relation being used at the same time, so that appropriate locks will + * already be held. Locking here would be too late anyway. + */ +TupleDesc +ENRMetadataGetTupDesc(EphemeralNamedRelationMetadata enrmd) +{ + TupleDesc tupdesc; + + /* One, and only one, of these fields must be filled. */ + Assert((enrmd->reliddesc == InvalidOid) != (enrmd->tupdesc == NULL)); + + if (enrmd->tupdesc != NULL) + tupdesc = enrmd->tupdesc; + else + { + Relation relation; + + relation = heap_open(enrmd->reliddesc, NoLock); + tupdesc = relation->rd_att; + heap_close(relation, NoLock); + } + + return tupdesc; +} diff --git a/src/backend/utils/sort/tuplestore.c b/src/backend/utils/sort/tuplestore.c index 84abf5f67e0..b3f6be74573 100644 --- a/src/backend/utils/sort/tuplestore.c +++ b/src/backend/utils/sort/tuplestore.c @@ -109,6 +109,7 @@ struct Tuplestorestate bool truncated; /* tuplestore_trim has removed tuples? */ int64 availMem; /* remaining memory available, in bytes */ int64 allowedMem; /* total memory allowed, in bytes */ + int64 tuples; /* number of tuples added */ BufFile *myfile; /* underlying file, or NULL if none */ MemoryContext context; /* memory context for holding tuples */ ResourceOwner resowner; /* resowner for holding temp files */ @@ -267,6 +268,7 @@ tuplestore_begin_common(int eflags, bool interXact, int maxKBytes) state->memtupdeleted = 0; state->memtupcount = 0; + state->tuples = 0; /* * Initial size of array must be more than ALLOCSET_SEPARATE_THRESHOLD; @@ -433,6 +435,7 @@ tuplestore_clear(Tuplestorestate *state) state->truncated = false; state->memtupdeleted = 0; state->memtupcount = 0; + state->tuples = 0; readptr = state->readptrs; for (i = 0; i < state->readptrcount; readptr++, i++) { @@ -534,6 +537,18 @@ tuplestore_select_read_pointer(Tuplestorestate *state, int ptr) } /* + * tuplestore_tuple_count + * + * Returns the number of tuples added since creation or the last + * tuplestore_clear(). + */ +int64 +tuplestore_tuple_count(Tuplestorestate *state) +{ + return state->tuples; +} + +/* * tuplestore_ateof * * Returns the active read pointer's eof_reached state. @@ -753,6 +768,8 @@ tuplestore_puttuple_common(Tuplestorestate *state, void *tuple) int i; ResourceOwner oldowner; + state->tuples++; + switch (state->status) { case TSS_INMEM: |