From 17dfaff62fe04cf18f5bba298974d42f92b597ef Mon Sep 17 00:00:00 2001 From: amit Date: Wed, 6 Sep 2017 09:28:14 +0900 Subject: [PATCH 1/5] Expand partitioned inheritance in a non-flattened manner ...except when the partitioned table in question is the result rel of the query. This allows us perform scan and join planning for each sub-tree in a given partition tree, with each sub-tree's root partitioned table getting its own RelOptInfo. Previously only the root of the whole partition tree got a RelOptInfo, along with the leaf partitions, with each leaf partition's AppendRelInfo pointing to the former as its parent. --- src/backend/optimizer/path/allpaths.c | 34 ++++++- src/backend/optimizer/plan/planner.c | 3 +- src/backend/optimizer/prep/prepunion.c | 166 +++++++++++++++++++++------------ src/backend/optimizer/util/plancat.c | 7 +- 4 files changed, 146 insertions(+), 64 deletions(-) diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 2d7e1d84d0..6c3511bd47 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -1289,11 +1289,39 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte; rte = planner_rt_fetch(rel->relid, root); + + /* + * Get the partitioned_rels list from root->pcinfo_list after + * confirming that rel is actually a root partitioned table. + */ if (rte->relkind == RELKIND_PARTITIONED_TABLE) { - partitioned_rels = get_partitioned_child_rels(root, rel->relid); - /* The root partitioned table is included as a child rel */ - Assert(list_length(partitioned_rels) >= 1); + int parent_relid; + bool is_root_partitioned_table = false; + + /* + * Normally, only the root partitioned rel will be RELOPT_BASEREL + * in a given partitione tree, except when the root table itself + * is a child in the case of a UNION ALL query. + */ + if (!IS_OTHER_REL(rel)) + is_root_partitioned_table = true; + else if (bms_get_singleton_member(rel->top_parent_relids, + &parent_relid)) + { + RelOptInfo *parent_rel; + + parent_rel = root->simple_rel_array[parent_relid]; + is_root_partitioned_table = + (parent_rel->rtekind != RTE_RELATION); + } + + if (is_root_partitioned_table) + { + partitioned_rels = get_partitioned_child_rels(root, rel->relid); + /* The root partitioned table is included as a child rel */ + Assert(list_length(partitioned_rels) >= 1); + } } /* diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 966230256e..02662fad5d 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -6076,7 +6076,8 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) * Returns a list of the RT indexes of the partitioned child relations * with rti as the root parent RT index. * - * Note: Only call this function on RTEs known to be partitioned tables. + * Note: Only call this function on RTEs known to be "root" partitioned + * tables. */ List * get_partitioned_child_rels(PlannerInfo *root, Index rti) diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index ccf21453fd..433505948d 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -113,7 +113,8 @@ static void expand_single_inheritance_child(PlannerInfo *root, Index parentRTindex, Relation parentrel, PlanRowMark *parentrc, Relation childrel, bool *has_child, List **appinfos, - List **partitioned_child_rels); + List **partitioned_child_rels, + RangeTblEntry **newrte, Index *newRTindex); static void make_inh_translation_list(Relation oldrelation, Relation newrelation, Index newvarno, @@ -1380,8 +1381,8 @@ expand_inherited_tables(PlannerInfo *root) * regular inheritance, a parent RTE must always have at least two associated * AppendRelInfos: one corresponding to the parent table as a simple member of * inheritance set and one or more corresponding to the actual children. - * Since a partitioned table is not scanned, it might have only one associated - * AppendRelInfo. + * Since a partitioned table parent is itself not scanned, it might have only + * one associated AppendRelInfo. */ static void expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) @@ -1473,13 +1474,10 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) { /* * If this table has partitions, recursively expand them in the order - * in which they appear in the PartitionDesc. But first, expand the - * parent itself. + * in which they appear in the PartitionDesc. Also, start collecting + * the RT indexes of the partitioned tables in the partition tree. */ - expand_single_inheritance_child(root, rte, rti, oldrelation, oldrc, - oldrelation, - &has_child, &appinfos, - &partitioned_child_rels); + partitioned_child_rels = list_make1_int(rti); expand_partitioned_rtentry(root, rte, rti, oldrelation, oldrc, RelationGetPartitionDesc(oldrelation), lockmode, @@ -1516,10 +1514,14 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) continue; } + /* + * Don't expect to find any partitioned tables in a regular + * inheritance tree, so pass NULL for partitioned_child_rels here. + */ expand_single_inheritance_child(root, rte, rti, oldrelation, oldrc, newrelation, &has_child, &appinfos, - &partitioned_child_rels); + NULL, NULL, NULL); /* Close child relations, but keep locks */ if (childOID != parentOID) @@ -1581,6 +1583,8 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte, { Oid childOID = partdesc->oids[i]; Relation childrel; + RangeTblEntry *childrte; + Index childRTindex; /* Open rel; we already have required locks */ childrel = heap_open(childOID, NoLock); @@ -1592,19 +1596,60 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte, continue; } - expand_single_inheritance_child(root, parentrte, parentRTindex, + expand_single_inheritance_child(root, + parentrte, parentRTindex, parentrel, parentrc, childrel, has_child, appinfos, - partitioned_child_rels); + partitioned_child_rels, + &childrte, &childRTindex); /* If this child is itself partitioned, recurse */ if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - expand_partitioned_rtentry(root, parentrte, parentRTindex, - parentrel, parentrc, - RelationGetPartitionDesc(childrel), - lockmode, - has_child, appinfos, - partitioned_child_rels); + { + RangeTblEntry *new_parentrte; + Index new_parentRTindex; + Relation new_parentrel; + + /* + * For SELECT queries, it's desirable to perform scan and join + * planning on the individual partition sub-trees, instead of + * doing the same on the whole tree at once. This allows to apply + * techniques such as parition-pruning and/or partition-wise join + * on the individual partition sub-trees. For that to happen, + * root parent of each sub-tree must get an RTE with inh set to + * true, which must be already taken care of by + * expand_single_inheritance_child(). Next, for each of the + * children, we must record immediate parent as its parent in the + * the child AppendRelInfo, instead of the root parent of the + * whole tree. + * + * If parent is the query's result relation, inheritance_planner() + * will expand the inheritance so as to apply the *whole* query to + * each leaf partition, which means we cannot apply + * partition-pruning and/or partition-wise join to a partitioned + * result relation, meaning there is not much point in expanding + * the tree hierarchically. + */ + if (parentRTindex == root->parse->resultRelation) + { + new_parentrte = parentrte; + new_parentRTindex = parentRTindex; + new_parentrel = parentrel; + } + else + { + new_parentrte = childrte; + new_parentRTindex = childRTindex; + new_parentrel = childrel; + } + + expand_partitioned_rtentry(root, new_parentrte, new_parentRTindex, + new_parentrel, parentrc, + RelationGetPartitionDesc(childrel), + lockmode, + has_child, appinfos, + partitioned_child_rels); + } /* Close child relation, but keep locks */ heap_close(childrel, NoLock); @@ -1619,13 +1664,17 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte, * anything at all. Otherwise, we'll set "has_child" to true, build a * RangeTblEntry and either a PartitionedChildRelInfo or AppendRelInfo as * appropriate, plus maybe a PlanRowMark. + * + * The newly created RT entry and its RT index are returned in *newrte and + * *newRTindex, respectively. */ static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, PlanRowMark *parentrc, Relation childrel, bool *has_child, List **appinfos, - List **partitioned_child_rels) + List **partitioned_child_rels, + RangeTblEntry **newrte, Index *newRTindex) { Query *parse = root->parse; Oid parentOID = RelationGetRelid(parentrel); @@ -1649,54 +1698,46 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, childrte = copyObject(parentrte); childrte->relid = childOID; childrte->relkind = childrel->rd_rel->relkind; - childrte->inh = false; + childrte->inh = (childrte->relkind == RELKIND_PARTITIONED_TABLE); childrte->requiredPerms = 0; childrte->securityQuals = NIL; parse->rtable = lappend(parse->rtable, childrte); childRTindex = list_length(parse->rtable); + /* Build an AppendRelInfo for this parent and child. */ + + /* Remember if we saw a real child. */ + if (childOID != parentOID) + *has_child = true; + + appinfo = makeNode(AppendRelInfo); + appinfo->parent_relid = parentRTindex; + appinfo->child_relid = childRTindex; + appinfo->parent_reltype = parentrel->rd_rel->reltype; + appinfo->child_reltype = childrel->rd_rel->reltype; + make_inh_translation_list(parentrel, childrel, childRTindex, + &appinfo->translated_vars); + appinfo->parent_reloid = parentOID; + *appinfos = lappend(*appinfos, appinfo); + /* - * Build an AppendRelInfo for this parent and child, unless the child is a - * partitioned table. + * 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) + if (childOID != parentOID) { - /* Remember if we saw a real child. */ - if (childOID != parentOID) - *has_child = true; - - appinfo = makeNode(AppendRelInfo); - appinfo->parent_relid = parentRTindex; - appinfo->child_relid = childRTindex; - appinfo->parent_reltype = parentrel->rd_rel->reltype; - appinfo->child_reltype = childrel->rd_rel->reltype; - make_inh_translation_list(parentrel, childrel, childRTindex, - &appinfo->translated_vars); - appinfo->parent_reloid = parentOID; - *appinfos = lappend(*appinfos, 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); } - else - *partitioned_child_rels = lappend_int(*partitioned_child_rels, - childRTindex); /* * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE. @@ -1726,6 +1767,15 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, root->rowMarks = lappend(root->rowMarks, childrc); } + + if (partitioned_child_rels && + childrte->relkind == RELKIND_PARTITIONED_TABLE) + *partitioned_child_rels = lappend_int(*partitioned_child_rels, + childRTindex); + if (newrte) + *newrte = childrte; + if (newRTindex) + *newRTindex = childRTindex; } /* diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index a1ebd4acc8..bfc05a1af5 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -1402,8 +1402,11 @@ relation_excluded_by_constraints(PlannerInfo *root, if (predicate_refuted_by(safe_restrictions, safe_restrictions, false)) return true; - /* Only plain relations have constraints */ - if (rte->rtekind != RTE_RELATION || rte->inh) + /* + * Only plain relations have constraints. In addition, there can be + * inheritance parent RTEs that are themselves partitions. + */ + if (rte->rtekind != RTE_RELATION || (rte->inh && !IS_OTHER_REL(rel))) return false; /* -- 2.11.0