aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/util/relnode.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/util/relnode.c')
-rw-r--r--src/backend/optimizer/util/relnode.c417
1 files changed, 358 insertions, 59 deletions
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 23ee8ba8111..d22daa0f638 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -1,109 +1,408 @@
/*-------------------------------------------------------------------------
*
* relnode.c
- * Relation manipulation routines
+ * Relation-node lookup/construction routines
*
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.22 2000/02/06 03:27:33 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.23 2000/02/07 04:41:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
+#include "optimizer/cost.h"
#include "optimizer/internal.h"
+#include "optimizer/joininfo.h"
#include "optimizer/pathnode.h"
#include "optimizer/plancat.h"
+#include "optimizer/tlist.h"
+static List *new_join_tlist(List *tlist, int first_resdomno);
+static List *build_joinrel_restrictlist(RelOptInfo *joinrel,
+ RelOptInfo *outer_rel,
+ RelOptInfo *inner_rel);
+static void build_joinrel_joinlist(RelOptInfo *joinrel,
+ RelOptInfo *outer_rel,
+ RelOptInfo *inner_rel);
+static List *subbuild_joinrel_restrictlist(RelOptInfo *joinrel,
+ List *joininfo_list);
+static void subbuild_joinrel_joinlist(RelOptInfo *joinrel,
+ List *joininfo_list);
+
/*
* get_base_rel
- * Returns relation entry corresponding to 'relid', creating a new one if
- * necessary. This is for base relations.
- *
+ * Returns relation entry corresponding to 'relid', creating a new one
+ * if necessary. This is for base relations.
*/
RelOptInfo *
get_base_rel(Query *root, int relid)
{
- Relids relids = lconsi(relid, NIL);
+ List *baserels;
RelOptInfo *rel;
- rel = rel_member(relids, root->base_rel_list);
- if (rel == NULL)
+ foreach(baserels, root->base_rel_list)
{
- rel = makeNode(RelOptInfo);
- rel->relids = relids;
- rel->rows = 0;
- rel->width = 0;
- rel->targetlist = NIL;
- rel->pathlist = NIL;
- rel->cheapestpath = (Path *) NULL;
- rel->pruneable = true;
- rel->indexed = false;
- rel->pages = 0;
- rel->tuples = 0;
- rel->restrictinfo = NIL;
- rel->joininfo = NIL;
- rel->innerjoin = NIL;
-
- root->base_rel_list = lcons(rel, root->base_rel_list);
-
- if (relid < 0)
- {
- /*
- * If the relation is a materialized relation, assume
- * constants for sizes.
- */
- rel->pages = _NONAME_RELATION_PAGES_;
- rel->tuples = _NONAME_RELATION_TUPLES_;
- }
- else
- {
- /*
- * Otherwise, retrieve relation statistics from the
- * system catalogs.
- */
- relation_info(root, relid,
- &rel->indexed, &rel->pages, &rel->tuples);
- }
+ rel = (RelOptInfo *) lfirst(baserels);
+
+ /* We know length(rel->relids) == 1 for all members of base_rel_list */
+ if (lfirsti(rel->relids) == relid)
+ return rel;
}
+
+ /* No existing RelOptInfo for this base rel, so make a new one */
+ rel = makeNode(RelOptInfo);
+ rel->relids = lconsi(relid, NIL);
+ rel->rows = 0;
+ rel->width = 0;
+ rel->targetlist = NIL;
+ rel->pathlist = NIL;
+ rel->cheapestpath = (Path *) NULL;
+ rel->pruneable = true;
+ rel->indexed = false;
+ rel->pages = 0;
+ rel->tuples = 0;
+ rel->baserestrictinfo = NIL;
+ rel->joininfo = NIL;
+ rel->innerjoin = NIL;
+
+ if (relid < 0)
+ {
+ /*
+ * If the relation is a materialized relation, assume
+ * constants for sizes.
+ */
+ rel->pages = _NONAME_RELATION_PAGES_;
+ rel->tuples = _NONAME_RELATION_TUPLES_;
+ }
+ else
+ {
+ /*
+ * Otherwise, retrieve relation statistics from the
+ * system catalogs.
+ */
+ relation_info(root, relid,
+ &rel->indexed, &rel->pages, &rel->tuples);
+ }
+
+ root->base_rel_list = lcons(rel, root->base_rel_list);
+
return rel;
}
/*
+ * find_join_rel
+ * Returns relation entry corresponding to 'relids' (a list of RT indexes),
+ * or NULL if none exists. This is for join relations.
+ *
+ * Note: there is probably no good reason for this to be called from
+ * anywhere except get_join_rel, but keep it as a separate routine
+ * just in case.
+ */
+static RelOptInfo *
+find_join_rel(Query *root, Relids relids)
+{
+ List *joinrels;
+
+ foreach(joinrels, root->join_rel_list)
+ {
+ RelOptInfo *rel = (RelOptInfo *) lfirst(joinrels);
+
+ if (sameseti(rel->relids, relids))
+ return rel;
+ }
+
+ return NULL;
+}
+
+/*
* get_join_rel
- * Returns relation entry corresponding to 'relid' (a list of relids),
- * or NULL.
+ * Returns relation entry corresponding to the union of two given rels,
+ * creating a new relation entry if none already exists.
+ *
+ * 'outer_rel' and 'inner_rel' are relation nodes for the relations to be
+ * joined
+ * 'restrictlist_ptr': result variable. If not NULL, *restrictlist_ptr
+ * receives the list of RestrictInfo nodes that apply to this
+ * particular pair of joinable relations.
+ *
+ * restrictlist_ptr makes the routine's API a little grotty, but it saves
+ * duplicated calculation of the restrictlist...
*/
RelOptInfo *
-get_join_rel(Query *root, Relids relid)
+get_join_rel(Query *root,
+ RelOptInfo *outer_rel,
+ RelOptInfo *inner_rel,
+ List **restrictlist_ptr)
{
- return rel_member(relid, root->join_rel_list);
+ List *joinrelids;
+ RelOptInfo *joinrel;
+ List *restrictlist;
+ List *new_outer_tlist;
+ List *new_inner_tlist;
+
+ /* We should never try to join two overlapping sets of rels. */
+ Assert(nonoverlap_setsi(outer_rel->relids, inner_rel->relids));
+
+ /*
+ * See if we already have a joinrel for this set of base rels.
+ *
+ * nconc(listCopy(x), y) is an idiom for making a new list without
+ * changing either input list.
+ */
+ joinrelids = nconc(listCopy(outer_rel->relids), inner_rel->relids);
+ joinrel = find_join_rel(root, joinrelids);
+
+ if (joinrel)
+ {
+ /*
+ * Yes, so we only need to figure the restrictlist for this
+ * particular pair of component relations.
+ */
+ if (restrictlist_ptr)
+ *restrictlist_ptr = build_joinrel_restrictlist(joinrel,
+ outer_rel,
+ inner_rel);
+ return joinrel;
+ }
+
+ /*
+ * Nope, so make one.
+ */
+ joinrel = makeNode(RelOptInfo);
+ joinrel->relids = joinrelids;
+ joinrel->rows = 0;
+ joinrel->width = 0;
+ joinrel->targetlist = NIL;
+ joinrel->pathlist = NIL;
+ joinrel->cheapestpath = (Path *) NULL;
+ joinrel->pruneable = true;
+ joinrel->indexed = false;
+ joinrel->pages = 0;
+ joinrel->tuples = 0;
+ joinrel->baserestrictinfo = NIL;
+ joinrel->joininfo = NIL;
+ joinrel->innerjoin = NIL;
+
+ /*
+ * Create a new tlist by removing irrelevant elements from both tlists
+ * of the outer and inner join relations and then merging the results
+ * together.
+ *
+ * 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.
+ *
+ * XXX someday: consider pruning vars from the join's targetlist
+ * if they are needed only to evaluate restriction clauses of this
+ * join, and will never be accessed at higher levels of the plantree.
+ */
+ new_outer_tlist = new_join_tlist(outer_rel->targetlist, 1);
+ new_inner_tlist = new_join_tlist(inner_rel->targetlist,
+ length(new_outer_tlist) + 1);
+ joinrel->targetlist = nconc(new_outer_tlist, new_inner_tlist);
+
+ /*
+ * Construct restrict and join clause lists for the new joinrel.
+ * (The caller might or might not need the restrictlist, but
+ * I need it anyway for set_joinrel_size_estimates().)
+ */
+ restrictlist = build_joinrel_restrictlist(joinrel, outer_rel, inner_rel);
+ if (restrictlist_ptr)
+ *restrictlist_ptr = restrictlist;
+ build_joinrel_joinlist(joinrel, outer_rel, inner_rel);
+
+ /*
+ * Set estimates of the joinrel's size.
+ */
+ set_joinrel_size_estimates(root, joinrel, outer_rel, inner_rel,
+ restrictlist);
+
+ /*
+ * Add the joinrel to the front of the query's joinrel list.
+ * (allpaths.c depends on this ordering!)
+ */
+ root->join_rel_list = lcons(joinrel, root->join_rel_list);
+
+ return joinrel;
}
/*
- * rel_member
- * Determines whether a relation of id 'relid' is contained within a list
- * 'rels'.
+ * new_join_tlist
+ * Builds a join relation's target list by keeping those elements that
+ * will be in the final target list and any other elements that are still
+ * needed for future joins. For a target list entry to still be needed
+ * for future joins, its 'joinlist' field must not be empty after removal
+ * of all relids in 'other_relids'.
*
- * Returns the corresponding entry in 'rels' if it is there.
+ * XXX the above comment refers to code that is long dead and gone;
+ * we don't keep track of joinlists for individual targetlist entries
+ * anymore. For now, all vars present in either input tlist will be
+ * emitted in the join's tlist.
*
+ * 'tlist' is the target list of one of the join relations
+ * 'first_resdomno' is the resdom number to use for the first created
+ * target list entry
+ *
+ * Returns the new target list.
*/
-RelOptInfo *
-rel_member(Relids relids, List *rels)
+static List *
+new_join_tlist(List *tlist,
+ int first_resdomno)
{
- List *temp;
+ int resdomno = first_resdomno - 1;
+ List *t_list = NIL;
+ List *i;
- foreach(temp, rels)
+ foreach(i, tlist)
{
- RelOptInfo *rel = (RelOptInfo *) lfirst(temp);
+ TargetEntry *xtl = lfirst(i);
- if (sameseti(rel->relids, relids))
- return rel;
+ resdomno += 1;
+ t_list = lappend(t_list,
+ create_tl_element(get_expr(xtl), resdomno));
+ }
+
+ return t_list;
+}
+
+/*
+ * build_joinrel_restrictlist
+ * build_joinrel_joinlist
+ * These routines build lists of restriction and join clauses for a
+ * join relation from the joininfo lists of the relations it joins.
+ *
+ * These routines are separate because the restriction list must be
+ * built afresh for each pair of input sub-relations we consider, whereas
+ * the join lists need only be computed once for any join RelOptInfo.
+ * The join lists are fully determined by the set of rels making up the
+ * joinrel, so we should get the same results (up to ordering) from any
+ * candidate pair of sub-relations. But the restriction list is whatever
+ * is not handled in the sub-relations, so it depends on which
+ * sub-relations are considered.
+ *
+ * If a join clause from an input relation refers to base rels still not
+ * present in the joinrel, then it is still a join clause for the joinrel;
+ * we put it into an appropriate JoinInfo list for the joinrel. Otherwise,
+ * the clause is now a restrict clause for the joined relation, and we
+ * return it to the caller of build_joinrel_restrictlist() to be stored in
+ * join paths made from this pair of sub-relations. (It will not need to
+ * be considered further up the join tree.)
+ *
+ * 'joinrel' is a join relation node
+ * 'outer_rel' and 'inner_rel' are a pair of relations that can be joined
+ * to form joinrel.
+ *
+ * build_joinrel_restrictlist() returns a list of relevant restrictinfos,
+ * whereas build_joinrel_joinlist() stores its results in the joinrel's
+ * joininfo lists. One or the other must accept each given clause!
+ *
+ * NB: Formerly, we made deep(!) copies of each input RestrictInfo to pass
+ * up to the join relation. I believe this is no longer necessary, because
+ * RestrictInfo nodes are no longer context-dependent. Instead, just include
+ * the original nodes in the lists made for the join relation.
+ */
+static List *
+build_joinrel_restrictlist(RelOptInfo *joinrel,
+ RelOptInfo *outer_rel,
+ RelOptInfo *inner_rel)
+{
+ /*
+ * We must eliminate duplicates, since we will see the
+ * same clauses arriving from both input relations...
+ */
+ return LispUnion(subbuild_joinrel_restrictlist(joinrel,
+ outer_rel->joininfo),
+ subbuild_joinrel_restrictlist(joinrel,
+ inner_rel->joininfo));
+}
+
+static void
+build_joinrel_joinlist(RelOptInfo *joinrel,
+ RelOptInfo *outer_rel,
+ RelOptInfo *inner_rel)
+{
+ subbuild_joinrel_joinlist(joinrel, outer_rel->joininfo);
+ subbuild_joinrel_joinlist(joinrel, inner_rel->joininfo);
+}
+
+static List *
+subbuild_joinrel_restrictlist(RelOptInfo *joinrel,
+ List *joininfo_list)
+{
+ List *restrictlist = NIL;
+ List *xjoininfo;
+
+ foreach(xjoininfo, joininfo_list)
+ {
+ JoinInfo *joininfo = (JoinInfo *) lfirst(xjoininfo);
+ Relids new_unjoined_relids;
+
+ new_unjoined_relids = set_differencei(joininfo->unjoined_relids,
+ joinrel->relids);
+ if (new_unjoined_relids == NIL)
+ {
+ /*
+ * Clauses in this JoinInfo list become restriction clauses
+ * for the joinrel, since they refer to no outside rels.
+ *
+ * We must copy the list to avoid disturbing the input relation,
+ * but we can use a shallow copy.
+ */
+ restrictlist = nconc(restrictlist,
+ listCopy(joininfo->jinfo_restrictinfo));
+ }
+ else
+ {
+ /*
+ * These clauses are still join clauses at this level,
+ * so we ignore them in this routine.
+ */
+ }
+ }
+
+ return restrictlist;
+}
+
+static void
+subbuild_joinrel_joinlist(RelOptInfo *joinrel,
+ List *joininfo_list)
+{
+ List *xjoininfo;
+
+ foreach(xjoininfo, joininfo_list)
+ {
+ JoinInfo *joininfo = (JoinInfo *) lfirst(xjoininfo);
+ Relids new_unjoined_relids;
+
+ new_unjoined_relids = set_differencei(joininfo->unjoined_relids,
+ joinrel->relids);
+ if (new_unjoined_relids == NIL)
+ {
+ /*
+ * Clauses in this JoinInfo list become restriction clauses
+ * for the joinrel, since they refer to no outside rels.
+ * So we can ignore them in this routine.
+ */
+ }
+ else
+ {
+ /*
+ * These clauses are still join clauses at this level,
+ * so find or make the appropriate JoinInfo item for the joinrel,
+ * and add the clauses to it (eliminating duplicates).
+ */
+ JoinInfo *new_joininfo;
+
+ new_joininfo = find_joininfo_node(joinrel, new_unjoined_relids);
+ new_joininfo->jinfo_restrictinfo =
+ LispUnion(new_joininfo->jinfo_restrictinfo,
+ joininfo->jinfo_restrictinfo);
+ }
}
- return NULL;
}