Re: UNION ALL on partitioned tables won't use indices. - Mailing list pgsql-hackers
From | Kyotaro HORIGUCHI |
---|---|
Subject | Re: UNION ALL on partitioned tables won't use indices. |
Date | |
Msg-id | 20131113.173811.35360486.horiguchi.kyotaro@lab.ntt.co.jp Whole thread Raw |
In response to | Re: UNION ALL on partitioned tables won't use indices. (Peter Eisentraut <peter_e@gmx.net>) |
Responses |
Re: UNION ALL on partitioned tables won't use indices.
(Noah Misch <noah@leadboat.com>)
|
List | pgsql-hackers |
Umm. I might be working on a bit unstable place.. > Your patch doesn't apply anymore. Please rebase it. Thank you. I rebased all patches to current master. regards, -- Kyotaro Horiguchi NTT Open Source Software Center diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c index 711b161..734ed47 100644 --- a/src/backend/optimizer/path/equivclass.c +++ b/src/backend/optimizer/path/equivclass.c @@ -1940,6 +1940,7 @@ add_child_rel_equivalences(PlannerInfo *root, Expr *child_expr; Relids new_relids; Relids new_nullable_relids; + bool has_children = false; child_expr = (Expr *) adjust_appendrel_attrs(root, @@ -1969,9 +1970,15 @@ add_child_rel_equivalences(PlannerInfo *root, child_rel->relids); } + /* + * Does this child_rel have children? If and only if so, tell + * add_eq_member to register new_relids to cur_ec. + */ + has_children = + root->simple_rte_array[child_rel->relid]->inh; (void) add_eq_member(cur_ec, child_expr, new_relids, new_nullable_relids, - true, cur_em->em_datatype); + !has_children, cur_em->em_datatype); } } } diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 64b1705..0e3cf4b 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -944,7 +944,8 @@ create_merge_append_path(PlannerInfo *root, MergeAppendPath *pathnode = makeNode(MergeAppendPath); Cost input_startup_cost; Cost input_total_cost; - ListCell *l; + ListCell *l, *lm; + bool collapse_subpaths = false; pathnode->path.pathtype = T_MergeAppend; pathnode->path.parent = rel; @@ -953,6 +954,47 @@ create_merge_append_path(PlannerInfo *root, pathnode->path.pathkeys = pathkeys; pathnode->subpaths= subpaths; + + /* + * If subpaths containes MergeAppendPaths already ordered on the pathkeys + * of the creating node, they can be expanded onto this node. + */ + foreach (lm, subpaths) + { + Path *spath = (Path *) lfirst(lm); + + if (IsA(spath, MergeAppendPath) && + pathkeys_contained_in(pathkeys, spath->pathkeys)) + { + collapse_subpaths = true; + break; + } + } + + if (collapse_subpaths) + { + subpaths = NIL; + + foreach (lm, pathnode->subpaths) + { + MergeAppendPath *mpath = (MergeAppendPath *) lfirst(lm); + ListCell *lcm; + + if (IsA(mpath, MergeAppendPath) && + pathkeys_contained_in(pathkeys, mpath->path.pathkeys)) + { + foreach (lcm, mpath->subpaths) + { + Path *smpath = (Path*) lfirst (lcm); + subpaths = lappend(subpaths, smpath); + } + } + else + subpaths = lappend(subpaths, subpaths); + } + pathnode->subpaths = subpaths; + } + /* * Apply query-wide LIMIT if known and path is for sole base relation. * (Handling this at this low levelis a bit klugy.) diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 64b1705..0e3cf4b 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -944,7 +944,8 @@ create_merge_append_path(PlannerInfo *root, MergeAppendPath *pathnode = makeNode(MergeAppendPath); Cost input_startup_cost; Cost input_total_cost; - ListCell *l; + ListCell *l, *lm; + bool collapse_subpaths = false; pathnode->path.pathtype = T_MergeAppend; pathnode->path.parent = rel; @@ -953,6 +954,47 @@ create_merge_append_path(PlannerInfo *root, pathnode->path.pathkeys = pathkeys; pathnode->subpaths= subpaths; + + /* + * If subpaths containes MergeAppendPaths already ordered on the pathkeys + * of the creating node, they can be expanded onto this node. + */ + foreach (lm, subpaths) + { + Path *spath = (Path *) lfirst(lm); + + if (IsA(spath, MergeAppendPath) && + pathkeys_contained_in(pathkeys, spath->pathkeys)) + { + collapse_subpaths = true; + break; + } + } + + if (collapse_subpaths) + { + subpaths = NIL; + + foreach (lm, pathnode->subpaths) + { + MergeAppendPath *mpath = (MergeAppendPath *) lfirst(lm); + ListCell *lcm; + + if (IsA(mpath, MergeAppendPath) && + pathkeys_contained_in(pathkeys, mpath->path.pathkeys)) + { + foreach (lcm, mpath->subpaths) + { + Path *smpath = (Path*) lfirst (lcm); + subpaths = lappend(subpaths, smpath); + } + } + else + subpaths = lappend(subpaths, subpaths); + } + pathnode->subpaths = subpaths; + } + /* * Apply query-wide LIMIT if known and path is for sole base relation. * (Handling this at this low levelis a bit klugy.) diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index d8aa35d..8167583 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -404,6 +404,15 @@ subquery_planner(PlannerGlobal *glob, Query *parse, expand_inherited_tables(root); /* + * Collapse multilevel inheritances into fewer levels. + * + * UNION ALL containing subselects on inherited tables leaves multilevel + * inheritance after calling expand_inherited_tables(). Fold them in + * order to make MergeAppend plan available for such queries. + */ + collapse_appendrels(root); + + /* * Set hasHavingQual to remember if HAVING clause is present. Needed * because preprocess_expression willreduce a constant-true condition to * an empty qual list ... but "HAVING TRUE" is not a semantic no-op. diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index e249628..c7933b5 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -57,6 +57,11 @@ typedef struct AppendRelInfo *appinfo;} adjust_appendrel_attrs_context; +typedef struct { + AppendRelInfo *child_appinfo; + Index target_rti; +} transvars_merge_context; +static Plan *recurse_set_operations(Node *setOp, PlannerInfo *root, double tuple_fraction, List *colTypes, List *colCollations, @@ -98,6 +103,8 @@ static List *generate_append_tlist(List *colTypes, List *colCollations, List *input_plans, List *refnames_tlist);static List *generate_setop_grouplist(SetOperationStmt *op, List*targetlist); +static Node *transvars_merge_mutator(Node *node, + transvars_merge_context *ctx);static void expand_inherited_rtentry(PlannerInfo *root,RangeTblEntry *rte, Index rti);static void make_inh_translation_list(Relation oldrelation, @@ -1211,6 +1218,131 @@ expand_inherited_tables(PlannerInfo *root)}/* + * collapse_appendrels + * Pulling up the descendents by recursively combining successive + * translations. + * + * Although the same thing could be done in adjust_appendrel_attrs(), + * doing it here all through at once is more efficient than individually + * (and maybe repetitively) done there. + */ +void +collapse_appendrels(PlannerInfo *root) +{ + Index last_parent_relid = 1; + AppendRelInfo *last_parent = NULL; + ListCell *lcchild; + ListCell *lcparent; + bool full_search = false; + + foreach(lcchild, root->append_rel_list) + { + AppendRelInfo *ch_appinfo = (AppendRelInfo *)lfirst(lcchild); + Index ch_parent_relid = ch_appinfo->parent_relid; + + if (last_parent_relid != ch_parent_relid) + { + /* + * Find the parent for the current child appinfo if parent relid + * is different from that of preveous child. + */ + do + { + /* + * For most cases, the relations are referred to as the parent + * in their apperarence order in rtable from + * append_rel_list. So start searching for the parent appinfos + * from just after the last parent. If the incremental search + * was failed, retry searching the entire list and exit on + * failure. + */ + if (!last_parent) + { + /* + * If this is the first time or the preveous search was + * failed, set up for full search. + */ + lcparent = list_head(root->append_rel_list); + last_parent_relid = 1; + full_search = true; + } + + last_parent = NULL; + for_each_cell(lcparent, lcparent) + { + /* + * Children's and parents' apprelinfos are bonded via + * rtable relations. So two apprelinfos are in + * parent-child relationship when the child_relid of the + * parent is equal to the parent_relid of the child. + */ + AppendRelInfo *papp = (AppendRelInfo*)lfirst(lcparent); + if (papp->child_relid == ch_parent_relid) + { + last_parent = papp; + last_parent_relid = ch_parent_relid; + + /* Search from the next cell next time. */ + lcparent = lnext(lcparent); + full_search = false; + break; /* Parent found */ + } + } + /* Retry only when incremental search was failed. */ + } while (!full_search && !last_parent); + } + + /* + * Replace child translated_vars so as to be a direct children of the + * topmost relation. + */ + if (last_parent) + { + transvars_merge_context ctx; + + ctx.child_appinfo = ch_appinfo; + ctx.target_rti = last_parent->child_relid; + + ch_appinfo->translated_vars = + (List*)expression_tree_mutator( + (Node*)last_parent->translated_vars, + transvars_merge_mutator, &ctx); + ch_appinfo->parent_relid = last_parent->parent_relid; + ch_appinfo->parent_reltype = last_parent->parent_reltype; + } + } +} + +/* + * Replace Var nodes with corresponding child nodes. + */ +static Node * +transvars_merge_mutator(Node *node, transvars_merge_context *ctx) +{ + if (node == NULL) + return NULL; + + if (IsA(node, Var)) + { + Var *oldv = (Var*)node; + + if (!oldv->varlevelsup && oldv->varno == ctx->target_rti) + { + if (oldv->varattno > + list_length(ctx->child_appinfo->translated_vars)) + elog(ERROR, + "attribute %d of relation \"%s\" does not exist", + oldv->varattno, + get_rel_name(ctx->child_appinfo->parent_reloid)); + return (Node*)copyObject(list_nth(ctx->child_appinfo->translated_vars, + oldv->varattno - 1)); + } + } + return expression_tree_mutator(node, + transvars_merge_mutator, (void*)ctx); +} + +/* * expand_inherited_rtentry * Check whether a rangetable entry represents an inheritance set. * If so, addentries for all the child tables to the query's diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.h index 0934e63..7a83938 100644 --- a/src/include/optimizer/prep.h +++ b/src/include/optimizer/prep.h @@ -49,6 +49,7 @@ extern Plan *plan_set_operations(PlannerInfo *root, double tuple_fraction, List **sortClauses);externvoid expand_inherited_tables(PlannerInfo *root); +extern void collapse_appendrels(PlannerInfo *root);extern Node *adjust_appendrel_attrs(PlannerInfo *root, Node *node, AppendRelInfo *appinfo); diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index e249628..3cba2a1 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -57,6 +57,11 @@ typedef struct AppendRelInfo *appinfo;} adjust_appendrel_attrs_context; +typedef struct { + AppendRelInfo *child_appinfo; + Index target_rti; +} transvars_merge_context; +static Plan *recurse_set_operations(Node *setOp, PlannerInfo *root, double tuple_fraction, List *colTypes, List *colCollations, @@ -98,6 +103,8 @@ static List *generate_append_tlist(List *colTypes, List *colCollations, List *input_plans, List *refnames_tlist);static List *generate_setop_grouplist(SetOperationStmt *op, List*targetlist); +static Node *transvars_merge_mutator(Node *node, + transvars_merge_context *ctx);static void expand_inherited_rtentry(PlannerInfo *root,RangeTblEntry *rte, Index rti);static void make_inh_translation_list(Relation oldrelation, @@ -1210,6 +1217,34 @@ expand_inherited_tables(PlannerInfo *root) }} +static Node * +transvars_merge_mutator(Node *node, transvars_merge_context *ctx) +{ + if (node == NULL) + return NULL; + + if (IsA(node, Var)) + { + Var *oldv = (Var*)node; + + if (!oldv->varlevelsup && oldv->varno == ctx->target_rti) + { + if (oldv->varattno > + list_length(ctx->child_appinfo->translated_vars)) + elog(ERROR, + "attribute %d of relation \"%s\" does not exist", + oldv->varattno, + get_rel_name(ctx->child_appinfo->parent_reloid)); + + return (Node*)copyObject( + list_nth(ctx->child_appinfo->translated_vars, + oldv->varattno - 1)); + } + } + return expression_tree_mutator(node, + transvars_merge_mutator, (void*)ctx); +} +/* * expand_inherited_rtentry * Check whether a rangetable entry represents an inheritance set. @@ -1237,6 +1272,7 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) List *inhOIDs; List *appinfos; ListCell *l; + AppendRelInfo *parent_appinfo = NULL; /* Does RT entry allow inheritance? */ if (!rte->inh) @@ -1301,6 +1337,21 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) oldrc->isParent= true; /* + * If parent relation is appearing in a subselect of UNION ALL, it has + * furthur parent appendrelinfo. Save it to pull up inheritance children + * later. + */ + foreach(l, root->append_rel_list) + { + AppendRelInfo *appinfo = (AppendRelInfo *)lfirst(l); + if(appinfo->child_relid == rti) + { + parent_appinfo = appinfo; + break; + } + } + + /* * Must open the parent relation to examine its tupdesc. We need not lock * it; we assume the rewriter alreadydid. */ @@ -1378,6 +1429,28 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) } /* + * Pull up this appinfo onto just above of the parent. The parent + * relation has its own parent when it appears as a subquery of UNION + * ALL. Pulling up these children gives a chance to consider + * MergeAppend on whole the UNION ALL tree. + */ + + if (parent_appinfo) + { + transvars_merge_context ctx; + + ctx.child_appinfo = appinfo; + ctx.target_rti = rti; + + appinfo->parent_relid = parent_appinfo->parent_relid; + appinfo->parent_reltype = parent_appinfo->parent_reltype; + appinfo->parent_reloid = parent_appinfo->parent_reloid; + appinfo->translated_vars = (List*)expression_tree_mutator( + parent_appinfo->translated_vars, + transvars_merge_mutator, &ctx); + } + + /* * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE. */ if (oldrc)
pgsql-hackers by date: