aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils')
-rw-r--r--src/backend/utils/adt/ruleutils.c1
-rw-r--r--src/backend/utils/cache/plancache.c34
-rw-r--r--src/backend/utils/misc/Makefile4
-rw-r--r--src/backend/utils/misc/queryenvironment.c144
-rw-r--r--src/backend/utils/sort/tuplestore.c17
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: