aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r--src/backend/optimizer/path/costsize.c8
-rw-r--r--src/backend/optimizer/path/joinpath.c23
-rw-r--r--src/backend/optimizer/path/joinrels.c4
-rw-r--r--src/backend/optimizer/path/pathkeys.c8
-rw-r--r--src/backend/optimizer/plan/createplan.c98
-rw-r--r--src/backend/optimizer/plan/initsplan.c214
-rw-r--r--src/backend/optimizer/plan/planmain.c38
-rw-r--r--src/backend/optimizer/plan/planner.c30
-rw-r--r--src/backend/optimizer/plan/setrefs.c74
-rw-r--r--src/backend/optimizer/prep/prepunion.c20
-rw-r--r--src/backend/optimizer/util/clauses.c11
-rw-r--r--src/backend/optimizer/util/relnode.c150
-rw-r--r--src/backend/optimizer/util/var.c375
13 files changed, 832 insertions, 221 deletions
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index e215a6cd366..2bc0bb8a137 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -42,7 +42,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.82 2002/03/01 20:50:20 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.83 2002/03/12 00:51:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -597,7 +597,7 @@ cost_mergejoin(Path *path, Query *root,
leftvar = get_leftop(firstclause->clause);
Assert(IsA(leftvar, Var));
- if (intMember(leftvar->varno, outer_path->parent->relids))
+ if (VARISRELMEMBER(leftvar->varno, outer_path->parent))
{
/* left side of clause is outer */
outerscansel = firstclause->left_mergescansel;
@@ -748,7 +748,7 @@ cost_hashjoin(Path *path, Query *root,
* a large query, we cache the bucketsize estimate in the RestrictInfo
* node to avoid repeated lookups of statistics.
*/
- if (intMember(right->varno, inner_path->parent->relids))
+ if (VARISRELMEMBER(right->varno, inner_path->parent))
{
/* righthand side is inner */
innerbucketsize = restrictinfo->right_bucketsize;
@@ -761,7 +761,7 @@ cost_hashjoin(Path *path, Query *root,
}
else
{
- Assert(intMember(left->varno, inner_path->parent->relids));
+ Assert(VARISRELMEMBER(left->varno, inner_path->parent));
/* lefthand side is inner */
innerbucketsize = restrictinfo->left_bucketsize;
if (innerbucketsize < 0)
diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c
index 4d60569e7ef..11bc8a9f7d3 100644
--- a/src/backend/optimizer/path/joinpath.c
+++ b/src/backend/optimizer/path/joinpath.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.67 2001/11/11 19:18:54 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.68 2002/03/12 00:51:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -24,6 +24,7 @@
#include "parser/parsetree.h"
#include "utils/lsyscache.h"
+
static void sort_inner_and_outer(Query *root, RelOptInfo *joinrel,
RelOptInfo *outerrel, RelOptInfo *innerrel,
List *restrictlist, List *mergeclause_list,
@@ -720,8 +721,6 @@ hash_inner_and_outer(Query *root,
List *restrictlist,
JoinType jointype)
{
- Relids outerrelids = outerrel->relids;
- Relids innerrelids = innerrel->relids;
bool isouterjoin;
List *i;
@@ -773,13 +772,13 @@ hash_inner_and_outer(Query *root,
/*
* Check if clause is usable with these input rels.
*/
- if (intMember(left->varno, outerrelids) &&
- intMember(right->varno, innerrelids))
+ if (VARISRELMEMBER(left->varno, outerrel) &&
+ VARISRELMEMBER(right->varno, innerrel))
{
/* righthand side is inner */
}
- else if (intMember(left->varno, innerrelids) &&
- intMember(right->varno, outerrelids))
+ else if (VARISRELMEMBER(left->varno, innerrel) &&
+ VARISRELMEMBER(right->varno, outerrel))
{
/* lefthand side is inner */
}
@@ -901,8 +900,6 @@ select_mergejoin_clauses(RelOptInfo *joinrel,
JoinType jointype)
{
List *result_list = NIL;
- Relids outerrelids = outerrel->relids;
- Relids innerrelids = innerrel->relids;
bool isouterjoin = IS_OUTER_JOIN(jointype);
List *i;
@@ -952,10 +949,10 @@ select_mergejoin_clauses(RelOptInfo *joinrel,
left = get_leftop(clause);
right = get_rightop(clause);
- if ((intMember(left->varno, outerrelids) &&
- intMember(right->varno, innerrelids)) ||
- (intMember(left->varno, innerrelids) &&
- intMember(right->varno, outerrelids)))
+ if ((VARISRELMEMBER(left->varno, outerrel) &&
+ VARISRELMEMBER(right->varno, innerrel)) ||
+ (VARISRELMEMBER(left->varno, innerrel) &&
+ VARISRELMEMBER(right->varno, outerrel)))
result_list = lcons(restrictinfo, result_list);
}
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index 745a1eb0b5b..d2d511e75e7 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.55 2001/10/25 05:49:32 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.56 2002/03/12 00:51:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -340,7 +340,7 @@ make_jointree_rel(Query *root, Node *jtnode)
{
int varno = ((RangeTblRef *) jtnode)->rtindex;
- return build_base_rel(root, varno);
+ return find_base_rel(root, varno);
}
else if (IsA(jtnode, FromExpr))
{
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index f91e25cdb4f..60e05ca340f 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -11,7 +11,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.36 2001/11/11 20:33:53 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.37 2002/03/12 00:51:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -854,7 +854,8 @@ make_pathkeys_for_mergeclauses(Query *root,
cache_mergeclause_pathkeys(root, restrictinfo);
key = (Node *) get_leftop(restrictinfo->clause);
- if (IsA(key, Var) &&intMember(((Var *) key)->varno, rel->relids))
+ if (IsA(key, Var) &&
+ VARISRELMEMBER(((Var *) key)->varno, rel))
{
/* Rel is left side of mergeclause */
pathkey = restrictinfo->left_pathkey;
@@ -862,7 +863,8 @@ make_pathkeys_for_mergeclauses(Query *root,
else
{
key = (Node *) get_rightop(restrictinfo->clause);
- if (IsA(key, Var) &&intMember(((Var *) key)->varno, rel->relids))
+ if (IsA(key, Var) &&
+ VARISRELMEMBER(((Var *) key)->varno, rel))
{
/* Rel is right side of mergeclause */
pathkey = restrictinfo->right_pathkey;
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 88c5499b302..79d90bf5a3c 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.111 2001/10/28 06:25:44 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.112 2002/03/12 00:51:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -43,7 +43,8 @@ static TidScan *create_tidscan_plan(TidPath *best_path, List *tlist,
List *scan_clauses);
static SubqueryScan *create_subqueryscan_plan(Path *best_path,
List *tlist, List *scan_clauses);
-static NestLoop *create_nestloop_plan(NestPath *best_path, List *tlist,
+static NestLoop *create_nestloop_plan(Query *root,
+ NestPath *best_path, List *tlist,
List *joinclauses, List *otherclauses,
Plan *outer_plan, List *outer_tlist,
Plan *inner_plan, List *inner_tlist);
@@ -52,7 +53,8 @@ static MergeJoin *create_mergejoin_plan(Query *root,
List *joinclauses, List *otherclauses,
Plan *outer_plan, List *outer_tlist,
Plan *inner_plan, List *inner_tlist);
-static HashJoin *create_hashjoin_plan(HashPath *best_path, List *tlist,
+static HashJoin *create_hashjoin_plan(Query *root,
+ HashPath *best_path, List *tlist,
List *joinclauses, List *otherclauses,
Plan *outer_plan, List *outer_tlist,
Plan *inner_plan, List *inner_tlist);
@@ -78,18 +80,18 @@ static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid,
static NestLoop *make_nestloop(List *tlist,
List *joinclauses, List *otherclauses,
Plan *lefttree, Plan *righttree,
- JoinType jointype);
+ JoinType jointype, Index joinrti);
static HashJoin *make_hashjoin(List *tlist,
List *joinclauses, List *otherclauses,
List *hashclauses,
Plan *lefttree, Plan *righttree,
- JoinType jointype);
+ JoinType jointype, Index joinrti);
static Hash *make_hash(List *tlist, Node *hashkey, Plan *lefttree);
static MergeJoin *make_mergejoin(List *tlist,
List *joinclauses, List *otherclauses,
List *mergeclauses,
Plan *lefttree, Plan *righttree,
- JoinType jointype);
+ JoinType jointype, Index joinrti);
/*
* create_plan
@@ -259,7 +261,8 @@ create_join_plan(Query *root, JoinPath *best_path)
inner_tlist);
break;
case T_HashJoin:
- plan = (Join *) create_hashjoin_plan((HashPath *) best_path,
+ plan = (Join *) create_hashjoin_plan(root,
+ (HashPath *) best_path,
join_tlist,
joinclauses,
otherclauses,
@@ -269,7 +272,8 @@ create_join_plan(Query *root, JoinPath *best_path)
inner_tlist);
break;
case T_NestLoop:
- plan = (Join *) create_nestloop_plan((NestPath *) best_path,
+ plan = (Join *) create_nestloop_plan(root,
+ (NestPath *) best_path,
join_tlist,
joinclauses,
otherclauses,
@@ -576,7 +580,8 @@ create_subqueryscan_plan(Path *best_path, List *tlist, List *scan_clauses)
*****************************************************************************/
static NestLoop *
-create_nestloop_plan(NestPath *best_path,
+create_nestloop_plan(Query *root,
+ NestPath *best_path,
List *tlist,
List *joinclauses,
List *otherclauses,
@@ -586,6 +591,7 @@ create_nestloop_plan(NestPath *best_path,
List *inner_tlist)
{
NestLoop *join_plan;
+ Index joinrti = best_path->path.parent->joinrti;
if (IsA(inner_plan, IndexScan))
{
@@ -630,19 +636,25 @@ create_nestloop_plan(NestPath *best_path,
/* only refs to outer vars get changed in the inner indexqual */
innerscan->indxqualorig = join_references(indxqualorig,
+ root,
outer_tlist,
NIL,
- innerrel);
+ innerrel,
+ joinrti);
innerscan->indxqual = join_references(innerscan->indxqual,
+ root,
outer_tlist,
NIL,
- innerrel);
+ innerrel,
+ joinrti);
/* fix the inner qpqual too, if it has join clauses */
if (NumRelids((Node *) inner_plan->qual) > 1)
inner_plan->qual = join_references(inner_plan->qual,
+ root,
outer_tlist,
NIL,
- innerrel);
+ innerrel,
+ joinrti);
}
}
else if (IsA(inner_plan, TidScan))
@@ -650,9 +662,11 @@ create_nestloop_plan(NestPath *best_path,
TidScan *innerscan = (TidScan *) inner_plan;
innerscan->tideval = join_references(innerscan->tideval,
+ root,
outer_tlist,
inner_tlist,
- innerscan->scan.scanrelid);
+ innerscan->scan.scanrelid,
+ joinrti);
}
else if (IsA_Join(inner_plan))
{
@@ -671,20 +685,25 @@ create_nestloop_plan(NestPath *best_path,
* Set quals to contain INNER/OUTER var references.
*/
joinclauses = join_references(joinclauses,
+ root,
outer_tlist,
inner_tlist,
- (Index) 0);
+ (Index) 0,
+ joinrti);
otherclauses = join_references(otherclauses,
+ root,
outer_tlist,
inner_tlist,
- (Index) 0);
+ (Index) 0,
+ joinrti);
join_plan = make_nestloop(tlist,
joinclauses,
otherclauses,
outer_plan,
inner_plan,
- best_path->jointype);
+ best_path->jointype,
+ joinrti);
copy_path_costsize(&join_plan->join.plan, &best_path->path);
@@ -704,6 +723,7 @@ create_mergejoin_plan(Query *root,
{
List *mergeclauses;
MergeJoin *join_plan;
+ Index joinrti = best_path->jpath.path.parent->joinrti;
mergeclauses = get_actual_clauses(best_path->path_mergeclauses);
@@ -713,26 +733,32 @@ create_mergejoin_plan(Query *root,
* clauses to contain INNER/OUTER var references.
*/
joinclauses = join_references(set_difference(joinclauses, mergeclauses),
+ root,
outer_tlist,
inner_tlist,
- (Index) 0);
+ (Index) 0,
+ joinrti);
/*
* Fix the additional qpquals too.
*/
otherclauses = join_references(otherclauses,
+ root,
outer_tlist,
inner_tlist,
- (Index) 0);
+ (Index) 0,
+ joinrti);
/*
* Now set the references in the mergeclauses and rearrange them so
* that the outer variable is always on the left.
*/
mergeclauses = switch_outer(join_references(mergeclauses,
+ root,
outer_tlist,
inner_tlist,
- (Index) 0));
+ (Index) 0,
+ joinrti));
/*
* Create explicit sort nodes for the outer and inner join paths if
@@ -798,7 +824,8 @@ create_mergejoin_plan(Query *root,
mergeclauses,
outer_plan,
inner_plan,
- best_path->jpath.jointype);
+ best_path->jpath.jointype,
+ joinrti);
copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path);
@@ -806,7 +833,8 @@ create_mergejoin_plan(Query *root,
}
static HashJoin *
-create_hashjoin_plan(HashPath *best_path,
+create_hashjoin_plan(Query *root,
+ HashPath *best_path,
List *tlist,
List *joinclauses,
List *otherclauses,
@@ -819,6 +847,7 @@ create_hashjoin_plan(HashPath *best_path,
HashJoin *join_plan;
Hash *hash_plan;
Node *innerhashkey;
+ Index joinrti = best_path->jpath.path.parent->joinrti;
/*
* NOTE: there will always be exactly one hashclause in the list
@@ -834,26 +863,32 @@ create_hashjoin_plan(HashPath *best_path,
* clauses to contain INNER/OUTER var references.
*/
joinclauses = join_references(set_difference(joinclauses, hashclauses),
+ root,
outer_tlist,
inner_tlist,
- (Index) 0);
+ (Index) 0,
+ joinrti);
/*
* Fix the additional qpquals too.
*/
otherclauses = join_references(otherclauses,
+ root,
outer_tlist,
inner_tlist,
- (Index) 0);
+ (Index) 0,
+ joinrti);
/*
* Now set the references in the hashclauses and rearrange them so
* that the outer variable is always on the left.
*/
hashclauses = switch_outer(join_references(hashclauses,
+ root,
outer_tlist,
inner_tlist,
- (Index) 0));
+ (Index) 0,
+ joinrti));
/* Now the righthand op of the sole hashclause is the inner hash key. */
innerhashkey = (Node *) get_rightop(lfirst(hashclauses));
@@ -868,7 +903,8 @@ create_hashjoin_plan(HashPath *best_path,
hashclauses,
outer_plan,
(Plan *) hash_plan,
- best_path->jpath.jointype);
+ best_path->jpath.jointype,
+ joinrti);
copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path);
@@ -1327,7 +1363,8 @@ make_nestloop(List *tlist,
List *otherclauses,
Plan *lefttree,
Plan *righttree,
- JoinType jointype)
+ JoinType jointype,
+ Index joinrti)
{
NestLoop *node = makeNode(NestLoop);
Plan *plan = &node->join.plan;
@@ -1340,6 +1377,7 @@ make_nestloop(List *tlist,
plan->righttree = righttree;
node->join.jointype = jointype;
node->join.joinqual = joinclauses;
+ node->join.joinrti = joinrti;
return node;
}
@@ -1351,7 +1389,8 @@ make_hashjoin(List *tlist,
List *hashclauses,
Plan *lefttree,
Plan *righttree,
- JoinType jointype)
+ JoinType jointype,
+ Index joinrti)
{
HashJoin *node = makeNode(HashJoin);
Plan *plan = &node->join.plan;
@@ -1365,6 +1404,7 @@ make_hashjoin(List *tlist,
node->hashclauses = hashclauses;
node->join.jointype = jointype;
node->join.joinqual = joinclauses;
+ node->join.joinrti = joinrti;
return node;
}
@@ -1399,7 +1439,8 @@ make_mergejoin(List *tlist,
List *mergeclauses,
Plan *lefttree,
Plan *righttree,
- JoinType jointype)
+ JoinType jointype,
+ Index joinrti)
{
MergeJoin *node = makeNode(MergeJoin);
Plan *plan = &node->join.plan;
@@ -1413,6 +1454,7 @@ make_mergejoin(List *tlist,
node->mergeclauses = mergeclauses;
node->join.jointype = jointype;
node->join.joinqual = joinclauses;
+ node->join.joinrti = joinrti;
return node;
}
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 76677b4059c..2c9acc73b7f 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.66 2002/03/01 06:01:19 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.67 2002/03/12 00:51:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -53,67 +53,29 @@ static void check_hashjoinable(RestrictInfo *restrictinfo);
/*****************************************************************************
*
- * TARGET LISTS
+ * JOIN TREES
*
*****************************************************************************/
/*
- * build_base_rel_tlists
- * Creates rel nodes for every relation mentioned in the target list
- * 'tlist' (if a node hasn't already been created) and adds them to
- * root->base_rel_list. Creates targetlist entries for each var seen
- * in 'tlist' and adds them to the tlist of the appropriate rel node.
- */
-void
-build_base_rel_tlists(Query *root, List *tlist)
-{
- List *tlist_vars = pull_var_clause((Node *) tlist, false);
-
- add_vars_to_targetlist(root, tlist_vars);
- freeList(tlist_vars);
-}
-
-/*
- * add_vars_to_targetlist
- * For each variable appearing in the list, add it to the relation's
- * targetlist if not already present. Corresponding base rel nodes
- * will be created if not already present.
- */
-static void
-add_vars_to_targetlist(Query *root, List *vars)
-{
- List *temp;
-
- foreach(temp, vars)
- {
- Var *var = (Var *) lfirst(temp);
- RelOptInfo *rel = build_base_rel(root, var->varno);
-
- add_var_to_tlist(rel, var);
- }
-}
-
-/*----------
- * add_missing_rels_to_query
+ * add_base_rels_to_query
+ *
+ * Scan the query's jointree and create baserel RelOptInfos for all
+ * the base relations (ie, table and subquery RTEs) appearing in the
+ * jointree. Also, create otherrel RelOptInfos for join RTEs.
*
- * If we have a relation listed in the join tree that does not appear
- * in the target list nor qualifications, we must add it to the base
- * relation list so that it can be processed. For instance,
- * select count(*) from foo;
- * would fail to scan foo if this routine were not called. More subtly,
- * select f.x from foo f, foo f2
- * is a join of f and f2. Note that if we have
- * select foo.x from foo f
- * this also gets turned into a join (between foo as foo and foo as f).
+ * The return value is a list of all the baserel indexes (but not join RTE
+ * indexes) included in the scanned jointree. This is actually just an
+ * internal convenience for marking join otherrels properly; no outside
+ * caller uses the result.
*
- * Returns a list of all the base relations (RelOptInfo nodes) that appear
- * in the join tree. This list can be used for cross-checking in the
- * reverse direction, ie, that we have a join tree entry for every
- * relation used in the query.
- *----------
+ * At the end of this process, there should be one baserel RelOptInfo for
+ * every non-join RTE that is used in the query. Therefore, this routine
+ * is the only place that should call build_base_rel. But build_other_rel
+ * will be used again later to build rels for inheritance children.
*/
List *
-add_missing_rels_to_query(Query *root, Node *jtnode)
+add_base_rels_to_query(Query *root, Node *jtnode)
{
List *result = NIL;
@@ -123,10 +85,8 @@ add_missing_rels_to_query(Query *root, Node *jtnode)
{
int varno = ((RangeTblRef *) jtnode)->rtindex;
- /* This call to build_base_rel does the primary work... */
- RelOptInfo *rel = build_base_rel(root, varno);
-
- result = makeList1(rel);
+ build_base_rel(root, varno);
+ result = makeListi1(varno);
}
else if (IsA(jtnode, FromExpr))
{
@@ -136,19 +96,33 @@ add_missing_rels_to_query(Query *root, Node *jtnode)
foreach(l, f->fromlist)
{
result = nconc(result,
- add_missing_rels_to_query(root, lfirst(l)));
+ add_base_rels_to_query(root, lfirst(l)));
}
}
else if (IsA(jtnode, JoinExpr))
{
JoinExpr *j = (JoinExpr *) jtnode;
+ RelOptInfo *jrel;
- result = add_missing_rels_to_query(root, j->larg);
+ result = add_base_rels_to_query(root, j->larg);
result = nconc(result,
- add_missing_rels_to_query(root, j->rarg));
+ add_base_rels_to_query(root, j->rarg));
+ /* the join's own rtindex is NOT added to result */
+ jrel = build_other_rel(root, j->rtindex);
+ /*
+ * Mark the join's otherrel with outerjoinset = list of baserel ids
+ * included in the join. Note we must copy here because result list
+ * is destructively modified by nconcs at higher levels.
+ */
+ jrel->outerjoinset = listCopy(result);
+ /*
+ * Safety check: join RTEs should not be SELECT FOR UPDATE targets
+ */
+ if (intMember(j->rtindex, root->rowMarks))
+ elog(ERROR, "SELECT FOR UPDATE cannot be applied to a join");
}
else
- elog(ERROR, "add_missing_rels_to_query: unexpected node type %d",
+ elog(ERROR, "add_base_rels_to_query: unexpected node type %d",
nodeTag(jtnode));
return result;
}
@@ -156,6 +130,66 @@ add_missing_rels_to_query(Query *root, Node *jtnode)
/*****************************************************************************
*
+ * TARGET LISTS
+ *
+ *****************************************************************************/
+
+/*
+ * build_base_rel_tlists
+ * Creates targetlist entries for each var seen in 'tlist' and adds
+ * them to the tlist of the appropriate rel node.
+ */
+void
+build_base_rel_tlists(Query *root, List *tlist)
+{
+ List *tlist_vars = pull_var_clause((Node *) tlist, false);
+
+ add_vars_to_targetlist(root, tlist_vars);
+ freeList(tlist_vars);
+}
+
+/*
+ * add_vars_to_targetlist
+ * For each variable appearing in the list, add it to the owning
+ * relation's targetlist if not already present.
+ *
+ * Note that join alias variables will be attached to the otherrel for
+ * the join RTE. They will later be transferred to the tlist of
+ * the corresponding joinrel. We will also cause entries to be made
+ * for the Vars that the alias will eventually depend on.
+ */
+static void
+add_vars_to_targetlist(Query *root, List *vars)
+{
+ List *temp;
+
+ foreach(temp, vars)
+ {
+ Var *var = (Var *) lfirst(temp);
+ RelOptInfo *rel = find_base_rel(root, var->varno);
+
+ add_var_to_tlist(rel, var);
+
+ if (rel->reloptkind == RELOPT_OTHER_JOIN_REL)
+ {
+ /* Var is an alias */
+ Var *leftsubvar,
+ *rightsubvar;
+
+ build_join_alias_subvars(root, var,
+ &leftsubvar, &rightsubvar);
+
+ rel = find_base_rel(root, leftsubvar->varno);
+ add_var_to_tlist(rel, leftsubvar);
+ rel = find_base_rel(root, rightsubvar->varno);
+ add_var_to_tlist(rel, rightsubvar);
+ }
+ }
+}
+
+
+/*****************************************************************************
+ *
* QUALIFICATIONS
*
*****************************************************************************/
@@ -165,10 +199,9 @@ add_missing_rels_to_query(Query *root, Node *jtnode)
* distribute_quals_to_rels
* Recursively scan the query's join tree for WHERE and JOIN/ON qual
* clauses, and add these to the appropriate RestrictInfo and JoinInfo
- * lists belonging to base RelOptInfos. New base rel entries are created
- * as needed. Also, base RelOptInfos are marked with outerjoinset
- * information, to aid in proper positioning of qual clauses that appear
- * above outer joins.
+ * lists belonging to base RelOptInfos. Also, base RelOptInfos are marked
+ * with outerjoinset information, to aid in proper positioning of qual
+ * clauses that appear above outer joins.
*
* NOTE: when dealing with inner joins, it is appropriate to let a qual clause
* be evaluated at the lowest level where all the variables it mentions are
@@ -181,7 +214,7 @@ add_missing_rels_to_query(Query *root, Node *jtnode)
* a rel, thereby forcing them up the join tree to the right level.
*
* To ease the calculation of these values, distribute_quals_to_rels() returns
- * the list of Relids involved in its own level of join. This is just an
+ * the list of base Relids involved in its own level of join. This is just an
* internal convenience; no outside callers pay attention to the result.
*/
Relids
@@ -302,7 +335,7 @@ mark_baserels_for_outer_join(Query *root, Relids rels, Relids outerrels)
foreach(relid, rels)
{
int relno = lfirsti(relid);
- RelOptInfo *rel = build_base_rel(root, relno);
+ RelOptInfo *rel = find_base_rel(root, relno);
/*
* Since we do this bottom-up, any outer-rels previously marked
@@ -382,11 +415,41 @@ distribute_qual_to_rels(Query *root, Node *clause,
clause_get_relids_vars(clause, &relids, &vars);
/*
- * Cross-check: clause should contain no relids not within its scope.
- * Otherwise the parser messed up.
+ * The clause might contain some join alias vars; if so, we want to
+ * remove the join otherrelids from relids and add the referent joins'
+ * scope lists instead (thus ensuring that the clause can be evaluated
+ * no lower than that join node). We rely here on the marking done
+ * earlier by add_base_rels_to_query.
+ *
+ * We can combine this step with a cross-check that the clause contains
+ * no relids not within its scope. If the first crosscheck succeeds,
+ * the clause contains no aliases and we needn't look more closely.
*/
if (!is_subseti(relids, qualscope))
- elog(ERROR, "JOIN qualification may not refer to other relations");
+ {
+ Relids newrelids = NIL;
+ List *relid;
+
+ foreach(relid, relids)
+ {
+ RelOptInfo *rel = find_other_rel(root, lfirsti(relid));
+
+ if (rel && rel->outerjoinset)
+ {
+ /* this relid is for a join RTE */
+ newrelids = set_unioni(newrelids, rel->outerjoinset);
+ }
+ else
+ {
+ /* this relid is for a true baserel */
+ newrelids = lappendi(newrelids, lfirsti(relid));
+ }
+ }
+ relids = newrelids;
+ /* Now repeat the crosscheck */
+ if (!is_subseti(relids, qualscope))
+ elog(ERROR, "JOIN qualification may not refer to other relations");
+ }
/*
* If the clause is variable-free, we force it to be evaluated at its
@@ -439,7 +502,7 @@ distribute_qual_to_rels(Query *root, Node *clause,
can_be_equijoin = true;
foreach(relid, relids)
{
- RelOptInfo *rel = build_base_rel(root, lfirsti(relid));
+ RelOptInfo *rel = find_base_rel(root, lfirsti(relid));
if (rel->outerjoinset &&
!is_subseti(rel->outerjoinset, relids))
@@ -475,7 +538,7 @@ distribute_qual_to_rels(Query *root, Node *clause,
* There is only one relation participating in 'clause', so
* 'clause' is a restriction clause for that relation.
*/
- RelOptInfo *rel = build_base_rel(root, lfirsti(relids));
+ RelOptInfo *rel = find_base_rel(root, lfirsti(relids));
/*
* Check for a "mergejoinable" clause even though it's not a join
@@ -595,7 +658,7 @@ add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
* Find or make the joininfo node for this combination of rels,
* and add the restrictinfo node to it.
*/
- joininfo = find_joininfo_node(build_base_rel(root, cur_relid),
+ joininfo = find_joininfo_node(find_base_rel(root, cur_relid),
unjoined_relids);
joininfo->jinfo_restrictinfo = lappend(joininfo->jinfo_restrictinfo,
restrictinfo);
@@ -640,9 +703,6 @@ process_implied_equality(Query *root, Node *item1, Node *item2,
* If both vars belong to same rel, we need to look at that rel's
* baserestrictinfo list. If different rels, each will have a
* joininfo node for the other, and we can scan either list.
- *
- * All baserel entries should already exist at this point, so use
- * find_base_rel not build_base_rel.
*/
rel1 = find_base_rel(root, irel1);
if (irel1 == irel2)
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index 57c9b963c73..ee1d1890712 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -14,7 +14,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.67 2001/10/25 05:49:33 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.68 2002/03/12 00:51:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -176,48 +176,32 @@ subplanner(Query *root,
List *flat_tlist,
double tuple_fraction)
{
- List *joined_rels;
- List *brel;
RelOptInfo *final_rel;
Plan *resultplan;
Path *cheapestpath;
Path *presortedpath;
- /*
- * Examine the targetlist and qualifications, adding entries to
- * base_rel_list as relation references are found (e.g., in the
- * qualification, the targetlist, etc.). Restrict and join clauses
- * are added to appropriate lists belonging to the mentioned
- * relations. We also build lists of equijoined keys for pathkey
- * construction.
- */
+ /* init lists to empty */
root->base_rel_list = NIL;
root->other_rel_list = NIL;
root->join_rel_list = NIL;
root->equi_key_list = NIL;
- build_base_rel_tlists(root, flat_tlist);
-
- (void) distribute_quals_to_rels(root, (Node *) root->jointree);
-
/*
- * Make sure we have RelOptInfo nodes for all relations to be joined.
+ * Construct RelOptInfo nodes for all base relations in query.
*/
- joined_rels = add_missing_rels_to_query(root, (Node *) root->jointree);
+ (void) add_base_rels_to_query(root, (Node *) root->jointree);
/*
- * Check that the join tree includes all the base relations used in
- * the query --- otherwise, the parser or rewriter messed up.
+ * Examine the targetlist and qualifications, adding entries to
+ * baserel targetlists for all referenced Vars. Restrict and join
+ * clauses are added to appropriate lists belonging to the mentioned
+ * relations. We also build lists of equijoined keys for pathkey
+ * construction.
*/
- foreach(brel, root->base_rel_list)
- {
- RelOptInfo *baserel = (RelOptInfo *) lfirst(brel);
- int relid = lfirsti(baserel->relids);
+ build_base_rel_tlists(root, flat_tlist);
- if (!ptrMember(baserel, joined_rels))
- elog(ERROR, "Internal error: no jointree entry for rel %s (%d)",
- rt_fetch(relid, root->rtable)->eref->relname, relid);
- }
+ (void) distribute_quals_to_rels(root, (Node *) root->jointree);
/*
* Use the completed lists of equijoined keys to deduce any implied
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index eb92c3d3af9..1df2cd29402 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.114 2001/12/10 22:54:12 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.115 2002/03/12 00:51:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -99,7 +99,7 @@ planner(Query *parse)
result_plan->nParamExec = length(PlannerParamVar);
/* final cleanup of the plan */
- set_plan_references(result_plan);
+ set_plan_references(parse, result_plan);
/* restore state for outer planner, if any */
PlannerQueryLevel = save_PlannerQueryLevel;
@@ -616,6 +616,9 @@ preprocess_jointree(Query *parse, Node *jtnode)
static Node *
preprocess_expression(Query *parse, Node *expr, int kind)
{
+ bool has_join_rtes;
+ List *rt;
+
/*
* Simplify constant expressions.
*
@@ -651,6 +654,29 @@ preprocess_expression(Query *parse, Node *expr, int kind)
if (PlannerQueryLevel > 1)
expr = SS_replace_correlation_vars(expr);
+ /*
+ * If the query has any join RTEs, try to replace join alias variables
+ * with base-relation variables, to allow quals to be pushed down.
+ * We must do this after sublink processing, since it does not recurse
+ * into sublinks.
+ *
+ * The flattening pass is expensive enough that it seems worthwhile to
+ * scan the rangetable to see if we can avoid it.
+ */
+ has_join_rtes = false;
+ foreach(rt, parse->rtable)
+ {
+ RangeTblEntry *rte = lfirst(rt);
+
+ if (rte->rtekind == RTE_JOIN)
+ {
+ has_join_rtes = true;
+ break;
+ }
+ }
+ if (has_join_rtes)
+ expr = flatten_join_alias_vars(expr, parse, 0);
+
return expr;
}
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 167901acdf0..2f48821ece6 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -9,25 +9,29 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.73 2001/11/05 17:46:26 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.74 2002/03/12 00:51:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
-#include <sys/types.h>
-
#include "postgres.h"
+#include <sys/types.h>
+
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/planmain.h"
#include "optimizer/tlist.h"
+#include "optimizer/var.h"
+
typedef struct
{
+ Query *root;
List *outer_tlist;
List *inner_tlist;
Index acceptable_rel;
+ Index join_rti;
} join_references_context;
typedef struct
@@ -38,7 +42,7 @@ typedef struct
} replace_vars_with_subplan_refs_context;
static void fix_expr_references(Plan *plan, Node *node);
-static void set_join_references(Join *join);
+static void set_join_references(Query *root, Join *join);
static void set_uppernode_references(Plan *plan, Index subvarno);
static Node *join_references_mutator(Node *node,
join_references_context *context);
@@ -71,7 +75,7 @@ static bool fix_opids_walker(Node *node, void *context);
* Returns nothing of interest, but modifies internal fields of nodes.
*/
void
-set_plan_references(Plan *plan)
+set_plan_references(Query *root, Plan *plan)
{
List *pl;
@@ -115,16 +119,16 @@ set_plan_references(Plan *plan)
fix_expr_references(plan, (Node *) plan->targetlist);
fix_expr_references(plan, (Node *) plan->qual);
/* Recurse into subplan too */
- set_plan_references(((SubqueryScan *) plan)->subplan);
+ set_plan_references(root, ((SubqueryScan *) plan)->subplan);
break;
case T_NestLoop:
- set_join_references((Join *) plan);
+ set_join_references(root, (Join *) plan);
fix_expr_references(plan, (Node *) plan->targetlist);
fix_expr_references(plan, (Node *) plan->qual);
fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
break;
case T_MergeJoin:
- set_join_references((Join *) plan);
+ set_join_references(root, (Join *) plan);
fix_expr_references(plan, (Node *) plan->targetlist);
fix_expr_references(plan, (Node *) plan->qual);
fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
@@ -132,7 +136,7 @@ set_plan_references(Plan *plan)
(Node *) ((MergeJoin *) plan)->mergeclauses);
break;
case T_HashJoin:
- set_join_references((Join *) plan);
+ set_join_references(root, (Join *) plan);
fix_expr_references(plan, (Node *) plan->targetlist);
fix_expr_references(plan, (Node *) plan->qual);
fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
@@ -186,7 +190,7 @@ set_plan_references(Plan *plan)
* recurse into subplans.
*/
foreach(pl, ((Append *) plan)->appendplans)
- set_plan_references((Plan *) lfirst(pl));
+ set_plan_references(root, (Plan *) lfirst(pl));
break;
default:
elog(ERROR, "set_plan_references: unknown plan type %d",
@@ -203,21 +207,21 @@ set_plan_references(Plan *plan)
* plan's var nodes against the already-modified nodes of the
* subplans.
*/
- set_plan_references(plan->lefttree);
- set_plan_references(plan->righttree);
+ set_plan_references(root, plan->lefttree);
+ set_plan_references(root, plan->righttree);
foreach(pl, plan->initPlan)
{
SubPlan *sp = (SubPlan *) lfirst(pl);
Assert(IsA(sp, SubPlan));
- set_plan_references(sp->plan);
+ set_plan_references(root, sp->plan);
}
foreach(pl, plan->subPlan)
{
SubPlan *sp = (SubPlan *) lfirst(pl);
Assert(IsA(sp, SubPlan));
- set_plan_references(sp->plan);
+ set_plan_references(root, sp->plan);
}
}
@@ -256,7 +260,7 @@ fix_expr_references(Plan *plan, Node *node)
* 'join' is a join plan node
*/
static void
-set_join_references(Join *join)
+set_join_references(Query *root, Join *join)
{
Plan *outer = join->plan.lefttree;
Plan *inner = join->plan.righttree;
@@ -264,9 +268,11 @@ set_join_references(Join *join)
List *inner_tlist = ((inner == NULL) ? NIL : inner->targetlist);
join->plan.targetlist = join_references(join->plan.targetlist,
+ root,
outer_tlist,
inner_tlist,
- (Index) 0);
+ (Index) 0,
+ join->joinrti);
}
/*
@@ -343,7 +349,8 @@ set_uppernode_references(Plan *plan, Index subvarno)
* Creates a new set of targetlist entries or join qual clauses by
* changing the varno/varattno values of variables in the clauses
* to reference target list values from the outer and inner join
- * relation target lists.
+ * relation target lists. Also, any join alias variables in the
+ * clauses are expanded into references to their component variables.
*
* This is used in two different scenarios: a normal join clause, where
* all the Vars in the clause *must* be replaced by OUTER or INNER references;
@@ -360,21 +367,27 @@ set_uppernode_references(Plan *plan, Index subvarno)
* 'inner_tlist' is the target list of the inner join relation, or NIL
* 'acceptable_rel' is either zero or the rangetable index of a relation
* whose Vars may appear in the clause without provoking an error.
+ * 'join_rti' is either zero or the join RTE index of join alias variables
+ * that should be expanded.
*
* Returns the new expression tree. The original clause structure is
* not modified.
*/
List *
join_references(List *clauses,
+ Query *root,
List *outer_tlist,
List *inner_tlist,
- Index acceptable_rel)
+ Index acceptable_rel,
+ Index join_rti)
{
join_references_context context;
+ context.root = root;
context.outer_tlist = outer_tlist;
context.inner_tlist = inner_tlist;
context.acceptable_rel = acceptable_rel;
+ context.join_rti = join_rti;
return (List *) join_references_mutator((Node *) clauses, &context);
}
@@ -387,12 +400,14 @@ join_references_mutator(Node *node,
if (IsA(node, Var))
{
Var *var = (Var *) node;
- Var *newvar = (Var *) copyObject(var);
Resdom *resdom;
+ /* First look for the var in the input tlists */
resdom = tlist_member((Node *) var, context->outer_tlist);
if (resdom)
{
+ Var *newvar = (Var *) copyObject(var);
+
newvar->varno = OUTER;
newvar->varattno = resdom->resno;
return (Node *) newvar;
@@ -400,18 +415,33 @@ join_references_mutator(Node *node,
resdom = tlist_member((Node *) var, context->inner_tlist);
if (resdom)
{
+ Var *newvar = (Var *) copyObject(var);
+
newvar->varno = INNER;
newvar->varattno = resdom->resno;
return (Node *) newvar;
}
+ /* Perhaps it's a join alias that can be resolved to input vars? */
+ if (var->varno == context->join_rti)
+ {
+ Node *newnode;
+
+ newnode = flatten_join_alias_vars((Node *) var,
+ context->root,
+ context->join_rti);
+ /* Must now resolve the input vars... */
+ newnode = join_references_mutator(newnode, context);
+ return newnode;
+ }
+
/*
- * Var not in either tlist --- either raise an error, or return
- * the Var unmodified.
+ * No referent found for Var --- either raise an error, or return
+ * the Var unmodified if it's for acceptable_rel.
*/
if (var->varno != context->acceptable_rel)
elog(ERROR, "join_references: variable not in subplan target lists");
- return (Node *) newvar;
+ return (Node *) copyObject(var);
}
return expression_tree_mutator(node,
join_references_mutator,
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 64d8b78f066..b2f18780fcc 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -14,7 +14,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.71 2002/03/05 05:10:24 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.72 2002/03/12 00:51:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -673,7 +673,7 @@ List *
expand_inherted_rtentry(Query *parse, Index rti, bool dup_parent)
{
RangeTblEntry *rte = rt_fetch(rti, parse->rtable);
- Oid parentOID = rte->relid;
+ Oid parentOID;
List *inhOIDs;
List *inhRTIs;
List *l;
@@ -681,10 +681,11 @@ expand_inherted_rtentry(Query *parse, Index rti, bool dup_parent)
/* Does RT entry allow inheritance? */
if (!rte->inh)
return NIL;
- Assert(parentOID != InvalidOid && rte->subquery == NULL);
+ Assert(rte->rtekind == RTE_RELATION);
/* Always clear the parent's inh flag, see above comments */
rte->inh = false;
/* Fast path for common case of childless table */
+ parentOID = rte->relid;
if (!has_subclass(parentOID))
return NIL;
/* Scan for all members of inheritance set */
@@ -811,6 +812,19 @@ adjust_inherited_attrs_mutator(Node *node,
rtr->rtindex = context->new_rt_index;
return (Node *) rtr;
}
+ if (IsA(node, JoinExpr))
+ {
+ /* Copy the JoinExpr node with correct mutation of subnodes */
+ JoinExpr *j;
+
+ j = (JoinExpr *) expression_tree_mutator(node,
+ adjust_inherited_attrs_mutator,
+ (void *) context);
+ /* now fix JoinExpr's rtindex */
+ if (j->rtindex == context->old_rt_index)
+ j->rtindex = context->new_rt_index;
+ return (Node *) j;
+ }
/*
* We have to process RestrictInfo nodes specially: we do NOT want to
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 8a4bcf4d9c4..6585fb1905d 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.93 2002/01/03 18:01:59 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.94 2002/03/12 00:51:50 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -1808,12 +1808,8 @@ expression_tree_walker(Node *node,
return true;
if (walker(join->quals, context))
return true;
- if (walker((Node *) join->colvars, context))
- return true;
-
/*
- * alias clause, using list, colnames list are deemed
- * uninteresting.
+ * alias clause, using list are deemed uninteresting.
*/
}
break;
@@ -2186,8 +2182,7 @@ expression_tree_mutator(Node *node,
MUTATE(newnode->larg, join->larg, Node *);
MUTATE(newnode->rarg, join->rarg, Node *);
MUTATE(newnode->quals, join->quals, Node *);
- MUTATE(newnode->colvars, join->colvars, List *);
- /* We do not mutate alias, using, or colnames by default */
+ /* We do not mutate alias or using by default */
return (Node *) newnode;
}
break;
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 457826f5d31..0fad16fbdd0 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.35 2001/10/25 05:49:34 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.36 2002/03/12 00:51:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -40,26 +40,26 @@ static void subbuild_joinrel_joinlist(RelOptInfo *joinrel,
/*
* build_base_rel
- * Returns relation entry corresponding to 'relid', creating a new one
- * if necessary. This is for base relations.
+ * Construct a new base relation RelOptInfo, and put it in the query's
+ * base_rel_list.
*/
-RelOptInfo *
+void
build_base_rel(Query *root, int relid)
{
List *rels;
RelOptInfo *rel;
- /* Already made? */
+ /* Rel should not exist already */
foreach(rels, root->base_rel_list)
{
rel = (RelOptInfo *) lfirst(rels);
/* length(rel->relids) == 1 for all members of base_rel_list */
if (lfirsti(rel->relids) == relid)
- return rel;
+ elog(ERROR, "build_base_rel: rel already exists");
}
- /* It should not exist as an "other" rel */
+ /* It should not exist as an "other" rel, either */
foreach(rels, root->other_rel_list)
{
rel = (RelOptInfo *) lfirst(rels);
@@ -73,14 +73,12 @@ build_base_rel(Query *root, int relid)
/* and add it to the list */
root->base_rel_list = lcons(rel, root->base_rel_list);
-
- return rel;
}
/*
* build_other_rel
* Returns relation entry corresponding to 'relid', creating a new one
- * if necessary. This is for 'other' relations, which are just like
+ * if necessary. This is for 'other' relations, which are much like
* base relations except that they live in a different list.
*/
RelOptInfo *
@@ -111,6 +109,10 @@ build_other_rel(Query *root, int relid)
/* No existing RelOptInfo for this other rel, so make a new one */
rel = make_base_rel(root, relid);
+ /* if it's not a join rel, must be a child rel */
+ if (rel->reloptkind == RELOPT_BASEREL)
+ rel->reloptkind = RELOPT_OTHER_CHILD_REL;
+
/* and add it to the list */
root->other_rel_list = lcons(rel, root->other_rel_list);
@@ -127,8 +129,9 @@ static RelOptInfo *
make_base_rel(Query *root, int relid)
{
RelOptInfo *rel = makeNode(RelOptInfo);
- Oid relationObjectId;
+ RangeTblEntry *rte = rt_fetch(relid, root->rtable);
+ rel->reloptkind = RELOPT_BASEREL;
rel->relids = makeListi1(relid);
rel->rows = 0;
rel->width = 0;
@@ -142,29 +145,40 @@ make_base_rel(Query *root, int relid)
rel->pages = 0;
rel->tuples = 0;
rel->subplan = NULL;
+ rel->joinrti = 0;
+ rel->joinrteids = NIL;
rel->baserestrictinfo = NIL;
rel->baserestrictcost = 0;
rel->outerjoinset = NIL;
rel->joininfo = NIL;
rel->innerjoin = NIL;
- /* Check rtable to see if it's a plain relation or a subquery */
- relationObjectId = getrelid(relid, root->rtable);
-
- if (relationObjectId != InvalidOid)
- {
- /* Plain relation --- retrieve statistics from the system catalogs */
- bool indexed;
-
- get_relation_info(relationObjectId,
- &indexed, &rel->pages, &rel->tuples);
- if (indexed)
- rel->indexlist = find_secondary_indexes(relationObjectId);
- }
- else
+ /* Check type of rtable entry */
+ switch (rte->rtekind)
{
- /* subquery --- mark it as such for later processing */
- rel->issubquery = true;
+ case RTE_RELATION:
+ {
+ /* Table --- retrieve statistics from the system catalogs */
+ bool indexed;
+
+ get_relation_info(rte->relid,
+ &indexed, &rel->pages, &rel->tuples);
+ if (indexed)
+ rel->indexlist = find_secondary_indexes(rte->relid);
+ break;
+ }
+ case RTE_SUBQUERY:
+ /* Subquery --- mark it as such for later processing */
+ rel->issubquery = true;
+ break;
+ case RTE_JOIN:
+ /* Join --- must be an otherrel */
+ rel->reloptkind = RELOPT_OTHER_JOIN_REL;
+ break;
+ default:
+ elog(ERROR, "make_base_rel: unsupported RTE kind %d",
+ (int) rte->rtekind);
+ break;
}
return rel;
@@ -204,6 +218,47 @@ find_base_rel(Query *root, int relid)
}
/*
+ * find_other_rel
+ * Find an otherrel entry, if one exists for the given relid.
+ * Return NULL if no entry.
+ */
+RelOptInfo *
+find_other_rel(Query *root, int relid)
+{
+ List *rels;
+
+ foreach(rels, root->other_rel_list)
+ {
+ RelOptInfo *rel = (RelOptInfo *) lfirst(rels);
+
+ if (lfirsti(rel->relids) == relid)
+ return rel;
+ }
+ return NULL;
+}
+
+/*
+ * find_other_rel_for_join
+ * Look for an otherrel for a join RTE matching the given baserel set.
+ * Return NULL if no entry.
+ */
+RelOptInfo *
+find_other_rel_for_join(Query *root, List *relids)
+{
+ List *rels;
+
+ foreach(rels, root->other_rel_list)
+ {
+ RelOptInfo *rel = (RelOptInfo *) lfirst(rels);
+
+ if (rel->reloptkind == RELOPT_OTHER_JOIN_REL
+ && sameseti(relids, rel->outerjoinset))
+ return rel;
+ }
+ return NULL;
+}
+
+/*
* find_join_rel
* Returns relation entry corresponding to 'relids' (a list of RT indexes),
* or NULL if none exists. This is for join relations.
@@ -252,6 +307,7 @@ build_join_rel(Query *root,
{
List *joinrelids;
RelOptInfo *joinrel;
+ RelOptInfo *joinrterel;
List *restrictlist;
List *new_outer_tlist;
List *new_inner_tlist;
@@ -286,6 +342,7 @@ build_join_rel(Query *root,
* Nope, so make one.
*/
joinrel = makeNode(RelOptInfo);
+ joinrel->reloptkind = RELOPT_JOINREL;
joinrel->relids = joinrelids;
joinrel->rows = 0;
joinrel->width = 0;
@@ -299,24 +356,38 @@ build_join_rel(Query *root,
joinrel->pages = 0;
joinrel->tuples = 0;
joinrel->subplan = NULL;
+ joinrel->joinrti = 0;
+ joinrel->joinrteids = nconc(listCopy(outer_rel->joinrteids),
+ inner_rel->joinrteids);
joinrel->baserestrictinfo = NIL;
joinrel->baserestrictcost = 0;
joinrel->outerjoinset = NIL;
joinrel->joininfo = NIL;
joinrel->innerjoin = NIL;
+ /* Is there a join RTE matching this join? */
+ joinrterel = find_other_rel_for_join(root, joinrelids);
+ if (joinrterel)
+ {
+ /* Yes, remember its RT index */
+ joinrel->joinrti = lfirsti(joinrterel->relids);
+ joinrel->joinrteids = lconsi(joinrel->joinrti, joinrel->joinrteids);
+ }
+
/*
* Create a new tlist by removing irrelevant elements from both tlists
* of the outer and inner join relations and then merging the results
* together.
*
+ * XXX right now we don't remove any irrelevant elements, we just
+ * append the two tlists together. 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.
+ *
* 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,
@@ -324,6 +395,23 @@ build_join_rel(Query *root,
joinrel->targetlist = nconc(new_outer_tlist, new_inner_tlist);
/*
+ * If there are any alias variables attached to the matching join RTE,
+ * attach them to the tlist too, so that they will be evaluated for use
+ * at higher plan levels.
+ */
+ if (joinrterel)
+ {
+ List *jrtetl;
+
+ foreach(jrtetl, joinrterel->targetlist)
+ {
+ TargetEntry *jrtete = lfirst(jrtetl);
+
+ add_var_to_tlist(joinrel, (Var *) jrtete->expr);
+ }
+ }
+
+ /*
* 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().)
diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c
index 2521ffb2b6c..568e024d20b 100644
--- a/src/backend/optimizer/util/var.c
+++ b/src/backend/optimizer/util/var.c
@@ -8,15 +8,18 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.33 2001/10/25 05:49:34 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.34 2002/03/12 00:51:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
+#include "nodes/makefuncs.h"
#include "nodes/plannodes.h"
#include "optimizer/clauses.h"
#include "optimizer/var.h"
+#include "parser/parsetree.h"
+#include "parser/parse_coerce.h"
typedef struct
@@ -38,6 +41,12 @@ typedef struct
bool includeUpperVars;
} pull_var_clause_context;
+typedef struct
+{
+ Query *root;
+ int expandRTI;
+} flatten_join_alias_vars_context;
+
static bool pull_varnos_walker(Node *node,
pull_varnos_context *context);
static bool contain_var_reference_walker(Node *node,
@@ -45,6 +54,10 @@ static bool contain_var_reference_walker(Node *node,
static bool contain_var_clause_walker(Node *node, void *context);
static bool pull_var_clause_walker(Node *node,
pull_var_clause_context *context);
+static Node *flatten_join_alias_vars_mutator(Node *node,
+ flatten_join_alias_vars_context *context);
+static Node *flatten_join_alias_var(Var *var, Query *root, int expandRTI);
+static Node *find_jointree_item(Node *jtnode, int rtindex);
/*
@@ -297,3 +310,363 @@ pull_var_clause_walker(Node *node, pull_var_clause_context *context)
return expression_tree_walker(node, pull_var_clause_walker,
(void *) context);
}
+
+
+/*
+ * flatten_join_alias_vars
+ * Whereever possible, replace Vars that reference JOIN outputs with
+ * references to the original relation variables instead. This allows
+ * quals involving such vars to be pushed down. Vars that cannot be
+ * simplified to non-join Vars are replaced by COALESCE expressions
+ * if they have varno = expandRTI, and are left as JOIN RTE references
+ * otherwise. (Pass expandRTI = 0 to prevent all COALESCE expansion.)
+ *
+ * Upper-level vars (with varlevelsup > 0) are ignored; normally there
+ * should not be any by the time this routine is called.
+ *
+ * Does not examine subqueries, therefore must only be used after reduction
+ * of sublinks to subplans!
+ */
+Node *
+flatten_join_alias_vars(Node *node, Query *root, int expandRTI)
+{
+ flatten_join_alias_vars_context context;
+
+ context.root = root;
+ context.expandRTI = expandRTI;
+
+ return flatten_join_alias_vars_mutator(node, &context);
+}
+
+static Node *
+flatten_join_alias_vars_mutator(Node *node,
+ flatten_join_alias_vars_context *context)
+{
+ if (node == NULL)
+ return NULL;
+ if (IsA(node, Var))
+ {
+ Var *var = (Var *) node;
+
+ if (var->varlevelsup != 0)
+ return node; /* no need to copy, really */
+ return flatten_join_alias_var(var, context->root, context->expandRTI);
+ }
+ return expression_tree_mutator(node, flatten_join_alias_vars_mutator,
+ (void *) context);
+}
+
+static Node *
+flatten_join_alias_var(Var *var, Query *root, int expandRTI)
+{
+ Index varno = var->varno;
+ AttrNumber varattno = var->varattno;
+ Oid vartype = var->vartype;
+ int32 vartypmod = var->vartypmod;
+ JoinExpr *jexpr = NULL;
+
+ /*
+ * Loop to cope with joins of joins
+ */
+ for (;;)
+ {
+ RangeTblEntry *rte = rt_fetch(varno, root->rtable);
+ Index leftrti,
+ rightrti;
+ AttrNumber leftattno,
+ rightattno;
+ RangeTblEntry *subrte;
+ Oid subtype;
+ int32 subtypmod;
+
+ if (rte->rtekind != RTE_JOIN)
+ break; /* reached a non-join RTE */
+ /*
+ * Find the RT indexes of the left and right children of the
+ * join node. We have to search the join tree to do this,
+ * which is a major pain in the neck --- but keeping RT indexes
+ * in other RT entries is worse, because it makes modifying
+ * querytrees difficult. (Perhaps we can improve on the
+ * rangetable/jointree datastructure someday.) One thing we
+ * can do is avoid repeated searches while tracing a single
+ * variable down to its baserel.
+ */
+ if (jexpr == NULL)
+ jexpr = (JoinExpr *)
+ find_jointree_item((Node *) root->jointree, varno);
+ if (jexpr == NULL ||
+ !IsA(jexpr, JoinExpr) ||
+ jexpr->rtindex != varno)
+ elog(ERROR, "flatten_join_alias_var: failed to find JoinExpr");
+ if (IsA(jexpr->larg, RangeTblRef))
+ leftrti = ((RangeTblRef *) jexpr->larg)->rtindex;
+ else if (IsA(jexpr->larg, JoinExpr))
+ leftrti = ((JoinExpr *) jexpr->larg)->rtindex;
+ else
+ {
+ elog(ERROR, "flatten_join_alias_var: unexpected subtree type");
+ leftrti = 0; /* keep compiler quiet */
+ }
+ if (IsA(jexpr->rarg, RangeTblRef))
+ rightrti = ((RangeTblRef *) jexpr->rarg)->rtindex;
+ else if (IsA(jexpr->rarg, JoinExpr))
+ rightrti = ((JoinExpr *) jexpr->rarg)->rtindex;
+ else
+ {
+ elog(ERROR, "flatten_join_alias_var: unexpected subtree type");
+ rightrti = 0; /* keep compiler quiet */
+ }
+ /*
+ * See if the join var is from the left side, the right side,
+ * or both (ie, it is a USING/NATURAL JOIN merger column).
+ */
+ Assert(varattno > 0);
+ leftattno = (AttrNumber) nthi(varattno-1, rte->joinleftcols);
+ rightattno = (AttrNumber) nthi(varattno-1, rte->joinrightcols);
+ if (leftattno && rightattno)
+ {
+ /*
+ * Var is a merge var. If a left or right join, we can replace
+ * it by the left or right input var respectively; we only need
+ * a COALESCE for a full join. However, beware of the possibility
+ * that there's been a type promotion to make the input vars
+ * compatible; do not replace a var by one of a different type!
+ */
+ if (rte->jointype == JOIN_INNER ||
+ rte->jointype == JOIN_LEFT)
+ {
+ subrte = rt_fetch(leftrti, root->rtable);
+ get_rte_attribute_type(subrte, leftattno,
+ &subtype, &subtypmod);
+ if (vartype == subtype && vartypmod == subtypmod)
+ {
+ varno = leftrti;
+ varattno = leftattno;
+ jexpr = (JoinExpr *) jexpr->larg;
+ continue;
+ }
+ }
+ if (rte->jointype == JOIN_INNER ||
+ rte->jointype == JOIN_RIGHT)
+ {
+ subrte = rt_fetch(rightrti, root->rtable);
+ get_rte_attribute_type(subrte, rightattno,
+ &subtype, &subtypmod);
+ if (vartype == subtype && vartypmod == subtypmod)
+ {
+ varno = rightrti;
+ varattno = rightattno;
+ jexpr = (JoinExpr *) jexpr->rarg;
+ continue;
+ }
+ }
+ /*
+ * This var cannot be substituted directly, only with a COALESCE.
+ * Do so only if it belongs to the particular join indicated by
+ * the caller.
+ */
+ if (varno != expandRTI)
+ break;
+ {
+ Node *l_var,
+ *r_var;
+ CaseExpr *c = makeNode(CaseExpr);
+ CaseWhen *w = makeNode(CaseWhen);
+ NullTest *n = makeNode(NullTest);
+
+ subrte = rt_fetch(leftrti, root->rtable);
+ get_rte_attribute_type(subrte, leftattno,
+ &subtype, &subtypmod);
+ l_var = (Node *) makeVar(leftrti,
+ leftattno,
+ subtype,
+ subtypmod,
+ 0);
+ if (subtype != vartype)
+ {
+ l_var = coerce_type(NULL, l_var, subtype,
+ vartype, vartypmod);
+ l_var = coerce_type_typmod(NULL, l_var,
+ vartype, vartypmod);
+ }
+ else if (subtypmod != vartypmod)
+ l_var = coerce_type_typmod(NULL, l_var,
+ vartype, vartypmod);
+
+ subrte = rt_fetch(rightrti, root->rtable);
+ get_rte_attribute_type(subrte, rightattno,
+ &subtype, &subtypmod);
+ r_var = (Node *) makeVar(rightrti,
+ rightattno,
+ subtype,
+ subtypmod,
+ 0);
+ if (subtype != vartype)
+ {
+ r_var = coerce_type(NULL, r_var, subtype,
+ vartype, vartypmod);
+ r_var = coerce_type_typmod(NULL, r_var,
+ vartype, vartypmod);
+ }
+ else if (subtypmod != vartypmod)
+ r_var = coerce_type_typmod(NULL, r_var,
+ vartype, vartypmod);
+
+ n->arg = l_var;
+ n->nulltesttype = IS_NOT_NULL;
+ w->expr = (Node *) n;
+ w->result = l_var;
+ c->casetype = vartype;
+ c->args = makeList1(w);
+ c->defresult = r_var;
+ return (Node *) c;
+ }
+ }
+ else if (leftattno)
+ {
+ /* Here we do not need to check the type */
+ varno = leftrti;
+ varattno = leftattno;
+ jexpr = (JoinExpr *) jexpr->larg;
+ }
+ else
+ {
+ Assert(rightattno);
+ /* Here we do not need to check the type */
+ varno = rightrti;
+ varattno = rightattno;
+ jexpr = (JoinExpr *) jexpr->rarg;
+ }
+ }
+
+ /*
+ * When we fall out of the loop, we've reached the base Var.
+ */
+ return (Node *) makeVar(varno,
+ varattno,
+ vartype,
+ vartypmod,
+ 0);
+}
+
+/*
+ * Given a join alias Var, construct Vars for the two input vars it directly
+ * depends on. Note that this should *only* be called for merger alias Vars.
+ * In practice it is only used for Vars that got past flatten_join_alias_vars.
+ */
+void
+build_join_alias_subvars(Query *root, Var *aliasvar,
+ Var **leftsubvar, Var **rightsubvar)
+{
+ Index varno = aliasvar->varno;
+ AttrNumber varattno = aliasvar->varattno;
+ RangeTblEntry *rte;
+ JoinExpr *jexpr;
+ Index leftrti,
+ rightrti;
+ AttrNumber leftattno,
+ rightattno;
+ RangeTblEntry *subrte;
+ Oid subtype;
+ int32 subtypmod;
+
+ Assert(aliasvar->varlevelsup == 0);
+ rte = rt_fetch(varno, root->rtable);
+ Assert(rte->rtekind == RTE_JOIN);
+
+ /*
+ * Find the RT indexes of the left and right children of the
+ * join node.
+ */
+ jexpr = (JoinExpr *) find_jointree_item((Node *) root->jointree, varno);
+ if (jexpr == NULL ||
+ !IsA(jexpr, JoinExpr) ||
+ jexpr->rtindex != varno)
+ elog(ERROR, "build_join_alias_subvars: failed to find JoinExpr");
+ if (IsA(jexpr->larg, RangeTblRef))
+ leftrti = ((RangeTblRef *) jexpr->larg)->rtindex;
+ else if (IsA(jexpr->larg, JoinExpr))
+ leftrti = ((JoinExpr *) jexpr->larg)->rtindex;
+ else
+ {
+ elog(ERROR, "build_join_alias_subvars: unexpected subtree type");
+ leftrti = 0; /* keep compiler quiet */
+ }
+ if (IsA(jexpr->rarg, RangeTblRef))
+ rightrti = ((RangeTblRef *) jexpr->rarg)->rtindex;
+ else if (IsA(jexpr->rarg, JoinExpr))
+ rightrti = ((JoinExpr *) jexpr->rarg)->rtindex;
+ else
+ {
+ elog(ERROR, "build_join_alias_subvars: unexpected subtree type");
+ rightrti = 0; /* keep compiler quiet */
+ }
+
+ Assert(varattno > 0);
+ leftattno = (AttrNumber) nthi(varattno-1, rte->joinleftcols);
+ rightattno = (AttrNumber) nthi(varattno-1, rte->joinrightcols);
+ if (!(leftattno && rightattno))
+ elog(ERROR, "build_join_alias_subvars: non-merger variable");
+
+ subrte = rt_fetch(leftrti, root->rtable);
+ get_rte_attribute_type(subrte, leftattno,
+ &subtype, &subtypmod);
+ *leftsubvar = makeVar(leftrti,
+ leftattno,
+ subtype,
+ subtypmod,
+ 0);
+
+ subrte = rt_fetch(rightrti, root->rtable);
+ get_rte_attribute_type(subrte, rightattno,
+ &subtype, &subtypmod);
+ *rightsubvar = makeVar(rightrti,
+ rightattno,
+ subtype,
+ subtypmod,
+ 0);
+}
+
+/*
+ * Find jointree item matching the specified RT index
+ */
+static Node *
+find_jointree_item(Node *jtnode, int rtindex)
+{
+ if (jtnode == NULL)
+ return NULL;
+ if (IsA(jtnode, RangeTblRef))
+ {
+ if (((RangeTblRef *) jtnode)->rtindex == rtindex)
+ return jtnode;
+ }
+ else if (IsA(jtnode, FromExpr))
+ {
+ FromExpr *f = (FromExpr *) jtnode;
+ List *l;
+
+ foreach(l, f->fromlist)
+ {
+ jtnode = find_jointree_item(lfirst(l), rtindex);
+ if (jtnode)
+ return jtnode;
+ }
+ }
+ else if (IsA(jtnode, JoinExpr))
+ {
+ JoinExpr *j = (JoinExpr *) jtnode;
+
+ if (j->rtindex == rtindex)
+ return jtnode;
+ jtnode = find_jointree_item(j->larg, rtindex);
+ if (jtnode)
+ return jtnode;
+ jtnode = find_jointree_item(j->rarg, rtindex);
+ if (jtnode)
+ return jtnode;
+ }
+ else
+ elog(ERROR, "find_jointree_item: unexpected node type %d",
+ nodeTag(jtnode));
+ return NULL;
+}