mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-01-12 18:34:36 +08:00
Nab some low-hanging fruit: replace the planner's base_rel_list and
other_rel_list with a single array indexed by rangetable index. This reduces find_base_rel from O(N) to O(1) without any real penalty. While find_base_rel isn't one of the major bottlenecks in any profile I've seen so far, it was starting to creep up on the radar screen for complex queries --- so might as well fix it.
This commit is contained in:
parent
9ab4d98168
commit
9a586fe0c5
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.253 2005/06/05 22:32:54 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.254 2005/06/06 04:13:35 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Every node type that can appear in stored rules' parsetrees *must*
|
||||
@ -1150,9 +1150,8 @@ _outPlannerInfo(StringInfo str, PlannerInfo *node)
|
||||
{
|
||||
WRITE_NODE_TYPE("PLANNERINFO");
|
||||
|
||||
/* NB: this isn't a complete set of fields */
|
||||
WRITE_NODE_FIELD(parse);
|
||||
WRITE_NODE_FIELD(base_rel_list);
|
||||
WRITE_NODE_FIELD(other_rel_list);
|
||||
WRITE_NODE_FIELD(join_rel_list);
|
||||
WRITE_NODE_FIELD(equi_key_list);
|
||||
WRITE_NODE_FIELD(in_info_list);
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.131 2005/06/05 22:32:55 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.132 2005/06/06 04:13:35 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -88,9 +88,33 @@ make_one_rel(PlannerInfo *root)
|
||||
rel = make_fromexpr_rel(root, root->parse->jointree);
|
||||
|
||||
/*
|
||||
* The result should join all the query's base rels.
|
||||
* The result should join all and only the query's base rels.
|
||||
*/
|
||||
Assert(bms_num_members(rel->relids) == list_length(root->base_rel_list));
|
||||
#ifdef USE_ASSERT_CHECKING
|
||||
{
|
||||
int num_base_rels = 0;
|
||||
Index rti;
|
||||
|
||||
for (rti = 1; rti < root->base_rel_array_size; rti++)
|
||||
{
|
||||
RelOptInfo *brel = root->base_rel_array[rti];
|
||||
|
||||
if (brel == NULL)
|
||||
continue;
|
||||
|
||||
Assert(brel->relid == rti); /* sanity check on array */
|
||||
|
||||
/* ignore RTEs that are "other rels" */
|
||||
if (brel->reloptkind != RELOPT_BASEREL)
|
||||
continue;
|
||||
|
||||
Assert(bms_is_member(rti, rel->relids));
|
||||
num_base_rels++;
|
||||
}
|
||||
|
||||
Assert(bms_num_members(rel->relids) == num_base_rels);
|
||||
}
|
||||
#endif
|
||||
|
||||
return rel;
|
||||
}
|
||||
@ -104,16 +128,29 @@ make_one_rel(PlannerInfo *root)
|
||||
static void
|
||||
set_base_rel_pathlists(PlannerInfo *root)
|
||||
{
|
||||
ListCell *l;
|
||||
Index rti;
|
||||
|
||||
foreach(l, root->base_rel_list)
|
||||
/*
|
||||
* 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++)
|
||||
{
|
||||
RelOptInfo *rel = (RelOptInfo *) lfirst(l);
|
||||
Index rti = rel->relid;
|
||||
RelOptInfo *rel = root->base_rel_array[rti];
|
||||
RangeTblEntry *rte;
|
||||
List *inheritlist;
|
||||
|
||||
Assert(rti > 0); /* better be base rel */
|
||||
/* there may be empty slots corresponding to non-baserel RTEs */
|
||||
if (rel == NULL)
|
||||
continue;
|
||||
|
||||
Assert(rel->relid == rti); /* sanity check on array */
|
||||
|
||||
/* ignore RTEs that are "other rels" */
|
||||
if (rel->reloptkind != RELOPT_BASEREL)
|
||||
continue;
|
||||
|
||||
rte = rt_fetch(rti, root->parse->rtable);
|
||||
|
||||
if (rel->rtekind == RTE_SUBQUERY)
|
||||
@ -246,10 +283,9 @@ set_inherited_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
|
||||
childOID = childrte->relid;
|
||||
|
||||
/*
|
||||
* Make a RelOptInfo for the child so we can do planning. Do NOT
|
||||
* attach the RelOptInfo to the query's base_rel_list, however,
|
||||
* since the child is not part of the main join tree. Instead,
|
||||
* the child RelOptInfo is added to other_rel_list.
|
||||
* 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);
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planmain.c,v 1.82 2005/06/05 22:32:56 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planmain.c,v 1.83 2005/06/06 04:13:35 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -106,12 +106,15 @@ query_planner(PlannerInfo *root, List *tlist, double tuple_fraction,
|
||||
&constant_quals);
|
||||
|
||||
/*
|
||||
* init planner lists to empty
|
||||
* Init planner lists to empty. We create the base_rel_array with a
|
||||
* size that will be sufficient if no pullups or inheritance additions
|
||||
* happen ... otherwise it will be enlarged as needed.
|
||||
*
|
||||
* NOTE: in_info_list was set up by subquery_planner, do not touch here
|
||||
*/
|
||||
root->base_rel_list = NIL;
|
||||
root->other_rel_list = NIL;
|
||||
root->base_rel_array_size = list_length(parse->rtable) + 1;
|
||||
root->base_rel_array = (RelOptInfo **)
|
||||
palloc0(root->base_rel_array_size * sizeof(RelOptInfo *));
|
||||
root->join_rel_list = NIL;
|
||||
root->equi_key_list = NIL;
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.67 2005/06/05 22:32:56 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.68 2005/06/06 04:13:36 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -25,7 +25,8 @@
|
||||
|
||||
static RelOptInfo *make_reloptinfo(PlannerInfo *root, int relid,
|
||||
RelOptKind reloptkind);
|
||||
static void build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel);
|
||||
static void build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
|
||||
RelOptInfo *input_rel);
|
||||
static List *build_joinrel_restrictlist(PlannerInfo *root,
|
||||
RelOptInfo *joinrel,
|
||||
RelOptInfo *outer_rel,
|
||||
@ -43,78 +44,60 @@ static void subbuild_joinrel_joinlist(RelOptInfo *joinrel,
|
||||
/*
|
||||
* build_base_rel
|
||||
* Construct a new base relation RelOptInfo, and put it in the query's
|
||||
* base_rel_list.
|
||||
* base_rel_array.
|
||||
*/
|
||||
void
|
||||
build_base_rel(PlannerInfo *root, int relid)
|
||||
{
|
||||
ListCell *l;
|
||||
RelOptInfo *rel;
|
||||
Assert(relid > 0);
|
||||
|
||||
/* Rel should not exist already */
|
||||
foreach(l, root->base_rel_list)
|
||||
{
|
||||
rel = (RelOptInfo *) lfirst(l);
|
||||
if (relid == rel->relid)
|
||||
elog(ERROR, "rel already exists");
|
||||
}
|
||||
|
||||
/* It should not exist as an "other" rel, either */
|
||||
foreach(l, root->other_rel_list)
|
||||
{
|
||||
rel = (RelOptInfo *) lfirst(l);
|
||||
if (relid == rel->relid)
|
||||
elog(ERROR, "rel already exists as \"other\" rel");
|
||||
}
|
||||
if (relid < root->base_rel_array_size &&
|
||||
root->base_rel_array[relid] != NULL)
|
||||
elog(ERROR, "rel already exists");
|
||||
|
||||
/* No existing RelOptInfo for this base rel, so make a new one */
|
||||
rel = make_reloptinfo(root, relid, RELOPT_BASEREL);
|
||||
|
||||
/* and add it to the list */
|
||||
root->base_rel_list = lcons(rel, root->base_rel_list);
|
||||
(void) make_reloptinfo(root, relid, RELOPT_BASEREL);
|
||||
}
|
||||
|
||||
/*
|
||||
* build_other_rel
|
||||
* Returns relation entry corresponding to 'relid', creating a new one
|
||||
* if necessary. This is for 'other' relations, which are much like
|
||||
* base relations except that they live in a different list.
|
||||
* base relations except that they have a different RelOptKind.
|
||||
*/
|
||||
RelOptInfo *
|
||||
build_other_rel(PlannerInfo *root, int relid)
|
||||
{
|
||||
ListCell *l;
|
||||
RelOptInfo *rel;
|
||||
|
||||
/* Already made? */
|
||||
foreach(l, root->other_rel_list)
|
||||
{
|
||||
rel = (RelOptInfo *) lfirst(l);
|
||||
if (relid == rel->relid)
|
||||
return rel;
|
||||
}
|
||||
Assert(relid > 0);
|
||||
|
||||
/* It should not exist as a base rel */
|
||||
foreach(l, root->base_rel_list)
|
||||
/* Already made? */
|
||||
if (relid < root->base_rel_array_size)
|
||||
{
|
||||
rel = (RelOptInfo *) lfirst(l);
|
||||
if (relid == rel->relid)
|
||||
elog(ERROR, "rel already exists as base rel");
|
||||
rel = root->base_rel_array[relid];
|
||||
if (rel)
|
||||
{
|
||||
/* it should not exist as a base rel */
|
||||
if (rel->reloptkind == RELOPT_BASEREL)
|
||||
elog(ERROR, "rel already exists as base rel");
|
||||
/* otherwise, A-OK */
|
||||
return rel;
|
||||
}
|
||||
}
|
||||
|
||||
/* No existing RelOptInfo for this other rel, so make a new one */
|
||||
/* presently, must be an inheritance child rel */
|
||||
rel = make_reloptinfo(root, relid, RELOPT_OTHER_CHILD_REL);
|
||||
|
||||
/* and add it to the list */
|
||||
root->other_rel_list = lcons(rel, root->other_rel_list);
|
||||
|
||||
return rel;
|
||||
}
|
||||
|
||||
/*
|
||||
* make_reloptinfo
|
||||
* Construct a RelOptInfo for the specified rangetable index.
|
||||
* Construct a RelOptInfo for the specified rangetable index,
|
||||
* and enter it into base_rel_array.
|
||||
*
|
||||
* Common code for build_base_rel and build_other_rel.
|
||||
*/
|
||||
@ -172,31 +155,40 @@ make_reloptinfo(PlannerInfo *root, int relid, RelOptKind reloptkind)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Add the finished struct to the base_rel_array */
|
||||
if (relid >= root->base_rel_array_size)
|
||||
{
|
||||
int oldsize = root->base_rel_array_size;
|
||||
int newsize;
|
||||
|
||||
newsize = Max(oldsize * 2, relid + 1);
|
||||
root->base_rel_array = (RelOptInfo **)
|
||||
repalloc(root->base_rel_array, newsize * sizeof(RelOptInfo *));
|
||||
MemSet(root->base_rel_array + oldsize, 0,
|
||||
(newsize - oldsize) * sizeof(RelOptInfo *));
|
||||
root->base_rel_array_size = newsize;
|
||||
}
|
||||
|
||||
root->base_rel_array[relid] = rel;
|
||||
|
||||
return rel;
|
||||
}
|
||||
|
||||
/*
|
||||
* find_base_rel
|
||||
* Find a base or other relation entry, which must already exist
|
||||
* (since we'd have no idea which list to add it to).
|
||||
* Find a base or other relation entry, which must already exist.
|
||||
*/
|
||||
RelOptInfo *
|
||||
find_base_rel(PlannerInfo *root, int relid)
|
||||
{
|
||||
ListCell *l;
|
||||
RelOptInfo *rel;
|
||||
|
||||
foreach(l, root->base_rel_list)
|
||||
{
|
||||
rel = (RelOptInfo *) lfirst(l);
|
||||
if (relid == rel->relid)
|
||||
return rel;
|
||||
}
|
||||
Assert(relid > 0);
|
||||
|
||||
foreach(l, root->other_rel_list)
|
||||
if (relid < root->base_rel_array_size)
|
||||
{
|
||||
rel = (RelOptInfo *) lfirst(l);
|
||||
if (relid == rel->relid)
|
||||
rel = root->base_rel_array[relid];
|
||||
if (rel)
|
||||
return rel;
|
||||
}
|
||||
|
||||
@ -308,8 +300,13 @@ build_join_rel(PlannerInfo *root,
|
||||
* Create a new tlist containing just the vars that need to be output
|
||||
* from this join (ie, are needed for higher joinclauses or final
|
||||
* output).
|
||||
*
|
||||
* NOTE: the tlist order for a join rel will depend on which pair of
|
||||
* outer and inner rels we first try to build it from. But the
|
||||
* contents should be the same regardless.
|
||||
*/
|
||||
build_joinrel_tlist(root, joinrel);
|
||||
build_joinrel_tlist(root, joinrel, outer_rel);
|
||||
build_joinrel_tlist(root, joinrel, inner_rel);
|
||||
|
||||
/*
|
||||
* Construct restrict and join clause lists for the new joinrel. (The
|
||||
@ -344,48 +341,39 @@ build_join_rel(PlannerInfo *root,
|
||||
* Builds a join relation's target list.
|
||||
*
|
||||
* The join's targetlist includes all Vars of its member relations that
|
||||
* will still be needed above the join.
|
||||
*
|
||||
* In a former lifetime, this just merged the tlists of the two member
|
||||
* relations first presented. While we could still do that, working from
|
||||
* lists of Vars would mean doing a find_base_rel lookup for each Var.
|
||||
* It seems more efficient to scan the list of base rels and collect the
|
||||
* needed vars directly from there.
|
||||
* will still be needed above the join. This subroutine adds all such
|
||||
* Vars from the specified input rel's tlist to the join rel's tlist.
|
||||
*
|
||||
* We also compute the expected width of the join's output, making use
|
||||
* of data that was cached at the baserel level by set_rel_width().
|
||||
*/
|
||||
static void
|
||||
build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel)
|
||||
build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
|
||||
RelOptInfo *input_rel)
|
||||
{
|
||||
Relids relids = joinrel->relids;
|
||||
ListCell *rels;
|
||||
ListCell *vars;
|
||||
|
||||
joinrel->reltargetlist = NIL;
|
||||
joinrel->width = 0;
|
||||
|
||||
foreach(rels, root->base_rel_list)
|
||||
foreach(vars, input_rel->reltargetlist)
|
||||
{
|
||||
RelOptInfo *baserel = (RelOptInfo *) lfirst(rels);
|
||||
ListCell *vars;
|
||||
Var *var = (Var *) lfirst(vars);
|
||||
RelOptInfo *baserel;
|
||||
int ndx;
|
||||
|
||||
if (!bms_is_member(baserel->relid, relids))
|
||||
continue;
|
||||
/* We can't run into any child RowExprs here */
|
||||
Assert(IsA(var, Var));
|
||||
|
||||
foreach(vars, baserel->reltargetlist)
|
||||
/* Get the Var's original base rel */
|
||||
baserel = find_base_rel(root, var->varno);
|
||||
|
||||
/* Is it still needed above this joinrel? */
|
||||
ndx = var->varattno - baserel->min_attr;
|
||||
if (bms_nonempty_difference(baserel->attr_needed[ndx], relids))
|
||||
{
|
||||
Var *var = (Var *) lfirst(vars);
|
||||
int ndx = var->varattno - baserel->min_attr;
|
||||
|
||||
/* We can't run into any child RowExprs here */
|
||||
Assert(IsA(var, Var));
|
||||
|
||||
if (bms_nonempty_difference(baserel->attr_needed[ndx], relids))
|
||||
{
|
||||
joinrel->reltargetlist = lappend(joinrel->reltargetlist, var);
|
||||
Assert(baserel->attr_widths[ndx] > 0);
|
||||
joinrel->width += baserel->attr_widths[ndx];
|
||||
}
|
||||
/* Yup, add it to the output */
|
||||
joinrel->reltargetlist = lappend(joinrel->reltargetlist, var);
|
||||
Assert(baserel->attr_widths[ndx] > 0);
|
||||
joinrel->width += baserel->attr_widths[ndx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.110 2005/06/05 22:32:57 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.111 2005/06/06 04:13:36 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -24,7 +24,6 @@
|
||||
* Relids
|
||||
* Set of relation identifiers (indexes into the rangetable).
|
||||
*/
|
||||
|
||||
typedef Bitmapset *Relids;
|
||||
|
||||
/*
|
||||
@ -63,8 +62,16 @@ typedef struct PlannerInfo
|
||||
|
||||
Query *parse; /* the Query being planned */
|
||||
|
||||
List *base_rel_list; /* list of base-relation RelOptInfos */
|
||||
List *other_rel_list; /* list of other 1-relation RelOptInfos */
|
||||
/*
|
||||
* base_rel_array holds pointers to "base rels" and "other rels" (see
|
||||
* comments for RelOptInfo for more info). It is indexed by rangetable
|
||||
* index (so entry 0 is always wasted). Entries can be NULL when
|
||||
* an RTE does not correspond to a base relation. Note that the array
|
||||
* may be enlarged on-the-fly.
|
||||
*/
|
||||
struct RelOptInfo **base_rel_array; /* All one-relation RelOptInfos */
|
||||
int base_rel_array_size; /* current allocated array len */
|
||||
|
||||
List *join_rel_list; /* list of join-relation RelOptInfos */
|
||||
|
||||
List *equi_key_list; /* list of lists of equijoined
|
||||
@ -90,15 +97,15 @@ typedef struct PlannerInfo
|
||||
* is the joining of two or more base rels. A joinrel is identified by
|
||||
* the set of RT indexes for its component baserels. We create RelOptInfo
|
||||
* nodes for each baserel and joinrel, and store them in the PlannerInfo's
|
||||
* base_rel_list and join_rel_list respectively.
|
||||
* base_rel_array and join_rel_list respectively.
|
||||
*
|
||||
* Note that there is only one joinrel for any given set of component
|
||||
* baserels, no matter what order we assemble them in; so an unordered
|
||||
* set is the right datatype to identify it with.
|
||||
*
|
||||
* We also have "other rels", which are like base rels in that they refer to
|
||||
* single RT indexes; but they are not part of the join tree, and are stored
|
||||
* in other_rel_list not base_rel_list.
|
||||
* single RT indexes; but they are not part of the join tree, and are given
|
||||
* a different RelOptKind to identify them.
|
||||
*
|
||||
* Currently the only kind of otherrels are those made for child relations
|
||||
* of an inheritance scan (SELECT FROM foo*). The parent table's RTE and
|
||||
|
Loading…
Reference in New Issue
Block a user