summaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan/planner.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/plan/planner.c')
-rw-r--r--src/backend/optimizer/plan/planner.c790
1 files changed, 133 insertions, 657 deletions
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 0886bf4ae8f..898d7fcb0bd 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -129,9 +129,7 @@ typedef struct
/* Local functions */
static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind);
static void preprocess_qual_conditions(PlannerInfo *root, Node *jtnode);
-static void inheritance_planner(PlannerInfo *root);
-static void grouping_planner(PlannerInfo *root, bool inheritance_update,
- double tuple_fraction);
+static void grouping_planner(PlannerInfo *root, double tuple_fraction);
static grouping_sets_data *preprocess_grouping_sets(PlannerInfo *root);
static List *remap_to_groupclause_idx(List *groupClause, List *gsets,
int *tleref_to_colnum_map);
@@ -615,15 +613,19 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
root->multiexpr_params = NIL;
root->eq_classes = NIL;
root->ec_merging_done = false;
+ root->all_result_relids =
+ parse->resultRelation ? bms_make_singleton(parse->resultRelation) : NULL;
+ root->leaf_result_relids = NULL; /* we'll find out leaf-ness later */
root->append_rel_list = NIL;
+ root->row_identity_vars = NIL;
root->rowMarks = NIL;
memset(root->upper_rels, 0, sizeof(root->upper_rels));
memset(root->upper_targets, 0, sizeof(root->upper_targets));
root->processed_tlist = NIL;
+ root->update_colnos = NIL;
root->grouping_map = NULL;
root->minmax_aggs = NIL;
root->qual_security_level = 0;
- root->inhTargetKind = INHKIND_NONE;
root->hasPseudoConstantQuals = false;
root->hasAlternativeSubPlans = false;
root->hasRecursion = hasRecursion;
@@ -744,6 +746,19 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
}
/*
+ * If we have now verified that the query target relation is
+ * non-inheriting, mark it as a leaf target.
+ */
+ if (parse->resultRelation)
+ {
+ RangeTblEntry *rte = rt_fetch(parse->resultRelation, parse->rtable);
+
+ if (!rte->inh)
+ root->leaf_result_relids =
+ bms_make_singleton(parse->resultRelation);
+ }
+
+ /*
* Preprocess RowMark information. We need to do this after subquery
* pullup, so that all base relations are present.
*/
@@ -999,14 +1014,9 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
remove_useless_result_rtes(root);
/*
- * Do the main planning. If we have an inherited target relation, that
- * needs special processing, else go straight to grouping_planner.
+ * Do the main planning.
*/
- if (parse->resultRelation &&
- rt_fetch(parse->resultRelation, parse->rtable)->inh)
- inheritance_planner(root);
- else
- grouping_planner(root, false, tuple_fraction);
+ grouping_planner(root, tuple_fraction);
/*
* Capture the set of outer-level param IDs we have access to, for use in
@@ -1180,621 +1190,6 @@ preprocess_phv_expression(PlannerInfo *root, Expr *expr)
return (Expr *) preprocess_expression(root, (Node *) expr, EXPRKIND_PHV);
}
-/*
- * inheritance_planner
- * Generate Paths in the case where the result relation is an
- * inheritance set.
- *
- * We have to handle this case differently from cases where a source relation
- * is an inheritance set. Source inheritance is expanded at the bottom of the
- * plan tree (see allpaths.c), but target inheritance has to be expanded at
- * the top. The reason is that for UPDATE, each target relation needs a
- * different targetlist matching its own column set. Fortunately,
- * the UPDATE/DELETE target can never be the nullable side of an outer join,
- * so it's OK to generate the plan this way.
- *
- * Returns nothing; the useful output is in the Paths we attach to
- * the (UPPERREL_FINAL, NULL) upperrel stored in *root.
- *
- * Note that we have not done set_cheapest() on the final rel; it's convenient
- * to leave this to the caller.
- */
-static void
-inheritance_planner(PlannerInfo *root)
-{
- Query *parse = root->parse;
- int top_parentRTindex = parse->resultRelation;
- List *select_rtable;
- List *select_appinfos;
- List *child_appinfos;
- List *old_child_rtis;
- List *new_child_rtis;
- Bitmapset *subqueryRTindexes;
- Index next_subquery_rti;
- int nominalRelation = -1;
- Index rootRelation = 0;
- List *final_rtable = NIL;
- List *final_rowmarks = NIL;
- List *final_appendrels = NIL;
- int save_rel_array_size = 0;
- RelOptInfo **save_rel_array = NULL;
- AppendRelInfo **save_append_rel_array = NULL;
- List *subpaths = NIL;
- List *subroots = NIL;
- List *resultRelations = NIL;
- List *withCheckOptionLists = NIL;
- List *returningLists = NIL;
- List *rowMarks;
- RelOptInfo *final_rel;
- ListCell *lc;
- ListCell *lc2;
- Index rti;
- RangeTblEntry *parent_rte;
- Bitmapset *parent_relids;
- Query **parent_parses;
-
- /* Should only get here for UPDATE or DELETE */
- Assert(parse->commandType == CMD_UPDATE ||
- parse->commandType == CMD_DELETE);
-
- /*
- * We generate a modified instance of the original Query for each target
- * relation, plan that, and put all the plans into a list that will be
- * controlled by a single ModifyTable node. All the instances share the
- * same rangetable, but each instance must have its own set of subquery
- * RTEs within the finished rangetable because (1) they are likely to get
- * scribbled on during planning, and (2) it's not inconceivable that
- * subqueries could get planned differently in different cases. We need
- * not create duplicate copies of other RTE kinds, in particular not the
- * target relations, because they don't have either of those issues. Not
- * having to duplicate the target relations is important because doing so
- * (1) would result in a rangetable of length O(N^2) for N targets, with
- * at least O(N^3) work expended here; and (2) would greatly complicate
- * management of the rowMarks list.
- *
- * To begin with, generate a bitmapset of the relids of the subquery RTEs.
- */
- subqueryRTindexes = NULL;
- rti = 1;
- foreach(lc, parse->rtable)
- {
- RangeTblEntry *rte = lfirst_node(RangeTblEntry, lc);
-
- if (rte->rtekind == RTE_SUBQUERY)
- subqueryRTindexes = bms_add_member(subqueryRTindexes, rti);
- rti++;
- }
-
- /*
- * If the parent RTE is a partitioned table, we should use that as the
- * nominal target relation, because the RTEs added for partitioned tables
- * (including the root parent) as child members of the inheritance set do
- * not appear anywhere else in the plan, so the confusion explained below
- * for non-partitioning inheritance cases is not possible.
- */
- parent_rte = rt_fetch(top_parentRTindex, parse->rtable);
- Assert(parent_rte->inh);
- if (parent_rte->relkind == RELKIND_PARTITIONED_TABLE)
- {
- nominalRelation = top_parentRTindex;
- rootRelation = top_parentRTindex;
- }
-
- /*
- * Before generating the real per-child-relation plans, do a cycle of
- * planning as though the query were a SELECT. The objective here is to
- * find out which child relations need to be processed, using the same
- * expansion and pruning logic as for a SELECT. We'll then pull out the
- * RangeTblEntry-s generated for the child rels, and make use of the
- * AppendRelInfo entries for them to guide the real planning. (This is
- * rather inefficient; we could perhaps stop short of making a full Path
- * tree. But this whole function is inefficient and slated for
- * destruction, so let's not contort query_planner for that.)
- */
- {
- PlannerInfo *subroot;
-
- /*
- * Flat-copy the PlannerInfo to prevent modification of the original.
- */
- subroot = makeNode(PlannerInfo);
- memcpy(subroot, root, sizeof(PlannerInfo));
-
- /*
- * Make a deep copy of the parsetree for this planning cycle to mess
- * around with, and change it to look like a SELECT. (Hack alert: the
- * target RTE still has updatedCols set if this is an UPDATE, so that
- * expand_partitioned_rtentry will correctly update
- * subroot->partColsUpdated.)
- */
- subroot->parse = copyObject(root->parse);
-
- subroot->parse->commandType = CMD_SELECT;
- subroot->parse->resultRelation = 0;
-
- /*
- * Ensure the subroot has its own copy of the original
- * append_rel_list, since it'll be scribbled on. (Note that at this
- * point, the list only contains AppendRelInfos for flattened UNION
- * ALL subqueries.)
- */
- subroot->append_rel_list = copyObject(root->append_rel_list);
-
- /*
- * Better make a private copy of the rowMarks, too.
- */
- subroot->rowMarks = copyObject(root->rowMarks);
-
- /* There shouldn't be any OJ info to translate, as yet */
- Assert(subroot->join_info_list == NIL);
- /* and we haven't created PlaceHolderInfos, either */
- Assert(subroot->placeholder_list == NIL);
-
- /* Generate Path(s) for accessing this result relation */
- grouping_planner(subroot, true, 0.0 /* retrieve all tuples */ );
-
- /* Extract the info we need. */
- select_rtable = subroot->parse->rtable;
- select_appinfos = subroot->append_rel_list;
-
- /*
- * We need to propagate partColsUpdated back, too. (The later
- * planning cycles will not set this because they won't run
- * expand_partitioned_rtentry for the UPDATE target.)
- */
- root->partColsUpdated = subroot->partColsUpdated;
- }
-
- /*----------
- * Since only one rangetable can exist in the final plan, we need to make
- * sure that it contains all the RTEs needed for any child plan. This is
- * complicated by the need to use separate subquery RTEs for each child.
- * We arrange the final rtable as follows:
- * 1. All original rtable entries (with their original RT indexes).
- * 2. All the relation RTEs generated for children of the target table.
- * 3. Subquery RTEs for children after the first. We need N * (K - 1)
- * RT slots for this, if there are N subqueries and K child tables.
- * 4. Additional RTEs generated during the child planning runs, such as
- * children of inheritable RTEs other than the target table.
- * We assume that each child planning run will create an identical set
- * of type-4 RTEs.
- *
- * So the next thing to do is append the type-2 RTEs (the target table's
- * children) to the original rtable. We look through select_appinfos
- * to find them.
- *
- * To identify which AppendRelInfos are relevant as we thumb through
- * select_appinfos, we need to look for both direct and indirect children
- * of top_parentRTindex, so we use a bitmap of known parent relids.
- * expand_inherited_rtentry() always processes a parent before any of that
- * parent's children, so we should see an intermediate parent before its
- * children.
- *----------
- */
- child_appinfos = NIL;
- old_child_rtis = NIL;
- new_child_rtis = NIL;
- parent_relids = bms_make_singleton(top_parentRTindex);
- foreach(lc, select_appinfos)
- {
- AppendRelInfo *appinfo = lfirst_node(AppendRelInfo, lc);
- RangeTblEntry *child_rte;
-
- /* append_rel_list contains all append rels; ignore others */
- if (!bms_is_member(appinfo->parent_relid, parent_relids))
- continue;
-
- /* remember relevant AppendRelInfos for use below */
- child_appinfos = lappend(child_appinfos, appinfo);
-
- /* extract RTE for this child rel */
- child_rte = rt_fetch(appinfo->child_relid, select_rtable);
-
- /* and append it to the original rtable */
- parse->rtable = lappend(parse->rtable, child_rte);
-
- /* remember child's index in the SELECT rtable */
- old_child_rtis = lappend_int(old_child_rtis, appinfo->child_relid);
-
- /* and its new index in the final rtable */
- new_child_rtis = lappend_int(new_child_rtis, list_length(parse->rtable));
-
- /* if child is itself partitioned, update parent_relids */
- if (child_rte->inh)
- {
- Assert(child_rte->relkind == RELKIND_PARTITIONED_TABLE);
- parent_relids = bms_add_member(parent_relids, appinfo->child_relid);
- }
- }
-
- /*
- * It's possible that the RTIs we just assigned for the child rels in the
- * final rtable are different from what they were in the SELECT query.
- * Adjust the AppendRelInfos so that they will correctly map RT indexes to
- * the final indexes. We can do this left-to-right since no child rel's
- * final RT index could be greater than what it had in the SELECT query.
- */
- forboth(lc, old_child_rtis, lc2, new_child_rtis)
- {
- int old_child_rti = lfirst_int(lc);
- int new_child_rti = lfirst_int(lc2);
-
- if (old_child_rti == new_child_rti)
- continue; /* nothing to do */
-
- Assert(old_child_rti > new_child_rti);
-
- ChangeVarNodes((Node *) child_appinfos,
- old_child_rti, new_child_rti, 0);
- }
-
- /*
- * Now set up rangetable entries for subqueries for additional children
- * (the first child will just use the original ones). These all have to
- * look more or less real, or EXPLAIN will get unhappy; so we just make
- * them all clones of the original subqueries.
- */
- next_subquery_rti = list_length(parse->rtable) + 1;
- if (subqueryRTindexes != NULL)
- {
- int n_children = list_length(child_appinfos);
-
- while (n_children-- > 1)
- {
- int oldrti = -1;
-
- while ((oldrti = bms_next_member(subqueryRTindexes, oldrti)) >= 0)
- {
- RangeTblEntry *subqrte;
-
- subqrte = rt_fetch(oldrti, parse->rtable);
- parse->rtable = lappend(parse->rtable, copyObject(subqrte));
- }
- }
- }
-
- /*
- * The query for each child is obtained by translating the query for its
- * immediate parent, since the AppendRelInfo data we have shows deltas
- * between parents and children. We use the parent_parses array to
- * remember the appropriate query trees. This is indexed by parent relid.
- * Since the maximum number of parents is limited by the number of RTEs in
- * the SELECT query, we use that number to allocate the array. An extra
- * entry is needed since relids start from 1.
- */
- parent_parses = (Query **) palloc0((list_length(select_rtable) + 1) *
- sizeof(Query *));
- parent_parses[top_parentRTindex] = parse;
-
- /*
- * And now we can get on with generating a plan for each child table.
- */
- foreach(lc, child_appinfos)
- {
- AppendRelInfo *appinfo = lfirst_node(AppendRelInfo, lc);
- Index this_subquery_rti = next_subquery_rti;
- Query *parent_parse;
- PlannerInfo *subroot;
- RangeTblEntry *child_rte;
- RelOptInfo *sub_final_rel;
- Path *subpath;
-
- /*
- * expand_inherited_rtentry() always processes a parent before any of
- * that parent's children, so the parent query for this relation
- * should already be available.
- */
- parent_parse = parent_parses[appinfo->parent_relid];
- Assert(parent_parse != NULL);
-
- /*
- * We need a working copy of the PlannerInfo so that we can control
- * propagation of information back to the main copy.
- */
- subroot = makeNode(PlannerInfo);
- memcpy(subroot, root, sizeof(PlannerInfo));
-
- /*
- * Generate modified query with this rel as target. We first apply
- * adjust_appendrel_attrs, which copies the Query and changes
- * references to the parent RTE to refer to the current child RTE,
- * then fool around with subquery RTEs.
- */
- subroot->parse = (Query *)
- adjust_appendrel_attrs(subroot,
- (Node *) parent_parse,
- 1, &appinfo);
-
- /*
- * If there are securityQuals attached to the parent, move them to the
- * child rel (they've already been transformed properly for that).
- */
- parent_rte = rt_fetch(appinfo->parent_relid, subroot->parse->rtable);
- child_rte = rt_fetch(appinfo->child_relid, subroot->parse->rtable);
- child_rte->securityQuals = parent_rte->securityQuals;
- parent_rte->securityQuals = NIL;
-
- /*
- * HACK: setting this to a value other than INHKIND_NONE signals to
- * relation_excluded_by_constraints() to treat the result relation as
- * being an appendrel member.
- */
- subroot->inhTargetKind =
- (rootRelation != 0) ? INHKIND_PARTITIONED : INHKIND_INHERITED;
-
- /*
- * If this child is further partitioned, remember it as a parent.
- * Since a partitioned table does not have any data, we don't need to
- * create a plan for it, and we can stop processing it here. We do,
- * however, need to remember its modified PlannerInfo for use when
- * processing its children, since we'll update their varnos based on
- * the delta from immediate parent to child, not from top to child.
- *
- * Note: a very non-obvious point is that we have not yet added
- * duplicate subquery RTEs to the subroot's rtable. We mustn't,
- * because then its children would have two sets of duplicates,
- * confusing matters.
- */
- if (child_rte->inh)
- {
- Assert(child_rte->relkind == RELKIND_PARTITIONED_TABLE);
- parent_parses[appinfo->child_relid] = subroot->parse;
- continue;
- }
-
- /*
- * Set the nominal target relation of the ModifyTable node if not
- * already done. If the target is a partitioned table, we already set
- * nominalRelation to refer to the partition root, above. For
- * non-partitioned inheritance cases, we'll use the first child
- * relation (even if it's excluded) as the nominal target relation.
- * Because of the way expand_inherited_rtentry works, that should be
- * the RTE representing the parent table in its role as a simple
- * member of the inheritance set.
- *
- * It would be logically cleaner to *always* use the inheritance
- * parent RTE as the nominal relation; but that RTE is not otherwise
- * referenced in the plan in the non-partitioned inheritance case.
- * Instead the duplicate child RTE created by expand_inherited_rtentry
- * is used elsewhere in the plan, so using the original parent RTE
- * would give rise to confusing use of multiple aliases in EXPLAIN
- * output for what the user will think is the "same" table. OTOH,
- * it's not a problem in the partitioned inheritance case, because
- * there is no duplicate RTE for the parent.
- */
- if (nominalRelation < 0)
- nominalRelation = appinfo->child_relid;
-
- /*
- * As above, each child plan run needs its own append_rel_list and
- * rowmarks, which should start out as pristine copies of the
- * originals. There can't be any references to UPDATE/DELETE target
- * rels in them; but there could be subquery references, which we'll
- * fix up in a moment.
- */
- subroot->append_rel_list = copyObject(root->append_rel_list);
- subroot->rowMarks = copyObject(root->rowMarks);
-
- /*
- * If this isn't the first child Query, adjust Vars and jointree
- * entries to reference the appropriate set of subquery RTEs.
- */
- if (final_rtable != NIL && subqueryRTindexes != NULL)
- {
- int oldrti = -1;
-
- while ((oldrti = bms_next_member(subqueryRTindexes, oldrti)) >= 0)
- {
- Index newrti = next_subquery_rti++;
-
- ChangeVarNodes((Node *) subroot->parse, oldrti, newrti, 0);
- ChangeVarNodes((Node *) subroot->append_rel_list,
- oldrti, newrti, 0);
- ChangeVarNodes((Node *) subroot->rowMarks, oldrti, newrti, 0);
- }
- }
-
- /* There shouldn't be any OJ info to translate, as yet */
- Assert(subroot->join_info_list == NIL);
- /* and we haven't created PlaceHolderInfos, either */
- Assert(subroot->placeholder_list == NIL);
-
- /* Generate Path(s) for accessing this result relation */
- grouping_planner(subroot, true, 0.0 /* retrieve all tuples */ );
-
- /*
- * Select cheapest path in case there's more than one. We always run
- * modification queries to conclusion, so we care only for the
- * cheapest-total path.
- */
- sub_final_rel = fetch_upper_rel(subroot, UPPERREL_FINAL, NULL);
- set_cheapest(sub_final_rel);
- subpath = sub_final_rel->cheapest_total_path;
-
- /*
- * If this child rel was excluded by constraint exclusion, exclude it
- * from the result plan.
- */
- if (IS_DUMMY_REL(sub_final_rel))
- continue;
-
- /*
- * If this is the first non-excluded child, its post-planning rtable
- * becomes the initial contents of final_rtable; otherwise, copy its
- * modified subquery RTEs into final_rtable, to ensure we have sane
- * copies of those. Also save the first non-excluded child's version
- * of the rowmarks list; we assume all children will end up with
- * equivalent versions of that. Likewise for append_rel_list.
- */
- if (final_rtable == NIL)
- {
- final_rtable = subroot->parse->rtable;
- final_rowmarks = subroot->rowMarks;
- final_appendrels = subroot->append_rel_list;
- }
- else
- {
- Assert(list_length(final_rtable) ==
- list_length(subroot->parse->rtable));
- if (subqueryRTindexes != NULL)
- {
- int oldrti = -1;
-
- while ((oldrti = bms_next_member(subqueryRTindexes, oldrti)) >= 0)
- {
- Index newrti = this_subquery_rti++;
- RangeTblEntry *subqrte;
- ListCell *newrticell;
-
- subqrte = rt_fetch(newrti, subroot->parse->rtable);
- newrticell = list_nth_cell(final_rtable, newrti - 1);
- lfirst(newrticell) = subqrte;
- }
- }
- }
-
- /*
- * We need to collect all the RelOptInfos from all child plans into
- * the main PlannerInfo, since setrefs.c will need them. We use the
- * last child's simple_rel_array, so we have to propagate forward the
- * RelOptInfos that were already built in previous children.
- */
- Assert(subroot->simple_rel_array_size >= save_rel_array_size);
- for (rti = 1; rti < save_rel_array_size; rti++)
- {
- RelOptInfo *brel = save_rel_array[rti];
-
- if (brel)
- subroot->simple_rel_array[rti] = brel;
- }
- save_rel_array_size = subroot->simple_rel_array_size;
- save_rel_array = subroot->simple_rel_array;
- save_append_rel_array = subroot->append_rel_array;
-
- /*
- * Make sure any initplans from this rel get into the outer list. Note
- * we're effectively assuming all children generate the same
- * init_plans.
- */
- root->init_plans = subroot->init_plans;
-
- /* Build list of sub-paths */
- subpaths = lappend(subpaths, subpath);
-
- /* Build list of modified subroots, too */
- subroots = lappend(subroots, subroot);
-
- /* Build list of target-relation RT indexes */
- resultRelations = lappend_int(resultRelations, appinfo->child_relid);
-
- /* Build lists of per-relation WCO and RETURNING targetlists */
- if (parse->withCheckOptions)
- withCheckOptionLists = lappend(withCheckOptionLists,
- subroot->parse->withCheckOptions);
- if (parse->returningList)
- returningLists = lappend(returningLists,
- subroot->parse->returningList);
-
- Assert(!parse->onConflict);
- }
-
- /* Result path must go into outer query's FINAL upperrel */
- final_rel = fetch_upper_rel(root, UPPERREL_FINAL, NULL);
-
- /*
- * We don't currently worry about setting final_rel's consider_parallel
- * flag in this case, nor about allowing FDWs or create_upper_paths_hook
- * to get control here.
- */
-
- if (subpaths == NIL)
- {
- /*
- * We managed to exclude every child rel, so generate a dummy path
- * representing the empty set. Although it's clear that no data will
- * be updated or deleted, we will still need to have a ModifyTable
- * node so that any statement triggers are executed. (This could be
- * cleaner if we fixed nodeModifyTable.c to support zero child nodes,
- * but that probably wouldn't be a net win.)
- */
- Path *dummy_path;
-
- /* tlist processing never got done, either */
- root->processed_tlist = preprocess_targetlist(root);
- final_rel->reltarget = create_pathtarget(root, root->processed_tlist);
-
- /* Make a dummy path, cf set_dummy_rel_pathlist() */
- dummy_path = (Path *) create_append_path(NULL, final_rel, NIL, NIL,
- NIL, NULL, 0, false,
- -1);
-
- /* These lists must be nonempty to make a valid ModifyTable node */
- subpaths = list_make1(dummy_path);
- subroots = list_make1(root);
- resultRelations = list_make1_int(parse->resultRelation);
- if (parse->withCheckOptions)
- withCheckOptionLists = list_make1(parse->withCheckOptions);
- if (parse->returningList)
- returningLists = list_make1(parse->returningList);
- /* Disable tuple routing, too, just to be safe */
- root->partColsUpdated = false;
- }
- else
- {
- /*
- * Put back the final adjusted rtable into the original copy of the
- * Query. (We mustn't do this if we found no non-excluded children,
- * since we never saved an adjusted rtable at all.)
- */
- parse->rtable = final_rtable;
- root->simple_rel_array_size = save_rel_array_size;
- root->simple_rel_array = save_rel_array;
- root->append_rel_array = save_append_rel_array;
-
- /* Must reconstruct original's simple_rte_array, too */
- root->simple_rte_array = (RangeTblEntry **)
- palloc0((list_length(final_rtable) + 1) * sizeof(RangeTblEntry *));
- rti = 1;
- foreach(lc, final_rtable)
- {
- RangeTblEntry *rte = lfirst_node(RangeTblEntry, lc);
-
- root->simple_rte_array[rti++] = rte;
- }
-
- /* Put back adjusted rowmarks and appendrels, too */
- root->rowMarks = final_rowmarks;
- root->append_rel_list = final_appendrels;
- }
-
- /*
- * If there was a FOR [KEY] UPDATE/SHARE clause, the LockRows node will
- * have dealt with fetching non-locked marked rows, else we need to have
- * ModifyTable do that.
- */
- if (parse->rowMarks)
- rowMarks = NIL;
- else
- rowMarks = root->rowMarks;
-
- /* Create Path representing a ModifyTable to do the UPDATE/DELETE work */
- add_path(final_rel, (Path *)
- create_modifytable_path(root, final_rel,
- parse->commandType,
- parse->canSetTag,
- nominalRelation,
- rootRelation,
- root->partColsUpdated,
- resultRelations,
- subpaths,
- subroots,
- withCheckOptionLists,
- returningLists,
- rowMarks,
- NULL,
- assign_special_exec_param(root)));
-}
-
/*--------------------
* grouping_planner
* Perform planning steps related to grouping, aggregation, etc.
@@ -1802,11 +1197,6 @@ inheritance_planner(PlannerInfo *root)
* This function adds all required top-level processing to the scan/join
* Path(s) produced by query_planner.
*
- * If inheritance_update is true, we're being called from inheritance_planner
- * and should not include a ModifyTable step in the resulting Path(s).
- * (inheritance_planner will create a single ModifyTable node covering all the
- * target tables.)
- *
* tuple_fraction is the fraction of tuples we expect will be retrieved.
* tuple_fraction is interpreted as follows:
* 0: expect all tuples to be retrieved (normal case)
@@ -1824,8 +1214,7 @@ inheritance_planner(PlannerInfo *root)
*--------------------
*/
static void
-grouping_planner(PlannerInfo *root, bool inheritance_update,
- double tuple_fraction)
+grouping_planner(PlannerInfo *root, double tuple_fraction)
{
Query *parse = root->parse;
int64 offset_est = 0;
@@ -1969,7 +1358,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
* that we can transfer its decoration (resnames etc) to the topmost
* tlist of the finished Plan. This is kept in processed_tlist.
*/
- root->processed_tlist = preprocess_targetlist(root);
+ preprocess_targetlist(root);
/*
* Mark all the aggregates with resolved aggtranstypes, and detect
@@ -2307,16 +1696,117 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
}
/*
- * If this is an INSERT/UPDATE/DELETE, and we're not being called from
- * inheritance_planner, add the ModifyTable node.
+ * If this is an INSERT/UPDATE/DELETE, add the ModifyTable node.
*/
- if (parse->commandType != CMD_SELECT && !inheritance_update)
+ if (parse->commandType != CMD_SELECT)
{
Index rootRelation;
- List *withCheckOptionLists;
- List *returningLists;
+ List *resultRelations = NIL;
+ List *updateColnosLists = NIL;
+ List *withCheckOptionLists = NIL;
+ List *returningLists = NIL;
List *rowMarks;
+ if (bms_membership(root->all_result_relids) == BMS_MULTIPLE)
+ {
+ /* Inherited UPDATE/DELETE */
+ RelOptInfo *top_result_rel = find_base_rel(root,
+ parse->resultRelation);
+ int resultRelation = -1;
+
+ /* Add only leaf children to ModifyTable. */
+ while ((resultRelation = bms_next_member(root->leaf_result_relids,
+ resultRelation)) >= 0)
+ {
+ RelOptInfo *this_result_rel = find_base_rel(root,
+ resultRelation);
+
+ /*
+ * Also exclude any leaf rels that have turned dummy since
+ * being added to the list, for example, by being excluded
+ * by constraint exclusion.
+ */
+ if (IS_DUMMY_REL(this_result_rel))
+ continue;
+
+ /* Build per-target-rel lists needed by ModifyTable */
+ resultRelations = lappend_int(resultRelations,
+ resultRelation);
+ if (parse->commandType == CMD_UPDATE)
+ {
+ List *update_colnos = root->update_colnos;
+
+ if (this_result_rel != top_result_rel)
+ update_colnos =
+ adjust_inherited_attnums_multilevel(root,
+ update_colnos,
+ this_result_rel->relid,
+ top_result_rel->relid);
+ updateColnosLists = lappend(updateColnosLists,
+ update_colnos);
+ }
+ if (parse->withCheckOptions)
+ {
+ List *withCheckOptions = parse->withCheckOptions;
+
+ if (this_result_rel != top_result_rel)
+ withCheckOptions = (List *)
+ adjust_appendrel_attrs_multilevel(root,
+ (Node *) withCheckOptions,
+ this_result_rel->relids,
+ top_result_rel->relids);
+ withCheckOptionLists = lappend(withCheckOptionLists,
+ withCheckOptions);
+ }
+ if (parse->returningList)
+ {
+ List *returningList = parse->returningList;
+
+ if (this_result_rel != top_result_rel)
+ returningList = (List *)
+ adjust_appendrel_attrs_multilevel(root,
+ (Node *) returningList,
+ this_result_rel->relids,
+ top_result_rel->relids);
+ returningLists = lappend(returningLists,
+ returningList);
+ }
+ }
+
+ if (resultRelations == NIL)
+ {
+ /*
+ * We managed to exclude every child rel, so generate a
+ * dummy one-relation plan using info for the top target
+ * rel (even though that may not be a leaf target).
+ * Although it's clear that no data will be updated or
+ * deleted, we still need to have a ModifyTable node so
+ * that any statement triggers will be executed. (This
+ * could be cleaner if we fixed nodeModifyTable.c to allow
+ * zero target relations, but that probably wouldn't be a
+ * net win.)
+ */
+ resultRelations = list_make1_int(parse->resultRelation);
+ if (parse->commandType == CMD_UPDATE)
+ updateColnosLists = list_make1(root->update_colnos);
+ if (parse->withCheckOptions)
+ withCheckOptionLists = list_make1(parse->withCheckOptions);
+ if (parse->returningList)
+ returningLists = list_make1(parse->returningList);
+ }
+ }
+ else
+ {
+ /* Single-relation INSERT/UPDATE/DELETE. */
+ resultRelations = list_make1_int(parse->resultRelation);
+ if (parse->commandType == CMD_UPDATE)
+ updateColnosLists = list_make1(root->update_colnos);
+ if (parse->withCheckOptions)
+ withCheckOptionLists = list_make1(parse->withCheckOptions);
+ if (parse->returningList)
+ returningLists = list_make1(parse->returningList);
+ }
+
/*
* If target is a partition root table, we need to mark the
* ModifyTable node appropriately for that.
@@ -2328,20 +1818,6 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
rootRelation = 0;
/*
- * Set up the WITH CHECK OPTION and RETURNING lists-of-lists, if
- * needed.
- */
- if (parse->withCheckOptions)
- withCheckOptionLists = list_make1(parse->withCheckOptions);
- else
- withCheckOptionLists = NIL;
-
- if (parse->returningList)
- returningLists = list_make1(parse->returningList);
- else
- returningLists = NIL;
-
- /*
* If there was a FOR [KEY] UPDATE/SHARE clause, the LockRows node
* will have dealt with fetching non-locked marked rows, else we
* need to have ModifyTable do that.
@@ -2353,14 +1829,14 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
path = (Path *)
create_modifytable_path(root, final_rel,
+ path,
parse->commandType,
parse->canSetTag,
parse->resultRelation,
rootRelation,
- false,
- list_make1_int(parse->resultRelation),
- list_make1(path),
- list_make1(root),
+ root->partColsUpdated,
+ resultRelations,
+ updateColnosLists,
withCheckOptionLists,
returningLists,
rowMarks,