]> granicus.if.org Git - postgresql/commitdiff
First cut at full support for OUTER JOINs. There are still a few loose
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 12 Sep 2000 21:07:18 +0000 (21:07 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 12 Sep 2000 21:07:18 +0000 (21:07 +0000)
ends to clean up (see my message of same date to pghackers), but mostly
it works.  INITDB REQUIRED!

93 files changed:
src/backend/catalog/heap.c
src/backend/commands/command.c
src/backend/commands/creatinh.c
src/backend/commands/explain.c
src/backend/commands/view.c
src/backend/executor/execMain.c
src/backend/executor/execTuples.c
src/backend/executor/execUtils.c
src/backend/executor/nodeHashjoin.c
src/backend/executor/nodeMergejoin.c
src/backend/executor/nodeNestloop.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/list.c
src/backend/nodes/outfuncs.c
src/backend/nodes/print.c
src/backend/nodes/readfuncs.c
src/backend/optimizer/README
src/backend/optimizer/geqo/geqo_eval.c
src/backend/optimizer/path/allpaths.c
src/backend/optimizer/path/indxpath.c
src/backend/optimizer/path/joinpath.c
src/backend/optimizer/path/joinrels.c
src/backend/optimizer/path/orindxpath.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/prepkeyset.c
src/backend/optimizer/prep/prepunion.c
src/backend/optimizer/util/clauses.c
src/backend/optimizer/util/pathnode.c
src/backend/optimizer/util/relnode.c
src/backend/optimizer/util/restrictinfo.c
src/backend/optimizer/util/var.c
src/backend/parser/Makefile
src/backend/parser/analyze.c
src/backend/parser/gram.y
src/backend/parser/parse_agg.c
src/backend/parser/parse_clause.c
src/backend/parser/parse_expr.c
src/backend/parser/parse_func.c
src/backend/parser/parse_node.c
src/backend/parser/parse_relation.c
src/backend/parser/parse_target.c
src/backend/parser/parser.c
src/backend/parser/scan.l
src/backend/rewrite/locks.c
src/backend/rewrite/rewriteHandler.c
src/backend/rewrite/rewriteManip.c
src/backend/utils/adt/ruleutils.c
src/include/catalog/catversion.h
src/include/executor/execdebug.h
src/include/executor/execdefs.h
src/include/executor/executor.h
src/include/nodes/execnodes.h
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/nodes/pg_list.h
src/include/nodes/plannodes.h
src/include/nodes/primnodes.h
src/include/nodes/relation.h
src/include/optimizer/clauses.h
src/include/optimizer/pathnode.h
src/include/optimizer/paths.h
src/include/optimizer/planmain.h
src/include/optimizer/restrictinfo.h
src/include/parser/gramparse.h
src/include/parser/parse_clause.h
src/include/parser/parse_func.h
src/include/parser/parse_node.h
src/include/parser/parse_relation.h
src/include/parser/parsetree.h
src/include/rewrite/rewriteHandler.h
src/include/rewrite/rewriteManip.h
src/test/regress/expected/case.out
src/test/regress/expected/geometry-cygwin-precision.out
src/test/regress/expected/geometry-i86-gnulibc.out
src/test/regress/expected/geometry-positive-zeros-bsd.out
src/test/regress/expected/geometry-positive-zeros.out
src/test/regress/expected/geometry-powerpc-aix4.out
src/test/regress/expected/geometry-powerpc-linux-gnulibc1.out
src/test/regress/expected/geometry-solaris-precision.out
src/test/regress/expected/geometry.out
src/test/regress/expected/join.out
src/test/regress/expected/point.out
src/test/regress/expected/rules.out
src/test/regress/expected/select_implicit.out
src/test/regress/sql/join.sql
src/test/regress/sql/rules.sql

index 68bb8276981568f3c9ed3c4cded35f1ba725d309..44728bf9c9aa8652cb87eb0ffab517d875aed821 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.143 2000/09/12 04:49:06 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.144 2000/09/12 21:06:46 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -1538,11 +1538,9 @@ StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin,
         */
        rte = makeNode(RangeTblEntry);
        rte->relname = RelationGetRelationName(rel);
-#ifndef DISABLE_EREF
-       rte->ref = makeNode(Attr);
-       rte->ref->relname = RelationGetRelationName(rel);
-#endif
        rte->relid = RelationGetRelid(rel);
+       rte->eref = makeNode(Attr);
+       rte->eref->relname = RelationGetRelationName(rel);
        rte->inh = false;
        rte->inFromCl = true;
        rte->skipAcl = false;
@@ -1623,11 +1621,9 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin)
         */
        rte = makeNode(RangeTblEntry);
        rte->relname = RelationGetRelationName(rel);
-#ifndef DISABLE_EREF
-       rte->ref = makeNode(Attr);
-       rte->ref->relname = RelationGetRelationName(rel);
-#endif
        rte->relid = RelationGetRelid(rel);
+       rte->eref = makeNode(Attr);
+       rte->eref->relname = RelationGetRelationName(rel);
        rte->inh = false;
        rte->inFromCl = true;
        rte->skipAcl = false;
@@ -1723,6 +1719,7 @@ AddRelationRawConstraints(Relation rel,
        int                     numoldchecks;
        ConstrCheck *oldchecks;
        ParseState *pstate;
+       RangeTblEntry *rte;
        int                     numchecks;
        List       *listptr;
        Relation        relrel;
@@ -1752,7 +1749,8 @@ AddRelationRawConstraints(Relation rel,
         */
        pstate = make_parsestate(NULL);
        makeRangeTable(pstate, NULL);
-       addRangeTableEntry(pstate, relname, makeAttr(relname, NULL), false, true, true);
+       rte = addRangeTableEntry(pstate, relname, NULL, false, true);
+       addRTEtoJoinTree(pstate, rte);
 
        /*
         * Process column default expressions.
index 9535e19741750216ae12bc1842de28c8b536a1d8..841806810e4377beccb4184eea79cdb1f89457ca 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.102 2000/09/12 05:09:43 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.103 2000/09/12 21:06:47 tgl Exp $
  *
  * NOTES
  *       The PerformAddAttribute() code, like most of the relation
@@ -61,8 +61,6 @@ static bool is_viewr(char *relname);
 static bool is_view(Relation rel);
 
 
-
-
 /* --------------------------------
  *             PortalCleanup
  * --------------------------------
@@ -536,7 +534,6 @@ AlterTableAlterColumn(const char *relationName,
        rel = heap_openr(relationName, AccessExclusiveLock);
        if ( rel->rd_rel->relkind == RELKIND_VIEW )
                elog(ERROR, "ALTER TABLE: %s is a view", relationName);
-
        myrelid = RelationGetRelid(rel);
        heap_close(rel, NoLock);
 
@@ -782,7 +779,7 @@ systable_getnext(void *scan)
  *     find a specified attribute in a node entry
  */
 static bool
-find_attribute_walker(Node *node, int attnum)
+find_attribute_walker(Node *node, int *attnump)
 {
        if (node == NULL)
                return false;
@@ -791,16 +788,17 @@ find_attribute_walker(Node *node, int attnum)
                Var                *var = (Var *) node;
 
                if (var->varlevelsup == 0 && var->varno == 1 &&
-                       var->varattno == attnum)
+                       var->varattno == *attnump)
                        return true;
        }
-       return expression_tree_walker(node, find_attribute_walker, (void *) attnum);
+       return expression_tree_walker(node, find_attribute_walker,
+                                                                 (void *) attnump);
 }
 
 static bool
 find_attribute_in_node(Node *node, int attnum)
 {
-       return expression_tree_walker(node, find_attribute_walker, (void *) attnum);
+       return find_attribute_walker(node, &attnum);
 }
 
 /*
@@ -1096,7 +1094,6 @@ void
 AlterTableAddConstraint(char *relationName,
                                                bool inh, Node *newConstraint)
 {
-
        if (newConstraint == NULL)
                elog(ERROR, "ALTER TABLE / ADD CONSTRAINT passed invalid constraint.");
 
@@ -1108,328 +1105,330 @@ AlterTableAddConstraint(char *relationName,
        /* check to see if the table to be constrained is a view. */
        if (is_viewr(relationName))
                 elog(ERROR, "ALTER TABLE: Cannot add constraints to views.");
-               
+
        switch (nodeTag(newConstraint))
        {
                case T_Constraint:
+               {
+                       Constraint *constr = (Constraint *) newConstraint;
+
+                       switch (constr->contype)
                        {
-                               Constraint *constr=(Constraint *)newConstraint;
-                               switch (constr->contype) {
-                                       case CONSTR_CHECK:
+                               case CONSTR_CHECK:
+                               {
+                                       ParseState *pstate;
+                                       bool successful = TRUE;
+                                       HeapScanDesc scan;
+                                       ExprContext *econtext;
+                                       TupleTableSlot *slot = makeNode(TupleTableSlot);
+                                       HeapTuple tuple;
+                                       RangeTblEntry *rte;
+                                       List       *rtlist;
+                                       List       *qual;
+                                       List       *constlist;
+                                       Relation        rel;
+                                       Node *expr;
+                                       char *name;
+
+                                       if (constr->name)
+                                               name=constr->name;
+                                       else
+                                               name="<unnamed>";
+
+                                       constlist=lcons(constr, NIL);
+
+                                       rel = heap_openr(relationName, AccessExclusiveLock);
+
+                                       /* make sure it is not a view */
+                                       if (rel->rd_rel->relkind == RELKIND_VIEW)
+                                               elog(ERROR, "ALTER TABLE: cannot add constraint to a view");
+
+                                       /*
+                                        * Scan all of the rows, looking for a false match
+                                        */
+                                       scan = heap_beginscan(rel, false, SnapshotNow, 0, NULL);
+                                       AssertState(scan != NULL);
+
+                                       /* 
+                                        * We need to make a parse state and range table to allow
+                                        * us to transformExpr and fix_opids to get a version of
+                                        * the expression we can pass to ExecQual
+                                        */
+                                       pstate = make_parsestate(NULL);
+                                       makeRangeTable(pstate, NULL);
+                                       rte = addRangeTableEntry(pstate, relationName, NULL,
+                                                                                        false, true);
+                                       addRTEtoJoinTree(pstate, rte);
+
+                                       /* Convert the A_EXPR in raw_expr into an EXPR */
+                                       expr = transformExpr(pstate, constr->raw_expr, EXPR_COLUMN_FIRST);
+
+                                       /*
+                                        * Make sure it yields a boolean result.
+                                        */
+                                       if (exprType(expr) != BOOLOID)
+                                               elog(ERROR, "CHECK '%s' does not yield boolean result",
+                                                        name);
+
+                                       /*
+                                        * Make sure no outside relations are referred to.
+                                        */
+                                       if (length(pstate->p_rtable) != 1)
+                                               elog(ERROR, "Only relation '%s' can be referenced in CHECK",
+                                                        relationName);
+
+                                       /*
+                                        * Might as well try to reduce any constant expressions.
+                                        */
+                                       expr = eval_const_expressions(expr);
+
+                                       /* And fix the opids */
+                                       fix_opids(expr);
+
+                                       qual = lcons(expr, NIL);
+
+                                       rte = makeNode(RangeTblEntry);
+                                       rte->relname = relationName;
+                                       rte->relid = RelationGetRelid(rel);
+                                       rte->eref = makeNode(Attr);
+                                       rte->eref->relname = relationName;
+                                       rtlist = lcons(rte, NIL);
+
+                                       /* 
+                                        * Scan through the rows now, making the necessary things
+                                        * for ExecQual, and then call it to evaluate the
+                                        * expression.
+                                        */
+                                       while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
                                        {
-                                               ParseState *pstate;
-                                               bool successful=TRUE;
-                                               HeapScanDesc scan;
-                                               ExprContext *econtext;
-                                               TupleTableSlot *slot = makeNode(TupleTableSlot);
-                                               HeapTuple tuple;
-                                               RangeTblEntry *rte = makeNode(RangeTblEntry);
-                                               List       *rtlist;
-                                               List       *qual;
-                                               List       *constlist;
-                                               Relation        rel;
-                                               Node *expr;
-                                               char *name;
-                                               if (constr->name)
-                                                       name=constr->name;
-                                               else
-                                                       name="<unnamed>";
-
-                                               rel = heap_openr(relationName, AccessExclusiveLock);
-
-                                               /* make sure it is not a view */
-                                               if (rel->rd_rel->relkind == RELKIND_VIEW)
-                                                  elog(ERROR, "ALTER TABLE: cannot add constraint to a view");
-
-                                               /*
-                                                * Scan all of the rows, looking for a false match
-                                                */
-                                               scan = heap_beginscan(rel, false, SnapshotNow, 0, NULL);
-                                               AssertState(scan != NULL);
-
-                                               /* 
-                                                *We need to make a parse state and range table to allow us
-                                                * to transformExpr and fix_opids to get a version of the
-                                                * expression we can pass to ExecQual
-                                                */
-                                               pstate = make_parsestate(NULL);
-                                               makeRangeTable(pstate, NULL);
-                                               addRangeTableEntry(pstate, relationName, 
-                                                       makeAttr(relationName, NULL), false, true,true);
-                                               constlist=lcons(constr, NIL);
-
-                                               /* Convert the A_EXPR in raw_expr into an EXPR */
-                                               expr = transformExpr(pstate, constr->raw_expr, EXPR_COLUMN_FIRST);
-
-                                               /*
-                                                * Make sure it yields a boolean result.
-                                                */
-                                               if (exprType(expr) != BOOLOID)
-                                                       elog(ERROR, "CHECK '%s' does not yield boolean result",
-                                                        name);
-
-                                               /*
-                                                * Make sure no outside relations are referred to.
-                                                */
-                                               if (length(pstate->p_rtable) != 1)
-                                                       elog(ERROR, "Only relation '%s' can be referenced in CHECK",
-                                                        relationName);
-
-                                               /*
-                                                * Might as well try to reduce any constant expressions.
-                                                */
-                                               expr = eval_const_expressions(expr);
-
-                                               /* And fix the opids */
-                                               fix_opids(expr);
-
-                                               qual = lcons(expr, NIL);
-                                                       rte->relname = relationName;
-                                               rte->ref = makeNode(Attr);
-                                               rte->ref->relname = rte->relname;
-                                               rte->relid = RelationGetRelid(rel);
-                                               rtlist = lcons(rte, NIL);
-
-                                               /* 
-                                                * Scan through the rows now, making the necessary things for
-                                                * ExecQual, and then call it to evaluate the expression.
-                                                */
-                                               while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
+                                               slot->val = tuple;
+                                               slot->ttc_shouldFree = false;
+                                               slot->ttc_descIsNew = true;
+                                               slot->ttc_tupleDescriptor = rel->rd_att;
+                                               slot->ttc_buffer = InvalidBuffer;
+                                               slot->ttc_whichplan = -1;
+
+                                               econtext = MakeExprContext(slot, CurrentMemoryContext);
+                                               econtext->ecxt_range_table = rtlist; /* range table */
+                                               if (!ExecQual(qual, econtext, true))
                                                {
-                                                       slot->val = tuple;
-                                                       slot->ttc_shouldFree = false;
-                                                       slot->ttc_descIsNew = true;
-                                                       slot->ttc_tupleDescriptor = rel->rd_att;
-                                                       slot->ttc_buffer = InvalidBuffer;
-                                                       slot->ttc_whichplan = -1;
-
-                                                       econtext = MakeExprContext(slot, CurrentMemoryContext);
-                                                       econtext->ecxt_range_table = rtlist;            /* range table */
-                                                       if (!ExecQual(qual, econtext, true)) {
-                                                               successful=false;
-                                                               break;
-                                                       }
-                                                       FreeExprContext(econtext);
+                                                       successful=false;
+                                                       break;
                                                }
+                                               FreeExprContext(econtext);
+                                       }
 
-                                               pfree(slot);
-                                               pfree(rtlist);
-                                               pfree(rte);
+                                       pfree(slot);
+                                       pfree(rtlist);
+                                       pfree(rte);
 
-                                               heap_endscan(scan);
-                                               heap_close(rel, NoLock);                
+                                       heap_endscan(scan);
+                                       heap_close(rel, NoLock);                
 
-                                               if (!successful) 
-                                               {
-                                                       elog(ERROR, "AlterTableAddConstraint: rejected due to CHECK constraint %s", name);
-                                               }
-                                               /* 
-                                                * Call AddRelationRawConstraints to do the real adding -- It duplicates some
-                                                * of the above, but does not check the validity of the constraint against
-                                                * tuples already in the table.
-                                                */
-                                               AddRelationRawConstraints(rel, NIL, constlist);
-                                               pfree(constlist);
-
-                                               break;
+                                       if (!successful) 
+                                       {
+                                               elog(ERROR, "AlterTableAddConstraint: rejected due to CHECK constraint %s", name);
                                        }
-                                       default:
-                                               elog(ERROR, "ALTER TABLE / ADD CONSTRAINT is not implemented for that constraint type.");
+                                       /* 
+                                        * Call AddRelationRawConstraints to do the real adding --
+                                        * It duplicates some of the above, but does not check the
+                                        * validity of the constraint against tuples already in
+                                        * the table.
+                                        */
+                                       AddRelationRawConstraints(rel, NIL, constlist);
+                                       pfree(constlist);
+
+                                       break;
                                }
+                               default:
+                                       elog(ERROR, "ALTER TABLE / ADD CONSTRAINT is not implemented for that constraint type.");
                        }
                        break;
+               }
                case T_FkConstraint:
-                       {
-                               FkConstraint *fkconstraint = (FkConstraint *) newConstraint;
-                               Relation        rel, pkrel;
-                               HeapScanDesc scan;
-                               HeapTuple       tuple;
-                               Trigger         trig;
-                               List       *list;
-                               int                     count;
-                               List       *indexoidlist,
-                                          *indexoidscan;
-                               Form_pg_index indexStruct = NULL;
-                               Form_pg_attribute *rel_attrs = NULL;
-                               int                     i;
-                               int found=0;
-
-                               if (get_temp_rel_by_username(fkconstraint->pktable_name)!=NULL &&
-                                   get_temp_rel_by_username(relationName)==NULL) {
-                                       elog(ERROR, "ALTER TABLE / ADD CONSTRAINT: Unable to reference temporary table from permanent table constraint.");
-                               }
+               {
+                       FkConstraint *fkconstraint = (FkConstraint *) newConstraint;
+                       Relation        rel, pkrel;
+                       HeapScanDesc scan;
+                       HeapTuple       tuple;
+                       Trigger         trig;
+                       List       *list;
+                       int                     count;
+                       List       *indexoidlist,
+                               *indexoidscan;
+                       Form_pg_index indexStruct = NULL;
+                       Form_pg_attribute *rel_attrs = NULL;
+                       int                     i;
+                       int found=0;
+
+                       if (get_temp_rel_by_username(fkconstraint->pktable_name)!=NULL &&
+                               get_temp_rel_by_username(relationName)==NULL) {
+                               elog(ERROR, "ALTER TABLE / ADD CONSTRAINT: Unable to reference temporary table from permanent table constraint.");
+                       }
+
+                       /*
+                        * Grab an exclusive lock on the pk table, so that someone
+                        * doesn't delete rows out from under us.
+                        */
+
+                       pkrel = heap_openr(fkconstraint->pktable_name, AccessExclusiveLock);
+                       if (pkrel->rd_rel->relkind != RELKIND_RELATION)
+                               elog(ERROR, "referenced table \"%s\" not a relation", 
+                                        fkconstraint->pktable_name);
+
+                       /*
+                        * Grab an exclusive lock on the fk table, and then scan
+                        * through each tuple, calling the RI_FKey_Match_Ins
+                        * (insert trigger) as if that tuple had just been
+                        * inserted.  If any of those fail, it should elog(ERROR)
+                        * and that's that.
+                        */
+                       rel = heap_openr(relationName, AccessExclusiveLock);
+                       if (rel->rd_rel->relkind != RELKIND_RELATION)
+                               elog(ERROR, "referencing table \"%s\" not a relation",
+                                        relationName);
+
+                       /* First we check for limited correctness of the constraint */
+
+                       rel_attrs = pkrel->rd_att->attrs;
+                       indexoidlist = RelationGetIndexList(pkrel);
 
-                               /*
-                                * Grab an exclusive lock on the pk table, so that someone
-                                * doesn't delete rows out from under us.
-                                */
-
-                               pkrel = heap_openr(fkconstraint->pktable_name, AccessExclusiveLock);
-                               if (pkrel == NULL)
-                                               elog(ERROR, "referenced table \"%s\" not found",
-                                                        fkconstraint->pktable_name);
-
-                               if (pkrel->rd_rel->relkind != RELKIND_RELATION)
-                                       elog(ERROR, "referenced table \"%s\" not a relation", 
-                                        fkconstraint->pktable_name);
-                               
-
-                               /*
-                                * Grab an exclusive lock on the fk table, and then scan
-                                * through each tuple, calling the RI_FKey_Match_Ins
-                                * (insert trigger) as if that tuple had just been
-                                * inserted.  If any of those fail, it should elog(ERROR)
-                                * and that's that.
-                                */
-                               rel = heap_openr(relationName, AccessExclusiveLock);
-                               if (rel == NULL)
-                                       elog(ERROR, "table \"%s\" not found",
-                                               relationName);
-
-                               if (rel->rd_rel->relkind != RELKIND_RELATION)
-                                       elog(ERROR, "referencing table \"%s\" not a relation", relationName);
-
-                               /* First we check for limited correctness of the constraint */
-
-                               rel_attrs = pkrel->rd_att->attrs;
-                               indexoidlist = RelationGetIndexList(pkrel);
-
-                               foreach(indexoidscan, indexoidlist)
-                               {
-                                       Oid             indexoid = lfirsti(indexoidscan);
-                                       HeapTuple       indexTuple;
-                                       List *attrl;
-                                       indexTuple = SearchSysCacheTuple(INDEXRELID,
-                                                                                 ObjectIdGetDatum(indexoid),
-                                                                                 0, 0, 0);
-                                       if (!HeapTupleIsValid(indexTuple))
-                                               elog(ERROR, "transformFkeyGetPrimaryKey: index %u not found",
-                                                        indexoid);
-                                       indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
-
-                                       if (indexStruct->indisunique) {
-                                               /* go through the fkconstraint->pk_attrs list */
-                                               foreach(attrl, fkconstraint->pk_attrs) {
-                                                       Ident *attr=lfirst(attrl);
-                                                       found=0;
-                                                       for (i = 0; i < INDEX_MAX_KEYS && indexStruct->indkey[i] != 0; i++)
-                                                       {
-                                                               int pkattno = indexStruct->indkey[i];
+                       foreach(indexoidscan, indexoidlist)
+                               {
+                                       Oid             indexoid = lfirsti(indexoidscan);
+                                       HeapTuple       indexTuple;
+                                       List *attrl;
+                                       indexTuple = SearchSysCacheTuple(INDEXRELID,
+                                                                                                        ObjectIdGetDatum(indexoid),
+                                                                                                        0, 0, 0);
+                                       if (!HeapTupleIsValid(indexTuple))
+                                               elog(ERROR, "transformFkeyGetPrimaryKey: index %u not found",
+                                                        indexoid);
+                                       indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
+
+                                       if (indexStruct->indisunique) {
+                                               /* go through the fkconstraint->pk_attrs list */
+                                               foreach(attrl, fkconstraint->pk_attrs) {
+                                                       Ident *attr=lfirst(attrl);
+                                                       found=0;
+                                                       for (i = 0; i < INDEX_MAX_KEYS && indexStruct->indkey[i] != 0; i++)
+                                                       {
+                                                               int pkattno = indexStruct->indkey[i];
                                                                if (pkattno>0) {
                                                                        char *name = NameStr(rel_attrs[pkattno-1]->attname);
-                                                                       if (strcmp(name, attr->name)==0) {
-                                                                               found=1;
-                                                                               break;
-                                                                       }
+                                                                       if (strcmp(name, attr->name)==0) {
+                                                                               found=1;
+                                                                               break;
+                                                                       }
                                                                }
-                                                       }
-                                                       if (!found)
-                                                               break;
-                                               }
-                                       }
-                                       if (found)
-                                               break;          
-                                       indexStruct = NULL;
-                               }
-                               if (!found)
-                                       elog(ERROR, "UNIQUE constraint matching given keys for referenced table \"%s\" not found",
-                                                fkconstraint->pktable_name);
-
-                               freeList(indexoidlist);
-                               heap_close(pkrel, NoLock);
-
-                               rel_attrs = rel->rd_att->attrs;
-                               if (fkconstraint->fk_attrs!=NIL) {
-                                       int found=0;
-                                       List *fkattrs;
-                                       Ident *fkattr;
-                                       foreach(fkattrs, fkconstraint->fk_attrs) {
-                                               int count=0;
-                                               found=0;
-                                               fkattr=lfirst(fkattrs);
-                                               for (; count < rel->rd_att->natts; count++) {
-                                                       char *name = NameStr(rel->rd_att->attrs[count]->attname);
-                                                       if (strcmp(name, fkattr->name)==0) {
-                                                               found=1;
-                                                               break;
-                                                       }
-                                               }
-                                               if (!found)
-                                                       break;
-                                       }
-                                       if (!found)
-                                               elog(ERROR, "columns referenced in foreign key constraint not found.");
-                               }
-
-                               trig.tgoid = 0;
-                               if (fkconstraint->constr_name)
-                                       trig.tgname = fkconstraint->constr_name;
-                               else
-                                       trig.tgname = "<unknown>";
-                               trig.tgfoid = 0;
-                               trig.tgtype = 0;
-                               trig.tgenabled = TRUE;
-                               trig.tgisconstraint = TRUE;
-                               trig.tginitdeferred = FALSE;
-                               trig.tgdeferrable = FALSE;
-
-                               trig.tgargs = (char **) palloc(
-                                        sizeof(char *) * (4 + length(fkconstraint->fk_attrs)
-                                                                          + length(fkconstraint->pk_attrs)));
-
-                               if (fkconstraint->constr_name)
-                                       trig.tgargs[0] = fkconstraint->constr_name;
-                               else
-                                       trig.tgargs[0] = "<unknown>";
-                               trig.tgargs[1] = (char *) relationName;
-                               trig.tgargs[2] = fkconstraint->pktable_name;
-                               trig.tgargs[3] = fkconstraint->match_type;
-                               count = 4;
-                               foreach(list, fkconstraint->fk_attrs)
+                                                       }
+                                                       if (!found)
+                                                               break;
+                                               }
+                                       }
+                                       if (found)
+                                               break;          
+                                       indexStruct = NULL;
+                               }
+                       if (!found)
+                               elog(ERROR, "UNIQUE constraint matching given keys for referenced table \"%s\" not found",
+                                        fkconstraint->pktable_name);
+
+                       freeList(indexoidlist);
+                       heap_close(pkrel, NoLock);
+
+                       rel_attrs = rel->rd_att->attrs;
+                       if (fkconstraint->fk_attrs!=NIL) {
+                               int found=0;
+                               List *fkattrs;
+                               Ident *fkattr;
+                               foreach(fkattrs, fkconstraint->fk_attrs) {
+                                       int count=0;
+                                       found=0;
+                                       fkattr=lfirst(fkattrs);
+                                       for (; count < rel->rd_att->natts; count++) {
+                                               char *name = NameStr(rel->rd_att->attrs[count]->attname);
+                                               if (strcmp(name, fkattr->name)==0) {
+                                                       found=1;
+                                                       break;
+                                               }
+                                       }
+                                       if (!found)
+                                               break;
+                               }
+                               if (!found)
+                                       elog(ERROR, "columns referenced in foreign key constraint not found.");
+                       }
+
+                       trig.tgoid = 0;
+                       if (fkconstraint->constr_name)
+                               trig.tgname = fkconstraint->constr_name;
+                       else
+                               trig.tgname = "<unknown>";
+                       trig.tgfoid = 0;
+                       trig.tgtype = 0;
+                       trig.tgenabled = TRUE;
+                       trig.tgisconstraint = TRUE;
+                       trig.tginitdeferred = FALSE;
+                       trig.tgdeferrable = FALSE;
+
+                       trig.tgargs = (char **) palloc(
+                               sizeof(char *) * (4 + length(fkconstraint->fk_attrs)
+                                                                 + length(fkconstraint->pk_attrs)));
+
+                       if (fkconstraint->constr_name)
+                               trig.tgargs[0] = fkconstraint->constr_name;
+                       else
+                               trig.tgargs[0] = "<unknown>";
+                       trig.tgargs[1] = (char *) relationName;
+                       trig.tgargs[2] = fkconstraint->pktable_name;
+                       trig.tgargs[3] = fkconstraint->match_type;
+                       count = 4;
+                       foreach(list, fkconstraint->fk_attrs)
                                {
                                        Ident      *fk_at = lfirst(list);
 
                                        trig.tgargs[count++] = fk_at->name;
                                }
-                               foreach(list, fkconstraint->pk_attrs)
+                       foreach(list, fkconstraint->pk_attrs)
                                {
                                        Ident      *pk_at = lfirst(list);
 
                                        trig.tgargs[count++] = pk_at->name;
                                }
-                               trig.tgnargs = count;
+                       trig.tgnargs = count;
 
-                               scan = heap_beginscan(rel, false, SnapshotNow, 0, NULL);
-                               AssertState(scan != NULL);
+                       scan = heap_beginscan(rel, false, SnapshotNow, 0, NULL);
+                       AssertState(scan != NULL);
 
-                               while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
-                               {
-                                       /* Make a call to the check function */
-                                       /* No parameters are passed, but we do set a context */
-                                       FunctionCallInfoData    fcinfo;
-                                       TriggerData                             trigdata;
-
-                                       MemSet(&fcinfo, 0, sizeof(fcinfo));
-                                       /* We assume RI_FKey_check_ins won't look at flinfo... */
+                       while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
+                       {
+                               /* Make a call to the check function */
+                               /* No parameters are passed, but we do set a context */
+                               FunctionCallInfoData    fcinfo;
+                               TriggerData                             trigdata;
 
-                                       trigdata.type = T_TriggerData;
-                                       trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
-                                       trigdata.tg_relation = rel;
-                                       trigdata.tg_trigtuple = tuple;
-                                       trigdata.tg_newtuple = NULL;
-                                       trigdata.tg_trigger = &trig;
+                               MemSet(&fcinfo, 0, sizeof(fcinfo));
+                               /* We assume RI_FKey_check_ins won't look at flinfo... */
 
-                                       fcinfo.context = (Node *) &trigdata;
+                               trigdata.type = T_TriggerData;
+                               trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
+                               trigdata.tg_relation = rel;
+                               trigdata.tg_trigtuple = tuple;
+                               trigdata.tg_newtuple = NULL;
+                               trigdata.tg_trigger = &trig;
 
-                                       RI_FKey_check_ins(&fcinfo);
-                               }
-                               heap_endscan(scan);
-                               heap_close(rel, NoLock);                /* close rel but keep
-                                                                                                * lock! */
+                               fcinfo.context = (Node *) &trigdata;
 
-                               pfree(trig.tgargs);
+                               RI_FKey_check_ins(&fcinfo);
                        }
+                       heap_endscan(scan);
+                       heap_close(rel, NoLock);                /* close rel but keep
+                                                                                        * lock! */
+
+                       pfree(trig.tgargs);
                        break;
+               }
                default:
                        elog(ERROR, "ALTER TABLE / ADD CONSTRAINT unable to determine type of constraint passed");
        }
@@ -1449,7 +1448,6 @@ AlterTableDropConstraint(const char *relationName,
 }
 
 
-
 /*
  * ALTER TABLE OWNER
  */
@@ -1464,14 +1462,14 @@ AlterTableOwner(const char *relationName, const char *newOwnerName)
        /*
         * first check that we are a superuser
         */
-       if (! superuser() )
+       if (! superuser())
                elog(ERROR, "ALTER TABLE: permission denied");
 
        /*
         * look up the new owner in pg_shadow and get the sysid
         */
        tuple = SearchSysCacheTuple(SHADOWNAME, PointerGetDatum(newOwnerName),
-                                                          0, 0, 0);
+                                                               0, 0, 0);
        if (!HeapTupleIsValid(tuple))
                elog(ERROR, "ALTER TABLE: user \"%s\" not found", newOwnerName);
 
@@ -1510,10 +1508,9 @@ AlterTableOwner(const char *relationName, const char *newOwnerName)
         */
        heap_freetuple(tuple);
        heap_close(class_rel, RowExclusiveLock);
-
-       return;
 }
 
+
 /*
  * ALTER TABLE CREATE TOAST TABLE
  */
@@ -1579,6 +1576,7 @@ AlterTableCreateToastTable(const char *relationName, bool silent)
         * allow to create TOAST tables for views. But why not - someone
         * can insert into a view, so it shouldn't be impossible to hide
         * huge data there :-)
+        *
         * Not any more.
         */
        if (((Form_pg_class) GETSTRUCT(reltup))->relkind != RELKIND_RELATION)
@@ -1799,8 +1797,7 @@ LockTableCommand(LockStmt *lockstmt)
 }
 
 
-static
-bool
+static bool
 is_viewr(char *name)
 {
        Relation rel = heap_openr(name, NoLock);
@@ -1812,18 +1809,15 @@ is_viewr(char *name)
        return retval;
 }
 
-static
-bool
-is_view (Relation rel)
+static bool
+is_view(Relation rel)
 {
        Relation        RewriteRelation;
        HeapScanDesc scanDesc;
        ScanKeyData scanKeyData;
        HeapTuple       tuple;
        Form_pg_rewrite data;
-
-
-       bool retval = 0;
+       bool retval = false;
 
        /*
         * Open the pg_rewrite relation.
@@ -1849,7 +1843,7 @@ is_view (Relation rel)
                        data = (Form_pg_rewrite) GETSTRUCT(tuple);
                        if (data->ev_type == '1')
                        {
-                               retval = 1;
+                               retval = true;
                                break;
                        }
                }
index e39c24f8dfa6f936c3e75c6a3ee80e3a1744ba06..b6485850eb3bdd911402afb3c0a3953826cd615a 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/Attic/creatinh.c,v 1.63 2000/08/04 06:12:11 inoue Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/Attic/creatinh.c,v 1.64 2000/09/12 21:06:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -259,7 +259,6 @@ change_varattnos_walker(Node *node, const AttrNumber *newattno)
        {
                Var     *var = (Var *) node;
 
-               Assert(newattno != NULL);
                if (var->varlevelsup == 0 && var->varno == 1)
                {
                        /*
@@ -270,18 +269,19 @@ change_varattnos_walker(Node *node, const AttrNumber *newattno)
                         */
                        Assert(newattno[var->varattno - 1] > 0);
                        var->varattno = newattno[var->varattno - 1];
-                       return true;
                }
-               else
-                       return false;
+               return false;
        }
-       return expression_tree_walker(node, change_varattnos_walker, (void *)newattno);
+       return expression_tree_walker(node, change_varattnos_walker,
+                                                                 (void *) newattno);
 }
+
 static bool
 change_varattnos_of_a_node(Node *node, const AttrNumber *newattno)
 {
-       return expression_tree_walker(node, change_varattnos_walker, (void *)newattno);
+       return change_varattnos_walker(node, newattno);
 }
+
 /*
  * MergeAttributes
  *             Returns new schema given initial schema and supers.
index 25915fe42bd014e0c552763d3b4067b2b5d88947..2b3d8b85726ab745d5e57f1ffa8fbd6dbe2dbd72 100644 (file)
@@ -5,7 +5,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994-5, Regents of the University of California
  *
- * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.57 2000/06/18 22:43:58 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.58 2000/09/12 21:06:47 tgl Exp $
  *
  */
 
@@ -229,21 +229,21 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
 
                                appendStringInfo(str, " on %s",
                                                                 stringStringInfo(rte->relname));
-                               if (rte->ref != NULL)
+                               if (rte->alias != NULL)
                                {
-                                       if ((strcmp(rte->ref->relname, rte->relname) != 0)
-                                               || (length(rte->ref->attrs) > 0))
+                                       if ((strcmp(rte->alias->relname, rte->relname) != 0)
+                                               || (length(rte->alias->attrs) > 0))
                                        {
                                                appendStringInfo(str, " %s",
-                                                                       stringStringInfo(rte->ref->relname));
+                                                                       stringStringInfo(rte->alias->relname));
 
-                                               if (length(rte->ref->attrs) > 0)
+                                               if (length(rte->alias->attrs) > 0)
                                                {
                                                        List       *c;
                                                        int                     firstEntry = true;
 
                                                        appendStringInfo(str, " (");
-                                                       foreach(c, rte->ref->attrs)
+                                                       foreach(c, rte->alias->attrs)
                                                        {
                                                                if (!firstEntry)
                                                                {
index af10805b71f39f28ff843257c21217c60cfe8970..d1d630009991fc8ce3df54a052cabe9811bcbd05 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $Id: view.c,v 1.47 2000/09/12 04:49:07 momjian Exp $
+ *     $Id: view.c,v 1.48 2000/09/12 21:06:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -116,12 +116,14 @@ char *
 MakeRetrieveViewRuleName(char *viewName)
 {
        char       *buf;
+#ifdef MULTIBYTE
+       int                     len;
+#endif
 
        buf = palloc(strlen(viewName) + 5);
        snprintf(buf, strlen(viewName) + 5, "_RET%s", viewName);
 
 #ifdef MULTIBYTE
-       int len;
        len = pg_mbcliplen(buf,strlen(buf),NAMEDATALEN-1);
        buf[len] = '\0';
 #else
@@ -203,6 +205,10 @@ DefineViewRules(char *viewName, Query *viewParse)
  * Of course we must also increase the 'varnos' of all the Var nodes
  * by 2...
  *
+ * These extra RT entries are not actually used in the query, obviously.
+ * We add them so that views look the same as ON SELECT rules ---
+ * the rule rewriter assumes that ALL rules have OLD and NEW RTEs.
+ *
  * NOTE: these are destructive changes. It would be difficult to
  * make a complete copy of the parse tree and make the changes
  * in the copy.
@@ -211,43 +217,32 @@ DefineViewRules(char *viewName, Query *viewParse)
 static void
 UpdateRangeTableOfViewParse(char *viewName, Query *viewParse)
 {
-       List       *old_rt;
        List       *new_rt;
        RangeTblEntry *rt_entry1,
                           *rt_entry2;
 
-       /*
-        * first offset all var nodes by 2
-        */
-       OffsetVarNodes((Node *) viewParse->targetList, 2, 0);
-       OffsetVarNodes(viewParse->qual, 2, 0);
-
-       OffsetVarNodes(viewParse->havingQual, 2, 0);
-
-
-       /*
-        * find the old range table...
-        */
-       old_rt = viewParse->rtable;
-
        /*
         * create the 2 new range table entries and form the new range
         * table... OLD first, then NEW....
         */
-       rt_entry1 = addRangeTableEntry(NULL, (char *) viewName,
+       rt_entry1 = addRangeTableEntry(NULL, viewName,
                                                                   makeAttr("*OLD*", NULL),
-                                                                  FALSE, FALSE, FALSE);
-       rt_entry2 = addRangeTableEntry(NULL, (char *) viewName,
+                                                                  false, false);
+       rt_entry2 = addRangeTableEntry(NULL, viewName,
                                                                   makeAttr("*NEW*", NULL),
-                                                                  FALSE, FALSE, FALSE);
-       new_rt = lcons(rt_entry2, old_rt);
-       new_rt = lcons(rt_entry1, new_rt);
+                                                                  false, false);
+       new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable));
 
        /*
         * Now the tricky part.... Update the range table in place... Be
         * careful here, or hell breaks loooooooooooooOOOOOOOOOOOOOOOOOOSE!
         */
        viewParse->rtable = new_rt;
+
+       /*
+        * now offset all var nodes by 2, and jointree RT indexes too.
+        */
+       OffsetVarNodes((Node *) viewParse, 2, 0);
 }
 
 /*-------------------------------------------------------------------
@@ -270,7 +265,7 @@ DefineView(char *viewName, Query *viewParse)
        viewTlist = viewParse->targetList;
 
        /*
-        * Create the "view" relation NOTE: if it already exists, the xaxt
+        * Create the "view" relation NOTE: if it already exists, the xact
         * will be aborted.
         */
        DefineVirtualRelation(viewName, viewTlist);
index d25530b44fb9d0e7219f828cd443b5c346676b00..d46e0d30f55eaf87f965973b868ee6db0260a2af 100644 (file)
@@ -27,7 +27,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.126 2000/09/12 04:49:08 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.127 2000/09/12 21:06:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -399,16 +399,17 @@ ExecCheckQueryPerms(CmdType operation, Query *parseTree, Plan *plan)
         * If we have a result relation, determine whether the result rel is
         * scanned or merely written.  If scanned, we will insist on read
         * permission as well as modify permission.
+        *
+        * Note: it might look faster to apply rangeTableEntry_used(), but
+        * that's not correct since it will trigger on jointree references
+        * to the RTE.  We only want to know about actual Var nodes.
         */
        if (resultRelation > 0)
        {
-               List       *qvars = pull_varnos(parseTree->qual);
-               List       *tvars = pull_varnos((Node *) parseTree->targetList);
+               List       *qvars = pull_varnos((Node *) parseTree);
 
-               resultIsScanned = (intMember(resultRelation, qvars) ||
-                                                  intMember(resultRelation, tvars));
+               resultIsScanned = intMember(resultRelation, qvars);
                freeList(qvars);
-               freeList(tvars);
        }
 
        /*
@@ -571,8 +572,8 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation,
                                  bool isResultRelation, bool resultIsScanned)
 {
        char       *relName;
+       Oid                     userid;
        int32           aclcheck_result;
-       Oid             userid;
 
        if (rte->skipAcl)
        {
@@ -703,13 +704,11 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
                 */
                RelationInfo *resultRelationInfo;
                Index           resultRelationIndex;
-               RangeTblEntry *rtentry;
                Oid                     resultRelationOid;
                Relation        resultRelationDesc;
 
                resultRelationIndex = resultRelation;
-               rtentry = rt_fetch(resultRelationIndex, rangeTable);
-               resultRelationOid = rtentry->relid;
+               resultRelationOid = getrelid(resultRelationIndex, rangeTable);
                resultRelationDesc = heap_open(resultRelationOid, RowExclusiveLock);
 
                if (resultRelationDesc->rd_rel->relkind == RELKIND_SEQUENCE)
@@ -770,7 +769,7 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
 
                        if (!(rm->info & ROW_MARK_FOR_UPDATE))
                                continue;
-                       relid = rt_fetch(rm->rti, rangeTable)->relid;
+                       relid = getrelid(rm->rti, rangeTable);
                        relation = heap_open(relid, RowShareLock);
                        erm = (execRowMark *) palloc(sizeof(execRowMark));
                        erm->relation = relation;
@@ -1623,10 +1622,10 @@ ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate)
                rte = makeNode(RangeTblEntry);
 
                rte->relname = RelationGetRelationName(rel);
-               rte->ref = makeNode(Attr);
-               rte->ref->relname = rte->relname;
                rte->relid = RelationGetRelid(rel);
-               /* inh, inFromCl, inJoinSet, skipAcl won't be used, leave them zero */
+               rte->eref = makeNode(Attr);
+               rte->eref->relname = rte->relname;
+               /* inh, inFromCl, skipAcl won't be used, leave them zero */
 
                /* Set up single-entry range table */
                econtext->ecxt_range_table = lcons(rte, NIL);
index 37b092fc20f03201343eaa84e4b53619a41739b3..05474bc64bc0ce9703e9349868f5bc86c9c12bfd 100644 (file)
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.38 2000/07/12 02:37:02 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.39 2000/09/12 21:06:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
  *                                                               type of tuple in a slot
  *
  *      CONVENIENCE INITIALIZATION ROUTINES
- *             ExecInitResultTupleSlot    \    convience routines to initialize
+ *             ExecInitResultTupleSlot    \    convenience routines to initialize
  *             ExecInitScanTupleSlot           \       the various tuple slots for nodes
- *             ExecInitMarkedTupleSlot         /  which store copies of tuples.
- *             ExecInitOuterTupleSlot     /
- *             ExecInitHashTupleSlot     /
+ *             ExecInitExtraTupleSlot          /       which store copies of tuples.
+ *             ExecInitNullTupleSlot      /
  *
  *      old routines:
  *             ExecGetTupType                  - get type of tuple returned by this node
@@ -560,10 +559,11 @@ ExecSlotDescriptorIsNew(TupleTableSlot *slot)     /* slot to inspect */
  * ----------------------------------------------------------------
  */
 /* --------------------------------
- *             ExecInit{Result,Scan,Raw,Marked,Outer,Hash}TupleSlot
+ *             ExecInit{Result,Scan,Extra}TupleSlot
  *
- *             These are convenience routines to initialize the specfied slot
- *             in nodes inheriting the appropriate state.
+ *             These are convenience routines to initialize the specified slot
+ *             in nodes inheriting the appropriate state.  ExecInitExtraTupleSlot
+ *             is used for initializing special-purpose slots.
  * --------------------------------
  */
 #define INIT_SLOT_DEFS \
@@ -583,7 +583,7 @@ ExecInitResultTupleSlot(EState *estate, CommonState *commonstate)
 {
        INIT_SLOT_DEFS;
        INIT_SLOT_ALLOC;
-       commonstate->cs_ResultTupleSlot = (TupleTableSlot *) slot;
+       commonstate->cs_ResultTupleSlot = slot;
 }
 
 /* ----------------
@@ -595,50 +595,51 @@ ExecInitScanTupleSlot(EState *estate, CommonScanState *commonscanstate)
 {
        INIT_SLOT_DEFS;
        INIT_SLOT_ALLOC;
-       commonscanstate->css_ScanTupleSlot = (TupleTableSlot *) slot;
+       commonscanstate->css_ScanTupleSlot = slot;
 }
 
-#ifdef NOT_USED
 /* ----------------
- *             ExecInitMarkedTupleSlot
+ *             ExecInitExtraTupleSlot
  * ----------------
  */
-void
-ExecInitMarkedTupleSlot(EState *estate, MergeJoinState *mergestate)
+TupleTableSlot *
+ExecInitExtraTupleSlot(EState *estate)
 {
        INIT_SLOT_DEFS;
        INIT_SLOT_ALLOC;
-       mergestate->mj_MarkedTupleSlot = (TupleTableSlot *) slot;
+       return slot;
 }
 
-#endif
-
 /* ----------------
- *             ExecInitOuterTupleSlot
+ *             ExecInitNullTupleSlot
+ *
+ * Build a slot containing an all-nulls tuple of the given type.
+ * This is used as a substitute for an input tuple when performing an
+ * outer join.
  * ----------------
  */
-void
-ExecInitOuterTupleSlot(EState *estate, HashJoinState *hashstate)
+TupleTableSlot *
+ExecInitNullTupleSlot(EState *estate, TupleDesc tupType)
 {
-       INIT_SLOT_DEFS;
-       INIT_SLOT_ALLOC;
-       hashstate->hj_OuterTupleSlot = slot;
-}
+       TupleTableSlot*   slot = ExecInitExtraTupleSlot(estate);
+       /*
+        * Since heap_getattr() will treat attributes beyond a tuple's t_natts
+        * as being NULL, we can make an all-nulls tuple just by making it be of
+        * zero length.  However, the slot descriptor must match the real tupType.
+        */
+       HeapTuple       nullTuple;
+       Datum           values[1];
+       char            nulls[1];
+       static struct tupleDesc NullTupleDesc;          /* we assume this inits to
+                                                                                                * zeroes */
 
-/* ----------------
- *             ExecInitHashTupleSlot
- * ----------------
- */
-#ifdef NOT_USED
-void
-ExecInitHashTupleSlot(EState *estate, HashJoinState *hashstate)
-{
-       INIT_SLOT_DEFS;
-       INIT_SLOT_ALLOC;
-       hashstate->hj_HashTupleSlot = slot;
+       ExecSetSlotDescriptor(slot, tupType);
+
+       nullTuple = heap_formtuple(&NullTupleDesc, values, nulls);
+
+       return ExecStoreTuple(nullTuple, slot, InvalidBuffer, true);
 }
 
-#endif
 
 static TupleTableSlot *
 NodeGetResultTupleSlot(Plan *node)
index 63c1e9e157f95e143a518c5676aae1a06781af55..39ae7dff10a58df9b7172861877f204c7ab28b6a 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.65 2000/08/22 04:06:19 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.66 2000/09/12 21:06:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -275,53 +275,17 @@ void
 ExecAssignResultTypeFromTL(Plan *node, CommonState *commonstate)
 {
        List       *targetList;
-       int                     i;
+       TupleDesc       tupDesc;
        int                     len;
-       List       *tl;
-       TargetEntry *tle;
-       List       *fjtl;
-       TupleDesc       origTupDesc;
 
        targetList = node->targetlist;
-       origTupDesc = ExecTypeFromTL(targetList);
+       tupDesc = ExecTypeFromTL(targetList);
        len = ExecTargetListLength(targetList);
 
-       fjtl = NIL;
-       tl = targetList;
-       i = 0;
-       while (tl != NIL || fjtl != NIL)
-       {
-               if (fjtl != NIL)
-               {
-                       tle = lfirst(fjtl);
-                       fjtl = lnext(fjtl);
-               }
-               else
-               {
-                       tle = lfirst(tl);
-                       tl = lnext(tl);
-               }
-#ifdef SETS_FIXED
-               if (!tl_is_resdom(tle))
-               {
-                       Fjoin      *fj = (Fjoin *) lfirst(tle);
-
-                       /* it is a FJoin */
-                       fjtl = lnext(tle);
-                       tle = fj->fj_innerNode;
-               }
-#endif
-               i++;
-       }
-
        if (len > 0)
-       {
-               ExecAssignResultType(commonstate,
-                                                        origTupDesc);
-       }
+               ExecAssignResultType(commonstate, tupDesc);
        else
-               ExecAssignResultType(commonstate,
-                                                        (TupleDesc) NULL);
+               ExecAssignResultType(commonstate, (TupleDesc) NULL);
 }
 
 /* ----------------
index 4b3b4a825050e4acdaa5a17c1f3c93030a2ec81f..d0eef4380b7650e31f37930013c15a3e205196d2 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.33 2000/08/24 03:29:03 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.34 2000/09/12 21:06:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -50,7 +50,8 @@ ExecHashJoin(HashJoin *node)
        Hash       *hashNode;
        List       *hjclauses;
        Expr       *clause;
-       List       *qual;
+       List       *joinqual;
+       List       *otherqual;
        ScanDirection dir;
        TupleTableSlot *inntuple;
        Node       *outerVar;
@@ -70,11 +71,12 @@ ExecHashJoin(HashJoin *node)
        hjstate = node->hashjoinstate;
        hjclauses = node->hashclauses;
        clause = lfirst(hjclauses);
-       estate = node->join.state;
-       qual = node->join.qual;
+       estate = node->join.plan.state;
+       joinqual = node->join.joinqual;
+       otherqual = node->join.plan.qual;
        hashNode = (Hash *) innerPlan(node);
        outerNode = outerPlan(node);
-       hashPhaseDone = node->hashdone;
+       hashPhaseDone = hjstate->hj_hashdone;
        dir = estate->es_direction;
 
        /* -----------------
@@ -132,7 +134,7 @@ ExecHashJoin(HashJoin *node)
                        hashNode->hashstate->hashtable = hashtable;
                        innerTupleSlot = ExecProcNode((Plan *) hashNode, (Plan *) node);
                }
-               node->hashdone = true;
+               hjstate->hj_hashdone = true;
                /* ----------------
                 * Open temp files for outer batches, if needed.
                 * Note that file buffers are palloc'd in regular executor context.
@@ -153,11 +155,10 @@ ExecHashJoin(HashJoin *node)
 
        for (;;)
        {
-
                /*
-                * if the current outer tuple is nil, get a new one
+                * If we don't have an outer tuple, get the next one
                 */
-               if (TupIsNull(outerTupleSlot))
+               if (hjstate->hj_NeedNewOuter)
                {
                        outerTupleSlot = ExecHashJoinOuterGetTuple(outerNode,
                                                                                                           (Plan *) node,
@@ -173,11 +174,15 @@ ExecHashJoin(HashJoin *node)
                                return NULL;
                        }
 
+                       hjstate->jstate.cs_OuterTupleSlot = outerTupleSlot;
+                       econtext->ecxt_outertuple = outerTupleSlot;
+                       hjstate->hj_NeedNewOuter = false;
+                       hjstate->hj_MatchedOuter = false;
+
                        /*
                         * now we have an outer tuple, find the corresponding bucket
                         * for this tuple from the hash table
                         */
-                       econtext->ecxt_outertuple = outerTupleSlot;
                        hjstate->hj_CurBucketNo = ExecHashGetBucket(hashtable, econtext,
                                                                                                                outerVar);
                        hjstate->hj_CurTuple = NULL;
@@ -205,7 +210,7 @@ ExecHashJoin(HashJoin *node)
                                        hashtable->outerBatchSize[batchno]++;
                                        ExecHashJoinSaveTuple(outerTupleSlot->val,
                                                                         hashtable->outerBatchFile[batchno]);
-                                       ExecClearTuple(outerTupleSlot);
+                                       hjstate->hj_NeedNewOuter = true;
                                        continue;       /* loop around for a new outer tuple */
                                }
                        }
@@ -223,7 +228,7 @@ ExecHashJoin(HashJoin *node)
                                break;                  /* out of matches */
 
                        /*
-                        * we've got a match, but still need to test qpqual
+                        * we've got a match, but still need to test non-hashed quals
                         */
                        inntuple = ExecStoreTuple(curtuple,
                                                                          hjstate->hj_HashTupleSlot,
@@ -231,35 +236,77 @@ ExecHashJoin(HashJoin *node)
                                                                          false);       /* don't pfree this tuple */
                        econtext->ecxt_innertuple = inntuple;
 
-                       /* reset temp memory each time to avoid leaks from qpqual */
+                       /* reset temp memory each time to avoid leaks from qual expr */
                        ResetExprContext(econtext);
 
                        /* ----------------
                         * if we pass the qual, then save state for next call and
                         * have ExecProject form the projection, store it
                         * in the tuple table, and return the slot.
+                        *
+                        * Only the joinquals determine MatchedOuter status,
+                        * but all quals must pass to actually return the tuple.
                         * ----------------
                         */
-                       if (ExecQual(qual, econtext, false))
+                       if (ExecQual(joinqual, econtext, false))
                        {
-                               TupleTableSlot *result;
+                               hjstate->hj_MatchedOuter = true;
 
-                               hjstate->jstate.cs_OuterTupleSlot = outerTupleSlot;
-                               result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone);
-                               if (isDone != ExprEndResult)
+                               if (otherqual == NIL || ExecQual(otherqual, econtext, false))
                                {
-                                       hjstate->jstate.cs_TupFromTlist = (isDone == ExprMultipleResult);
-                                       return result;
+                                       TupleTableSlot *result;
+
+                                       result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone);
+
+                                       if (isDone != ExprEndResult)
+                                       {
+                                               hjstate->jstate.cs_TupFromTlist =
+                                                       (isDone == ExprMultipleResult);
+                                               return result;
+                                       }
                                }
                        }
                }
 
                /* ----------------
                 *       Now the current outer tuple has run out of matches,
-                *       so we free it and loop around to get a new outer tuple.
+                *       so check whether to emit a dummy outer-join tuple.
+                *       If not, loop around to get a new outer tuple.
                 * ----------------
                 */
-               ExecClearTuple(outerTupleSlot);
+               hjstate->hj_NeedNewOuter = true;
+
+               if (! hjstate->hj_MatchedOuter &&
+                       node->join.jointype == JOIN_LEFT)
+               {
+                       /*
+                        * We are doing an outer join and there were no join matches
+                        * for this outer tuple.  Generate a fake join tuple with
+                        * nulls for the inner tuple, and return it if it passes
+                        * the non-join quals.
+                        */
+                       econtext->ecxt_innertuple = hjstate->hj_NullInnerTupleSlot;
+
+                       if (ExecQual(otherqual, econtext, false))
+                       {
+                               /* ----------------
+                                *      qualification was satisfied so we project and
+                                *      return the slot containing the result tuple
+                                *      using ExecProject().
+                                * ----------------
+                                */
+                               TupleTableSlot *result;
+
+                               result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone);
+
+                               if (isDone != ExprEndResult)
+                               {
+                                       hjstate->jstate.cs_TupFromTlist =
+                                               (isDone == ExprMultipleResult);
+                                       return result;
+                               }
+                       }
+               }
        }
 }
 
@@ -280,14 +327,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
         *      assign the node's execution state
         * ----------------
         */
-       node->join.state = estate;
+       node->join.plan.state = estate;
 
        /* ----------------
         * create state structure
         * ----------------
         */
        hjstate = makeNode(HashJoinState);
-
        node->hashjoinstate = hjstate;
 
        /* ----------------
@@ -298,14 +344,6 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
         */
        ExecAssignExprContext(estate, &hjstate->jstate);
 
-#define HASHJOIN_NSLOTS 2
-       /* ----------------
-        *      tuple table initialization
-        * ----------------
-        */
-       ExecInitResultTupleSlot(estate, &hjstate->jstate);
-       ExecInitOuterTupleSlot(estate, hjstate);
-
        /* ----------------
         * initializes child nodes
         * ----------------
@@ -316,6 +354,28 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
        ExecInitNode(outerNode, estate, (Plan *) node);
        ExecInitNode((Plan *) hashNode, estate, (Plan *) node);
 
+#define HASHJOIN_NSLOTS 3
+       /* ----------------
+        *      tuple table initialization
+        * ----------------
+        */
+       ExecInitResultTupleSlot(estate, &hjstate->jstate);
+       hjstate->hj_OuterTupleSlot = ExecInitExtraTupleSlot(estate);
+
+       switch (node->join.jointype)
+       {
+               case JOIN_INNER:
+                       break;
+               case JOIN_LEFT:
+                       hjstate->hj_NullInnerTupleSlot =
+                               ExecInitNullTupleSlot(estate,
+                                                                         ExecGetTupType((Plan *) hashNode));
+                       break;
+               default:
+                       elog(ERROR, "ExecInitHashJoin: unsupported join type %d",
+                                (int) node->join.jointype);
+       }
+
        /* ----------------
         *      now for some voodoo.  our temporary tuple slot
         *      is actually the result tuple slot of the Hash node
@@ -331,11 +391,6 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
 
                hjstate->hj_HashTupleSlot = slot;
        }
-       hjstate->hj_OuterTupleSlot->ttc_tupleDescriptor = ExecGetTupType(outerNode);
-
-/*
-       hjstate->hj_OuterTupleSlot->ttc_execTupDescriptor = ExecGetExecTupDesc(outerNode);
-*/
 
        /* ----------------
         *      initialize tuple type and projection info
@@ -344,20 +399,25 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
        ExecAssignResultTypeFromTL((Plan *) node, &hjstate->jstate);
        ExecAssignProjectionInfo((Plan *) node, &hjstate->jstate);
 
+       ExecSetSlotDescriptor(hjstate->hj_OuterTupleSlot,
+                                                 ExecGetTupType(outerNode));
+
        /* ----------------
         *      initialize hash-specific info
         * ----------------
         */
 
-       node->hashdone = false;
+       hjstate->hj_hashdone = false;
 
        hjstate->hj_HashTable = (HashJoinTable) NULL;
        hjstate->hj_CurBucketNo = 0;
        hjstate->hj_CurTuple = (HashJoinTuple) NULL;
        hjstate->hj_InnerHashKey = (Node *) NULL;
 
-       hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot *) NULL;
+       hjstate->jstate.cs_OuterTupleSlot = NULL;
        hjstate->jstate.cs_TupFromTlist = false;
+       hjstate->hj_NeedNewOuter = true;
+       hjstate->hj_MatchedOuter = false;
 
        return TRUE;
 }
@@ -646,10 +706,10 @@ ExecReScanHashJoin(HashJoin *node, ExprContext *exprCtxt, Plan *parent)
 {
        HashJoinState *hjstate = node->hashjoinstate;
 
-       if (!node->hashdone)
+       if (!hjstate->hj_hashdone)
                return;
 
-       node->hashdone = false;
+       hjstate->hj_hashdone = false;
 
        /*
         * Unfortunately, currently we have to destroy hashtable in all
@@ -667,6 +727,8 @@ ExecReScanHashJoin(HashJoin *node, ExprContext *exprCtxt, Plan *parent)
 
        hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot *) NULL;
        hjstate->jstate.cs_TupFromTlist = false;
+       hjstate->hj_NeedNewOuter = true;
+       hjstate->hj_MatchedOuter = false;
 
        /*
         * if chgParam of subnodes is not null then plans will be re-scanned
index 5a2f45028a0348d44ed9e75352eb472e9e96a44d..9d4ca0a8d54e91ec96ee390fce4ee390226bc8f8 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.37 2000/08/24 03:29:03 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.38 2000/09/12 21:06:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
  * INTERFACE ROUTINES
  *             ExecMergeJoin                   mergejoin outer and inner relations.
  *             ExecInitMergeJoin               creates and initializes run time states
- *             ExecEndMergeJoin                cleand up the node.
+ *             ExecEndMergeJoin                cleans up the node.
  *
  * NOTES
  *             Essential operation of the merge join algorithm is as follows:
- *             (** indicates the tuples satisfy the merge clause).
  *
  *             Join {                                                                                             -
  *                     get initial outer and inner tuples                              INITIALIZE
  *                     }                                                                                                  -
  *             }                                                                                                          -
  *
- *             Skip Outer {                                                                            SKIPOUTER
+ *             Skip Outer {                                                                            SKIPOUTER_BEGIN
  *                     if (inner == outer) Join Tuples                                 JOINTUPLES
- *                     while (outer < inner)                                                   SKIPOUTER
- *                             advance outer                                                           SKIPOUTER
- *                     if (outer > inner)                                                              SKIPOUTER
+ *                     while (outer < inner)                                                   SKIPOUTER_TEST
+ *                             advance outer                                                           SKIPOUTER_ADVANCE
+ *                     if (outer > inner)                                                              SKIPOUTER_TEST
  *                             Skip Inner                                                                      SKIPINNER
  *             }                                                                                                          -
  *
- *             Skip Inner {                                                                            SKIPINNER
+ *             Skip Inner {                                                                            SKIPINNER_BEGIN
  *                     if (inner == outer) Join Tuples                                 JOINTUPLES
- *                     while (outer > inner)                                                   SKIPINNER
- *                             advance inner                                                           SKIPINNER
- *                     if (outer < inner)                                                              SKIPINNER
+ *                     while (outer > inner)                                                   SKIPINNER_TEST
+ *                             advance inner                                                           SKIPINNER_ADVANCE
+ *                     if (outer < inner)                                                              SKIPINNER_TEST
  *                             Skip Outer                                                                      SKIPOUTER
  *             }                                                                                                          -
  *
@@ -68,6 +67,7 @@
 #include "postgres.h"
 
 #include "access/heapam.h"
+#include "access/printtup.h"
 #include "catalog/pg_operator.h"
 #include "executor/execdebug.h"
 #include "executor/execdefs.h"
@@ -273,52 +273,39 @@ MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
  * ----------------------------------------------------------------
  */
 #ifdef EXEC_MERGEJOINDEBUG
-void
-                       ExecMergeTupleDumpInner(ExprContext *econtext);
 
-void
-ExecMergeTupleDumpInner(ExprContext *econtext)
+static void
+ExecMergeTupleDumpOuter(MergeJoinState *mergestate)
 {
-       TupleTableSlot *innerSlot;
+       TupleTableSlot *outerSlot = mergestate->mj_OuterTupleSlot;
 
-       printf("==== inner tuple ====\n");
-       innerSlot = econtext->ecxt_innertuple;
-       if (TupIsNull(innerSlot))
+       printf("==== outer tuple ====\n");
+       if (TupIsNull(outerSlot))
                printf("(nil)\n");
        else
-               MJ_debugtup(innerSlot->val,
-                                       innerSlot->ttc_tupleDescriptor);
+               MJ_debugtup(outerSlot->val,
+                                       outerSlot->ttc_tupleDescriptor);
 }
 
-void
-                       ExecMergeTupleDumpOuter(ExprContext *econtext);
-
-void
-ExecMergeTupleDumpOuter(ExprContext *econtext)
+static void
+ExecMergeTupleDumpInner(MergeJoinState *mergestate)
 {
-       TupleTableSlot *outerSlot;
+       TupleTableSlot *innerSlot = mergestate->mj_InnerTupleSlot;
 
-       printf("==== outer tuple ====\n");
-       outerSlot = econtext->ecxt_outertuple;
-       if (TupIsNull(outerSlot))
+       printf("==== inner tuple ====\n");
+       if (TupIsNull(innerSlot))
                printf("(nil)\n");
        else
-               MJ_debugtup(outerSlot->val,
-                                       outerSlot->ttc_tupleDescriptor);
+               MJ_debugtup(innerSlot->val,
+                                       innerSlot->ttc_tupleDescriptor);
 }
 
-void ExecMergeTupleDumpMarked(ExprContext *econtext,
-                                                MergeJoinState *mergestate);
-
-void
-ExecMergeTupleDumpMarked(ExprContext *econtext,
-                                                MergeJoinState *mergestate)
+static void
+ExecMergeTupleDumpMarked(MergeJoinState *mergestate)
 {
-       TupleTableSlot *markedSlot;
+       TupleTableSlot *markedSlot = mergestate->mj_MarkedTupleSlot;
 
        printf("==== marked tuple ====\n");
-       markedSlot = mergestate->mj_MarkedTupleSlot;
-
        if (TupIsNull(markedSlot))
                printf("(nil)\n");
        else
@@ -326,17 +313,14 @@ ExecMergeTupleDumpMarked(ExprContext *econtext,
                                        markedSlot->ttc_tupleDescriptor);
 }
 
-void
-                       ExecMergeTupleDump(ExprContext *econtext, MergeJoinState *mergestate);
-
-void
-ExecMergeTupleDump(ExprContext *econtext, MergeJoinState *mergestate)
+static void
+ExecMergeTupleDump(MergeJoinState *mergestate)
 {
        printf("******** ExecMergeTupleDump ********\n");
 
-       ExecMergeTupleDumpInner(econtext);
-       ExecMergeTupleDumpOuter(econtext);
-       ExecMergeTupleDumpMarked(econtext, mergestate);
+       ExecMergeTupleDumpOuter(mergestate);
+       ExecMergeTupleDumpInner(mergestate);
+       ExecMergeTupleDumpMarked(mergestate);
 
        printf("******** \n");
 }
@@ -404,7 +388,8 @@ ExecMergeJoin(MergeJoin *node)
        List       *innerSkipQual;
        List       *outerSkipQual;
        List       *mergeclauses;
-       List       *qual;
+       List       *joinqual;
+       List       *otherqual;
        bool            qualResult;
        bool            compareResult;
        Plan       *innerPlan;
@@ -412,27 +397,48 @@ ExecMergeJoin(MergeJoin *node)
        Plan       *outerPlan;
        TupleTableSlot *outerTupleSlot;
        ExprContext *econtext;
-#ifdef ENABLE_OUTER_JOINS
-       /*
-        * These should be set from the expression context! - thomas
-        * 1999-02-20
-        */
-       static bool isLeftJoin = true;
-       static bool isRightJoin = false;
-#endif
+       bool            doFillOuter;
+       bool            doFillInner;
 
        /* ----------------
         *      get information from node
         * ----------------
         */
        mergestate = node->mergestate;
-       estate = node->join.state;
+       estate = node->join.plan.state;
        direction = estate->es_direction;
        innerPlan = innerPlan((Plan *) node);
        outerPlan = outerPlan((Plan *) node);
        econtext = mergestate->jstate.cs_ExprContext;
        mergeclauses = node->mergeclauses;
-       qual = node->join.qual;
+       joinqual = node->join.joinqual;
+       otherqual = node->join.plan.qual;
+
+       switch (node->join.jointype)
+       {
+               case JOIN_INNER:
+                       doFillOuter = false;
+                       doFillInner = false;
+                       break;
+               case JOIN_LEFT:
+                       doFillOuter = true;
+                       doFillInner = false;
+                       break;
+               case JOIN_FULL:
+                       doFillOuter = true;
+                       doFillInner = true;
+                       break;
+               case JOIN_RIGHT:
+                       doFillOuter = false;
+                       doFillInner = true;
+                       break;
+               default:
+                       elog(ERROR, "ExecMergeJoin: unsupported join type %d",
+                                (int) node->join.jointype);
+                       doFillOuter = false; /* keep compiler quiet */
+                       doFillInner = false;
+                       break;
+       }
 
        if (ScanDirectionIsForward(direction))
        {
@@ -483,7 +489,7 @@ ExecMergeJoin(MergeJoin *node)
                 *                improved readability.
                 * ----------------
                 */
-               MJ_dump(econtext, mergestate);
+               MJ_dump(mergestate);
 
                switch (mergestate->mj_JoinState)
                {
@@ -491,46 +497,60 @@ ExecMergeJoin(MergeJoin *node)
                                /*
                                 * EXEC_MJ_INITIALIZE means that this is the first time
                                 * ExecMergeJoin() has been called and so we have to
-                                * initialize the inner, outer and marked tuples as well
-                                * as various stuff in the expression context.
+                                * fetch the first tuple for both outer and inner subplans.
+                                * If we fail to get a tuple here, then that subplan is
+                                * empty, and we either end the join or go to one of the
+                                * fill-remaining-tuples states.
                                 */
                        case EXEC_MJ_INITIALIZE:
                                MJ_printf("ExecMergeJoin: EXEC_MJ_INITIALIZE\n");
 
-                               /*
-                                * Note: at this point, if either of our inner or outer
-                                * tuples are nil, then the join ends immediately because
-                                * we know one of the subplans is empty.
-                                */
-                               innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
-                               if (TupIsNull(innerTupleSlot))
+                               outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
+                               mergestate->mj_OuterTupleSlot = outerTupleSlot;
+                               if (TupIsNull(outerTupleSlot))
                                {
-                                       MJ_printf("ExecMergeJoin: **** inner tuple is nil ****\n");
+                                       MJ_printf("ExecMergeJoin: outer subplan is empty\n");
+                                       if (doFillInner)
+                                       {
+                                               /*
+                                                * Need to emit right-join tuples for remaining
+                                                * inner tuples.  We set MatchedInner = true to
+                                                * force the ENDOUTER state to advance inner.
+                                                */
+                                               mergestate->mj_JoinState = EXEC_MJ_ENDOUTER;
+                                               mergestate->mj_MatchedInner = true;
+                                               break;
+                                       }
+                                       /* Otherwise we're done. */
                                        return NULL;
                                }
 
-                               outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
-                               if (TupIsNull(outerTupleSlot))
+                               innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
+                               mergestate->mj_InnerTupleSlot = innerTupleSlot;
+                               if (TupIsNull(innerTupleSlot))
                                {
-                                       MJ_printf("ExecMergeJoin: **** outer tuple is nil ****\n");
+                                       MJ_printf("ExecMergeJoin: inner subplan is empty\n");
+                                       if (doFillOuter)
+                                       {
+                                               /*
+                                                * Need to emit left-join tuples for remaining
+                                                * outer tuples.  We set MatchedOuter = true to
+                                                * force the ENDINNER state to advance outer.
+                                                */
+                                               mergestate->mj_JoinState = EXEC_MJ_ENDINNER;
+                                               mergestate->mj_MatchedOuter = true;
+                                               break;
+                                       }
+                                       /* Otherwise we're done. */
                                        return NULL;
                                }
 
                                /* ----------------
-                                *       store the inner and outer tuple in the merge state
+                                *      OK, we have the initial tuples.  Begin by skipping
+                                *      unmatched inner tuples.
                                 * ----------------
                                 */
-                               econtext->ecxt_innertuple = innerTupleSlot;
-                               econtext->ecxt_outertuple = outerTupleSlot;
-
-                               mergestate->mj_MarkedTupleSlot->ttc_tupleDescriptor =
-                                       innerTupleSlot->ttc_tupleDescriptor;
-
-                               /* ----------------
-                                *      initialize merge join state to skip inner tuples.
-                                * ----------------
-                                */
-                               mergestate->mj_JoinState = EXEC_MJ_SKIPINNER;
+                               mergestate->mj_JoinState = EXEC_MJ_SKIPINNER_BEGIN;
                                break;
 
                                /*
@@ -541,9 +561,10 @@ ExecMergeJoin(MergeJoin *node)
                                 */
                        case EXEC_MJ_JOINMARK:
                                MJ_printf("ExecMergeJoin: EXEC_MJ_JOINMARK\n");
+
                                ExecMarkPos(innerPlan);
 
-                               MarkInnerTuple(econtext->ecxt_innertuple, mergestate);
+                               MarkInnerTuple(mergestate->mj_InnerTupleSlot, mergestate);
 
                                mergestate->mj_JoinState = EXEC_MJ_JOINTEST;
                                break;
@@ -562,7 +583,12 @@ ExecMergeJoin(MergeJoin *node)
 
                                ResetExprContext(econtext);
 
-                               qualResult = ExecQual((List *) mergeclauses, econtext, false);
+                               outerTupleSlot = mergestate->mj_OuterTupleSlot;
+                               econtext->ecxt_outertuple = outerTupleSlot;
+                               innerTupleSlot = mergestate->mj_InnerTupleSlot;
+                               econtext->ecxt_innertuple = innerTupleSlot;
+
+                               qualResult = ExecQual(mergeclauses, econtext, false);
                                MJ_DEBUG_QUAL(mergeclauses, qualResult);
 
                                if (qualResult)
@@ -578,38 +604,57 @@ ExecMergeJoin(MergeJoin *node)
                                 */
                        case EXEC_MJ_JOINTUPLES:
                                MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTUPLES\n");
+
                                mergestate->mj_JoinState = EXEC_MJ_NEXTINNER;
 
                                /*
-                                * Check the qpqual to see if we actually want to return
-                                * this join tuple.  If not, can proceed with merge.
+                                * Check the extra qual conditions to see if we actually
+                                * want to return this join tuple.  If not, can proceed with
+                                * merge.  We must distinguish the additional joinquals
+                                * (which must pass to consider the tuples "matched" for
+                                * outer-join logic) from the otherquals (which must pass
+                                * before we actually return the tuple).
                                 *
-                                * (We don't bother with a ResetExprContext here, on the
+                                * We don't bother with a ResetExprContext here, on the
                                 * assumption that we just did one before checking the merge
-                                * qual.  One per tuple should be sufficient.)
+                                * qual.  One per tuple should be sufficient.  Also, the
+                                * econtext's tuple pointers were set up before checking
+                                * the merge qual, so we needn't do it again.
                                 */
-                               qualResult = ExecQual((List *) qual, econtext, false);
-                               MJ_DEBUG_QUAL(qual, qualResult);
+                               qualResult = (joinqual == NIL ||
+                                                         ExecQual(joinqual, econtext, false));
+                               MJ_DEBUG_QUAL(joinqual, qualResult);
 
                                if (qualResult)
                                {
-                                       /* ----------------
-                                        *      qualification succeeded.  now form the desired
-                                        *      projection tuple and return the slot containing it.
-                                        * ----------------
-                                        */
-                                       TupleTableSlot *result;
-                                       ExprDoneCond isDone;
+                                       mergestate->mj_MatchedOuter = true;
+                                       mergestate->mj_MatchedInner = true;
 
-                                       MJ_printf("ExecMergeJoin: **** returning tuple ****\n");
+                                       qualResult = (otherqual == NIL ||
+                                                                 ExecQual(otherqual, econtext, false));
+                                       MJ_DEBUG_QUAL(otherqual, qualResult);
 
-                                       result = ExecProject(mergestate->jstate.cs_ProjInfo,
-                                                                                &isDone);
-
-                                       if (isDone != ExprEndResult)
+                                       if (qualResult)
                                        {
-                                               mergestate->jstate.cs_TupFromTlist = (isDone == ExprMultipleResult);
-                                               return result;
+                                               /* ----------------
+                                                *      qualification succeeded.  now form the desired
+                                                *      projection tuple and return the slot containing it.
+                                                * ----------------
+                                                */
+                                               TupleTableSlot *result;
+                                               ExprDoneCond isDone;
+
+                                               MJ_printf("ExecMergeJoin: returning tuple\n");
+
+                                               result = ExecProject(mergestate->jstate.cs_ProjInfo,
+                                                                                        &isDone);
+
+                                               if (isDone != ExprEndResult)
+                                               {
+                                                       mergestate->jstate.cs_TupFromTlist =
+                                                               (isDone == ExprMultipleResult);
+                                                       return result;
+                                               }
                                        }
                                }
                                break;
@@ -618,17 +663,60 @@ ExecMergeJoin(MergeJoin *node)
                                 * EXEC_MJ_NEXTINNER means advance the inner scan to the
                                 * next tuple. If the tuple is not nil, we then proceed to
                                 * test it against the join qualification.
+                                *
+                                * Before advancing, we check to see if we must emit an
+                                * outer-join fill tuple for this inner tuple.
                                 */
                        case EXEC_MJ_NEXTINNER:
                                MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTINNER\n");
 
+                               if (doFillInner && !mergestate->mj_MatchedInner)
+                               {
+                                       /*
+                                        * Generate a fake join tuple with nulls for the outer
+                                        * tuple, and return it if it passes the non-join quals.
+                                        */
+                                       mergestate->mj_MatchedInner = true;     /* do it only once */
+
+                                       ResetExprContext(econtext);
+
+                                       outerTupleSlot = mergestate->mj_NullOuterTupleSlot;
+                                       econtext->ecxt_outertuple = outerTupleSlot;
+                                       innerTupleSlot = mergestate->mj_InnerTupleSlot;
+                                       econtext->ecxt_innertuple = innerTupleSlot;
+
+                                       if (ExecQual(otherqual, econtext, false))
+                                       {
+                                               /* ----------------
+                                                *      qualification succeeded.  now form the desired
+                                                *      projection tuple and return the slot containing it.
+                                                * ----------------
+                                                */
+                                               TupleTableSlot *result;
+                                               ExprDoneCond isDone;
+
+                                               MJ_printf("ExecMergeJoin: returning fill tuple\n");
+
+                                               result = ExecProject(mergestate->jstate.cs_ProjInfo,
+                                                                                        &isDone);
+
+                                               if (isDone != ExprEndResult)
+                                               {
+                                                       mergestate->jstate.cs_TupFromTlist =
+                                                               (isDone == ExprMultipleResult);
+                                                       return result;
+                                               }
+                                       }
+                               }
+
                                /* ----------------
                                 *      now we get the next inner tuple, if any
                                 * ----------------
                                 */
                                innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
+                               mergestate->mj_InnerTupleSlot = innerTupleSlot;
                                MJ_DEBUG_PROC_NODE(innerTupleSlot);
-                               econtext->ecxt_innertuple = innerTupleSlot;
+                               mergestate->mj_MatchedInner = false;
 
                                if (TupIsNull(innerTupleSlot))
                                        mergestate->mj_JoinState = EXEC_MJ_NEXTOUTER;
@@ -650,23 +738,81 @@ ExecMergeJoin(MergeJoin *node)
                                 * so get a new outer tuple and then
                                 * proceed to test it against the marked tuple
                                 * (EXEC_MJ_TESTOUTER)
+                                *
+                                * Before advancing, we check to see if we must emit an
+                                * outer-join fill tuple for this outer tuple.
                                 *------------------------------------------------
                                 */
                        case EXEC_MJ_NEXTOUTER:
                                MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTOUTER\n");
 
+                               if (doFillOuter && !mergestate->mj_MatchedOuter)
+                               {
+                                       /*
+                                        * Generate a fake join tuple with nulls for the inner
+                                        * tuple, and return it if it passes the non-join quals.
+                                        */
+                                       mergestate->mj_MatchedOuter = true;     /* do it only once */
+
+                                       ResetExprContext(econtext);
+
+                                       outerTupleSlot = mergestate->mj_OuterTupleSlot;
+                                       econtext->ecxt_outertuple = outerTupleSlot;
+                                       innerTupleSlot = mergestate->mj_NullInnerTupleSlot;
+                                       econtext->ecxt_innertuple = innerTupleSlot;
+
+                                       if (ExecQual(otherqual, econtext, false))
+                                       {
+                                               /* ----------------
+                                                *      qualification succeeded.  now form the desired
+                                                *      projection tuple and return the slot containing it.
+                                                * ----------------
+                                                */
+                                               TupleTableSlot *result;
+                                               ExprDoneCond isDone;
+
+                                               MJ_printf("ExecMergeJoin: returning fill tuple\n");
+
+                                               result = ExecProject(mergestate->jstate.cs_ProjInfo,
+                                                                                        &isDone);
+
+                                               if (isDone != ExprEndResult)
+                                               {
+                                                       mergestate->jstate.cs_TupFromTlist =
+                                                               (isDone == ExprMultipleResult);
+                                                       return result;
+                                               }
+                                       }
+                               }
+
+                               /* ----------------
+                                *      now we get the next outer tuple, if any
+                                * ----------------
+                                */
                                outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
+                               mergestate->mj_OuterTupleSlot = outerTupleSlot;
                                MJ_DEBUG_PROC_NODE(outerTupleSlot);
-                               econtext->ecxt_outertuple = outerTupleSlot;
+                               mergestate->mj_MatchedOuter = false;
 
                                /* ----------------
-                                *      if the outer tuple is null then we know
-                                *      we are done with the join
+                                *      if the outer tuple is null then we are done with the
+                                *      join, unless we have inner tuples we need to null-fill.
                                 * ----------------
                                 */
                                if (TupIsNull(outerTupleSlot))
                                {
-                                       MJ_printf("ExecMergeJoin: **** outer tuple is nil ****\n");
+                                       MJ_printf("ExecMergeJoin: end of outer subplan\n");
+                                       innerTupleSlot = mergestate->mj_InnerTupleSlot;
+                                       if (doFillInner && !TupIsNull(innerTupleSlot))
+                                       {
+                                               /*
+                                                * Need to emit right-join tuples for remaining
+                                                * inner tuples.
+                                                */
+                                               mergestate->mj_JoinState = EXEC_MJ_ENDOUTER;
+                                               break;
+                                       }
+                                       /* Otherwise we're done. */
                                        return NULL;
                                }
 
@@ -712,39 +858,45 @@ ExecMergeJoin(MergeJoin *node)
 
                                /* ----------------
                                 *      here we compare the outer tuple with the marked inner tuple
-                                *      by using the marked tuple in place of the inner tuple.
                                 * ----------------
                                 */
-                               innerTupleSlot = econtext->ecxt_innertuple;
-                               econtext->ecxt_innertuple = mergestate->mj_MarkedTupleSlot;
-
                                ResetExprContext(econtext);
 
-                               qualResult = ExecQual((List *) mergeclauses, econtext, false);
+                               outerTupleSlot = mergestate->mj_OuterTupleSlot;
+                               econtext->ecxt_outertuple = outerTupleSlot;
+                               innerTupleSlot = mergestate->mj_MarkedTupleSlot;
+                               econtext->ecxt_innertuple = innerTupleSlot;
+
+                               qualResult = ExecQual(mergeclauses, econtext, false);
                                MJ_DEBUG_QUAL(mergeclauses, qualResult);
 
                                if (qualResult)
                                {
 
                                        /*
-                                        * the merge clause matched so now we juggle the slots
-                                        * back the way they were and proceed to JOINTEST.
+                                        * the merge clause matched so now we restore the inner
+                                        * scan position to the first mark, and loop back to
+                                        * JOINTEST.  Actually, since we know the mergeclause
+                                        * matches, we can skip JOINTEST and go straight to
+                                        * JOINTUPLES.
                                         *
-                                        * I can't understand why we have to go to JOINTEST and
-                                        * compare outer tuple with the same inner one again
-                                        * -> go to JOINTUPLES...        - vadim 02/27/98
+                                        * NOTE: we do not need to worry about the MatchedInner
+                                        * state for the rescanned inner tuples.  We know all
+                                        * of them will match this new outer tuple and therefore
+                                        * won't be emitted as fill tuples.  This works *only*
+                                        * because we require the extra joinquals to be nil when
+                                        * doing a right or full join --- otherwise some of the
+                                        * rescanned tuples might fail the extra joinquals.
                                         */
-
                                        ExecRestrPos(innerPlan);
                                        mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
                                }
                                else
                                {
-                                       econtext->ecxt_innertuple = innerTupleSlot;
                                        /* ----------------
                                         *      if the inner tuple was nil and the new outer
                                         *      tuple didn't match the marked outer tuple then
-                                        *      we may have the case:
+                                        *      we have the case:
                                         *
                                         *                       outer inner
                                         *                         4     4      - marked tuple
@@ -753,31 +905,33 @@ ExecMergeJoin(MergeJoin *node)
                                         *                         7
                                         *
                                         *      which means that all subsequent outer tuples will be
-                                        *      larger than our inner tuples.
+                                        *      larger than our marked inner tuples.  So we're done.
                                         * ----------------
                                         */
+                                       innerTupleSlot = mergestate->mj_InnerTupleSlot;
                                        if (TupIsNull(innerTupleSlot))
                                        {
-#ifdef ENABLE_OUTER_JOINS
-                                               if (isLeftJoin)
+                                               if (doFillOuter)
                                                {
-                                                       /* continue on to null fill outer tuples */
-                                                       mergestate->mj_JoinState = EXEC_MJ_FILLOUTER;
+                                                       /*
+                                                        * Need to emit left-join tuples for remaining
+                                                        * outer tuples.
+                                                        */
+                                                       mergestate->mj_JoinState = EXEC_MJ_ENDINNER;
                                                        break;
                                                }
-#endif
-                                               MJ_printf("ExecMergeJoin: **** weird case 1 ****\n");
+                                               /* Otherwise we're done. */
                                                return NULL;
                                        }
 
                                        /* continue on to skip outer tuples */
-                                       mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER;
+                                       mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER_BEGIN;
                                }
                                break;
 
                                /*----------------------------------------------------------
                                 * EXEC_MJ_SKIPOUTER means skip over tuples in the outer plan
-                                * until we find an outer tuple > current inner tuple.
+                                * until we find an outer tuple >= current inner tuple.
                                 *
                                 * For example:
                                 *
@@ -790,10 +944,14 @@ ExecMergeJoin(MergeJoin *node)
                                 *
                                 * we have to advance the outer scan
                                 * until we find the outer 8.
+                                *
+                                * To avoid redundant tests, we divide this into three
+                                * sub-states: BEGIN, TEST, ADVANCE.
                                 *----------------------------------------------------------
                                 */
-                       case EXEC_MJ_SKIPOUTER:
-                               MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER\n");
+                       case EXEC_MJ_SKIPOUTER_BEGIN:
+                               MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER_BEGIN\n");
+
                                /* ----------------
                                 *      before we advance, make sure the current tuples
                                 *      do not satisfy the mergeclauses.  If they do, then
@@ -802,23 +960,39 @@ ExecMergeJoin(MergeJoin *node)
                                 */
                                ResetExprContext(econtext);
 
-                               qualResult = ExecQual((List *) mergeclauses, econtext, false);
+                               outerTupleSlot = mergestate->mj_OuterTupleSlot;
+                               econtext->ecxt_outertuple = outerTupleSlot;
+                               innerTupleSlot = mergestate->mj_InnerTupleSlot;
+                               econtext->ecxt_innertuple = innerTupleSlot;
+
+                               qualResult = ExecQual(mergeclauses, econtext, false);
                                MJ_DEBUG_QUAL(mergeclauses, qualResult);
 
                                if (qualResult)
                                {
                                        ExecMarkPos(innerPlan);
 
-                                       MarkInnerTuple(econtext->ecxt_innertuple, mergestate);
+                                       MarkInnerTuple(innerTupleSlot, mergestate);
 
                                        mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
                                        break;
                                }
 
+                               mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER_TEST;
+                               break;
+
+                       case EXEC_MJ_SKIPOUTER_TEST:
+                               MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER_TEST\n");
+
                                /* ----------------
                                 *      ok, now test the skip qualification
                                 * ----------------
                                 */
+                               outerTupleSlot = mergestate->mj_OuterTupleSlot;
+                               econtext->ecxt_outertuple = outerTupleSlot;
+                               innerTupleSlot = mergestate->mj_InnerTupleSlot;
+                               econtext->ecxt_innertuple = innerTupleSlot;
+
                                compareResult = MergeCompare(mergeclauses,
                                                                                         outerSkipQual,
                                                                                         econtext);
@@ -827,42 +1001,12 @@ ExecMergeJoin(MergeJoin *node)
 
                                /* ----------------
                                 *      compareResult is true as long as we should
-                                *      continue skipping tuples.
+                                *      continue skipping outer tuples.
                                 * ----------------
                                 */
                                if (compareResult)
                                {
-#ifdef ENABLE_OUTER_JOINS
-                                       /* ----------------
-                                        *      if this is a left or full outer join, then fill
-                                        * ----------------
-                                        */
-                                       if (isLeftJoin)
-                                       {
-                                               mergestate->mj_JoinState = EXEC_MJ_FILLOUTER;
-                                               break;
-                                       }
-#endif
-
-                                       outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
-                                       MJ_DEBUG_PROC_NODE(outerTupleSlot);
-                                       econtext->ecxt_outertuple = outerTupleSlot;
-
-                                       /* ----------------
-                                        *      if the outer tuple is null then we know
-                                        *      we are done with the join
-                                        * ----------------
-                                        */
-                                       if (TupIsNull(outerTupleSlot))
-                                       {
-                                               MJ_printf("ExecMergeJoin: **** outerTuple is nil ****\n");
-                                               return NULL;
-                                       }
-                                       /* ----------------
-                                        *      otherwise test the new tuple against the skip qual.
-                                        *      (we remain in the EXEC_MJ_SKIPOUTER state)
-                                        * ----------------
-                                        */
+                                       mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER_ADVANCE;
                                        break;
                                }
 
@@ -880,14 +1024,99 @@ ExecMergeJoin(MergeJoin *node)
                                MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult);
 
                                if (compareResult)
-                                       mergestate->mj_JoinState = EXEC_MJ_SKIPINNER;
+                                       mergestate->mj_JoinState = EXEC_MJ_SKIPINNER_BEGIN;
                                else
                                        mergestate->mj_JoinState = EXEC_MJ_JOINMARK;
                                break;
 
+                               /*------------------------------------------------
+                                * Before advancing, we check to see if we must emit an
+                                * outer-join fill tuple for this outer tuple.
+                                *------------------------------------------------
+                                */
+                       case EXEC_MJ_SKIPOUTER_ADVANCE:
+                               MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER_ADVANCE\n");
+
+                               if (doFillOuter && !mergestate->mj_MatchedOuter)
+                               {
+                                       /*
+                                        * Generate a fake join tuple with nulls for the inner
+                                        * tuple, and return it if it passes the non-join quals.
+                                        */
+                                       mergestate->mj_MatchedOuter = true;     /* do it only once */
+
+                                       ResetExprContext(econtext);
+
+                                       outerTupleSlot = mergestate->mj_OuterTupleSlot;
+                                       econtext->ecxt_outertuple = outerTupleSlot;
+                                       innerTupleSlot = mergestate->mj_NullInnerTupleSlot;
+                                       econtext->ecxt_innertuple = innerTupleSlot;
+
+                                       if (ExecQual(otherqual, econtext, false))
+                                       {
+                                               /* ----------------
+                                                *      qualification succeeded.  now form the desired
+                                                *      projection tuple and return the slot containing it.
+                                                * ----------------
+                                                */
+                                               TupleTableSlot *result;
+                                               ExprDoneCond isDone;
+
+                                               MJ_printf("ExecMergeJoin: returning fill tuple\n");
+
+                                               result = ExecProject(mergestate->jstate.cs_ProjInfo,
+                                                                                        &isDone);
+
+                                               if (isDone != ExprEndResult)
+                                               {
+                                                       mergestate->jstate.cs_TupFromTlist =
+                                                               (isDone == ExprMultipleResult);
+                                                       return result;
+                                               }
+                                       }
+                               }
+
+                               /* ----------------
+                                *      now we get the next outer tuple, if any
+                                * ----------------
+                                */
+                               outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
+                               mergestate->mj_OuterTupleSlot = outerTupleSlot;
+                               MJ_DEBUG_PROC_NODE(outerTupleSlot);
+                               mergestate->mj_MatchedOuter = false;
+
+                               /* ----------------
+                                *      if the outer tuple is null then we are done with the
+                                *      join, unless we have inner tuples we need to null-fill.
+                                * ----------------
+                                */
+                               if (TupIsNull(outerTupleSlot))
+                               {
+                                       MJ_printf("ExecMergeJoin: end of outer subplan\n");
+                                       innerTupleSlot = mergestate->mj_InnerTupleSlot;
+                                       if (doFillInner && !TupIsNull(innerTupleSlot))
+                                       {
+                                               /*
+                                                * Need to emit right-join tuples for remaining
+                                                * inner tuples.
+                                                */
+                                               mergestate->mj_JoinState = EXEC_MJ_ENDOUTER;
+                                               break;
+                                       }
+                                       /* Otherwise we're done. */
+                                       return NULL;
+                               }
+
+                               /* ----------------
+                                *      otherwise test the new tuple against the skip qual.
+                                * ----------------
+                                */
+                               mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER_TEST;
+                               break;
+
                                /*-----------------------------------------------------------
                                 * EXEC_MJ_SKIPINNER means skip over tuples in the inner plan
-                                * until we find an inner tuple > current outer tuple.
+                                * until we find an inner tuple >= current outer tuple.
                                 *
                                 * For example:
                                 *
@@ -901,10 +1130,13 @@ ExecMergeJoin(MergeJoin *node)
                                 * we have to advance the inner scan
                                 * until we find the inner 12.
                                 *
+                                * To avoid redundant tests, we divide this into three
+                                * sub-states: BEGIN, TEST, ADVANCE.
                                 *-------------------------------------------------------
                                 */
-                       case EXEC_MJ_SKIPINNER:
-                               MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER\n");
+                       case EXEC_MJ_SKIPINNER_BEGIN:
+                               MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER_BEGIN\n");
+
                                /* ----------------
                                 *      before we advance, make sure the current tuples
                                 *      do not satisfy the mergeclauses.  If they do, then
@@ -913,23 +1145,39 @@ ExecMergeJoin(MergeJoin *node)
                                 */
                                ResetExprContext(econtext);
 
-                               qualResult = ExecQual((List *) mergeclauses, econtext, false);
+                               outerTupleSlot = mergestate->mj_OuterTupleSlot;
+                               econtext->ecxt_outertuple = outerTupleSlot;
+                               innerTupleSlot = mergestate->mj_InnerTupleSlot;
+                               econtext->ecxt_innertuple = innerTupleSlot;
+
+                               qualResult = ExecQual(mergeclauses, econtext, false);
                                MJ_DEBUG_QUAL(mergeclauses, qualResult);
 
                                if (qualResult)
                                {
                                        ExecMarkPos(innerPlan);
 
-                                       MarkInnerTuple(econtext->ecxt_innertuple, mergestate);
+                                       MarkInnerTuple(innerTupleSlot, mergestate);
 
                                        mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
                                        break;
                                }
 
+                               mergestate->mj_JoinState = EXEC_MJ_SKIPINNER_TEST;
+                               break;
+
+                       case EXEC_MJ_SKIPINNER_TEST:
+                               MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER_TEST\n");
+
                                /* ----------------
                                 *      ok, now test the skip qualification
                                 * ----------------
                                 */
+                               outerTupleSlot = mergestate->mj_OuterTupleSlot;
+                               econtext->ecxt_outertuple = outerTupleSlot;
+                               innerTupleSlot = mergestate->mj_InnerTupleSlot;
+                               econtext->ecxt_innertuple = innerTupleSlot;
+
                                compareResult = MergeCompare(mergeclauses,
                                                                                         innerSkipQual,
                                                                                         econtext);
@@ -938,70 +1186,20 @@ ExecMergeJoin(MergeJoin *node)
 
                                /* ----------------
                                 *      compareResult is true as long as we should
-                                *      continue skipping tuples.
+                                *      continue skipping inner tuples.
                                 * ----------------
                                 */
                                if (compareResult)
                                {
-#ifdef ENABLE_OUTER_JOINS
-                                       /* ----------------
-                                        *      if this is a right or full outer join, then fill
-                                        * ----------------
-                                        */
-                                       if (isRightJoin)
-                                       {
-                                               mergestate->mj_JoinState = EXEC_MJ_FILLINNER;
-                                               break;
-                                       }
-#endif
-
-                                       /* ----------------
-                                        *      now try and get a new inner tuple
-                                        * ----------------
-                                        */
-                                       innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
-                                       MJ_DEBUG_PROC_NODE(innerTupleSlot);
-                                       econtext->ecxt_innertuple = innerTupleSlot;
-
-                                       /* ----------------
-                                        *      if the inner tuple is null then we know
-                                        *      we have to restore the inner scan
-                                        *      and advance to the next outer tuple
-                                        * ----------------
-                                        */
-                                       if (TupIsNull(innerTupleSlot))
-                                       {
-                                               /* ----------------
-                                                *      this is an interesting case.. all our
-                                                *      inner tuples are smaller then our outer
-                                                *      tuples so we never found an inner tuple
-                                                *      to mark.
-                                                *
-                                                *                        outer inner
-                                                *       outer tuple -  5         4
-                                                *                                      5         4
-                                                *                                      6        nil  - inner tuple
-                                                *                                      7
-                                                *
-                                                *      This means the join should end.
-                                                * ----------------
-                                                */
-                                               MJ_printf("ExecMergeJoin: **** weird case 2 ****\n");
-                                               return NULL;
-                                       }
-
-                                       /* ----------------
-                                        *      otherwise test the new tuple against the skip qual.
-                                        *      (we remain in the EXEC_MJ_SKIPINNER state)
-                                        * ----------------
-                                        */
+                                       mergestate->mj_JoinState = EXEC_MJ_SKIPINNER_ADVANCE;
                                        break;
                                }
 
                                /* ----------------
-                                *      compare finally failed and we have stopped skipping
-                                *      inner tuples so now check the outer skip qual
-                                *      to see if we should now skip outer tuples...
+                                *      now check the outer skip qual to see if we
+                                *      should now skip outer tuples... if we fail the
+                                *      outer skip qual, then we know we have a new pair
+                                *      of matching tuples.
                                 * ----------------
                                 */
                                compareResult = MergeCompare(mergeclauses,
@@ -1011,120 +1209,237 @@ ExecMergeJoin(MergeJoin *node)
                                MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult);
 
                                if (compareResult)
-                                       mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER;
+                                       mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER_BEGIN;
                                else
                                        mergestate->mj_JoinState = EXEC_MJ_JOINMARK;
-
                                break;
 
-#ifdef ENABLE_OUTER_JOINS
-
-                               /*
-                                * EXEC_MJ_FILLINNER means we have an unmatched inner
-                                * tuple which must be null-expanded into the projection
-                                * tuple. get the next inner tuple and reset markers
-                                * (EXEC_MJ_JOINMARK).
+                               /*------------------------------------------------
+                                * Before advancing, we check to see if we must emit an
+                                * outer-join fill tuple for this inner tuple.
+                                *------------------------------------------------
                                 */
-                       case EXEC_MJ_FILLINNER:
-                               MJ_printf("ExecMergeJoin: EXEC_MJ_FILLINNER\n");
-                               mergestate->mj_JoinState = EXEC_MJ_JOINMARK;
+                       case EXEC_MJ_SKIPINNER_ADVANCE:
+                               MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER_ADVANCE\n");
 
-                               /* ----------------
-                                *      project the inner tuple into the result
-                                * ----------------
-                                */
-                               MJ_printf("ExecMergeJoin: project inner tuple into the result (not yet implemented)\n");
+                               if (doFillInner && !mergestate->mj_MatchedInner)
+                               {
+                                       /*
+                                        * Generate a fake join tuple with nulls for the outer
+                                        * tuple, and return it if it passes the non-join quals.
+                                        */
+                                       mergestate->mj_MatchedInner = true;     /* do it only once */
+
+                                       ResetExprContext(econtext);
+
+                                       outerTupleSlot = mergestate->mj_NullOuterTupleSlot;
+                                       econtext->ecxt_outertuple = outerTupleSlot;
+                                       innerTupleSlot = mergestate->mj_InnerTupleSlot;
+                                       econtext->ecxt_innertuple = innerTupleSlot;
+
+                                       if (ExecQual(otherqual, econtext, false))
+                                       {
+                                               /* ----------------
+                                                *      qualification succeeded.  now form the desired
+                                                *      projection tuple and return the slot containing it.
+                                                * ----------------
+                                                */
+                                               TupleTableSlot *result;
+                                               ExprDoneCond isDone;
+
+                                               MJ_printf("ExecMergeJoin: returning fill tuple\n");
+
+                                               result = ExecProject(mergestate->jstate.cs_ProjInfo,
+                                                                                        &isDone);
+
+                                               if (isDone != ExprEndResult)
+                                               {
+                                                       mergestate->jstate.cs_TupFromTlist =
+                                                               (isDone == ExprMultipleResult);
+                                                       return result;
+                                               }
+                                       }
+                               }
 
                                /* ----------------
-                                *      now skip this inner tuple
+                                *      now we get the next inner tuple, if any
                                 * ----------------
                                 */
                                innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
+                               mergestate->mj_InnerTupleSlot = innerTupleSlot;
                                MJ_DEBUG_PROC_NODE(innerTupleSlot);
-                               econtext->ecxt_innertuple = innerTupleSlot;
+                               mergestate->mj_MatchedInner = false;
 
                                /* ----------------
-                                *      if the inner tuple is null then we know
-                                *      we have to restore the inner scan
-                                *      and advance to the next outer tuple
+                                *      if the inner tuple is null then we are done with the
+                                *      join, unless we have outer tuples we need to null-fill.
                                 * ----------------
                                 */
                                if (TupIsNull(innerTupleSlot))
                                {
-                                       if (isLeftJoin && !TupIsNull(outerTupleSlot))
+                                       MJ_printf("ExecMergeJoin: end of inner subplan\n");
+                                       outerTupleSlot = mergestate->mj_OuterTupleSlot;
+                                       if (doFillOuter && !TupIsNull(outerTupleSlot))
                                        {
-                                               mergestate->mj_JoinState = EXEC_MJ_FILLOUTER;
-                                               MJ_printf("ExecMergeJoin: try to complete outer fill\n");
+                                               /*
+                                                * Need to emit left-join tuples for remaining
+                                                * outer tuples.
+                                                */
+                                               mergestate->mj_JoinState = EXEC_MJ_ENDINNER;
                                                break;
                                        }
-
-                                       MJ_printf("ExecMergeJoin: **** weird case 2 ****\n");
+                                       /* Otherwise we're done. */
                                        return NULL;
                                }
 
                                /* ----------------
                                 *      otherwise test the new tuple against the skip qual.
-                                *      (we move to the EXEC_MJ_JOINMARK state)
                                 * ----------------
                                 */
+                               mergestate->mj_JoinState = EXEC_MJ_SKIPINNER_TEST;
                                break;
 
                                /*
-                                * EXEC_MJ_FILLOUTER means we have an unmatched outer
-                                * tuple which must be null-expanded into the projection
-                                * tuple. get the next outer tuple and reset markers
-                                * (EXEC_MJ_JOINMARK).
+                                * EXEC_MJ_ENDOUTER means we have run out of outer tuples,
+                                * but are doing a right/full join and therefore must null-
+                                * fill any remaing unmatched inner tuples.
                                 */
-                       case EXEC_MJ_FILLOUTER:
-                               MJ_printf("ExecMergeJoin: EXEC_MJ_FILLOUTER\n");
-                               mergestate->mj_JoinState = EXEC_MJ_JOINMARK;
+                       case EXEC_MJ_ENDOUTER:
+                               MJ_printf("ExecMergeJoin: EXEC_MJ_ENDOUTER\n");
+
+                               Assert(doFillInner);
+
+                               if (!mergestate->mj_MatchedInner)
+                               {
+                                       /*
+                                        * Generate a fake join tuple with nulls for the outer
+                                        * tuple, and return it if it passes the non-join quals.
+                                        */
+                                       mergestate->mj_MatchedInner = true;     /* do it only once */
+
+                                       ResetExprContext(econtext);
+
+                                       outerTupleSlot = mergestate->mj_NullOuterTupleSlot;
+                                       econtext->ecxt_outertuple = outerTupleSlot;
+                                       innerTupleSlot = mergestate->mj_InnerTupleSlot;
+                                       econtext->ecxt_innertuple = innerTupleSlot;
+
+                                       if (ExecQual(otherqual, econtext, false))
+                                       {
+                                               /* ----------------
+                                                *      qualification succeeded.  now form the desired
+                                                *      projection tuple and return the slot containing it.
+                                                * ----------------
+                                                */
+                                               TupleTableSlot *result;
+                                               ExprDoneCond isDone;
+
+                                               MJ_printf("ExecMergeJoin: returning fill tuple\n");
+
+                                               result = ExecProject(mergestate->jstate.cs_ProjInfo,
+                                                                                        &isDone);
+
+                                               if (isDone != ExprEndResult)
+                                               {
+                                                       mergestate->jstate.cs_TupFromTlist =
+                                                               (isDone == ExprMultipleResult);
+                                                       return result;
+                                               }
+                                       }
+                               }
 
                                /* ----------------
-                                *      project the outer tuple into the result
+                                *      now we get the next inner tuple, if any
                                 * ----------------
                                 */
-                               MJ_printf("ExecMergeJoin: project outer tuple into the result (not yet implemented)\n");
+                               innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
+                               mergestate->mj_InnerTupleSlot = innerTupleSlot;
+                               MJ_DEBUG_PROC_NODE(innerTupleSlot);
+                               mergestate->mj_MatchedInner = false;
+
+                               if (TupIsNull(innerTupleSlot))
+                               {
+                                       MJ_printf("ExecMergeJoin: end of inner subplan\n");
+                                       return NULL;
+                               }
+
+                               /* Else remain in ENDOUTER state and process next tuple. */
+                               break;
+
+                               /*
+                                * EXEC_MJ_ENDINNER means we have run out of inner tuples,
+                                * but are doing a left/full join and therefore must null-
+                                * fill any remaing unmatched outer tuples.
+                                */
+                       case EXEC_MJ_ENDINNER:
+                               MJ_printf("ExecMergeJoin: EXEC_MJ_ENDINNER\n");
+
+                               Assert(doFillOuter);
+
+                               if (!mergestate->mj_MatchedOuter)
+                               {
+                                       /*
+                                        * Generate a fake join tuple with nulls for the inner
+                                        * tuple, and return it if it passes the non-join quals.
+                                        */
+                                       mergestate->mj_MatchedOuter = true;     /* do it only once */
+
+                                       ResetExprContext(econtext);
+
+                                       outerTupleSlot = mergestate->mj_OuterTupleSlot;
+                                       econtext->ecxt_outertuple = outerTupleSlot;
+                                       innerTupleSlot = mergestate->mj_NullInnerTupleSlot;
+                                       econtext->ecxt_innertuple = innerTupleSlot;
+
+                                       if (ExecQual(otherqual, econtext, false))
+                                       {
+                                               /* ----------------
+                                                *      qualification succeeded.  now form the desired
+                                                *      projection tuple and return the slot containing it.
+                                                * ----------------
+                                                */
+                                               TupleTableSlot *result;
+                                               ExprDoneCond isDone;
+
+                                               MJ_printf("ExecMergeJoin: returning fill tuple\n");
+
+                                               result = ExecProject(mergestate->jstate.cs_ProjInfo,
+                                                                                        &isDone);
+
+                                               if (isDone != ExprEndResult)
+                                               {
+                                                       mergestate->jstate.cs_TupFromTlist =
+                                                               (isDone == ExprMultipleResult);
+                                                       return result;
+                                               }
+                                       }
+                               }
 
                                /* ----------------
-                                *      now skip this outer tuple
+                                *      now we get the next outer tuple, if any
                                 * ----------------
                                 */
                                outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
+                               mergestate->mj_OuterTupleSlot = outerTupleSlot;
                                MJ_DEBUG_PROC_NODE(outerTupleSlot);
-                               econtext->ecxt_outertuple = outerTupleSlot;
+                               mergestate->mj_MatchedOuter = false;
 
-                               /* ----------------
-                                *      if the outer tuple is null then we know
-                                *      we are done with the left half of the join
-                                * ----------------
-                                */
                                if (TupIsNull(outerTupleSlot))
                                {
-                                       if (isRightJoin && !TupIsNull(innerTupleSlot))
-                                       {
-                                               mergestate->mj_JoinState = EXEC_MJ_FILLINNER;
-                                               MJ_printf("ExecMergeJoin: try to complete inner fill\n");
-                                               break;
-                                       }
-
-                                       MJ_printf("ExecMergeJoin: **** outerTuple is nil ****\n");
+                                       MJ_printf("ExecMergeJoin: end of outer subplan\n");
                                        return NULL;
                                }
 
-                               /* ----------------
-                                *      otherwise test the new tuple against the skip qual.
-                                *      (we move to the EXEC_MJ_JOINMARK state)
-                                * ----------------
-                                */
+                               /* Else remain in ENDINNER state and process next tuple. */
                                break;
-#endif
 
                                /*
                                 * if we get here it means our code is fouled up and so we
                                 * just end the join prematurely.
                                 */
                        default:
-                               elog(NOTICE, "ExecMergeJoin: invalid join state. aborting");
+                               elog(NOTICE, "ExecMergeJoin: invalid join state %d, aborting",
+                                        mergestate->mj_JoinState);
                                return NULL;
                }
        }
@@ -1143,7 +1458,6 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent)
 {
        MergeJoinState *mergestate;
        List       *joinclauses;
-       TupleTableSlot *mjSlot;
 
        MJ1_printf("ExecInitMergeJoin: %s\n",
                           "initializing node");
@@ -1153,17 +1467,13 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent)
         *      get the range table and direction from it
         * ----------------
         */
-       node->join.state = estate;
+       node->join.plan.state = estate;
 
        /* ----------------
         *      create new merge state for node
         * ----------------
         */
        mergestate = makeNode(MergeJoinState);
-       mergestate->mj_OuterSkipQual = NIL;
-       mergestate->mj_InnerSkipQual = NIL;
-       mergestate->mj_JoinState = 0;
-       mergestate->mj_MarkedTupleSlot = NULL;
        node->mergestate = mergestate;
 
        /* ----------------
@@ -1174,22 +1484,67 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent)
         */
        ExecAssignExprContext(estate, &mergestate->jstate);
 
-#define MERGEJOIN_NSLOTS 2
+       /* ----------------
+        *      initialize subplans
+        * ----------------
+        */
+       ExecInitNode(outerPlan((Plan *) node), estate, (Plan *) node);
+       ExecInitNode(innerPlan((Plan *) node), estate, (Plan *) node);
+
+#define MERGEJOIN_NSLOTS 4
        /* ----------------
         *      tuple table initialization
-        *
-        *      XXX why aren't we getting a tuple table slot in the normal way?
         * ----------------
         */
        ExecInitResultTupleSlot(estate, &mergestate->jstate);
-       mjSlot = makeNode(TupleTableSlot);
-       mjSlot->val = NULL;
-       mjSlot->ttc_shouldFree = true;
-       mjSlot->ttc_descIsNew = true;
-       mjSlot->ttc_tupleDescriptor = NULL;
-       mjSlot->ttc_buffer = InvalidBuffer;
-       mjSlot->ttc_whichplan = -1;
-       mergestate->mj_MarkedTupleSlot = mjSlot;
+
+       mergestate->mj_MarkedTupleSlot = ExecInitExtraTupleSlot(estate);
+       ExecSetSlotDescriptor(mergestate->mj_MarkedTupleSlot,
+                                                 ExecGetTupType(innerPlan((Plan *) node)));
+
+       switch (node->join.jointype)
+       {
+               case JOIN_INNER:
+                       break;
+               case JOIN_LEFT:
+                       mergestate->mj_NullInnerTupleSlot =
+                               ExecInitNullTupleSlot(estate,
+                                                                         ExecGetTupType(innerPlan((Plan*) node)));
+                       break;
+               case JOIN_RIGHT:
+                       mergestate->mj_NullOuterTupleSlot =
+                               ExecInitNullTupleSlot(estate,
+                                                                         ExecGetTupType(outerPlan((Plan*) node)));
+                       /*
+                        * Can't handle right or full join with non-nil extra joinclauses.
+                        */
+                       if (node->join.joinqual != NIL)
+                               elog(ERROR, "RIGHT JOIN is only supported with mergejoinable join conditions");
+                       break;
+               case JOIN_FULL:
+                       mergestate->mj_NullOuterTupleSlot =
+                               ExecInitNullTupleSlot(estate,
+                                                                         ExecGetTupType(outerPlan((Plan*) node)));
+                       mergestate->mj_NullInnerTupleSlot =
+                               ExecInitNullTupleSlot(estate,
+                                                                         ExecGetTupType(innerPlan((Plan*) node)));
+                       /*
+                        * Can't handle right or full join with non-nil extra joinclauses.
+                        */
+                       if (node->join.joinqual != NIL)
+                               elog(ERROR, "FULL JOIN is only supported with mergejoinable join conditions");
+                       break;
+               default:
+                       elog(ERROR, "ExecInitMergeJoin: unsupported join type %d",
+                                (int) node->join.jointype);
+       }
+
+       /* ----------------
+        *      initialize tuple type and projection info
+        * ----------------
+        */
+       ExecAssignResultTypeFromTL((Plan *) node, &mergestate->jstate);
+       ExecAssignProjectionInfo((Plan *) node, &mergestate->jstate);
 
        /* ----------------
         *      form merge skip qualifications
@@ -1210,22 +1565,12 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent)
         * ----------------
         */
        mergestate->mj_JoinState = EXEC_MJ_INITIALIZE;
-
-       /* ----------------
-        *      initialize subplans
-        * ----------------
-        */
-       ExecInitNode(outerPlan((Plan *) node), estate, (Plan *) node);
-       ExecInitNode(innerPlan((Plan *) node), estate, (Plan *) node);
-
-       /* ----------------
-        *      initialize tuple type and projection info
-        * ----------------
-        */
-       ExecAssignResultTypeFromTL((Plan *) node, &mergestate->jstate);
-       ExecAssignProjectionInfo((Plan *) node, &mergestate->jstate);
-
        mergestate->jstate.cs_TupFromTlist = false;
+       mergestate->mj_MatchedOuter = false;
+       mergestate->mj_MatchedInner = false;
+       mergestate->mj_OuterTupleSlot = NULL;
+       mergestate->mj_InnerTupleSlot = NULL;
+
        /* ----------------
         *      initialization successful
         * ----------------
@@ -1285,15 +1630,11 @@ ExecEndMergeJoin(MergeJoin *node)
        ExecEndNode((Plan *) outerPlan((Plan *) node), (Plan *) node);
 
        /* ----------------
-        *      clean out the tuple table so that we don't try and
-        *      pfree the marked tuples..  see HACK ALERT at the top of
-        *      this file.
+        *      clean out the tuple table
         * ----------------
         */
        ExecClearTuple(mergestate->jstate.cs_ResultTupleSlot);
        ExecClearTuple(mergestate->mj_MarkedTupleSlot);
-       pfree(mergestate->mj_MarkedTupleSlot);
-       mergestate->mj_MarkedTupleSlot = NULL;
 
        MJ1_printf("ExecEndMergeJoin: %s\n",
                           "node processing ended");
@@ -1303,14 +1644,15 @@ void
 ExecReScanMergeJoin(MergeJoin *node, ExprContext *exprCtxt, Plan *parent)
 {
        MergeJoinState *mergestate = node->mergestate;
-       TupleTableSlot *mjSlot = mergestate->mj_MarkedTupleSlot;
 
-       ExecClearTuple(mjSlot);
-       mjSlot->ttc_tupleDescriptor = NULL;
-       mjSlot->ttc_descIsNew = true;
-       mjSlot->ttc_whichplan = -1;
+       ExecClearTuple(mergestate->mj_MarkedTupleSlot);
 
        mergestate->mj_JoinState = EXEC_MJ_INITIALIZE;
+       mergestate->jstate.cs_TupFromTlist = false;
+       mergestate->mj_MatchedOuter = false;
+       mergestate->mj_MatchedInner = false;
+       mergestate->mj_OuterTupleSlot = NULL;
+       mergestate->mj_InnerTupleSlot = NULL;
 
        /*
         * if chgParam of subnodes is not null then plans will be re-scanned
index 3685232c7e42b15e5b9777e81a573920d461ec5f..5abd4ffc3a18712ce7b46f5d603a04275292e86c 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.20 2000/08/24 03:29:03 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.21 2000/09/12 21:06:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -62,10 +62,10 @@ ExecNestLoop(NestLoop *node)
        NestLoopState *nlstate;
        Plan       *innerPlan;
        Plan       *outerPlan;
-       bool            needNewOuterTuple;
        TupleTableSlot *outerTupleSlot;
        TupleTableSlot *innerTupleSlot;
-       List       *qual;
+       List       *joinqual;
+       List       *otherqual;
        ExprContext *econtext;
 
        /* ----------------
@@ -75,9 +75,10 @@ ExecNestLoop(NestLoop *node)
        ENL1_printf("getting info from node");
 
        nlstate = node->nlstate;
-       qual = node->join.qual;
-       outerPlan = outerPlan(&node->join);
-       innerPlan = innerPlan(&node->join);
+       joinqual = node->join.joinqual;
+       otherqual = node->join.plan.qual;
+       outerPlan = outerPlan((Plan *) node);
+       innerPlan = innerPlan((Plan *) node);
        econtext = nlstate->jstate.cs_ExprContext;
 
        /* ----------------
@@ -115,7 +116,7 @@ ExecNestLoop(NestLoop *node)
 
        /* ----------------
         *      Ok, everything is setup for the join so now loop until
-        *      we return a qualifying join tuple..
+        *      we return a qualifying join tuple.
         * ----------------
         */
        ENL1_printf("entering main loop");
@@ -123,44 +124,14 @@ ExecNestLoop(NestLoop *node)
        for (;;)
        {
                /* ----------------
-                *      The essential idea now is to get the next inner tuple
-                *      and join it with the current outer tuple.
+                *      If we don't have an outer tuple, get the next one and
+                *      reset the inner scan.
                 * ----------------
                 */
-               needNewOuterTuple = TupIsNull(outerTupleSlot);
-
-               /* ----------------
-                *      if we have an outerTuple, try to get the next inner tuple.
-                * ----------------
-                */
-               if (!needNewOuterTuple)
-               {
-                       ENL1_printf("getting new inner tuple");
-
-                       innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
-                       econtext->ecxt_innertuple = innerTupleSlot;
-
-                       if (TupIsNull(innerTupleSlot))
-                       {
-                               ENL1_printf("no inner tuple, need new outer tuple");
-                               needNewOuterTuple = true;
-                       }
-               }
-
-               /* ----------------
-                *      loop until we have a new outer tuple and a new
-                *      inner tuple.
-                * ----------------
-                */
-               while (needNewOuterTuple)
+               if (nlstate->nl_NeedNewOuter)
                {
-                       /* ----------------
-                        *      now try to get the next outer tuple
-                        * ----------------
-                        */
                        ENL1_printf("getting new outer tuple");
                        outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
-                       econtext->ecxt_outertuple = outerTupleSlot;
 
                        /* ----------------
                         *      if there are no more outer tuples, then the join
@@ -175,12 +146,14 @@ ExecNestLoop(NestLoop *node)
 
                        ENL1_printf("saving new outer tuple information");
                        nlstate->jstate.cs_OuterTupleSlot = outerTupleSlot;
+                       econtext->ecxt_outertuple = outerTupleSlot;
+                       nlstate->nl_NeedNewOuter = false;
+                       nlstate->nl_MatchedOuter = false;
 
                        /* ----------------
-                        *      now rescan the inner plan and get a new inner tuple
+                        *      now rescan the inner plan
                         * ----------------
                         */
-
                        ENL1_printf("rescanning inner plan");
 
                        /*
@@ -189,48 +162,101 @@ ExecNestLoop(NestLoop *node)
                         * expr context.
                         */
                        ExecReScan(innerPlan, econtext, (Plan *) node);
+               }
+
+               /* ----------------
+                *      we have an outerTuple, try to get the next inner tuple.
+                * ----------------
+                */
+               ENL1_printf("getting new inner tuple");
+
+               innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
+               econtext->ecxt_innertuple = innerTupleSlot;
 
-                       ENL1_printf("getting new inner tuple");
+               if (TupIsNull(innerTupleSlot))
+               {
+                       ENL1_printf("no inner tuple, need new outer tuple");
 
-                       innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
-                       econtext->ecxt_innertuple = innerTupleSlot;
+                       nlstate->nl_NeedNewOuter = true;
 
-                       if (TupIsNull(innerTupleSlot))
-                               ENL1_printf("couldn't get inner tuple - need new outer tuple");
-                       else
+                       if (! nlstate->nl_MatchedOuter &&
+                               node->join.jointype == JOIN_LEFT)
                        {
-                               ENL1_printf("got inner and outer tuples");
-                               needNewOuterTuple = false;
+                               /*
+                                * We are doing an outer join and there were no join matches
+                                * for this outer tuple.  Generate a fake join tuple with
+                                * nulls for the inner tuple, and return it if it passes
+                                * the non-join quals.
+                                */
+                               econtext->ecxt_innertuple = nlstate->nl_NullInnerTupleSlot;
+
+                               ENL1_printf("testing qualification for outer-join tuple");
+
+                               if (ExecQual(otherqual, econtext, false))
+                               {
+                                       /* ----------------
+                                        *      qualification was satisfied so we project and
+                                        *      return the slot containing the result tuple
+                                        *      using ExecProject().
+                                        * ----------------
+                                        */
+                                       TupleTableSlot *result;
+                                       ExprDoneCond isDone;
+
+                                       ENL1_printf("qualification succeeded, projecting tuple");
+
+                                       result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone);
+
+                                       if (isDone != ExprEndResult)
+                                       {
+                                               nlstate->jstate.cs_TupFromTlist =
+                                                       (isDone == ExprMultipleResult);
+                                               return result;
+                                       }
+                               }
                        }
-               }                                               /* while (needNewOuterTuple) */
+                       /*
+                        * Otherwise just return to top of loop for a new outer tuple.
+                        */
+                       continue;
+               }
 
                /* ----------------
                 *       at this point we have a new pair of inner and outer
                 *       tuples so we test the inner and outer tuples to see
-                *       if they satisify the node's qualification.
+                *       if they satisfy the node's qualification.
+                *
+                *       Only the joinquals determine MatchedOuter status,
+                *       but all quals must pass to actually return the tuple.
                 * ----------------
                 */
                ENL1_printf("testing qualification");
 
-               if (ExecQual((List *) qual, econtext, false))
+               if (ExecQual(joinqual, econtext, false))
                {
-                       /* ----------------
-                        *      qualification was satisified so we project and
-                        *      return the slot containing the result tuple
-                        *      using ExecProject().
-                        * ----------------
-                        */
-                       TupleTableSlot *result;
-                       ExprDoneCond isDone;
+                       nlstate->nl_MatchedOuter = true;
 
-                       ENL1_printf("qualification succeeded, projecting tuple");
-
-                       result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone);
-
-                       if (isDone != ExprEndResult)
+                       if (otherqual == NIL || ExecQual(otherqual, econtext, false))
                        {
-                               nlstate->jstate.cs_TupFromTlist = (isDone == ExprMultipleResult);
-                               return result;
+                               /* ----------------
+                                *      qualification was satisfied so we project and
+                                *      return the slot containing the result tuple
+                                *      using ExecProject().
+                                * ----------------
+                                */
+                               TupleTableSlot *result;
+                               ExprDoneCond isDone;
+
+                               ENL1_printf("qualification succeeded, projecting tuple");
+
+                               result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone);
+
+                               if (isDone != ExprEndResult)
+                               {
+                                       nlstate->jstate.cs_TupFromTlist =
+                                               (isDone == ExprMultipleResult);
+                                       return result;
+                               }
                        }
                }
 
@@ -264,7 +290,7 @@ ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent)
         *      assign execution state to node
         * ----------------
         */
-       node->join.state = estate;
+       node->join.plan.state = estate;
 
        /* ----------------
         *        create new nest loop state
@@ -281,19 +307,33 @@ ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent)
         */
        ExecAssignExprContext(estate, &nlstate->jstate);
 
-#define NESTLOOP_NSLOTS 1
        /* ----------------
-        *      tuple table initialization
+        *        now initialize children
         * ----------------
         */
-       ExecInitResultTupleSlot(estate, &nlstate->jstate);
+       ExecInitNode(outerPlan((Plan *) node), estate, (Plan *) node);
+       ExecInitNode(innerPlan((Plan *) node), estate, (Plan *) node);
 
+#define NESTLOOP_NSLOTS 2
        /* ----------------
-        *        now initialize children
+        *      tuple table initialization
         * ----------------
         */
-       ExecInitNode(outerPlan((Plan *) node), estate, (Plan *) node);
-       ExecInitNode(innerPlan((Plan *) node), estate, (Plan *) node);
+       ExecInitResultTupleSlot(estate, &nlstate->jstate);
+
+       switch (node->join.jointype)
+       {
+               case JOIN_INNER:
+                       break;
+               case JOIN_LEFT:
+                       nlstate->nl_NullInnerTupleSlot =
+                               ExecInitNullTupleSlot(estate,
+                                                                         ExecGetTupType(innerPlan((Plan*) node)));
+                       break;
+               default:
+                       elog(ERROR, "ExecInitNestLoop: unsupported join type %d",
+                                (int) node->join.jointype);
+       }
 
        /* ----------------
         *      initialize tuple type and projection info
@@ -308,6 +348,8 @@ ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent)
         */
        nlstate->jstate.cs_OuterTupleSlot = NULL;
        nlstate->jstate.cs_TupFromTlist = false;
+       nlstate->nl_NeedNewOuter = true;
+       nlstate->nl_MatchedOuter = false;
 
        NL1_printf("ExecInitNestLoop: %s\n",
                           "node initialized");
@@ -394,4 +436,6 @@ ExecReScanNestLoop(NestLoop *node, ExprContext *exprCtxt, Plan *parent)
        /* let outerPlan to free its result tuple ... */
        nlstate->jstate.cs_OuterTupleSlot = NULL;
        nlstate->jstate.cs_TupFromTlist = false;
+       nlstate->nl_NeedNewOuter = true;
+       nlstate->nl_MatchedOuter = false;
 }
index 7270d3116d8823ce3cdaf88da94e200e50df0703..77f17ee0a60f8eeb81e5ffbf3d69bb67dfbeb33e 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.120 2000/08/11 23:45:31 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.121 2000/09/12 21:06:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -311,8 +311,12 @@ _copyTidScan(TidScan *from)
 static void
 CopyJoinFields(Join *from, Join *newnode)
 {
-       /* nothing extra */
-       return;
+       newnode->jointype = from->jointype;
+       Node_Copy(from, newnode, joinqual);
+       /* subPlan list must point to subplans in the new subtree, not the old */
+       if (from->plan.subPlan != NIL)
+               newnode->plan.subPlan = nconc(newnode->plan.subPlan,
+                                                                         pull_subplans((Node *) newnode->joinqual));
 }
 
 
@@ -381,8 +385,8 @@ _copyMergeJoin(MergeJoin *from)
        /*
         * We must add subplans in mergeclauses to the new plan's subPlan list
         */
-       if (from->join.subPlan != NIL)
-               newnode->join.subPlan = nconc(newnode->join.subPlan,
+       if (from->join.plan.subPlan != NIL)
+               newnode->join.plan.subPlan = nconc(newnode->join.plan.subPlan,
                                                  pull_subplans((Node *) newnode->mergeclauses));
 
        return newnode;
@@ -414,8 +418,8 @@ _copyHashJoin(HashJoin *from)
        /*
         * We must add subplans in hashclauses to the new plan's subPlan list
         */
-       if (from->join.subPlan != NIL)
-               newnode->join.subPlan = nconc(newnode->join.subPlan,
+       if (from->join.plan.subPlan != NIL)
+               newnode->join.plan.subPlan = nconc(newnode->join.plan.subPlan,
                                                   pull_subplans((Node *) newnode->hashclauses));
 
        return newnode;
@@ -510,21 +514,6 @@ _copyGroupClause(GroupClause *from)
        return newnode;
 }
 
-static JoinExpr *
-_copyJoinExpr(JoinExpr *from)
-{
-       JoinExpr *newnode = makeNode(JoinExpr);
-
-       newnode->jointype = from->jointype;
-       newnode->isNatural = from->isNatural;
-       Node_Copy(from, newnode, larg);
-       Node_Copy(from, newnode, rarg);
-       Node_Copy(from, newnode, alias);
-       Node_Copy(from, newnode, quals);
-
-       return newnode;
-}
-
 /* ----------------
  *             _copyUnique
  * ----------------
@@ -914,6 +903,34 @@ _copyRelabelType(RelabelType *from)
        return newnode;
 }
 
+static RangeTblRef *
+_copyRangeTblRef(RangeTblRef *from)
+{
+       RangeTblRef *newnode = makeNode(RangeTblRef);
+
+       newnode->rtindex = from->rtindex;
+
+       return newnode;
+}
+
+static JoinExpr *
+_copyJoinExpr(JoinExpr *from)
+{
+       JoinExpr *newnode = makeNode(JoinExpr);
+
+       newnode->jointype = from->jointype;
+       newnode->isNatural = from->isNatural;
+       Node_Copy(from, newnode, larg);
+       Node_Copy(from, newnode, rarg);
+       Node_Copy(from, newnode, using);
+       Node_Copy(from, newnode, quals);
+       Node_Copy(from, newnode, alias);
+       Node_Copy(from, newnode, colnames);
+       Node_Copy(from, newnode, colvars);
+
+       return newnode;
+}
+
 /* ----------------
  *             _copyCaseExpr
  * ----------------
@@ -1014,6 +1031,7 @@ _copyRelOptInfo(RelOptInfo *from)
 
        Node_Copy(from, newnode, baserestrictinfo);
        newnode->baserestrictcost = from->baserestrictcost;
+       newnode->outerjoinset = listCopy(from->outerjoinset);
        Node_Copy(from, newnode, joininfo);
        Node_Copy(from, newnode, innerjoin);
 
@@ -1137,6 +1155,7 @@ _copyIndexPath(IndexPath *from)
        Node_Copy(from, newnode, indexqual);
        newnode->indexscandir = from->indexscandir;
        newnode->joinrelids = listCopy(from->joinrelids);
+       newnode->alljoinquals = from->alljoinquals;
        newnode->rows = from->rows;
 
        return newnode;
@@ -1177,6 +1196,7 @@ _copyTidPath(TidPath *from)
 static void
 CopyJoinPathFields(JoinPath *from, JoinPath *newnode)
 {
+       newnode->jointype = from->jointype;
        Node_Copy(from, newnode, outerjoinpath);
        Node_Copy(from, newnode, innerjoinpath);
        Node_Copy(from, newnode, joinrestrictinfo);
@@ -1286,6 +1306,7 @@ _copyRestrictInfo(RestrictInfo *from)
         * ----------------
         */
        Node_Copy(from, newnode, clause);
+       newnode->isjoinqual = from->isjoinqual;
        Node_Copy(from, newnode, subclauseindices);
        newnode->mergejoinoperator = from->mergejoinoperator;
        newnode->left_sortop = from->left_sortop;
@@ -1370,12 +1391,11 @@ _copyRangeTblEntry(RangeTblEntry *from)
 
        if (from->relname)
                newnode->relname = pstrdup(from->relname);
-       Node_Copy(from, newnode, ref);
-       Node_Copy(from, newnode, eref);
        newnode->relid = from->relid;
+       Node_Copy(from, newnode, alias);
+       Node_Copy(from, newnode, eref);
        newnode->inh = from->inh;
        newnode->inFromCl = from->inFromCl;
-       newnode->inJoinSet = from->inJoinSet;
        newnode->skipAcl = from->skipAcl;
 
        return newnode;
@@ -1526,18 +1546,6 @@ _copyTypeName(TypeName *from)
        return newnode;
 }
 
-static RelExpr *
-_copyRelExpr(RelExpr *from)
-{
-       RelExpr   *newnode = makeNode(RelExpr);
-
-       if (from->relname)
-               newnode->relname = pstrdup(from->relname);
-       newnode->inh = from->inh;
-
-       return newnode;
-}
-
 static SortGroupBy *
 _copySortGroupBy(SortGroupBy *from)
 {
@@ -1555,7 +1563,20 @@ _copyRangeVar(RangeVar *from)
 {
        RangeVar   *newnode = makeNode(RangeVar);
 
-       Node_Copy(from, newnode, relExpr);
+       if (from->relname)
+               newnode->relname = pstrdup(from->relname);
+       newnode->inh = from->inh;
+       Node_Copy(from, newnode, name);
+
+       return newnode;
+}
+
+static RangeSubselect *
+_copyRangeSubselect(RangeSubselect *from)
+{
+       RangeSubselect   *newnode = makeNode(RangeSubselect);
+
+       Node_Copy(from, newnode, subquery);
        Node_Copy(from, newnode, name);
 
        return newnode;
@@ -1650,6 +1671,8 @@ _copyQuery(Query *from)
        newnode->hasSubLinks = from->hasSubLinks;
 
        Node_Copy(from, newnode, rtable);
+       Node_Copy(from, newnode, jointree);
+
        Node_Copy(from, newnode, targetList);
        Node_Copy(from, newnode, qual);
        Node_Copy(from, newnode, rowMark);
@@ -2548,6 +2571,12 @@ copyObject(void *from)
                case T_RelabelType:
                        retval = _copyRelabelType(from);
                        break;
+               case T_RangeTblRef:
+                       retval = _copyRangeTblRef(from);
+                       break;
+               case T_JoinExpr:
+                       retval = _copyJoinExpr(from);
+                       break;
 
                        /*
                         * RELATION NODES
@@ -2809,15 +2838,15 @@ copyObject(void *from)
                case T_TypeCast:
                        retval = _copyTypeCast(from);
                        break;
-               case T_RelExpr:
-                       retval = _copyRelExpr(from);
-                       break;
                case T_SortGroupBy:
                        retval = _copySortGroupBy(from);
                        break;
                case T_RangeVar:
                        retval = _copyRangeVar(from);
                        break;
+               case T_RangeSubselect:
+                       retval = _copyRangeSubselect(from);
+                       break;
                case T_TypeName:
                        retval = _copyTypeName(from);
                        break;
@@ -2845,9 +2874,6 @@ copyObject(void *from)
                case T_GroupClause:
                        retval = _copyGroupClause(from);
                        break;
-               case T_JoinExpr:
-                       retval = _copyJoinExpr(from);
-                       break;
                case T_CaseExpr:
                        retval = _copyCaseExpr(from);
                        break;
index b059e5cd5f0b92fed74c6ac3efc4bc434b51124f..51a7a03fc1b8deabe62ef3bb1eaa3c82ac9abc12 100644 (file)
@@ -20,7 +20,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.72 2000/08/11 23:45:31 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.73 2000/09/12 21:06:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -256,6 +256,26 @@ _equalSubLink(SubLink *a, SubLink *b)
        return true;
 }
 
+static bool
+_equalArrayRef(ArrayRef *a, ArrayRef *b)
+{
+       if (a->refelemtype != b->refelemtype)
+               return false;
+       if (a->refattrlength != b->refattrlength)
+               return false;
+       if (a->refelemlength != b->refelemlength)
+               return false;
+       if (a->refelembyval != b->refelembyval)
+               return false;
+       if (!equal(a->refupperindexpr, b->refupperindexpr))
+               return false;
+       if (!equal(a->reflowerindexpr, b->reflowerindexpr))
+               return false;
+       if (!equal(a->refexpr, b->refexpr))
+               return false;
+       return equal(a->refassgnexpr, b->refassgnexpr);
+}
+
 static bool
 _equalFieldSelect(FieldSelect *a, FieldSelect *b)
 {
@@ -283,23 +303,37 @@ _equalRelabelType(RelabelType *a, RelabelType *b)
 }
 
 static bool
-_equalArrayRef(ArrayRef *a, ArrayRef *b)
+_equalRangeTblRef(RangeTblRef *a, RangeTblRef *b)
 {
-       if (a->refelemtype != b->refelemtype)
+       if (a->rtindex != b->rtindex)
                return false;
-       if (a->refattrlength != b->refattrlength)
+
+       return true;
+}
+
+static bool
+_equalJoinExpr(JoinExpr *a, JoinExpr *b)
+{
+       if (a->jointype != b->jointype)
                return false;
-       if (a->refelemlength != b->refelemlength)
+       if (a->isNatural != b->isNatural)
                return false;
-       if (a->refelembyval != b->refelembyval)
+       if (!equal(a->larg, b->larg))
                return false;
-       if (!equal(a->refupperindexpr, b->refupperindexpr))
+       if (!equal(a->rarg, b->rarg))
                return false;
-       if (!equal(a->reflowerindexpr, b->reflowerindexpr))
+       if (!equal(a->using, b->using))
                return false;
-       if (!equal(a->refexpr, b->refexpr))
+       if (!equal(a->quals, b->quals))
                return false;
-       return equal(a->refassgnexpr, b->refassgnexpr);
+       if (!equal(a->alias, b->alias))
+               return false;
+       if (!equal(a->colnames, b->colnames))
+               return false;
+       if (!equal(a->colvars, b->colvars))
+               return false;
+
+       return true;
 }
 
 /*
@@ -370,6 +404,8 @@ _equalIndexPath(IndexPath *a, IndexPath *b)
                return false;
        if (!equali(a->joinrelids, b->joinrelids))
                return false;
+       if (a->alljoinquals != b->alljoinquals)
+               return false;
 
        /*
         * Skip 'rows' because of possibility of floating-point roundoff
@@ -395,6 +431,8 @@ _equalJoinPath(JoinPath *a, JoinPath *b)
 {
        if (!_equalPath((Path *) a, (Path *) b))
                return false;
+       if (a->jointype != b->jointype)
+               return false;
        if (!equal(a->outerjoinpath, b->outerjoinpath))
                return false;
        if (!equal(a->innerjoinpath, b->innerjoinpath))
@@ -457,6 +495,8 @@ _equalRestrictInfo(RestrictInfo *a, RestrictInfo *b)
 {
        if (!equal(a->clause, b->clause))
                return false;
+       if (a->isjoinqual != b->isjoinqual)
+               return false;
        if (!equal(a->subclauseindices, b->subclauseindices))
                return false;
        if (a->mergejoinoperator != b->mergejoinoperator)
@@ -557,6 +597,8 @@ _equalQuery(Query *a, Query *b)
                return false;
        if (!equal(a->rtable, b->rtable))
                return false;
+       if (!equal(a->jointree, b->jointree))
+               return false;
        if (!equal(a->targetList, b->targetList))
                return false;
        if (!equal(a->qual, b->qual))
@@ -1476,31 +1518,33 @@ _equalTypeCast(TypeCast *a, TypeCast *b)
 }
 
 static bool
-_equalRelExpr(RelExpr *a, RelExpr *b)
+_equalSortGroupBy(SortGroupBy *a, SortGroupBy *b)
 {
-       if (!equalstr(a->relname, b->relname))
+       if (!equalstr(a->useOp, b->useOp))
                return false;
-       if (a->inh != b->inh)
+       if (!equal(a->node, b->node))
                return false;
 
        return true;
 }
 
 static bool
-_equalSortGroupBy(SortGroupBy *a, SortGroupBy *b)
+_equalRangeVar(RangeVar *a, RangeVar *b)
 {
-       if (!equalstr(a->useOp, b->useOp))
+       if (!equalstr(a->relname, b->relname))
                return false;
-       if (!equal(a->node, b->node))
+       if (a->inh != b->inh)
+               return false;
+       if (!equal(a->name, b->name))
                return false;
 
        return true;
 }
 
 static bool
-_equalRangeVar(RangeVar *a, RangeVar *b)
+_equalRangeSubselect(RangeSubselect *a, RangeSubselect *b)
 {
-       if (!equal(a->relExpr, b->relExpr))
+       if (!equal(a->subquery, b->subquery))
                return false;
        if (!equal(a->name, b->name))
                return false;
@@ -1605,17 +1649,16 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
 {
        if (!equalstr(a->relname, b->relname))
                return false;
-       if (!equal(a->ref, b->ref))
-               return false;
-       /* XXX what about eref? */
        if (a->relid != b->relid)
                return false;
+       if (!equal(a->alias, b->alias))
+               return false;
+       if (!equal(a->eref, b->eref))
+               return false;
        if (a->inh != b->inh)
                return false;
        if (a->inFromCl != b->inFromCl)
                return false;
-       if (a->inJoinSet != b->inJoinSet)
-               return false;
        if (a->skipAcl != b->skipAcl)
                return false;
 
@@ -1644,25 +1687,6 @@ _equalRowMark(RowMark *a, RowMark *b)
        return true;
 }
 
-static bool
-_equalJoinExpr(JoinExpr *a, JoinExpr *b)
-{
-       if (a->jointype != b->jointype)
-               return false;
-       if (a->isNatural != b->isNatural)
-               return false;
-       if (!equal(a->larg, b->larg))
-               return false;
-       if (!equal(a->rarg, b->rarg))
-               return false;
-       if (!equal(a->alias, b->alias))
-               return false;
-       if (!equal(a->quals, b->quals))
-               return false;
-
-       return true;
-}
-
 static bool
 _equalFkConstraint(FkConstraint *a, FkConstraint *b)
 {
@@ -1808,6 +1832,12 @@ equal(void *a, void *b)
                case T_RelabelType:
                        retval = _equalRelabelType(a, b);
                        break;
+               case T_RangeTblRef:
+                       retval = _equalRangeTblRef(a, b);
+                       break;
+               case T_JoinExpr:
+                       retval = _equalJoinExpr(a, b);
+                       break;
 
                case T_RelOptInfo:
                        retval = _equalRelOptInfo(a, b);
@@ -2067,15 +2097,15 @@ equal(void *a, void *b)
                case T_TypeCast:
                        retval = _equalTypeCast(a, b);
                        break;
-               case T_RelExpr:
-                       retval = _equalRelExpr(a, b);
-                       break;
                case T_SortGroupBy:
                        retval = _equalSortGroupBy(a, b);
                        break;
                case T_RangeVar:
                        retval = _equalRangeVar(a, b);
                        break;
+               case T_RangeSubselect:
+                       retval = _equalRangeSubselect(a, b);
+                       break;
                case T_TypeName:
                        retval = _equalTypeName(a, b);
                        break;
@@ -2104,9 +2134,6 @@ equal(void *a, void *b)
                        /* GroupClause is equivalent to SortClause */
                        retval = _equalSortClause(a, b);
                        break;
-               case T_JoinExpr:
-                       retval = _equalJoinExpr(a, b);
-                       break;
                case T_CaseExpr:
                        retval = _equalCaseExpr(a, b);
                        break;
index 45f42dc502448d315f1a63ffe10398384f526b3d..e94b357d24b1db8ed740068353a496736f3c758c 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/list.c,v 1.32 2000/06/09 01:44:12 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/list.c,v 1.33 2000/09/12 21:06:49 tgl Exp $
  *
  * NOTES
  *       XXX a few of the following functions are duplicated to handle
@@ -351,6 +351,25 @@ member(void *l1, List *l2)
        return false;
 }
 
+/*
+ * like member(), but use when pointer-equality comparison is sufficient
+ */
+bool
+ptrMember(void *l1, List *l2)
+{
+       List       *i;
+
+       foreach(i, l2)
+       {
+               if (l1 == ((void *) lfirst(i)))
+                       return true;
+       }
+       return false;
+}
+
+/*
+ * membership test for integer lists
+ */
 bool
 intMember(int l1, List *l2)
 {
index 14f2ab106c73e1e4d6a0fa2d87f508ac91075530..8b24b82122fdbd8763c80d30d932098549ab2712 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.125 2000/08/08 15:41:26 tgl Exp $
+ *     $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.126 2000/09/12 21:06:49 tgl Exp $
  *
  * NOTES
  *       Every (plan) node in POSTGRES has an associated "out" routine which
@@ -268,6 +268,9 @@ _outQuery(StringInfo str, Query *node)
        appendStringInfo(str, " :rtable ");
        _outNode(str, node->rtable);
 
+       appendStringInfo(str, " :jointree ");
+       _outNode(str, node->jointree);
+
        appendStringInfo(str, " :targetlist ");
        _outNode(str, node->targetList);
 
@@ -389,7 +392,6 @@ _outAppend(StringInfo str, Append *node)
                                         " :inheritrelid %u :inheritrtable ",
                                         node->inheritrelid);
        _outNode(str, node->inheritrtable);
-
 }
 
 /*
@@ -400,7 +402,9 @@ _outJoin(StringInfo str, Join *node)
 {
        appendStringInfo(str, " JOIN ");
        _outPlanInfo(str, (Plan *) node);
-
+       appendStringInfo(str, " :jointype %d :joinqual ",
+                                        (int) node->jointype);
+       _outNode(str, node->joinqual);
 }
 
 /*
@@ -411,6 +415,9 @@ _outNestLoop(StringInfo str, NestLoop *node)
 {
        appendStringInfo(str, " NESTLOOP ");
        _outPlanInfo(str, (Plan *) node);
+       appendStringInfo(str, " :jointype %d :joinqual ",
+                                        (int) node->join.jointype);
+       _outNode(str, node->join.joinqual);
 }
 
 /*
@@ -421,6 +428,9 @@ _outMergeJoin(StringInfo str, MergeJoin *node)
 {
        appendStringInfo(str, " MERGEJOIN ");
        _outPlanInfo(str, (Plan *) node);
+       appendStringInfo(str, " :jointype %d :joinqual ",
+                                        (int) node->join.jointype);
+       _outNode(str, node->join.joinqual);
 
        appendStringInfo(str, " :mergeclauses ");
        _outNode(str, node->mergeclauses);
@@ -434,17 +444,14 @@ _outHashJoin(StringInfo str, HashJoin *node)
 {
        appendStringInfo(str, " HASHJOIN ");
        _outPlanInfo(str, (Plan *) node);
+       appendStringInfo(str, " :jointype %d :joinqual ",
+                                        (int) node->join.jointype);
+       _outNode(str, node->join.joinqual);
 
        appendStringInfo(str, " :hashclauses ");
        _outNode(str, node->hashclauses);
-
-       appendStringInfo(str,
-                                        " :hashjoinop %u ",
+       appendStringInfo(str, " :hashjoinop %u ",
                                         node->hashjoinop);
-
-       appendStringInfo(str,
-                                        " :hashdone %d ",
-                                        node->hashdone);
 }
 
 static void
@@ -757,32 +764,6 @@ _outSubLink(StringInfo str, SubLink *node)
        _outNode(str, node->subselect);
 }
 
-/*
- *     FieldSelect
- */
-static void
-_outFieldSelect(StringInfo str, FieldSelect *node)
-{
-       appendStringInfo(str, " FIELDSELECT :arg ");
-       _outNode(str, node->arg);
-
-       appendStringInfo(str, " :fieldnum %d :resulttype %u :resulttypmod %d ",
-                                        node->fieldnum, node->resulttype, node->resulttypmod);
-}
-
-/*
- *     RelabelType
- */
-static void
-_outRelabelType(StringInfo str, RelabelType *node)
-{
-       appendStringInfo(str, " RELABELTYPE :arg ");
-       _outNode(str, node->arg);
-
-       appendStringInfo(str, " :resulttype %u :resulttypmod %d ",
-                                        node->resulttype, node->resulttypmod);
-}
-
 /*
  *     ArrayRef is a subclass of Expr
  */
@@ -846,6 +827,66 @@ _outParam(StringInfo str, Param *node)
        appendStringInfo(str, " :paramtype %u ", node->paramtype);
 }
 
+/*
+ *     FieldSelect
+ */
+static void
+_outFieldSelect(StringInfo str, FieldSelect *node)
+{
+       appendStringInfo(str, " FIELDSELECT :arg ");
+       _outNode(str, node->arg);
+
+       appendStringInfo(str, " :fieldnum %d :resulttype %u :resulttypmod %d ",
+                                        node->fieldnum, node->resulttype, node->resulttypmod);
+}
+
+/*
+ *     RelabelType
+ */
+static void
+_outRelabelType(StringInfo str, RelabelType *node)
+{
+       appendStringInfo(str, " RELABELTYPE :arg ");
+       _outNode(str, node->arg);
+
+       appendStringInfo(str, " :resulttype %u :resulttypmod %d ",
+                                        node->resulttype, node->resulttypmod);
+}
+
+/*
+ *     RangeTblRef
+ */
+static void
+_outRangeTblRef(StringInfo str, RangeTblRef *node)
+{
+       appendStringInfo(str, " RANGETBLREF %d ",
+                                        node->rtindex);
+}
+
+/*
+ *     JoinExpr
+ */
+static void
+_outJoinExpr(StringInfo str, JoinExpr *node)
+{
+       appendStringInfo(str, " JOINEXPR :jointype %d :isNatural %s :larg ",
+                                        (int) node->jointype,
+                                        node->isNatural ? "true" : "false");
+       _outNode(str, node->larg);
+       appendStringInfo(str, " :rarg ");
+       _outNode(str, node->rarg);
+       appendStringInfo(str, " :using ");
+       _outNode(str, node->using);
+       appendStringInfo(str, " :quals ");
+       _outNode(str, node->quals);
+       appendStringInfo(str, " :alias ");
+       _outNode(str, node->alias);
+       appendStringInfo(str, " :colnames ");
+       _outNode(str, node->colnames);
+       appendStringInfo(str, " :colvars ");
+       _outNode(str, node->colvars);
+}
+
 /*
  *     Stuff from execnodes.h
  */
@@ -897,6 +938,11 @@ _outRelOptInfo(StringInfo str, RelOptInfo *node)
                                         node->pruneable ? "true" : "false");
        _outNode(str, node->baserestrictinfo);
 
+       appendStringInfo(str,
+                                        " :baserestrictcost %.2f :outerjoinset ",
+                                        node->baserestrictcost);
+       _outIntList(str, node->outerjoinset);
+
        appendStringInfo(str, " :joininfo ");
        _outNode(str, node->joininfo);
 
@@ -931,14 +977,14 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
 {
        appendStringInfo(str, " RTE :relname ");
        _outToken(str, node->relname);
-       appendStringInfo(str, " :ref ");
-       _outNode(str, node->ref);
-       appendStringInfo(str,
-                        " :relid %u :inh %s :inFromCl %s :inJoinSet %s :skipAcl %s",
-                                        node->relid,
+       appendStringInfo(str, " :relid %u :alias ",
+                                        node->relid);
+       _outNode(str, node->alias);
+       appendStringInfo(str, " :eref ");
+       _outNode(str, node->eref);
+       appendStringInfo(str, " :inh %s :inFromCl %s :skipAcl %s",
                                         node->inh ? "true" : "false",
                                         node->inFromCl ? "true" : "false",
-                                        node->inJoinSet ? "true" : "false",
                                         node->skipAcl ? "true" : "false");
 }
 
@@ -985,7 +1031,8 @@ _outIndexPath(StringInfo str, IndexPath *node)
                                         (int) node->indexscandir);
        _outIntList(str, node->joinrelids);
 
-       appendStringInfo(str, " :rows %.2f ",
+       appendStringInfo(str, " :alljoinquals %s :rows %.2f ",
+                                        node->alljoinquals ? "true" : "false",
                                         node->rows);
 }
 
@@ -1021,7 +1068,8 @@ _outNestPath(StringInfo str, NestPath *node)
                                         node->path.startup_cost,
                                         node->path.total_cost);
        _outNode(str, node->path.pathkeys);
-       appendStringInfo(str, " :outerjoinpath ");
+       appendStringInfo(str, " :jointype %d :outerjoinpath ",
+                                        (int) node->jointype);
        _outNode(str, node->outerjoinpath);
        appendStringInfo(str, " :innerjoinpath ");
        _outNode(str, node->innerjoinpath);
@@ -1041,7 +1089,8 @@ _outMergePath(StringInfo str, MergePath *node)
                                         node->jpath.path.startup_cost,
                                         node->jpath.path.total_cost);
        _outNode(str, node->jpath.path.pathkeys);
-       appendStringInfo(str, " :outerjoinpath ");
+       appendStringInfo(str, " :jointype %d :outerjoinpath ",
+                                        (int) node->jpath.jointype);
        _outNode(str, node->jpath.outerjoinpath);
        appendStringInfo(str, " :innerjoinpath ");
        _outNode(str, node->jpath.innerjoinpath);
@@ -1070,7 +1119,8 @@ _outHashPath(StringInfo str, HashPath *node)
                                         node->jpath.path.startup_cost,
                                         node->jpath.path.total_cost);
        _outNode(str, node->jpath.path.pathkeys);
-       appendStringInfo(str, " :outerjoinpath ");
+       appendStringInfo(str, " :jointype %d :outerjoinpath ",
+                                        (int) node->jpath.jointype);
        _outNode(str, node->jpath.outerjoinpath);
        appendStringInfo(str, " :innerjoinpath ");
        _outNode(str, node->jpath.innerjoinpath);
@@ -1101,7 +1151,8 @@ _outRestrictInfo(StringInfo str, RestrictInfo *node)
        appendStringInfo(str, " RESTRICTINFO :clause ");
        _outNode(str, node->clause);
 
-       appendStringInfo(str, " :subclauseindices ");
+       appendStringInfo(str, " :isjoinqual %s :subclauseindices ",
+                                        node->isjoinqual ? "true" : "false");
        _outNode(str, node->subclauseindices);
 
        appendStringInfo(str, " :mergejoinoperator %u ", node->mergejoinoperator);
@@ -1483,12 +1534,6 @@ _outNode(StringInfo str, void *obj)
                        case T_SubLink:
                                _outSubLink(str, obj);
                                break;
-                       case T_FieldSelect:
-                               _outFieldSelect(str, obj);
-                               break;
-                       case T_RelabelType:
-                               _outRelabelType(str, obj);
-                               break;
                        case T_ArrayRef:
                                _outArrayRef(str, obj);
                                break;
@@ -1501,6 +1546,18 @@ _outNode(StringInfo str, void *obj)
                        case T_Param:
                                _outParam(str, obj);
                                break;
+                       case T_FieldSelect:
+                               _outFieldSelect(str, obj);
+                               break;
+                       case T_RelabelType:
+                               _outRelabelType(str, obj);
+                               break;
+                       case T_RangeTblRef:
+                               _outRangeTblRef(str, obj);
+                               break;
+                       case T_JoinExpr:
+                               _outJoinExpr(str, obj);
+                               break;
                        case T_EState:
                                _outEState(str, obj);
                                break;
index 104735cf6f63813c72ae865163ca44127ca4c8dc..7bf78e134bc3968e35c174a73498d4fc69374acd 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.39 2000/06/18 22:44:05 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.40 2000/09/12 21:06:49 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -133,7 +133,7 @@ print_rt(List *rtable)
                RangeTblEntry *rte = lfirst(l);
 
                printf("%d\t%s(%s)\t%u\t%d\t%s\n",
-                          i, rte->relname, rte->ref->relname, rte->relid,
+                          i, rte->relname, rte->eref->relname, rte->relid,
                           rte->inFromCl,
                           (rte->inh ? "inh" : ""));
                i++;
@@ -157,7 +157,6 @@ print_expr(Node *expr, List *rtable)
        if (IsA(expr, Var))
        {
                Var                *var = (Var *) expr;
-               RangeTblEntry *rt;
                char       *relname,
                                   *attname;
 
@@ -173,10 +172,10 @@ print_expr(Node *expr, List *rtable)
                                break;
                        default:
                                {
+                                       RangeTblEntry *rt;
+
                                        rt = rt_fetch(var->varno, rtable);
-                                       relname = rt->relname;
-                                       if (rt->ref && rt->ref->relname)
-                                               relname = rt->ref->relname;             /* table renamed */
+                                       relname = rt->eref->relname;
                                        attname = get_attname(rt->relid, var->varattno);
                                }
                                break;
index 17e0396e5fee4f4d7e67a611b22f1575116835bf..00a6407db8b2c62012b74d9755310a5af61b24a5 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.95 2000/08/08 15:41:27 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.96 2000/09/12 21:06:49 tgl Exp $
  *
  * NOTES
  *       Most of the read functions for plan nodes are tested. (In fact, they
@@ -119,6 +119,9 @@ _readQuery()
        token = lsptok(NULL, &length);          /* skip :rtable */
        local_node->rtable = nodeRead(true);
 
+       token = lsptok(NULL, &length);          /* skip :jointree */
+       local_node->jointree = nodeRead(true);
+
        token = lsptok(NULL, &length);          /* skip :targetlist */
        local_node->targetList = nodeRead(true);
 
@@ -335,14 +338,22 @@ _readAppend()
 
 /* ----------------
  *             _getJoin
- *
- * In case Join is not the same structure as Plan someday.
  * ----------------
  */
 static void
 _getJoin(Join *node)
 {
+       char       *token;
+       int                     length;
+
        _getPlan((Plan *) node);
+
+       token = lsptok(NULL, &length);          /* skip the :jointype */
+       token = lsptok(NULL, &length);          /* get the jointype */
+       node->jointype = (JoinType) atoi(token);
+
+       token = lsptok(NULL, &length);          /* skip the :joinqual */
+       node->joinqual = nodeRead(true);        /* get the joinqual */
 }
 
 
@@ -399,6 +410,7 @@ _readMergeJoin()
        local_node = makeNode(MergeJoin);
 
        _getJoin((Join *) local_node);
+
        token = lsptok(NULL, &length);          /* eat :mergeclauses */
        local_node->mergeclauses = nodeRead(true);      /* now read it */
 
@@ -429,19 +441,13 @@ _readHashJoin()
        token = lsptok(NULL, &length);          /* get hashjoinop */
        local_node->hashjoinop = strtoul(token, NULL, 10);
 
-       token = lsptok(NULL, &length);          /* eat :hashdone */
-       token = lsptok(NULL, &length);          /* eat hashdone */
-       local_node->hashdone = false;
-
        return local_node;
 }
 
 /* ----------------
  *             _getScan
  *
- *     Scan is a subclass of Node
- *     (Actually, according to the plannodes.h include file, it is a
- *     subclass of Plan.  This is why _getPlan is used here.)
+ *     Scan is a subclass of Plan.
  *
  *     Scan gets its own get function since stuff inherits it.
  * ----------------
@@ -462,7 +468,7 @@ _getScan(Scan *node)
 /* ----------------
  *             _readScan
  *
- * Scan is a subclass of Plan (Not Node, see above).
+ * Scan is a subclass of Plan.
  * ----------------
  */
 static Scan *
@@ -1154,6 +1160,74 @@ _readRelabelType()
        return local_node;
 }
 
+/* ----------------
+ *             _readRangeTblRef
+ *
+ *     RangeTblRef is a subclass of Node
+ * ----------------
+ */
+static RangeTblRef *
+_readRangeTblRef()
+{
+       RangeTblRef *local_node;
+       char       *token;
+       int                     length;
+
+       local_node = makeNode(RangeTblRef);
+
+       token = lsptok(NULL, &length);          /* get rtindex */
+       local_node->rtindex = atoi(token);
+
+       return local_node;
+}
+
+/* ----------------
+ *             _readJoinExpr
+ *
+ *     JoinExpr is a subclass of Node
+ * ----------------
+ */
+static JoinExpr *
+_readJoinExpr()
+{
+       JoinExpr   *local_node;
+       char       *token;
+       int                     length;
+
+       local_node = makeNode(JoinExpr);
+
+       token = lsptok(NULL, &length);          /* eat :jointype */
+       token = lsptok(NULL, &length);          /* get jointype */
+       local_node->jointype = (JoinType) atoi(token);
+
+       token = lsptok(NULL, &length);          /* eat :isNatural */
+       token = lsptok(NULL, &length);          /* get :isNatural */
+       local_node->isNatural = (token[0] == 't') ? true : false;
+
+       token = lsptok(NULL, &length);          /* eat :larg */
+       local_node->larg = nodeRead(true);      /* now read it */
+
+       token = lsptok(NULL, &length);          /* eat :rarg */
+       local_node->rarg = nodeRead(true);      /* now read it */
+
+       token = lsptok(NULL, &length);          /* eat :using */
+       local_node->using = nodeRead(true);     /* now read it */
+
+       token = lsptok(NULL, &length);          /* eat :quals */
+       local_node->quals = nodeRead(true);     /* now read it */
+
+       token = lsptok(NULL, &length);          /* eat :alias */
+       local_node->alias = nodeRead(true);     /* now read it */
+
+       token = lsptok(NULL, &length);          /* eat :colnames */
+       local_node->colnames = nodeRead(true); /* now read it */
+
+       token = lsptok(NULL, &length);          /* eat :colvars */
+       local_node->colvars = nodeRead(true); /* now read it */
+
+       return local_node;
+}
+
 /*
  *     Stuff from execnodes.h
  */
@@ -1252,7 +1326,14 @@ _readRelOptInfo()
        local_node->pruneable = (token[0] == 't') ? true : false;
 
        token = lsptok(NULL, &length);          /* get :baserestrictinfo */
-       local_node->baserestrictinfo = nodeRead(true);          /* now read it */
+       local_node->baserestrictinfo = nodeRead(true); /* now read it */
+
+       token = lsptok(NULL, &length);          /* get :baserestrictcost */
+       token = lsptok(NULL, &length);          /* now read it */
+       local_node->baserestrictcost = (Cost) atof(token);
+
+       token = lsptok(NULL, &length);          /* get :outerjoinset */
+       local_node->outerjoinset = toIntList(nodeRead(true)); /* now read it */
 
        token = lsptok(NULL, &length);          /* get :joininfo */
        local_node->joininfo = nodeRead(true);          /* now read it */
@@ -1324,13 +1405,16 @@ _readRangeTblEntry()
        else
                local_node->relname = debackslash(token, length);
 
-       token = lsptok(NULL, &length);          /* eat :ref */
-       local_node->ref = nodeRead(true);       /* now read it */
-
        token = lsptok(NULL, &length);          /* eat :relid */
        token = lsptok(NULL, &length);          /* get :relid */
        local_node->relid = strtoul(token, NULL, 10);
 
+       token = lsptok(NULL, &length);          /* eat :alias */
+       local_node->alias = nodeRead(true);     /* now read it */
+
+       token = lsptok(NULL, &length);          /* eat :eref */
+       local_node->eref = nodeRead(true);      /* now read it */
+
        token = lsptok(NULL, &length);          /* eat :inh */
        token = lsptok(NULL, &length);          /* get :inh */
        local_node->inh = (token[0] == 't') ? true : false;
@@ -1339,10 +1423,6 @@ _readRangeTblEntry()
        token = lsptok(NULL, &length);          /* get :inFromCl */
        local_node->inFromCl = (token[0] == 't') ? true : false;
 
-       token = lsptok(NULL, &length);          /* eat :inJoinSet */
-       token = lsptok(NULL, &length);          /* get :inJoinSet */
-       local_node->inJoinSet = (token[0] == 't') ? true : false;
-
        token = lsptok(NULL, &length);          /* eat :skipAcl */
        token = lsptok(NULL, &length);          /* get :skipAcl */
        local_node->skipAcl = (token[0] == 't') ? true : false;
@@ -1444,6 +1524,10 @@ _readIndexPath()
        token = lsptok(NULL, &length);          /* get :joinrelids */
        local_node->joinrelids = toIntList(nodeRead(true));
 
+       token = lsptok(NULL, &length);          /* get :alljoinquals */
+       token = lsptok(NULL, &length);          /* now read it */
+       local_node->alljoinquals = (token[0] == 't') ? true : false;
+
        token = lsptok(NULL, &length);          /* get :rows */
        token = lsptok(NULL, &length);          /* now read it */
        local_node->rows = atof(token);
@@ -1520,6 +1604,10 @@ _readNestPath()
        token = lsptok(NULL, &length);          /* get :pathkeys */
        local_node->path.pathkeys = nodeRead(true); /* now read it */
 
+       token = lsptok(NULL, &length);          /* get :jointype */
+       token = lsptok(NULL, &length);          /* now read it */
+       local_node->jointype = (JoinType) atoi(token);
+
        token = lsptok(NULL, &length);          /* get :outerjoinpath */
        local_node->outerjoinpath = nodeRead(true); /* now read it */
 
@@ -1527,7 +1615,7 @@ _readNestPath()
        local_node->innerjoinpath = nodeRead(true); /* now read it */
 
        token = lsptok(NULL, &length);          /* get :joinrestrictinfo */
-       local_node->joinrestrictinfo = nodeRead(true);          /* now read it */
+       local_node->joinrestrictinfo = nodeRead(true); /* now read it */
 
        return local_node;
 }
@@ -1562,6 +1650,10 @@ _readMergePath()
        token = lsptok(NULL, &length);          /* get :pathkeys */
        local_node->jpath.path.pathkeys = nodeRead(true);       /* now read it */
 
+       token = lsptok(NULL, &length);          /* get :jointype */
+       token = lsptok(NULL, &length);          /* now read it */
+       local_node->jpath.jointype = (JoinType) atoi(token);
+
        token = lsptok(NULL, &length);          /* get :outerjoinpath */
        local_node->jpath.outerjoinpath = nodeRead(true);       /* now read it */
 
@@ -1569,7 +1661,7 @@ _readMergePath()
        local_node->jpath.innerjoinpath = nodeRead(true);       /* now read it */
 
        token = lsptok(NULL, &length);          /* get :joinrestrictinfo */
-       local_node->jpath.joinrestrictinfo = nodeRead(true);            /* now read it */
+       local_node->jpath.joinrestrictinfo = nodeRead(true); /* now read it */
 
        token = lsptok(NULL, &length);          /* get :path_mergeclauses */
        local_node->path_mergeclauses = nodeRead(true);         /* now read it */
@@ -1613,6 +1705,10 @@ _readHashPath()
        token = lsptok(NULL, &length);          /* get :pathkeys */
        local_node->jpath.path.pathkeys = nodeRead(true);       /* now read it */
 
+       token = lsptok(NULL, &length);          /* get :jointype */
+       token = lsptok(NULL, &length);          /* now read it */
+       local_node->jpath.jointype = (JoinType) atoi(token);
+
        token = lsptok(NULL, &length);          /* get :outerjoinpath */
        local_node->jpath.outerjoinpath = nodeRead(true);       /* now read it */
 
@@ -1620,7 +1716,7 @@ _readHashPath()
        local_node->jpath.innerjoinpath = nodeRead(true);       /* now read it */
 
        token = lsptok(NULL, &length);          /* get :joinrestrictinfo */
-       local_node->jpath.joinrestrictinfo = nodeRead(true);            /* now read it */
+       local_node->jpath.joinrestrictinfo = nodeRead(true); /* now read it */
 
        token = lsptok(NULL, &length);          /* get :path_hashclauses */
        local_node->path_hashclauses = nodeRead(true);          /* now read it */
@@ -1672,6 +1768,10 @@ _readRestrictInfo()
        token = lsptok(NULL, &length);          /* get :clause */
        local_node->clause = nodeRead(true);            /* now read it */
 
+       token = lsptok(NULL, &length);          /* get :isjoinqual */
+       token = lsptok(NULL, &length);          /* now read it */
+       local_node->isjoinqual = (token[0] == 't') ? true : false;
+
        token = lsptok(NULL, &length);          /* get :subclauseindices */
        local_node->subclauseindices = nodeRead(true);          /* now read it */
 
@@ -1789,6 +1889,10 @@ parsePlanString(void)
                return_value = _readFieldSelect();
        else if (length == 11 && strncmp(token, "RELABELTYPE", length) == 0)
                return_value = _readRelabelType();
+       else if (length == 11 && strncmp(token, "RANGETBLREF", length) == 0)
+               return_value = _readRangeTblRef();
+       else if (length == 8 && strncmp(token, "JOINEXPR", length) == 0)
+               return_value = _readJoinExpr();
        else if (length == 3 && strncmp(token, "AGG", length) == 0)
                return_value = _readAgg();
        else if (length == 4 && strncmp(token, "HASH", length) == 0)
index a867cd885edd04debb5c9bd0e759e3271860be2f..38901ede1fd9aff04492e7f4af925530d0a86aab 100644 (file)
@@ -35,10 +35,10 @@ RelOptInfo.pathlist.  (Actually, we discard Paths that are obviously
 inferior alternatives before they ever get into the pathlist --- what
 ends up in the pathlist is the cheapest way of generating each potentially
 useful sort ordering of the relation.)  Also create RelOptInfo.joininfo
-nodes that list all the joins that involve this relation.  For example,
-the WHERE clause "tab1.col1 = tab2.col1" generates a JoinInfo for tab1
-listing tab2 as an unjoined relation, and also one for tab2 showing tab1
-as an unjoined relation.
+nodes that list all the join clauses that involve this relation.  For
+example, the WHERE clause "tab1.col1 = tab2.col1" generates a JoinInfo
+for tab1 listing tab2 as an unjoined relation, and also one for tab2
+showing tab1 as an unjoined relation.
 
 If we have only a single base relation in the query, we are done now.
 Otherwise we have to figure out how to join the base relations into a
@@ -128,6 +128,19 @@ Once we have built the final join rel, we use either the cheapest path
 for it or the cheapest path with the desired ordering (if that's cheaper
 than applying a sort to the cheapest other path).
 
+The above dynamic-programming search is only conducted for simple cross
+joins (ie, SELECT FROM tab1, tab2, ...).  When the FROM clause contains
+explicit JOIN clauses, we join rels in exactly the order implied by the
+join tree.  Searching for the best possible join order is done only at
+the top implicit-cross-join level.  For example, in
+       SELECT FROM tab1, tab2, (tab3 NATURAL JOIN tab4)
+we will always join tab3 to tab4 and then consider all ways to join that
+result to tab1 and tab2.  Note that the JOIN syntax only constrains the
+order of joining --- we will still consider all available Paths and
+join methods for each JOIN operator.  We also consider both sides of
+the JOIN operator as inner or outer (so that we can transform RIGHT JOIN
+into LEFT JOIN).
+
 
 Optimizer Functions
 -------------------
@@ -158,13 +171,12 @@ planner()
    get a target list that only contains column names, no expressions
    if none, then return
 ---subplanner()
-    make list of relations in target
-    make list of relations in where clause
+    make list of base relations used in query
     split up the qual into restrictions (a=1) and joins (b=c)
-    find relation clauses can do merge sort and hash joins
+    find relation clauses that can do merge sort and hash joins
 ----make_one_rel()
      set_base_rel_pathlist()
-      find scan and all index paths for each relation
+      find scan and all index paths for each base relation
       find selectivity of columns used in joins
 -----make_one_rel_by_joins()
       jump to geqo if needed
index 7b9542cb1b21ced4fbc81c4ea6e2d7be9742b392..f32b0d64eebfc762a25ddd635d3bf68d4513fdb3 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: geqo_eval.c,v 1.53 2000/07/28 02:13:16 tgl Exp $
+ * $Id: geqo_eval.c,v 1.54 2000/09/12 21:06:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -93,11 +93,11 @@ geqo_eval(Query *root, Gene *tour, int num_gene)
  * Returns a new join relation incorporating all joins in a left-sided tree.
  */
 RelOptInfo *
-gimme_tree(Query *root, Gene *tour, int rel_count, int num_gene, RelOptInfo *old_rel)
+gimme_tree(Query *root, Gene *tour, int rel_count, int num_gene,
+                  RelOptInfo *old_rel)
 {
        RelOptInfo *inner_rel;          /* current relation */
        int                     base_rel_index;
-       RelOptInfo *new_rel;
 
        if (rel_count < num_gene)
        {                                                       /* tree not yet finished */
@@ -116,16 +116,22 @@ gimme_tree(Query *root, Gene *tour, int rel_count, int num_gene, RelOptInfo *old
                else
                {                                               /* tree main part */
                        List       *acceptable_rels = lcons(inner_rel, NIL);
-
-                       new_rel = make_rels_by_clause_joins(root, old_rel,
-                                                                                               acceptable_rels);
-                       if (!new_rel)
+                       List       *new_rels;
+                       RelOptInfo *new_rel;
+
+                       new_rels = make_rels_by_clause_joins(root, old_rel,
+                                                                                                acceptable_rels);
+                       /* Shouldn't get more than one result */
+                       Assert(length(new_rels) <= 1);
+                       if (new_rels == NIL)
                        {
-                               new_rel = make_rels_by_clauseless_joins(root, old_rel,
-                                                                                                               acceptable_rels);
-                               if (!new_rel)
+                               new_rels = make_rels_by_clauseless_joins(root, old_rel,
+                                                                                                                acceptable_rels);
+                               Assert(length(new_rels) <= 1);
+                               if (new_rels == NIL)
                                        elog(ERROR, "gimme_tree: failed to construct join rel");
                        }
+                       new_rel = (RelOptInfo *) lfirst(new_rels);
 
                        rel_count++;
                        Assert(length(new_rel->relids) == rel_count);
index 999364d563778132fd14fc9c60c7cd81d5b16bca..605b60b5845794349c916b9da6a616ca75df514d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.62 2000/05/31 00:28:22 petere Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.63 2000/09/12 21:06:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -26,7 +26,9 @@ int                   geqo_rels = DEFAULT_GEQO_RELS;
 
 
 static void set_base_rel_pathlist(Query *root);
-static RelOptInfo *make_one_rel_by_joins(Query *root, int levels_needed);
+static List *build_jointree_rels(Query *root);
+static RelOptInfo *make_one_rel_by_joins(Query *root, int levels_needed,
+                                                                                List *initial_rels);
 
 #ifdef OPTIMIZER_DEBUG
 static void debug_print_rel(Query *root, RelOptInfo *rel);
@@ -43,27 +45,38 @@ RelOptInfo *
 make_one_rel(Query *root)
 {
        int                     levels_needed;
+       List       *initial_rels;
 
        /*
-        * Set the number of join (not nesting) levels yet to be processed.
+        * Count the number of top-level jointree nodes.  This is the depth
+        * of the dynamic-programming algorithm we must employ to consider
+        * all ways of joining the top-level nodes.  Currently, we build
+        * JoinExpr joins in exactly the order implied by the join expression,
+        * so no dynamic-programming search is needed within a JoinExpr.
         */
-       levels_needed = length(root->base_rel_list);
+       levels_needed = length(root->jointree);
 
        if (levels_needed <= 0)
-               return NULL;
+               return NULL;                    /* nothing to do? */
 
        /*
         * Generate access paths for the base rels.
         */
        set_base_rel_pathlist(root);
 
+       /*
+        * Construct a list of rels corresponding to the toplevel jointree nodes.
+        * This may contain both base rels and rels constructed according to
+        * explicit JOIN directives.
+        */
+       initial_rels = build_jointree_rels(root);
+
        if (levels_needed == 1)
        {
-
                /*
-                * Single relation, no more processing is required.
+                * Single jointree node, so we're done.
                 */
-               return (RelOptInfo *) lfirst(root->base_rel_list);
+               return (RelOptInfo *) lfirst(initial_rels);
        }
        else
        {
@@ -71,7 +84,7 @@ make_one_rel(Query *root)
                /*
                 * Generate join tree.
                 */
-               return make_one_rel_by_joins(root, levels_needed);
+               return make_one_rel_by_joins(root, levels_needed, initial_rels);
        }
 }
 
@@ -125,20 +138,47 @@ set_base_rel_pathlist(Query *root)
        }
 }
 
+/*
+ * build_jointree_rels
+ *       Construct a RelOptInfo for each item in the query's jointree.
+ *
+ * At present, we handle explicit joins in the FROM clause exactly as
+ * specified, with no search for other join orders.  Only the cross-product
+ * joins at the top level are involved in the dynamic-programming search.
+ */
+static List *
+build_jointree_rels(Query *root)
+{
+       List       *rels = NIL;
+       List       *jt;
+
+       foreach(jt, root->jointree)
+       {
+               Node       *jtnode = (Node *) lfirst(jt);
+
+               rels = lappend(rels, make_rel_from_jointree(root, jtnode));
+       }
+       return rels;
+}
+
 /*
  * make_one_rel_by_joins
  *       Find all possible joinpaths for a query by successively finding ways
  *       to join component relations into join relations.
  *
  * 'levels_needed' is the number of iterations needed, ie, the number of
- *             base relations present in the query
+ *             independent jointree items in the query.  This is > 1.
+ *
+ * 'initial_rels' is a list of RelOptInfo nodes for each independent
+ *             jointree item.  These are the components to be joined together.
  *
  * Returns the final level of join relations, i.e., the relation that is
  * the result of joining all the original relations together.
  */
 static RelOptInfo *
-make_one_rel_by_joins(Query *root, int levels_needed)
+make_one_rel_by_joins(Query *root, int levels_needed, List *initial_rels)
 {
+       List      **joinitems;
        int                     lev;
        RelOptInfo *rel;
 
@@ -152,34 +192,35 @@ make_one_rel_by_joins(Query *root, int levels_needed)
 
        /*
         * We employ a simple "dynamic programming" algorithm: we first find
-        * all ways to build joins of two base relations, then all ways to
-        * build joins of three base relations (from two-base-rel joins and
-        * other base rels), then four-base-rel joins, and so on until we have
-        * considered all ways to join all N relations into one rel.
+        * all ways to build joins of two jointree items, then all ways to
+        * build joins of three items (from two-item joins and single items),
+        * then four-item joins, and so on until we have considered all ways
+        * to join all the items into one rel.
+        *
+        * joinitems[j] is a list of all the j-item rels.  Initially we set
+        * joinitems[1] to represent all the single-jointree-item relations.
         */
+       joinitems = (List **) palloc((levels_needed+1) * sizeof(List *));
+       MemSet(joinitems, 0, (levels_needed+1) * sizeof(List *));
+
+       joinitems[1] = initial_rels;
 
        for (lev = 2; lev <= levels_needed; lev++)
        {
-               List       *first_old_rel = root->join_rel_list;
                List       *x;
 
                /*
                 * Determine all possible pairs of relations to be joined at this
                 * level, and build paths for making each one from every available
-                * pair of lower-level relations.  Results are prepended to
-                * root->join_rel_list.
+                * pair of lower-level relations.
                 */
-               make_rels_by_joins(root, lev);
+               joinitems[lev] = make_rels_by_joins(root, lev, joinitems);
 
                /*
-                * The relations created at the current level will appear at the
-                * front of root->join_rel_list.
+                * Do cleanup work on each just-processed rel.
                 */
-               foreach(x, root->join_rel_list)
+               foreach(x, joinitems[lev])
                {
-                       if (x == first_old_rel)
-                               break;                  /* no more rels added at this level */
-
                        rel = (RelOptInfo *) lfirst(x);
 
 #ifdef NOT_USED
@@ -202,14 +243,12 @@ make_one_rel_by_joins(Query *root, int levels_needed)
        }
 
        /*
-        * Now, the front of the join_rel_list should be the single rel
+        * We should have a single rel at the final level,
         * representing the join of all the base rels.
         */
-       Assert(length(root->join_rel_list) > 0);
-       rel = (RelOptInfo *) lfirst(root->join_rel_list);
-       Assert(length(rel->relids) == levels_needed);
-       Assert(length(root->join_rel_list) == 1 ||
-                  length(((RelOptInfo *) lsecond(root->join_rel_list))->relids) < levels_needed);
+       Assert(length(joinitems[levels_needed]) == 1);
+       rel = (RelOptInfo *) lfirst(joinitems[levels_needed]);
+       Assert(length(rel->relids) == length(root->base_rel_list));
 
        return rel;
 }
index 05f32d25972d24dd10a4f72dba920807d0b9c704..3156a95131427b063d5f36cd3d79880b89e5d4e4 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.94 2000/08/24 03:29:04 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.95 2000/09/12 21:06:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1482,7 +1482,9 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
        {
                List       *clausegroup = lfirst(i);
                IndexPath  *pathnode = makeNode(IndexPath);
-               List       *indexquals;
+               List       *indexquals = NIL;
+               bool            alljoinquals = true;
+               List       *temp;
 
                /* XXX this code ought to be merged with create_index_path? */
 
@@ -1496,7 +1498,16 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
                 */
                pathnode->path.pathkeys = NIL;
 
-               indexquals = get_actual_clauses(clausegroup);
+               /* extract bare indexqual clauses, check whether all from JOIN/ON */
+               foreach(temp, clausegroup)
+               {
+                       RestrictInfo *clause = (RestrictInfo *) lfirst(temp);
+
+                       indexquals = lappend(indexquals, clause->clause);
+                       if (! clause->isjoinqual)
+                               alljoinquals = false;
+               }
+
                /* expand special operators to indexquals the executor can handle */
                indexquals = expand_indexqual_conditions(indexquals);
 
@@ -1514,6 +1525,8 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
                /* joinrelids saves the rels needed on the outer side of the join */
                pathnode->joinrelids = lfirst(outerrelids_list);
 
+               pathnode->alljoinquals = alljoinquals;
+
                /*
                 * We must compute the estimated number of output rows for the
                 * indexscan.  This is less than rel->rows because of the
index c2ca38490e393fecbcf286bd5e0b0faaa56ff42f..367e1ac9767c5f1f5c0961e1d60ae7ffb2d6b0c2 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.55 2000/05/30 00:49:47 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.56 2000/09/12 21:06:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "utils/lsyscache.h"
 
 static void sort_inner_and_outer(Query *root, RelOptInfo *joinrel,
-                                        RelOptInfo *outerrel, RelOptInfo *innerrel,
-                                        List *restrictlist, List *mergeclause_list);
+                                                                RelOptInfo *outerrel, RelOptInfo *innerrel,
+                                                                List *restrictlist, List *mergeclause_list,
+                                                                JoinType jointype);
 static void match_unsorted_outer(Query *root, RelOptInfo *joinrel,
-                                        RelOptInfo *outerrel, RelOptInfo *innerrel,
-                                        List *restrictlist, List *mergeclause_list);
+                                                                RelOptInfo *outerrel, RelOptInfo *innerrel,
+                                                                List *restrictlist, List *mergeclause_list,
+                                                                JoinType jointype);
 
 #ifdef NOT_USED
 static void match_unsorted_inner(Query *root, RelOptInfo *joinrel,
-                                        RelOptInfo *outerrel, RelOptInfo *innerrel,
-                                        List *restrictlist, List *mergeclause_list);
+                                                                RelOptInfo *outerrel, RelOptInfo *innerrel,
+                                                                List *restrictlist, List *mergeclause_list,
+                                                                JoinType jointype);
 
 #endif
 static void hash_inner_and_outer(Query *root, RelOptInfo *joinrel,
-                                        RelOptInfo *outerrel, RelOptInfo *innerrel,
-                                        List *restrictlist);
-static Path *best_innerjoin(List *join_paths, List *outer_relid);
+                                                                RelOptInfo *outerrel, RelOptInfo *innerrel,
+                                                                List *restrictlist, JoinType jointype);
+static Path *best_innerjoin(List *join_paths, List *outer_relid,
+                                                       JoinType jointype);
 static Selectivity estimate_disbursion(Query *root, Var *var);
 static List *select_mergejoin_clauses(RelOptInfo *joinrel,
-                                                RelOptInfo *outerrel,
-                                                RelOptInfo *innerrel,
-                                                List *restrictlist);
+                                                                         RelOptInfo *outerrel,
+                                                                         RelOptInfo *innerrel,
+                                                                         List *restrictlist,
+                                                                         JoinType jointype);
 
 
 /*
@@ -64,6 +69,7 @@ add_paths_to_joinrel(Query *root,
                                         RelOptInfo *joinrel,
                                         RelOptInfo *outerrel,
                                         RelOptInfo *innerrel,
+                                        JoinType jointype,
                                         List *restrictlist)
 {
        List       *mergeclause_list = NIL;
@@ -75,14 +81,15 @@ add_paths_to_joinrel(Query *root,
                mergeclause_list = select_mergejoin_clauses(joinrel,
                                                                                                        outerrel,
                                                                                                        innerrel,
-                                                                                                       restrictlist);
+                                                                                                       restrictlist,
+                                                                                                       jointype);
 
        /*
         * 1. Consider mergejoin paths where both relations must be explicitly
         * sorted.
         */
        sort_inner_and_outer(root, joinrel, outerrel, innerrel,
-                                                restrictlist, mergeclause_list);
+                                                restrictlist, mergeclause_list, jointype);
 
        /*
         * 2. Consider paths where the outer relation need not be explicitly
@@ -90,7 +97,7 @@ add_paths_to_joinrel(Query *root,
         * path is already ordered.
         */
        match_unsorted_outer(root, joinrel, outerrel, innerrel,
-                                                restrictlist, mergeclause_list);
+                                                restrictlist, mergeclause_list, jointype);
 
 #ifdef NOT_USED
 
@@ -107,7 +114,7 @@ add_paths_to_joinrel(Query *root,
         * other order.
         */
        match_unsorted_inner(root, joinrel, outerrel, innerrel,
-                                                restrictlist, mergeclause_list);
+                                                restrictlist, mergeclause_list, jointype);
 #endif
 
        /*
@@ -116,7 +123,7 @@ add_paths_to_joinrel(Query *root,
         */
        if (enable_hashjoin)
                hash_inner_and_outer(root, joinrel, outerrel, innerrel,
-                                                        restrictlist);
+                                                        restrictlist, jointype);
 }
 
 /*
@@ -131,6 +138,7 @@ add_paths_to_joinrel(Query *root,
  *             clauses that apply to this join
  * 'mergeclause_list' is a list of RestrictInfo nodes for available
  *             mergejoin clauses in this join
+ * 'jointype' is the type of join to do
  */
 static void
 sort_inner_and_outer(Query *root,
@@ -138,7 +146,8 @@ sort_inner_and_outer(Query *root,
                                         RelOptInfo *outerrel,
                                         RelOptInfo *innerrel,
                                         List *restrictlist,
-                                        List *mergeclause_list)
+                                        List *mergeclause_list,
+                                        JoinType jointype)
 {
        List       *i;
 
@@ -187,10 +196,10 @@ sort_inner_and_outer(Query *root,
                 */
                outerkeys = make_pathkeys_for_mergeclauses(root,
                                                                                                   curclause_list,
-                                                                                                  outerrel->targetlist);
+                                                                                                  outerrel);
                innerkeys = make_pathkeys_for_mergeclauses(root,
                                                                                                   curclause_list,
-                                                                                                  innerrel->targetlist);
+                                                                                                  innerrel);
                /* Build pathkeys representing output sort order. */
                merge_pathkeys = build_join_pathkeys(outerkeys,
                                                                                         joinrel->targetlist,
@@ -204,6 +213,7 @@ sort_inner_and_outer(Query *root,
                 */
                add_path(joinrel, (Path *)
                                 create_mergejoin_path(joinrel,
+                                                                          jointype,
                                                                           outerrel->cheapest_total_path,
                                                                           innerrel->cheapest_total_path,
                                                                           restrictlist,
@@ -243,6 +253,7 @@ sort_inner_and_outer(Query *root,
  *             clauses that apply to this join
  * 'mergeclause_list' is a list of RestrictInfo nodes for available
  *             mergejoin clauses in this join
+ * 'jointype' is the type of join to do
  */
 static void
 match_unsorted_outer(Query *root,
@@ -250,16 +261,33 @@ match_unsorted_outer(Query *root,
                                         RelOptInfo *outerrel,
                                         RelOptInfo *innerrel,
                                         List *restrictlist,
-                                        List *mergeclause_list)
+                                        List *mergeclause_list,
+                                        JoinType jointype)
 {
+       bool            nestjoinOK;
        Path       *bestinnerjoin;
        List       *i;
 
+       /*
+        * Nestloop only supports inner and left joins.
+        */
+       switch (jointype)
+       {
+               case JOIN_INNER:
+               case JOIN_LEFT:
+                       nestjoinOK = true;
+                       break;
+               default:
+                       nestjoinOK = false;
+                       break;
+       }
+
        /*
         * Get the best innerjoin indexpath (if any) for this outer rel. It's
         * the same for all outer paths.
         */
-       bestinnerjoin = best_innerjoin(innerrel->innerjoin, outerrel->relids);
+       bestinnerjoin = best_innerjoin(innerrel->innerjoin, outerrel->relids,
+                                                                  jointype);
 
        foreach(i, outerrel->pathlist)
        {
@@ -282,31 +310,38 @@ match_unsorted_outer(Query *root,
                                                                                         joinrel->targetlist,
                                                                                         root->equi_key_list);
 
-               /*
-                * Always consider a nestloop join with this outer and cheapest-
-                * total-cost inner.  Consider nestloops using the cheapest-
-                * startup-cost inner as well, and the best innerjoin indexpath.
-                */
-               add_path(joinrel, (Path *)
-                                create_nestloop_path(joinrel,
-                                                                         outerpath,
-                                                                         innerrel->cheapest_total_path,
-                                                                         restrictlist,
-                                                                         merge_pathkeys));
-               if (innerrel->cheapest_startup_path != innerrel->cheapest_total_path)
-                       add_path(joinrel, (Path *)
-                                        create_nestloop_path(joinrel,
-                                                                                 outerpath,
-                                                                                 innerrel->cheapest_startup_path,
-                                                                                 restrictlist,
-                                                                                 merge_pathkeys));
-               if (bestinnerjoin != NULL)
+               if (nestjoinOK)
+               {
+                       /*
+                        * Always consider a nestloop join with this outer and cheapest-
+                        * total-cost inner.  Consider nestloops using the cheapest-
+                        * startup-cost inner as well, and the best innerjoin indexpath.
+                        */
                        add_path(joinrel, (Path *)
                                         create_nestloop_path(joinrel,
+                                                                                 jointype,
                                                                                  outerpath,
-                                                                                 bestinnerjoin,
+                                                                                 innerrel->cheapest_total_path,
                                                                                  restrictlist,
                                                                                  merge_pathkeys));
+                       if (innerrel->cheapest_startup_path !=
+                               innerrel->cheapest_total_path)
+                               add_path(joinrel, (Path *)
+                                                create_nestloop_path(joinrel,
+                                                                                         jointype,
+                                                                                         outerpath,
+                                                                                         innerrel->cheapest_startup_path,
+                                                                                         restrictlist,
+                                                                                         merge_pathkeys));
+                       if (bestinnerjoin != NULL)
+                               add_path(joinrel, (Path *)
+                                                create_nestloop_path(joinrel,
+                                                                                         jointype,
+                                                                                         outerpath,
+                                                                                         bestinnerjoin,
+                                                                                         restrictlist,
+                                                                                         merge_pathkeys));
+               }
 
                /* Look for useful mergeclauses (if any) */
                mergeclauses = find_mergeclauses_for_pathkeys(outerpath->pathkeys,
@@ -319,7 +354,7 @@ match_unsorted_outer(Query *root,
                /* Compute the required ordering of the inner path */
                innersortkeys = make_pathkeys_for_mergeclauses(root,
                                                                                                           mergeclauses,
-                                                                                                  innerrel->targetlist);
+                                                                                                          innerrel);
 
                /*
                 * Generate a mergejoin on the basis of sorting the cheapest
@@ -328,6 +363,7 @@ match_unsorted_outer(Query *root,
                 */
                add_path(joinrel, (Path *)
                                 create_mergejoin_path(joinrel,
+                                                                          jointype,
                                                                           outerpath,
                                                                           innerrel->cheapest_total_path,
                                                                           restrictlist,
@@ -373,6 +409,7 @@ match_unsorted_outer(Query *root,
                                        newclauses = mergeclauses;
                                add_path(joinrel, (Path *)
                                                 create_mergejoin_path(joinrel,
+                                                                                          jointype,
                                                                                           outerpath,
                                                                                           innerpath,
                                                                                           restrictlist,
@@ -409,6 +446,7 @@ match_unsorted_outer(Query *root,
                                        }
                                        add_path(joinrel, (Path *)
                                                         create_mergejoin_path(joinrel,
+                                                                                                  jointype,
                                                                                                   outerpath,
                                                                                                   innerpath,
                                                                                                   restrictlist,
@@ -437,6 +475,7 @@ match_unsorted_outer(Query *root,
  *             clauses that apply to this join
  * 'mergeclause_list' is a list of RestrictInfo nodes for available
  *             mergejoin clauses in this join
+ * 'jointype' is the type of join to do
  */
 static void
 match_unsorted_inner(Query *root,
@@ -444,7 +483,8 @@ match_unsorted_inner(Query *root,
                                         RelOptInfo *outerrel,
                                         RelOptInfo *innerrel,
                                         List *restrictlist,
-                                        List *mergeclause_list)
+                                        List *mergeclause_list,
+                                        JoinType jointype)
 {
        List       *i;
 
@@ -466,7 +506,7 @@ match_unsorted_inner(Query *root,
                /* Compute the required ordering of the outer path */
                outersortkeys = make_pathkeys_for_mergeclauses(root,
                                                                                                           mergeclauses,
-                                                                                                  outerrel->targetlist);
+                                                                                                          outerrel);
 
                /*
                 * Generate a mergejoin on the basis of sorting the cheapest
@@ -478,6 +518,7 @@ match_unsorted_inner(Query *root,
                                                                                         root->equi_key_list);
                add_path(joinrel, (Path *)
                                 create_mergejoin_path(joinrel,
+                                                                          jointype,
                                                                           outerrel->cheapest_total_path,
                                                                           innerpath,
                                                                           restrictlist,
@@ -506,6 +547,7 @@ match_unsorted_inner(Query *root,
                                                                                         root->equi_key_list);
                add_path(joinrel, (Path *)
                                 create_mergejoin_path(joinrel,
+                                                                          jointype,
                                                                           totalouterpath,
                                                                           innerpath,
                                                                           restrictlist,
@@ -524,6 +566,7 @@ match_unsorted_inner(Query *root,
                                                                                                 root->equi_key_list);
                        add_path(joinrel, (Path *)
                                         create_mergejoin_path(joinrel,
+                                                                                  jointype,
                                                                                   startupouterpath,
                                                                                   innerpath,
                                                                                   restrictlist,
@@ -547,18 +590,36 @@ match_unsorted_inner(Query *root,
  * 'innerrel' is the inner join relation
  * 'restrictlist' contains all of the RestrictInfo nodes for restriction
  *             clauses that apply to this join
+ * 'jointype' is the type of join to do
  */
 static void
 hash_inner_and_outer(Query *root,
                                         RelOptInfo *joinrel,
                                         RelOptInfo *outerrel,
                                         RelOptInfo *innerrel,
-                                        List *restrictlist)
+                                        List *restrictlist,
+                                        JoinType jointype)
 {
        Relids          outerrelids = outerrel->relids;
        Relids          innerrelids = innerrel->relids;
+       bool            isouterjoin;
        List       *i;
 
+       /*
+        * Hashjoin only supports inner and left joins.
+        */
+       switch (jointype)
+       {
+               case JOIN_INNER:
+                       isouterjoin = false;
+                       break;
+               case JOIN_LEFT:
+                       isouterjoin = true;
+                       break;
+               default:
+                       return;
+       }
+
        /*
         * Scan the join's restrictinfo list to find hashjoinable clauses that
         * are usable with this pair of sub-relations.  Since we currently
@@ -581,6 +642,13 @@ hash_inner_and_outer(Query *root,
                if (restrictinfo->hashjoinoperator == InvalidOid)
                        continue;                       /* not hashjoinable */
 
+               /*
+                * If processing an outer join, only use explicit join clauses for
+                * hashing.  For inner joins we need not be so picky.
+                */
+               if (isouterjoin && !restrictinfo->isjoinqual)
+                       continue;
+
                clause = restrictinfo->clause;
                /* these must be OK, since check_hashjoinable accepted the clause */
                left = get_leftop(clause);
@@ -609,6 +677,7 @@ hash_inner_and_outer(Query *root,
                 */
                add_path(joinrel, (Path *)
                                 create_hashjoin_path(joinrel,
+                                                                         jointype,
                                                                          outerrel->cheapest_total_path,
                                                                          innerrel->cheapest_total_path,
                                                                          restrictlist,
@@ -617,6 +686,7 @@ hash_inner_and_outer(Query *root,
                if (outerrel->cheapest_startup_path != outerrel->cheapest_total_path)
                        add_path(joinrel, (Path *)
                                         create_hashjoin_path(joinrel,
+                                                                                 jointype,
                                                                                  outerrel->cheapest_startup_path,
                                                                                  innerrel->cheapest_total_path,
                                                                                  restrictlist,
@@ -641,26 +711,49 @@ hash_inner_and_outer(Query *root,
  * usable path.
  */
 static Path *
-best_innerjoin(List *join_paths, Relids outer_relids)
+best_innerjoin(List *join_paths, Relids outer_relids, JoinType jointype)
 {
        Path       *cheapest = (Path *) NULL;
+       bool            isouterjoin;
        List       *join_path;
 
+       /*
+        * Nestloop only supports inner and left joins.
+        */
+       switch (jointype)
+       {
+               case JOIN_INNER:
+                       isouterjoin = false;
+                       break;
+               case JOIN_LEFT:
+                       isouterjoin = true;
+                       break;
+               default:
+                       return NULL;
+       }
+
        foreach(join_path, join_paths)
        {
-               Path       *path = (Path *) lfirst(join_path);
+               IndexPath   *path = (IndexPath *) lfirst(join_path);
 
                Assert(IsA(path, IndexPath));
 
+               /*
+                * If processing an outer join, only use explicit join clauses in the
+                * inner indexscan.  For inner joins we need not be so picky.
+                */
+               if (isouterjoin && !path->alljoinquals)
+                       continue;
+
                /*
                 * path->joinrelids is the set of base rels that must be part of
                 * outer_relids in order to use this inner path, because those
                 * rels are used in the index join quals of this inner path.
                 */
-               if (is_subseti(((IndexPath *) path)->joinrelids, outer_relids) &&
+               if (is_subseti(path->joinrelids, outer_relids) &&
                        (cheapest == NULL ||
-                        compare_path_costs(path, cheapest, TOTAL_COST) < 0))
-                       cheapest = path;
+                        compare_path_costs((Path *) path, cheapest, TOTAL_COST) < 0))
+                       cheapest = (Path *) path;
        }
        return cheapest;
 }
@@ -684,6 +777,9 @@ estimate_disbursion(Query *root, Var *var)
 
        relid = getrelid(var->varno, root->rtable);
 
+       if (relid == InvalidOid)
+               return 0.1;
+
        return (Selectivity) get_attdisbursion(relid, var->varattno, 0.1);
 }
 
@@ -707,11 +803,13 @@ static List *
 select_mergejoin_clauses(RelOptInfo *joinrel,
                                                 RelOptInfo *outerrel,
                                                 RelOptInfo *innerrel,
-                                                List *restrictlist)
+                                                List *restrictlist,
+                                                JoinType jointype)
 {
        List       *result_list = NIL;
        Relids          outerrelids = outerrel->relids;
        Relids          innerrelids = innerrel->relids;
+       bool            isouterjoin = IS_OUTER_JOIN(jointype);
        List       *i;
 
        foreach(i, restrictlist)
@@ -721,6 +819,37 @@ select_mergejoin_clauses(RelOptInfo *joinrel,
                Var                *left,
                                   *right;
 
+               /*
+                * If processing an outer join, only use explicit join clauses in the
+                * merge.  For inner joins we need not be so picky.
+                *
+                * Furthermore, if it is a right/full join then *all* the explicit
+                * join clauses must be mergejoinable, else the executor will fail.
+                * If we are asked for a right join then just return NIL to indicate
+                * no mergejoin is possible (we can handle it as a left join instead).
+                * If we are asked for a full join then emit an error, because there
+                * is no fallback.
+                */
+               if (isouterjoin)
+               {
+                       if (!restrictinfo->isjoinqual)
+                               continue;
+                       switch (jointype)
+                       {
+                               case JOIN_RIGHT:
+                                       if (restrictinfo->mergejoinoperator == InvalidOid)
+                                               return NIL;     /* not mergejoinable */
+                                       break;
+                               case JOIN_FULL:
+                                       if (restrictinfo->mergejoinoperator == InvalidOid)
+                                               elog(ERROR, "FULL JOIN is only supported with mergejoinable join conditions");
+                                       break;
+                               default:
+                                       /* otherwise, it's OK to have nonmergeable join quals */
+                                       break;
+                       }
+               }
+
                if (restrictinfo->mergejoinoperator == InvalidOid)
                        continue;                       /* not mergejoinable */
 
index 741efe928c20d1a38e793b6650fff7bf1fd14b46..3cab2daba5c6e25e15bc16460171e0b21687814a 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.46 2000/05/30 00:49:47 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.47 2000/09/12 21:06:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 
 static RelOptInfo *make_join_rel(Query *root, RelOptInfo *rel1,
-                         RelOptInfo *rel2);
+                                                                RelOptInfo *rel2, JoinType jointype);
 
 
 /*
  * make_rels_by_joins
  *       Consider ways to produce join relations containing exactly 'level'
- *       base relations.  (This is one step of the dynamic-programming method
+ *       jointree items.  (This is one step of the dynamic-programming method
  *       embodied in make_one_rel_by_joins.)  Join rel nodes for each feasible
- *       combination of base rels are created and added to the front of the
- *       query's join_rel_list.  Implementation paths are created for each
- *       such joinrel, too.
+ *       combination of lower-level rels are created and returned in a list.
+ *       Implementation paths are created for each such joinrel, too.
  *
- * Returns nothing, but adds entries to root->join_rel_list.
+ * level: level of rels we want to make this time.
+ * joinrels[j], 1 <= j < level, is a list of rels containing j items.
  */
-void
-make_rels_by_joins(Query *root, int level)
+List *
+make_rels_by_joins(Query *root, int level, List **joinrels)
 {
-       List       *first_old_rel = root->join_rel_list;
+       List       *result_rels = NIL;
+       List       *new_rels;
+       List       *nr;
        List       *r;
+       int                     k;
 
        /*
         * First, consider left-sided and right-sided plans, in which rels of
-        * exactly level-1 member relations are joined against base relations.
-        * We prefer to join using join clauses, but if we find a rel of
-        * level-1 members that has no join clauses, we will generate
-        * Cartesian-product joins against all base rels not already contained
-        * in it.
+        * exactly level-1 member relations are joined against initial relations.
+        * We prefer to join using join clauses, but if we find a rel of level-1
+        * members that has no join clauses, we will generate Cartesian-product
+        * joins against all initial rels not already contained in it.
         *
-        * In the first pass (level == 2), we try to join each base rel to each
-        * base rel that appears later in base_rel_list.  (The mirror-image
+        * In the first pass (level == 2), we try to join each initial rel to each
+        * initial rel that appears later in joinrels[1].  (The mirror-image
         * joins are handled automatically by make_join_rel.)  In later
-        * passes, we try to join rels of size level-1 from join_rel_list to
-        * each base rel in base_rel_list.
-        *
-        * We assume that the rels already present in join_rel_list appear in
-        * decreasing order of level (number of members).  This should be true
-        * since we always add new higher-level rels to the front of the list.
+        * passes, we try to join rels of size level-1 from joinrels[level-1]
+        * to each initial rel in joinrels[1].
         */
-       if (level == 2)
-               r = root->base_rel_list;/* level-1 is base rels */
-       else
-               r = root->join_rel_list;
-       for (; r != NIL; r = lnext(r))
+       foreach(r, joinrels[level-1])
        {
                RelOptInfo *old_rel = (RelOptInfo *) lfirst(r);
-               int                     old_level = length(old_rel->relids);
                List       *other_rels;
 
-               if (old_level != level - 1)
-                       break;
-
                if (level == 2)
-                       other_rels = lnext(r);          /* only consider remaining base
+                       other_rels = lnext(r);          /* only consider remaining initial
                                                                                 * rels */
                else
-                       other_rels = root->base_rel_list;       /* consider all base rels */
+                       other_rels = joinrels[1];       /* consider all initial rels */
 
                if (old_rel->joininfo != NIL)
                {
@@ -87,9 +77,9 @@ make_rels_by_joins(Query *root, int level)
                         * have those other rels collected into a join rel.  See also
                         * the last-ditch case below.
                         */
-                       make_rels_by_clause_joins(root,
-                                                                         old_rel,
-                                                                         other_rels);
+                       new_rels = make_rels_by_clause_joins(root,
+                                                                                                old_rel,
+                                                                                                other_rels);
                }
                else
                {
@@ -98,64 +88,90 @@ make_rels_by_joins(Query *root, int level)
                         * Oops, we have a relation that is not joined to any other
                         * relation.  Cartesian product time.
                         */
-                       make_rels_by_clauseless_joins(root,
-                                                                                 old_rel,
-                                                                                 other_rels);
+                       new_rels = make_rels_by_clauseless_joins(root,
+                                                                                                        old_rel,
+                                                                                                        other_rels);
+               }
+
+               /*
+                * At levels above 2 we will generate the same joined relation
+                * in multiple ways --- for example (a join b) join c is the same
+                * RelOptInfo as (b join c) join a, though the second case will
+                * add a different set of Paths to it.  To avoid making extra work
+                * for subsequent passes, do not enter the same RelOptInfo into our
+                * output list multiple times.
+                */
+               foreach(nr, new_rels)
+               {
+                       RelOptInfo         *jrel = (RelOptInfo *) lfirst(nr);
+
+                       if (!ptrMember(jrel, result_rels))
+                               result_rels = lcons(jrel, result_rels);
                }
        }
 
        /*
-        * Now, consider "bushy plans" in which relations of k base rels are
-        * joined to relations of level-k base rels, for 2 <= k <= level-2.
-        * The previous loop left r pointing to the first rel of level
-        * level-2.
+        * Now, consider "bushy plans" in which relations of k initial rels are
+        * joined to relations of level-k initial rels, for 2 <= k <= level-2.
         *
         * We only consider bushy-plan joins for pairs of rels where there is a
         * suitable join clause, in order to avoid unreasonable growth of
         * planning time.
         */
-       for (; r != NIL; r = lnext(r))
+       for (k = 2; ; k++)
        {
-               RelOptInfo *old_rel = (RelOptInfo *) lfirst(r);
-               int                     old_level = length(old_rel->relids);
-               List       *r2;
+               int                     other_level = level - k;
 
                /*
-                * We can quit once past the halfway point (make_join_rel took
-                * care of making the opposite-direction joins)
+                * Since make_join_rel(x, y) handles both x,y and y,x cases,
+                * we only need to go as far as the halfway point.
                 */
-               if (old_level * 2 < level)
+               if (k > other_level)
                        break;
 
-               if (old_rel->joininfo == NIL)
-                       continue;                       /* we ignore clauseless joins here */
-
-               foreach(r2, lnext(r))
+               foreach(r, joinrels[k])
                {
-                       RelOptInfo *new_rel = (RelOptInfo *) lfirst(r2);
-                       int                     new_level = length(new_rel->relids);
-
-                       if (old_level + new_level > level)
-                               continue;               /* scan down to new_rels of right size */
-                       if (old_level + new_level < level)
-                               break;                  /* no more new_rels of right size */
-                       if (nonoverlap_setsi(old_rel->relids, new_rel->relids))
+                       RelOptInfo *old_rel = (RelOptInfo *) lfirst(r);
+                       List       *other_rels;
+                       List       *r2;
+
+                       if (old_rel->joininfo == NIL)
+                               continue;               /* we ignore clauseless joins here */
+
+                       if (k == other_level)
+                               other_rels = lnext(r); /* only consider remaining rels */
+                       else
+                               other_rels = joinrels[other_level];
+
+                       foreach(r2, other_rels)
                        {
-                               List       *i;
-
-                               /*
-                                * OK, we can build a rel of the right level from this
-                                * pair of rels.  Do so if there is at least one usable
-                                * join clause.
-                                */
-                               foreach(i, old_rel->joininfo)
-                               {
-                                       JoinInfo   *joininfo = (JoinInfo *) lfirst(i);
+                               RelOptInfo *new_rel = (RelOptInfo *) lfirst(r2);
 
-                                       if (is_subseti(joininfo->unjoined_relids, new_rel->relids))
+                               if (nonoverlap_setsi(old_rel->relids, new_rel->relids))
+                               {
+                                       List       *i;
+
+                                       /*
+                                        * OK, we can build a rel of the right level from this
+                                        * pair of rels.  Do so if there is at least one usable
+                                        * join clause.
+                                        */
+                                       foreach(i, old_rel->joininfo)
                                        {
-                                               make_join_rel(root, old_rel, new_rel);
-                                               break;
+                                               JoinInfo   *joininfo = (JoinInfo *) lfirst(i);
+
+                                               if (is_subseti(joininfo->unjoined_relids,
+                                                                          new_rel->relids))
+                                               {
+                                                       RelOptInfo *jrel;
+
+                                                       jrel = make_join_rel(root, old_rel, new_rel,
+                                                                                                JOIN_INNER);
+                                                       /* Avoid making duplicate entries ... */
+                                                       if (!ptrMember(jrel, result_rels))
+                                                               result_rels = lcons(jrel, result_rels);
+                                                       break; /* need not consider more joininfos */
+                                               }
                                        }
                                }
                        }
@@ -174,39 +190,41 @@ make_rels_by_joins(Query *root, int level)
         * no choice but to make cartesian joins.  We consider only left-sided
         * and right-sided cartesian joins in this case (no bushy).
         */
-       if (root->join_rel_list == first_old_rel)
+       if (result_rels == NIL)
        {
                /* This loop is just like the first one, except we always call
                 * make_rels_by_clauseless_joins().
                 */
-               if (level == 2)
-                       r = root->base_rel_list; /* level-1 is base rels */
-               else
-                       r = root->join_rel_list;
-               for (; r != NIL; r = lnext(r))
+               foreach(r, joinrels[level-1])
                {
                        RelOptInfo *old_rel = (RelOptInfo *) lfirst(r);
-                       int                     old_level = length(old_rel->relids);
                        List       *other_rels;
 
-                       if (old_level != level - 1)
-                               break;
-
                        if (level == 2)
-                               other_rels = lnext(r); /* only consider remaining base
+                               other_rels = lnext(r); /* only consider remaining initial
                                                                                * rels */
                        else
-                               other_rels = root->base_rel_list; /* consider all base rels */
+                               other_rels = joinrels[1]; /* consider all initial rels */
+
+                       new_rels = make_rels_by_clauseless_joins(root,
+                                                                                                        old_rel,
+                                                                                                        other_rels);
+
+                       foreach(nr, new_rels)
+                       {
+                               RelOptInfo         *jrel = (RelOptInfo *) lfirst(nr);
 
-                       make_rels_by_clauseless_joins(root,
-                                                                                 old_rel,
-                                                                                 other_rels);
+                               if (!ptrMember(jrel, result_rels))
+                                       result_rels = lcons(jrel, result_rels);
+                       }
                }
 
-               if (root->join_rel_list == first_old_rel)
+               if (result_rels == NIL)
                        elog(ERROR, "make_rels_by_joins: failed to build any %d-way joins",
                                 level);
        }
+
+       return result_rels;
 }
 
 /*
@@ -214,28 +232,23 @@ make_rels_by_joins(Query *root, int level)
  *       Build joins between the given relation 'old_rel' and other relations
  *       that are mentioned within old_rel's joininfo nodes (i.e., relations
  *       that participate in join clauses that 'old_rel' also participates in).
- *       The join rel nodes are added to root->join_rel_list.
+ *       The join rel nodes are returned in a list.
  *
  * 'old_rel' is the relation entry for the relation to be joined
  * 'other_rels': other rels to be considered for joining
  *
- * Currently, this is only used with base rels in other_rels, but it would
- * work for joining to joinrels too, if the caller ensures there is no
+ * Currently, this is only used with initial rels in other_rels, but it
+ * will work for joining to joinrels too, if the caller ensures there is no
  * membership overlap between old_rel and the rels in other_rels.  (We need
- * no extra test for overlap for base rels, since the is_subset test can
+ * no extra test for overlap for initial rels, since the is_subset test can
  * only succeed when other_rel is not already part of old_rel.)
- *
- * Returns NULL if no suitable joins were found, else the last suitable
- * joinrel processed.  (The only caller who checks the return value is
- * geqo_eval.c, and it sets things up so there can be no more than one
- * "suitable" joinrel; so we don't bother with returning a list.)
  */
-RelOptInfo *
+List *
 make_rels_by_clause_joins(Query *root,
                                                  RelOptInfo *old_rel,
                                                  List *other_rels)
 {
-       RelOptInfo *result = NULL;
+       List       *result = NIL;
        List       *i,
                           *j;
 
@@ -249,7 +262,9 @@ make_rels_by_clause_joins(Query *root,
                        RelOptInfo *other_rel = (RelOptInfo *) lfirst(j);
 
                        if (is_subseti(unjoined_relids, other_rel->relids))
-                               result = make_join_rel(root, old_rel, other_rel);
+                               result = lcons(make_join_rel(root, old_rel, other_rel,
+                                                                                        JOIN_INNER),
+                                                          result);
                }
        }
 
@@ -261,24 +276,20 @@ make_rels_by_clause_joins(Query *root,
  *       Given a relation 'old_rel' and a list of other relations
  *       'other_rels', create a join relation between 'old_rel' and each
  *       member of 'other_rels' that isn't already included in 'old_rel'.
+ *       The join rel nodes are returned in a list.
  *
  * 'old_rel' is the relation entry for the relation to be joined
  * 'other_rels': other rels to be considered for joining
  *
- * Currently, this is only used with base rels in other_rels, but it would
+ * Currently, this is only used with initial rels in other_rels, but it would
  * work for joining to joinrels too.
- *
- * Returns NULL if no suitable joins were found, else the last suitable
- * joinrel processed.  (The only caller who checks the return value is
- * geqo_eval.c, and it sets things up so there can be no more than one
- * "suitable" joinrel; so we don't bother with returning a list.)
  */
-RelOptInfo *
+List *
 make_rels_by_clauseless_joins(Query *root,
                                                          RelOptInfo *old_rel,
                                                          List *other_rels)
 {
-       RelOptInfo *result = NULL;
+       List       *result = NIL;
        List       *i;
 
        foreach(i, other_rels)
@@ -286,13 +297,61 @@ make_rels_by_clauseless_joins(Query *root,
                RelOptInfo *other_rel = (RelOptInfo *) lfirst(i);
 
                if (nonoverlap_setsi(other_rel->relids, old_rel->relids))
-                       result = make_join_rel(root, old_rel, other_rel);
+                       result = lcons(make_join_rel(root, old_rel, other_rel,
+                                                                                JOIN_INNER),
+                                                  result);
        }
 
        return result;
 }
 
 
+/*
+ * make_rel_from_jointree
+ *             Find or build a RelOptInfojoin rel representing a specific
+ *             jointree item.  For JoinExprs, we only consider the construction
+ *             path that corresponds exactly to what the user wrote.
+ */
+RelOptInfo *
+make_rel_from_jointree(Query *root, Node *jtnode)
+{
+       if (IsA(jtnode, RangeTblRef))
+       {
+               int                     varno = ((RangeTblRef *) jtnode)->rtindex;
+
+               return get_base_rel(root, varno);
+       }
+       else if (IsA(jtnode, JoinExpr))
+       {
+               JoinExpr   *j = (JoinExpr *) jtnode;
+               RelOptInfo *rel,
+                                  *lrel,
+                                  *rrel;
+
+               /* Recurse */
+               lrel = make_rel_from_jointree(root, j->larg);
+               rrel = make_rel_from_jointree(root, j->rarg);
+
+               /* Make this join rel */
+               rel = make_join_rel(root, lrel, rrel, j->jointype);
+
+               /*
+                * Since we are only going to consider this one way to do it,
+                * we're done generating Paths for this joinrel and can now select
+                * the cheapest.  In fact we *must* do so now, since next level up
+                * will need it!
+                */
+               set_cheapest(rel);
+
+               return rel;
+       }
+       else
+               elog(ERROR, "make_rel_from_jointree: unexpected node type %d",
+                        nodeTag(jtnode));
+       return NULL;                            /* keep compiler quiet */
+}
+
+
 /*
  * make_join_rel
  *        Find or create a join RelOptInfo that represents the join of
@@ -300,10 +359,10 @@ make_rels_by_clauseless_joins(Query *root,
  *        created with the two rels as outer and inner rel.
  *        (The join rel may already contain paths generated from other
  *        pairs of rels that add up to the same set of base rels.)
- *        The join rel is stored in the query's join_rel_list.
  */
 static RelOptInfo *
-make_join_rel(Query *root, RelOptInfo *rel1, RelOptInfo *rel2)
+make_join_rel(Query *root, RelOptInfo *rel1, RelOptInfo *rel2,
+                         JoinType jointype)
 {
        RelOptInfo *joinrel;
        List       *restrictlist;
@@ -315,10 +374,39 @@ make_join_rel(Query *root, RelOptInfo *rel1, RelOptInfo *rel2)
        joinrel = get_join_rel(root, rel1, rel2, &restrictlist);
 
        /*
-        * We consider paths using each rel as both outer and inner.
+        * Consider paths using each rel as both outer and inner.
         */
-       add_paths_to_joinrel(root, joinrel, rel1, rel2, restrictlist);
-       add_paths_to_joinrel(root, joinrel, rel2, rel1, restrictlist);
+       switch (jointype)
+       {
+               case JOIN_INNER:
+                       add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_INNER,
+                                                                restrictlist);
+                       add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_INNER,
+                                                                restrictlist);
+                       break;
+               case JOIN_LEFT:
+                       add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_LEFT,
+                                                                restrictlist);
+                       add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_RIGHT,
+                                                                restrictlist);
+                       break;
+               case JOIN_FULL:
+                       add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_FULL,
+                                                                restrictlist);
+                       add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_FULL,
+                                                                restrictlist);
+                       break;
+               case JOIN_RIGHT:
+                       add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_RIGHT,
+                                                                restrictlist);
+                       add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_LEFT,
+                                                                restrictlist);
+                       break;
+               default:
+                       elog(ERROR, "make_join_rel: unsupported join type %d",
+                                (int) jointype);
+                       break;
+       }
 
        return joinrel;
 }
index 62a02836fec33778b76a0393aa581b644fd8e476..2a76f63eb7c3aa18037a62b4f1093d0f371a7410 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.40 2000/05/30 00:49:47 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.41 2000/09/12 21:06:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -101,6 +101,7 @@ create_or_index_paths(Query *root,
 
                                /* This isn't a nestloop innerjoin, so: */
                                pathnode->joinrelids = NIL;             /* no join clauses here */
+                               pathnode->alljoinquals = false;
                                pathnode->rows = rel->rows;
 
                                best_or_subclause_indices(root,
index 6d7b67bee3df6a359f54bdc05a29ba4965ff527b..c6eccddab195fdc1059d5806eff3363495d8122c 100644 (file)
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.24 2000/08/08 15:41:31 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.25 2000/09/12 21:06:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -694,8 +694,8 @@ find_mergeclauses_for_pathkeys(List *pathkeys, List *restrictinfos)
  *
  * 'mergeclauses' is a list of RestrictInfos for mergejoin clauses
  *                     that will be used in a merge join.
- * 'tlist' is a relation target list for either the inner or outer
- *                     side of the proposed join rel.  (Not actually needed anymore)
+ * 'rel' is the relation the pathkeys will apply to (ie, either the inner
+ *                     or outer side of the proposed join rel).
  *
  * Returns a pathkeys list that can be applied to the indicated relation.
  *
@@ -706,7 +706,7 @@ find_mergeclauses_for_pathkeys(List *pathkeys, List *restrictinfos)
 List *
 make_pathkeys_for_mergeclauses(Query *root,
                                                           List *mergeclauses,
-                                                          List *tlist)
+                                                          RelOptInfo *rel)
 {
        List       *pathkeys = NIL;
        List       *i;
@@ -722,30 +722,37 @@ make_pathkeys_for_mergeclauses(Query *root,
                Assert(restrictinfo->mergejoinoperator != InvalidOid);
 
                /*
-                * Find the key and sortop needed for this mergeclause.
-                *
-                * Both sides of the mergeclause should appear in one of the query's
-                * pathkey equivalence classes, so it doesn't matter which one we
-                * use here.
+                * Which key and sortop is needed for this relation?
                 */
                key = (Node *) get_leftop(restrictinfo->clause);
                sortop = restrictinfo->left_sortop;
+               if (!IsA(key, Var) ||
+                       !intMember(((Var *) key)->varno, rel->relids))
+               {
+                       key = (Node *) get_rightop(restrictinfo->clause);
+                       sortop = restrictinfo->right_sortop;
+                       if (!IsA(key, Var) ||
+                               !intMember(((Var *) key)->varno, rel->relids))
+                               elog(ERROR, "make_pathkeys_for_mergeclauses: can't identify which side of mergeclause to use");
+               }
 
                /*
-                * Find pathkey sublist for this sort item.  We expect to find the
-                * canonical set including the mergeclause's left and right sides;
-                * if we get back just the one item, something is rotten.
+                * Find or create canonical pathkey sublist for this sort item.
                 */
                item = makePathKeyItem(key, sortop);
                pathkey = make_canonical_pathkey(root, item);
-               Assert(length(pathkey) > 1);
 
                /*
-                * Since the item we just made is not in the returned canonical
-                * set, we can free it --- this saves a useful amount of storage
-                * in a big join tree.
+                * Most of the time we will get back a canonical pathkey set
+                * including both the mergeclause's left and right sides (the only
+                * case where we don't is if the mergeclause appeared in an OUTER
+                * JOIN, which causes us not to generate an equijoin set from it).
+                * Therefore, most of the time the item we just made is not part
+                * of the returned structure, and we can free it.  This check
+                * saves a useful amount of storage in a big join tree.
                 */
-               pfree(item);
+               if (item != (PathKeyItem *) lfirst(pathkey))
+                       pfree(item);
 
                pathkeys = lappend(pathkeys, pathkey);
        }
index c049f5d86b6a6af4295632b73403c4ac5bdb70f3..96dc3327b7ff052cf15cdb3b5cc5267ce3c1f82a 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.95 2000/08/13 02:50:06 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.96 2000/09/12 21:06:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -42,36 +42,47 @@ static IndexScan *create_indexscan_node(Query *root, IndexPath *best_path,
 static TidScan *create_tidscan_node(TidPath *best_path, List *tlist,
                                        List *scan_clauses);
 static NestLoop *create_nestloop_node(NestPath *best_path, List *tlist,
-                                        List *clauses, Plan *outer_node, List *outer_tlist,
-                                        Plan *inner_node, List *inner_tlist);
+                                                                         List *joinclauses, List *otherclauses,
+                                                                         Plan *outer_node, List *outer_tlist,
+                                                                         Plan *inner_node, List *inner_tlist);
 static MergeJoin *create_mergejoin_node(MergePath *best_path, List *tlist,
-                                         List *clauses, Plan *outer_node, List *outer_tlist,
-                                         Plan *inner_node, List *inner_tlist);
+                                                                               List *joinclauses, List *otherclauses,
+                                                                               Plan *outer_node, List *outer_tlist,
+                                                                               Plan *inner_node, List *inner_tlist);
 static HashJoin *create_hashjoin_node(HashPath *best_path, List *tlist,
-                                        List *clauses, Plan *outer_node, List *outer_tlist,
-                                        Plan *inner_node, List *inner_tlist);
+                                                                         List *joinclauses, List *otherclauses,
+                                                                         Plan *outer_node, List *outer_tlist,
+                                                                         Plan *inner_node, List *inner_tlist);
 static List *fix_indxqual_references(List *indexquals, IndexPath *index_path);
 static List *fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam,
                                         Form_pg_index index);
 static Node *fix_indxqual_operand(Node *node, int baserelid,
                                         Form_pg_index index,
                                         Oid *opclass);
+static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);
 static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
                           List *indxid, List *indxqual,
                           List *indxqualorig,
                           ScanDirection indexscandir);
 static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid,
                         List *tideval);
-static NestLoop *make_nestloop(List *qptlist, List *qpqual, Plan *lefttree,
-                         Plan *righttree);
-static HashJoin *make_hashjoin(List *tlist, List *qpqual,
-                         List *hashclauses, Plan *lefttree, Plan *righttree);
+static NestLoop *make_nestloop(List *tlist,
+                                                          List *joinclauses, List *otherclauses,
+                                                          Plan *lefttree, Plan *righttree,
+                                                          JoinType jointype);
+static HashJoin *make_hashjoin(List *tlist,
+                                                          List *joinclauses, List *otherclauses,
+                                                          List *hashclauses,
+                                                          Plan *lefttree, Plan *righttree,
+                                                          JoinType jointype);
 static Hash *make_hash(List *tlist, Node *hashkey, Plan *lefttree);
-static MergeJoin *make_mergejoin(List *tlist, List *qpqual,
-                          List *mergeclauses, Plan *righttree, Plan *lefttree);
+static MergeJoin *make_mergejoin(List *tlist,
+                                                                List *joinclauses, List *otherclauses,
+                                                                List *mergeclauses,
+                                                                Plan *lefttree, Plan *righttree,
+                                                                JoinType jointype);
 static void copy_path_costsize(Plan *dest, Path *src);
 static void copy_plan_costsize(Plan *dest, Plan *src);
-static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);
 
 /*
  * create_plan
@@ -195,7 +206,8 @@ create_join_node(Query *root, JoinPath *best_path, List *tlist)
        List       *outer_tlist;
        Plan       *inner_node;
        List       *inner_tlist;
-       List       *clauses;
+       List       *joinclauses;
+       List       *otherclauses;
        Join       *retval = NULL;
 
        outer_node = create_plan(root, best_path->outerjoinpath);
@@ -204,14 +216,25 @@ create_join_node(Query *root, JoinPath *best_path, List *tlist)
        inner_node = create_plan(root, best_path->innerjoinpath);
        inner_tlist = inner_node->targetlist;
 
-       clauses = get_actual_clauses(best_path->joinrestrictinfo);
+       if (IS_OUTER_JOIN(best_path->jointype))
+       {
+               get_actual_join_clauses(best_path->joinrestrictinfo,
+                                                               &joinclauses, &otherclauses);
+       }
+       else
+       {
+               /* We can treat all clauses alike for an inner join */
+               joinclauses = get_actual_clauses(best_path->joinrestrictinfo);
+               otherclauses = NIL;
+       }
 
        switch (best_path->path.pathtype)
        {
                case T_MergeJoin:
                        retval = (Join *) create_mergejoin_node((MergePath *) best_path,
                                                                                                        tlist,
-                                                                                                       clauses,
+                                                                                                       joinclauses,
+                                                                                                       otherclauses,
                                                                                                        outer_node,
                                                                                                        outer_tlist,
                                                                                                        inner_node,
@@ -220,7 +243,8 @@ create_join_node(Query *root, JoinPath *best_path, List *tlist)
                case T_HashJoin:
                        retval = (Join *) create_hashjoin_node((HashPath *) best_path,
                                                                                                   tlist,
-                                                                                                  clauses,
+                                                                                                  joinclauses,
+                                                                                                  otherclauses,
                                                                                                   outer_node,
                                                                                                   outer_tlist,
                                                                                                   inner_node,
@@ -229,7 +253,8 @@ create_join_node(Query *root, JoinPath *best_path, List *tlist)
                case T_NestLoop:
                        retval = (Join *) create_nestloop_node((NestPath *) best_path,
                                                                                                   tlist,
-                                                                                                  clauses,
+                                                                                                  joinclauses,
+                                                                                                  otherclauses,
                                                                                                   outer_node,
                                                                                                   outer_tlist,
                                                                                                   inner_node,
@@ -411,30 +436,6 @@ create_indexscan_node(Query *root,
        return scan_node;
 }
 
-static TidScan *
-make_tidscan(List *qptlist,
-                        List *qpqual,
-                        Index scanrelid,
-                        List *tideval)
-{
-       TidScan    *node = makeNode(TidScan);
-       Plan       *plan = &node->scan.plan;
-
-       /* cost should be inserted by caller */
-       plan->state = (EState *) NULL;
-       plan->targetlist = qptlist;
-       plan->qual = qpqual;
-       plan->lefttree = NULL;
-       plan->righttree = NULL;
-       node->scan.scanrelid = scanrelid;
-       node->tideval = copyObject(tideval);            /* XXX do we really need a
-                                                                                                * copy? */
-       node->needRescan = false;
-       node->scan.scanstate = (CommonScanState *) NULL;
-
-       return node;
-}
-
 /*
  * create_tidscan_node
  *      Returns a tidscan node for the base relation scanned by 'best_path'
@@ -488,7 +489,8 @@ create_tidscan_node(TidPath *best_path, List *tlist, List *scan_clauses)
 static NestLoop *
 create_nestloop_node(NestPath *best_path,
                                         List *tlist,
-                                        List *clauses,
+                                        List *joinclauses,
+                                        List *otherclauses,
                                         Plan *outer_node,
                                         List *outer_tlist,
                                         Plan *inner_node,
@@ -535,7 +537,8 @@ create_nestloop_node(NestPath *best_path,
                         * attnos, and may have been commuted as well).
                         */
                        if (length(indxqualorig) == 1)          /* single indexscan? */
-                               clauses = set_difference(clauses, lfirst(indxqualorig));
+                               joinclauses = set_difference(joinclauses,
+                                                                                        lfirst(indxqualorig));
 
                        /* only refs to outer vars get changed in the inner indexqual */
                        innerscan->indxqualorig = join_references(indxqualorig,
@@ -577,15 +580,26 @@ create_nestloop_node(NestPath *best_path,
                                                                                        inner_node);
        }
 
+       /*
+        * Set quals to contain INNER/OUTER var references.
+        */
+       joinclauses = join_references(joinclauses,
+                                                                 outer_tlist,
+                                                                 inner_tlist,
+                                                                 (Index) 0);
+       otherclauses = join_references(otherclauses,
+                                                                  outer_tlist,
+                                                                  inner_tlist,
+                                                                  (Index) 0);
+
        join_node = make_nestloop(tlist,
-                                                         join_references(clauses,
-                                                                                         outer_tlist,
-                                                                                         inner_tlist,
-                                                                                         (Index) 0),
+                                                         joinclauses,
+                                                         otherclauses,
                                                          outer_node,
-                                                         inner_node);
+                                                         inner_node,
+                                                         best_path->jointype);
 
-       copy_path_costsize(&join_node->join, &best_path->path);
+       copy_path_costsize(&join_node->join.plan, &best_path->path);
 
        return join_node;
 }
@@ -593,14 +607,14 @@ create_nestloop_node(NestPath *best_path,
 static MergeJoin *
 create_mergejoin_node(MergePath *best_path,
                                          List *tlist,
-                                         List *clauses,
+                                         List *joinclauses,
+                                         List *otherclauses,
                                          Plan *outer_node,
                                          List *outer_tlist,
                                          Plan *inner_node,
                                          List *inner_tlist)
 {
-       List       *qpqual,
-                          *mergeclauses;
+       List       *mergeclauses;
        MergeJoin  *join_node;
 
        mergeclauses = get_actual_clauses(best_path->path_mergeclauses);
@@ -610,10 +624,18 @@ create_mergejoin_node(MergePath *best_path,
         * the list of quals that must be checked as qpquals. Set those
         * clauses to contain INNER/OUTER var references.
         */
-       qpqual = join_references(set_difference(clauses, mergeclauses),
-                                                        outer_tlist,
-                                                        inner_tlist,
-                                                        (Index) 0);
+       joinclauses = join_references(set_difference(joinclauses, mergeclauses),
+                                                                 outer_tlist,
+                                                                 inner_tlist,
+                                                                 (Index) 0);
+
+       /*
+        * Fix the additional qpquals too.
+        */
+       otherclauses = join_references(otherclauses,
+                                                                  outer_tlist,
+                                                                  inner_tlist,
+                                                                  (Index) 0);
 
        /*
         * Now set the references in the mergeclauses and rearrange them so
@@ -640,13 +662,54 @@ create_mergejoin_node(MergePath *best_path,
                                                                        inner_node,
                                                                        best_path->innersortkeys);
 
+       /*
+        * The executor requires the inner side of a mergejoin to support "mark"
+        * and "restore" operations.  Not all plan types do, so we must be careful
+        * not to generate an invalid plan.  If necessary, an invalid inner plan
+        * can be handled by inserting a Materialize node.
+        *
+        * Since the inner side must be ordered, and only Sorts and IndexScans can
+        * create order to begin with, you might think there's no problem --- but
+        * you'd be wrong.  Nestloop and merge joins can *preserve* the order of
+        * their inputs, so they can be selected as the input of a mergejoin,
+        * and that won't work in the present executor.
+        *
+        * Doing this here is a bit of a kluge since the cost of the Materialize
+        * wasn't taken into account in our earlier decisions.  But Materialize
+        * is hard to estimate a cost for, and the above consideration shows that
+        * this is a rare case anyway, so this seems an acceptable way to proceed.
+        *
+        * This check must agree with ExecMarkPos/ExecRestrPos in
+        * executor/execAmi.c!
+        */
+       switch (nodeTag(inner_node))
+       {
+               case T_SeqScan:
+               case T_IndexScan:
+               case T_Material:
+               case T_Sort:
+                       /* OK, these inner plans support mark/restore */
+                       break;
+
+               default:
+                       /* Ooops, need to materialize the inner plan */
+                       inner_node = (Plan *) make_material(inner_tlist,
+                                                                                               inner_node);
+                       break;
+       }
+
+       /*
+        * Now we can build the mergejoin node.
+        */
        join_node = make_mergejoin(tlist,
-                                                          qpqual,
+                                                          joinclauses,
+                                                          otherclauses,
                                                           mergeclauses,
+                                                          outer_node,
                                                           inner_node,
-                                                          outer_node);
+                                                          best_path->jpath.jointype);
 
-       copy_path_costsize(&join_node->join, &best_path->jpath.path);
+       copy_path_costsize(&join_node->join.plan, &best_path->jpath.path);
 
        return join_node;
 }
@@ -654,13 +717,13 @@ create_mergejoin_node(MergePath *best_path,
 static HashJoin *
 create_hashjoin_node(HashPath *best_path,
                                         List *tlist,
-                                        List *clauses,
+                                        List *joinclauses,
+                                        List *otherclauses,
                                         Plan *outer_node,
                                         List *outer_tlist,
                                         Plan *inner_node,
                                         List *inner_tlist)
 {
-       List       *qpqual;
        List       *hashclauses;
        HashJoin   *join_node;
        Hash       *hash_node;
@@ -679,10 +742,18 @@ create_hashjoin_node(HashPath *best_path,
         * the list of quals that must be checked as qpquals. Set those
         * clauses to contain INNER/OUTER var references.
         */
-       qpqual = join_references(set_difference(clauses, hashclauses),
-                                                        outer_tlist,
-                                                        inner_tlist,
-                                                        (Index) 0);
+       joinclauses = join_references(set_difference(joinclauses, hashclauses),
+                                                                 outer_tlist,
+                                                                 inner_tlist,
+                                                                 (Index) 0);
+
+       /*
+        * Fix the additional qpquals too.
+        */
+       otherclauses = join_references(otherclauses,
+                                                                  outer_tlist,
+                                                                  inner_tlist,
+                                                                  (Index) 0);
 
        /*
         * Now set the references in the hashclauses and rearrange them so
@@ -701,12 +772,14 @@ create_hashjoin_node(HashPath *best_path,
         */
        hash_node = make_hash(inner_tlist, innerhashkey, inner_node);
        join_node = make_hashjoin(tlist,
-                                                         qpqual,
+                                                         joinclauses,
+                                                         otherclauses,
                                                          hashclauses,
                                                          outer_node,
-                                                         (Plan *) hash_node);
+                                                         (Plan *) hash_node,
+                                                         best_path->jpath.jointype);
 
-       copy_path_costsize(&join_node->join, &best_path->jpath.path);
+       copy_path_costsize(&join_node->join.plan, &best_path->jpath.path);
 
        return join_node;
 }
@@ -1065,45 +1138,75 @@ make_indexscan(List *qptlist,
        return node;
 }
 
+static TidScan *
+make_tidscan(List *qptlist,
+                        List *qpqual,
+                        Index scanrelid,
+                        List *tideval)
+{
+       TidScan    *node = makeNode(TidScan);
+       Plan       *plan = &node->scan.plan;
+
+       /* cost should be inserted by caller */
+       plan->state = (EState *) NULL;
+       plan->targetlist = qptlist;
+       plan->qual = qpqual;
+       plan->lefttree = NULL;
+       plan->righttree = NULL;
+       node->scan.scanrelid = scanrelid;
+       node->tideval = copyObject(tideval);            /* XXX do we really need a
+                                                                                                * copy? */
+       node->needRescan = false;
+       node->scan.scanstate = (CommonScanState *) NULL;
+
+       return node;
+}
+
 
 static NestLoop *
-make_nestloop(List *qptlist,
-                         List *qpqual,
+make_nestloop(List *tlist,
+                         List *joinclauses,
+                         List *otherclauses,
                          Plan *lefttree,
-                         Plan *righttree)
+                         Plan *righttree,
+                         JoinType jointype)
 {
        NestLoop   *node = makeNode(NestLoop);
-       Plan       *plan = &node->join;
+       Plan       *plan = &node->join.plan;
 
        /* cost should be inserted by caller */
        plan->state = (EState *) NULL;
-       plan->targetlist = qptlist;
-       plan->qual = qpqual;
+       plan->targetlist = tlist;
+       plan->qual = otherclauses;
        plan->lefttree = lefttree;
        plan->righttree = righttree;
-       node->nlstate = (NestLoopState *) NULL;
+       node->join.jointype = jointype;
+       node->join.joinqual = joinclauses;
 
        return node;
 }
 
 static HashJoin *
 make_hashjoin(List *tlist,
-                         List *qpqual,
+                         List *joinclauses,
+                         List *otherclauses,
                          List *hashclauses,
                          Plan *lefttree,
-                         Plan *righttree)
+                         Plan *righttree,
+                         JoinType jointype)
 {
        HashJoin   *node = makeNode(HashJoin);
-       Plan       *plan = &node->join;
+       Plan       *plan = &node->join.plan;
 
        /* cost should be inserted by caller */
        plan->state = (EState *) NULL;
        plan->targetlist = tlist;
-       plan->qual = qpqual;
+       plan->qual = otherclauses;
        plan->lefttree = lefttree;
        plan->righttree = righttree;
        node->hashclauses = hashclauses;
-       node->hashdone = false;
+       node->join.jointype = jointype;
+       node->join.joinqual = joinclauses;
 
        return node;
 }
@@ -1133,21 +1236,25 @@ make_hash(List *tlist, Node *hashkey, Plan *lefttree)
 
 static MergeJoin *
 make_mergejoin(List *tlist,
-                          List *qpqual,
+                          List *joinclauses,
+                          List *otherclauses,
                           List *mergeclauses,
+                          Plan *lefttree,
                           Plan *righttree,
-                          Plan *lefttree)
+                          JoinType jointype)
 {
        MergeJoin  *node = makeNode(MergeJoin);
-       Plan       *plan = &node->join;
+       Plan       *plan = &node->join.plan;
 
        /* cost should be inserted by caller */
        plan->state = (EState *) NULL;
        plan->targetlist = tlist;
-       plan->qual = qpqual;
+       plan->qual = otherclauses;
        plan->lefttree = lefttree;
        plan->righttree = righttree;
        node->mergeclauses = mergeclauses;
+       node->join.jointype = jointype;
+       node->join.joinqual = joinclauses;
 
        return node;
 }
index 8ffd35c9bb04f4479cfd72fd2178e001f01e1209..bf728ca1bdc51ba97ccd7e6ef6bc01585dd4f7e8 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.49 2000/08/13 02:50:07 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.50 2000/09/12 21:06:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "optimizer/planmain.h"
 #include "optimizer/tlist.h"
 #include "optimizer/var.h"
+#include "parser/parsetree.h"
 #include "parser/parse_expr.h"
 #include "parser/parse_oper.h"
 #include "parser/parse_type.h"
 #include "utils/lsyscache.h"
 
 
-static void add_restrict_and_join_to_rel(Query *root, Node *clause);
+static void mark_baserels_for_outer_join(Query *root, Relids rels,
+                                                                                Relids outerrels);
+static void add_restrict_and_join_to_rel(Query *root, Node *clause,
+                                                                                bool isjoinqual,
+                                                                                Relids outerjoinrelids);
 static void add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
                                          Relids join_relids);
 static void add_vars_to_targetlist(Query *root, List *vars);
@@ -47,14 +52,14 @@ static void check_hashjoinable(RestrictInfo *restrictinfo);
  *****************************************************************************/
 
 /*
- * make_var_only_tlist
+ * build_base_rel_tlists
  *       Creates rel nodes for every relation mentioned in the target list
  *       'tlist' (if a node hasn't already been created) and adds them to
- *       *query_relation_list*.  Creates targetlist entries for each member of
- *       'tlist' and adds them to the tlist field of the appropriate rel node.
+ *       root->base_rel_list.  Creates targetlist entries for each var seen
+ *       in 'tlist' and adds them to the tlist of the appropriate rel node.
  */
 void
-make_var_only_tlist(Query *root, List *tlist)
+build_base_rel_tlists(Query *root, List *tlist)
 {
        List       *tlist_vars = pull_var_clause((Node *) tlist, false);
 
@@ -82,48 +87,75 @@ add_vars_to_targetlist(Query *root, List *vars)
        }
 }
 
-/*
+/*----------
  * add_missing_rels_to_query
  *
- *       If we have a range variable in the FROM clause that does not appear
+ *       If we have a relation listed in the join tree that does not appear
  *       in the target list nor qualifications, we must add it to the base
- *       relation list so that it will be joined.      For instance, "select f.x
- *       from foo f, foo f2" is a join of f and f2.  Note that if we have
- *       "select foo.x from foo f", it also gets turned into a join (between
- *       foo as foo and foo as f).
+ *       relation list so that it can be processed.  For instance,
+ *                     select f.x from foo f, foo f2
+ *       is a join of f and f2.  Note that if we have
+ *                     select foo.x from foo f
+ *       this also gets turned into a join (between foo as foo and foo as f).
  *
  *       To avoid putting useless entries into the per-relation targetlists,
  *       this should only be called after all the variables in the targetlist
  *       and quals have been processed by the routines above.
+ *
+ *       Returns a list of all the base relations (RelOptInfo nodes) that appear
+ *       in the join tree.  This list can be used for cross-checking in the
+ *       reverse direction, ie, that we have a join tree entry for every
+ *       relation used in the query.
+ *----------
  */
-void
-add_missing_rels_to_query(Query *root)
+List *
+add_missing_rels_to_query(Query *root, Node *jtnode)
 {
-       int                     varno = 1;
-       List       *l;
+       List       *result = NIL;
 
-       foreach(l, root->rtable)
+       if (jtnode == NULL)
+               return NIL;
+       if (IsA(jtnode, List))
        {
-               RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
+               List       *l;
 
-               if (rte->inJoinSet)
+               foreach(l, (List *) jtnode)
                {
-                       RelOptInfo *rel = get_base_rel(root, varno);
+                       result = nconc(result,
+                                                  add_missing_rels_to_query(root, lfirst(l)));
+               }
+       }
+       else if (IsA(jtnode, RangeTblRef))
+       {
+               int                     varno = ((RangeTblRef *) jtnode)->rtindex;
+               RelOptInfo *rel = get_base_rel(root, varno);
 
-                       /*
-                        * If the rel isn't otherwise referenced, give it a dummy
-                        * targetlist consisting of its own OID.
-                        */
-                       if (rel->targetlist == NIL)
-                       {
-                               Var                *var = makeVar(varno, ObjectIdAttributeNumber,
-                                                                                 OIDOID, -1, 0);
+               /*
+                * If the rel isn't otherwise referenced, give it a dummy
+                * targetlist consisting of its own OID.
+                */
+               if (rel->targetlist == NIL)
+               {
+                       Var                *var = makeVar(varno, ObjectIdAttributeNumber,
+                                                                         OIDOID, -1, 0);
 
-                               add_var_to_tlist(rel, var);
-                       }
+                       add_var_to_tlist(rel, var);
                }
-               varno++;
+
+               result = lcons(rel, NIL);
        }
+       else if (IsA(jtnode, JoinExpr))
+       {
+               JoinExpr   *j = (JoinExpr *) jtnode;
+
+               result = add_missing_rels_to_query(root, j->larg);
+               result = nconc(result,
+                                          add_missing_rels_to_query(root, j->rarg));
+       }
+       else
+               elog(ERROR, "add_missing_rels_to_query: unexpected node type %d",
+                        nodeTag(jtnode));
+       return result;
 }
 
 
@@ -134,11 +166,145 @@ add_missing_rels_to_query(Query *root)
  *****************************************************************************/
 
 
+/*
+ * add_join_quals_to_rels
+ *       Recursively scan the join tree for JOIN/ON (and JOIN/USING) qual
+ *       clauses, and add these to the appropriate JoinInfo lists.  Also,
+ *       mark base RelOptInfos with outerjoinset information, which will
+ *       be needed for proper placement of WHERE clauses during
+ *       add_restrict_and_join_to_rels().
+ *
+ * NOTE: when dealing with inner joins, it is appropriate to let a qual clause
+ * be evaluated at the lowest level where all the variables it mentions are
+ * available.  However, we cannot do this within an outer join since the qual
+ * might eliminate matching rows and cause a NULL row to be added improperly.
+ * Therefore, rels appearing within (the nullable side of) an outer join
+ * are marked with outerjoinset = list of Relids used at the outer join node.
+ * This list will be added to the list of rels referenced by quals using
+ * such a rel, thereby forcing them up the join tree to the right level.
+ *
+ * To ease the calculation of these values, add_join_quals_to_rels() returns
+ * the list of Relids involved in its own level of join.  This is just an
+ * internal convenience; no outside callers pay attention to the result.
+ */
+Relids
+add_join_quals_to_rels(Query *root, Node *jtnode)
+{
+       Relids          result = NIL;
+
+       if (jtnode == NULL)
+               return result;
+       if (IsA(jtnode, List))
+       {
+               List       *l;
+
+               /*
+                * Note: we assume it's impossible to see same RT index from more
+                * than one subtree, so nconc() is OK rather than LispUnioni().
+                */
+               foreach(l, (List *) jtnode)
+                       result = nconc(result,
+                                                  add_join_quals_to_rels(root, lfirst(l)));
+       }
+       else if (IsA(jtnode, RangeTblRef))
+       {
+               int                     varno = ((RangeTblRef *) jtnode)->rtindex;
+
+               /* No quals to deal with, just return correct result */
+               result = lconsi(varno, NIL);
+       }
+       else if (IsA(jtnode, JoinExpr))
+       {
+               JoinExpr   *j = (JoinExpr *) jtnode;
+               Relids          leftids,
+                                       rightids,
+                                       outerjoinids;
+               List       *qual;
+
+               /*
+                * Order of operations here is subtle and critical.  First we recurse
+                * to handle sub-JOINs.  Their join quals will be placed without
+                * regard for whether this level is an outer join, which is correct.
+                * Then, if we are an outer join, we mark baserels contained within
+                * the nullable side(s) with our own rel list; this will restrict
+                * placement of subsequent quals using those rels, including our own
+                * quals, quals above us in the join tree, and WHERE quals.
+                * Finally we place our own join quals.
+                */
+               leftids = add_join_quals_to_rels(root, j->larg);
+               rightids = add_join_quals_to_rels(root, j->rarg);
+
+               result = nconc(listCopy(leftids), rightids);
+
+               outerjoinids = NIL;
+               switch (j->jointype)
+               {
+                       case JOIN_INNER:
+                               /* Inner join adds no restrictions for quals */
+                               break;
+                       case JOIN_LEFT:
+                               mark_baserels_for_outer_join(root, rightids, result);
+                               outerjoinids = result;
+                               break;
+                       case JOIN_FULL:
+                               mark_baserels_for_outer_join(root, result, result);
+                               outerjoinids = result;
+                               break;
+                       case JOIN_RIGHT:
+                               mark_baserels_for_outer_join(root, leftids, result);
+                               outerjoinids = result;
+                               break;
+                       case JOIN_UNION:
+                               /*
+                                * This is where we fail if upper levels of planner haven't
+                                * rewritten UNION JOIN as an Append ...
+                                */
+                               elog(ERROR, "UNION JOIN is not implemented yet");
+                               break;
+                       default:
+                               elog(ERROR, "add_join_quals_to_rels: unsupported join type %d",
+                                        (int) j->jointype);
+                               break;
+               }
+
+               foreach(qual, (List *) j->quals)
+                       add_restrict_and_join_to_rel(root, (Node *) lfirst(qual),
+                                                                                true, outerjoinids);
+       }
+       else
+               elog(ERROR, "add_join_quals_to_rels: unexpected node type %d",
+                        nodeTag(jtnode));
+       return result;
+}
+
+/*
+ * mark_baserels_for_outer_join
+ *       Mark all base rels listed in 'rels' as having the given outerjoinset.
+ */
+static void
+mark_baserels_for_outer_join(Query *root, Relids rels, Relids outerrels)
+{
+       List       *relid;
+
+       foreach(relid, rels)
+       {
+               RelOptInfo *rel = get_base_rel(root, lfirsti(relid));
+
+               /*
+                * Since we do this bottom-up, any outer-rels previously marked
+                * should be within the new outer join set.
+                */
+               Assert(is_subseti(rel->outerjoinset, outerrels));
+
+               rel->outerjoinset = outerrels;
+       }
+}
+
 /*
  * add_restrict_and_join_to_rels
  *       Fill RestrictInfo and JoinInfo lists of relation entries for all
  *       relations appearing within clauses.  Creates new relation entries if
- *       necessary, adding them to *query_relation_list*.
+ *       necessary, adding them to root->base_rel_list.
  *
  * 'clauses': the list of clauses in the cnfify'd query qualification.
  */
@@ -148,7 +314,8 @@ add_restrict_and_join_to_rels(Query *root, List *clauses)
        List       *clause;
 
        foreach(clause, clauses)
-               add_restrict_and_join_to_rel(root, (Node *) lfirst(clause));
+               add_restrict_and_join_to_rel(root, (Node *) lfirst(clause),
+                                                                        false, NIL);
 }
 
 /*
@@ -157,17 +324,31 @@ add_restrict_and_join_to_rels(Query *root, List *clauses)
  *       (depending on whether the clause is a join) of each base relation
  *       mentioned in the clause.      A RestrictInfo node is created and added to
  *       the appropriate list for each rel.  Also, if the clause uses a
- *       mergejoinable operator, enter the left- and right-side expressions
- *       into the query's lists of equijoined vars.
+ *       mergejoinable operator and is not an outer-join qual, enter the left-
+ *       and right-side expressions into the query's lists of equijoined vars.
+ *
+ * isjoinqual is true if the clause came from JOIN/ON or JOIN/USING;
+ * we have to mark the created RestrictInfo accordingly.  If the JOIN
+ * is an OUTER join, the caller must set outerjoinrelids = all relids of join,
+ * which will override the joinrel identifiers extracted from the clause
+ * itself.  For inner join quals and WHERE clauses, set outerjoinrelids = NIL.
+ * (Passing the whole list, and not just an "isouterjoin" boolean, is simply
+ * a speed optimization: we could extract the same list from the base rels'
+ * outerjoinsets, but since add_join_quals_to_rels() already knows what we
+ * should use, might as well pass it in instead of recalculating it.)
  */
 static void
-add_restrict_and_join_to_rel(Query *root, Node *clause)
+add_restrict_and_join_to_rel(Query *root, Node *clause,
+                                                        bool isjoinqual,
+                                                        Relids outerjoinrelids)
 {
        RestrictInfo *restrictinfo = makeNode(RestrictInfo);
        Relids          relids;
        List       *vars;
+       bool            can_be_equijoin;
 
        restrictinfo->clause = (Expr *) clause;
+       restrictinfo->isjoinqual = isjoinqual;
        restrictinfo->subclauseindices = NIL;
        restrictinfo->mergejoinoperator = InvalidOid;
        restrictinfo->left_sortop = InvalidOid;
@@ -179,6 +360,44 @@ add_restrict_and_join_to_rel(Query *root, Node *clause)
         */
        clause_get_relids_vars(clause, &relids, &vars);
 
+       /*
+        * If caller has given us a join relid list, use it; otherwise, we must
+        * scan the referenced base rels and add in any outer-join rel lists.
+        * This prevents the clause from being applied at a lower level of joining
+        * than any OUTER JOIN that should be evaluated before it.
+        */
+       if (outerjoinrelids)
+       {
+               /* Safety check: parser should have enforced this to start with */
+               if (! is_subseti(relids, outerjoinrelids))
+                       elog(ERROR, "JOIN qualification may not refer to other relations");
+               relids = outerjoinrelids;
+               can_be_equijoin = false;
+       }
+       else
+       {
+               Relids          newrelids = relids;
+               List       *relid;
+
+               /* We rely on LispUnioni to be nondestructive of its input lists... */
+               can_be_equijoin = true;
+               foreach(relid, relids)
+               {
+                       RelOptInfo *rel = get_base_rel(root, lfirsti(relid));
+
+                       if (rel->outerjoinset)
+                       {
+                               newrelids = LispUnioni(newrelids, rel->outerjoinset);
+                               /*
+                                * Because application of the qual will be delayed by outer
+                                * join, we mustn't assume its vars are equal everywhere.
+                                */
+                               can_be_equijoin = false;
+                       }
+               }
+               relids = newrelids;
+       }
+
        if (length(relids) == 1)
        {
 
@@ -199,7 +418,8 @@ add_restrict_and_join_to_rel(Query *root, Node *clause)
                 * that "a.x = a.y AND a.x = b.z AND a.y = c.q" allows us to
                 * consider z and q equal after their rels are joined.
                 */
-               check_mergejoinable(restrictinfo);
+               if (can_be_equijoin)
+                       check_mergejoinable(restrictinfo);
        }
        else if (relids != NIL)
        {
@@ -209,11 +429,11 @@ add_restrict_and_join_to_rel(Query *root, Node *clause)
                 * the relid list.      Set additional RestrictInfo fields for
                 * joining.
                 *
-                * We need the merge info whether or not mergejoin is enabled (for
-                * constructing equijoined-var lists), but we don't bother setting
-                * hash info if hashjoin is disabled.
+                * We don't bother setting the merge/hashjoin info if we're not
+                * going to need it.
                 */
-               check_mergejoinable(restrictinfo);
+               if (enable_mergejoin || can_be_equijoin)
+                       check_mergejoinable(restrictinfo);
                if (enable_hashjoin)
                        check_hashjoinable(restrictinfo);
 
@@ -223,7 +443,7 @@ add_restrict_and_join_to_rel(Query *root, Node *clause)
                add_join_info_to_rels(root, restrictinfo, relids);
 
                /*
-                * Add vars used in the join clause to targetlists of member
+                * Add vars used in the join clause to targetlists of their
                 * relations, so that they will be emitted by the plan nodes that
                 * scan those relations (else they won't be available at the join
                 * node!).
@@ -241,12 +461,14 @@ add_restrict_and_join_to_rel(Query *root, Node *clause)
        }
 
        /*
-        * If the clause has a mergejoinable operator, then the two sides
+        * If the clause has a mergejoinable operator, and is not an outer-join
+        * qualification nor bubbled up due to an outer join, then the two sides
         * represent equivalent PathKeyItems for path keys: any path that is
-        * sorted by one side will also be sorted by the other (after joining,
-        * that is).  Record the key equivalence for future use.
+        * sorted by one side will also be sorted by the other (as soon as the
+        * two rels are joined, that is).  Record the key equivalence for future
+        * use.
         */
-       if (restrictinfo->mergejoinoperator != InvalidOid)
+       if (can_be_equijoin && restrictinfo->mergejoinoperator != InvalidOid)
                add_equijoined_keys(root, restrictinfo);
 }
 
@@ -392,7 +614,8 @@ process_implied_equality(Query *root, Node *item1, Node *item2,
                                                                         BOOLOID); /* operator result type */
        clause->args = lcons(item1, lcons(item2, NIL));
 
-       add_restrict_and_join_to_rel(root, (Node *) clause);
+       add_restrict_and_join_to_rel(root, (Node *) clause,
+                                                                false, NIL);
 }
 
 
index abb468aa8d1dc9a3ed7002b402a79c2eb99120fa..1fcbe64e8880963bf2e7bae36aac481e83df2768 100644 (file)
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.58 2000/08/13 02:50:07 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.59 2000/09/12 21:06:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -28,6 +28,7 @@
 #include "optimizer/paths.h"
 #include "optimizer/planmain.h"
 #include "optimizer/tlist.h"
+#include "parser/parsetree.h"
 #include "utils/memutils.h"
 
 
@@ -41,11 +42,8 @@ static Plan *subplanner(Query *root, List *flat_tlist, List *qual,
  *       not any fancier features.
  *
  * tlist is the target list the query should produce (NOT root->targetList!)
- * qual is the qualification of the query (likewise!)
  * tuple_fraction is the fraction of tuples we expect will be retrieved
  *
- * qual must already have been converted to implicit-AND form.
- *
  * Note: the Query node now also includes a query_pathkeys field, which
  * is both an input and an output of query_planner().  The input value
  * signals query_planner that the indicated sort order is wanted in the
@@ -75,9 +73,9 @@ static Plan *subplanner(Query *root, List *flat_tlist, List *qual,
 Plan *
 query_planner(Query *root,
                          List *tlist,
-                         List *qual,
                          double tuple_fraction)
 {
+       List       *normal_qual;
        List       *noncachable_qual;
        List       *constant_qual;
        List       *var_only_tlist;
@@ -96,7 +94,7 @@ query_planner(Query *root,
                root->query_pathkeys = NIL;             /* signal unordered result */
 
                /* Make childless Result node to evaluate given tlist. */
-               return (Plan *) make_result(tlist, (Node *) qual, (Plan *) NULL);
+               return (Plan *) make_result(tlist, root->qual, (Plan *) NULL);
        }
 
        /*
@@ -111,10 +109,12 @@ query_planner(Query *root,
         * noncachable functions but no vars, such as "WHERE random() < 0.5".
         * These cannot be treated as normal restriction or join quals, but
         * they're not constants either.  Instead, attach them to the qpqual
-        * of the top-level plan, so that they get evaluated once per potential
+        * of the top plan, so that they get evaluated once per potential
         * output tuple.
         */
-       qual = pull_constant_clauses(qual, &noncachable_qual, &constant_qual);
+       normal_qual = pull_constant_clauses((List *) root->qual,
+                                                                               &noncachable_qual,
+                                                                               &constant_qual);
 
        /*
         * Create a target list that consists solely of (resdom var) target
@@ -132,7 +132,7 @@ query_planner(Query *root,
        /*
         * Choose the best access path and build a plan for it.
         */
-       subplan = subplanner(root, var_only_tlist, qual, tuple_fraction);
+       subplan = subplanner(root, var_only_tlist, normal_qual, tuple_fraction);
 
        /*
         * Handle the noncachable quals.
@@ -188,6 +188,8 @@ subplanner(Query *root,
                   List *qual,
                   double tuple_fraction)
 {
+       List       *joined_rels;
+       List       *brel;
        RelOptInfo *final_rel;
        Plan       *resultplan;
        MemoryContext mycontext;
@@ -196,7 +198,7 @@ subplanner(Query *root,
        Path       *presortedpath;
 
        /*
-        * Initialize the targetlist and qualification, adding entries to
+        * Examine the targetlist and qualifications, adding entries to
         * base_rel_list as relation references are found (e.g., in the
         * qualification, the targetlist, etc.).  Restrict and join clauses
         * are added to appropriate lists belonging to the mentioned
@@ -207,13 +209,29 @@ subplanner(Query *root,
        root->join_rel_list = NIL;
        root->equi_key_list = NIL;
 
-       make_var_only_tlist(root, flat_tlist);
+       build_base_rel_tlists(root, flat_tlist);
+       (void) add_join_quals_to_rels(root, (Node *) root->jointree);
+       /* this must happen after add_join_quals_to_rels: */
        add_restrict_and_join_to_rels(root, qual);
 
        /*
-        * Make sure we have RelOptInfo nodes for all relations used.
+        * Make sure we have RelOptInfo nodes for all relations to be joined.
+        */
+       joined_rels = add_missing_rels_to_query(root, (Node *) root->jointree);
+
+       /*
+        * Check that the join tree includes all the base relations used in
+        * the query --- otherwise, the parser or rewriter messed up.
         */
-       add_missing_rels_to_query(root);
+       foreach(brel, root->base_rel_list)
+       {
+               RelOptInfo *baserel = (RelOptInfo *) lfirst(brel);
+               int             relid = lfirsti(baserel->relids);
+
+               if (! ptrMember(baserel, joined_rels))
+                       elog(ERROR, "Internal error: no jointree entry for rel %s (%d)",
+                                rt_fetch(relid, root->rtable)->eref->relname, relid);
+       }
 
        /*
         * Use the completed lists of equijoined keys to deduce any implied
@@ -258,12 +276,11 @@ subplanner(Query *root,
                 * We expect to end up here for a trivial INSERT ... VALUES query
                 * (which will have a target relation, so it gets past
                 * query_planner's check for empty range table; but the target rel
-                * is unreferenced and not marked inJoinSet, so we find there is
-                * nothing to join).
+                * is not in the join tree, so we find there is nothing to join).
                 *
                 * It's also possible to get here if the query was rewritten by the
-                * rule processor (creating rangetable entries not marked
-                * inJoinSet) but the rules either did nothing or were simplified
+                * rule processor (creating dummy rangetable entries that are not in
+                * the join tree) but the rules either did nothing or were simplified
                 * to nothing by constant-expression folding.  So, don't complain.
                 */
                root->query_pathkeys = NIL;             /* signal unordered result */
index 4be9b05bb900ee1ab7b7428c956b7ad33efa9f5b..7ffbb4666d9d588a7b9bbe2c8adc7445353b8d9b 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.88 2000/08/21 20:55:29 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.89 2000/09/12 21:06:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -29,6 +29,7 @@
 #include "utils/lsyscache.h"
 
 
+static void preprocess_join_conditions(Query *parse, Node *jtnode);
 static List *make_subplanTargetList(Query *parse, List *tlist,
                                           AttrNumber **groupColIdx);
 static Plan *make_groupplan(List *group_tlist, bool tuplePerGroup,
@@ -163,6 +164,7 @@ subquery_planner(Query *parse, double tuple_fraction)
         * canonicalize_qual?
         */
        parse->qual = (Node *) canonicalize_qual((Expr *) parse->qual, true);
+
 #ifdef OPTIMIZER_DEBUG
        printf("After canonicalize_qual()\n");
        pprint(parse->qual);
@@ -211,6 +213,9 @@ subquery_planner(Query *parse, double tuple_fraction)
                parse->havingQual = SS_replace_correlation_vars(parse->havingQual);
        }
 
+       /* Do all the above for each qual condition (ON clause) in the join tree */
+       preprocess_join_conditions(parse, (Node *) parse->jointree);
+
        /* Do the main planning (potentially recursive) */
 
        return union_planner(parse, tuple_fraction);
@@ -224,6 +229,58 @@ subquery_planner(Query *parse, double tuple_fraction)
         */
 }
 
+/*
+ * preprocess_join_conditions
+ *             Recursively scan the query's jointree and do subquery_planner's
+ *             qual preprocessing work on each ON condition found therein.
+ */
+static void
+preprocess_join_conditions(Query *parse, Node *jtnode)
+{
+       if (jtnode == NULL)
+               return;
+       if (IsA(jtnode, List))
+       {
+               List       *l;
+
+               foreach(l, (List *) jtnode)
+                       preprocess_join_conditions(parse, lfirst(l));
+       }
+       else if (IsA(jtnode, RangeTblRef))
+       {
+               /* nothing to do here */
+       }
+       else if (IsA(jtnode, JoinExpr))
+       {
+               JoinExpr   *j = (JoinExpr *) jtnode;
+
+               preprocess_join_conditions(parse, j->larg);
+               preprocess_join_conditions(parse, j->rarg);
+
+               /* Simplify constant expressions */
+               j->quals = eval_const_expressions(j->quals);
+
+               /* Canonicalize the qual, and convert it to implicit-AND format */
+               j->quals = (Node *) canonicalize_qual((Expr *) j->quals, true);
+
+               /* Expand SubLinks to SubPlans */
+               if (parse->hasSubLinks)
+               {
+                       j->quals = SS_process_sublinks(j->quals);
+                       /*
+                        * ON conditions, like WHERE clauses, are evaluated pre-GROUP;
+                        * so we allow ungrouped vars in them.
+                        */
+               }
+
+               /* Replace uplevel vars with Param nodes */
+               if (PlannerQueryLevel > 1)
+                       j->quals = SS_replace_correlation_vars(j->quals);
+       }
+       else
+               elog(ERROR, "preprocess_join_conditions: unexpected node type %d",
+                        nodeTag(jtnode));
+}
 
 /*--------------------
  * union_planner
@@ -542,7 +599,6 @@ union_planner(Query *parse,
                /* Generate the (sub) plan */
                result_plan = query_planner(parse,
                                                                        sub_tlist,
-                                                                       (List *) parse->qual,
                                                                        tuple_fraction);
 
                /*
index d8a09c017dd0279e8ec5a23c3076e5beebd069f9..d30636c185e32b09b2dcb5cbb30ab2716c048f13 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.64 2000/06/04 20:50:50 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.65 2000/09/12 21:06:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -106,11 +106,13 @@ set_plan_references(Plan *plan)
                        set_join_references((Join *) plan);
                        fix_expr_references(plan, (Node *) plan->targetlist);
                        fix_expr_references(plan, (Node *) plan->qual);
+                       fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
                        break;
                case T_MergeJoin:
                        set_join_references((Join *) plan);
                        fix_expr_references(plan, (Node *) plan->targetlist);
                        fix_expr_references(plan, (Node *) plan->qual);
+                       fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
                        fix_expr_references(plan,
                                                                (Node *) ((MergeJoin *) plan)->mergeclauses);
                        break;
@@ -118,6 +120,7 @@ set_plan_references(Plan *plan)
                        set_join_references((Join *) plan);
                        fix_expr_references(plan, (Node *) plan->targetlist);
                        fix_expr_references(plan, (Node *) plan->qual);
+                       fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
                        fix_expr_references(plan,
                                                                (Node *) ((HashJoin *) plan)->hashclauses);
                        break;
@@ -236,15 +239,15 @@ fix_expr_references(Plan *plan, Node *node)
 static void
 set_join_references(Join *join)
 {
-       Plan       *outer = join->lefttree;
-       Plan       *inner = join->righttree;
+       Plan       *outer = join->plan.lefttree;
+       Plan       *inner = join->plan.righttree;
        List       *outer_tlist = ((outer == NULL) ? NIL : outer->targetlist);
        List       *inner_tlist = ((inner == NULL) ? NIL : inner->targetlist);
 
-       join->targetlist = join_references(join->targetlist,
-                                                                          outer_tlist,
-                                                                          inner_tlist,
-                                                                          (Index) 0);
+       join->plan.targetlist = join_references(join->plan.targetlist,
+                                                                                       outer_tlist,
+                                                                                       inner_tlist,
+                                                                                       (Index) 0);
 }
 
 /*
index b0772b83f1c7d860c45815b5f4a13917168fe1f8..24a0aae55cd4ab48a6f23b2742483390b86e9759 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.40 2000/08/06 04:13:22 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.41 2000/09/12 21:06:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -649,12 +649,21 @@ SS_finalize_plan(Plan *plan)
                         */
                        break;
 
+               case T_NestLoop:
+                       finalize_primnode((Node *) ((Join *) plan)->joinqual,
+                                                         &results);
+                       break;
+
                case T_MergeJoin:
+                       finalize_primnode((Node *) ((Join *) plan)->joinqual,
+                                                         &results);
                        finalize_primnode((Node *) ((MergeJoin *) plan)->mergeclauses,
                                                          &results);
                        break;
 
                case T_HashJoin:
+                       finalize_primnode((Node *) ((Join *) plan)->joinqual,
+                                                         &results);
                        finalize_primnode((Node *) ((HashJoin *) plan)->hashclauses,
                                                          &results);
                        break;
@@ -671,7 +680,6 @@ SS_finalize_plan(Plan *plan)
 
                case T_Agg:
                case T_SeqScan:
-               case T_NestLoop:
                case T_Material:
                case T_Sort:
                case T_Unique:
index fc192e6f28bae730bf9aeaed13da3f2678925eb6..a28e329e537a9f9e27cfb5cdf54f0ae918b9b89c 100644 (file)
@@ -107,6 +107,7 @@ transformKeySetQuery(Query *origNode)
                Node_Copy(origNode, unionNode, distinctClause);
                Node_Copy(origNode, unionNode, sortClause);
                Node_Copy(origNode, unionNode, rtable);
+               Node_Copy(origNode, unionNode, jointree);
                Node_Copy(origNode, unionNode, targetList);
 
                origNode->unionClause = lappend(origNode->unionClause, unionNode);
index f069cafdf66f716af515b9be7b3505ca9fdd8518..d284dd51e02f12ee5b7580c74ccf7424caff21be 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.51 2000/06/20 04:22:16 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.52 2000/09/12 21:06:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -528,12 +528,9 @@ fix_parsetree_attnums(Index rt_index,
        context.new_relid = new_relid;
        context.sublevels_up = 0;
 
-       /*
-        * We must scan both the targetlist and qual, but we know the
-        * havingQual is empty, so we can ignore it.
-        */
-       fix_parsetree_attnums_walker((Node *) parsetree->targetList, &context);
-       fix_parsetree_attnums_walker((Node *) parsetree->qual, &context);
+       query_tree_walker(parsetree,
+                                         fix_parsetree_attnums_walker,
+                                         (void *) &context);
 }
 
 /*
@@ -565,38 +562,17 @@ fix_parsetree_attnums_walker(Node *node,
                }
                return false;
        }
-       if (IsA(node, SubLink))
+       if (IsA(node, Query))
        {
+               /* Recurse into subselects */
+               bool            result;
 
-               /*
-                * Standard expression_tree_walker will not recurse into
-                * subselect, but here we must do so.
-                */
-               SubLink    *sub = (SubLink *) node;
-
-               if (fix_parsetree_attnums_walker((Node *) (sub->lefthand), context))
-                       return true;
                context->sublevels_up++;
-               if (fix_parsetree_attnums_walker((Node *) (sub->subselect), context))
-               {
-                       context->sublevels_up--;
-                       return true;
-               }
+               result = query_tree_walker((Query *) node,
+                                                                  fix_parsetree_attnums_walker,
+                                                                  (void *) context);
                context->sublevels_up--;
-               return false;
-       }
-       if (IsA(node, Query))
-       {
-               /* Reach here after recursing down into subselect above... */
-               Query      *qry = (Query *) node;
-
-               if (fix_parsetree_attnums_walker((Node *) (qry->targetList), context))
-                       return true;
-               if (fix_parsetree_attnums_walker((Node *) (qry->qual), context))
-                       return true;
-               if (fix_parsetree_attnums_walker((Node *) (qry->havingQual), context))
-                       return true;
-               return false;
+               return result;
        }
        return expression_tree_walker(node, fix_parsetree_attnums_walker,
                                                                  (void *) context);
index cf0b6dd703c76b3a904c4322b47b49b9b11ee5fc..36c7abd85b53de76b720f6ac6a7fc0fc2a58baa5 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.73 2000/08/24 03:29:05 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.74 2000/09/12 21:06:58 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -591,7 +591,7 @@ check_subplans_for_ungrouped_vars_walker(Node *node,
                                        elog(ERROR, "cache lookup of attribute %d in relation %u failed",
                                                 var->varattno, rte->relid);
                                elog(ERROR, "Sub-SELECT uses un-GROUPed attribute %s.%s from outer query",
-                                        rte->ref->relname, attname);
+                                        rte->eref->relname, attname);
                        }
                }
        }
@@ -1639,25 +1639,44 @@ simplify_op_or_func(Expr *expr, List *args)
  * will have List structure at the top level, and it handles TargetEntry nodes
  * so that a scan of a target list can be handled without additional code.
  * (But only the "expr" part of a TargetEntry is examined, unless the walker
- * chooses to process TargetEntry nodes specially.)
+ * chooses to process TargetEntry nodes specially.)  Also, RangeTblRef and
+ * JoinExpr nodes are handled, so that qual expressions in a jointree can be
+ * processed without additional code.
+ *
+ * expression_tree_walker will handle SubLink and SubPlan nodes by recursing
+ * normally into the "lefthand" arguments (which belong to the outer plan).
+ * It will also call the walker on the sub-Query node; however, when
+ * expression_tree_walker itself is called on a Query node, it does nothing
+ * and returns "false".  The net effect is that unless the walker does
+ * something special at a Query node, sub-selects will not be visited
+ * during an expression tree walk.  This is exactly the behavior wanted
+ * in many cases --- and for those walkers that do want to recurse into
+ * sub-selects, special behavior is typically needed anyway at the entry
+ * to a sub-select (such as incrementing a depth counter).  A walker that
+ * wants to examine sub-selects should include code along the lines of:
+ *
+ *             if (IsA(node, Query))
+ *             {
+ *                     adjust context for subquery;
+ *                     result = query_tree_walker((Query *) node, my_walker, context);
+ *                     restore context if needed;
+ *                     return result;
+ *             }
  *
- * expression_tree_walker will handle a SUBPLAN_EXPR node by recursing into
- * the args and slink->oper lists (which belong to the outer plan), but it
- * will *not* visit the inner plan, since that's typically what expression
- * tree walkers want.  A walker that wants to visit the subplan can force
- * appropriate behavior by recognizing subplan expression nodes and doing
- * the right thing.
+ * query_tree_walker is a convenience routine (see below) that calls the
+ * walker on all the expression subtrees of the given Query node.
  *
- * Bare SubLink nodes (without a SUBPLAN_EXPR) are handled by recursing into
- * the "lefthand" argument list only.  (A bare SubLink should be seen only if
- * the tree has not yet been processed by subselect.c.)  Again, this can be
- * overridden by the walker, but it seems to be the most useful default
- * behavior.
+ * NOTE: currently, because make_subplan() clears the subselect link in
+ * a SubLink node, it is not actually possible to recurse into subselects
+ * of an already-planned expression tree.  This is OK for current uses,
+ * but ought to be cleaned up when we redesign querytree processing.
  *--------------------
  */
 
 bool
-                       expression_tree_walker(Node *node, bool (*walker) (), void *context)
+expression_tree_walker(Node *node,
+                                          bool (*walker) (),
+                                          void *context)
 {
        List       *temp;
 
@@ -1677,6 +1696,7 @@ bool
                case T_Const:
                case T_Var:
                case T_Param:
+               case T_RangeTblRef:
                        /* primitive node types with no subnodes */
                        break;
                case T_Expr:
@@ -1750,17 +1770,31 @@ bool
 
                                /*
                                 * If the SubLink has already been processed by
-                                * subselect.c, it will have lefthand=NIL, and we only
-                                * need to look at the oper list.  Otherwise we only need
-                                * to look at lefthand (the Oper nodes in the oper list
-                                * are deemed uninteresting).
+                                * subselect.c, it will have lefthand=NIL, and we need to
+                                * scan the oper list.  Otherwise we only need to look at
+                                * the lefthand list (the incomplete Oper nodes in the oper
+                                * list are deemed uninteresting, perhaps even confusing).
                                 */
                                if (sublink->lefthand)
-                                       return walker((Node *) sublink->lefthand, context);
+                               {
+                                       if (walker((Node *) sublink->lefthand, context))
+                                               return true;
+                               }
                                else
-                                       return walker((Node *) sublink->oper, context);
+                               {
+                                       if (walker((Node *) sublink->oper, context))
+                                               return true;
+                               }
+                               /*
+                                * Also invoke the walker on the sublink's Query node,
+                                * so it can recurse into the sub-query if it wants to.
+                                */
+                               return walker(sublink->subselect, context);
                        }
                        break;
+               case T_Query:
+                       /* Do nothing with a sub-Query, per discussion above */
+                       break;
                case T_List:
                        foreach(temp, (List *) node)
                        {
@@ -1770,6 +1804,23 @@ bool
                        break;
                case T_TargetEntry:
                        return walker(((TargetEntry *) node)->expr, context);
+               case T_JoinExpr:
+                       {
+                               JoinExpr    *join = (JoinExpr *) node;
+
+                               if (walker(join->larg, context))
+                                       return true;
+                               if (walker(join->rarg, context))
+                                       return true;
+                               if (walker(join->quals, context))
+                                       return true;
+                               if (walker((Node *) join->colvars, context))
+                                       return true;
+                               /* alias clause, using list, colnames list are deemed
+                                * uninteresting.
+                                */
+                       }
+                       break;
                default:
                        elog(ERROR, "expression_tree_walker: Unexpected node type %d",
                                 nodeTag(node));
@@ -1778,6 +1829,37 @@ bool
        return false;
 }
 
+/*
+ * query_tree_walker --- initiate a walk of a Query's expressions
+ *
+ * This routine exists just to reduce the number of places that need to know
+ * where all the expression subtrees of a Query are.  Note it can be used
+ * for starting a walk at top level of a Query regardless of whether the
+ * walker intends to descend into subqueries.  It is also useful for
+ * descending into subqueries within a walker.
+ */
+bool
+query_tree_walker(Query *query,
+                                 bool (*walker) (),
+                                 void *context)
+{
+       Assert(query != NULL && IsA(query, Query));
+
+       if (walker((Node *) query->targetList, context))
+               return true;
+       if (walker(query->qual, context))
+               return true;
+       if (walker(query->havingQual, context))
+               return true;
+       if (walker((Node *) query->jointree, context))
+               return true;
+       /*
+        * XXX for subselect-in-FROM, may need to examine rtable as well
+        */
+       return false;
+}
+
+
 /*--------------------
  * expression_tree_mutator() is designed to support routines that make a
  * modified copy of an expression tree, with some nodes being added,
@@ -1838,7 +1920,9 @@ bool
  */
 
 Node *
-                       expression_tree_mutator(Node *node, Node *(*mutator) (), void *context)
+expression_tree_mutator(Node *node,
+                                               Node *(*mutator) (),
+                                               void *context)
 {
 
        /*
@@ -1866,6 +1950,7 @@ Node *
                case T_Const:
                case T_Var:
                case T_Param:
+               case T_RangeTblRef:
                        /* primitive node types with no subnodes */
                        return (Node *) copyObject(node);
                case T_Expr:
@@ -2044,6 +2129,20 @@ Node *
                                return (Node *) newnode;
                        }
                        break;
+               case T_JoinExpr:
+                       {
+                               JoinExpr *join = (JoinExpr *) node;
+                               JoinExpr *newnode;
+
+                               FLATCOPY(newnode, join, JoinExpr);
+                               MUTATE(newnode->larg, join->larg, Node *);
+                               MUTATE(newnode->rarg, join->rarg, Node *);
+                               MUTATE(newnode->quals, join->quals, Node *);
+                               MUTATE(newnode->colvars, join->colvars, List *);
+                               /* We do not mutate alias, using, or colnames by default */
+                               return (Node *) newnode;
+                       }
+                       break;
                default:
                        elog(ERROR, "expression_tree_mutator: Unexpected node type %d",
                                 nodeTag(node));
index 5588e91e5b718d72fd72953dffcbbc92c47ea2dd..fc73bb2b6642459f861a78dd7ad914f7da1cc3b5 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.64 2000/05/30 00:49:49 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.65 2000/09/12 21:06:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -119,7 +119,9 @@ set_cheapest(RelOptInfo *parent_rel)
        Path       *cheapest_total_path;
 
        Assert(IsA(parent_rel, RelOptInfo));
-       Assert(pathlist != NIL);
+
+       if (pathlist == NIL)
+               elog(ERROR, "Unable to devise a query plan for the given query");
 
        cheapest_startup_path = cheapest_total_path = (Path *) lfirst(pathlist);
 
@@ -352,6 +354,7 @@ create_index_path(Query *root,
         * number of rows is the same as the parent rel's estimate.
         */
        pathnode->joinrelids = NIL; /* no join clauses here */
+       pathnode->alljoinquals = false;
        pathnode->rows = rel->rows;
 
        cost_index(&pathnode->path, root, rel, index, indexquals, false);
@@ -393,6 +396,7 @@ create_tidscan_path(RelOptInfo *rel, List *tideval)
  *       relations.
  *
  * 'joinrel' is the join relation.
+ * 'jointype' is the type of join required
  * 'outer_path' is the outer path
  * 'inner_path' is the inner path
  * 'restrict_clauses' are the RestrictInfo nodes to apply at the join
@@ -403,6 +407,7 @@ create_tidscan_path(RelOptInfo *rel, List *tideval)
  */
 NestPath   *
 create_nestloop_path(RelOptInfo *joinrel,
+                                        JoinType jointype,
                                         Path *outer_path,
                                         Path *inner_path,
                                         List *restrict_clauses,
@@ -412,6 +417,7 @@ create_nestloop_path(RelOptInfo *joinrel,
 
        pathnode->path.pathtype = T_NestLoop;
        pathnode->path.parent = joinrel;
+       pathnode->jointype = jointype;
        pathnode->outerjoinpath = outer_path;
        pathnode->innerjoinpath = inner_path;
        pathnode->joinrestrictinfo = restrict_clauses;
@@ -428,6 +434,7 @@ create_nestloop_path(RelOptInfo *joinrel,
  *       two relations
  *
  * 'joinrel' is the join relation
+ * 'jointype' is the type of join required
  * 'outer_path' is the outer path
  * 'inner_path' is the inner path
  * 'restrict_clauses' are the RestrictInfo nodes to apply at the join
@@ -440,6 +447,7 @@ create_nestloop_path(RelOptInfo *joinrel,
  */
 MergePath  *
 create_mergejoin_path(RelOptInfo *joinrel,
+                                         JoinType jointype,
                                          Path *outer_path,
                                          Path *inner_path,
                                          List *restrict_clauses,
@@ -463,6 +471,7 @@ create_mergejoin_path(RelOptInfo *joinrel,
 
        pathnode->jpath.path.pathtype = T_MergeJoin;
        pathnode->jpath.path.parent = joinrel;
+       pathnode->jpath.jointype = jointype;
        pathnode->jpath.outerjoinpath = outer_path;
        pathnode->jpath.innerjoinpath = inner_path;
        pathnode->jpath.joinrestrictinfo = restrict_clauses;
@@ -486,6 +495,7 @@ create_mergejoin_path(RelOptInfo *joinrel,
  *       Creates a pathnode corresponding to a hash join between two relations.
  *
  * 'joinrel' is the join relation
+ * 'jointype' is the type of join required
  * 'outer_path' is the cheapest outer path
  * 'inner_path' is the cheapest inner path
  * 'restrict_clauses' are the RestrictInfo nodes to apply at the join
@@ -496,6 +506,7 @@ create_mergejoin_path(RelOptInfo *joinrel,
  */
 HashPath   *
 create_hashjoin_path(RelOptInfo *joinrel,
+                                        JoinType jointype,
                                         Path *outer_path,
                                         Path *inner_path,
                                         List *restrict_clauses,
@@ -506,6 +517,7 @@ create_hashjoin_path(RelOptInfo *joinrel,
 
        pathnode->jpath.path.pathtype = T_HashJoin;
        pathnode->jpath.path.parent = joinrel;
+       pathnode->jpath.jointype = jointype;
        pathnode->jpath.outerjoinpath = outer_path;
        pathnode->jpath.innerjoinpath = inner_path;
        pathnode->jpath.joinrestrictinfo = restrict_clauses;
index 070fabf7669049e3b6c5e207f630050d2262c649..87e87597d11e0dcb8c300cc5253e5b28b51d77ed 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.27 2000/06/18 22:44:12 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.28 2000/09/12 21:06:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -72,6 +72,7 @@ get_base_rel(Query *root, int relid)
        rel->tuples = 0;
        rel->baserestrictinfo = NIL;
        rel->baserestrictcost = 0;
+       rel->outerjoinset = NIL;
        rel->joininfo = NIL;
        rel->innerjoin = NIL;
 
@@ -178,6 +179,7 @@ get_join_rel(Query *root,
        joinrel->tuples = 0;
        joinrel->baserestrictinfo = NIL;
        joinrel->baserestrictcost = 0;
+       joinrel->outerjoinset = NIL;
        joinrel->joininfo = NIL;
        joinrel->innerjoin = NIL;
 
@@ -216,8 +218,7 @@ get_join_rel(Query *root,
                                                           restrictlist);
 
        /*
-        * Add the joinrel to the front of the query's joinrel list.
-        * (allpaths.c depends on this ordering!)
+        * Add the joinrel to the query's joinrel list.
         */
        root->join_rel_list = lcons(joinrel, root->join_rel_list);
 
index 3ce924de1bb812b6d89dd32ebbf7a2191fcb1d10..adbfd884c3647998a0537e11c92086111dc8192e 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.10 2000/05/30 00:49:49 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.11 2000/09/12 21:06:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -54,3 +54,29 @@ get_actual_clauses(List *restrictinfo_list)
        }
        return result;
 }
+
+/*
+ * get_actual_join_clauses
+ *
+ * Extract clauses from 'restrictinfo_list', separating those that
+ * came from JOIN/ON conditions from those that didn't.
+ */
+void
+get_actual_join_clauses(List *restrictinfo_list,
+                                               List **joinquals, List **otherquals)
+{
+       List       *temp;
+
+       *joinquals = NIL;
+       *otherquals = NIL;
+
+       foreach(temp, restrictinfo_list)
+       {
+               RestrictInfo *clause = (RestrictInfo *) lfirst(temp);
+
+               if (clause->isjoinqual)
+                       *joinquals = lappend(*joinquals, clause->clause);
+               else
+                       *otherquals = lappend(*otherquals, clause->clause);
+       }
+}
index bed7be7f08a583c600001613bb56255b4b059d95..ec9f9dafd0b4f92d84905f3ba7e6f4f36511d27a 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.26 2000/04/12 17:15:24 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.27 2000/09/12 21:06:59 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "postgres.h"
 
+#include "nodes/plannodes.h"
 #include "optimizer/clauses.h"
 #include "optimizer/var.h"
 
 
+typedef struct
+{
+       List       *varlist;
+       int                     sublevels_up;
+} pull_varnos_context;
+
 typedef struct
 {
        List       *varlist;
        bool            includeUpperVars;
 } pull_var_clause_context;
 
-static bool pull_varnos_walker(Node *node, List **listptr);
+static bool pull_varnos_walker(Node *node,
+                                                          pull_varnos_context *context);
 static bool contain_var_clause_walker(Node *node, void *context);
 static bool pull_var_clause_walker(Node *node,
                                           pull_var_clause_context *context);
@@ -35,21 +43,39 @@ static bool pull_var_clause_walker(Node *node,
 /*
  *             pull_varnos
  *
- *             Create a list of all the distinct varnos present in a parsetree
- *             (tlist or qual).  Note that only varnos attached to level-zero
- *             Vars are considered --- upper Vars refer to some other rtable!
+ *             Create a list of all the distinct varnos present in a parsetree.
+ *             Only varnos that reference level-zero rtable entries are considered.
+ *
+ * NOTE: unlike other routines in this file, pull_varnos() is used on
+ * not-yet-planned expressions.  It may therefore find bare SubLinks,
+ * and if so it needs to recurse into them to look for uplevel references
+ * to the desired rtable level!  But when we find a completed SubPlan,
+ * we only need to look at the parameters passed to the subplan.
  */
 List *
 pull_varnos(Node *node)
 {
-       List       *result = NIL;
+       pull_varnos_context context;
+
+       context.varlist = NIL;
+       context.sublevels_up = 0;
+
+       /*
+        * Must be prepared to start with a Query or a bare expression tree;
+        * if it's a Query, go straight to query_tree_walker to make sure that
+        * sublevels_up doesn't get incremented prematurely.
+        */
+       if (node && IsA(node, Query))
+               query_tree_walker((Query *) node, pull_varnos_walker,
+                                                 (void *) &context);
+       else
+               pull_varnos_walker(node, &context);
 
-       pull_varnos_walker(node, &result);
-       return result;
+       return context.varlist;
 }
 
 static bool
-pull_varnos_walker(Node *node, List **listptr)
+pull_varnos_walker(Node *node, pull_varnos_context *context)
 {
        if (node == NULL)
                return false;
@@ -57,11 +83,42 @@ pull_varnos_walker(Node *node, List **listptr)
        {
                Var                *var = (Var *) node;
 
-               if (var->varlevelsup == 0 && !intMember(var->varno, *listptr))
-                       *listptr = lconsi(var->varno, *listptr);
+               if (var->varlevelsup == context->sublevels_up &&
+                       !intMember(var->varno, context->varlist))
+                       context->varlist = lconsi(var->varno, context->varlist);
+               return false;
+       }
+       if (is_subplan(node))
+       {
+               /*
+                * Already-planned subquery.  Examine the args list (parameters
+                * to be passed to subquery), as well as the "oper" list which
+                * is executed by the outer query.  But short-circuit recursion into
+                * the subquery itself, which would be a waste of effort.
+                */
+               Expr       *expr = (Expr *) node;
+
+               if (pull_varnos_walker((Node*) ((SubPlan*) expr->oper)->sublink->oper,
+                                                          context))
+                       return true;
+               if (pull_varnos_walker((Node *) expr->args,
+                                                          context))
+                       return true;
                return false;
        }
-       return expression_tree_walker(node, pull_varnos_walker, (void *) listptr);
+       if (IsA(node, Query))
+       {
+               /* Recurse into not-yet-planned subquery */
+               bool            result;
+
+               context->sublevels_up++;
+               result = query_tree_walker((Query *) node, pull_varnos_walker,
+                                                                  (void *) context);
+               context->sublevels_up--;
+               return result;
+       }
+       return expression_tree_walker(node, pull_varnos_walker,
+                                                                 (void *) context);
 }
 
 /*
index 6ef084d04b6a63d61440b6e26c44c1b812f815d9..68fed79d382891ef920fb6ae5efb92ee1e5ab220 100644 (file)
@@ -2,7 +2,7 @@
 #
 # Makefile for parser
 #
-# $Header: /cvsroot/pgsql/src/backend/parser/Makefile,v 1.29 2000/08/28 11:53:19 petere Exp $
+# $Header: /cvsroot/pgsql/src/backend/parser/Makefile,v 1.30 2000/09/12 21:07:00 tgl Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -31,7 +31,7 @@ $(srcdir)/gram.c $(srcdir)/parse.h: gram.y
 
 $(srcdir)/scan.c: scan.l
 ifdef FLEX
-       $(FLEX) $(FLEXFLAGS) -o'$@' $<
+       $(FLEX) $(FLEXFLAGS) -Pbase_yy -o'$@' $<
 else
        @$(missing) flex $< $@
 endif
index fe21804a2c46ac336a0d2b09ebea5657c0b665ac..0165ef15c215f211ab2454b094543630843e672d 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $Id: analyze.c,v 1.156 2000/08/29 04:20:44 momjian Exp $
+ *     $Id: analyze.c,v 1.157 2000/09/12 21:07:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -25,6 +25,7 @@
 #include "parser/parse_relation.h"
 #include "parser/parse_target.h"
 #include "parser/parse_type.h"
+#include "rewrite/rewriteManip.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
 #include "utils/relcache.h"
@@ -54,6 +55,8 @@ static void transformConstraintAttrs(List *constraintList);
 static void transformColumnType(ParseState *pstate, ColumnDef *column);
 static void transformFkeyCheckAttrs(FkConstraint *fkconstraint);
 
+static void release_pstate_resources(ParseState *pstate);
+
 /* kluge to return extra info from transformCreateStmt() */
 static List *extras_before;
 static List *extras_after;
@@ -71,28 +74,22 @@ List *
 parse_analyze(List *pl, ParseState *parentParseState)
 {
        List       *result = NIL;
-       ParseState *pstate;
-       Query      *parsetree;
 
        while (pl != NIL)
        {
+               ParseState *pstate = make_parsestate(parentParseState);
+               Query      *parsetree;
+
                extras_before = extras_after = NIL;
-               pstate = make_parsestate(parentParseState);
 
                parsetree = transformStmt(pstate, lfirst(pl));
-               if (pstate->p_target_relation != NULL)
-                       heap_close(pstate->p_target_relation, AccessShareLock);
-               pstate->p_target_relation = NULL;
-               pstate->p_target_rangetblentry = NULL;
+               release_pstate_resources(pstate);
 
                while (extras_before != NIL)
                {
                        result = lappend(result,
-                                                  transformStmt(pstate, lfirst(extras_before)));
-                       if (pstate->p_target_relation != NULL)
-                               heap_close(pstate->p_target_relation, AccessShareLock);
-                       pstate->p_target_relation = NULL;
-                       pstate->p_target_rangetblentry = NULL;
+                                                        transformStmt(pstate, lfirst(extras_before)));
+                       release_pstate_resources(pstate);
                        extras_before = lnext(extras_before);
                }
 
@@ -102,10 +99,7 @@ parse_analyze(List *pl, ParseState *parentParseState)
                {
                        result = lappend(result,
                                                         transformStmt(pstate, lfirst(extras_after)));
-                       if (pstate->p_target_relation != NULL)
-                               heap_close(pstate->p_target_relation, AccessShareLock);
-                       pstate->p_target_relation = NULL;
-                       pstate->p_target_rangetblentry = NULL;
+                       release_pstate_resources(pstate);
                        extras_after = lnext(extras_after);
                }
 
@@ -116,6 +110,15 @@ parse_analyze(List *pl, ParseState *parentParseState)
        return result;
 }
 
+static void
+release_pstate_resources(ParseState *pstate)
+{
+       if (pstate->p_target_relation != NULL)
+               heap_close(pstate->p_target_relation, AccessShareLock);
+       pstate->p_target_relation = NULL;
+       pstate->p_target_rangetblentry = NULL;
+}
+
 /*
  * transformStmt -
  *       transform a Parse tree. If it is an optimizable statement, turn it
@@ -176,11 +179,11 @@ transformStmt(ParseState *pstate, Node *parseTree)
                                                Resdom     *rd;
 
                                                id = nth(i, n->aliases);
-                                               Assert(nodeTag(id) == T_Ident);
+                                               Assert(IsA(id, Ident));
                                                te = nth(i, targetList);
-                                               Assert(nodeTag(te) == T_TargetEntry);
+                                               Assert(IsA(te, TargetEntry));
                                                rd = te->resdom;
-                                               Assert(nodeTag(rd) == T_Resdom);
+                                               Assert(IsA(rd, Resdom));
                                                rd->resname = pstrdup(id->name);
                                        }
                                }
@@ -290,15 +293,17 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
        qry->commandType = CMD_DELETE;
 
        /* set up a range table */
-       makeRangeTable(pstate, NULL);
-       setTargetTable(pstate, stmt->relname, stmt->inh);
+       makeRangeTable(pstate, NIL);
+       setTargetTable(pstate, stmt->relname, stmt->inh, true);
 
        qry->distinctClause = NIL;
 
        /* fix where clause */
        qry->qual = transformWhereClause(pstate, stmt->whereClause);
 
+       /* done building the rtable */
        qry->rtable = pstate->p_rtable;
+       qry->jointree = pstate->p_jointree;
        qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
 
        qry->hasSubLinks = pstate->p_hasSubLinks;
@@ -387,12 +392,14 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
         *
         * In particular, it's time to add the INSERT target to the rangetable.
         * (We didn't want it there until now since it shouldn't be visible in
-        * the SELECT part.)
+        * the SELECT part.)  Note that the INSERT target is NOT added to the
+        * join tree, since we don't want to join over it.
         */
-       setTargetTable(pstate, stmt->relname, FALSE);
+       setTargetTable(pstate, stmt->relname, false, false);
 
        /* now the range table will not change */
        qry->rtable = pstate->p_rtable;
+       qry->jointree = pstate->p_jointree;
        qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
 
        /* Prepare to assign non-conflicting resnos to resjunk attributes */
@@ -908,7 +915,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
        while (dlist != NIL)
        {
                constraint = lfirst(dlist);
-               Assert(nodeTag(constraint) == T_Constraint);
+               Assert(IsA(constraint, Constraint));
                Assert((constraint->contype == CONSTR_PRIMARY)
                           || (constraint->contype == CONSTR_UNIQUE));
 
@@ -1427,17 +1434,68 @@ static Query *
 transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
 {
        Query      *qry;
-       Query      *action;
-       List       *actions;
+       RangeTblEntry *oldrte;
+       RangeTblEntry *newrte;
 
        qry = makeNode(Query);
        qry->commandType = CMD_UTILITY;
+       qry->utilityStmt = (Node *) stmt;
+
+       /*
+        * NOTE: 'OLD' must always have a varno equal to 1 and 'NEW'
+        * equal to 2.  Set up their RTEs in the main pstate for use
+        * in parsing the rule qualification.
+        */
+       Assert(pstate->p_rtable == NIL);
+       oldrte = addRangeTableEntry(pstate, stmt->object->relname,
+                                                               makeAttr("*OLD*", NULL),
+                                                               false, true);
+       newrte = addRangeTableEntry(pstate, stmt->object->relname,
+                                                               makeAttr("*NEW*", NULL),
+                                                               false, true);
+       /*
+        * They must be in the jointree too for lookup purposes, but only add
+        * the one(s) that are relevant for the current kind of rule.  In an
+        * UPDATE rule, quals must refer to OLD.field or NEW.field to be
+        * unambiguous, but there's no need to be so picky for INSERT & DELETE.
+        * (Note we marked the RTEs "inFromCl = true" above to allow unqualified
+        * references to their fields.)
+        */
+       switch (stmt->event)
+       {
+               case CMD_SELECT:
+                       addRTEtoJoinTree(pstate, oldrte);
+                       break;
+               case CMD_UPDATE:
+                       addRTEtoJoinTree(pstate, oldrte);
+                       addRTEtoJoinTree(pstate, newrte);
+                       break;
+               case CMD_INSERT:
+                       addRTEtoJoinTree(pstate, newrte);
+                       break;
+               case CMD_DELETE:
+                       addRTEtoJoinTree(pstate, oldrte);
+                       break;
+               default:
+                       elog(ERROR, "transformRuleStmt: unexpected event type %d",
+                                (int) stmt->event);
+                       break;
+       }
+
+       /* take care of the where clause */
+       stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
+
+       if (length(pstate->p_rtable) != 2) /* naughty, naughty... */
+               elog(ERROR, "Rule WHERE condition may not contain references to other relations");
+
+       /* save info about sublinks in where clause */
+       qry->hasSubLinks = pstate->p_hasSubLinks;
 
        /*
-        * 'instead nothing' rules with a qualification need a query a
+        * 'instead nothing' rules with a qualification need a query
         * rangetable so the rewrite handler can add the negated rule
         * qualification to the original query. We create a query with the new
-        * command type CMD_NOTHING here that is treated special by the
+        * command type CMD_NOTHING here that is treated specially by the
         * rewrite system.
         */
        if (stmt->actions == NIL)
@@ -1445,54 +1503,95 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
                Query      *nothing_qry = makeNode(Query);
 
                nothing_qry->commandType = CMD_NOTHING;
-
-               addRangeTableEntry(pstate, stmt->object->relname,
-                                                  makeAttr("*OLD*", NULL),
-                                                  FALSE, FALSE, FALSE);
-               addRangeTableEntry(pstate, stmt->object->relname,
-                                                  makeAttr("*NEW*", NULL),
-                                                  FALSE, FALSE, FALSE);
-
                nothing_qry->rtable = pstate->p_rtable;
+               nothing_qry->jointree = NIL; /* no join actually wanted */
 
                stmt->actions = lappend(NIL, nothing_qry);
        }
-
-       actions = stmt->actions;
-
-       /*
-        * transform each statment, like parse_analyze()
-        */
-       while (actions != NIL)
+       else
        {
+               List       *actions;
 
                /*
-                * NOTE: 'OLD' must always have a varno equal to 1 and 'NEW'
-                * equal to 2.
+                * transform each statement, like parse_analyze()
                 */
-               addRangeTableEntry(pstate, stmt->object->relname,
-                                                  makeAttr("*OLD*", NULL),
-                                                  FALSE, FALSE, FALSE);
-               addRangeTableEntry(pstate, stmt->object->relname,
-                                                  makeAttr("*NEW*", NULL),
-                                                  FALSE, FALSE, FALSE);
-
-               pstate->p_last_resno = 1;
-               pstate->p_is_rule = true;               /* for expand all */
-               pstate->p_hasAggs = false;
-
-               action = (Query *) lfirst(actions);
-               if (action->commandType != CMD_NOTHING)
-                       lfirst(actions) = transformStmt(pstate, lfirst(actions));
-               actions = lnext(actions);
-       }
+               foreach(actions, stmt->actions)
+               {
+                       ParseState *sub_pstate = make_parsestate(pstate->parentParseState);
+                       Query      *sub_qry;
+                       bool            has_old,
+                                               has_new;
 
-       /* take care of the where clause */
-       stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
+                       /*
+                        * Set up OLD/NEW in the rtable for this statement.  The entries
+                        * are marked not inFromCl because we don't want them to be
+                        * referred to by unqualified field names nor "*" in the rule
+                        * actions.  We don't need to add them to the jointree for
+                        * qualified-name lookup, either (see qualifiedNameToVar()).
+                        */
+                       oldrte = addRangeTableEntry(sub_pstate, stmt->object->relname,
+                                                                               makeAttr("*OLD*", NULL),
+                                                                               false, false);
+                       newrte = addRangeTableEntry(sub_pstate, stmt->object->relname,
+                                                                               makeAttr("*NEW*", NULL),
+                                                                               false, false);
 
-       qry->hasSubLinks = pstate->p_hasSubLinks;
+                       /* Transform the rule action statement */
+                       sub_qry = transformStmt(sub_pstate, lfirst(actions));
+
+                       /*
+                        * Validate action's use of OLD/NEW, qual too
+                        */
+                       has_old =
+                               rangeTableEntry_used((Node *) sub_qry, PRS2_OLD_VARNO, 0) ||
+                               rangeTableEntry_used(stmt->whereClause, PRS2_OLD_VARNO, 0);
+                       has_new =
+                               rangeTableEntry_used((Node *) sub_qry, PRS2_NEW_VARNO, 0) ||
+                               rangeTableEntry_used(stmt->whereClause, PRS2_NEW_VARNO, 0);
+
+                       switch (stmt->event)
+                       {
+                               case CMD_SELECT:
+                                       if (has_old)
+                                               elog(ERROR, "ON SELECT rule may not use OLD");
+                                       if (has_new)
+                                               elog(ERROR, "ON SELECT rule may not use NEW");
+                                       break;
+                               case CMD_UPDATE:
+                                       /* both are OK */
+                                       break;
+                               case CMD_INSERT:
+                                       if (has_old)
+                                               elog(ERROR, "ON INSERT rule may not use OLD");
+                                       break;
+                               case CMD_DELETE:
+                                       if (has_new)
+                                               elog(ERROR, "ON DELETE rule may not use NEW");
+                                       break;
+                               default:
+                                       elog(ERROR, "transformRuleStmt: unexpected event type %d",
+                                                (int) stmt->event);
+                                       break;
+                       }
+
+                       /*
+                        * For efficiency's sake, add OLD to the rule action's jointree
+                        * only if it was actually referenced in the statement or qual.
+                        * NEW is not really a relation and should never be added.
+                        */
+                       if (has_old)
+                       {
+                               addRTEtoJoinTree(sub_pstate, oldrte);
+                               sub_qry->jointree = sub_pstate->p_jointree;
+                       }
+
+                       lfirst(actions) = sub_qry;
+
+                       release_pstate_resources(sub_pstate);
+                       pfree(sub_pstate);
+               }
+       }
 
-       qry->utilityStmt = (Node *) stmt;
        return qry;
 }
 
@@ -1558,6 +1657,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
        qry->intersectClause = stmt->intersectClause;
 
        qry->rtable = pstate->p_rtable;
+       qry->jointree = pstate->p_jointree;
 
        if (stmt->forUpdate != NULL)
                transformForUpdate(qry, stmt->forUpdate);
@@ -1585,17 +1685,17 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
         * do this with REPLACE in POSTQUEL so we keep the feature.
         */
        makeRangeTable(pstate, stmt->fromClause);
-       setTargetTable(pstate, stmt->relname, stmt->inh);
+       setTargetTable(pstate, stmt->relname, stmt->inh, true);
 
        qry->targetList = transformTargetList(pstate, stmt->targetList);
 
        qry->qual = transformWhereClause(pstate, stmt->whereClause);
 
-       qry->hasSubLinks = pstate->p_hasSubLinks;
-
        qry->rtable = pstate->p_rtable;
+       qry->jointree = pstate->p_jointree;
        qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
 
+       qry->hasSubLinks = pstate->p_hasSubLinks;
        qry->hasAggs = pstate->p_hasAggs;
        if (pstate->p_hasAggs)
                parseCheckAggregates(pstate, qry);
@@ -1689,7 +1789,7 @@ transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt)
                        transformColumnType(pstate, (ColumnDef *) stmt->def);
                        break;
                case 'C':
-                       if (stmt->def && nodeTag(stmt->def) == T_FkConstraint)
+                       if (stmt->def && IsA(stmt->def, FkConstraint))
                        {
                                CreateTrigStmt *fk_trigger;
                                List       *fk_attr;
@@ -2085,7 +2185,7 @@ transformForUpdate(Query *qry, List *forUpdate)
                        i++;
                }
                if (l2 == NULL)
-                       elog(ERROR, "FOR UPDATE: relation '%s' not found in FROM clause",
+                       elog(ERROR, "FOR UPDATE: relation \"%s\" not found in FROM clause",
                                 relname);
        }
 
index 7e970ab1871d6d50420d2ae0c7a505fd8afae19c..301be9eb9b957c787e604ba7f7a723aaee1622f5 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.188 2000/09/12 05:09:44 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.189 2000/09/12 21:07:01 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -36,6 +36,7 @@
 #include <ctype.h>
 
 #include "postgres.h"
+
 #include "access/htup.h"
 #include "access/xact.h"
 #include "catalog/catname.h"
@@ -77,15 +78,10 @@ static Node *makeA_Expr(int oper, char *opname, Node *lexpr, Node *rexpr);
 static Node *makeTypeCast(Node *arg, TypeName *typename);
 static Node *makeRowExpr(char *opr, List *largs, List *rargs);
 static void mapTargetColumns(List *source, List *target);
-static void param_type_init(Oid *typev, int nargs);
 static bool exprIsNullConstant(Node *arg);
 static Node *doNegate(Node *n);
 static void doNegateFloat(Value *v);
 
-/* old versions of flex define this as a macro */
-#if defined(yywrap)
-#undef yywrap
-#endif /* yywrap */
 %}
 
 
@@ -95,6 +91,7 @@ static void doNegateFloat(Value *v);
        char                            chr;
        char                            *str;
        bool                            boolean;
+       JoinType                        jtype;
        List                            *list;
        Node                            *node;
        Value                           *value;
@@ -108,7 +105,6 @@ static void doNegateFloat(Value *v);
        JoinExpr                        *jexpr;
        IndexElem                       *ielem;
        RangeVar                        *range;
-       RelExpr                         *relexp;
        A_Indices                       *aind;
        ResTarget                       *target;
        ParamNo                         *paramno;
@@ -194,19 +190,8 @@ static void doNegateFloat(Value *v);
 %type <boolean>        opt_table
 %type <boolean>        opt_chain, opt_trans
 
-%type <jexpr>  from_expr, join_clause, join_expr
-%type <jexpr>  join_clause_with_union, join_expr_with_union
 %type <node>   join_outer, join_qual
-%type <ival>   join_type
-%type <list>   using_list
-%type <ident>  using_expr
-/***
-#ifdef ENABLE_ORACLE_JOIN_SYNTAX
-%type <list>   oracle_list
-%type <jexpr>  oracle_expr
-%type <boolean>        oracle_outer
-#endif
-***/
+%type <jtype>  join_type
 
 %type <list>   extract_list, position_list
 %type <list>   substr_list, substr_from, substr_for, trim_list
@@ -246,8 +231,9 @@ static void doNegateFloat(Value *v);
 %type <attr>   event_object, attr, alias_clause
 %type <sortgroupby>            sortby
 %type <ielem>  index_elem, func_index
-%type <range>  table_expr
-%type <relexp> relation_expr
+%type <node>   table_ref
+%type <jexpr>  joined_table
+%type <range>  relation_expr
 %type <target> target_el, update_target_el
 %type <paramno> ParamNo
 
@@ -356,6 +342,12 @@ static void doNegateFloat(Value *v);
                TEMP, TOAST, TRUNCATE, TRUSTED, 
                UNLISTEN, UNTIL, VACUUM, VALID, VERBOSE, VERSION
 
+/* The grammar thinks these are keywords, but they are not in the keywords.c
+ * list and so can never be entered directly.  The filter in parser.c
+ * creates these tokens when required.
+ */
+%token                 UNIONJOIN
+
 /* Special keywords, not in the query language - see the "lex" file */
 %token <str>   IDENT, FCONST, SCONST, Op
 %token <ival>  ICONST, PARAM
@@ -364,7 +356,9 @@ static void doNegateFloat(Value *v);
 %token                 OP
 
 /* precedence: lowest to highest */
-%left          UNION INTERSECT EXCEPT
+%left          UNION EXCEPT
+%left          INTERSECT
+%left          JOIN UNIONJOIN CROSS LEFT FULL RIGHT INNER_P NATURAL
 %left          OR
 %left          AND
 %right         NOT
@@ -800,7 +794,7 @@ VariableSetStmt:  SET ColId TO var_value
                                        n->value = $3;
                                        $$ = (Node *) n;
 #else
-                                       elog(ERROR, "SET NAMES is not supported.");
+                                       elog(ERROR, "SET NAMES is not supported");
 #endif
                                }
                ;
@@ -1031,7 +1025,6 @@ AlterTableStmt:
                                        n->relname = $3;
                                        $$ = (Node *)n;
                                }
-
 /* ALTER TABLE <name> OWNER TO UserId */
                | ALTER TABLE relation_name OWNER TO UserId
                                {
@@ -2956,7 +2949,7 @@ CreatedbStmt:  CREATE DATABASE database_name WITH createdb_opt_location createdb
                                        CreatedbStmt *n;
 
                                        if ($5 == NULL && $6 == -1)
-                                               elog(ERROR, "CREATE DATABASE WITH requires at least one option.");
+                                               elog(ERROR, "CREATE DATABASE WITH requires at least one option");
 
                     n = makeNode(CreatedbStmt);
                                        n->dbname = $3;
@@ -3465,7 +3458,7 @@ SelectStmt:         select_clause sort_clause for_update_clause opt_select_limit
 /* This rule parses Select statements that can appear within set operations,
  * including UNION, INTERSECT and EXCEPT.  '(' and ')' can be used to specify
  * the ordering of the set operations.  Without '(' and ')' we want the
- * operations to be left associative.
+ * operations to be ordered per the precedence specs at the head of this file.
  *
  * Note that sort clauses cannot be included at this level --- a sort clause
  * can only appear at the end of the complete Select, and it will be handled
@@ -3486,10 +3479,12 @@ select_clause: '(' select_clause ')'
                        {
                                $$ = $1; 
                        }
-               | select_clause EXCEPT select_clause
+               | select_clause EXCEPT opt_all select_clause
                        {
                                $$ = (Node *)makeA_Expr(AND,NULL,$1,
-                                                                               makeA_Expr(NOT,NULL,NULL,$3));
+                                                                               makeA_Expr(NOT,NULL,NULL,$4));
+                               if ($3)
+                                       elog(ERROR, "EXCEPT ALL is not implemented yet");
                        }
                | select_clause UNION opt_all select_clause
                        {       
@@ -3506,9 +3501,11 @@ select_clause: '(' select_clause ')'
                                }
                                $$ = (Node *)makeA_Expr(OR,NULL,$1,$4);
                        }
-               | select_clause INTERSECT select_clause
+               | select_clause INTERSECT opt_all select_clause
                        {
-                               $$ = (Node *)makeA_Expr(AND,NULL,$1,$3);
+                               $$ = (Node *)makeA_Expr(AND,NULL,$1,$4);
+                               if ($3)
+                                       elog(ERROR, "INTERSECT ALL is not implemented yet");
                        }
                ; 
 
@@ -3741,113 +3738,63 @@ update_list:  OF va_list                                               { $$ = $2; }
  *****************************************************************************/
 
 from_clause:  FROM from_list                                   { $$ = $2; }
-/***
-#ifdef ENABLE_ORACLE_JOIN_SYNTAX
-               | FROM oracle_list                                              { $$ = $2; }
-#endif
-***/
-               | FROM from_expr                                                { $$ = lcons($2, NIL); }
                | /*EMPTY*/                                                             { $$ = NIL; }
                ;
 
-from_list:  from_list ',' table_expr                   { $$ = lappend($1, $3); }
-               | table_expr                                                    { $$ = lcons($1, NIL); }
-               ;
-
-/***********
- * This results in one shift/reduce conflict, presumably due to the trailing "(+)"
- * - Thomas 1999-09-20
- *
-#ifdef ENABLE_ORACLE_JOIN_SYNTAX
-oracle_list:  oracle_expr                                              { $$ = lcons($1, NIL); }
-               ;
-
-oracle_expr:  ColId ',' ColId oracle_outer
-                               {
-                                       elog(ERROR,"Oracle OUTER JOIN not yet supported");
-                                       $$ = NULL;
-                               }
-               | oracle_outer ColId ',' ColId
-                               {
-                                       elog(ERROR,"Oracle OUTER JOIN not yet supported");
-                                       $$ = NULL;
-                               }
-               ;
-
-oracle_outer:  '(' '+' ')'                                             { $$ = TRUE; }
-               ;
-#endif
-***********/
-
-from_expr:  '(' join_clause_with_union ')' alias_clause
-                               {
-                                       JoinExpr *j = $2;
-                                       j->alias = $4;
-                                       $$ = j;
-                               }
-               | join_clause
-                               {       $$ = $1; }
-               ;
-
-table_expr:  relation_expr alias_clause
-                               {
-                                       $$ = makeNode(RangeVar);
-                                       $$->relExpr = $1;
-                                       $$->name = $2;
-
-#ifdef DISABLE_JOIN_SYNTAX
-                                       if (($2 != NULL) && ($2->attrs != NULL))
-                                               elog(ERROR, "Column aliases in table expressions not yet supported");
-#endif
-                               }
+from_list:  from_list ',' table_ref                            { $$ = lappend($1, $3); }
+               | table_ref                                                             { $$ = lcons($1, NIL); }
                ;
 
-alias_clause:  AS ColId '(' name_list ')'
-                               {
-                                       $$ = makeNode(Attr);
-                                       $$->relname = $2;
-                                       $$->attrs = $4;
-                               }
-               | AS ColId
+/*
+ * table_ref is where an alias clause can be attached.  Note we cannot make
+ * alias_clause have an empty production because that causes parse conflicts
+ * between table_ref := '(' joined_table ')' alias_clause
+ * and joined_table := '(' joined_table ')'.  So, we must have the
+ * redundant-looking productions here instead.
+ */
+table_ref:  relation_expr
                                {
-                                       $$ = makeNode(Attr);
-                                       $$->relname = $2;
+                                       $$ = (Node *) $1;
                                }
-               | ColId '(' name_list ')'
+               | relation_expr alias_clause
                                {
-                                       $$ = makeNode(Attr);
-                                       $$->relname = $1;
-                                       $$->attrs = $3;
+                                       $1->name = $2;
+                                       $$ = (Node *) $1;
                                }
-               | ColId
+               | '(' select_clause ')'
                                {
-                                       $$ = makeNode(Attr);
-                                       $$->relname = $1;
+                                       RangeSubselect *n = makeNode(RangeSubselect);
+                                       n->subquery = $2;
+                                       n->name = NULL;
+                                       $$ = (Node *) n;
                                }
-               | /*EMPTY*/
+               | '(' select_clause ')' alias_clause
                                {
-                                       $$ = NULL;  /* no qualifiers */
+                                       RangeSubselect *n = makeNode(RangeSubselect);
+                                       n->subquery = $2;
+                                       n->name = $4;
+                                       $$ = (Node *) n;
                                }
-               ;
-
-/* A UNION JOIN is the same as a FULL OUTER JOIN which *omits*
- * all result rows which would have matched on an INNER JOIN.
- * Syntactically, must enclose the UNION JOIN in parens to avoid
- * conflicts with SELECT/UNION.
- */
-join_clause:  join_clause join_expr
+               | joined_table
                                {
-                                       $2->larg = (Node *)$1;
-                                       $$ = $2;
+                                       $$ = (Node *) $1;
                                }
-               | table_expr join_expr
+               | '(' joined_table ')' alias_clause
                                {
-                                       $2->larg = (Node *)$1;
-                                       $$ = $2;
+                                       $2->alias = $4;
+                                       $$ = (Node *) $2;
                                }
                ;
 
-/* This is everything but the left side of a join.
+/*
+ * It may seem silly to separate joined_table from table_ref, but there is
+ * method in SQL92's madness: if you don't do it this way you get reduce-
+ * reduce conflicts, because it's not clear to the parser generator whether
+ * to expect alias_clause after ')' or not.  For the same reason we must
+ * treat 'JOIN' and 'join_type JOIN' separately, rather than allowing
+ * join_type to expand to empty; if we try it, the parser generator can't
+ * figure out when to reduce an empty join_type right after table_ref.
+ *
  * Note that a CROSS JOIN is the same as an unqualified
  * INNER JOIN, and an INNER JOIN/ON has the same shape
  * but a qualification expression to limit membership.
@@ -3855,71 +3802,122 @@ join_clause:  join_clause join_expr
  * tables and the shape is determined by which columns are
  * in common. We'll collect columns during the later transformations.
  */
-join_expr:  join_type JOIN table_expr join_qual
+
+joined_table:  '(' joined_table ')'
+                               {
+                                       $$ = $2;
+                               }
+               | table_ref CROSS JOIN table_ref
                                {
+                                       /* CROSS JOIN is same as unqualified inner join */
                                        JoinExpr *n = makeNode(JoinExpr);
-                                       n->jointype = $1;
-                                       n->rarg = (Node *)$3;
-                                       n->quals = (List *)$4;
+                                       n->jointype = JOIN_INNER;
+                                       n->isNatural = FALSE;
+                                       n->larg = $1;
+                                       n->rarg = $4;
+                                       n->using = NIL;
+                                       n->quals = NULL;
                                        $$ = n;
                                }
-               | NATURAL join_type JOIN table_expr
+               | table_ref UNIONJOIN table_ref
                                {
+                                       /* UNION JOIN is made into 1 token to avoid shift/reduce
+                                        * conflict against regular UNION keyword.
+                                        */
                                        JoinExpr *n = makeNode(JoinExpr);
-                                       n->jointype = $2;
-                                       n->isNatural = TRUE;
-                                       n->rarg = (Node *)$4;
-                                       n->quals = NULL; /* figure out which columns later... */
+                                       n->jointype = JOIN_UNION;
+                                       n->isNatural = FALSE;
+                                       n->larg = $1;
+                                       n->rarg = $3;
+                                       n->using = NIL;
+                                       n->quals = NULL;
                                        $$ = n;
                                }
-               | CROSS JOIN table_expr
+               | table_ref join_type JOIN table_ref join_qual
                                {
                                        JoinExpr *n = makeNode(JoinExpr);
-                                       n->jointype = INNER_P;
+                                       n->jointype = $2;
                                        n->isNatural = FALSE;
-                                       n->rarg = (Node *)$3;
-                                       n->quals = NULL;
+                                       n->larg = $1;
+                                       n->rarg = $4;
+                                       if ($5 != NULL && IsA($5, List))
+                                               n->using = (List *) $5; /* USING clause */
+                                       else
+                                               n->quals = $5; /* ON clause */
                                        $$ = n;
                                }
-               ;
-
-join_clause_with_union:  join_clause_with_union join_expr_with_union
+               | table_ref JOIN table_ref join_qual
                                {
-                                       $2->larg = (Node *)$1;
-                                       $$ = $2;
+                                       /* letting join_type reduce to empty doesn't work */
+                                       JoinExpr *n = makeNode(JoinExpr);
+                                       n->jointype = JOIN_INNER;
+                                       n->isNatural = FALSE;
+                                       n->larg = $1;
+                                       n->rarg = $3;
+                                       if ($4 != NULL && IsA($4, List))
+                                               n->using = (List *) $4; /* USING clause */
+                                       else
+                                               n->quals = $4; /* ON clause */
+                                       $$ = n;
                                }
-               | table_expr join_expr_with_union
+               | table_ref NATURAL join_type JOIN table_ref
                                {
-                                       $2->larg = (Node *)$1;
-                                       $$ = $2;
+                                       JoinExpr *n = makeNode(JoinExpr);
+                                       n->jointype = $3;
+                                       n->isNatural = TRUE;
+                                       n->larg = $1;
+                                       n->rarg = $5;
+                                       n->using = NIL; /* figure out which columns later... */
+                                       n->quals = NULL; /* fill later */
+                                       $$ = n;
                                }
-               ;
-
-join_expr_with_union:  join_expr
-                               {       $$ = $1; }
-               | UNION JOIN table_expr
+               | table_ref NATURAL JOIN table_ref
                                {
+                                       /* letting join_type reduce to empty doesn't work */
                                        JoinExpr *n = makeNode(JoinExpr);
-                                       n->jointype = UNION;
-                                       n->rarg = (Node *)$3;
-                                       n->quals = NULL;
+                                       n->jointype = JOIN_INNER;
+                                       n->isNatural = TRUE;
+                                       n->larg = $1;
+                                       n->rarg = $4;
+                                       n->using = NIL; /* figure out which columns later... */
+                                       n->quals = NULL; /* fill later */
                                        $$ = n;
+                               }
+               ;
 
-                                       elog(ERROR,"UNION JOIN not yet implemented");
+alias_clause:  AS ColId '(' name_list ')'
+                               {
+                                       $$ = makeNode(Attr);
+                                       $$->relname = $2;
+                                       $$->attrs = $4;
+                               }
+               | AS ColId
+                               {
+                                       $$ = makeNode(Attr);
+                                       $$->relname = $2;
+                               }
+               | ColId '(' name_list ')'
+                               {
+                                       $$ = makeNode(Attr);
+                                       $$->relname = $1;
+                                       $$->attrs = $3;
+                               }
+               | ColId
+                               {
+                                       $$ = makeNode(Attr);
+                                       $$->relname = $1;
                                }
                ;
 
-/* OUTER is just noise... */
-join_type:  FULL join_outer                                            { $$ = FULL; }
-               | LEFT join_outer                                               { $$ = LEFT; }
-               | RIGHT join_outer                                              { $$ = RIGHT; }
-               | OUTER_P                                                               { $$ = LEFT; }
-               | INNER_P                                                               { $$ = INNER_P; }
-               | /*EMPTY*/                                                             { $$ = INNER_P; }
+join_type:  FULL join_outer                                            { $$ = JOIN_FULL; }
+               | LEFT join_outer                                               { $$ = JOIN_LEFT; }
+               | RIGHT join_outer                                              { $$ = JOIN_RIGHT; }
+               | INNER_P                                                               { $$ = JOIN_INNER; }
                ;
 
+/* OUTER is just noise... */
 join_outer:  OUTER_P                                                   { $$ = NULL; }
-               | /*EMPTY*/                                                             { $$ = NULL;  /* no qualifiers */ }
+               | /*EMPTY*/                                                             { $$ = NULL; }
                ;
 
 /* JOIN qualification clauses
@@ -3927,60 +3925,43 @@ join_outer:  OUTER_P                                                    { $$ = NULL; }
  *  USING ( column list ) allows only unqualified column names,
  *                        which must match between tables.
  *  ON expr allows more general qualifications.
- * - thomas 1999-01-07
+ *
+ * We return USING as a List node, while an ON-expr will not be a List.
  */
 
-join_qual:  USING '(' using_list ')'                   { $$ = (Node *)$3; }
-               | ON a_expr                                                             { $$ = (Node *)$2; }
+join_qual:  USING '(' name_list ')'                            { $$ = (Node *) $3; }
+               | ON a_expr                                                             { $$ = $2; }
                ;
 
-using_list:  using_list ',' using_expr                 { $$ = lappend($1, $3); }
-               | using_expr                                                    { $$ = lcons($1, NIL); }
-               ;
-
-using_expr:  ColId
-                               {
-                                       /* could be a column name or a relation_name */
-                                       Ident *n = makeNode(Ident);
-                                       n->name = $1;
-                                       n->indirection = NULL;
-                                       $$ = n;
-                               }
-               ;
-
-where_clause:  WHERE a_expr                                            { $$ = $2; }
-               | /*EMPTY*/                                                             { $$ = NULL;  /* no qualifiers */ }
-               ;
 
 relation_expr: relation_name
                                {
                                /* default inheritance */
-                                       $$ = makeNode(RelExpr);
+                                       $$ = makeNode(RangeVar);
                                        $$->relname = $1;
                                        $$->inh = SQL_inheritance;
+                                       $$->name = NULL;
                                }
                | relation_name '*'                             %prec '='
                                {
                                        /* inheritance query */
-                                       $$ = makeNode(RelExpr);
+                                       $$ = makeNode(RangeVar);
                                        $$->relname = $1;
                                        $$->inh = TRUE;
+                                       $$->name = NULL;
                                }
                | ONLY relation_name                    %prec '='
                                {
                                        /* no inheritance */
-                                       $$ = makeNode(RelExpr);
+                                       $$ = makeNode(RangeVar);
                                        $$->relname = $2;
                                        $$->inh = FALSE;
+                                       $$->name = NULL;
                 }
                ;
 
-opt_array_bounds:      '[' ']' opt_array_bounds
-                               {  $$ = lcons(makeInteger(-1), $3); }
-               | '[' Iconst ']' opt_array_bounds
-                               {  $$ = lcons(makeInteger($2), $4); }
-               | /*EMPTY*/
-                               {  $$ = NIL; }
+where_clause:  WHERE a_expr                                            { $$ = $2; }
+               | /*EMPTY*/                                                             { $$ = NULL;  /* no qualifiers */ }
                ;
 
 
@@ -4023,6 +4004,14 @@ Typename:  SimpleTypename opt_array_bounds
                                }
                ;
 
+opt_array_bounds:      '[' ']' opt_array_bounds
+                               {  $$ = lcons(makeInteger(-1), $3); }
+               | '[' Iconst ']' opt_array_bounds
+                               {  $$ = lcons(makeInteger($2), $4); }
+               | /*EMPTY*/
+                               {  $$ = NIL; }
+               ;
+
 SimpleTypename:  ConstTypename
                | ConstInterval
                ;
@@ -6024,29 +6013,19 @@ xlateSqlType(char *name)
 
 void parser_init(Oid *typev, int nargs)
 {
+       saved_relname[0] = '\0';
        QueryIsRule = FALSE;
-       saved_relname[0]= '\0';
-
-       param_type_init(typev, nargs);
-}
-
-
-/*
- * param_type_init()
- *
- * Keep enough information around to fill out the type of param nodes
- * used in postquel functions
- */
-static void
-param_type_init(Oid *typev, int nargs)
-{
-       pfunc_num_args = nargs;
+       /*
+        * Keep enough information around to fill out the type of param nodes
+        * used in postquel functions
+        */
        param_type_info = typev;
+       pfunc_num_args = nargs;
 }
 
 Oid param_type(int t)
 {
-       if ((t > pfunc_num_args) || (t == 0))
+       if ((t > pfunc_num_args) || (t <= 0))
                return InvalidOid;
        return param_type_info[t - 1];
 }
index bbc8f5c70764e37c987008267136bbc772b079b8..955be022e4e235b7ad401dc9a64ffec798880c48 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.39 2000/07/17 03:05:02 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.40 2000/09/12 21:07:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -152,6 +152,11 @@ parseCheckAggregates(ParseState *pstate, Query *qry)
         */
        if (contain_agg_clause(qry->qual))
                elog(ERROR, "Aggregates not allowed in WHERE clause");
+       /*
+        * ON-conditions in JOIN expressions are like WHERE clauses.
+        */
+       if (contain_agg_clause((Node *) qry->jointree))
+               elog(ERROR, "Aggregates not allowed in JOIN conditions");
 
        /*
         * No aggregates allowed in GROUP BY clauses, either.
index 3f874cc9643d3b900ab3853171ef7b29e2ea8198..c35b41b911b27a577f47859bec860ea80a153803 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.65 2000/06/15 03:32:19 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.66 2000/09/12 21:07:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,7 +18,9 @@
 #include "access/heapam.h"
 #include "optimizer/tlist.h"
 #include "nodes/makefuncs.h"
+#include "parser/analyze.h"
 #include "parser/parse.h"
+#include "parser/parsetree.h"
 #include "parser/parse_clause.h"
 #include "parser/parse_coerce.h"
 #include "parser/parse_expr.h"
 
 static char *clauseText[] = {"ORDER BY", "GROUP BY", "DISTINCT ON"};
 
+static void extractUniqueColumns(List *common_colnames,
+                                                                List *src_colnames, List *src_colvars,
+                                                                List **res_colnames, List **res_colvars);
+static Node *transformUsingClause(ParseState *pstate,
+                                                                 List *leftVars, List *rightVars);
+static RangeTblRef *transformTableEntry(ParseState *pstate, RangeVar *r);
+static RangeTblRef *transformRangeSubselect(ParseState *pstate,
+                                                                                       RangeSubselect *r);
+static Node *transformFromClauseItem(ParseState *pstate, Node *n);
 static TargetEntry *findTargetlistEntry(ParseState *pstate, Node *node,
                                        List *tlist, int clause);
-static void parseFromClause(ParseState *pstate, List *frmList);
-static RangeTblEntry *transformTableEntry(ParseState *pstate, RangeVar *r);
 static List *addTargetToSortList(TargetEntry *tle, List *sortlist,
                                        List *targetlist, char *opname);
 static bool exprIsInSortList(Node *expr, List *sortList, List *targetList);
 
-#ifndef DISABLE_OUTER_JOINS
-static List *transformUsingClause(ParseState *pstate, List *using,
-                                                                 List *left, List *right);
-#endif
-
 
 /*
  * makeRangeTable -
  *       Build the initial range table from the FROM clause.
+ *
+ * The range table constructed here may grow as we transform the expressions
+ * in the query's quals and target list. (Note that this happens because in
+ * POSTQUEL, we allow references to relations not specified in the
+ * from-clause.  PostgreSQL keeps this extension to standard SQL.)
+ *
+ * Note: we assume that pstate's p_rtable and p_jointree lists were
+ * initialized to NIL when the pstate was created.  We will add onto
+ * any entries already present --- this is needed for rule processing!
  */
 void
 makeRangeTable(ParseState *pstate, List *frmList)
 {
-       /* Currently, nothing to do except this: */
-       parseFromClause(pstate, frmList);
+       List       *fl;
+
+       /*
+        * The grammar will have produced a list of RangeVars, RangeSubselects,
+        * and/or JoinExprs. Transform each one, and then add it to the join tree.
+        */
+       foreach(fl, frmList)
+       {
+               Node       *n = lfirst(fl);
+
+               n = transformFromClauseItem(pstate, n);
+               pstate->p_jointree = lappend(pstate->p_jointree, n);
+       }
 }
 
 /*
  * setTargetTable
- *       Add the target relation of INSERT or UPDATE to the range table,
+ *       Add the target relation of INSERT/UPDATE/DELETE to the range table,
  *       and make the special links to it in the ParseState.
  *
- *       Note that the target is not marked as either inFromCl or inJoinSet.
+ *       inJoinSet says whether to add the target to the join tree.
  *       For INSERT, we don't want the target to be joined to; it's a
  *       destination of tuples, not a source.  For UPDATE/DELETE, we do
- *       need to scan or join the target.      This will happen without the
- *       inJoinSet flag because the planner's preprocess_targetlist()
- *       adds the destination's CTID attribute to the targetlist, and
- *       therefore the destination will be a referenced table even if
- *       there is no other use of any of its attributes.  Tricky, eh?
+ *       need to scan or join the target.
  */
 void
-setTargetTable(ParseState *pstate, char *relname, bool inh)
+setTargetTable(ParseState *pstate, char *relname, bool inh, bool inJoinSet)
 {
        RangeTblEntry *rte;
 
        /* look for relname only at current nesting level... */
        if (refnameRangeTablePosn(pstate, relname, NULL) == 0)
-               rte = addRangeTableEntry(pstate, relname,
-                                                                makeAttr(relname, NULL),
-                                                                inh, FALSE, FALSE);
+       {
+               rte = addRangeTableEntry(pstate, relname, NULL, inh, false);
+       }
        else
+       {
                rte = refnameRangeTableEntry(pstate, relname);
+               /* XXX what if pre-existing entry has wrong inh setting? */
+       }
+
+       if (inJoinSet)
+               addRTEtoJoinTree(pstate, rte);
 
        /* This could only happen for multi-action rules */
        if (pstate->p_target_relation != NULL)
@@ -95,625 +121,500 @@ setTargetTable(ParseState *pstate, char *relname, bool inh)
 }
 
 
-static Node *
-mergeInnerJoinQuals(ParseState *pstate, Node *clause)
-{
-       List       *jquals;
-
-       foreach(jquals, pstate->p_join_quals)
-       {
-               Node       *jqual = (Node *) lfirst(jquals);
-
-               if (clause == NULL)
-                       clause = jqual;
-               else
-               {
-                       A_Expr     *a = makeNode(A_Expr);
-
-                       a->oper = AND;
-                       a->opname = NULL;
-                       a->lexpr = clause;
-                       a->rexpr = jqual;
-                       clause = (Node *) a;
-               }
-       }
-
-       /* Make sure that we don't add same quals twice... */
-       pstate->p_join_quals = NIL;
-
-       return clause;
-}      /* mergeInnerJoinQuals() */
-
 /*
- * transformWhereClause -
- *       transforms the qualification and make sure it is of type Boolean
+ * Extract all not-in-common columns from column lists of a source table
  */
-Node *
-transformWhereClause(ParseState *pstate, Node *clause)
-{
-       Node       *qual;
-
-       if (pstate->p_join_quals != NIL)
-               clause = mergeInnerJoinQuals(pstate, clause);
-
-       if (clause == NULL)
-               return NULL;
-
-       pstate->p_in_where_clause = true;
-       qual = transformExpr(pstate, clause, EXPR_COLUMN_FIRST);
-       pstate->p_in_where_clause = false;
-
-       if (exprType(qual) != BOOLOID)
-       {
-               elog(ERROR, "WHERE clause must return type bool, not type %s",
-                        typeidTypeName(exprType(qual)));
-       }
-       return qual;
-}
-
-#ifndef DISABLE_JOIN_SYNTAX
-char *
-                       AttrString(Attr *attr);
-
-char *
-AttrString(Attr *attr)
-{
-       Value      *val;
-
-       Assert(length(attr->attrs) == 1);
-
-       val = lfirst(attr->attrs);
-
-       Assert(IsA(val, String));
-
-       return strVal(val);
-}
-
-List *
-                       ListTableAsAttrs(ParseState *pstate, char *table);
-List *
-ListTableAsAttrs(ParseState *pstate, char *table)
-{
-       Attr       *attr = expandTable(pstate, table, TRUE);
-       List       *rlist = NIL;
-       List       *col;
-
-       foreach(col, attr->attrs)
-       {
-               Attr       *a = makeAttr(table, strVal((Value *) lfirst(col)));
-
-               rlist = lappend(rlist, a);
-       }
-
-       return rlist;
-}
-
-List *
-                       makeUniqueAttrList(List *candidates, List *idents);
-List *
-makeUniqueAttrList(List *attrs, List *filter)
+static void
+extractUniqueColumns(List *common_colnames,
+                                        List *src_colnames, List *src_colvars,
+                                        List **res_colnames, List **res_colvars)
 {
-       List       *result = NULL;
-       List       *candidate;
+       List       *new_colnames = NIL;
+       List       *new_colvars = NIL;
+       List       *lnames,
+                          *lvars = src_colvars;
 
-       foreach(candidate, attrs)
+       foreach(lnames, src_colnames)
        {
-               List       *fmember;
-               bool            match = FALSE;
-               Attr       *cattr = lfirst(candidate);
+               char       *colname = strVal(lfirst(lnames));
+               bool            match = false;
+               List       *cnames;
 
-               Assert(IsA(cattr, Attr));
-               Assert(length(cattr->attrs) == 1);
-
-               foreach(fmember, filter)
+               foreach(cnames, common_colnames)
                {
-                       Attr       *fattr = lfirst(fmember);
-
-                       Assert(IsA(fattr, Attr));
-                       Assert(length(fattr->attrs) == 1);
+                       char       *ccolname = strVal(lfirst(cnames));
 
-                       if (strcmp(strVal(lfirst(cattr->attrs)), strVal(lfirst(fattr->attrs))) == 0)
+                       if (strcmp(colname, ccolname) == 0)
                        {
-                               match = TRUE;
+                               match = true;
                                break;
                        }
                }
 
                if (!match)
-                       result = lappend(result, cattr);
-       }
-
-       return result;
-}
-
-List *
-                       makeAttrList(Attr *attr);
-
-List *
-makeAttrList(Attr *attr)
-{
-       List       *result = NULL;
-
-       char       *name = attr->relname;
-       List       *col;
-
-       foreach(col, attr->attrs)
-       {
-               Attr       *newattr = makeAttr(name, strVal((Value *) lfirst(col)));
-
-               result = lappend(result, newattr);
-       }
-
-       return result;
-}
-#ifdef NOT_USED
-/* ExpandAttrs()
- * Take an existing attribute node and return a list of attribute nodes
- * with one attribute name per node.
- */
-List *
-ExpandAttrs(Attr *attr)
-{
-       List       *col;
-       char       *relname = attr->relname;
-       List       *rlist = NULL;
-
-       Assert(attr != NULL);
-
-       if ((attr->attrs == NULL) || (length(attr->attrs) <= 1))
-               return lcons(attr, NIL);
-
-       foreach(col, attr->attrs)
-       {
-               Attr       *attr = lfirst(col);
+               {
+                       new_colnames = lappend(new_colnames, lfirst(lnames));
+                       new_colvars = lappend(new_colvars, lfirst(lvars));
+               }
 
-               rlist = lappend(rlist, makeAttr(relname, AttrString(attr)));
+               lvars = lnext(lvars);
        }
 
-       return rlist;
+       *res_colnames = new_colnames;
+       *res_colvars = new_colvars;
 }
-#endif
 
 /* transformUsingClause()
- * Take an ON or USING clause from a join expression and expand if necessary.
- * Result is an implicitly-ANDed list of untransformed qualification clauses.
+ * Build a complete ON clause from a partially-transformed USING list.
+ * We are given lists of Var nodes representing left and right match columns.
+ * Result is a transformed qualification expression.
  */
-static List *
-transformUsingClause(ParseState *pstate, List *usingList,
-                                        List *leftList, List *rightList)
+static Node *
+transformUsingClause(ParseState *pstate, List *leftVars, List *rightVars)
 {
-       List       *result = NIL;
-       List       *using;
+       Node       *result = NULL;
+       List       *lvars,
+                          *rvars = rightVars;
 
-       foreach(using, usingList)
+       /*
+        * We cheat a little bit here by building an untransformed operator
+        * tree whose leaves are the already-transformed Vars.  This is OK
+        * because transformExpr() won't complain about already-transformed
+        * subnodes.
+        */
+       foreach(lvars, leftVars)
        {
-               Attr       *uattr = lfirst(using);
-               Attr       *lattr = NULL,
-                                  *rattr = NULL;
-               List       *col;
+               Node       *lvar = (Node *) lfirst(lvars);
+               Node       *rvar = (Node *) lfirst(rvars);
                A_Expr     *e;
 
-               /*
-                * find the first instances of this column in the shape list and
-                * the last table in the shape list...
-                */
-               foreach(col, leftList)
-               {
-                       Attr       *attr = lfirst(col);
+               e = makeNode(A_Expr);
+               e->oper = OP;
+               e->opname = "=";
+               e->lexpr = copyObject(lvar);
+               e->rexpr = copyObject(rvar);
 
-                       if (strcmp(AttrString(attr), AttrString(uattr)) == 0)
-                       {
-                               lattr = attr;
-                               break;
-                       }
-               }
-               foreach(col, rightList)
+               if (result == NULL)
+                       result = (Node *) e;
+               else
                {
-                       Attr       *attr = lfirst(col);
+                       A_Expr     *a = makeNode(A_Expr);
 
-                       if (strcmp(AttrString(attr), AttrString(uattr)) == 0)
-                       {
-                               rattr = attr;
-                               break;
-                       }
+                       a->oper = AND;
+                       a->opname = NULL;
+                       a->lexpr = result;
+                       a->rexpr = (Node *) e;
+                       result = (Node *) a;
                }
 
-               Assert((lattr != NULL) && (rattr != NULL));
+               rvars = lnext(rvars);
+       }
 
-               e = makeNode(A_Expr);
-               e->oper = OP;
-               e->opname = "=";
-               e->lexpr = (Node *) lattr;
-               e->rexpr = (Node *) rattr;
+       result = transformExpr(pstate, result, EXPR_COLUMN_FIRST);
 
-               result = lappend(result, e);
+       if (exprType(result) != BOOLOID)
+       {
+               /* This could only happen if someone defines a funny version of '=' */
+               elog(ERROR, "USING clause must return type bool, not type %s",
+                        typeidTypeName(exprType(result)));
        }
 
        return result;
 }      /* transformUsingClause() */
 
-#endif
-
 
-static RangeTblEntry *
+/*
+ * transformTableEntry --- transform a RangeVar (simple relation reference)
+ */
+static RangeTblRef *
 transformTableEntry(ParseState *pstate, RangeVar *r)
 {
-       RelExpr    *baserel = r->relExpr;
-       char       *relname = baserel->relname;
-
-#if 0
-       char       *refname;
-       List       *columns;
-
-#endif
+       char       *relname = r->relname;
        RangeTblEntry *rte;
-
-#if 0
-       if (r->name != NULL)
-               refname = r->name->relname;
-       else
-               refname = NULL;
-
-       columns = ListTableAsAttrs(pstate, relname);
-
-       /* alias might be specified... */
-       if (r->name != NULL)
-       {
-#ifndef DISABLE_JOIN_SYNTAX
-               if (length(columns) > 0)
-               {
-                       if (length(r->name->attrs) > 0)
-                       {
-                               if (length(columns) != length(r->name->attrs))
-                                       elog(ERROR, "'%s' has %d columns but %d %s specified",
-                                                relname, length(columns), length(r->name->attrs),
-                                                ((length(r->name->attrs) != 1) ? "aliases" : "alias"));
-
-                               aliasList = nconc(aliasList, r->name->attrs);
-                       }
-                       else
-                       {
-                               r->name->attrs = columns;
-
-                               aliasList = nconc(aliasList, r->name->attrs);
-                       }
-               }
-               else
-                       elog(NOTICE, "transformTableEntry: column aliases not handled (internal error)");
-#else
-               elog(ERROR, "Column aliases not yet supported");
-#endif
-       }
-       else
-       {
-               refname = relname;
-               aliasList = nconc(aliasList, columns);
-       }
-#endif
-
-       if (r->name == NULL)
-               r->name = makeAttr(relname, NULL);
+       RangeTblRef *rtr;
 
        /*
-        * marks this entry to indicate it comes from the FROM clause. In SQL,
+        * mark this entry to indicate it comes from the FROM clause. In SQL,
         * the target list can only refer to range variables specified in the
         * from clause but we follow the more powerful POSTQUEL semantics and
         * automatically generate the range variable if not specified. However
         * there are times we need to know whether the entries are legitimate.
-        *
-        * eg. select * from foo f where f.x = 1; will generate wrong answer if
-        * we expand * to foo.x.
         */
+       rte = addRangeTableEntry(pstate, relname, r->name, r->inh, true);
 
-       rte = addRangeTableEntry(pstate, relname, r->name,
-                                                        baserel->inh, TRUE, TRUE);
+       /*
+        * We create a RangeTblRef, but we do not add it to the jointree here.
+        * makeRangeTable will do so, if we are at top level of the FROM clause.
+        */
+       rtr = makeNode(RangeTblRef);
+       /* assume new rte is at end */
+       rtr->rtindex = length(pstate->p_rtable);
+       Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));
 
-       return rte;
-}      /* transformTableEntry() */
+       return rtr;
+}
 
 
 /*
- * parseFromClause -
- *       turns the table references specified in the from-clause into a
- *       range table. The range table may grow as we transform the expressions
- *       in the target list. (Note that this happens because in POSTQUEL, we
- *       allow references to relations not specified in the from-clause. We
- *       also allow now as an extension.)
- *
- * The FROM clause can now contain JoinExpr nodes, which contain parsing info
- * for inner and outer joins. The USING clause must be expanded into a qualification
- * for an inner join at least, since that is compatible with the old syntax.
- * Not sure yet how to handle outer joins, but it will become clear eventually?
- * - thomas 1998-12-16
+ * transformRangeSubselect --- transform a sub-SELECT appearing in FROM
  */
-static void
-parseFromClause(ParseState *pstate, List *frmList)
+static RangeTblRef *
+transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
 {
-       List       *fl;
+       SelectStmt *subquery = (SelectStmt *) r->subquery;
+       List       *parsetrees;
+       Query      *query;
 
-       foreach(fl, frmList)
-       {
-               Node       *n = lfirst(fl);
+       /*
+        * subquery node might not be SelectStmt if user wrote something like
+        * FROM (SELECT ... UNION SELECT ...).  Our current implementation of
+        * UNION/INTERSECT/EXCEPT is too messy to deal with here, so punt until
+        * we redesign querytrees to make it more reasonable.
+        */
+       if (subquery == NULL || !IsA(subquery, SelectStmt))
+               elog(ERROR, "Set operations not yet supported in subselects in FROM");
 
-               /*
-                * marks this entry to indicate it comes from the FROM clause. In
-                * SQL, the target list can only refer to range variables
-                * specified in the from clause but we follow the more powerful
-                * POSTQUEL semantics and automatically generate the range
-                * variable if not specified. However there are times we need to
-                * know whether the entries are legitimate.
-                *
-                * eg. select * from foo f where f.x = 1; will generate wrong answer
-                * if we expand * to foo.x.
-                */
+       /*
+        * Analyze and transform the subquery as if it were an independent
+        * statement (we do NOT want it to see the outer query as a parent).
+        */
+       parsetrees = parse_analyze(lcons(subquery, NIL), NULL);
 
-               /* Plain vanilla inner join, just like we've always had? */
-               if (IsA(n, RangeVar))
-                       transformTableEntry(pstate, (RangeVar *) n);
+       /*
+        * Check that we got something reasonable.  Some of these conditions
+        * are probably impossible given restrictions of the grammar, but
+        * check 'em anyway.
+        */
+       if (length(parsetrees) != 1)
+               elog(ERROR, "Unexpected parse analysis result for subselect in FROM");
+       query = (Query *) lfirst(parsetrees);
+       if (query == NULL || !IsA(query, Query))
+               elog(ERROR, "Unexpected parse analysis result for subselect in FROM");
 
-               /* A newfangled join expression? */
-               else if (IsA(n, JoinExpr))
-               {
-#ifndef DISABLE_JOIN_SYNTAX
-                       RangeTblEntry *l_rte,
-                                          *r_rte;
-                       Attr       *l_name,
-                                          *r_name = NULL;
-                       JoinExpr   *j = (JoinExpr *) n;
-
-                       if (j->alias != NULL)
-                               elog(ERROR, "JOIN table aliases are not supported");
-
-                       /* nested join? then handle the left one first... */
-                       if (IsA(j->larg, JoinExpr))
-                       {
-                               parseFromClause(pstate, lcons(j->larg, NIL));
-                               l_name = ((JoinExpr *) j->larg)->alias;
-                       }
-                       else
-                       {
-                               Assert(IsA(j->larg, RangeVar));
-                               l_rte = transformTableEntry(pstate, (RangeVar *) j->larg);
-                               l_name = expandTable(pstate, l_rte->eref->relname, TRUE);
-                       }
+       if (query->commandType != CMD_SELECT)
+               elog(ERROR, "Expected SELECT query from subselect in FROM");
+       if (query->resultRelation != 0 || query->into != NULL)
+               elog(ERROR, "Subselect in FROM may not have SELECT INTO");
 
-                       if (IsA(j->rarg, JoinExpr))
-                       {
-                               parseFromClause(pstate, lcons(j->rarg, NIL));
-                               l_name = ((JoinExpr *) j->larg)->alias;
-                       }
-                       else
-                       {
-                               Assert(IsA(j->rarg, RangeVar));
-                               r_rte = transformTableEntry(pstate, (RangeVar *) j->rarg);
-                               r_name = expandTable(pstate, r_rte->eref->relname, TRUE);
-                       }
 
-                       /*
-                        * Natural join does not explicitly specify columns; must
-                        * generate columns to join. Need to run through the list of
-                        * columns from each table or join result and match up the
-                        * column names. Use the first table, and check every column
-                        * in the second table for a match.
-                        */
-                       if (j->isNatural)
-                       {
-                               List       *lx,
-                                                  *rx;
-                               List       *rlist = NULL;
+       elog(ERROR, "Subselect in FROM not done yet");
 
-                               foreach(lx, l_name->attrs)
-                               {
-                                       Ident      *id = NULL;
-                                       Value      *l_col = lfirst(lx);
+       return NULL;
+}
 
-                                       Assert(IsA(l_col, String));
 
-                                       foreach(rx, r_name->attrs)
-                                       {
-                                               Value      *r_col = lfirst(rx);
+/*
+ * transformFromClauseItem -
+ *       Transform a FROM-clause item, adding any required entries to the
+ *       range table list being built in the ParseState, and return the
+ *       transformed item ready to include in the jointree list.
+ *       This routine can recurse to handle SQL92 JOIN expressions.
+ */
+static Node *
+transformFromClauseItem(ParseState *pstate, Node *n)
+{
+       if (IsA(n, RangeVar))
+       {
+               /* Plain relation reference */
+               return (Node *) transformTableEntry(pstate, (RangeVar *) n);
+       }
+       else if (IsA(n, RangeSubselect))
+       {
+               /* Plain relation reference */
+               return (Node *) transformRangeSubselect(pstate, (RangeSubselect *) n);
+       }
+       else if (IsA(n, JoinExpr))
+       {
+               /* A newfangled join expression */
+               JoinExpr   *j = (JoinExpr *) n;
+               List       *l_colnames,
+                                  *r_colnames,
+                                  *res_colnames,
+                                  *l_colvars,
+                                  *r_colvars,
+                                  *res_colvars;
 
-                                               Assert(IsA(r_col, String));
+               /*
+                * Recursively process the left and right subtrees
+                */
+               j->larg = transformFromClauseItem(pstate, j->larg);
+               j->rarg = transformFromClauseItem(pstate, j->rarg);
 
-                                               if (strcmp(strVal(l_col), strVal(r_col)) == 0)
-                                               {
-                                                       id = (Ident *) makeNode(Ident);
-                                                       id->name = strVal(l_col);
-                                                       break;
-                                               }
-                                       }
+               /*
+                * Extract column name and var lists from both subtrees
+                */
+               if (IsA(j->larg, JoinExpr))
+               {
+                       /* Make a copy of the subtree's lists so we can modify! */
+                       l_colnames = copyObject(((JoinExpr *) j->larg)->colnames);
+                       l_colvars = copyObject(((JoinExpr *) j->larg)->colvars);
+               }
+               else
+               {
+                       RangeTblEntry *rte;
 
-                                       /* right column matched? then keep as join column... */
-                                       if (id != NULL)
-                                               rlist = lappend(rlist, id);
-                               }
-                               j->quals = rlist;
+                       Assert(IsA(j->larg, RangeTblRef));
+                       rte = rt_fetch(((RangeTblRef *) j->larg)->rtindex,
+                                                  pstate->p_rtable);
+                       expandRTE(pstate, rte, &l_colnames, &l_colvars);
+                       /* expandRTE returns new lists, so no need for copyObject */
+               }
+               if (IsA(j->rarg, JoinExpr))
+               {
+                       /* Make a copy of the subtree's lists so we can modify! */
+                       r_colnames = copyObject(((JoinExpr *) j->rarg)->colnames);
+                       r_colvars = copyObject(((JoinExpr *) j->rarg)->colvars);
+               }
+               else
+               {
+                       RangeTblEntry *rte;
 
-                               printf("NATURAL JOIN columns are %s\n", nodeToString(rlist));
-                       }
+                       Assert(IsA(j->rarg, RangeTblRef));
+                       rte = rt_fetch(((RangeTblRef *) j->rarg)->rtindex,
+                                                  pstate->p_rtable);
+                       expandRTE(pstate, rte, &r_colnames, &r_colvars);
+                       /* expandRTE returns new lists, so no need for copyObject */
+               }
 
-                       if (j->jointype == INNER_P)
+               /*
+                * Natural join does not explicitly specify columns; must
+                * generate columns to join. Need to run through the list of
+                * columns from each table or join result and match up the
+                * column names. Use the first table, and check every column
+                * in the second table for a match.  (We'll check that the
+                * matches were unique later on.)
+                * The result of this step is a list of column names just like an
+                * explicitly-written USING list.
+                */
+               if (j->isNatural)
+               {
+                       List       *rlist = NIL;
+                       List       *lx,
+                                          *rx;
+
+                       Assert(j->using == NIL); /* shouldn't have USING() too */
+
+                       foreach(lx, l_colnames)
                        {
-                               /* CROSS JOIN */
-                               if (j->quals == NULL)
-                                       printf("CROSS JOIN...\n");
+                               char       *l_colname = strVal(lfirst(lx));
+                               Value      *m_name = NULL;
 
-                               /*
-                                * JOIN/USING This is an inner join, so rip apart the join
-                                * node and transform into a traditional FROM list.
-                                * NATURAL JOIN and JOIN USING both change the shape of
-                                * the result. Need to generate a list of result columns
-                                * to use for target list expansion and validation.
-                                */
-                               else if (IsA(j->quals, List))
+                               foreach(rx, r_colnames)
                                {
+                                       char       *r_colname = strVal(lfirst(rx));
 
-                                       /*
-                                        * List of Ident nodes means column names from a real
-                                        * USING clause. Determine the shape of the joined
-                                        * table.
-                                        */
-                                       List       *ucols,
-                                                          *ucol;
-                                       List       *shape = NULL;
-                                       List       *alias = NULL;
-                                       List       *l_shape,
-                                                          *r_shape;
-
-                                       List       *l_cols = makeAttrList(l_name);
-                                       List       *r_cols = makeAttrList(r_name);
-
-                                       printf("USING input tables are:\n %s\n %s\n",
-                                                  nodeToString(l_name), nodeToString(r_name));
-
-                                       printf("USING expanded tables are:\n %s\n %s\n",
-                                                  nodeToString(l_cols), nodeToString(r_cols));
-
-                                       /* Columns from the USING clause... */
-                                       ucols = (List *) j->quals;
-                                       foreach(ucol, ucols)
+                                       if (strcmp(l_colname, r_colname) == 0)
                                        {
-                                               List       *col;
-                                               Attr       *l_attr = NULL,
-                                                                  *r_attr = NULL;
-                                               Ident      *id = lfirst(ucol);
-
-                                               Attr       *attr = makeAttr("", id->name);
-
-                                               foreach(col, l_cols)
-                                               {
-                                                       attr = lfirst(col);
-                                                       if (strcmp(AttrString(attr), id->name) == 0)
-                                                       {
-                                                               l_attr = attr;
-                                                               break;
-                                                       }
-                                               }
-
-                                               foreach(col, r_cols)
-                                               {
-                                                       attr = lfirst(col);
-                                                       if (strcmp(AttrString(attr), id->name) == 0)
-                                                       {
-                                                               r_attr = attr;
-                                                               break;
-                                                       }
-                                               }
-
-                                               if (l_attr == NULL)
-                                                       elog(ERROR, "USING column '%s' not found in table '%s'",
-                                                                id->name, l_name->relname);
-                                               if (r_attr == NULL)
-                                                       elog(ERROR, "USING column '%s' not found in table '%s'",
-                                                                id->name, r_name->relname);
-
-                                               shape = lappend(shape, l_attr);
-                                               alias = lappend(alias, makeAttr("", AttrString(l_attr)));
+                                               m_name = makeString(l_colname);
+                                               break;
                                        }
-                                       printf("JOIN/USING join columns are %s\n", nodeToString(shape));
-
-                                       /* Remaining columns from the left side... */
-                                       l_shape = makeUniqueAttrList(makeAttrList(l_name), shape);
-
-                                       printf("JOIN/USING left columns are %s\n", nodeToString(l_shape));
-
-                                       r_shape = makeUniqueAttrList(makeAttrList(r_name), shape);
+                               }
 
-                                       printf("JOIN/USING right columns are %s\n", nodeToString(r_shape));
+                               /* matched a right column? then keep as join column... */
+                               if (m_name != NULL)
+                                       rlist = lappend(rlist, m_name);
+                       }
 
-                                       printf("JOIN/USING input quals are %s\n", nodeToString(j->quals));
+                       j->using = rlist;
+               }
 
-                                       j->quals = transformUsingClause(pstate, shape, l_cols, r_cols);
+               /*
+                * Now transform the join qualifications, if any.
+                */
+               res_colnames = NIL;
+               res_colvars = NIL;
 
-                                       printf("JOIN/USING transformed quals are %s\n", nodeToString(j->quals));
+               if (j->using)
+               {
+                       /*
+                        * JOIN/USING (or NATURAL JOIN, as transformed above).
+                        * Transform the list into an explicit ON-condition,
+                        * and generate a list of result columns.
+                        */
+                       List       *ucols = j->using;
+                       List       *l_usingvars = NIL;
+                       List       *r_usingvars = NIL;
+                       List       *ucol;
 
-                                       alias = nconc(nconc(alias, listCopy(l_shape)), listCopy(r_shape));
-                                       shape = nconc(nconc(shape, l_shape), r_shape);
+                       Assert(j->quals == NULL); /* shouldn't have ON() too */
 
-                                       printf("JOIN/USING shaped table is %s\n", nodeToString(shape));
-                                       printf("JOIN/USING alias list is %s\n", nodeToString(alias));
+                       foreach(ucol, ucols)
+                       {
+                               char       *u_colname = strVal(lfirst(ucol));
+                               List       *col;
+                               Node       *l_colvar,
+                                                  *r_colvar,
+                                                  *colvar;
+                               int                     ndx;
+                               int                     l_index = -1;
+                               int                     r_index = -1;
+
+                               ndx = 0;
+                               foreach(col, l_colnames)
+                               {
+                                       char       *l_colname = strVal(lfirst(col));
 
-                                       pstate->p_shape = shape;
-                                       pstate->p_alias = alias;
+                                       if (strcmp(l_colname, u_colname) == 0)
+                                       {
+                                               if (l_index >= 0)
+                                                       elog(ERROR, "Common column name \"%s\" appears more than once in left table", u_colname);
+                                               l_index = ndx;
+                                       }
+                                       ndx++;
                                }
+                               if (l_index < 0)
+                                       elog(ERROR, "USING column \"%s\" not found in left table",
+                                                u_colname);
 
-                               /* otherwise, must be an expression from an ON clause... */
-                               else
-                                       j->quals = (List *) lcons(j->quals, NIL);
-
-                               /* listCopy may not be needed here --- will j->quals list
-                                * be used again anywhere?  The #ifdef'd code below may need
-                                * it, if it ever gets used...
-                                */
-                               pstate->p_join_quals = nconc(pstate->p_join_quals,
-                                                                                        listCopy(j->quals));
-
-#if 0
-                               if (qual == NULL)
-                                       elog(ERROR, "JOIN/ON not supported in this context");
-
-                               printf("Table aliases are %s\n", nodeToString(*aliasList));
-#endif
-
-#if 0
-                               /* XXX this code is WRONG because j->quals is a List
-                                * not a simple expression.  Perhaps *qual
-                                * ought also to be a List and we append to it,
-                                * similarly to the way p_join_quals is handled above?
-                                */
-                               if (*qual == NULL)
+                               ndx = 0;
+                               foreach(col, r_colnames)
                                {
-                                       /* merge qualified join clauses... */
-                                       if (j->quals != NULL)
+                                       char       *r_colname = strVal(lfirst(col));
+
+                                       if (strcmp(r_colname, u_colname) == 0)
                                        {
-                                               if (*qual != NULL)
-                                               {
-                                                       A_Expr     *a = makeNode(A_Expr);
-
-                                                       a->oper = AND;
-                                                       a->opname = NULL;
-                                                       a->lexpr = (Node *) *qual;
-                                                       a->rexpr = (Node *) j->quals;
-
-                                                       *qual = (Node *) a;
-                                               }
-                                               else
-                                                       *qual = (Node *) j->quals;
+                                               if (r_index >= 0)
+                                                       elog(ERROR, "Common column name \"%s\" appears more than once in right table", u_colname);
+                                               r_index = ndx;
                                        }
+                                       ndx++;
                                }
-                               else
+                               if (r_index < 0)
+                                       elog(ERROR, "USING column \"%s\" not found in right table",
+                                                u_colname);
+
+                               l_colvar = nth(l_index, l_colvars);
+                               l_usingvars = lappend(l_usingvars, l_colvar);
+                               r_colvar = nth(r_index, r_colvars);
+                               r_usingvars = lappend(r_usingvars, r_colvar);
+
+                               res_colnames = lappend(res_colnames,
+                                                                          nth(l_index, l_colnames));
+                               switch (j->jointype)
                                {
-                                       elog(ERROR, "Multiple JOIN/ON clauses not handled (internal error)");
-                                       *qual = lappend(*qual, j->quals);
+                                       case JOIN_INNER:
+                                       case JOIN_LEFT:
+                                               colvar = l_colvar;
+                                               break;
+                                       case JOIN_RIGHT:
+                                               colvar = r_colvar;
+                                               break;
+                                       default:
+                                       {
+                                               /* Need COALESCE(l_colvar, r_colvar) */
+                                               CaseExpr *c = makeNode(CaseExpr);
+                                               CaseWhen *w = makeNode(CaseWhen);
+                                               A_Expr *a = makeNode(A_Expr);
+
+                                               a->oper = NOTNULL;
+                                               a->lexpr = l_colvar;
+                                               w->expr = (Node *) a;
+                                               w->result = l_colvar;
+                                               c->args = lcons(w, NIL);
+                                               c->defresult = r_colvar;
+                                               colvar = transformExpr(pstate, (Node *) c,
+                                                                                          EXPR_COLUMN_FIRST);
+                                               break;
+                                       }
                                }
-#endif
-
-                               /*
-                                * if we are transforming this node back into a FROM list,
-                                * then we will need to replace the node with two nodes.
-                                * Will need access to the previous list item to change
-                                * the link pointer to reference these new nodes. Try
-                                * accumulating and returning a new list. - thomas
-                                * 1999-01-08 Not doing this yet though!
-                                */
+                               res_colvars = lappend(res_colvars, colvar);
+                       }
 
+                       j->quals = transformUsingClause(pstate, l_usingvars, r_usingvars);
+               }
+               else if (j->quals)
+               {
+                       /* User-written ON-condition; transform it */
+                       j->quals = transformExpr(pstate, j->quals, EXPR_COLUMN_FIRST);
+                       if (exprType(j->quals) != BOOLOID)
+                       {
+                               elog(ERROR, "ON clause must return type bool, not type %s",
+                                        typeidTypeName(exprType(j->quals)));
                        }
-                       else if ((j->jointype == LEFT)
-                                        || (j->jointype == RIGHT)
-                                        || (j->jointype == FULL))
-                               elog(ERROR, "OUTER JOIN is not yet supported");
-                       else
-                               elog(ERROR, "Unrecognized JOIN clause; tag is %d (internal error)",
-                                        j->jointype);
-#else
-                       elog(ERROR, "JOIN expressions are not yet implemented");
-#endif
+                       /* XXX should check that ON clause refers only to joined tbls */
                }
                else
-                       elog(ERROR, "parseFromClause: unexpected FROM clause node (internal error)"
-                                "\n\t%s", nodeToString(n));
+               {
+                       /* CROSS JOIN: no quals */
+               }
+
+               /* Add remaining columns from each side to the output columns */
+               extractUniqueColumns(res_colnames,
+                                                        l_colnames, l_colvars,
+                                                        &l_colnames, &l_colvars);
+               extractUniqueColumns(res_colnames,
+                                                        r_colnames, r_colvars,
+                                                        &r_colnames, &r_colvars);
+               res_colnames = nconc(res_colnames, l_colnames);
+               res_colvars = nconc(res_colvars, l_colvars);
+               res_colnames = nconc(res_colnames, r_colnames);
+               res_colvars = nconc(res_colvars, r_colvars);
+
+               /*
+                * Process alias (AS clause), if any.
+                *
+                * The given table alias must be unique in the current nesting level,
+                * ie it cannot match any RTE refname or jointable alias.  This is
+                * a bit painful to check because my own child joins are not yet in
+                * the pstate's jointree, so they have to be scanned separately.
+                */
+               if (j->alias)
+               {
+                       /* Check against previously created RTEs and jointree entries */
+                       if (refnameRangeOrJoinEntry(pstate, j->alias->relname, NULL))
+                               elog(ERROR, "Table name \"%s\" specified more than once",
+                                        j->alias->relname);
+                       /* Check children */
+                       if (scanJoinTreeForRefname(j->larg, j->alias->relname) ||
+                               scanJoinTreeForRefname(j->rarg, j->alias->relname))
+                               elog(ERROR, "Table name \"%s\" specified more than once",
+                                        j->alias->relname);
+                       /*
+                        * If a column alias list is specified, substitute the alias
+                        * names into my output-column list
+                        */
+                       if (j->alias->attrs != NIL)
+                       {
+                               if (length(j->alias->attrs) != length(res_colnames))
+                                       elog(ERROR, "Column alias list for \"%s\" has wrong number of entries (need %d)",
+                                                j->alias->relname, length(res_colnames));
+                               res_colnames = j->alias->attrs;
+                       }
+               }
+
+               j->colnames = res_colnames;
+               j->colvars = res_colvars;
+
+               return (Node *) j;
+       }
+       else
+               elog(ERROR, "transformFromClauseItem: unexpected node (internal error)"
+                        "\n\t%s", nodeToString(n));
+       return NULL;                            /* can't get here, just keep compiler quiet */
+}
+
+
+/*
+ * transformWhereClause -
+ *       transforms the qualification and make sure it is of type Boolean
+ */
+Node *
+transformWhereClause(ParseState *pstate, Node *clause)
+{
+       Node       *qual;
+
+       if (clause == NULL)
+               return NULL;
+
+       qual = transformExpr(pstate, clause, EXPR_COLUMN_FIRST);
+
+       if (exprType(qual) != BOOLOID)
+       {
+               elog(ERROR, "WHERE clause must return type bool, not type %s",
+                        typeidTypeName(exprType(qual)));
        }
-}      /* parseFromClause() */
+       return qual;
+}
 
 
 /*
@@ -786,10 +687,10 @@ findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause)
                         * is a matching column.  If so, fall through to let
                         * transformExpr() do the rest.  NOTE: if name could refer
                         * ambiguously to more than one column name exposed by FROM,
-                        * colnameRangeTableEntry will elog(ERROR).  That's just what
+                        * colnameToVar will elog(ERROR).  That's just what
                         * we want here.
                         */
-                       if (colnameRangeTableEntry(pstate, name) != NULL)
+                       if (colnameToVar(pstate, name) != NULL)
                                name = NULL;
                }
 
index 7976f5e779590ee41d59a98209ba5dc4b631d379..a033ff4be22edd26b20ec09158ea2303e94fc845 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.82 2000/08/08 15:42:03 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.83 2000/09/12 21:07:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -157,41 +157,51 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
                                {
                                        case OP:
                                                {
-                                                       Node       *lexpr = transformExpr(pstate, a->lexpr, precedence);
-                                                       Node       *rexpr = transformExpr(pstate, a->rexpr, precedence);
+                                                       Node       *lexpr = transformExpr(pstate,
+                                                                                                                         a->lexpr,
+                                                                                                                         precedence);
+                                                       Node       *rexpr = transformExpr(pstate,
+                                                                                                                         a->rexpr,
+                                                                                                                         precedence);
 
                                                        result = (Node *) make_op(a->opname, lexpr, rexpr);
                                                }
                                                break;
                                        case ISNULL:
                                                {
-                                                       Node       *lexpr = transformExpr(pstate, a->lexpr, precedence);
+                                                       Node       *lexpr = transformExpr(pstate,
+                                                                                                                         a->lexpr,
+                                                                                                                         precedence);
 
                                                        result = ParseFuncOrColumn(pstate,
                                                                                                           "nullvalue",
                                                                                                           lcons(lexpr, NIL),
                                                                                                           false, false,
-                                                                                                  &pstate->p_last_resno,
                                                                                                           precedence);
                                                }
                                                break;
                                        case NOTNULL:
                                                {
-                                                       Node       *lexpr = transformExpr(pstate, a->lexpr, precedence);
+                                                       Node       *lexpr = transformExpr(pstate,
+                                                                                                                         a->lexpr,
+                                                                                                                         precedence);
 
                                                        result = ParseFuncOrColumn(pstate,
                                                                                                           "nonnullvalue",
                                                                                                           lcons(lexpr, NIL),
                                                                                                           false, false,
-                                                                                                  &pstate->p_last_resno,
                                                                                                           precedence);
                                                }
                                                break;
                                        case AND:
                                                {
+                                                       Node       *lexpr = transformExpr(pstate,
+                                                                                                                         a->lexpr,
+                                                                                                                         precedence);
+                                                       Node       *rexpr = transformExpr(pstate,
+                                                                                                                         a->rexpr,
+                                                                                                                         precedence);
                                                        Expr       *expr = makeNode(Expr);
-                                                       Node       *lexpr = transformExpr(pstate, a->lexpr, precedence);
-                                                       Node       *rexpr = transformExpr(pstate, a->rexpr, precedence);
 
                                                        if (exprType(lexpr) != BOOLOID)
                                                                elog(ERROR, "left-hand side of AND is type '%s', not '%s'",
@@ -209,9 +219,13 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
                                                break;
                                        case OR:
                                                {
+                                                       Node       *lexpr = transformExpr(pstate,
+                                                                                                                         a->lexpr,
+                                                                                                                         precedence);
+                                                       Node       *rexpr = transformExpr(pstate,
+                                                                                                                         a->rexpr,
+                                                                                                                         precedence);
                                                        Expr       *expr = makeNode(Expr);
-                                                       Node       *lexpr = transformExpr(pstate, a->lexpr, precedence);
-                                                       Node       *rexpr = transformExpr(pstate, a->rexpr, precedence);
 
                                                        if (exprType(lexpr) != BOOLOID)
                                                                elog(ERROR, "left-hand side of OR is type '%s', not '%s'",
@@ -227,8 +241,10 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
                                                break;
                                        case NOT:
                                                {
+                                                       Node       *rexpr = transformExpr(pstate,
+                                                                                                                         a->rexpr,
+                                                                                                                         precedence);
                                                        Expr       *expr = makeNode(Expr);
-                                                       Node       *rexpr = transformExpr(pstate, a->rexpr, precedence);
 
                                                        if (exprType(rexpr) != BOOLOID)
                                                                elog(ERROR, "argument to NOT is type '%s', not '%s'",
@@ -254,13 +270,14 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
 
                                /* transform the list of arguments */
                                foreach(args, fn->args)
-                                       lfirst(args) = transformExpr(pstate, (Node *) lfirst(args), precedence);
+                                       lfirst(args) = transformExpr(pstate,
+                                                                                                (Node *) lfirst(args),
+                                                                                                precedence);
                                result = ParseFuncOrColumn(pstate,
                                                                                   fn->funcname,
                                                                                   fn->args,
                                                                                   fn->agg_star,
                                                                                   fn->agg_distinct,
-                                                                                  &pstate->p_last_resno,
                                                                                   precedence);
                                break;
                        }
@@ -609,8 +626,7 @@ transformAttr(ParseState *pstate, Attr *att, int precedence)
 {
        Node       *basenode;
 
-       basenode = ParseNestedFuncOrColumn(pstate, att, &pstate->p_last_resno,
-                                                                          precedence);
+       basenode = ParseNestedFuncOrColumn(pstate, att, precedence);
        return transformIndirection(pstate, basenode, att->indirection);
 }
 
@@ -618,7 +634,6 @@ static Node *
 transformIdent(ParseState *pstate, Ident *ident, int precedence)
 {
        Node       *result = NULL;
-       RangeTblEntry *rte;
 
        /*
         * try to find the ident as a relation ... but not if subscripts
@@ -634,14 +649,10 @@ transformIdent(ParseState *pstate, Ident *ident, int precedence)
        if (result == NULL || precedence == EXPR_COLUMN_FIRST)
        {
                /* try to find the ident as a column */
-               if ((rte = colnameRangeTableEntry(pstate, ident->name)) != NULL)
-               {
-                       /* Convert it to a fully qualified Attr, and transform that */
-                       Attr       *att = makeAttr(rte->eref->relname, ident->name);
-
-                       att->indirection = ident->indirection;
-                       return transformAttr(pstate, att, precedence);
-               }
+               Node   *var = colnameToVar(pstate, ident->name);
+
+               if (var != NULL)
+                       result = transformIndirection(pstate, var, ident->indirection);
        }
 
        if (result == NULL)
index bad9401c609f9118525c006b425477c6a1bf50eb..1f19b1b949e4eaf38513e34a6e0635878500bd21 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.89 2000/08/24 03:29:05 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.90 2000/09/12 21:07:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -64,19 +64,20 @@ static Oid  agg_select_candidate(Oid typeid, CandidateList candidates);
  ** a tree with of Iter and Func nodes.
  */
 Node *
-ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr, int *curr_resno, int precedence)
+ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr, int precedence)
 {
        List       *mutator_iter;
        Node       *retval = NULL;
 
        if (attr->paramNo != NULL)
        {
-               Param      *param = (Param *) transformExpr(pstate, (Node *) attr->paramNo, EXPR_RELATION_FIRST);
+               Param      *param = (Param *) transformExpr(pstate,
+                                                                                                       (Node *) attr->paramNo,
+                                                                                                       EXPR_RELATION_FIRST);
 
                retval = ParseFuncOrColumn(pstate, strVal(lfirst(attr->attrs)),
                                                                   lcons(param, NIL),
                                                                   false, false,
-                                                                  curr_resno,
                                                                   precedence);
        }
        else
@@ -88,7 +89,6 @@ ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr, int *curr_resno, int pre
                retval = ParseFuncOrColumn(pstate, strVal(lfirst(attr->attrs)),
                                                                   lcons(ident, NIL),
                                                                   false, false,
-                                                                  curr_resno,
                                                                   precedence);
        }
 
@@ -98,7 +98,6 @@ ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr, int *curr_resno, int pre
                retval = ParseFuncOrColumn(pstate, strVal(lfirst(mutator_iter)),
                                                                   lcons(retval, NIL),
                                                                   false, false,
-                                                                  curr_resno,
                                                                   precedence);
        }
 
@@ -241,17 +240,15 @@ agg_select_candidate(Oid typeid, CandidateList candidates)
 Node *
 ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
                                  bool agg_star, bool agg_distinct,
-                                 int *curr_resno, int precedence)
+                                 int precedence)
 {
        Oid                     rettype = InvalidOid;
        Oid                     argrelid = InvalidOid;
        Oid                     funcid = InvalidOid;
        List       *i = NIL;
        Node       *first_arg = NULL;
-       char       *relname = NULL;
-       char       *refname = NULL;
+       char       *refname;
        Relation        rd;
-       Oid                     relid;
        int                     nargs = length(fargs);
        Func       *funcnode;
        Oid                     oid_array[FUNC_MAX_ARGS];
@@ -283,81 +280,17 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
                if (IsA(first_arg, Ident) && ((Ident *) first_arg)->isRel)
                {
                        Ident      *ident = (Ident *) first_arg;
-                       RangeTblEntry *rte;
-                       AttrNumber      attnum;
 
                        /*
                         * first arg is a relation. This could be a projection.
                         */
                        refname = ident->name;
 
-                       rte = refnameRangeTableEntry(pstate, refname);
-                       if (rte == NULL)
-                       {
-                               rte = addRangeTableEntry(pstate, refname,
-                                                                                makeAttr(refname, NULL),
-                                                                                FALSE, FALSE, TRUE);
-                               warnAutoRange(pstate, refname);
-                       }
-
-                       relname = rte->relname;
-                       relid = rte->relid;
-                       attnum = InvalidAttrNumber;
-
-                       /*
-                        * If the attr isn't a set, just make a var for it.  If it is
-                        * a set, treat it like a function and drop through. Look
-                        * through the explicit column list first, since we now allow
-                        * column aliases. - thomas 2000-02-07
-                        */
-                       if (rte->eref->attrs != NULL)
-                       {
-                               List       *c;
-
-                               /*
-                                * start counting attributes/columns from one. zero is
-                                * reserved for InvalidAttrNumber. - thomas 2000-01-27
-                                */
-                               int                     i = 1;
-
-                               foreach(c, rte->eref->attrs)
-                               {
-                                       char       *colname = strVal(lfirst(c));
-
-                                       /* found a match? */
-                                       if (strcmp(colname, funcname) == 0)
-                                       {
-                                               char       *basename = get_attname(relid, i);
-
-                                               if (basename != NULL)
-                                               {
-                                                       funcname = basename;
-                                                       attnum = i;
-                                               }
-
-                                               /*
-                                                * attnum was initialized to InvalidAttrNumber
-                                                * earlier, so no need to reset it if the above
-                                                * test fails. - thomas 2000-02-07
-                                                */
-                                               break;
-                                       }
-                                       i++;
-                               }
-                               if (attnum == InvalidAttrNumber)
-                                       attnum = specialAttNum(funcname);
-                       }
-                       else
-                               attnum = get_attnum(relid, funcname);
+                       retval = qualifiedNameToVar(pstate, refname, funcname, true);
+                       if (retval)
+                               return retval;
 
-                       if (attnum != InvalidAttrNumber)
-                       {
-                               return (Node *) make_var(pstate,
-                                                                                relid,
-                                                                                refname,
-                                                                                funcname);
-                       }
-                       /* else drop through - attr is a set */
+                       /* else drop through - attr is a set or function */
                }
                else if (ISCOMPLEX(exprType(first_arg)))
                {
@@ -376,10 +309,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
                                toid = exprType(first_arg);
                                rd = heap_openr_nofail(typeidTypeName(toid));
                                if (RelationIsValid(rd))
-                               {
-                                       relname = RelationGetRelationName(rd);
                                        heap_close(rd, NoLock);
-                               }
                                else
                                        elog(ERROR, "Type '%s' is not a relation type",
                                                 typeidTypeName(toid));
@@ -506,17 +436,9 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
 
                        rte = refnameRangeTableEntry(pstate, refname);
                        if (rte == NULL)
-                       {
-                               rte = addRangeTableEntry(pstate, refname,
-                                                                                makeAttr(refname, NULL),
-                                                                                FALSE, FALSE, TRUE);
-                               warnAutoRange(pstate, refname);
-                       }
-
-                       relname = rte->relname;
+                               rte = addImplicitRTE(pstate, refname);
 
-                       vnum = refnameRangeTablePosn(pstate, rte->eref->relname,
-                                                                                &sublevels_up);
+                       vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
 
                        /*
                         * for func(relname), the param to the function is the tuple
@@ -525,7 +447,8 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
                         * but has varattno == 0 to signal that the whole tuple is the
                         * argument.
                         */
-                       toid = typeTypeId(typenameType(relname));
+                       toid = typeTypeId(typenameType(rte->relname));
+
                        /* replace it in the arg list */
                        lfirst(i) = makeVar(vnum, 0, toid, -1, sublevels_up);
                }
@@ -666,16 +589,6 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
        /* perform the necessary typecasting of arguments */
        make_arguments(pstate, nargs, fargs, oid_array, true_oid_array);
 
-       /*
-        * Special checks to disallow sequence functions with side-effects
-        * in WHERE clauses.  This is pretty much of a hack; why disallow these
-        * when we have no way to check for side-effects of user-defined fns?
-        */
-       if (funcid == F_NEXTVAL && pstate->p_in_where_clause)
-               elog(ERROR, "Sequence function nextval is not allowed in WHERE clauses");
-       if (funcid == F_SETVAL && pstate->p_in_where_clause)
-               elog(ERROR, "Sequence function setval is not allowed in WHERE clauses");
-
        expr = makeNode(Expr);
        expr->typeOid = rettype;
        expr->opType = FUNC_EXPR;
index 5d363ea3e690d085a5545a816d629ee1ae873feb..85a56067bd2e0c227362066b0bf11218cfa42439 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.45 2000/08/24 03:29:05 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.46 2000/09/12 21:07:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -49,8 +49,8 @@ make_parsestate(ParseState *parentParseState)
        pstate = palloc(sizeof(ParseState));
        MemSet(pstate, 0, sizeof(ParseState));
 
-       pstate->p_last_resno = 1;
        pstate->parentParseState = parentParseState;
+       pstate->p_last_resno = 1;
 
        return pstate;
 }
@@ -164,35 +164,44 @@ make_op(char *opname, Node *ltree, Node *rtree)
 
 /*
  * make_var
- *             Build a Var node for an attribute identified by name
+ *             Build a Var node for an attribute identified by RTE and attrno
  */
 Var *
-make_var(ParseState *pstate, Oid relid, char *refname,
-                char *attrname)
+make_var(ParseState *pstate, RangeTblEntry *rte, int attrno)
 {
-       HeapTuple       tp;
-       Form_pg_attribute att_tup;
        int                     vnum,
-                               attid;
+                               sublevels_up;
        Oid                     vartypeid;
        int32           type_mod;
-       int                     sublevels_up;
-
-       vnum = refnameRangeTablePosn(pstate, refname, &sublevels_up);
-
-       tp = SearchSysCacheTuple(ATTNAME,
-                                                        ObjectIdGetDatum(relid),
-                                                        PointerGetDatum(attrname),
-                                                        0, 0);
-       if (!HeapTupleIsValid(tp))
-               elog(ERROR, "Relation %s does not have attribute %s",
-                        refname, attrname);
-       att_tup = (Form_pg_attribute) GETSTRUCT(tp);
-       attid = att_tup->attnum;
-       vartypeid = att_tup->atttypid;
-       type_mod = att_tup->atttypmod;
-
-       return makeVar(vnum, attid, vartypeid, type_mod, sublevels_up);
+
+       vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
+
+       if (rte->relid != InvalidOid)
+       {
+               /* Plain relation RTE --- get the attribute's type info */
+               HeapTuple       tp;
+               Form_pg_attribute att_tup;
+
+               tp = SearchSysCacheTuple(ATTNUM,
+                                                                ObjectIdGetDatum(rte->relid),
+                                                                Int16GetDatum(attrno),
+                                                                0, 0);
+               /* this shouldn't happen... */
+               if (!HeapTupleIsValid(tp))
+                       elog(ERROR, "Relation %s does not have attribute %d",
+                                rte->relname, attrno);
+               att_tup = (Form_pg_attribute) GETSTRUCT(tp);
+               vartypeid = att_tup->atttypid;
+               type_mod = att_tup->atttypmod;
+       }
+       else
+       {
+               /* Subselect RTE --- get type info from subselect's tlist */
+               elog(ERROR, "make_var: subselect in FROM not implemented yet");
+               vartypeid = type_mod = 0;
+       }
+
+       return makeVar(vnum, attrno, vartypeid, type_mod, sublevels_up);
 }
 
 /*
index 802299c8966856265f752fd30d4b80664c679fa7..491cbc5ef08780cfec069a90d487b1e187f34630 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.46 2000/08/08 15:42:04 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.47 2000/09/12 21:07:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "access/htup.h"
 #include "catalog/pg_type.h"
 #include "nodes/makefuncs.h"
+#include "parser/parsetree.h"
 #include "parser/parse_coerce.h"
+#include "parser/parse_expr.h"
 #include "parser/parse_relation.h"
 #include "parser/parse_type.h"
+#include "rewrite/rewriteManip.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
 
 
+static Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte,
+                                                         char *colname);
+static Node *scanJoinForColumn(JoinExpr *join, char *colname,
+                                                          int sublevels_up);
+static List *expandNamesVars(ParseState *pstate, List *names, List *vars);
+static void warnAutoRange(ParseState *pstate, char *refname);
+
+
 /*
  * Information defining the "system" attributes of every relation.
  */
@@ -65,40 +76,96 @@ static struct
 #define SPECIALS ((int) (sizeof(special_attr)/sizeof(special_attr[0])))
 
 
-#ifdef NOT_USED
-/* refnameRangeTableEntries()
- * Given refname, return a list of range table entries
- * This is possible with JOIN syntax, where tables in a join
- * acquire the same reference name.
- * - thomas 2000-01-20
- * But at the moment we aren't carrying along a full list of
- * table/column aliases, so we don't have the full mechanism
- * to support outer joins in place yet.
- * - thomas 2000-03-04
+/*
+ * refnameRangeOrJoinEntry
+ *       Given a refname, look to see if it matches any RTE or join table.
+ *       If so, return a pointer to the RangeTblEntry or JoinExpr.
+ *       Optionally get its nesting depth (0 = current).       If sublevels_up
+ *       is NULL, only consider items at the current nesting level.
  */
-
-static List *
-refnameRangeTableEntries(ParseState *pstate, char *refname)
+Node *
+refnameRangeOrJoinEntry(ParseState *pstate,
+                                               char *refname,
+                                               int *sublevels_up)
 {
-       List       *rteList = NULL;
-       List       *temp;
+       if (sublevels_up)
+               *sublevels_up = 0;
 
        while (pstate != NULL)
        {
+               List       *temp;
+               JoinExpr   *join;
+
+               /*
+                * Check the rangetable for RTEs; if no match, recursively scan
+                * the jointree for join tables.  We assume that no duplicate
+                * entries have been made in any one nesting level.
+                */
                foreach(temp, pstate->p_rtable)
                {
                        RangeTblEntry *rte = lfirst(temp);
 
                        if (strcmp(rte->eref->relname, refname) == 0)
-                               rteList = lappend(rteList, rte);
+                               return (Node *) rte;
                }
+
+               join = scanJoinTreeForRefname((Node *) pstate->p_jointree, refname);
+               if (join)
+                       return (Node *) join;
+
                pstate = pstate->parentParseState;
+               if (sublevels_up)
+                       (*sublevels_up)++;
+               else
+                       break;
        }
-       return rteList;
+       return NULL;
 }
-#endif
 
-/* given refname, return a pointer to the range table entry */
+/* Recursively search a jointree for a joinexpr with given refname */
+JoinExpr *
+scanJoinTreeForRefname(Node *jtnode, char *refname)
+{
+       JoinExpr   *result = NULL;
+
+       if (jtnode == NULL)
+               return NULL;
+       if (IsA(jtnode, List))
+       {
+               List       *l;
+
+               foreach(l, (List *) jtnode)
+               {
+                       result = scanJoinTreeForRefname(lfirst(l), refname);
+                       if (result)
+                               break;
+               }
+       }
+       else if (IsA(jtnode, RangeTblRef))
+       {
+               /* ignore ... */
+       }
+       else if (IsA(jtnode, JoinExpr))
+       {
+               JoinExpr   *j = (JoinExpr *) jtnode;
+
+               if (j->alias && strcmp(j->alias->relname, refname) == 0)
+                       return j;
+               result = scanJoinTreeForRefname(j->larg, refname);
+               if (! result)
+                       result = scanJoinTreeForRefname(j->rarg, refname);
+       }
+       else
+               elog(ERROR, "scanJoinTreeForRefname: unexpected node type %d",
+                        nodeTag(jtnode));
+       return result;
+}
+
+/*
+ * given refname, return a pointer to the range table entry.
+ *
+ * NOTE that this routine will ONLY find RTEs, not join tables.
+ */
 RangeTblEntry *
 refnameRangeTableEntry(ParseState *pstate, char *refname)
 {
@@ -118,9 +185,13 @@ refnameRangeTableEntry(ParseState *pstate, char *refname)
        return NULL;
 }
 
-/* given refname, return RT index (starting with 1) of the relation,
+/*
+ * given refname, return RT index (starting with 1) of the relation,
  * and optionally get its nesting depth (0 = current). If sublevels_up
  * is NULL, only consider rels at the current nesting level.
+ * A zero result means name not found.
+ *
+ * NOTE that this routine will ONLY find RTEs, not join tables.
  */
 int
 refnameRangeTablePosn(ParseState *pstate, char *refname, int *sublevels_up)
@@ -152,114 +223,264 @@ refnameRangeTablePosn(ParseState *pstate, char *refname, int *sublevels_up)
 }
 
 /*
- * returns range entry if found, else NULL
+ * given an RTE, return RT index (starting with 1) of the entry,
+ * and optionally get its nesting depth (0 = current). If sublevels_up
+ * is NULL, only consider rels at the current nesting level.
+ * Raises error if RTE not found.
  */
-RangeTblEntry *
-colnameRangeTableEntry(ParseState *pstate, char *colname)
+int
+RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up)
 {
-       List       *et;
-       List       *rtable;
-       RangeTblEntry *rte_result = NULL;
+       int                     index;
+       List       *temp;
+
+       if (sublevels_up)
+               *sublevels_up = 0;
 
        while (pstate != NULL)
        {
-               if (pstate->p_is_rule)
-                       rtable = lnext(lnext(pstate->p_rtable));
+               index = 1;
+               foreach(temp, pstate->p_rtable)
+               {
+                       if (rte == (RangeTblEntry *) lfirst(temp))
+                               return index;
+                       index++;
+               }
+               pstate = pstate->parentParseState;
+               if (sublevels_up)
+                       (*sublevels_up)++;
                else
-                       rtable = pstate->p_rtable;
+                       break;
+       }
+       elog(ERROR, "RTERangeTablePosn: RTE not found (internal error)");
+       return 0;                                       /* keep compiler quiet */
+}
+
+/*
+ * scanRTEForColumn
+ *       Search the column names of a single RTE for the given name.
+ *       If found, return an appropriate Var node, else return NULL.
+ *       If the name proves ambiguous within this RTE, raise error.
+ */
+static Node *
+scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
+{
+       Node       *result = NULL;
+       int                     attnum = 0;
+       List       *c;
 
-               foreach(et, rtable)
+       /*
+        * Scan the user column names (or aliases) for a match.
+        * Complain if multiple matches.
+        */
+       foreach(c, rte->eref->attrs)
+       {
+               attnum++;
+               if (strcmp(strVal(lfirst(c)), colname) == 0)
                {
-                       RangeTblEntry *rte_candidate = NULL;
-                       RangeTblEntry *rte = lfirst(et);
+                       if (result)
+                               elog(ERROR, "Column reference \"%s\" is ambiguous", colname);
+                       result = (Node *) make_var(pstate, rte, attnum);
+               }
+       }
 
-                       /* only consider RTEs mentioned in FROM or UPDATE/DELETE */
-                       if (!rte->inFromCl && rte != pstate->p_target_rangetblentry)
-                               continue;
+       /*
+        * If we have a unique match, return it.  Note that this allows a user
+        * alias to override a system column name (such as OID) without error.
+        */
+       if (result)
+               return result;
 
-                       if (rte->eref->attrs != NULL)
-                       {
-                               List       *c;
-
-                               foreach(c, rte->ref->attrs)
-                               {
-                                       if (strcmp(strVal(lfirst(c)), colname) == 0)
-                                       {
-                                               if (rte_candidate != NULL)
-                                                       elog(ERROR, "Column '%s' is ambiguous"
-                                                                " (internal error)", colname);
-                                               rte_candidate = rte;
-                                       }
-                               }
-                       }
+       /*
+        * If the RTE represents a table (not a sub-select), consider system
+        * column names.
+        */
+       if (rte->relid != InvalidOid)
+       {
+               attnum = specialAttNum(colname);
+               if (attnum != InvalidAttrNumber)
+                       result = (Node *) make_var(pstate, rte, attnum);
+       }
+
+       return result;
+}
 
+/*
+ * scanJoinForColumn
+ *       Search the column names of a single join table for the given name.
+ *       If found, return an appropriate Var node or expression, else return NULL.
+ *       If the name proves ambiguous within this jointable, raise error.
+ */
+static Node *
+scanJoinForColumn(JoinExpr *join, char *colname, int sublevels_up)
+{
+       Node       *result = NULL;
+       int                     attnum = 0;
+       List       *c;
+
+       foreach(c, join->colnames)
+       {
+               attnum++;
+               if (strcmp(strVal(lfirst(c)), colname) == 0)
+               {
+                       if (result)
+                               elog(ERROR, "Column reference \"%s\" is ambiguous", colname);
+                       result = copyObject(nth(attnum-1, join->colvars));
                        /*
-                        * Even if we have an attribute list in the RTE, look for the
-                        * column here anyway. This is the only way we will find
-                        * implicit columns like "oid". - thomas 2000-02-07
+                        * If referencing an uplevel join item, we must adjust
+                        * sublevels settings in the copied expression.
                         */
-                       if ((rte_candidate == NULL)
-                               && (get_attnum(rte->relid, colname) != InvalidAttrNumber))
-                               rte_candidate = rte;
+                       if (sublevels_up > 0)
+                               IncrementVarSublevelsUp(result, sublevels_up, 0);
+               }
+       }
+       return result;
+}
+
+/*
+ * colnameToVar
+ *       Search for an unqualified column name.
+ *       If found, return the appropriate Var node (or expression).
+ *       If not found, return NULL.  If the name proves ambiguous, raise error.
+ */
+Node *
+colnameToVar(ParseState *pstate, char *colname)
+{
+       Node       *result = NULL;
+       ParseState *orig_pstate = pstate;
+       int                     levels_up = 0;
 
-                       if (rte_candidate == NULL)
-                               continue;
+       while (pstate != NULL)
+       {
+               List       *jt;
 
-                       if (rte_result != NULL)
+               /*
+                * We want to look only at top-level jointree items, and even for
+                * those, ignore RTEs that are marked as not inFromCl and not
+                * the query's target relation.
+                */
+               foreach(jt, pstate->p_jointree)
+               {
+                       Node   *jtnode = (Node *) lfirst(jt);
+                       Node   *newresult = NULL;
+
+                       if (IsA(jtnode, RangeTblRef))
                        {
-                               if (!pstate->p_is_insert ||
+                               int                     varno = ((RangeTblRef *) jtnode)->rtindex;
+                               RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);
+
+                               if (! rte->inFromCl &&
                                        rte != pstate->p_target_rangetblentry)
-                                       elog(ERROR, "Column '%s' is ambiguous", colname);
+                                       continue;
+
+                               /* use orig_pstate here to get the right sublevels_up */
+                               newresult = scanRTEForColumn(orig_pstate, rte, colname);
+                       }
+                       else if (IsA(jtnode, JoinExpr))
+                       {
+                               JoinExpr   *j = (JoinExpr *) jtnode;
+
+                               newresult = scanJoinForColumn(j, colname, levels_up);
                        }
                        else
-                               rte_result = rte;
+                               elog(ERROR, "colnameToVar: unexpected node type %d",
+                                        nodeTag(jtnode));
+
+                       if (newresult)
+                       {
+                               if (result)
+                                       elog(ERROR, "Column reference \"%s\" is ambiguous",
+                                                colname);
+                               result = newresult;
+                       }
                }
 
-               if (rte_result != NULL)
+               if (result != NULL)
                        break;                          /* found */
 
                pstate = pstate->parentParseState;
+               levels_up++;
        }
-       return rte_result;
+
+       return result;
 }
 
 /*
- * put new entry in pstate p_rtable structure, or return pointer
- * if pstate null
+ * qualifiedNameToVar
+ *       Search for a qualified column name (refname + column name).
+ *       If found, return the appropriate Var node (or expression).
+ *       If not found, return NULL.  If the name proves ambiguous, raise error.
+ */
+Node *
+qualifiedNameToVar(ParseState *pstate, char *refname, char *colname,
+                                  bool implicitRTEOK)
+{
+       Node       *result;
+       Node       *rteorjoin;
+       int                     sublevels_up;
+
+       rteorjoin = refnameRangeOrJoinEntry(pstate, refname, &sublevels_up);
+
+       if (rteorjoin == NULL)
+       {
+               if (! implicitRTEOK)
+                       return NULL;
+               rteorjoin = (Node *) addImplicitRTE(pstate, refname);
+               sublevels_up = 0;
+       }
+
+       if (IsA(rteorjoin, RangeTblEntry))
+               result = scanRTEForColumn(pstate, (RangeTblEntry *) rteorjoin,
+                                                                 colname);
+       else if (IsA(rteorjoin, JoinExpr))
+               result = scanJoinForColumn((JoinExpr *) rteorjoin,
+                                                                 colname, sublevels_up);
+       else
+       {
+               elog(ERROR, "qualifiedNameToVar: unexpected node type %d",
+                        nodeTag(rteorjoin));
+               result = NULL;                  /* keep compiler quiet */
+       }
+
+       return result;
+}
+
+/*
+ * Add an entry to the pstate's range table (p_rtable), unless the
+ * specified refname is already present, in which case raise error.
+ *
+ * If pstate is NULL, we just build an RTE and return it without worrying
+ * about membership in an rtable list.
  */
 RangeTblEntry *
 addRangeTableEntry(ParseState *pstate,
                                   char *relname,
-                                  Attr *ref,
+                                  Attr *alias,
                                   bool inh,
-                                  bool inFromCl,
-                                  bool inJoinSet)
+                                  bool inFromCl)
 {
+       char       *refname = alias ? alias->relname : relname;
        Relation        rel;
        RangeTblEntry *rte;
        Attr       *eref;
        int                     maxattrs;
-       int                     sublevels_up;
+       int                     numaliases;
        int                     varattno;
 
-       /* Look for an existing rte, if available... */
+       /* Check for conflicting RTE or jointable alias (at level 0 only) */
        if (pstate != NULL)
        {
-               int                     rt_index = refnameRangeTablePosn(pstate, ref->relname,
-                                                                                                        &sublevels_up);
+               Node   *rteorjoin = refnameRangeOrJoinEntry(pstate, refname, NULL);
 
-               if (rt_index != 0 && (!inFromCl || sublevels_up == 0))
-               {
-                       if (!strcmp(ref->relname, "*OLD*") || !strcmp(ref->relname, "*NEW*"))
-                               return (RangeTblEntry *) nth(rt_index - 1, pstate->p_rtable);
-                       elog(ERROR, "Table name '%s' specified more than once", ref->relname);
-               }
+               if (rteorjoin)
+                       elog(ERROR, "Table name \"%s\" specified more than once",
+                                refname);
        }
 
        rte = makeNode(RangeTblEntry);
 
        rte->relname = relname;
-       rte->ref = ref;
+       rte->alias = alias;
 
        /*
         * Get the rel's OID.  This access also ensures that we have an
@@ -271,30 +492,34 @@ addRangeTableEntry(ParseState *pstate,
        rte->relid = RelationGetRelid(rel);
        maxattrs = RelationGetNumberOfAttributes(rel);
 
-       eref = copyObject(ref);
-       if (maxattrs < length(eref->attrs))
-               elog(ERROR, "Table '%s' has %d columns available but %d columns specified",
-                        relname, maxattrs, length(eref->attrs));
+       eref = alias ? copyObject(alias) : makeAttr(refname, NULL);
+       numaliases = length(eref->attrs);
+
+       if (maxattrs < numaliases)
+               elog(ERROR, "Table \"%s\" has %d columns available but %d columns specified",
+                        refname, maxattrs, numaliases);
 
        /* fill in any unspecified alias columns */
-       for (varattno = length(eref->attrs); varattno < maxattrs; varattno++)
+       for (varattno = numaliases; varattno < maxattrs; varattno++)
        {
                char       *attrname;
 
                attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname));
                eref->attrs = lappend(eref->attrs, makeString(attrname));
        }
-       heap_close(rel, AccessShareLock);
        rte->eref = eref;
 
-       /*
-        * Flags: - this RTE should be expanded to include descendant tables,
-        * - this RTE is in the FROM clause, - this RTE should be included in
-        * the planner's final join.
+       heap_close(rel, AccessShareLock);
+
+       /*----------
+        * Flags:
+        * - this RTE should be expanded to include descendant tables,
+        * - this RTE is in the FROM clause,
+        * - this RTE should not be checked for access rights.
+        *----------
         */
        rte->inh = inh;
        rte->inFromCl = inFromCl;
-       rte->inJoinSet = inJoinSet;
        rte->skipAcl = false;           /* always starts out false */
 
        /*
@@ -306,118 +531,184 @@ addRangeTableEntry(ParseState *pstate,
        return rte;
 }
 
-/* expandTable()
- * Populates an Attr with table name and column names
- * This is similar to expandAll(), but does not create an RTE
- * if it does not already exist.
- * - thomas 2000-01-19
+/*
+ * Add the given RTE as a top-level entry in the pstate's join tree,
+ * unless there already is an entry for it.
  */
-Attr *
-expandTable(ParseState *pstate, char *refname, bool getaliases)
+void
+addRTEtoJoinTree(ParseState *pstate, RangeTblEntry *rte)
+{
+       int                     rtindex = RTERangeTablePosn(pstate, rte, NULL);
+       List       *jt;
+       RangeTblRef *rtr;
+
+       foreach(jt, pstate->p_jointree)
+       {
+               Node       *n = (Node *) lfirst(jt);
+
+               if (IsA(n, RangeTblRef))
+               {
+                       if (rtindex == ((RangeTblRef *) n)->rtindex)
+                               return;                 /* it's already being joined to */
+               }
+       }
+
+       /* Not present, so add it */
+       rtr = makeNode(RangeTblRef);
+       rtr->rtindex = rtindex;
+       pstate->p_jointree = lappend(pstate->p_jointree, rtr);
+}
+
+/*
+ * Add a POSTQUEL-style implicit RTE.
+ *
+ * We assume caller has already checked that there is no such RTE now.
+ */
+RangeTblEntry *
+addImplicitRTE(ParseState *pstate, char *relname)
 {
-       Attr       *attr;
        RangeTblEntry *rte;
+
+       rte = addRangeTableEntry(pstate, relname, NULL, false, false);
+       addRTEtoJoinTree(pstate, rte);
+       warnAutoRange(pstate, relname);
+
+       return rte;
+}
+
+/* expandRTE()
+ *
+ * Given a rangetable entry, create lists of its column names (aliases if
+ * provided, else real names) and Vars for each column.  Only user columns
+ * are considered, since this is primarily used to expand '*' and determine
+ * the contents of JOIN tables.
+ *
+ * If only one of the two kinds of output list is needed, pass NULL for the
+ * output pointer for the unwanted one.
+ */
+void
+expandRTE(ParseState *pstate, RangeTblEntry *rte,
+                 List **colnames, List **colvars)
+{
        Relation        rel;
        int                     varattno,
-                               maxattrs;
+                               maxattrs,
+                               rtindex,
+                               sublevels_up;
 
-       rte = refnameRangeTableEntry(pstate, refname);
+       if (colnames)
+               *colnames = NIL;
+       if (colvars)
+               *colvars = NIL;
 
-       if (getaliases && (rte != NULL))
-               return rte->eref;
+       /* Need the RT index of the entry for creating Vars */
+       rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
 
-       if (rte != NULL)
-               rel = heap_open(rte->relid, AccessShareLock);
-       else
-               rel = heap_openr(refname, AccessShareLock);
-
-       if (rel == NULL)
-               elog(ERROR, "Relation '%s' not found", refname);
+       rel = heap_open(rte->relid, AccessShareLock);
 
        maxattrs = RelationGetNumberOfAttributes(rel);
 
-       attr = makeAttr(refname, NULL);
-
        for (varattno = 0; varattno < maxattrs; varattno++)
        {
-               char       *attrname;
+               Form_pg_attribute attr = rel->rd_att->attrs[varattno];
 
 #ifdef _DROP_COLUMN_HACK__
-               if (COLUMN_IS_DROPPED(rel->rd_att->attrs[varattno]))
+               if (COLUMN_IS_DROPPED(attr))
                        continue;
 #endif  /* _DROP_COLUMN_HACK__ */
-               attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname));
-               attr->attrs = lappend(attr->attrs, makeString(attrname));
+
+               if (colnames)
+               {
+                       char       *label;
+
+                       if (varattno < length(rte->eref->attrs))
+                               label = strVal(nth(varattno, rte->eref->attrs));
+                       else
+                               label = NameStr(attr->attname);
+                       *colnames = lappend(*colnames, makeString(pstrdup(label)));
+               }
+
+               if (colvars)
+               {
+                       Var                *varnode;
+
+                       varnode = makeVar(rtindex, attr->attnum,
+                                                         attr->atttypid, attr->atttypmod,
+                                                         sublevels_up);
+
+                       *colvars = lappend(*colvars, varnode);
+               }
        }
 
        heap_close(rel, AccessShareLock);
-
-       return attr;
 }
 
 /*
- * expandAll -
- *       makes a list of attributes
+ * expandRelAttrs -
+ *       makes a list of TargetEntry nodes for the attributes of the rel
  */
 List *
-expandAll(ParseState *pstate, char *relname, Attr *ref, int *this_resno)
+expandRelAttrs(ParseState *pstate, RangeTblEntry *rte)
 {
-       List       *te_list = NIL;
-       RangeTblEntry *rte;
-       Relation        rel;
-       int                     varattno,
-                               maxattrs;
+       List       *name_list,
+                          *var_list;
 
-       rte = refnameRangeTableEntry(pstate, ref->relname);
-       if (rte == NULL)
-       {
-               rte = addRangeTableEntry(pstate, relname, ref,
-                                                                FALSE, FALSE, TRUE);
-               warnAutoRange(pstate, ref->relname);
-       }
+       expandRTE(pstate, rte, &name_list, &var_list);
 
-       rel = heap_open(rte->relid, AccessShareLock);
+       return expandNamesVars(pstate, name_list, var_list);
+}
 
-       maxattrs = RelationGetNumberOfAttributes(rel);
+/*
+ * expandJoinAttrs -
+ *       makes a list of TargetEntry nodes for the attributes of the join
+ */
+List *
+expandJoinAttrs(ParseState *pstate, JoinExpr *join, int sublevels_up)
+{
+       List       *vars;
 
-       for (varattno = 0; varattno < maxattrs; varattno++)
-       {
-               char       *attrname;
-               char       *label;
-               Var                *varnode;
-               TargetEntry *te = makeNode(TargetEntry);
+       vars = copyObject(join->colvars);
+       /*
+        * If referencing an uplevel join item, we must adjust
+        * sublevels settings in the copied expression.
+        */
+       if (sublevels_up > 0)
+               IncrementVarSublevelsUp((Node *) vars, sublevels_up, 0);
 
-#ifdef _DROP_COLUMN_HACK__
-               if (COLUMN_IS_DROPPED(rel->rd_att->attrs[varattno]))
-                       continue;
-#endif  /* _DROP_COLUMN_HACK__ */
-               attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname));
+       return expandNamesVars(pstate,
+                                                  copyObject(join->colnames),
+                                                  vars);
+}
 
-               /*
-                * varattno is zero-based, so check that length() is always
-                * greater
-                */
-               if (length(rte->eref->attrs) > varattno)
-                       label = pstrdup(strVal(nth(varattno, rte->eref->attrs)));
-               else
-                       label = attrname;
-               varnode = make_var(pstate, rte->relid, relname, attrname);
+/*
+ * expandNamesVars -
+ *             Workhorse for "*" expansion: produce a list of targetentries
+ *             given lists of column names (as String nodes) and var references.
+ */
+static List *
+expandNamesVars(ParseState *pstate, List *names, List *vars)
+{
+       List       *te_list = NIL;
 
-               /*
-                * Even if the elements making up a set are complex, the set
-                * itself is not.
-                */
+       while (names)
+       {
+               char       *label = strVal(lfirst(names));
+               Node       *varnode = (Node *) lfirst(vars);
+               TargetEntry *te = makeNode(TargetEntry);
 
-               te->resdom = makeResdom((AttrNumber) (*this_resno)++,
-                                                               varnode->vartype,
-                                                               varnode->vartypmod,
+               te->resdom = makeResdom((AttrNumber) (pstate->p_last_resno)++,
+                                                               exprType(varnode),
+                                                               exprTypmod(varnode),
                                                                label,
                                                                false);
-               te->expr = (Node *) varnode;
+               te->expr = varnode;
                te_list = lappend(te_list, te);
+
+               names = lnext(names);
+               vars = lnext(vars);
        }
 
-       heap_close(rel, AccessShareLock);
+       Assert(vars == NIL);            /* lists not same length? */
 
        return te_list;
 }
@@ -531,11 +822,17 @@ attnumTypeId(Relation rd, int attid)
        return rd->rd_att->attrs[attid - 1]->atttypid;
 }
 
-void
+/*
+ * Generate a warning about an implicit RTE, if appropriate.
+ *
+ * Our current theory on this is that we should allow "SELECT foo.*"
+ * but warn about a mixture of explicit and implicit RTEs.
+ */
+static void
 warnAutoRange(ParseState *pstate, char *refname)
 {
-       List       *temp;
        bool            foundInFromCl = false;
+       List       *temp;
 
        foreach(temp, pstate->p_rtable)
        {
@@ -548,8 +845,8 @@ warnAutoRange(ParseState *pstate, char *refname)
                }
        }
        if (foundInFromCl)
-               elog(NOTICE, "Adding missing FROM-clause entry%s for table %s",
-                       pstate->parentParseState != NULL ? " in subquery" : "",
-                       refname);
+               elog(NOTICE, "Adding missing FROM-clause entry%s for table \"%s\"",
+                        pstate->parentParseState != NULL ? " in subquery" : "",
+                        refname);
 }
 
index 1564f976b042b0ef13154b5640b51e76ecacbda0..b8e1570985f2cba6283e074fe31ede401d167c61 100644 (file)
@@ -8,13 +8,14 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.61 2000/08/08 15:42:04 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.62 2000/09/12 21:07:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "postgres.h"
 #include "nodes/makefuncs.h"
+#include "parser/parsetree.h"
 #include "parser/parse_coerce.h"
 #include "parser/parse_expr.h"
 #include "parser/parse_func.h"
@@ -104,36 +105,8 @@ transformTargetList(ParseState *pstate, List *targetlist)
                                 * Target item is a single '*', expand all tables (eg.
                                 * SELECT * FROM emp)
                                 */
-                               if (pstate->p_shape != NULL)
-                               {
-                                       List       *s,
-                                                          *a;
-                                       int                     i;
-
-                                       Assert(length(pstate->p_shape) == length(pstate->p_alias));
-
-                                       s = pstate->p_shape;
-                                       a = pstate->p_alias;
-                                       for (i = 0; i < length(pstate->p_shape); i++)
-                                       {
-                                               TargetEntry *te;
-                                               char       *colname;
-                                               Attr       *shape = lfirst(s);
-                                               Attr       *alias = lfirst(a);
-
-                                               Assert(IsA(shape, Attr) &&IsA(alias, Attr));
-
-                                               colname = strVal(lfirst(alias->attrs));
-                                               te = transformTargetEntry(pstate, (Node *) shape,
-                                                                                                 NULL, colname, false);
-                                               p_target = lappend(p_target, te);
-                                               s = lnext(s);
-                                               a = lnext(a);
-                                       }
-                               }
-                               else
-                                       p_target = nconc(p_target,
-                                                                        ExpandAllTables(pstate));
+                               p_target = nconc(p_target,
+                                                                ExpandAllTables(pstate));
                        }
                        else if (att->attrs != NIL &&
                                         strcmp(strVal(lfirst(att->attrs)), "*") == 0)
@@ -143,10 +116,30 @@ transformTargetList(ParseState *pstate, List *targetlist)
                                 * Target item is relation.*, expand that table (eg.
                                 * SELECT emp.*, dname FROM emp, dept)
                                 */
-                               p_target = nconc(p_target,
-                                                                expandAll(pstate, att->relname,
-                                                                                  makeAttr(att->relname, NULL),
-                                                                                  &pstate->p_last_resno));
+                               Node       *rteorjoin;
+                               int                     sublevels_up;
+
+                               rteorjoin = refnameRangeOrJoinEntry(pstate, att->relname,
+                                                                                                       &sublevels_up);
+
+                               if (rteorjoin == NULL)
+                               {
+                                       rteorjoin = (Node *) addImplicitRTE(pstate, att->relname);
+                                       sublevels_up = 0;
+                               }
+
+                               if (IsA(rteorjoin, RangeTblEntry))
+                                       p_target = nconc(p_target,
+                                                                        expandRelAttrs(pstate,
+                                                                                                       (RangeTblEntry *) rteorjoin));
+                               else if (IsA(rteorjoin, JoinExpr))
+                                       p_target = nconc(p_target,
+                                                                        expandJoinAttrs(pstate,
+                                                                                                        (JoinExpr *) rteorjoin,
+                                                                                                        sublevels_up));
+                               else
+                                       elog(ERROR, "transformTargetList: unexpected node type %d",
+                                                nodeTag(rteorjoin));
                        }
                        else
                        {
@@ -219,23 +212,12 @@ updateTargetListEntry(ParseState *pstate,
         */
        if (indirection)
        {
-#ifndef DISABLE_JOIN_SYNTAX
-               Attr       *att = makeAttr(pstrdup(RelationGetRelationName(rd)), colname);
-
-#else
-               Attr       *att = makeNode(Attr);
-
-#endif
+               Attr       *att = makeAttr(pstrdup(RelationGetRelationName(rd)),
+                                                                  colname);
                Node       *arrayBase;
                ArrayRef   *aref;
 
-#ifdef DISABLE_JOIN_SYNTAX
-               att->relname = pstrdup(RelationGetRelationName(rd));
-               att->attrs = lcons(makeString(colname), NIL);
-#endif
-               arrayBase = ParseNestedFuncOrColumn(pstate, att,
-                                                                                       &pstate->p_last_resno,
-                                                                                       EXPR_COLUMN_FIRST);
+               arrayBase = ParseNestedFuncOrColumn(pstate, att, EXPR_COLUMN_FIRST);
                aref = transformArraySubscripts(pstate, arrayBase,
                                                                                indirection,
                                                                                pstate->p_is_insert,
@@ -401,46 +383,54 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
 }
 
 /* ExpandAllTables()
- * Turns '*' (in the target list) into a list of attributes
- * (of all relations in the range table)
+ * Turns '*' (in the target list) into a list of targetlist entries.
+ *
+ * tlist entries are generated for each relation appearing in the FROM list,
+ * which by now has been expanded into a join tree.
  */
 static List *
 ExpandAllTables(ParseState *pstate)
 {
        List       *target = NIL;
-       List       *rt,
-                          *rtable;
-
-       rtable = pstate->p_rtable;
-       if (pstate->p_is_rule)
-       {
-
-               /*
-                * skip first two entries, "*new*" and "*current*"
-                */
-               rtable = lnext(lnext(rtable));
-       }
+       List       *jt;
 
        /* SELECT *; */
-       if (rtable == NIL)
+       if (pstate->p_jointree == NIL)
                elog(ERROR, "Wildcard with no tables specified not allowed");
 
-       foreach(rt, rtable)
+       foreach(jt, pstate->p_jointree)
        {
-               RangeTblEntry *rte = lfirst(rt);
+               Node       *n = (Node *) lfirst(jt);
 
-               /*
-                * we only expand those listed in the from clause. (This will also
-                * prevent us from using the wrong table in inserts: eg. tenk2 in
-                * "insert into tenk2 select * from tenk1;")
-                */
-               if (!rte->inFromCl)
-                       continue;
+               if (IsA(n, RangeTblRef))
+               {
+                       RangeTblEntry *rte;
 
-               target = nconc(target,
-                                          expandAll(pstate, rte->eref->relname, rte->eref,
-                                                                &pstate->p_last_resno));
+                       rte = rt_fetch(((RangeTblRef *) n)->rtindex,
+                                                  pstate->p_rtable);
+
+                       /*
+                        * Ignore added-on relations that were not listed in the FROM
+                        * clause.
+                        */
+                       if (!rte->inFromCl)
+                               continue;
+
+                       target = nconc(target, expandRelAttrs(pstate, rte));
+               }
+               else if (IsA(n, JoinExpr))
+               {
+                       /* A newfangled join expression */
+                       JoinExpr   *j = (JoinExpr *) n;
+
+                       /* Currently, a join expr could only have come from FROM. */
+                       target = nconc(target, expandJoinAttrs(pstate, j, 0));
+               }
+               else
+                       elog(ERROR, "ExpandAllTables: unexpected node (internal error)"
+                                "\n\t%s", nodeToString(n));
        }
+
        return target;
 }
 
index a7652407b73252f8fdc98661529e3f9188f73a65..4a6c825498afd76cae0997390787e1c54743a91e 100644 (file)
@@ -1,32 +1,40 @@
 /*-------------------------------------------------------------------------
  *
  * parser.c
+ *             Main entry point/driver for PostgreSQL parser
+ *
  *
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parser.c,v 1.45 2000/04/12 17:15:27 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parser.c,v 1.46 2000/09/12 21:07:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "postgres.h"
+
+#include "nodes/parsenodes.h"
+#include "nodes/pg_list.h"
 #include "parser/analyze.h"
 #include "parser/gramparse.h"
+#include "parser/parse.h"
 #include "parser/parser.h"
 #include "parser/parse_expr.h"
 
+
 #if defined(FLEX_SCANNER)
 extern void DeleteBuffer(void);
-
 #endif  /* FLEX_SCANNER */
 
 char      *parseString;                /* the char* which holds the string to be
                                                                 * parsed */
 List      *parsetree;                  /* result of parsing is left here */
 
+static int     lookahead_token;        /* one-token lookahead */
+static bool have_lookahead;            /* lookahead_token set? */
+
 #ifdef SETS_FIXED
 static void fixupsets();
 static void define_sets();
@@ -42,11 +50,11 @@ parser(char *str, Oid *typev, int nargs)
        List       *queryList;
        int                     yyresult;
 
-       init_io();
-
-       parseString = pstrdup(str);
+       parseString = str;
        parsetree = NIL;                        /* in case parser forgets to set it */
+       have_lookahead = false;
 
+       scanner_init();
        parser_init(typev, nargs);
        parse_expr_init();
 
@@ -83,6 +91,52 @@ parser(char *str, Oid *typev, int nargs)
        return queryList;
 }
 
+
+/*
+ * Intermediate filter between parser and base lexer (base_yylex in scan.l).
+ *
+ * The filter is needed because in some cases SQL92 requires more than one
+ * token lookahead.  We reduce these cases to one-token lookahead by combining
+ * tokens here, in order to keep the grammar LR(1).
+ *
+ * Using a filter is simpler than trying to recognize multiword tokens 
+ * directly in scan.l, because we'd have to allow for comments between the
+ * words ...
+ */
+int
+yylex(void)
+{
+       int                     cur_token;
+
+       /* Get next token --- we might already have it */
+       if (have_lookahead)
+       {
+               cur_token = lookahead_token;
+               have_lookahead = false;
+       }
+       else
+               cur_token = base_yylex();
+
+       /* Do we need to look ahead for a possible multiword token? */
+       switch (cur_token)
+       {
+               case UNION:
+                       /* UNION JOIN must be reduced to a single UNIONJOIN token */
+                       lookahead_token = base_yylex();
+                       if (lookahead_token == JOIN)
+                               cur_token = UNIONJOIN;
+                       else
+                               have_lookahead = true;
+                       break;
+
+               default:
+                       break;
+       }
+
+       return cur_token;
+}
+
+
 #ifdef SETS_FIXED
 static void
 fixupsets(Query *parse)
index f5597d1593ed245c1b96c5bae50e21c5a57ef359..5700915ad94ed27b3322dfc219d178c7df38aa0d 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/scan.l,v 1.76 2000/08/22 13:01:20 ishii Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/scan.l,v 1.77 2000/09/12 21:07:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -76,7 +76,7 @@ static char      *literalbuf;         /* expandable buffer */
 static int             literallen;             /* actual current length */
 static int             literalalloc;   /* current allocated buffer size */
 
-static int             xcdepth = 0;
+static int             xcdepth = 0;    /* depth of nesting in slash-star comments */
 
 #define startlit()  (literalbuf[0] = '\0', literallen = 0)
 static void addlit(char *ytext, int yleng);
@@ -510,22 +510,24 @@ other                     .
 
 %%
 
-void yyerror(const char * message)
+void
+yyerror(const char *message)
 {
        elog(ERROR, "parser: %s at or near \"%s\"", message, yytext);
 }
 
-int yywrap()
+int
+yywrap(void)
 {
        return(1);
 }
 
 /*
init_io:
scanner_init:
        called by postgres before any actual parsing is done
 */
 void
-init_io()
+scanner_init(void)
 {
        /* it's important to set this to NULL
           because input()/myinput() checks the non-nullness of parseCh
index a14e1b4868453d32cdf03bc72c22bc7e22ed0b53..78fdad8960aaef7fdd2a3f10e520801ae97434be 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/rewrite/Attic/locks.c,v 1.31 2000/09/06 14:15:20 petere Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/rewrite/Attic/locks.c,v 1.32 2000/09/12 21:07:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -56,38 +56,16 @@ thisLockWasTriggered_walker(Node *node,
                        return true;
                return false;
        }
-       if (IsA(node, SubLink))
+       if (IsA(node, Query))
        {
+               /* Recurse into subselects */
+               bool            result;
 
-               /*
-                * Standard expression_tree_walker will not recurse into
-                * subselect, but here we must do so.
-                */
-               SubLink    *sub = (SubLink *) node;
-
-               if (thisLockWasTriggered_walker((Node *) (sub->lefthand), context))
-                       return true;
                context->sublevels_up++;
-               if (thisLockWasTriggered_walker((Node *) (sub->subselect), context))
-               {
-                       context->sublevels_up--;        /* not really necessary */
-                       return true;
-               }
+               result = query_tree_walker((Query *) node, thisLockWasTriggered_walker,
+                                                                  (void *) context);
                context->sublevels_up--;
-               return false;
-       }
-       if (IsA(node, Query))
-       {
-               /* Reach here after recursing down into subselect above... */
-               Query      *qry = (Query *) node;
-
-               if (thisLockWasTriggered_walker((Node *) (qry->targetList), context))
-                       return true;
-               if (thisLockWasTriggered_walker((Node *) (qry->qual), context))
-                       return true;
-               if (thisLockWasTriggered_walker((Node *) (qry->havingQual), context))
-                       return true;
-               return false;
+               return result;
        }
        return expression_tree_walker(node, thisLockWasTriggered_walker,
                                                                  (void *) context);
@@ -175,7 +153,7 @@ matchLocks(CmdType event,
 
 typedef struct
 {
-       Oid     evowner;
+       Oid                     evowner;
 } checkLockPerms_context;
 
 static bool
@@ -184,23 +162,8 @@ checkLockPerms_walker(Node *node,
 {
        if (node == NULL)
                return false;
-       if (IsA(node, SubLink))
-       {
-               /*
-                * Standard expression_tree_walker will not recurse into
-                * subselect, but here we must do so.
-                */
-               SubLink    *sub = (SubLink *) node;
-
-               if (checkLockPerms_walker((Node *) (sub->lefthand), context))
-                       return true;
-               if (checkLockPerms_walker((Node *) (sub->subselect), context))
-                       return true;
-               return false;
-       }
        if (IsA(node, Query))
        {
-               /* Reach here after recursing down into subselect above... */
                Query      *qry = (Query *) node;
                int                     rtablength = length(qry->rtable);
                int                     i;
@@ -212,13 +175,10 @@ checkLockPerms_walker(Node *node,
                        int32           reqperm;
                        int32           aclcheck_res;
 
-                       if (rte->ref != NULL)
-                       {
-                               if (strcmp(rte->ref->relname, "*NEW*") == 0)
-                                       continue;
-                               if (strcmp(rte->ref->relname, "*OLD*") == 0)
-                                       continue;
-                       }
+                       if (strcmp(rte->eref->relname, "*NEW*") == 0)
+                               continue;
+                       if (strcmp(rte->eref->relname, "*OLD*") == 0)
+                               continue;
 
                        if (i == qry->resultRelation)
                                switch (qry->commandType)
@@ -250,14 +210,8 @@ checkLockPerms_walker(Node *node,
 
                /* If there are sublinks, search for them and check their RTEs */
                if (qry->hasSubLinks)
-               {
-                       if (checkLockPerms_walker((Node *) (qry->targetList), context))
-                               return true;
-                       if (checkLockPerms_walker((Node *) (qry->qual), context))
-                               return true;
-                       if (checkLockPerms_walker((Node *) (qry->havingQual), context))
-                               return true;
-               }
+                       return query_tree_walker(qry, checkLockPerms_walker,
+                                                                        (void *) context);
                return false;
        }
        return expression_tree_walker(node, checkLockPerms_walker,
@@ -278,7 +232,7 @@ checkLockPerms(List *locks, Query *parsetree, int rt_index)
                return;                                 /* nothing to check */
 
        /*
-        * Get the usename of the rule's event relation owner
+        * Get the userid of the rule's event relation owner
         */
        rte = rt_fetch(rt_index, parsetree->rtable);
        ev_rel = heap_openr(rte->relname, AccessShareLock);
index 4362687f8b8e177a8305a6ce7a010e9aeba45ef7..49dfae5b905434a4e4086a41755f99519bc278b3 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.79 2000/09/06 14:15:20 petere Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.80 2000/09/12 21:07:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -51,12 +51,11 @@ static RewriteInfo *gatherRewriteMeta(Query *parsetree,
                                  Node *rule_qual,
                                  int rt_index,
                                  CmdType event,
-                                 bool *instead_flag);
-static bool rangeTableEntry_used(Node *node, int rt_index, int sublevels_up);
-static bool attribute_used(Node *node, int rt_index, int attno,
-                          int sublevels_up);
-static bool modifyAggrefChangeVarnodes(Node *node, int rt_index, int new_index,
-                                                  int sublevels_up, int new_sublevels_up);
+                                 bool instead_flag);
+static List *adjustJoinTree(Query *parsetree, int rt_index, bool *found);
+static bool modifyAggrefChangeVarnodes(Query *query,
+                                                                          int rt_index, int new_index,
+                                                                          int sublevels_up, int new_sublevels_up);
 static Node *modifyAggrefDropQual(Node *node, Node *targetNode);
 static SubLink *modifyAggrefMakeSublink(Aggref *aggref, Query *parsetree);
 static Node *modifyAggrefQual(Node *node, Query *parsetree);
@@ -80,16 +79,15 @@ gatherRewriteMeta(Query *parsetree,
                                  Node *rule_qual,
                                  int rt_index,
                                  CmdType event,
-                                 bool *instead_flag)
+                                 bool instead_flag)
 {
        RewriteInfo *info;
        int                     rt_length;
-       int                     result_reln;
 
        info = (RewriteInfo *) palloc(sizeof(RewriteInfo));
        info->rt_index = rt_index;
        info->event = event;
-       info->instead_flag = *instead_flag;
+       info->instead_flag = instead_flag;
        info->rule_action = (Query *) copyObject(rule_action);
        info->rule_qual = (Node *) copyObject(rule_qual);
        if (info->rule_action == NULL)
@@ -99,21 +97,54 @@ gatherRewriteMeta(Query *parsetree,
                info->nothing = FALSE;
                info->action = info->rule_action->commandType;
                info->current_varno = rt_index;
-               info->rt = parsetree->rtable;
-               rt_length = length(info->rt);
-               info->rt = nconc(info->rt, copyObject(info->rule_action->rtable));
+               rt_length = length(parsetree->rtable);
 
+               /* Adjust rule action and qual to offset its varnos */
                info->new_varno = PRS2_NEW_VARNO + rt_length;
-               OffsetVarNodes(info->rule_action->qual, rt_length, 0);
-               OffsetVarNodes((Node *) info->rule_action->targetList, rt_length, 0);
+               OffsetVarNodes((Node *) info->rule_action, rt_length, 0);
                OffsetVarNodes(info->rule_qual, rt_length, 0);
-               ChangeVarNodes((Node *) info->rule_action->qual,
-                                          PRS2_OLD_VARNO + rt_length, rt_index, 0);
-               ChangeVarNodes((Node *) info->rule_action->targetList,
+               /* but its references to *OLD* should point at original rt_index */
+               ChangeVarNodes((Node *) info->rule_action,
                                           PRS2_OLD_VARNO + rt_length, rt_index, 0);
                ChangeVarNodes(info->rule_qual,
                                           PRS2_OLD_VARNO + rt_length, rt_index, 0);
 
+               /*
+                * We want the main parsetree's rtable to end up as the concatenation
+                * of its original contents plus those of all the relevant rule
+                * actions.  Also store same into all the rule_action rtables.
+                * Some of the entries may be unused after we finish rewriting, but
+                * if we tried to clean those out we'd have a much harder job to
+                * adjust RT indexes in the query's Vars.  It's OK to have unused
+                * RT entries, since planner will ignore them.
+                *
+                * NOTE KLUGY HACK: we assume the parsetree rtable had at least one
+                * entry to begin with (OK enough, else where'd the rule come from?).
+                * Because of this, if multiple rules nconc() their rtable additions
+                * onto parsetree->rtable, they'll all see the same rtable because
+                * they all have the same list head pointer.
+                */
+               parsetree->rtable = nconc(parsetree->rtable,
+                                                                 info->rule_action->rtable);
+               info->rule_action->rtable = parsetree->rtable;
+
+               /*
+                * Each rule action's jointree should be the main parsetree's jointree
+                * plus that rule's jointree, but *without* the original rtindex
+                * that we're replacing (if present, which it won't be for INSERT).
+                * Note that if the rule refers to OLD, its jointree will add back
+                * a reference to rt_index.
+                *
+                * XXX This might be wrong for subselect-in-FROM?
+                */
+               {
+                       bool    found;
+                       List   *newjointree = adjustJoinTree(parsetree, rt_index, &found);
+
+                       info->rule_action->jointree = nconc(newjointree,
+                                                                                               info->rule_action->jointree);
+               }
+
                /*
                 * bug here about replace CURRENT  -- sort of replace current is
                 * deprecated now so this code shouldn't really need to be so
@@ -121,7 +152,8 @@ gatherRewriteMeta(Query *parsetree,
                 */
                if (info->action != CMD_SELECT)
                {                                               /* i.e update XXXXX */
-                       int                     new_result_reln = 0;
+                       int                     result_reln;
+                       int                     new_result_reln;
 
                        result_reln = info->rule_action->resultRelation;
                        switch (result_reln)
@@ -140,152 +172,31 @@ gatherRewriteMeta(Query *parsetree,
        return info;
 }
 
-
-/*
- * rangeTableEntry_used -
- *     we need to process a RTE for RIR rules only if it is
- *     referenced somewhere in var nodes of the query.
- */
-
-typedef struct
-{
-       int                     rt_index;
-       int                     sublevels_up;
-} rangeTableEntry_used_context;
-
-static bool
-rangeTableEntry_used_walker(Node *node,
-                                                       rangeTableEntry_used_context *context)
-{
-       if (node == NULL)
-               return false;
-       if (IsA(node, Var))
-       {
-               Var                *var = (Var *) node;
-
-               if (var->varlevelsup == context->sublevels_up &&
-                       var->varno == context->rt_index)
-                       return true;
-               return false;
-       }
-       if (IsA(node, SubLink))
-       {
-
-               /*
-                * Standard expression_tree_walker will not recurse into
-                * subselect, but here we must do so.
-                */
-               SubLink    *sub = (SubLink *) node;
-
-               if (rangeTableEntry_used_walker((Node *) (sub->lefthand), context))
-                       return true;
-               if (rangeTableEntry_used((Node *) (sub->subselect),
-                                                                context->rt_index,
-                                                                context->sublevels_up + 1))
-                       return true;
-               return false;
-       }
-       if (IsA(node, Query))
-       {
-               /* Reach here after recursing down into subselect above... */
-               Query      *qry = (Query *) node;
-
-               if (rangeTableEntry_used_walker((Node *) (qry->targetList), context))
-                       return true;
-               if (rangeTableEntry_used_walker((Node *) (qry->qual), context))
-                       return true;
-               if (rangeTableEntry_used_walker((Node *) (qry->havingQual), context))
-                       return true;
-               return false;
-       }
-       return expression_tree_walker(node, rangeTableEntry_used_walker,
-                                                                 (void *) context);
-}
-
-static bool
-rangeTableEntry_used(Node *node, int rt_index, int sublevels_up)
-{
-       rangeTableEntry_used_context context;
-
-       context.rt_index = rt_index;
-       context.sublevels_up = sublevels_up;
-       return rangeTableEntry_used_walker(node, &context);
-}
-
-
 /*
- * attribute_used -
- *     Check if a specific attribute number of a RTE is used
- *     somewhere in the query
+ * Copy the query's jointree list, and attempt to remove any occurrence
+ * of the given rt_index as a top-level join item (we do not look for it
+ * within JoinExprs).  Returns modified jointree list --- original list
+ * is not changed.  *found is set to indicate if we found the rt_index.
  */
-
-typedef struct
-{
-       int                     rt_index;
-       int                     attno;
-       int                     sublevels_up;
-} attribute_used_context;
-
-static bool
-attribute_used_walker(Node *node,
-                                         attribute_used_context *context)
+static List *
+adjustJoinTree(Query *parsetree, int rt_index, bool *found)
 {
-       if (node == NULL)
-               return false;
-       if (IsA(node, Var))
-       {
-               Var                *var = (Var *) node;
+       List       *newjointree = listCopy(parsetree->jointree);
+       List       *jjt;
 
-               if (var->varlevelsup == context->sublevels_up &&
-                       var->varno == context->rt_index &&
-                       var->varattno == context->attno)
-                       return true;
-               return false;
-       }
-       if (IsA(node, SubLink))
+       *found = false;
+       foreach(jjt, newjointree)
        {
+               RangeTblRef *rtr = lfirst(jjt);
 
-               /*
-                * Standard expression_tree_walker will not recurse into
-                * subselect, but here we must do so.
-                */
-               SubLink    *sub = (SubLink *) node;
-
-               if (attribute_used_walker((Node *) (sub->lefthand), context))
-                       return true;
-               if (attribute_used((Node *) (sub->subselect),
-                                                  context->rt_index,
-                                                  context->attno,
-                                                  context->sublevels_up + 1))
-                       return true;
-               return false;
-       }
-       if (IsA(node, Query))
-       {
-               /* Reach here after recursing down into subselect above... */
-               Query      *qry = (Query *) node;
-
-               if (attribute_used_walker((Node *) (qry->targetList), context))
-                       return true;
-               if (attribute_used_walker((Node *) (qry->qual), context))
-                       return true;
-               if (attribute_used_walker((Node *) (qry->havingQual), context))
-                       return true;
-               return false;
+               if (IsA(rtr, RangeTblRef) && rtr->rtindex == rt_index)
+               {
+                       newjointree = lremove(rtr, newjointree);
+                       *found = true;
+                       break;
+               }
        }
-       return expression_tree_walker(node, attribute_used_walker,
-                                                                 (void *) context);
-}
-
-static bool
-attribute_used(Node *node, int rt_index, int attno, int sublevels_up)
-{
-       attribute_used_context context;
-
-       context.rt_index = rt_index;
-       context.attno = attno;
-       context.sublevels_up = sublevels_up;
-       return attribute_used_walker(node, &context);
+       return newjointree;
 }
 
 
@@ -330,48 +241,26 @@ modifyAggrefChangeVarnodes_walker(Node *node,
                }
                return false;
        }
-       if (IsA(node, SubLink))
-       {
-
-               /*
-                * Standard expression_tree_walker will not recurse into
-                * subselect, but here we must do so.
-                */
-               SubLink    *sub = (SubLink *) node;
-
-               if (modifyAggrefChangeVarnodes_walker((Node *) (sub->lefthand),
-                                                                                         context))
-                       return true;
-               if (modifyAggrefChangeVarnodes((Node *) (sub->subselect),
-                                                                          context->rt_index,
-                                                                          context->new_index,
-                                                                          context->sublevels_up + 1,
-                                                                          context->new_sublevels_up + 1))
-                       return true;
-               return false;
-       }
        if (IsA(node, Query))
        {
-               /* Reach here after recursing down into subselect above... */
-               Query      *qry = (Query *) node;
-
-               if (modifyAggrefChangeVarnodes_walker((Node *) (qry->targetList),
-                                                                                         context))
-                       return true;
-               if (modifyAggrefChangeVarnodes_walker((Node *) (qry->qual),
-                                                                                         context))
-                       return true;
-               if (modifyAggrefChangeVarnodes_walker((Node *) (qry->havingQual),
-                                                                                         context))
-                       return true;
-               return false;
+               /* Recurse into subselects */
+               bool            result;
+
+               context->sublevels_up++;
+               context->new_sublevels_up++;
+               result = query_tree_walker((Query *) node,
+                                                                  modifyAggrefChangeVarnodes_walker,
+                                                                  (void *) context);
+               context->sublevels_up--;
+               context->new_sublevels_up--;
+               return result;
        }
        return expression_tree_walker(node, modifyAggrefChangeVarnodes_walker,
                                                                  (void *) context);
 }
 
 static bool
-modifyAggrefChangeVarnodes(Node *node, int rt_index, int new_index,
+modifyAggrefChangeVarnodes(Query *query, int rt_index, int new_index,
                                                   int sublevels_up, int new_sublevels_up)
 {
        modifyAggrefChangeVarnodes_context context;
@@ -380,7 +269,8 @@ modifyAggrefChangeVarnodes(Node *node, int rt_index, int new_index,
        context.new_index = new_index;
        context.sublevels_up = sublevels_up;
        context.new_sublevels_up = new_sublevels_up;
-       return modifyAggrefChangeVarnodes_walker(node, &context);
+       return query_tree_walker(query, modifyAggrefChangeVarnodes_walker,
+                                                        (void *) &context);
 }
 
 
@@ -453,6 +343,7 @@ modifyAggrefMakeSublink(Aggref *aggref, Query *parsetree)
        SubLink    *sublink;
        TargetEntry *tle;
        Resdom     *resdom;
+       RangeTblRef *rtr;
 
        aggVarNos = pull_varnos(aggref->target);
        if (length(aggVarNos) != 1)
@@ -492,6 +383,9 @@ modifyAggrefMakeSublink(Aggref *aggref, Query *parsetree)
        subquery->distinctClause = NIL;
        subquery->sortClause = NIL;
        subquery->rtable = lcons(copyObject(rte), NIL);
+       rtr = makeNode(RangeTblRef);
+       rtr->rtindex = 1;
+       subquery->jointree = lcons(rtr, NIL);
        subquery->targetList = lcons(tle, NIL);
        subquery->qual = modifyAggrefDropQual((Node *) parsetree->qual,
                                                                                  (Node *) aggref);
@@ -517,7 +411,7 @@ modifyAggrefMakeSublink(Aggref *aggref, Query *parsetree)
         * Note that because of previous line, these references have
         * varlevelsup = 1, which must be changed to 0.
         */
-       modifyAggrefChangeVarnodes((Node *) subquery,
+       modifyAggrefChangeVarnodes(subquery,
                                                           lfirsti(aggVarNos), 1,
                                                           1, 0);
 
@@ -675,6 +569,8 @@ apply_RIR_view_mutator(Node *node,
                           apply_RIR_view_mutator, context);
                MUTATE(newnode->havingQual, query->havingQual, Node *,
                           apply_RIR_view_mutator, context);
+               MUTATE(newnode->jointree, query->jointree, List *,
+                          apply_RIR_view_mutator, context);
                return (Node *) newnode;
        }
        return expression_tree_mutator(node, apply_RIR_view_mutator,
@@ -703,7 +599,7 @@ ApplyRetrieveRule(Query *parsetree,
                                  int rt_index,
                                  int relation_level,
                                  Relation relation,
-                                 bool relWasInJoinSet)
+                                 bool relIsUsed)
 {
        Query      *rule_action = NULL;
        Node       *rule_qual;
@@ -735,17 +631,34 @@ ApplyRetrieveRule(Query *parsetree,
        addedrtable = copyObject(rule_action->rtable);
 
        /*
-        * If the original rel wasn't in the join set, none of its spawn is.
-        * If it was, then leave the spawn's flags as they are.
+        * If the original rel wasn't in the join set (which'd be the case
+        * for the target of an INSERT, for example), none of its spawn is.
+        * If it was, then the spawn has to be added to the join set.
         */
-       if (!relWasInJoinSet)
+       if (relIsUsed)
        {
-               foreach(l, addedrtable)
-               {
-                       RangeTblEntry *rte = lfirst(l);
-
-                       rte->inJoinSet = false;
-               }
+               /*
+                * QUICK HACK: this all needs to be replaced, but for now, find
+                * the original rel in the jointree, remove it, and add the rule
+                * action's jointree.  This will not work for views referenced
+                * in JoinExprs!!
+                *
+                * Note: it is possible that the old rel is referenced in the query
+                * but isn't present in the jointree; this should only happen for
+                * *OLD* and *NEW*.  We must not fail if so, but add the rule's
+                * jointree anyway.  (This is a major crock ... should fix rule
+                * representation ...)
+                */
+               bool    found;
+               List   *newjointree = adjustJoinTree(parsetree, rt_index, &found);
+               List   *addedjointree = (List *) copyObject(rule_action->jointree);
+
+               if (!found)
+                       elog(DEBUG, "ApplyRetrieveRule: can't find old rel %s (%d) in jointree",
+                                rt_fetch(rt_index, rtable)->eref->relname, rt_index);
+               OffsetVarNodes((Node *) addedjointree, rt_length, 0);
+               newjointree = nconc(newjointree, addedjointree);
+               parsetree->jointree = newjointree;
        }
 
        rtable = nconc(rtable, addedrtable);
@@ -845,6 +758,10 @@ ApplyRetrieveRule(Query *parsetree,
  * NOTE: although this has the form of a walker, we cheat and modify the
  * SubLink nodes in-place.     It is caller's responsibility to ensure that
  * no unwanted side-effects occur!
+ *
+ * This is unlike most of the other routines that recurse into subselects,
+ * because we must take control at the SubLink node in order to replace
+ * the SubLink's subselect link with the possibly-rewritten subquery.
  */
 static bool
 fireRIRonSubselect(Node *node, void *context)
@@ -854,30 +771,15 @@ fireRIRonSubselect(Node *node, void *context)
        if (IsA(node, SubLink))
        {
                SubLink    *sub = (SubLink *) node;
-               Query      *qry;
 
-               /* Process lefthand args */
-               if (fireRIRonSubselect((Node *) (sub->lefthand), context))
-                       return true;
                /* Do what we came for */
-               qry = fireRIRrules((Query *) (sub->subselect));
-               sub->subselect = (Node *) qry;
-               /* Need not recurse into subselect, because fireRIRrules did it */
-               return false;
-       }
-       if (IsA(node, Query))
-       {
-               /* Reach here when called from fireRIRrules */
-               Query      *qry = (Query *) node;
-
-               if (fireRIRonSubselect((Node *) (qry->targetList), context))
-                       return true;
-               if (fireRIRonSubselect((Node *) (qry->qual), context))
-                       return true;
-               if (fireRIRonSubselect((Node *) (qry->havingQual), context))
-                       return true;
-               return false;
+               sub->subselect = (Node *) fireRIRrules((Query *) (sub->subselect));
+               /* Fall through to process lefthand args of SubLink */
        }
+       /*
+        * Do NOT recurse into Query nodes, because fireRIRrules already
+        * processed subselects for us.
+        */
        return expression_tree_walker(node, fireRIRonSubselect,
                                                                  (void *) context);
 }
@@ -897,7 +799,7 @@ fireRIRrules(Query *parsetree)
        RuleLock   *rules;
        RewriteRule *rule;
        RewriteRule RIRonly;
-       bool            relWasInJoinSet;
+       bool            relIsUsed;
        int                     i;
        List       *l;
 
@@ -916,11 +818,12 @@ fireRIRrules(Query *parsetree)
                 * If the table is not referenced in the query, then we ignore it.
                 * This prevents infinite expansion loop due to new rtable entries
                 * inserted by expansion of a rule. A table is referenced if it is
-                * part of the join set (a source table), or is the result table,
-                * or is referenced by any Var nodes.
+                * part of the join set (a source table), or is referenced by any
+                * Var nodes, or is the result table.
                 */
-               if (!rte->inJoinSet && rt_index != parsetree->resultRelation &&
-                       !rangeTableEntry_used((Node *) parsetree, rt_index, 0))
+               relIsUsed = rangeTableEntry_used((Node *) parsetree, rt_index, 0);
+
+               if (!relIsUsed && rt_index != parsetree->resultRelation)
                        continue;
 
                rel = heap_openr(rte->relname, AccessShareLock);
@@ -931,9 +834,6 @@ fireRIRrules(Query *parsetree)
                        continue;
                }
 
-               relWasInJoinSet = rte->inJoinSet;               /* save before possibly
-                                                                                                * clearing */
-
                /*
                 * Collect the RIR rules that we must apply
                 */
@@ -947,22 +847,10 @@ fireRIRrules(Query *parsetree)
                        if (rule->attrno > 0)
                        {
                                /* per-attr rule; do we need it? */
-                               if (!attribute_used((Node *) parsetree,
-                                                                       rt_index,
+                               if (!attribute_used((Node *) parsetree, rt_index,
                                                                        rule->attrno, 0))
                                        continue;
                        }
-                       else
-                       {
-
-                               /*
-                                * Rel-wide ON SELECT DO INSTEAD means this is a view.
-                                * Remove the view from the planner's join target set, or
-                                * we'll get no rows out because view itself is empty!
-                                */
-                               if (rule->isInstead)
-                                       rte->inJoinSet = false;
-                       }
 
                        locks = lappend(locks, rule);
                }
@@ -989,7 +877,7 @@ fireRIRrules(Query *parsetree)
                                                                                  rt_index,
                                                                                  RIRonly.attrno == -1,
                                                                                  rel,
-                                                                                 relWasInJoinSet);
+                                                                                 relIsUsed);
                }
 
                heap_close(rel, AccessShareLock);
@@ -999,7 +887,7 @@ fireRIRrules(Query *parsetree)
                parsetree->qual = modifyAggrefQual(parsetree->qual, parsetree);
 
        if (parsetree->hasSubLinks)
-               fireRIRonSubselect((Node *) parsetree, NULL);
+               query_tree_walker(parsetree, fireRIRonSubselect, NULL);
 
        return parsetree;
 }
@@ -1056,13 +944,20 @@ CopyAndAddQual(Query *parsetree,
        {
                List       *rtable;
                int                     rt_length;
+               List       *jointree;
 
                rtable = new_tree->rtable;
                rt_length = length(rtable);
                rtable = nconc(rtable, copyObject(rule_action->rtable));
+               /* XXX above possibly wrong for subselect-in-FROM */
                new_tree->rtable = rtable;
                OffsetVarNodes(new_qual, rt_length, 0);
                ChangeVarNodes(new_qual, PRS2_OLD_VARNO + rt_length, rt_index, 0);
+               jointree = copyObject(rule_action->jointree);
+               OffsetVarNodes((Node *) jointree, rt_length, 0);
+               ChangeVarNodes((Node *) jointree, PRS2_OLD_VARNO + rt_length,
+                                          rt_index, 0);
+               new_tree->jointree = nconc(new_tree->jointree, jointree);
        }
        /* XXX -- where current doesn't work for instead nothing.... yet */
        AddNotQual(new_tree, new_qual);
@@ -1103,8 +998,7 @@ fireRules(Query *parsetree,
        foreach(i, locks)
        {
                RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
-               Node       *qual,
-                                  *event_qual;
+               Node       *event_qual;
                List       *actions;
                List       *r;
 
@@ -1227,7 +1121,7 @@ fireRules(Query *parsetree,
                         *--------------------------------------------------
                         */
                        info = gatherRewriteMeta(parsetree, rule_action, rule_qual,
-                                                                        rt_index, event, instead_flag);
+                                                                        rt_index, event, *instead_flag);
 
                        /* handle escapable cases, or those handled by other code */
                        if (info->nothing)
@@ -1247,11 +1141,9 @@ fireRules(Query *parsetree,
                         * splitting into two queries one w/rule_qual, one w/NOT
                         * rule_qual. Also add user query qual onto rule action
                         */
-                       qual = parsetree->qual;
-                       AddQual(info->rule_action, qual);
+                       AddQual(info->rule_action, parsetree->qual);
 
-                       if (info->rule_qual != NULL)
-                               AddQual(info->rule_action, info->rule_qual);
+                       AddQual(info->rule_action, info->rule_qual);
 
                        /*--------------------------------------------------
                         * Step 2:
@@ -1264,18 +1156,6 @@ fireRules(Query *parsetree,
 
                        /*--------------------------------------------------
                         * Step 3:
-                        *        rewriting due to retrieve rules
-                        *--------------------------------------------------
-                        */
-                       info->rule_action->rtable = info->rt;
-
-                       /*
-                        * ProcessRetrieveQuery(info->rule_action, info->rt,
-                        * &orig_instead_flag, TRUE);
-                        */
-
-                       /*--------------------------------------------------
-                        * Step 4
                         *        Simplify? hey, no algorithm for simplification... let
                         *        the planner do it.
                         *--------------------------------------------------
@@ -1403,7 +1283,7 @@ deepRewriteQuery(Query *parsetree)
                rewritten = nconc(rewritten, qual_products);
 
        /* ----------
-        * The original query is appended last if not instead
+        * The original query is appended last (if no "instead" rule)
         * because update and delete rule actions might not do
         * anything if they are invoked after the update or
         * delete is performed. The command counter increment
@@ -1471,17 +1351,15 @@ BasicQueryRewrite(Query *parsetree)
                 */
                if (query->hasAggs)
                {
-                       query->hasAggs =
-                               checkExprHasAggs((Node *) (query->targetList)) ||
-                               checkExprHasAggs((Node *) (query->havingQual));
-                       if (checkExprHasAggs((Node *) (query->qual)))
-                               elog(ERROR, "BasicQueryRewrite: failed to remove aggs from qual");
+                       query->hasAggs = checkExprHasAggs((Node *) query);
+                       if (query->hasAggs)
+                               if (checkExprHasAggs(query->qual))
+                                       elog(ERROR, "BasicQueryRewrite: failed to remove aggs from qual");
                }
                if (query->hasSubLinks)
-                       query->hasSubLinks =
-                               checkExprHasSubLink((Node *) (query->targetList)) ||
-                               checkExprHasSubLink((Node *) (query->qual)) ||
-                               checkExprHasSubLink((Node *) (query->havingQual));
+               {
+                       query->hasSubLinks = checkExprHasSubLink((Node *) query);
+               }
                results = lappend(results, query);
        }
 
index a8ec560c741724176edf48535e467abaf23013cb..e83ac05485304210074687d2618a2a5e9a8fbdc0 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.47 2000/05/30 00:49:51 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.48 2000/09/12 21:07:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -42,7 +42,15 @@ static bool checkExprHasSubLink_walker(Node *node, void *context);
 bool
 checkExprHasAggs(Node *node)
 {
-       return checkExprHasAggs_walker(node, NULL);
+       /*
+        * If a Query is passed, examine it --- but we will not recurse
+        * into sub-Queries.
+        */
+       if (node && IsA(node, Query))
+               return query_tree_walker((Query *) node, checkExprHasAggs_walker,
+                                                                NULL);
+       else
+               return checkExprHasAggs_walker(node, NULL);
 }
 
 static bool
@@ -64,7 +72,15 @@ checkExprHasAggs_walker(Node *node, void *context)
 bool
 checkExprHasSubLink(Node *node)
 {
-       return checkExprHasSubLink_walker(node, NULL);
+       /*
+        * If a Query is passed, examine it --- but we will not recurse
+        * into sub-Queries.
+        */
+       if (node && IsA(node, Query))
+               return query_tree_walker((Query *) node, checkExprHasSubLink_walker,
+                                                                NULL);
+       else
+               return checkExprHasSubLink_walker(node, NULL);
 }
 
 static bool
@@ -84,10 +100,11 @@ checkExprHasSubLink_walker(Node *node, void *context)
  *
  * Find all Var nodes in the given tree with varlevelsup == sublevels_up,
  * and increment their varno fields (rangetable indexes) by 'offset'.
- * The varnoold fields are adjusted similarly.
+ * The varnoold fields are adjusted similarly.  Also, RangeTblRef nodes
+ * in join trees are adjusted.
  *
  * NOTE: although this has the form of a walker, we cheat and modify the
- * Var nodes in-place. The given expression tree should have been copied
+ * nodes in-place.     The given expression tree should have been copied
  * earlier to ensure that no unwanted side-effects occur!
  */
 
@@ -113,38 +130,24 @@ OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context)
                }
                return false;
        }
-       if (IsA(node, SubLink))
+       if (IsA(node, RangeTblRef))
        {
+               RangeTblRef        *rtr = (RangeTblRef *) node;
 
-               /*
-                * Standard expression_tree_walker will not recurse into
-                * subselect, but here we must do so.
-                */
-               SubLink    *sub = (SubLink *) node;
-
-               if (OffsetVarNodes_walker((Node *) (sub->lefthand),
-                                                                 context))
-                       return true;
-               OffsetVarNodes((Node *) (sub->subselect),
-                                          context->offset,
-                                          context->sublevels_up + 1);
+               if (context->sublevels_up == 0)
+                       rtr->rtindex += context->offset;
                return false;
        }
        if (IsA(node, Query))
        {
-               /* Reach here after recursing down into subselect above... */
-               Query      *qry = (Query *) node;
+               /* Recurse into subselects */
+               bool            result;
 
-               if (OffsetVarNodes_walker((Node *) (qry->targetList),
-                                                                 context))
-                       return true;
-               if (OffsetVarNodes_walker((Node *) (qry->qual),
-                                                                 context))
-                       return true;
-               if (OffsetVarNodes_walker((Node *) (qry->havingQual),
-                                                                 context))
-                       return true;
-               return false;
+               context->sublevels_up++;
+               result = query_tree_walker((Query *) node, OffsetVarNodes_walker,
+                                                                  (void *) context);
+               context->sublevels_up--;
+               return result;
        }
        return expression_tree_walker(node, OffsetVarNodes_walker,
                                                                  (void *) context);
@@ -157,7 +160,17 @@ OffsetVarNodes(Node *node, int offset, int sublevels_up)
 
        context.offset = offset;
        context.sublevels_up = sublevels_up;
-       OffsetVarNodes_walker(node, &context);
+
+       /*
+        * Must be prepared to start with a Query or a bare expression tree;
+        * if it's a Query, go straight to query_tree_walker to make sure that
+        * sublevels_up doesn't get incremented prematurely.
+        */
+       if (node && IsA(node, Query))
+               query_tree_walker((Query *) node, OffsetVarNodes_walker,
+                                                 (void *) &context);
+       else
+               OffsetVarNodes_walker(node, &context);
 }
 
 /*
@@ -165,10 +178,11 @@ OffsetVarNodes(Node *node, int offset, int sublevels_up)
  *
  * Find all Var nodes in the given tree belonging to a specific relation
  * (identified by sublevels_up and rt_index), and change their varno fields
- * to 'new_index'.     The varnoold fields are changed too.
+ * to 'new_index'.     The varnoold fields are changed too.  Also, RangeTblRef
+ * nodes in join trees are adjusted.
  *
  * NOTE: although this has the form of a walker, we cheat and modify the
- * Var nodes in-place. The given expression tree should have been copied
+ * nodes in-place.     The given expression tree should have been copied
  * earlier to ensure that no unwanted side-effects occur!
  */
 
@@ -196,39 +210,25 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
                }
                return false;
        }
-       if (IsA(node, SubLink))
+       if (IsA(node, RangeTblRef))
        {
+               RangeTblRef        *rtr = (RangeTblRef *) node;
 
-               /*
-                * Standard expression_tree_walker will not recurse into
-                * subselect, but here we must do so.
-                */
-               SubLink    *sub = (SubLink *) node;
-
-               if (ChangeVarNodes_walker((Node *) (sub->lefthand),
-                                                                 context))
-                       return true;
-               ChangeVarNodes((Node *) (sub->subselect),
-                                          context->rt_index,
-                                          context->new_index,
-                                          context->sublevels_up + 1);
+               if (context->sublevels_up == 0 &&
+                       rtr->rtindex == context->rt_index)
+                       rtr->rtindex = context->new_index;
                return false;
        }
        if (IsA(node, Query))
        {
-               /* Reach here after recursing down into subselect above... */
-               Query      *qry = (Query *) node;
+               /* Recurse into subselects */
+               bool            result;
 
-               if (ChangeVarNodes_walker((Node *) (qry->targetList),
-                                                                 context))
-                       return true;
-               if (ChangeVarNodes_walker((Node *) (qry->qual),
-                                                                 context))
-                       return true;
-               if (ChangeVarNodes_walker((Node *) (qry->havingQual),
-                                                                 context))
-                       return true;
-               return false;
+               context->sublevels_up++;
+               result = query_tree_walker((Query *) node, ChangeVarNodes_walker,
+                                                                  (void *) context);
+               context->sublevels_up--;
+               return result;
        }
        return expression_tree_walker(node, ChangeVarNodes_walker,
                                                                  (void *) context);
@@ -242,7 +242,17 @@ ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up)
        context.rt_index = rt_index;
        context.new_index = new_index;
        context.sublevels_up = sublevels_up;
-       ChangeVarNodes_walker(node, &context);
+
+       /*
+        * Must be prepared to start with a Query or a bare expression tree;
+        * if it's a Query, go straight to query_tree_walker to make sure that
+        * sublevels_up doesn't get incremented prematurely.
+        */
+       if (node && IsA(node, Query))
+               query_tree_walker((Query *) node, ChangeVarNodes_walker,
+                                                 (void *) &context);
+       else
+               ChangeVarNodes_walker(node, &context);
 }
 
 /*
@@ -282,54 +292,181 @@ IncrementVarSublevelsUp_walker(Node *node,
                        var->varlevelsup += context->delta_sublevels_up;
                return false;
        }
-       if (IsA(node, SubLink))
+       if (IsA(node, Query))
        {
+               /* Recurse into subselects */
+               bool            result;
 
-               /*
-                * Standard expression_tree_walker will not recurse into
-                * subselect, but here we must do so.
-                */
-               SubLink    *sub = (SubLink *) node;
+               context->min_sublevels_up++;
+               result = query_tree_walker((Query *) node,
+                                                                  IncrementVarSublevelsUp_walker,
+                                                                  (void *) context);
+               context->min_sublevels_up--;
+               return result;
+       }
+       return expression_tree_walker(node, IncrementVarSublevelsUp_walker,
+                                                                 (void *) context);
+}
+
+void
+IncrementVarSublevelsUp(Node *node, int delta_sublevels_up,
+                                               int min_sublevels_up)
+{
+       IncrementVarSublevelsUp_context context;
+
+       context.delta_sublevels_up = delta_sublevels_up;
+       context.min_sublevels_up = min_sublevels_up;
+
+       /*
+        * Must be prepared to start with a Query or a bare expression tree;
+        * if it's a Query, go straight to query_tree_walker to make sure that
+        * sublevels_up doesn't get incremented prematurely.
+        */
+       if (node && IsA(node, Query))
+               query_tree_walker((Query *) node, IncrementVarSublevelsUp_walker,
+                                                 (void *) &context);
+       else
+               IncrementVarSublevelsUp_walker(node, &context);
+}
+
+
+/*
+ * rangeTableEntry_used - detect whether an RTE is referenced somewhere
+ *     in var nodes or jointree nodes of a query or expression.
+ */
+
+typedef struct
+{
+       int                     rt_index;
+       int                     sublevels_up;
+} rangeTableEntry_used_context;
+
+static bool
+rangeTableEntry_used_walker(Node *node,
+                                                       rangeTableEntry_used_context *context)
+{
+       if (node == NULL)
+               return false;
+       if (IsA(node, Var))
+       {
+               Var                *var = (Var *) node;
 
-               if (IncrementVarSublevelsUp_walker((Node *) (sub->lefthand),
-                                                                                  context))
+               if (var->varlevelsup == context->sublevels_up &&
+                       var->varno == context->rt_index)
                        return true;
-               IncrementVarSublevelsUp((Node *) (sub->subselect),
-                                                               context->delta_sublevels_up,
-                                                               context->min_sublevels_up + 1);
                return false;
        }
-       if (IsA(node, Query))
+       if (IsA(node, RangeTblRef))
        {
-               /* Reach here after recursing down into subselect above... */
-               Query      *qry = (Query *) node;
+               RangeTblRef *rtr = (RangeTblRef *) node;
 
-               if (IncrementVarSublevelsUp_walker((Node *) (qry->targetList),
-                                                                                  context))
+               if (rtr->rtindex == context->rt_index &&
+                       context->sublevels_up == 0)
                        return true;
-               if (IncrementVarSublevelsUp_walker((Node *) (qry->qual),
-                                                                                  context))
-                       return true;
-               if (IncrementVarSublevelsUp_walker((Node *) (qry->havingQual),
-                                                                                  context))
+               return false;
+       }
+       if (IsA(node, Query))
+       {
+               /* Recurse into subselects */
+               bool            result;
+
+               context->sublevels_up++;
+               result = query_tree_walker((Query *) node, rangeTableEntry_used_walker,
+                                                                  (void *) context);
+               context->sublevels_up--;
+               return result;
+       }
+       return expression_tree_walker(node, rangeTableEntry_used_walker,
+                                                                 (void *) context);
+}
+
+bool
+rangeTableEntry_used(Node *node, int rt_index, int sublevels_up)
+{
+       rangeTableEntry_used_context context;
+
+       context.rt_index = rt_index;
+       context.sublevels_up = sublevels_up;
+
+       /*
+        * Must be prepared to start with a Query or a bare expression tree;
+        * if it's a Query, go straight to query_tree_walker to make sure that
+        * sublevels_up doesn't get incremented prematurely.
+        */
+       if (node && IsA(node, Query))
+               return query_tree_walker((Query *) node, rangeTableEntry_used_walker,
+                                                                (void *) &context);
+       else
+               return rangeTableEntry_used_walker(node, &context);
+}
+
+
+/*
+ * attribute_used -
+ *     Check if a specific attribute number of a RTE is used
+ *     somewhere in the query or expression.
+ */
+
+typedef struct
+{
+       int                     rt_index;
+       int                     attno;
+       int                     sublevels_up;
+} attribute_used_context;
+
+static bool
+attribute_used_walker(Node *node,
+                                         attribute_used_context *context)
+{
+       if (node == NULL)
+               return false;
+       if (IsA(node, Var))
+       {
+               Var                *var = (Var *) node;
+
+               if (var->varlevelsup == context->sublevels_up &&
+                       var->varno == context->rt_index &&
+                       var->varattno == context->attno)
                        return true;
                return false;
        }
-       return expression_tree_walker(node, IncrementVarSublevelsUp_walker,
+       if (IsA(node, Query))
+       {
+               /* Recurse into subselects */
+               bool            result;
+
+               context->sublevels_up++;
+               result = query_tree_walker((Query *) node, attribute_used_walker,
+                                                                  (void *) context);
+               context->sublevels_up--;
+               return result;
+       }
+       return expression_tree_walker(node, attribute_used_walker,
                                                                  (void *) context);
 }
 
-void
-IncrementVarSublevelsUp(Node *node, int delta_sublevels_up,
-                                               int min_sublevels_up)
+bool
+attribute_used(Node *node, int rt_index, int attno, int sublevels_up)
 {
-       IncrementVarSublevelsUp_context context;
+       attribute_used_context context;
 
-       context.delta_sublevels_up = delta_sublevels_up;
-       context.min_sublevels_up = min_sublevels_up;
-       IncrementVarSublevelsUp_walker(node, &context);
+       context.rt_index = rt_index;
+       context.attno = attno;
+       context.sublevels_up = sublevels_up;
+
+       /*
+        * Must be prepared to start with a Query or a bare expression tree;
+        * if it's a Query, go straight to query_tree_walker to make sure that
+        * sublevels_up doesn't get incremented prematurely.
+        */
+       if (node && IsA(node, Query))
+               return query_tree_walker((Query *) node, attribute_used_walker,
+                                                                (void *) &context);
+       else
+               return attribute_used_walker(node, &context);
 }
 
+
 /*
  * Add the given qualifier condition to the query's WHERE clause
  */
@@ -615,11 +752,6 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context)
                Query      *query = (Query *) node;
                Query      *newnode;
 
-               /*
-                * XXX original code for ResolveNew only recursed into qual field
-                * of subquery.  I'm assuming that was an oversight ... tgl 9/99
-                */
-
                FLATCOPY(newnode, query, Query);
                MUTATE(newnode->targetList, query->targetList, List *,
                           ResolveNew_mutator, context);
@@ -627,6 +759,8 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context)
                           ResolveNew_mutator, context);
                MUTATE(newnode->havingQual, query->havingQual, Node *,
                           ResolveNew_mutator, context);
+               MUTATE(newnode->jointree, query->jointree, List *,
+                          ResolveNew_mutator, context);
                return (Node *) newnode;
        }
        return expression_tree_mutator(node, ResolveNew_mutator,
@@ -650,13 +784,15 @@ void
 FixNew(RewriteInfo *info, Query *parsetree)
 {
        info->rule_action->targetList = (List *)
-       ResolveNew((Node *) info->rule_action->targetList,
-                          info, parsetree->targetList, 0);
+               ResolveNew((Node *) info->rule_action->targetList,
+                                  info, parsetree->targetList, 0);
        info->rule_action->qual = ResolveNew(info->rule_action->qual,
                                                                                 info, parsetree->targetList, 0);
-       /* XXX original code didn't fix havingQual; presumably an oversight? */
        info->rule_action->havingQual = ResolveNew(info->rule_action->havingQual,
-                                                                                info, parsetree->targetList, 0);
+                                                                                          info, parsetree->targetList, 0);
+       info->rule_action->jointree = (List *)
+               ResolveNew((Node *) info->rule_action->jointree,
+                                  info, parsetree->targetList, 0);
 }
 
 /*
@@ -758,11 +894,6 @@ HandleRIRAttributeRule_mutator(Node *node,
                Query      *query = (Query *) node;
                Query      *newnode;
 
-               /*
-                * XXX original code for HandleRIRAttributeRule only recursed into
-                * qual field of subquery.      I'm assuming that was an oversight ...
-                */
-
                FLATCOPY(newnode, query, Query);
                MUTATE(newnode->targetList, query->targetList, List *,
                           HandleRIRAttributeRule_mutator, context);
@@ -770,6 +901,8 @@ HandleRIRAttributeRule_mutator(Node *node,
                           HandleRIRAttributeRule_mutator, context);
                MUTATE(newnode->havingQual, query->havingQual, Node *,
                           HandleRIRAttributeRule_mutator, context);
+               MUTATE(newnode->jointree, query->jointree, List *,
+                          HandleRIRAttributeRule_mutator, context);
                return (Node *) newnode;
        }
        return expression_tree_mutator(node, HandleRIRAttributeRule_mutator,
@@ -798,9 +931,13 @@ HandleRIRAttributeRule(Query *parsetree,
        parsetree->targetList = (List *)
                HandleRIRAttributeRule_mutator((Node *) parsetree->targetList,
                                                                           &context);
-       parsetree->qual = HandleRIRAttributeRule_mutator(parsetree->qual,
-                                                                                                        &context);
-       /* XXX original code did not fix havingQual ... oversight? */
-       parsetree->havingQual = HandleRIRAttributeRule_mutator(parsetree->havingQual,
-                                                                                                                  &context);
+       parsetree->qual =
+               HandleRIRAttributeRule_mutator(parsetree->qual,
+                                                                          &context);
+       parsetree->havingQual =
+               HandleRIRAttributeRule_mutator(parsetree->havingQual,
+                                                                          &context);
+       parsetree->jointree = (List *)
+               HandleRIRAttributeRule_mutator((Node *) parsetree->jointree,
+                                                                          &context);
 }
index 571854d446c7bfd45c31961d0c057d9c7af05ed2..26ebe21c4a2c17b6bd34c81782e17a90d07c4991 100644 (file)
@@ -1,9 +1,9 @@
 /**********************************************************************
- * get_ruledef.c       - Function to get a rules definition text
- *                       out of its tuple
+ * ruleutils.c - Functions to convert stored expressions/querytrees
+ *                             back to source text
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.60 2000/09/12 04:15:58 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.61 2000/09/12 21:07:05 tgl Exp $
  *
  *       This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -43,6 +43,7 @@
 #include "catalog/pg_index.h"
 #include "catalog/pg_operator.h"
 #include "catalog/pg_shadow.h"
+#include "commands/view.h"
 #include "executor/spi.h"
 #include "lib/stringinfo.h"
 #include "optimizer/clauses.h"
@@ -50,8 +51,8 @@
 #include "parser/keywords.h"
 #include "parser/parse_expr.h"
 #include "parser/parsetree.h"
+#include "rewrite/rewriteManip.h"
 #include "utils/lsyscache.h"
-#include "commands/view.h"
 
 
 /* ----------
@@ -65,12 +66,6 @@ typedef struct
        bool            varprefix;              /* TRUE to print prefixes on Vars */
 } deparse_context;
 
-typedef struct
-{
-       Index           rt_index;
-       int                     levelsup;
-} check_if_rte_used_context;
-
 
 /* ----------
  * Global data
@@ -108,13 +103,13 @@ static void get_func_expr(Expr *expr, deparse_context *context);
 static void get_tle_expr(TargetEntry *tle, deparse_context *context);
 static void get_const_expr(Const *constval, deparse_context *context);
 static void get_sublink_expr(Node *node, deparse_context *context);
+static void get_from_clause(Query *query, deparse_context *context);
+static void get_from_clause_item(Node *jtnode, Query *query,
+                                                                deparse_context *context);
 static bool tleIsArrayAssign(TargetEntry *tle);
 static char *quote_identifier(char *ident);
 static char *get_relation_name(Oid relid);
 static char *get_attribute_name(Oid relid, int2 attnum);
-static bool check_if_rte_used(Node *node, Index rt_index, int levelsup);
-static bool check_if_rte_used_walker(Node *node,
-                                                check_if_rte_used_context *context);
 
 #define only_marker(rte)  ((rte)->inh ? "" : "ONLY ")
 
@@ -230,13 +225,13 @@ pg_get_viewdef(PG_FUNCTION_ARGS)
        Name            vname = PG_GETARG_NAME(0);
        text       *ruledef;
        Datum           args[1];
-       char            nulls[2];
+       char            nulls[1];
        int                     spirc;
        HeapTuple       ruletup;
        TupleDesc       rulettc;
        StringInfoData buf;
        int                     len;
-       char            *name;
+       char       *name;
 
        /* ----------
         * We need the view name somewhere deep down
@@ -276,7 +271,6 @@ pg_get_viewdef(PG_FUNCTION_ARGS)
        name = MakeRetrieveViewRuleName(rulename);
        args[0] = PointerGetDatum(name);
        nulls[0] = ' ';
-       nulls[1] = '\0';
        spirc = SPI_execp(plan_getview, args, nulls, 1);
        if (spirc != SPI_OK_SELECT)
                elog(ERROR, "failed to get pg_rewrite tuple for view %s", rulename);
@@ -883,60 +877,8 @@ get_select_query_def(Query *query, deparse_context *context)
 {
        StringInfo      buf = context->buf;
        char       *sep;
-       TargetEntry *tle;
-       RangeTblEntry *rte;
-       bool       *rt_used;
-       int                     rt_length;
-       int                     rt_numused = 0;
-       bool            rt_constonly = TRUE;
-       int                     i;
        List       *l;
 
-       /* ----------
-        * First we need to know which and how many of the
-        * range table entries in the query are used in the target list
-        * or queries qualification
-        * ----------
-        */
-       rt_length = length(query->rtable);
-       rt_used = palloc(sizeof(bool) * rt_length);
-       for (i = 0; i < rt_length; i++)
-       {
-               if (check_if_rte_used((Node *) (query->targetList), i + 1, 0) ||
-                       check_if_rte_used(query->qual, i + 1, 0) ||
-                       check_if_rte_used(query->havingQual, i + 1, 0))
-               {
-                       rt_used[i] = TRUE;
-                       rt_numused++;
-               }
-               else
-                       rt_used[i] = FALSE;
-       }
-
-       /* ----------
-        * Now check if any of the used rangetable entries is different
-        * from *NEW* and *OLD*. If so we must provide the FROM clause
-        * later.
-        * ----------
-        */
-       i = 0;
-       foreach(l, query->rtable)
-       {
-               if (!rt_used[i++])
-                       continue;
-
-               rte = (RangeTblEntry *) lfirst(l);
-               if (rte->ref == NULL)
-                       continue;
-               if (strcmp(rte->ref->relname, "*NEW*") == 0)
-                       continue;
-               if (strcmp(rte->ref->relname, "*OLD*") == 0)
-                       continue;
-
-               rt_constonly = FALSE;
-               break;
-       }
-
        /* ----------
         * Build up the query string - first we say SELECT
         * ----------
@@ -947,9 +889,9 @@ get_select_query_def(Query *query, deparse_context *context)
        sep = " ";
        foreach(l, query->targetList)
        {
+               TargetEntry *tle = (TargetEntry *) lfirst(l);
                bool            tell_as = false;
 
-               tle = (TargetEntry *) lfirst(l);
                appendStringInfo(buf, sep);
                sep = ", ";
 
@@ -962,6 +904,7 @@ get_select_query_def(Query *query, deparse_context *context)
                else
                {
                        Var                *var = (Var *) (tle->expr);
+                       RangeTblEntry *rte;
                        char       *attname;
 
                        rte = get_rte_for_var(var, context);
@@ -975,60 +918,8 @@ get_select_query_def(Query *query, deparse_context *context)
                                                         quote_identifier(tle->resdom->resname));
        }
 
-       /* If we need other tables than *NEW* or *OLD* add the FROM clause */
-       if (!rt_constonly && rt_numused > 0)
-       {
-               sep = " FROM ";
-               i = 0;
-               foreach(l, query->rtable)
-               {
-                       if (rt_used[i++])
-                       {
-                               rte = (RangeTblEntry *) lfirst(l);
-
-                               if (rte->ref == NULL)
-                                       continue;
-                               if (strcmp(rte->ref->relname, "*NEW*") == 0)
-                                       continue;
-                               if (strcmp(rte->ref->relname, "*OLD*") == 0)
-                                       continue;
-
-                               appendStringInfo(buf, sep);
-                               sep = ", ";
-                               appendStringInfo(buf, "%s%s",
-                                                                only_marker(rte),
-                                                                quote_identifier(rte->relname));
-
-                               /*
-                                * NOTE: SQL92 says you can't write column aliases unless
-                                * you write a table alias --- so, if there's an alias
-                                * list, make sure we emit a table alias even if it's the
-                                * same as the table's real name.
-                                */
-                               if ((rte->ref != NULL)
-                                       && ((strcmp(rte->relname, rte->ref->relname) != 0)
-                                               || (rte->ref->attrs != NIL)))
-                               {
-                                       appendStringInfo(buf, " %s",
-                                                                        quote_identifier(rte->ref->relname));
-                                       if (rte->ref->attrs != NIL)
-                                       {
-                                               List       *col;
-
-                                               appendStringInfo(buf, " (");
-                                               foreach(col, rte->ref->attrs)
-                                               {
-                                                       if (col != rte->ref->attrs)
-                                                               appendStringInfo(buf, ", ");
-                                                       appendStringInfo(buf, "%s",
-                                                                 quote_identifier(strVal(lfirst(col))));
-                                               }
-                                               appendStringInfoChar(buf, ')');
-                                       }
-                               }
-                       }
-               }
-       }
+       /* Add the FROM clause if needed */
+       get_from_clause(query, context);
 
        /* Add the WHERE clause if given */
        if (query->qual != NULL)
@@ -1066,52 +957,32 @@ get_insert_query_def(Query *query, deparse_context *context)
 {
        StringInfo      buf = context->buf;
        char       *sep;
-       TargetEntry *tle;
-       RangeTblEntry *rte;
-       bool       *rt_used;
-       int                     rt_length;
-       int                     rt_numused = 0;
        bool            rt_constonly = TRUE;
+       RangeTblEntry *rte;
        int                     i;
        List       *l;
 
        /* ----------
         * We need to know if other tables than *NEW* or *OLD*
         * are used in the query. If not, it's an INSERT ... VALUES,
-        * otherwise an INSERT ... SELECT.
+        * otherwise an INSERT ... SELECT.  (Pretty klugy ... fix this
+        * when we redesign querytrees!)
         * ----------
         */
-       rt_length = length(query->rtable);
-       rt_used = palloc(sizeof(bool) * rt_length);
-       for (i = 0; i < rt_length; i++)
-       {
-               if (check_if_rte_used((Node *) (query->targetList), i + 1, 0) ||
-                       check_if_rte_used(query->qual, i + 1, 0) ||
-                       check_if_rte_used(query->havingQual, i + 1, 0))
-               {
-                       rt_used[i] = TRUE;
-                       rt_numused++;
-               }
-               else
-                       rt_used[i] = FALSE;
-       }
-
        i = 0;
        foreach(l, query->rtable)
        {
-               if (!rt_used[i++])
-                       continue;
-
                rte = (RangeTblEntry *) lfirst(l);
-               if (rte->ref == NULL)
-                       continue;
-               if (strcmp(rte->ref->relname, "*NEW*") == 0)
+               i++;
+               if (strcmp(rte->eref->relname, "*NEW*") == 0)
                        continue;
-               if (strcmp(rte->ref->relname, "*OLD*") == 0)
+               if (strcmp(rte->eref->relname, "*OLD*") == 0)
                        continue;
-
-               rt_constonly = FALSE;
-               break;
+               if (rangeTableEntry_used((Node *) query, i, 0))
+               {
+                       rt_constonly = FALSE;
+                       break;
+               }
        }
 
        /* ----------
@@ -1122,11 +993,11 @@ get_insert_query_def(Query *query, deparse_context *context)
        appendStringInfo(buf, "INSERT INTO %s",
                                         quote_identifier(rte->relname));
 
-       /* Add the target list */
+       /* Add the insert-column-names list */
        sep = " (";
        foreach(l, query->targetList)
        {
-               tle = (TargetEntry *) lfirst(l);
+               TargetEntry *tle = (TargetEntry *) lfirst(l);
 
                appendStringInfo(buf, sep);
                sep = ", ";
@@ -1141,7 +1012,7 @@ get_insert_query_def(Query *query, deparse_context *context)
                sep = "";
                foreach(l, query->targetList)
                {
-                       tle = (TargetEntry *) lfirst(l);
+                       TargetEntry *tle = (TargetEntry *) lfirst(l);
 
                        appendStringInfo(buf, sep);
                        sep = ", ";
@@ -1195,6 +1066,9 @@ get_update_query_def(Query *query, deparse_context *context)
                get_tle_expr(tle, context);
        }
 
+       /* Add the FROM clause if needed */
+       get_from_clause(query, context);
+
        /* Finally add a WHERE clause if given */
        if (query->qual != NULL)
        {
@@ -1281,16 +1155,13 @@ get_rule_expr(Node *node, deparse_context *context)
 
                                if (context->varprefix)
                                {
-                                       if (rte->ref == NULL)
-                                               appendStringInfo(buf, "%s.",
-                                                                                quote_identifier(rte->relname));
-                                       else if (strcmp(rte->ref->relname, "*NEW*") == 0)
+                                       if (strcmp(rte->eref->relname, "*NEW*") == 0)
                                                appendStringInfo(buf, "new.");
-                                       else if (strcmp(rte->ref->relname, "*OLD*") == 0)
+                                       else if (strcmp(rte->eref->relname, "*OLD*") == 0)
                                                appendStringInfo(buf, "old.");
                                        else
                                                appendStringInfo(buf, "%s.",
-                                                                       quote_identifier(rte->ref->relname));
+                                                                       quote_identifier(rte->eref->relname));
                                }
                                appendStringInfo(buf, "%s",
                                                  quote_identifier(get_attribute_name(rte->relid,
@@ -1860,6 +1731,165 @@ get_sublink_expr(Node *node, deparse_context *context)
                appendStringInfoChar(buf, ')');
 }
 
+
+/* ----------
+ * get_from_clause                     - Parse back a FROM clause
+ * ----------
+ */
+static void
+get_from_clause(Query *query, deparse_context *context)
+{
+       StringInfo      buf = context->buf;
+       char       *sep;
+       List       *l;
+
+       /*
+        * We use the query's jointree as a guide to what to print.  However,
+        * we must ignore auto-added RTEs that are marked not inFromCl.
+        * Also ignore the rule pseudo-RTEs for NEW and OLD.
+        */
+       sep = " FROM ";
+
+       foreach(l, query->jointree)
+       {
+               Node   *jtnode = (Node *) lfirst(l);
+
+               if (IsA(jtnode, RangeTblRef))
+               {
+                       int                     varno = ((RangeTblRef *) jtnode)->rtindex;
+                       RangeTblEntry *rte = rt_fetch(varno, query->rtable);
+
+                       if (!rte->inFromCl)
+                               continue;
+                       if (strcmp(rte->eref->relname, "*NEW*") == 0)
+                               continue;
+                       if (strcmp(rte->eref->relname, "*OLD*") == 0)
+                               continue;
+               }
+
+               appendStringInfo(buf, sep);
+               get_from_clause_item(jtnode, query, context);
+               sep = ", ";
+       }
+}
+
+static void
+get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
+{
+       StringInfo      buf = context->buf;
+
+       if (IsA(jtnode, RangeTblRef))
+       {
+               int                     varno = ((RangeTblRef *) jtnode)->rtindex;
+               RangeTblEntry *rte = rt_fetch(varno, query->rtable);
+
+               appendStringInfo(buf, "%s%s",
+                                                only_marker(rte),
+                                                quote_identifier(rte->relname));
+               if (rte->alias != NULL)
+               {
+                       appendStringInfo(buf, " %s",
+                                                        quote_identifier(rte->alias->relname));
+                       if (rte->alias->attrs != NIL)
+                       {
+                               List       *col;
+
+                               appendStringInfo(buf, " (");
+                               foreach(col, rte->alias->attrs)
+                               {
+                                       if (col != rte->alias->attrs)
+                                               appendStringInfo(buf, ", ");
+                                       appendStringInfo(buf, "%s",
+                                                                        quote_identifier(strVal(lfirst(col))));
+                               }
+                               appendStringInfoChar(buf, ')');
+                       }
+               }
+       }
+       else if (IsA(jtnode, JoinExpr))
+       {
+               JoinExpr   *j = (JoinExpr *) jtnode;
+
+               appendStringInfoChar(buf, '(');
+               get_from_clause_item(j->larg, query, context);
+               if (j->isNatural)
+                       appendStringInfo(buf, " NATURAL");
+               switch (j->jointype)
+               {
+                       case JOIN_INNER:
+                               if (j->quals)
+                                       appendStringInfo(buf, " JOIN ");
+                               else
+                                       appendStringInfo(buf, " CROSS JOIN ");
+                               break;
+                       case JOIN_LEFT:
+                               appendStringInfo(buf, " LEFT JOIN ");
+                               break;
+                       case JOIN_FULL:
+                               appendStringInfo(buf, " FULL JOIN ");
+                               break;
+                       case JOIN_RIGHT:
+                               appendStringInfo(buf, " RIGHT JOIN ");
+                               break;
+                       case JOIN_UNION:
+                               appendStringInfo(buf, " UNION JOIN ");
+                               break;
+                       default:
+                               elog(ERROR, "get_from_clause_item: unknown join type %d",
+                                        (int) j->jointype);
+               }
+               get_from_clause_item(j->rarg, query, context);
+               if (! j->isNatural)
+               {
+                       if (j->using)
+                       {
+                               List       *col;
+
+                               appendStringInfo(buf, " USING (");
+                               foreach(col, j->using)
+                               {
+                                       if (col != j->using)
+                                               appendStringInfo(buf, ", ");
+                                       appendStringInfo(buf, "%s",
+                                                                        quote_identifier(strVal(lfirst(col))));
+                               }
+                               appendStringInfoChar(buf, ')');
+                       }
+                       else if (j->quals)
+                       {
+                               appendStringInfo(buf, " ON (");
+                               get_rule_expr(j->quals, context);
+                               appendStringInfoChar(buf, ')');
+                       }
+               }
+               appendStringInfoChar(buf, ')');
+               /* Yes, it's correct to put alias after the right paren ... */
+               if (j->alias != NULL)
+               {
+                       appendStringInfo(buf, " %s",
+                                                        quote_identifier(j->alias->relname));
+                       if (j->alias->attrs != NIL)
+                       {
+                               List       *col;
+
+                               appendStringInfo(buf, " (");
+                               foreach(col, j->alias->attrs)
+                               {
+                                       if (col != j->alias->attrs)
+                                               appendStringInfo(buf, ", ");
+                                       appendStringInfo(buf, "%s",
+                                                                        quote_identifier(strVal(lfirst(col))));
+                               }
+                               appendStringInfoChar(buf, ')');
+                       }
+               }
+       }
+       else
+               elog(ERROR, "get_from_clause_item: unexpected node type %d",
+                        nodeTag(jtnode));
+}
+
+
 /* ----------
  * tleIsArrayAssign                    - check for array assignment
  * ----------
@@ -1990,56 +2020,3 @@ get_attribute_name(Oid relid, int2 attnum)
        attStruct = (Form_pg_attribute) GETSTRUCT(atttup);
        return pstrdup(NameStr(attStruct->attname));
 }
-
-
-/* ----------
- * check_if_rte_used
- *             Check a targetlist or qual to see if a given rangetable entry
- *             is used in it
- * ----------
- */
-static bool
-check_if_rte_used(Node *node, Index rt_index, int levelsup)
-{
-       check_if_rte_used_context context;
-
-       context.rt_index = rt_index;
-       context.levelsup = levelsup;
-       return check_if_rte_used_walker(node, &context);
-}
-
-static bool
-check_if_rte_used_walker(Node *node,
-                                                check_if_rte_used_context *context)
-{
-       if (node == NULL)
-               return false;
-       if (IsA(node, Var))
-       {
-               Var                *var = (Var *) node;
-
-               return var->varno == context->rt_index &&
-                       var->varlevelsup == context->levelsup;
-       }
-       if (IsA(node, SubLink))
-       {
-               SubLink    *sublink = (SubLink *) node;
-               Query      *query = (Query *) sublink->subselect;
-
-               /* Recurse into subquery; expression_tree_walker will not */
-               if (check_if_rte_used((Node *) (query->targetList),
-                                                         context->rt_index, context->levelsup + 1) ||
-                       check_if_rte_used(query->qual,
-                                                         context->rt_index, context->levelsup + 1) ||
-                       check_if_rte_used(query->havingQual,
-                                                         context->rt_index, context->levelsup + 1))
-                       return true;
-
-               /*
-                * fall through to let expression_tree_walker examine lefthand
-                * args
-                */
-       }
-       return expression_tree_walker(node, check_if_rte_used_walker,
-                                                                 (void *) context);
-}
index b73ea0e6136e105db47812d3f8c3471aa10fea39..eb067fe50575ff3561b537b2928c7bc12e0617bc 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: catversion.h,v 1.44 2000/09/12 04:49:15 momjian Exp $
+ * $Id: catversion.h,v 1.45 2000/09/12 21:07:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     200009111
+#define CATALOG_VERSION_NO     200009121
 
 #endif
index 7eb6667da03e76b368f7983990184e1292444b74..c4ba3d1f5b6e8be8743d730f6d90e362d2980570 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: execdebug.h,v 1.13 2000/06/15 00:52:07 momjian Exp $
+ * $Id: execdebug.h,v 1.14 2000/09/12 21:07:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #undef EXEC_MERGEJOINDEBUG
  */
 
-/* ----------------
- *             EXEC_MERGEJOINPFREE is a flag which causes merge joins
- *             to pfree intermittant tuples (which is the proper thing)
- *             Not defining this means we avoid menory management problems
- *             at the cost of doing deallocation of stuff only at the
- *             end of the transaction
- * ----------------
-#undef EXEC_MERGEJOINPFREE
- */
-
 /* ----------------
  *             EXEC_DEBUGINTERACTIVE is a flag which enables the
  *             user to issue "DEBUG" commands from an interactive
  *                       only as necessary -cim 10/26/89
  * ----------------------------------------------------------------
  */
-#define T_OR_F(b)                              (b ? "true" : "false")
+#define T_OR_F(b)                              ((b) ? "true" : "false")
 #define NULL_OR_TUPLE(slot)            (TupIsNull(slot) ? "null" : "a tuple")
 
 
-/* #define EXEC_TUPLECOUNT - XXX take out for now for executor stubbing -- jolly*/
 /* ----------------
  *             tuple count debugging defines
  * ----------------
@@ -326,28 +315,31 @@ extern int        NIndexTupleInserted;
 #define MJ1_printf(s, p)                               printf(s, p)
 #define MJ2_printf(s, p1, p2)                  printf(s, p1, p2)
 #define MJ_debugtup(tuple, type)               debugtup(tuple, type, NULL)
-#define MJ_dump(context, state)                        ExecMergeTupleDump(econtext, state)
+#define MJ_dump(state)                                 ExecMergeTupleDump(state)
 #define MJ_DEBUG_QUAL(clause, res) \
   MJ2_printf("  ExecQual(%s, econtext) returns %s\n", \
                         CppAsString(clause), T_OR_F(res));
 
 #define MJ_DEBUG_MERGE_COMPARE(qual, res) \
-  MJ2_printf("  MergeCompare(mergeclauses, %s, ..) returns %s\n", \
+  MJ2_printf("  MergeCompare(mergeclauses, %s, ...) returns %s\n", \
                         CppAsString(qual), T_OR_F(res));
 
 #define MJ_DEBUG_PROC_NODE(slot) \
-  MJ2_printf("  %s = ExecProcNode(innerPlan) returns %s\n", \
+  MJ2_printf("  %s = ExecProcNode(...) returns %s\n", \
                         CppAsString(slot), NULL_OR_TUPLE(slot));
+
 #else
+
 #define MJ_nodeDisplay(l)
 #define MJ_printf(s)
 #define MJ1_printf(s, p)
 #define MJ2_printf(s, p1, p2)
 #define MJ_debugtup(tuple, type)
-#define MJ_dump(context, state)
+#define MJ_dump(state)
 #define MJ_DEBUG_QUAL(clause, res)
 #define MJ_DEBUG_MERGE_COMPARE(qual, res)
 #define MJ_DEBUG_PROC_NODE(slot)
+
 #endif  /* EXEC_MERGEJOINDEBUG */
 
 /* ----------------------------------------------------------------
index 89fed192cdd6b99cc43a6deb3724467218da87ba..6b9457969b1a054fd13006490063e9285d3a192c 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: execdefs.h,v 1.6 2000/01/26 05:58:05 momjian Exp $
+ * $Id: execdefs.h,v 1.7 2000/09/12 21:07:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #define EXEC_MJ_NEXTOUTER                              5
 #define EXEC_MJ_TESTOUTER                              6
 #define EXEC_MJ_NEXTINNER                              7
-#define EXEC_MJ_SKIPINNER                              8
-#define EXEC_MJ_SKIPOUTER                              9
-#define EXEC_MJ_FILLINNER                         10
-#define EXEC_MJ_FILLOUTER                         11
+#define EXEC_MJ_SKIPOUTER_BEGIN                        8
+#define EXEC_MJ_SKIPOUTER_TEST                 9
+#define EXEC_MJ_SKIPOUTER_ADVANCE              10
+#define EXEC_MJ_SKIPINNER_BEGIN                        11
+#define EXEC_MJ_SKIPINNER_TEST                 12
+#define EXEC_MJ_SKIPINNER_ADVANCE              13
+#define EXEC_MJ_ENDOUTER                               14
+#define EXEC_MJ_ENDINNER                               15
 
 #endif  /* EXECDEFS_H */
index 5eb7cbb93bae73664ff370c83360d8ed0499e941..5c330915e754454de0b10c9c778e7b3025792729 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: executor.h,v 1.50 2000/08/24 23:34:09 tgl Exp $
+ * $Id: executor.h,v 1.51 2000/09/12 21:07:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -117,7 +117,9 @@ extern void ExecSetSlotDescriptorIsNew(TupleTableSlot *slot, bool isNew);
 extern void ExecInitResultTupleSlot(EState *estate, CommonState *commonstate);
 extern void ExecInitScanTupleSlot(EState *estate,
                                          CommonScanState *commonscanstate);
-extern void ExecInitOuterTupleSlot(EState *estate, HashJoinState *hashstate);
+extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate);
+extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate,
+                                                                                        TupleDesc tupType);
 
 extern TupleDesc ExecGetTupType(Plan *node);
 extern TupleDesc ExecTypeFromTL(List *targetList);
index 9626dbf8b1c0c658122af32860d5ed3118b0c5b9..83ed6c5234bb3913457e8388480e77d5a65c1971 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: execnodes.h,v 1.48 2000/08/24 03:29:13 tgl Exp $
+ * $Id: execnodes.h,v 1.49 2000/09/12 21:07:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -469,11 +469,18 @@ typedef CommonState JoinState;
 
 /* ----------------
  *      NestLoopState information
+ *
+ *             NeedNewOuter       true if need new outer tuple on next call
+ *             MatchedOuter       true if found a join match for current outer tuple
+ *             NullInnerTupleSlot prepared null tuple for left outer joins
  * ----------------
  */
 typedef struct NestLoopState
 {
        JoinState       jstate;                 /* its first field is NodeTag */
+       bool            nl_NeedNewOuter;
+       bool            nl_MatchedOuter;
+       TupleTableSlot *nl_NullInnerTupleSlot;
 } NestLoopState;
 
 /* ----------------
@@ -482,7 +489,13 @@ typedef struct NestLoopState
  *             OuterSkipQual      outerKey1 < innerKey1 ...
  *             InnerSkipQual      outerKey1 > innerKey1 ...
  *             JoinState                  current "state" of join. see executor.h
+ *             MatchedOuter       true if found a join match for current outer tuple
+ *             MatchedInner       true if found a join match for current inner tuple
+ *             OuterTupleSlot     pointer to slot in tuple table for cur outer tuple
+ *             InnerTupleSlot     pointer to slot in tuple table for cur inner tuple
  *             MarkedTupleSlot    pointer to slot in tuple table for marked tuple
+ *             NullOuterTupleSlot prepared null tuple for right outer joins
+ *             NullInnerTupleSlot prepared null tuple for left outer joins
  * ----------------
  */
 typedef struct MergeJoinState
@@ -491,7 +504,13 @@ typedef struct MergeJoinState
        List       *mj_OuterSkipQual;
        List       *mj_InnerSkipQual;
        int                     mj_JoinState;
+       bool            mj_MatchedOuter;
+       bool            mj_MatchedInner;
+       TupleTableSlot *mj_OuterTupleSlot;
+       TupleTableSlot *mj_InnerTupleSlot;
        TupleTableSlot *mj_MarkedTupleSlot;
+       TupleTableSlot *mj_NullOuterTupleSlot;
+       TupleTableSlot *mj_NullInnerTupleSlot;
 } MergeJoinState;
 
 /* ----------------
@@ -506,6 +525,10 @@ typedef struct MergeJoinState
  *             hj_InnerHashKey                 the inner hash key in the hashjoin condition
  *             hj_OuterTupleSlot               tuple slot for outer tuples
  *             hj_HashTupleSlot                tuple slot for hashed tuples
+ *             hj_NullInnerTupleSlot   prepared null tuple for left outer joins
+ *             hj_NeedNewOuter         true if need new outer tuple on next call
+ *             hj_MatchedOuter         true if found a join match for current outer
+ *             hj_hashdone                             true if hash-table-build phase is done
  * ----------------
  */
 typedef struct HashJoinState
@@ -517,6 +540,10 @@ typedef struct HashJoinState
        Node       *hj_InnerHashKey;
        TupleTableSlot *hj_OuterTupleSlot;
        TupleTableSlot *hj_HashTupleSlot;
+       TupleTableSlot *hj_NullInnerTupleSlot;
+       bool            hj_NeedNewOuter;
+       bool            hj_MatchedOuter;
+       bool            hj_hashdone;
 } HashJoinState;
 
 
index d825c8fe395bcd8f64f4d191277884213608104e..f3929d8b2c67e27560feab1082075adea1ec3e0c 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodes.h,v 1.75 2000/08/24 03:29:13 tgl Exp $
+ * $Id: nodes.h,v 1.76 2000/09/12 21:07:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -68,6 +68,8 @@ typedef enum NodeTag
        T_ArrayRef,
        T_Iter,
        T_RelabelType,
+       T_RangeTblRef,
+       T_JoinExpr,
 
        /*---------------------
         * TAGS FOR PLANNER NODES (relation.h)
@@ -204,7 +206,7 @@ typedef enum NodeTag
        T_A_Indices,
        T_ResTarget,
        T_TypeCast,
-       T_RelExpr,
+       T_RangeSubselect,
        T_SortGroupBy,
        T_RangeVar,
        T_TypeName,
@@ -217,14 +219,14 @@ typedef enum NodeTag
        T_SortClause,
        T_GroupClause,
        T_SubSelectXXX,                         /* not used anymore; this tag# is available */
-       T_JoinExpr,
+       T_oldJoinExprXXX,                       /* not used anymore; this tag# is available */
        T_CaseExpr,
        T_CaseWhen,
        T_RowMark,
        T_FkConstraint,
 
        /*---------------------
-        * TAGS FOR FUNCTION-CALL CONTEXT AND RESULTINFO NODES (cf. fmgr.h)
+        * TAGS FOR FUNCTION-CALL CONTEXT AND RESULTINFO NODES (see fmgr.h)
         *---------------------
         */
        T_TriggerData = 800,            /* in commands/trigger.h */
@@ -310,7 +312,7 @@ typedef double Cost;                        /* execution cost (in page-access units) */
 
 /*
  * CmdType -
- *       enums for type of operation to aid debugging
+ *       enums for type of operation represented by a Query
  *
  * ??? could have put this in parsenodes.h but many files not in the
  *       optimizer also need this...
@@ -329,4 +331,40 @@ typedef enum CmdType
 } CmdType;
 
 
+/*
+ * JoinType -
+ *       enums for types of relation joins
+ *
+ * JoinType determines the exact semantics of joining two relations using
+ * a matching qualification.  For example, it tells what to do with a tuple
+ * that has no match in the other relation.
+ *
+ * This is needed in both parsenodes.h and plannodes.h, so put it here...
+ */
+typedef enum JoinType
+{
+       /*
+        * The canonical kinds of joins
+        */
+       JOIN_INNER,                                     /* matching tuple pairs only */
+       JOIN_LEFT,                                      /* pairs + unmatched outer tuples */
+       JOIN_FULL,                                      /* pairs + unmatched outer + unmatched inner */
+       JOIN_RIGHT,                                     /* pairs + unmatched inner tuples */
+       /*
+        * SQL92 considers UNION JOIN to be a kind of join, so list it here for
+        * parser convenience, even though it's not implemented like a join in
+        * the executor.  (The planner must convert it to an Append plan.)
+        */
+       JOIN_UNION
+       /*
+        * Eventually we will have some additional join types for efficient
+        * support of queries like WHERE foo IN (SELECT bar FROM ...).
+        */
+} JoinType;
+
+#define IS_OUTER_JOIN(jointype) \
+       ((jointype) == JOIN_LEFT || \
+        (jointype) == JOIN_FULL || \
+        (jointype) == JOIN_RIGHT)
+
 #endif  /* NODES_H */
index f6c75c197810f96634539b9173be7b38cf14da24..440a7609d83fddffee462e716dcbc1b018a5a9e4 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsenodes.h,v 1.112 2000/09/12 05:09:50 momjian Exp $
+ * $Id: parsenodes.h,v 1.113 2000/09/12 21:07:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,7 +40,7 @@ typedef struct Query
        Node       *utilityStmt;        /* non-null if this is a non-optimizable
                                                                 * statement */
 
-       int                     resultRelation; /* target relation (index to rtable) */
+       int                     resultRelation; /* target relation (index into rtable) */
        char       *into;                       /* portal (cursor) name */
        bool            isPortal;               /* is this a retrieve into portal? */
        bool            isBinary;               /* binary portal? */
@@ -50,6 +50,8 @@ typedef struct Query
        bool            hasSubLinks;    /* has subquery SubLink */
 
        List       *rtable;                     /* list of range table entries */
+       List       *jointree;           /* table join tree (from the FROM clause) */
+
        List       *targetList;         /* target list (of TargetEntry) */
        Node       *qual;                       /* qualifications applied to tuples */
        List       *rowMark;            /* list of RowMark entries */
@@ -1057,16 +1059,6 @@ typedef struct ResTarget
                                                                 * assign */
 } ResTarget;
 
-/*
- * RelExpr - relation expressions
- */
-typedef struct RelExpr
-{
-       NodeTag         type;
-       char       *relname;            /* the relation name */
-       bool            inh;                    /* inheritance query */
-} RelExpr;
-
 /*
  * SortGroupBy - for ORDER BY clause
  */
@@ -1083,10 +1075,21 @@ typedef struct SortGroupBy
 typedef struct RangeVar
 {
        NodeTag         type;
-       RelExpr    *relExpr;            /* the relation expression */
-       Attr       *name;                       /* the name to be referenced (optional) */
+       char       *relname;            /* the relation name */
+       bool            inh;                    /* expand rel by inheritance? */
+       Attr       *name;                       /* optional table alias & column aliases */
 } RangeVar;
 
+/*
+ * RangeSubselect - subquery appearing in a FROM clause
+ */
+typedef struct RangeSubselect
+{
+       NodeTag         type;
+       Node       *subquery;           /* the untransformed sub-select clause */
+       Attr       *name;                       /* optional table alias & column aliases */
+} RangeSubselect;
+
 /*
  * IndexElem - index parameters (used in CREATE INDEX)
  *
@@ -1114,20 +1117,6 @@ typedef struct DefElem
        Node       *arg;                        /* a (Value *) or a (TypeName *) */
 } DefElem;
 
-/*
- * JoinExpr - for JOIN expressions
- */
-typedef struct JoinExpr
-{
-       NodeTag         type;
-       int                     jointype;
-       bool            isNatural;              /* Natural join? Will need to shape table */
-       Node       *larg;                       /* RangeVar or join expression */
-       Node       *rarg;                       /* RangeVar or join expression */
-       Attr       *alias;                      /* table and column aliases, if any */
-       List       *quals;                      /* qualifiers on join, if any */
-} JoinExpr;
-
 
 /****************************************************************************
  *     Nodes for a Query tree
@@ -1155,11 +1144,12 @@ typedef struct TargetEntry
  *       Some of the following are only used in one of
  *       the parsing, optimizing, execution stages.
  *
- *       eref is the expanded table name and columns for the underlying
- *       relation. Note that for outer join syntax, allowed reference names
- *       could be modified as one evaluates the nested clauses (e.g.
- *       "SELECT ... FROM t1 NATURAL JOIN t2 WHERE ..." forbids explicit mention
- *       of a table name in any reference to the join column.
+ *       alias is an Attr node representing the AS alias-clause attached to the
+ *       FROM expression, or NULL if no clause.
+ *
+ *       eref is the table reference name and column reference names (either
+ *       real or aliases).  This is filled in during parse analysis.  Note that
+ *       system columns (OID etc) are not included in the column list.
  *
  *       inFromCl marks those range variables that are listed in the FROM clause.
  *       In SQL, the query can only refer to range variables listed in the
@@ -1170,29 +1160,17 @@ typedef struct TargetEntry
  *       implicitly-added RTE shouldn't change the namespace for unqualified
  *       column names processed later, and it also shouldn't affect the
  *       expansion of '*'.
- *
- *       inJoinSet marks those range variables that the planner should join
- *       over even if they aren't explicitly referred to in the query.  For
- *       example, "SELECT COUNT(1) FROM tx" should produce the number of rows
- *       in tx.  A more subtle example uses a POSTQUEL implicit RTE:
- *                     SELECT COUNT(1) FROM tx WHERE TRUE OR (tx.f1 = ty.f2)
- *       Here we should get the product of the sizes of tx and ty.  However,
- *       the query optimizer can simplify the WHERE clause to "TRUE", so
- *       ty will no longer be referred to explicitly; without a flag forcing
- *       it to be included in the join, we will get the wrong answer.  So,
- *       a POSTQUEL implicit RTE must be marked inJoinSet but not inFromCl.
  *--------------------
  */
 typedef struct RangeTblEntry
 {
        NodeTag         type;
        char       *relname;            /* real name of the relation */
-       Attr       *ref;                        /* reference names (given in FROM clause) */
-       Attr       *eref;                       /* expanded reference names */
        Oid                     relid;                  /* OID of the relation */
+       Attr       *alias;                      /* user-written alias clause, if any */
+       Attr       *eref;                       /* expanded reference names */
        bool            inh;                    /* inheritance requested? */
        bool            inFromCl;               /* present in FROM clause */
-       bool            inJoinSet;              /* planner must include this rel */
        bool            skipAcl;                /* skip ACL check in executor */
 } RangeTblEntry;
 
index a0e9881dc87235481202aa5479bc7ad16e4e62a7..4e0bcfc70539c4ec87208d9439353c45e70f8113 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_list.h,v 1.18 2000/06/09 01:44:26 momjian Exp $
+ * $Id: pg_list.h,v 1.19 2000/09/12 21:07:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -101,6 +101,7 @@ extern List *nconc(List *list1, List *list2);
 extern List *lcons(void *datum, List *list);
 extern List *lconsi(int datum, List *list);
 extern bool member(void *datum, List *list);
+extern bool ptrMember(void *datum, List *list);
 extern bool intMember(int datum, List *list);
 extern Value *makeInteger(long i);
 extern Value *makeFloat(char *numericStr);
index e348d25b2baca11299f0c2ad06ca057f019d172d..cf93b9dee17e979288e05800deec63c50f59e8ca 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: plannodes.h,v 1.41 2000/07/12 02:37:33 tgl Exp $
+ * $Id: plannodes.h,v 1.42 2000/09/12 21:07:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -82,7 +82,7 @@ typedef struct Plan
                                                                 * individual nodes point to one EState
                                                                 * for the whole top-level plan */
        List       *targetlist;
-       List       *qual;                       /* Node* or List* ?? */
+       List       *qual;                       /* implicitly-ANDed qual conditions */
        struct Plan *lefttree;
        struct Plan *righttree;
        List       *extParam;           /* indices of _all_ _external_ PARAM_EXEC
@@ -210,9 +210,26 @@ typedef struct TidScan
 
 /* ----------------
  *             Join node
+ *
+ * jointype:   rule for joining tuples from left and right subtrees
+ * joinqual:   qual conditions that came from JOIN/ON or JOIN/USING
+ *                             (plan.qual contains conditions that came from WHERE)
+ *
+ * When jointype is INNER, joinqual and plan.qual are semantically
+ * interchangeable.  For OUTER jointypes, the two are *not* interchangeable;
+ * only joinqual is used to determine whether a match has been found for
+ * the purpose of deciding whether to generate null-extended tuples.
+ * (But plan.qual is still applied before actually returning a tuple.)
+ * For an outer join, only joinquals are allowed to be used as the merge
+ * or hash condition of a merge or hash join.
  * ----------------
  */
-typedef Plan Join;
+typedef struct Join
+{
+       Plan            plan;
+       JoinType        jointype;
+       List       *joinqual;           /* JOIN quals (in addition to plan.qual) */
+} Join;
 
 /* ----------------
  *             nest loop join node
@@ -245,7 +262,6 @@ typedef struct HashJoin
        List       *hashclauses;
        Oid                     hashjoinop;
        HashJoinState *hashjoinstate;
-       bool            hashdone;
 } HashJoin;
 
 /* ---------------
index 0ef350687dc41a6466dc0192260b875c20424aba..bc17773642c276561ae50cc757f1eec167938b4a 100644 (file)
@@ -1,13 +1,16 @@
 /*-------------------------------------------------------------------------
  *
  * primnodes.h
- *       Definitions for parse tree/query tree ("primitive") nodes.
+ *       Definitions for "primitive" node types, those that are used in more
+ *       than one of the parse/plan/execute stages of the query pipeline.
+ *       Currently, these are mostly nodes for executable expressions
+ *       and join trees.
  *
  *
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: primnodes.h,v 1.47 2000/08/24 03:29:13 tgl Exp $
+ * $Id: primnodes.h,v 1.48 2000/09/12 21:07:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -98,6 +101,12 @@ typedef struct Fjoin
        BoolPtr         fj_alwaysDone;
 } Fjoin;
 
+
+/* ----------------------------------------------------------------
+ *                                     node types for executable expressions
+ * ----------------------------------------------------------------
+ */
+
 /* ----------------
  * Expr
  *             typeOid                 - oid of the type of this expression
@@ -155,7 +164,7 @@ typedef struct Var
        AttrNumber      varattno;
        Oid                     vartype;
        int32           vartypmod;
-       Index           varlevelsup;    /* erased by upper optimizer */
+       Index           varlevelsup;
        Index           varnoold;               /* mainly for debugging --- see above */
        AttrNumber      varoattno;
 } Var;
@@ -480,4 +489,76 @@ typedef struct RelabelType
        int32           resulttypmod;
 } RelabelType;
 
+
+/* ----------------------------------------------------------------
+ *                                     node types for join trees
+ *
+ * The leaves of a join tree structure are RangeTblRef nodes.  Above
+ * these, JoinExpr nodes can appear to denote a specific kind of join
+ * or qualified join.  A join tree can also contain List nodes --- a list
+ * implies an unqualified cross-product join of its members.  The planner
+ * is allowed to combine the elements of a list using whatever join order
+ * seems good to it.  At present, JoinExpr nodes are always joined in
+ * exactly the order implied by the tree structure (except the planner
+ * may choose to swap inner and outer members of a join pair).
+ *
+ * NOTE: currently, the planner only supports a List at the top level of
+ * a join tree.  Should generalize this to allow Lists at lower levels.
+ *
+ * NOTE: the qualification expressions present in JoinExpr nodes are
+ * *in addition to* the query's main WHERE clause.  For outer joins there
+ * is a real semantic difference between a join qual and a WHERE clause,
+ * though if all joins are inner joins they are interchangeable.
+ *
+ * NOTE: in the raw output of gram.y, a join tree contains RangeVar and
+ * RangeSubselect nodes, which are both replaced by RangeTblRef nodes
+ * during the parse analysis phase.
+ * ----------------------------------------------------------------
+ */
+
+/*
+ * RangeTblRef - reference to an entry in the query's rangetable
+ *
+ * We could use direct pointers to the RT entries and skip having these
+ * nodes, but multiple pointers to the same node in a querytree cause
+ * lots of headaches, so it seems better to store an index into the RT.
+ */
+typedef struct RangeTblRef
+{
+       NodeTag         type;
+       int                     rtindex;
+} RangeTblRef;
+
+/*----------
+ * JoinExpr - for SQL JOIN expressions
+ *
+ * isNatural, using, and quals are interdependent.  The user can write only
+ * one of NATURAL, USING(), or ON() (this is enforced by the grammar).
+ * If he writes NATURAL then parse analysis generates the equivalent USING()
+ * list, and from that fills in "quals" with the right equality comparisons.
+ * If he writes USING() then "quals" is filled with equality comparisons.
+ * If he writes ON() then only "quals" is set.  Note that NATURAL/USING
+ * are not equivalent to ON() since they also affect the output column list.
+ *
+ * alias is an Attr node representing the AS alias-clause attached to the
+ * join expression, or NULL if no clause.  During parse analysis, colnames
+ * is filled with a list of String nodes giving the column names (real or
+ * alias) of the output of the join, and colvars is filled with a list of
+ * expressions that can be copied to reference the output columns.
+ *----------
+ */
+typedef struct JoinExpr
+{
+       NodeTag         type;
+       JoinType        jointype;               /* type of join */
+       bool            isNatural;              /* Natural join? Will need to shape table */
+       Node       *larg;                       /* left subtree */
+       Node       *rarg;                       /* right subtree */
+       List       *using;                      /* USING clause, if any (list of String) */
+       Node       *quals;                      /* qualifiers on join, if any */
+       struct Attr *alias;                     /* user-written alias clause, if any */
+       List       *colnames;           /* output column names (list of String) */
+       List       *colvars;            /* output column nodes (list of expressions) */
+} JoinExpr;
+
 #endif  /* PRIMNODES_H */
index b7d651310669e0e342ad13614ead91eeb737c76e..767e2e114e0502a44c174c28487e9358bce0661b 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: relation.h,v 1.47 2000/04/12 17:16:40 momjian Exp $
+ * $Id: relation.h,v 1.48 2000/09/12 21:07:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -73,6 +73,9 @@ typedef enum CostSelector
  *                                     participates (only used for base rels)
  *             baserestrictcost - Estimated cost of evaluating the baserestrictinfo
  *                                     clauses at a single tuple (only used for base rels)
+ *             outerjoinset - If the rel appears within the nullable side of an outer
+ *                                     join, the list of all relids participating in the highest
+ *                                     such outer join; else NIL (only used for base rels)
  *             joininfo  - List of JoinInfo nodes, containing info about each join
  *                                     clause in which this relation participates
  *             innerjoin - List of Path nodes that represent indices that may be used
@@ -94,6 +97,10 @@ typedef enum CostSelector
  * We store baserestrictcost in the RelOptInfo (for base relations) because
  * we know we will need it at least once (to price the sequential scan)
  * and may need it multiple times to price index scans.
+ *
+ * outerjoinset is used to ensure correct placement of WHERE clauses that
+ * apply to outer-joined relations; we must not apply such WHERE clauses
+ * until after the outer join is performed.
  */
 
 typedef struct RelOptInfo
@@ -124,6 +131,7 @@ typedef struct RelOptInfo
        List       *baserestrictinfo;           /* RestrictInfo structures (if
                                                                                 * base rel) */
        Cost            baserestrictcost;               /* cost of evaluating the above */
+       Relids          outerjoinset;                   /* integer list of base relids */
        List       *joininfo;           /* JoinInfo structures */
        List       *innerjoin;          /* potential indexscans for nestloop joins */
 
@@ -263,6 +271,9 @@ typedef struct Path
  * that refer to values of other rels, so those other rels must be
  * included in the outer joinrel in order to make a usable join.
  *
+ * 'alljoinquals' is also used only for inner paths of nestloop joins.
+ * This flag is TRUE iff all the indexquals came from JOIN/ON conditions.
+ *
  * 'rows' is the estimated result tuple count for the indexscan.  This
  * is the same as path.parent->rows for a simple indexscan, but it is
  * different for a nestloop inner path, because the additional indexquals
@@ -277,6 +288,7 @@ typedef struct IndexPath
        List       *indexqual;
        ScanDirection indexscandir;
        Relids          joinrelids;             /* other rels mentioned in indexqual */
+       bool            alljoinquals;   /* all indexquals derived from JOIN conds? */
        double          rows;                   /* estimated number of result tuples */
 } IndexPath;
 
@@ -295,8 +307,11 @@ typedef struct JoinPath
 {
        Path            path;
 
+       JoinType        jointype;
+
        Path       *outerjoinpath;      /* path for the outer side of the join */
        Path       *innerjoinpath;      /* path for the inner side of the join */
+
        List       *joinrestrictinfo;           /* RestrictInfos to apply to join */
 
        /*
@@ -375,11 +390,12 @@ typedef struct HashPath
  * The clause cannot actually be applied until we have built a join rel
  * containing all the base rels it references, however.
  *
- * When we construct a join rel that describes exactly the set of base rels
- * referenced in a multi-relation restriction clause, we place that clause
- * into the joinrestrictinfo lists of paths for the join rel.  It will be
- * applied at that join level, and will not propagate any further up the
- * join tree.  (Note: the "predicate migration" code was once intended to
+ * When we construct a join rel that includes all the base rels referenced
+ * in a multi-relation restriction clause, we place that clause into the
+ * joinrestrictinfo lists of paths for the join rel, if neither left nor
+ * right sub-path includes all base rels referenced in the clause.  The clause
+ * will be applied at that join level, and will not propagate any further up
+ * the join tree.  (Note: the "predicate migration" code was once intended to
  * push restriction clauses up and down the plan tree based on evaluation
  * costs, but it's dead code and is unlikely to be resurrected in the
  * foreseeable future.)
@@ -394,18 +410,30 @@ typedef struct HashPath
  * or hashjoin clauses are fairly limited --- the code for each kind of
  * path is responsible for identifying the restrict clauses it can use
  * and ignoring the rest.  Clauses not implemented by an indexscan,
- * mergejoin, or hashjoin will be placed in the qpqual field of the
- * final Plan node, where they will be enforced by general-purpose
+ * mergejoin, or hashjoin will be placed in the plan qual or joinqual field
+ * of the final Plan node, where they will be enforced by general-purpose
  * qual-expression-evaluation code.  (But we are still entitled to count
  * their selectivity when estimating the result tuple count, if we
  * can guess what it is...)
+ *
+ * When dealing with outer joins we must distinguish between qual clauses
+ * that came from WHERE and those that came from JOIN/ON or JOIN/USING.
+ * (For inner joins there's no semantic difference and we can treat the
+ * clauses interchangeably.)  Both kinds of quals are stored as RestrictInfo
+ * nodes during planning, but there's a flag to indicate where they came from.
+ * Note also that when outer joins are present, a qual clause may be treated
+ * as referencing more rels than it really does.  This trick ensures that the
+ * qual will be evaluated at the right level of the join tree --- we don't
+ * want quals from WHERE to be evaluated until after the outer join is done.
  */
 
 typedef struct RestrictInfo
 {
        NodeTag         type;
 
-       Expr       *clause;                     /* the represented clause of WHERE cond */
+       Expr       *clause;                     /* the represented clause of WHERE or JOIN */
+
+       bool            isjoinqual;             /* TRUE if clause came from JOIN/ON */
 
        /* only used if clause is an OR clause: */
        List       *subclauseindices;           /* indexes matching subclauses */
@@ -437,7 +465,7 @@ typedef struct RestrictInfo
 typedef struct JoinInfo
 {
        NodeTag         type;
-       Relids          unjoined_relids;/* some rels not yet part of my RelOptInfo */
+       Relids          unjoined_relids; /* some rels not yet part of my RelOptInfo */
        List       *jinfo_restrictinfo;         /* relevant RestrictInfos */
 } JoinInfo;
 
index 1b2bcd920551f0d8850f2761548ecc15d97121d1..62bb401193d5250d620fd498e7143ad2bc41a0b6 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: clauses.h,v 1.38 2000/08/13 02:50:26 tgl Exp $
+ * $Id: clauses.h,v 1.39 2000/09/12 21:07:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -73,9 +73,11 @@ extern void CommuteClause(Expr *clause);
 extern Node *eval_const_expressions(Node *node);
 
 extern bool expression_tree_walker(Node *node, bool (*walker) (),
-                                                                                          void *context);
+                                                                  void *context);
 extern Node *expression_tree_mutator(Node *node, Node *(*mutator) (),
-                                                                                                void *context);
+                                                                        void *context);
+extern bool query_tree_walker(Query *query, bool (*walker) (),
+                                                         void *context);
 
 #define is_subplan(clause)     ((clause) != NULL && \
                                                         IsA(clause, Expr) && \
index b8788851e2b4dd151f613a7e7957f930020691eb..0bf57ef0cc5cb6bcfb828b6cd81dffadfd93bd82 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pathnode.h,v 1.27 2000/04/12 17:16:42 momjian Exp $
+ * $Id: pathnode.h,v 1.28 2000/09/12 21:07:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -34,26 +34,29 @@ extern IndexPath *create_index_path(Query *root, RelOptInfo *rel,
 extern TidPath *create_tidscan_path(RelOptInfo *rel, List *tideval);
 
 extern NestPath *create_nestloop_path(RelOptInfo *joinrel,
-                                        Path *outer_path,
-                                        Path *inner_path,
-                                        List *restrict_clauses,
-                                        List *pathkeys);
+                                                                         JoinType jointype,
+                                                                         Path *outer_path,
+                                                                         Path *inner_path,
+                                                                         List *restrict_clauses,
+                                                                         List *pathkeys);
 
 extern MergePath *create_mergejoin_path(RelOptInfo *joinrel,
-                                         Path *outer_path,
-                                         Path *inner_path,
-                                         List *restrict_clauses,
-                                         List *pathkeys,
-                                         List *mergeclauses,
-                                         List *outersortkeys,
-                                         List *innersortkeys);
+                                                                               JoinType jointype,
+                                                                               Path *outer_path,
+                                                                               Path *inner_path,
+                                                                               List *restrict_clauses,
+                                                                               List *pathkeys,
+                                                                               List *mergeclauses,
+                                                                               List *outersortkeys,
+                                                                               List *innersortkeys);
 
 extern HashPath *create_hashjoin_path(RelOptInfo *joinrel,
-                                        Path *outer_path,
-                                        Path *inner_path,
-                                        List *restrict_clauses,
-                                        List *hashclauses,
-                                        Selectivity innerdisbursion);
+                                                                         JoinType jointype,
+                                                                         Path *outer_path,
+                                                                         Path *inner_path,
+                                                                         List *restrict_clauses,
+                                                                         List *hashclauses,
+                                                                         Selectivity innerdisbursion);
 
 /*
  * prototypes for relnode.c
index 66520d6a897bfd0953293ed30ceeeb7527538d59..35eb3190f1c8d9a1839666b9843d8036f1a30e44 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: paths.h,v 1.46 2000/07/24 03:10:54 tgl Exp $
+ * $Id: paths.h,v 1.47 2000/09/12 21:07:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -61,21 +61,23 @@ extern void create_tidscan_paths(Query *root, RelOptInfo *rel);
  *        routines to create join paths
  */
 extern void add_paths_to_joinrel(Query *root, RelOptInfo *joinrel,
-                                        RelOptInfo *outerrel,
-                                        RelOptInfo *innerrel,
-                                        List *restrictlist);
+                                                                RelOptInfo *outerrel,
+                                                                RelOptInfo *innerrel,
+                                                                JoinType jointype,
+                                                                List *restrictlist);
 
 /*
  * joinrels.c
  *       routines to determine which relations to join
  */
-extern void make_rels_by_joins(Query *root, int level);
-extern RelOptInfo *make_rels_by_clause_joins(Query *root,
-                                                 RelOptInfo *old_rel,
-                                                 List *other_rels);
-extern RelOptInfo *make_rels_by_clauseless_joins(Query *root,
-                                                         RelOptInfo *old_rel,
-                                                         List *other_rels);
+extern List *make_rels_by_joins(Query *root, int level, List **joinrels);
+extern List *make_rels_by_clause_joins(Query *root,
+                                                                          RelOptInfo *old_rel,
+                                                                          List *other_rels);
+extern List *make_rels_by_clauseless_joins(Query *root,
+                                                                                  RelOptInfo *old_rel,
+                                                                                  List *other_rels);
+extern RelOptInfo *make_rel_from_jointree(Query *root, Node *jtnode);
 
 /*
  * pathkeys.c
@@ -110,7 +112,7 @@ extern List *make_pathkeys_for_sortclauses(List *sortclauses,
 extern List *find_mergeclauses_for_pathkeys(List *pathkeys,
                                                           List *restrictinfos);
 extern List *make_pathkeys_for_mergeclauses(Query *root,
-                                                          List *mergeclauses,
-                                                          List *tlist);
+                                                                                       List *mergeclauses,
+                                                                                       RelOptInfo *rel);
 
 #endif  /* PATHS_H */
index 723543c437c5a35835ac3fc32d37658c2dde1905..43c93978cdfff4a8c2095daa6d744754b13cee54 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: planmain.h,v 1.43 2000/07/24 03:10:54 tgl Exp $
+ * $Id: planmain.h,v 1.44 2000/09/12 21:07:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,8 +20,7 @@
 /*
  * prototypes for plan/planmain.c
  */
-extern Plan *query_planner(Query *root, List *tlist, List *qual,
-                         double tuple_fraction);
+extern Plan *query_planner(Query *root, List *tlist, double tuple_fraction);
 
 /*
  * prototypes for plan/createplan.c
@@ -40,9 +39,10 @@ extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
 /*
  * prototypes for plan/initsplan.c
  */
-extern void make_var_only_tlist(Query *root, List *tlist);
+extern void build_base_rel_tlists(Query *root, List *tlist);
+extern Relids add_join_quals_to_rels(Query *root, Node *jtnode);
 extern void add_restrict_and_join_to_rels(Query *root, List *clauses);
-extern void add_missing_rels_to_query(Query *root);
+extern List *add_missing_rels_to_query(Query *root, Node *jtnode);
 extern void process_implied_equality(Query *root, Node *item1, Node *item2,
                                                                         Oid sortop1, Oid sortop2);
 
@@ -58,6 +58,7 @@ extern void fix_opids(Node *node);
  * prep/prepkeyset.c
  */
 extern bool _use_keyset_query_optimizer;
+
 extern void transformKeySetQuery(Query *origNode);
 
 #endif  /* PLANMAIN_H */
index 3d94854e03b6b01e1412a591730a310c24505203..2e1d4d66f999424a8742f53d6ab02c857f5ad8c1 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: restrictinfo.h,v 1.8 2000/01/26 05:58:21 momjian Exp $
+ * $Id: restrictinfo.h,v 1.9 2000/09/12 21:07:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,5 +18,7 @@
 
 extern bool restriction_is_or_clause(RestrictInfo *restrictinfo);
 extern List *get_actual_clauses(List *restrictinfo_list);
+extern void get_actual_join_clauses(List *restrictinfo_list,
+                                                                       List **joinquals, List **otherquals);
 
 #endif  /* RESTRICTINFO_H */
index 02c95745fee356d10881bbdee69f11d4aa971c02..54d6e869ad9a57eee4f7ac3bdd80113293b73835 100644 (file)
@@ -1,28 +1,31 @@
 /*-------------------------------------------------------------------------
  *
  * gramparse.h
- *       scanner support routines.  used by both the bootstrap lexer
- * as well as the normal lexer
+ *       Declarations for routines exported from lexer and parser files.
+ *
  *
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: gramparse.h,v 1.12 2000/04/12 17:16:44 momjian Exp $
+ * $Id: gramparse.h,v 1.13 2000/09/12 21:07:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #ifndef GRAMPARSE_H
-#define GRAMPARSE_H                            /* include once only */
+#define GRAMPARSE_H
 
-/* from scan.l */
-extern void init_io(void);
+/* from parser.c */
 extern int     yylex(void);
+
+/* from scan.l */
+extern void scanner_init(void);
+extern int     base_yylex(void);
 extern void yyerror(const char *message);
 
 /* from gram.y */
-extern Oid     param_type(int t);
 extern void parser_init(Oid *typev, int nargs);
+extern Oid     param_type(int t);
 extern int     yyparse(void);
 
 #endif  /* GRAMPARSE_H */
index b9a868d442030310c1a1c9b98d2c6a5c11267c60..fd1cfdb3604efe82a6a4181b4e9972daf592156f 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_clause.h,v 1.18 2000/06/09 01:44:29 momjian Exp $
+ * $Id: parse_clause.h,v 1.19 2000/09/12 21:07:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,7 +17,8 @@
 #include "parser/parse_node.h"
 
 extern void makeRangeTable(ParseState *pstate, List *frmList);
-extern void setTargetTable(ParseState *pstate, char *relname, bool inh);
+extern void setTargetTable(ParseState *pstate, char *relname,
+                                                  bool inh, bool inJoinSet);
 extern Node *transformWhereClause(ParseState *pstate, Node *where);
 extern List *transformGroupClause(ParseState *pstate, List *grouplist,
                                         List *targetlist);
index be96652cfb21810e4a89ff3b6d6f9261922dc39d..d221c600c8ee555897cfcf4d12338ca9ea2aa264 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_func.h,v 1.26 2000/08/20 00:44:17 tgl Exp $
+ * $Id: parse_func.h,v 1.27 2000/09/12 21:07:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -39,11 +39,11 @@ typedef struct _CandidateList
 }                 *CandidateList;
 
 extern Node *ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr,
-                                               int *curr_resno, int precedence);
+                                                                        int precedence);
 extern Node *ParseFuncOrColumn(ParseState *pstate,
-                                 char *funcname, List *fargs,
-                                 bool agg_star, bool agg_distinct,
-                                 int *curr_resno, int precedence);
+                                                          char *funcname, List *fargs,
+                                                          bool agg_star, bool agg_distinct,
+                                                          int precedence);
 
 extern bool func_get_detail(char *funcname, int nargs, Oid *argtypes,
                                                        Oid *funcid, Oid *rettype,
index d4231e8819d5eceed9344e360e4f67c9de147e8b..002391d65308e541beb2187cdb7bdc004c89bf08 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_node.h,v 1.20 2000/05/12 01:33:52 tgl Exp $
+ * $Id: parse_node.h,v 1.21 2000/09/12 21:07:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "nodes/parsenodes.h"
 #include "utils/rel.h"
 
-/* State information used during parse analysis
- * p_join_quals is a list of untransformed qualification expressions
- * (implicitly ANDed together) found in the FROM clause.
- * Needs to be available later to merge with other qualifiers from the
- * WHERE clause.
+/*
+ * State information used during parse analysis
  */
 typedef struct ParseState
 {
-       int                     p_last_resno;
-       List       *p_rtable;
-       struct ParseState *parentParseState;
+       struct ParseState *parentParseState; /* stack link */
+       List       *p_rtable;           /* range table so far */
+       List       *p_jointree;         /* join tree so far */
+       int                     p_last_resno;   /* last targetlist resno assigned */
        bool            p_hasAggs;
        bool            p_hasSubLinks;
        bool            p_is_insert;
        bool            p_is_update;
-       bool            p_is_rule;
-       bool            p_in_where_clause;
        Relation        p_target_relation;
        RangeTblEntry *p_target_rangetblentry;
-       List       *p_shape;
-       List       *p_alias;
-       List       *p_join_quals;
 } ParseState;
 
 extern ParseState *make_parsestate(ParseState *parentParseState);
 extern Expr *make_op(char *opname, Node *ltree, Node *rtree);
 extern Node *make_operand(char *opname, Node *tree,
                         Oid orig_typeId, Oid target_typeId);
-extern Var *make_var(ParseState *pstate, Oid relid, char *refname,
-                char *attrname);
+extern Var *make_var(ParseState *pstate, RangeTblEntry *rte, int attrno);
 extern ArrayRef *transformArraySubscripts(ParseState *pstate,
                                                 Node *arrayBase,
                                                 List *indirection,
index 4f89bcc65c344ca7e28c09e42457346e672c483d..7c7a04844e4226cffba7f21965cc389ee796c348 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_relation.h,v 1.18 2000/06/08 22:37:53 momjian Exp $
+ * $Id: parse_relation.h,v 1.19 2000/09/12 21:07:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "parser/parse_node.h"
 
-extern RangeTblEntry *refnameRangeTableEntry(ParseState *pstate, char *refname);
+extern Node *refnameRangeOrJoinEntry(ParseState *pstate,
+                                                                        char *refname,
+                                                                        int *sublevels_up);
+extern RangeTblEntry *refnameRangeTableEntry(ParseState *pstate,
+                                                                                        char *refname);
 extern int refnameRangeTablePosn(ParseState *pstate,
-                                         char *refname,
-                                         int *sublevels_up);
-extern RangeTblEntry *colnameRangeTableEntry(ParseState *pstate, char *colname);
+                                                                char *refname,
+                                                                int *sublevels_up);
+extern int RTERangeTablePosn(ParseState *pstate,
+                                                        RangeTblEntry *rte,
+                                                        int *sublevels_up);
+extern JoinExpr *scanJoinTreeForRefname(Node *jtnode, char *refname);
+extern Node *colnameToVar(ParseState *pstate, char *colname);
+extern Node *qualifiedNameToVar(ParseState *pstate, char *refname,
+                                                               char *colname, bool implicitRTEOK);
 extern RangeTblEntry *addRangeTableEntry(ParseState *pstate,
-                                  char *relname,
-                                  Attr *ref,
-                                  bool inh,
-                                  bool inFromCl,
-                                  bool inJoinSet);
-extern Attr *expandTable(ParseState *pstate, char *refname, bool getaliases);
-extern List *expandAll(ParseState *pstate, char *relname, Attr *ref,
-                 int *this_resno);
+                                                                                char *relname,
+                                                                                Attr *alias,
+                                                                                bool inh,
+                                                                                bool inFromCl);
+extern void addRTEtoJoinTree(ParseState *pstate, RangeTblEntry *rte);
+extern RangeTblEntry *addImplicitRTE(ParseState *pstate, char *relname);
+extern void expandRTE(ParseState *pstate, RangeTblEntry *rte,
+                                         List **colnames, List **colvars);
+extern List *expandRelAttrs(ParseState *pstate, RangeTblEntry *rte);
+extern List *expandJoinAttrs(ParseState *pstate, JoinExpr *join,
+                                                        int sublevels_up);
 extern int     attnameAttNum(Relation rd, char *a);
 extern int     specialAttNum(char *a);
 extern Oid     attnumTypeId(Relation rd, int attid);
-extern void warnAutoRange(ParseState *pstate, char *refname);
 
 #endif  /* PARSE_RELATION_H */
index 277bc32a504883f50adbd4a2294523f9c57f1aa5..ff727cfd07a6229f26852d09f7ed3125b393deb4 100644 (file)
@@ -8,41 +8,26 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsetree.h,v 1.10 2000/06/12 19:40:51 momjian Exp $
+ * $Id: parsetree.h,v 1.11 2000/09/12 21:07:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef PARSETREE_H
-#define PARSETREE_H                            /* include once only */
+#define PARSETREE_H
 
 #include "nodes/parsenodes.h"
 #include "nodes/pg_list.h"
 
 /* ----------------
- *             need pg_list.h for definitions of CAR(), etc. macros
+ *             need pg_list.h for definitions of nth(), etc.
  * ----------------
  */
 
 /* ----------------
  *             range table macros
- *
- *     parse tree:
- *             (root targetlist qual)
- *              ^^^^
- *     parse root:
- *             (numlevels cmdtype resrel rangetable priority ruleinfo nestdotinfo)
- *                                                               ^^^^^^^^^^
- *     range table:
- *             (rtentry ...)
- *     rtentry:
  * ----------------
  */
 
-#define rt_relname(rt_entry) \
-         ((!strcmp(((rt_entry)->ref->relname),"*OLD*") ||\
-               !strcmp(((rt_entry)->ref->relname),"*NEW*")) ? ((rt_entry)->ref->relname) : \
-               ((char *)(rt_entry)->relname))
-
 /*
  *             rt_fetch
  *             rt_store
  *
  */
 #define rt_fetch(rangetable_index, rangetable) \
-       ((RangeTblEntry*)nth((rangetable_index)-1, rangetable))
+       ((RangeTblEntry*) nth((rangetable_index)-1, rangetable))
 
 #define rt_store(rangetable_index, rangetable, rt) \
        set_nth(rangetable, (rangetable_index)-1, rt)
 
 /*
  *             getrelid
- *             getrelname
  *
  *             Given the range index of a relation, return the corresponding
- *             relation id or relation name.
+ *             relation OID.
  */
 #define getrelid(rangeindex,rangetable) \
-       ((RangeTblEntry*)nth((rangeindex)-1, rangetable))->relid
-
-#define getrelname(rangeindex, rangetable) \
-       rt_relname((RangeTblEntry*)nth((rangeindex)-1, rangetable))
+       (rt_fetch(rangeindex, rangetable)->relid)
 
 #endif  /* PARSETREE_H */
index 7af0f3932ec28337b80f5e5d9cc187f5db298e97..5271d78717c6b153ed59c5892087683c109ba79f 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: rewriteHandler.h,v 1.12 2000/01/26 05:58:30 momjian Exp $
+ * $Id: rewriteHandler.h,v 1.13 2000/09/12 21:07:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,9 +16,8 @@
 
 #include "nodes/parsenodes.h"
 
-struct _rewrite_meta_knowledge
+typedef struct RewriteInfo
 {
-       List       *rt;
        int                     rt_index;
        bool            instead_flag;
        int                     event;
@@ -28,9 +27,7 @@ struct _rewrite_meta_knowledge
        Query      *rule_action;
        Node       *rule_qual;
        bool            nothing;
-};
-
-typedef struct _rewrite_meta_knowledge RewriteInfo;
+} RewriteInfo;
 
 
 extern List *QueryRewrite(Query *parsetree);
index af052a65510849b42cd891e634c420623604fb26..c41519acb8c9b9ce6393f7f8d0c394e3912e14c6 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: rewriteManip.h,v 1.21 2000/04/12 17:16:50 momjian Exp $
+ * $Id: rewriteManip.h,v 1.22 2000/09/12 21:07:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,6 +22,12 @@ extern void ChangeVarNodes(Node *node, int old_varno, int new_varno,
                           int sublevels_up);
 extern void IncrementVarSublevelsUp(Node *node, int delta_sublevels_up,
                                                int min_sublevels_up);
+
+extern bool rangeTableEntry_used(Node *node, int rt_index,
+                                                                int sublevels_up);
+extern bool attribute_used(Node *node, int rt_index, int attno,
+                                                  int sublevels_up);
+
 extern void AddQual(Query *parsetree, Node *qual);
 extern void AddHavingQual(Query *parsetree, Node *havingQual);
 extern void AddNotQual(Query *parsetree, Node *qual);
index 0ad8be9eec48f9b6b7c1b6cc73e566f724de88ec..2b53bc699dc8a03b70fed206c45ffbe082b71f8b 100644 (file)
@@ -157,28 +157,28 @@ SELECT COALESCE(a.f, b.i, b.j)
  case  
 -------
   10.1
-  20.2
- -30.3
-     1
   10.1
-  20.2
- -30.3
-     2
   10.1
-  20.2
- -30.3
-     3
   10.1
-  20.2
- -30.3
-     2
   10.1
-  20.2
- -30.3
-     1
   10.1
   20.2
+  20.2
+  20.2
+  20.2
+  20.2
+  20.2
+ -30.3
+ -30.3
+ -30.3
  -30.3
+ -30.3
+ -30.3
+     1
+     2
+     3
+     2
+     1
     -6
 (24 rows)
 
@@ -197,28 +197,28 @@ SELECT '' AS Five, NULLIF(a.i,b.i) AS "NULLIF(a.i,b.i)",
  five | NULLIF(a.i,b.i) | NULLIF(b.i,4) 
 ------+-----------------+---------------
       |                 |             1
-      |               2 |             1
-      |               3 |             1
-      |               4 |             1
       |               1 |             2
-      |                 |             2
-      |               3 |             2
-      |               4 |             2
       |               1 |             3
-      |               2 |             3
-      |                 |             3
-      |               4 |             3
       |               1 |             2
-      |                 |             2
-      |               3 |             2
-      |               4 |             2
       |                 |             1
-      |               2 |             1
-      |               3 |             1
-      |               4 |             1
       |               1 |              
+      |               2 |             1
+      |                 |             2
+      |               2 |             3
+      |                 |             2
+      |               2 |             1
       |               2 |              
+      |               3 |             1
+      |               3 |             2
+      |                 |             3
+      |               3 |             2
+      |               3 |             1
       |               3 |              
+      |               4 |             1
+      |               4 |             2
+      |               4 |             3
+      |               4 |             2
+      |               4 |             1
       |               4 |              
 (24 rows)
 
index 4e0651e46cd66a0a50f77294f25bac162f1a249a..67ab299c39eab7973a8258bb50ed0f8831b0d822 100644 (file)
@@ -163,28 +163,28 @@ SELECT '' AS twentyfour, b.f1 + p.f1 AS translation
  twentyfour |       translation       
 ------------+-------------------------
             | (2,2),(0,0)
-            | (3,3),(1,1)
-            | (2.5,3.5),(2.5,2.5)
-            | (3,3),(3,3)
             | (-8,2),(-10,0)
-            | (-7,3),(-9,1)
-            | (-7.5,3.5),(-7.5,2.5)
-            | (-7,3),(-7,3)
             | (-1,6),(-3,4)
-            | (0,7),(-2,5)
-            | (-0.5,7.5),(-0.5,6.5)
-            | (0,7),(0,7)
             | (7.1,36.5),(5.1,34.5)
-            | (8.1,37.5),(6.1,35.5)
-            | (7.6,38),(7.6,37)
-            | (8.1,37.5),(8.1,37.5)
             | (-3,-10),(-5,-12)
-            | (-2,-9),(-4,-11)
-            | (-2.5,-8.5),(-2.5,-9.5)
-            | (-2,-9),(-2,-9)
             | (12,12),(10,10)
+            | (3,3),(1,1)
+            | (-7,3),(-9,1)
+            | (0,7),(-2,5)
+            | (8.1,37.5),(6.1,35.5)
+            | (-2,-9),(-4,-11)
             | (13,13),(11,11)
+            | (2.5,3.5),(2.5,2.5)
+            | (-7.5,3.5),(-7.5,2.5)
+            | (-0.5,7.5),(-0.5,6.5)
+            | (7.6,38),(7.6,37)
+            | (-2.5,-8.5),(-2.5,-9.5)
             | (12.5,13.5),(12.5,12.5)
+            | (3,3),(3,3)
+            | (-7,3),(-7,3)
+            | (0,7),(0,7)
+            | (8.1,37.5),(8.1,37.5)
+            | (-2,-9),(-2,-9)
             | (13,13),(13,13)
 (24 rows)
 
@@ -193,28 +193,28 @@ SELECT '' AS twentyfour, b.f1 - p.f1 AS translation
  twentyfour |        translation        
 ------------+---------------------------
             | (2,2),(0,0)
-            | (3,3),(1,1)
-            | (2.5,3.5),(2.5,2.5)
-            | (3,3),(3,3)
             | (12,2),(10,0)
-            | (13,3),(11,1)
-            | (12.5,3.5),(12.5,2.5)
-            | (13,3),(13,3)
             | (5,-2),(3,-4)
-            | (6,-1),(4,-3)
-            | (5.5,-0.5),(5.5,-1.5)
-            | (6,-1),(6,-1)
             | (-3.1,-32.5),(-5.1,-34.5)
-            | (-2.1,-31.5),(-4.1,-33.5)
-            | (-2.6,-31),(-2.6,-32)
-            | (-2.1,-31.5),(-2.1,-31.5)
             | (7,14),(5,12)
-            | (8,15),(6,13)
-            | (7.5,15.5),(7.5,14.5)
-            | (8,15),(8,15)
             | (-8,-8),(-10,-10)
+            | (3,3),(1,1)
+            | (13,3),(11,1)
+            | (6,-1),(4,-3)
+            | (-2.1,-31.5),(-4.1,-33.5)
+            | (8,15),(6,13)
             | (-7,-7),(-9,-9)
+            | (2.5,3.5),(2.5,2.5)
+            | (12.5,3.5),(12.5,2.5)
+            | (5.5,-0.5),(5.5,-1.5)
+            | (-2.6,-31),(-2.6,-32)
+            | (7.5,15.5),(7.5,14.5)
             | (-7.5,-6.5),(-7.5,-7.5)
+            | (3,3),(3,3)
+            | (13,3),(13,3)
+            | (6,-1),(6,-1)
+            | (-2.1,-31.5),(-2.1,-31.5)
+            | (8,15),(8,15)
             | (-7,-7),(-7,-7)
 (24 rows)
 
@@ -223,29 +223,29 @@ SELECT '' AS twentyfour, b.f1 * p.f1 AS rotation
    FROM BOX_TBL b, POINT_TBL p;
  twentyfour |          rotation           
 ------------+-----------------------------
-            | (0,0),(0,0)
-            | (0,0),(0,0)
-            | (0,0),(0,0)
             | (0,0),(0,0)
             | (-0,0),(-20,-20)
-            | (-10,-10),(-30,-30)
-            | (-25,-25),(-25,-35)
-            | (-30,-30),(-30,-30)
             | (-0,2),(-14,0)
-            | (-7,3),(-21,1)
-            | (-17.5,2.5),(-21.5,-0.5)
-            | (-21,3),(-21,3)
             | (0,79.2),(-58.8,0)
-            | (-29.4,118.8),(-88.2,39.6)
-            | (-73.5,104.1),(-108,99)
-            | (-88.2,118.8),(-88.2,118.8)
             | (14,-0),(0,-34)
-            | (21,-17),(7,-51)
-            | (29.5,-42.5),(17.5,-47.5)
-            | (21,-51),(21,-51)
             | (0,40),(0,0)
+            | (0,0),(0,0)
+            | (-10,-10),(-30,-30)
+            | (-7,3),(-21,1)
+            | (-29.4,118.8),(-88.2,39.6)
+            | (21,-17),(7,-51)
             | (0,60),(0,20)
+            | (0,0),(0,0)
+            | (-25,-25),(-25,-35)
+            | (-17.5,2.5),(-21.5,-0.5)
+            | (-73.5,104.1),(-108,99)
+            | (29.5,-42.5),(17.5,-47.5)
             | (0,60),(-10,50)
+            | (0,0),(0,0)
+            | (-30,-30),(-30,-30)
+            | (-21,3),(-21,3)
+            | (-88.2,118.8),(-88.2,118.8)
+            | (21,-51),(21,-51)
             | (0,60),(0,60)
 (24 rows)
 
index c2e689dea96a30b8af897908b948b2236f6e6d93..6ad08de4154ea60b09ffab9d0691f7e5116e2bae 100644 (file)
@@ -163,28 +163,28 @@ SELECT '' AS twentyfour, b.f1 + p.f1 AS translation
  twentyfour |       translation       
 ------------+-------------------------
             | (2,2),(0,0)
-            | (3,3),(1,1)
-            | (2.5,3.5),(2.5,2.5)
-            | (3,3),(3,3)
             | (-8,2),(-10,0)
-            | (-7,3),(-9,1)
-            | (-7.5,3.5),(-7.5,2.5)
-            | (-7,3),(-7,3)
             | (-1,6),(-3,4)
-            | (0,7),(-2,5)
-            | (-0.5,7.5),(-0.5,6.5)
-            | (0,7),(0,7)
             | (7.1,36.5),(5.1,34.5)
-            | (8.1,37.5),(6.1,35.5)
-            | (7.6,38),(7.6,37)
-            | (8.1,37.5),(8.1,37.5)
             | (-3,-10),(-5,-12)
-            | (-2,-9),(-4,-11)
-            | (-2.5,-8.5),(-2.5,-9.5)
-            | (-2,-9),(-2,-9)
             | (12,12),(10,10)
+            | (3,3),(1,1)
+            | (-7,3),(-9,1)
+            | (0,7),(-2,5)
+            | (8.1,37.5),(6.1,35.5)
+            | (-2,-9),(-4,-11)
             | (13,13),(11,11)
+            | (2.5,3.5),(2.5,2.5)
+            | (-7.5,3.5),(-7.5,2.5)
+            | (-0.5,7.5),(-0.5,6.5)
+            | (7.6,38),(7.6,37)
+            | (-2.5,-8.5),(-2.5,-9.5)
             | (12.5,13.5),(12.5,12.5)
+            | (3,3),(3,3)
+            | (-7,3),(-7,3)
+            | (0,7),(0,7)
+            | (8.1,37.5),(8.1,37.5)
+            | (-2,-9),(-2,-9)
             | (13,13),(13,13)
 (24 rows)
 
@@ -193,28 +193,28 @@ SELECT '' AS twentyfour, b.f1 - p.f1 AS translation
  twentyfour |        translation        
 ------------+---------------------------
             | (2,2),(0,0)
-            | (3,3),(1,1)
-            | (2.5,3.5),(2.5,2.5)
-            | (3,3),(3,3)
             | (12,2),(10,0)
-            | (13,3),(11,1)
-            | (12.5,3.5),(12.5,2.5)
-            | (13,3),(13,3)
             | (5,-2),(3,-4)
-            | (6,-1),(4,-3)
-            | (5.5,-0.5),(5.5,-1.5)
-            | (6,-1),(6,-1)
             | (-3.1,-32.5),(-5.1,-34.5)
-            | (-2.1,-31.5),(-4.1,-33.5)
-            | (-2.6,-31),(-2.6,-32)
-            | (-2.1,-31.5),(-2.1,-31.5)
             | (7,14),(5,12)
-            | (8,15),(6,13)
-            | (7.5,15.5),(7.5,14.5)
-            | (8,15),(8,15)
             | (-8,-8),(-10,-10)
+            | (3,3),(1,1)
+            | (13,3),(11,1)
+            | (6,-1),(4,-3)
+            | (-2.1,-31.5),(-4.1,-33.5)
+            | (8,15),(6,13)
             | (-7,-7),(-9,-9)
+            | (2.5,3.5),(2.5,2.5)
+            | (12.5,3.5),(12.5,2.5)
+            | (5.5,-0.5),(5.5,-1.5)
+            | (-2.6,-31),(-2.6,-32)
+            | (7.5,15.5),(7.5,14.5)
             | (-7.5,-6.5),(-7.5,-7.5)
+            | (3,3),(3,3)
+            | (13,3),(13,3)
+            | (6,-1),(6,-1)
+            | (-2.1,-31.5),(-2.1,-31.5)
+            | (8,15),(8,15)
             | (-7,-7),(-7,-7)
 (24 rows)
 
@@ -223,29 +223,29 @@ SELECT '' AS twentyfour, b.f1 * p.f1 AS rotation
    FROM BOX_TBL b, POINT_TBL p;
  twentyfour |          rotation           
 ------------+-----------------------------
-            | (0,0),(0,0)
-            | (0,0),(0,0)
-            | (0,0),(0,0)
             | (0,0),(0,0)
             | (-0,0),(-20,-20)
-            | (-10,-10),(-30,-30)
-            | (-25,-25),(-25,-35)
-            | (-30,-30),(-30,-30)
             | (-0,2),(-14,0)
-            | (-7,3),(-21,1)
-            | (-17.5,2.5),(-21.5,-0.5)
-            | (-21,3),(-21,3)
             | (0,79.2),(-58.8,0)
-            | (-29.4,118.8),(-88.2,39.6)
-            | (-73.5,104.1),(-108,99)
-            | (-88.2,118.8),(-88.2,118.8)
             | (14,-0),(0,-34)
-            | (21,-17),(7,-51)
-            | (29.5,-42.5),(17.5,-47.5)
-            | (21,-51),(21,-51)
             | (0,40),(0,0)
+            | (0,0),(0,0)
+            | (-10,-10),(-30,-30)
+            | (-7,3),(-21,1)
+            | (-29.4,118.8),(-88.2,39.6)
+            | (21,-17),(7,-51)
             | (0,60),(0,20)
+            | (0,0),(0,0)
+            | (-25,-25),(-25,-35)
+            | (-17.5,2.5),(-21.5,-0.5)
+            | (-73.5,104.1),(-108,99)
+            | (29.5,-42.5),(17.5,-47.5)
             | (0,60),(-10,50)
+            | (0,0),(0,0)
+            | (-30,-30),(-30,-30)
+            | (-21,3),(-21,3)
+            | (-88.2,118.8),(-88.2,118.8)
+            | (21,-51),(21,-51)
             | (0,60),(0,60)
 (24 rows)
 
index 169709e37bc03e9aa2432f587036f6392be806ed..78940c084687df17c802cbed507ff31bfb9c6175 100644 (file)
@@ -163,28 +163,28 @@ SELECT '' AS twentyfour, b.f1 + p.f1 AS translation
  twentyfour |       translation       
 ------------+-------------------------
             | (2,2),(0,0)
-            | (3,3),(1,1)
-            | (2.5,3.5),(2.5,2.5)
-            | (3,3),(3,3)
             | (-8,2),(-10,0)
-            | (-7,3),(-9,1)
-            | (-7.5,3.5),(-7.5,2.5)
-            | (-7,3),(-7,3)
             | (-1,6),(-3,4)
-            | (0,7),(-2,5)
-            | (-0.5,7.5),(-0.5,6.5)
-            | (0,7),(0,7)
             | (7.1,36.5),(5.1,34.5)
-            | (8.1,37.5),(6.1,35.5)
-            | (7.6,38),(7.6,37)
-            | (8.1,37.5),(8.1,37.5)
             | (-3,-10),(-5,-12)
-            | (-2,-9),(-4,-11)
-            | (-2.5,-8.5),(-2.5,-9.5)
-            | (-2,-9),(-2,-9)
             | (12,12),(10,10)
+            | (3,3),(1,1)
+            | (-7,3),(-9,1)
+            | (0,7),(-2,5)
+            | (8.1,37.5),(6.1,35.5)
+            | (-2,-9),(-4,-11)
             | (13,13),(11,11)
+            | (2.5,3.5),(2.5,2.5)
+            | (-7.5,3.5),(-7.5,2.5)
+            | (-0.5,7.5),(-0.5,6.5)
+            | (7.6,38),(7.6,37)
+            | (-2.5,-8.5),(-2.5,-9.5)
             | (12.5,13.5),(12.5,12.5)
+            | (3,3),(3,3)
+            | (-7,3),(-7,3)
+            | (0,7),(0,7)
+            | (8.1,37.5),(8.1,37.5)
+            | (-2,-9),(-2,-9)
             | (13,13),(13,13)
 (24 rows)
 
@@ -193,28 +193,28 @@ SELECT '' AS twentyfour, b.f1 - p.f1 AS translation
  twentyfour |        translation        
 ------------+---------------------------
             | (2,2),(0,0)
-            | (3,3),(1,1)
-            | (2.5,3.5),(2.5,2.5)
-            | (3,3),(3,3)
             | (12,2),(10,0)
-            | (13,3),(11,1)
-            | (12.5,3.5),(12.5,2.5)
-            | (13,3),(13,3)
             | (5,-2),(3,-4)
-            | (6,-1),(4,-3)
-            | (5.5,-0.5),(5.5,-1.5)
-            | (6,-1),(6,-1)
             | (-3.1,-32.5),(-5.1,-34.5)
-            | (-2.1,-31.5),(-4.1,-33.5)
-            | (-2.6,-31),(-2.6,-32)
-            | (-2.1,-31.5),(-2.1,-31.5)
             | (7,14),(5,12)
-            | (8,15),(6,13)
-            | (7.5,15.5),(7.5,14.5)
-            | (8,15),(8,15)
             | (-8,-8),(-10,-10)
+            | (3,3),(1,1)
+            | (13,3),(11,1)
+            | (6,-1),(4,-3)
+            | (-2.1,-31.5),(-4.1,-33.5)
+            | (8,15),(6,13)
             | (-7,-7),(-9,-9)
+            | (2.5,3.5),(2.5,2.5)
+            | (12.5,3.5),(12.5,2.5)
+            | (5.5,-0.5),(5.5,-1.5)
+            | (-2.6,-31),(-2.6,-32)
+            | (7.5,15.5),(7.5,14.5)
             | (-7.5,-6.5),(-7.5,-7.5)
+            | (3,3),(3,3)
+            | (13,3),(13,3)
+            | (6,-1),(6,-1)
+            | (-2.1,-31.5),(-2.1,-31.5)
+            | (8,15),(8,15)
             | (-7,-7),(-7,-7)
 (24 rows)
 
@@ -223,29 +223,29 @@ SELECT '' AS twentyfour, b.f1 * p.f1 AS rotation
    FROM BOX_TBL b, POINT_TBL p;
  twentyfour |          rotation           
 ------------+-----------------------------
-            | (0,0),(0,0)
-            | (0,0),(0,0)
-            | (0,0),(0,0)
             | (0,0),(0,0)
             | (0,0),(-20,-20)
-            | (-10,-10),(-30,-30)
-            | (-25,-25),(-25,-35)
-            | (-30,-30),(-30,-30)
             | (0,2),(-14,0)
-            | (-7,3),(-21,1)
-            | (-17.5,2.5),(-21.5,-0.5)
-            | (-21,3),(-21,3)
             | (0,79.2),(-58.8,0)
-            | (-29.4,118.8),(-88.2,39.6)
-            | (-73.5,104.1),(-108,99)
-            | (-88.2,118.8),(-88.2,118.8)
             | (14,0),(0,-34)
-            | (21,-17),(7,-51)
-            | (29.5,-42.5),(17.5,-47.5)
-            | (21,-51),(21,-51)
             | (0,40),(0,0)
+            | (0,0),(0,0)
+            | (-10,-10),(-30,-30)
+            | (-7,3),(-21,1)
+            | (-29.4,118.8),(-88.2,39.6)
+            | (21,-17),(7,-51)
             | (0,60),(0,20)
+            | (0,0),(0,0)
+            | (-25,-25),(-25,-35)
+            | (-17.5,2.5),(-21.5,-0.5)
+            | (-73.5,104.1),(-108,99)
+            | (29.5,-42.5),(17.5,-47.5)
             | (0,60),(-10,50)
+            | (0,0),(0,0)
+            | (-30,-30),(-30,-30)
+            | (-21,3),(-21,3)
+            | (-88.2,118.8),(-88.2,118.8)
+            | (21,-51),(21,-51)
             | (0,60),(0,60)
 (24 rows)
 
index 113733d9420ebbf76bd12118166a33ae9c127545..914d9e775282c8b62820e1d3c499162ddd8ebd15 100644 (file)
@@ -163,28 +163,28 @@ SELECT '' AS twentyfour, b.f1 + p.f1 AS translation
  twentyfour |       translation       
 ------------+-------------------------
             | (2,2),(0,0)
-            | (3,3),(1,1)
-            | (2.5,3.5),(2.5,2.5)
-            | (3,3),(3,3)
             | (-8,2),(-10,0)
-            | (-7,3),(-9,1)
-            | (-7.5,3.5),(-7.5,2.5)
-            | (-7,3),(-7,3)
             | (-1,6),(-3,4)
-            | (0,7),(-2,5)
-            | (-0.5,7.5),(-0.5,6.5)
-            | (0,7),(0,7)
             | (7.1,36.5),(5.1,34.5)
-            | (8.1,37.5),(6.1,35.5)
-            | (7.6,38),(7.6,37)
-            | (8.1,37.5),(8.1,37.5)
             | (-3,-10),(-5,-12)
-            | (-2,-9),(-4,-11)
-            | (-2.5,-8.5),(-2.5,-9.5)
-            | (-2,-9),(-2,-9)
             | (12,12),(10,10)
+            | (3,3),(1,1)
+            | (-7,3),(-9,1)
+            | (0,7),(-2,5)
+            | (8.1,37.5),(6.1,35.5)
+            | (-2,-9),(-4,-11)
             | (13,13),(11,11)
+            | (2.5,3.5),(2.5,2.5)
+            | (-7.5,3.5),(-7.5,2.5)
+            | (-0.5,7.5),(-0.5,6.5)
+            | (7.6,38),(7.6,37)
+            | (-2.5,-8.5),(-2.5,-9.5)
             | (12.5,13.5),(12.5,12.5)
+            | (3,3),(3,3)
+            | (-7,3),(-7,3)
+            | (0,7),(0,7)
+            | (8.1,37.5),(8.1,37.5)
+            | (-2,-9),(-2,-9)
             | (13,13),(13,13)
 (24 rows)
 
@@ -193,28 +193,28 @@ SELECT '' AS twentyfour, b.f1 - p.f1 AS translation
  twentyfour |        translation        
 ------------+---------------------------
             | (2,2),(0,0)
-            | (3,3),(1,1)
-            | (2.5,3.5),(2.5,2.5)
-            | (3,3),(3,3)
             | (12,2),(10,0)
-            | (13,3),(11,1)
-            | (12.5,3.5),(12.5,2.5)
-            | (13,3),(13,3)
             | (5,-2),(3,-4)
-            | (6,-1),(4,-3)
-            | (5.5,-0.5),(5.5,-1.5)
-            | (6,-1),(6,-1)
             | (-3.1,-32.5),(-5.1,-34.5)
-            | (-2.1,-31.5),(-4.1,-33.5)
-            | (-2.6,-31),(-2.6,-32)
-            | (-2.1,-31.5),(-2.1,-31.5)
             | (7,14),(5,12)
-            | (8,15),(6,13)
-            | (7.5,15.5),(7.5,14.5)
-            | (8,15),(8,15)
             | (-8,-8),(-10,-10)
+            | (3,3),(1,1)
+            | (13,3),(11,1)
+            | (6,-1),(4,-3)
+            | (-2.1,-31.5),(-4.1,-33.5)
+            | (8,15),(6,13)
             | (-7,-7),(-9,-9)
+            | (2.5,3.5),(2.5,2.5)
+            | (12.5,3.5),(12.5,2.5)
+            | (5.5,-0.5),(5.5,-1.5)
+            | (-2.6,-31),(-2.6,-32)
+            | (7.5,15.5),(7.5,14.5)
             | (-7.5,-6.5),(-7.5,-7.5)
+            | (3,3),(3,3)
+            | (13,3),(13,3)
+            | (6,-1),(6,-1)
+            | (-2.1,-31.5),(-2.1,-31.5)
+            | (8,15),(8,15)
             | (-7,-7),(-7,-7)
 (24 rows)
 
@@ -223,29 +223,29 @@ SELECT '' AS twentyfour, b.f1 * p.f1 AS rotation
    FROM BOX_TBL b, POINT_TBL p;
  twentyfour |          rotation           
 ------------+-----------------------------
-            | (0,0),(0,0)
-            | (0,0),(0,0)
-            | (0,0),(0,0)
             | (0,0),(0,0)
             | (0,0),(-20,-20)
-            | (-10,-10),(-30,-30)
-            | (-25,-25),(-25,-35)
-            | (-30,-30),(-30,-30)
             | (0,2),(-14,0)
-            | (-7,3),(-21,1)
-            | (-17.5,2.5),(-21.5,-0.5)
-            | (-21,3),(-21,3)
             | (0,79.2),(-58.8,0)
-            | (-29.4,118.8),(-88.2,39.6)
-            | (-73.5,104.1),(-108,99)
-            | (-88.2,118.8),(-88.2,118.8)
             | (14,0),(0,-34)
-            | (21,-17),(7,-51)
-            | (29.5,-42.5),(17.5,-47.5)
-            | (21,-51),(21,-51)
             | (0,40),(0,0)
+            | (0,0),(0,0)
+            | (-10,-10),(-30,-30)
+            | (-7,3),(-21,1)
+            | (-29.4,118.8),(-88.2,39.6)
+            | (21,-17),(7,-51)
             | (0,60),(0,20)
+            | (0,0),(0,0)
+            | (-25,-25),(-25,-35)
+            | (-17.5,2.5),(-21.5,-0.5)
+            | (-73.5,104.1),(-108,99)
+            | (29.5,-42.5),(17.5,-47.5)
             | (0,60),(-10,50)
+            | (0,0),(0,0)
+            | (-30,-30),(-30,-30)
+            | (-21,3),(-21,3)
+            | (-88.2,118.8),(-88.2,118.8)
+            | (21,-51),(21,-51)
             | (0,60),(0,60)
 (24 rows)
 
index 07017606bad897d05d30a48092064661ca38e725..f7ab9e6509692280ffe39dd8254ec02d99028830 100644 (file)
@@ -163,28 +163,28 @@ SELECT '' AS twentyfour, b.f1 + p.f1 AS translation
  twentyfour |       translation       
 ------------+-------------------------
             | (2,2),(0,0)
-            | (3,3),(1,1)
-            | (2.5,3.5),(2.5,2.5)
-            | (3,3),(3,3)
             | (-8,2),(-10,0)
-            | (-7,3),(-9,1)
-            | (-7.5,3.5),(-7.5,2.5)
-            | (-7,3),(-7,3)
             | (-1,6),(-3,4)
-            | (0,7),(-2,5)
-            | (-0.5,7.5),(-0.5,6.5)
-            | (0,7),(0,7)
             | (7.1,36.5),(5.1,34.5)
-            | (8.1,37.5),(6.1,35.5)
-            | (7.6,38),(7.6,37)
-            | (8.1,37.5),(8.1,37.5)
             | (-3,-10),(-5,-12)
-            | (-2,-9),(-4,-11)
-            | (-2.5,-8.5),(-2.5,-9.5)
-            | (-2,-9),(-2,-9)
             | (12,12),(10,10)
+            | (3,3),(1,1)
+            | (-7,3),(-9,1)
+            | (0,7),(-2,5)
+            | (8.1,37.5),(6.1,35.5)
+            | (-2,-9),(-4,-11)
             | (13,13),(11,11)
+            | (2.5,3.5),(2.5,2.5)
+            | (-7.5,3.5),(-7.5,2.5)
+            | (-0.5,7.5),(-0.5,6.5)
+            | (7.6,38),(7.6,37)
+            | (-2.5,-8.5),(-2.5,-9.5)
             | (12.5,13.5),(12.5,12.5)
+            | (3,3),(3,3)
+            | (-7,3),(-7,3)
+            | (0,7),(0,7)
+            | (8.1,37.5),(8.1,37.5)
+            | (-2,-9),(-2,-9)
             | (13,13),(13,13)
 (24 rows)
 
@@ -193,28 +193,28 @@ SELECT '' AS twentyfour, b.f1 - p.f1 AS translation
  twentyfour |        translation        
 ------------+---------------------------
             | (2,2),(0,0)
-            | (3,3),(1,1)
-            | (2.5,3.5),(2.5,2.5)
-            | (3,3),(3,3)
             | (12,2),(10,0)
-            | (13,3),(11,1)
-            | (12.5,3.5),(12.5,2.5)
-            | (13,3),(13,3)
             | (5,-2),(3,-4)
-            | (6,-1),(4,-3)
-            | (5.5,-0.5),(5.5,-1.5)
-            | (6,-1),(6,-1)
             | (-3.1,-32.5),(-5.1,-34.5)
-            | (-2.1,-31.5),(-4.1,-33.5)
-            | (-2.6,-31),(-2.6,-32)
-            | (-2.1,-31.5),(-2.1,-31.5)
             | (7,14),(5,12)
-            | (8,15),(6,13)
-            | (7.5,15.5),(7.5,14.5)
-            | (8,15),(8,15)
             | (-8,-8),(-10,-10)
+            | (3,3),(1,1)
+            | (13,3),(11,1)
+            | (6,-1),(4,-3)
+            | (-2.1,-31.5),(-4.1,-33.5)
+            | (8,15),(6,13)
             | (-7,-7),(-9,-9)
+            | (2.5,3.5),(2.5,2.5)
+            | (12.5,3.5),(12.5,2.5)
+            | (5.5,-0.5),(5.5,-1.5)
+            | (-2.6,-31),(-2.6,-32)
+            | (7.5,15.5),(7.5,14.5)
             | (-7.5,-6.5),(-7.5,-7.5)
+            | (3,3),(3,3)
+            | (13,3),(13,3)
+            | (6,-1),(6,-1)
+            | (-2.1,-31.5),(-2.1,-31.5)
+            | (8,15),(8,15)
             | (-7,-7),(-7,-7)
 (24 rows)
 
@@ -223,29 +223,29 @@ SELECT '' AS twentyfour, b.f1 * p.f1 AS rotation
    FROM BOX_TBL b, POINT_TBL p;
  twentyfour |          rotation           
 ------------+-----------------------------
-            | (0,0),(0,0)
-            | (0,0),(0,0)
-            | (0,0),(0,0)
             | (0,0),(0,0)
             | (-0,0),(-20,-20)
-            | (-10,-10),(-30,-30)
-            | (-25,-25),(-25,-35)
-            | (-30,-30),(-30,-30)
             | (-0,2),(-14,0)
-            | (-7,3),(-21,1)
-            | (-17.5,2.5),(-21.5,-0.5)
-            | (-21,3),(-21,3)
             | (0,79.2),(-58.8,0)
-            | (-29.4,118.8),(-88.2,39.6)
-            | (-73.5,104.1),(-108,99)
-            | (-88.2,118.8),(-88.2,118.8)
             | (14,-0),(0,-34)
-            | (21,-17),(7,-51)
-            | (29.5,-42.5),(17.5,-47.5)
-            | (21,-51),(21,-51)
             | (0,40),(0,0)
+            | (0,0),(0,0)
+            | (-10,-10),(-30,-30)
+            | (-7,3),(-21,1)
+            | (-29.4,118.8),(-88.2,39.6)
+            | (21,-17),(7,-51)
             | (0,60),(0,20)
+            | (0,0),(0,0)
+            | (-25,-25),(-25,-35)
+            | (-17.5,2.5),(-21.5,-0.5)
+            | (-73.5,104.1),(-108,99)
+            | (29.5,-42.5),(17.5,-47.5)
             | (0,60),(-10,50)
+            | (0,0),(0,0)
+            | (-30,-30),(-30,-30)
+            | (-21,3),(-21,3)
+            | (-88.2,118.8),(-88.2,118.8)
+            | (21,-51),(21,-51)
             | (0,60),(0,60)
 (24 rows)
 
index 1b0714b69522fa6888d6ca1daebee53ad2c2df30..3c2e4557ffb3f14e543da5978761702cad3dccde 100644 (file)
@@ -163,28 +163,28 @@ SELECT '' AS twentyfour, b.f1 + p.f1 AS translation
  twentyfour |       translation       
 ------------+-------------------------
             | (2,2),(0,0)
-            | (3,3),(1,1)
-            | (2.5,3.5),(2.5,2.5)
-            | (3,3),(3,3)
             | (-8,2),(-10,0)
-            | (-7,3),(-9,1)
-            | (-7.5,3.5),(-7.5,2.5)
-            | (-7,3),(-7,3)
             | (-1,6),(-3,4)
-            | (0,7),(-2,5)
-            | (-0.5,7.5),(-0.5,6.5)
-            | (0,7),(0,7)
             | (7.1,36.5),(5.1,34.5)
-            | (8.1,37.5),(6.1,35.5)
-            | (7.6,38),(7.6,37)
-            | (8.1,37.5),(8.1,37.5)
             | (-3,-10),(-5,-12)
-            | (-2,-9),(-4,-11)
-            | (-2.5,-8.5),(-2.5,-9.5)
-            | (-2,-9),(-2,-9)
             | (12,12),(10,10)
+            | (3,3),(1,1)
+            | (-7,3),(-9,1)
+            | (0,7),(-2,5)
+            | (8.1,37.5),(6.1,35.5)
+            | (-2,-9),(-4,-11)
             | (13,13),(11,11)
+            | (2.5,3.5),(2.5,2.5)
+            | (-7.5,3.5),(-7.5,2.5)
+            | (-0.5,7.5),(-0.5,6.5)
+            | (7.6,38),(7.6,37)
+            | (-2.5,-8.5),(-2.5,-9.5)
             | (12.5,13.5),(12.5,12.5)
+            | (3,3),(3,3)
+            | (-7,3),(-7,3)
+            | (0,7),(0,7)
+            | (8.1,37.5),(8.1,37.5)
+            | (-2,-9),(-2,-9)
             | (13,13),(13,13)
 (24 rows)
 
@@ -193,28 +193,28 @@ SELECT '' AS twentyfour, b.f1 - p.f1 AS translation
  twentyfour |        translation        
 ------------+---------------------------
             | (2,2),(0,0)
-            | (3,3),(1,1)
-            | (2.5,3.5),(2.5,2.5)
-            | (3,3),(3,3)
             | (12,2),(10,0)
-            | (13,3),(11,1)
-            | (12.5,3.5),(12.5,2.5)
-            | (13,3),(13,3)
             | (5,-2),(3,-4)
-            | (6,-1),(4,-3)
-            | (5.5,-0.5),(5.5,-1.5)
-            | (6,-1),(6,-1)
             | (-3.1,-32.5),(-5.1,-34.5)
-            | (-2.1,-31.5),(-4.1,-33.5)
-            | (-2.6,-31),(-2.6,-32)
-            | (-2.1,-31.5),(-2.1,-31.5)
             | (7,14),(5,12)
-            | (8,15),(6,13)
-            | (7.5,15.5),(7.5,14.5)
-            | (8,15),(8,15)
             | (-8,-8),(-10,-10)
+            | (3,3),(1,1)
+            | (13,3),(11,1)
+            | (6,-1),(4,-3)
+            | (-2.1,-31.5),(-4.1,-33.5)
+            | (8,15),(6,13)
             | (-7,-7),(-9,-9)
+            | (2.5,3.5),(2.5,2.5)
+            | (12.5,3.5),(12.5,2.5)
+            | (5.5,-0.5),(5.5,-1.5)
+            | (-2.6,-31),(-2.6,-32)
+            | (7.5,15.5),(7.5,14.5)
             | (-7.5,-6.5),(-7.5,-7.5)
+            | (3,3),(3,3)
+            | (13,3),(13,3)
+            | (6,-1),(6,-1)
+            | (-2.1,-31.5),(-2.1,-31.5)
+            | (8,15),(8,15)
             | (-7,-7),(-7,-7)
 (24 rows)
 
@@ -223,29 +223,29 @@ SELECT '' AS twentyfour, b.f1 * p.f1 AS rotation
    FROM BOX_TBL b, POINT_TBL p;
  twentyfour |          rotation           
 ------------+-----------------------------
-            | (0,0),(0,0)
-            | (0,0),(0,0)
-            | (0,0),(0,0)
             | (0,0),(0,0)
             | (-0,0),(-20,-20)
-            | (-10,-10),(-30,-30)
-            | (-25,-25),(-25,-35)
-            | (-30,-30),(-30,-30)
             | (-0,2),(-14,0)
-            | (-7,3),(-21,1)
-            | (-17.5,2.5),(-21.5,-0.5)
-            | (-21,3),(-21,3)
             | (0,79.2),(-58.8,0)
-            | (-29.4,118.8),(-88.2,39.6)
-            | (-73.5,104.1),(-108,99)
-            | (-88.2,118.8),(-88.2,118.8)
             | (14,-0),(0,-34)
-            | (21,-17),(7,-51)
-            | (29.5,-42.5),(17.5,-47.5)
-            | (21,-51),(21,-51)
             | (0,40),(0,0)
+            | (0,0),(0,0)
+            | (-10,-10),(-30,-30)
+            | (-7,3),(-21,1)
+            | (-29.4,118.8),(-88.2,39.6)
+            | (21,-17),(7,-51)
             | (0,60),(0,20)
+            | (0,0),(0,0)
+            | (-25,-25),(-25,-35)
+            | (-17.5,2.5),(-21.5,-0.5)
+            | (-73.5,104.1),(-108,99)
+            | (29.5,-42.5),(17.5,-47.5)
             | (0,60),(-10,50)
+            | (0,0),(0,0)
+            | (-30,-30),(-30,-30)
+            | (-21,3),(-21,3)
+            | (-88.2,118.8),(-88.2,118.8)
+            | (21,-51),(21,-51)
             | (0,60),(0,60)
 (24 rows)
 
index 4e0651e46cd66a0a50f77294f25bac162f1a249a..67ab299c39eab7973a8258bb50ed0f8831b0d822 100644 (file)
@@ -163,28 +163,28 @@ SELECT '' AS twentyfour, b.f1 + p.f1 AS translation
  twentyfour |       translation       
 ------------+-------------------------
             | (2,2),(0,0)
-            | (3,3),(1,1)
-            | (2.5,3.5),(2.5,2.5)
-            | (3,3),(3,3)
             | (-8,2),(-10,0)
-            | (-7,3),(-9,1)
-            | (-7.5,3.5),(-7.5,2.5)
-            | (-7,3),(-7,3)
             | (-1,6),(-3,4)
-            | (0,7),(-2,5)
-            | (-0.5,7.5),(-0.5,6.5)
-            | (0,7),(0,7)
             | (7.1,36.5),(5.1,34.5)
-            | (8.1,37.5),(6.1,35.5)
-            | (7.6,38),(7.6,37)
-            | (8.1,37.5),(8.1,37.5)
             | (-3,-10),(-5,-12)
-            | (-2,-9),(-4,-11)
-            | (-2.5,-8.5),(-2.5,-9.5)
-            | (-2,-9),(-2,-9)
             | (12,12),(10,10)
+            | (3,3),(1,1)
+            | (-7,3),(-9,1)
+            | (0,7),(-2,5)
+            | (8.1,37.5),(6.1,35.5)
+            | (-2,-9),(-4,-11)
             | (13,13),(11,11)
+            | (2.5,3.5),(2.5,2.5)
+            | (-7.5,3.5),(-7.5,2.5)
+            | (-0.5,7.5),(-0.5,6.5)
+            | (7.6,38),(7.6,37)
+            | (-2.5,-8.5),(-2.5,-9.5)
             | (12.5,13.5),(12.5,12.5)
+            | (3,3),(3,3)
+            | (-7,3),(-7,3)
+            | (0,7),(0,7)
+            | (8.1,37.5),(8.1,37.5)
+            | (-2,-9),(-2,-9)
             | (13,13),(13,13)
 (24 rows)
 
@@ -193,28 +193,28 @@ SELECT '' AS twentyfour, b.f1 - p.f1 AS translation
  twentyfour |        translation        
 ------------+---------------------------
             | (2,2),(0,0)
-            | (3,3),(1,1)
-            | (2.5,3.5),(2.5,2.5)
-            | (3,3),(3,3)
             | (12,2),(10,0)
-            | (13,3),(11,1)
-            | (12.5,3.5),(12.5,2.5)
-            | (13,3),(13,3)
             | (5,-2),(3,-4)
-            | (6,-1),(4,-3)
-            | (5.5,-0.5),(5.5,-1.5)
-            | (6,-1),(6,-1)
             | (-3.1,-32.5),(-5.1,-34.5)
-            | (-2.1,-31.5),(-4.1,-33.5)
-            | (-2.6,-31),(-2.6,-32)
-            | (-2.1,-31.5),(-2.1,-31.5)
             | (7,14),(5,12)
-            | (8,15),(6,13)
-            | (7.5,15.5),(7.5,14.5)
-            | (8,15),(8,15)
             | (-8,-8),(-10,-10)
+            | (3,3),(1,1)
+            | (13,3),(11,1)
+            | (6,-1),(4,-3)
+            | (-2.1,-31.5),(-4.1,-33.5)
+            | (8,15),(6,13)
             | (-7,-7),(-9,-9)
+            | (2.5,3.5),(2.5,2.5)
+            | (12.5,3.5),(12.5,2.5)
+            | (5.5,-0.5),(5.5,-1.5)
+            | (-2.6,-31),(-2.6,-32)
+            | (7.5,15.5),(7.5,14.5)
             | (-7.5,-6.5),(-7.5,-7.5)
+            | (3,3),(3,3)
+            | (13,3),(13,3)
+            | (6,-1),(6,-1)
+            | (-2.1,-31.5),(-2.1,-31.5)
+            | (8,15),(8,15)
             | (-7,-7),(-7,-7)
 (24 rows)
 
@@ -223,29 +223,29 @@ SELECT '' AS twentyfour, b.f1 * p.f1 AS rotation
    FROM BOX_TBL b, POINT_TBL p;
  twentyfour |          rotation           
 ------------+-----------------------------
-            | (0,0),(0,0)
-            | (0,0),(0,0)
-            | (0,0),(0,0)
             | (0,0),(0,0)
             | (-0,0),(-20,-20)
-            | (-10,-10),(-30,-30)
-            | (-25,-25),(-25,-35)
-            | (-30,-30),(-30,-30)
             | (-0,2),(-14,0)
-            | (-7,3),(-21,1)
-            | (-17.5,2.5),(-21.5,-0.5)
-            | (-21,3),(-21,3)
             | (0,79.2),(-58.8,0)
-            | (-29.4,118.8),(-88.2,39.6)
-            | (-73.5,104.1),(-108,99)
-            | (-88.2,118.8),(-88.2,118.8)
             | (14,-0),(0,-34)
-            | (21,-17),(7,-51)
-            | (29.5,-42.5),(17.5,-47.5)
-            | (21,-51),(21,-51)
             | (0,40),(0,0)
+            | (0,0),(0,0)
+            | (-10,-10),(-30,-30)
+            | (-7,3),(-21,1)
+            | (-29.4,118.8),(-88.2,39.6)
+            | (21,-17),(7,-51)
             | (0,60),(0,20)
+            | (0,0),(0,0)
+            | (-25,-25),(-25,-35)
+            | (-17.5,2.5),(-21.5,-0.5)
+            | (-73.5,104.1),(-108,99)
+            | (29.5,-42.5),(17.5,-47.5)
             | (0,60),(-10,50)
+            | (0,0),(0,0)
+            | (-30,-30),(-30,-30)
+            | (-21,3),(-21,3)
+            | (-88.2,118.8),(-88.2,118.8)
+            | (21,-51),(21,-51)
             | (0,60),(0,60)
 (24 rows)
 
index 9dfe6081eb167cbfd688725a79c24b522242c718..dcefbf78f54a0b5c2c90a9e59d85aba2fd82a38c 100644 (file)
@@ -163,28 +163,28 @@ SELECT '' AS twentyfour, b.f1 + p.f1 AS translation
  twentyfour |       translation       
 ------------+-------------------------
             | (2,2),(0,0)
-            | (3,3),(1,1)
-            | (2.5,3.5),(2.5,2.5)
-            | (3,3),(3,3)
             | (-8,2),(-10,0)
-            | (-7,3),(-9,1)
-            | (-7.5,3.5),(-7.5,2.5)
-            | (-7,3),(-7,3)
             | (-1,6),(-3,4)
-            | (0,7),(-2,5)
-            | (-0.5,7.5),(-0.5,6.5)
-            | (0,7),(0,7)
             | (7.1,36.5),(5.1,34.5)
-            | (8.1,37.5),(6.1,35.5)
-            | (7.6,38),(7.6,37)
-            | (8.1,37.5),(8.1,37.5)
             | (-3,-10),(-5,-12)
-            | (-2,-9),(-4,-11)
-            | (-2.5,-8.5),(-2.5,-9.5)
-            | (-2,-9),(-2,-9)
             | (12,12),(10,10)
+            | (3,3),(1,1)
+            | (-7,3),(-9,1)
+            | (0,7),(-2,5)
+            | (8.1,37.5),(6.1,35.5)
+            | (-2,-9),(-4,-11)
             | (13,13),(11,11)
+            | (2.5,3.5),(2.5,2.5)
+            | (-7.5,3.5),(-7.5,2.5)
+            | (-0.5,7.5),(-0.5,6.5)
+            | (7.6,38),(7.6,37)
+            | (-2.5,-8.5),(-2.5,-9.5)
             | (12.5,13.5),(12.5,12.5)
+            | (3,3),(3,3)
+            | (-7,3),(-7,3)
+            | (0,7),(0,7)
+            | (8.1,37.5),(8.1,37.5)
+            | (-2,-9),(-2,-9)
             | (13,13),(13,13)
 (24 rows)
 
@@ -193,28 +193,28 @@ SELECT '' AS twentyfour, b.f1 - p.f1 AS translation
  twentyfour |        translation        
 ------------+---------------------------
             | (2,2),(0,0)
-            | (3,3),(1,1)
-            | (2.5,3.5),(2.5,2.5)
-            | (3,3),(3,3)
             | (12,2),(10,0)
-            | (13,3),(11,1)
-            | (12.5,3.5),(12.5,2.5)
-            | (13,3),(13,3)
             | (5,-2),(3,-4)
-            | (6,-1),(4,-3)
-            | (5.5,-0.5),(5.5,-1.5)
-            | (6,-1),(6,-1)
             | (-3.1,-32.5),(-5.1,-34.5)
-            | (-2.1,-31.5),(-4.1,-33.5)
-            | (-2.6,-31),(-2.6,-32)
-            | (-2.1,-31.5),(-2.1,-31.5)
             | (7,14),(5,12)
-            | (8,15),(6,13)
-            | (7.5,15.5),(7.5,14.5)
-            | (8,15),(8,15)
             | (-8,-8),(-10,-10)
+            | (3,3),(1,1)
+            | (13,3),(11,1)
+            | (6,-1),(4,-3)
+            | (-2.1,-31.5),(-4.1,-33.5)
+            | (8,15),(6,13)
             | (-7,-7),(-9,-9)
+            | (2.5,3.5),(2.5,2.5)
+            | (12.5,3.5),(12.5,2.5)
+            | (5.5,-0.5),(5.5,-1.5)
+            | (-2.6,-31),(-2.6,-32)
+            | (7.5,15.5),(7.5,14.5)
             | (-7.5,-6.5),(-7.5,-7.5)
+            | (3,3),(3,3)
+            | (13,3),(13,3)
+            | (6,-1),(6,-1)
+            | (-2.1,-31.5),(-2.1,-31.5)
+            | (8,15),(8,15)
             | (-7,-7),(-7,-7)
 (24 rows)
 
@@ -223,29 +223,29 @@ SELECT '' AS twentyfour, b.f1 * p.f1 AS rotation
    FROM BOX_TBL b, POINT_TBL p;
  twentyfour |          rotation           
 ------------+-----------------------------
-            | (0,0),(0,0)
-            | (0,0),(0,0)
-            | (0,0),(0,0)
             | (0,0),(0,0)
             | (-0,0),(-20,-20)
-            | (-10,-10),(-30,-30)
-            | (-25,-25),(-25,-35)
-            | (-30,-30),(-30,-30)
             | (-0,2),(-14,0)
-            | (-7,3),(-21,1)
-            | (-17.5,2.5),(-21.5,-0.5)
-            | (-21,3),(-21,3)
             | (0,79.2),(-58.8,0)
-            | (-29.4,118.8),(-88.2,39.6)
-            | (-73.5,104.1),(-108,99)
-            | (-88.2,118.8),(-88.2,118.8)
             | (14,-0),(0,-34)
-            | (21,-17),(7,-51)
-            | (29.5,-42.5),(17.5,-47.5)
-            | (21,-51),(21,-51)
             | (0,40),(0,0)
+            | (0,0),(0,0)
+            | (-10,-10),(-30,-30)
+            | (-7,3),(-21,1)
+            | (-29.4,118.8),(-88.2,39.6)
+            | (21,-17),(7,-51)
             | (0,60),(0,20)
+            | (0,0),(0,0)
+            | (-25,-25),(-25,-35)
+            | (-17.5,2.5),(-21.5,-0.5)
+            | (-73.5,104.1),(-108,99)
+            | (29.5,-42.5),(17.5,-47.5)
             | (0,60),(-10,50)
+            | (0,0),(0,0)
+            | (-30,-30),(-30,-30)
+            | (-21,3),(-21,3)
+            | (-88.2,118.8),(-88.2,118.8)
+            | (21,-51),(21,-51)
             | (0,60),(0,60)
 (24 rows)
 
index 2b4f1b655aa2f4fa0ccd6e6842ea9a7e48b015e2..3c156ee46c14d7e577eedcf04c6df9aff18d7edc 100644 (file)
@@ -1,6 +1,6 @@
 --
 -- JOIN
--- Test join clauses
+-- Test JOIN clauses
 --
 CREATE TABLE J1_TBL (
   i integer,
@@ -28,6 +28,7 @@ INSERT INTO J2_TBL VALUES (1, -1);
 INSERT INTO J2_TBL VALUES (2, 2);
 INSERT INTO J2_TBL VALUES (3, -3);
 INSERT INTO J2_TBL VALUES (2, 4);
+INSERT INTO J2_TBL VALUES (5, -5);
 --
 -- CORRELATION NAMES
 -- Make sure that table/column aliases are supported
@@ -78,22 +79,26 @@ SELECT '' AS "xxx", *
  xxx | a | b |   c   | d | e  
 -----+---+---+-------+---+----
      | 1 | 3 | one   | 1 | -1
-     | 2 | 2 | two   | 1 | -1
-     | 3 | 1 | three | 1 | -1
-     | 4 | 0 | four  | 1 | -1
      | 1 | 3 | one   | 2 |  2
-     | 2 | 2 | two   | 2 |  2
-     | 3 | 1 | three | 2 |  2
-     | 4 | 0 | four  | 2 |  2
      | 1 | 3 | one   | 3 | -3
-     | 2 | 2 | two   | 3 | -3
-     | 3 | 1 | three | 3 | -3
-     | 4 | 0 | four  | 3 | -3
      | 1 | 3 | one   | 2 |  4
+     | 1 | 3 | one   | 5 | -5
+     | 2 | 2 | two   | 1 | -1
+     | 2 | 2 | two   | 2 |  2
+     | 2 | 2 | two   | 3 | -3
      | 2 | 2 | two   | 2 |  4
+     | 2 | 2 | two   | 5 | -5
+     | 3 | 1 | three | 1 | -1
+     | 3 | 1 | three | 2 |  2
+     | 3 | 1 | three | 3 | -3
      | 3 | 1 | three | 2 |  4
+     | 3 | 1 | three | 5 | -5
+     | 4 | 0 | four  | 1 | -1
+     | 4 | 0 | four  | 2 |  2
+     | 4 | 0 | four  | 3 | -3
      | 4 | 0 | four  | 2 |  4
-(16 rows)
+     | 4 | 0 | four  | 5 | -5
+(20 rows)
 
 SELECT '' AS "xxx", t1.a, t2.e
   FROM J1_TBL t1 (a, b, c), J2_TBL t2 (d, e)
@@ -116,58 +121,218 @@ SELECT '' AS "xxx", *
  xxx | i | j |   t   | i | k  
 -----+---+---+-------+---+----
      | 1 | 3 | one   | 1 | -1
-     | 2 | 2 | two   | 1 | -1
-     | 3 | 1 | three | 1 | -1
-     | 4 | 0 | four  | 1 | -1
      | 1 | 3 | one   | 2 |  2
-     | 2 | 2 | two   | 2 |  2
-     | 3 | 1 | three | 2 |  2
-     | 4 | 0 | four  | 2 |  2
      | 1 | 3 | one   | 3 | -3
-     | 2 | 2 | two   | 3 | -3
-     | 3 | 1 | three | 3 | -3
-     | 4 | 0 | four  | 3 | -3
      | 1 | 3 | one   | 2 |  4
+     | 1 | 3 | one   | 5 | -5
+     | 2 | 2 | two   | 1 | -1
+     | 2 | 2 | two   | 2 |  2
+     | 2 | 2 | two   | 3 | -3
      | 2 | 2 | two   | 2 |  4
+     | 2 | 2 | two   | 5 | -5
+     | 3 | 1 | three | 1 | -1
+     | 3 | 1 | three | 2 |  2
+     | 3 | 1 | three | 3 | -3
      | 3 | 1 | three | 2 |  4
+     | 3 | 1 | three | 5 | -5
+     | 4 | 0 | four  | 1 | -1
+     | 4 | 0 | four  | 2 |  2
+     | 4 | 0 | four  | 3 | -3
      | 4 | 0 | four  | 2 |  4
-(16 rows)
+     | 4 | 0 | four  | 5 | -5
+(20 rows)
 
 -- ambiguous column
 SELECT '' AS "xxx", i, k, t
   FROM J1_TBL CROSS JOIN J2_TBL;
-ERROR:  Column 'i' is ambiguous
+ERROR:  Column reference "i" is ambiguous
 -- resolve previous ambiguity by specifying the table name
 SELECT '' AS "xxx", t1.i, k, t
   FROM J1_TBL t1 CROSS JOIN J2_TBL t2;
  xxx | i | k  |   t   
 -----+---+----+-------
      | 1 | -1 | one
-     | 2 | -1 | two
-     | 3 | -1 | three
-     | 4 | -1 | four
      | 1 |  2 | one
-     | 2 |  2 | two
-     | 3 |  2 | three
-     | 4 |  2 | four
      | 1 | -3 | one
-     | 2 | -3 | two
-     | 3 | -3 | three
-     | 4 | -3 | four
      | 1 |  4 | one
+     | 1 | -5 | one
+     | 2 | -1 | two
+     | 2 |  2 | two
+     | 2 | -3 | two
      | 2 |  4 | two
+     | 2 | -5 | two
+     | 3 | -1 | three
+     | 3 |  2 | three
+     | 3 | -3 | three
      | 3 |  4 | three
+     | 3 | -5 | three
+     | 4 | -1 | four
+     | 4 |  2 | four
+     | 4 | -3 | four
      | 4 |  4 | four
-(16 rows)
+     | 4 | -5 | four
+(20 rows)
 
 SELECT '' AS "xxx", ii, tt, kk
   FROM (J1_TBL CROSS JOIN J2_TBL)
     AS tx (ii, jj, tt, ii2, kk);
-ERROR:  JOIN table aliases are not supported
+ xxx | ii |  tt   | kk 
+-----+----+-------+----
+     |  1 | one   | -1
+     |  1 | one   |  2
+     |  1 | one   | -3
+     |  1 | one   |  4
+     |  1 | one   | -5
+     |  2 | two   | -1
+     |  2 | two   |  2
+     |  2 | two   | -3
+     |  2 | two   |  4
+     |  2 | two   | -5
+     |  3 | three | -1
+     |  3 | three |  2
+     |  3 | three | -3
+     |  3 | three |  4
+     |  3 | three | -5
+     |  4 | four  | -1
+     |  4 | four  |  2
+     |  4 | four  | -3
+     |  4 | four  |  4
+     |  4 | four  | -5
+(20 rows)
+
 SELECT '' AS "xxx", tx.ii, tx.jj, tx.kk
   FROM (J1_TBL t1 (a, b, c) CROSS JOIN J2_TBL t2 (d, e))
     AS tx (ii, jj, tt, ii2, kk);
-ERROR:  JOIN table aliases are not supported
+ xxx | ii | jj | kk 
+-----+----+----+----
+     |  1 |  3 | -1
+     |  1 |  3 |  2
+     |  1 |  3 | -3
+     |  1 |  3 |  4
+     |  1 |  3 | -5
+     |  2 |  2 | -1
+     |  2 |  2 |  2
+     |  2 |  2 | -3
+     |  2 |  2 |  4
+     |  2 |  2 | -5
+     |  3 |  1 | -1
+     |  3 |  1 |  2
+     |  3 |  1 | -3
+     |  3 |  1 |  4
+     |  3 |  1 | -5
+     |  4 |  0 | -1
+     |  4 |  0 |  2
+     |  4 |  0 | -3
+     |  4 |  0 |  4
+     |  4 |  0 | -5
+(20 rows)
+
+SELECT '' AS "xxx", *
+  FROM J1_TBL CROSS JOIN J2_TBL a CROSS JOIN J2_TBL b;
+ xxx | i | j |   t   | i | k  | i | k  
+-----+---+---+-------+---+----+---+----
+     | 1 | 3 | one   | 1 | -1 | 1 | -1
+     | 1 | 3 | one   | 1 | -1 | 2 |  2
+     | 1 | 3 | one   | 1 | -1 | 3 | -3
+     | 1 | 3 | one   | 1 | -1 | 2 |  4
+     | 1 | 3 | one   | 1 | -1 | 5 | -5
+     | 1 | 3 | one   | 2 |  2 | 1 | -1
+     | 1 | 3 | one   | 2 |  2 | 2 |  2
+     | 1 | 3 | one   | 2 |  2 | 3 | -3
+     | 1 | 3 | one   | 2 |  2 | 2 |  4
+     | 1 | 3 | one   | 2 |  2 | 5 | -5
+     | 1 | 3 | one   | 3 | -3 | 1 | -1
+     | 1 | 3 | one   | 3 | -3 | 2 |  2
+     | 1 | 3 | one   | 3 | -3 | 3 | -3
+     | 1 | 3 | one   | 3 | -3 | 2 |  4
+     | 1 | 3 | one   | 3 | -3 | 5 | -5
+     | 1 | 3 | one   | 2 |  4 | 1 | -1
+     | 1 | 3 | one   | 2 |  4 | 2 |  2
+     | 1 | 3 | one   | 2 |  4 | 3 | -3
+     | 1 | 3 | one   | 2 |  4 | 2 |  4
+     | 1 | 3 | one   | 2 |  4 | 5 | -5
+     | 1 | 3 | one   | 5 | -5 | 1 | -1
+     | 1 | 3 | one   | 5 | -5 | 2 |  2
+     | 1 | 3 | one   | 5 | -5 | 3 | -3
+     | 1 | 3 | one   | 5 | -5 | 2 |  4
+     | 1 | 3 | one   | 5 | -5 | 5 | -5
+     | 2 | 2 | two   | 1 | -1 | 1 | -1
+     | 2 | 2 | two   | 1 | -1 | 2 |  2
+     | 2 | 2 | two   | 1 | -1 | 3 | -3
+     | 2 | 2 | two   | 1 | -1 | 2 |  4
+     | 2 | 2 | two   | 1 | -1 | 5 | -5
+     | 2 | 2 | two   | 2 |  2 | 1 | -1
+     | 2 | 2 | two   | 2 |  2 | 2 |  2
+     | 2 | 2 | two   | 2 |  2 | 3 | -3
+     | 2 | 2 | two   | 2 |  2 | 2 |  4
+     | 2 | 2 | two   | 2 |  2 | 5 | -5
+     | 2 | 2 | two   | 3 | -3 | 1 | -1
+     | 2 | 2 | two   | 3 | -3 | 2 |  2
+     | 2 | 2 | two   | 3 | -3 | 3 | -3
+     | 2 | 2 | two   | 3 | -3 | 2 |  4
+     | 2 | 2 | two   | 3 | -3 | 5 | -5
+     | 2 | 2 | two   | 2 |  4 | 1 | -1
+     | 2 | 2 | two   | 2 |  4 | 2 |  2
+     | 2 | 2 | two   | 2 |  4 | 3 | -3
+     | 2 | 2 | two   | 2 |  4 | 2 |  4
+     | 2 | 2 | two   | 2 |  4 | 5 | -5
+     | 2 | 2 | two   | 5 | -5 | 1 | -1
+     | 2 | 2 | two   | 5 | -5 | 2 |  2
+     | 2 | 2 | two   | 5 | -5 | 3 | -3
+     | 2 | 2 | two   | 5 | -5 | 2 |  4
+     | 2 | 2 | two   | 5 | -5 | 5 | -5
+     | 3 | 1 | three | 1 | -1 | 1 | -1
+     | 3 | 1 | three | 1 | -1 | 2 |  2
+     | 3 | 1 | three | 1 | -1 | 3 | -3
+     | 3 | 1 | three | 1 | -1 | 2 |  4
+     | 3 | 1 | three | 1 | -1 | 5 | -5
+     | 3 | 1 | three | 2 |  2 | 1 | -1
+     | 3 | 1 | three | 2 |  2 | 2 |  2
+     | 3 | 1 | three | 2 |  2 | 3 | -3
+     | 3 | 1 | three | 2 |  2 | 2 |  4
+     | 3 | 1 | three | 2 |  2 | 5 | -5
+     | 3 | 1 | three | 3 | -3 | 1 | -1
+     | 3 | 1 | three | 3 | -3 | 2 |  2
+     | 3 | 1 | three | 3 | -3 | 3 | -3
+     | 3 | 1 | three | 3 | -3 | 2 |  4
+     | 3 | 1 | three | 3 | -3 | 5 | -5
+     | 3 | 1 | three | 2 |  4 | 1 | -1
+     | 3 | 1 | three | 2 |  4 | 2 |  2
+     | 3 | 1 | three | 2 |  4 | 3 | -3
+     | 3 | 1 | three | 2 |  4 | 2 |  4
+     | 3 | 1 | three | 2 |  4 | 5 | -5
+     | 3 | 1 | three | 5 | -5 | 1 | -1
+     | 3 | 1 | three | 5 | -5 | 2 |  2
+     | 3 | 1 | three | 5 | -5 | 3 | -3
+     | 3 | 1 | three | 5 | -5 | 2 |  4
+     | 3 | 1 | three | 5 | -5 | 5 | -5
+     | 4 | 0 | four  | 1 | -1 | 1 | -1
+     | 4 | 0 | four  | 1 | -1 | 2 |  2
+     | 4 | 0 | four  | 1 | -1 | 3 | -3
+     | 4 | 0 | four  | 1 | -1 | 2 |  4
+     | 4 | 0 | four  | 1 | -1 | 5 | -5
+     | 4 | 0 | four  | 2 |  2 | 1 | -1
+     | 4 | 0 | four  | 2 |  2 | 2 |  2
+     | 4 | 0 | four  | 2 |  2 | 3 | -3
+     | 4 | 0 | four  | 2 |  2 | 2 |  4
+     | 4 | 0 | four  | 2 |  2 | 5 | -5
+     | 4 | 0 | four  | 3 | -3 | 1 | -1
+     | 4 | 0 | four  | 3 | -3 | 2 |  2
+     | 4 | 0 | four  | 3 | -3 | 3 | -3
+     | 4 | 0 | four  | 3 | -3 | 2 |  4
+     | 4 | 0 | four  | 3 | -3 | 5 | -5
+     | 4 | 0 | four  | 2 |  4 | 1 | -1
+     | 4 | 0 | four  | 2 |  4 | 2 |  2
+     | 4 | 0 | four  | 2 |  4 | 3 | -3
+     | 4 | 0 | four  | 2 |  4 | 2 |  4
+     | 4 | 0 | four  | 2 |  4 | 5 | -5
+     | 4 | 0 | four  | 5 | -5 | 1 | -1
+     | 4 | 0 | four  | 5 | -5 | 2 |  2
+     | 4 | 0 | four  | 5 | -5 | 3 | -3
+     | 4 | 0 | four  | 5 | -5 | 2 |  4
+     | 4 | 0 | four  | 5 | -5 | 5 | -5
+(100 rows)
+
 --
 --
 -- Inner joins (equi-joins)
@@ -249,14 +414,6 @@ SELECT '' AS "xxx", *
      | 4 | 0 | four | 2
 (2 rows)
 
-SELECT '' AS "xxx", *
-  FROM J1_TBL t1 (a, b, c) NATURAL JOIN J2_TBL t2 (d, a);
- xxx | a | b |  c   | d 
------+---+---+------+---
-     | 2 | 2 | two  | 2
-     | 4 | 0 | four | 2
-(2 rows)
-
 -- mismatch number of columns
 -- currently, Postgres will fill in with underlying names
 SELECT '' AS "xxx", *
@@ -290,28 +447,6 @@ SELECT '' AS "xxx", *
      | 4 | 0 | four | 2 | 4
 (2 rows)
 
-SELECT '' AS "xxx", *
-  FROM J1_TBL CROSS JOIN J2_TBL;
- xxx | i | j |   t   | i | k  
------+---+---+-------+---+----
-     | 1 | 3 | one   | 1 | -1
-     | 2 | 2 | two   | 1 | -1
-     | 3 | 1 | three | 1 | -1
-     | 4 | 0 | four  | 1 | -1
-     | 1 | 3 | one   | 2 |  2
-     | 2 | 2 | two   | 2 |  2
-     | 3 | 1 | three | 2 |  2
-     | 4 | 0 | four  | 2 |  2
-     | 1 | 3 | one   | 3 | -3
-     | 2 | 2 | two   | 3 | -3
-     | 3 | 1 | three | 3 | -3
-     | 4 | 0 | four  | 3 | -3
-     | 1 | 3 | one   | 2 |  4
-     | 2 | 2 | two   | 2 |  4
-     | 3 | 1 | three | 2 |  4
-     | 4 | 0 | four  | 2 |  4
-(16 rows)
-
 --
 -- Non-equi-joins
 --
@@ -320,8 +455,8 @@ SELECT '' AS "xxx", *
  xxx | i | j |   t   | i | k 
 -----+---+---+-------+---+---
      | 1 | 3 | one   | 2 | 2
-     | 2 | 2 | two   | 2 | 2
      | 1 | 3 | one   | 2 | 4
+     | 2 | 2 | two   | 2 | 2
      | 2 | 2 | two   | 2 | 4
      | 3 | 1 | three | 2 | 4
      | 4 | 0 | four  | 2 | 4
@@ -330,21 +465,48 @@ SELECT '' AS "xxx", *
 --
 -- Outer joins
 --
-SELECT '' AS "xxx", *
-  FROM J1_TBL OUTER JOIN J2_TBL USING (i);
-ERROR:  OUTER JOIN is not yet supported
 SELECT '' AS "xxx", *
   FROM J1_TBL LEFT OUTER JOIN J2_TBL USING (i);
-ERROR:  OUTER JOIN is not yet supported
+ xxx | i | j |   t   | k  
+-----+---+---+-------+----
+     | 1 | 3 | one   | -1
+     | 2 | 2 | two   |  2
+     | 2 | 2 | two   |  4
+     | 3 | 1 | three | -3
+     | 4 | 0 | four  |   
+(5 rows)
+
 SELECT '' AS "xxx", *
   FROM J1_TBL RIGHT OUTER JOIN J2_TBL USING (i);
-ERROR:  OUTER JOIN is not yet supported
+ xxx | i | j |   t   | k  
+-----+---+---+-------+----
+     | 1 | 3 | one   | -1
+     | 2 | 2 | two   |  2
+     | 2 | 2 | two   |  4
+     | 3 | 1 | three | -3
+     | 5 |   |       | -5
+(5 rows)
+
+-- Note that OUTER is a noise word
 SELECT '' AS "xxx", *
-  FROM J1_TBL FULL OUTER JOIN J2_TBL USING (i);
-ERROR:  OUTER JOIN is not yet supported
+  FROM J1_TBL FULL JOIN J2_TBL USING (i);
+ xxx | i | j |   t   | k  
+-----+---+---+-------+----
+     | 1 | 3 | one   | -1
+     | 2 | 2 | two   |  2
+     | 2 | 2 | two   |  4
+     | 3 | 1 | three | -3
+     | 4 | 0 | four  |   
+     | 5 |   |       | -5
+(6 rows)
+
 --
 -- More complicated constructs
 --
+-- UNION JOIN isn't implemented yet
+SELECT '' AS "xxx", *
+  FROM J1_TBL UNION JOIN J2_TBL;
+ERROR:  UNION JOIN is not implemented yet
 --
 -- Clean up
 --
index 9f347bce7971dec79843370953efff113aba5fb7..3eab1c5b5d523efb2d9df3585935179de9a202e3 100644 (file)
@@ -154,36 +154,36 @@ SELECT '' AS thirty, p1.f1 AS point1, p2.f1 AS point2
    WHERE (p1.f1 <-> p2.f1) > 3;
  thirty |   point1   |   point2   
 --------+------------+------------
-        | (-10,0)    | (0,0)
-        | (-3,4)     | (0,0)
-        | (5.1,34.5) | (0,0)
-        | (-5,-12)   | (0,0)
-        | (10,10)    | (0,0)
         | (0,0)      | (-10,0)
-        | (-3,4)     | (-10,0)
-        | (5.1,34.5) | (-10,0)
-        | (-5,-12)   | (-10,0)
-        | (10,10)    | (-10,0)
         | (0,0)      | (-3,4)
-        | (-10,0)    | (-3,4)
-        | (5.1,34.5) | (-3,4)
-        | (-5,-12)   | (-3,4)
-        | (10,10)    | (-3,4)
         | (0,0)      | (5.1,34.5)
-        | (-10,0)    | (5.1,34.5)
-        | (-3,4)     | (5.1,34.5)
-        | (-5,-12)   | (5.1,34.5)
-        | (10,10)    | (5.1,34.5)
         | (0,0)      | (-5,-12)
-        | (-10,0)    | (-5,-12)
-        | (-3,4)     | (-5,-12)
-        | (5.1,34.5) | (-5,-12)
-        | (10,10)    | (-5,-12)
         | (0,0)      | (10,10)
+        | (-10,0)    | (0,0)
+        | (-10,0)    | (-3,4)
+        | (-10,0)    | (5.1,34.5)
+        | (-10,0)    | (-5,-12)
         | (-10,0)    | (10,10)
+        | (-3,4)     | (0,0)
+        | (-3,4)     | (-10,0)
+        | (-3,4)     | (5.1,34.5)
+        | (-3,4)     | (-5,-12)
         | (-3,4)     | (10,10)
+        | (5.1,34.5) | (0,0)
+        | (5.1,34.5) | (-10,0)
+        | (5.1,34.5) | (-3,4)
+        | (5.1,34.5) | (-5,-12)
         | (5.1,34.5) | (10,10)
+        | (-5,-12)   | (0,0)
+        | (-5,-12)   | (-10,0)
+        | (-5,-12)   | (-3,4)
+        | (-5,-12)   | (5.1,34.5)
         | (-5,-12)   | (10,10)
+        | (10,10)    | (0,0)
+        | (10,10)    | (-10,0)
+        | (10,10)    | (-3,4)
+        | (10,10)    | (5.1,34.5)
+        | (10,10)    | (-5,-12)
 (30 rows)
 
 -- put distance result into output to allow sorting with GEQ optimizer - tgl 97/05/10
index 34059b8a6e7ab0bcfc60b2727b7cf5bbf9ad9008..eb19122cbcea0ea1cabc2c24a2a616e137fa7835 100644 (file)
@@ -1158,6 +1158,47 @@ SELECT count(*) FROM shoe;
      4
 (1 row)
 
+--
+-- Simple test of qualified ON INSERT ... this did not work in 7.0 ...
+--
+create table foo (f1 int);
+create table foo2 (f1 int);
+create rule foorule as on insert to foo where f1 < 100
+do instead nothing;
+insert into foo values(1);
+insert into foo values(1001);
+select * from foo;
+  f1  
+------
+ 1001
+(1 row)
+
+drop rule foorule;
+-- this should fail because f1 is not exposed for unqualified reference:
+create rule foorule as on insert to foo where f1 < 100
+do instead insert into foo2 values (f1);
+ERROR:  Attribute 'f1' not found
+-- this is the correct way:
+create rule foorule as on insert to foo where f1 < 100
+do instead insert into foo2 values (new.f1);
+insert into foo values(2);
+insert into foo values(100);
+select * from foo;
+  f1  
+------
+ 1001
+  100
+(2 rows)
+
+select * from foo2;
+ f1 
+----
+  2
+(1 row)
+
+drop rule foorule;
+drop table foo;
+drop table foo2;
 --
 -- Check that ruleutils are working
 --
@@ -1200,7 +1241,7 @@ SELECT tablename, rulename, definition FROM pg_rules
  rtest_order1  | rtest_order_r1  | CREATE RULE rtest_order_r1 AS ON INSERT TO rtest_order1 DO INSTEAD INSERT INTO rtest_order2 (a, b, c) VALUES (new.a, nextval('rtest_seq'::text), 'rule 1 - this should run 3rd or 4th'::text);
  rtest_order1  | rtest_order_r2  | CREATE RULE rtest_order_r2 AS ON INSERT TO rtest_order1 DO INSERT INTO rtest_order2 (a, b, c) VALUES (new.a, nextval('rtest_seq'::text), 'rule 2 - this should run 1st'::text);
  rtest_order1  | rtest_order_r3  | CREATE RULE rtest_order_r3 AS ON INSERT TO rtest_order1 DO INSTEAD INSERT INTO rtest_order2 (a, b, c) VALUES (new.a, nextval('rtest_seq'::text), 'rule 3 - this should run 3rd or 4th'::text);
- rtest_order1  | rtest_order_r4  | CREATE RULE rtest_order_r4 AS ON INSERT TO rtest_order1 WHERE (rtest_order2.a < 100) DO INSTEAD INSERT INTO rtest_order2 (a, b, c) VALUES (new.a, nextval('rtest_seq'::text), 'rule 4 - this should run 2nd'::text);
+ rtest_order1  | rtest_order_r4  | CREATE RULE rtest_order_r4 AS ON INSERT TO rtest_order1 WHERE (new.a < 100) DO INSTEAD INSERT INTO rtest_order2 (a, b, c) VALUES (new.a, nextval('rtest_seq'::text), 'rule 4 - this should run 2nd'::text);
  rtest_person  | rtest_pers_del  | CREATE RULE rtest_pers_del AS ON DELETE TO rtest_person DO DELETE FROM rtest_admin WHERE (rtest_admin.pname = old.pname);
  rtest_person  | rtest_pers_upd  | CREATE RULE rtest_pers_upd AS ON UPDATE TO rtest_person DO UPDATE rtest_admin SET pname = new.pname WHERE (rtest_admin.pname = old.pname);
  rtest_system  | rtest_sys_del   | CREATE RULE rtest_sys_del AS ON DELETE TO rtest_system DO (DELETE FROM rtest_interface WHERE (rtest_interface.sysname = old.sysname); DELETE FROM rtest_admin WHERE (rtest_admin.sysname = old.sysname); );
index adf0f794774857848b78cbcac4969dcd5857807c..e3d74e5daba3f7227348819f8e275602f6fb7ec9 100644 (file)
@@ -120,7 +120,7 @@ ERROR:  GROUP BY position 3 is not in target list
 SELECT count(*) FROM test_missing_target x, test_missing_target y 
        WHERE x.a = y.a
        GROUP BY b ORDER BY b;
-ERROR:  Column 'b' is ambiguous
+ERROR:  Column reference "b" is ambiguous
 --   order w/ target under ambiguous condition
 --   failure NOT expected
 SELECT a, a FROM test_missing_target
@@ -282,7 +282,7 @@ SELECT count(b) FROM test_missing_target
 SELECT count(x.a) FROM test_missing_target x, test_missing_target y 
        WHERE x.a = y.a
        GROUP BY b/2 ORDER BY b/2;
-ERROR:  Column 'b' is ambiguous
+ERROR:  Column reference "b" is ambiguous
 --   group w/ existing GROUP BY target under ambiguous condition
 SELECT x.b/2, count(x.b) FROM test_missing_target x, test_missing_target y 
        WHERE x.a = y.a
@@ -299,7 +299,7 @@ SELECT x.b/2, count(x.b) FROM test_missing_target x, test_missing_target y
 SELECT count(b) FROM test_missing_target x, test_missing_target y 
        WHERE x.a = y.a
        GROUP BY x.b/2;
-ERROR:  Column 'b' is ambiguous
+ERROR:  Column reference "b" is ambiguous
 --   group w/o existing GROUP BY target under ambiguous condition
 --   into a table
 SELECT count(x.b) INTO TABLE test_missing_target3 
index c63bd0596fb8d3767b5c1bcc074f2ae4690456a5..88972de5ea90d179eb4c272614a97b8d3cbefec8 100644 (file)
@@ -1,6 +1,6 @@
 --
 -- JOIN
--- Test join clauses
+-- Test JOIN clauses
 --
 
 CREATE TABLE J1_TBL (
@@ -34,6 +34,7 @@ INSERT INTO J2_TBL VALUES (1, -1);
 INSERT INTO J2_TBL VALUES (2, 2);
 INSERT INTO J2_TBL VALUES (3, -3);
 INSERT INTO J2_TBL VALUES (2, 4);
+INSERT INTO J2_TBL VALUES (5, -5);
 
 --
 -- CORRELATION NAMES
@@ -86,6 +87,9 @@ SELECT '' AS "xxx", tx.ii, tx.jj, tx.kk
   FROM (J1_TBL t1 (a, b, c) CROSS JOIN J2_TBL t2 (d, e))
     AS tx (ii, jj, tt, ii2, kk);
 
+SELECT '' AS "xxx", *
+  FROM J1_TBL CROSS JOIN J2_TBL a CROSS JOIN J2_TBL b;
+
 
 --
 --
@@ -128,9 +132,6 @@ SELECT '' AS "xxx", *
 SELECT '' AS "xxx", *
   FROM J1_TBL t1 (a, b, c) NATURAL JOIN J2_TBL t2 (d, a);
 
-SELECT '' AS "xxx", *
-  FROM J1_TBL t1 (a, b, c) NATURAL JOIN J2_TBL t2 (d, a);
-
 -- mismatch number of columns
 -- currently, Postgres will fill in with underlying names
 SELECT '' AS "xxx", *
@@ -147,9 +148,6 @@ SELECT '' AS "xxx", *
 SELECT '' AS "xxx", *
   FROM J1_TBL JOIN J2_TBL ON (J1_TBL.i = J2_TBL.k);
 
-SELECT '' AS "xxx", *
-  FROM J1_TBL CROSS JOIN J2_TBL;
-
 
 --
 -- Non-equi-joins
@@ -163,23 +161,25 @@ SELECT '' AS "xxx", *
 -- Outer joins
 --
 
-SELECT '' AS "xxx", *
-  FROM J1_TBL OUTER JOIN J2_TBL USING (i);
-
 SELECT '' AS "xxx", *
   FROM J1_TBL LEFT OUTER JOIN J2_TBL USING (i);
 
 SELECT '' AS "xxx", *
   FROM J1_TBL RIGHT OUTER JOIN J2_TBL USING (i);
 
+-- Note that OUTER is a noise word
 SELECT '' AS "xxx", *
-  FROM J1_TBL FULL OUTER JOIN J2_TBL USING (i);
+  FROM J1_TBL FULL JOIN J2_TBL USING (i);
 
 
 --
 -- More complicated constructs
 --
 
+-- UNION JOIN isn't implemented yet
+SELECT '' AS "xxx", *
+  FROM J1_TBL UNION JOIN J2_TBL;
+
 --
 -- Clean up
 --
index 2b7aa33a8cf2d8dcf658c5758b1f96f3649b9602..2c99f2a3ccbe6bfd7427b59a82141562f239ce1d 100644 (file)
@@ -686,6 +686,39 @@ SELECT * FROM shoe ORDER BY shoename;
 SELECT count(*) FROM shoe;
 
 
+--
+-- Simple test of qualified ON INSERT ... this did not work in 7.0 ...
+--
+create table foo (f1 int);
+create table foo2 (f1 int);
+
+create rule foorule as on insert to foo where f1 < 100
+do instead nothing;
+
+insert into foo values(1);
+insert into foo values(1001);
+select * from foo;
+
+drop rule foorule;
+
+-- this should fail because f1 is not exposed for unqualified reference:
+create rule foorule as on insert to foo where f1 < 100
+do instead insert into foo2 values (f1);
+-- this is the correct way:
+create rule foorule as on insert to foo where f1 < 100
+do instead insert into foo2 values (new.f1);
+
+insert into foo values(2);
+insert into foo values(100);
+
+select * from foo;
+select * from foo2;
+
+drop rule foorule;
+drop table foo;
+drop table foo2;
+
+
 --
 -- Check that ruleutils are working
 --