*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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;
}
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)
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);
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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,
/*
* 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.
*/
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;
}
* 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
* 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];
}
}
}
* 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 $
*
*-------------------------------------------------------------------------
*/
* Relids
* Set of relation identifiers (indexes into the rangetable).
*/
-
typedef Bitmapset *Relids;
/*
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
* 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