Further planner/optimizer cleanups. Move all set_tlist_references
authorTom Lane
Sun, 22 Aug 1999 20:15:04 +0000 (20:15 +0000)
committerTom Lane
Sun, 22 Aug 1999 20:15:04 +0000 (20:15 +0000)
and fix_opids processing to a single recursive pass over the plan tree
executed at the very tail end of planning, rather than haphazardly here
and there at different places.  Now that tlist Vars do not get modified
until the very end, it's possible to get rid of the klugy var_equal and
match_varid partial-matching routines, and just use plain equal()
throughout the optimizer.  This is a step towards allowing merge and
hash joins to be done on expressions instead of only Vars ...

24 files changed:
doc/src/sgml/arch-dev.sgml
src/backend/commands/indexcmds.c
src/backend/executor/nodeAgg.c
src/backend/nodes/makefuncs.c
src/backend/optimizer/path/costsize.c
src/backend/optimizer/path/pathkeys.c
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/plan/initsplan.c
src/backend/optimizer/plan/planmain.c
src/backend/optimizer/plan/planner.c
src/backend/optimizer/plan/setrefs.c
src/backend/optimizer/plan/subselect.c
src/backend/optimizer/prep/preptlist.c
src/backend/optimizer/util/clauses.c
src/backend/optimizer/util/tlist.c
src/backend/optimizer/util/var.c
src/backend/parser/parse_func.c
src/backend/parser/parse_node.c
src/include/nodes/makefuncs.h
src/include/nodes/primnodes.h
src/include/optimizer/clauses.h
src/include/optimizer/planmain.h
src/include/optimizer/tlist.h
src/include/optimizer/var.h

index 7d867c45cae3685f59f1b6213a2c68a287d1363f..93f53c515551576f192ca8724968e5beb0f7c8c6 100644 (file)
@@ -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);
index 3644d1ba0426bc99b7ce0a02efaf22d148252442..31d3419bee671b5e2773ca7d6368dfcabd75d800 100644 (file)
@@ -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);
    }
 
index b600c0e3a8f1cdbc2713d194ee298a63cf4f3ad2..2936694f67993a74935dd40b3220d2470bab3f95 100644 (file)
@@ -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;
 
index 26ebed1d458ac9b7b5cdcc17aff9273c0f460c51..ddfef9d5eb4508e7a16e553fb374e181e1047826 100644 (file)
@@ -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;
 }
index 55787062dc513f033ccf3c7d8157ad70230df461..fcf462b83eb08a268e2080f1533f58f514ad5d2f 100644 (file)
@@ -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;
 }
index 41a3ff35b4813a6112d08762966ae9fe2000ee54..b9a982e828325f4d8b6c6c43d5af442696267c1d 100644 (file)
@@ -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);
@@ -118,45 +116,6 @@ makePathKeyItem(Node *key, Oid sortop)
  *     PATHKEY COMPARISONS
  ****************************************************************************/
 
-/*
- * 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
@@ -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));
    }
 
index d1f756fc7c19fc44be0f1a15dd3989fab8c485b8..ae1e2d3266b24700ff8048bcfdd5a5331896d4a5 100644 (file)
@@ -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)
index db97c732070e621e1c65a981afe40c4fb1eaea4f..a89c40b943598bde2181c7637a0f98782cb51806 100644 (file)
@@ -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);
index f6f62abfe0841e8d6b05f7bf3e818cc7669e6dc6..802e5970416558d727334fff1e3408fd34a2ceb3 100644 (file)
@@ -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;
-}
index b328c40f226714a1ed601a004981c302fb5f5a9f..0003262a454e1688befe7733e05363d37d65aa99 100644 (file)
@@ -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");
+       }
    }
 
    /*
@@ -314,13 +325,6 @@ union_planner(Query *parse)
        /* HAVING clause, if any, becomes qual of the Agg node */
        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);
 }
 
 /*
index 1492df8b03015b4ca8615a4a76ec2aa5ce7a4bcf..a983aa32143d41e1b1b5c55e28ee69e430a630c2 100644 (file)
@@ -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 $
  *
  *-------------------------------------------------------------------------
  */
 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);
 }
index 188379c9a2dbf0fac71b8aa04a9aa9cf49c8bf25..c275b7adc457a819af76ee383bbeeda6e00f3ac1 100644 (file)
@@ -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;
index 2a9ddfc716a2dc2ef4cb906544d01fd883a4eb76..95e5ddbc9db923277571ed8a09aceabd7763c9b9 100644 (file)
@@ -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,
index ca4353f60852309cc139af9aa337dcf932a31a57..fbb5a98e83db1501a7f779c863010f061a60c3ea 100644 (file)
@@ -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
 #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,11 +359,117 @@ 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.
@@ -416,31 +530,6 @@ NumRelids(Node *clause)
    return result;
 }
 
-/*
- * 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
index 37a790cc3dd5cabd1f27c32ad5d652c0f7862696..dfe2963581feec6d9b934956226bfd64ea3272b1 100644 (file)
@@ -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,84 +113,10 @@ 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
@@ -220,37 +143,6 @@ new_unsorted_tlist(List *targetlist)
    return new_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)
 {
index be181ea626f2bce9d8826359700eb6a6bd6ee006..a544041122b2f4aff43b692330665c4ce9a33b76 100644 (file)
@@ -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;
-}
index 4a227de2c5ba32620ffd39f6e2eac11121914c06..19a287d99e882fb5bb7637cbadb0ab83a09c7295 100644 (file)
@@ -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);
index 80a8543d5a5addf8d9fe871bda674a4e0492a90d..48da11d8d2394fd27a3c1e1514a3eff517ecfc7a 100644 (file)
@@ -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;
 }
index 6a387fd5c5c05fe2a84af3d1d57c9bad174a930e..1aaead722d8aced8e501942e2468ea3bd236b33c 100644 (file)
@@ -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);
 
index 4eea81446b2e23877c9a03774a56c013278d3b24..10e51e40268eed36463e7e0edd453d888c7c19eb 100644 (file)
@@ -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;
 
 /* ----------------
index ec2dce883fccbedded84148895ef73e6e6f03967..6ea6b4f97eadc74cc625fa48d23c0214fa1ea09c 100644 (file)
@@ -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 */
index 38ff367384be5b1d08089b1664dcd36d9fabbabb..3abda02d932c13436e3966ebb677025ec8521b51 100644 (file)
@@ -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
index f0ddb9ac9dcc4b406ae668384a043042f834813f..58b1b7b2e994fd5ba3c33ef2e6a72e302c001b95 100644 (file)
@@ -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 $
  *
  *-------------------------------------------------------------------------
  */
 
 #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,
index 16f9f4f6634a01dee9a2345f64a0e9f95b7a4598..440b62f49adc75d5b6bf5c693c9bfb48f8958288 100644 (file)
@@ -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 */