aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor')
-rw-r--r--src/backend/executor/Makefile5
-rw-r--r--src/backend/executor/execAmi.c5
-rw-r--r--src/backend/executor/execProcnode.c16
-rw-r--r--src/backend/executor/execQual.c154
-rw-r--r--src/backend/executor/nodeProjectSet.c300
5 files changed, 398 insertions, 82 deletions
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 51edd4c5e70..c51415830ae 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -17,11 +17,12 @@ OBJS = execAmi.o execCurrent.o execGrouping.o execIndexing.o execJunk.o \
execScan.o execTuples.o \
execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
nodeBitmapAnd.o nodeBitmapOr.o \
- nodeBitmapHeapscan.o nodeBitmapIndexscan.o nodeCustom.o nodeGather.o \
+ nodeBitmapHeapscan.o nodeBitmapIndexscan.o \
+ nodeCustom.o nodeFunctionscan.o nodeGather.o \
nodeHash.o nodeHashjoin.o nodeIndexscan.o nodeIndexonlyscan.o \
nodeLimit.o nodeLockRows.o \
nodeMaterial.o nodeMergeAppend.o nodeMergejoin.o nodeModifyTable.o \
- nodeNestloop.o nodeFunctionscan.o nodeRecursiveunion.o nodeResult.o \
+ nodeNestloop.o nodeProjectSet.o nodeRecursiveunion.o nodeResult.o \
nodeSamplescan.o nodeSeqscan.o nodeSetOp.o nodeSort.o nodeUnique.o \
nodeValuesscan.o nodeCtescan.o nodeWorktablescan.o \
nodeGroup.o nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o \
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index 3ea36979b34..b52cfaa41f4 100644
--- a/src/backend/executor/execAmi.c
+++ b/src/backend/executor/execAmi.c
@@ -39,6 +39,7 @@
#include "executor/nodeMergejoin.h"
#include "executor/nodeModifyTable.h"
#include "executor/nodeNestloop.h"
+#include "executor/nodeProjectSet.h"
#include "executor/nodeRecursiveunion.h"
#include "executor/nodeResult.h"
#include "executor/nodeSamplescan.h"
@@ -130,6 +131,10 @@ ExecReScan(PlanState *node)
ExecReScanResult((ResultState *) node);
break;
+ case T_ProjectSetState:
+ ExecReScanProjectSet((ProjectSetState *) node);
+ break;
+
case T_ModifyTableState:
ExecReScanModifyTable((ModifyTableState *) node);
break;
diff --git a/src/backend/executor/execProcnode.c b/src/backend/executor/execProcnode.c
index b8edd364703..0dd95c6d174 100644
--- a/src/backend/executor/execProcnode.c
+++ b/src/backend/executor/execProcnode.c
@@ -88,6 +88,7 @@
#include "executor/nodeCustom.h"
#include "executor/nodeForeignscan.h"
#include "executor/nodeFunctionscan.h"
+#include "executor/nodeGather.h"
#include "executor/nodeGroup.h"
#include "executor/nodeHash.h"
#include "executor/nodeHashjoin.h"
@@ -100,7 +101,7 @@
#include "executor/nodeMergejoin.h"
#include "executor/nodeModifyTable.h"
#include "executor/nodeNestloop.h"
-#include "executor/nodeGather.h"
+#include "executor/nodeProjectSet.h"
#include "executor/nodeRecursiveunion.h"
#include "executor/nodeResult.h"
#include "executor/nodeSamplescan.h"
@@ -155,6 +156,11 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
estate, eflags);
break;
+ case T_ProjectSet:
+ result = (PlanState *) ExecInitProjectSet((ProjectSet *) node,
+ estate, eflags);
+ break;
+
case T_ModifyTable:
result = (PlanState *) ExecInitModifyTable((ModifyTable *) node,
estate, eflags);
@@ -392,6 +398,10 @@ ExecProcNode(PlanState *node)
result = ExecResult((ResultState *) node);
break;
+ case T_ProjectSetState:
+ result = ExecProjectSet((ProjectSetState *) node);
+ break;
+
case T_ModifyTableState:
result = ExecModifyTable((ModifyTableState *) node);
break;
@@ -634,6 +644,10 @@ ExecEndNode(PlanState *node)
ExecEndResult((ResultState *) node);
break;
+ case T_ProjectSetState:
+ ExecEndProjectSet((ProjectSetState *) node);
+ break;
+
case T_ModifyTableState:
ExecEndModifyTable((ModifyTableState *) node);
break;
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index bf007b7efd8..eed7e95c759 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -29,9 +29,9 @@
* instead of doing needless copying. -cim 5/31/91
*
* During expression evaluation, we check_stack_depth only in
- * ExecMakeFunctionResult (and substitute routines) rather than at every
- * single node. This is a compromise that trades off precision of the
- * stack limit setting to gain speed.
+ * ExecMakeFunctionResultSet/ExecMakeFunctionResultNoSets rather than at
+ * every single node. This is a compromise that trades off precision of
+ * the stack limit setting to gain speed.
*/
#include "postgres.h"
@@ -92,7 +92,7 @@ static Datum ExecEvalParamExec(ExprState *exprstate, ExprContext *econtext,
static Datum ExecEvalParamExtern(ExprState *exprstate, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
static void init_fcache(Oid foid, Oid input_collation, FuncExprState *fcache,
- MemoryContext fcacheCxt, bool needDescForSets);
+ MemoryContext fcacheCxt, bool allowSRF, bool needDescForSRF);
static void ShutdownFuncExpr(Datum arg);
static TupleDesc get_cached_rowtype(Oid type_id, int32 typmod,
TupleDesc *cache_field, ExprContext *econtext);
@@ -104,10 +104,6 @@ static void ExecPrepareTuplestoreResult(FuncExprState *fcache,
Tuplestorestate *resultStore,
TupleDesc resultDesc);
static void tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc);
-static Datum ExecMakeFunctionResult(FuncExprState *fcache,
- ExprContext *econtext,
- bool *isNull,
- ExprDoneCond *isDone);
static Datum ExecMakeFunctionResultNoSets(FuncExprState *fcache,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
@@ -1327,7 +1323,7 @@ GetAttributeByName(HeapTupleHeader tuple, const char *attname, bool *isNull)
*/
static void
init_fcache(Oid foid, Oid input_collation, FuncExprState *fcache,
- MemoryContext fcacheCxt, bool needDescForSets)
+ MemoryContext fcacheCxt, bool allowSRF, bool needDescForSRF)
{
AclResult aclresult;
@@ -1360,8 +1356,17 @@ init_fcache(Oid foid, Oid input_collation, FuncExprState *fcache,
list_length(fcache->args),
input_collation, NULL, NULL);
+ /* If function returns set, check if that's allowed by caller */
+ if (fcache->func.fn_retset && !allowSRF)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("set-valued function called in context that cannot accept a set")));
+
+ /* Otherwise, ExecInitExpr should have marked the fcache correctly */
+ Assert(fcache->func.fn_retset == fcache->funcReturnsSet);
+
/* If function returns set, prepare expected tuple descriptor */
- if (fcache->func.fn_retset && needDescForSets)
+ if (fcache->func.fn_retset && needDescForSRF)
{
TypeFuncClass functypclass;
Oid funcrettype;
@@ -1549,7 +1554,7 @@ ExecEvalFuncArgs(FunctionCallInfo fcinfo,
/*
* ExecPrepareTuplestoreResult
*
- * Subroutine for ExecMakeFunctionResult: prepare to extract rows from a
+ * Subroutine for ExecMakeFunctionResultSet: prepare to extract rows from a
* tuplestore function result. We must set up a funcResultSlot (unless
* already done in a previous call cycle) and verify that the function
* returned the expected tuple descriptor.
@@ -1673,19 +1678,17 @@ tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc)
}
/*
- * ExecMakeFunctionResult
+ * ExecMakeFunctionResultSet
*
- * Evaluate the arguments to a function and then the function itself.
- * init_fcache is presumed already run on the FuncExprState.
- *
- * This function handles the most general case, wherein the function or
- * one of its arguments can return a set.
+ * Evaluate the arguments to a set-returning function and then call the
+ * function itself. The argument expressions may not contain set-returning
+ * functions (the planner is supposed to have separated evaluation for those).
*/
-static Datum
-ExecMakeFunctionResult(FuncExprState *fcache,
- ExprContext *econtext,
- bool *isNull,
- ExprDoneCond *isDone)
+Datum
+ExecMakeFunctionResultSet(FuncExprState *fcache,
+ ExprContext *econtext,
+ bool *isNull,
+ ExprDoneCond *isDone)
{
List *arguments;
Datum result;
@@ -1702,6 +1705,31 @@ restart:
check_stack_depth();
/*
+ * Initialize function cache if first time through. The expression node
+ * could be either a FuncExpr or an OpExpr.
+ */
+ if (fcache->func.fn_oid == InvalidOid)
+ {
+ if (IsA(fcache->xprstate.expr, FuncExpr))
+ {
+ FuncExpr *func = (FuncExpr *) fcache->xprstate.expr;
+
+ init_fcache(func->funcid, func->inputcollid, fcache,
+ econtext->ecxt_per_query_memory, true, true);
+ }
+ else if (IsA(fcache->xprstate.expr, OpExpr))
+ {
+ OpExpr *op = (OpExpr *) fcache->xprstate.expr;
+
+ init_fcache(op->opfuncid, op->inputcollid, fcache,
+ econtext->ecxt_per_query_memory, true, true);
+ }
+ else
+ elog(ERROR, "unrecognized node type: %d",
+ (int) nodeTag(fcache->xprstate.expr));
+ }
+
+ /*
* If a previous call of the function returned a set result in the form of
* a tuplestore, continue reading rows from the tuplestore until it's
* empty.
@@ -1750,19 +1778,11 @@ restart:
if (!fcache->setArgsValid)
{
argDone = ExecEvalFuncArgs(fcinfo, arguments, econtext);
- if (argDone == ExprEndResult)
- {
- /* input is an empty set, so return an empty set. */
- *isNull = true;
- if (isDone)
- *isDone = ExprEndResult;
- else
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("set-valued function called in context that cannot accept a set")));
- return (Datum) 0;
- }
- hasSetArg = (argDone != ExprSingleResult);
+ if (argDone != ExprSingleResult)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("set-valued function called in context that cannot accept a set")));
+ hasSetArg = false;
}
else
{
@@ -1989,8 +2009,8 @@ restart:
/*
* ExecMakeFunctionResultNoSets
*
- * Simplified version of ExecMakeFunctionResult that can only handle
- * non-set cases. Hand-tuned for speed.
+ * Evaluate a function or operator node with a non-set-returning function.
+ * Assumes init_fcache() already done. Hand-tuned for speed.
*/
static Datum
ExecMakeFunctionResultNoSets(FuncExprState *fcache,
@@ -2120,7 +2140,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
ExprDoneCond argDone;
/*
- * This path is similar to ExecMakeFunctionResult.
+ * This path is similar to ExecMakeFunctionResultSet.
*/
direct_function_call = true;
@@ -2132,7 +2152,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
FuncExpr *func = (FuncExpr *) fcache->xprstate.expr;
init_fcache(func->funcid, func->inputcollid, fcache,
- econtext->ecxt_per_query_memory, false);
+ econtext->ecxt_per_query_memory, true, false);
}
returnsSet = fcache->func.fn_retset;
InitFunctionCallInfoData(fcinfo, &(fcache->func),
@@ -2423,24 +2443,11 @@ ExecEvalFunc(FuncExprState *fcache,
/* Initialize function lookup info */
init_fcache(func->funcid, func->inputcollid, fcache,
- econtext->ecxt_per_query_memory, true);
+ econtext->ecxt_per_query_memory, false, false);
- /*
- * We need to invoke ExecMakeFunctionResult if either the function itself
- * or any of its input expressions can return a set. Otherwise, invoke
- * ExecMakeFunctionResultNoSets. In either case, change the evalfunc
- * pointer to go directly there on subsequent uses.
- */
- if (fcache->func.fn_retset || expression_returns_set((Node *) func->args))
- {
- fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResult;
- return ExecMakeFunctionResult(fcache, econtext, isNull, isDone);
- }
- else
- {
- fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResultNoSets;
- return ExecMakeFunctionResultNoSets(fcache, econtext, isNull, isDone);
- }
+ /* Change the evalfunc pointer to save a few cycles in additional calls */
+ fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResultNoSets;
+ return ExecMakeFunctionResultNoSets(fcache, econtext, isNull, isDone);
}
/* ----------------------------------------------------------------
@@ -2458,24 +2465,11 @@ ExecEvalOper(FuncExprState *fcache,
/* Initialize function lookup info */
init_fcache(op->opfuncid, op->inputcollid, fcache,
- econtext->ecxt_per_query_memory, true);
+ econtext->ecxt_per_query_memory, false, false);
- /*
- * We need to invoke ExecMakeFunctionResult if either the function itself
- * or any of its input expressions can return a set. Otherwise, invoke
- * ExecMakeFunctionResultNoSets. In either case, change the evalfunc
- * pointer to go directly there on subsequent uses.
- */
- if (fcache->func.fn_retset || expression_returns_set((Node *) op->args))
- {
- fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResult;
- return ExecMakeFunctionResult(fcache, econtext, isNull, isDone);
- }
- else
- {
- fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResultNoSets;
- return ExecMakeFunctionResultNoSets(fcache, econtext, isNull, isDone);
- }
+ /* Change the evalfunc pointer to save a few cycles in additional calls */
+ fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResultNoSets;
+ return ExecMakeFunctionResultNoSets(fcache, econtext, isNull, isDone);
}
/* ----------------------------------------------------------------
@@ -2512,8 +2506,7 @@ ExecEvalDistinct(FuncExprState *fcache,
DistinctExpr *op = (DistinctExpr *) fcache->xprstate.expr;
init_fcache(op->opfuncid, op->inputcollid, fcache,
- econtext->ecxt_per_query_memory, true);
- Assert(!fcache->func.fn_retset);
+ econtext->ecxt_per_query_memory, false, false);
}
/*
@@ -2589,8 +2582,7 @@ ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
if (sstate->fxprstate.func.fn_oid == InvalidOid)
{
init_fcache(opexpr->opfuncid, opexpr->inputcollid, &sstate->fxprstate,
- econtext->ecxt_per_query_memory, true);
- Assert(!sstate->fxprstate.func.fn_retset);
+ econtext->ecxt_per_query_memory, false, false);
}
/*
@@ -3857,8 +3849,7 @@ ExecEvalNullIf(FuncExprState *nullIfExpr,
NullIfExpr *op = (NullIfExpr *) nullIfExpr->xprstate.expr;
init_fcache(op->opfuncid, op->inputcollid, nullIfExpr,
- econtext->ecxt_per_query_memory, true);
- Assert(!nullIfExpr->func.fn_retset);
+ econtext->ecxt_per_query_memory, false, false);
}
/*
@@ -4739,6 +4730,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
fstate->args = (List *)
ExecInitExpr((Expr *) funcexpr->args, parent);
fstate->func.fn_oid = InvalidOid; /* not initialized */
+ fstate->funcReturnsSet = funcexpr->funcretset;
state = (ExprState *) fstate;
}
break;
@@ -4751,6 +4743,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
fstate->args = (List *)
ExecInitExpr((Expr *) opexpr->args, parent);
fstate->func.fn_oid = InvalidOid; /* not initialized */
+ fstate->funcReturnsSet = opexpr->opretset;
state = (ExprState *) fstate;
}
break;
@@ -4763,6 +4756,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
fstate->args = (List *)
ExecInitExpr((Expr *) distinctexpr->args, parent);
fstate->func.fn_oid = InvalidOid; /* not initialized */
+ fstate->funcReturnsSet = false; /* not supported */
state = (ExprState *) fstate;
}
break;
@@ -4775,6 +4769,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
fstate->args = (List *)
ExecInitExpr((Expr *) nullifexpr->args, parent);
fstate->func.fn_oid = InvalidOid; /* not initialized */
+ fstate->funcReturnsSet = false; /* not supported */
state = (ExprState *) fstate;
}
break;
@@ -4787,6 +4782,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
sstate->fxprstate.args = (List *)
ExecInitExpr((Expr *) opexpr->args, parent);
sstate->fxprstate.func.fn_oid = InvalidOid; /* not initialized */
+ sstate->fxprstate.funcReturnsSet = false; /* not supported */
sstate->element_type = InvalidOid; /* ditto */
state = (ExprState *) sstate;
}
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);
+}