]> granicus.if.org Git - postgresql/commitdiff
Nab some low-hanging fruit: replace the planner's base_rel_list and
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 6 Jun 2005 04:13:36 +0000 (04:13 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 6 Jun 2005 04:13:36 +0000 (04:13 +0000)
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.

src/backend/nodes/outfuncs.c
src/backend/optimizer/path/allpaths.c
src/backend/optimizer/plan/planmain.c
src/backend/optimizer/util/relnode.c
src/include/nodes/relation.h

index 51a85a90b3c11475a588aa01ac29aaac05283ac5..8310894e0c2ce27fa7639465dec1ff7200a2f484 100644 (file)
@@ -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);
index e41701d044bff3216a4e06525886dd137a7c25fa..f17d1af5a63165837cefcdc9fb6fd596c9334027 100644 (file)
@@ -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);
 
index 79a28cae3a95e32ad63d2c36393364bbe144e595..1c87b24e4c6c07982823d8bf4269d282100bd50a 100644 (file)
@@ -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;
 
index 5a39bf3f05138a5bfbcb17e1960cd1228b9b5e7e..996f7691870a6e337b9ff8e8438743c504233d2f 100644 (file)
@@ -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];
                }
        }
 }
index 4cbf285029c8f284e1d9805dbf4c69bbcb7d8368..93dc78aece4fd4bc0d95ff03f7e114a793631a9c 100644 (file)
@@ -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