From f14e09f119ce85c344b581af6b1623982d7cc2b1 Mon Sep 17 00:00:00 2001 From: amit Date: Wed, 17 Oct 2018 11:18:12 +0900 Subject: [PATCH v5 2/6] Overhaul inheritance update/delete planning With the current method, inheritance_planner, which handles the planning for update/delete commands targeting inheritance trees, performs the *whole* planning for each child table. That involves translating the query tree to set a given child table in place of the original inheritance root table as the query's target table, followed by calling grouping_planner on the translated query. That's inefficient for two reasons: 1. partprune.c cannot be used to perform partition pruning, because it can only be invoked from query_planner. With the current approach, query_planner only sees the individual partitions in the query tree it receives, not the parent partitioned table. This leaves each partition to be pruned using constraint exclusion. 2. Repeated invocation of query_planner results in allocating large amounts of memory, especially if there are many child tables. Also, it involves repeatedly performing the same processing, such jointree processing. This commit addresses both of the issues by rewriting portions of inheritance_planner and adding some supporting functionality to allpath.c. With the new implementation, inheritance_planner calls query_planner only once at the beginning with the original unmodified query, which creates the access paths of individual relations after pruning any unnecessary partitions. If the query involves join against the target relation, join paths are created for each target child relation by replacing the original target table in the join tree by a given child table. Join relations (RelOptInfos thereof) for all target child relations are collected in a global list in PlannerInfo. After creating the join paths for all target child relations, it calls grouping_planner() on each child join relation to finish up the paths such that they produce query's top-level target list expanded per a given child relation's descriptor. grouping_planner()'s interface is modified so that we can pass it what's called a 'planned_rel', a RelOptInfo that already contains the necessary paths needed to produce its output. This removes some existing code in inheritance_planner that dealt with any subquery RTEs in the query. The rationale of that code was that the subquery RTEs may change during each iteration of planning (that is, for different children), so different iterations better use different copies of those RTEs. That was handled by making fresh copies of those RTEs for each iteration of planning which were appended to the range table, accompanied by modifying all expressions and auxiliary structures that referenced the original subquery RTEs to instead reference the copies (that is, change the varnos). This copying meant we would end up adding S * N new entries to the original range table by the time we got to the last unpruned child, where S is the number of subquery RTEs in the original query and N the number of unpruned children. Since with the new code we perform planning just once, I think we don't need this special handling. Actually, there is a regression test output change due to no longer having copies of subquery RTEs (see the diff of partition_join.out file.) Also we no longer need the hack in relation_excluded_by_constraints() involving setting PlannerInfo.inhTargetKind to enable constraint exclusion for target child relation is no longer needed. This commit removes inheritance_planner in favor of rearranging things such that, only the planning steps that affect 1 and 2 above are repeated for unpruned child relations. --- doc/src/sgml/ddl.sgml | 14 +- src/backend/nodes/outfuncs.c | 1 - src/backend/optimizer/path/allpaths.c | 212 +++++++++++++- src/backend/optimizer/plan/createplan.c | 10 - src/backend/optimizer/plan/planner.c | 400 +++++++-------------------- src/backend/optimizer/prep/prepjointree.c | 1 - src/backend/optimizer/prep/prepunion.c | 33 ++- src/backend/optimizer/util/plancat.c | 50 +--- src/include/nodes/relation.h | 18 +- src/test/regress/expected/partition_join.out | 4 +- 10 files changed, 356 insertions(+), 387 deletions(-) diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml index b5ed1b7939..8cb69b1a47 100644 --- a/doc/src/sgml/ddl.sgml +++ b/doc/src/sgml/ddl.sgml @@ -3935,15 +3935,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate >= DATE '2008-01-01'; - Currently, pruning of partitions during the planning of an - UPDATE or DELETE command is - implemented using the constraint exclusion method (however, it is - controlled by the enable_partition_pruning rather than - constraint_exclusion) — see the following section - for details and caveats that apply. - - - Execution-time partition pruning currently occurs for the Append and MergeAppend node types. @@ -3964,9 +3955,8 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate >= DATE '2008-01-01'; Constraint exclusion is a query optimization - technique similar to partition pruning. While it is primarily used - for partitioning implemented using the legacy inheritance method, it can be - used for other purposes, including with declarative partitioning. + technique similar to partition pruning. It is primarily used + for partitioning implemented using the legacy inheritance method. diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 53a657c0ae..a95153fea7 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -2309,7 +2309,6 @@ _outPlannerInfo(StringInfo str, const PlannerInfo *node) WRITE_FLOAT_FIELD(tuple_fraction, "%.4f"); WRITE_FLOAT_FIELD(limit_tuples, "%.0f"); WRITE_UINT_FIELD(qual_security_level); - WRITE_ENUM_FIELD(inhTargetKind, InheritanceKind); WRITE_BOOL_FIELD(hasJoinRTEs); WRITE_BOOL_FIELD(hasLateralRTEs); WRITE_BOOL_FIELD(hasDeletedRTEs); diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 738bb30848..8de8d383f9 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -36,6 +36,7 @@ #include "optimizer/pathnode.h" #include "optimizer/paths.h" #include "optimizer/plancat.h" +#include "optimizer/planmain.h" #include "optimizer/planner.h" #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" @@ -119,6 +120,9 @@ static void set_namedtuplestore_pathlist(PlannerInfo *root, RelOptInfo *rel, static void set_worktable_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte); static RelOptInfo *make_rel_from_joinlist(PlannerInfo *root, List *joinlist); +static void inheritance_make_rel_from_joinlist(PlannerInfo *root, + RelOptInfo *parent, + List *joinlist); static bool subquery_is_pushdown_safe(Query *subquery, Query *topquery, pushdown_safety_info *safetyInfo); static bool recurse_pushdown_safe(Node *setOp, Query *topquery, @@ -217,13 +221,28 @@ make_one_rel(PlannerInfo *root, List *joinlist) /* * Generate access paths for the entire join tree. + * + * For UPDATE/DELETE on an inheritance parent, join paths should be + * generated for each child result rel separately. */ - rel = make_rel_from_joinlist(root, joinlist); + if (root->parse->resultRelation && + root->simple_rte_array[root->parse->resultRelation]->inh) + { + rel = root->simple_rel_array[root->parse->resultRelation]; + inheritance_make_rel_from_joinlist(root, rel, joinlist); + Assert(list_length(root->inh_target_child_rels) == + list_length(root->inh_target_child_joinrels)); + } + else + { + rel = make_rel_from_joinlist(root, joinlist); - /* - * The result should join all and only the query's base rels. - */ - Assert(bms_equal(rel->relids, root->all_baserels)); + /* + * The result should join all and only the query's base rels. + */ + Assert(bms_equal(rel->relids, root->all_baserels)); + + } return rel; } @@ -1219,6 +1238,63 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, set_rel_consider_parallel(root, childrel, childRTE); /* + * If the root parent is the result relation, we need a reltarget + * for the child that will be suitable to use the child as + * target relation. + */ + if (childrel->inh_root_parent > 0 && + childrel->inh_root_parent == root->parse->resultRelation) + { + Query *parse, + *orig_parse = root->parse; + List *tlist; + List *other_exprs; + ListCell *lc2; + Relids child_relids, + top_parent_relids; + + /* + * We'd like to build the reltarget afresh; save the translated + * version of parent's expressions aside. + */ + other_exprs = childrel->reltarget->exprs; + childrel->reltarget->exprs = NIL; + + /* + * Translate the original query's expressions to this child, + * mainly for updating the childrel's reltarget so that it + * contains necessary expressions for using childrel as target + * relation correctly. We need to use multilevel translation + * as we can only access the original version of the query. + */ + child_relids = bms_make_singleton(appinfo->child_relid); + top_parent_relids = bms_make_singleton(childrel->inh_root_parent); + parse = (Query *) + adjust_appendrel_attrs_multilevel(root, + (Node *) root->parse, + child_relids, + top_parent_relids); + root->parse = parse; + tlist = preprocess_targetlist(root); + build_base_rel_tlists(root, tlist); + root->parse = orig_parse; + + /* + * Some of the expressions in parent's reltarget might not be + * in child's just built expressions. Check to add any that are + * missing. + */ + foreach(lc2, other_exprs) + { + Expr *expr = lfirst(lc2); + + if (!list_member(childrel->reltarget->exprs, expr)) + childrel->reltarget->exprs = + lappend(childrel->reltarget->exprs, expr); + } + } + + /* * Compute the child's size. */ set_rel_size(root, childrel, childRTindex, childRTE); @@ -2624,6 +2700,132 @@ generate_gather_paths(PlannerInfo *root, RelOptInfo *rel, bool override_rows) } /* + * inheritance_make_rel_from_joinlist + * Perform join planning for all non-dummy leaf inheritance children + * in their role as query's target relation + * + * If a child relation is a partitioned table, its children are processed in + * turn by recursively calling this function. + */ +static void +inheritance_make_rel_from_joinlist(PlannerInfo *root, + RelOptInfo *parent, + List *joinlist) +{ + List *join_info_list = root->join_info_list; + ListCell *lc; +#ifdef USE_ASSERT_CHECKING + int top_parent_relid = parent->inh_root_parent > 0 ? + parent->inh_root_parent : + parent->relid; + Relids all_baserels; +#endif + + /* + * For UPDATE/DELETE queries, the top parent can only ever be a table. + * As a contrast, it could be a UNION ALL subquery in the case of SELECT. + */ + Assert(planner_rt_fetch(top_parent_relid, root)->rtekind == RTE_RELATION); + + /* Nothing to do. */ + if (IS_DUMMY_REL(parent)) + return; + + foreach(lc, root->append_rel_list) + { + RelOptInfo *childrel; + AppendRelInfo *appinfo = lfirst(lc); + List *translated_joinlist; + Relids child_relids, + top_parent_relids; + + /* Only consider this parent's children. */ + if (appinfo->parent_relid != parent->relid) + continue; + + childrel = find_base_rel(root, appinfo->child_relid); + + /* Ignore excluded/pruned children. */ + if (IS_DUMMY_REL(childrel)) + continue; + + /* + * Sub-partitions have to be processed recursively, because + * AppendRelInfos link sub-partitions to their immediate parents, not + * the root partitioned table. + */ + if (childrel->part_scheme != NULL) + { + inheritance_make_rel_from_joinlist(root, childrel, joinlist); + continue; + } + + /* + * Modify joinlist such that relations joined to the top parent rel + * appear to be joined to the child rel instead. Do the same for + * any SpecialJoinInfo structs. + */ + child_relids = bms_make_singleton(appinfo->child_relid); + Assert(childrel->inh_root_parent > 0); + top_parent_relids = bms_make_singleton(childrel->inh_root_parent); + translated_joinlist = (List *) + adjust_appendrel_attrs_multilevel(root, + (Node *) joinlist, + child_relids, + top_parent_relids); + root->join_info_list = (List *) + adjust_appendrel_attrs_multilevel(root, + (Node *) join_info_list, + child_relids, + top_parent_relids); + + /* + * Since we added the child rel directly into the join tree, we must + * modify it to be a "base" rel instead of an "other" rel, which the + * join planning code expects the relations being joined to be. + * + * NB: Do we need to change the child EC members to be marked + * as non-child somehow? + */ + childrel->reloptkind = RELOPT_BASEREL; + + /* Reset join planning specific data structures. */ + root->join_rel_list = NIL; + root->join_rel_hash = NULL; + + /* Perform join planning and save the resulting RelOptInfo. */ + childrel = make_rel_from_joinlist(root, translated_joinlist); + + /* + * Remember this child target rel. inheritance_planner will perform + * the remaining steps of planning for each child relation separately. + * Specifically, it will call grouping_planner on every + * RelOptInfo contained in the inh_target_child_joinrels list, each + * of which represents the source of tuples to be modified for a given + * target child rel. + */ + root->inh_target_child_rels = lappend_int(root->inh_target_child_rels, + appinfo->child_relid); + root->inh_target_child_joinrels = + lappend(root->inh_target_child_joinrels, childrel); + +#ifdef USE_ASSERT_CHECKING + /* + * The following implements essentially the same Assert as in + * make_one_rel, our caller. + */ + all_baserels = bms_copy(root->all_baserels); + all_baserels = bms_del_member(all_baserels, top_parent_relid); + all_baserels = bms_add_member(all_baserels, appinfo->child_relid); + Assert(bms_equal(childrel->relids, all_baserels)); +#endif + + /* Restore the original for processing the next child. */ + root->join_info_list = join_info_list; + } +} + +/* * make_rel_from_joinlist * Build access paths using a "joinlist" to guide the join path search. * diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index da7a92081a..28c4b53fea 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -1998,12 +1998,7 @@ create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path) /* * During setrefs.c, we'll need the grouping_map to fix up the cols lists * in GroupingFunc nodes. Save it for setrefs.c to use. - * - * This doesn't work if we're in an inheritance subtree (see notes in - * create_modifytable_plan). Fortunately we can't be because there would - * never be grouping in an UPDATE/DELETE; but let's Assert that. */ - Assert(root->inhTargetKind == INHKIND_NONE); Assert(root->grouping_map == NULL); root->grouping_map = grouping_map; @@ -2160,12 +2155,7 @@ create_minmaxagg_plan(PlannerInfo *root, MinMaxAggPath *best_path) * with InitPlan output params. (We can't just do that locally in the * MinMaxAgg node, because path nodes above here may have Agg references * as well.) Save the mmaggregates list to tell setrefs.c to do that. - * - * This doesn't work if we're in an inheritance subtree (see notes in - * create_modifytable_plan). Fortunately we can't be because there would - * never be aggregates in an UPDATE/DELETE; but let's Assert that. */ - Assert(root->inhTargetKind == INHKIND_NONE); Assert(root->minmax_aggs == NIL); root->minmax_aggs = best_path->mmaggregates; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index c729a99f8b..93b8c761c2 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -37,6 +37,7 @@ #ifdef OPTIMIZER_DEBUG #include "nodes/print.h" #endif +#include "nodes/relation.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" #include "optimizer/pathnode.h" @@ -126,7 +127,7 @@ 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); + RelOptInfo *planned_rel, 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); @@ -630,7 +631,6 @@ subquery_planner(PlannerGlobal *glob, Query *parse, root->grouping_map = NULL; root->minmax_aggs = NIL; root->qual_security_level = 0; - root->inhTargetKind = INHKIND_NONE; root->hasRecursion = hasRecursion; if (hasRecursion) root->wt_param_id = SS_assign_special_param(root); @@ -970,7 +970,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse, rt_fetch(parse->resultRelation, parse->rtable)->inh) inheritance_planner(root); else - grouping_planner(root, false, tuple_fraction); + grouping_planner(root, false, NULL, tuple_fraction); /* * Capture the set of outer-level param IDs we have access to, for use in @@ -1146,12 +1146,19 @@ preprocess_phv_expression(PlannerInfo *root, Expr *expr) * 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. + * is an inheritance set. That's mainly because for UPDATE, each target + * relation needs a different targetlist matching its own column set. So, we + * must modify the source scan/join path for each target relation such that it + * produces the desired target list. + * + * The source scan/join paths for individual target relations are still + * created in allpaths.c by first expanding the inheritance in the usual way + * by set_append_rel_size, followed by join planning for each target relation + * separately in make_one_rel. Finally, we apply grouping_planner here to each + * child scan/join path so that it produces the desired targetlist. + * + * 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. @@ -1164,14 +1171,8 @@ inheritance_planner(PlannerInfo *root) { Query *parse = root->parse; int top_parentRTindex = parse->resultRelation; - Bitmapset *subqueryRTindexes; - Bitmapset *modifiableARIindexes; int nominalRelation = -1; Index rootRelation = 0; - List *final_rtable = 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; @@ -1179,70 +1180,60 @@ inheritance_planner(PlannerInfo *root) List *returningLists = NIL; List *rowMarks; RelOptInfo *final_rel; - ListCell *lc; - Index rti; + ListCell *lc1, + *lc2; RangeTblEntry *parent_rte; - PlannerInfo *parent_root; - Query *parent_parse; - Bitmapset *parent_relids = bms_make_singleton(top_parentRTindex); - PlannerInfo **parent_roots = NULL; + List *tlist, + *orig_tlist; + standard_qp_extra qp_extra; + RelOptInfo *planned_rel; + List *partitioned_rels = NIL; + /* Inheritance is never used for insert. */ Assert(parse->commandType != CMD_INSERT); + parent_rte = planner_rt_fetch(top_parentRTindex, root); /* - * 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. + * Generate the access paths for all relations mentioned in the query, + * including the target inheritance set. When doing the join planning, + * references in the join tree to the original target relation that's the + * root parent of the inheritance tree is replaced by each of its + * inheritance children and the resulting joinrel RelOptInfo's are + * added to root->inh_target_child_joinrels. * - * To begin with, generate a bitmapset of the relids of the subquery RTEs. + * Final planning steps (grouping_planner) are applied to the best path + * of each of those child joinrels using a modified instance of the + * original query for a given child target rel. All the paths so generated + * are put into a list that will be controlled by a single ModifyTable + * node. All the instances share the same rangetable. */ - 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++; - } /* - * Next, we want to identify which AppendRelInfo items contain references - * to any of the aforesaid subquery RTEs. These items will need to be - * copied and modified to adjust their subquery references; whereas the - * other ones need not be touched. It's worth being tense over this - * because we can usually avoid processing most of the AppendRelInfo - * items, thereby saving O(N^2) space and time when the target is a large - * inheritance tree. We can identify AppendRelInfo items by their - * child_relid, since that should be unique within the list. + * Save the unmodified version of the query's targetlist to be used below + * for passing to grouping_planner for each child target relation. */ - modifiableARIindexes = NULL; - if (subqueryRTindexes != NULL) - { - foreach(lc, root->append_rel_list) - { - AppendRelInfo *appinfo = lfirst_node(AppendRelInfo, lc); + orig_tlist = list_copy(root->parse->targetList); - if (bms_is_member(appinfo->parent_relid, subqueryRTindexes) || - bms_is_member(appinfo->child_relid, subqueryRTindexes) || - bms_overlap(pull_varnos((Node *) appinfo->translated_vars), - subqueryRTindexes)) - modifiableARIindexes = bms_add_member(modifiableARIindexes, - appinfo->child_relid); - } + /* Do the scan/join planning. */ + tlist = preprocess_targetlist(root); + qp_extra.tlist = tlist; + qp_extra.activeWindows = qp_extra.groupClause = NIL; + planned_rel = query_planner(root, tlist, standard_qp_callback, &qp_extra); + + /* + * If it turned out during query planning that all the children are dummy + * (pruned or excluded by constraints), no need to do the steps below. + * Let grouping_planner finish up the final path. + */ + if (IS_DUMMY_REL(planned_rel)) + { + grouping_planner(root, false, planned_rel, 0.0); + return; } + Assert(planned_rel->relid == top_parentRTindex); + Assert(planned_rel->reloptkind == RELOPT_BASEREL); + /* * If the parent RTE is a partitioned table, we should use that as the * nominal target relation, because the RTEs added for partitioned tables @@ -1250,67 +1241,50 @@ inheritance_planner(PlannerInfo *root) * 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, root->parse->rtable); if (parent_rte->relkind == RELKIND_PARTITIONED_TABLE) { nominalRelation = top_parentRTindex; rootRelation = top_parentRTindex; + partitioned_rels = lappend(partitioned_rels, + list_make1_int(top_parentRTindex)); } /* - * The PlannerInfo for each child is obtained by translating the relevant - * members of the PlannerInfo for its immediate parent, which we find - * using the parent_relid in its AppendRelInfo. We save the PlannerInfo - * for each parent in an array indexed by relid for fast retrieval. Since - * the maximum number of parents is limited by the number of RTEs in the - * query, we use that number to allocate the array. An extra entry is - * needed since relids start from 1. + * Get on with finalizing the path for each child target relation by + * calling grouping_planner on its joinrel. Note that we're restoring + * the query's targetlist to the original one for grouping_planner's + * targetlist expansion steps to perform the expansion with individual + * child descriptors. */ - parent_roots = (PlannerInfo **) palloc0((list_length(parse->rtable) + 1) * - sizeof(PlannerInfo *)); - parent_roots[top_parentRTindex] = root; - - /* - * And now we can get on with generating a plan for each child table. - */ - foreach(lc, root->append_rel_list) + parse->targetList = orig_tlist; + forboth(lc1, root->inh_target_child_rels, + lc2, root->inh_target_child_joinrels) { - AppendRelInfo *appinfo = lfirst_node(AppendRelInfo, lc); + AppendRelInfo *appinfo = root->append_rel_array[lfirst_int(lc1)]; + RelOptInfo *child_joinrel = lfirst(lc2); PlannerInfo *subroot; RangeTblEntry *child_rte; RelOptInfo *sub_final_rel; Path *subpath; - - /* append_rel_list contains all append rels; ignore others */ - if (!bms_is_member(appinfo->parent_relid, parent_relids)) - continue; - - /* - * expand_inherited_rtentry() always processes a parent before any of - * that parent's children, so the parent_root for this relation should - * already be available. - */ - parent_root = parent_roots[appinfo->parent_relid]; - Assert(parent_root != NULL); - parent_parse = parent_root->parse; + Relids child_relids = bms_make_singleton(appinfo->child_relid), + top_parent_relids = bms_make_singleton(top_parentRTindex); /* * 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, parent_root, sizeof(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. + * references to the parent RTE to refer to the current child RTE. */ subroot->parse = (Query *) - adjust_appendrel_attrs(parent_root, - (Node *) parent_parse, - 1, &appinfo); + adjust_appendrel_attrs_multilevel(root, (Node *) parse, + child_relids, + top_parent_relids); /* * If there are securityQuals attached to the parent, move them to the @@ -1322,33 +1296,10 @@ inheritance_planner(PlannerInfo *root) parent_rte->securityQuals = NIL; /* - * Mark whether we're planning a query to a partitioned table or an - * inheritance parent. + * inheritance_make_rel_from_joinlist only adds the leaf partitions to + * PlannerInfo. */ - 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_relids = bms_add_member(parent_relids, appinfo->child_relid); - parent_roots[appinfo->child_relid] = subroot; - - continue; - } + Assert(!child_rte->inh); /* * Set the nominal target relation of the ModifyTable node if not @@ -1374,111 +1325,8 @@ inheritance_planner(PlannerInfo *root) if (nominalRelation < 0) nominalRelation = appinfo->child_relid; - /* - * The rowMarks list might contain references to subquery RTEs, so - * make a copy that we can apply ChangeVarNodes to. (Fortunately, the - * executor doesn't need to see the modified copies --- we can just - * pass it the original rowMarks list.) - */ - subroot->rowMarks = copyObject(parent_root->rowMarks); - - /* - * The append_rel_list likewise might contain references to subquery - * RTEs (if any subqueries were flattenable UNION ALLs). So prepare - * to apply ChangeVarNodes to that, too. As explained above, we only - * want to copy items that actually contain such references; the rest - * can just get linked into the subroot's append_rel_list. - * - * If we know there are no such references, we can just use the outer - * append_rel_list unmodified. - */ - if (modifiableARIindexes != NULL) - { - ListCell *lc2; - - subroot->append_rel_list = NIL; - foreach(lc2, parent_root->append_rel_list) - { - AppendRelInfo *appinfo2 = lfirst_node(AppendRelInfo, lc2); - - if (bms_is_member(appinfo2->child_relid, modifiableARIindexes)) - appinfo2 = copyObject(appinfo2); - - subroot->append_rel_list = lappend(subroot->append_rel_list, - appinfo2); - } - } - - /* - * Add placeholders to the child Query's rangetable list to fill the - * RT indexes already reserved for subqueries in previous children. - * These won't be referenced, so there's no need to make them very - * valid-looking. - */ - while (list_length(subroot->parse->rtable) < list_length(final_rtable)) - subroot->parse->rtable = lappend(subroot->parse->rtable, - makeNode(RangeTblEntry)); - - /* - * If this isn't the first child Query, generate duplicates of all - * subquery RTEs, and adjust Var numbering to reference the - * duplicates. To simplify the loop logic, we scan the original rtable - * not the copy just made by adjust_appendrel_attrs; that should be OK - * since subquery RTEs couldn't contain any references to the target - * rel. - */ - if (final_rtable != NIL && subqueryRTindexes != NULL) - { - ListCell *lr; - - rti = 1; - foreach(lr, parent_parse->rtable) - { - RangeTblEntry *rte = lfirst_node(RangeTblEntry, lr); - - if (bms_is_member(rti, subqueryRTindexes)) - { - Index newrti; - - /* - * The RTE can't contain any references to its own RT - * index, except in its securityQuals, so we can save a - * few cycles by applying ChangeVarNodes to the rest of - * the rangetable before we append the RTE to it. - */ - newrti = list_length(subroot->parse->rtable) + 1; - ChangeVarNodes((Node *) subroot->parse, rti, newrti, 0); - ChangeVarNodes((Node *) subroot->rowMarks, rti, newrti, 0); - /* Skip processing unchanging parts of append_rel_list */ - if (modifiableARIindexes != NULL) - { - ListCell *lc2; - - foreach(lc2, subroot->append_rel_list) - { - AppendRelInfo *appinfo2 = lfirst_node(AppendRelInfo, lc2); - - if (bms_is_member(appinfo2->child_relid, - modifiableARIindexes)) - ChangeVarNodes((Node *) appinfo2, rti, newrti, 0); - } - } - rte = copyObject(rte); - ChangeVarNodes((Node *) rte->securityQuals, rti, newrti, 0); - subroot->parse->rtable = lappend(subroot->parse->rtable, - rte); - } - rti++; - } - } - - /* 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 */ ); + /* Apply the top-level targetlist to child_joinrel's path. */ + grouping_planner(subroot, true, child_joinrel, 0.0); /* * Select cheapest path in case there's more than one. We always run @@ -1490,45 +1338,10 @@ inheritance_planner(PlannerInfo *root) subpath = sub_final_rel->cheapest_total_path; /* - * If this child rel was excluded by constraint exclusion, exclude it - * from the result plan. + * child rel cannot be empty, or inheritance_make_rel_from_joinlist + * wouldn't have put it in the list to begin with. */ - if (IS_DUMMY_PATH(subpath)) - continue; - - /* - * If this is the first non-excluded child, its post-planning rtable - * becomes the initial contents of final_rtable; otherwise, append - * just its modified subquery RTEs to final_rtable. - */ - if (final_rtable == NIL) - final_rtable = subroot->parse->rtable; - else - final_rtable = list_concat(final_rtable, - list_copy_tail(subroot->parse->rtable, - list_length(final_rtable))); - - /* - * 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 (previous ones are too short), 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 */ - root->init_plans = subroot->init_plans; + Assert(!IS_DUMMY_PATH(subpath)); /* Build list of sub-paths */ subpaths = lappend(subpaths, subpath); @@ -1560,36 +1373,6 @@ inheritance_planner(PlannerInfo *root) */ /* - * If we managed to exclude every child rel, return a dummy plan; it - * doesn't even need a ModifyTable node. - */ - if (subpaths == NIL) - { - set_dummy_rel_pathlist(final_rel); - return; - } - - /* - * Put back the final adjusted rtable into the master copy of the Query. - * (We mustn't do this if we found no non-excluded children.) - */ - 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 master'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; - } - - /* * 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. @@ -1629,6 +1412,12 @@ inheritance_planner(PlannerInfo *root) * (inheritance_planner will create a single ModifyTable node covering all the * target tables.) * + * If non-NULL, planned_rel is a RelOptInfo containing paths for the query's + * top-level joinrel, which the caller produced by itself. In that case, this + * function only needs to adjust the targetlist of its cheapest_total_path. + * The only caller that may pass such a RelOptInfo currently is + * inheritance_planner. + * * 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) @@ -1647,6 +1436,7 @@ inheritance_planner(PlannerInfo *root) */ static void grouping_planner(PlannerInfo *root, bool inheritance_update, + RelOptInfo *planned_rel, double tuple_fraction) { Query *parse = root->parse; @@ -1659,7 +1449,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update, List *final_targets; List *final_targets_contain_srfs; bool final_target_parallel_safe; - RelOptInfo *current_rel; + RelOptInfo *current_rel = planned_rel; RelOptInfo *final_rel; ListCell *lc; @@ -1699,6 +1489,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update, * special work for recursive unions is the responsibility of * plan_set_operations. */ + Assert(current_rel == NULL); current_rel = plan_set_operations(root); /* @@ -1878,8 +1669,9 @@ grouping_planner(PlannerInfo *root, bool inheritance_update, * We also generate (in standard_qp_callback) pathkey representations * of the query's sort clause, distinct clause, etc. */ - current_rel = query_planner(root, tlist, - standard_qp_callback, &qp_extra); + if (current_rel == NULL) + current_rel = query_planner(root, tlist, + standard_qp_callback, &qp_extra); /* * Convert the query's result tlist into PathTarget format. diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index cd6e11904e..78baec00dc 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -917,7 +917,6 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, subroot->grouping_map = NULL; subroot->minmax_aggs = NIL; subroot->qual_security_level = 0; - subroot->inhTargetKind = INHKIND_NONE; subroot->hasRecursion = false; subroot->wt_param_id = -1; subroot->non_recursive_path = NULL; diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index d5720518a8..ef1f978889 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -2253,8 +2253,39 @@ adjust_appendrel_attrs_mutator(Node *node, context->appinfos); return (Node *) phv; } + + /* + * This is needed, because inheritance_make_rel_from_joinlist needs to + * translate root->join_info_list executing make_rel_from_joinlist for a + * given child. + */ + if (IsA(node, SpecialJoinInfo)) + { + SpecialJoinInfo *oldinfo = (SpecialJoinInfo *) node; + SpecialJoinInfo *newinfo = makeNode(SpecialJoinInfo); + + memcpy(newinfo, oldinfo, sizeof(SpecialJoinInfo)); + newinfo->min_lefthand = adjust_child_relids(oldinfo->min_lefthand, + context->nappinfos, + context->appinfos); + newinfo->min_righthand = adjust_child_relids(oldinfo->min_righthand, + context->nappinfos, + context->appinfos); + newinfo->syn_lefthand = adjust_child_relids(oldinfo->syn_lefthand, + context->nappinfos, + context->appinfos); + newinfo->syn_righthand = adjust_child_relids(oldinfo->syn_righthand, + context->nappinfos, + context->appinfos); + newinfo->semi_rhs_exprs = + (List *) expression_tree_mutator((Node *) + oldinfo->semi_rhs_exprs, + adjust_appendrel_attrs_mutator, + (void *) context); + return (Node *) newinfo; + } + /* Shouldn't need to handle planner auxiliary nodes here */ - Assert(!IsA(node, SpecialJoinInfo)); Assert(!IsA(node, AppendRelInfo)); Assert(!IsA(node, PlaceHolderInfo)); Assert(!IsA(node, MinMaxAggInfo)); diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 0c88c90de4..ed0953f9e1 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -1264,36 +1264,6 @@ get_relation_constraints(PlannerInfo *root, } } - /* - * Append partition predicates, if any. - * - * For selects, partition pruning uses the parent table's partition bound - * descriptor, instead of constraint exclusion which is driven by the - * individual partition's partition constraint. - */ - if (enable_partition_pruning && root->parse->commandType != CMD_SELECT) - { - List *pcqual = RelationGetPartitionQual(relation); - - if (pcqual) - { - /* - * Run the partition quals through const-simplification similar to - * check constraints. We skip canonicalize_qual, though, because - * partition quals should be in canonical form already; also, - * since the qual is in implicit-AND format, we'd have to - * explicitly convert it to explicit-AND format and back again. - */ - pcqual = (List *) eval_const_expressions(root, (Node *) pcqual); - - /* Fix Vars to have the desired varno */ - if (varno != 1) - ChangeVarNodes((Node *) pcqual, 1, varno, 0); - - result = list_concat(result, pcqual); - } - } - heap_close(relation, NoLock); return result; @@ -1420,31 +1390,15 @@ relation_excluded_by_constraints(PlannerInfo *root, switch (constraint_exclusion) { case CONSTRAINT_EXCLUSION_OFF: - - /* - * Don't prune if feature turned off -- except if the relation is - * a partition. While partprune.c-style partition pruning is not - * yet in use for all cases (update/delete is not handled), it - * would be a UI horror to use different user-visible controls - * depending on such a volatile implementation detail. Therefore, - * for partitioned tables we use enable_partition_pruning to - * control this behavior. - */ - if (root->inhTargetKind == INHKIND_PARTITIONED) - break; return false; case CONSTRAINT_EXCLUSION_PARTITION: /* * When constraint_exclusion is set to 'partition' we only handle - * OTHER_MEMBER_RELs, or BASERELs in cases where the result target - * is an inheritance parent or a partitioned table. + * OTHER_MEMBER_RELs. */ - if ((rel->reloptkind != RELOPT_OTHER_MEMBER_REL) && - !(rel->reloptkind == RELOPT_BASEREL && - root->inhTargetKind != INHKIND_NONE && - rel->relid == root->parse->resultRelation)) + if (rel->reloptkind != RELOPT_OTHER_MEMBER_REL) return false; break; diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 32ac3315a4..29339d6c72 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -319,9 +319,6 @@ typedef struct PlannerInfo Index qual_security_level; /* minimum security_level for quals */ /* Note: qual_security_level is zero if there are no securityQuals */ - InheritanceKind inhTargetKind; /* indicates if the target relation is an - * inheritance child or partition or a - * partitioned table */ bool hasJoinRTEs; /* true if any RTEs are RTE_JOIN kind */ bool hasLateralRTEs; /* true if any RTEs are marked LATERAL */ bool hasDeletedRTEs; /* true if any RTE was deleted from jointree */ @@ -343,6 +340,21 @@ typedef struct PlannerInfo /* Does this query modify any partition key columns? */ bool partColsUpdated; + + /* + * The following fields are set during query planning portion of an + * inherited UPDATE/DEELETE operation. + */ + + /* RT indexes of child target rels */ + List *inh_target_child_rels; + + /* + * RelOptInfos representing the output of make_rel_from_joinlist() for + * a each child rel in the above list, acting as the target relation in + * place of the original parent target relation. + */ + List *inh_target_child_joinrels; } PlannerInfo; diff --git a/src/test/regress/expected/partition_join.out b/src/test/regress/expected/partition_join.out index 3ba3aaf2d8..a539280851 100644 --- a/src/test/regress/expected/partition_join.out +++ b/src/test/regress/expected/partition_join.out @@ -1781,7 +1781,7 @@ WHERE EXISTS ( Filter: (c IS NULL) -> Nested Loop -> Seq Scan on int4_tbl - -> Subquery Scan on ss_1 + -> Subquery Scan on ss -> Limit -> Seq Scan on int8_tbl int8_tbl_1 -> Nested Loop Semi Join @@ -1789,7 +1789,7 @@ WHERE EXISTS ( Filter: (c IS NULL) -> Nested Loop -> Seq Scan on int4_tbl - -> Subquery Scan on ss_2 + -> Subquery Scan on ss -> Limit -> Seq Scan on int8_tbl int8_tbl_2 (28 rows) -- 2.11.0