aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/src/sgml/arch-dev.sgml2
-rw-r--r--src/backend/commands/indexcmds.c7
-rw-r--r--src/backend/executor/nodeAgg.c3
-rw-r--r--src/backend/nodes/makefuncs.c16
-rw-r--r--src/backend/optimizer/path/costsize.c7
-rw-r--r--src/backend/optimizer/path/pathkeys.c95
-rw-r--r--src/backend/optimizer/plan/createplan.c128
-rw-r--r--src/backend/optimizer/plan/initsplan.c5
-rw-r--r--src/backend/optimizer/plan/planmain.c73
-rw-r--r--src/backend/optimizer/plan/planner.c52
-rw-r--r--src/backend/optimizer/plan/setrefs.c723
-rw-r--r--src/backend/optimizer/plan/subselect.c31
-rw-r--r--src/backend/optimizer/prep/preptlist.c10
-rw-r--r--src/backend/optimizer/util/clauses.c145
-rw-r--r--src/backend/optimizer/util/tlist.c179
-rw-r--r--src/backend/optimizer/util/var.c28
-rw-r--r--src/backend/parser/parse_func.c8
-rw-r--r--src/backend/parser/parse_node.c5
-rw-r--r--src/include/nodes/makefuncs.h6
-rw-r--r--src/include/nodes/primnodes.h28
-rw-r--r--src/include/optimizer/clauses.h14
-rw-r--r--src/include/optimizer/planmain.h19
-rw-r--r--src/include/optimizer/tlist.h14
-rw-r--r--src/include/optimizer/var.h3
24 files changed, 650 insertions, 951 deletions
diff --git a/doc/src/sgml/arch-dev.sgml b/doc/src/sgml/arch-dev.sgml
index 7d867c45cae..93f53c51555 100644
--- a/doc/src/sgml/arch-dev.sgml
+++ b/doc/src/sgml/arch-dev.sgml
@@ -2475,7 +2475,7 @@ having clause} is found.
+ if(node->plan.qual != NULL)
+ {
+ qual_result =
-+ ExecQual(fix_opids(node->plan.qual),
++ ExecQual(node->plan.qual,
+ econtext);
+ }
+ if (oneTuple) pfree(oneTuple);
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 3644d1ba042..31d3419bee6 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.9 1999/07/17 20:16:52 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.10 1999/08/22 20:14:37 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -24,6 +24,7 @@
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "optimizer/clauses.h"
+#include "optimizer/planmain.h"
#include "optimizer/prep.h"
#include "parser/parsetree.h"
#include "utils/builtins.h"
@@ -142,7 +143,7 @@ DefineIndex(char *heapRelationName,
if (predicate != NULL && rangetable != NIL)
{
cnfPred = cnfify((Expr *) copyObject(predicate), true);
- fix_opids(cnfPred);
+ fix_opids((Node *) cnfPred);
CheckPredicate(cnfPred, rangetable, relationId);
}
@@ -285,7 +286,7 @@ ExtendIndex(char *indexRelationName, Expr *predicate, List *rangetable)
if (rangetable != NIL)
{
cnfPred = cnfify((Expr *) copyObject(predicate), true);
- fix_opids(cnfPred);
+ fix_opids((Node *) cnfPred);
CheckPredicate(cnfPred, rangetable, relationId);
}
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index b600c0e3a8f..2936694f679 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -23,7 +23,6 @@
#include "executor/executor.h"
#include "executor/nodeAgg.h"
#include "optimizer/clauses.h"
-#include "optimizer/planmain.h"
#include "parser/parse_type.h"
#include "utils/syscache.h"
@@ -443,7 +442,7 @@ ExecAgg(Agg *node)
* qualifications it is ignored and the next group is fetched
*/
if (node->plan.qual != NULL)
- qual_result = ExecQual(fix_opids(node->plan.qual), econtext);
+ qual_result = ExecQual(node->plan.qual, econtext);
else
qual_result = false;
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index 26ebed1d458..ddfef9d5eb4 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/makefuncs.c,v 1.17 1999/08/21 03:48:58 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/makefuncs.c,v 1.18 1999/08/22 20:14:59 tgl Exp $
*
* NOTES
* Creator functions in POSTGRES 4.2 are generated automatically. Most of
@@ -52,9 +52,7 @@ makeVar(Index varno,
AttrNumber varattno,
Oid vartype,
int32 vartypmod,
- Index varlevelsup,
- Index varnoold,
- AttrNumber varoattno)
+ Index varlevelsup)
{
Var *var = makeNode(Var);
@@ -63,8 +61,14 @@ makeVar(Index varno,
var->vartype = vartype;
var->vartypmod = vartypmod;
var->varlevelsup = varlevelsup;
- var->varnoold = varnoold;
- var->varoattno = varoattno;
+ /*
+ * Since few if any routines ever create Var nodes with varnoold/varoattno
+ * different from varno/varattno, we don't provide separate arguments
+ * for them, but just initialize them to the given varno/varattno.
+ * This reduces code clutter and chance of error for most callers.
+ */
+ var->varnoold = varno;
+ var->varoattno = varattno;
return var;
}
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 55787062dc5..fcf462b83eb 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -18,7 +18,7 @@
* Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.44 1999/08/06 04:00:15 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.45 1999/08/22 20:14:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -431,7 +431,7 @@ compute_rel_size(RelOptInfo *rel)
int
compute_rel_width(RelOptInfo *rel)
{
- return compute_targetlist_width(get_actual_tlist(rel->targetlist));
+ return compute_targetlist_width(rel->targetlist);
}
/*
@@ -448,8 +448,7 @@ compute_targetlist_width(List *targetlist)
foreach(temp_tl, targetlist)
{
- tuple_width = tuple_width +
- compute_attribute_width(lfirst(temp_tl));
+ tuple_width += compute_attribute_width(lfirst(temp_tl));
}
return tuple_width;
}
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 41a3ff35b48..b9a982e8283 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.15 1999/08/21 03:49:01 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.16 1999/08/22 20:14:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -24,8 +24,6 @@
#include "utils/lsyscache.h"
static PathKeyItem *makePathKeyItem(Node *key, Oid sortop);
-static bool pathkeyitem_equal(PathKeyItem *a, PathKeyItem *b);
-static bool pathkeyitem_member(PathKeyItem *a, List *l);
static Var *find_indexkey_var(int indexkey, List *tlist);
static List *build_join_pathkey(List *pathkeys, List *join_rel_tlist,
List *joinclauses);
@@ -119,45 +117,6 @@ makePathKeyItem(Node *key, Oid sortop)
****************************************************************************/
/*
- * Compare two pathkey items for equality.
- *
- * This is unlike straight equal() because when the two keys are both Vars,
- * we want to apply the weaker var_equal() condition (doesn't check varnoold
- * or varoattno). But if that fails, try equal() so that we recognize
- * functional-index keys.
- */
-static bool
-pathkeyitem_equal (PathKeyItem *a, PathKeyItem *b)
-{
- Assert(a && IsA(a, PathKeyItem));
- Assert(b && IsA(b, PathKeyItem));
-
- if (a->sortop != b->sortop)
- return false;
- if (var_equal((Var *) a->key, (Var *) b->key))
- return true;
- return equal(a->key, b->key);
-}
-
-/*
- * member() test using pathkeyitem_equal
- */
-static bool
-pathkeyitem_member (PathKeyItem *a, List *l)
-{
- List *i;
-
- Assert(a && IsA(a, PathKeyItem));
-
- foreach(i, l)
- {
- if (pathkeyitem_equal(a, (PathKeyItem *) lfirst(i)))
- return true;
- }
- return false;
-}
-
-/*
* compare_pathkeys
* Compare two pathkeys to see if they are equivalent, and if not whether
* one is "better" than the other.
@@ -191,7 +150,7 @@ compare_pathkeys(List *keys1, List *keys2)
{
foreach(i, subkey1)
{
- if (! pathkeyitem_member((PathKeyItem *) lfirst(i), subkey2))
+ if (! member(lfirst(i), subkey2))
{
key1_subsetof_key2 = false;
break;
@@ -203,7 +162,7 @@ compare_pathkeys(List *keys1, List *keys2)
{
foreach(i, subkey2)
{
- if (! pathkeyitem_member((PathKeyItem *) lfirst(i), subkey1))
+ if (! member(lfirst(i), subkey1))
{
key2_subsetof_key1 = false;
break;
@@ -336,8 +295,8 @@ build_index_pathkeys(Query *root, RelOptInfo *rel, RelOptInfo *index)
int32 type_mod = get_atttypmod(reloid, varattno);
funcargs = lappend(funcargs,
- makeVar(relid, varattno, vartypeid, type_mod,
- 0, relid, varattno));
+ makeVar(relid, varattno, vartypeid,
+ type_mod, 0));
indexkeys++;
}
@@ -483,13 +442,13 @@ build_join_pathkey(List *pathkey,
foreach(i, pathkey)
{
PathKeyItem *key = (PathKeyItem *) lfirst(i);
- Expr *tlist_key;
+ Node *tlist_key;
Assert(key && IsA(key, PathKeyItem));
- tlist_key = matching_tlist_var((Var *) key->key, join_rel_tlist);
+ tlist_key = matching_tlist_expr(key->key, join_rel_tlist);
if (tlist_key)
- new_pathkey = lcons(makePathKeyItem((Node *) tlist_key,
+ new_pathkey = lcons(makePathKeyItem(tlist_key,
key->sortop),
new_pathkey);
@@ -498,17 +457,17 @@ build_join_pathkey(List *pathkey,
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(j);
Expr *joinclause = restrictinfo->clause;
/* We assume the clause is a binary opclause... */
- Var *l = get_leftop(joinclause);
- Var *r = get_rightop(joinclause);
- Var *other_var = NULL;
+ Node *l = (Node *) get_leftop(joinclause);
+ Node *r = (Node *) get_rightop(joinclause);
+ Node *other_var = NULL;
Oid other_sortop = InvalidOid;
- if (var_equal((Var *) key->key, l))
+ if (equal(key->key, l))
{
other_var = r;
other_sortop = restrictinfo->right_sortop;
}
- else if (var_equal((Var *) key->key, r))
+ else if (equal(key->key, r))
{
other_var = l;
other_sortop = restrictinfo->left_sortop;
@@ -516,9 +475,9 @@ build_join_pathkey(List *pathkey,
if (other_var && other_sortop)
{
- tlist_key = matching_tlist_var(other_var, join_rel_tlist);
+ tlist_key = matching_tlist_expr(other_var, join_rel_tlist);
if (tlist_key)
- new_pathkey = lcons(makePathKeyItem((Node *) tlist_key,
+ new_pathkey = lcons(makePathKeyItem(tlist_key,
other_sortop),
new_pathkey);
}
@@ -638,20 +597,17 @@ find_mergeclauses_for_pathkeys(List *pathkeys, List *restrictinfos)
foreach(j, pathkey)
{
PathKeyItem *keyitem = lfirst(j);
- Var *keyvar = (Var *) keyitem->key;
+ Node *key = keyitem->key;
List *k;
- if (! IsA(keyvar, Var))
- continue; /* for now, only Vars can be mergejoined */
-
foreach(k, restrictinfos)
{
RestrictInfo *restrictinfo = lfirst(k);
Assert(restrictinfo->mergejoinoperator != InvalidOid);
- if ((var_equal(keyvar, get_leftop(restrictinfo->clause)) ||
- var_equal(keyvar, get_rightop(restrictinfo->clause))) &&
+ if ((equal(key, get_leftop(restrictinfo->clause)) ||
+ equal(key, get_rightop(restrictinfo->clause))) &&
! member(restrictinfo, mergeclauses))
{
matched_restrictinfo = restrictinfo;
@@ -705,23 +661,24 @@ make_pathkeys_for_mergeclauses(List *mergeclauses, List *tlist)
foreach(i, mergeclauses)
{
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i);
- Var *key;
+ Node *key;
Oid sortop;
+ Assert(restrictinfo->mergejoinoperator != InvalidOid);
+
/*
* Find the key and sortop needed for this mergeclause.
*
* We can use either side of the mergeclause, since we haven't yet
* committed to which side will be inner.
*/
- Assert(restrictinfo->mergejoinoperator != InvalidOid);
- key = (Var *) matching_tlist_var(get_leftop(restrictinfo->clause),
- tlist);
+ key = matching_tlist_expr((Node *) get_leftop(restrictinfo->clause),
+ tlist);
sortop = restrictinfo->left_sortop;
if (! key)
{
- key = (Var *) matching_tlist_var(get_rightop(restrictinfo->clause),
- tlist);
+ key = matching_tlist_expr((Node *) get_rightop(restrictinfo->clause),
+ tlist);
sortop = restrictinfo->right_sortop;
}
if (! key)
@@ -730,7 +687,7 @@ make_pathkeys_for_mergeclauses(List *mergeclauses, List *tlist)
* Add a pathkey sublist for this sort item
*/
pathkeys = lappend(pathkeys,
- lcons(makePathKeyItem((Node *) key, sortop),
+ lcons(makePathKeyItem(key, sortop),
NIL));
}
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index d1f756fc7c1..ae1e2d3266b 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -1,13 +1,15 @@
/*-------------------------------------------------------------------------
*
* createplan.c
- * Routines to create the desired plan for processing a query
+ * Routines to create the desired plan for processing a query.
+ * Planning is complete, we just need to convert the selected
+ * Path into a Plan.
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.74 1999/08/21 03:49:02 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.75 1999/08/22 20:14:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -70,13 +72,13 @@ static void copy_costsize(Plan *dest, Plan *src);
* every pathnode found:
* (1) Create a corresponding plan node containing appropriate id,
* target list, and qualification information.
- * (2) Modify ALL clauses so that attributes are referenced using
- * relative values.
- * (3) Target lists are not modified, but will be in another routine.
+ * (2) Modify qual clauses of join nodes so that subplan attributes are
+ * referenced using relative values.
+ * (3) Target lists are not modified, but will be in setrefs.c.
*
* best_path is the best access path
*
- * Returns the optimal(?) access plan.
+ * Returns the access plan.
*/
Plan *
create_plan(Path *best_path)
@@ -90,7 +92,7 @@ create_plan(Path *best_path)
int tuples;
parent_rel = best_path->parent;
- tlist = get_actual_tlist(parent_rel->targetlist);
+ tlist = parent_rel->targetlist;
size = parent_rel->size;
width = parent_rel->width;
pages = parent_rel->pages;
@@ -152,9 +154,8 @@ create_scan_node(Path *best_path, List *tlist)
/*
* Extract the relevant restriction clauses from the parent relation;
* the executor must apply all these restrictions during the scan.
- * Fix regproc ids in the restriction clauses.
*/
- scan_clauses = fix_opids(get_actual_clauses(best_path->parent->restrictinfo));
+ scan_clauses = get_actual_clauses(best_path->parent->restrictinfo);
switch (best_path->pathtype)
{
@@ -235,7 +236,6 @@ create_join_node(JoinPath *best_path, List *tlist)
inner_tlist);
break;
default:
- /* do nothing */
elog(ERROR, "create_join_node: unknown node type",
best_path->path.pathtype);
}
@@ -249,8 +249,7 @@ create_join_node(JoinPath *best_path, List *tlist)
if (get_loc_restrictinfo(best_path) != NIL)
set_qpqual((Plan) retval,
nconc(get_qpqual((Plan) retval),
- fix_opids(get_actual_clauses
- (get_loc_restrictinfo(best_path)))));
+ get_actual_clauses(get_loc_restrictinfo(best_path))));
#endif
return retval;
@@ -282,8 +281,7 @@ create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses)
scan_node = make_seqscan(tlist,
scan_clauses,
- scan_relid,
- (Plan *) NULL);
+ scan_relid);
scan_node->plan.cost = best_path->path_cost;
@@ -386,17 +384,10 @@ create_indexscan_node(IndexPath *best_path,
qpqual = NIL;
/* The executor needs a copy with the indexkey on the left of each clause
- * and with index attrs substituted for table ones.
+ * and with index attr numbers substituted for table ones.
*/
fixed_indxqual = fix_indxqual_references(indxqual, best_path);
- /*
- * Fix opids in the completed indxquals.
- * XXX this ought to only happen at final exit from the planner...
- */
- indxqual = fix_opids(indxqual);
- fixed_indxqual = fix_opids(fixed_indxqual);
-
scan_node = make_indexscan(tlist,
qpqual,
lfirsti(best_path->path.parent->relids),
@@ -413,6 +404,21 @@ create_indexscan_node(IndexPath *best_path,
*
* JOIN METHODS
*
+ * A general note about join_references() processing in these routines:
+ * once we have changed a Var node to refer to a subplan output rather than
+ * the original relation, it is no longer equal() to an unmodified Var node
+ * for the same var. So, we cannot easily compare reference-adjusted qual
+ * clauses to clauses that have not been adjusted. Fortunately, that
+ * doesn't seem to be necessary; all the decisions are made before we do
+ * the reference adjustments.
+ *
+ * A cleaner solution would be to not call join_references() here at all,
+ * but leave it for setrefs.c to do at the end of plan tree construction.
+ * But that would make switch_outer() much more complicated, and some care
+ * would be needed to get setrefs.c to do the right thing with nestloop
+ * inner indexscan quals. So, we do subplan reference adjustment here for
+ * quals of join nodes (and *only* for quals of join nodes).
+ *
*****************************************************************************/
static NestLoop *
@@ -432,7 +438,7 @@ create_nestloop_node(NestPath *best_path,
* An index is being used to reduce the number of tuples scanned
* in the inner relation. If there are join clauses being used
* with the index, we must update their outer-rel var nodes to
- * refer to the outer relation.
+ * refer to the outer side of the join.
*
* We can also remove those join clauses from the list of clauses
* that have to be checked as qpquals at the join node, but only
@@ -442,7 +448,12 @@ create_nestloop_node(NestPath *best_path,
* Note: if the index is lossy, the same clauses may also be getting
* checked as qpquals in the indexscan. We can still remove them
* from the nestloop's qpquals, but we gotta update the outer-rel
- * vars in the indexscan's qpquals too...
+ * vars in the indexscan's qpquals too.
+ *
+ * Note: we can safely do set_difference() against my clauses and
+ * join_references() because the innerscan is a primitive plan,
+ * and therefore has not itself done join_references renumbering
+ * of the vars in its quals.
*/
IndexScan *innerscan = (IndexScan *) inner_node;
List *indxqualorig = innerscan->indxqualorig;
@@ -450,6 +461,8 @@ create_nestloop_node(NestPath *best_path,
/* No work needed if indxqual refers only to its own relation... */
if (NumRelids((Node *) indxqualorig) > 1)
{
+ Index innerrel = innerscan->scan.scanrelid;
+
/* Remove redundant tests from my clauses, if possible.
* Note we must compare against indxqualorig not the "fixed"
* indxqual (which has index attnos instead of relation attnos,
@@ -461,20 +474,28 @@ create_nestloop_node(NestPath *best_path,
/* only refs to outer vars get changed in the inner indexqual */
innerscan->indxqualorig = join_references(indxqualorig,
outer_tlist,
- NIL);
+ NIL,
+ innerrel);
innerscan->indxqual = join_references(innerscan->indxqual,
outer_tlist,
- NIL);
+ NIL,
+ innerrel);
/* fix the inner qpqual too, if it has join clauses */
if (NumRelids((Node *) inner_node->qual) > 1)
inner_node->qual = join_references(inner_node->qual,
outer_tlist,
- NIL);
+ NIL,
+ innerrel);
}
}
else if (IsA_Join(inner_node))
{
- /* Materialize the inner join for speed reasons */
+ /*
+ * Materialize the inner join for speed reasons.
+ *
+ * XXX It is probably *not* always fastest to materialize an inner
+ * join --- how can we estimate whether this is a good thing to do?
+ */
inner_node = (Plan *) make_noname(inner_tlist,
NIL,
inner_node);
@@ -483,7 +504,8 @@ create_nestloop_node(NestPath *best_path,
join_node = make_nestloop(tlist,
join_references(clauses,
outer_tlist,
- inner_tlist),
+ inner_tlist,
+ (Index) 0),
outer_node,
inner_node);
@@ -513,7 +535,8 @@ create_mergejoin_node(MergePath *best_path,
qpqual = join_references(set_difference(clauses,
best_path->path_mergeclauses),
outer_tlist,
- inner_tlist);
+ inner_tlist,
+ (Index) 0);
/*
* Now set the references in the mergeclauses and rearrange them so
@@ -521,7 +544,8 @@ create_mergejoin_node(MergePath *best_path,
*/
mergeclauses = switch_outer(join_references(best_path->path_mergeclauses,
outer_tlist,
- inner_tlist));
+ inner_tlist,
+ (Index) 0));
/*
* Create explicit sort nodes for the outer and inner join paths if
@@ -578,7 +602,8 @@ create_hashjoin_node(HashPath *best_path,
qpqual = join_references(set_difference(clauses,
best_path->path_hashclauses),
outer_tlist,
- inner_tlist);
+ inner_tlist,
+ (Index) 0);
/*
* Now set the references in the hashclauses and rearrange them so
@@ -586,7 +611,8 @@ create_hashjoin_node(HashPath *best_path,
*/
hashclauses = switch_outer(join_references(best_path->path_hashclauses,
outer_tlist,
- inner_tlist));
+ inner_tlist,
+ (Index) 0));
/* Now the righthand op of the sole hashclause is the inner hash key. */
innerhashkey = get_rightop(lfirst(hashclauses));
@@ -839,7 +865,7 @@ set_tlist_sort_info(List *tlist, List *pathkeys)
{
pathkey = lfirst(j);
Assert(IsA(pathkey, PathKeyItem));
- resdom = tlist_member((Var *) pathkey->key, tlist);
+ resdom = tlist_member(pathkey->key, tlist);
if (resdom)
break;
}
@@ -939,17 +965,16 @@ make_noname(List *tlist,
SeqScan *
make_seqscan(List *qptlist,
List *qpqual,
- Index scanrelid,
- Plan *lefttree)
+ Index scanrelid)
{
SeqScan *node = makeNode(SeqScan);
Plan *plan = &node->plan;
- copy_costsize(plan, lefttree);
+ copy_costsize(plan, NULL);
plan->state = (EState *) NULL;
plan->targetlist = qptlist;
plan->qual = qpqual;
- plan->lefttree = lefttree;
+ plan->lefttree = NULL;
plan->righttree = NULL;
node->scanrelid = scanrelid;
node->scanstate = (CommonScanState *) NULL;
@@ -1158,9 +1183,7 @@ make_group(List *tlist,
}
/*
- * A unique node always has a SORT node in the lefttree.
- *
- * the uniqueAttr argument must be a null-terminated string,
+ * The uniqueAttr argument must be a null-terminated string,
* either the name of the attribute to select unique on
* or "*"
*/
@@ -1186,6 +1209,29 @@ make_unique(List *tlist, Plan *lefttree, char *uniqueAttr)
return node;
}
+Result *
+make_result(List *tlist,
+ Node *resconstantqual,
+ Plan *subplan)
+{
+ Result *node = makeNode(Result);
+ Plan *plan = &node->plan;
+
+#ifdef NOT_USED
+ tlist = generate_fjoin(tlist);
+#endif
+ copy_costsize(plan, subplan);
+ plan->state = (EState *) NULL;
+ plan->targetlist = tlist;
+ plan->qual = NIL;
+ plan->lefttree = subplan;
+ plan->righttree = NULL;
+ node->resconstantqual = resconstantqual;
+ node->resstate = NULL;
+
+ return node;
+}
+
#ifdef NOT_USED
List *
generate_fjoin(List *tlist)
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index db97c732070..a89c40b9435 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.37 1999/08/16 02:17:54 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.38 1999/08/22 20:14:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -104,8 +104,7 @@ add_missing_vars_to_tlist(Query *root, List *tlist)
/* add it to base_rel_list */
rel = get_base_rel(root, varno);
/* give it a dummy tlist entry for its OID */
- var = makeVar(varno, ObjectIdAttributeNumber,
- OIDOID, -1, 0, varno, ObjectIdAttributeNumber);
+ var = makeVar(varno, ObjectIdAttributeNumber, OIDOID, -1, 0);
add_var_to_tlist(rel, var);
}
pfree(relids);
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index f6f62abfe08..802e5970416 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.41 1999/08/21 03:49:03 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.42 1999/08/22 20:14:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -27,7 +27,6 @@
static Plan *subplanner(Query *root, List *flat_tlist, List *qual);
-static Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
/*
* query_planner
@@ -84,11 +83,6 @@ query_planner(Query *root,
* topmost result node.
*/
qual = pull_constant_clauses(qual, &constant_qual);
- /*
- * The opids for the variable qualifications will be fixed later, but
- * someone seems to think that the constant quals need to be fixed here.
- */
- fix_opids(constant_qual);
/*
* Create a target list that consists solely of (resdom var) target
@@ -124,8 +118,7 @@ query_planner(Query *root,
{
SeqScan *scan = make_seqscan(tlist,
NIL,
- root->resultRelation,
- (Plan *) NULL);
+ root->resultRelation);
if (constant_qual != NULL)
return ((Plan *) make_result(tlist,
@@ -141,14 +134,10 @@ query_planner(Query *root,
}
/*
- * Find the subplan (access path) and destructively modify the target
- * list of the newly created subplan to contain the appropriate join
- * references.
+ * Choose the best access path and build a plan for it.
*/
subplan = subplanner(root, level_tlist, qual);
- set_tlist_references(subplan);
-
/*
* Build a result node linking the plan if we have constant quals
*/
@@ -158,33 +147,24 @@ query_planner(Query *root,
(Node *) constant_qual,
subplan);
- /*
- * Fix all varno's of the Result's node target list.
- */
- set_tlist_references(subplan);
-
root->query_pathkeys = NIL; /* result is unordered, no? */
return subplan;
}
/*
- * fix up the flattened target list of the plan root node so that
- * expressions are evaluated. this forces expression evaluations that
- * may involve expensive function calls to be delayed to the very last
- * stage of query execution. this could be bad. but it is joey's
- * responsibility to optimally push these expressions down the plan
- * tree. -- Wei
+ * Replace the toplevel plan node's flattened target list with the
+ * targetlist given by my caller, so that expressions are evaluated.
*
- * Note: formerly there was a test here to skip the unflatten call if
- * we expected union_planner to insert a Group or Agg node above our
- * result. However, now union_planner tells us exactly what it wants
- * returned, and we just do it. Much cleaner.
+ * This implies that all expression evaluations are done at the root
+ * of the plan tree. Once upon a time there was code to try to push
+ * expensive function calls down to lower plan nodes, but that's dead
+ * code and has been for a long time...
*/
else
{
- subplan->targetlist = unflatten_tlist(tlist,
- subplan->targetlist);
+ subplan->targetlist = tlist;
+
return subplan;
}
@@ -330,36 +310,11 @@ subplanner(Query *root,
/* Nothing for it but to sort the cheapestpath...
*
- * we indicate we failed to sort the plan, and let the caller
- * stick the appropriate sortplan on top.
+ * We indicate we failed to sort the plan, and let the caller
+ * stick the appropriate sort node on top. union_planner has to be
+ * able to add a sort node anyway, so no need for extra code here.
*/
root->query_pathkeys = NIL; /* sorry, it ain't sorted */
return create_plan(final_rel->cheapestpath);
}
-
-/*****************************************************************************
- *
- *****************************************************************************/
-
-static Result *
-make_result(List *tlist,
- Node *resconstantqual,
- Plan *subplan)
-{
- Result *node = makeNode(Result);
- Plan *plan = &node->plan;
-
-#ifdef NOT_USED
- tlist = generate_fjoin(tlist);
-#endif
- plan->cost = (subplan ? subplan->cost : 0);
- plan->state = (EState *) NULL;
- plan->targetlist = tlist;
- plan->lefttree = subplan;
- plan->righttree = NULL;
- node->resconstantqual = resconstantqual;
- node->resstate = NULL;
-
- return node;
-}
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index b328c40f226..0003262a454 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.63 1999/08/21 03:49:03 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.64 1999/08/22 20:14:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -70,6 +70,8 @@ planner(Query *parse)
}
result_plan->nParamExec = length(PlannerParamVar);
+ set_plan_references(result_plan);
+
return result_plan;
}
@@ -173,8 +175,7 @@ union_planner(Query *parse)
0,
true);
- var = makeVar(rowmark->rti, -1, TIDOID,
- -1, 0, rowmark->rti, -1);
+ var = makeVar(rowmark->rti, -1, TIDOID, -1, 0);
ctid = makeTargetEntry(resdom, (Node *) var);
tlist = lappend(tlist, ctid);
@@ -279,6 +280,8 @@ union_planner(Query *parse)
*/
if (parse->havingQual)
{
+ List *ql;
+
/* convert the havingQual to conjunctive normal form (cnf) */
parse->havingQual = (Node *) cnfify((Expr *) parse->havingQual, true);
@@ -295,13 +298,21 @@ union_planner(Query *parse)
* Check for ungrouped variables passed to subplans. (Probably
* this should be done for the targetlist as well???)
*/
- check_having_for_ungrouped_vars(parse->havingQual,
- parse->groupClause,
- parse->targetList);
+ if (check_subplans_for_ungrouped_vars(parse->havingQual,
+ parse->groupClause,
+ parse->targetList))
+ elog(ERROR, "Sub-SELECT in HAVING clause must use only GROUPed attributes from outer SELECT");
}
- /* Calculate the opfids from the opnos */
- parse->havingQual = (Node *) fix_opids((List *) parse->havingQual);
+ /*
+ * Require an aggregate function to appear in each clause of the
+ * havingQual (else it could have been done as a WHERE constraint).
+ */
+ foreach(ql, (List *) parse->havingQual)
+ {
+ if (pull_agg_clause(lfirst(ql)) == NIL)
+ elog(ERROR, "SELECT/HAVING requires aggregates to be valid");
+ }
}
/*
@@ -315,13 +326,6 @@ union_planner(Query *parse)
result_plan->qual = (List *) parse->havingQual;
/*
- * Update vars to refer to subplan result tuples, and
- * make sure there is an Aggref in every HAVING clause.
- */
- if (!set_agg_tlist_references((Agg *) result_plan))
- elog(ERROR, "SELECT/HAVING requires aggregates to be valid");
-
- /*
* Assume result is not ordered suitably for ORDER BY.
* XXX it might be; improve this!
*/
@@ -474,7 +478,6 @@ make_groupplan(List *group_tlist,
Plan *subplan)
{
int numCols = length(groupClause);
- Group *grpplan;
if (! is_sorted)
{
@@ -515,21 +518,8 @@ make_groupplan(List *group_tlist,
keyno);
}
- /*
- * Fix variables in tlist (should be done somewhere else?)
- */
- group_tlist = copyObject(group_tlist); /* necessary?? */
- replace_tlist_with_subplan_refs(group_tlist,
- (Index) 0,
- subplan->targetlist);
-
- /*
- * Make the Group node
- */
- grpplan = make_group(group_tlist, tuplePerGroup, numCols,
- grpColIdx, subplan);
-
- return (Plan *) grpplan;
+ return (Plan *) make_group(group_tlist, tuplePerGroup, numCols,
+ grpColIdx, subplan);
}
/*
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 1492df8b030..a983aa32143 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -1,13 +1,14 @@
/*-------------------------------------------------------------------------
*
* setrefs.c
- * Routines to change varno/attno entries to contain references
+ * Post-processing of a completed plan tree: fix references to subplan
+ * vars, and compute regproc values for operators
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.56 1999/08/21 03:49:03 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.57 1999/08/22 20:14:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -25,36 +26,24 @@
typedef struct {
List *outer_tlist;
List *inner_tlist;
-} replace_joinvar_refs_context;
+ Index acceptable_rel;
+} join_references_context;
typedef struct {
Index subvarno;
List *subplanTargetList;
} replace_vars_with_subplan_refs_context;
-typedef struct {
- List *groupClause;
- List *targetList;
-} check_having_for_ungrouped_vars_context;
-
-static void set_join_tlist_references(Join *join);
-static void set_nonamescan_tlist_references(SeqScan *nonamescan);
-static void set_noname_tlist_references(Noname *noname);
-static Node *replace_joinvar_refs(Node *clause,
- List *outer_tlist,
- List *inner_tlist);
-static Node *replace_joinvar_refs_mutator(Node *node,
- replace_joinvar_refs_context *context);
-static List *tlist_noname_references(Oid nonameid, List *tlist);
-static void set_result_tlist_references(Result *resultNode);
-static void replace_vars_with_subplan_refs(Node *clause,
- Index subvarno,
- List *subplanTargetList);
-static bool replace_vars_with_subplan_refs_walker(Node *node,
+static void set_join_references(Join *join);
+static void set_uppernode_references(Plan *plan, Index subvarno);
+static Node *join_references_mutator(Node *node,
+ join_references_context *context);
+static Node *replace_vars_with_subplan_refs(Node *node,
+ Index subvarno,
+ List *subplanTargetList);
+static Node *replace_vars_with_subplan_refs_mutator(Node *node,
replace_vars_with_subplan_refs_context *context);
-static bool pull_agg_clause_walker(Node *node, List **listptr);
-static bool check_having_for_ungrouped_vars_walker(Node *node,
- check_having_for_ungrouped_vars_context *context);
+static bool fix_opids_walker(Node *node, void *context);
/*****************************************************************************
*
@@ -63,340 +52,298 @@ static bool check_having_for_ungrouped_vars_walker(Node *node,
*****************************************************************************/
/*
- * set_tlist_references
- * Modifies the target list of nodes in a plan to reference target lists
- * at lower levels.
+ * set_plan_references
+ * This is the final processing pass of the planner/optimizer. The plan
+ * tree is complete; we just have to adjust some representational details
+ * for the convenience of the executor. We update Vars in upper plan nodes
+ * to refer to the outputs of their subplans, and we compute regproc OIDs
+ * for operators (ie, we look up the function that implements each op).
*
- * 'plan' is the plan whose target list and children's target lists will
- * be modified
+ * set_plan_references recursively traverses the whole plan tree.
*
* Returns nothing of interest, but modifies internal fields of nodes.
- *
*/
void
-set_tlist_references(Plan *plan)
+set_plan_references(Plan *plan)
{
+ List *pl;
+
if (plan == NULL)
return;
- if (IsA_Join(plan))
- set_join_tlist_references((Join *) plan);
- else if (IsA(plan, SeqScan) && plan->lefttree &&
- IsA_Noname(plan->lefttree))
- set_nonamescan_tlist_references((SeqScan *) plan);
- else if (IsA_Noname(plan))
- set_noname_tlist_references((Noname *) plan);
- else if (IsA(plan, Result))
- set_result_tlist_references((Result *) plan);
- else if (IsA(plan, Hash))
- set_tlist_references(plan->lefttree);
-}
+ /*
+ * Plan-type-specific fixes
+ */
+ switch (nodeTag(plan))
+ {
+ case T_SeqScan:
+ /* nothing special */
+ break;
+ case T_IndexScan:
+ fix_opids((Node *) ((IndexScan *) plan)->indxqual);
+ fix_opids((Node *) ((IndexScan *) plan)->indxqualorig);
+ break;
+ case T_NestLoop:
+ set_join_references((Join *) plan);
+ break;
+ case T_MergeJoin:
+ set_join_references((Join *) plan);
+ fix_opids((Node *) ((MergeJoin *) plan)->mergeclauses);
+ break;
+ case T_HashJoin:
+ set_join_references((Join *) plan);
+ fix_opids((Node *) ((HashJoin *) plan)->hashclauses);
+ break;
+ case T_Material:
+ case T_Sort:
+ case T_Unique:
+ case T_Hash:
+ /* These plan types don't actually bother to evaluate their
+ * targetlists or quals (because they just return their
+ * unmodified input tuples). The optimizer is lazy about
+ * creating really valid targetlists for them. Best to
+ * just leave the targetlist alone.
+ */
+ break;
+ case T_Agg:
+ case T_Group:
+ set_uppernode_references(plan, (Index) 0);
+ break;
+ case T_Result:
+ /* XXX why does Result use a different subvarno? */
+ set_uppernode_references(plan, (Index) OUTER);
+ fix_opids(((Result *) plan)->resconstantqual);
+ break;
+ case T_Append:
+ foreach(pl, ((Append *) plan)->appendplans)
+ {
+ set_plan_references((Plan *) lfirst(pl));
+ }
+ break;
+ default:
+ elog(ERROR, "set_plan_references: unknown plan type %d",
+ nodeTag(plan));
+ break;
+ }
-/*
- * set_join_tlist_references
- * Modifies the target list of a join node by setting the varnos and
- * varattnos to reference the target list of the outer and inner join
- * relations.
- *
- * Creates a target list for a join node to contain references by setting
- * varno values to OUTER or INNER and setting attno values to the
- * result domain number of either the corresponding outer or inner join
- * tuple.
- *
- * 'join' is a join plan node
- *
- * Returns nothing of interest, but modifies internal fields of nodes.
- *
- */
-static void
-set_join_tlist_references(Join *join)
-{
- Plan *outer = join->lefttree;
- Plan *inner = join->righttree;
- List *outer_tlist = ((outer == NULL) ? NIL : outer->targetlist);
- List *inner_tlist = ((inner == NULL) ? NIL : inner->targetlist);
- List *new_join_targetlist = NIL;
- List *qptlist = join->targetlist;
- List *entry;
+ /*
+ * For all plan types, fix operators in targetlist and qual expressions
+ */
+ fix_opids((Node *) plan->targetlist);
+ fix_opids((Node *) plan->qual);
- foreach(entry, qptlist)
+ /*
+ * Now recurse into subplans, if any
+ *
+ * NOTE: it is essential that we recurse into subplans AFTER we set
+ * subplan references in this plan's tlist and quals. If we did the
+ * reference-adjustments bottom-up, then we would fail to match this
+ * plan's var nodes against the already-modified nodes of the subplans.
+ */
+ set_plan_references(plan->lefttree);
+ set_plan_references(plan->righttree);
+ foreach(pl, plan->initPlan)
{
- TargetEntry *xtl = (TargetEntry *) lfirst(entry);
- Node *joinexpr = replace_joinvar_refs(xtl->expr,
- outer_tlist,
- inner_tlist);
+ SubPlan *sp = (SubPlan *) lfirst(pl);
- new_join_targetlist = lappend(new_join_targetlist,
- makeTargetEntry(xtl->resdom, joinexpr));
+ Assert(IsA(sp, SubPlan));
+ set_plan_references(sp->plan);
}
- join->targetlist = new_join_targetlist;
+ foreach(pl, plan->subPlan)
+ {
+ SubPlan *sp = (SubPlan *) lfirst(pl);
- set_tlist_references(outer);
- set_tlist_references(inner);
+ Assert(IsA(sp, SubPlan));
+ set_plan_references(sp->plan);
+ }
}
/*
- * set_nonamescan_tlist_references
- * Modifies the target list of a node that scans a noname relation (i.e., a
- * sort or materialize node) so that the varnos refer to the child noname.
+ * set_join_references
+ * Modifies the target list of a join node to reference its subplans,
+ * by setting the varnos to OUTER or INNER and setting attno values to the
+ * result domain number of either the corresponding outer or inner join
+ * tuple item.
*
- * 'nonamescan' is a seqscan node
+ * Note: this same transformation has already been applied to the quals
+ * of the join by createplan.c. It's a little odd to do it here for the
+ * targetlist and there for the quals, but it's easier that way. (Look
+ * at switch_outer() and the handling of nestloop inner indexscans to
+ * see why.)
*
- * Returns nothing of interest, but modifies internal fields of nodes.
+ * Because the quals are reference-adjusted sooner, we cannot do equal()
+ * comparisons between qual and tlist var nodes during the time between
+ * creation of a plan node by createplan.c and its fixing by this module.
+ * Fortunately, there doesn't seem to be any need to do that.
*
+ * 'join' is a join plan node
*/
static void
-set_nonamescan_tlist_references(SeqScan *nonamescan)
+set_join_references(Join *join)
{
- Noname *noname = (Noname *) nonamescan->plan.lefttree;
+ Plan *outer = join->lefttree;
+ Plan *inner = join->righttree;
+ List *outer_tlist = ((outer == NULL) ? NIL : outer->targetlist);
+ List *inner_tlist = ((inner == NULL) ? NIL : inner->targetlist);
- nonamescan->plan.targetlist = tlist_noname_references(noname->nonameid,
- nonamescan->plan.targetlist);
- /* since we know child is a Noname, skip recursion through
- * set_tlist_references() and just get the job done
- */
- set_noname_tlist_references(noname);
+ join->targetlist = join_references(join->targetlist,
+ outer_tlist,
+ inner_tlist,
+ (Index) 0);
}
/*
- * set_noname_tlist_references
- * The noname's vars are made consistent with (actually, identical to) the
- * modified version of the target list of the node from which noname node
- * receives its tuples.
- *
- * 'noname' is a noname (e.g., sort, materialize) plan node
- *
- * Returns nothing of interest, but modifies internal fields of nodes.
+ * set_uppernode_references
+ * Update the targetlist and quals of an upper-level plan node
+ * to refer to the tuples returned by its lefttree subplan.
*
+ * This is used for single-input plan types like Agg, Group, Result.
*/
static void
-set_noname_tlist_references(Noname *noname)
+set_uppernode_references(Plan *plan, Index subvarno)
{
- Plan *source = noname->plan.lefttree;
+ Plan *subplan = plan->lefttree;
+ List *subplanTargetList;
- if (source != NULL)
- {
- set_tlist_references(source);
- noname->plan.targetlist = copy_vars(noname->plan.targetlist,
- source->targetlist);
- }
+ if (subplan != NULL)
+ subplanTargetList = subplan->targetlist;
else
- elog(ERROR, "calling set_noname_tlist_references with empty lefttree");
+ subplanTargetList = NIL;
+
+ plan->targetlist = (List *)
+ replace_vars_with_subplan_refs((Node *) plan->targetlist,
+ subvarno,
+ subplanTargetList);
+
+ plan->qual = (List *)
+ replace_vars_with_subplan_refs((Node *) plan->qual,
+ subvarno,
+ subplanTargetList);
}
/*
* join_references
- * Creates a new set of join 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.
- * This is just an external interface for replace_joinvar_refs.
- *
- * 'clauses' is the list of join clauses
+ * 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.
+ *
+ * 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;
+ * and an indexscan being used on the inner side of a nestloop join.
+ * In the latter case we want to replace the outer-relation Vars by OUTER
+ * references, but not touch the Vars of the inner relation.
+ *
+ * For a normal join, acceptable_rel should be zero so that any failure to
+ * match a Var will be reported as an error. For the indexscan case,
+ * pass inner_tlist = NIL and acceptable_rel = the ID of the inner relation.
+ *
+ * 'clauses' is the targetlist or list of join clauses
* 'outer_tlist' is the target list of the outer join relation
- * 'inner_tlist' is the target list of the inner join relation
+ * '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.
*
- * Returns the new join clauses. The original clause structure is
+ * Returns the new expression tree. The original clause structure is
* not modified.
- *
*/
List *
join_references(List *clauses,
List *outer_tlist,
- List *inner_tlist)
+ List *inner_tlist,
+ Index acceptable_rel)
{
- return (List *) replace_joinvar_refs((Node *) clauses,
- outer_tlist,
- inner_tlist);
-}
-
-/*
- * replace_joinvar_refs
- *
- * Replaces all variables within a join clause with a new var node
- * whose varno/varattno fields contain a reference to a target list
- * element from either the outer or inner join relation.
- *
- * Returns a suitably modified copy of the join clause;
- * the original is not modified (and must not be!)
- *
- * Side effect: also runs fix_opids on the modified join clause.
- * Really ought to make that happen in a uniform, consistent place...
- *
- * 'clause' is the join clause
- * 'outer_tlist' is the target list of the outer join relation
- * 'inner_tlist' is the target list of the inner join relation
- */
-static Node *
-replace_joinvar_refs(Node *clause,
- List *outer_tlist,
- List *inner_tlist)
-{
- replace_joinvar_refs_context context;
+ join_references_context context;
context.outer_tlist = outer_tlist;
context.inner_tlist = inner_tlist;
- return (Node *) fix_opids((List *)
- replace_joinvar_refs_mutator(clause, &context));
+ context.acceptable_rel = acceptable_rel;
+ return (List *) join_references_mutator((Node *) clauses, &context);
}
static Node *
-replace_joinvar_refs_mutator(Node *node,
- replace_joinvar_refs_context *context)
+join_references_mutator(Node *node,
+ join_references_context *context)
{
if (node == NULL)
return NULL;
if (IsA(node, Var))
{
Var *var = (Var *) node;
- Resdom *resdom = tlist_member(var, context->outer_tlist);
-
- if (resdom != NULL && IsA(resdom, Resdom))
- return (Node *) makeVar(OUTER,
- resdom->resno,
- var->vartype,
- var->vartypmod,
- 0,
- var->varnoold,
- var->varoattno);
- resdom = tlist_member(var, context->inner_tlist);
- if (resdom != NULL && IsA(resdom, Resdom))
- return (Node *) makeVar(INNER,
- resdom->resno,
- var->vartype,
- var->vartypmod,
- 0,
- var->varnoold,
- var->varoattno);
- /* Var not in either tlist, return an unmodified copy. */
- return copyObject(node);
- }
- return expression_tree_mutator(node,
- replace_joinvar_refs_mutator,
- (void *) context);
-}
+ Var *newvar = (Var *) copyObject(var);
+ Resdom *resdom;
-/*
- * tlist_noname_references
- * Creates a new target list for a node that scans a noname relation,
- * setting the varnos to the id of the noname relation and setting varids
- * if necessary (varids are only needed if this is a targetlist internal
- * to the tree, in which case the targetlist entry always contains a var
- * node, so we can just copy it from the noname).
- *
- * 'nonameid' is the id of the noname relation
- * 'tlist' is the target list to be modified
- *
- * Returns new target list
- *
- */
-static List *
-tlist_noname_references(Oid nonameid,
- List *tlist)
-{
- List *t_list = NIL;
- List *entry;
-
- foreach(entry, tlist)
- {
- TargetEntry *xtl = lfirst(entry);
- AttrNumber oattno;
- TargetEntry *noname;
-
- if (IsA(get_expr(xtl), Var))
- oattno = ((Var *) xtl->expr)->varoattno;
- else
- oattno = 0;
-
- noname = makeTargetEntry(xtl->resdom,
- (Node *) makeVar(nonameid,
- xtl->resdom->resno,
- xtl->resdom->restype,
- xtl->resdom->restypmod,
- 0,
- nonameid,
- oattno));
-
- t_list = lappend(t_list, noname);
+ resdom = tlist_member((Node *) var, context->outer_tlist);
+ if (resdom)
+ {
+ newvar->varno = OUTER;
+ newvar->varattno = resdom->resno;
+ return (Node *) newvar;
+ }
+ resdom = tlist_member((Node *) var, context->inner_tlist);
+ if (resdom)
+ {
+ newvar->varno = INNER;
+ newvar->varattno = resdom->resno;
+ return (Node *) newvar;
+ }
+ /*
+ * Var not in either tlist --- either raise an error,
+ * or return the Var unmodified.
+ */
+ if (var->varno != context->acceptable_rel)
+ elog(ERROR, "join_references: variable not in subplan target lists");
+ return (Node *) newvar; /* copy is probably not necessary here... */
}
- return t_list;
-}
-
-/*---------------------------------------------------------
- *
- * set_result_tlist_references
- *
- * Change the target list of a Result node, so that it correctly
- * addresses the tuples returned by its left tree subplan.
- *
- * NOTE:
- * 1) we ignore the right tree! (in the current implementation
- * it is always nil)
- * 2) this routine will probably *NOT* work with nested dot
- * fields....
- */
-static void
-set_result_tlist_references(Result *resultNode)
-{
- Plan *subplan;
- List *resultTargetList;
- List *subplanTargetList;
-
- resultTargetList = ((Plan *) resultNode)->targetlist;
-
/*
- * NOTE: we only consider the left tree subplan. This is usually a seq
- * scan.
+ * expression_tree_mutator will copy SubPlan nodes if given a chance.
+ * We do not want to do that here, because subselect.c has already
+ * constructed the initPlan and subPlan lists of the current plan node
+ * and we mustn't leave those dangling (ie, pointing to different
+ * copies of the nodes than what's in the targetlist & quals...)
+ * Instead, alter the SubPlan in-place. Grotty --- is there a better way?
*/
- subplan = ((Plan *) resultNode)->lefttree;
- if (subplan != NULL)
- subplanTargetList = subplan->targetlist;
- else
- subplanTargetList = NIL;
-
- replace_tlist_with_subplan_refs(resultTargetList,
- (Index) OUTER,
- subplanTargetList);
-}
-
-/*---------------------------------------------------------
- *
- * replace_tlist_with_subplan_refs
- *
- * Applies replace_vars_with_subplan_refs() to each entry of a targetlist.
- */
-void
-replace_tlist_with_subplan_refs(List *tlist,
- Index subvarno,
- List *subplanTargetList)
-{
- List *t;
-
- foreach(t, tlist)
+ if (is_subplan(node))
{
- TargetEntry *entry = (TargetEntry *) lfirst(t);
-
- replace_vars_with_subplan_refs((Node *) get_expr(entry),
- subvarno, subplanTargetList);
+ Expr *expr = (Expr *) node;
+ SubLink *sublink = ((SubPlan *) expr->oper)->sublink;
+
+ /* transform args list (params to be passed to subplan) */
+ expr->args = (List *)
+ join_references_mutator((Node *) expr->args,
+ context);
+ /* transform sublink's oper list as well */
+ sublink->oper = (List *)
+ join_references_mutator((Node *) sublink->oper,
+ context);
+
+ return (Node *) expr;
}
+ return expression_tree_mutator(node,
+ join_references_mutator,
+ (void *) context);
}
-/*---------------------------------------------------------
- *
+/*
* replace_vars_with_subplan_refs
+ * This routine modifies an expression tree so that all Var nodes
+ * reference target nodes of a subplan. It is used to fix up
+ * target and qual expressions of non-join upper-level plan nodes.
*
- * This routine modifies (destructively!) an expression tree so that all
- * Var nodes reference target nodes of a subplan. It is used to fix up
- * target expressions of upper-level plan nodes.
+ * An error is raised if no matching var can be found in the subplan tlist
+ * --- so this routine should only be applied to nodes whose subplans'
+ * targetlists were generated via flatten_tlist() or some such method.
*
- * 'clause': the tree to be fixed
+ * 'node': the tree to be fixed (a targetlist or qual list)
* 'subvarno': varno to be assigned to all Vars
* 'subplanTargetList': target list for subplan
*
- * Afterwards, all Var nodes have varno = subvarno, varattno = resno
- * of corresponding subplan target.
+ * The resulting tree is a copy of the original in which all Var nodes have
+ * varno = subvarno, varattno = resno of corresponding subplan target.
+ * The original tree is not modified.
*/
-static void
-replace_vars_with_subplan_refs(Node *clause,
+static Node *
+replace_vars_with_subplan_refs(Node *node,
Index subvarno,
List *subplanTargetList)
{
@@ -404,182 +351,84 @@ replace_vars_with_subplan_refs(Node *clause,
context.subvarno = subvarno;
context.subplanTargetList = subplanTargetList;
- replace_vars_with_subplan_refs_walker(clause, &context);
+ return replace_vars_with_subplan_refs_mutator(node, &context);
}
-static bool
-replace_vars_with_subplan_refs_walker(Node *node,
+static Node *
+replace_vars_with_subplan_refs_mutator(Node *node,
replace_vars_with_subplan_refs_context *context)
{
if (node == NULL)
- return false;
+ return NULL;
if (IsA(node, Var))
{
Var *var = (Var *) node;
- TargetEntry *subplanVar;
+ Var *newvar = (Var *) copyObject(var);
+ Resdom *resdom;
- subplanVar = match_varid(var, context->subplanTargetList);
- if (!subplanVar)
- elog(ERROR, "replace_vars_with_subplan_refs: variable not in target list");
+ resdom = tlist_member((Node *) var, context->subplanTargetList);
+ if (!resdom)
+ elog(ERROR, "replace_vars_with_subplan_refs: variable not in subplan target list");
- /*
- * Change the varno & varattno fields of the var node.
- */
- var->varno = context->subvarno;
- var->varattno = subplanVar->resdom->resno;
- return false;
+ newvar->varno = context->subvarno;
+ newvar->varattno = resdom->resno;
+ return (Node *) newvar;
+ }
+ /*
+ * expression_tree_mutator will copy SubPlan nodes if given a chance.
+ * We do not want to do that here, because subselect.c has already
+ * constructed the initPlan and subPlan lists of the current plan node
+ * and we mustn't leave those dangling (ie, pointing to different
+ * copies of the nodes than what's in the targetlist & quals...)
+ * Instead, alter the SubPlan in-place. Grotty --- is there a better way?
+ */
+ if (is_subplan(node))
+ {
+ Expr *expr = (Expr *) node;
+ SubLink *sublink = ((SubPlan *) expr->oper)->sublink;
+
+ /* transform args list (params to be passed to subplan) */
+ expr->args = (List *)
+ replace_vars_with_subplan_refs_mutator((Node *) expr->args,
+ context);
+ /* transform sublink's oper list as well */
+ sublink->oper = (List *)
+ replace_vars_with_subplan_refs_mutator((Node *) sublink->oper,
+ context);
+
+ return (Node *) expr;
}
- return expression_tree_walker(node,
- replace_vars_with_subplan_refs_walker,
- (void *) context);
+ return expression_tree_mutator(node,
+ replace_vars_with_subplan_refs_mutator,
+ (void *) context);
}
/*****************************************************************************
- *
+ * OPERATOR REGPROC LOOKUP
*****************************************************************************/
-/*---------------------------------------------------------
- *
- * set_agg_tlist_references -
- * This routine has several responsibilities:
- * * Update the target list of an Agg node so that it points to
- * the tuples returned by its left tree subplan.
- * * If there is a qual list (from a HAVING clause), similarly update
- * vars in it to point to the subplan target list.
- *
- * The return value is TRUE if all qual clauses include Aggrefs, or FALSE
- * if any do not (caller may choose to raise an error condition).
- */
-bool
-set_agg_tlist_references(Agg *aggNode)
-{
- List *subplanTargetList;
- List *tl;
- List *ql;
- bool all_quals_ok;
-
- subplanTargetList = aggNode->plan.lefttree->targetlist;
-
- foreach(tl, aggNode->plan.targetlist)
- {
- TargetEntry *tle = lfirst(tl);
-
- replace_vars_with_subplan_refs(tle->expr,
- (Index) 0,
- subplanTargetList);
- }
-
- all_quals_ok = true;
- foreach(ql, aggNode->plan.qual)
- {
- Node *qual = lfirst(ql);
-
- replace_vars_with_subplan_refs(qual,
- (Index) 0,
- subplanTargetList);
- if (pull_agg_clause(qual) == NIL)
- all_quals_ok = false; /* this qual clause has no agg
- * functions! */
- }
-
- return all_quals_ok;
-}
-
/*
- * pull_agg_clause
- * Recursively pulls all Aggref nodes from an expression clause.
+ * fix_opids
+ * Calculate opid field from opno for each Oper node in given tree.
+ * The given tree can be anything expression_tree_walker handles.
*
- * Returns list of Aggref nodes found. Note the nodes themselves are not
- * copied, only referenced.
+ * The argument is modified in-place. (This is OK since we'd want the
+ * same change for any node, even if it gets visited more than once due to
+ * shared structure.)
*/
-List *
-pull_agg_clause(Node *clause)
-{
- List *result = NIL;
-
- pull_agg_clause_walker(clause, &result);
- return result;
-}
-
-static bool
-pull_agg_clause_walker(Node *node, List **listptr)
-{
- if (node == NULL)
- return false;
- if (IsA(node, Aggref))
- {
- *listptr = lappend(*listptr, node);
- return false;
- }
- return expression_tree_walker(node, pull_agg_clause_walker,
- (void *) listptr);
-}
-
-/*
- * check_having_for_ungrouped_vars takes the havingQual and the list of
- * GROUP BY clauses and checks for subplans in the havingQual that are being
- * passed ungrouped variables as parameters. In other contexts, ungrouped
- * vars in the havingQual will be detected by the parser (see parse_agg.c,
- * exprIsAggOrGroupCol()). But that routine currently does not check subplans,
- * because the necessary info is not computed until the planner runs.
- * This ought to be cleaned up someday.
- */
-
void
-check_having_for_ungrouped_vars(Node *clause, List *groupClause,
- List *targetList)
+fix_opids(Node *node)
{
- check_having_for_ungrouped_vars_context context;
-
- context.groupClause = groupClause;
- context.targetList = targetList;
- check_having_for_ungrouped_vars_walker(clause, &context);
+ /* This tree walk requires no special setup, so away we go... */
+ fix_opids_walker(node, NULL);
}
static bool
-check_having_for_ungrouped_vars_walker(Node *node,
- check_having_for_ungrouped_vars_context *context)
+fix_opids_walker (Node *node, void *context)
{
if (node == NULL)
return false;
- /*
- * We can ignore Vars other than in subplan args lists,
- * since the parser already checked 'em.
- */
- if (is_subplan(node))
- {
- /*
- * The args list of the subplan node represents attributes from
- * outside passed into the sublink.
- */
- List *t;
-
- foreach(t, ((Expr *) node)->args)
- {
- Node *thisarg = lfirst(t);
- bool contained_in_group_clause = false;
- List *gl;
-
- foreach(gl, context->groupClause)
- {
- GroupClause *gcl = lfirst(gl);
- Node *groupexpr;
-
- groupexpr = get_sortgroupclause_expr(gcl,
- context->targetList);
- /* XXX is var_equal correct, or should we use equal()? */
- if (var_equal((Var *) thisarg, (Var *) groupexpr))
- {
- contained_in_group_clause = true;
- break;
- }
- }
-
- if (!contained_in_group_clause)
- elog(ERROR, "Sub-SELECT in HAVING clause must use only GROUPed attributes from outer SELECT");
- }
- }
- return expression_tree_walker(node,
- check_having_for_ungrouped_vars_walker,
- (void *) context);
+ if (is_opclause(node))
+ replace_opid((Oper *) ((Expr *) node)->oper);
+ return expression_tree_walker(node, fix_opids_walker, context);
}
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 188379c9a2d..c275b7adc45 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -6,7 +6,7 @@
* Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.22 1999/08/21 03:49:03 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.23 1999/08/22 20:14:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -48,29 +48,13 @@ int PlannerPlanId; /* to assign unique ID to subquery plans */
static int
_new_param(Var *var, int varlevel)
{
- List *last;
- int i = 0;
+ Var *paramVar = (Var *) copyObject(var);
- if (PlannerParamVar == NULL)
- last = PlannerParamVar = makeNode(List);
- else
- {
- for (last = PlannerParamVar;;)
- {
- i++;
- if (lnext(last) == NULL)
- break;
- last = lnext(last);
- }
- lnext(last) = makeNode(List);
- last = lnext(last);
- }
+ paramVar->varlevelsup = varlevel;
- lnext(last) = NULL;
- lfirst(last) = makeVar(var->varno, var->varattno, var->vartype,
- var->vartypmod, varlevel, var->varnoold, var->varoattno);
+ PlannerParamVar = lappend(PlannerParamVar, paramVar);
- return i;
+ return length(PlannerParamVar) - 1;
}
/*
@@ -193,8 +177,7 @@ _make_subplan(SubLink *slink)
List *rside = lnext(((Expr *) lfirst(lst))->args);
TargetEntry *te = nth(i, plan->targetlist);
Var *var = makeVar(0, 0, te->resdom->restype,
- te->resdom->restypmod,
- 0, 0, 0);
+ te->resdom->restypmod, 0);
Param *prm = makeNode(Param);
prm->paramkind = PARAM_EXEC;
@@ -214,7 +197,7 @@ _make_subplan(SubLink *slink)
}
else if (node->parParam == NULL && slink->subLinkType == EXISTS_SUBLINK)
{
- Var *var = makeVar(0, 0, BOOLOID, -1, 0, 0, 0);
+ Var *var = makeVar(0, 0, BOOLOID, -1, 0);
Param *prm = makeNode(Param);
prm->paramkind = PARAM_EXEC;
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
index 2a9ddfc716a..95e5ddbc9db 100644
--- a/src/backend/optimizer/prep/preptlist.c
+++ b/src/backend/optimizer/prep/preptlist.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.30 1999/08/21 03:49:05 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.31 1999/08/22 20:14:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -59,8 +59,6 @@ preprocess_targetlist(List *tlist,
*/
expanded_tlist = expand_targetlist(tlist, relid, command_type, result_relation);
- /* XXX should the fix-opids be this early?? */
- fix_opids(expanded_tlist);
t_list = copyObject(expanded_tlist);
/* ------------------
@@ -87,7 +85,7 @@ preprocess_targetlist(List *tlist,
0,
true);
- var = makeVar(result_relation, -1, TIDOID, -1, 0, result_relation, -1);
+ var = makeVar(result_relation, -1, TIDOID, -1, 0);
ctid = makeTargetEntry(resdom, (Node *) var);
t_list = lappend(t_list, ctid);
@@ -340,8 +338,8 @@ new_relation_targetlist(Oid relid, Index rt_index, NodeTag node_type)
Var *temp_var;
TargetEntry *temp_tle;
- temp_var = makeVar(rt_index, attno, atttype, atttypmod,
- 0, rt_index, attno);
+ temp_var = makeVar(rt_index, attno, atttype,
+ atttypmod, 0);
temp_tle = makeTargetEntry(makeResdom(attno,
atttype,
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index ca4353f6085..fbb5a98e83d 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.47 1999/08/16 02:17:56 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.48 1999/08/22 20:14:53 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -24,11 +24,19 @@
#include "nodes/plannodes.h"
#include "optimizer/clauses.h"
#include "optimizer/internal.h"
+#include "optimizer/tlist.h"
#include "optimizer/var.h"
#include "utils/lsyscache.h"
-static bool fix_opids_walker(Node *node, void *context);
+typedef struct {
+ List *groupClause;
+ List *targetList;
+} check_subplans_for_ungrouped_vars_context;
+
+static bool pull_agg_clause_walker(Node *node, List **listptr);
+static bool check_subplans_for_ungrouped_vars_walker(Node *node,
+ check_subplans_for_ungrouped_vars_context *context);
static int is_single_func(Node *node);
@@ -351,12 +359,118 @@ pull_constant_clauses(List *quals, List **constantQual)
else
restqual = lcons(lfirst(q), restqual);
}
- freeList(quals); /* XXX seems a tad risky? */
*constantQual = constqual;
return restqual;
}
/*
+ * pull_agg_clause
+ * Recursively pulls all Aggref nodes from an expression tree.
+ *
+ * Returns list of Aggref nodes found. Note the nodes themselves are not
+ * copied, only referenced.
+ */
+List *
+pull_agg_clause(Node *clause)
+{
+ List *result = NIL;
+
+ pull_agg_clause_walker(clause, &result);
+ return result;
+}
+
+static bool
+pull_agg_clause_walker(Node *node, List **listptr)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, Aggref))
+ {
+ *listptr = lappend(*listptr, node);
+ /* continue, to iterate over agg's arg as well (do nested aggregates
+ * actually work?)
+ */
+ }
+ return expression_tree_walker(node, pull_agg_clause_walker,
+ (void *) listptr);
+}
+
+/*
+ * check_subplans_for_ungrouped_vars
+ * Check for subplans that are being passed ungrouped variables as
+ * parameters; return TRUE if any are found.
+ *
+ * In most contexts, ungrouped variables will be detected by the parser (see
+ * parse_agg.c, exprIsAggOrGroupCol()). But that routine currently does not
+ * check subplans, because the necessary info is not computed until the
+ * planner runs. So we do it here, after we have processed the subplan.
+ * This ought to be cleaned up someday.
+ *
+ * 'clause' is the expression tree to be searched for subplans.
+ * 'groupClause' is the GROUP BY list (a list of GroupClause nodes).
+ * 'targetList' is the target list that the group clauses refer to.
+ */
+bool
+check_subplans_for_ungrouped_vars(Node *clause,
+ List *groupClause,
+ List *targetList)
+{
+ check_subplans_for_ungrouped_vars_context context;
+
+ context.groupClause = groupClause;
+ context.targetList = targetList;
+ return check_subplans_for_ungrouped_vars_walker(clause, &context);
+}
+
+static bool
+check_subplans_for_ungrouped_vars_walker(Node *node,
+ check_subplans_for_ungrouped_vars_context *context)
+{
+ if (node == NULL)
+ return false;
+ /*
+ * We can ignore Vars other than in subplan args lists,
+ * since the parser already checked 'em.
+ */
+ if (is_subplan(node))
+ {
+ /*
+ * The args list of the subplan node represents attributes from
+ * outside passed into the sublink.
+ */
+ List *t;
+
+ foreach(t, ((Expr *) node)->args)
+ {
+ Node *thisarg = lfirst(t);
+ bool contained_in_group_clause = false;
+ List *gl;
+
+ foreach(gl, context->groupClause)
+ {
+ GroupClause *gcl = lfirst(gl);
+ Node *groupexpr;
+
+ groupexpr = get_sortgroupclause_expr(gcl,
+ context->targetList);
+ if (equal(thisarg, groupexpr))
+ {
+ contained_in_group_clause = true;
+ break;
+ }
+ }
+
+ if (!contained_in_group_clause)
+ return true; /* found an ungrouped argument */
+ }
+ }
+ return expression_tree_walker(node,
+ check_subplans_for_ungrouped_vars_walker,
+ (void *) context);
+}
+
+
+/*
* clause_relids_vars
* Retrieves distinct relids and vars appearing within a clause.
*
@@ -417,31 +531,6 @@ NumRelids(Node *clause)
}
/*
- * fix_opids
- * Calculate opid field from opno for each Oper node in given tree.
- * (The given tree can be anything expression_tree_walker handles.)
- *
- * Returns its argument, which has been modified in-place.
- */
-List *
-fix_opids(List *clauses)
-{
- /* This tree walk requires no special setup, so away we go... */
- fix_opids_walker((Node *) clauses, NULL);
- return clauses;
-}
-
-static bool
-fix_opids_walker (Node *node, void *context)
-{
- if (node == NULL)
- return false;
- if (is_opclause(node))
- replace_opid((Oper *) ((Expr *) node)->oper);
- return expression_tree_walker(node, fix_opids_walker, context);
-}
-
-/*
* get_relattval
* Extract information from a restriction or join clause for
* selectivity estimation. The inputs are an expression
diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c
index 37a790cc3dd..dfe2963581f 100644
--- a/src/backend/optimizer/util/tlist.c
+++ b/src/backend/optimizer/util/tlist.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.39 1999/08/21 03:49:07 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.40 1999/08/22 20:14:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -19,8 +19,6 @@
#include "optimizer/tlist.h"
#include "optimizer/var.h"
-static Node *unflatten_tlist_mutator(Node *node, List *flat_tlist);
-
/*****************************************************************************
* ---------- RELATION node target list routines ----------
*****************************************************************************/
@@ -28,39 +26,38 @@ static Node *unflatten_tlist_mutator(Node *node, List *flat_tlist);
/*
* tlistentry_member
* Finds the (first) member of the given tlist whose expression is
- * var_equal() to the given var. Result is NULL if no such member.
+ * equal() to the given expression. Result is NULL if no such member.
*/
TargetEntry *
-tlistentry_member(Var *var, List *targetlist)
+tlistentry_member(Node *node, List *targetlist)
{
- if (var && IsA(var, Var))
+ List *temp;
+
+ foreach(temp, targetlist)
{
- List *temp;
+ TargetEntry *tlentry = (TargetEntry *) lfirst(temp);
- foreach(temp, targetlist)
- {
- if (var_equal(var, get_expr(lfirst(temp))))
- return (TargetEntry *) lfirst(temp);
- }
+ if (equal(node, tlentry->expr))
+ return tlentry;
}
return NULL;
}
/*
- * matching_tlist_var
+ * matching_tlist_expr
* Same as tlistentry_member(), except returns the tlist expression
* rather than its parent TargetEntry node.
*/
-Expr *
-matching_tlist_var(Var *var, List *targetlist)
+Node *
+matching_tlist_expr(Node *node, List *targetlist)
{
TargetEntry *tlentry;
- tlentry = tlistentry_member(var, targetlist);
+ tlentry = tlistentry_member(node, targetlist);
if (tlentry)
- return (Expr *) get_expr(tlentry);
+ return tlentry->expr;
- return (Expr *) NULL;
+ return (Node *) NULL;
}
/*
@@ -69,11 +66,11 @@ matching_tlist_var(Var *var, List *targetlist)
* rather than its parent TargetEntry node.
*/
Resdom *
-tlist_member(Var *var, List *tlist)
+tlist_member(Node *node, List *targetlist)
{
TargetEntry *tlentry;
- tlentry = tlistentry_member(var, tlist);
+ tlentry = tlistentry_member(node, targetlist);
if (tlentry)
return tlentry->resdom;
@@ -89,7 +86,7 @@ tlist_member(Var *var, List *tlist)
void
add_var_to_tlist(RelOptInfo *rel, Var *var)
{
- if (! tlistentry_member(var, rel->targetlist))
+ if (! tlistentry_member((Node *) var, rel->targetlist))
{
/* XXX is copyObject necessary here? */
rel->targetlist = lappend(rel->targetlist,
@@ -116,85 +113,11 @@ create_tl_element(Var *var, int resdomno)
(Node *) var);
}
-/*
- * get_actual_tlist
- * Returns the targetlist elements from a relation tlist.
- *
- */
-List *
-get_actual_tlist(List *tlist)
-{
-
- /*
- * this function is not making sense. - ay 10/94
- */
-#ifdef NOT_USED
- List *element = NIL;
- List *result = NIL;
-
- if (tlist == NULL)
- {
- elog(DEBUG, "calling get_actual_tlist with empty tlist");
- return NIL;
- }
-
- /*
- * XXX - it is unclear to me what exactly get_entry should be doing,
- * as it is unclear to me the exact relationship between "TL" "TLE"
- * and joinlists
- */
-
- foreach(element, tlist)
- result = lappend(result, lfirst((List *) lfirst(element)));
-
- return result;
-#endif
- return tlist;
-}
-
/*****************************************************************************
* ---------- GENERAL target list routines ----------
*****************************************************************************/
/*
- * match_varid
- * Searches a target list for an entry matching a given var.
- *
- * Returns the target list entry (resdom var) of the matching var,
- * or NULL if no match.
- */
-TargetEntry *
-match_varid(Var *test_var, List *tlist)
-{
- List *tl;
-
- Assert(test_var->varlevelsup == 0); /* XXX why? */
-
- foreach(tl, tlist)
- {
- TargetEntry *entry = lfirst(tl);
- Var *tlvar = get_expr(entry);
-
- if (!IsA(tlvar, Var))
- continue;
-
- /*
- * we test the original varno, instead of varno which might be
- * changed to INNER/OUTER. XXX is test on vartype necessary?
- */
- Assert(tlvar->varlevelsup == 0);
-
- if (tlvar->varnoold == test_var->varnoold &&
- tlvar->varoattno == test_var->varoattno &&
- tlvar->vartype == test_var->vartype)
- return entry;
- }
-
- return NULL;
-}
-
-
-/*
* new_unsorted_tlist
* Creates a copy of a target list by creating new resdom nodes
* without sort information.
@@ -221,37 +144,6 @@ new_unsorted_tlist(List *targetlist)
}
/*
- * copy_vars
- * Replaces the var nodes in the first target list with those from
- * the second target list. The two target lists are assumed to be
- * identical except their actual resdoms and vars are different.
- *
- * 'target' is the target list to be replaced
- * 'source' is the target list to be copied
- *
- * Returns a new target list.
- *
- */
-List *
-copy_vars(List *target, List *source)
-{
- List *result = NIL;
- List *src;
- List *dest;
-
- for (src = source, dest = target;
- src != NIL && dest != NIL;
- src = lnext(src), dest = lnext(dest))
- {
- TargetEntry *temp = makeTargetEntry(((TargetEntry *) lfirst(dest))->resdom,
- (Node *) get_expr(lfirst(src)));
-
- result = lappend(result, temp);
- }
- return result;
-}
-
-/*
* flatten_tlist
* Create a target list that only contains unique variables.
*
@@ -292,7 +184,7 @@ add_to_flat_tlist(List *tlist, List *vars)
{
Var *var = lfirst(v);
- if (! tlistentry_member(var, tlist))
+ if (! tlistentry_member((Node *) var, tlist))
{
Resdom *r;
@@ -310,39 +202,6 @@ add_to_flat_tlist(List *tlist, List *vars)
return tlist;
}
-/*
- * unflatten_tlist
- * Reconstructs the target list of a query by replacing vars within
- * target expressions with vars from the 'flattened' target list.
- *
- * XXX is this really necessary? Why can't we just use the tlist as is?
- *
- * 'full_tlist' is the original target list
- * 'flat_tlist' is the flattened (var-only) target list
- *
- * Returns the rebuilt target list. The original is not modified.
- *
- */
-List *
-unflatten_tlist(List *full_tlist, List *flat_tlist)
-{
- return (List *) unflatten_tlist_mutator((Node *) full_tlist,
- flat_tlist);
-}
-
-static Node *
-unflatten_tlist_mutator(Node *node, List *flat_tlist)
-{
- if (node == NULL)
- return NULL;
- if (IsA(node, Var))
- return (Node *) get_expr(match_varid((Var *) node,
- flat_tlist));
- return expression_tree_mutator(node, unflatten_tlist_mutator,
- (void *) flat_tlist);
-}
-
-
Var *
get_expr(TargetEntry *tle)
{
diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c
index be181ea626f..a544041122b 100644
--- a/src/backend/optimizer/util/var.c
+++ b/src/backend/optimizer/util/var.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.22 1999/08/10 03:00:15 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.23 1999/08/22 20:14:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -105,29 +105,3 @@ pull_var_clause_walker(Node *node, List **listptr)
return expression_tree_walker(node, pull_var_clause_walker,
(void *) listptr);
}
-
-/*
- * var_equal
- *
- * This is like equal() except that it does NOT test varnoold and
- * varoattno. Also, it will not compare non-Var nodes.
- *
- * Returns t iff two var nodes correspond to the same attribute.
- */
-bool
-var_equal(Var *var1, Var *var2)
-{
- if (var1 != NULL && IsA(var1, Var) &&
- var2 != NULL && IsA(var2, Var) &&
- var1->varno == var2->varno &&
- var1->varattno == var2->varattno &&
- var1->vartype == var2->vartype &&
- var1->vartypmod == var2->vartypmod &&
- var1->varlevelsup == var2->varlevelsup)
- {
- Assert(var1->varlevelsup == 0); /* XXX why do this here??? */
- return true;
- }
- else
- return false;
-}
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 4a227de2c5b..19a287d99e8 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.53 1999/08/21 03:48:55 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.54 1999/08/22 20:15:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -444,7 +444,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
*/
toid = typeTypeId(typenameType(relname));
/* replace it in the arg list */
- lfirst(fargs) = makeVar(vnum, 0, toid, -1, 0, vnum, 0);
+ lfirst(fargs) = makeVar(vnum, 0, toid, -1, 0);
}
else if (!attisset)
{ /* set functions don't have parameters */
@@ -1300,7 +1300,7 @@ setup_tlist(char *attname, Oid relid)
0,
InvalidOid,
false);
- varnode = makeVar(-1, attno, typeid, type_mod, 0, -1, attno);
+ varnode = makeVar(-1, attno, typeid, type_mod, 0);
tle = makeTargetEntry(resnode, (Node *) varnode);
return lcons(tle, NIL);
@@ -1325,7 +1325,7 @@ setup_base_tlist(Oid typeid)
0,
InvalidOid,
false);
- varnode = makeVar(-1, 1, typeid, -1, 0, -1, 1);
+ varnode = makeVar(-1, 1, typeid, -1, 0);
tle = makeTargetEntry(resnode, (Node *) varnode);
return lcons(tle, NIL);
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index 80a8543d5a5..48da11d8d23 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.29 1999/07/19 00:26:19 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.30 1999/08/22 20:15:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -216,8 +216,7 @@ make_var(ParseState *pstate, Oid relid, char *refname,
vartypeid = get_atttype(relid, attid);
type_mod = get_atttypmod(relid, attid);
- varnode = makeVar(vnum, attid, vartypeid, type_mod,
- sublevels_up, vnum, attid);
+ varnode = makeVar(vnum, attid, vartypeid, type_mod, sublevels_up);
return varnode;
}
diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h
index 6a387fd5c5c..1aaead722d8 100644
--- a/src/include/nodes/makefuncs.h
+++ b/src/include/nodes/makefuncs.h
@@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: makefuncs.h,v 1.20 1999/07/15 23:03:52 momjian Exp $
+ * $Id: makefuncs.h,v 1.21 1999/08/22 20:15:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -25,9 +25,7 @@ extern Var *makeVar(Index varno,
AttrNumber varattno,
Oid vartype,
int32 vartypmod,
- Index varlevelsup,
- Index varnoold,
- AttrNumber varoattno);
+ Index varlevelsup);
extern TargetEntry *makeTargetEntry(Resdom *resdom, Node *expr);
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4eea81446b2..10e51e40268 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: primnodes.h,v 1.34 1999/08/21 03:49:09 tgl Exp $
+ * $Id: primnodes.h,v 1.35 1999/08/22 20:15:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -120,15 +120,23 @@ typedef struct Expr
/* ----------------
* Var
* varno - index of this var's relation in the range table
- * (could be INNER or OUTER)
+ * (could also be INNER or OUTER)
* varattno - attribute number of this var, or zero for all
- * vartype - pg_type tuple oid for the type of this var
+ * vartype - pg_type tuple OID for the type of this var
* vartypmod - pg_attribute typmod value
- * varlevelsup - for subquery variables referencing outer relations
- * varnoold - keep varno around in case it got changed to INNER/
- * OUTER (see match_varid)
- * varoattno - attribute number of this var
- * [ '(varnoold varoattno) was varid -ay 2/95]
+ * varlevelsup - for subquery variables referencing outer relations;
+ * 0 in a normal var, >0 means N levels up
+ * varnoold - original value of varno
+ * varoattno - original value of varattno
+ *
+ * Note: during parsing/planning, varnoold/varoattno are always just copies
+ * of varno/varattno. At the tail end of planning, Var nodes appearing in
+ * upper-level plan nodes are reassigned to point to the outputs of their
+ * subplans; for example, in a join node varno becomes INNER or OUTER and
+ * varattno becomes the index of the proper element of that subplan's target
+ * list. But varnoold/varoattno continue to hold the original values.
+ * The code doesn't really need varnoold/varoattno, but they are very useful
+ * for debugging and interpreting completed plans, so we keep them around.
* ----------------
*/
#define INNER 65000
@@ -145,8 +153,8 @@ typedef struct Var
Oid vartype;
int32 vartypmod;
Index varlevelsup; /* erased by upper optimizer */
- Index varnoold; /* only used by optimizer */
- AttrNumber varoattno; /* only used by optimizer */
+ Index varnoold; /* mainly for debugging --- see above */
+ AttrNumber varoattno;
} Var;
/* ----------------
diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h
index ec2dce883fc..6ea6b4f97ea 100644
--- a/src/include/optimizer/clauses.h
+++ b/src/include/optimizer/clauses.h
@@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: clauses.h,v 1.28 1999/08/16 02:17:44 tgl Exp $
+ * $Id: clauses.h,v 1.29 1999/08/22 20:14:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -38,9 +38,13 @@ extern Expr *make_ands_explicit(List *andclauses);
extern List *make_ands_implicit(Expr *clause);
extern List *pull_constant_clauses(List *quals, List **constantQual);
+extern List *pull_agg_clause(Node *clause);
+extern bool check_subplans_for_ungrouped_vars(Node *clause,
+ List *groupClause,
+ List *targetList);
+
extern void clause_get_relids_vars(Node *clause, Relids *relids, List **vars);
extern int NumRelids(Node *clause);
-extern List *fix_opids(List *clauses);
extern void get_relattval(Node *clause, int targetrelid,
int *relid, AttrNumber *attno,
Datum *constval, int *flag);
@@ -53,8 +57,8 @@ extern bool expression_tree_walker(Node *node, bool (*walker) (),
extern Node *expression_tree_mutator(Node *node, Node * (*mutator) (),
void *context);
-#define is_subplan(clause) ((Node*) (clause) != NULL && \
- nodeTag((Node*) (clause)) == T_Expr && \
- ((Expr *) (clause))->opType == SUBPLAN_EXPR)
+#define is_subplan(clause) ((clause) != NULL && \
+ IsA(clause, Expr) && \
+ ((Expr *) (clause))->opType == SUBPLAN_EXPR)
#endif /* CLAUSES_H */
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index 38ff367384b..3abda02d932 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: planmain.h,v 1.31 1999/08/21 03:49:15 tgl Exp $
+ * $Id: planmain.h,v 1.32 1999/08/22 20:14:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -27,14 +27,14 @@ extern Plan *query_planner(Query *root,
* prototypes for plan/createplan.c
*/
extern Plan *create_plan(Path *best_path);
-extern SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid,
- Plan *lefttree);
+extern SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);
extern Sort *make_sort(List *tlist, Oid nonameid, Plan *lefttree,
int keycount);
extern Agg *make_agg(List *tlist, Plan *lefttree);
extern Group *make_group(List *tlist, bool tuplePerGroup, int ngrp,
AttrNumber *grpColIdx, Plan *lefttree);
extern Unique *make_unique(List *tlist, Plan *lefttree, char *uniqueAttr);
+extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
/*
* prototypes for plan/initsplan.c
@@ -47,17 +47,10 @@ extern void add_missing_vars_to_tlist(Query *root, List *tlist);
/*
* prototypes for plan/setrefs.c
*/
-extern void set_tlist_references(Plan *plan);
+extern void set_plan_references(Plan *plan);
extern List *join_references(List *clauses, List *outer_tlist,
- List *inner_tlist);
-extern void replace_tlist_with_subplan_refs(List *tlist,
- Index subvarno,
- List *subplanTargetList);
-extern bool set_agg_tlist_references(Agg *aggNode);
-extern List *pull_agg_clause(Node *clause);
-extern void check_having_for_ungrouped_vars(Node *clause,
- List *groupClause,
- List *targetList);
+ List *inner_tlist, Index acceptable_rel);
+extern void fix_opids(Node *node);
/*
* prep/prepkeyset.c
diff --git a/src/include/optimizer/tlist.h b/src/include/optimizer/tlist.h
index f0ddb9ac9dc..58b1b7b2e99 100644
--- a/src/include/optimizer/tlist.h
+++ b/src/include/optimizer/tlist.h
@@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: tlist.h,v 1.21 1999/08/21 03:49:15 tgl Exp $
+ * $Id: tlist.h,v 1.22 1999/08/22 20:14:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -15,20 +15,16 @@
#include "nodes/relation.h"
-extern TargetEntry *tlistentry_member(Var *var, List *targetlist);
-extern Expr *matching_tlist_var(Var *var, List *targetlist);
+extern TargetEntry *tlistentry_member(Node *node, List *targetlist);
+extern Node *matching_tlist_expr(Node *node, List *targetlist);
+extern Resdom *tlist_member(Node *node, List *targetlist);
+
extern void add_var_to_tlist(RelOptInfo *rel, Var *var);
extern TargetEntry *create_tl_element(Var *var, int resdomno);
-extern List *get_actual_tlist(List *tlist);
-extern Resdom *tlist_member(Var *var, List *tlist);
-extern TargetEntry *match_varid(Var *test_var, List *tlist);
extern List *new_unsorted_tlist(List *targetlist);
-extern List *copy_vars(List *target, List *source);
extern List *flatten_tlist(List *tlist);
extern List *add_to_flat_tlist(List *tlist, List *vars);
-extern List *unflatten_tlist(List *full_tlist,
- List *flat_tlist);
extern Var *get_expr(TargetEntry *tle);
extern Node *get_sortgroupclause_expr(SortClause *sortClause,
diff --git a/src/include/optimizer/var.h b/src/include/optimizer/var.h
index 16f9f4f6634..440b62f49ad 100644
--- a/src/include/optimizer/var.h
+++ b/src/include/optimizer/var.h
@@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: var.h,v 1.8 1999/07/15 15:21:23 momjian Exp $
+ * $Id: var.h,v 1.9 1999/08/22 20:14:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -18,6 +18,5 @@
extern List *pull_varnos(Node *me);
extern bool contain_var_clause(Node *clause);
extern List *pull_var_clause(Node *clause);
-extern bool var_equal(Var *var1, Var *var2);
#endif /* VAR_H */