From: Tom Lane Date: Mon, 6 Jun 2005 04:13:36 +0000 (+0000) Subject: Nab some low-hanging fruit: replace the planner's base_rel_list and X-Git-Tag: REL8_1_0BETA1~634 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=9a586fe0c5a9228cc60428207a3fb64fb392b848;p=postgresql 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. --- diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 51a85a90b3..8310894e0c 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -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); diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index e41701d044..f17d1af5a6 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -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); diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c index 79a28cae3a..1c87b24e4c 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -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; diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 5a39bf3f05..996f769187 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -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; + Assert(relid > 0); + /* Already made? */ - 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) + { + /* 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; - } - - /* It should not exist as a base rel */ - foreach(l, root->base_rel_list) - { - rel = (RelOptInfo *) lfirst(l); - if (relid == rel->relid) - elog(ERROR, "rel already exists as base 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; + + /* We can't run into any child RowExprs here */ + Assert(IsA(var, Var)); - if (!bms_is_member(baserel->relid, relids)) - continue; + /* Get the Var's original base rel */ + baserel = find_base_rel(root, var->varno); - foreach(vars, baserel->reltargetlist) + /* 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]; } } } diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 4cbf285029..93dc78aece 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -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