From 45334d7f43033adbc9682d0cbb549301d2c4366d Mon Sep 17 00:00:00 2001 From: amit Date: Thu, 13 Sep 2018 15:11:46 +0900 Subject: [PATCH v3 4/4] WIP: generate *all* inheritance child RelOptInfos after pruning --- src/backend/optimizer/path/allpaths.c | 209 ++++++--------- src/backend/optimizer/plan/initsplan.c | 57 ++-- src/backend/optimizer/plan/planner.c | 31 ++- src/backend/optimizer/prep/prepunion.c | 477 ++++++++++++++++++--------------- src/backend/optimizer/util/plancat.c | 58 ++-- src/backend/optimizer/util/relnode.c | 292 +++++++++++++------- src/backend/partitioning/partprune.c | 5 +- src/include/optimizer/pathnode.h | 9 +- src/include/optimizer/plancat.h | 9 +- src/include/optimizer/prep.h | 28 +- 10 files changed, 620 insertions(+), 555 deletions(-) diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index e54cccd077..21723e7cdc 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -367,8 +367,23 @@ static void set_rel_size(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte) { + + /* + * Check if the relation's constraints contradict the clauses matched + * to it. Only plain relations have constraints. In a partitioning + * hierarchy, but not with regular table inheritance, it's OK to assume + * that any constraints that hold for the parent also hold for every + * child; for instance, table inheritance allows the parent to have + * constraints marked NO INHERIT, but table partitioning does not. We + * choose to check whether the partitioning parents can be excluded here; + * doing so consumes some cycles, but potentially saves us the work of + * excluding each child individually. + */ if (rel->reloptkind == RELOPT_BASEREL && - relation_excluded_by_constraints(root, rel, rte)) + rte->rtekind == RTE_RELATION && + (!rte->inh || rte->relkind == RELKIND_PARTITIONED_TABLE) && + relation_excluded_by_constraints(root, rel->baserestrictinfo, + rti, rte->relid, false)) { /* * We proved we don't need to scan the rel via constraint exclusion, @@ -916,11 +931,7 @@ 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; @@ -951,6 +962,17 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, */ prune_append_rel_partitions(root, rel); } + else + { + expand_inherited_rtentry(root, rel, rte, rti); + if (IS_DUMMY_REL(rel)) + return; + if (!rte->inh) + { + set_rel_size(root, rel, rti, rte); + return; + } + } /* * If this is a partitioned baserel, set the consider_partitionwise_join @@ -989,9 +1011,9 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, int childRTindex; RangeTblEntry *childRTE; RelOptInfo *childrel; - List *childquals; + List *childquals = NIL; + bool have_const_false_cq = false; Index cq_min_security; - bool have_const_false_cq; ListCell *parentvars; ListCell *childvars; ListCell *lc; @@ -1004,53 +1026,23 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, childRTE = root->simple_rte_array[childRTindex]; /* - * The child rel's RelOptInfo was already created during - * add_base_rels_to_query. + * The child rel's RelOptInfo may not have been created in some + * cases, for example, if it is not a partition. */ - childrel = find_base_rel(root, childRTindex); + childrel = root->simple_rel_array[childRTindex]; + if (childrel == NULL) + childrel = build_inheritance_child_rel(root, childRTindex, rel); Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL); - /* - * 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 - * inner-indexscan joins on the individual children) or if the parent - * has useful pathkeys (because we should try to build MergeAppend - * paths that produce those sort orderings). Even if this child is - * deemed dummy, it may fall on nullable side in a child-join, which - * in turn may participate in a MergeAppend, where we will need the - * EquivalenceClass data structures. - */ - if (rel->has_eclass_joins || has_useful_pathkeys(root, rel)) - add_child_rel_equivalences(root, appinfo, rel, childrel); - childrel->has_eclass_joins = rel->has_eclass_joins; - - /* - * We have to copy the parent's quals to the child, with appropriate - * substitution of variables. However, only the baserestrictinfo - * quals are needed before we can check for constraint exclusion; so - * do that first and then check to see if we can disregard this child. - * - * The child rel's targetlist might contain non-Var expressions, which - * means that substitution into the quals could produce opportunities - * for const-simplification, and perhaps even pseudoconstant quals. - * Therefore, transform each RestrictInfo separately to see if it - * reduces to a constant or pseudoconstant. (We must process them - * separately to keep track of the security level of each qual.) - */ - childquals = NIL; - cq_min_security = UINT_MAX; - have_const_false_cq = false; + /* Convert parent's baserestrictinfo to store into childrel. */ foreach(lc, rel->baserestrictinfo) { RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); - Node *childqual; + Node *childqual = (Node *) rinfo->clause; ListCell *lc2; Assert(IsA(rinfo, RestrictInfo)); - childqual = adjust_appendrel_attrs(root, - (Node *) rinfo->clause, - 1, &appinfo); + childqual = adjust_appendrel_attrs(root, childqual, 1, &appinfo); childqual = eval_const_expressions(root, childqual); /* check for flat-out constant */ if (childqual && IsA(childqual, Const)) @@ -1082,13 +1074,12 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, } /* reconstitute RestrictInfo with appropriate properties */ childquals = lappend(childquals, - make_restrictinfo((Expr *) onecq, - rinfo->is_pushed_down, - rinfo->outerjoin_delayed, - pseudoconstant, - rinfo->security_level, - NULL, NULL, NULL)); - /* track minimum security level among child quals */ + make_restrictinfo((Expr *) onecq, + rinfo->is_pushed_down, + rinfo->outerjoin_delayed, + pseudoconstant, + rinfo->security_level, + NULL, NULL, NULL)); cq_min_security = Min(cq_min_security, rinfo->security_level); } } @@ -1106,6 +1097,7 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, */ if (childRTE->securityQuals) { + List *security_quals = NIL; Index security_level = 0; foreach(lc, childRTE->securityQuals) @@ -1118,16 +1110,37 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, Expr *qual = (Expr *) lfirst(lc2); /* not likely that we'd see constants here, so no check */ - childquals = lappend(childquals, - make_restrictinfo(qual, - true, false, false, - security_level, - NULL, NULL, NULL)); + security_quals = lappend(security_quals, + make_restrictinfo(qual, + true, false, + false, + security_level, + NULL, NULL, + NULL)); cq_min_security = Min(cq_min_security, security_level); } security_level++; } Assert(security_level <= root->qual_security_level); + + /* Concatenate with parent's quals. */ + childquals = list_concat(childquals, security_quals); + } + + /* + * Check if any of the security quals contradict table + * constraints. + */ + if (have_const_false_cq || + (childRTE->rtekind == RTE_RELATION && + relation_excluded_by_constraints(root, childquals, + childRTindex, + childRTE->relid, + true))) + { + childrel->reltarget = copy_pathtarget(rel->reltarget); + set_dummy_rel_pathlist(childrel); + continue; } /* @@ -1137,77 +1150,19 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, childrel->baserestrict_min_security = cq_min_security; /* - * Mark the child as empty in certain cases, such as if a restriction - * clause reduced to constant FALSE or NULL, or if constaint exclusion - * pruned it. + * 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 + * inner-indexscan joins on the individual children) or if the parent + * has useful pathkeys (because we should try to build MergeAppend + * paths that produce those sort orderings). Even if this child is + * deemed dummy, it may fall on nullable side in a child-join, which + * in turn may participate in a MergeAppend, where we will need the + * EquivalenceClass data structures. */ - if (have_const_false_cq || - 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); - - set_dummy_rel_pathlist(childrel); - continue; - } - - /* - * 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) - { - 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; - - /* - * Now add members of parent's reltarget->exprs, not already in - * child's. - */ - 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); + if (rel->has_eclass_joins || has_useful_pathkeys(root, rel)) + add_child_rel_equivalences(root, appinfo, rel, childrel); + childrel->has_eclass_joins = rel->has_eclass_joins; /* CE failed, so finish copying/modifying join quals. */ childrel->joininfo = (List *) diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index beb3e95101..e7927cc136 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -16,6 +16,7 @@ #include "catalog/pg_type.h" #include "catalog/pg_class.h" +#include "catalog/pg_inherits.h" #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" @@ -145,47 +146,29 @@ add_rel_partitions_to_query(PlannerInfo *root, RelOptInfo *rel, bool scan_all_parts, Bitmapset *partindexes) { - int new_size; - int num_added_parts; - int i; + int i; + Index rootRTindex; + PlanRowMark *rootrc = NULL; - Assert(partindexes != NULL || scan_all_parts); + rootRTindex = get_inheritance_root_parent(root, rel); + if (root->rowMarks) + rootrc = get_plan_rowmark(root->rowMarks, rootRTindex); - /* Expand the PlannerInfo arrays to hold new partition objects. */ - num_added_parts = scan_all_parts ? rel->nparts : - bms_num_members(partindexes); - new_size = root->simple_rel_array_size + num_added_parts; - root->simple_rte_array = (RangeTblEntry **) - repalloc(root->simple_rte_array, - sizeof(RangeTblEntry *) * new_size); - root->simple_rel_array = (RelOptInfo **) - repalloc(root->simple_rel_array, - sizeof(RelOptInfo *) * new_size); - if (root->append_rel_array) - root->append_rel_array = (AppendRelInfo **) - repalloc(root->append_rel_array, - sizeof(AppendRelInfo *) * new_size); - else - root->append_rel_array = (AppendRelInfo **) - palloc0(sizeof(AppendRelInfo *) * - new_size); + expand_planner_arrays_for_inheritance(root, + scan_all_parts ? rel->nparts : + bms_num_members(partindexes)); - /* Set the contents of just allocated memory to 0. */ - MemSet(root->simple_rte_array + root->simple_rel_array_size, - 0, sizeof(RangeTblEntry *) * num_added_parts); - MemSet(root->simple_rel_array + root->simple_rel_array_size, - 0, sizeof(RelOptInfo *) * num_added_parts); - MemSet(root->append_rel_array + root->simple_rel_array_size, - 0, sizeof(AppendRelInfo *) * num_added_parts); - root->simple_rel_array_size = new_size; - - /* And add the partitions. */ + /* And add the partitions to the relation. */ + rel->part_rels = (RelOptInfo **) + palloc0(sizeof(RelOptInfo *) * rel->nparts); if (scan_all_parts) { for (i = 0; i < rel->nparts; i++) { rel->part_rels[i] = build_partition_rel(root, rel, - rel->part_oids[i]); + rel->part_oids[i], + rootRTindex, + rootrc); rel->live_parts = bms_add_member(rel->live_parts, i); } } @@ -195,8 +178,12 @@ add_rel_partitions_to_query(PlannerInfo *root, RelOptInfo *rel, i = -1; while ((i = bms_next_member(partindexes, i)) >= 0) rel->part_rels[i] = build_partition_rel(root, rel, - rel->part_oids[i]); + rel->part_oids[i], + rootRTindex, + rootrc); } + + } /***************************************************************************** @@ -683,6 +670,7 @@ create_lateral_join_info(PlannerInfo *root) } } +#ifdef NOT_USED /* * Lastly, propagate lateral_relids and lateral_referencers from appendrel * parent rels to their child rels. We intentionally give each child rel @@ -740,6 +728,7 @@ create_lateral_join_info(PlannerInfo *root) } } } +#endif } diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index a757aba551..556eb64ae4 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -713,16 +713,6 @@ subquery_planner(PlannerGlobal *glob, Query *parse, preprocess_rowmarks(root); /* - * Expand any rangetable entries that are inheritance sets into "append - * relations". This can add entries to the rangetable, but they must be - * plain base relations not joins, so it's OK (and marginally more - * efficient) to do it after checking for join RTEs. We must do it after - * pulling up subqueries, else we'd fail to handle inherited tables in - * subqueries. - */ - expand_inherited_tables(root); - - /* * Set hasHavingQual to remember if HAVING clause is present. Needed * because preprocess_expression will reduce a constant-true condition to * an empty qual list ... but "HAVING TRUE" is not a semantic no-op. @@ -2369,7 +2359,7 @@ preprocess_rowmarks(PlannerInfo *root) newrc->allMarkTypes = (1 << newrc->markType); newrc->strength = rc->strength; newrc->waitPolicy = rc->waitPolicy; - newrc->isParent = false; + newrc->isParent = has_subclass(rte->relid); prowmarks = lappend(prowmarks, newrc); } @@ -2394,7 +2384,9 @@ preprocess_rowmarks(PlannerInfo *root) newrc->allMarkTypes = (1 << newrc->markType); newrc->strength = LCS_NONE; newrc->waitPolicy = LockWaitBlock; /* doesn't matter */ - newrc->isParent = false; + newrc->isParent = rte->rtekind == RTE_RELATION ? + has_subclass(rte->relid) : + false; prowmarks = lappend(prowmarks, newrc); } @@ -5871,8 +5863,19 @@ plan_create_index_workers(Oid tableOid, Oid indexOid) rte->inFromCl = true; query->rtable = list_make1(rte); - /* Set up RTE/RelOptInfo arrays */ - setup_simple_rel_arrays(root); + /* Set up RTE/RelOptInfo arrays. */ + + /* Arrays are accessed using RT indexes (1..N) */ + root->simple_rel_array_size = list_length(query->rtable) + 1; + + /* simple_rel_array is initialized to all NULLs */ + root->simple_rel_array = (RelOptInfo **) + palloc0(root->simple_rel_array_size * sizeof(RelOptInfo *)); + + /* simple_rte_array is an array equivalent of the rtable list */ + root->simple_rte_array = (RangeTblEntry **) + palloc0(root->simple_rel_array_size * sizeof(RangeTblEntry *)); + root->simple_rte_array[1] = rte; /* Build RelOptInfo */ rel = build_simple_rel(root, 1, NULL); diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 4d9583e63b..400568a9e7 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -39,15 +39,20 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" +#include "optimizer/clauses.h" #include "optimizer/cost.h" #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" #include "optimizer/tlist.h" +#include "optimizer/var.h" #include "parser/parse_coerce.h" #include "parser/parsetree.h" +#include "rewrite/rewriteManip.h" #include "utils/lsyscache.h" #include "utils/lsyscache.h" #include "utils/partcache.h" @@ -101,12 +106,6 @@ static List *generate_append_tlist(List *colTypes, List *colCollations, List *input_tlists, List *refnames_tlist); static List *generate_setop_grouplist(SetOperationStmt *op, List *targetlist); -static void expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, - Index rti); -static void make_inh_translation_list(TupleDesc old_tupdesc, - TupleDesc new_tupdesc, - RangeTblEntry *oldrte, RangeTblEntry *newrte, - Index newvarno, List **translated_vars); static Bitmapset *translate_col_privs(const Bitmapset *parent_privs, List *translated_vars); static Node *adjust_appendrel_attrs_mutator(Node *node, @@ -1452,37 +1451,6 @@ generate_setop_grouplist(SetOperationStmt *op, List *targetlist) return grouplist; } - -/* - * expand_inherited_tables - * Expand each rangetable entry that represents an inheritance set - * into an "append relation". At the conclusion of this process, - * the "inh" flag is set in all and only those RTEs that are append - * relation parents. - */ -void -expand_inherited_tables(PlannerInfo *root) -{ - Index nrtes; - Index rti; - ListCell *rl; - - /* - * expand_inherited_rtentry may add RTEs to parse->rtable. The function is - * expected to recursively handle any RTEs that it creates with inh=true. - * So just scan as far as the original end of the rtable list. - */ - nrtes = list_length(root->parse->rtable); - rl = list_head(root->parse->rtable); - for (rti = 1; rti <= nrtes; rti++) - { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(rl); - - expand_inherited_rtentry(root, rte, rti); - rl = lnext(rl); - } -} - /* * expand_inherited_rtentry * Check whether a rangetable entry represents an inheritance set. @@ -1503,30 +1471,34 @@ expand_inherited_tables(PlannerInfo *root) * Since a partitioned table is not scanned, it might have only one associated * AppendRelInfo. */ -static void -expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) +void +expand_inherited_rtentry(PlannerInfo *root, + RelOptInfo *rel, + RangeTblEntry *rte, + Index rti) { Query *parse = root->parse; - Oid parentOID; PlanRowMark *oldrc; - Relation oldrelation; LOCKMODE lockmode; List *inhOIDs; - ListCell *l; - List *appinfos = NIL; + ListCell *l, + *l2; + List *childrtes = NIL, + *appinfos = NIL; + int num_children = 0; - /* Does RT entry allow inheritance? */ - if (!rte->inh) + Assert(rte->inh); + /* Inheritance parent or UNION ALL parent query. */ + Assert(rte->rtekind == RTE_RELATION || rte->rtekind == RTE_SUBQUERY); + /* No partitioned tables though. */ + Assert(rte->relkind != RELKIND_PARTITIONED_TABLE); + + /* Nothing to do for already-expanded UNION ALL nodes. */ + if (rte->rtekind == RTE_SUBQUERY) return; - /* Ignore any already-expanded UNION ALL nodes */ - if (rte->rtekind != RTE_RELATION) - { - Assert(rte->rtekind == RTE_SUBQUERY); - return; - } + /* Fast path for common case of childless table */ - parentOID = rte->relid; - if (!has_subclass(parentOID)) + if (!has_subclass(rte->relid)) { /* Clear flag before returning */ rte->inh = false; @@ -1555,15 +1527,14 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) lockmode = AccessShareLock; /* Scan for all members of inheritance set, acquire needed locks */ - if (rte->relkind != RELKIND_PARTITIONED_TABLE) - inhOIDs = find_all_inheritors(parentOID, lockmode, NULL); + inhOIDs = find_all_inheritors(rte->relid, lockmode, NULL); /* * Check that there's at least one descendant, else treat as no-child * case. This could happen despite above has_subclass() check, if table * once had a child but no longer does. */ - if (rte->relkind != RELKIND_PARTITIONED_TABLE && list_length(inhOIDs) < 2) + if (list_length(inhOIDs) < 2) { /* Clear flag before returning */ rte->inh = false; @@ -1578,33 +1549,145 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) if (oldrc) oldrc->isParent = true; - /* Partitioned tables are expanded elsewhere. */ - if (rte->relkind == RELKIND_PARTITIONED_TABLE) - return; - - /* - * Must open the parent relation to examine its tupdesc. We need not lock - * it; we assume the rewriter already did. - */ - oldrelation = heap_open(parentOID, NoLock); - foreach(l, inhOIDs) { Oid childOID = lfirst_oid(l); + Relation childrel; Index childRTindex = 0; RangeTblEntry *childrte = NULL; - AppendRelInfo *appinfo = NULL; + AppendRelInfo *appinfo; + List *childquals; + bool have_const_false_cq; + ListCell *lc; - add_inheritance_child_to_query(root, rte, rti, - oldrelation->rd_rel->reltype, - RelationGetDescr(oldrelation), - oldrc, childOID, NoLock, - &appinfo, &childrte, - &childRTindex, NULL); + /* find_all_inheritors already locked. */ + childrel = heap_open(childOID, NoLock); + + /* + * If childrel doesn't belong to this session, skip it, also + * relinquishing the lock. + */ + if (RELATION_IS_OTHER_TEMP(childrel)) + { + heap_close(childrel, lockmode); + continue; + } + + /* + * Initialize the AppendRelInfo that's used to map the child to its + * parent in subsequent planning steps. + */ + appinfo = makeNode(AppendRelInfo); + appinfo->parent_relid = rti; + + /* + * We'll replace it with childRTindex once we're done adding the child + * to the range table. + */ + appinfo->child_relid = rti; + appinfo->parent_reltype = rel->reltype; + appinfo->child_reltype = childrel->rd_rel->reltype; + make_inh_translation_list(rel->tupdesc, + RelationGetDescr(childrel), + rte->relid, childOID, rti, + &appinfo->translated_vars); + appinfo->parent_reloid = rte->relid; + + /* + * Check if we don't need to include this child based on whether its + * constraints contradict query's quals. For a child that's not same + * as the parent, we'll need to convert the quals' Vars so that they + * contain child's attribute numbers. + */ + childquals = NIL; + have_const_false_cq = false; + foreach(lc, rel->baserestrictinfo) + { + RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); + Node *childqual = (Node *) rinfo->clause; + ListCell *lc2; + + Assert(IsA(rinfo, RestrictInfo)); + if (childOID != rte->relid) + childqual = adjust_appendrel_attrs(root, childqual, + 1, &appinfo); + childqual = eval_const_expressions(root, childqual); + /* check for flat-out constant */ + if (childqual && IsA(childqual, Const)) + { + if (((Const *) childqual)->constisnull || + !DatumGetBool(((Const *) childqual)->constvalue)) + { + /* Restriction reduces to constant FALSE or NULL */ + have_const_false_cq = true; + break; + } + /* Restriction reduces to constant TRUE, so drop it */ + continue; + } + /* might have gotten an AND clause, if so flatten it */ + foreach(lc2, make_ands_implicit((Expr *) childqual)) + { + Node *onecq = (Node *) lfirst(lc2); + bool pseudoconstant; + + /* check for pseudoconstant (no Vars or volatile functions) */ + pseudoconstant = + !contain_vars_of_level(onecq, 0) && + !contain_volatile_functions(onecq); + if (pseudoconstant) + { + /* tell createplan.c to check for gating quals */ + root->hasPseudoConstantQuals = true; + } + /* reconstitute RestrictInfo with appropriate properties */ + childquals = lappend(childquals, + make_restrictinfo((Expr *) onecq, + rinfo->is_pushed_down, + rinfo->outerjoin_delayed, + pseudoconstant, + rinfo->security_level, + NULL, NULL, NULL)); + } + } + + /* + * Mark the child as empty in certain cases, such as if a restriction + * clause reduced to constant FALSE or NULL, or if constaint exclusion + * pruned it. Note that we pass 'rti' for RT index that will be used + * to initialize constraint expressions, because we haven't initialized + * one for child yet. + */ + if (have_const_false_cq || + relation_excluded_by_constraints(root, childquals, rti, childOID, + true)) + { + heap_close(childrel, NoLock); + continue; + } + + childRTindex = add_inheritance_child_to_query(root, rte, childrel, + appinfo, oldrc, + &childrte); + heap_close(childrel, NoLock); + + /* Fix up the AppendRelInfo using the child's actual RT index. */ Assert(childRTindex > 1); - Assert(childrte != NULL); - Assert(appinfo != NULL); + appinfo->child_relid = childRTindex; + ChangeVarNodes((Node *) appinfo->translated_vars, rti, childRTindex, 0); + + /* Will be added to planner arrays below. */ + childrtes = lappend(childrtes, childrte); appinfos = lappend(appinfos, appinfo); + + /* We'll scan this child. */ + num_children++; + } + + if (num_children == 0) + { + set_dummy_rel_pathlist(rel); + return; } /* @@ -1620,95 +1703,28 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) root->append_rel_list = list_concat(root->append_rel_list, appinfos); - heap_close(oldrelation, NoLock); + expand_planner_arrays_for_inheritance(root, num_children); + forboth(l, childrtes, l2, appinfos) + { + RangeTblEntry *childrte = lfirst(l); + AppendRelInfo *appinfo = lfirst(l2); + + Assert(appinfo->child_relid < root->simple_rel_array_size); + root->simple_rte_array[appinfo->child_relid] = childrte; + root->append_rel_array[appinfo->child_relid] = appinfo; + } } -/* - * add_inheritance_child_to_query - * Build a RangeTblEntry and an AppendRelInfo, if appropriate, plus - * maybe a PlanRowMark for a child relation. - * - * We now expand the partition hierarchy level by level, creating a - * corresponding hierarchy of AppendRelInfos and RelOptInfos, where each - * partitioned descendant acts as a parent of its immediate partitions. - * (This is a difference from what older versions of PostgreSQL did and what - * is still done in the case of table inheritance for unpartitioned tables, - * where the hierarchy is flattened during RTE expansion.) - * - * PlanRowMarks still carry the top-parent's RTI, and the top-parent's - * allMarkTypes field still accumulates values from all descendents. - * - * "parentrte" and "parentRTindex" are immediate parent's RTE and - * RTI. "top_parentrc" is top parent's PlanRowMark. - * - * The child RangeTblEntry and its RTI are returned in "childrte_p" and - * "childRTindex_p" resp. - */ -void -add_inheritance_child_to_query(PlannerInfo *root, RangeTblEntry *parentrte, - Index parentRTindex, Oid parentRelType, - TupleDesc parentDesc, +Index +add_inheritance_child_to_query(PlannerInfo *root, + RangeTblEntry *parentrte, + Relation childrel, + AppendRelInfo *appinfo, PlanRowMark *top_parentrc, - Oid childOID, int lockmode, - AppendRelInfo **appinfo_p, - RangeTblEntry **childrte_p, - Index *childRTindex_p, - PlanRowMark **child_rowmark) + RangeTblEntry **childrte_p) { - Query *parse = root->parse; - Oid parentOID = parentrte->relid; + Query *parse = root->parse; RangeTblEntry *childrte; - Index childRTindex; - AppendRelInfo *appinfo; - Relation childrel = NULL; - char child_relkind; - Oid child_reltype; - TupleDesc childDesc; - - *appinfo_p = NULL; - *childrte_p = NULL; - *childRTindex_p = 0; - - /* Open rel if needed; we already have required locks */ - if (childOID != parentOID) - { - childrel = heap_open(childOID, lockmode); - - /* - * Temporary partitions belonging to other sessions should have been - * disallowed at definition, but for paranoia's sake, let's double - * check. - */ - if (RELATION_IS_OTHER_TEMP(childrel)) - { - if (childrel->rd_rel->relispartition) - elog(ERROR, "temporary relation from another session found as partition"); - heap_close(childrel, lockmode); - return; - } - - child_relkind = childrel->rd_rel->relkind; - - /* - * No point in adding to the query a partitioned table that has no - * partitions. - */ - if (child_relkind == RELKIND_PARTITIONED_TABLE && - RelationGetPartitionDesc(childrel)->nparts == 0) - { - heap_close(childrel, lockmode); - return; - } - - child_reltype = childrel->rd_rel->reltype; - childDesc = RelationGetDescr(childrel); - } - else - { - child_relkind = parentrte->relkind; - child_reltype = parentRelType; - childDesc = parentDesc; - } /* * Build an RTE for the child, and attach to query's rangetable list. We @@ -1723,11 +1739,10 @@ add_inheritance_child_to_query(PlannerInfo *root, RangeTblEntry *parentrte, * restriction clauses, so we don't need to do it here. */ childrte = copyObject(parentrte); - *childrte_p = childrte; - childrte->relid = childOID; - childrte->relkind = child_relkind; + childrte->relid = RelationGetRelid(childrel); + childrte->relkind = RelationGetForm(childrel)->relkind; /* A partitioned child will need to be expanded further. */ - if (childOID != parentOID && + if (childrte->relid != parentrte->relid && childrte->relkind == RELKIND_PARTITIONED_TABLE) childrte->inh = true; else @@ -1735,62 +1750,36 @@ add_inheritance_child_to_query(PlannerInfo *root, RangeTblEntry *parentrte, childrte->requiredPerms = 0; childrte->securityQuals = NIL; parse->rtable = lappend(parse->rtable, childrte); - childRTindex = list_length(parse->rtable); - *childRTindex_p = childRTindex; /* - * We need an AppendRelInfo if paths will be built for the child RTE. If - * childrte->inh is true, then we'll always need to generate append paths - * for it. If childrte->inh is false, we must scan it if it's not a - * partitioned table; but if it is a partitioned table, then it never has - * any data of its own and need not be scanned. + * Translate the column permissions bitmaps to the child's attnums (we + * have to build the translated_vars list before we can do this). But + * if this is the parent table, leave copyObject's result alone. + * + * Note: we need to do this even though the executor won't run any + * permissions checks on the child RTE. The insertedCols/updatedCols + * bitmaps may be examined for trigger-firing purposes. */ - if (childrte->relkind != RELKIND_PARTITIONED_TABLE || childrte->inh) + if (childrte->relid != parentrte->relid) { - appinfo = makeNode(AppendRelInfo); - appinfo->parent_relid = parentRTindex; - appinfo->child_relid = childRTindex; - appinfo->parent_reltype = parentRelType; - appinfo->child_reltype = child_reltype; - make_inh_translation_list(parentDesc, childDesc, - parentrte, childrte, childRTindex, - &appinfo->translated_vars); - appinfo->parent_reloid = parentOID; - *appinfo_p = appinfo; - - /* - * Translate the column permissions bitmaps to the child's attnums (we - * have to build the translated_vars list before we can do this). But - * if this is the parent table, leave copyObject's result alone. - * - * Note: we need to do this even though the executor won't run any - * permissions checks on the child RTE. The insertedCols/updatedCols - * bitmaps may be examined for trigger-firing purposes. - */ - if (childOID != parentOID) - { - childrte->selectedCols = translate_col_privs(parentrte->selectedCols, - appinfo->translated_vars); - childrte->insertedCols = translate_col_privs(parentrte->insertedCols, - appinfo->translated_vars); - childrte->updatedCols = translate_col_privs(parentrte->updatedCols, - appinfo->translated_vars); - } + childrte->selectedCols = translate_col_privs(parentrte->selectedCols, + appinfo->translated_vars); + childrte->insertedCols = translate_col_privs(parentrte->insertedCols, + appinfo->translated_vars); + childrte->updatedCols = translate_col_privs(parentrte->updatedCols, + appinfo->translated_vars); } - /* - * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE. - */ if (top_parentrc) { PlanRowMark *childrc = makeNode(PlanRowMark); + int orig_allMarkTypes = top_parentrc->allMarkTypes; - childrc->rti = childRTindex; + childrc->rti = list_length(parse->rtable); childrc->prti = top_parentrc->rti; childrc->rowmarkId = top_parentrc->rowmarkId; /* Reselect rowmark type, because relkind might not match parent */ - childrc->markType = select_rowmark_type(childrte, - top_parentrc->strength); + childrc->markType = select_rowmark_type(childrte, top_parentrc->strength); childrc->allMarkTypes = (1 << childrc->markType); childrc->strength = top_parentrc->strength; childrc->waitPolicy = top_parentrc->waitPolicy; @@ -1804,18 +1793,65 @@ add_inheritance_child_to_query(PlannerInfo *root, RangeTblEntry *parentrte, /* Include child's rowmark type in top parent's allMarkTypes */ top_parentrc->allMarkTypes |= childrc->allMarkTypes; - root->rowMarks = lappend(root->rowMarks, childrc); - if (child_rowmark) - *child_rowmark = childrc; + + /* + * Update query's targetlist if the newly added child row mark requires + * additional columns and also add the relevant expressions to root + * parent's reltarget. + */ + if (top_parentrc->allMarkTypes != orig_allMarkTypes) + { + List *tlist = root->parse->targetList; + + tlist = add_rowmark_columns(root, tlist, list_make1(top_parentrc)); + build_base_rel_tlists(root, tlist); + } } - /* Close child relations, but keep locks */ - if (childOID != parentOID) + *childrte_p = childrte; + return list_length(parse->rtable); +} + +/* + * Starting with 'rel', find the root inheritance parent's RT index. + */ +Index +get_inheritance_root_parent(PlannerInfo *root, RelOptInfo *rel) +{ + Index rootRTindex = 0; + + /* No AppendRelInfo array yet. */ + if (root->append_rel_array == NULL) + return rel->relid; + + /* + * The inheritance root itself might be a child of UNION ALL subquery, + * so we must resort to finding it like this. + */ + rootRTindex = rel->relid; + if (root->append_rel_array[rootRTindex]) { - Assert(childrel != NULL); - heap_close(childrel, lockmode); + AppendRelInfo *appinfo = root->append_rel_array[rootRTindex]; + RangeTblEntry *parent = root->simple_rte_array[rootRTindex]; + + /* + * Keep moving up until we reach the parent RTE that's not a + * RTE_RELATION. The last RTE_RELATION entry would be the + * inheritance root's. + */ + while(parent->rtekind == RTE_RELATION) + { + appinfo = root->append_rel_array[appinfo->parent_relid]; + if (appinfo == NULL) + break; + rootRTindex = appinfo->parent_relid; + parent = root->simple_rte_array[rootRTindex]; + } } + + Assert(rootRTindex > 0); + return rootRTindex; } /* @@ -1825,13 +1861,12 @@ add_inheritance_child_to_query(PlannerInfo *root, RangeTblEntry *parentrte, * * For paranoia's sake, we match type/collation as well as attribute name. */ -static void +void make_inh_translation_list(TupleDesc old_tupdesc, TupleDesc new_tupdesc, - RangeTblEntry *oldrte, RangeTblEntry *newrte, + Oid from_rel, Oid to_rel, Index newvarno, List **translated_vars) { List *vars = NIL; - Oid new_relid = newrte->relid; int oldnatts = old_tupdesc->natts; int newnatts = new_tupdesc->natts; int old_attno; @@ -1861,7 +1896,7 @@ make_inh_translation_list(TupleDesc old_tupdesc, TupleDesc new_tupdesc, * When we are generating the "translation list" for the parent table * of an inheritance set, no need to search for matches. */ - if (oldrte->relid == newrte->relid) + if (from_rel == to_rel) { vars = lappend(vars, makeVar(newvarno, (AttrNumber) (old_attno + 1), @@ -1887,10 +1922,10 @@ make_inh_translation_list(TupleDesc old_tupdesc, TupleDesc new_tupdesc, { HeapTuple newtup; - newtup = SearchSysCacheAttName(new_relid, attname); + newtup = SearchSysCacheAttName(to_rel, attname); if (!newtup) elog(ERROR, "could not find inherited attribute \"%s\" of relation \"%s\"", - attname, get_rel_name(newrte->relid)); + attname, get_rel_name(to_rel)); new_attno = ((Form_pg_attribute) GETSTRUCT(newtup))->attnum - 1; ReleaseSysCache(newtup); @@ -1900,10 +1935,10 @@ make_inh_translation_list(TupleDesc old_tupdesc, TupleDesc new_tupdesc, /* Found it, check type and collation match */ if (atttypid != att->atttypid || atttypmod != att->atttypmod) elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's type", - attname, get_rel_name(newrte->relid)); + attname, get_rel_name(to_rel)); if (attcollation != att->attcollation) elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's collation", - attname, get_rel_name(newrte->relid)); + attname, get_rel_name(to_rel)); vars = lappend(vars, makeVar(newvarno, (AttrNumber) (new_attno + 1), diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 77ae4b538a..c4a485af91 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -65,7 +65,7 @@ static bool infer_collation_opclass_match(InferenceElem *elem, Relation idxRel, List *idxExprs); static int32 get_rel_data_width(Relation rel, int32 *attr_widths); static List *get_relation_constraints(PlannerInfo *root, - Oid relationObjectId, RelOptInfo *rel, + Oid relationObjectId, Index varno, bool include_notnull); static List *build_index_tlist(PlannerInfo *root, IndexOptInfo *index, Relation heapRelation); @@ -99,14 +99,13 @@ static void set_baserel_partition_key_exprs(Relation relation, * cases these are left as zeroes, but sometimes we need to compute attr * widths here, and we may as well cache the results for costsize.c. * - * If inhparent is true, all we need to do is set up the attr arrays: + * If rte->inh is true, all we need to do is set up the attr arrays: * the RelOptInfo actually represents the appendrel formed by an inheritance * tree, and so the parent rel's physical size and index information isn't * important for it. */ void -get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, - Bitmapset *updatedCols, RelOptInfo *rel) +get_relation_info(PlannerInfo *root, RangeTblEntry *rte, RelOptInfo *rel) { Index varno = rel->relid; Relation relation; @@ -118,7 +117,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, * the rewriter or when expand_inherited_rtentry() added it to the query's * rangetable. */ - relation = heap_open(relationObjectId, NoLock); + relation = heap_open(rte->relid, NoLock); /* Temporary and unlogged relations are inaccessible during recovery. */ if (!RelationNeedsWAL(relation) && RecoveryInProgress()) @@ -142,7 +141,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, * must leave it zero for now to avoid bollixing the total_table_pages * calculation. */ - if (!inhparent) + if (!rte->inh) estimate_rel_size(relation, rel->attr_widths - rel->min_attr, &rel->pages, &rel->tuples, &rel->allvisfrac); @@ -153,7 +152,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, * Make list of indexes. Ignore indexes on system catalogs if told to. * Don't bother with indexes for an inheritance parent, either. */ - if (inhparent || + if (rte->inh || (IgnoreSystemIndexes && IsSystemRelation(relation))) hasindex = false; else @@ -442,18 +441,18 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, } /* Collect info about relation's foreign keys, if relevant */ - get_relation_foreign_keys(root, rel, relation, inhparent); + get_relation_foreign_keys(root, rel, relation, rte->inh); /* * Collect info about relation's partitioning scheme, if any. Only * inheritance parents may be partitioned. */ - if (inhparent && relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + if (rte->inh && relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { set_relation_partition_info(root, rel, relation); if (!root->partColsUpdated) root->partColsUpdated = - has_partition_attrs(relation, updatedCols, NULL); + has_partition_attrs(relation, rte->updatedCols, NULL); } rel->tupdesc = RelationGetDescr(relation); @@ -467,7 +466,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, * removing an index, or adding a hypothetical index to the indexlist. */ if (get_relation_info_hook) - (*get_relation_info_hook) (root, relationObjectId, inhparent, rel); + (*get_relation_info_hook) (root, rte->relid, rte->inh, rel); } /* @@ -1181,11 +1180,10 @@ get_relation_data_width(Oid relid, int32 *attr_widths) */ static List * get_relation_constraints(PlannerInfo *root, - Oid relationObjectId, RelOptInfo *rel, + Oid relationObjectId, Index varno, bool include_notnull) { List *result = NIL; - Index varno = rel->relid; Relation relation; TupleConstr *constr; @@ -1363,16 +1361,16 @@ get_relation_statistics(RelOptInfo *rel, Relation relation) */ bool relation_excluded_by_constraints(PlannerInfo *root, - RelOptInfo *rel, RangeTblEntry *rte) + List *baserestrictinfo, + Index varno, + Oid table_oid, + bool is_child) { List *safe_restrictions; List *constraint_pred; List *safe_constraints; ListCell *lc; - /* As of now, constraint exclusion works only with simple relations. */ - Assert(IS_SIMPLE_REL(rel)); - /* * Regardless of the setting of constraint_exclusion, detect * constant-FALSE-or-NULL restriction clauses. Because const-folding will @@ -1382,9 +1380,9 @@ relation_excluded_by_constraints(PlannerInfo *root, * this, we'd miss some optimizations that 9.5 and earlier found via much * more roundabout methods.) */ - if (list_length(rel->baserestrictinfo) == 1) + if (list_length(baserestrictinfo) == 1) { - RestrictInfo *rinfo = (RestrictInfo *) linitial(rel->baserestrictinfo); + RestrictInfo *rinfo = (RestrictInfo *) linitial(baserestrictinfo); Expr *clause = rinfo->clause; if (clause && IsA(clause, Const) && @@ -1407,7 +1405,7 @@ relation_excluded_by_constraints(PlannerInfo *root, * When constraint_exclusion is set to 'partition' we only handle * OTHER_MEMBER_RELs. */ - if (rel->reloptkind != RELOPT_OTHER_MEMBER_REL) + if (!is_child) return false; break; @@ -1424,7 +1422,7 @@ relation_excluded_by_constraints(PlannerInfo *root, * expecting to see any in its predicate argument. */ safe_restrictions = NIL; - foreach(lc, rel->baserestrictinfo) + foreach(lc, baserestrictinfo) { RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); @@ -1440,24 +1438,10 @@ relation_excluded_by_constraints(PlannerInfo *root, return true; /* - * Only plain relations have constraints. In a partitioning hierarchy, - * but not with regular table inheritance, it's OK to assume that any - * constraints that hold for the parent also hold for every child; for - * instance, table inheritance allows the parent to have constraints - * marked NO INHERIT, but table partitioning does not. We choose to check - * whether the partitioning parents can be excluded here; doing so - * consumes some cycles, but potentially saves us the work of excluding - * each child individually. - */ - if (rte->rtekind != RTE_RELATION || - (rte->inh && rte->relkind != RELKIND_PARTITIONED_TABLE)) - return false; - - /* * OK to fetch the constraint expressions. Include "col IS NOT NULL" * expressions for attnotnull columns, in case we can refute those. */ - constraint_pred = get_relation_constraints(root, rte->relid, rel, true); + constraint_pred = get_relation_constraints(root, table_oid, varno, true); /* * We do not currently enforce that CHECK constraints contain only @@ -1488,7 +1472,7 @@ relation_excluded_by_constraints(PlannerInfo *root, * We need strong refutation because we have to prove that the constraints * would yield false, not just NULL. */ - if (predicate_refuted_by(safe_constraints, rel->baserestrictinfo, false)) + if (predicate_refuted_by(safe_constraints, baserestrictinfo, false)) return true; return false; diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index e98b626395..f87283e261 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -16,7 +16,10 @@ #include +#include "access/heapam.h" +#include "catalog/partition.h" #include "catalog/pg_class.h" +#include "catalog/pg_inherits.h" #include "miscadmin.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" @@ -24,12 +27,15 @@ #include "optimizer/paths.h" #include "optimizer/placeholder.h" #include "optimizer/plancat.h" +#include "optimizer/planmain.h" #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" #include "partitioning/partbounds.h" +#include "rewrite/rewriteManip.h" #include "storage/lockdefs.h" #include "utils/hsearch.h" +#include "utils/rel.h" typedef struct JoinHashEntry @@ -91,6 +97,13 @@ setup_simple_rel_arrays(PlannerInfo *root) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); + /* + * If a table has no inheritance children, then turn off + * inheritance so that further plannig steps process it as a + * regular table. + */ + if (rte->rtekind == RTE_RELATION && !has_subclass(rte->relid)) + rte->inh = false; root->simple_rte_array[rti++] = rte; } } @@ -227,8 +240,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) { case RTE_RELATION: /* Table --- retrieve statistics from the system catalogs */ - get_relation_info(root, rte->relid, rte->inh, rte->updatedCols, - rel); + get_relation_info(root, rte, rel); break; case RTE_SUBQUERY: case RTE_FUNCTION: @@ -269,29 +281,17 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) root->qual_security_level = Max(root->qual_security_level, list_length(rte->securityQuals)); +#ifdef NOT_USED /* * If this rel is an appendrel parent, recurse to build "other rel" * RelOptInfos for its children. They are "other rels" because they are * not in the main join tree, but we will need RelOptInfos to plan access * to them. */ - if (rte->inh) + if (rte->inh && rte->rtekind == RTE_SUBQUERY) { ListCell *l; - /* - * For partitioned tables, we just allocate space for RelOptInfo's. - * pointers for all partitions and copy the partition OIDs from the - * relcache. Actual RelOptInfo is built for a partition only if it is - * not pruned. - */ - if (rte->relkind == RELKIND_PARTITIONED_TABLE) - { - rel->part_rels = (RelOptInfo **) - palloc0(sizeof(RelOptInfo *) * rel->nparts); - return rel; - } - foreach(l, root->append_rel_list) { AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l); @@ -303,6 +303,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) (void) build_simple_rel(root, appinfo->child_relid, rel); } } +#endif return rel; } @@ -1746,6 +1747,60 @@ build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel, } /* + * build_child_join_reltarget + * Set up a child-join relation's reltarget from a parent-join relation. + */ +static void +build_child_join_reltarget(PlannerInfo *root, + RelOptInfo *parentrel, + RelOptInfo *childrel, + int nappinfos, + AppendRelInfo **appinfos) +{ + /* Build the targetlist */ + childrel->reltarget->exprs = (List *) + adjust_appendrel_attrs(root, + (Node *) parentrel->reltarget->exprs, + nappinfos, appinfos); + + /* Set the cost and width fields */ + childrel->reltarget->cost.startup = parentrel->reltarget->cost.startup; + childrel->reltarget->cost.per_tuple = parentrel->reltarget->cost.per_tuple; + childrel->reltarget->width = parentrel->reltarget->width; +} + +/* Expand the PlannerInfo arrays to hold new inheritance children objects. */ +void +expand_planner_arrays_for_inheritance(PlannerInfo *root, int num_children) +{ + int new_size = root->simple_rel_array_size + num_children; + + root->simple_rte_array = (RangeTblEntry **) + repalloc(root->simple_rte_array, + sizeof(RangeTblEntry *) * new_size); + root->simple_rel_array = (RelOptInfo **) + repalloc(root->simple_rel_array, + sizeof(RelOptInfo *) * new_size); + if (root->append_rel_array) + root->append_rel_array = (AppendRelInfo **) + repalloc(root->append_rel_array, + sizeof(AppendRelInfo *) * new_size); + else + root->append_rel_array = (AppendRelInfo **) + palloc0(sizeof(AppendRelInfo *) * + new_size); + + /* Set the contents of just allocated memory to 0. */ + MemSet(root->simple_rte_array + root->simple_rel_array_size, + 0, sizeof(RangeTblEntry *) * num_children); + MemSet(root->simple_rel_array + root->simple_rel_array_size, + 0, sizeof(RelOptInfo *) * num_children); + MemSet(root->append_rel_array + root->simple_rel_array_size, + 0, sizeof(AppendRelInfo *) * num_children); + root->simple_rel_array_size = new_size; +} + +/* * build_dummy_partition_rel * Build a RelOptInfo and AppendRelInfo for a pruned partition * @@ -1804,55 +1859,20 @@ build_dummy_partition_rel(PlannerInfo *root, RelOptInfo *parent, int partidx) * range table and creating planner data structures for it */ RelOptInfo * -build_partition_rel(PlannerInfo *root, RelOptInfo *parent, Oid partoid) +build_partition_rel(PlannerInfo *root, + RelOptInfo *parent, + Oid partoid, + Index rootRTindex, + PlanRowMark *rootrc) { RangeTblEntry *parentrte = root->simple_rte_array[parent->relid]; - RelOptInfo *result; - Index rootRTindex = 0; + RangeTblEntry *partrte; + Relation partrel; + PartitionDesc partdesc; Index partRTindex = 0; - RangeTblEntry *partrte = NULL; - AppendRelInfo *appinfo = NULL; - PlanRowMark *rootrc = NULL; - int orig_allMarkTypes; - PlanRowMark *childrc = NULL; + AppendRelInfo *appinfo; int lockmode; - /* Locate the root partitioned table and fetch its PlanRowMark, if any. */ - if (root->rowMarks) - { - /* - * The root partitioned table itself might be a child of UNION ALL - * parent, so we must resort to finding the root parent like this. - */ - rootRTindex = parent->relid; - if (root->append_rel_array[rootRTindex]) - { - AppendRelInfo *tmp = root->append_rel_array[rootRTindex]; - - /* - * Keep moving up until we each the parent rel that's not a - * partitioned table. The one before that one would be the root - * parent. - */ - while(root->simple_rel_array[rootRTindex]->part_scheme) - { - tmp = root->append_rel_array[tmp->parent_relid]; - if (tmp == NULL) - break; - rootRTindex = tmp->parent_relid; - } - } - - rootrc = get_plan_rowmark(root->rowMarks, rootRTindex); - - /* - * Addition of certain child rowmarks will change the value, in which - * case, we need to add additional columns to the query's targetlist. - */ - if (rootrc) - orig_allMarkTypes = rootrc->allMarkTypes; - } - /* Determine the correct lockmode to use. */ if (rootRTindex == root->parse->resultRelation) lockmode = RowExclusiveLock; @@ -1860,58 +1880,124 @@ build_partition_rel(PlannerInfo *root, RelOptInfo *parent, Oid partoid) lockmode = RowShareLock; else lockmode = AccessShareLock; - add_inheritance_child_to_query(root, parentrte, parent->relid, - parent->reltype, parent->tupdesc, - rootrc, partoid, lockmode, - &appinfo, &partrte, &partRTindex, - &childrc); - /* Partition turned out to be a partitioned table with 0 partitions. */ - if (partrte == NULL) + partrel = heap_open(partoid, lockmode); + Assert(!RELATION_IS_OTHER_TEMP(partrel)); + partdesc = RelationGetPartitionDesc(partrel); + if (partdesc && partdesc->nparts == 0) + { + heap_close(partrel, NoLock); return NULL; + } + + appinfo = makeNode(AppendRelInfo); + appinfo->parent_relid = parent->relid; + appinfo->child_relid = 0; /* Set to correct value below. */ + appinfo->parent_reltype = parent->reltype; + appinfo->child_reltype = partrel->rd_rel->reltype; + make_inh_translation_list(parent->tupdesc, + RelationGetDescr(partrel), + parentrte->relid, partoid, + parent->relid, + &appinfo->translated_vars); + appinfo->parent_reloid = parentrte->relid; + + partRTindex = add_inheritance_child_to_query(root, parentrte, partrel, + appinfo, rootrc, &partrte); + heap_close(partrel, NoLock); + + Assert(partrte != NULL); + + /* Fix up the AppendRelInfo with correct child RT index. */ + appinfo->child_relid = partRTindex; + ChangeVarNodes((Node *) appinfo->translated_vars, parent->relid, + partRTindex, 0); - Assert(appinfo != NULL); root->append_rel_list = lappend(root->append_rel_list, appinfo); root->simple_rte_array[partRTindex] = partrte; root->append_rel_array[partRTindex] = appinfo; - /* Build the RelOptInfo. */ - result = build_simple_rel(root, partRTindex, parent); - - /* Set the information created by create_lateral_join_info(). */ - result->direct_lateral_relids = parent->direct_lateral_relids; - result->lateral_relids = parent->lateral_relids; - result->lateral_referencers = parent->lateral_referencers; - - if (rootrc && rootrc->allMarkTypes != orig_allMarkTypes) - { - List *tlist = root->parse->targetList; - - tlist = add_rowmark_columns(root, tlist, list_make1(rootrc)); - } - - return result; + return build_inheritance_child_rel(root, partRTindex, parent); } -/* - * build_child_join_reltarget - * Set up a child-join relation's reltarget from a parent-join relation. - */ -static void -build_child_join_reltarget(PlannerInfo *root, - RelOptInfo *parentrel, - RelOptInfo *childrel, - int nappinfos, - AppendRelInfo **appinfos) +RelOptInfo * +build_inheritance_child_rel(PlannerInfo *root, Index childRTindex, + RelOptInfo *parent) { - /* Build the targetlist */ - childrel->reltarget->exprs = (List *) - adjust_appendrel_attrs(root, - (Node *) parentrel->reltarget->exprs, - nappinfos, appinfos); + Relids top_parent_relids = parent->top_parent_relids; + int rootParentRTindex = top_parent_relids ? + bms_singleton_member(top_parent_relids) : + parent->relid; + AppendRelInfo *appinfo = root->append_rel_array[childRTindex]; + /* Build the RelOptInfo. */ + RelOptInfo *childrel = build_simple_rel(root, childRTindex, parent); - /* Set the cost and width fields */ - childrel->reltarget->cost.startup = parentrel->reltarget->cost.startup; - childrel->reltarget->cost.per_tuple = parentrel->reltarget->cost.per_tuple; - childrel->reltarget->width = parentrel->reltarget->width; + /* + * Propagate lateral_relids and lateral_referencers from appendrel + * parent rels to their child rels. We intentionally give each child rel + * the same minimum parameterization, even though it's quite possible that + * some don't reference all the lateral rels. This is because any append + * path for the parent will have to have the same parameterization for + * every child anyway, and there's no value in forcing extra + * reparameterize_path() calls. Similarly, a lateral reference to the + * parent prevents use of otherwise-movable join rels for each child. + */ + childrel->direct_lateral_relids = parent->direct_lateral_relids; + childrel->lateral_relids = parent->lateral_relids; + childrel->lateral_referencers = parent->lateral_referencers; + + /* + * 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) + { + 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; + + /* + * Now add members of parent's reltarget->exprs, not already in + * child's. + */ + other_exprs = (List *) + adjust_appendrel_attrs(root, + (Node *) parent->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 *) parent->reltarget->exprs, + 1, &appinfo); + } + + return childrel; } diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c index 345ab19989..d5dad5a1e9 100644 --- a/src/backend/partitioning/partprune.c +++ b/src/backend/partitioning/partprune.c @@ -601,8 +601,6 @@ prune_append_rel_partitions(PlannerInfo *root, RelOptInfo *rel) if (bms_is_empty(partindexes)) return; } - else - scan_all_parts = true; } else scan_all_parts = true; @@ -611,7 +609,8 @@ prune_append_rel_partitions(PlannerInfo *root, RelOptInfo *rel) * Build selected partitions' range table entries, RelOptInfos, and * AppendRelInfos. */ - add_rel_partitions_to_query(root, rel, scan_all_parts, partindexes); + if (scan_all_parts || !bms_is_empty(partindexes)) + add_rel_partitions_to_query(root, rel, scan_all_parts, partindexes); } /* diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index 4f567765a4..bf5a025b69 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -15,6 +15,7 @@ #define PATHNODE_H #include "nodes/bitmapset.h" +#include "nodes/plannodes.h" #include "nodes/relation.h" @@ -261,6 +262,8 @@ extern Path *reparameterize_path_by_child(PlannerInfo *root, Path *path, */ extern void setup_simple_rel_arrays(PlannerInfo *root); extern void setup_append_rel_array(PlannerInfo *root); +extern void expand_planner_arrays_for_inheritance(PlannerInfo *root, + int num_children); extern RelOptInfo *build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent); extern RelOptInfo *find_base_rel(PlannerInfo *root, int relid); @@ -300,8 +303,12 @@ extern RelOptInfo *build_child_join_rel(PlannerInfo *root, extern RelOptInfo *build_dummy_partition_rel(PlannerInfo *root, RelOptInfo *parent, int partidx); +RelOptInfo *build_inheritance_child_rel(PlannerInfo *root, Index childRTindex, + RelOptInfo *parent); extern RelOptInfo *build_partition_rel(PlannerInfo *root, RelOptInfo *parent, - Oid partoid); + Oid partoid, + Index rootRTindex, + PlanRowMark *rootrc); #endif /* PATHNODE_H */ diff --git a/src/include/optimizer/plancat.h b/src/include/optimizer/plancat.h index edaf2a3b4f..5e1e7478bf 100644 --- a/src/include/optimizer/plancat.h +++ b/src/include/optimizer/plancat.h @@ -25,8 +25,8 @@ typedef void (*get_relation_info_hook_type) (PlannerInfo *root, extern PGDLLIMPORT get_relation_info_hook_type get_relation_info_hook; -extern void get_relation_info(PlannerInfo *root, Oid relationObjectId, - bool inhparent, Bitmapset *updatedCols, RelOptInfo *rel); +extern void get_relation_info(PlannerInfo *root, RangeTblEntry *rte, + RelOptInfo *rel); extern List *infer_arbiter_indexes(PlannerInfo *root); @@ -36,7 +36,10 @@ extern void estimate_rel_size(Relation rel, int32 *attr_widths, extern int32 get_relation_data_width(Oid relid, int32 *attr_widths); extern bool relation_excluded_by_constraints(PlannerInfo *root, - RelOptInfo *rel, RangeTblEntry *rte); + List *baserestrictinfo, + Index varno, + Oid table_oid, + bool is_child); extern List *build_physical_tlist(PlannerInfo *root, RelOptInfo *rel); diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.h index ee7406e4f6..1462e7b92f 100644 --- a/src/include/optimizer/prep.h +++ b/src/include/optimizer/prep.h @@ -16,6 +16,7 @@ #include "nodes/plannodes.h" #include "nodes/relation.h" +#include "utils/relcache.h" /* @@ -50,18 +51,21 @@ extern PlanRowMark *get_plan_rowmark(List *rowmarks, Index rtindex); */ extern RelOptInfo *plan_set_operations(PlannerInfo *root); -extern void expand_inherited_tables(PlannerInfo *root); - -extern void add_inheritance_child_to_query(PlannerInfo *root, - RangeTblEntry *parentrte, - Index parentRTindex, Oid parentRelType, - TupleDesc parentDesc, - PlanRowMark *top_parentrc, - Oid childOID, int lockmode, - AppendRelInfo **appinfo_p, - RangeTblEntry **childrte_p, - Index *childRTindex_p, - PlanRowMark **child_rowmark); +extern void expand_inherited_rtentry(PlannerInfo *root, + RelOptInfo *rel, + RangeTblEntry *rte, + Index rti); +extern Index add_inheritance_child_to_query(PlannerInfo *root, + RangeTblEntry *parentrte, + Relation childrel, + AppendRelInfo *appinfo, + PlanRowMark *top_parentrc, + RangeTblEntry **childrte_p); +extern Index get_inheritance_root_parent(PlannerInfo *root, RelOptInfo *rel); +extern void make_inh_translation_list(TupleDesc old_tupdesc, + TupleDesc new_tupdesc, + Oid from_rel, Oid to_rel, + Index newvarno, List **translated_vars); extern Node *adjust_appendrel_attrs(PlannerInfo *root, Node *node, int nappinfos, AppendRelInfo **appinfos); -- 2.11.0