From d38013a7e9d49dd7b1e9c6c2b22c9906945e7010 Mon Sep 17 00:00:00 2001 From: Andres Freund Date: Thu, 25 Aug 2016 11:24:49 -0700 Subject: [PATCH 6/6] Remove unused code related to targetlist SRFs. --- src/backend/catalog/index.c | 3 +- src/backend/commands/copy.c | 2 +- src/backend/commands/prepare.c | 3 +- src/backend/commands/tablecmds.c | 3 +- src/backend/commands/typecmds.c | 2 +- src/backend/executor/execAmi.c | 42 +- src/backend/executor/execQual.c | 1174 ++++------------------------- src/backend/executor/execScan.c | 30 +- src/backend/executor/execUtils.c | 6 - src/backend/executor/nodeAgg.c | 52 +- src/backend/executor/nodeBitmapHeapscan.c | 2 - src/backend/executor/nodeCtescan.c | 2 - src/backend/executor/nodeCustom.c | 2 - src/backend/executor/nodeForeignscan.c | 2 - src/backend/executor/nodeFunctionscan.c | 16 +- src/backend/executor/nodeGather.c | 25 +- src/backend/executor/nodeGroup.c | 42 +- src/backend/executor/nodeHash.c | 2 +- src/backend/executor/nodeHashjoin.c | 52 +- src/backend/executor/nodeIndexonlyscan.c | 2 - src/backend/executor/nodeIndexscan.c | 11 +- src/backend/executor/nodeLimit.c | 19 +- src/backend/executor/nodeMergejoin.c | 59 +- src/backend/executor/nodeModifyTable.c | 4 +- src/backend/executor/nodeNestloop.c | 41 +- src/backend/executor/nodeResult.c | 33 +- src/backend/executor/nodeSamplescan.c | 8 +- src/backend/executor/nodeSeqscan.c | 2 - src/backend/executor/nodeSubplan.c | 31 +- src/backend/executor/nodeSubqueryscan.c | 2 - src/backend/executor/nodeTidscan.c | 8 +- src/backend/executor/nodeValuesscan.c | 5 +- src/backend/executor/nodeWindowAgg.c | 58 +- src/backend/executor/nodeWorktablescan.c | 2 - src/backend/optimizer/plan/planner.c | 119 +-- src/backend/optimizer/util/clauses.c | 46 +- src/backend/optimizer/util/predtest.c | 2 +- src/backend/utils/adt/domains.c | 2 +- src/backend/utils/adt/xml.c | 4 +- src/include/executor/executor.h | 13 +- src/include/nodes/execnodes.h | 16 +- src/include/optimizer/clauses.h | 1 - src/pl/plpgsql/src/pl_exec.c | 5 +- 43 files changed, 284 insertions(+), 1671 deletions(-) diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index b0b43cf..fd82855 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1788,8 +1788,7 @@ FormIndexDatum(IndexInfo *indexInfo, elog(ERROR, "wrong number of index expressions"); iDatum = ExecEvalExprSwitchContext((ExprState *) lfirst(indexpr_item), GetPerTupleExprContext(estate), - &isNull, - NULL); + &isNull); indexpr_item = lnext(indexpr_item); } values[i] = iDatum; diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index f45b330..28466ac 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -3172,7 +3172,7 @@ NextCopyFrom(CopyState cstate, ExprContext *econtext, Assert(CurrentMemoryContext == econtext->ecxt_per_tuple_memory); values[defmap[i]] = ExecEvalExpr(defexprs[i], econtext, - &nulls[defmap[i]], NULL); + &nulls[defmap[i]]); } return true; diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c index cec37ce..451c8d5 100644 --- a/src/backend/commands/prepare.c +++ b/src/backend/commands/prepare.c @@ -404,8 +404,7 @@ EvaluateParams(PreparedStatement *pstmt, List *params, prm->pflags = PARAM_FLAG_CONST; prm->value = ExecEvalExprSwitchContext(n, GetPerTupleExprContext(estate), - &prm->isnull, - NULL); + &prm->isnull); i++; } diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 86e9814..92e468d 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -4151,8 +4151,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) values[ex->attnum - 1] = ExecEvalExpr(ex->exprstate, econtext, - &isnull[ex->attnum - 1], - NULL); + &isnull[ex->attnum - 1]); } /* diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index ce04211..755af68 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -2741,7 +2741,7 @@ validateDomainConstraint(Oid domainoid, char *ccbin) conResult = ExecEvalExprSwitchContext(exprstate, econtext, - &isNull, NULL); + &isNull); if (!isNull && !DatumGetBool(conResult)) { diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c index ea2f09e..959d27a 100644 --- a/src/backend/executor/execAmi.c +++ b/src/backend/executor/execAmi.c @@ -58,7 +58,6 @@ #include "utils/syscache.h" -static bool TargetListSupportsBackwardScan(List *targetlist); static bool IndexSupportsBackwardScan(Oid indexid); @@ -119,7 +118,7 @@ ExecReScan(PlanState *node) UpdateChangedParamSet(node->righttree, node->chgParam); } - /* Shut down any SRFs in the plan node's targetlist */ + /* Call expression callbacks */ if (node->ps_ExprContext) ReScanExprContext(node->ps_ExprContext); @@ -455,8 +454,7 @@ ExecSupportsBackwardScan(Plan *node) { case T_Result: if (outerPlan(node) != NULL) - return ExecSupportsBackwardScan(outerPlan(node)) && - TargetListSupportsBackwardScan(node->targetlist); + return ExecSupportsBackwardScan(outerPlan(node)); else return false; @@ -473,12 +471,6 @@ ExecSupportsBackwardScan(Plan *node) return true; } - case T_SeqScan: - case T_TidScan: - case T_ValuesScan: - case T_CteScan: - return TargetListSupportsBackwardScan(node->targetlist); - case T_SampleScan: /* Simplify life for tablesample methods by disallowing this */ return false; @@ -487,35 +479,33 @@ ExecSupportsBackwardScan(Plan *node) return false; case T_IndexScan: - return IndexSupportsBackwardScan(((IndexScan *) node)->indexid) && - TargetListSupportsBackwardScan(node->targetlist); + return IndexSupportsBackwardScan(((IndexScan *) node)->indexid); case T_IndexOnlyScan: - return IndexSupportsBackwardScan(((IndexOnlyScan *) node)->indexid) && - TargetListSupportsBackwardScan(node->targetlist); + return IndexSupportsBackwardScan(((IndexOnlyScan *) node)->indexid); case T_SubqueryScan: - return ExecSupportsBackwardScan(((SubqueryScan *) node)->subplan) && - TargetListSupportsBackwardScan(node->targetlist); + return ExecSupportsBackwardScan(((SubqueryScan *) node)->subplan); case T_CustomScan: { uint32 flags = ((CustomScan *) node)->flags; - if ((flags & CUSTOMPATH_SUPPORT_BACKWARD_SCAN) && - TargetListSupportsBackwardScan(node->targetlist)) + if (flags & CUSTOMPATH_SUPPORT_BACKWARD_SCAN) return true; } return false; + case T_SeqScan: + case T_TidScan: + case T_ValuesScan: + case T_CteScan: case T_Material: case T_Sort: - /* these don't evaluate tlist */ return true; case T_LockRows: case T_Limit: - /* these don't evaluate tlist */ return ExecSupportsBackwardScan(outerPlan(node)); default: @@ -524,18 +514,6 @@ ExecSupportsBackwardScan(Plan *node) } /* - * If the tlist contains set-returning functions, we can't support backward - * scan, because the TupFromTlist code is direction-ignorant. - */ -static bool -TargetListSupportsBackwardScan(List *targetlist) -{ - if (expression_returns_set((Node *) targetlist)) - return false; - return true; -} - -/* * An IndexScan or IndexOnlyScan node supports backward scan only if the * index's AM does. */ diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index d9e2797..1bdc0ac 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -64,127 +64,115 @@ /* static function decls */ static Datum ExecEvalArrayRef(ArrayRefExprState *astate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static bool isAssignmentIndirectionExpr(ExprState *exprstate); static Datum ExecEvalAggref(AggrefExprState *aggref, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalWindowFunc(WindowFuncExprState *wfunc, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalScalarVarFast(ExprState *exprstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalWholeRowFast(WholeRowVarExprState *wrvstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalWholeRowSlow(WholeRowVarExprState *wrvstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalConst(ExprState *exprstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalParamExec(ExprState *exprstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalParamExtern(ExprState *exprstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); -static void ShutdownFuncExpr(Datum arg); -static TupleDesc get_cached_rowtype(Oid type_id, int32 typmod, - TupleDesc *cache_field, ExprContext *econtext); + bool *isNull); static void ShutdownTupleDescRef(Datum arg); -static void ExecPrepareTuplestoreResult(FuncExprState *fcache, - ExprContext *econtext, - 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); + bool *isNull); static Datum ExecEvalFunc(FuncExprState *fcache, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalOper(FuncExprState *fcache, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalDistinct(FuncExprState *fcache, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalNot(BoolExprState *notclause, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalOr(BoolExprState *orExpr, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalCaseTestExpr(ExprState *exprstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalArray(ArrayExprState *astate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalRow(RowExprState *rstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalRowCompare(RowCompareExprState *rstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalCoalesce(CoalesceExprState *coalesceExpr, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalMinMax(MinMaxExprState *minmaxExpr, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalSQLValueFunction(ExprState *svfExpr, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalNullIf(FuncExprState *nullIfExpr, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalNullTest(NullTestState *nstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalBooleanTest(GenericExprState *bstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalCoerceToDomain(CoerceToDomainState *cstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalCoerceToDomainValue(ExprState *exprstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalFieldSelect(FieldSelectState *fstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalFieldStore(FieldStoreState *fstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalRelabelType(GenericExprState *exprstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalCoerceViaIO(CoerceViaIOState *iostate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); static Datum ExecEvalGroupingFuncExpr(GroupingFuncExprState *gstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull); /* ---------------------------------------------------------------- @@ -195,8 +183,7 @@ static Datum ExecEvalGroupingFuncExpr(GroupingFuncExprState *gstate, * Each of the following routines having the signature * Datum ExecEvalFoo(ExprState *expression, * ExprContext *econtext, - * bool *isNull, - * ExprDoneCond *isDone); + * bool *isNull); * is responsible for evaluating one type or subtype of ExprState node. * They are normally called via the ExecEvalExpr macro, which makes use of * the function pointer set up when the ExprState node was built by @@ -220,22 +207,6 @@ static Datum ExecEvalGroupingFuncExpr(GroupingFuncExprState *gstate, * return value: Datum value of result * *isNull: set to TRUE if result is NULL (actual return value is * meaningless if so); set to FALSE if non-null result - * *isDone: set to indicator of set-result status - * - * A caller that can only accept a singleton (non-set) result should pass - * NULL for isDone; if the expression computes a set result then an error - * will be reported via ereport. If the caller does pass an isDone pointer - * then *isDone is set to one of these three states: - * ExprSingleResult singleton result (not a set) - * ExprMultipleResult return value is one element of a set - * ExprEndResult there are no more elements in the set - * When ExprMultipleResult is returned, the caller should invoke - * ExecEvalExpr() repeatedly until ExprEndResult is returned. ExprEndResult - * is returned after the last real set element. For convenience isNull will - * always be set TRUE when ExprEndResult is returned, but this should not be - * taken as indicating a NULL element of the set. Note that these return - * conventions allow us to distinguish among a singleton NULL, a NULL element - * of a set, and an empty set. * * The caller should already have switched into the temporary memory * context econtext->ecxt_per_tuple_memory. The convenience entry point @@ -260,8 +231,7 @@ static Datum ExecEvalGroupingFuncExpr(GroupingFuncExprState *gstate, static Datum ExecEvalArrayRef(ArrayRefExprState *astate, ExprContext *econtext, - bool *isNull, - ExprDoneCond *isDone) + bool *isNull) { ArrayRef *arrayRef = (ArrayRef *) astate->xprstate.expr; Datum array_source; @@ -278,8 +248,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate, array_source = ExecEvalExpr(astate->refexpr, econtext, - isNull, - isDone); + isNull); /* * If refexpr yields NULL, and it's a fetch, then result is NULL. In the @@ -287,8 +256,6 @@ ExecEvalArrayRef(ArrayRefExprState *astate, */ if (*isNull) { - if (isDone && *isDone == ExprEndResult) - return (Datum) NULL; /* end of set result */ if (!isAssignment) return (Datum) NULL; } @@ -314,8 +281,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate, upper.indx[i++] = DatumGetInt32(ExecEvalExpr(eltstate, econtext, - &eisnull, - NULL)); + &eisnull)); /* If any index expr yields NULL, result is NULL or error */ if (eisnull) { @@ -350,8 +316,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate, lower.indx[j++] = DatumGetInt32(ExecEvalExpr(eltstate, econtext, - &eisnull, - NULL)); + &eisnull)); /* If any index expr yields NULL, result is NULL or error */ if (eisnull) { @@ -438,8 +403,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate, */ sourceData = ExecEvalExpr(astate->refassgnexpr, econtext, - &eisnull, - NULL); + &eisnull); econtext->caseValue_datum = save_datum; econtext->caseValue_isNull = save_isNull; @@ -542,11 +506,8 @@ isAssignmentIndirectionExpr(ExprState *exprstate) */ static Datum ExecEvalAggref(AggrefExprState *aggref, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) + bool *isNull) { - if (isDone) - *isDone = ExprSingleResult; - if (econtext->ecxt_aggvalues == NULL) /* safety check */ elog(ERROR, "no aggregates in this expression context"); @@ -563,11 +524,8 @@ ExecEvalAggref(AggrefExprState *aggref, ExprContext *econtext, */ static Datum ExecEvalWindowFunc(WindowFuncExprState *wfunc, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) + bool *isNull) { - if (isDone) - *isDone = ExprSingleResult; - if (econtext->ecxt_aggvalues == NULL) /* safety check */ elog(ERROR, "no window functions in this expression context"); @@ -588,15 +546,12 @@ ExecEvalWindowFunc(WindowFuncExprState *wfunc, ExprContext *econtext, */ static Datum ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) + bool *isNull) { Var *variable = (Var *) exprstate->expr; TupleTableSlot *slot; AttrNumber attnum; - if (isDone) - *isDone = ExprSingleResult; - /* Get the input slot and attribute number we want */ switch (variable->varno) { @@ -677,15 +632,12 @@ ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext, */ static Datum ExecEvalScalarVarFast(ExprState *exprstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) + bool *isNull) { Var *variable = (Var *) exprstate->expr; TupleTableSlot *slot; AttrNumber attnum; - if (isDone) - *isDone = ExprSingleResult; - /* Get the input slot and attribute number we want */ switch (variable->varno) { @@ -725,7 +677,7 @@ ExecEvalScalarVarFast(ExprState *exprstate, ExprContext *econtext, */ static Datum ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) + bool *isNull) { Var *variable = (Var *) wrvstate->xprstate.expr; TupleTableSlot *slot; @@ -733,9 +685,6 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext, MemoryContext oldcontext; bool needslow = false; - if (isDone) - *isDone = ExprSingleResult; - /* This was checked by ExecInitExpr */ Assert(variable->varattno == InvalidAttrNumber); @@ -941,7 +890,7 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext, /* Fetch the value */ return (*wrvstate->xprstate.evalfunc) ((ExprState *) wrvstate, econtext, - isNull, isDone); + isNull); } /* ---------------------------------------------------------------- @@ -952,14 +901,12 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext, */ static Datum ExecEvalWholeRowFast(WholeRowVarExprState *wrvstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) + bool *isNull) { Var *variable = (Var *) wrvstate->xprstate.expr; TupleTableSlot *slot; HeapTupleHeader dtuple; - if (isDone) - *isDone = ExprSingleResult; *isNull = false; /* Get the input slot we want */ @@ -1008,7 +955,7 @@ ExecEvalWholeRowFast(WholeRowVarExprState *wrvstate, ExprContext *econtext, */ static Datum ExecEvalWholeRowSlow(WholeRowVarExprState *wrvstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) + bool *isNull) { Var *variable = (Var *) wrvstate->xprstate.expr; TupleTableSlot *slot; @@ -1018,8 +965,6 @@ ExecEvalWholeRowSlow(WholeRowVarExprState *wrvstate, ExprContext *econtext, HeapTupleHeader dtuple; int i; - if (isDone) - *isDone = ExprSingleResult; *isNull = false; /* Get the input slot we want */ @@ -1097,13 +1042,10 @@ ExecEvalWholeRowSlow(WholeRowVarExprState *wrvstate, ExprContext *econtext, */ static Datum ExecEvalConst(ExprState *exprstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) + bool *isNull) { Const *con = (Const *) exprstate->expr; - if (isDone) - *isDone = ExprSingleResult; - *isNull = con->constisnull; return con->constvalue; } @@ -1116,15 +1058,12 @@ ExecEvalConst(ExprState *exprstate, ExprContext *econtext, */ static Datum ExecEvalParamExec(ExprState *exprstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) + bool *isNull) { Param *expression = (Param *) exprstate->expr; int thisParamId = expression->paramid; ParamExecData *prm; - if (isDone) - *isDone = ExprSingleResult; - /* * PARAM_EXEC params (internal executor parameters) are stored in the * ecxt_param_exec_vals array, and can be accessed by array index. @@ -1149,15 +1088,12 @@ ExecEvalParamExec(ExprState *exprstate, ExprContext *econtext, */ static Datum ExecEvalParamExtern(ExprState *exprstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) + bool *isNull) { Param *expression = (Param *) exprstate->expr; int thisParamId = expression->paramid; ParamListInfo paramInfo = econtext->ecxt_param_list_info; - if (isDone) - *isDone = ExprSingleResult; - /* * PARAM_EXTERN parameters must be sought in ecxt_param_list_info. */ @@ -1323,7 +1259,7 @@ GetAttributeByName(HeapTupleHeader tuple, const char *attname, bool *isNull) */ void ExecInitFcache(Oid foid, Oid input_collation, FuncExprState *fcache, - MemoryContext fcacheCxt, bool needDescForSets) + MemoryContext fcacheCxt) { AclResult aclresult; @@ -1356,88 +1292,9 @@ ExecInitFcache(Oid foid, Oid input_collation, FuncExprState *fcache, list_length(fcache->args), input_collation, NULL, NULL); - /* If function returns set, prepare expected tuple descriptor */ - if (fcache->func.fn_retset && needDescForSets) - { - TypeFuncClass functypclass; - Oid funcrettype; - TupleDesc tupdesc; - MemoryContext oldcontext; - - functypclass = get_expr_result_type(fcache->func.fn_expr, - &funcrettype, - &tupdesc); - - /* Must save tupdesc in fcache's context */ - oldcontext = MemoryContextSwitchTo(fcacheCxt); - - if (functypclass == TYPEFUNC_COMPOSITE) - { - /* Composite data type, e.g. a table's row type */ - Assert(tupdesc); - /* Must copy it out of typcache for safety */ - fcache->funcResultDesc = CreateTupleDescCopy(tupdesc); - fcache->funcReturnsTuple = true; - } - else if (functypclass == TYPEFUNC_SCALAR) - { - /* Base data type, i.e. scalar */ - tupdesc = CreateTemplateTupleDesc(1, false); - TupleDescInitEntry(tupdesc, - (AttrNumber) 1, - NULL, - funcrettype, - -1, - 0); - fcache->funcResultDesc = tupdesc; - fcache->funcReturnsTuple = false; - } - else if (functypclass == TYPEFUNC_RECORD) - { - /* This will work if function doesn't need an expectedDesc */ - fcache->funcResultDesc = NULL; - fcache->funcReturnsTuple = true; - } - else - { - /* Else, we will fail if function needs an expectedDesc */ - fcache->funcResultDesc = NULL; - } - - MemoryContextSwitchTo(oldcontext); - } - else - fcache->funcResultDesc = NULL; - /* Initialize additional state */ fcache->funcResultStore = NULL; fcache->funcResultSlot = NULL; - fcache->setArgsValid = false; - fcache->shutdown_reg = false; -} - -/* - * callback function in case a FuncExpr returning a set needs to be shut down - * before it has been run to completion - */ -static void -ShutdownFuncExpr(Datum arg) -{ - FuncExprState *fcache = (FuncExprState *) DatumGetPointer(arg); - - /* If we have a slot, make sure it's let go of any tuplestore pointer */ - if (fcache->funcResultSlot) - ExecClearTuple(fcache->funcResultSlot); - - /* Release any open tuplestore */ - if (fcache->funcResultStore) - tuplestore_end(fcache->funcResultStore); - fcache->funcResultStore = NULL; - - /* Clear any active set-argument state */ - fcache->setArgsValid = false; - - /* execUtils will deregister the callback... */ fcache->shutdown_reg = false; } @@ -1499,500 +1356,38 @@ ShutdownTupleDescRef(Datum arg) /* * Evaluate arguments for a function. */ -ExprDoneCond +void ExecEvalFuncArgs(FunctionCallInfo fcinfo, List *argList, ExprContext *econtext) { - ExprDoneCond argIsDone; int i; ListCell *arg; - argIsDone = ExprSingleResult; /* default assumption */ - i = 0; foreach(arg, argList) { ExprState *argstate = (ExprState *) lfirst(arg); - ExprDoneCond thisArgIsDone; fcinfo->arg[i] = ExecEvalExpr(argstate, econtext, - &fcinfo->argnull[i], - &thisArgIsDone); - - if (thisArgIsDone != ExprSingleResult) - { - /* - * We allow only one argument to have a set value; we'd need much - * more complexity to keep track of multiple set arguments (cf. - * ExecTargetList) and it doesn't seem worth it. - */ - if (argIsDone != ExprSingleResult) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("functions and operators can take at most one set argument"))); - argIsDone = thisArgIsDone; - } + &fcinfo->argnull[i]); i++; } Assert(i == fcinfo->nargs); - - return argIsDone; -} - -/* - * ExecPrepareTuplestoreResult - * - * Subroutine for ExecMakeFunctionResult: 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. - */ -static void -ExecPrepareTuplestoreResult(FuncExprState *fcache, - ExprContext *econtext, - Tuplestorestate *resultStore, - TupleDesc resultDesc) -{ - fcache->funcResultStore = resultStore; - - if (fcache->funcResultSlot == NULL) - { - /* Create a slot so we can read data out of the tuplestore */ - TupleDesc slotDesc; - MemoryContext oldcontext; - - oldcontext = MemoryContextSwitchTo(fcache->func.fn_mcxt); - - /* - * If we were not able to determine the result rowtype from context, - * and the function didn't return a tupdesc, we have to fail. - */ - if (fcache->funcResultDesc) - slotDesc = fcache->funcResultDesc; - else if (resultDesc) - { - /* don't assume resultDesc is long-lived */ - slotDesc = CreateTupleDescCopy(resultDesc); - } - else - { - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("function returning setof record called in " - "context that cannot accept type record"))); - slotDesc = NULL; /* keep compiler quiet */ - } - - fcache->funcResultSlot = MakeSingleTupleTableSlot(slotDesc); - MemoryContextSwitchTo(oldcontext); - } - - /* - * If function provided a tupdesc, cross-check it. We only really need to - * do this for functions returning RECORD, but might as well do it always. - */ - if (resultDesc) - { - if (fcache->funcResultDesc) - tupledesc_match(fcache->funcResultDesc, resultDesc); - - /* - * If it is a dynamically-allocated TupleDesc, free it: it is - * typically allocated in a per-query context, so we must avoid - * leaking it across multiple usages. - */ - if (resultDesc->tdrefcount == -1) - FreeTupleDesc(resultDesc); - } - - /* Register cleanup callback if we didn't already */ - if (!fcache->shutdown_reg) - { - RegisterExprContextCallback(econtext, - ShutdownFuncExpr, - PointerGetDatum(fcache)); - fcache->shutdown_reg = true; - } -} - -/* - * Check that function result tuple type (src_tupdesc) matches or can - * be considered to match what the query expects (dst_tupdesc). If - * they don't match, ereport. - * - * We really only care about number of attributes and data type. - * Also, we can ignore type mismatch on columns that are dropped in the - * destination type, so long as the physical storage matches. This is - * helpful in some cases involving out-of-date cached plans. - */ -static void -tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc) -{ - int i; - - if (dst_tupdesc->natts != src_tupdesc->natts) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("function return row and query-specified return row do not match"), - errdetail_plural("Returned row contains %d attribute, but query expects %d.", - "Returned row contains %d attributes, but query expects %d.", - src_tupdesc->natts, - src_tupdesc->natts, dst_tupdesc->natts))); - - for (i = 0; i < dst_tupdesc->natts; i++) - { - Form_pg_attribute dattr = dst_tupdesc->attrs[i]; - Form_pg_attribute sattr = src_tupdesc->attrs[i]; - - if (IsBinaryCoercible(sattr->atttypid, dattr->atttypid)) - continue; /* no worries */ - if (!dattr->attisdropped) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("function return row and query-specified return row do not match"), - errdetail("Returned type %s at ordinal position %d, but query expects %s.", - format_type_be(sattr->atttypid), - i + 1, - format_type_be(dattr->atttypid)))); - - if (dattr->attlen != sattr->attlen || - dattr->attalign != sattr->attalign) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("function return row and query-specified return row do not match"), - errdetail("Physical storage mismatch on dropped attribute at ordinal position %d.", - i + 1))); - } -} - -/* - * ExecMakeFunctionResult - * - * 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. - */ -static Datum -ExecMakeFunctionResult(FuncExprState *fcache, - ExprContext *econtext, - bool *isNull, - ExprDoneCond *isDone) -{ - List *arguments; - Datum result; - FunctionCallInfo fcinfo; - PgStat_FunctionCallUsage fcusage; - ReturnSetInfo rsinfo; /* for functions returning sets */ - ExprDoneCond argDone; - bool hasSetArg; - int i; - -restart: - - /* Guard against stack overflow due to overly complex expressions */ - check_stack_depth(); - - /* - * 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. - */ - if (fcache->funcResultStore) - { - Assert(isDone); /* it was provided before ... */ - if (tuplestore_gettupleslot(fcache->funcResultStore, true, false, - fcache->funcResultSlot)) - { - *isDone = ExprMultipleResult; - if (fcache->funcReturnsTuple) - { - /* We must return the whole tuple as a Datum. */ - *isNull = false; - return ExecFetchSlotTupleDatum(fcache->funcResultSlot); - } - else - { - /* Extract the first column and return it as a scalar. */ - return slot_getattr(fcache->funcResultSlot, 1, isNull); - } - } - /* Exhausted the tuplestore, so clean up */ - tuplestore_end(fcache->funcResultStore); - fcache->funcResultStore = NULL; - /* We are done unless there was a set-valued argument */ - if (!fcache->setHasSetArg) - { - *isDone = ExprEndResult; - *isNull = true; - return (Datum) 0; - } - /* If there was, continue evaluating the argument values */ - Assert(!fcache->setArgsValid); - } - - /* - * arguments is a list of expressions to evaluate before passing to the - * function manager. We skip the evaluation if it was already done in the - * previous call (ie, we are continuing the evaluation of a set-valued - * function). Otherwise, collect the current argument values into fcinfo. - */ - fcinfo = &fcache->fcinfo_data; - arguments = fcache->args; - 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); - } - else - { - /* Re-use callinfo from previous evaluation */ - hasSetArg = fcache->setHasSetArg; - /* Reset flag (we may set it again below) */ - fcache->setArgsValid = false; - } - - /* - * Now call the function, passing the evaluated parameter values. - */ - if (fcache->func.fn_retset || hasSetArg) - { - /* - * We need to return a set result. Complain if caller not ready to - * accept one. - */ - if (isDone == NULL) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - - /* - * Prepare a resultinfo node for communication. If the function - * doesn't itself return set, we don't pass the resultinfo to the - * function, but we need to fill it in anyway for internal use. - */ - if (fcache->func.fn_retset) - fcinfo->resultinfo = (Node *) &rsinfo; - rsinfo.type = T_ReturnSetInfo; - rsinfo.econtext = econtext; - rsinfo.expectedDesc = fcache->funcResultDesc; - rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize); - /* note we do not set SFRM_Materialize_Random or _Preferred */ - rsinfo.returnMode = SFRM_ValuePerCall; - /* isDone is filled below */ - rsinfo.setResult = NULL; - rsinfo.setDesc = NULL; - - /* - * This loop handles the situation where we have both a set argument - * and a set-valued function. Once we have exhausted the function's - * value(s) for a particular argument value, we have to get the next - * argument value and start the function over again. We might have to - * do it more than once, if the function produces an empty result set - * for a particular input value. - */ - for (;;) - { - /* - * If function is strict, and there are any NULL arguments, skip - * calling the function (at least for this set of args). - */ - bool callit = true; - - if (fcache->func.fn_strict) - { - for (i = 0; i < fcinfo->nargs; i++) - { - if (fcinfo->argnull[i]) - { - callit = false; - break; - } - } - } - - if (callit) - { - pgstat_init_function_usage(fcinfo, &fcusage); - - fcinfo->isnull = false; - rsinfo.isDone = ExprSingleResult; - result = FunctionCallInvoke(fcinfo); - *isNull = fcinfo->isnull; - *isDone = rsinfo.isDone; - - pgstat_end_function_usage(&fcusage, - rsinfo.isDone != ExprMultipleResult); - } - else if (fcache->func.fn_retset) - { - /* for a strict SRF, result for NULL is an empty set */ - result = (Datum) 0; - *isNull = true; - *isDone = ExprEndResult; - } - else - { - /* for a strict non-SRF, result for NULL is a NULL */ - result = (Datum) 0; - *isNull = true; - *isDone = ExprSingleResult; - } - - /* Which protocol does function want to use? */ - if (rsinfo.returnMode == SFRM_ValuePerCall) - { - if (*isDone != ExprEndResult) - { - /* - * Got a result from current argument. If function itself - * returns set, save the current argument values to re-use - * on the next call. - */ - if (fcache->func.fn_retset && - *isDone == ExprMultipleResult) - { - fcache->setHasSetArg = hasSetArg; - fcache->setArgsValid = true; - /* Register cleanup callback if we didn't already */ - if (!fcache->shutdown_reg) - { - RegisterExprContextCallback(econtext, - ShutdownFuncExpr, - PointerGetDatum(fcache)); - fcache->shutdown_reg = true; - } - } - - /* - * Make sure we say we are returning a set, even if the - * function itself doesn't return sets. - */ - if (hasSetArg) - *isDone = ExprMultipleResult; - break; - } - } - else if (rsinfo.returnMode == SFRM_Materialize) - { - /* check we're on the same page as the function author */ - if (rsinfo.isDone != ExprSingleResult) - ereport(ERROR, - (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED), - errmsg("table-function protocol for materialize mode was not followed"))); - if (rsinfo.setResult != NULL) - { - /* prepare to return values from the tuplestore */ - ExecPrepareTuplestoreResult(fcache, econtext, - rsinfo.setResult, - rsinfo.setDesc); - /* remember whether we had set arguments */ - fcache->setHasSetArg = hasSetArg; - /* loop back to top to start returning from tuplestore */ - goto restart; - } - /* if setResult was left null, treat it as empty set */ - *isDone = ExprEndResult; - *isNull = true; - result = (Datum) 0; - } - else - ereport(ERROR, - (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED), - errmsg("unrecognized table-function returnMode: %d", - (int) rsinfo.returnMode))); - - /* Else, done with this argument */ - if (!hasSetArg) - break; /* input not a set, so done */ - - /* Re-eval args to get the next element of the input set */ - argDone = ExecEvalFuncArgs(fcinfo, arguments, econtext); - - if (argDone != ExprMultipleResult) - { - /* End of argument set, so we're done. */ - *isNull = true; - *isDone = ExprEndResult; - result = (Datum) 0; - break; - } - - /* - * If we reach here, loop around to run the function on the new - * argument. - */ - } - } - else - { - /* - * Non-set case: much easier. - * - * In common cases, this code path is unreachable because we'd have - * selected ExecMakeFunctionResultNoSets instead. However, it's - * possible to get here if an argument sometimes produces set results - * and sometimes scalar results. For example, a CASE expression might - * call a set-returning function in only some of its arms. - */ - if (isDone) - *isDone = ExprSingleResult; - - /* - * If function is strict, and there are any NULL arguments, skip - * calling the function and return NULL. - */ - if (fcache->func.fn_strict) - { - for (i = 0; i < fcinfo->nargs; i++) - { - if (fcinfo->argnull[i]) - { - *isNull = true; - return (Datum) 0; - } - } - } - - pgstat_init_function_usage(fcinfo, &fcusage); - - fcinfo->isnull = false; - result = FunctionCallInvoke(fcinfo); - *isNull = fcinfo->isnull; - - pgstat_end_function_usage(&fcusage, true); - } - - return result; } /* * ExecMakeFunctionResultNoSets * - * Simplified version of ExecMakeFunctionResult that can only handle - * non-set cases. Hand-tuned for speed. + * Portion of ExecMakeFunctionResult that does not need initialization. + * Hand-tuned for speed. */ static Datum ExecMakeFunctionResultNoSets(FuncExprState *fcache, ExprContext *econtext, - bool *isNull, - ExprDoneCond *isDone) + bool *isNull) { ListCell *arg; Datum result; @@ -2003,9 +1398,6 @@ ExecMakeFunctionResultNoSets(FuncExprState *fcache, /* Guard against stack overflow due to overly complex expressions */ check_stack_depth(); - if (isDone) - *isDone = ExprSingleResult; - /* inlined, simplified version of ExecEvalFuncArgs */ fcinfo = &fcache->fcinfo_data; i = 0; @@ -2015,8 +1407,7 @@ ExecMakeFunctionResultNoSets(FuncExprState *fcache, fcinfo->arg[i] = ExecEvalExpr(argstate, econtext, - &fcinfo->argnull[i], - NULL); + &fcinfo->argnull[i]); i++; } @@ -2064,15 +1455,14 @@ ExecMakeFunctionResultNoSets(FuncExprState *fcache, static Datum ExecEvalFunc(FuncExprState *fcache, ExprContext *econtext, - bool *isNull, - ExprDoneCond *isDone) + bool *isNull) { /* This is called only the first time through */ FuncExpr *func = (FuncExpr *) fcache->xprstate.expr; /* Initialize function lookup info */ ExecInitFcache(func->funcid, func->inputcollid, fcache, - econtext->ecxt_per_query_memory, true); + econtext->ecxt_per_query_memory); if (fcache->func.fn_retset) { @@ -2081,22 +1471,9 @@ ExecEvalFunc(FuncExprState *fcache, errmsg("set-valued function called in context that cannot accept a set"))); } - /* - * 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 skip the above initialization. */ + fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResultNoSets; + return ExecMakeFunctionResultNoSets(fcache, econtext, isNull); } /* ---------------------------------------------------------------- @@ -2106,32 +1483,25 @@ ExecEvalFunc(FuncExprState *fcache, static Datum ExecEvalOper(FuncExprState *fcache, ExprContext *econtext, - bool *isNull, - ExprDoneCond *isDone) + bool *isNull) { /* This is called only the first time through */ OpExpr *op = (OpExpr *) fcache->xprstate.expr; /* Initialize function lookup info */ ExecInitFcache(op->opfuncid, op->inputcollid, fcache, - econtext->ecxt_per_query_memory, true); + econtext->ecxt_per_query_memory); - /* - * 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)) + if (fcache->func.fn_retset) { - fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResult; - return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); - } - else - { - fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResultNoSets; - return ExecMakeFunctionResultNoSets(fcache, econtext, isNull, isDone); + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("set-valued function called in context that cannot accept a set"))); } + + /* Change the evalfunc pointer, to skip the above initialization. */ + fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResultNoSets; + return ExecMakeFunctionResultNoSets(fcache, econtext, isNull); } /* ---------------------------------------------------------------- @@ -2148,17 +1518,13 @@ ExecEvalOper(FuncExprState *fcache, static Datum ExecEvalDistinct(FuncExprState *fcache, ExprContext *econtext, - bool *isNull, - ExprDoneCond *isDone) + bool *isNull) { Datum result; FunctionCallInfo fcinfo; - ExprDoneCond argDone; - /* Set default values for result flags: non-null, not a set result */ + /* Set non-null as default */ *isNull = false; - if (isDone) - *isDone = ExprSingleResult; /* * Initialize function cache if first time through @@ -2168,7 +1534,7 @@ ExecEvalDistinct(FuncExprState *fcache, DistinctExpr *op = (DistinctExpr *) fcache->xprstate.expr; ExecInitFcache(op->opfuncid, op->inputcollid, fcache, - econtext->ecxt_per_query_memory, true); + econtext->ecxt_per_query_memory); Assert(!fcache->func.fn_retset); } @@ -2176,11 +1542,7 @@ ExecEvalDistinct(FuncExprState *fcache, * Evaluate arguments */ fcinfo = &fcache->fcinfo_data; - argDone = ExecEvalFuncArgs(fcinfo, fcache->args, econtext); - if (argDone != ExprSingleResult) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("IS DISTINCT FROM does not support set arguments"))); + ExecEvalFuncArgs(fcinfo, fcache->args, econtext); Assert(fcinfo->nargs == 2); if (fcinfo->argnull[0] && fcinfo->argnull[1]) @@ -2216,7 +1578,7 @@ ExecEvalDistinct(FuncExprState *fcache, static Datum ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) + bool *isNull) { ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) sstate->fxprstate.xprstate.expr; bool useOr = opexpr->useOr; @@ -2225,7 +1587,6 @@ ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate, Datum result; bool resultnull; FunctionCallInfo fcinfo; - ExprDoneCond argDone; int i; int16 typlen; bool typbyval; @@ -2234,10 +1595,8 @@ ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate, bits8 *bitmap; int bitmask; - /* Set default values for result flags: non-null, not a set result */ + /* Set non-null as default */ *isNull = false; - if (isDone) - *isDone = ExprSingleResult; /* * Initialize function cache if first time through @@ -2245,7 +1604,7 @@ ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate, if (sstate->fxprstate.func.fn_oid == InvalidOid) { ExecInitFcache(opexpr->opfuncid, opexpr->inputcollid, &sstate->fxprstate, - econtext->ecxt_per_query_memory, true); + econtext->ecxt_per_query_memory); Assert(!sstate->fxprstate.func.fn_retset); } @@ -2253,11 +1612,7 @@ ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate, * Evaluate arguments */ fcinfo = &sstate->fxprstate.fcinfo_data; - argDone = ExecEvalFuncArgs(fcinfo, sstate->fxprstate.args, econtext); - if (argDone != ExprSingleResult) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("op ANY/ALL (array) does not support set arguments"))); + ExecEvalFuncArgs(fcinfo, sstate->fxprstate.args, econtext); Assert(fcinfo->nargs == 2); /* @@ -2403,15 +1758,12 @@ ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate, */ static Datum ExecEvalNot(BoolExprState *notclause, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) + bool *isNull) { ExprState *clause = linitial(notclause->args); Datum expr_value; - if (isDone) - *isDone = ExprSingleResult; - - expr_value = ExecEvalExpr(clause, econtext, isNull, NULL); + expr_value = ExecEvalExpr(clause, econtext, isNull); /* * if the expression evaluates to null, then we just cascade the null back @@ -2433,15 +1785,12 @@ ExecEvalNot(BoolExprState *notclause, ExprContext *econtext, */ static Datum ExecEvalOr(BoolExprState *orExpr, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) + bool *isNull) { List *clauses = orExpr->args; ListCell *clause; bool AnyNull; - if (isDone) - *isDone = ExprSingleResult; - AnyNull = false; /* @@ -2462,7 +1811,7 @@ ExecEvalOr(BoolExprState *orExpr, ExprContext *econtext, ExprState *clausestate = (ExprState *) lfirst(clause); Datum clause_value; - clause_value = ExecEvalExpr(clausestate, econtext, isNull, NULL); + clause_value = ExecEvalExpr(clausestate, econtext, isNull); /* * if we have a non-null true result, then return it. @@ -2484,15 +1833,12 @@ ExecEvalOr(BoolExprState *orExpr, ExprContext *econtext, */ static Datum ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) + bool *isNull) { List *clauses = andExpr->args; ListCell *clause; bool AnyNull; - if (isDone) - *isDone = ExprSingleResult; - AnyNull = false; /* @@ -2509,7 +1855,7 @@ ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext, ExprState *clausestate = (ExprState *) lfirst(clause); Datum clause_value; - clause_value = ExecEvalExpr(clausestate, econtext, isNull, NULL); + clause_value = ExecEvalExpr(clausestate, econtext, isNull); /* * if we have a non-null false result, then return it. @@ -2535,7 +1881,7 @@ ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext, static Datum ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) + bool *isNull) { ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) cstate->xprstate.expr; HeapTuple result; @@ -2543,7 +1889,7 @@ ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate, HeapTupleHeader tuple; HeapTupleData tmptup; - tupDatum = ExecEvalExpr(cstate->arg, econtext, isNull, isDone); + tupDatum = ExecEvalExpr(cstate->arg, econtext, isNull); /* this test covers the isDone exception too: */ if (*isNull) @@ -2619,16 +1965,13 @@ ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate, */ static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) + bool *isNull) { List *clauses = caseExpr->args; ListCell *clause; Datum save_datum; bool save_isNull; - if (isDone) - *isDone = ExprSingleResult; - /* * If there's a test expression, we have to evaluate it and save the value * where the CaseTestExpr placeholders can find it. We must save and @@ -2652,8 +1995,7 @@ ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext, econtext->caseValue_datum = ExecEvalExpr(caseExpr->arg, econtext, - &arg_isNull, - NULL); + &arg_isNull); econtext->caseValue_isNull = arg_isNull; } @@ -2670,8 +2012,7 @@ ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext, clause_value = ExecEvalExpr(wclause->expr, econtext, - &clause_isNull, - NULL); + &clause_isNull); /* * if we have a true test, then we return the result, since the case @@ -2684,8 +2025,7 @@ ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext, econtext->caseValue_isNull = save_isNull; return ExecEvalExpr(wclause->result, econtext, - isNull, - isDone); + isNull); } } @@ -2696,8 +2036,7 @@ ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext, { return ExecEvalExpr(caseExpr->defresult, econtext, - isNull, - isDone); + isNull); } *isNull = true; @@ -2712,10 +2051,8 @@ ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext, static Datum ExecEvalCaseTestExpr(ExprState *exprstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) + bool *isNull) { - if (isDone) - *isDone = ExprSingleResult; *isNull = econtext->caseValue_isNull; return econtext->caseValue_datum; } @@ -2732,17 +2069,13 @@ ExecEvalCaseTestExpr(ExprState *exprstate, static Datum ExecEvalGroupingFuncExpr(GroupingFuncExprState *gstate, ExprContext *econtext, - bool *isNull, - ExprDoneCond *isDone) + bool *isNull) { int result = 0; int attnum = 0; Bitmapset *grouped_cols = gstate->aggstate->grouped_cols; ListCell *lc; - if (isDone) - *isDone = ExprSingleResult; - *isNull = false; foreach(lc, (gstate->clauses)) @@ -2764,7 +2097,7 @@ ExecEvalGroupingFuncExpr(GroupingFuncExprState *gstate, */ static Datum ExecEvalArray(ArrayExprState *astate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) + bool *isNull) { ArrayExpr *arrayExpr = (ArrayExpr *) astate->xprstate.expr; ArrayType *result; @@ -2774,10 +2107,8 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext, int dims[MAXDIM]; int lbs[MAXDIM]; - /* Set default values for result flags: non-null, not a set result */ + /* Set default values for result flags: non-null */ *isNull = false; - if (isDone) - *isDone = ExprSingleResult; if (!arrayExpr->multidims) { @@ -2802,7 +2133,7 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext, { ExprState *e = (ExprState *) lfirst(element); - dvalues[i] = ExecEvalExpr(e, econtext, &dnulls[i], NULL); + dvalues[i] = ExecEvalExpr(e, econtext, &dnulls[i]); i++; } @@ -2852,7 +2183,7 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext, ArrayType *array; int this_ndims; - arraydatum = ExecEvalExpr(e, econtext, &eisnull, NULL); + arraydatum = ExecEvalExpr(e, econtext, &eisnull); /* temporarily ignore null subarrays */ if (eisnull) { @@ -2991,7 +2322,7 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext, static Datum ExecEvalRow(RowExprState *rstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) + bool *isNull) { HeapTuple tuple; Datum *values; @@ -3000,10 +2331,8 @@ ExecEvalRow(RowExprState *rstate, ListCell *arg; int i; - /* Set default values for result flags: non-null, not a set result */ + /* Set default values for result flag: non-null */ *isNull = false; - if (isDone) - *isDone = ExprSingleResult; /* Allocate workspace */ natts = rstate->tupdesc->natts; @@ -3019,7 +2348,7 @@ ExecEvalRow(RowExprState *rstate, { ExprState *e = (ExprState *) lfirst(arg); - values[i] = ExecEvalExpr(e, econtext, &isnull[i], NULL); + values[i] = ExecEvalExpr(e, econtext, &isnull[i]); i++; } @@ -3038,7 +2367,7 @@ ExecEvalRow(RowExprState *rstate, static Datum ExecEvalRowCompare(RowCompareExprState *rstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) + bool *isNull) { bool result; RowCompareType rctype = ((RowCompareExpr *) rstate->xprstate.expr)->rctype; @@ -3047,8 +2376,6 @@ ExecEvalRowCompare(RowCompareExprState *rstate, ListCell *r; int i; - if (isDone) - *isDone = ExprSingleResult; *isNull = true; /* until we get a result */ i = 0; @@ -3062,9 +2389,9 @@ ExecEvalRowCompare(RowCompareExprState *rstate, rstate->collations[i], NULL, NULL); locfcinfo.arg[0] = ExecEvalExpr(le, econtext, - &locfcinfo.argnull[0], NULL); + &locfcinfo.argnull[0]); locfcinfo.arg[1] = ExecEvalExpr(re, econtext, - &locfcinfo.argnull[1], NULL); + &locfcinfo.argnull[1]); if (rstate->funcs[i].fn_strict && (locfcinfo.argnull[0] || locfcinfo.argnull[1])) return (Datum) 0; /* force NULL result */ @@ -3108,20 +2435,17 @@ ExecEvalRowCompare(RowCompareExprState *rstate, */ static Datum ExecEvalCoalesce(CoalesceExprState *coalesceExpr, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) + bool *isNull) { ListCell *arg; - if (isDone) - *isDone = ExprSingleResult; - /* Simply loop through until something NOT NULL is found */ foreach(arg, coalesceExpr->args) { ExprState *e = (ExprState *) lfirst(arg); Datum value; - value = ExecEvalExpr(e, econtext, isNull, NULL); + value = ExecEvalExpr(e, econtext, isNull); if (!*isNull) return value; } @@ -3137,7 +2461,7 @@ ExecEvalCoalesce(CoalesceExprState *coalesceExpr, ExprContext *econtext, */ static Datum ExecEvalMinMax(MinMaxExprState *minmaxExpr, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) + bool *isNull) { Datum result = (Datum) 0; MinMaxExpr *minmax = (MinMaxExpr *) minmaxExpr->xprstate.expr; @@ -3146,8 +2470,6 @@ ExecEvalMinMax(MinMaxExprState *minmaxExpr, ExprContext *econtext, FunctionCallInfoData locfcinfo; ListCell *arg; - if (isDone) - *isDone = ExprSingleResult; *isNull = true; /* until we get a result */ InitFunctionCallInfoData(locfcinfo, &minmaxExpr->cfunc, 2, @@ -3162,7 +2484,7 @@ ExecEvalMinMax(MinMaxExprState *minmaxExpr, ExprContext *econtext, bool valueIsNull; int32 cmpresult; - value = ExecEvalExpr(e, econtext, &valueIsNull, NULL); + value = ExecEvalExpr(e, econtext, &valueIsNull); if (valueIsNull) continue; /* ignore NULL inputs */ @@ -3198,14 +2520,12 @@ ExecEvalMinMax(MinMaxExprState *minmaxExpr, ExprContext *econtext, static Datum ExecEvalSQLValueFunction(ExprState *svfExpr, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) + bool *isNull) { Datum result = (Datum) 0; SQLValueFunction *svf = (SQLValueFunction *) svfExpr->expr; FunctionCallInfoData fcinfo; - if (isDone) - *isDone = ExprSingleResult; *isNull = false; /* @@ -3266,7 +2586,7 @@ ExecEvalSQLValueFunction(ExprState *svfExpr, */ static Datum ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) + bool *isNull) { XmlExpr *xexpr = (XmlExpr *) xmlExpr->xprstate.expr; Datum value; @@ -3274,8 +2594,6 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, ListCell *arg; ListCell *narg; - if (isDone) - *isDone = ExprSingleResult; *isNull = true; /* until we get a result */ switch (xexpr->op) @@ -3288,7 +2606,7 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, { ExprState *e = (ExprState *) lfirst(arg); - value = ExecEvalExpr(e, econtext, &isnull, NULL); + value = ExecEvalExpr(e, econtext, &isnull); if (!isnull) values = lappend(values, DatumGetPointer(value)); } @@ -3313,7 +2631,7 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, ExprState *e = (ExprState *) lfirst(arg); char *argname = strVal(lfirst(narg)); - value = ExecEvalExpr(e, econtext, &isnull, NULL); + value = ExecEvalExpr(e, econtext, &isnull); if (!isnull) { appendStringInfo(&buf, "<%s>%s", @@ -3356,13 +2674,13 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, Assert(list_length(xmlExpr->args) == 2); e = (ExprState *) linitial(xmlExpr->args); - value = ExecEvalExpr(e, econtext, &isnull, NULL); + value = ExecEvalExpr(e, econtext, &isnull); if (isnull) return (Datum) 0; data = DatumGetTextP(value); e = (ExprState *) lsecond(xmlExpr->args); - value = ExecEvalExpr(e, econtext, &isnull, NULL); + value = ExecEvalExpr(e, econtext, &isnull); if (isnull) /* probably can't happen */ return (Datum) 0; preserve_whitespace = DatumGetBool(value); @@ -3386,7 +2704,7 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, if (xmlExpr->args) { e = (ExprState *) linitial(xmlExpr->args); - value = ExecEvalExpr(e, econtext, &isnull, NULL); + value = ExecEvalExpr(e, econtext, &isnull); if (isnull) arg = NULL; else @@ -3413,20 +2731,20 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, Assert(list_length(xmlExpr->args) == 3); e = (ExprState *) linitial(xmlExpr->args); - value = ExecEvalExpr(e, econtext, &isnull, NULL); + value = ExecEvalExpr(e, econtext, &isnull); if (isnull) return (Datum) 0; data = DatumGetXmlP(value); e = (ExprState *) lsecond(xmlExpr->args); - value = ExecEvalExpr(e, econtext, &isnull, NULL); + value = ExecEvalExpr(e, econtext, &isnull); if (isnull) version = NULL; else version = DatumGetTextP(value); e = (ExprState *) lthird(xmlExpr->args); - value = ExecEvalExpr(e, econtext, &isnull, NULL); + value = ExecEvalExpr(e, econtext, &isnull); standalone = DatumGetInt32(value); *isNull = false; @@ -3445,7 +2763,7 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, Assert(list_length(xmlExpr->args) == 1); e = (ExprState *) linitial(xmlExpr->args); - value = ExecEvalExpr(e, econtext, &isnull, NULL); + value = ExecEvalExpr(e, econtext, &isnull); if (isnull) return (Datum) 0; @@ -3463,7 +2781,7 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, Assert(list_length(xmlExpr->args) == 1); e = (ExprState *) linitial(xmlExpr->args); - value = ExecEvalExpr(e, econtext, &isnull, NULL); + value = ExecEvalExpr(e, econtext, &isnull); if (isnull) return (Datum) 0; else @@ -3490,14 +2808,10 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, static Datum ExecEvalNullIf(FuncExprState *nullIfExpr, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) + bool *isNull) { Datum result; FunctionCallInfo fcinfo; - ExprDoneCond argDone; - - if (isDone) - *isDone = ExprSingleResult; /* * Initialize function cache if first time through @@ -3507,7 +2821,7 @@ ExecEvalNullIf(FuncExprState *nullIfExpr, NullIfExpr *op = (NullIfExpr *) nullIfExpr->xprstate.expr; ExecInitFcache(op->opfuncid, op->inputcollid, nullIfExpr, - econtext->ecxt_per_query_memory, true); + econtext->ecxt_per_query_memory); Assert(!nullIfExpr->func.fn_retset); } @@ -3515,11 +2829,7 @@ ExecEvalNullIf(FuncExprState *nullIfExpr, * Evaluate arguments */ fcinfo = &nullIfExpr->fcinfo_data; - argDone = ExecEvalFuncArgs(fcinfo, nullIfExpr->args, econtext); - if (argDone != ExprSingleResult) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("NULLIF does not support set arguments"))); + ExecEvalFuncArgs(fcinfo, nullIfExpr->args, econtext); Assert(fcinfo->nargs == 2); /* if either argument is NULL they can't be equal */ @@ -3549,16 +2859,12 @@ ExecEvalNullIf(FuncExprState *nullIfExpr, static Datum ExecEvalNullTest(NullTestState *nstate, ExprContext *econtext, - bool *isNull, - ExprDoneCond *isDone) + bool *isNull) { NullTest *ntest = (NullTest *) nstate->xprstate.expr; Datum result; - result = ExecEvalExpr(nstate->arg, econtext, isNull, isDone); - - if (isDone && *isDone == ExprEndResult) - return result; /* nothing to check */ + result = ExecEvalExpr(nstate->arg, econtext, isNull); if (ntest->argisrow && !(*isNull)) { @@ -3658,16 +2964,12 @@ ExecEvalNullTest(NullTestState *nstate, static Datum ExecEvalBooleanTest(GenericExprState *bstate, ExprContext *econtext, - bool *isNull, - ExprDoneCond *isDone) + bool *isNull) { BooleanTest *btest = (BooleanTest *) bstate->xprstate.expr; Datum result; - result = ExecEvalExpr(bstate->arg, econtext, isNull, isDone); - - if (isDone && *isDone == ExprEndResult) - return result; /* nothing to check */ + result = ExecEvalExpr(bstate->arg, econtext, isNull); switch (btest->booltesttype) { @@ -3743,16 +3045,13 @@ ExecEvalBooleanTest(GenericExprState *bstate, */ static Datum ExecEvalCoerceToDomain(CoerceToDomainState *cstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) + bool *isNull) { CoerceToDomain *ctest = (CoerceToDomain *) cstate->xprstate.expr; Datum result; ListCell *l; - result = ExecEvalExpr(cstate->arg, econtext, isNull, isDone); - - if (isDone && *isDone == ExprEndResult) - return result; /* nothing to check */ + result = ExecEvalExpr(cstate->arg, econtext, isNull); /* Make sure we have up-to-date constraints */ UpdateDomainConstraintRef(cstate->constraint_ref); @@ -3790,8 +3089,8 @@ ExecEvalCoerceToDomain(CoerceToDomainState *cstate, ExprContext *econtext, econtext->domainValue_datum = result; econtext->domainValue_isNull = *isNull; - conResult = ExecEvalExpr(con->check_expr, - econtext, &conIsNull, NULL); + conResult = ExecEvalExpr(con->check_expr, econtext, + &conIsNull); if (!conIsNull && !DatumGetBool(conResult)) @@ -3826,10 +3125,8 @@ ExecEvalCoerceToDomain(CoerceToDomainState *cstate, ExprContext *econtext, static Datum ExecEvalCoerceToDomainValue(ExprState *exprstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) + bool *isNull) { - if (isDone) - *isDone = ExprSingleResult; *isNull = econtext->domainValue_isNull; return econtext->domainValue_datum; } @@ -3843,8 +3140,7 @@ ExecEvalCoerceToDomainValue(ExprState *exprstate, static Datum ExecEvalFieldSelect(FieldSelectState *fstate, ExprContext *econtext, - bool *isNull, - ExprDoneCond *isDone) + bool *isNull) { FieldSelect *fselect = (FieldSelect *) fstate->xprstate.expr; AttrNumber fieldnum = fselect->fieldnum; @@ -3857,7 +3153,7 @@ ExecEvalFieldSelect(FieldSelectState *fstate, Form_pg_attribute attr; HeapTupleData tmptup; - tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull, isDone); + tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull); /* this test covers the isDone exception too: */ if (*isNull) @@ -3922,8 +3218,7 @@ ExecEvalFieldSelect(FieldSelectState *fstate, static Datum ExecEvalFieldStore(FieldStoreState *fstate, ExprContext *econtext, - bool *isNull, - ExprDoneCond *isDone) + bool *isNull) { FieldStore *fstore = (FieldStore *) fstate->xprstate.expr; HeapTuple tuple; @@ -3936,10 +3231,7 @@ ExecEvalFieldStore(FieldStoreState *fstate, ListCell *l1, *l2; - tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull, isDone); - - if (isDone && *isDone == ExprEndResult) - return tupDatum; + tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull); /* Lookup tupdesc if first time through or after rescan */ tupDesc = get_cached_rowtype(fstore->resulttype, -1, @@ -3999,8 +3291,7 @@ ExecEvalFieldStore(FieldStoreState *fstate, values[fieldnum - 1] = ExecEvalExpr(newval, econtext, - &isnull[fieldnum - 1], - NULL); + &isnull[fieldnum - 1]); } econtext->caseValue_datum = save_datum; @@ -4023,9 +3314,9 @@ ExecEvalFieldStore(FieldStoreState *fstate, static Datum ExecEvalRelabelType(GenericExprState *exprstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) + bool *isNull) { - return ExecEvalExpr(exprstate->arg, econtext, isNull, isDone); + return ExecEvalExpr(exprstate->arg, econtext, isNull); } /* ---------------------------------------------------------------- @@ -4037,16 +3328,13 @@ ExecEvalRelabelType(GenericExprState *exprstate, static Datum ExecEvalCoerceViaIO(CoerceViaIOState *iostate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) + bool *isNull) { Datum result; Datum inputval; char *string; - inputval = ExecEvalExpr(iostate->arg, econtext, isNull, isDone); - - if (isDone && *isDone == ExprEndResult) - return inputval; /* nothing to do */ + inputval = ExecEvalExpr(iostate->arg, econtext, isNull); if (*isNull) string = NULL; /* output functions are not called on nulls */ @@ -4071,16 +3359,14 @@ ExecEvalCoerceViaIO(CoerceViaIOState *iostate, static Datum ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) + bool *isNull) { ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) astate->xprstate.expr; Datum result; FunctionCallInfoData locfcinfo; - result = ExecEvalExpr(astate->arg, econtext, isNull, isDone); + result = ExecEvalExpr(astate->arg, econtext, isNull); - if (isDone && *isDone == ExprEndResult) - return result; /* nothing to do */ if (*isNull) return result; /* nothing to do */ @@ -4148,7 +3434,7 @@ ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate, */ static Datum ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) + bool *isNull) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), @@ -4165,14 +3451,13 @@ ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext, Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econtext, - bool *isNull, - ExprDoneCond *isDone) + bool *isNull) { Datum retDatum; MemoryContext oldContext; oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - retDatum = ExecEvalExpr(expression, econtext, isNull, isDone); + retDatum = ExecEvalExpr(expression, econtext, isNull); MemoryContextSwitchTo(oldContext); return retDatum; } @@ -5032,7 +4317,7 @@ ExecQual(List *qual, ExprContext *econtext, bool resultForNull) Datum expr_value; bool isNull; - expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL); + expr_value = ExecEvalExpr(clause, econtext, &isNull); if (isNull) { @@ -5090,17 +4375,9 @@ ExecCleanTargetListLength(List *targetlist) /* * ExecTargetList * Evaluates a targetlist with respect to the given - * expression context. Returns TRUE if we were able to create - * a result, FALSE if we have exhausted a set-valued expression. + * expression context. * * Results are stored into the passed values and isnull arrays. - * The caller must provide an itemIsDone array that persists across calls. - * - * As with ExecEvalExpr, the caller should pass isDone = NULL if not - * prepared to deal with sets of result tuples. Otherwise, a return - * of *isDone = ExprMultipleResult signifies a set element, and a return - * of *isDone = ExprEndResult signifies end of the set of tuple. - * We assume that *isDone has been initialized to ExprSingleResult by caller. * * Since fields of the result tuple might be multiply referenced in higher * plan nodes, we have to force any read/write expanded values to read-only @@ -5109,19 +4386,16 @@ ExecCleanTargetListLength(List *targetlist) * actually-multiply-referenced Vars and insert an expression node that * would do that only where really required. */ -static bool +static void ExecTargetList(List *targetlist, TupleDesc tupdesc, ExprContext *econtext, Datum *values, - bool *isnull, - ExprDoneCond *itemIsDone, - ExprDoneCond *isDone) + bool *isnull) { Form_pg_attribute *att = tupdesc->attrs; MemoryContext oldContext; ListCell *tl; - bool haveDoneSets; /* * Run in short-lived per-tuple context while computing expressions. @@ -5131,8 +4405,6 @@ ExecTargetList(List *targetlist, /* * evaluate all the expressions in the target list */ - haveDoneSets = false; /* any exhausted set exprs in tlist? */ - foreach(tl, targetlist) { GenericExprState *gstate = (GenericExprState *) lfirst(tl); @@ -5141,117 +4413,15 @@ ExecTargetList(List *targetlist, values[resind] = ExecEvalExpr(gstate->arg, econtext, - &isnull[resind], - &itemIsDone[resind]); + &isnull[resind]); values[resind] = MakeExpandedObjectReadOnly(values[resind], isnull[resind], att[resind]->attlen); - - if (itemIsDone[resind] != ExprSingleResult) - { - /* We have a set-valued expression in the tlist */ - if (isDone == NULL) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (itemIsDone[resind] == ExprMultipleResult) - { - /* we have undone sets in the tlist, set flag */ - *isDone = ExprMultipleResult; - } - else - { - /* we have done sets in the tlist, set flag for that */ - haveDoneSets = true; - } - } - } - - if (haveDoneSets) - { - /* - * note: can't get here unless we verified isDone != NULL - */ - if (*isDone == ExprSingleResult) - { - /* - * all sets are done, so report that tlist expansion is complete. - */ - *isDone = ExprEndResult; - MemoryContextSwitchTo(oldContext); - return false; - } - else - { - /* - * We have some done and some undone sets. Restart the done ones - * so that we can deliver a tuple (if possible). - */ - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - if (itemIsDone[resind] == ExprEndResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] == ExprEndResult) - { - /* - * Oh dear, this item is returning an empty set. Guess - * we can't make a tuple after all. - */ - *isDone = ExprEndResult; - break; - } - } - } - - /* - * If we cannot make a tuple because some sets are empty, we still - * have to cycle the nonempty sets to completion, else resources - * will not be released from subplans etc. - * - * XXX is that still necessary? - */ - if (*isDone == ExprEndResult) - { - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - while (itemIsDone[resind] == ExprMultipleResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - /* no need for MakeExpandedObjectReadOnly */ - } - } - - MemoryContextSwitchTo(oldContext); - return false; - } - } } /* Report success */ MemoryContextSwitchTo(oldContext); - - return true; } /* @@ -5268,7 +4438,7 @@ ExecTargetList(List *targetlist, * result slot. */ TupleTableSlot * -ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) +ExecProject(ProjectionInfo *projInfo) { TupleTableSlot *slot; ExprContext *econtext; @@ -5285,10 +4455,6 @@ ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) slot = projInfo->pi_slot; econtext = projInfo->pi_exprContext; - /* Assume single result row until proven otherwise */ - if (isDone) - *isDone = ExprSingleResult; - /* * Clear any former contents of the result slot. This makes it safe for * us to use the slot's Datum/isnull arrays as workspace. (Also, we can @@ -5356,21 +4522,15 @@ ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) } /* - * If there are any generic expressions, evaluate them. It's possible - * that there are set-returning functions in such expressions; if so and - * we have reached the end of the set, we return the result slot, which we - * already marked empty. + * If there are any generic expressions, evaluate them. */ if (projInfo->pi_targetlist) { - if (!ExecTargetList(projInfo->pi_targetlist, - slot->tts_tupleDescriptor, - econtext, - slot->tts_values, - slot->tts_isnull, - projInfo->pi_itemIsDone, - isDone)) - return slot; /* no more result rows, return empty slot */ + ExecTargetList(projInfo->pi_targetlist, + slot->tts_tupleDescriptor, + econtext, + slot->tts_values, + slot->tts_isnull); } /* diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index fb0013d..eb224b4 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -125,8 +125,6 @@ ExecScan(ScanState *node, ExprContext *econtext; List *qual; ProjectionInfo *projInfo; - ExprDoneCond isDone; - TupleTableSlot *resultSlot; /* * Fetch data from node @@ -146,21 +144,6 @@ ExecScan(ScanState *node, } /* - * 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->ps.ps_TupFromTlist) - { - Assert(projInfo); /* can't get here if not projecting */ - resultSlot = ExecProject(projInfo, &isDone); - if (isDone == ExprMultipleResult) - return resultSlot; - /* Done with that source tuple... */ - node->ps.ps_TupFromTlist = false; - } - - /* * 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. @@ -214,15 +197,9 @@ ExecScan(ScanState *node, { /* * Form a projection tuple, store it in the result tuple slot - * and return it --- unless we find we can project no tuples - * from this scan tuple, in which case continue scan. + * and return it. */ - resultSlot = ExecProject(projInfo, &isDone); - if (isDone != ExprEndResult) - { - node->ps.ps_TupFromTlist = (isDone == ExprMultipleResult); - return resultSlot; - } + return ExecProject(projInfo); } else { @@ -352,9 +329,6 @@ ExecScanReScan(ScanState *node) { EState *estate = node->ps.state; - /* Stop projecting any tuples from SRFs in the targetlist */ - node->ps.ps_TupFromTlist = false; - /* Rescan EvalPlanQual tuple if we're inside an EvalPlanQual recheck */ if (estate->es_epqScanDone != NULL) { diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index e937cf8..ded073a 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -592,12 +592,6 @@ ExecBuildProjectionInfo(List *targetList, projInfo->pi_numSimpleVars = numSimpleVars; projInfo->pi_directMap = directMap; - if (exprlist == NIL) - projInfo->pi_itemIsDone = NULL; /* not needed */ - else - projInfo->pi_itemIsDone = (ExprDoneCond *) - palloc(len * sizeof(ExprDoneCond)); - return projInfo; } diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index ce2fc28..f2ba170 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -859,13 +859,13 @@ advance_aggregates(AggState *aggstate, AggStatePerGroup pergroup) bool isnull; res = ExecEvalExprSwitchContext(filter, aggstate->tmpcontext, - &isnull, NULL); + &isnull); if (isnull || !DatumGetBool(res)) continue; } /* Evaluate the current input expressions for this aggregate */ - slot = ExecProject(pertrans->evalproj, NULL); + slot = ExecProject(pertrans->evalproj); if (pertrans->numSortCols > 0) { @@ -951,7 +951,7 @@ combine_aggregates(AggState *aggstate, AggStatePerGroup pergroup) FunctionCallInfo fcinfo = &pertrans->transfn_fcinfo; /* Evaluate the current input expressions for this aggregate */ - slot = ExecProject(pertrans->evalproj, NULL); + slot = ExecProject(pertrans->evalproj); Assert(slot->tts_nvalid >= 1); /* @@ -1325,8 +1325,7 @@ finalize_aggregate(AggState *aggstate, fcinfo.arg[i] = ExecEvalExpr(expr, aggstate->ss.ps.ps_ExprContext, - &fcinfo.argnull[i], - NULL); + &fcinfo.argnull[i]); anynull |= fcinfo.argnull[i]; i++; } @@ -1566,7 +1565,7 @@ finalize_aggregates(AggState *aggstate, /* * Project the result of a group (whose aggs have already been calculated by * finalize_aggregates). Returns the result slot, or NULL if no row is - * projected (suppressed by qual or by an empty SRF). + * projected (suppressed by qual). */ static TupleTableSlot * project_aggregates(AggState *aggstate) @@ -1579,20 +1578,10 @@ project_aggregates(AggState *aggstate) if (ExecQual(aggstate->ss.ps.qual, econtext, false)) { /* - * Form and return or store a projection tuple using the aggregate - * results and the representative input tuple. + * Form and return projection tuple using the aggregate results and + * the representative input tuple. */ - ExprDoneCond isDone; - TupleTableSlot *result; - - result = ExecProject(aggstate->ss.ps.ps_ProjInfo, &isDone); - - if (isDone != ExprEndResult) - { - aggstate->ss.ps.ps_TupFromTlist = - (isDone == ExprMultipleResult); - return result; - } + return ExecProject(aggstate->ss.ps.ps_ProjInfo); } else InstrCountFiltered1(aggstate, 1); @@ -1802,27 +1791,6 @@ ExecAgg(AggState *node) { TupleTableSlot *result; - /* - * Check to see if we're still projecting out tuples from a previous agg - * tuple (because there is a function-returning-set in the projection - * expressions). If so, try to project another one. - */ - if (node->ss.ps.ps_TupFromTlist) - { - ExprDoneCond isDone; - - result = ExecProject(node->ss.ps.ps_ProjInfo, &isDone); - if (isDone == ExprMultipleResult) - return result; - /* Done with that source tuple... */ - node->ss.ps.ps_TupFromTlist = false; - } - - /* - * (We must do the ps_TupFromTlist check first, because in some cases - * agg_done gets set before we emit the final aggregate tuple, and we have - * to finish running SRFs for it.) - */ if (!node->agg_done) { /* Dispatch based on strategy */ @@ -2443,8 +2411,6 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) ExecAssignResultTypeFromTL(&aggstate->ss.ps); ExecAssignProjectionInfo(&aggstate->ss.ps, NULL); - aggstate->ss.ps.ps_TupFromTlist = false; - /* * get the count of aggregates in targetlist and quals */ @@ -3411,8 +3377,6 @@ ExecReScanAgg(AggState *node) node->agg_done = false; - node->ss.ps.ps_TupFromTlist = false; - if (aggnode->aggstrategy == AGG_HASHED) { /* diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c index 449aacb..16381f6 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -575,8 +575,6 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags) */ ExecAssignExprContext(estate, &scanstate->ss.ps); - scanstate->ss.ps.ps_TupFromTlist = false; - /* * initialize child expressions */ diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c index 3c2f684..1acb166 100644 --- a/src/backend/executor/nodeCtescan.c +++ b/src/backend/executor/nodeCtescan.c @@ -265,8 +265,6 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags) ExecAssignResultTypeFromTL(&scanstate->ss.ps); ExecAssignScanProjectionInfo(&scanstate->ss); - scanstate->ss.ps.ps_TupFromTlist = false; - return scanstate; } diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c index 322abca..b465252 100644 --- a/src/backend/executor/nodeCustom.c +++ b/src/backend/executor/nodeCustom.c @@ -48,8 +48,6 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) /* create expression context for node */ ExecAssignExprContext(estate, &css->ss.ps); - css->ss.ps.ps_TupFromTlist = false; - /* initialize child expressions */ css->ss.ps.targetlist = (List *) ExecInitExpr((Expr *) cscan->scan.plan.targetlist, diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index d886aaf..3762843 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -152,8 +152,6 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) */ ExecAssignExprContext(estate, &scanstate->ss.ps); - scanstate->ss.ps.ps_TupFromTlist = false; - /* * initialize child expressions */ diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index 1da4fde..725a5fe 100644 --- a/src/backend/executor/nodeFunctionscan.c +++ b/src/backend/executor/nodeFunctionscan.c @@ -282,8 +282,6 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) */ ExecAssignExprContext(estate, &scanstate->ss.ps); - scanstate->ss.ps.ps_TupFromTlist = false; - /* * tuple table initialization */ @@ -647,7 +645,6 @@ ExecBeginFunctionResult(FunctionScanState *node, IsA(funcexpr->expr, FuncExpr)) { FuncExprState *fcache = (FuncExprState *) funcexpr; - ExprDoneCond argDone; /* * This path is similar to ExecMakeFunctionResult. @@ -662,7 +659,7 @@ ExecBeginFunctionResult(FunctionScanState *node, FuncExpr *func = (FuncExpr *) fcache->xprstate.expr; ExecInitFcache(func->funcid, func->inputcollid, fcache, - econtext->ecxt_per_query_memory, false); + econtext->ecxt_per_query_memory); } returnsSet = fcache->func.fn_retset; InitFunctionCallInfoData(perfunc->fcinfo, &(fcache->func), @@ -681,15 +678,9 @@ ExecBeginFunctionResult(FunctionScanState *node, * and can be reset each time the node is re-scanned. */ oldcontext = MemoryContextSwitchTo(node->argcontext); - argDone = ExecEvalFuncArgs(&perfunc->fcinfo, fcache->args, econtext); + ExecEvalFuncArgs(&perfunc->fcinfo, fcache->args, econtext); MemoryContextSwitchTo(oldcontext); - /* We don't allow sets in the arguments of the table function */ - if (argDone != ExprSingleResult) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - /* * If function is strict, and there are any NULL arguments, skip * calling the function and act like it returned NULL (or an empty @@ -742,8 +733,7 @@ ExecBeginFunctionResult(FunctionScanState *node, else { perfunc->rsinfo.isDone = ExprSingleResult; - result = ExecEvalExpr(funcexpr, econtext, - &perfunc->fcinfo.isnull, NULL); + result = ExecEvalExpr(funcexpr, econtext, &perfunc->fcinfo.isnull); /* done after this, will use SFRM_ValuePerCall branch below */ } diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c index 438d1b2..51754c8 100644 --- a/src/backend/executor/nodeGather.c +++ b/src/backend/executor/nodeGather.c @@ -99,8 +99,6 @@ ExecInitGather(Gather *node, EState *estate, int eflags) outerNode = outerPlan(node); outerPlanState(gatherstate) = ExecInitNode(outerNode, estate, eflags); - gatherstate->ps.ps_TupFromTlist = false; - /* * Initialize result tuple type and projection info. */ @@ -131,8 +129,6 @@ ExecGather(GatherState *node) TupleTableSlot *fslot = node->funnel_slot; int i; TupleTableSlot *slot; - TupleTableSlot *resultSlot; - ExprDoneCond isDone; ExprContext *econtext; /* @@ -198,20 +194,6 @@ ExecGather(GatherState *node) } /* - * 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->ps.ps_TupFromTlist) - { - resultSlot = ExecProject(node->ps.ps_ProjInfo, &isDone); - if (isDone == ExprMultipleResult) - return resultSlot; - /* Done with that source tuple... */ - node->ps.ps_TupFromTlist = false; - } - - /* * Reset per-tuple memory context to free any expression evaluation * storage allocated in the previous tuple cycle. Note we can't do this * until we're done projecting. This will also clear any previous tuple @@ -239,13 +221,8 @@ ExecGather(GatherState *node) * back around for another tuple */ econtext->ecxt_outertuple = slot; - resultSlot = ExecProject(node->ps.ps_ProjInfo, &isDone); - if (isDone != ExprEndResult) - { - node->ps.ps_TupFromTlist = (isDone == ExprMultipleResult); - return resultSlot; - } + return ExecProject(node->ps.ps_ProjInfo); } return slot; diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index dcf5175..2f55c70 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -50,23 +50,6 @@ ExecGroup(GroupState *node) grpColIdx = ((Group *) node->ss.ps.plan)->grpColIdx; /* - * Check to see if we're still projecting out tuples from a previous group - * tuple (because there is a function-returning-set in the projection - * expressions). If so, try to project another one. - */ - if (node->ss.ps.ps_TupFromTlist) - { - TupleTableSlot *result; - ExprDoneCond isDone; - - result = ExecProject(node->ss.ps.ps_ProjInfo, &isDone); - if (isDone == ExprMultipleResult) - return result; - /* Done with that source tuple... */ - node->ss.ps.ps_TupFromTlist = false; - } - - /* * The ScanTupleSlot holds the (copied) first tuple of each group. */ firsttupleslot = node->ss.ss_ScanTupleSlot; @@ -107,16 +90,7 @@ ExecGroup(GroupState *node) /* * Form and return a projection tuple using the first input tuple. */ - TupleTableSlot *result; - ExprDoneCond isDone; - - result = ExecProject(node->ss.ps.ps_ProjInfo, &isDone); - - if (isDone != ExprEndResult) - { - node->ss.ps.ps_TupFromTlist = (isDone == ExprMultipleResult); - return result; - } + return ExecProject(node->ss.ps.ps_ProjInfo); } else InstrCountFiltered1(node, 1); @@ -170,16 +144,7 @@ ExecGroup(GroupState *node) /* * Form and return a projection tuple using the first input tuple. */ - TupleTableSlot *result; - ExprDoneCond isDone; - - result = ExecProject(node->ss.ps.ps_ProjInfo, &isDone); - - if (isDone != ExprEndResult) - { - node->ss.ps.ps_TupFromTlist = (isDone == ExprMultipleResult); - return result; - } + return ExecProject(node->ss.ps.ps_ProjInfo); } else InstrCountFiltered1(node, 1); @@ -246,8 +211,6 @@ ExecInitGroup(Group *node, EState *estate, int eflags) ExecAssignResultTypeFromTL(&grpstate->ss.ps); ExecAssignProjectionInfo(&grpstate->ss.ps, NULL); - grpstate->ss.ps.ps_TupFromTlist = false; - /* * Precompute fmgr lookup data for inner loop */ @@ -283,7 +246,6 @@ ExecReScanGroup(GroupState *node) PlanState *outerPlan = outerPlanState(node); node->grp_done = FALSE; - node->ss.ps.ps_TupFromTlist = false; /* must clear first tuple */ ExecClearTuple(node->ss.ss_ScanTupleSlot); diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 9ed09a7..e008a51 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -963,7 +963,7 @@ ExecHashGetHashValue(HashJoinTable hashtable, /* * Get the join attribute value of the tuple */ - keyval = ExecEvalExpr(keyexpr, econtext, &isNull, NULL); + keyval = ExecEvalExpr(keyexpr, econtext, &isNull); /* * If the attribute is NULL, and the join operator is strict, then diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index 369e666..45c7be2 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -66,7 +66,6 @@ ExecHashJoin(HashJoinState *node) List *joinqual; List *otherqual; ExprContext *econtext; - ExprDoneCond isDone; HashJoinTable hashtable; TupleTableSlot *outerTupleSlot; uint32 hashvalue; @@ -83,22 +82,6 @@ ExecHashJoin(HashJoinState *node) econtext = node->js.ps.ps_ExprContext; /* - * Check to see if we're still projecting out tuples from a previous join - * tuple (because there is a function-returning-set in the projection - * expressions). If so, try to project another one. - */ - if (node->js.ps.ps_TupFromTlist) - { - TupleTableSlot *result; - - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); - if (isDone == ExprMultipleResult) - return result; - /* Done with that source tuple... */ - node->js.ps.ps_TupFromTlist = false; - } - - /* * 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 join tuple. @@ -315,16 +298,7 @@ ExecHashJoin(HashJoinState *node) if (otherqual == NIL || ExecQual(otherqual, econtext, false)) { - TupleTableSlot *result; - - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); - - if (isDone != ExprEndResult) - { - node->js.ps.ps_TupFromTlist = - (isDone == ExprMultipleResult); - return result; - } + return ExecProject(node->js.ps.ps_ProjInfo); } else InstrCountFiltered2(node, 1); @@ -354,16 +328,7 @@ ExecHashJoin(HashJoinState *node) if (otherqual == NIL || ExecQual(otherqual, econtext, false)) { - TupleTableSlot *result; - - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); - - if (isDone != ExprEndResult) - { - node->js.ps.ps_TupFromTlist = - (isDone == ExprMultipleResult); - return result; - } + return ExecProject(node->js.ps.ps_ProjInfo); } else InstrCountFiltered2(node, 1); @@ -393,16 +358,7 @@ ExecHashJoin(HashJoinState *node) if (otherqual == NIL || ExecQual(otherqual, econtext, false)) { - TupleTableSlot *result; - - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); - - if (isDone != ExprEndResult) - { - node->js.ps.ps_TupFromTlist = - (isDone == ExprMultipleResult); - return result; - } + return ExecProject(node->js.ps.ps_ProjInfo); } else InstrCountFiltered2(node, 1); @@ -586,7 +542,6 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) /* child Hash node needs to evaluate inner hash keys, too */ ((HashState *) innerPlanState(hjstate))->hashkeys = rclauses; - hjstate->js.ps.ps_TupFromTlist = false; hjstate->hj_JoinState = HJ_BUILD_HASHTABLE; hjstate->hj_MatchedOuter = false; hjstate->hj_OuterNotEmpty = false; @@ -1000,7 +955,6 @@ ExecReScanHashJoin(HashJoinState *node) node->hj_CurSkewBucketNo = INVALID_SKEW_BUCKET_NO; node->hj_CurTuple = NULL; - node->js.ps.ps_TupFromTlist = false; node->hj_MatchedOuter = false; node->hj_FirstOuterTupleSlot = NULL; diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c index 4f6f91c..edd45661 100644 --- a/src/backend/executor/nodeIndexonlyscan.c +++ b/src/backend/executor/nodeIndexonlyscan.c @@ -412,8 +412,6 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags) */ ExecAssignExprContext(estate, &indexstate->ss.ps); - indexstate->ss.ps.ps_TupFromTlist = false; - /* * initialize child expressions * diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 3143bd9..d1b1c23 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -336,8 +336,7 @@ EvalOrderByExpressions(IndexScanState *node, ExprContext *econtext) node->iss_OrderByValues[i] = ExecEvalExpr(orderby, econtext, - &node->iss_OrderByNulls[i], - NULL); + &node->iss_OrderByNulls[i]); i++; } @@ -590,8 +589,7 @@ ExecIndexEvalRuntimeKeys(ExprContext *econtext, */ scanvalue = ExecEvalExpr(key_expr, econtext, - &isNull, - NULL); + &isNull); if (isNull) { scan_key->sk_argument = scanvalue; @@ -648,8 +646,7 @@ ExecIndexEvalArrayKeys(ExprContext *econtext, */ arraydatum = ExecEvalExpr(array_expr, econtext, - &isNull, - NULL); + &isNull); if (isNull) { result = false; @@ -837,8 +834,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) */ ExecAssignExprContext(estate, &indexstate->ss.ps); - indexstate->ss.ps.ps_TupFromTlist = false; - /* * initialize child expressions * diff --git a/src/backend/executor/nodeLimit.c b/src/backend/executor/nodeLimit.c index faf32e1..0ef9ea5 100644 --- a/src/backend/executor/nodeLimit.c +++ b/src/backend/executor/nodeLimit.c @@ -239,8 +239,7 @@ recompute_limits(LimitState *node) { val = ExecEvalExprSwitchContext(node->limitOffset, econtext, - &isNull, - NULL); + &isNull); /* Interpret NULL offset as no offset */ if (isNull) node->offset = 0; @@ -263,8 +262,7 @@ recompute_limits(LimitState *node) { val = ExecEvalExprSwitchContext(node->limitCount, econtext, - &isNull, - NULL); + &isNull); /* Interpret NULL count as no count (LIMIT ALL) */ if (isNull) { @@ -346,18 +344,11 @@ pass_down_bound(LimitState *node, PlanState *child_node) else if (IsA(child_node, ResultState)) { /* - * An extra consideration here is that if the Result is projecting a - * targetlist that contains any SRFs, we can't assume that every input - * tuple generates an output tuple, so a Sort underneath might need to - * return more than N tuples to satisfy LIMIT N. So we cannot use - * bounded sort. - * * If Result supported qual checking, we'd have to punt on seeing a - * qual, too. Note that having a resconstantqual is not a - * showstopper: if that fails we're not getting any rows at all. + * qual. Note that having a resconstantqual is not a showstopper: if + * that fails we're not getting any rows at all. */ - if (outerPlanState(child_node) && - !expression_returns_set((Node *) child_node->plan->targetlist)) + if (outerPlanState(child_node)) pass_down_bound(node, outerPlanState(child_node)); } } diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 6db09b8..340a2a9 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -313,7 +313,7 @@ MJEvalOuterValues(MergeJoinState *mergestate) MergeJoinClause clause = &mergestate->mj_Clauses[i]; clause->ldatum = ExecEvalExpr(clause->lexpr, econtext, - &clause->lisnull, NULL); + &clause->lisnull); if (clause->lisnull) { /* match is impossible; can we end the join early? */ @@ -360,7 +360,7 @@ MJEvalInnerValues(MergeJoinState *mergestate, TupleTableSlot *innerslot) MergeJoinClause clause = &mergestate->mj_Clauses[i]; clause->rdatum = ExecEvalExpr(clause->rexpr, econtext, - &clause->risnull, NULL); + &clause->risnull); if (clause->risnull) { /* match is impossible; can we end the join early? */ @@ -465,19 +465,10 @@ MJFillOuter(MergeJoinState *node) * qualification succeeded. now form the desired projection tuple and * return the slot containing it. */ - TupleTableSlot *result; - ExprDoneCond isDone; MJ_printf("ExecMergeJoin: returning outer fill tuple\n"); - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); - - if (isDone != ExprEndResult) - { - node->js.ps.ps_TupFromTlist = - (isDone == ExprMultipleResult); - return result; - } + return ExecProject(node->js.ps.ps_ProjInfo); } else InstrCountFiltered2(node, 1); @@ -506,19 +497,9 @@ MJFillInner(MergeJoinState *node) * qualification succeeded. now form the desired projection tuple and * return the slot containing it. */ - TupleTableSlot *result; - ExprDoneCond isDone; - MJ_printf("ExecMergeJoin: returning inner fill tuple\n"); - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); - - if (isDone != ExprEndResult) - { - node->js.ps.ps_TupFromTlist = - (isDone == ExprMultipleResult); - return result; - } + return ExecProject(node->js.ps.ps_ProjInfo); } else InstrCountFiltered2(node, 1); @@ -642,23 +623,6 @@ ExecMergeJoin(MergeJoinState *node) doFillInner = node->mj_FillInner; /* - * Check to see if we're still projecting out tuples from a previous join - * tuple (because there is a function-returning-set in the projection - * expressions). If so, try to project another one. - */ - if (node->js.ps.ps_TupFromTlist) - { - TupleTableSlot *result; - ExprDoneCond isDone; - - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); - if (isDone == ExprMultipleResult) - return result; - /* Done with that source tuple... */ - node->js.ps.ps_TupFromTlist = false; - } - - /* * 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 join tuple. @@ -856,20 +820,9 @@ ExecMergeJoin(MergeJoinState *node) * qualification succeeded. now form the desired * projection tuple and return the slot containing it. */ - TupleTableSlot *result; - ExprDoneCond isDone; - MJ_printf("ExecMergeJoin: returning tuple\n"); - result = ExecProject(node->js.ps.ps_ProjInfo, - &isDone); - - if (isDone != ExprEndResult) - { - node->js.ps.ps_TupFromTlist = - (isDone == ExprMultipleResult); - return result; - } + return ExecProject(node->js.ps.ps_ProjInfo); } else InstrCountFiltered2(node, 1); @@ -1629,7 +1582,6 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) * initialize join state */ mergestate->mj_JoinState = EXEC_MJ_INITIALIZE_OUTER; - mergestate->js.ps.ps_TupFromTlist = false; mergestate->mj_MatchedOuter = false; mergestate->mj_MatchedInner = false; mergestate->mj_OuterTupleSlot = NULL; @@ -1684,7 +1636,6 @@ ExecReScanMergeJoin(MergeJoinState *node) ExecClearTuple(node->mj_MarkedTupleSlot); node->mj_JoinState = EXEC_MJ_INITIALIZE_OUTER; - node->js.ps.ps_TupFromTlist = false; node->mj_MatchedOuter = false; node->mj_MatchedInner = false; node->mj_OuterTupleSlot = NULL; diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index af7b26c..0e6187b 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -175,7 +175,7 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo, econtext->ecxt_outertuple = planSlot; /* Compute the RETURNING expressions */ - return ExecProject(projectReturning, NULL); + return ExecProject(projectReturning); } /* @@ -1216,7 +1216,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, } /* Project the new tuple version */ - ExecProject(resultRelInfo->ri_onConflictSetProj, NULL); + ExecProject(resultRelInfo->ri_onConflictSetProj); /* * Note that it is possible that the target tuple has been modified in diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index 555fa09..5d30e75 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -82,23 +82,6 @@ ExecNestLoop(NestLoopState *node) econtext = node->js.ps.ps_ExprContext; /* - * Check to see if we're still projecting out tuples from a previous join - * tuple (because there is a function-returning-set in the projection - * expressions). If so, try to project another one. - */ - if (node->js.ps.ps_TupFromTlist) - { - TupleTableSlot *result; - ExprDoneCond isDone; - - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); - if (isDone == ExprMultipleResult) - return result; - /* Done with that source tuple... */ - node->js.ps.ps_TupFromTlist = false; - } - - /* * 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 join tuple. @@ -201,19 +184,10 @@ ExecNestLoop(NestLoopState *node) * the slot containing the result tuple using * ExecProject(). */ - TupleTableSlot *result; - ExprDoneCond isDone; ENL1_printf("qualification succeeded, projecting tuple"); - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); - - if (isDone != ExprEndResult) - { - node->js.ps.ps_TupFromTlist = - (isDone == ExprMultipleResult); - return result; - } + return ExecProject(node->js.ps.ps_ProjInfo); } else InstrCountFiltered2(node, 1); @@ -259,19 +233,10 @@ ExecNestLoop(NestLoopState *node) * qualification was satisfied so we project and return the * slot containing the result tuple using ExecProject(). */ - TupleTableSlot *result; - ExprDoneCond isDone; ENL1_printf("qualification succeeded, projecting tuple"); - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); - - if (isDone != ExprEndResult) - { - node->js.ps.ps_TupFromTlist = - (isDone == ExprMultipleResult); - return result; - } + return ExecProject(node->js.ps.ps_ProjInfo); } else InstrCountFiltered2(node, 1); @@ -377,7 +342,6 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags) /* * finally, wipe the current outer tuple clean. */ - nlstate->js.ps.ps_TupFromTlist = false; nlstate->nl_NeedNewOuter = true; nlstate->nl_MatchedOuter = false; @@ -441,7 +405,6 @@ ExecReScanNestLoop(NestLoopState *node) * outer Vars are used as run-time keys... */ - node->js.ps.ps_TupFromTlist = false; node->nl_NeedNewOuter = true; node->nl_MatchedOuter = false; } diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index 4007b76..3901351 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -67,10 +67,8 @@ TupleTableSlot * ExecResult(ResultState *node) { TupleTableSlot *outerTupleSlot; - TupleTableSlot *resultSlot; PlanState *outerPlan; ExprContext *econtext; - ExprDoneCond isDone; econtext = node->ps.ps_ExprContext; @@ -92,20 +90,6 @@ ExecResult(ResultState *node) } /* - * 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->ps.ps_TupFromTlist) - { - resultSlot = ExecProject(node->ps.ps_ProjInfo, &isDone); - if (isDone == ExprMultipleResult) - return resultSlot; - /* Done with that source tuple... */ - node->ps.ps_TupFromTlist = false; - } - - /* * 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. @@ -147,18 +131,8 @@ ExecResult(ResultState *node) node->rs_done = true; } - /* - * form the result tuple using ExecProject(), and return it --- unless - * the projection produces an empty set, in which case we must loop - * back to see if there are more outerPlan tuples. - */ - resultSlot = ExecProject(node->ps.ps_ProjInfo, &isDone); - - if (isDone != ExprEndResult) - { - node->ps.ps_TupFromTlist = (isDone == ExprMultipleResult); - return resultSlot; - } + /* form the result tuple using ExecProject(), and return it */ + return ExecProject(node->ps.ps_ProjInfo); } return NULL; @@ -228,8 +202,6 @@ ExecInitResult(Result *node, EState *estate, int eflags) */ ExecAssignExprContext(estate, &resstate->ps); - resstate->ps.ps_TupFromTlist = false; - /* * tuple table initialization */ @@ -295,7 +267,6 @@ void ExecReScanResult(ResultState *node) { node->rs_done = false; - node->ps.ps_TupFromTlist = false; node->rs_checkqual = (node->resconstantqual == NULL) ? false : true; /* diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c index 9ce7c02..64396e1 100644 --- a/src/backend/executor/nodeSamplescan.c +++ b/src/backend/executor/nodeSamplescan.c @@ -188,8 +188,6 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags) */ InitScanRelation(scanstate, estate, eflags); - scanstate->ss.ps.ps_TupFromTlist = false; - /* * Initialize result tuple type and projection info. */ @@ -299,8 +297,7 @@ tablesample_init(SampleScanState *scanstate) params[i] = ExecEvalExprSwitchContext(argstate, econtext, - &isnull, - NULL); + &isnull); if (isnull) ereport(ERROR, (errcode(ERRCODE_INVALID_TABLESAMPLE_ARGUMENT), @@ -312,8 +309,7 @@ tablesample_init(SampleScanState *scanstate) { datum = ExecEvalExprSwitchContext(scanstate->repeatable, econtext, - &isnull, - NULL); + &isnull); if (isnull) ereport(ERROR, (errcode(ERRCODE_INVALID_TABLESAMPLE_REPEAT), diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index 00bf3a5..477dc42 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -206,8 +206,6 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags) */ InitScanRelation(scanstate, estate, eflags); - scanstate->ss.ps.ps_TupFromTlist = false; - /* * Initialize result tuple type and projection info. */ diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index e503494..5800ca8 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -41,12 +41,10 @@ static Datum ExecSubPlan(SubPlanState *node, ExprContext *econtext, - bool *isNull, - ExprDoneCond *isDone); + bool *isNull); static Datum ExecAlternativeSubPlan(AlternativeSubPlanState *node, ExprContext *econtext, - bool *isNull, - ExprDoneCond *isDone); + bool *isNull); static Datum ExecHashSubPlan(SubPlanState *node, ExprContext *econtext, bool *isNull); @@ -69,15 +67,12 @@ static bool slotNoNulls(TupleTableSlot *slot); static Datum ExecSubPlan(SubPlanState *node, ExprContext *econtext, - bool *isNull, - ExprDoneCond *isDone) + bool *isNull) { SubPlan *subplan = (SubPlan *) node->xprstate.expr; /* Set default values for result flags: non-null, not a set result */ *isNull = false; - if (isDone) - *isDone = ExprSingleResult; /* Sanity checks */ if (subplan->subLinkType == CTE_SUBLINK) @@ -128,7 +123,7 @@ ExecHashSubPlan(SubPlanState *node, * have to set the econtext to use (hack alert!). */ node->projLeft->pi_exprContext = econtext; - slot = ExecProject(node->projLeft, NULL); + slot = ExecProject(node->projLeft); /* * Note: because we are typically called in a per-tuple context, we have @@ -285,8 +280,7 @@ ExecScanSubPlan(SubPlanState *node, prm->value = ExecEvalExprSwitchContext((ExprState *) lfirst(pvar), econtext, - &(prm->isnull), - NULL); + &(prm->isnull)); planstate->chgParam = bms_add_member(planstate->chgParam, paramid); } @@ -403,7 +397,7 @@ ExecScanSubPlan(SubPlanState *node, } rowresult = ExecEvalExprSwitchContext(node->testexpr, econtext, - &rownull, NULL); + &rownull); if (subLinkType == ANY_SUBLINK) { @@ -570,7 +564,7 @@ buildSubPlanHash(SubPlanState *node, ExprContext *econtext) &(prmdata->isnull)); col++; } - slot = ExecProject(node->projRight, NULL); + slot = ExecProject(node->projRight); /* * If result contains any nulls, store separately or not at all. @@ -987,8 +981,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext) prm->value = ExecEvalExprSwitchContext((ExprState *) lfirst(pvar), econtext, - &(prm->isnull), - NULL); + &(prm->isnull)); planstate->chgParam = bms_add_member(planstate->chgParam, paramid); } @@ -1224,8 +1217,7 @@ ExecInitAlternativeSubPlan(AlternativeSubPlan *asplan, PlanState *parent) static Datum ExecAlternativeSubPlan(AlternativeSubPlanState *node, ExprContext *econtext, - bool *isNull, - ExprDoneCond *isDone) + bool *isNull) { /* Just pass control to the active subplan */ SubPlanState *activesp = (SubPlanState *) list_nth(node->subplans, @@ -1233,8 +1225,5 @@ ExecAlternativeSubPlan(AlternativeSubPlanState *node, Assert(IsA(activesp, SubPlanState)); - return ExecSubPlan(activesp, - econtext, - isNull, - isDone); + return ExecSubPlan(activesp, econtext, isNull); } diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c index 9bafc62..4de7024 100644 --- a/src/backend/executor/nodeSubqueryscan.c +++ b/src/backend/executor/nodeSubqueryscan.c @@ -138,8 +138,6 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags) */ subquerystate->subplan = ExecInitNode(node->subplan, estate, eflags); - subquerystate->ss.ps.ps_TupFromTlist = false; - /* * Initialize scan tuple type (needed by ExecAssignScanProjectionInfo) */ diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index 2604103..e1c736c 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -104,8 +104,7 @@ TidListCreate(TidScanState *tidstate) itemptr = (ItemPointer) DatumGetPointer(ExecEvalExprSwitchContext(exstate, econtext, - &isNull, - NULL)); + &isNull)); if (!isNull && ItemPointerIsValid(itemptr) && ItemPointerGetBlockNumber(itemptr) < nblocks) @@ -133,8 +132,7 @@ TidListCreate(TidScanState *tidstate) exstate = (ExprState *) lsecond(saexstate->fxprstate.args); arraydatum = ExecEvalExprSwitchContext(exstate, econtext, - &isNull, - NULL); + &isNull); if (isNull) continue; itemarray = DatumGetArrayTypeP(arraydatum); @@ -469,8 +467,6 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags) */ ExecAssignExprContext(estate, &tidstate->ss.ps); - tidstate->ss.ps.ps_TupFromTlist = false; - /* * initialize child expressions */ diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c index 9c03f8a..18c8ae9 100644 --- a/src/backend/executor/nodeValuesscan.c +++ b/src/backend/executor/nodeValuesscan.c @@ -140,8 +140,7 @@ ValuesNext(ValuesScanState *node) values[resind] = ExecEvalExpr(estate, econtext, - &isnull[resind], - NULL); + &isnull[resind]); /* * We must force any R/W expanded datums to read-only state, in @@ -272,8 +271,6 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags) scanstate->exprlists[i++] = (List *) lfirst(vtl); } - scanstate->ss.ps.ps_TupFromTlist = false; - /* * Initialize result tuple type and projection info. */ diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index d4c88a1..42550c9 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -256,7 +256,7 @@ advance_windowaggregate(WindowAggState *winstate, if (filter) { bool isnull; - Datum res = ExecEvalExpr(filter, econtext, &isnull, NULL); + Datum res = ExecEvalExpr(filter, econtext, &isnull); if (isnull || !DatumGetBool(res)) { @@ -272,7 +272,7 @@ advance_windowaggregate(WindowAggState *winstate, ExprState *argstate = (ExprState *) lfirst(arg); fcinfo->arg[i] = ExecEvalExpr(argstate, econtext, - &fcinfo->argnull[i], NULL); + &fcinfo->argnull[i]); i++; } @@ -418,7 +418,7 @@ advance_windowaggregate_base(WindowAggState *winstate, if (filter) { bool isnull; - Datum res = ExecEvalExpr(filter, econtext, &isnull, NULL); + Datum res = ExecEvalExpr(filter, econtext, &isnull); if (isnull || !DatumGetBool(res)) { @@ -434,7 +434,7 @@ advance_windowaggregate_base(WindowAggState *winstate, ExprState *argstate = (ExprState *) lfirst(arg); fcinfo->arg[i] = ExecEvalExpr(argstate, econtext, - &fcinfo->argnull[i], NULL); + &fcinfo->argnull[i]); i++; } @@ -1551,15 +1551,12 @@ update_frametailpos(WindowObject winobj, TupleTableSlot *slot) * ExecWindowAgg receives tuples from its outer subplan and * stores them into a tuplestore, then processes window functions. * This node doesn't reduce nor qualify any row so the number of - * returned rows is exactly the same as its outer subplan's result - * (ignoring the case of SRFs in the targetlist, that is). + * returned rows is exactly the same as its outer subplan's result. * ----------------- */ TupleTableSlot * ExecWindowAgg(WindowAggState *winstate) { - TupleTableSlot *result; - ExprDoneCond isDone; ExprContext *econtext; int i; int numfuncs; @@ -1568,23 +1565,6 @@ ExecWindowAgg(WindowAggState *winstate) return NULL; /* - * Check to see if we're still projecting out tuples from a previous - * output tuple (because there is a function-returning-set in the - * projection expressions). If so, try to project another one. - */ - if (winstate->ss.ps.ps_TupFromTlist) - { - TupleTableSlot *result; - ExprDoneCond isDone; - - result = ExecProject(winstate->ss.ps.ps_ProjInfo, &isDone); - if (isDone == ExprMultipleResult) - return result; - /* Done with that source tuple... */ - winstate->ss.ps.ps_TupFromTlist = false; - } - - /* * Compute frame offset values, if any, during first call. */ if (winstate->all_first) @@ -1601,8 +1581,7 @@ ExecWindowAgg(WindowAggState *winstate) Assert(winstate->startOffset != NULL); value = ExecEvalExprSwitchContext(winstate->startOffset, econtext, - &isnull, - NULL); + &isnull); if (isnull) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), @@ -1627,8 +1606,7 @@ ExecWindowAgg(WindowAggState *winstate) Assert(winstate->endOffset != NULL); value = ExecEvalExprSwitchContext(winstate->endOffset, econtext, - &isnull, - NULL); + &isnull); if (isnull) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), @@ -1651,7 +1629,6 @@ ExecWindowAgg(WindowAggState *winstate) winstate->all_first = false; } -restart: if (winstate->buffer == NULL) { /* Initialize for first partition and set current row = 0 */ @@ -1743,17 +1720,8 @@ restart: * evaluated with respect to that row. */ econtext->ecxt_outertuple = winstate->ss.ss_ScanTupleSlot; - result = ExecProject(winstate->ss.ps.ps_ProjInfo, &isDone); - if (isDone == ExprEndResult) - { - /* SRF in tlist returned no rows, so advance to next input tuple */ - goto restart; - } - - winstate->ss.ps.ps_TupFromTlist = - (isDone == ExprMultipleResult); - return result; + return ExecProject(winstate->ss.ps.ps_ProjInfo); } /* ----------------- @@ -1867,8 +1835,6 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) ExecAssignResultTypeFromTL(&winstate->ss.ps); ExecAssignProjectionInfo(&winstate->ss.ps, NULL); - winstate->ss.ps.ps_TupFromTlist = false; - /* Set up data for comparing tuples */ if (node->partNumCols > 0) winstate->partEqfunctions = execTuplesMatchPrepare(node->partNumCols, @@ -2061,8 +2027,6 @@ ExecReScanWindowAgg(WindowAggState *node) ExprContext *econtext = node->ss.ps.ps_ExprContext; node->all_done = false; - - node->ss.ps.ps_TupFromTlist = false; node->all_first = true; /* release tuplestore et al */ @@ -2685,7 +2649,7 @@ WinGetFuncArgInPartition(WindowObject winobj, int argno, } econtext->ecxt_outertuple = slot; return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno), - econtext, isnull, NULL); + econtext, isnull); } } @@ -2784,7 +2748,7 @@ WinGetFuncArgInFrame(WindowObject winobj, int argno, } econtext->ecxt_outertuple = slot; return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno), - econtext, isnull, NULL); + econtext, isnull); } } @@ -2814,5 +2778,5 @@ WinGetFuncArgCurrent(WindowObject winobj, int argno, bool *isnull) econtext->ecxt_outertuple = winstate->ss.ss_ScanTupleSlot; return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno), - econtext, isnull, NULL); + econtext, isnull); } diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c index cfed6e6..dbb8ea3 100644 --- a/src/backend/executor/nodeWorktablescan.c +++ b/src/backend/executor/nodeWorktablescan.c @@ -174,8 +174,6 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags) */ ExecAssignResultTypeFromTL(&scanstate->ss.ps); - scanstate->ss.ps.ps_TupFromTlist = false; - return scanstate; } diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 986c92b..73862a2 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -151,8 +151,7 @@ static PathTarget *make_window_input_target(PlannerInfo *root, static List *make_pathkeys_for_window(PlannerInfo *root, WindowClause *wc, List *tlist); static PathTarget *make_sort_input_target(PlannerInfo *root, - PathTarget *final_target, - bool *have_postponed_srfs); + PathTarget *final_target); /***************************************************************************** @@ -1443,8 +1442,6 @@ grouping_planner(PlannerInfo *root, bool inheritance_update, int64 offset_est = 0; int64 count_est = 0; double limit_tuples = -1.0; - bool have_postponed_srfs = false; - double tlist_rows; PathTarget *final_target; RelOptInfo *current_rel; RelOptInfo *final_rel; @@ -1710,10 +1707,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update, * Figure out whether there's a hard limit on the number of rows that * query_planner's result subplan needs to return. Even if we know a * hard limit overall, it doesn't apply if the query has any - * grouping/aggregation operations. (XXX it also doesn't apply if the - * tlist contains any SRFs; but checking for that here seems more - * costly than it's worth, since root->limit_tuples is only used for - * cost estimates, and only in a small number of cases.) + * grouping/aggregation operations. */ if (parse->groupClause || parse->groupingSets || @@ -1757,8 +1751,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update, */ if (parse->sortClause) sort_input_target = make_sort_input_target(root, - final_target, - &have_postponed_srfs); + final_target); else sort_input_target = final_target; @@ -1915,50 +1908,17 @@ grouping_planner(PlannerInfo *root, bool inheritance_update, /* * If ORDER BY was given, consider ways to implement that, and generate a * new upperrel containing only paths that emit the correct ordering and - * project the correct final_target. We can apply the original - * limit_tuples limit in sort costing here, but only if there are no - * postponed SRFs. + * project the correct final_target. */ if (parse->sortClause) { current_rel = create_ordered_paths(root, current_rel, final_target, - have_postponed_srfs ? -1.0 : limit_tuples); } /* - * If there are set-returning functions in the tlist, scale up the output - * rowcounts of all surviving Paths to account for that. Note that if any - * SRFs appear in sorting or grouping columns, we'll have underestimated - * the numbers of rows passing through earlier steps; but that's such a - * weird usage that it doesn't seem worth greatly complicating matters to - * account for it. - */ - tlist_rows = tlist_returns_set_rows(tlist); - if (tlist_rows > 1) - { - foreach(lc, current_rel->pathlist) - { - Path *path = (Path *) lfirst(lc); - - /* - * We assume that execution costs of the tlist as such were - * already accounted for. However, it still seems appropriate to - * charge something more for the executor's general costs of - * processing the added tuples. The cost is probably less than - * cpu_tuple_cost, though, so we arbitrarily use half of that. - */ - path->total_cost += path->rows * (tlist_rows - 1) * - cpu_tuple_cost / 2; - - path->rows *= tlist_rows; - } - /* No need to run set_cheapest; we're keeping all paths anyway. */ - } - - /* * Now we are prepared to build the final-output upperrel. */ final_rel = fetch_upper_rel(root, UPPERREL_FINAL, NULL); @@ -4907,20 +4867,12 @@ make_pathkeys_for_window(PlannerInfo *root, WindowClause *wc, * * Our current policy is to postpone volatile expressions till after the sort * unconditionally (assuming that that's possible, ie they are in plain tlist - * columns and not ORDER BY/GROUP BY/DISTINCT columns). We also prefer to - * postpone set-returning expressions, because running them beforehand would - * bloat the sort dataset, and because it might cause unexpected output order - * if the sort isn't stable. However there's a constraint on that: all SRFs - * in the tlist should be evaluated at the same plan step, so that they can - * run in sync in ExecTargetList. So if any SRFs are in sort columns, we - * mustn't postpone any SRFs. (Note that in principle that policy should - * probably get applied to the group/window input targetlists too, but we - * have not done that historically.) Lastly, expensive expressions are - * postponed if there is a LIMIT, or if root->tuple_fraction shows that - * partial evaluation of the query is possible (if neither is true, we expect - * to have to evaluate the expressions for every row anyway), or if there are - * any volatile or set-returning expressions (since once we've put in a - * projection at all, it won't cost any more to postpone more stuff). + * columns and not ORDER BY/GROUP BY/DISTINCT columns). Also, expensive + * expressions are postponed if there is a LIMIT, or if root->tuple_fraction + * shows that partial evaluation of the query is possible (if neither is true, + * we expect to have to evaluate the expressions for every row anyway), or if + * there are any volatile or set-returning expressions (since once we've put + * in a projection at all, it won't cost any more to postpone more stuff). * * Another issue that could potentially be considered here is that * evaluating tlist expressions could result in data that's either wider @@ -4944,30 +4896,21 @@ make_pathkeys_for_window(PlannerInfo *root, WindowClause *wc, * computed earlier. * * 'final_target' is the query's final target list (in PathTarget form) - * 'have_postponed_srfs' is an output argument, see below * * The result is the PathTarget to be computed by the plan node immediately * below the Sort step (and the Distinct step, if any). This will be * exactly final_target if we decide a projection step wouldn't be helpful. - * - * In addition, *have_postponed_srfs is set to TRUE if we choose to postpone - * any set-returning functions to after the Sort. */ static PathTarget * make_sort_input_target(PlannerInfo *root, - PathTarget *final_target, - bool *have_postponed_srfs) + PathTarget *final_target) { Query *parse = root->parse; PathTarget *input_target; int ncols; - bool *col_is_srf; bool *postpone_col; - bool have_srf; bool have_volatile; bool have_expensive; - bool have_srf_sortcols; - bool postpone_srfs; List *postponable_cols; List *postponable_vars; int i; @@ -4976,13 +4919,10 @@ make_sort_input_target(PlannerInfo *root, /* Shouldn't get here unless query has ORDER BY */ Assert(parse->sortClause); - *have_postponed_srfs = false; /* default result */ - /* Inspect tlist and collect per-column information */ ncols = list_length(final_target->exprs); - col_is_srf = (bool *) palloc0(ncols * sizeof(bool)); postpone_col = (bool *) palloc0(ncols * sizeof(bool)); - have_srf = have_volatile = have_expensive = have_srf_sortcols = false; + have_volatile = have_expensive = false; i = 0; foreach(lc, final_target->exprs) @@ -5000,16 +4940,9 @@ make_sort_input_target(PlannerInfo *root, if (get_pathtarget_sortgroupref(final_target, i) == 0) { /* - * Check for SRF or volatile functions. Check the SRF case first - * because we must know whether we have any postponed SRFs. + * Check for volatile functions. */ - if (expression_returns_set((Node *) expr)) - { - /* We'll decide below whether these are postponable */ - col_is_srf[i] = true; - have_srf = true; - } - else if (contain_volatile_functions((Node *) expr)) + if (contain_volatile_functions((Node *) expr)) { /* Unconditionally postpone */ postpone_col[i] = true; @@ -5038,39 +4971,19 @@ make_sort_input_target(PlannerInfo *root, } } } - else - { - /* For sortgroupref cols, just check if any contain SRFs */ - if (!have_srf_sortcols && - expression_returns_set((Node *) expr)) - have_srf_sortcols = true; - } i++; } /* - * We can postpone SRFs if we have some but none are in sortgroupref cols. - */ - postpone_srfs = (have_srf && !have_srf_sortcols); - - /* * If we don't need a post-sort projection, just return final_target. */ - if (!(postpone_srfs || have_volatile || + if (!(have_volatile || (have_expensive && (parse->limitCount || root->tuple_fraction > 0)))) return final_target; /* - * Report whether the post-sort projection will contain set-returning - * functions. This is important because it affects whether the Sort can - * rely on the query's LIMIT (if any) to bound the number of rows it needs - * to return. - */ - *have_postponed_srfs = postpone_srfs; - - /* * Construct the sort-input target, taking all non-postponable columns and * then adding Vars, PlaceHolderVars, Aggrefs, and WindowFuncs found in * the postponable ones. @@ -5083,7 +4996,7 @@ make_sort_input_target(PlannerInfo *root, { Expr *expr = (Expr *) lfirst(lc); - if (postpone_col[i] || (postpone_srfs && col_is_srf[i])) + if (postpone_col[i]) postponable_cols = lappend(postponable_cols, expr); else add_column_to_pathtarget(input_target, expr, diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 9c502bd..8f7a8bd 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -807,11 +807,13 @@ find_window_functions_walker(Node *node, WindowFuncLists *lists) * Estimate the number of rows returned by a set-returning expression. * The result is 1 if there are no set-returning functions. * - * We use the product of the rowcount estimates of all the functions in - * the given tree (this corresponds to the behavior of ExecMakeFunctionResult - * for nested set-returning functions). + * We use the product of the rowcount estimates of all the functions in the + * given tree (this corresponds to the behavior of ExecMakeFunctionResult for + * nested set-returning functions). * * Note: keep this in sync with expression_returns_set() in nodes/nodeFuncs.c. + * + * FIXME: This possibly be simplified now that targetlist SRFs are gone. */ double expression_returns_set_rows(Node *clause) @@ -881,40 +883,6 @@ expression_returns_set_rows_walker(Node *node, double *count) (void *) count); } -/* - * tlist_returns_set_rows - * Estimate the number of rows returned by a set-returning targetlist. - * The result is 1 if there are no set-returning functions. - * - * Here, the result is the largest rowcount estimate of any of the tlist's - * expressions, not the product as you would get from naively applying - * expression_returns_set_rows() to the whole tlist. The behavior actually - * implemented by ExecTargetList produces a number of rows equal to the least - * common multiple of the expression rowcounts, so that the product would be - * a worst-case estimate that is typically not realistic. Taking the max as - * we do here is a best-case estimate that might not be realistic either, - * but it's probably closer for typical usages. We don't try to compute the - * actual LCM because we're working with very approximate estimates, so their - * LCM would be unduly noisy. - */ -double -tlist_returns_set_rows(List *tlist) -{ - double result = 1; - ListCell *lc; - - foreach(lc, tlist) - { - TargetEntry *tle = (TargetEntry *) lfirst(lc); - double colresult; - - colresult = expression_returns_set_rows((Node *) tle->expr); - if (result < colresult) - result = colresult; - } - return result; -} - /***************************************************************************** * Subplan clause manipulation @@ -4899,7 +4867,7 @@ inline_function(Oid funcid, Oid result_type, Oid result_collid, /* * Forget it if the function is not SQL-language or has other showstopper - * properties. (The nargs check is just paranoia.) + * properties. (The nargs and retset checks are just paranoia.) */ if (funcform->prolang != SQLlanguageId || funcform->prosecdef || @@ -5287,7 +5255,7 @@ evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod, */ const_val = ExecEvalExprSwitchContext(exprstate, GetPerTupleExprContext(estate), - &const_is_null, NULL); + &const_is_null); /* Get info needed about result datatype */ get_typlenbyval(result_type, &resultTypLen, &resultTypByVal); diff --git a/src/backend/optimizer/util/predtest.c b/src/backend/optimizer/util/predtest.c index 2c2efb1..0c59fe8 100644 --- a/src/backend/optimizer/util/predtest.c +++ b/src/backend/optimizer/util/predtest.c @@ -1596,7 +1596,7 @@ operator_predicate_proof(Expr *predicate, Node *clause, bool refute_it) /* And execute it. */ test_result = ExecEvalExprSwitchContext(test_exprstate, GetPerTupleExprContext(estate), - &isNull, NULL); + &isNull); /* Get back to outer memory context */ MemoryContextSwitchTo(oldcontext); diff --git a/src/backend/utils/adt/domains.c b/src/backend/utils/adt/domains.c index 19ee4ce..c568c6c 100644 --- a/src/backend/utils/adt/domains.c +++ b/src/backend/utils/adt/domains.c @@ -164,7 +164,7 @@ domain_check_input(Datum value, bool isnull, DomainIOData *my_extra) conResult = ExecEvalExprSwitchContext(con->check_expr, econtext, - &conIsNull, NULL); + &conIsNull); if (!conIsNull && !DatumGetBool(conResult)) diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c index 7ed5bcb..65bf6ad 100644 --- a/src/backend/utils/adt/xml.c +++ b/src/backend/utils/adt/xml.c @@ -603,7 +603,7 @@ xmlelement(XmlExprState *xmlExpr, ExprContext *econtext) bool isnull; char *str; - value = ExecEvalExpr(e, econtext, &isnull, NULL); + value = ExecEvalExpr(e, econtext, &isnull); if (isnull) str = NULL; else @@ -620,7 +620,7 @@ xmlelement(XmlExprState *xmlExpr, ExprContext *econtext) bool isnull; char *str; - value = ExecEvalExpr(e, econtext, &isnull, NULL); + value = ExecEvalExpr(e, econtext, &isnull); /* here we can just forget NULL elements immediately */ if (!isnull) { diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 7f11285..79a31a9 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -69,8 +69,8 @@ * now it's just a macro invoking the function pointed to by an ExprState * node. Beware of double evaluation of the ExprState argument! */ -#define ExecEvalExpr(expr, econtext, isNull, isDone) \ - ((*(expr)->evalfunc) (expr, econtext, isNull, isDone)) +#define ExecEvalExpr(expr, econtext, isNull) \ + ((*(expr)->evalfunc) (expr, econtext, isNull)) /* Hook for plugins to get control in ExecutorStart() */ @@ -235,18 +235,17 @@ extern Datum GetAttributeByNum(HeapTupleHeader tuple, AttrNumber attrno, extern Datum GetAttributeByName(HeapTupleHeader tuple, const char *attname, bool *isNull); extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); -extern ExprDoneCond ExecEvalFuncArgs(FunctionCallInfo fcinfo, + bool *isNull); +extern void ExecEvalFuncArgs(FunctionCallInfo fcinfo, List *argList, ExprContext *econtext); extern ExprState *ExecInitExpr(Expr *node, PlanState *parent); extern void ExecInitFcache(Oid foid, Oid input_collation, FuncExprState *fcache, - MemoryContext fcacheCxt, bool needDescForSets); + MemoryContext fcacheCxt); extern ExprState *ExecPrepareExpr(Expr *node, EState *estate); extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull); extern int ExecTargetListLength(List *targetlist); extern int ExecCleanTargetListLength(List *targetlist); -extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, - ExprDoneCond *isDone); +extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo); /* * prototypes from functions in execScan.c diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index e7fd7bd..043f969 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -243,7 +243,6 @@ typedef struct ProjectionInfo List *pi_targetlist; ExprContext *pi_exprContext; TupleTableSlot *pi_slot; - ExprDoneCond *pi_itemIsDone; bool pi_directMap; int pi_numSimpleVars; int *pi_varSlotOffsets; @@ -569,8 +568,7 @@ typedef struct ExprState ExprState; typedef Datum (*ExprStateEvalFunc) (ExprState *expression, ExprContext *econtext, - bool *isNull, - ExprDoneCond *isDone); + bool *isNull); struct ExprState { @@ -692,21 +690,13 @@ typedef struct FuncExprState TupleTableSlot *funcResultSlot; /* - * In some cases we need to compute a tuple descriptor for the function's - * output. If so, it's stored here. - */ - TupleDesc funcResultDesc; - bool funcReturnsTuple; /* valid when funcResultDesc isn't - * NULL */ - - /* * setArgsValid is true when we are evaluating a set-returning function * that uses value-per-call mode and we are in the middle of a call * series; we want to pass the same argument values to the function again * (and again, until it returns ExprEndResult). This indicates that * fcinfo_data already contains valid argument data. */ - bool setArgsValid; + bool setArgsValid2; /* * Flag to remember whether we found a set-valued argument to the @@ -1057,8 +1047,6 @@ typedef struct PlanState TupleTableSlot *ps_ResultTupleSlot; /* slot for my result tuples */ ExprContext *ps_ExprContext; /* node's expression-evaluation context */ ProjectionInfo *ps_ProjInfo; /* info for doing tuple projection */ - bool ps_TupFromTlist;/* state flag for processing set-valued - * functions in targetlist */ } PlanState; /* ---------------- diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h index 7fb5005..e2d44ce 100644 --- a/src/include/optimizer/clauses.h +++ b/src/include/optimizer/clauses.h @@ -54,7 +54,6 @@ extern bool contain_window_function(Node *clause); extern WindowFuncLists *find_window_functions(Node *clause, Index maxWinRef); extern double expression_returns_set_rows(Node *clause); -extern double tlist_returns_set_rows(List *tlist); extern bool contain_subplans(Node *clause); diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index f9b3b22..d8905c9 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -5644,8 +5644,7 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate, */ *result = ExecEvalExpr(expr->expr_simple_state, econtext, - isNull, - NULL); + isNull); /* Assorted cleanup */ expr->expr_simple_in_use = false; @@ -6312,7 +6311,7 @@ exec_cast_value(PLpgSQL_execstate *estate, cast_entry->cast_in_use = true; value = ExecEvalExpr(cast_entry->cast_exprstate, econtext, - isnull, NULL); + isnull); cast_entry->cast_in_use = false; -- 2.9.3