From 75de30def4aa674908775176ce03a1a2f99b1d39 Mon Sep 17 00:00:00 2001 From: Marina Polyakova Date: Thu, 24 May 2018 12:49:25 +0300 Subject: [PATCH v9] Precalculate stable/immutable expressions: executor Cached expressions are always executed as PARAM_EXEC. Usually plans for all cached expressions are handled during query planning and they are compiled separatly. But sometimes they are used in dynamically loaded plans (for example, domain constraints plans). In this case all information about them is stored in ExprState, not in EState. Usually in this case the new node PlannedExpr is used. It is returned by a function expression_planner, and contains an expression and the list of its cached expressions. Note: the cached ids (= the PARAM_EXEC ids for cached expressions) of all non-internal cached expressions must be set by the function set_non_internal_cachedexprs_walker after all mutations. Otherwise all the created cached expressions are considered internal and will not be cached by themselves. --- src/backend/catalog/heap.c | 2 +- src/backend/commands/copy.c | 6 +- src/backend/commands/indexcmds.c | 2 +- src/backend/commands/tablecmds.c | 14 +- src/backend/executor/execExpr.c | 263 +++++++++++++++++++++++-- src/backend/executor/execExprInterp.c | 56 +++++- src/backend/executor/execMain.c | 5 + src/backend/executor/execParallel.c | 1 + src/backend/executor/execPartition.c | 6 +- src/backend/executor/execSRF.c | 2 +- src/backend/executor/nodeAgg.c | 2 +- src/backend/executor/nodeBitmapHeapscan.c | 4 +- src/backend/executor/nodeCtescan.c | 2 +- src/backend/executor/nodeCustom.c | 2 +- src/backend/executor/nodeForeignscan.c | 4 +- src/backend/executor/nodeFunctionscan.c | 2 +- src/backend/executor/nodeGroup.c | 2 +- src/backend/executor/nodeHash.c | 2 +- src/backend/executor/nodeHashjoin.c | 10 +- src/backend/executor/nodeIndexonlyscan.c | 4 +- src/backend/executor/nodeIndexscan.c | 12 +- src/backend/executor/nodeLimit.c | 4 +- src/backend/executor/nodeMergejoin.c | 10 +- src/backend/executor/nodeModifyTable.c | 4 +- src/backend/executor/nodeNamedtuplestorescan.c | 2 +- src/backend/executor/nodeNestloop.c | 4 +- src/backend/executor/nodeProjectSet.c | 2 +- src/backend/executor/nodeResult.c | 5 +- src/backend/executor/nodeSamplescan.c | 4 +- src/backend/executor/nodeSeqscan.c | 2 +- src/backend/executor/nodeSubplan.c | 2 +- src/backend/executor/nodeSubqueryscan.c | 2 +- src/backend/executor/nodeTableFuncscan.c | 6 +- src/backend/executor/nodeTidscan.c | 8 +- src/backend/executor/nodeValuesscan.c | 2 +- src/backend/executor/nodeWindowAgg.c | 4 +- src/backend/executor/nodeWorktablescan.c | 2 +- src/backend/optimizer/plan/planner.c | 13 +- src/backend/optimizer/util/clauses.c | 2 +- src/backend/optimizer/util/predtest.c | 2 +- src/backend/parser/parse_utilcmd.c | 2 +- src/backend/partitioning/partbounds.c | 2 +- src/backend/partitioning/partprune.c | 5 +- src/backend/replication/logical/worker.c | 7 +- src/backend/utils/cache/typcache.c | 18 +- src/include/executor/execExpr.h | 11 ++ src/include/executor/executor.h | 13 +- src/include/nodes/execnodes.h | 35 +++- src/include/nodes/params.h | 13 +- src/include/optimizer/planner.h | 2 +- src/pl/plpgsql/src/pl_exec.c | 34 +++- src/pl/plpgsql/src/plpgsql.h | 2 + 52 files changed, 508 insertions(+), 119 deletions(-) diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index ffdf6f8..113cc43 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -2102,7 +2102,7 @@ StoreAttrDefault(Relation rel, AttrNumber attnum, if (add_column_mode) { - expr2 = expression_planner(expr2); + expr2 = expression_planner(expr2)->expr; estate = CreateExecutorState(); exprState = ExecPrepareExpr(expr2, estate); econtext = GetPerTupleExprContext(estate); diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 0a1706c..b77635b 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -3075,10 +3075,12 @@ BeginCopyFrom(ParseState *pstate, if (defexpr != NULL) { /* Run the expression through planner */ - defexpr = expression_planner(defexpr); + PlannedExpr *planned_defexpr = expression_planner(defexpr); + defexpr = planned_defexpr->expr; /* Initialize executable expression in copycontext */ - defexprs[num_defaults] = ExecInitExpr(defexpr, NULL); + defexprs[num_defaults] = + ExecInitExpr(defexpr, NULL, planned_defexpr->cachedExprs); defmap[num_defaults] = attnum - 1; num_defaults++; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 3a3223b..7550913 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -1335,7 +1335,7 @@ CheckMutability(Expr *expr) * * We assume here that expression_planner() won't scribble on its input. */ - expr = expression_planner(expr); + expr = expression_planner(expr)->expr; /* Now we can search for non-immutable functions */ return contain_mutable_functions((Node *) expr); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 0e95037..5cc9cf2 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -200,7 +200,7 @@ typedef struct NewConstraint typedef struct NewColumnValue { AttrNumber attnum; /* which column */ - Expr *expr; /* expression to compute */ + PlannedExpr *expr; /* expression to compute */ ExprState *exprstate; /* execution state */ } NewColumnValue; @@ -4621,9 +4621,11 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) foreach(l, tab->newvals) { NewColumnValue *ex = lfirst(l); + PlannedExpr *planned_expr = ex->expr; /* expr already planned */ - ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL); + ex->exprstate = ExecInitExpr((Expr *) planned_expr->expr, NULL, + planned_expr->cachedExprs); } notnull_attrs = NIL; @@ -9052,6 +9054,7 @@ ATPrepAlterColumnType(List **wqueue, ParseState *pstate = make_parsestate(NULL); AclResult aclresult; bool is_expr; + PlannedExpr *planned_transform; if (rel->rd_rel->reloftype && !recursing) ereport(ERROR, @@ -9164,7 +9167,8 @@ ATPrepAlterColumnType(List **wqueue, assign_expr_collations(pstate, transform); /* Plan the expr now so we can accurately assess the need to rewrite. */ - transform = (Node *) expression_planner((Expr *) transform); + planned_transform = expression_planner((Expr *) transform); + transform = (Node *) planned_transform->expr; /* * Add a work queue item to make ATRewriteTable update the column @@ -9172,7 +9176,7 @@ ATPrepAlterColumnType(List **wqueue, */ newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue)); newval->attnum = attnum; - newval->expr = (Expr *) transform; + newval->expr = planned_transform; tab->newvals = lappend(tab->newvals, newval); if (ATColumnChangeRequiresRewrite(transform, attnum)) @@ -13733,7 +13737,7 @@ ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs, * expression destructively and we have already saved the * expression to be stored into the catalog above. */ - expr = (Node *) expression_planner((Expr *) expr); + expr = (Node *) expression_planner((Expr *) expr)->expr; /* * Partition expression cannot contain mutable functions, diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c index 29c21c3..55996ff 100644 --- a/src/backend/executor/execExpr.c +++ b/src/backend/executor/execExpr.c @@ -78,6 +78,8 @@ static void ExecBuildAggTransCall(ExprState *state, AggState *aggstate, ExprEvalStep *scratch, FunctionCallInfo fcinfo, AggStatePerTrans pertrans, int transno, int setno, int setoff, bool ishash); +static void ExecInitDynamicallyPlannedCachedExprs(ExprState *state, + List *cachedexprs); /* @@ -114,9 +116,12 @@ static void ExecBuildAggTransCall(ExprState *state, AggState *aggstate, * callers that may or may not have an expression that needs to be compiled. * Note that a NULL ExprState pointer *cannot* be handed to ExecEvalExpr, * although ExecQual and ExecCheck will accept one (and treat it as "true"). + * + * Initialize cached expressions as dynamically planned if cachedexprs is not + * NULL. */ ExprState * -ExecInitExpr(Expr *node, PlanState *parent) +ExecInitExpr(Expr *node, PlanState *parent, List *cachedexprs) { ExprState *state; ExprEvalStep scratch = {0}; @@ -134,6 +139,10 @@ ExecInitExpr(Expr *node, PlanState *parent) /* Insert EEOP_*_FETCHSOME steps as needed */ ExecInitExprSlots(state, (Node *) node); + /* Initialize dynamically planned cached expressions */ + if (cachedexprs) + ExecInitDynamicallyPlannedCachedExprs(state, cachedexprs); + /* Compile the expression proper */ ExecInitExprRec(node, state, &state->resvalue, &state->resnull); @@ -153,7 +162,7 @@ ExecInitExpr(Expr *node, PlanState *parent) * and instead we may have a ParamListInfo describing PARAM_EXTERN Params. */ ExprState * -ExecInitExprWithParams(Expr *node, ParamListInfo ext_params) +ExecInitExprWithParams(Expr *node, ParamListInfo ext_params, List *cachedexprs) { ExprState *state; ExprEvalStep scratch = {0}; @@ -171,6 +180,10 @@ ExecInitExprWithParams(Expr *node, ParamListInfo ext_params) /* Insert EEOP_*_FETCHSOME steps as needed */ ExecInitExprSlots(state, (Node *) node); + /* Initialize dynamically planned cached expressions */ + if (cachedexprs) + ExecInitDynamicallyPlannedCachedExprs(state, cachedexprs); + /* Compile the expression proper */ ExecInitExprRec(node, state, &state->resvalue, &state->resnull); @@ -200,9 +213,12 @@ ExecInitExprWithParams(Expr *node, ParamListInfo ext_params) * is false. This makes ExecQual primarily useful for evaluating WHERE * clauses, since SQL specifies that tuples with null WHERE results do not * get selected. + * + * As in ExecInitExpr, initialize cached expressions as dynamically planned if + * cachedexprs is not NULL. */ ExprState * -ExecInitQual(List *qual, PlanState *parent) +ExecInitQual(List *qual, PlanState *parent, List *cachedexprs) { ExprState *state; ExprEvalStep scratch = {0}; @@ -226,6 +242,10 @@ ExecInitQual(List *qual, PlanState *parent) /* Insert EEOP_*_FETCHSOME steps as needed */ ExecInitExprSlots(state, (Node *) qual); + /* Initialize dynamically planned cached expressions */ + if (cachedexprs) + ExecInitDynamicallyPlannedCachedExprs(state, cachedexprs); + /* * ExecQual() needs to return false for an expression returning NULL. That * allows us to short-circuit the evaluation the first time a NULL is @@ -291,7 +311,7 @@ ExecInitQual(List *qual, PlanState *parent) * can just apply ExecInitExpr to produce suitable input for ExecCheck. */ ExprState * -ExecInitCheck(List *qual, PlanState *parent) +ExecInitCheck(List *qual, PlanState *parent, List *cachedexprs) { /* short-circuit (here and in ExecCheck) for empty restriction list */ if (qual == NIL) @@ -304,7 +324,7 @@ ExecInitCheck(List *qual, PlanState *parent) * than one entry), and compile normally. Unlike ExecQual, we can't * short-circuit on NULL results, so the regular AND behavior is needed. */ - return ExecInitExpr(make_ands_explicit(qual), parent); + return ExecInitExpr(make_ands_explicit(qual), parent, cachedexprs); } /* @@ -320,7 +340,7 @@ ExecInitExprList(List *nodes, PlanState *parent) { Expr *e = lfirst(lc); - result = lappend(result, ExecInitExpr(e, parent)); + result = lappend(result, ExecInitExpr(e, parent, NULL)); } return result; @@ -489,12 +509,13 @@ ExecPrepareExpr(Expr *node, EState *estate) { ExprState *result; MemoryContext oldcontext; + PlannedExpr *planned_expr; oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - node = expression_planner(node); + planned_expr = expression_planner(node); - result = ExecInitExpr(node, NULL); + result = ExecInitExpr(planned_expr->expr, NULL, planned_expr->cachedExprs); MemoryContextSwitchTo(oldcontext); @@ -517,12 +538,14 @@ ExecPrepareQual(List *qual, EState *estate) { ExprState *result; MemoryContext oldcontext; + PlannedExpr *planned_qual; oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - qual = (List *) expression_planner((Expr *) qual); + planned_qual = expression_planner((Expr *) qual); - result = ExecInitQual(qual, NULL); + result = ExecInitQual((List *) planned_qual->expr, NULL, + planned_qual->cachedExprs); MemoryContextSwitchTo(oldcontext); @@ -540,12 +563,14 @@ ExecPrepareCheck(List *qual, EState *estate) { ExprState *result; MemoryContext oldcontext; + PlannedExpr *planned_qual; oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - qual = (List *) expression_planner((Expr *) qual); + planned_qual = expression_planner((Expr *) qual); - result = ExecInitCheck(qual, NULL); + result = ExecInitCheck((List *) planned_qual->expr, NULL, + planned_qual->cachedExprs); MemoryContextSwitchTo(oldcontext); @@ -734,6 +759,7 @@ ExecInitExprRec(Expr *node, ExprState *state, scratch.opcode = EEOP_PARAM_EXEC; scratch.d.param.paramid = param->paramid; scratch.d.param.paramtype = param->paramtype; + scratch.d.param.dynamically_planned = false; ExprEvalPushStep(state, &scratch); break; case PARAM_EXTERN: @@ -761,6 +787,7 @@ ExecInitExprRec(Expr *node, ExprState *state, scratch.opcode = EEOP_PARAM_EXTERN; scratch.d.param.paramid = param->paramid; scratch.d.param.paramtype = param->paramtype; + scratch.d.param.dynamically_planned = false; ExprEvalPushStep(state, &scratch); } break; @@ -842,7 +869,7 @@ ExecInitExprRec(Expr *node, ExprState *state, wfstate->args = ExecInitExprList(wfunc->args, state->parent); wfstate->aggfilter = ExecInitExpr(wfunc->aggfilter, - state->parent); + state->parent, NULL); /* * Complain if the windowfunc's arguments contain any @@ -867,6 +894,61 @@ ExecInitExprRec(Expr *node, ExprState *state, break; } + case T_CachedExpr: + { + CachedExpr *cachedexpr = (CachedExpr *) node; + int cached_id = cachedexpr->cached_id; + + if (cached_id < 0) + { + /* + * This is an internal cached expression. So just add its + * subexpression steps. + */ + ExecInitExprRec((Expr *) cachedexpr->subexpr, state, resv, + resnull); + } + else + { + CachedExprsInfo *cachedexprs_info; + bool dynamically_planned; + + if (state->cachedexprs_vals) + { + cachedexprs_info = &(state->cachedexprs_info); + dynamically_planned = true; + } + else + { + if (!state->parent) + { + /* planner messed up */ + elog(ERROR, "CachedExpr found with no parent plan"); + } + + if (!state->parent->state) + { + /* planner messed up */ + elog(ERROR, + "CachedExpr found with no parent plan's executor state"); + } + + cachedexprs_info = + &(state->parent->state->es_cachedexprs_info); + dynamically_planned = false; + } + + scratch.opcode = EEOP_PARAM_EXEC; + scratch.d.param.paramid = + (cachedexprs_info->first_cached_paramid + cached_id); + scratch.d.param.paramtype = exprType((const Node *) node); + scratch.d.param.dynamically_planned = dynamically_planned; + ExprEvalPushStep(state, &scratch); + } + + break; + } + case T_ArrayRef: { ArrayRef *aref = (ArrayRef *) node; @@ -2716,7 +2798,7 @@ ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest, foreach(l, constraint_ref->constraints) { DomainConstraintState *con = (DomainConstraintState *) lfirst(l); - Expr *check_expr = con->check_expr; + PlannedExpr *check_expr = con->check_expr; ExprState *check_exprstate; scratch->d.domaincheck.constraintname = con->name; @@ -2762,7 +2844,7 @@ ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest, } check_exprstate = makeNode(ExprState); - check_exprstate->expr = check_expr; + check_exprstate->expr = check_expr->expr; check_exprstate->parent = state->parent; check_exprstate->ext_params = state->ext_params; @@ -2771,7 +2853,9 @@ ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest, check_exprstate->innermost_domainnull = domainnull; /* evaluate check expression value */ - ExecInitExprRec(check_expr, check_exprstate, + ExecInitDynamicallyPlannedCachedExprs(check_exprstate, + check_expr->cachedExprs); + ExecInitExprRec(check_expr->expr, check_exprstate, &check_exprstate->resvalue, &check_exprstate->resnull); @@ -3345,3 +3429,150 @@ ExecBuildGroupingEqual(TupleDesc ldesc, TupleDesc rdesc, return state; } + +void +ExecInitCachedExprs(QueryDesc *queryDesc) +{ + List *cachedexprs = queryDesc->plannedstmt->cachedExprs; + EState *estate = queryDesc->estate; + PlanState *planstate = queryDesc->planstate; + CachedExprsInfo *cachedexprs_info = &(estate->es_cachedexprs_info); + MemoryContext oldcontext; + int num_cachedexprs; + ListCell *cell; + + if (cachedexprs == NIL) + { + /* nothing to do here */ + return; + } + + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + num_cachedexprs = list_length(cachedexprs); + cachedexprs_info->subexpr_states = (ExprState *) + palloc0(num_cachedexprs * sizeof(ExprState)); + cachedexprs_info->num_cachedexprs = num_cachedexprs; + + /* + * Set the assigned PARAM_EXEC slot number of the first cached expression + * since the PARAM_EXEC nodes must be the first. + */ + cachedexprs_info->first_cached_paramid = + (list_length(queryDesc->plannedstmt->paramExecTypes) - num_cachedexprs); + + foreach(cell, cachedexprs) + { + CachedExpr *cachedexpr = (CachedExpr *) lfirst(cell); + int cached_id = cachedexpr->cached_id; + int paramid; + Expr *subexpr = (Expr *) cachedexpr->subexpr; + ExprState *subexpr_state; + ExprEvalStep scratch = {0}; + + if (cached_id < 0) + { + /* planner messed up */ + elog(ERROR, + "Internal cached expression found in the list of non-internal cached expressions"); + } + + paramid = cachedexprs_info->first_cached_paramid + cached_id; + subexpr_state = &(cachedexprs_info->subexpr_states[cached_id]); + + /* make ExprState for subexpression evaluation */ + subexpr_state->tag.type = T_ExprState; + subexpr_state->expr = subexpr; + subexpr_state->parent = planstate; + subexpr_state->ext_params = NULL; + + /* Compile the subexpression proper */ + ExecInitExprRec(subexpr, subexpr_state, + &subexpr_state->resvalue, + &subexpr_state->resnull); + + /* Finally, append a DONE step */ + scratch.opcode = EEOP_DONE; + ExprEvalPushStep(subexpr_state, &scratch); + + ExecReadyExpr(subexpr_state); + + estate->es_param_exec_vals[paramid].execPlan = subexpr_state; + } + + MemoryContextSwitchTo(oldcontext); +} + +static void +ExecInitDynamicallyPlannedCachedExprs(ExprState *state, List *cachedexprs) +{ + CachedExprsInfo *cachedexprs_info = &(state->cachedexprs_info); + int num_cachedexprs; + ListCell *cell; + + if (cachedexprs == NIL) + { + /* nothing to do here */ + return; + } + + num_cachedexprs = list_length(cachedexprs); + state->cachedexprs_vals = (ParamExecData *) + palloc0(num_cachedexprs * sizeof(ParamExecData)); + cachedexprs_info->subexpr_states = (ExprState *) + palloc0(num_cachedexprs * sizeof(ExprState)); + cachedexprs_info->num_cachedexprs = num_cachedexprs; + cachedexprs_info->first_cached_paramid = 0; + + foreach(cell, cachedexprs) + { + CachedExpr *cachedexpr = (CachedExpr *) lfirst(cell); + int cached_id = cachedexpr->cached_id; + Expr *subexpr = (Expr *) cachedexpr->subexpr; + ExprState *subexpr_state; + ExprEvalStep scratch = {0}; + + if (cached_id < 0) + { + /* planner messed up */ + elog(ERROR, + "Internal cached expression found in the list of non-internal cached expressions"); + } + + subexpr_state = &(cachedexprs_info->subexpr_states[cached_id]); + + /* make ExprState for subexpression evaluation */ + subexpr_state->tag.type = T_ExprState; + subexpr_state->expr = subexpr; + subexpr_state->parent = state->parent; + subexpr_state->ext_params = state->ext_params; + + /* Compile the subexpression proper */ + ExecInitExprRec(subexpr, subexpr_state, + &subexpr_state->resvalue, + &subexpr_state->resnull); + + /* Finally, append a DONE step */ + scratch.opcode = EEOP_DONE; + ExprEvalPushStep(subexpr_state, &scratch); + + ExecReadyExpr(subexpr_state); + + state->cachedexprs_vals[cached_id].execPlan = subexpr_state; + } +} + +void +ExecSetDynamicallyPlannedCachedExprs(ExprState *state) +{ + CachedExprsInfo *cachedexprs_info = &(state->cachedexprs_info); + int cached_id; + + for (cached_id = 0; + cached_id < cachedexprs_info->num_cachedexprs; + ++cached_id) + { + state->cachedexprs_vals[cached_id].execPlan = + &(cachedexprs_info->subexpr_states[cached_id]); + } +} diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index f0246ce..d18faf8 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2239,13 +2239,24 @@ void ExecEvalParamExec(ExprState *state, ExprEvalStep *op, ExprContext *econtext) { ParamExecData *prm; + int paramid = op->d.param.paramid; + + if (op->d.param.dynamically_planned) + prm = &(state->cachedexprs_vals[paramid]); + else + prm = &(econtext->ecxt_param_exec_vals[paramid]); - prm = &(econtext->ecxt_param_exec_vals[op->d.param.paramid]); if (unlikely(prm->execPlan != NULL)) { /* Parameter not evaluated yet, so go do it */ - ExecSetParamPlan(prm->execPlan, econtext); - /* ExecSetParamPlan should have processed this param... */ + if (IsA(prm->execPlan, ExprState)) + ExecEvalCachedExpr(state, op, econtext); + else + ExecSetParamPlan(prm->execPlan, econtext); + /* + * ExecSetParamPlan/ExecEvalCachedExpr should have processed this + * param... + */ Assert(prm->execPlan == NULL); } *op->resvalue = prm->value; @@ -4139,3 +4150,42 @@ ExecEvalAggOrderedTransTuple(ExprState *state, ExprEvalStep *op, ExecStoreVirtualTuple(pertrans->sortslot); tuplesort_puttupleslot(pertrans->sortstates[setno], pertrans->sortslot); } + +void +ExecEvalCachedExpr(ExprState *state, ExprEvalStep *op, ExprContext *econtext) +{ + ParamExecData *prm; + int paramid = op->d.param.paramid; + MemoryContext oldContext; + int16 restyplen; + bool restypbyval; + + if (op->d.param.dynamically_planned) + prm = &(state->cachedexprs_vals[paramid]); + else + prm = &(econtext->ecxt_param_exec_vals[paramid]); + + prm->value = ExecEvalExpr((ExprState *) prm->execPlan, econtext, + &(prm->isnull)); + + /* Save result */ + if (!(prm->isnull)) + { + get_typlenbyval(op->d.param.paramtype, &restyplen, &restypbyval); + + /* + * Switch per-query memory context. It is necessary to save the + * subexpression result between all tuples if its value datum is a + * pointer. + */ + oldContext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); + + prm->value = datumCopy(prm->value, restypbyval, restyplen); + + /* Switch memory context back */ + MemoryContextSwitchTo(oldContext); + } + + /* Mark this cached expression as done */ + prm->execPlan = NULL; +} diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 3d12f9c..88c83f1 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -259,6 +259,11 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags) AfterTriggerBeginQuery(); /* + * Initialize cached expressions used in the plan + */ + ExecInitCachedExprs(queryDesc); + + /* * Initialize the plan state tree */ InitPlan(queryDesc, eflags); diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index 52f1a96..5aa050e 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -208,6 +208,7 @@ ExecSerializePlan(Plan *plan, EState *estate) pstmt->utilityStmt = NULL; pstmt->stmt_location = -1; pstmt->stmt_len = -1; + pstmt->cachedExprs = estate->es_plannedstmt->cachedExprs; /* Return serialized copy of our dummy PlannedStmt. */ return nodeToString(pstmt); diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index c83991c..f127b03 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -420,7 +420,7 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, { WithCheckOption *wco = castNode(WithCheckOption, lfirst(ll)); ExprState *wcoExpr = ExecInitQual(castNode(List, wco->qual), - &mtstate->ps); + &mtstate->ps, NULL); wcoExprs = lappend(wcoExprs, wcoExpr); } @@ -646,7 +646,7 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, &found_whole_row); /* We ignore the value of found_whole_row. */ leaf_part_rri->ri_onConflict->oc_WhereClause = - ExecInitQual((List *) clause, &mtstate->ps); + ExecInitQual((List *) clause, &mtstate->ps, NULL); } } } @@ -1504,7 +1504,7 @@ ExecSetupPartitionPruneState(PlanState *planstate, List *partitionpruneinfo) stateidx = PruneCxtStateIdx(partnatts, step->step.step_id, keyno); context->exprstates[stateidx] = - ExecInitExpr(expr, context->planstate); + ExecInitExpr(expr, context->planstate, NULL); } keyno++; } diff --git a/src/backend/executor/execSRF.c b/src/backend/executor/execSRF.c index b97b8d7..98598c4 100644 --- a/src/backend/executor/execSRF.c +++ b/src/backend/executor/execSRF.c @@ -83,7 +83,7 @@ ExecInitTableFunctionResult(Expr *expr, } else { - state->elidedFuncState = ExecInitExpr(expr, parent); + state->elidedFuncState = ExecInitExpr(expr, parent, NULL); } return state; diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index 0fe0c22..7781e87 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -2237,7 +2237,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) * in the targetlist are found during ExecAssignProjectionInfo, below. */ aggstate->ss.ps.qual = - ExecInitQual(node->plan.qual, (PlanState *) aggstate); + ExecInitQual(node->plan.qual, (PlanState *) aggstate, NULL); /* * We should now have found all Aggrefs in the targetlist and quals. diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c index 3e1c9e0..b06e7c9 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -937,9 +937,9 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags) * initialize child expressions */ scanstate->ss.ps.qual = - ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate); + ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate, NULL); scanstate->bitmapqualorig = - ExecInitQual(node->bitmapqualorig, (PlanState *) scanstate); + ExecInitQual(node->bitmapqualorig, (PlanState *) scanstate, NULL); /* * Determine the maximum for prefetch_target. If the tablespace has a diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c index 24700dd..0af8b82 100644 --- a/src/backend/executor/nodeCtescan.c +++ b/src/backend/executor/nodeCtescan.c @@ -272,7 +272,7 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags) * initialize child expressions */ scanstate->ss.ps.qual = - ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate); + ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate, NULL); return scanstate; } diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c index b816e0b..77369bf 100644 --- a/src/backend/executor/nodeCustom.c +++ b/src/backend/executor/nodeCustom.c @@ -92,7 +92,7 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) /* initialize child expressions */ css->ss.ps.qual = - ExecInitQual(cscan->scan.plan.qual, (PlanState *) css); + ExecInitQual(cscan->scan.plan.qual, (PlanState *) css, NULL); /* * The callback of custom-scan provider applies the final initialization diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index a2a28b7..18faf88 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -205,9 +205,9 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) * initialize child expressions */ scanstate->ss.ps.qual = - ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate); + ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate, NULL); scanstate->fdw_recheck_quals = - ExecInitQual(node->fdw_recheck_quals, (PlanState *) scanstate); + ExecInitQual(node->fdw_recheck_quals, (PlanState *) scanstate, NULL); /* * Initialize FDW-related state. diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index fb7c9f6..9492012 100644 --- a/src/backend/executor/nodeFunctionscan.c +++ b/src/backend/executor/nodeFunctionscan.c @@ -494,7 +494,7 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) * initialize child expressions */ scanstate->ss.ps.qual = - ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate); + ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate, NULL); /* * Create a memory context that ExecMakeTableFunctionResult can use to diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index 2ea80e8..41c3b77 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -200,7 +200,7 @@ ExecInitGroup(Group *node, EState *estate, int eflags) * initialize child expressions */ grpstate->ss.ps.qual = - ExecInitQual(node->plan.qual, (PlanState *) grpstate); + ExecInitQual(node->plan.qual, (PlanState *) grpstate, NULL); /* * Precompute fmgr lookup data for inner loop diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 4f069d1..d139f5a 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -389,7 +389,7 @@ ExecInitHash(Hash *node, EState *estate, int eflags) * initialize child expressions */ hashstate->ps.qual = - ExecInitQual(node->plan.qual, (PlanState *) hashstate); + ExecInitQual(node->plan.qual, (PlanState *) hashstate, NULL); return hashstate; } diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index dd94cff..23215a9 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -701,11 +701,11 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) * initialize child expressions */ hjstate->js.ps.qual = - ExecInitQual(node->join.plan.qual, (PlanState *) hjstate); + ExecInitQual(node->join.plan.qual, (PlanState *) hjstate, NULL); hjstate->js.joinqual = - ExecInitQual(node->join.joinqual, (PlanState *) hjstate); + ExecInitQual(node->join.joinqual, (PlanState *) hjstate, NULL); hjstate->hashclauses = - ExecInitQual(node->hashclauses, (PlanState *) hjstate); + ExecInitQual(node->hashclauses, (PlanState *) hjstate, NULL); /* * initialize hash-specific info @@ -732,9 +732,9 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) OpExpr *hclause = lfirst_node(OpExpr, l); lclauses = lappend(lclauses, ExecInitExpr(linitial(hclause->args), - (PlanState *) hjstate)); + (PlanState *) hjstate, NULL)); rclauses = lappend(rclauses, ExecInitExpr(lsecond(hclause->args), - (PlanState *) hjstate)); + (PlanState *) hjstate, NULL)); hoperators = lappend_oid(hoperators, hclause->opno); } hjstate->hj_OuterHashKeys = lclauses; diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c index 3a02a99..790d6d9 100644 --- a/src/backend/executor/nodeIndexonlyscan.c +++ b/src/backend/executor/nodeIndexonlyscan.c @@ -550,9 +550,9 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags) * sub-parts corresponding to runtime keys (see below). */ indexstate->ss.ps.qual = - ExecInitQual(node->scan.plan.qual, (PlanState *) indexstate); + ExecInitQual(node->scan.plan.qual, (PlanState *) indexstate, NULL); indexstate->indexqual = - ExecInitQual(node->indexqual, (PlanState *) indexstate); + ExecInitQual(node->indexqual, (PlanState *) indexstate, NULL); /* * If we are just doing EXPLAIN (ie, aren't going to run the plan), stop diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index d601219..404c61e 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -971,9 +971,9 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) * in the expression must be found now...) */ indexstate->ss.ps.qual = - ExecInitQual(node->scan.plan.qual, (PlanState *) indexstate); + ExecInitQual(node->scan.plan.qual, (PlanState *) indexstate, NULL); indexstate->indexqualorig = - ExecInitQual(node->indexqualorig, (PlanState *) indexstate); + ExecInitQual(node->indexqualorig, (PlanState *) indexstate, NULL); indexstate->indexorderbyorig = ExecInitExprList(node->indexorderbyorig, (PlanState *) indexstate); @@ -1308,7 +1308,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, } runtime_keys[n_runtime_keys].scan_key = this_scan_key; runtime_keys[n_runtime_keys].key_expr = - ExecInitExpr(rightop, planstate); + ExecInitExpr(rightop, planstate, NULL); runtime_keys[n_runtime_keys].key_toastable = TypeIsToastable(op_righttype); n_runtime_keys++; @@ -1438,7 +1438,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, } runtime_keys[n_runtime_keys].scan_key = this_sub_key; runtime_keys[n_runtime_keys].key_expr = - ExecInitExpr(rightop, planstate); + ExecInitExpr(rightop, planstate, NULL); runtime_keys[n_runtime_keys].key_toastable = TypeIsToastable(op_righttype); n_runtime_keys++; @@ -1556,7 +1556,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, } runtime_keys[n_runtime_keys].scan_key = this_scan_key; runtime_keys[n_runtime_keys].key_expr = - ExecInitExpr(rightop, planstate); + ExecInitExpr(rightop, planstate, NULL); /* * Careful here: the runtime expression is not of @@ -1574,7 +1574,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, /* Executor has to expand the array value */ array_keys[n_array_keys].scan_key = this_scan_key; array_keys[n_array_keys].array_expr = - ExecInitExpr(rightop, planstate); + ExecInitExpr(rightop, planstate, NULL); /* the remaining fields were zeroed by palloc0 */ n_array_keys++; scanvalue = (Datum) 0; diff --git a/src/backend/executor/nodeLimit.c b/src/backend/executor/nodeLimit.c index 56d98b4..3d12b79 100644 --- a/src/backend/executor/nodeLimit.c +++ b/src/backend/executor/nodeLimit.c @@ -363,9 +363,9 @@ ExecInitLimit(Limit *node, EState *estate, int eflags) * initialize child expressions */ limitstate->limitOffset = ExecInitExpr((Expr *) node->limitOffset, - (PlanState *) limitstate); + (PlanState *) limitstate, NULL); limitstate->limitCount = ExecInitExpr((Expr *) node->limitCount, - (PlanState *) limitstate); + (PlanState *) limitstate, NULL); /* * Initialize result slot and type. (XXX not actually used, but upper diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 5e52b90..93d91ff 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -207,8 +207,10 @@ MJExamineQuals(List *mergeclauses, /* * Prepare the input expressions for execution. */ - clause->lexpr = ExecInitExpr((Expr *) linitial(qual->args), parent); - clause->rexpr = ExecInitExpr((Expr *) lsecond(qual->args), parent); + clause->lexpr = ExecInitExpr((Expr *) linitial(qual->args), parent, + NULL); + clause->rexpr = ExecInitExpr((Expr *) lsecond(qual->args), parent, + NULL); /* Set up sort support data */ clause->ssup.ssup_cxt = CurrentMemoryContext; @@ -1524,9 +1526,9 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) * initialize child expressions */ mergestate->js.ps.qual = - ExecInitQual(node->join.plan.qual, (PlanState *) mergestate); + ExecInitQual(node->join.plan.qual, (PlanState *) mergestate, NULL); mergestate->js.joinqual = - ExecInitQual(node->join.joinqual, (PlanState *) mergestate); + ExecInitQual(node->join.joinqual, (PlanState *) mergestate, NULL); /* mergeclauses are handled below */ /* diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index c4c841c..845b206 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -2351,7 +2351,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) { WithCheckOption *wco = (WithCheckOption *) lfirst(ll); ExprState *wcoExpr = ExecInitQual((List *) wco->qual, - mtstate->mt_plans[i]); + mtstate->mt_plans[i], NULL); wcoExprs = lappend(wcoExprs, wcoExpr); } @@ -2483,7 +2483,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) ExprState *qualexpr; qualexpr = ExecInitQual((List *) node->onConflictWhere, - &mtstate->ps); + &mtstate->ps, NULL); resultRelInfo->ri_onConflict->oc_WhereClause = qualexpr; } } diff --git a/src/backend/executor/nodeNamedtuplestorescan.c b/src/backend/executor/nodeNamedtuplestorescan.c index b260ad2..ca5a70c 100644 --- a/src/backend/executor/nodeNamedtuplestorescan.c +++ b/src/backend/executor/nodeNamedtuplestorescan.c @@ -145,7 +145,7 @@ ExecInitNamedTuplestoreScan(NamedTuplestoreScan *node, EState *estate, int eflag * initialize child expressions */ scanstate->ss.ps.qual = - ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate); + ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate, NULL); /* * Initialize projection. diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index 9ae9863..caa4049 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -311,10 +311,10 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags) * initialize child expressions */ nlstate->js.ps.qual = - ExecInitQual(node->join.plan.qual, (PlanState *) nlstate); + ExecInitQual(node->join.plan.qual, (PlanState *) nlstate, NULL); nlstate->js.jointype = node->join.jointype; nlstate->js.joinqual = - ExecInitQual(node->join.joinqual, (PlanState *) nlstate); + ExecInitQual(node->join.joinqual, (PlanState *) nlstate, NULL); /* * detect whether we need only consider the first matching inner tuple diff --git a/src/backend/executor/nodeProjectSet.c b/src/backend/executor/nodeProjectSet.c index 6d6ed38..c8d4226 100644 --- a/src/backend/executor/nodeProjectSet.c +++ b/src/backend/executor/nodeProjectSet.c @@ -287,7 +287,7 @@ ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags) else { Assert(!expression_returns_set((Node *) expr)); - state->elems[off] = (Node *) ExecInitExpr(expr, &state->ps); + state->elems[off] = (Node *) ExecInitExpr(expr, &state->ps, NULL); } off++; diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index e4418a2..e99b1c1 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -224,9 +224,10 @@ ExecInitResult(Result *node, EState *estate, int eflags) * initialize child expressions */ resstate->ps.qual = - ExecInitQual(node->plan.qual, (PlanState *) resstate); + ExecInitQual(node->plan.qual, (PlanState *) resstate, NULL); resstate->resconstantqual = - ExecInitQual((List *) node->resconstantqual, (PlanState *) resstate); + ExecInitQual((List *) node->resconstantqual, (PlanState *) resstate, + NULL); return resstate; } diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c index 15177db..900839d 100644 --- a/src/backend/executor/nodeSamplescan.c +++ b/src/backend/executor/nodeSamplescan.c @@ -163,11 +163,11 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags) * initialize child expressions */ scanstate->ss.ps.qual = - ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate); + ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate, NULL); scanstate->args = ExecInitExprList(tsc->args, (PlanState *) scanstate); scanstate->repeatable = - ExecInitExpr(tsc->repeatable, (PlanState *) scanstate); + ExecInitExpr(tsc->repeatable, (PlanState *) scanstate, NULL); /* * If we don't have a REPEATABLE clause, select a random seed. We want to diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index 9db3689..ad50b2c 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -188,7 +188,7 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags) * initialize child expressions */ scanstate->ss.ps.qual = - ExecInitQual(node->plan.qual, (PlanState *) scanstate); + ExecInitQual(node->plan.qual, (PlanState *) scanstate, NULL); return scanstate; } diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index 44f551b..2c0addf 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -778,7 +778,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) sstate->parent = parent; /* Initialize subexpressions */ - sstate->testexpr = ExecInitExpr((Expr *) subplan->testexpr, parent); + sstate->testexpr = ExecInitExpr((Expr *) subplan->testexpr, parent, NULL); sstate->args = ExecInitExprList(subplan->args, parent); /* diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c index fa61884..ebb1de2 100644 --- a/src/backend/executor/nodeSubqueryscan.c +++ b/src/backend/executor/nodeSubqueryscan.c @@ -141,7 +141,7 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags) * initialize child expressions */ subquerystate->ss.ps.qual = - ExecInitQual(node->scan.plan.qual, (PlanState *) subquerystate); + ExecInitQual(node->scan.plan.qual, (PlanState *) subquerystate, NULL); return subquerystate; } diff --git a/src/backend/executor/nodeTableFuncscan.c b/src/backend/executor/nodeTableFuncscan.c index fed6f2b..a7a04d1 100644 --- a/src/backend/executor/nodeTableFuncscan.c +++ b/src/backend/executor/nodeTableFuncscan.c @@ -159,7 +159,7 @@ ExecInitTableFuncScan(TableFuncScan *node, EState *estate, int eflags) * initialize child expressions */ scanstate->ss.ps.qual = - ExecInitQual(node->scan.plan.qual, &scanstate->ss.ps); + ExecInitQual(node->scan.plan.qual, &scanstate->ss.ps, NULL); /* Only XMLTABLE is supported currently */ scanstate->routine = &XmlTableRoutine; @@ -175,9 +175,9 @@ ExecInitTableFuncScan(TableFuncScan *node, EState *estate, int eflags) scanstate->ns_uris = ExecInitExprList(tf->ns_uris, (PlanState *) scanstate); scanstate->docexpr = - ExecInitExpr((Expr *) tf->docexpr, (PlanState *) scanstate); + ExecInitExpr((Expr *) tf->docexpr, (PlanState *) scanstate, NULL); scanstate->rowexpr = - ExecInitExpr((Expr *) tf->rowexpr, (PlanState *) scanstate); + ExecInitExpr((Expr *) tf->rowexpr, (PlanState *) scanstate, NULL); scanstate->colexprs = ExecInitExprList(tf->colexprs, (PlanState *) scanstate); scanstate->coldefexprs = diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index e207b1f..8c8c53d 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -83,10 +83,10 @@ TidExprListCreate(TidScanState *tidstate) arg2 = get_rightop(expr); if (IsCTIDVar(arg1)) tidexpr->exprstate = ExecInitExpr((Expr *) arg2, - &tidstate->ss.ps); + &tidstate->ss.ps, NULL); else if (IsCTIDVar(arg2)) tidexpr->exprstate = ExecInitExpr((Expr *) arg1, - &tidstate->ss.ps); + &tidstate->ss.ps, NULL); else elog(ERROR, "could not identify CTID variable"); tidexpr->isarray = false; @@ -97,7 +97,7 @@ TidExprListCreate(TidScanState *tidstate) Assert(IsCTIDVar(linitial(saex->args))); tidexpr->exprstate = ExecInitExpr(lsecond(saex->args), - &tidstate->ss.ps); + &tidstate->ss.ps, NULL); tidexpr->isarray = true; } else if (expr && IsA(expr, CurrentOfExpr)) @@ -561,7 +561,7 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags) * initialize child expressions */ tidstate->ss.ps.qual = - ExecInitQual(node->scan.plan.qual, (PlanState *) tidstate); + ExecInitQual(node->scan.plan.qual, (PlanState *) tidstate, NULL); TidExprListCreate(tidstate); diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c index f76999d..5681f8b 100644 --- a/src/backend/executor/nodeValuesscan.c +++ b/src/backend/executor/nodeValuesscan.c @@ -273,7 +273,7 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags) * initialize child expressions */ scanstate->ss.ps.qual = - ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate); + ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate, NULL); /* * Other node-specific setup diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index fe5369a..0aaac10 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -2476,9 +2476,9 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) /* initialize frame bound offset expressions */ winstate->startOffset = ExecInitExpr((Expr *) node->startOffset, - (PlanState *) winstate); + (PlanState *) winstate, NULL); winstate->endOffset = ExecInitExpr((Expr *) node->endOffset, - (PlanState *) winstate); + (PlanState *) winstate, NULL); /* Lookup in_range support functions if needed */ if (OidIsValid(node->startInRangeFunc)) diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c index 2ff9a21..b2ea099 100644 --- a/src/backend/executor/nodeWorktablescan.c +++ b/src/backend/executor/nodeWorktablescan.c @@ -166,7 +166,7 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags) * initialize child expressions */ scanstate->ss.ps.qual = - ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate); + ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate, NULL); /* * Do not yet initialize projection info, see ExecWorkTableScan() for diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 67a2c7a..882a752 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -5883,22 +5883,25 @@ adjust_paths_for_srfs(PlannerInfo *root, RelOptInfo *rel, * tree. (It would actually be okay to apply fix_opfuncids to it, but since * we first do an expression_tree_mutator-based walk, what is returned will * be a new node tree.) + * + * Note: all cached expressions must be added to the corresponding EState or + * ExprState of this expression. */ -Expr * +PlannedExpr * expression_planner(Expr *expr) { - Node *result; + PlannedExpr *result = makeNode(PlannedExpr); /* * Convert named-argument function calls, insert default arguments and * simplify constant subexprs */ - result = eval_const_expressions(NULL, (Node *) expr); + result->expr = (Expr *) eval_const_expressions(NULL, (Node *) expr); /* Fill in opfuncid values if missing */ - fix_opfuncids(result); + fix_opfuncids((Node *) result->expr); - return (Expr *) result; + return result; } diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 505ae0a..17bcdba 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -4867,7 +4867,7 @@ evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod, * Prepare expr for execution. (Note: we can't use ExecPrepareExpr * because it'd result in recursively invoking eval_const_expressions.) */ - exprstate = ExecInitExpr(expr, NULL); + exprstate = ExecInitExpr(expr, NULL, NULL); /* * And evaluate it. diff --git a/src/backend/optimizer/util/predtest.c b/src/backend/optimizer/util/predtest.c index 446207d..02ce883 100644 --- a/src/backend/optimizer/util/predtest.c +++ b/src/backend/optimizer/util/predtest.c @@ -1749,7 +1749,7 @@ operator_predicate_proof(Expr *predicate, Node *clause, fix_opfuncids((Node *) test_expr); /* Prepare it for execution */ - test_exprstate = ExecInitExpr(test_expr, NULL); + test_exprstate = ExecInitExpr(test_expr, NULL, NULL); /* And execute it. */ test_result = ExecEvalExprSwitchContext(test_exprstate, diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 17b54b2..1691229 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -3871,7 +3871,7 @@ transformPartitionBoundValue(ParseState *pstate, A_Const *con, /* Simplify the expression, in case we had a coercion */ if (!IsA(value, Const)) - value = (Node *) expression_planner((Expr *) value); + value = (Node *) expression_planner((Expr *) value)->expr; /* Fail if we don't have a constant (i.e., non-immutable coercion) */ if (!IsA(value, Const)) diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c index 09c7c3e..001aaef 100644 --- a/src/backend/partitioning/partbounds.c +++ b/src/backend/partitioning/partbounds.c @@ -1767,7 +1767,7 @@ get_qual_for_range(Relation parent, PartitionBoundSpec *spec, (Expr *) lower_val, (Expr *) upper_val); fix_opfuncids((Node *) test_expr); - test_exprstate = ExecInitExpr(test_expr, NULL); + test_exprstate = ExecInitExpr(test_expr, NULL, NULL); test_result = ExecEvalExprSwitchContext(test_exprstate, GetPerTupleExprContext(estate), &isNull); diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c index 58ec2a6..d407a89 100644 --- a/src/backend/partitioning/partprune.c +++ b/src/backend/partitioning/partprune.c @@ -386,7 +386,7 @@ gen_partprune_steps(RelOptInfo *rel, List *clauses, bool *contradictory) { List *partqual = rel->partition_qual; - partqual = (List *) expression_planner((Expr *) partqual); + partqual = (List *) expression_planner((Expr *) partqual)->expr; /* Fix Vars to have the desired varno */ if (rel->relid != 1) @@ -689,7 +689,8 @@ gen_partprune_steps_internal(GeneratePruningStepsContext *context, if (partconstr) { partconstr = (List *) - expression_planner((Expr *) partconstr); + expression_planner((Expr *) partconstr)->expr; + if (rel->relid != 1) ChangeVarNodes((Node *) partconstr, 1, rel->relid, 0); diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index 2ed8144..5d2ea90 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -252,6 +252,7 @@ slot_fill_defaults(LogicalRepRelMapEntry *rel, EState *estate, for (attnum = 0; attnum < num_phys_attrs; attnum++) { Expr *defexpr; + PlannedExpr *planned_defexpr; if (TupleDescAttr(desc, attnum)->attisdropped) continue; @@ -264,10 +265,12 @@ slot_fill_defaults(LogicalRepRelMapEntry *rel, EState *estate, if (defexpr != NULL) { /* Run the expression through planner */ - defexpr = expression_planner(defexpr); + planned_defexpr = expression_planner(defexpr); + defexpr = planned_defexpr->expr; /* Initialize executable expression in copycontext */ - defexprs[num_defaults] = ExecInitExpr(defexpr, NULL); + defexprs[num_defaults] = ExecInitExpr(defexpr, NULL, + planned_defexpr->cachedExprs); defmap[num_defaults] = attnum; num_defaults++; } diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c index e38fd16..2079506 100644 --- a/src/backend/utils/cache/typcache.c +++ b/src/backend/utils/cache/typcache.c @@ -957,6 +957,7 @@ load_domaintype_info(TypeCacheEntry *typentry) char *constring; Expr *check_expr; DomainConstraintState *r; + PlannedExpr *planned_check_expr; /* Ignore non-CHECK constraints (presently, shouldn't be any) */ if (c->contype != CONSTRAINT_CHECK) @@ -993,12 +994,13 @@ load_domaintype_info(TypeCacheEntry *typentry) check_expr = (Expr *) stringToNode(constring); /* ExecInitExpr will assume we've planned the expression */ - check_expr = expression_planner(check_expr); + planned_check_expr = expression_planner(check_expr); + check_expr = planned_check_expr->expr; r = makeNode(DomainConstraintState); r->constrainttype = DOM_CONSTRAINT_CHECK; r->name = pstrdup(NameStr(c->conname)); - r->check_expr = check_expr; + r->check_expr = planned_check_expr; r->check_exprstate = NULL; MemoryContextSwitchTo(oldcxt); @@ -1167,7 +1169,17 @@ prep_domain_constraints(List *constraints, MemoryContext execctx) newr->constrainttype = r->constrainttype; newr->name = r->name; newr->check_expr = r->check_expr; - newr->check_exprstate = ExecInitExpr(r->check_expr, NULL); + + /* check_expr is NULL if this is NOT NULL Constraint */ + if (r->check_expr) + { + newr->check_exprstate = ExecInitExpr(r->check_expr->expr, NULL, + r->check_expr->cachedExprs); + } + else + { + newr->check_exprstate = ExecInitExpr(NULL, NULL, NULL); + } result = lappend(result, newr); } diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h index 0ec8947..5521ae2 100644 --- a/src/include/executor/execExpr.h +++ b/src/include/executor/execExpr.h @@ -349,6 +349,15 @@ typedef struct ExprEvalStep { int paramid; /* numeric ID for parameter */ Oid paramtype; /* OID of parameter's datatype */ + + /* + * Usually plans for all cached expressions are handled during query + * planning and they are compiled separatly. But sometimes they are + * used in dynamically loaded plans (for example, domain constraints + * plans). In this case all information about them is stored in + * ExprState, not in EState. + */ + bool dynamically_planned; } param; /* for EEOP_PARAM_CALLBACK */ @@ -747,5 +756,7 @@ extern void ExecEvalAggOrderedTransDatum(ExprState *state, ExprEvalStep *op, ExprContext *econtext); extern void ExecEvalAggOrderedTransTuple(ExprState *state, ExprEvalStep *op, ExprContext *econtext); +extern void ExecEvalCachedExpr(ExprState *state, ExprEvalStep *op, + ExprContext *econtext); #endif /* EXEC_EXPR_H */ diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index a7ea3c7..3c4708f 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -242,10 +242,13 @@ ExecProcNode(PlanState *node) /* * prototypes from functions in execExpr.c */ -extern ExprState *ExecInitExpr(Expr *node, PlanState *parent); -extern ExprState *ExecInitExprWithParams(Expr *node, ParamListInfo ext_params); -extern ExprState *ExecInitQual(List *qual, PlanState *parent); -extern ExprState *ExecInitCheck(List *qual, PlanState *parent); +extern ExprState *ExecInitExpr(Expr *node, PlanState *parent, + List *cachedexprs); +extern ExprState *ExecInitExprWithParams(Expr *node, ParamListInfo ext_params, + List *cachedexprs); +extern ExprState *ExecInitQual(List *qual, PlanState *parent, List *cachedexpr); +extern ExprState *ExecInitCheck(List *qual, PlanState *parent, + List *cachedexpr); extern List *ExecInitExprList(List *nodes, PlanState *parent); extern ExprState *ExecBuildAggTrans(AggState *aggstate, struct AggStatePerPhaseData *phase, bool doSort, bool doHash); @@ -263,6 +266,8 @@ extern ExprState *ExecPrepareExpr(Expr *node, EState *estate); extern ExprState *ExecPrepareQual(List *qual, EState *estate); extern ExprState *ExecPrepareCheck(List *qual, EState *estate); extern List *ExecPrepareExprList(List *nodes, EState *estate); +extern void ExecInitCachedExprs(QueryDesc *queryDesc); +extern void ExecSetDynamicallyPlannedCachedExprs(ExprState *state); /* * ExecEvalExpr diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index da7f52c..127a17b 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -54,6 +54,25 @@ typedef Datum (*ExprStateEvalFunc) (struct ExprState *expression, /* expression is for use with ExecQual() */ #define EEO_FLAG_IS_QUAL (1 << 0) +/* + * CachedExprsInfo + * + * This struct is used to store the ExprStates of subexpressions of cached + * expressions. It also stores the assigned PARAM_EXEC slot number of the first + * cached expression since the PARAM_EXEC nodes must be the first. + */ +typedef struct CachedExprsInfo +{ + struct ExprState *subexpr_states; /* array of states of subexpressions */ + int num_cachedexprs; /* length of array */ + + /* + * The paramid of the first cached expression in es_param_exec_vals or + * cachedexprs_vals (it is always 0 in the second case). + */ + int first_cached_paramid; +} CachedExprsInfo; + typedef struct ExprState { Node tag; @@ -93,6 +112,17 @@ typedef struct ExprState void *evalfunc_private; /* + * Usually plans for all cached expressions are handled during query + * planning and they are compiled separatly. But sometimes they are used in + * dynamically loaded plans (for example, domain constraints plans). In this + * case all information about them is stored in the ExprState of this plan, + * and not in EState. + */ + ParamExecData *cachedexprs_vals; /* values of cached expressions */ + struct CachedExprsInfo cachedexprs_info; /* to evaluate subexpressions of + * cached expressions */ + + /* * XXX: following fields only needed during "compilation" (ExecInitExpr); * could be thrown away afterwards. */ @@ -571,6 +601,9 @@ typedef struct EState */ int es_jit_flags; struct JitContext *es_jit; + + /* To evaluate subexpressions of cached expressions. */ + struct CachedExprsInfo es_cachedexprs_info; } EState; @@ -875,7 +908,7 @@ typedef struct DomainConstraintState NodeTag type; DomainConstraintType constrainttype; /* constraint type */ char *name; /* name of constraint (for error msgs) */ - Expr *check_expr; /* for CHECK, a boolean expression */ + PlannedExpr *check_expr; /* for CHECK, a boolean expression */ ExprState *check_exprstate; /* check_expr's eval state, or NULL */ } DomainConstraintState; diff --git a/src/include/nodes/params.h b/src/include/nodes/params.h index 04b03c7..0622a2b 100644 --- a/src/include/nodes/params.h +++ b/src/include/nodes/params.h @@ -129,14 +129,15 @@ typedef struct ParamListInfoData * ParamExecData * * ParamExecData entries are used for executor internal parameters - * (that is, values being passed into or out of a sub-query). The - * paramid of a PARAM_EXEC Param is a (zero-based) index into an - * array of ParamExecData records, which is referenced through + * (that is, values being passed into or out of a sub-query) or executor + * cached expressions (that is, the expression is executed only once for all + * output rows). The paramid of a PARAM_EXEC Param is a (zero-based) index + * into an array of ParamExecData records, which is referenced through * es_param_exec_vals or ecxt_param_exec_vals. * - * If execPlan is not NULL, it points to a SubPlanState node that needs - * to be executed to produce the value. (This is done so that we can have - * lazy evaluation of InitPlans: they aren't executed until/unless a + * If execPlan is not NULL, it points to a SubPlanState or ExprState node + * that needs to be executed to produce the value. (This is done so that we + * can have lazy evaluation of InitPlans: they aren't executed until/unless a * result value is needed.) Otherwise the value is assumed to be valid * when needed. * ---------------- diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h index c090396..81b6a84 100644 --- a/src/include/optimizer/planner.h +++ b/src/include/optimizer/planner.h @@ -52,7 +52,7 @@ extern void mark_partial_aggref(Aggref *agg, AggSplit aggsplit); extern Path *get_cheapest_fractional_path(RelOptInfo *rel, double tuple_fraction); -extern Expr *expression_planner(Expr *expr); +extern PlannedExpr *expression_planner(Expr *expr); extern Expr *preprocess_phv_expression(PlannerInfo *root, Expr *expr); diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 1eb421b..f963e0b 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -150,7 +150,7 @@ typedef struct /* lookup key for cast info */ typedef struct /* cast_hash table entry */ { plpgsql_CastHashKey key; /* hash key --- MUST BE FIRST */ - Expr *cast_expr; /* cast expression, or NULL if no-op cast */ + PlannedExpr *cast_expr; /* cast expression, or NULL if no-op cast */ /* ExprState is valid only when cast_lxid matches current LXID */ ExprState *cast_exprstate; /* expression's eval tree */ bool cast_in_use; /* true while we're executing eval tree */ @@ -6055,9 +6055,15 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate, if (expr->expr_simple_lxid != curlxid) { oldcontext = MemoryContextSwitchTo(estate->simple_eval_estate->es_query_cxt); + + /* + * Initialize cached expressions as dynamically planned since a simple + * expression can be replanned for the same executor state. + */ expr->expr_simple_state = ExecInitExprWithParams(expr->expr_simple_expr, - econtext->ecxt_param_list_info); + econtext->ecxt_param_list_info, + expr->expr_simple_cachedexprs); expr->expr_simple_in_use = false; expr->expr_simple_lxid = curlxid; MemoryContextSwitchTo(oldcontext); @@ -6082,6 +6088,12 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate, expr->expr_simple_in_use = true; /* + * The executor is used (again) from the very beginning, so the cached + * expressions must be (re)calculated. + */ + ExecSetDynamicallyPlannedCachedExprs(expr->expr_simple_state); + + /* * Finally we can call the executor to evaluate the expression */ *result = ExecEvalExpr(expr->expr_simple_state, @@ -7501,6 +7513,7 @@ get_cast_hashentry(PLpgSQL_execstate *estate, /* We've not looked up this coercion before */ Node *cast_expr; CaseTestExpr *placeholder; + PlannedExpr *planned_cast_expr; /* * Since we could easily fail (no such coercion), construct a @@ -7574,12 +7587,17 @@ get_cast_hashentry(PLpgSQL_execstate *estate, if (cast_expr) { /* ExecInitExpr assumes we've planned the expression */ - cast_expr = (Node *) expression_planner((Expr *) cast_expr); + planned_cast_expr = expression_planner((Expr *) cast_expr); /* Now copy the tree into cast_hash_context */ MemoryContextSwitchTo(estate->cast_hash_context); - cast_expr = copyObject(cast_expr); + planned_cast_expr = copyObject(planned_cast_expr); + cast_expr = (Node *) planned_cast_expr->expr; + } + else + { + planned_cast_expr = NULL; } MemoryContextSwitchTo(oldcontext); @@ -7589,7 +7607,7 @@ get_cast_hashentry(PLpgSQL_execstate *estate, (void *) &cast_key, HASH_ENTER, &found); Assert(!found); /* wasn't there a moment ago */ - cast_entry->cast_expr = (Expr *) cast_expr; + cast_entry->cast_expr = planned_cast_expr; cast_entry->cast_exprstate = NULL; cast_entry->cast_in_use = false; cast_entry->cast_lxid = InvalidLocalTransactionId; @@ -7614,8 +7632,11 @@ get_cast_hashentry(PLpgSQL_execstate *estate, curlxid = MyProc->lxid; if (cast_entry->cast_lxid != curlxid || cast_entry->cast_in_use) { + PlannedExpr *cast_expr = cast_entry->cast_expr; + oldcontext = MemoryContextSwitchTo(estate->simple_eval_estate->es_query_cxt); - cast_entry->cast_exprstate = ExecInitExpr(cast_entry->cast_expr, NULL); + cast_entry->cast_exprstate = ExecInitExpr(cast_expr->expr, NULL, + cast_expr->cachedExprs); cast_entry->cast_in_use = false; cast_entry->cast_lxid = curlxid; MemoryContextSwitchTo(oldcontext); @@ -7803,6 +7824,7 @@ exec_save_simple_expr(PLpgSQL_expr *expr, CachedPlan *cplan) /* Also stash away the expression result type */ expr->expr_simple_type = exprType((Node *) tle_expr); expr->expr_simple_typmod = exprTypmod((Node *) tle_expr); + expr->expr_simple_cachedexprs = stmt->cachedExprs; } /* diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index fe61779..0415953 100644 --- a/src/pl/plpgsql/src/plpgsql.h +++ b/src/pl/plpgsql/src/plpgsql.h @@ -230,6 +230,8 @@ typedef struct PLpgSQL_expr int expr_simple_generation; /* plancache generation we checked */ Oid expr_simple_type; /* result type Oid, if simple */ int32 expr_simple_typmod; /* result typmod, if simple */ + List *expr_simple_cachedexprs; /* list of non-internal cached + * expressions, or NIL */ /* * if expr is simple AND prepared in current transaction, -- 2.7.4