1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-27 12:41:57 +03:00

Restructure planner's handling of inheritance. Rather than processing

inheritance trees on-the-fly, which pretty well constrained us to considering
only one way of planning inheritance, expand inheritance sets during the
planner prep phase, and build a side data structure that can be consulted
later to find which RTEs are members of which inheritance sets.  As proof of
concept, use the data structure to plan joins against inheritance sets more
efficiently: we can now use indexes on the set members in inner-indexscan
joins.  (The generated plans could be improved further, but it'll take some
executor changes.)  This data structure will also support handling UNION ALL
subqueries in the same way as inheritance sets, but that aspect of it isn't
finished yet.
This commit is contained in:
Tom Lane
2006-01-31 21:39:25 +00:00
parent 097df388b7
commit 8a1468af4e
18 changed files with 1042 additions and 508 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.139 2005/12/20 02:30:35 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.140 2006/01/31 21:39:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -44,9 +44,8 @@ int geqo_threshold;
static void set_base_rel_pathlists(PlannerInfo *root);
static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
RangeTblEntry *rte);
static void set_inherited_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte,
List *inheritlist);
static void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte);
static void set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte);
static void set_function_pathlist(PlannerInfo *root, RelOptInfo *rel,
@ -96,9 +95,9 @@ make_one_rel(PlannerInfo *root, List *joinlist)
int num_base_rels = 0;
Index rti;
for (rti = 1; rti < root->base_rel_array_size; rti++)
for (rti = 1; rti < root->simple_rel_array_size; rti++)
{
RelOptInfo *brel = root->base_rel_array[rti];
RelOptInfo *brel = root->simple_rel_array[rti];
if (brel == NULL)
continue;
@ -131,16 +130,10 @@ set_base_rel_pathlists(PlannerInfo *root)
{
Index rti;
/*
* Note: because we call expand_inherited_rtentry inside the loop, it's
* quite possible for the base_rel_array to be enlarged while the loop
* runs. Hence don't try to optimize the loop.
*/
for (rti = 1; rti < root->base_rel_array_size; rti++)
for (rti = 1; rti < root->simple_rel_array_size; rti++)
{
RelOptInfo *rel = root->base_rel_array[rti];
RelOptInfo *rel = root->simple_rel_array[rti];
RangeTblEntry *rte;
List *inheritlist;
/* there may be empty slots corresponding to non-baserel RTEs */
if (rel == NULL)
@ -154,7 +147,12 @@ set_base_rel_pathlists(PlannerInfo *root)
rte = rt_fetch(rti, root->parse->rtable);
if (rel->rtekind == RTE_SUBQUERY)
if (rte->inh)
{
/* It's an "append relation", process accordingly */
set_append_rel_pathlist(root, rel, rti, rte);
}
else if (rel->rtekind == RTE_SUBQUERY)
{
/* Subquery --- generate a separate plan for it */
set_subquery_pathlist(root, rel, rti, rte);
@ -164,11 +162,6 @@ set_base_rel_pathlists(PlannerInfo *root)
/* RangeFunction --- generate a separate plan for it */
set_function_pathlist(root, rel, rte);
}
else if ((inheritlist = expand_inherited_rtentry(root, rti)) != NIL)
{
/* Relation is root of an inheritance tree, process specially */
set_inherited_rel_pathlist(root, rel, rti, rte, inheritlist);
}
else
{
/* Plain relation */
@ -188,6 +181,9 @@ set_base_rel_pathlists(PlannerInfo *root)
static void
set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
{
Assert(rel->rtekind == RTE_RELATION);
Assert(!rte->inh);
/* Mark rel with estimated output rows, width, etc */
set_baserel_size_estimates(root, rel);
@ -224,37 +220,29 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
}
/*
* set_inherited_rel_pathlist
* Build access paths for a inheritance tree rooted at rel
* set_append_rel_pathlist
* Build access paths for an "append relation"
*
* inheritlist is a list of RT indexes of all tables in the inheritance tree,
* including a duplicate of the parent itself. Note we will not come here
* unless there's at least one child in addition to the parent.
*
* NOTE: the passed-in rel and RTE will henceforth represent the appended
* result of the whole inheritance tree. The members of inheritlist represent
* the individual tables --- in particular, the inheritlist member that is a
* duplicate of the parent RTE represents the parent table alone.
* We will generate plans to scan the individual tables that refer to
* the inheritlist RTEs, whereas Vars elsewhere in the plan tree that
* refer to the original RTE are taken to refer to the append output.
* In particular, this means we have separate RelOptInfos for the parent
* table and for the append output, which is a good thing because they're
* not the same size.
* The passed-in rel and RTE represent the entire append relation. The
* relation's contents are computed by appending together the output of
* the individual member relations. Note that in the inheritance case,
* the first member relation is actually the same table as is mentioned in
* the parent RTE ... but it has a different RTE and RelOptInfo. This is
* a good thing because their outputs are not the same size.
*/
static void
set_inherited_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte,
List *inheritlist)
set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte)
{
int parentRTindex = rti;
Oid parentOID = rte->relid;
List *subpaths = NIL;
ListCell *il;
ListCell *l;
/*
* XXX for now, can't handle inherited expansion of FOR UPDATE/SHARE; can
* we do better?
* we do better? (This will take some redesign because the executor
* currently supposes that every rowMark relation is involved in every
* row returned by the query.)
*/
if (list_member_int(root->parse->rowMarks, parentRTindex))
ereport(ERROR,
@ -262,64 +250,79 @@ set_inherited_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
errmsg("SELECT FOR UPDATE/SHARE is not supported for inheritance queries")));
/*
* Initialize to compute size estimates for whole inheritance tree
* Initialize to compute size estimates for whole append relation
*/
rel->rows = 0;
rel->width = 0;
/*
* Generate access paths for each table in the tree (parent AND children),
* and pick the cheapest path for each table.
* Generate access paths for each member relation, and pick the cheapest
* path for each one.
*/
foreach(il, inheritlist)
foreach(l, root->append_rel_list)
{
int childRTindex = lfirst_int(il);
RangeTblEntry *childrte;
Oid childOID;
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
int childRTindex;
RelOptInfo *childrel;
RangeTblEntry *childrte;
ListCell *parentvars;
ListCell *childvars;
childrte = rt_fetch(childRTindex, root->parse->rtable);
childOID = childrte->relid;
/* append_rel_list contains all append rels; ignore others */
if (appinfo->parent_relid != parentRTindex)
continue;
childRTindex = appinfo->child_relid;
/*
* Make a RelOptInfo for the child so we can do planning. Mark it as
* an "other rel" since it will not be part of the main join tree.
*/
childrel = build_other_rel(root, childRTindex);
childrel = build_simple_rel(root, childRTindex,
RELOPT_OTHER_MEMBER_REL);
/*
* Copy the parent's targetlist and restriction quals to the child,
* with attribute-number adjustment as needed. We don't bother to
* copy the join quals, since we can't do any joining of the
* individual tables. Also, we just zap attr_needed rather than
* trying to adjust it; it won't be looked at in the child.
* Copy the parent's targetlist and quals to the child, with
* appropriate substitution of variables.
*/
childrel->reltargetlist = (List *)
adjust_inherited_attrs((Node *) rel->reltargetlist,
parentRTindex,
parentOID,
childRTindex,
childOID);
childrel->attr_needed = NULL;
adjust_appendrel_attrs((Node *) rel->reltargetlist,
appinfo);
childrel->baserestrictinfo = (List *)
adjust_inherited_attrs((Node *) rel->baserestrictinfo,
parentRTindex,
parentOID,
childRTindex,
childOID);
adjust_appendrel_attrs((Node *) rel->baserestrictinfo,
appinfo);
childrel->joininfo = (List *)
adjust_appendrel_attrs((Node *) rel->joininfo,
appinfo);
/*
* Copy the parent's attr_needed data as well, with appropriate
* adjustment of relids and attribute numbers.
*/
pfree(childrel->attr_needed);
childrel->attr_needed =
adjust_appendrel_attr_needed(rel, appinfo,
childrel->min_attr,
childrel->max_attr);
/*
* If we can prove we don't need to scan this child via constraint
* exclusion, just ignore it. (We have to have converted the
* baserestrictinfo Vars before we can make the test.)
*
* XXX it'd probably be better to give the child some kind of dummy
* cheapest path, or otherwise explicitly mark it as ignorable.
* Currently there is an ugly check in join_before_append() to handle
* excluded children.
*/
if (constraint_exclusion)
childrte = rt_fetch(childRTindex, root->parse->rtable);
if (constraint_exclusion &&
childrte->rtekind == RTE_RELATION)
{
List *constraint_pred;
constraint_pred = get_relation_constraints(childOID, childrel);
constraint_pred = get_relation_constraints(childrte->relid,
childrel);
/*
* We do not currently enforce that CHECK constraints contain only
@ -363,7 +366,8 @@ set_inherited_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
Var *parentvar = (Var *) lfirst(parentvars);
Var *childvar = (Var *) lfirst(childvars);
if (IsA(parentvar, Var) &&IsA(childvar, Var))
if (IsA(parentvar, Var) &&
IsA(childvar, Var))
{
int pndx = parentvar->varattno - rel->min_attr;
int cndx = childvar->varattno - childrel->min_attr;
@ -392,9 +396,9 @@ has_multiple_baserels(PlannerInfo *root)
int num_base_rels = 0;
Index rti;
for (rti = 1; rti < root->base_rel_array_size; rti++)
for (rti = 1; rti < root->simple_rel_array_size; rti++)
{
RelOptInfo *brel = root->base_rel_array[rti];
RelOptInfo *brel = root->simple_rel_array[rti];
if (brel == NULL)
continue;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/joinpath.c,v 1.98 2005/11/22 18:17:12 momjian Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/path/joinpath.c,v 1.99 2006/01/31 21:39:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -20,6 +20,7 @@
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/prep.h"
#include "parser/parsetree.h"
#include "utils/lsyscache.h"
@ -35,6 +36,9 @@ static void match_unsorted_outer(PlannerInfo *root, RelOptInfo *joinrel,
static void hash_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel,
RelOptInfo *outerrel, RelOptInfo *innerrel,
List *restrictlist, JoinType jointype);
static void join_before_append(PlannerInfo *root, RelOptInfo *joinrel,
RelOptInfo *outerrel, RelOptInfo *innerrel,
JoinType jointype);
static List *select_mergejoin_clauses(RelOptInfo *joinrel,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
@ -115,6 +119,13 @@ add_paths_to_joinrel(PlannerInfo *root,
if (enable_hashjoin)
hash_inner_and_outer(root, joinrel, outerrel, innerrel,
restrictlist, jointype);
/*
* 5. If the inner relation is an append relation, consider joining
* the outer rel to each append member and then appending the results.
*/
if (innerrel->cheapest_total_path->pathtype == T_Append)
join_before_append(root, joinrel, outerrel, innerrel, jointype);
}
/*
@ -777,6 +788,154 @@ hash_inner_and_outer(PlannerInfo *root,
}
}
/*
* join_before_append
* Creates possible join paths for processing a single join relation
* 'joinrel' when the inner input is an append relation.
*
* The idea here is to swap the order of the APPEND and JOIN operators.
* This is only really helpful if it allows us to reduce the cost of
* scanning the members of the append relation, and so we only consider
* plans involving nestloops with inner indexscans. Also, since the APPEND
* will certainly yield an unsorted result, there's no point in considering
* any but the cheapest-total outer path.
*
* XXX this is a bit of a kluge, because the resulting plan has to evaluate
* the outer relation multiple times. Would be better to allow
* best_inner_indexscan to generate an AppendPath and not have this routine
* at all. But we can't do that without some executor changes (need a way
* to pass outer keys down through Append). FIXME later.
*
* 'joinrel' is the join relation
* 'outerrel' is the outer join relation
* 'innerrel' is the inner join relation
* 'jointype' is the type of join to do
*/
static void
join_before_append(PlannerInfo *root,
RelOptInfo *joinrel,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
JoinType jointype)
{
Path *outer_cheapest_total = outerrel->cheapest_total_path;
int parentRTindex = innerrel->relid;
List *append_paths = NIL;
ListCell *l;
/*
* Swapping JOIN with APPEND only works for inner joins, not outer joins.
* However, we can also handle a unique-ified outer path.
*/
switch (jointype)
{
case JOIN_INNER:
break;
case JOIN_UNIQUE_OUTER:
outer_cheapest_total = (Path *)
create_unique_path(root, outerrel, outer_cheapest_total);
break;
case JOIN_LEFT:
case JOIN_RIGHT:
case JOIN_FULL:
case JOIN_IN:
case JOIN_UNIQUE_INNER:
return; /* can't join this way */
default:
elog(ERROR, "unrecognized join type: %d",
(int) jointype);
break;
}
/*
* Generate suitable access paths for each member relation.
*/
foreach(l, root->append_rel_list)
{
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
int childRTindex;
RelOptInfo *childrel;
Path *bestinnerjoin;
Relids joinrelids;
Relids *save_attr_needed;
RelOptInfo *this_joinrel;
List *this_restrictlist;
/* append_rel_list contains all append rels; ignore others */
if (appinfo->parent_relid != parentRTindex)
continue;
childRTindex = appinfo->child_relid;
childrel = find_base_rel(root, childRTindex);
Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);
/*
* If the child has no cheapest_total_path, assume it was deemed
* excludable by constraint exclusion (see set_append_rel_pathlist).
*/
if (childrel->cheapest_total_path == NULL)
{
Assert(constraint_exclusion);
continue;
}
/*
* Get the best innerjoin indexpath (if any) for this outer rel.
*/
bestinnerjoin = best_inner_indexscan(root, childrel,
outerrel->relids, JOIN_INNER);
/*
* If no luck on an indexpath for this rel, we'll still consider
* an Append substituting the cheapest-total inner path. This
* is only likely to win if there's at least one member rel for
* which an indexscan path does exist.
*/
if (!bestinnerjoin)
bestinnerjoin = childrel->cheapest_total_path;
/*
* We need a joinrel that describes this join accurately. Although
* the joinrel won't ever be used by the join path search algorithm
* in joinrels.c, it provides necessary context for the Path,
* such as properly-translated target and quals lists.
*/
joinrelids = bms_copy(joinrel->relids);
joinrelids = bms_del_member(joinrelids, parentRTindex);
joinrelids = bms_add_member(joinrelids, childRTindex);
/*
* Kluge: temporarily adjust the outer rel's attr_needed info so
* that it references the member rel instead of the appendrel.
* This is needed to build the correct targetlist for the joinrel.
*/
save_attr_needed = outerrel->attr_needed;
outerrel->attr_needed =
adjust_other_rel_attr_needed(outerrel, appinfo,
outerrel->min_attr,
outerrel->max_attr);
this_joinrel = build_join_rel(root, joinrelids, outerrel, childrel,
jointype, &this_restrictlist);
/* Now we can undo the hack on attr_needed */
pfree(outerrel->attr_needed);
outerrel->attr_needed = save_attr_needed;
/* Build Path for join and add to result list */
append_paths = lappend(append_paths,
create_nestloop_path(root,
this_joinrel,
JOIN_INNER,
outer_cheapest_total,
bestinnerjoin,
this_restrictlist,
NIL));
}
/* Form the completed Append path and add it to the join relation. */
add_path(joinrel, (Path *) create_append_path(joinrel, append_paths));
}
/*
* select_mergejoin_clauses
* Select mergejoin clauses that are usable for a particular join.