From 1e793976d1affae8aa6fa1c835752bad530132b6 Mon Sep 17 00:00:00 2001 From: Amit Date: Sat, 2 Mar 2019 14:13:13 +0900 Subject: [PATCH v27 1/6] Build "other rels" of appendrel baserels in a separate step Currently they're built in a stanza in build_simple_rel() which builds the child rels in the same invocation of build_simple_rel that's used to build the parent relation. Since query hasn't been processed to distribute restriction clauses to individual baserels at this point, we cannot perform partition pruning before adding the children. Newly added add_other_rels_to_query() runs *after* query_planner has distributed restriction clauses to base relations. This will allow us to use the clauses applied a given partitioned baserel to perform partition pruning, and add other rels for only the unpruned partitions. Later patches will do that. --- src/backend/optimizer/path/allpaths.c | 2 +- src/backend/optimizer/plan/initsplan.c | 60 ++++++++++++++++++-- src/backend/optimizer/plan/planmain.c | 15 +++-- src/backend/optimizer/util/relnode.c | 101 +++++++++++++++++++++------------ src/include/optimizer/pathnode.h | 2 + src/include/optimizer/planmain.h | 1 + 6 files changed, 135 insertions(+), 46 deletions(-) diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 0debac75c6..8d8a8f17d5 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -1028,7 +1028,7 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, /* * The child rel's RelOptInfo was already created during - * add_base_rels_to_query. + * add_other_rels_to_query. */ childrel = find_base_rel(root, childRTindex); Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL); diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index 2afc3f1dfe..077d3203ba 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -20,6 +20,7 @@ #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/inherit.h" #include "optimizer/joininfo.h" #include "optimizer/optimizer.h" #include "optimizer/pathnode.h" @@ -30,6 +31,7 @@ #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "parser/analyze.h" +#include "parser/parsetree.h" #include "rewrite/rewriteManip.h" #include "utils/lsyscache.h" @@ -97,10 +99,11 @@ static void check_hashjoinable(RestrictInfo *restrictinfo); * jtnode. Internally, the function recurses through the jointree. * * At the end of this process, there should be one baserel RelOptInfo for - * every non-join RTE that is used in the query. Therefore, this routine - * is the only place that should call build_simple_rel with reloptkind - * RELOPT_BASEREL. (Note: build_simple_rel recurses internally to build - * "other rel" RelOptInfos for the members of any appendrels we find here.) + * every non-join RTE that is specified in the query. Therefore, this + * routine is the only place that should call build_simple_rel with + * reloptkind RELOPT_BASEREL. (Note: "other rel" RelOptInfos for the + * members of any appendrels we find here are built later when query_planner + * calls add_other_rels_to_query().) */ void add_base_rels_to_query(PlannerInfo *root, Node *jtnode) @@ -133,6 +136,55 @@ add_base_rels_to_query(PlannerInfo *root, Node *jtnode) (int) nodeTag(jtnode)); } +/* + * add_other_rels_to_query + * + * Scan the query's jointree and for each base rels that is an appendrel, + * create otherrel RelOptInfos of its children + * + * At the end of this process, there should be RelOptInfos for all relations + * that will be scanned by the query. + */ +void +add_other_rels_to_query(PlannerInfo *root, Node *jtnode) +{ + if (jtnode == NULL) + return; + if (IsA(jtnode, RangeTblRef)) + { + int varno = ((RangeTblRef *) jtnode)->rtindex; + RangeTblEntry *rte = rt_fetch(varno, root->parse->rtable); + + /* + * Only the parent subquery of a flattened UNION ALL and an inherited + * table can have children. + */ + if (rte->rtekind != RTE_SUBQUERY && rte->rtekind != RTE_RELATION) + return; + + if (rte->inh) + (void) add_appendrel_other_rels(root, rte, varno); + } + else if (IsA(jtnode, FromExpr)) + { + FromExpr *f = (FromExpr *) jtnode; + ListCell *l; + + foreach(l, f->fromlist) + add_other_rels_to_query(root, lfirst(l)); + } + else if (IsA(jtnode, JoinExpr)) + { + JoinExpr *j = (JoinExpr *) jtnode; + + add_other_rels_to_query(root, j->larg); + add_other_rels_to_query(root, j->rarg); + } + else + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(jtnode)); +} + /***************************************************************************** * diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c index 3cedd01c98..03c81772a3 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -159,10 +159,8 @@ query_planner(PlannerInfo *root, List *tlist, setup_append_rel_array(root); /* - * Construct RelOptInfo nodes for all base relations in query, and - * indirectly for all appendrel member relations ("other rels"). This - * will give us a RelOptInfo for every "simple" (non-join) rel involved in - * the query. + * Construct RelOptInfo nodes for all base relations directly mentioned + * in query, but not any appendrel member relations ("other rels") yet. * * Note: the reason we find the rels by searching the jointree and * appendrel list, rather than just scanning the rangetable, is that the @@ -204,6 +202,15 @@ query_planner(PlannerInfo *root, List *tlist, generate_base_implied_equalities(root); /* + * Now that we have restrict clauses figured out and assigned to proper + * base rels, we can proceed to add otherrels, that is, UNION ALL child + * tables, inheritance child tables. Having restrict clauses ready helps + * to exclude any children that wouldn't be necessary to scan, based on + * constraint exclusion and partition pruning. + */ + add_other_rels_to_query(root, (Node *) root->parse->jointree); + + /* * We have completed merging equivalence sets, so it's now possible to * generate pathkeys in canonical form; so compute query_pathkeys and * other pathkeys fields in PlannerInfo. diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 4130514952..a618950f88 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -16,19 +16,26 @@ #include +#include "access/table.h" #include "miscadmin.h" +#include "nodes/makefuncs.h" #include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/inherit.h" +#include "optimizer/optimizer.h" #include "optimizer/pathnode.h" #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 "partitioning/partdesc.h" #include "utils/hsearch.h" +#include "utils/rel.h" typedef struct JoinHashEntry @@ -273,53 +280,73 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) root->qual_security_level = Max(root->qual_security_level, list_length(rte->securityQuals)); + return rel; +} + +/* + * add_appendrel_other_rels + * This adds the "other rel" RelOptInfos a given "appendrel" baserel + * + * Parent relation in this case is the parent subquery in the flattened UNION + * ALL case or an inheritance parent table. + */ +void +add_appendrel_other_rels(PlannerInfo *root, RangeTblEntry *rte, Index rti) +{ + ListCell *l; + RelOptInfo *rel; + int i; + + rel = find_base_rel(root, rti); + /* - * 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. + * For partitioned tables, we need to store the child RelOptInfos in the + * rel->part_rels array too. */ - if (rte->inh) + if (rel->part_scheme) + rel->part_rels = (RelOptInfo **) + palloc0(sizeof(RelOptInfo *) * rel->nparts); + + i = 0; + foreach(l, root->append_rel_list) { - ListCell *l; - int nparts = rel->nparts; - int cnt_parts = 0; + AppendRelInfo *appinfo = lfirst(l); + Index childRTindex = appinfo->child_relid; + RangeTblEntry *childrte; + RelOptInfo *childrel; - if (nparts > 0) - rel->part_rels = (RelOptInfo **) - palloc(sizeof(RelOptInfo *) * nparts); + if (appinfo->parent_relid != rti) + continue; - foreach(l, root->append_rel_list) + Assert(childRTindex < root->simple_rel_array_size); + childrte = root->simple_rte_array[childRTindex]; + Assert(childrte != NULL); + childrel = build_simple_rel(root, childRTindex, rel); + + /* + * For partitioned parents, we also need to add childrel to its + * part_rels array. The order in which child tables appear in + * append_rel_list is same as the order in which they appear in the + * parent's PartitionDesc, so assigning partitions like this works. + */ + if (rel->part_scheme != NULL) { - AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l); - RelOptInfo *childrel; - - /* append_rel_list contains all append rels; ignore others */ - if (appinfo->parent_relid != relid) - continue; - - childrel = build_simple_rel(root, appinfo->child_relid, - rel); - - /* Nothing more to do for an unpartitioned table. */ - if (!rel->part_scheme) - continue; - - /* - * The order of partition OIDs in append_rel_list is the same as - * the order in the PartitionDesc, so the order of part_rels will - * also match the PartitionDesc. See expand_partitioned_rtentry. - */ - Assert(cnt_parts < nparts); - rel->part_rels[cnt_parts] = childrel; - cnt_parts++; + Assert(rel->nparts > 0 && i < rel->nparts); + rel->part_rels[i] = childrel; } - /* We should have seen all the child partitions. */ - Assert(cnt_parts == nparts); + i++; + + /* Child may itself be an inherited relation. */ + if (childrte->inh) + add_appendrel_other_rels(root, childrte, childRTindex); } - return rel; + /* + * For a partitioned table with non-zero number of partitions, we must + * have assigned all elements of its part_rels array. + */ + Assert(rel->nparts == 0 || i == rel->nparts); } /* diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index 574bb85b50..1a07963a7d 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -279,6 +279,8 @@ extern void setup_simple_rel_arrays(PlannerInfo *root); extern void setup_append_rel_array(PlannerInfo *root); extern RelOptInfo *build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent); +extern void add_appendrel_other_rels(PlannerInfo *root, RangeTblEntry *rte, + Index rti); extern RelOptInfo *find_base_rel(PlannerInfo *root, int relid); extern RelOptInfo *find_join_rel(PlannerInfo *root, Relids relids); extern RelOptInfo *build_join_rel(PlannerInfo *root, diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h index 3bbdb5e2f7..035caac500 100644 --- a/src/include/optimizer/planmain.h +++ b/src/include/optimizer/planmain.h @@ -65,6 +65,7 @@ extern int from_collapse_limit; extern int join_collapse_limit; extern void add_base_rels_to_query(PlannerInfo *root, Node *jtnode); +extern void add_other_rels_to_query(PlannerInfo *root, Node *jtnode); extern void build_base_rel_tlists(PlannerInfo *root, List *final_tlist); extern void add_vars_to_targetlist(PlannerInfo *root, List *vars, Relids where_needed, bool create_new_ph); -- 2.11.0