From d23d4740b6a0eb533205f8c94e3509d23609991e Mon Sep 17 00:00:00 2001 From: amit Date: Fri, 24 Aug 2018 12:39:36 +0900 Subject: [PATCH v3 1/4] Overhaul inheritance update/delete planning Current method, inheritance_planner, applies grouping_planner and hence query_planner to the query repeatedly with each child relation replacing the root parent as the query's result relation. One big drawback of this approach is that it cannot use partprune.c to perform partition pruning when the root parent is a partitioned table, because it can only be invoked if query_planner sees the partitioned relation itself in the query. That is not true with how inheritance_planner invokes query_planner, which it does after replacing the partitioned relation with individual leaf partitions. While most of the work in each repitition of grouping_planner (and query_planner) is same, a couple of things may differ from one child relation to another -- 1. Join planning may produce different Paths when joining against different child result partitions, 2. grouping_planner may produce different top-level target lists for different child relations, based on their own TupleDescs. 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 | 15 +- src/backend/nodes/outfuncs.c | 1 - src/backend/optimizer/path/allpaths.c | 224 +++++- src/backend/optimizer/plan/createplan.c | 10 - src/backend/optimizer/plan/planmain.c | 4 +- src/backend/optimizer/plan/planner.c | 1018 ++++++++++---------------- src/backend/optimizer/prep/prepjointree.c | 1 - src/backend/optimizer/prep/prepunion.c | 28 +- src/backend/optimizer/util/plancat.c | 50 +- src/include/nodes/relation.h | 6 +- src/test/regress/expected/partition_join.out | 4 +- 11 files changed, 631 insertions(+), 730 deletions(-) diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml index b5ed1b7939..53c479fbb8 100644 --- a/doc/src/sgml/ddl.sgml +++ b/doc/src/sgml/ddl.sgml @@ -3933,16 +3933,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate >= DATE '2008-01-01'; setting. - - - 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 +3954,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 b5af904c18..3f2339a3ea 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 5db1688bf0..968cac42a3 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 RelOptInfo *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, @@ -181,13 +185,27 @@ 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) + { + RelOptInfo *targetrel = root->simple_rel_array[root->parse->resultRelation]; - /* - * The result should join all and only the query's base rels. - */ - Assert(bms_equal(rel->relids, root->all_baserels)); + rel = inheritance_make_rel_from_joinlist(root, targetrel, joinlist); + } + 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)); + + } return rel; } @@ -868,7 +886,11 @@ static void set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte) { + Relids top_parent_relids = rel->top_parent_relids; int parentRTindex = rti; + int rootParentRTindex = top_parent_relids ? + bms_singleton_member(top_parent_relids) : + parentRTindex; bool has_live_children; double parent_rows; double parent_size; @@ -968,23 +990,6 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL); /* - * Copy/Modify targetlist. Even if this child is deemed empty, we need - * its targetlist in case it falls on nullable side in a child-join - * because of partitionwise join. - * - * NB: the resulting childrel->reltarget->exprs may contain arbitrary - * expressions, which otherwise would not occur in a rel's targetlist. - * Code that might be looking at an appendrel child must cope with - * such. (Normally, a rel's targetlist would only include Vars and - * PlaceHolderVars.) XXX we do not bother to update the cost or width - * fields of childrel->reltarget; not clear if that would be useful. - */ - childrel->reltarget->exprs = (List *) - adjust_appendrel_attrs(root, - (Node *) rel->reltarget->exprs, - 1, &appinfo); - - /* * We have to make child entries in the EquivalenceClass data * structures as well. This is needed either if the parent * participates in some eclass joins (because we will want to consider @@ -1110,9 +1115,28 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, childrel->baserestrictinfo = childquals; childrel->baserestrict_min_security = cq_min_security; - if (have_const_false_cq) + /* + * Mark the child as empty in certain cases, such as if a restriction + * clause reduced to constant FALSE or NULL, or if the partition + * of a partitioned table has been pruned, or if constaint exclusion + * pruned it. + */ + if (have_const_false_cq || + (did_pruning && + !bms_is_member(appinfo->child_relid, live_children)) || + relation_excluded_by_constraints(root, childrel, childRTE)) { /* + * The child relation won't be scanned, but certain partitionwise + * planning operations require its reltarget to contain valid + * expressions, so oblige. + */ + childrel->reltarget->exprs = (List *) + adjust_appendrel_attrs(root, + (Node *) rel->reltarget->exprs, + 1, &appinfo); + + /* * Some restriction clause reduced to constant FALSE or NULL after * substitution, so this child need not be scanned. */ @@ -1120,22 +1144,56 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, continue; } - if (did_pruning && !bms_is_member(appinfo->child_relid, live_children)) + /* + * Copy/Modify targetlist. Even if this child is deemed empty, we need + * its targetlist in case it falls on nullable side in a child-join + * because of partitionwise join. + * + * NB: the resulting childrel->reltarget->exprs may contain arbitrary + * expressions, which otherwise would not occur in a rel's targetlist. + * Code that might be looking at an appendrel child must cope with + * such. (Normally, a rel's targetlist would only include Vars and + * PlaceHolderVars.) XXX we do not bother to update the cost or width + * fields of childrel->reltarget; not clear if that would be useful. + */ + if (rootParentRTindex == root->parse->resultRelation) { - /* This partition was pruned; skip it. */ - set_dummy_rel_pathlist(childrel); - continue; - } + Query *parse, + *orig_parse = root->parse; + List *tlist; + List *other_exprs; + ListCell *lc2; + + parse = (Query *) + adjust_appendrel_attrs(root, + (Node *) root->parse, + 1, &appinfo); + root->parse = parse; + tlist = preprocess_targetlist(root); + build_base_rel_tlists(root, tlist); + root->parse = orig_parse; - if (relation_excluded_by_constraints(root, childrel, childRTE)) - { /* - * This child need not be scanned, so we can omit it from the - * appendrel. + * Now add members of parent's reltarget->exprs, not already in + * child's. */ - set_dummy_rel_pathlist(childrel); - continue; + other_exprs = (List *) + adjust_appendrel_attrs(root, + (Node *) rel->reltarget->exprs, + 1, &appinfo); + foreach(lc2, other_exprs) + { + if (!list_member(childrel->reltarget->exprs, lfirst(lc2))) + childrel->reltarget->exprs = + lappend(childrel->reltarget->exprs, + lfirst(lc2)); + } } + else + childrel->reltarget->exprs = (List *) + adjust_appendrel_attrs(root, + (Node *) rel->reltarget->exprs, + 1, &appinfo); /* CE failed, so finish copying/modifying join quals. */ childrel->joininfo = (List *) @@ -2570,6 +2628,104 @@ generate_gather_paths(PlannerInfo *root, RelOptInfo *rel, bool override_rows) } /* + * inheritance_make_rel_from_joinlist + * Invokes make_rel_from_joinlist for each parent's child relations + * + * This function is called recursively if a child relation is itself a + * partitioned table. + */ +static RelOptInfo * +inheritance_make_rel_from_joinlist(PlannerInfo *root, + RelOptInfo *parent, + List *joinlist) +{ + ListCell *lc; + + foreach(lc, root->append_rel_list) + { + RelOptInfo *childrel; + AppendRelInfo *appinfo = lfirst(lc); + List *translated_joinlist; + List *saved_join_info_list = root->join_info_list; + + /* 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; + + /* + * Modify joinlist such that relations joined to parent rel appear to + * be joined to child rel instead. + */ + translated_joinlist = (List *) + adjust_appendrel_attrs(root, + (Node *) joinlist, + 1, &appinfo); + + /* + * Since we modified the join tree to include child rel, we can + * modify it to be a "base" rel instead of an "other" rel, which + * join planning code expects the callers to do anyway. + */ + childrel->reloptkind = RELOPT_BASEREL; + root->join_info_list = (List *) + adjust_appendrel_attrs(root, + (Node *) root->join_info_list, + 1, &appinfo); + + /* + * Recurse if the child is itself a partitioned table. We must, + * because grandchildren can only be processed this way. + */ + if (childrel->part_scheme != NULL) + childrel = + inheritance_make_rel_from_joinlist(root, childrel, + translated_joinlist); + else + { +#ifdef USE_ASSERT_CHECKING + Relids all_baserels; + Index top_parent_relid; +#endif + + /* Reset interal join planning data structures. */ + root->join_rel_list = NIL; + root->join_rel_hash = NULL; + + /* Perform join planning and save the resulting joinrel. */ + childrel = make_rel_from_joinlist(root, translated_joinlist); + +#ifdef USE_ASSERT_CHECKING + all_baserels = bms_copy(root->all_baserels); + top_parent_relid = parent->top_parent_relids ? + bms_singleton_member(parent->top_parent_relids) : + parent->relid; + 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 + } + + /* + * Save child joinrel to be processed later in + * inheritance_adjust_scanjoin_target, which adjusts its paths to + * be able to emit expressions in query's top-level target list. + */ + root->inh_target_child_rels = lappend(root->inh_target_child_rels, + childrel); + + root->join_info_list = saved_join_info_list; + } + + return parent; +} + +/* * 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 ae41c9efa0..9e3b62ba1c 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -2003,12 +2003,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; @@ -2165,12 +2160,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/planmain.c b/src/backend/optimizer/plan/planmain.c index b05adc70c4..3f0d80eaa6 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -266,7 +266,9 @@ query_planner(PlannerInfo *root, List *tlist, /* Check that we got at least one usable path */ if (!final_rel || !final_rel->cheapest_total_path || - final_rel->cheapest_total_path->param_info != NULL) + final_rel->cheapest_total_path->param_info != NULL || + (final_rel->relid == root->parse->resultRelation && + root->parse->commandType == CMD_INSERT)) elog(ERROR, "failed to construct the join relation"); return final_rel; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 96bf0601a8..9636d7a4ee 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -113,9 +113,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); @@ -238,6 +236,20 @@ static bool group_by_has_partkey(RelOptInfo *input_rel, List *targetList, List *groupClause); +static void inheritance_adjust_scanjoin_target(PlannerInfo *root, + Query *parse, + RelOptInfo *parent, + List **partition_subroots, + Index *nominalRelation, + List **partitioned_rels, + List **resultRelations, + List **subpaths, + List **WCOLists, + List **returningLists, + List **rowMarks); +static void adjust_child_reltarget(PlannerInfo *root, Relids where_needed, + List *tlist); + /***************************************************************************** * @@ -622,7 +634,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); @@ -954,15 +965,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse, if (hasOuterJoins) reduce_outer_joins(root); - /* - * Do the main planning. If we have an inherited target relation, that - * needs special processing, else go straight to grouping_planner. - */ - if (parse->resultRelation && - rt_fetch(parse->resultRelation, parse->rtable)->inh) - inheritance_planner(root); - else - grouping_planner(root, false, tuple_fraction); + /* Do the main planning. */ + grouping_planner(root, tuple_fraction); /* * Capture the set of outer-level param IDs we have access to, for use in @@ -1132,517 +1136,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; - Bitmapset *subqueryRTindexes; - Bitmapset *modifiableARIindexes; - int nominalRelation = -1; - 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; - List *withCheckOptionLists = NIL; - List *returningLists = NIL; - List *rowMarks; - RelOptInfo *final_rel; - ListCell *lc; - Index rti; - RangeTblEntry *parent_rte; - Relids partitioned_relids = NULL; - List *partitioned_rels = NIL; - PlannerInfo *parent_root; - Query *parent_parse; - Bitmapset *parent_relids = bms_make_singleton(top_parentRTindex); - PlannerInfo **parent_roots = NULL; - - Assert(parse->commandType != CMD_INSERT); - - /* - * 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++; - } - - /* - * 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. - */ - modifiableARIindexes = NULL; - if (subqueryRTindexes != NULL) - { - foreach(lc, root->append_rel_list) - { - AppendRelInfo *appinfo = lfirst_node(AppendRelInfo, lc); - - 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); - } - } - - /* - * If the parent RTE is a partitioned table, we should use that as the - * nominal 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. The situation is exactly the - * opposite in the case of non-partitioned inheritance parent as described - * below. For the same reason, collect the list of descendant partitioned - * tables to be saved in ModifyTable node, so that executor can lock those - * as well. - */ - parent_rte = rt_fetch(top_parentRTindex, root->parse->rtable); - if (parent_rte->relkind == RELKIND_PARTITIONED_TABLE) - { - nominalRelation = top_parentRTindex; - - /* - * Root parent's RT index is always present in the partitioned_rels of - * the ModifyTable node, if one is needed at all. - */ - partitioned_relids = bms_make_singleton(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. - */ - 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) - { - AppendRelInfo *appinfo = lfirst_node(AppendRelInfo, lc); - 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; - - /* - * 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)); - - /* - * 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(parent_root, - (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; - - /* - * Mark whether we're planning a query to a partitioned table or an - * inheritance parent. - */ - subroot->inhTargetKind = - partitioned_relids ? 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; - } - - /* - * Set the nominal target relation of the ModifyTable node if not - * already done. We use the inheritance parent RTE as the nominal - * target relation if it's a partitioned table (see just above this - * loop). In the non-partitioned parent case, 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, the - * latter 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 the - * duplicate child RTE added for the parent does not appear anywhere - * else in the plan tree. - */ - 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 */ ); - - /* - * 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_PATH(subpath)) - continue; - - /* - * Add the current parent's RT index to the partitioned_relids set if - * we're creating the ModifyTable path for a partitioned root table. - * (We only care about parents of non-excluded children.) - */ - if (partitioned_relids) - partitioned_relids = bms_add_member(partitioned_relids, - appinfo->parent_relid); - - /* - * 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; - - /* 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 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. - */ - if (parse->rowMarks) - rowMarks = NIL; - else - rowMarks = root->rowMarks; - - if (partitioned_relids) - { - int i; - - i = -1; - while ((i = bms_next_member(partitioned_relids, i)) >= 0) - partitioned_rels = lappend_int(partitioned_rels, i); - - /* - * If we're going to create ModifyTable at all, the list should - * contain at least one member, that is, the root parent's index. - */ - Assert(list_length(partitioned_rels) >= 1); - partitioned_rels = list_make1(partitioned_rels); - } - - /* 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, - partitioned_rels, - root->partColsUpdated, - resultRelations, - subpaths, - subroots, - withCheckOptionLists, - returningLists, - rowMarks, - NULL, - SS_assign_special_param(root))); -} - /*-------------------- * grouping_planner * Perform planning steps related to grouping, aggregation, etc. @@ -1650,11 +1143,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) @@ -1672,8 +1160,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; List *tlist; @@ -1688,6 +1175,15 @@ grouping_planner(PlannerInfo *root, bool inheritance_update, RelOptInfo *current_rel; RelOptInfo *final_rel; ListCell *lc; + List *orig_parse_tlist = list_copy(parse->targetList); + Index nominalRelation = 0; + List *partition_subroots = NIL; + List *partitioned_rels = NIL; + List *partition_resultRelations = NIL; + List *partition_subpaths = NIL; + List *partition_WCOLists = NIL; + List *partition_returningLists = NIL; + List *partition_rowMarks = NIL; /* Tweak caller-supplied tuple_fraction if have LIMIT/OFFSET */ if (parse->limitCount || parse->limitOffset) @@ -2018,13 +1514,45 @@ grouping_planner(PlannerInfo *root, bool inheritance_update, scanjoin_targets_contain_srfs = NIL; } - /* Apply scan/join target. */ - scanjoin_target_same_exprs = list_length(scanjoin_targets) == 1 - && equal(scanjoin_target->exprs, current_rel->reltarget->exprs); - apply_scanjoin_target_to_paths(root, current_rel, scanjoin_targets, - scanjoin_targets_contain_srfs, - scanjoin_target_parallel_safe, - scanjoin_target_same_exprs); + /* + * For UPDATE/DELETE on an inheritance parent, we must generate and + * apply scanjoin target based on targetlist computed using each + * of the child relations. + */ + if (parse->commandType != CMD_INSERT && + current_rel->reloptkind == RELOPT_BASEREL && + current_rel->relid == root->parse->resultRelation && + root->simple_rte_array[current_rel->relid]->inh) + { + /* + * scanjoin_target shouldn't have changed from final_target, + * because UPDATE/DELETE doesn't support various features that + * would've required modifications that are performed above. + * That's important because we'll generate final_target freshly + * for each partition in inheritance_adjust_scanjoin_target. + */ + Assert(scanjoin_target == final_target); + root->parse->targetList = orig_parse_tlist; + inheritance_adjust_scanjoin_target(root, root->parse, current_rel, + &partition_subroots, + &nominalRelation, + &partitioned_rels, + &partition_resultRelations, + &partition_subpaths, + &partition_WCOLists, + &partition_returningLists, + &partition_rowMarks); + } + else + { + /* Apply scan/join target. */ + scanjoin_target_same_exprs = list_length(scanjoin_targets) == 1 + && equal(scanjoin_target->exprs, current_rel->reltarget->exprs); + apply_scanjoin_target_to_paths(root, current_rel, scanjoin_targets, + scanjoin_targets_contain_srfs, + scanjoin_target_parallel_safe, + scanjoin_target_same_exprs); + } /* * Save the various upper-rel PathTargets we just computed into @@ -2137,92 +1665,123 @@ grouping_planner(PlannerInfo *root, bool inheritance_update, final_rel->fdwroutine = current_rel->fdwroutine; /* - * Generate paths for the final_rel. Insert all surviving paths, with - * LockRows, Limit, and/or ModifyTable steps added if needed. + * If the table has partitions, but all of them were pruned, then + * nothing to do. */ - foreach(lc, current_rel->pathlist) + if (current_rel->reloptkind == RELOPT_BASEREL && + current_rel->relid == root->parse->resultRelation && + !IS_DUMMY_REL(current_rel) && + root->simple_rte_array[current_rel->relid]->inh && + parse->commandType != CMD_INSERT) { - Path *path = (Path *) lfirst(lc); - - /* - * If there is a FOR [KEY] UPDATE/SHARE clause, add the LockRows node. - * (Note: we intentionally test parse->rowMarks not root->rowMarks - * here. If there are only non-locking rowmarks, they should be - * handled by the ModifyTable node instead. However, root->rowMarks - * is what goes into the LockRows node.) - */ - if (parse->rowMarks) - { - path = (Path *) create_lockrows_path(root, final_rel, path, - root->rowMarks, - SS_assign_special_param(root)); - } - - /* - * If there is a LIMIT/OFFSET clause, add the LIMIT node. - */ - if (limit_needed(parse)) - { - path = (Path *) create_limit_path(root, final_rel, path, - parse->limitOffset, - parse->limitCount, - offset_est, count_est); - } - - /* - * If this is an INSERT/UPDATE/DELETE, and we're not being called from - * inheritance_planner, add the ModifyTable node. - */ - if (parse->commandType != CMD_SELECT && !inheritance_update) - { - List *withCheckOptionLists; - List *returningLists; - List *rowMarks; - - /* - * 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. - */ - if (parse->rowMarks) - rowMarks = NIL; - else - rowMarks = root->rowMarks; - - path = (Path *) + Path *path = (Path *) create_modifytable_path(root, final_rel, parse->commandType, parse->canSetTag, - parse->resultRelation, - NIL, - false, - list_make1_int(parse->resultRelation), - list_make1(path), - list_make1(root), - withCheckOptionLists, - returningLists, - rowMarks, - parse->onConflict, + nominalRelation, + partitioned_rels, + root->partColsUpdated, + partition_resultRelations, + partition_subpaths, + partition_subroots, + partition_WCOLists, + partition_returningLists, + partition_rowMarks, + NULL, SS_assign_special_param(root)); - } - - /* And shove it into final_rel */ add_path(final_rel, path); } + else + { + /* + * Generate paths for the final_rel. Insert all surviving paths, with + * LockRows, Limit, and/or ModifyTable steps added if needed. + */ + foreach(lc, current_rel->pathlist) + { + Path *path = (Path *) lfirst(lc); + + /* + * If there is a FOR [KEY] UPDATE/SHARE clause, add the LockRows + * node. (Note: we intentionally test parse->rowMarks not + * root->rowMarks here. If there are only non-locking rowmarks, + * they should be handled by the ModifyTable node instead. + * However, root->rowMarks is what goes into the LockRows node.) + */ + if (parse->rowMarks) + { + path = (Path *) + create_lockrows_path(root, final_rel, path, + root->rowMarks, + SS_assign_special_param(root)); + } + + /* + * If there is a LIMIT/OFFSET clause, add the LIMIT node. + */ + if (limit_needed(parse)) + { + path = (Path *) create_limit_path(root, final_rel, path, + parse->limitOffset, + parse->limitCount, + offset_est, count_est); + } + + /* + * If this is an INSERT/UPDATE/DELETE, and we're not being called + * from inheritance_planner, add the ModifyTable node. + */ + if (parse->commandType != CMD_SELECT) + { + List *withCheckOptionLists; + List *returningLists; + List *rowMarks; + + /* + * 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. + */ + if (parse->rowMarks) + rowMarks = NIL; + else + rowMarks = root->rowMarks; + + path = (Path *) + create_modifytable_path(root, final_rel, + parse->commandType, + parse->canSetTag, + parse->resultRelation, + NIL, + false, + list_make1_int(parse->resultRelation), + list_make1(path), + list_make1(root), + withCheckOptionLists, + returningLists, + rowMarks, + parse->onConflict, + SS_assign_special_param(root)); + } + + /* And shove it into final_rel */ + add_path(final_rel, path); + } + } /* * Generate partial paths for final_rel, too, if outer query levels might @@ -2259,6 +1818,233 @@ grouping_planner(PlannerInfo *root, bool inheritance_update, } /* + * inheritance_adjust_scanjoin_target + * adjusts query's targetlist for each partition in the partition tree + * whose root is 'parent' and apply it to their paths via + * apply_scanjoin_target_to_paths + * + * Its output also consists of various pieces of information that will go + * into the ModifyTable node that will be created for this query. + */ +static void +inheritance_adjust_scanjoin_target(PlannerInfo *root, + Query *parse, + RelOptInfo *parent, + List **subroots, + Index *nominalRelation, + List **partitioned_rels, + List **resultRelations, + List **subpaths, + List **WCOLists, + List **returningLists, + List **rowMarks) +{ + ListCell *lc; + + /* + * If all of child relations of parent were pruned, let + * apply_scanjoin_target_to_paths finish it up with a dummy path + * containing correct target list. + */ + if (IS_DUMMY_REL(parent)) + { + PathTarget *scanjoin_target = create_pathtarget(root, + root->processed_tlist); + apply_scanjoin_target_to_paths(root, parent, + list_make1(scanjoin_target), + NIL, false, false); + return; + } + + if (parent->part_scheme) + { + *partitioned_rels = lappend(*partitioned_rels, + list_make1_int(parent->relid)); + if (*nominalRelation == 0) + *nominalRelation = parent->relid; + } + + foreach(lc, root->inh_target_child_rels) + { + RelOptInfo *child_joinrel = lfirst(lc); + RelOptInfo *child_rel; + AppendRelInfo *appinfo; + int child_base_relid; + bool found; + List *tlist; + PathTarget *scanjoin_target; + bool scanjoin_target_parallel_safe; + bool scanjoin_target_same_exprs; + PlannerInfo *partition_subroot; + Query *partition_parse; + + /* + * Check if this child joinrel is a child of parent. To do so, check + * if its relids contains an index that belongs to one of parent's + * child base rels. + */ + found = false; + child_base_relid = -1; + while ((child_base_relid = bms_next_member(child_joinrel->relids, + child_base_relid)) >= 0) + { + AppendRelInfo *appinfo = root->append_rel_array[child_base_relid]; + + if (appinfo && appinfo->parent_relid == parent->relid) + { + found = true; + break; + } + } + if (!found) + continue; + + if (*nominalRelation == 0) + *nominalRelation = child_base_relid; + + /* + * If the child was pruned, its corresponding joinrel will be empty, + * which we can ignore safely. + */ + if (IS_DUMMY_REL(child_joinrel)) + continue; + + appinfo = root->append_rel_array[child_base_relid]; + Assert(appinfo != NULL); + child_rel = root->simple_rel_array[child_base_relid]; + Assert(IS_SIMPLE_REL(child_rel)); + + /* Translate Query structure for this partition. */ + partition_parse = (Query *) + adjust_appendrel_attrs(root, + (Node *) parse, + 1, &appinfo); + + if (child_rel->part_scheme) + { + Assert(child_joinrel == child_rel); + inheritance_adjust_scanjoin_target(root, + partition_parse, + child_rel, + subroots, + nominalRelation, + partitioned_rels, + resultRelations, + subpaths, + WCOLists, + returningLists, + rowMarks); + /* Restore the Query for processing the next partition. */ + } + else + { + /* + * Generate a separate PlannerInfo for this partition. We'll need + * it when generating the ModifyTable subplan for this partition. + */ + partition_subroot = makeNode(PlannerInfo); + memcpy(partition_subroot, root, sizeof(PlannerInfo)); + *subroots = lappend(*subroots, partition_subroot); + + /* + * Preprocess the translated targetlist and save it in the + * partition's PlannerInfo for use by createplan.c. + */ + partition_subroot->parse = partition_parse; + tlist = preprocess_targetlist(partition_subroot); + partition_subroot->processed_tlist = tlist; + + /* + * Fix up child rel's reltarget to add Vars from the top-level + * targetlist that may not have yet been added to it. Note that + * it would already contain entries for inherited attributes + * (although we may still need to set their attr_needed), but + * not its own attributes that preprocess_targetlist called + * above would've added to the top-level tlist. + */ + adjust_child_reltarget(partition_subroot, bms_make_singleton(0), + tlist); + + /* + * Apply the newly computed scan/join target to child_joinrel's + * paths. + */ + scanjoin_target = create_pathtarget(root, tlist); + scanjoin_target_same_exprs = + equal(scanjoin_target->exprs, + child_joinrel->reltarget->exprs); + scanjoin_target_parallel_safe = + is_parallel_safe(root, (Node *) scanjoin_target->exprs); + apply_scanjoin_target_to_paths(root, child_joinrel, + list_make1(scanjoin_target), + NIL, + scanjoin_target_parallel_safe, + scanjoin_target_same_exprs); + + /* Collect information that will go into the ModifyTable */ + *resultRelations = lappend_int(*resultRelations, child_base_relid); + *subpaths = lappend(*subpaths, child_joinrel->cheapest_total_path); + if (partition_parse->withCheckOptions) + *WCOLists = lappend(*WCOLists, partition_parse->withCheckOptions); + if (partition_parse->returningList) + *returningLists = lappend(*returningLists, + partition_parse->returningList); + if (partition_parse->rowMarks) + *rowMarks = lappend(*rowMarks, partition_parse->rowMarks); + } + } +} + +static void +adjust_child_reltarget(PlannerInfo *root, Relids where_needed, List *tlist) +{ + List *tlist_vars = pull_var_clause((Node *) tlist, + PVC_RECURSE_AGGREGATES | + PVC_RECURSE_WINDOWFUNCS | + PVC_INCLUDE_PLACEHOLDERS); + ListCell *lc; + + foreach(lc, tlist_vars) + { + Node *node = (Node *) lfirst(lc); + + if (IsA(node, Var)) + { + Var *var = (Var *) node; + RelOptInfo *rel = find_base_rel(root, var->varno); + int attno = var->varattno; + + if (bms_is_subset(where_needed, rel->relids)) + continue; + + Assert(attno >= rel->min_attr && attno <= rel->max_attr); + attno -= rel->min_attr; + if (rel->attr_needed[attno] == NULL) + { + bool found; + ListCell *lc2; + + rel->attr_needed[attno] = + bms_add_members(rel->attr_needed[attno], + where_needed); + found = false; + foreach(lc2, rel->reltarget->exprs) + { + if (equal(var, lfirst(lc2))) + { + found = true; + break; + } + } + if (!found) + rel->reltarget->exprs = lappend(rel->reltarget->exprs, + var); + } + } + } +} + +/* * Do preprocessing for groupingSets clause and related data. This handles the * preliminary steps of expanding the grouping sets, organizing them into lists * of rollups, and preparing annotations which will later be filled in with diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index c3f46a26c3..0d10a6b914 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -914,7 +914,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 690b6bbab7..f4c485cdc9 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -2265,8 +2265,34 @@ adjust_appendrel_attrs_mutator(Node *node, context->appinfos); return (Node *) phv; } + + 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 8369e3ad62..5a37221780 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -1265,36 +1265,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; @@ -1421,31 +1391,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 adb4265047..c008d626a1 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,9 @@ typedef struct PlannerInfo /* Does this query modify any partition key columns? */ bool partColsUpdated; + + /* RelOptInfo of child joinrels of an inherited update/delete operation */ + List *inh_target_child_rels; } 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