diff options
Diffstat (limited to 'src/backend/executor')
-rw-r--r-- | src/backend/executor/Makefile | 5 | ||||
-rw-r--r-- | src/backend/executor/execAmi.c | 5 | ||||
-rw-r--r-- | src/backend/executor/execProcnode.c | 16 | ||||
-rw-r--r-- | src/backend/executor/execQual.c | 154 | ||||
-rw-r--r-- | src/backend/executor/nodeProjectSet.c | 300 |
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); +} |