diff options
Diffstat (limited to 'src/backend/executor')
-rw-r--r-- | src/backend/executor/execMain.c | 2 | ||||
-rw-r--r-- | src/backend/executor/execPartition.c | 31 | ||||
-rw-r--r-- | src/backend/executor/execUtils.c | 18 | ||||
-rw-r--r-- | src/backend/executor/nodeModifyTable.c | 32 |
4 files changed, 70 insertions, 13 deletions
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 0493b7d5365..e9bd98c7738 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1006,7 +1006,7 @@ InitPlan(QueryDesc *queryDesc, int eflags) case ROW_MARK_SHARE: case ROW_MARK_KEYSHARE: case ROW_MARK_REFERENCE: - relation = ExecGetRangeTableRelation(estate, rc->rti); + relation = ExecGetRangeTableRelation(estate, rc->rti, false); break; case ROW_MARK_COPY: /* no physical table access is required */ diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 5cd5e2eeb80..84ccd7d457d 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -1819,6 +1819,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; @@ -1868,6 +1869,34 @@ ExecDoInitialPruning(EState *estate) } /* + * 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. */ @@ -2076,7 +2105,7 @@ CreatePartitionPruneState(EState *estate, PartitionPruneInfo *pruneinfo, * because that entry will be held open and locked for the * duration of this executor run. */ - partrel = ExecGetRangeTableRelation(estate, pinfo->rtindex); + partrel = ExecGetRangeTableRelation(estate, pinfo->rtindex, false); /* Remember for InitExecPartitionPruneContext(). */ pprune->partrel = partrel; diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 39d6f4d819e..55ab18fb826 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -746,7 +746,7 @@ ExecOpenScanRelation(EState *estate, Index scanrelid, int eflags) Relation rel; /* Open the relation. */ - rel = ExecGetRangeTableRelation(estate, scanrelid); + rel = ExecGetRangeTableRelation(estate, scanrelid, false); /* * Complain if we're attempting a scan of an unscannable relation, except @@ -815,18 +815,22 @@ ExecInitRangeTable(EState *estate, List *rangeTable, List *permInfos, * * The Relations will be closed in ExecEndPlan(). * - * Note: The caller must ensure that 'rti' refers to an unpruned relation - * (i.e., it is a member of estate->es_unpruned_relids) before calling this - * function. Attempting to open a pruned relation will result in an error. + * If isResultRel is true, the relation is being used as a result relation. + * Such a relation might have been pruned, which is OK for result relations, + * but not for scan relations; see the details in ExecInitModifyTable(). If + * isResultRel is false, the caller must ensure that 'rti' refers to an + * unpruned relation (i.e., it is a member of estate->es_unpruned_relids) + * before calling this function. Attempting to open a pruned relation for + * scanning will result in an error. */ Relation -ExecGetRangeTableRelation(EState *estate, Index rti) +ExecGetRangeTableRelation(EState *estate, Index rti, bool isResultRel) { Relation rel; Assert(rti > 0 && rti <= estate->es_range_table_size); - if (!bms_is_member(rti, estate->es_unpruned_relids)) + if (!isResultRel && !bms_is_member(rti, estate->es_unpruned_relids)) elog(ERROR, "trying to open a pruned relation"); rel = estate->es_relations[rti - 1]; @@ -880,7 +884,7 @@ ExecInitResultRelation(EState *estate, ResultRelInfo *resultRelInfo, { Relation resultRelationDesc; - resultRelationDesc = ExecGetRangeTableRelation(estate, rti); + resultRelationDesc = ExecGetRangeTableRelation(estate, rti, true); InitResultRelInfo(resultRelInfo, resultRelationDesc, rti, diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index b0fe50075ad..87c820276a8 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -4471,6 +4471,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) ModifyTableState *mtstate; Plan *subplan = outerPlan(node); CmdType operation = node->operation; + int total_nrels = list_length(node->resultRelations); int nrels; List *resultRelations = NIL; List *withCheckOptionLists = NIL; @@ -4490,13 +4491,35 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) /* * Only consider unpruned relations for initializing their ResultRelInfo * struct and other fields such as withCheckOptions, etc. + * + * Note: We must avoid pruning every result relation. This is important + * for MERGE, since even if every result relation is pruned from the + * subplan, there might still be NOT MATCHED rows, for which there may be + * INSERT actions to perform. To allow these actions to be found, at + * least one result relation must be kept. Also, when inserting into a + * partitioned table, ExecInitPartitionInfo() needs a ResultRelInfo struct + * as a reference for building the ResultRelInfo of the target partition. + * In either case, it doesn't matter which result relation is kept, so we + * just keep the first one, if all others have been pruned. See also, + * ExecDoInitialPruning(), which ensures that this first result relation + * has been locked. */ i = 0; foreach(l, node->resultRelations) { Index rti = lfirst_int(l); + bool keep_rel; + + keep_rel = bms_is_member(rti, estate->es_unpruned_relids); + if (!keep_rel && i == total_nrels - 1 && resultRelations == NIL) + { + /* all result relations pruned; keep the first one */ + keep_rel = true; + rti = linitial_int(node->resultRelations); + i = 0; + } - if (bms_is_member(rti, estate->es_unpruned_relids)) + if (keep_rel) { resultRelations = lappend_int(resultRelations, rti); if (node->withCheckOptionLists) @@ -4537,6 +4560,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) i++; } nrels = list_length(resultRelations); + Assert(nrels > 0); /* * create state structure @@ -4735,7 +4759,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) */ mtstate->mt_resultOidAttno = ExecFindJunkAttributeInTlist(subplan->targetlist, "tableoid"); - Assert(AttributeNumberIsValid(mtstate->mt_resultOidAttno) || nrels == 1); + Assert(AttributeNumberIsValid(mtstate->mt_resultOidAttno) || total_nrels == 1); mtstate->mt_lastResultOid = InvalidOid; /* force lookup at first tuple */ mtstate->mt_lastResultIndex = 0; /* must be zero if no such attr */ @@ -4832,7 +4856,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) if (node->onConflictAction != ONCONFLICT_NONE) { /* insert may only have one relation, inheritance is not expanded */ - Assert(nrels == 1); + Assert(total_nrels == 1); resultRelInfo->ri_onConflictArbiterIndexes = node->arbiterIndexes; } @@ -4979,7 +5003,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) if (operation == CMD_INSERT) { /* insert may only have one relation, inheritance is not expanded */ - Assert(nrels == 1); + Assert(total_nrels == 1); resultRelInfo = mtstate->resultRelInfo; if (!resultRelInfo->ri_usesFdwDirectModify && resultRelInfo->ri_FdwRoutine != NULL && |