diff options
Diffstat (limited to 'src/backend/executor')
-rw-r--r-- | src/backend/executor/README | 35 | ||||
-rw-r--r-- | src/backend/executor/execGrouping.c | 4 | ||||
-rw-r--r-- | src/backend/executor/execIndexing.c | 4 | ||||
-rw-r--r-- | src/backend/executor/execMain.c | 127 | ||||
-rw-r--r-- | src/backend/executor/execParallel.c | 12 | ||||
-rw-r--r-- | src/backend/executor/execPartition.c | 66 | ||||
-rw-r--r-- | src/backend/executor/execUtils.c | 1 | ||||
-rw-r--r-- | src/backend/executor/functions.c | 5 | ||||
-rw-r--r-- | src/backend/executor/nodeModifyTable.c | 140 | ||||
-rw-r--r-- | src/backend/executor/nodeTidrangescan.c | 6 | ||||
-rw-r--r-- | src/backend/executor/spi.c | 29 |
11 files changed, 164 insertions, 265 deletions
diff --git a/src/backend/executor/README b/src/backend/executor/README index 02745c23ed9..54f4782f31b 100644 --- a/src/backend/executor/README +++ b/src/backend/executor/README @@ -285,28 +285,6 @@ are typically reset to empty once per tuple. Per-tuple contexts are usually associated with ExprContexts, and commonly each PlanState node has its own ExprContext to evaluate its qual and targetlist expressions in. -Relation Locking ----------------- - -When the executor initializes a plan tree for execution, it doesn't lock -non-index relations if the plan tree is freshly generated and not derived -from a CachedPlan. This is because such locks have already been established -during the query's parsing, rewriting, and planning phases. However, with a -cached plan tree, some relations may remain unlocked. The function -AcquireExecutorLocks() only locks unprunable relations in the plan, deferring -the locking of prunable ones to executor initialization. This avoids -unnecessary locking of relations that will be pruned during "initial" runtime -pruning in ExecDoInitialPruning(). - -This approach creates a window where a cached plan tree with child tables -could become outdated if another backend modifies these tables before -ExecDoInitialPruning() locks them. As a result, the executor has the added duty -to verify the plan tree's validity whenever it locks a child table after -doing initial pruning. This validation is done by checking the CachedPlan.is_valid -flag. If the plan tree is outdated (is_valid = false), the executor stops -further initialization, cleans up anything in EState that would have been -allocated up to that point, and retries execution after recreating the -invalid plan in the CachedPlan. See ExecutorStartCachedPlan(). Query Processing Control Flow ----------------------------- @@ -315,13 +293,11 @@ This is a sketch of control flow for full query processing: CreateQueryDesc - ExecutorStart or ExecutorStartCachedPlan + ExecutorStart CreateExecutorState creates per-query context - switch to per-query context to run ExecDoInitialPruning and ExecInitNode + switch to per-query context to run ExecInitNode AfterTriggerBeginQuery - ExecDoInitialPruning - does initial pruning and locks surviving partitions if needed ExecInitNode --- recursively scans plan tree ExecInitNode recurse into subsidiary nodes @@ -345,12 +321,7 @@ This is a sketch of control flow for full query processing: FreeQueryDesc -As mentioned in the "Relation Locking" section, if the plan tree is found to -be stale after locking partitions in ExecDoInitialPruning(), the control is -immediately returned to ExecutorStartCachedPlan(), which will create a new plan -tree and perform the steps starting from CreateExecutorState() again. - -Per above comments, it's not really critical for ExecEndPlan to free any +Per above comments, it's not really critical for ExecEndNode to free any memory; it'll all go away in FreeExecutorState anyway. However, we do need to be careful to close relations, drop buffer pins, etc, so we do need to scan the plan state tree to find these sorts of resources. diff --git a/src/backend/executor/execGrouping.c b/src/backend/executor/execGrouping.c index 255bd795361..b5400749353 100644 --- a/src/backend/executor/execGrouping.c +++ b/src/backend/executor/execGrouping.c @@ -144,7 +144,7 @@ execTuplesHashPrepare(int numCols, * hashfunctions: FmgrInfos of datatype-specific hashing functions to use * collations: collations to use in comparisons * nbuckets: initial estimate of hashtable size - * additionalsize: size of data stored in ->additional + * additionalsize: size of data that may be stored along with the hash entry * metacxt: memory context for long-lived allocation, but not per-entry data * tablecxt: memory context in which to store table entries * tempcxt: short-lived context for evaluation hash and comparison functions @@ -288,7 +288,7 @@ ResetTupleHashTable(TupleHashTable hashtable) * * If isnew isn't NULL, then a new entry is created if no existing entry * matches. On return, *isnew is true if the entry is newly created, - * false if it existed already. ->additional_data in the new entry has + * false if it existed already. The additional data in the new entry has * been zeroed. */ TupleHashEntry diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c index bdf862b2406..ca33a854278 100644 --- a/src/backend/executor/execIndexing.c +++ b/src/backend/executor/execIndexing.c @@ -279,7 +279,7 @@ ExecCloseIndices(ResultRelInfo *resultRelInfo) * executor is performing an UPDATE that could not use an * optimization like heapam's HOT (in more general terms a * call to table_tuple_update() took place and set - * 'update_indexes' to TUUI_All). Receiving this hint makes + * 'update_indexes' to TU_All). Receiving this hint makes * us consider if we should pass down the 'indexUnchanged' * hint in turn. That's something that we figure out for * each index_insert() call iff 'update' is true. @@ -290,7 +290,7 @@ ExecCloseIndices(ResultRelInfo *resultRelInfo) * HOT has been applied and any updated columns are indexed * only by summarizing indexes (or in more general terms a * call to table_tuple_update() took place and set - * 'update_indexes' to TUUI_Summarizing). We can (and must) + * 'update_indexes' to TU_Summarizing). We can (and must) * therefore only update the indexes that have * 'amsummarizing' = true. * diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 7230f968101..0391798dd2c 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -55,13 +55,11 @@ #include "parser/parse_relation.h" #include "pgstat.h" #include "rewrite/rewriteHandler.h" -#include "storage/lmgr.h" #include "tcop/utility.h" #include "utils/acl.h" #include "utils/backend_status.h" #include "utils/lsyscache.h" #include "utils/partcache.h" -#include "utils/plancache.h" #include "utils/rls.h" #include "utils/snapmgr.h" @@ -119,16 +117,11 @@ static void ReportNotNullViolationError(ResultRelInfo *resultRelInfo, * get control when ExecutorStart is called. Such a plugin would * normally call standard_ExecutorStart(). * - * Return value indicates if the plan has been initialized successfully so - * that queryDesc->planstate contains a valid PlanState tree. It may not - * if the plan got invalidated during InitPlan(). * ---------------------------------------------------------------- */ -bool +void ExecutorStart(QueryDesc *queryDesc, int eflags) { - bool plan_valid; - /* * In some cases (e.g. an EXECUTE statement or an execute message with the * extended query protocol) the query_id won't be reported, so do it now. @@ -140,14 +133,12 @@ ExecutorStart(QueryDesc *queryDesc, int eflags) pgstat_report_query_id(queryDesc->plannedstmt->queryId, false); if (ExecutorStart_hook) - plan_valid = (*ExecutorStart_hook) (queryDesc, eflags); + (*ExecutorStart_hook) (queryDesc, eflags); else - plan_valid = standard_ExecutorStart(queryDesc, eflags); - - return plan_valid; + standard_ExecutorStart(queryDesc, eflags); } -bool +void standard_ExecutorStart(QueryDesc *queryDesc, int eflags) { EState *estate; @@ -271,64 +262,6 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags) InitPlan(queryDesc, eflags); MemoryContextSwitchTo(oldcontext); - - return ExecPlanStillValid(queryDesc->estate); -} - -/* - * ExecutorStartCachedPlan - * Start execution for a given query in the CachedPlanSource, replanning - * if the plan is invalidated due to deferred locks taken during the - * plan's initialization - * - * This function handles cases where the CachedPlan given in queryDesc->cplan - * might become invalid during the initialization of the plan given in - * queryDesc->plannedstmt, particularly when prunable relations in it are - * locked after performing initial pruning. If the locks invalidate the plan, - * the function calls UpdateCachedPlan() to replan all queries in the - * CachedPlan, and then retries initialization. - * - * The function repeats the process until ExecutorStart() successfully - * initializes the plan, that is without the CachedPlan becoming invalid. - */ -void -ExecutorStartCachedPlan(QueryDesc *queryDesc, int eflags, - CachedPlanSource *plansource, - int query_index) -{ - if (unlikely(queryDesc->cplan == NULL)) - elog(ERROR, "ExecutorStartCachedPlan(): missing CachedPlan"); - if (unlikely(plansource == NULL)) - elog(ERROR, "ExecutorStartCachedPlan(): missing CachedPlanSource"); - - /* - * Loop and retry with an updated plan until no further invalidation - * occurs. - */ - while (1) - { - if (!ExecutorStart(queryDesc, eflags)) - { - /* - * Clean up the current execution state before creating the new - * plan to retry ExecutorStart(). Mark execution as aborted to - * ensure that AFTER trigger state is properly reset. - */ - queryDesc->estate->es_aborted = true; - ExecutorEnd(queryDesc); - - /* Retry ExecutorStart() with an updated plan tree. */ - queryDesc->plannedstmt = UpdateCachedPlan(plansource, query_index, - queryDesc->queryEnv); - } - else - - /* - * Exit the loop if the plan is initialized successfully and no - * sinval messages were received that invalidated the CachedPlan. - */ - break; - } } /* ---------------------------------------------------------------- @@ -387,7 +320,6 @@ standard_ExecutorRun(QueryDesc *queryDesc, estate = queryDesc->estate; Assert(estate != NULL); - Assert(!estate->es_aborted); Assert(!(estate->es_top_eflags & EXEC_FLAG_EXPLAIN_ONLY)); /* caller must ensure the query's snapshot is active */ @@ -494,11 +426,8 @@ standard_ExecutorFinish(QueryDesc *queryDesc) Assert(estate != NULL); Assert(!(estate->es_top_eflags & EXEC_FLAG_EXPLAIN_ONLY)); - /* - * This should be run once and only once per Executor instance and never - * if the execution was aborted. - */ - Assert(!estate->es_finished && !estate->es_aborted); + /* This should be run once and only once per Executor instance */ + Assert(!estate->es_finished); /* Switch into per-query memory context */ oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); @@ -561,10 +490,11 @@ standard_ExecutorEnd(QueryDesc *queryDesc) (PgStat_Counter) estate->es_parallel_workers_launched); /* - * Check that ExecutorFinish was called, unless in EXPLAIN-only mode or if - * execution was aborted. + * Check that ExecutorFinish was called, unless in EXPLAIN-only mode. This + * Assert is needed because ExecutorFinish is new as of 9.1, and callers + * might forget to call it. */ - Assert(estate->es_finished || estate->es_aborted || + Assert(estate->es_finished || (estate->es_top_eflags & EXEC_FLAG_EXPLAIN_ONLY)); /* @@ -579,14 +509,6 @@ standard_ExecutorEnd(QueryDesc *queryDesc) UnregisterSnapshot(estate->es_crosscheck_snapshot); /* - * Reset AFTER trigger module if the query execution was aborted. - */ - if (estate->es_aborted && - !(estate->es_top_eflags & - (EXEC_FLAG_SKIP_TRIGGERS | EXEC_FLAG_EXPLAIN_ONLY))) - AfterTriggerAbortQuery(); - - /* * Must switch out of context before destroying it */ MemoryContextSwitchTo(oldcontext); @@ -684,21 +606,6 @@ ExecCheckPermissions(List *rangeTable, List *rteperminfos, (rte->rtekind == RTE_SUBQUERY && rte->relkind == RELKIND_VIEW)); - /* - * Ensure that we have at least an AccessShareLock on relations - * whose permissions need to be checked. - * - * Skip this check in a parallel worker because locks won't be - * taken until ExecInitNode() performs plan initialization. - * - * XXX: ExecCheckPermissions() in a parallel worker may be - * redundant with the checks done in the leader process, so this - * should be reviewed to ensure it’s necessary. - */ - Assert(IsParallelWorker() || - CheckRelationOidLockedByMe(rte->relid, AccessShareLock, - true)); - (void) getRTEPermissionInfo(rteperminfos, rte); /* Many-to-one mapping not allowed */ Assert(!bms_is_member(rte->perminfoindex, indexset)); @@ -924,12 +831,6 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt) * * Initializes the query plan: open files, allocate storage * and start up the rule manager - * - * If the plan originates from a CachedPlan (given in queryDesc->cplan), - * it can become invalid during runtime "initial" pruning when the - * remaining set of locks is taken. The function returns early in that - * case without initializing the plan, and the caller is expected to - * retry with a new valid plan. * ---------------------------------------------------------------- */ static void @@ -937,7 +838,6 @@ InitPlan(QueryDesc *queryDesc, int eflags) { CmdType operation = queryDesc->operation; PlannedStmt *plannedstmt = queryDesc->plannedstmt; - CachedPlan *cachedplan = queryDesc->cplan; Plan *plan = plannedstmt->planTree; List *rangeTable = plannedstmt->rtable; EState *estate = queryDesc->estate; @@ -958,7 +858,6 @@ InitPlan(QueryDesc *queryDesc, int eflags) bms_copy(plannedstmt->unprunableRelids)); estate->es_plannedstmt = plannedstmt; - estate->es_cachedplan = cachedplan; estate->es_part_prune_infos = plannedstmt->partPruneInfos; /* @@ -972,9 +871,6 @@ InitPlan(QueryDesc *queryDesc, int eflags) */ ExecDoInitialPruning(estate); - if (!ExecPlanStillValid(estate)) - return; - /* * Next, build the ExecRowMark array from the PlanRowMark(s), if any. */ @@ -3092,9 +2988,6 @@ EvalPlanQualStart(EPQState *epqstate, Plan *planTree) * the snapshot, rangetable, and external Param info. They need their own * copies of local state, including a tuple table, es_param_exec_vals, * result-rel info, etc. - * - * es_cachedplan is not copied because EPQ plan execution does not acquire - * any new locks that could invalidate the CachedPlan. */ rcestate->es_direction = ForwardScanDirection; rcestate->es_snapshot = parentestate->es_snapshot; diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index 39c990ae638..f3e77bda279 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -1278,15 +1278,8 @@ ExecParallelGetQueryDesc(shm_toc *toc, DestReceiver *receiver, paramspace = shm_toc_lookup(toc, PARALLEL_KEY_PARAMLISTINFO, false); paramLI = RestoreParamList(¶mspace); - /* - * Create a QueryDesc for the query. We pass NULL for cachedplan, because - * we don't have a pointer to the CachedPlan in the leader's process. It's - * fine because the only reason the executor needs to see it is to decide - * if it should take locks on certain relations, but parallel workers - * always take locks anyway. - */ + /* Create a QueryDesc for the query. */ return CreateQueryDesc(pstmt, - NULL, queryString, GetActiveSnapshot(), InvalidSnapshot, receiver, paramLI, NULL, instrument_options); @@ -1471,8 +1464,7 @@ ParallelQueryMain(dsm_segment *seg, shm_toc *toc) /* Start up the executor */ queryDesc->plannedstmt->jitFlags = fpes->jit_flags; - if (!ExecutorStart(queryDesc, fpes->eflags)) - elog(ERROR, "ExecutorStart() failed unexpectedly"); + ExecutorStart(queryDesc, fpes->eflags); /* Special executor initialization steps for parallel workers */ queryDesc->planstate->state->es_query_dsa = area; diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 3f8a4cb5244..514eae1037d 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -26,7 +26,6 @@ #include "partitioning/partdesc.h" #include "partitioning/partprune.h" #include "rewrite/rewriteManip.h" -#include "storage/lmgr.h" #include "utils/acl.h" #include "utils/lsyscache.h" #include "utils/partcache.h" @@ -1771,8 +1770,7 @@ adjust_partition_colnos_using_map(List *colnos, AttrMap *attrMap) * ExecDoInitialPruning: * Perform runtime "initial" pruning, if necessary, to determine the set * of child subnodes that need to be initialized during ExecInitNode() for - * all plan nodes that contain a PartitionPruneInfo. This also locks the - * leaf partitions whose subnodes will be initialized if needed. + * all plan nodes that contain a PartitionPruneInfo. * * ExecInitPartitionExecPruning: * Updates the PartitionPruneState found at given part_prune_index in @@ -1798,8 +1796,7 @@ adjust_partition_colnos_using_map(List *colnos, AttrMap *attrMap) * ExecDoInitialPruning * Perform runtime "initial" pruning, if necessary, to determine the set * of child subnodes that need to be initialized during ExecInitNode() for - * plan nodes that support partition pruning. This also locks the leaf - * partitions whose subnodes will be initialized if needed. + * plan nodes that support partition pruning. * * This function iterates over each PartitionPruneInfo entry in * estate->es_part_prune_infos. For each entry, it creates a PartitionPruneState @@ -1821,9 +1818,7 @@ adjust_partition_colnos_using_map(List *colnos, AttrMap *attrMap) void ExecDoInitialPruning(EState *estate) { - PlannedStmt *stmt = estate->es_plannedstmt; ListCell *lc; - List *locked_relids = NIL; foreach(lc, estate->es_part_prune_infos) { @@ -1849,68 +1844,11 @@ ExecDoInitialPruning(EState *estate) else validsubplan_rtis = all_leafpart_rtis; - if (ExecShouldLockRelations(estate)) - { - int rtindex = -1; - - while ((rtindex = bms_next_member(validsubplan_rtis, - rtindex)) >= 0) - { - RangeTblEntry *rte = exec_rt_fetch(rtindex, estate); - - Assert(rte->rtekind == RTE_RELATION && - rte->rellockmode != NoLock); - LockRelationOid(rte->relid, rte->rellockmode); - locked_relids = lappend_int(locked_relids, rtindex); - } - } estate->es_unpruned_relids = bms_add_members(estate->es_unpruned_relids, validsubplan_rtis); estate->es_part_prune_results = lappend(estate->es_part_prune_results, validsubplans); } - - /* - * Lock the first result relation of each ModifyTable node, even if it was - * pruned. This is required for ExecInitModifyTable(), which keeps its - * first result relation if all other result relations have been pruned, - * because some executor paths (e.g., in nodeModifyTable.c and - * execPartition.c) rely on there being at least one result relation. - * - * There's room for improvement here --- we actually only need to do this - * if all other result relations of the ModifyTable node were pruned, but - * we don't have an easy way to tell that here. - */ - if (stmt->resultRelations && ExecShouldLockRelations(estate)) - { - foreach(lc, stmt->firstResultRels) - { - Index firstResultRel = lfirst_int(lc); - - if (!bms_is_member(firstResultRel, estate->es_unpruned_relids)) - { - RangeTblEntry *rte = exec_rt_fetch(firstResultRel, estate); - - Assert(rte->rtekind == RTE_RELATION && rte->rellockmode != NoLock); - LockRelationOid(rte->relid, rte->rellockmode); - locked_relids = lappend_int(locked_relids, firstResultRel); - } - } - } - - /* - * Release the useless locks if the plan won't be executed. This is the - * same as what CheckCachedPlan() in plancache.c does. - */ - if (!ExecPlanStillValid(estate)) - { - foreach(lc, locked_relids) - { - RangeTblEntry *rte = exec_rt_fetch(lfirst_int(lc), estate); - - UnlockRelationOid(rte->relid, rte->rellockmode); - } - } } /* diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 772c86e70e9..fdc65c2b42b 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -147,7 +147,6 @@ CreateExecutorState(void) estate->es_top_eflags = 0; estate->es_instrument = 0; estate->es_finished = false; - estate->es_aborted = false; estate->es_exprcontexts = NIL; diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index 8d4d062d579..359aafea681 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -34,6 +34,7 @@ #include "utils/funccache.h" #include "utils/lsyscache.h" #include "utils/memutils.h" +#include "utils/plancache.h" #include "utils/snapmgr.h" #include "utils/syscache.h" @@ -1338,7 +1339,6 @@ postquel_start(execution_state *es, SQLFunctionCachePtr fcache) dest = None_Receiver; es->qd = CreateQueryDesc(es->stmt, - NULL, fcache->func->src, GetActiveSnapshot(), InvalidSnapshot, @@ -1363,8 +1363,7 @@ postquel_start(execution_state *es, SQLFunctionCachePtr fcache) eflags = EXEC_FLAG_SKIP_TRIGGERS; else eflags = 0; /* default run-to-completion flags */ - if (!ExecutorStart(es->qd, eflags)) - elog(ERROR, "ExecutorStart() failed unexpectedly"); + ExecutorStart(es->qd, eflags); } es->status = F_EXEC_RUN; diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 46d533b7288..54da8e7995b 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -64,6 +64,7 @@ #include "nodes/nodeFuncs.h" #include "optimizer/optimizer.h" #include "rewrite/rewriteHandler.h" +#include "rewrite/rewriteManip.h" #include "storage/lmgr.h" #include "utils/builtins.h" #include "utils/datum.h" @@ -3735,6 +3736,7 @@ ExecInitMerge(ModifyTableState *mtstate, EState *estate) switch (action->commandType) { case CMD_INSERT: + /* INSERT actions always use rootRelInfo */ ExecCheckPlanOutput(rootRelInfo->ri_RelationDesc, action->targetList); @@ -3774,9 +3776,23 @@ ExecInitMerge(ModifyTableState *mtstate, EState *estate) } else { - /* not partitioned? use the stock relation and slot */ - tgtslot = resultRelInfo->ri_newTupleSlot; - tgtdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc); + /* + * If the MERGE targets an inherited table, we insert + * into the root table, so we must initialize its + * "new" tuple slot, if not already done, and use its + * relation descriptor for the projection. + * + * For non-inherited tables, rootRelInfo and + * resultRelInfo are the same, and the "new" tuple + * slot will already have been initialized. + */ + if (rootRelInfo->ri_newTupleSlot == NULL) + rootRelInfo->ri_newTupleSlot = + table_slot_create(rootRelInfo->ri_RelationDesc, + &estate->es_tupleTable); + + tgtslot = rootRelInfo->ri_newTupleSlot; + tgtdesc = RelationGetDescr(rootRelInfo->ri_RelationDesc); } action_state->mas_proj = @@ -3809,6 +3825,114 @@ ExecInitMerge(ModifyTableState *mtstate, EState *estate) } } } + + /* + * If the MERGE targets an inherited table, any INSERT actions will use + * rootRelInfo, and rootRelInfo will not be in the resultRelInfo array. + * Therefore we must initialize its WITH CHECK OPTION constraints and + * RETURNING projection, as ExecInitModifyTable did for the resultRelInfo + * entries. + * + * Note that the planner does not build a withCheckOptionList or + * returningList for the root relation, but as in ExecInitPartitionInfo, + * we can use the first resultRelInfo entry as a reference to calculate + * the attno's for the root table. + */ + if (rootRelInfo != mtstate->resultRelInfo && + rootRelInfo->ri_RelationDesc->rd_rel->relkind != RELKIND_PARTITIONED_TABLE && + (mtstate->mt_merge_subcommands & MERGE_INSERT) != 0) + { + ModifyTable *node = (ModifyTable *) mtstate->ps.plan; + Relation rootRelation = rootRelInfo->ri_RelationDesc; + Relation firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc; + int firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex; + AttrMap *part_attmap = NULL; + bool found_whole_row; + + if (node->withCheckOptionLists != NIL) + { + List *wcoList; + List *wcoExprs = NIL; + + /* There should be as many WCO lists as result rels */ + Assert(list_length(node->withCheckOptionLists) == + list_length(node->resultRelations)); + + /* + * Use the first WCO list as a reference. In the most common case, + * this will be for the same relation as rootRelInfo, and so there + * will be no need to adjust its attno's. + */ + wcoList = linitial(node->withCheckOptionLists); + if (rootRelation != firstResultRel) + { + /* Convert any Vars in it to contain the root's attno's */ + part_attmap = + build_attrmap_by_name(RelationGetDescr(rootRelation), + RelationGetDescr(firstResultRel), + false); + + wcoList = (List *) + map_variable_attnos((Node *) wcoList, + firstVarno, 0, + part_attmap, + RelationGetForm(rootRelation)->reltype, + &found_whole_row); + } + + foreach(lc, wcoList) + { + WithCheckOption *wco = lfirst_node(WithCheckOption, lc); + ExprState *wcoExpr = ExecInitQual(castNode(List, wco->qual), + &mtstate->ps); + + wcoExprs = lappend(wcoExprs, wcoExpr); + } + + rootRelInfo->ri_WithCheckOptions = wcoList; + rootRelInfo->ri_WithCheckOptionExprs = wcoExprs; + } + + if (node->returningLists != NIL) + { + List *returningList; + + /* There should be as many returning lists as result rels */ + Assert(list_length(node->returningLists) == + list_length(node->resultRelations)); + + /* + * Use the first returning list as a reference. In the most common + * case, this will be for the same relation as rootRelInfo, and so + * there will be no need to adjust its attno's. + */ + returningList = linitial(node->returningLists); + if (rootRelation != firstResultRel) + { + /* Convert any Vars in it to contain the root's attno's */ + if (part_attmap == NULL) + part_attmap = + build_attrmap_by_name(RelationGetDescr(rootRelation), + RelationGetDescr(firstResultRel), + false); + + returningList = (List *) + map_variable_attnos((Node *) returningList, + firstVarno, 0, + part_attmap, + RelationGetForm(rootRelation)->reltype, + &found_whole_row); + } + rootRelInfo->ri_returningList = returningList; + + /* Initialize the RETURNING projection */ + rootRelInfo->ri_projectReturning = + ExecBuildProjectionInfo(returningList, econtext, + mtstate->ps.ps_ResultTupleSlot, + &mtstate->ps, + RelationGetDescr(rootRelation)); + } + } } /* @@ -4830,12 +4954,11 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) ExprContext *econtext; /* - * Initialize result tuple slot and assign its rowtype using the first - * RETURNING list. We assume the rest will look the same. + * Initialize result tuple slot and assign its rowtype using the plan + * node's declared targetlist, which the planner set up to be the same + * as the first (before runtime pruning) RETURNING list. We assume + * all the result rels will produce compatible output. */ - mtstate->ps.plan->targetlist = (List *) linitial(returningLists); - - /* Set up a slot for the output of the RETURNING projection(s) */ ExecInitResultTupleSlotTL(&mtstate->ps, &TTSOpsVirtual); slot = mtstate->ps.ps_ResultTupleSlot; @@ -4865,7 +4988,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) * We still must construct a dummy result tuple type, because InitPlan * expects one (maybe should change that?). */ - mtstate->ps.plan->targetlist = NIL; ExecInitResultTypeTL(&mtstate->ps); mtstate->ps.ps_ExprContext = NULL; diff --git a/src/backend/executor/nodeTidrangescan.c b/src/backend/executor/nodeTidrangescan.c index ab2eab9596e..26f7420b64b 100644 --- a/src/backend/executor/nodeTidrangescan.c +++ b/src/backend/executor/nodeTidrangescan.c @@ -128,9 +128,11 @@ TidExprListCreate(TidRangeScanState *tidrangestate) * TidRangeEval * * Compute and set node's block and offset range to scan by evaluating - * the trss_tidexprs. Returns false if we detect the range cannot + * node->trss_tidexprs. Returns false if we detect the range cannot * contain any tuples. Returns true if it's possible for the range to - * contain tuples. + * contain tuples. We don't bother validating that trss_mintid is less + * than or equal to trss_maxtid, as the scan_set_tidrange() table AM + * function will handle that. * ---------------------------------------------------------------- */ static bool diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index 3288396def3..ecb2e4ccaa1 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -70,8 +70,7 @@ static int _SPI_execute_plan(SPIPlanPtr plan, const SPIExecuteOptions *options, static ParamListInfo _SPI_convert_params(int nargs, Oid *argtypes, Datum *Values, const char *Nulls); -static int _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, uint64 tcount, - CachedPlanSource *plansource, int query_index); +static int _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, uint64 tcount); static void _SPI_error_callback(void *arg); @@ -1686,8 +1685,7 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan, query_string, plansource->commandTag, stmt_list, - cplan, - plansource); + cplan); /* * Set up options for portal. Default SCROLL type is chosen the same way @@ -2502,7 +2500,6 @@ _SPI_execute_plan(SPIPlanPtr plan, const SPIExecuteOptions *options, CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc1); List *stmt_list; ListCell *lc2; - int query_index = 0; spicallbackarg.query = plansource->query_string; @@ -2693,16 +2690,14 @@ _SPI_execute_plan(SPIPlanPtr plan, const SPIExecuteOptions *options, snap = InvalidSnapshot; qdesc = CreateQueryDesc(stmt, - cplan, plansource->query_string, snap, crosscheck_snapshot, dest, options->params, _SPI_current->queryEnv, 0); - - res = _SPI_pquery(qdesc, fire_triggers, canSetTag ? options->tcount : 0, - plansource, query_index); + res = _SPI_pquery(qdesc, fire_triggers, + canSetTag ? options->tcount : 0); FreeQueryDesc(qdesc); } else @@ -2799,8 +2794,6 @@ _SPI_execute_plan(SPIPlanPtr plan, const SPIExecuteOptions *options, my_res = res; goto fail; } - - query_index++; } /* Done with this plan, so release refcount */ @@ -2878,8 +2871,7 @@ _SPI_convert_params(int nargs, Oid *argtypes, } static int -_SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, uint64 tcount, - CachedPlanSource *plansource, int query_index) +_SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, uint64 tcount) { int operation = queryDesc->operation; int eflags; @@ -2935,16 +2927,7 @@ _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, uint64 tcount, else eflags = EXEC_FLAG_SKIP_TRIGGERS; - if (queryDesc->cplan) - { - ExecutorStartCachedPlan(queryDesc, eflags, plansource, query_index); - Assert(queryDesc->planstate); - } - else - { - if (!ExecutorStart(queryDesc, eflags)) - elog(ERROR, "ExecutorStart() failed unexpectedly"); - } + ExecutorStart(queryDesc, eflags); ExecutorRun(queryDesc, ForwardScanDirection, tcount); |