Create ResultRelInfos later in InitPlan, index them by RT index.

Instead of allocating all the ResultRelInfos upfront in one big array,
allocate them in ExecInitModifyTable(). es_result_relations is now an
array of ResultRelInfo pointers, rather than an array of structs, and it
is indexed by the RT index.

This simplifies things: we get rid of the separate concept of a "result
rel index", and don't need to set it in setrefs.c anymore. This also
allows follow-up optimizations (not included in this commit yet) to skip
initializing ResultRelInfos for target relations that were not needed at
runtime, and removal of the es_result_relation_info pointer.

The EState arrays of regular result rels and root result rels are merged
into one array. Similarly, the resultRelations and rootResultRelations
lists in PlannedStmt are merged into one. It's not actually clear to me
why they were kept separate in the first place, but now that the
es_result_relations array is indexed by RT index, it certainly seems
pointless.

The PlannedStmt->resultRelations list is now only needed for
ExecRelationIsTargetRelation(). One visible effect of this change is that
ExecRelationIsTargetRelation() will now return 'true' also for the
partition root, if a partitioned table is updated. That seems like a good
thing, although the function isn't used in core code, and I don't see any
reason for an FDW to call it on a partition root.

Author: Amit Langote
Discussion: https://www.postgresql.org/message-id/CA%2BHiwqGEmiib8FLiHMhKB%2BCH5dRgHSLc5N5wnvc4kym%2BZYpQEQ%40mail.gmail.com
This commit is contained in:
Heikki Linnakangas 2020-10-13 12:57:02 +03:00
parent 2050832d0d
commit 1375422c78
19 changed files with 184 additions and 303 deletions

View File

@ -2727,6 +2727,7 @@ CopyFrom(CopyState cstate)
bool leafpart_use_multi_insert = false; bool leafpart_use_multi_insert = false;
Assert(cstate->rel); Assert(cstate->rel);
Assert(list_length(cstate->range_table) == 1);
/* /*
* The target must be a plain, foreign, or partitioned relation, or have * The target must be a plain, foreign, or partitioned relation, or have
@ -2829,25 +2830,17 @@ CopyFrom(CopyState cstate)
* index-entry-making machinery. (There used to be a huge amount of code * index-entry-making machinery. (There used to be a huge amount of code
* here that basically duplicated execUtils.c ...) * here that basically duplicated execUtils.c ...)
*/ */
resultRelInfo = makeNode(ResultRelInfo); ExecInitRangeTable(estate, cstate->range_table);
InitResultRelInfo(resultRelInfo, resultRelInfo = target_resultRelInfo = makeNode(ResultRelInfo);
cstate->rel, ExecInitResultRelation(estate, resultRelInfo, 1);
1, /* must match rel's position in range_table */
NULL,
0);
target_resultRelInfo = resultRelInfo;
/* Verify the named relation is a valid target for INSERT */ /* Verify the named relation is a valid target for INSERT */
CheckValidResultRel(resultRelInfo, CMD_INSERT); CheckValidResultRel(resultRelInfo, CMD_INSERT);
ExecOpenIndices(resultRelInfo, false); ExecOpenIndices(resultRelInfo, false);
estate->es_result_relations = resultRelInfo;
estate->es_num_result_relations = 1;
estate->es_result_relation_info = resultRelInfo; estate->es_result_relation_info = resultRelInfo;
ExecInitRangeTable(estate, cstate->range_table);
/* /*
* Set up a ModifyTableState so we can let FDW(s) init themselves for * Set up a ModifyTableState so we can let FDW(s) init themselves for
* foreign-table result relation(s). * foreign-table result relation(s).
@ -2856,7 +2849,7 @@ CopyFrom(CopyState cstate)
mtstate->ps.plan = NULL; mtstate->ps.plan = NULL;
mtstate->ps.state = estate; mtstate->ps.state = estate;
mtstate->operation = CMD_INSERT; mtstate->operation = CMD_INSERT;
mtstate->resultRelInfo = estate->es_result_relations; mtstate->resultRelInfo = resultRelInfo;
if (resultRelInfo->ri_FdwRoutine != NULL && if (resultRelInfo->ri_FdwRoutine != NULL &&
resultRelInfo->ri_FdwRoutine->BeginForeignInsert != NULL) resultRelInfo->ri_FdwRoutine->BeginForeignInsert != NULL)
@ -3359,14 +3352,13 @@ CopyFrom(CopyState cstate)
if (insertMethod != CIM_SINGLE) if (insertMethod != CIM_SINGLE)
CopyMultiInsertInfoCleanup(&multiInsertInfo); CopyMultiInsertInfoCleanup(&multiInsertInfo);
ExecCloseIndices(target_resultRelInfo);
/* Close all the partitioned tables, leaf partitions, and their indices */ /* Close all the partitioned tables, leaf partitions, and their indices */
if (proute) if (proute)
ExecCleanupTupleRouting(mtstate, proute); ExecCleanupTupleRouting(mtstate, proute);
/* Close any trigger target relations */ /* Close the result relations, including any trigger target relations */
ExecCleanUpTriggerState(estate); ExecCloseResultRelations(estate);
ExecCloseRangeTableRelations(estate);
FreeExecutorState(estate); FreeExecutorState(estate);

View File

