aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/nodeProjectSet.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor/nodeProjectSet.c')
-rw-r--r--src/backend/executor/nodeProjectSet.c300
1 files changed, 300 insertions, 0 deletions
diff --git a/src/backend/executor/nodeProjectSet.c b/src/backend/executor/nodeProjectSet.c
new file mode 100644
index 00000000000..391e97ea6fe
--- /dev/null
+++ b/src/backend/executor/nodeProjectSet.c
@@ -0,0 +1,300 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeProjectSet.c
+ * support for evaluating targetlists containing set-returning functions
+ *
+ * DESCRIPTION
+ *
+ * ProjectSet nodes are inserted by the planner to evaluate set-returning
+ * functions in the targetlist. It's guaranteed that all set-returning
+ * functions are directly at the top level of the targetlist, i.e. they
+ * can't be inside more-complex expressions. If that'd otherwise be
+ * the case, the planner adds additional ProjectSet nodes.
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/executor/nodeProjectSet.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "executor/executor.h"
+#include "executor/nodeProjectSet.h"
+#include "utils/memutils.h"
+
+
+static TupleTableSlot *ExecProjectSRF(ProjectSetState *node, bool continuing);
+
+
+/* ----------------------------------------------------------------
+ * ExecProjectSet(node)
+ *
+ * Return tuples after evaluating the targetlist (which contains set
+ * returning functions).
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *
+ExecProjectSet(ProjectSetState *node)
+{
+ TupleTableSlot *outerTupleSlot;
+ TupleTableSlot *resultSlot;
+ PlanState *outerPlan;
+ ExprContext *econtext;
+
+ econtext = node->ps.ps_ExprContext;
+
+ /*
+ * Check to see if we're still projecting out tuples from a previous scan
+ * tuple (because there is a function-returning-set in the projection
+ * expressions). If so, try to project another one.
+ */
+ if (node->pending_srf_tuples)
+ {
+ resultSlot = ExecProjectSRF(node, true);
+
+ if (resultSlot != NULL)
+ return resultSlot;
+ }
+
+ /*
+ * Reset per-tuple memory context to free any expression evaluation
+ * storage allocated in the previous tuple cycle. Note this can't happen
+ * until we're done projecting out tuples from a scan tuple.
+ */
+ ResetExprContext(econtext);
+
+ /*
+ * Get another input tuple and project SRFs from it.
+ */
+ for (;;)
+ {
+ /*
+ * Retrieve tuples from the outer plan until there are no more.
+ */
+ outerPlan = outerPlanState(node);
+ outerTupleSlot = ExecProcNode(outerPlan);
+
+ if (TupIsNull(outerTupleSlot))
+ return NULL;
+
+ /*
+ * Prepare to compute projection expressions, which will expect to
+ * access the input tuples as varno OUTER.
+ */
+ econtext->ecxt_outertuple = outerTupleSlot;
+
+ /* Evaluate the expressions */
+ resultSlot = ExecProjectSRF(node, false);
+
+ /*
+ * Return the tuple unless the projection produced no rows (due to an
+ * empty set), in which case we must loop back to see if there are
+ * more outerPlan tuples.
+ */
+ if (resultSlot)
+ return resultSlot;
+ }
+
+ return NULL;
+}
+
+/* ----------------------------------------------------------------
+ * ExecProjectSRF
+ *
+ * Project a targetlist containing one or more set-returning functions.
+ *
+ * 'continuing' indicates whether to continue projecting rows for the
+ * same input tuple; or whether a new input tuple is being projected.
+ *
+ * Returns NULL if no output tuple has been produced.
+ *
+ * ----------------------------------------------------------------
+ */
+static TupleTableSlot *
+ExecProjectSRF(ProjectSetState *node, bool continuing)
+{
+ TupleTableSlot *resultSlot = node->ps.ps_ResultTupleSlot;
+ ExprContext *econtext = node->ps.ps_ExprContext;
+ bool hassrf PG_USED_FOR_ASSERTS_ONLY = false;
+ bool hasresult;
+ int argno;
+ ListCell *lc;
+
+ ExecClearTuple(resultSlot);
+
+ /*
+ * Assume no further tuples are produced unless an ExprMultipleResult is
+ * encountered from a set returning function.
+ */
+ node->pending_srf_tuples = false;
+
+ hasresult = false;
+ argno = 0;
+ foreach(lc, node->ps.targetlist)
+ {
+ GenericExprState *gstate = (GenericExprState *) lfirst(lc);
+ ExprDoneCond *isdone = &node->elemdone[argno];
+ Datum *result = &resultSlot->tts_values[argno];
+ bool *isnull = &resultSlot->tts_isnull[argno];
+
+ if (continuing && *isdone == ExprEndResult)
+ {
+ /*
+ * If we're continuing to project output rows from a source tuple,
+ * return NULLs once the SRF has been exhausted.
+ */
+ *result = (Datum) 0;
+ *isnull = true;
+ hassrf = true;
+ }
+ else if (IsA(gstate->arg, FuncExprState) &&
+ ((FuncExprState *) gstate->arg)->funcReturnsSet)
+ {
+ /*
+ * Evaluate SRF - possibly continuing previously started output.
+ */
+ *result = ExecMakeFunctionResultSet((FuncExprState *) gstate->arg,
+ econtext, isnull, isdone);
+
+ if (*isdone != ExprEndResult)
+ hasresult = true;
+ if (*isdone == ExprMultipleResult)
+ node->pending_srf_tuples = true;
+ hassrf = true;
+ }
+ else
+ {
+ /* Non-SRF tlist expression, just evaluate normally. */
+ *result = ExecEvalExpr(gstate->arg, econtext, isnull, NULL);
+ *isdone = ExprSingleResult;
+ }
+
+ argno++;
+ }
+
+ /* ProjectSet should not be used if there's no SRFs */
+ Assert(hassrf);
+
+ /*
+ * If all the SRFs returned EndResult, we consider that as no row being
+ * produced.
+ */
+ if (hasresult)
+ {
+ ExecStoreVirtualTuple(resultSlot);
+ return resultSlot;
+ }
+
+ return NULL;
+}
+
+/* ----------------------------------------------------------------
+ * ExecInitProjectSet
+ *
+ * Creates the run-time state information for the ProjectSet node
+ * produced by the planner and initializes outer relations
+ * (child nodes).
+ * ----------------------------------------------------------------
+ */
+ProjectSetState *
+ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags)
+{
+ ProjectSetState *state;
+
+ /* check for unsupported flags */
+ Assert(!(eflags & (EXEC_FLAG_MARK | EXEC_FLAG_BACKWARD)));
+
+ /*
+ * create state structure
+ */
+ state = makeNode(ProjectSetState);
+ state->ps.plan = (Plan *) node;
+ state->ps.state = estate;
+
+ state->pending_srf_tuples = false;
+
+ /*
+ * Miscellaneous initialization
+ *
+ * create expression context for node
+ */
+ ExecAssignExprContext(estate, &state->ps);
+
+ /*
+ * tuple table initialization
+ */
+ ExecInitResultTupleSlot(estate, &state->ps);
+
+ /*
+ * initialize child expressions
+ */
+ state->ps.targetlist = (List *)
+ ExecInitExpr((Expr *) node->plan.targetlist,
+ (PlanState *) state);
+ Assert(node->plan.qual == NIL);
+
+ /*
+ * initialize child nodes
+ */
+ outerPlanState(state) = ExecInitNode(outerPlan(node), estate, eflags);
+
+ /*
+ * we don't use inner plan
+ */
+ Assert(innerPlan(node) == NULL);
+
+ /*
+ * initialize tuple type and projection info
+ */
+ ExecAssignResultTypeFromTL(&state->ps);
+
+ /* Create workspace for per-SRF is-done state */
+ state->nelems = list_length(node->plan.targetlist);
+ state->elemdone = (ExprDoneCond *)
+ palloc(sizeof(ExprDoneCond) * state->nelems);
+
+ return state;
+}
+
+/* ----------------------------------------------------------------
+ * ExecEndProjectSet
+ *
+ * frees up storage allocated through C routines
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndProjectSet(ProjectSetState *node)
+{
+ /*
+ * Free the exprcontext
+ */
+ ExecFreeExprContext(&node->ps);
+
+ /*
+ * clean out the tuple table
+ */
+ ExecClearTuple(node->ps.ps_ResultTupleSlot);
+
+ /*
+ * shut down subplans
+ */
+ ExecEndNode(outerPlanState(node));
+}
+
+void
+ExecReScanProjectSet(ProjectSetState *node)
+{
+ /* Forget any incompletely-evaluated SRFs */
+ node->pending_srf_tuples = false;
+
+ /*
+ * If chgParam of subnode is not null then plan will be re-scanned by
+ * first ExecProcNode.
+ */
+ if (node->ps.lefttree->chgParam == NULL)
+ ExecReScan(node->ps.lefttree);
+}