@ -769,27 +769,24 @@ ExplainPrintTriggers(ExplainState *es, QueryDesc *queryDesc)
{ {
ResultRelInfo *rInfo; ResultRelInfo *rInfo;
bool show_relname; bool show_relname;
int numrels = queryDesc->estate->es_num_result_relations; List *resultrels;
int numrootrels = queryDesc->estate->es_num_root_result_relations;
List *routerels; List *routerels;
List *targrels; List *targrels;
int nr;
ListCell *l; ListCell *l;
resultrels = queryDesc->estate->es_opened_result_relations;
routerels = queryDesc->estate->es_tuple_routing_result_relations; routerels = queryDesc->estate->es_tuple_routing_result_relations;
targrels = queryDesc->estate->es_trig_target_relations; targrels = queryDesc->estate->es_trig_target_relations;
ExplainOpenGroup("Triggers", "Triggers", false, es); ExplainOpenGroup("Triggers", "Triggers", false, es);
show_relname = (numrels > 1 || numrootrels > 0 || show_relname = (list_length(resultrels) > 1 ||
routerels != NIL || targrels != NIL); routerels != NIL || targrels != NIL);
rInfo = queryDesc->estate->es_result_relations; foreach(l, resultrels)
for (nr = 0; nr < numrels; rInfo++, nr++) {
report_triggers(rInfo, show_relname, es); rInfo = (ResultRelInfo *) lfirst(l);
rInfo = queryDesc->estate->es_root_result_relations;
for (nr = 0; nr < numrootrels; rInfo++, nr++)
report_triggers(rInfo, show_relname, es); report_triggers(rInfo, show_relname, es);
}
foreach(l, routerels) foreach(l, routerels)
{ {

View File

@ -1787,6 +1787,11 @@ ExecuteTruncateGuts(List *explicit_rels, List *relids, List *relids_logged,
/* /*
* To fire triggers, we'll need an EState as well as a ResultRelInfo for * To fire triggers, we'll need an EState as well as a ResultRelInfo for
* each relation. We don't need to call ExecOpenIndices, though. * each relation. We don't need to call ExecOpenIndices, though.
*
* We put the ResultRelInfos in the es_opened_result_relations list, even
* though we don't have a range table and don't populate the
* es_result_relations array. That's a big bogus, but it's enough to make
* ExecGetTriggerResultRel() find them.
*/ */
estate = CreateExecutorState(); estate = CreateExecutorState();
resultRelInfos = (ResultRelInfo *) resultRelInfos = (ResultRelInfo *)
@ -1801,10 +1806,10 @@ ExecuteTruncateGuts(List *explicit_rels, List *relids, List *relids_logged,
0, /* dummy rangetable index */ 0, /* dummy rangetable index */
NULL, NULL,
0); 0);
estate->es_opened_result_relations =
lappend(estate->es_opened_result_relations, resultRelInfo);
resultRelInfo++; resultRelInfo++;
} }
estate->es_result_relations = resultRelInfos;
estate->es_num_result_relations = list_length(rels);
/* /*
* Process all BEFORE STATEMENT TRUNCATE triggers before we begin * Process all BEFORE STATEMENT TRUNCATE triggers before we begin

View File

@ -4227,7 +4227,7 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events,
if (local_estate) if (local_estate)
{ {
ExecCleanUpTriggerState(estate); ExecCloseResultRelations(estate);
ExecResetTupleTable(estate->es_tupleTable, false); ExecResetTupleTable(estate->es_tupleTable, false);
FreeExecutorState(estate); FreeExecutorState(estate);
} }

View File

@ -827,86 +827,8 @@ InitPlan(QueryDesc *queryDesc, int eflags)
estate->es_plannedstmt = plannedstmt; estate->es_plannedstmt = plannedstmt;
/* /* es_result_relation_info is NULL except when within ModifyTable */
* Initialize ResultRelInfo data structures, and open the result rels. estate->es_result_relation_info = NULL;
*/
if (plannedstmt->resultRelations)
{
List *resultRelations = plannedstmt->resultRelations;
int numResultRelations = list_length(resultRelations);
ResultRelInfo *resultRelInfos;
ResultRelInfo *resultRelInfo;
resultRelInfos = (ResultRelInfo *)
palloc(numResultRelations * sizeof(ResultRelInfo));
resultRelInfo = resultRelInfos;
foreach(l, resultRelations)
{
Index resultRelationIndex = lfirst_int(l);
Relation resultRelation;
resultRelation = ExecGetRangeTableRelation(estate,
resultRelationIndex);
InitResultRelInfo(resultRelInfo,
resultRelation,
resultRelationIndex,
NULL,
estate->es_instrument);
resultRelInfo++;
}
estate->es_result_relations = resultRelInfos;
estate->es_num_result_relations = numResultRelations;
/* es_result_relation_info is NULL except when within ModifyTable */
estate->es_result_relation_info = NULL;
/*
* In the partitioned result relation case, also build ResultRelInfos
* for all the partitioned table roots, because we will need them to
* fire statement-level triggers, if any.
*/
if (plannedstmt->rootResultRelations)
{
int num_roots = list_length(plannedstmt->rootResultRelations);
resultRelInfos = (ResultRelInfo *)
palloc(num_roots * sizeof(ResultRelInfo));
resultRelInfo = resultRelInfos;
foreach(l, plannedstmt->rootResultRelations)
{
Index resultRelIndex = lfirst_int(l);
Relation resultRelDesc;
resultRelDesc = ExecGetRangeTableRelation(estate,
resultRelIndex);
InitResultRelInfo(resultRelInfo,
resultRelDesc,
resultRelIndex,
NULL,
estate->es_instrument);
resultRelInfo++;
}
estate->es_root_result_relations = resultRelInfos;
estate->es_num_root_result_relations = num_roots;
}
else
{
estate->es_root_result_relations = NULL;
estate->es_num_root_result_relations = 0;
}
}
else
{
/*
* if no result relation, then set state appropriately
*/
estate->es_result_relations = NULL;
estate->es_num_result_relations = 0;
estate->es_result_relation_info = NULL;
estate->es_root_result_relations = NULL;
estate->es_num_root_result_relations = 0;
}
/* /*
* Next, build the ExecRowMark array from the PlanRowMark(s), if any. * Next, build the ExecRowMark array from the PlanRowMark(s), if any.
@ -1334,8 +1256,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
* *
* Most of the time, triggers are fired on one of the result relations of the * Most of the time, triggers are fired on one of the result relations of the
* query, and so we can just return a member of the es_result_relations array, * query, and so we can just return a member of the es_result_relations array,
* or the es_root_result_relations array (if any), or the * or the es_tuple_routing_result_relations list (if any). (Note: in self-join
* es_tuple_routing_result_relations list (if any). (Note: in self-join
* situations there might be multiple members with the same OID; if so it * situations there might be multiple members with the same OID; if so it
* doesn't matter which one we pick.) * doesn't matter which one we pick.)
* *
@ -1352,30 +1273,16 @@ ResultRelInfo *
ExecGetTriggerResultRel(EState *estate, Oid relid) ExecGetTriggerResultRel(EState *estate, Oid relid)
{ {
ResultRelInfo *rInfo; ResultRelInfo *rInfo;
int nr;
ListCell *l; ListCell *l;
Relation rel; Relation rel;
MemoryContext oldcontext; MemoryContext oldcontext;
/* First, search through the query result relations */ /* First, search through the query result relations */
rInfo = estate->es_result_relations; foreach(l, estate->es_opened_result_relations)
nr = estate->es_num_result_relations;
while (nr > 0)
{ {
rInfo = lfirst(l);
if (RelationGetRelid(rInfo->ri_RelationDesc) == relid) if (RelationGetRelid(rInfo->ri_RelationDesc) == relid)
return rInfo; return rInfo;
rInfo++;
nr--;
}
/* Second, search through the root result relations, if any */
rInfo = estate->es_root_result_relations;
nr = estate->es_num_root_result_relations;
while (nr > 0)
{
if (RelationGetRelid(rInfo->ri_RelationDesc) == relid)
return rInfo;
rInfo++;
nr--;
} }
/* /*
@ -1428,35 +1335,6 @@ ExecGetTriggerResultRel(EState *estate, Oid relid)
return rInfo; return rInfo;
} }
/*
* Close any relations that have been opened by ExecGetTriggerResultRel().
*/
void
ExecCleanUpTriggerState(EState *estate)
{
ListCell *l;
foreach(l, estate->es_trig_target_relations)
{
ResultRelInfo *resultRelInfo = (ResultRelInfo *) lfirst(l);
/*
* Assert this is a "dummy" ResultRelInfo, see above. Otherwise we
* might be issuing a duplicate close against a Relation opened by
* ExecGetRangeTableRelation.
*/
Assert(resultRelInfo->ri_RangeTableIndex == 0);
/*
* Since ExecGetTriggerResultRel doesn't call ExecOpenIndices for
* these rels, we needn't call ExecCloseIndices either.
*/
Assert(resultRelInfo->ri_NumIndices == 0);
table_close(resultRelInfo->ri_RelationDesc, NoLock);
}
}
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* ExecPostprocessPlan * ExecPostprocessPlan
* *
@ -1512,9 +1390,6 @@ ExecPostprocessPlan(EState *estate)
static void static void
ExecEndPlan(PlanState *planstate, EState *estate) ExecEndPlan(PlanState *planstate, EState *estate)
{ {
ResultRelInfo *resultRelInfo;
Index num_relations;
Index i;
ListCell *l; ListCell *l;
/* /*
@ -1541,29 +1416,69 @@ ExecEndPlan(PlanState *planstate, EState *estate)
ExecResetTupleTable(estate->es_tupleTable, false); ExecResetTupleTable(estate->es_tupleTable, false);
/* /*
* close indexes of result relation(s) if any. (Rels themselves get * Close any Relations that have been opened for range table entries or
* closed next.) * result relations.
*/ */
resultRelInfo = estate->es_result_relations; ExecCloseResultRelations(estate);
for (i = estate->es_num_result_relations; i > 0; i--) ExecCloseRangeTableRelations(estate);
{ }
ExecCloseIndices(resultRelInfo);
resultRelInfo++; /*
} * Close any relations that have been opened for ResultRelInfos.
*/
void
ExecCloseResultRelations(EState *estate)
{
ListCell *l;
/* /*
* close whatever rangetable Relations have been opened. We do not * close indexes of result relation(s) if any. (Rels themselves are
* release any locks we might hold on those rels. * closed in ExecCloseRangeTableRelations())
*/ */
num_relations = estate->es_range_table_size; foreach(l, estate->es_opened_result_relations)
for (i = 0; i < num_relations; i++) {
ResultRelInfo *resultRelInfo = lfirst(l);
ExecCloseIndices(resultRelInfo);
}
/* Close any relations that have been opened by ExecGetTriggerResultRel(). */
foreach(l, estate->es_trig_target_relations)
{
ResultRelInfo *resultRelInfo = (ResultRelInfo *) lfirst(l);
/*
* Assert this is a "dummy" ResultRelInfo, see above. Otherwise we
* might be issuing a duplicate close against a Relation opened by
* ExecGetRangeTableRelation.
*/
Assert(resultRelInfo->ri_RangeTableIndex == 0);
/*
* Since ExecGetTriggerResultRel doesn't call ExecOpenIndices for
* these rels, we needn't call ExecCloseIndices either.
*/
Assert(resultRelInfo->ri_NumIndices == 0);
table_close(resultRelInfo->ri_RelationDesc, NoLock);
}
}
/*
* Close all relations opened by ExecGetRangeTableRelation().
*
* We do not release any locks we might hold on those rels.
*/
void
ExecCloseRangeTableRelations(EState *estate)
{
int i;
for (i = 0; i < estate->es_range_table_size; i++)
{ {
if (estate->es_relations[i]) if (estate->es_relations[i])
table_close(estate->es_relations[i], NoLock); table_close(estate->es_relations[i], NoLock);
} }
/* likewise close any trigger target relations */
ExecCleanUpTriggerState(estate);
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
@ -2758,17 +2673,9 @@ EvalPlanQualStart(EPQState *epqstate, Plan *planTree)
/* /*
* Child EPQ EStates share the parent's copy of unchanging state such as * Child EPQ EStates share the parent's copy of unchanging state such as
* the snapshot, rangetable, result-rel info, and external Param info. * the snapshot, rangetable, and external Param info. They need their own
* They need their own copies of local state, including a tuple table, * copies of local state, including a tuple table, es_param_exec_vals,
* es_param_exec_vals, etc. * result-rel info, etc.
*
* The ResultRelInfo array management is trickier than it looks. We
* create fresh arrays for the child but copy all the content from the
* parent. This is because it's okay for the child to share any
* per-relation state the parent has already created --- but if the child
* sets up any ResultRelInfo fields, such as its own junkfilter, that
* state must *not* propagate back to the parent. (For one thing, the
* pointed-to data is in a memory context that won't last long enough.)
*/ */
rcestate->es_direction = ForwardScanDirection; rcestate->es_direction = ForwardScanDirection;
rcestate->es_snapshot = parentestate->es_snapshot; rcestate->es_snapshot = parentestate->es_snapshot;
@ -2781,30 +2688,12 @@ EvalPlanQualStart(EPQState *epqstate, Plan *planTree)
rcestate->es_plannedstmt = parentestate->es_plannedstmt; rcestate->es_plannedstmt = parentestate->es_plannedstmt;
rcestate->es_junkFilter = parentestate->es_junkFilter; rcestate->es_junkFilter = parentestate->es_junkFilter;
rcestate->es_output_cid = parentestate->es_output_cid; rcestate->es_output_cid = parentestate->es_output_cid;
if (parentestate->es_num_result_relations > 0)
{
int numResultRelations = parentestate->es_num_result_relations;
int numRootResultRels = parentestate->es_num_root_result_relations;
ResultRelInfo *resultRelInfos;
resultRelInfos = (ResultRelInfo *) /*
palloc(numResultRelations * sizeof(ResultRelInfo)); * ResultRelInfos needed by subplans are initialized from scratch when the
memcpy(resultRelInfos, parentestate->es_result_relations, * subplans themselves are initialized.
numResultRelations * sizeof(ResultRelInfo)); */
rcestate->es_result_relations = resultRelInfos; parentestate->es_result_relations = NULL;
rcestate->es_num_result_relations = numResultRelations;
/* Also transfer partitioned root result relations. */
if (numRootResultRels > 0)
{
resultRelInfos = (ResultRelInfo *)
palloc(numRootResultRels * sizeof(ResultRelInfo));
memcpy(resultRelInfos, parentestate->es_root_result_relations,
numRootResultRels * sizeof(ResultRelInfo));
rcestate->es_root_result_relations = resultRelInfos;
rcestate->es_num_root_result_relations = numRootResultRels;
}
}
/* es_result_relation_info must NOT be copied */ /* es_result_relation_info must NOT be copied */
/* es_trig_target_relations must NOT be copied */ /* es_trig_target_relations must NOT be copied */
rcestate->es_top_eflags = parentestate->es_top_eflags; rcestate->es_top_eflags = parentestate->es_top_eflags;
@ -2914,8 +2803,9 @@ EvalPlanQualStart(EPQState *epqstate, Plan *planTree)
* This is a cut-down version of ExecutorEnd(); basically we want to do most * This is a cut-down version of ExecutorEnd(); basically we want to do most
* of the normal cleanup, but *not* close result relations (which we are * of the normal cleanup, but *not* close result relations (which we are
* just sharing from the outer query). We do, however, have to close any * just sharing from the outer query). We do, however, have to close any
* trigger target relations that got opened, since those are not shared. * result and trigger target relations that got opened, since those are not
* (There probably shouldn't be any of the latter, but just in case...) * shared. (There probably shouldn't be any of the latter, but just in
* case...)
*/ */
void void
EvalPlanQualEnd(EPQState *epqstate) EvalPlanQualEnd(EPQState *epqstate)
@ -2957,8 +2847,8 @@ EvalPlanQualEnd(EPQState *epqstate)
/* throw away the per-estate tuple table, some node may have used it */ /* throw away the per-estate tuple table, some node may have used it */
ExecResetTupleTable(estate->es_tupleTable, false); ExecResetTupleTable(estate->es_tupleTable, false);
/* close any trigger target relations attached to this EState */ /* Close any result and trigger target relations attached to this EState */
ExecCleanUpTriggerState(estate); ExecCloseResultRelations(estate);
MemoryContextSwitchTo(oldcontext); MemoryContextSwitchTo(oldcontext);

View File

@ -184,7 +184,6 @@ ExecSerializePlan(Plan *plan, EState *estate)
pstmt->planTree = plan; pstmt->planTree = plan;
pstmt->rtable = estate->es_range_table; pstmt->rtable = estate->es_range_table;
pstmt->resultRelations = NIL; pstmt->resultRelations = NIL;
pstmt->rootResultRelations = NIL;
pstmt->appendRelations = NIL; pstmt->appendRelations = NIL;
/* /*

View File

@ -124,14 +124,9 @@ CreateExecutorState(void)
estate->es_output_cid = (CommandId) 0; estate->es_output_cid = (CommandId) 0;
estate->es_result_relations = NULL; estate->es_result_relations = NULL;
estate->es_num_result_relations = 0; estate->es_opened_result_relations = NIL;
estate->es_result_relation_info = NULL; estate->es_result_relation_info = NULL;
estate->es_root_result_relations = NULL;
estate->es_num_root_result_relations = 0;
estate->es_tuple_routing_result_relations = NIL; estate->es_tuple_routing_result_relations = NIL;
estate->es_trig_target_relations = NIL; estate->es_trig_target_relations = NIL;
estate->es_param_list_info = NULL; estate->es_param_list_info = NULL;
@ -711,16 +706,7 @@ ExecCreateScanSlotFromOuterPlan(EState *estate,
bool bool
ExecRelationIsTargetRelation(EState *estate, Index scanrelid) ExecRelationIsTargetRelation(EState *estate, Index scanrelid)
{ {
ResultRelInfo *resultRelInfos; return list_member_int(estate->es_plannedstmt->resultRelations, scanrelid);
int i;
resultRelInfos = estate->es_result_relations;
for (i = 0; i < estate->es_num_result_relations; i++)
{
if (resultRelInfos[i].ri_RangeTableIndex == scanrelid)
return true;
}
return false;
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
@ -779,9 +765,10 @@ ExecInitRangeTable(EState *estate, List *rangeTable)
palloc0(estate->es_range_table_size * sizeof(Relation)); palloc0(estate->es_range_table_size * sizeof(Relation));
/* /*
* es_rowmarks is also parallel to the es_range_table, but it's allocated * es_result_relations and es_rowmarks are also parallel to
* only if needed. * es_range_table, but are allocated only if needed.
*/ */
estate->es_result_relations = NULL;
estate->es_rowmarks = NULL; estate->es_rowmarks = NULL;
} }
@ -835,6 +822,40 @@ ExecGetRangeTableRelation(EState *estate, Index rti)
return rel; return rel;
} }
/*
* ExecInitResultRelation
* Open relation given by the passed-in RT index and fill its
* ResultRelInfo node
*
* Here, we also save the ResultRelInfo in estate->es_result_relations array
* such that it can be accessed later using the RT index.
*/
void
ExecInitResultRelation(EState *estate, ResultRelInfo *resultRelInfo,
Index rti)
{
Relation resultRelationDesc;
resultRelationDesc = ExecGetRangeTableRelation(estate, rti);
InitResultRelInfo(resultRelInfo,
resultRelationDesc,
rti,
NULL,
estate->es_instrument);
if (estate->es_result_relations == NULL)
estate->es_result_relations = (ResultRelInfo **)
palloc0(estate->es_range_table_size * sizeof(ResultRelInfo *));
estate->es_result_relations[rti - 1] = resultRelInfo;
/*
* Saving in the list allows to avoid needlessly traversing the whole
* array when only a few of its entries are possibly non-NULL.
*/
estate->es_opened_result_relations =
lappend(estate->es_opened_result_relations, resultRelInfo);
}
/* /*
* UpdateChangedParamSet * UpdateChangedParamSet
* Add changed parameters to a plan node's chgParam set * Add changed parameters to a plan node's chgParam set

View File

@ -2301,7 +2301,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
ResultRelInfo *saved_resultRelInfo; ResultRelInfo *saved_resultRelInfo;
ResultRelInfo *resultRelInfo; ResultRelInfo *resultRelInfo;
Plan *subplan; Plan *subplan;
ListCell *l; ListCell *l,
*l1;
int i; int i;
Relation rel; Relation rel;
bool update_tuple_routing_needed = node->partColsUpdated; bool update_tuple_routing_needed = node->partColsUpdated;
@ -2322,13 +2323,17 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
mtstate->mt_done = false; mtstate->mt_done = false;
mtstate->mt_plans = (PlanState **) palloc0(sizeof(PlanState *) * nplans); mtstate->mt_plans = (PlanState **) palloc0(sizeof(PlanState *) * nplans);
mtstate->resultRelInfo = estate->es_result_relations + node->resultRelIndex; mtstate->resultRelInfo = (ResultRelInfo *)
palloc(nplans * sizeof(ResultRelInfo));
mtstate->mt_scans = (TupleTableSlot **) palloc0(sizeof(TupleTableSlot *) * nplans); mtstate->mt_scans = (TupleTableSlot **) palloc0(sizeof(TupleTableSlot *) * nplans);
/* If modifying a partitioned table, initialize the root table info */ /* If modifying a partitioned table, initialize the root table info */
if (node->rootResultRelIndex >= 0) if (node->rootRelation > 0)
mtstate->rootResultRelInfo = estate->es_root_result_relations + {
node->rootResultRelIndex; mtstate->rootResultRelInfo = makeNode(ResultRelInfo);
ExecInitResultRelation(estate, mtstate->rootResultRelInfo,
node->rootRelation);
}
mtstate->mt_arowmarks = (List **) palloc0(sizeof(List *) * nplans); mtstate->mt_arowmarks = (List **) palloc0(sizeof(List *) * nplans);
mtstate->mt_nplans = nplans; mtstate->mt_nplans = nplans;
@ -2351,9 +2356,14 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
resultRelInfo = mtstate->resultRelInfo; resultRelInfo = mtstate->resultRelInfo;
i = 0; i = 0;
foreach(l, node->plans) forboth(l, node->resultRelations, l1, node->plans)
{ {
subplan = (Plan *) lfirst(l); Index resultRelation = lfirst_int(l);
subplan = (Plan *) lfirst(l1);
/* This opens the relation and fills ResultRelInfo. */
ExecInitResultRelation(estate, resultRelInfo, resultRelation);
/* Initialize the usesFdwDirectModify flag */ /* Initialize the usesFdwDirectModify flag */
resultRelInfo->ri_usesFdwDirectModify = bms_is_member(i, resultRelInfo->ri_usesFdwDirectModify = bms_is_member(i,

View File

@ -91,7 +91,6 @@ _copyPlannedStmt(const PlannedStmt *from)
COPY_NODE_FIELD(planTree); COPY_NODE_FIELD(planTree);
COPY_NODE_FIELD(rtable); COPY_NODE_FIELD(rtable);
COPY_NODE_FIELD(resultRelations); COPY_NODE_FIELD(resultRelations);
COPY_NODE_FIELD(rootResultRelations);
COPY_NODE_FIELD(appendRelations); COPY_NODE_FIELD(appendRelations);
COPY_NODE_FIELD(subplans); COPY_NODE_FIELD(subplans);
COPY_BITMAPSET_FIELD(rewindPlanIDs); COPY_BITMAPSET_FIELD(rewindPlanIDs);
@ -207,8 +206,6 @@ _copyModifyTable(const ModifyTable *from)
COPY_SCALAR_FIELD(rootRelation); COPY_SCALAR_FIELD(rootRelation);
COPY_SCALAR_FIELD(partColsUpdated); COPY_SCALAR_FIELD(partColsUpdated);
COPY_NODE_FIELD(resultRelations); COPY_NODE_FIELD(resultRelations);
COPY_SCALAR_FIELD(resultRelIndex);
COPY_SCALAR_FIELD(rootResultRelIndex);
COPY_NODE_FIELD(plans); COPY_NODE_FIELD(plans);
COPY_NODE_FIELD(withCheckOptionLists); COPY_NODE_FIELD(withCheckOptionLists);
COPY_NODE_FIELD(returningLists); COPY_NODE_FIELD(returningLists);

View File

@ -309,7 +309,6 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node)
WRITE_NODE_FIELD(planTree); WRITE_NODE_FIELD(planTree);
WRITE_NODE_FIELD(rtable); WRITE_NODE_FIELD(rtable);
WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(resultRelations);
WRITE_NODE_FIELD(rootResultRelations);
WRITE_NODE_FIELD(appendRelations); WRITE_NODE_FIELD(appendRelations);
WRITE_NODE_FIELD(subplans); WRITE_NODE_FIELD(subplans);
WRITE_BITMAPSET_FIELD(rewindPlanIDs); WRITE_BITMAPSET_FIELD(rewindPlanIDs);
@ -408,8 +407,6 @@ _outModifyTable(StringInfo str, const ModifyTable *node)
WRITE_UINT_FIELD(rootRelation); WRITE_UINT_FIELD(rootRelation);
WRITE_BOOL_FIELD(partColsUpdated); WRITE_BOOL_FIELD(partColsUpdated);
WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(resultRelations);
WRITE_INT_FIELD(resultRelIndex);
WRITE_INT_FIELD(rootResultRelIndex);
WRITE_NODE_FIELD(plans); WRITE_NODE_FIELD(plans);
WRITE_NODE_FIELD(withCheckOptionLists); WRITE_NODE_FIELD(withCheckOptionLists);
WRITE_NODE_FIELD(returningLists); WRITE_NODE_FIELD(returningLists);
@ -2194,7 +2191,6 @@ _outPlannerGlobal(StringInfo str, const PlannerGlobal *node)
WRITE_NODE_FIELD(finalrtable); WRITE_NODE_FIELD(finalrtable);
WRITE_NODE_FIELD(finalrowmarks); WRITE_NODE_FIELD(finalrowmarks);
WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(resultRelations);
WRITE_NODE_FIELD(rootResultRelations);
WRITE_NODE_FIELD(appendRelations); WRITE_NODE_FIELD(appendRelations);
WRITE_NODE_FIELD(relationOids); WRITE_NODE_FIELD(relationOids);
WRITE_NODE_FIELD(invalItems); WRITE_NODE_FIELD(invalItems);

View File

@ -1542,7 +1542,6 @@ _readPlannedStmt(void)
READ_NODE_FIELD(planTree); READ_NODE_FIELD(planTree);
READ_NODE_FIELD(rtable); READ_NODE_FIELD(rtable);
READ_NODE_FIELD(resultRelations); READ_NODE_FIELD(resultRelations);
READ_NODE_FIELD(rootResultRelations);
READ_NODE_FIELD(appendRelations); READ_NODE_FIELD(appendRelations);
READ_NODE_FIELD(subplans); READ_NODE_FIELD(subplans);
READ_BITMAPSET_FIELD(rewindPlanIDs); READ_BITMAPSET_FIELD(rewindPlanIDs);
@ -1639,8 +1638,6 @@ _readModifyTable(void)
READ_UINT_FIELD(rootRelation); READ_UINT_FIELD(rootRelation);
READ_BOOL_FIELD(partColsUpdated); READ_BOOL_FIELD(partColsUpdated);
READ_NODE_FIELD(resultRelations); READ_NODE_FIELD(resultRelations);
READ_INT_FIELD(resultRelIndex);
READ_INT_FIELD(rootResultRelIndex);
READ_NODE_FIELD(plans); READ_NODE_FIELD(plans);
READ_NODE_FIELD(withCheckOptionLists); READ_NODE_FIELD(withCheckOptionLists);
READ_NODE_FIELD(returningLists); READ_NODE_FIELD(returningLists);

View File

@ -6808,8 +6808,6 @@ make_modifytable(PlannerInfo *root,
node->rootRelation = rootRelation; node->rootRelation = rootRelation;
node->partColsUpdated = partColsUpdated; node->partColsUpdated = partColsUpdated;
node->resultRelations = resultRelations; node->resultRelations = resultRelations;
node->resultRelIndex = -1; /* will be set correctly in setrefs.c */
node->rootResultRelIndex = -1; /* will be set correctly in setrefs.c */
node->plans = subplans; node->plans = subplans;
if (!onconflict) if (!onconflict)
{ {

View File

@ -305,7 +305,6 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
glob->finalrtable = NIL; glob->finalrtable = NIL;
glob->finalrowmarks = NIL; glob->finalrowmarks = NIL;
glob->resultRelations = NIL; glob->resultRelations = NIL;
glob->rootResultRelations = NIL;
glob->appendRelations = NIL; glob->appendRelations = NIL;
glob->relationOids = NIL; glob->relationOids = NIL;
glob->invalItems = NIL; glob->invalItems = NIL;
@ -493,7 +492,6 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
Assert(glob->finalrtable == NIL); Assert(glob->finalrtable == NIL);
Assert(glob->finalrowmarks == NIL); Assert(glob->finalrowmarks == NIL);
Assert(glob->resultRelations == NIL); Assert(glob->resultRelations == NIL);
Assert(glob->rootResultRelations == NIL);
Assert(glob->appendRelations == NIL); Assert(glob->appendRelations == NIL);
top_plan = set_plan_references(root, top_plan); top_plan = set_plan_references(root, top_plan);
/* ... and the subplans (both regular subplans and initplans) */ /* ... and the subplans (both regular subplans and initplans) */
@ -520,7 +518,6 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
result->planTree = top_plan; result->planTree = top_plan;
result->rtable = glob->finalrtable; result->rtable = glob->finalrtable;
result->resultRelations = glob->resultRelations; result->resultRelations = glob->resultRelations;
result->rootResultRelations = glob->rootResultRelations;
result->appendRelations = glob->appendRelations; result->appendRelations = glob->appendRelations;
result->subplans = glob->subplans; result->subplans = glob->subplans;
result->rewindPlanIDs = glob->rewindPlanIDs; result->rewindPlanIDs = glob->rewindPlanIDs;

View File

@ -975,26 +975,15 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
/* /*
* Append this ModifyTable node's final result relation RT * Append this ModifyTable node's final result relation RT
* index(es) to the global list for the plan, and set its * index(es) to the global list for the plan.
* resultRelIndex to reflect their starting position in the
* global list.
*/ */
splan->resultRelIndex = list_length(root->glob->resultRelations);
root->glob->resultRelations = root->glob->resultRelations =
list_concat(root->glob->resultRelations, list_concat(root->glob->resultRelations,
splan->resultRelations); splan->resultRelations);
/*
* If the main target relation is a partitioned table, also
* add the partition root's RT index to rootResultRelations,
* and remember its index in that list in rootResultRelIndex.
*/
if (splan->rootRelation) if (splan->rootRelation)
{ {
splan->rootResultRelIndex = root->glob->resultRelations =
list_length(root->glob->rootResultRelations); lappend_int(root->glob->resultRelations,
root->glob->rootResultRelations =
lappend_int(root->glob->rootResultRelations,
splan->rootRelation); splan->rootRelation);
} }
} }

View File

@ -344,7 +344,6 @@ static EState *
create_estate_for_relation(LogicalRepRelMapEntry *rel) create_estate_for_relation(LogicalRepRelMapEntry *rel)
{ {
EState *estate; EState *estate;
ResultRelInfo *resultRelInfo;
RangeTblEntry *rte; RangeTblEntry *rte;
estate = CreateExecutorState(); estate = CreateExecutorState();
@ -356,13 +355,6 @@ create_estate_for_relation(LogicalRepRelMapEntry *rel)
rte->rellockmode = AccessShareLock; rte->rellockmode = AccessShareLock;
ExecInitRangeTable(estate, list_make1(rte)); ExecInitRangeTable(estate, list_make1(rte));
resultRelInfo = makeNode(ResultRelInfo);
InitResultRelInfo(resultRelInfo, rel->localrel, 1, NULL, 0);
estate->es_result_relations = resultRelInfo;
estate->es_num_result_relations = 1;
estate->es_result_relation_info = resultRelInfo;
estate->es_output_cid = GetCurrentCommandId(true); estate->es_output_cid = GetCurrentCommandId(true);
/* Prepare to catch AFTER triggers. */ /* Prepare to catch AFTER triggers. */
@ -1150,6 +1142,7 @@ GetRelationIdentityOrPK(Relation rel)
static void static void
apply_handle_insert(StringInfo s) apply_handle_insert(StringInfo s)
{ {
ResultRelInfo *resultRelInfo;
LogicalRepRelMapEntry *rel; LogicalRepRelMapEntry *rel;
LogicalRepTupleData newtup; LogicalRepTupleData newtup;
LogicalRepRelId relid; LogicalRepRelId relid;
@ -1179,6 +1172,9 @@ apply_handle_insert(StringInfo s)
remoteslot = ExecInitExtraTupleSlot(estate, remoteslot = ExecInitExtraTupleSlot(estate,
RelationGetDescr(rel->localrel), RelationGetDescr(rel->localrel),
&TTSOpsVirtual); &TTSOpsVirtual);
resultRelInfo = makeNode(ResultRelInfo);
InitResultRelInfo(resultRelInfo, rel->localrel, 1, NULL, 0);
estate->es_result_relation_info = resultRelInfo;
/* Input functions may need an active snapshot, so get one */ /* Input functions may need an active snapshot, so get one */
PushActiveSnapshot(GetTransactionSnapshot()); PushActiveSnapshot(GetTransactionSnapshot());
@ -1191,10 +1187,10 @@ apply_handle_insert(StringInfo s)
/* For a partitioned table, insert the tuple into a partition. */ /* For a partitioned table, insert the tuple into a partition. */
if (rel->localrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) if (rel->localrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
apply_handle_tuple_routing(estate->es_result_relation_info, estate, apply_handle_tuple_routing(resultRelInfo, estate,
remoteslot, NULL, rel, CMD_INSERT); remoteslot, NULL, rel, CMD_INSERT);
else else
apply_handle_insert_internal(estate->es_result_relation_info, estate, apply_handle_insert_internal(resultRelInfo, estate,
remoteslot); remoteslot);
PopActiveSnapshot(); PopActiveSnapshot();
@ -1265,6 +1261,7 @@ check_relation_updatable(LogicalRepRelMapEntry *rel)
static void static void
apply_handle_update(StringInfo s) apply_handle_update(StringInfo s)
{ {
ResultRelInfo *resultRelInfo;
LogicalRepRelMapEntry *rel; LogicalRepRelMapEntry *rel;
LogicalRepRelId relid; LogicalRepRelId relid;
EState *estate; EState *estate;
@ -1301,6 +1298,9 @@ apply_handle_update(StringInfo s)
remoteslot = ExecInitExtraTupleSlot(estate, remoteslot = ExecInitExtraTupleSlot(estate,
RelationGetDescr(rel->localrel), RelationGetDescr(rel->localrel),
&TTSOpsVirtual); &TTSOpsVirtual);
resultRelInfo = makeNode(ResultRelInfo);
InitResultRelInfo(resultRelInfo, rel->localrel, 1, NULL, 0);
estate->es_result_relation_info = resultRelInfo;
/* /*
* Populate updatedCols so that per-column triggers can fire. This could * Populate updatedCols so that per-column triggers can fire. This could
@ -1337,10 +1337,10 @@ apply_handle_update(StringInfo s)
/* For a partitioned table, apply update to correct partition. */ /* For a partitioned table, apply update to correct partition. */
if (rel->localrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) if (rel->localrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
apply_handle_tuple_routing(estate->es_result_relation_info, estate, apply_handle_tuple_routing(resultRelInfo, estate,
remoteslot, &newtup, rel, CMD_UPDATE); remoteslot, &newtup, rel, CMD_UPDATE);
else else
apply_handle_update_internal(estate->es_result_relation_info, estate, apply_handle_update_internal(resultRelInfo, estate,
remoteslot, &newtup, rel); remoteslot, &newtup, rel);
PopActiveSnapshot(); PopActiveSnapshot();
@ -1420,6 +1420,7 @@ apply_handle_update_internal(ResultRelInfo *relinfo,
static void static void
apply_handle_delete(StringInfo s) apply_handle_delete(StringInfo s)
{ {
ResultRelInfo *resultRelInfo;
LogicalRepRelMapEntry *rel; LogicalRepRelMapEntry *rel;
LogicalRepTupleData oldtup; LogicalRepTupleData oldtup;
LogicalRepRelId relid; LogicalRepRelId relid;
@ -1452,6 +1453,9 @@ apply_handle_delete(StringInfo s)
remoteslot = ExecInitExtraTupleSlot(estate, remoteslot = ExecInitExtraTupleSlot(estate,
RelationGetDescr(rel->localrel), RelationGetDescr(rel->localrel),
&TTSOpsVirtual); &TTSOpsVirtual);
resultRelInfo = makeNode(ResultRelInfo);
InitResultRelInfo(resultRelInfo, rel->localrel, 1, NULL, 0);
estate->es_result_relation_info = resultRelInfo;
PushActiveSnapshot(GetTransactionSnapshot()); PushActiveSnapshot(GetTransactionSnapshot());
@ -1462,10 +1466,10 @@ apply_handle_delete(StringInfo s)
/* For a partitioned table, apply delete to correct partition. */ /* For a partitioned table, apply delete to correct partition. */
if (rel->localrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) if (rel->localrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
apply_handle_tuple_routing(estate->es_result_relation_info, estate, apply_handle_tuple_routing(resultRelInfo, estate,
remoteslot, NULL, rel, CMD_DELETE); remoteslot, NULL, rel, CMD_DELETE);
else else
apply_handle_delete_internal(estate->es_result_relation_info, estate, apply_handle_delete_internal(resultRelInfo, estate,
remoteslot, &rel->remoterel); remoteslot, &rel->remoterel);
PopActiveSnapshot(); PopActiveSnapshot();

View File

@ -191,7 +191,6 @@ extern void InitResultRelInfo(ResultRelInfo *resultRelInfo,
Relation partition_root, Relation partition_root,
int instrument_options); int instrument_options);
extern ResultRelInfo *ExecGetTriggerResultRel(EState *estate, Oid relid); extern ResultRelInfo *ExecGetTriggerResultRel(EState *estate, Oid relid);
extern void ExecCleanUpTriggerState(EState *estate);
extern void ExecConstraints(ResultRelInfo *resultRelInfo, extern void ExecConstraints(ResultRelInfo *resultRelInfo,
TupleTableSlot *slot, EState *estate); TupleTableSlot *slot, EState *estate);
extern bool ExecPartitionCheck(ResultRelInfo *resultRelInfo, extern bool ExecPartitionCheck(ResultRelInfo *resultRelInfo,
@ -538,6 +537,8 @@ extern bool ExecRelationIsTargetRelation(EState *estate, Index scanrelid);
extern Relation ExecOpenScanRelation(EState *estate, Index scanrelid, int eflags); extern Relation ExecOpenScanRelation(EState *estate, Index scanrelid, int eflags);
extern void ExecInitRangeTable(EState *estate, List *rangeTable); extern void ExecInitRangeTable(EState *estate, List *rangeTable);
extern void ExecCloseRangeTableRelations(EState *estate);
extern void ExecCloseResultRelations(EState *estate);
static inline RangeTblEntry * static inline RangeTblEntry *
exec_rt_fetch(Index rti, EState *estate) exec_rt_fetch(Index rti, EState *estate)
@ -546,6 +547,8 @@ exec_rt_fetch(Index rti, EState *estate)
} }
extern Relation ExecGetRangeTableRelation(EState *estate, Index rti); extern Relation ExecGetRangeTableRelation(EState *estate, Index rti);
extern void ExecInitResultRelation(EState *estate, ResultRelInfo *resultRelInfo,
Index rti);
extern int executor_errposition(EState *estate, int location); extern int executor_errposition(EState *estate, int location);

View File

@ -519,23 +519,19 @@ typedef struct EState
CommandId es_output_cid; CommandId es_output_cid;
/* Info about target table(s) for insert/update/delete queries: */ /* Info about target table(s) for insert/update/delete queries: */
ResultRelInfo *es_result_relations; /* array of ResultRelInfos */ ResultRelInfo **es_result_relations; /* Array of per-range-table-entry
int es_num_result_relations; /* length of array */ * ResultRelInfo pointers, or NULL
* if not a target table */
List *es_opened_result_relations; /* List of non-NULL entries in
* es_result_relations in no
* specific order */
ResultRelInfo *es_result_relation_info; /* currently active array elt */ ResultRelInfo *es_result_relation_info; /* currently active array elt */
/*
* Info about the partition root table(s) for insert/update/delete queries
* targeting partitioned tables. Only leaf partitions are mentioned in
* es_result_relations, but we need access to the roots for firing
* triggers and for runtime tuple routing.
*/
ResultRelInfo *es_root_result_relations; /* array of ResultRelInfos */
int es_num_root_result_relations; /* length of the array */
PartitionDirectory es_partition_directory; /* for PartitionDesc lookup */ PartitionDirectory es_partition_directory; /* for PartitionDesc lookup */
/* /*
* The following list contains ResultRelInfos created by the tuple routing * The following list contains ResultRelInfos created by the tuple routing
* code for partitions that don't already have one. * code for partitions that aren't found in the es_result_relations array.
*/ */
List *es_tuple_routing_result_relations; List *es_tuple_routing_result_relations;

View File

@ -120,8 +120,6 @@ typedef struct PlannerGlobal
List *resultRelations; /* "flat" list of integer RT indexes */ List *resultRelations; /* "flat" list of integer RT indexes */
List *rootResultRelations; /* "flat" list of integer RT indexes */
List *appendRelations; /* "flat" list of AppendRelInfos */ List *appendRelations; /* "flat" list of AppendRelInfos */
List *relationOids; /* OIDs of relations the plan depends on */ List *relationOids; /* OIDs of relations the plan depends on */

View File

@ -68,12 +68,6 @@ typedef struct PlannedStmt
/* rtable indexes of target relations for INSERT/UPDATE/DELETE */ /* rtable indexes of target relations for INSERT/UPDATE/DELETE */
List *resultRelations; /* integer list of RT indexes, or NIL */ List *resultRelations; /* integer list of RT indexes, or NIL */
/*
* rtable indexes of partitioned table roots that are UPDATE/DELETE
* targets; needed for trigger firing.
*/
List *rootResultRelations;
List *appendRelations; /* list of AppendRelInfo nodes */ List *appendRelations; /* list of AppendRelInfo nodes */
List *subplans; /* Plan trees for SubPlan expressions; note List *subplans; /* Plan trees for SubPlan expressions; note
@ -224,8 +218,6 @@ typedef struct ModifyTable
Index rootRelation; /* Root RT index, if target is partitioned */ Index rootRelation; /* Root RT index, if target is partitioned */
bool partColsUpdated; /* some part key in hierarchy updated */ bool partColsUpdated; /* some part key in hierarchy updated */
List *resultRelations; /* integer list of RT indexes */ List *resultRelations; /* integer list of RT indexes */
int resultRelIndex; /* index of first resultRel in plan's list */
int rootResultRelIndex; /* index of the partitioned table root */
List *plans; /* plan(s) producing source data */ List *plans; /* plan(s) producing source data */
List *withCheckOptionLists; /* per-target-table WCO lists */ List *withCheckOptionLists; /* per-target-table WCO lists */
List *returningLists; /* per-target-table RETURNING tlists */ List *returningLists; /* per-target-table RETURNING tlists */