OSDN Git Service

Subselects in FROM clause, per ISO syntax: FROM (SELECT ...) [AS] alias.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 29 Sep 2000 18:21:41 +0000 (18:21 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 29 Sep 2000 18:21:41 +0000 (18:21 +0000)
(Don't forget that an alias is required.)  Views reimplemented as expanding
to subselect-in-FROM.  Grouping, aggregates, DISTINCT in views actually
work now (he says optimistically).  No UNION support in subselects/views
yet, but I have some ideas about that.  Rule-related permissions checking
moved out of rewriter and into executor.
INITDB REQUIRED!

77 files changed:
src/backend/catalog/heap.c
src/backend/commands/command.c
src/backend/commands/explain.c
src/backend/commands/view.c
src/backend/executor/Makefile
src/backend/executor/execAmi.c
src/backend/executor/execMain.c
src/backend/executor/execProcnode.c
src/backend/executor/execTuples.c
src/backend/executor/nodeSubqueryscan.c [new file with mode: 0644]
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_main.c
src/backend/optimizer/path/allpaths.c
src/backend/optimizer/path/costsize.c
src/backend/optimizer/path/indxpath.c
src/backend/optimizer/path/joinpath.c
src/backend/optimizer/path/joinrels.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/Makefile
src/backend/optimizer/util/clauses.c
src/backend/optimizer/util/indexnode.c [deleted file]
src/backend/optimizer/util/pathnode.c
src/backend/optimizer/util/plancat.c
src/backend/optimizer/util/relnode.c
src/backend/optimizer/util/restrictinfo.c
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/rewrite/Makefile
src/backend/rewrite/locks.c [deleted file]
src/backend/rewrite/rewriteDefine.c
src/backend/rewrite/rewriteHandler.c
src/backend/rewrite/rewriteManip.c
src/backend/rewrite/rewriteRemove.c
src/backend/rewrite/rewriteSupport.c
src/backend/utils/adt/ruleutils.c
src/include/catalog/catversion.h
src/include/executor/nodeSubqueryscan.h [new file with mode: 0644]
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/plancat.h
src/include/optimizer/planmain.h
src/include/parser/parse_agg.h
src/include/parser/parse_node.h
src/include/parser/parse_relation.h
src/include/rewrite/locks.h [deleted file]
src/include/rewrite/rewriteManip.h
src/include/rewrite/rewriteSupport.h
src/test/regress/expected/errors.out

index 44728bf..1e4dca2 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.144 2000/09/12 21:06:46 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.145 2000/09/29 18:21:25 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -1543,8 +1543,7 @@ StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin,
        rte->eref->relname = RelationGetRelationName(rel);
        rte->inh = false;
        rte->inFromCl = true;
-       rte->skipAcl = false;
-       adsrc = deparse_expression(expr, lcons(lcons(rte, NIL), NIL), false);
+       adsrc = deparse_expression(expr, makeList1(makeList1(rte)), false);
 
        values[Anum_pg_attrdef_adrelid - 1] = RelationGetRelid(rel);
        values[Anum_pg_attrdef_adnum - 1] = attnum;
@@ -1626,8 +1625,7 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin)
        rte->eref->relname = RelationGetRelationName(rel);
        rte->inh = false;
        rte->inFromCl = true;
-       rte->skipAcl = false;
-       ccsrc = deparse_expression(expr, lcons(lcons(rte, NIL), NIL), false);
+       ccsrc = deparse_expression(expr, makeList1(makeList1(rte)), false);
 
        values[Anum_pg_relcheck_rcrelid - 1] = RelationGetRelid(rel);
        values[Anum_pg_relcheck_rcname - 1] = DirectFunctionCall1(namein,
@@ -1750,7 +1748,7 @@ AddRelationRawConstraints(Relation rel,
        pstate = make_parsestate(NULL);
        makeRangeTable(pstate, NULL);
        rte = addRangeTableEntry(pstate, relname, NULL, false, true);
-       addRTEtoJoinTree(pstate, rte);
+       addRTEtoJoinList(pstate, rte);
 
        /*
         * Process column default expressions.
@@ -1775,6 +1773,14 @@ AddRelationRawConstraints(Relation rel,
                        elog(ERROR, "Cannot use attribute(s) in DEFAULT clause");
 
                /*
+                * No subplans or aggregates, either...
+                */
+               if (contain_subplans(expr))
+                       elog(ERROR, "Cannot use subselect in DEFAULT clause");
+               if (contain_agg_clause(expr))
+                       elog(ERROR, "Cannot use aggregate in DEFAULT clause");
+
+               /*
                 * Check that it will be possible to coerce the expression to the
                 * column's type.  We store the expression without coercion,
                 * however, to avoid premature coercion in cases like
@@ -1884,10 +1890,18 @@ AddRelationRawConstraints(Relation rel,
                 * Make sure no outside relations are referred to.
                 */
                if (length(pstate->p_rtable) != 1)
-                       elog(ERROR, "Only relation '%s' can be referenced in CHECK",
+                       elog(ERROR, "Only relation \"%s\" can be referenced in CHECK",
                                 relname);
 
                /*
+                * No subplans or aggregates, either...
+                */
+               if (contain_subplans(expr))
+                       elog(ERROR, "Cannot use subselect in CHECK clause");
+               if (contain_agg_clause(expr))
+                       elog(ERROR, "Cannot use aggregate in CHECK clause");
+
+               /*
                 * Might as well try to reduce any constant expressions.
                 */
                expr = eval_const_expressions(expr);
index 8418068..f072c71 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.103 2000/09/12 21:06:47 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.104 2000/09/29 18:21:26 tgl Exp $
  *
  * NOTES
  *       The PerformAddAttribute() code, like most of the relation
@@ -1135,7 +1135,7 @@ AlterTableAddConstraint(char *relationName,
                                        else
                                                name="<unnamed>";
 
-                                       constlist=lcons(constr, NIL);
+                                       constlist = makeList1(constr);
 
                                        rel = heap_openr(relationName, AccessExclusiveLock);
 
@@ -1158,10 +1158,11 @@ AlterTableAddConstraint(char *relationName,
                                        makeRangeTable(pstate, NULL);
                                        rte = addRangeTableEntry(pstate, relationName, NULL,
                                                                                         false, true);
-                                       addRTEtoJoinTree(pstate, rte);
+                                       addRTEtoJoinList(pstate, rte);
 
                                        /* Convert the A_EXPR in raw_expr into an EXPR */
-                                       expr = transformExpr(pstate, constr->raw_expr, EXPR_COLUMN_FIRST);
+                                       expr = transformExpr(pstate, constr->raw_expr,
+                                                                                EXPR_COLUMN_FIRST);
 
                                        /*
                                         * Make sure it yields a boolean result.
@@ -1185,14 +1186,14 @@ AlterTableAddConstraint(char *relationName,
                                        /* And fix the opids */
                                        fix_opids(expr);
 
-                                       qual = lcons(expr, NIL);
+                                       qual = makeList1(expr);
 
                                        rte = makeNode(RangeTblEntry);
                                        rte->relname = relationName;
                                        rte->relid = RelationGetRelid(rel);
                                        rte->eref = makeNode(Attr);
                                        rte->eref->relname = relationName;
-                                       rtlist = lcons(rte, NIL);
+                                       rtlist = makeList1(rte);
 
                                        /* 
                                         * Scan through the rows now, making the necessary things
index 2b3d8b8..25b7517 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.58 2000/09/12 21:06:47 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.59 2000/09/29 18:21:26 tgl Exp $
  *
  */
 
@@ -176,6 +176,12 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
                case T_IndexScan:
                        pname = "Index Scan";
                        break;
+               case T_TidScan:
+                       pname = "Tid Scan";
+                       break;
+               case T_SubqueryScan:
+                       pname = "Subquery Scan";
+                       break;
                case T_Material:
                        pname = "Materialize";
                        break;
@@ -194,9 +200,6 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
                case T_Hash:
                        pname = "Hash";
                        break;
-               case T_TidScan:
-                       pname = "Tid Scan";
-                       break;
                default:
                        pname = "???";
                        break;
@@ -225,37 +228,27 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
                case T_TidScan:
                        if (((Scan *) plan)->scanrelid > 0)
                        {
-                               RangeTblEntry *rte = nth(((Scan *) plan)->scanrelid - 1, es->rtable);
+                               RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
+                                                                                         es->rtable);
+
+                               /* Assume it's on a real relation */
+                               Assert(rte->relname);
 
                                appendStringInfo(str, " on %s",
                                                                 stringStringInfo(rte->relname));
-                               if (rte->alias != NULL)
-                               {
-                                       if ((strcmp(rte->alias->relname, rte->relname) != 0)
-                                               || (length(rte->alias->attrs) > 0))
-                                       {
-                                               appendStringInfo(str, " %s",
-                                                                       stringStringInfo(rte->alias->relname));
-
-                                               if (length(rte->alias->attrs) > 0)
-                                               {
-                                                       List       *c;
-                                                       int                     firstEntry = true;
-
-                                                       appendStringInfo(str, " (");
-                                                       foreach(c, rte->alias->attrs)
-                                                       {
-                                                               if (!firstEntry)
-                                                               {
-                                                                       appendStringInfo(str, ", ");
-                                                                       firstEntry = false;
-                                                               }
-                                                               appendStringInfo(str, "%s", strVal(lfirst(c)));
-                                                       }
-                                                       appendStringInfo(str, ")");
-                                               }
-                                       }
-                               }
+                               if (strcmp(rte->eref->relname, rte->relname) != 0)
+                                       appendStringInfo(str, " %s",
+                                                                        stringStringInfo(rte->eref->relname));
+                       }
+                       break;
+               case T_SubqueryScan:
+                       if (((Scan *) plan)->scanrelid > 0)
+                       {
+                               RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
+                                                                                         es->rtable);
+
+                               appendStringInfo(str, " %s",
+                                                                stringStringInfo(rte->eref->relname));
                        }
                        break;
                default:
@@ -284,7 +277,8 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
                        for (i = 0; i < indent; i++)
                                appendStringInfo(str, "  ");
                        appendStringInfo(str, "    ->  ");
-                       explain_outNode(str, ((SubPlan *) lfirst(lst))->plan, indent + 2, es);
+                       explain_outNode(str, ((SubPlan *) lfirst(lst))->plan,
+                                                       indent + 4, es);
                }
                es->rtable = saved_rtable;
        }
@@ -307,32 +301,12 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
                explain_outNode(str, innerPlan(plan), indent + 3, es);
        }
 
-       /* subPlan-s */
-       if (plan->subPlan)
-       {
-               List       *saved_rtable = es->rtable;
-               List       *lst;
-
-               for (i = 0; i < indent; i++)
-                       appendStringInfo(str, "  ");
-               appendStringInfo(str, "  SubPlan\n");
-               foreach(lst, plan->subPlan)
-               {
-                       es->rtable = ((SubPlan *) lfirst(lst))->rtable;
-                       for (i = 0; i < indent; i++)
-                               appendStringInfo(str, "  ");
-                       appendStringInfo(str, "    ->  ");
-                       explain_outNode(str, ((SubPlan *) lfirst(lst))->plan, indent + 4, es);
-               }
-               es->rtable = saved_rtable;
-       }
-
-       if (nodeTag(plan) == T_Append)
+       if (IsA(plan, Append))
        {
+               Append     *appendplan = (Append *) plan;
                List       *saved_rtable = es->rtable;
-               List       *lst;
                int                     whichplan = 0;
-               Append     *appendplan = (Append *) plan;
+               List       *lst;
 
                foreach(lst, appendplan->appendplans)
                {
@@ -351,14 +325,55 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
 
                        for (i = 0; i < indent; i++)
                                appendStringInfo(str, "  ");
-                       appendStringInfo(str, "    ->  ");
+                       appendStringInfo(str, "  ->  ");
 
-                       explain_outNode(str, subnode, indent + 4, es);
+                       explain_outNode(str, subnode, indent + 3, es);
 
                        whichplan++;
                }
                es->rtable = saved_rtable;
        }
+
+       if (IsA(plan, SubqueryScan))
+       {
+               SubqueryScan *subqueryscan = (SubqueryScan *) plan;
+               Plan       *subnode = subqueryscan->subplan;
+               RangeTblEntry *rte = rt_fetch(subqueryscan->scan.scanrelid,
+                                                                         es->rtable);
+               List       *saved_rtable = es->rtable;
+
+               Assert(rte->subquery != NULL);
+               es->rtable = rte->subquery->rtable;
+
+               for (i = 0; i < indent; i++)
+                       appendStringInfo(str, "  ");
+               appendStringInfo(str, "  ->  ");
+
+               explain_outNode(str, subnode, indent + 3, es);
+
+               es->rtable = saved_rtable;
+       }
+
+       /* subPlan-s */
+       if (plan->subPlan)
+       {
+               List       *saved_rtable = es->rtable;
+               List       *lst;
+
+               for (i = 0; i < indent; i++)
+                       appendStringInfo(str, "  ");
+               appendStringInfo(str, "  SubPlan\n");
+               foreach(lst, plan->subPlan)
+               {
+                       es->rtable = ((SubPlan *) lfirst(lst))->rtable;
+                       for (i = 0; i < indent; i++)
+                               appendStringInfo(str, "  ");
+                       appendStringInfo(str, "    ->  ");
+                       explain_outNode(str, ((SubPlan *) lfirst(lst))->plan,
+                                                       indent + 4, es);
+               }
+               es->rtable = saved_rtable;
+       }
 }
 
 static char *
index d1d6300..1f245f1 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.48 2000/09/12 21:06:47 tgl Exp $
+ *     $Id: view.c,v 1.49 2000/09/29 18:21:26 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 static void
 DefineVirtualRelation(char *relname, List *tlist)
 {
-       CreateStmt      createStmt;
+       CreateStmt *createStmt = makeNode(CreateStmt);
        List       *attrList,
                           *t;
-       TargetEntry *entry;
-       Resdom     *res;
-       char       *resname;
-       char       *restypename;
 
        /*
-        * create a list with one entry per attribute of this relation. Each
-        * entry is a two element list. The first element is the name of the
-        * attribute (a string) and the second the name of the type (NOTE: a
-        * string, not a type id!).
+        * create a list of ColumnDef nodes based on the names and types of
+        * the (non-junk) targetlist items from the view's SELECT list.
         */
        attrList = NIL;
-       if (tlist != NIL)
+       foreach(t, tlist)
        {
-               foreach(t, tlist)
+               TargetEntry *entry = lfirst(t);
+               Resdom     *res = entry->resdom;
+
+               if (! res->resjunk)
                {
+                       char       *resname = res->resname;
+                       char       *restypename = typeidTypeName(res->restype);
                        ColumnDef  *def = makeNode(ColumnDef);
-                       TypeName   *typename;
-
-                       /*
-                        * find the names of the attribute & its type
-                        */
-                       entry = lfirst(t);
-                       res = entry->resdom;
-                       resname = res->resname;
-                       restypename = typeidTypeName(res->restype);
+                       TypeName   *typename = makeNode(TypeName);
 
-                       typename = makeNode(TypeName);
+                       def->colname = pstrdup(resname);
 
                        typename->name = pstrdup(restypename);
                        typename->typmod = res->restypmod;
-
-                       def->colname = pstrdup(resname);
-
                        def->typename = typename;
 
                        def->is_not_null = false;
+                       def->is_sequence = false;
                        def->raw_default = NULL;
                        def->cooked_default = NULL;
+                       def->constraints = NIL;
 
                        attrList = lappend(attrList, def);
                }
        }
-       else
+
+       if (attrList == NIL)
                elog(ERROR, "attempted to define virtual relation with no attrs");
 
        /*
-        * now create the parametesr for keys/inheritance etc. All of them are
+        * now create the parameters for keys/inheritance etc. All of them are
         * nil...
         */
-       createStmt.relname = relname;
-       createStmt.istemp = false;
-       createStmt.tableElts = attrList;
-/*       createStmt.tableType = NULL;*/
-       createStmt.inhRelnames = NIL;
-       createStmt.constraints = NIL;
+       createStmt->relname = relname;
+       createStmt->istemp = false;
+       createStmt->tableElts = attrList;
+       createStmt->inhRelnames = NIL;
+       createStmt->constraints = NIL;
 
        /*
         * finally create the relation...
         */
-       DefineRelation(&createStmt, RELKIND_VIEW);
+       DefineRelation(createStmt, RELKIND_VIEW);
 }
 
 /*------------------------------------------------------------------
@@ -149,13 +139,12 @@ FormViewRetrieveRule(char *viewName, Query *viewParse)
 
        attr = makeNode(Attr);
        attr->relname = pstrdup(viewName);
-/*       attr->refname = pstrdup(viewName);*/
        rule->rulename = pstrdup(rname);
        rule->whereClause = NULL;
        rule->event = CMD_SELECT;
        rule->object = attr;
        rule->instead = true;
-       rule->actions = lcons(viewParse, NIL);
+       rule->actions = makeList1(viewParse);
 
        return rule;
 }
@@ -231,6 +220,10 @@ UpdateRangeTableOfViewParse(char *viewName, Query *viewParse)
        rt_entry2 = addRangeTableEntry(NULL, viewName,
                                                                   makeAttr("*NEW*", NULL),
                                                                   false, false);
+       /* Must override addRangeTableEntry's default access-check flags */
+       rt_entry1->checkForRead = false;
+       rt_entry2->checkForRead = false;
+
        new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable));
 
        /*
index bf329b9..f26c35e 100644 (file)
@@ -4,7 +4,7 @@
 #    Makefile for executor
 #
 # IDENTIFICATION
-#    $Header: /cvsroot/pgsql/src/backend/executor/Makefile,v 1.13 2000/08/31 16:09:56 petere Exp $
+#    $Header: /cvsroot/pgsql/src/backend/executor/Makefile,v 1.14 2000/09/29 18:21:28 tgl Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -18,7 +18,7 @@ OBJS = execAmi.o execFlatten.o execJunk.o execMain.o \
        nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \
        nodeNestloop.o nodeResult.o nodeSeqscan.o nodeSort.o \
        nodeUnique.o nodeGroup.o spi.o nodeSubplan.o \
-       nodeTidscan.o
+       nodeSubqueryscan.o nodeTidscan.o
 
 all: SUBSYS.o
 
index 99d6b7b..31ad291 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $Id: execAmi.c,v 1.51 2000/08/03 19:19:30 tgl Exp $
+ *     $Id: execAmi.c,v 1.52 2000/09/29 18:21:28 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -44,6 +44,7 @@
 #include "executor/nodeSeqscan.h"
 #include "executor/nodeSort.h"
 #include "executor/nodeSubplan.h"
+#include "executor/nodeSubqueryscan.h"
 #include "executor/nodeUnique.h"
 
 static Pointer ExecBeginScan(Relation relation, int nkeys, ScanKey skeys,
@@ -304,6 +305,14 @@ ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent)
                        ExecIndexReScan((IndexScan *) node, exprCtxt, parent);
                        break;
 
+               case T_TidScan:
+                       ExecTidReScan((TidScan *) node, exprCtxt, parent);
+                       break;
+
+               case T_SubqueryScan:
+                       ExecSubqueryReScan((SubqueryScan *) node, exprCtxt, parent);
+                       break;
+
                case T_Material:
                        ExecMaterialReScan((Material *) node, exprCtxt, parent);
                        break;
@@ -348,10 +357,6 @@ ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent)
                        ExecReScanAppend((Append *) node, exprCtxt, parent);
                        break;
 
-               case T_TidScan:
-                       ExecTidReScan((TidScan *) node, exprCtxt, parent);
-                       break;
-
                default:
                        elog(ERROR, "ExecReScan: node type %d not supported",
                                 nodeTag(node));
index d46e0d3..a202da9 100644 (file)
@@ -27,7 +27,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.127 2000/09/12 21:06:48 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.128 2000/09/29 18:21:28 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -69,13 +69,11 @@ static void ExecReplace(TupleTableSlot *slot, ItemPointer tupleid,
 static TupleTableSlot *EvalPlanQualNext(EState *estate);
 static void EndEvalPlanQual(EState *estate);
 static void ExecCheckQueryPerms(CmdType operation, Query *parseTree,
-                                       Plan *plan);
-static void ExecCheckPlanPerms(Plan *plan, CmdType operation,
-                                  int resultRelation, bool resultIsScanned);
-static void ExecCheckRTPerms(List *rangeTable, CmdType operation,
-                                int resultRelation, bool resultIsScanned);
-static void ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation,
-                                 bool isResultRelation, bool resultIsScanned);
+                                                               Plan *plan);
+static void ExecCheckPlanPerms(Plan *plan, List *rangeTable,
+                                                          CmdType operation);
+static void ExecCheckRTPerms(List *rangeTable, CmdType operation);
+static void ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation);
 
 /* end of local decls */
 
@@ -390,51 +388,15 @@ ExecutorEnd(QueryDesc *queryDesc, EState *estate)
 static void
 ExecCheckQueryPerms(CmdType operation, Query *parseTree, Plan *plan)
 {
-       List       *rangeTable = parseTree->rtable;
-       int                     resultRelation = parseTree->resultRelation;
-       bool            resultIsScanned = false;
-       List       *lp;
-
-       /*
-        * 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((Node *) parseTree);
-
-               resultIsScanned = intMember(resultRelation, qvars);
-               freeList(qvars);
-       }
-
        /*
         * Check RTEs in the query's primary rangetable.
         */
-       ExecCheckRTPerms(rangeTable, operation, resultRelation, resultIsScanned);
-
-       /*
-        * Check SELECT FOR UPDATE access rights.
-        */
-       foreach(lp, parseTree->rowMark)
-       {
-               RowMark    *rm = lfirst(lp);
-
-               if (!(rm->info & ROW_ACL_FOR_UPDATE))
-                       continue;
-
-               ExecCheckRTEPerms(rt_fetch(rm->rti, rangeTable),
-                                                 CMD_UPDATE, true, false);
-       }
+       ExecCheckRTPerms(parseTree->rtable, operation);
 
        /*
         * Search for subplans and APPEND nodes to check their rangetables.
         */
-       ExecCheckPlanPerms(plan, operation, resultRelation, resultIsScanned);
+       ExecCheckPlanPerms(plan, parseTree->rtable, operation);
 }
 
 /*
@@ -447,8 +409,7 @@ ExecCheckQueryPerms(CmdType operation, Query *parseTree, Plan *plan)
  * in the query's main rangetable.  But at the moment, they're not.
  */
 static void
-ExecCheckPlanPerms(Plan *plan, CmdType operation,
-                                  int resultRelation, bool resultIsScanned)
+ExecCheckPlanPerms(Plan *plan, List *rangeTable, CmdType operation)
 {
        List       *subp;
 
@@ -461,28 +422,37 @@ ExecCheckPlanPerms(Plan *plan, CmdType operation,
        {
                SubPlan    *subplan = (SubPlan *) lfirst(subp);
 
-               ExecCheckRTPerms(subplan->rtable, CMD_SELECT, 0, false);
-               ExecCheckPlanPerms(subplan->plan, CMD_SELECT, 0, false);
+               ExecCheckRTPerms(subplan->rtable, CMD_SELECT);
+               ExecCheckPlanPerms(subplan->plan, subplan->rtable, CMD_SELECT);
        }
        foreach(subp, plan->subPlan)
        {
                SubPlan    *subplan = (SubPlan *) lfirst(subp);
 
-               ExecCheckRTPerms(subplan->rtable, CMD_SELECT, 0, false);
-               ExecCheckPlanPerms(subplan->plan, CMD_SELECT, 0, false);
+               ExecCheckRTPerms(subplan->rtable, CMD_SELECT);
+               ExecCheckPlanPerms(subplan->plan, subplan->rtable, CMD_SELECT);
        }
 
        /* Check lower plan nodes */
 
-       ExecCheckPlanPerms(plan->lefttree, operation,
-                                          resultRelation, resultIsScanned);
-       ExecCheckPlanPerms(plan->righttree, operation,
-                                          resultRelation, resultIsScanned);
+       ExecCheckPlanPerms(plan->lefttree, rangeTable, operation);
+       ExecCheckPlanPerms(plan->righttree, rangeTable, operation);
 
        /* Do node-type-specific checks */
 
        switch (nodeTag(plan))
        {
+               case T_SubqueryScan:
+                       {
+                               SubqueryScan   *scan = (SubqueryScan *) plan;
+                               RangeTblEntry *rte;
+
+                               /* Recursively check the subquery */
+                               rte = rt_fetch(scan->scan.scanrelid, rangeTable);
+                               Assert(rte->subquery != NULL);
+                               ExecCheckQueryPerms(operation, rte->subquery, scan->subplan);
+                               break;
+                       }
                case T_Append:
                        {
                                Append     *app = (Append *) plan;
@@ -490,43 +460,31 @@ ExecCheckPlanPerms(Plan *plan, CmdType operation,
 
                                if (app->inheritrelid > 0)
                                {
+                                       /* Append implements expansion of inheritance */
+                                       ExecCheckRTPerms(app->inheritrtable, operation);
 
-                                       /*
-                                        * Append implements expansion of inheritance; all
-                                        * members of inheritrtable list will be plugged into
-                                        * same RTE slot. Therefore, they are either all
-                                        * result relations or none.
-                                        */
-                                       List       *rtable;
-
-                                       foreach(rtable, app->inheritrtable)
+                                       /* Check appended plans w/outer rangetable */
+                                       foreach(appendplans, app->appendplans)
                                        {
-                                               ExecCheckRTEPerms((RangeTblEntry *) lfirst(rtable),
-                                                                                 operation,
-                                                                  (app->inheritrelid == resultRelation),
-                                                                                 resultIsScanned);
+                                               ExecCheckPlanPerms((Plan *) lfirst(appendplans),
+                                                                                  rangeTable,
+                                                                                  operation);
                                        }
                                }
                                else
                                {
                                        /* Append implements UNION, which must be a SELECT */
-                                       List       *rtables;
+                                       List       *rtables = app->unionrtables;
 
-                                       foreach(rtables, app->unionrtables)
+                                       /* Check appended plans with their rangetables */
+                                       foreach(appendplans, app->appendplans)
                                        {
-                                               ExecCheckRTPerms((List *) lfirst(rtables),
-                                                                                CMD_SELECT, 0, false);
+                                               ExecCheckPlanPerms((Plan *) lfirst(appendplans),
+                                                                                  (List *) lfirst(rtables),
+                                                                                  CMD_SELECT);
+                                               rtables = lnext(rtables);
                                        }
                                }
-
-                               /* Check appended plans */
-                               foreach(appendplans, app->appendplans)
-                               {
-                                       ExecCheckPlanPerms((Plan *) lfirst(appendplans),
-                                                                          operation,
-                                                                          resultRelation,
-                                                                          resultIsScanned);
-                               }
                                break;
                        }
 
@@ -538,28 +496,17 @@ ExecCheckPlanPerms(Plan *plan, CmdType operation,
 /*
  * ExecCheckRTPerms
  *             Check access permissions for all relations listed in a range table.
- *
- * If resultRelation is not 0, it is the RT index of the relation to be
- * treated as the result relation.     All other relations are assumed to be
- * read-only for the query.
  */
 static void
-ExecCheckRTPerms(List *rangeTable, CmdType operation,
-                                int resultRelation, bool resultIsScanned)
+ExecCheckRTPerms(List *rangeTable, CmdType operation)
 {
-       int                     rtindex = 0;
        List       *lp;
 
        foreach(lp, rangeTable)
        {
                RangeTblEntry *rte = lfirst(lp);
 
-               ++rtindex;
-
-               ExecCheckRTEPerms(rte,
-                                                 operation,
-                                                 (rtindex == resultRelation),
-                                                 resultIsScanned);
+               ExecCheckRTEPerms(rte, operation);
        }
 }
 
@@ -568,45 +515,48 @@ ExecCheckRTPerms(List *rangeTable, CmdType operation,
  *             Check access permissions for a single RTE.
  */
 static void
-ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation,
-                                 bool isResultRelation, bool resultIsScanned)
+ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation)
 {
        char       *relName;
        Oid                     userid;
        int32           aclcheck_result;
 
-       if (rte->skipAcl)
-       {
-
-               /*
-                * This happens if the access to this table is due to a view query
-                * rewriting - the rewrite handler already checked the permissions
-                * against the view owner, so we just skip this entry.
-                */
+       /*
+        * If it's a subquery RTE, ignore it --- it will be checked when
+        * ExecCheckPlanPerms finds the SubqueryScan node for it.
+        */
+       if (rte->subquery)
                return;
-       }
 
        relName = rte->relname;
 
        /*
+        * userid to check as: current user unless we have a setuid indication.
+        *
         * Note: GetUserId() is presently fast enough that there's no harm
         * in calling it separately for each RTE.  If that stops being true,
         * we could call it once in ExecCheckQueryPerms and pass the userid
         * down from there.  But for now, no need for the extra clutter.
         */
-       userid = GetUserId();
+       userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
 
 #define CHECK(MODE)            pg_aclcheck(relName, userid, MODE)
 
-       if (isResultRelation)
+       if (rte->checkForRead)
        {
-               if (resultIsScanned)
-               {
-                       aclcheck_result = CHECK(ACL_RD);
-                       if (aclcheck_result != ACLCHECK_OK)
-                               elog(ERROR, "%s: %s",
-                                        relName, aclcheck_error_strings[aclcheck_result]);
-               }
+               aclcheck_result = CHECK(ACL_RD);
+               if (aclcheck_result != ACLCHECK_OK)
+                       elog(ERROR, "%s: %s",
+                                relName, aclcheck_error_strings[aclcheck_result]);
+       }
+
+       if (rte->checkForWrite)
+       {
+               /*
+                * Note: write access in a SELECT context means SELECT FOR UPDATE.
+                * Right now we don't distinguish that from true update as far as
+                * permissions checks are concerned.
+                */
                switch (operation)
                {
                        case CMD_INSERT:
@@ -615,6 +565,7 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation,
                                if (aclcheck_result != ACLCHECK_OK)
                                        aclcheck_result = CHECK(ACL_WR);
                                break;
+                       case CMD_SELECT:
                        case CMD_DELETE:
                        case CMD_UPDATE:
                                aclcheck_result = CHECK(ACL_WR);
@@ -625,13 +576,10 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation,
                                aclcheck_result = ACLCHECK_OK;  /* keep compiler quiet */
                                break;
                }
+               if (aclcheck_result != ACLCHECK_OK)
+                       elog(ERROR, "%s: %s",
+                                relName, aclcheck_error_strings[aclcheck_result]);
        }
-       else
-               aclcheck_result = CHECK(ACL_RD);
-
-       if (aclcheck_result != ACLCHECK_OK)
-               elog(ERROR, "%s: %s",
-                        relName, aclcheck_error_strings[aclcheck_result]);
 }
 
 
@@ -755,26 +703,23 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
        /*
         * Have to lock relations selected for update
         */
-       estate->es_rowMark = NULL;
-       if (parseTree->rowMark != NULL)
+       estate->es_rowMark = NIL;
+       if (parseTree->rowMarks != NIL)
        {
                List       *l;
 
-               foreach(l, parseTree->rowMark)
+               foreach(l, parseTree->rowMarks)
                {
-                       RowMark    *rm = lfirst(l);
-                       Oid                     relid;
+                       Index           rti = lfirsti(l);
+                       Oid                     relid = getrelid(rti, rangeTable);
                        Relation        relation;
                        execRowMark *erm;
 
-                       if (!(rm->info & ROW_MARK_FOR_UPDATE))
-                               continue;
-                       relid = getrelid(rm->rti, rangeTable);
                        relation = heap_open(relid, RowShareLock);
                        erm = (execRowMark *) palloc(sizeof(execRowMark));
                        erm->relation = relation;
-                       erm->rti = rm->rti;
-                       sprintf(erm->resname, "ctid%u", rm->rti);
+                       erm->rti = rti;
+                       sprintf(erm->resname, "ctid%u", rti);
                        estate->es_rowMark = lappend(estate->es_rowMark, erm);
                }
        }
@@ -1097,7 +1042,7 @@ lnext:    ;
                                                                                 * ctid!! */
                                tupleid = &tuple_ctid;
                        }
-                       else if (estate->es_rowMark != NULL)
+                       else if (estate->es_rowMark != NIL)
                        {
                                List       *l;
 
@@ -1115,10 +1060,12 @@ lnext:  ;
                                                                                          erm->resname,
                                                                                          &datum,
                                                                                          &isNull))
-                                               elog(ERROR, "ExecutePlan: NO (junk) `%s' was found!", erm->resname);
+                                               elog(ERROR, "ExecutePlan: NO (junk) `%s' was found!",
+                                                        erm->resname);
 
                                        if (isNull)
-                                               elog(ERROR, "ExecutePlan: (junk) `%s' is NULL!", erm->resname);
+                                               elog(ERROR, "ExecutePlan: (junk) `%s' is NULL!",
+                                                        erm->resname);
 
                                        tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
                                        test = heap_mark4update(erm->relation, &tuple, &buffer);
@@ -1625,10 +1572,10 @@ ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate)
                rte->relid = RelationGetRelid(rel);
                rte->eref = makeNode(Attr);
                rte->eref->relname = rte->relname;
-               /* inh, inFromCl, skipAcl won't be used, leave them zero */
+               /* other fields won't be used, leave them zero */
 
                /* Set up single-entry range table */
-               econtext->ecxt_range_table = lcons(rte, NIL);
+               econtext->ecxt_range_table = makeList1(rte);
 
                estate->es_result_relation_constraints =
                        (List **) palloc(ncheck * sizeof(List *));
index 8c9970a..d6a1537 100644 (file)
@@ -12,7 +12,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.19 2000/08/13 02:50:03 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.20 2000/09/29 18:21:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -90,6 +90,7 @@
 #include "executor/nodeSeqscan.h"
 #include "executor/nodeSort.h"
 #include "executor/nodeSubplan.h"
+#include "executor/nodeSubqueryscan.h"
 #include "executor/nodeUnique.h"
 #include "miscadmin.h"
 #include "tcop/tcopprot.h"
@@ -153,6 +154,15 @@ ExecInitNode(Plan *node, EState *estate, Plan *parent)
                        result = ExecInitIndexScan((IndexScan *) node, estate, parent);
                        break;
 
+               case T_TidScan:
+                       result = ExecInitTidScan((TidScan *) node, estate, parent);
+                       break;
+
+               case T_SubqueryScan:
+                       result = ExecInitSubqueryScan((SubqueryScan *) node, estate,
+                                                                                 parent);
+                       break;
+
                        /* ----------------
                         *              join nodes
                         * ----------------
@@ -165,6 +175,14 @@ ExecInitNode(Plan *node, EState *estate, Plan *parent)
                        result = ExecInitMergeJoin((MergeJoin *) node, estate, parent);
                        break;
 
+               case T_Hash:
+                       result = ExecInitHash((Hash *) node, estate, parent);
+                       break;
+
+               case T_HashJoin:
+                       result = ExecInitHashJoin((HashJoin *) node, estate, parent);
+                       break;
+
                        /* ----------------
                         *              materialization nodes
                         * ----------------
@@ -189,18 +207,6 @@ ExecInitNode(Plan *node, EState *estate, Plan *parent)
                        result = ExecInitAgg((Agg *) node, estate, parent);
                        break;
 
-               case T_Hash:
-                       result = ExecInitHash((Hash *) node, estate, parent);
-                       break;
-
-               case T_HashJoin:
-                       result = ExecInitHashJoin((HashJoin *) node, estate, parent);
-                       break;
-
-               case T_TidScan:
-                       result = ExecInitTidScan((TidScan *) node, estate, parent);
-                       break;
-
                default:
                        elog(ERROR, "ExecInitNode: node %d unsupported", nodeTag(node));
                        result = FALSE;
@@ -272,6 +278,14 @@ ExecProcNode(Plan *node, Plan *parent)
                        result = ExecIndexScan((IndexScan *) node);
                        break;
 
+               case T_TidScan:
+                       result = ExecTidScan((TidScan *) node);
+                       break;
+
+               case T_SubqueryScan:
+                       result = ExecSubqueryScan((SubqueryScan *) node);
+                       break;
+
                        /* ----------------
                         *              join nodes
                         * ----------------
@@ -284,6 +298,14 @@ ExecProcNode(Plan *node, Plan *parent)
                        result = ExecMergeJoin((MergeJoin *) node);
                        break;
 
+               case T_Hash:
+                       result = ExecHash((Hash *) node);
+                       break;
+
+               case T_HashJoin:
+                       result = ExecHashJoin((HashJoin *) node);
+                       break;
+
                        /* ----------------
                         *              materialization nodes
                         * ----------------
@@ -308,18 +330,6 @@ ExecProcNode(Plan *node, Plan *parent)
                        result = ExecAgg((Agg *) node);
                        break;
 
-               case T_Hash:
-                       result = ExecHash((Hash *) node);
-                       break;
-
-               case T_HashJoin:
-                       result = ExecHashJoin((HashJoin *) node);
-                       break;
-
-               case T_TidScan:
-                       result = ExecTidScan((TidScan *) node);
-                       break;
-
                default:
                        elog(ERROR, "ExecProcNode: node %d unsupported", nodeTag(node));
                        result = NULL;
@@ -356,6 +366,12 @@ ExecCountSlotsNode(Plan *node)
                case T_IndexScan:
                        return ExecCountSlotsIndexScan((IndexScan *) node);
 
+               case T_TidScan:
+                       return ExecCountSlotsTidScan((TidScan *) node);
+
+               case T_SubqueryScan:
+                       return ExecCountSlotsSubqueryScan((SubqueryScan *) node);
+
                        /* ----------------
                         *              join nodes
                         * ----------------
@@ -366,6 +382,12 @@ ExecCountSlotsNode(Plan *node)
                case T_MergeJoin:
                        return ExecCountSlotsMergeJoin((MergeJoin *) node);
 
+               case T_Hash:
+                       return ExecCountSlotsHash((Hash *) node);
+
+               case T_HashJoin:
+                       return ExecCountSlotsHashJoin((HashJoin *) node);
+
                        /* ----------------
                         *              materialization nodes
                         * ----------------
@@ -385,15 +407,6 @@ ExecCountSlotsNode(Plan *node)
                case T_Agg:
                        return ExecCountSlotsAgg((Agg *) node);
 
-               case T_Hash:
-                       return ExecCountSlotsHash((Hash *) node);
-
-               case T_HashJoin:
-                       return ExecCountSlotsHashJoin((HashJoin *) node);
-
-               case T_TidScan:
-                       return ExecCountSlotsTidScan((TidScan *) node);
-
                default:
                        elog(ERROR, "ExecCountSlotsNode: node not yet supported: %d",
                                 nodeTag(node));
@@ -462,6 +475,14 @@ ExecEndNode(Plan *node, Plan *parent)
                        ExecEndIndexScan((IndexScan *) node);
                        break;
 
+               case T_TidScan:
+                       ExecEndTidScan((TidScan *) node);
+                       break;
+
+               case T_SubqueryScan:
+                       ExecEndSubqueryScan((SubqueryScan *) node);
+                       break;
+
                        /* ----------------
                         *              join nodes
                         * ----------------
@@ -474,6 +495,14 @@ ExecEndNode(Plan *node, Plan *parent)
                        ExecEndMergeJoin((MergeJoin *) node);
                        break;
 
+               case T_Hash:
+                       ExecEndHash((Hash *) node);
+                       break;
+
+               case T_HashJoin:
+                       ExecEndHashJoin((HashJoin *) node);
+                       break;
+
                        /* ----------------
                         *              materialization nodes
                         * ----------------
@@ -498,22 +527,6 @@ ExecEndNode(Plan *node, Plan *parent)
                        ExecEndAgg((Agg *) node);
                        break;
 
-                       /* ----------------
-                        *              XXX add hooks to these
-                        * ----------------
-                        */
-               case T_Hash:
-                       ExecEndHash((Hash *) node);
-                       break;
-
-               case T_HashJoin:
-                       ExecEndHashJoin((HashJoin *) node);
-                       break;
-
-               case T_TidScan:
-                       ExecEndTidScan((TidScan *) node);
-                       break;
-
                default:
                        elog(ERROR, "ExecEndNode: node %d unsupported", nodeTag(node));
                        break;
index 05474bc..0081057 100644 (file)
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.39 2000/09/12 21:06:48 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.40 2000/09/29 18:21:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -698,6 +698,22 @@ NodeGetResultTupleSlot(Plan *node)
                        }
                        break;
 
+               case T_TidScan:
+                       {
+                               CommonScanState *scanstate = ((TidScan *) node)->scan.scanstate;
+
+                               slot = scanstate->cstate.cs_ResultTupleSlot;
+                       }
+                       break;
+
+               case T_SubqueryScan:
+                       {
+                               CommonScanState *scanstate = ((SubqueryScan *) node)->scan.scanstate;
+
+                               slot = scanstate->cstate.cs_ResultTupleSlot;
+                       }
+                       break;
+
                case T_Material:
                        {
                                MaterialState *matstate = ((Material *) node)->matstate;
@@ -762,14 +778,6 @@ NodeGetResultTupleSlot(Plan *node)
                        }
                        break;
 
-               case T_TidScan:
-                       {
-                               CommonScanState *scanstate = ((IndexScan *) node)->scan.scanstate;
-
-                               slot = scanstate->cstate.cs_ResultTupleSlot;
-                       }
-                       break;
-
                default:
                        /* ----------------
                         *        should never get here
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
new file mode 100644 (file)
index 0000000..45f07a0
--- /dev/null
@@ -0,0 +1,289 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeSubqueryscan.c
+ *       Support routines for scanning subqueries (subselects in rangetable).
+ *
+ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *       $Header: /cvsroot/pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.1 2000/09/29 18:21:29 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ *             ExecSubqueryScan                        scans a subquery.
+ *             ExecSubqueryNext                        retrieve next tuple in sequential order.
+ *             ExecInitSubqueryScan            creates and initializes a subqueryscan node.
+ *             ExecEndSubqueryScan                     releases any storage allocated.
+ *             ExecSubqueryReScan                      rescans the relation
+ *
+ */
+#include "postgres.h"
+
+#include "catalog/pg_type.h"
+#include "executor/execdebug.h"
+#include "executor/execdefs.h"
+#include "executor/execdesc.h"
+#include "executor/nodeSubqueryscan.h"
+#include "parser/parsetree.h"
+#include "tcop/pquery.h"
+
+static TupleTableSlot *SubqueryNext(SubqueryScan *node);
+
+/* ----------------------------------------------------------------
+ *                                             Scan Support
+ * ----------------------------------------------------------------
+ */
+/* ----------------------------------------------------------------
+ *             SubqueryNext
+ *
+ *             This is a workhorse for ExecSubqueryScan
+ * ----------------------------------------------------------------
+ */
+static TupleTableSlot *
+SubqueryNext(SubqueryScan *node)
+{
+       SubqueryScanState *subquerystate;
+       EState     *estate;
+       ScanDirection direction;
+       int                     execdir;
+       TupleTableSlot *slot;
+       Const           countOne;
+
+       /* ----------------
+        *      get information from the estate and scan state
+        * ----------------
+        */
+       estate = node->scan.plan.state;
+       subquerystate = (SubqueryScanState *) node->scan.scanstate;
+       direction = estate->es_direction;
+       execdir = ScanDirectionIsBackward(direction) ? EXEC_BACK : EXEC_FOR;
+       slot = subquerystate->csstate.css_ScanTupleSlot;
+
+       /*
+        * Check if we are evaluating PlanQual for tuple of this relation.
+        * Additional checking is not good, but no other way for now. We could
+        * introduce new nodes for this case and handle SubqueryScan --> NewNode
+        * switching in Init/ReScan plan...
+        */
+       if (estate->es_evTuple != NULL &&
+               estate->es_evTuple[node->scan.scanrelid - 1] != NULL)
+       {
+               ExecClearTuple(slot);
+               if (estate->es_evTupleNull[node->scan.scanrelid - 1])
+                       return slot;            /* return empty slot */
+
+               /* probably ought to use ExecStoreTuple here... */
+               slot->val = estate->es_evTuple[node->scan.scanrelid - 1];
+               slot->ttc_shouldFree = false;
+
+               /* Flag for the next call that no more tuples */
+               estate->es_evTupleNull[node->scan.scanrelid - 1] = true;
+               return (slot);
+       }
+
+       memset(&countOne, 0, sizeof(countOne));
+       countOne.type = T_Const;
+       countOne.consttype = INT4OID;
+       countOne.constlen = sizeof(int4);
+       countOne.constvalue = Int32GetDatum(1);
+       countOne.constisnull = false;
+       countOne.constbyval = true;
+       countOne.constisset = false;
+       countOne.constiscast = false;
+
+       /* ----------------
+        *      get the next tuple from the sub-query
+        * ----------------
+        */
+       slot = ExecutorRun(subquerystate->sss_SubQueryDesc,
+                                          subquerystate->sss_SubEState,
+                                          execdir,
+                                          NULL,        /* offset */
+                                          (Node *) &countOne);
+
+       subquerystate->csstate.css_ScanTupleSlot = slot;
+
+       return slot;
+}
+
+/* ----------------------------------------------------------------
+ *             ExecSubqueryScan(node)
+ *
+ *             Scans the subquery sequentially and returns the next qualifying
+ *             tuple.
+ *             It calls the ExecScan() routine and passes it the access method
+ *             which retrieve tuples sequentially.
+ *
+ */
+
+TupleTableSlot *
+ExecSubqueryScan(SubqueryScan *node)
+{
+       /* ----------------
+        *      use SubqueryNext as access method
+        * ----------------
+        */
+       return ExecScan(&node->scan, (ExecScanAccessMtd) SubqueryNext);
+}
+
+/* ----------------------------------------------------------------
+ *             ExecInitSubqueryScan
+ * ----------------------------------------------------------------
+ */
+bool
+ExecInitSubqueryScan(SubqueryScan *node, EState *estate, Plan *parent)
+{
+       SubqueryScanState *subquerystate;
+       RangeTblEntry *rte;
+
+       /* ----------------
+        *      SubqueryScan should not have any "normal" children.
+        * ----------------
+        */
+       Assert(outerPlan((Plan *) node) == NULL);
+       Assert(innerPlan((Plan *) node) == NULL);
+
+       /* ----------------
+        *      assign the node's execution state
+        * ----------------
+        */
+       node->scan.plan.state = estate;
+
+       /* ----------------
+        *       create new SubqueryScanState for node
+        * ----------------
+        */
+       subquerystate = makeNode(SubqueryScanState);
+       node->scan.scanstate = (CommonScanState *) subquerystate;
+
+       /* ----------------
+        *      Miscellaneous initialization
+        *
+        *               +      create expression context for node
+        * ----------------
+        */
+       ExecAssignExprContext(estate, &subquerystate->csstate.cstate);
+
+#define SUBQUERYSCAN_NSLOTS 2
+       /* ----------------
+        *      tuple table initialization
+        * ----------------
+        */
+       ExecInitResultTupleSlot(estate, &subquerystate->csstate.cstate);
+
+       /* ----------------
+        *      initialize subquery
+        * ----------------
+        */
+       rte = rt_fetch(node->scan.scanrelid, estate->es_range_table);
+       Assert(rte->subquery != NULL);
+
+       subquerystate->sss_SubQueryDesc = CreateQueryDesc(rte->subquery,
+                                                                                                         node->subplan,
+                                                                                                         None);
+       subquerystate->sss_SubEState = CreateExecutorState();
+
+       ExecutorStart(subquerystate->sss_SubQueryDesc,
+                                 subquerystate->sss_SubEState);
+
+       subquerystate->csstate.css_ScanTupleSlot = NULL;
+       subquerystate->csstate.cstate.cs_TupFromTlist = false;
+
+       /* ----------------
+        *      initialize tuple type
+        * ----------------
+        */
+       ExecAssignResultTypeFromTL((Plan *) node, &subquerystate->csstate.cstate);
+       ExecAssignProjectionInfo((Plan *) node, &subquerystate->csstate.cstate);
+
+       return TRUE;
+}
+
+int
+ExecCountSlotsSubqueryScan(SubqueryScan *node)
+{
+       /*
+        * The subplan has its own tuple table and must not be counted here!
+        */
+       return ExecCountSlotsNode(outerPlan(node)) +
+       ExecCountSlotsNode(innerPlan(node)) +
+       SUBQUERYSCAN_NSLOTS;
+}
+
+/* ----------------------------------------------------------------
+ *             ExecEndSubqueryScan
+ *
+ *             frees any storage allocated through C routines.
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndSubqueryScan(SubqueryScan *node)
+{
+       SubqueryScanState *subquerystate;
+
+       /* ----------------
+        *      get information from node
+        * ----------------
+        */
+       subquerystate = (SubqueryScanState *) node->scan.scanstate;
+
+       /* ----------------
+        *      Free the projection info and the scan attribute info
+        *
+        *      Note: we don't ExecFreeResultType(subquerystate)
+        *                because the rule manager depends on the tupType
+        *                returned by ExecMain().  So for now, this
+        *                is freed at end-transaction time.  -cim 6/2/91
+        * ----------------
+        */
+       ExecFreeProjectionInfo(&subquerystate->csstate.cstate);
+       ExecFreeExprContext(&subquerystate->csstate.cstate);
+
+       /* ----------------
+        * close down subquery
+        * ----------------
+        */
+       ExecutorEnd(subquerystate->sss_SubQueryDesc,
+                               subquerystate->sss_SubEState);
+
+       /* XXX we seem to be leaking the querydesc and sub-EState... */
+
+       subquerystate->csstate.css_ScanTupleSlot = NULL;
+
+       /* ----------------
+        *      clean out the tuple table
+        * ----------------
+        */
+       ExecClearTuple(subquerystate->csstate.cstate.cs_ResultTupleSlot);
+}
+
+/* ----------------------------------------------------------------
+ *             ExecSubqueryReScan
+ *
+ *             Rescans the relation.
+ * ----------------------------------------------------------------
+ */
+void
+ExecSubqueryReScan(SubqueryScan *node, ExprContext *exprCtxt, Plan *parent)
+{
+       SubqueryScanState *subquerystate;
+       EState     *estate;
+
+       subquerystate = (SubqueryScanState *) node->scan.scanstate;
+       estate = node->scan.plan.state;
+
+       /* If this is re-scanning of PlanQual ... */
+       if (estate->es_evTuple != NULL &&
+               estate->es_evTuple[node->scan.scanrelid - 1] != NULL)
+       {
+               estate->es_evTupleNull[node->scan.scanrelid - 1] = false;
+               return;
+       }
+
+       ExecReScan(node->subplan, NULL, NULL);
+       subquerystate->csstate.css_ScanTupleSlot = NULL;
+}
index d6883bb..808ffbd 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.122 2000/09/20 15:28:01 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.123 2000/09/29 18:21:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -300,6 +300,31 @@ _copyTidScan(TidScan *from)
        return newnode;
 }
 
+/* ----------------
+ *             _copySubqueryScan
+ * ----------------
+ */
+static SubqueryScan *
+_copySubqueryScan(SubqueryScan *from)
+{
+       SubqueryScan  *newnode = makeNode(SubqueryScan);
+
+       /* ----------------
+        *      copy node superclass fields
+        * ----------------
+        */
+       CopyPlanFields((Plan *) from, (Plan *) newnode);
+       CopyScanFields((Scan *) from, (Scan *) newnode);
+
+       /* ----------------
+        *      copy remainder of node
+        * ----------------
+        */
+       Node_Copy(from, newnode, subplan);
+
+       return newnode;
+}
+
 
 /* ----------------
  *             CopyJoinFields
@@ -913,6 +938,17 @@ _copyRangeTblRef(RangeTblRef *from)
        return newnode;
 }
 
+static FromExpr *
+_copyFromExpr(FromExpr *from)
+{
+       FromExpr *newnode = makeNode(FromExpr);
+
+       Node_Copy(from, newnode, fromlist);
+       Node_Copy(from, newnode, quals);
+
+       return newnode;
+}
+
 static JoinExpr *
 _copyJoinExpr(JoinExpr *from)
 {
@@ -1025,9 +1061,11 @@ _copyRelOptInfo(RelOptInfo *from)
        Node_Copy(from, newnode, cheapest_total_path);
        newnode->pruneable = from->pruneable;
 
+       newnode->issubquery = from->issubquery;
        newnode->indexed = from->indexed;
        newnode->pages = from->pages;
        newnode->tuples = from->tuples;
+       Node_Copy(from, newnode, subplan);
 
        Node_Copy(from, newnode, baserestrictinfo);
        newnode->baserestrictcost = from->baserestrictcost;
@@ -1306,7 +1344,7 @@ _copyRestrictInfo(RestrictInfo *from)
         * ----------------
         */
        Node_Copy(from, newnode, clause);
-       newnode->isjoinqual = from->isjoinqual;
+       newnode->ispusheddown = from->ispusheddown;
        Node_Copy(from, newnode, subclauseindices);
        newnode->mergejoinoperator = from->mergejoinoperator;
        newnode->left_sortop = from->left_sortop;
@@ -1392,22 +1430,14 @@ _copyRangeTblEntry(RangeTblEntry *from)
        if (from->relname)
                newnode->relname = pstrdup(from->relname);
        newnode->relid = from->relid;
+       Node_Copy(from, newnode, subquery);
        Node_Copy(from, newnode, alias);
        Node_Copy(from, newnode, eref);
        newnode->inh = from->inh;
        newnode->inFromCl = from->inFromCl;
-       newnode->skipAcl = from->skipAcl;
-
-       return newnode;
-}
-
-static RowMark *
-_copyRowMark(RowMark *from)
-{
-       RowMark    *newnode = makeNode(RowMark);
-
-       newnode->rti = from->rti;
-       newnode->info = from->info;
+       newnode->checkForRead = from->checkForRead;
+       newnode->checkForWrite = from->checkForWrite;
+       newnode->checkAsUser = from->checkAsUser;
 
        return newnode;
 }
@@ -1674,8 +1704,8 @@ _copyQuery(Query *from)
        Node_Copy(from, newnode, jointree);
 
        Node_Copy(from, newnode, targetList);
-       Node_Copy(from, newnode, qual);
-       Node_Copy(from, newnode, rowMark);
+
+       newnode->rowMarks = listCopy(from->rowMarks);
 
        Node_Copy(from, newnode, distinctClause);
        Node_Copy(from, newnode, sortClause);
@@ -2493,6 +2523,9 @@ copyObject(void *from)
                case T_TidScan:
                        retval = _copyTidScan(from);
                        break;
+               case T_SubqueryScan:
+                       retval = _copySubqueryScan(from);
+                       break;
                case T_Join:
                        retval = _copyJoin(from);
                        break;
@@ -2575,6 +2608,9 @@ copyObject(void *from)
                case T_RangeTblRef:
                        retval = _copyRangeTblRef(from);
                        break;
+               case T_FromExpr:
+                       retval = _copyFromExpr(from);
+                       break;
                case T_JoinExpr:
                        retval = _copyJoinExpr(from);
                        break;
@@ -2881,9 +2917,6 @@ copyObject(void *from)
                case T_CaseWhen:
                        retval = _copyCaseWhen(from);
                        break;
-               case T_RowMark:
-                       retval = _copyRowMark(from);
-                       break;
                case T_FkConstraint:
                        retval = _copyFkConstraint(from);
                        break;
index 51a7a03..bcb8e39 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.73 2000/09/12 21:06:49 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.74 2000/09/29 18:21:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -312,6 +312,17 @@ _equalRangeTblRef(RangeTblRef *a, RangeTblRef *b)
 }
 
 static bool
+_equalFromExpr(FromExpr *a, FromExpr *b)
+{
+       if (!equal(a->fromlist, b->fromlist))
+               return false;
+       if (!equal(a->quals, b->quals))
+               return false;
+
+       return true;
+}
+
+static bool
 _equalJoinExpr(JoinExpr *a, JoinExpr *b)
 {
        if (a->jointype != b->jointype)
@@ -346,7 +357,7 @@ _equalRelOptInfo(RelOptInfo *a, RelOptInfo *b)
 
        /*
         * We treat RelOptInfos as equal if they refer to the same base rels
-        * joined in the same order.  Is this sufficient?
+        * joined in the same order.  Is this appropriate/sufficient?
         */
        return equali(a->relids, b->relids);
 }
@@ -495,7 +506,7 @@ _equalRestrictInfo(RestrictInfo *a, RestrictInfo *b)
 {
        if (!equal(a->clause, b->clause))
                return false;
-       if (a->isjoinqual != b->isjoinqual)
+       if (a->ispusheddown != b->ispusheddown)
                return false;
        if (!equal(a->subclauseindices, b->subclauseindices))
                return false;
@@ -601,9 +612,7 @@ _equalQuery(Query *a, Query *b)
                return false;
        if (!equal(a->targetList, b->targetList))
                return false;
-       if (!equal(a->qual, b->qual))
-               return false;
-       if (!equal(a->rowMark, b->rowMark))
+       if (!equali(a->rowMarks, b->rowMarks))
                return false;
        if (!equal(a->distinctClause, b->distinctClause))
                return false;
@@ -1651,6 +1660,8 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
                return false;
        if (a->relid != b->relid)
                return false;
+       if (!equal(a->subquery, b->subquery))
+               return false;
        if (!equal(a->alias, b->alias))
                return false;
        if (!equal(a->eref, b->eref))
@@ -1659,7 +1670,11 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
                return false;
        if (a->inFromCl != b->inFromCl)
                return false;
-       if (a->skipAcl != b->skipAcl)
+       if (a->checkForRead != b->checkForRead)
+               return false;
+       if (a->checkForWrite != b->checkForWrite)
+               return false;
+       if (a->checkAsUser != b->checkAsUser)
                return false;
 
        return true;
@@ -1677,17 +1692,6 @@ _equalSortClause(SortClause *a, SortClause *b)
 }
 
 static bool
-_equalRowMark(RowMark *a, RowMark *b)
-{
-       if (a->rti != b->rti)
-               return false;
-       if (a->info != b->info)
-               return false;
-
-       return true;
-}
-
-static bool
 _equalFkConstraint(FkConstraint *a, FkConstraint *b)
 {
        if (!equalstr(a->constr_name, b->constr_name))
@@ -1835,6 +1839,9 @@ equal(void *a, void *b)
                case T_RangeTblRef:
                        retval = _equalRangeTblRef(a, b);
                        break;
+               case T_FromExpr:
+                       retval = _equalFromExpr(a, b);
+                       break;
                case T_JoinExpr:
                        retval = _equalJoinExpr(a, b);
                        break;
@@ -2140,9 +2147,6 @@ equal(void *a, void *b)
                case T_CaseWhen:
                        retval = _equalCaseWhen(a, b);
                        break;
-               case T_RowMark:
-                       retval = _equalRowMark(a, b);
-                       break;
                case T_FkConstraint:
                        retval = _equalFkConstraint(a, b);
                        break;
index e94b357..358e6a7 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/list.c,v 1.33 2000/09/12 21:06:49 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/list.c,v 1.34 2000/09/29 18:21:29 tgl Exp $
  *
  * NOTES
  *       XXX a few of the following functions are duplicated to handle
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
-#include "nodes/parsenodes.h"
-
-/*
- *     makeList
- *
- *     Take varargs, terminated by -1, and make a List
- */
-List *
-makeList(void *elem,...)
-{
-       va_list         args;
-       List       *retval = NIL;
-       List       *temp = NIL;
-       List       *tempcons = NIL;
-
-       va_start(args, elem);
-
-       temp = elem;
-       while (temp != (void *) -1)
-       {
-               temp = lcons(temp, NIL);
-               if (tempcons == NIL)
-                       retval = temp;
-               else
-                       lnext(tempcons) = temp;
-               tempcons = temp;
 
-               temp = va_arg(args, void *);
-       }
-
-       va_end(args);
+#include "nodes/parsenodes.h"
 
-       return retval;
-}
 
 /*
  *     makeInteger
@@ -307,7 +276,7 @@ sameseti(List *list1, List *list2)
  * as were in the inputs.
  */
 List *
-LispUnion(List *l1, List *l2)
+set_union(List *l1, List *l2)
 {
        List       *retval = listCopy(l1);
        List       *i;
@@ -321,7 +290,7 @@ LispUnion(List *l1, List *l2)
 }
 
 List *
-LispUnioni(List *l1, List *l2)
+set_unioni(List *l1, List *l2)
 {
        List       *retval = listCopy(l1);
        List       *i;
@@ -385,7 +354,8 @@ intMember(int l1, List *l2)
 
 /*
  * lremove
- *       Removes 'elem' from the the linked list.
+ *       Removes 'elem' from the linked list (destructively changing the list!).
+ *
  *       This version matches 'elem' using simple pointer comparison.
  *       See also LispRemove.
  */
@@ -414,7 +384,8 @@ lremove(void *elem, List *list)
 
 /*
  *     LispRemove
- *       Removes 'elem' from the the linked list.
+ *       Removes 'elem' from the linked list (destructively changing the list!).
+ *
  *       This version matches 'elem' using equal().
  *       (If there is more than one equal list member, the first is removed.)
  *       See also lremove.
@@ -442,10 +413,12 @@ LispRemove(void *elem, List *list)
        return result;
 }
 
-#ifdef NOT_USED
-
+/*
+ *     lremovei
+ *             lremove() for integer lists.
+ */
 List *
-intLispRemove(int elem, List *list)
+lremovei(int elem, List *list)
 {
        List       *l;
        List       *prev = NIL;
@@ -467,8 +440,6 @@ intLispRemove(int elem, List *list)
        return result;
 }
 
-#endif
-
 /*
  * ltruncate
  *             Truncate a list to n elements.
index 8b24b82..39bc497 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.126 2000/09/12 21:06:49 tgl Exp $
+ *     $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.127 2000/09/29 18:21:29 tgl Exp $
  *
  * NOTES
  *       Every (plan) node in POSTGRES has an associated "out" routine which
@@ -220,6 +220,9 @@ _outQuery(StringInfo str, Query *node)
 
        if (node->utilityStmt)
        {
+               /*
+                * Hack to make up for lack of outfuncs for utility-stmt nodes
+                */
                switch (nodeTag(node->utilityStmt))
                {
                        case T_CreateStmt:
@@ -239,7 +242,7 @@ _outQuery(StringInfo str, Query *node)
                                break;
 
                        case T_NotifyStmt:
-                               appendStringInfo(str, " :utility ");
+                               appendStringInfo(str, " :notify ");
                                _outToken(str, ((NotifyStmt *) (node->utilityStmt))->relname);
                                break;
 
@@ -250,32 +253,34 @@ _outQuery(StringInfo str, Query *node)
        else
                appendStringInfo(str, " :utility <>");
 
-       appendStringInfo(str, " :resultRelation %u :into ",
+       appendStringInfo(str, " :resultRelation %d :into ",
                                         node->resultRelation);
        _outToken(str, node->into);
 
-       appendStringInfo(str,
-       " :isPortal %s :isBinary %s :isTemp %s :unionall %s :distinctClause ",
+       appendStringInfo(str, " :isPortal %s :isBinary %s :isTemp %s"
+                                        " :unionall %s :hasAggs %s :hasSubLinks %s :rtable ",
                                         node->isPortal ? "true" : "false",
                                         node->isBinary ? "true" : "false",
                                         node->isTemp ? "true" : "false",
-                                        node->unionall ? "true" : "false");
-       _outNode(str, node->distinctClause);
-
-       appendStringInfo(str, " :sortClause ");
-       _outNode(str, node->sortClause);
-
-       appendStringInfo(str, " :rtable ");
+                                        node->unionall ? "true" : "false",
+                                        node->hasAggs ? "true" : "false",
+                                        node->hasSubLinks ? "true" : "false");
        _outNode(str, node->rtable);
 
        appendStringInfo(str, " :jointree ");
        _outNode(str, node->jointree);
 
-       appendStringInfo(str, " :targetlist ");
+       appendStringInfo(str, " :targetList ");
        _outNode(str, node->targetList);
 
-       appendStringInfo(str, " :qual ");
-       _outNode(str, node->qual);
+       appendStringInfo(str, " :rowMarks ");
+       _outIntList(str, node->rowMarks);
+
+       appendStringInfo(str, " :distinctClause ");
+       _outNode(str, node->distinctClause);
+
+       appendStringInfo(str, " :sortClause ");
+       _outNode(str, node->sortClause);
 
        appendStringInfo(str, " :groupClause ");
        _outNode(str, node->groupClause);
@@ -283,23 +288,17 @@ _outQuery(StringInfo str, Query *node)
        appendStringInfo(str, " :havingQual ");
        _outNode(str, node->havingQual);
 
-       appendStringInfo(str, " :hasAggs %s :hasSubLinks %s :unionClause ",
-                                        node->hasAggs ? "true" : "false",
-                                        node->hasSubLinks ? "true" : "false");
-       _outNode(str, node->unionClause);
-
        appendStringInfo(str, " :intersectClause ");
        _outNode(str, node->intersectClause);
 
+       appendStringInfo(str, " :unionClause ");
+       _outNode(str, node->unionClause);
+
        appendStringInfo(str, " :limitOffset ");
        _outNode(str, node->limitOffset);
 
        appendStringInfo(str, " :limitCount ");
        _outNode(str, node->limitCount);
-
-       appendStringInfo(str, " :rowMark ");
-       _outNode(str, node->rowMark);
-
 }
 
 static void
@@ -536,6 +535,19 @@ _outTidScan(StringInfo str, TidScan *node)
 }
 
 /*
+ *     SubqueryScan is a subclass of Scan
+ */
+static void
+_outSubqueryScan(StringInfo str, SubqueryScan *node)
+{
+       appendStringInfo(str, " SUBQUERYSCAN ");
+       _outPlanInfo(str, (Plan *) node);
+
+       appendStringInfo(str, " :scanrelid %u :subplan ", node->scan.scanrelid);
+       _outNode(str, node->subplan);
+}
+
+/*
  *     Material is a subclass of Plan
  */
 static void
@@ -864,6 +876,18 @@ _outRangeTblRef(StringInfo str, RangeTblRef *node)
 }
 
 /*
+ *     FromExpr
+ */
+static void
+_outFromExpr(StringInfo str, FromExpr *node)
+{
+       appendStringInfo(str, " FROMEXPR :fromlist ");
+       _outNode(str, node->fromlist);
+       appendStringInfo(str, " :quals ");
+       _outNode(str, node->quals);
+}
+
+/*
  *     JoinExpr
  */
 static void
@@ -916,36 +940,33 @@ _outRelOptInfo(StringInfo str, RelOptInfo *node)
        appendStringInfo(str, " RELOPTINFO :relids ");
        _outIntList(str, node->relids);
 
-       appendStringInfo(str,
-                                        " :rows %.0f :width %d :indexed %s :pages %ld :tuples %.0f :targetlist ",
+       appendStringInfo(str, " :rows %.0f :width %d :targetlist ",
                                         node->rows,
-                                        node->width,
-                                        node->indexed ? "true" : "false",
-                                        node->pages,
-                                        node->tuples);
+                                        node->width);
        _outNode(str, node->targetlist);
 
        appendStringInfo(str, " :pathlist ");
        _outNode(str, node->pathlist);
-
        appendStringInfo(str, " :cheapest_startup_path ");
        _outNode(str, node->cheapest_startup_path);
        appendStringInfo(str, " :cheapest_total_path ");
        _outNode(str, node->cheapest_total_path);
 
-       appendStringInfo(str,
-                                        " :pruneable %s :baserestrictinfo ",
-                                        node->pruneable ? "true" : "false");
-       _outNode(str, node->baserestrictinfo);
+       appendStringInfo(str, " :pruneable %s :issubquery %s :indexed %s :pages %ld :tuples %.0f :subplan ",
+                                        node->pruneable ? "true" : "false",
+                                        node->issubquery ? "true" : "false",
+                                        node->indexed ? "true" : "false",
+                                        node->pages,
+                                        node->tuples);
+       _outNode(str, node->subplan);
 
-       appendStringInfo(str,
-                                        " :baserestrictcost %.2f :outerjoinset ",
+       appendStringInfo(str, " :baserestrictinfo ");
+       _outNode(str, node->baserestrictinfo);
+       appendStringInfo(str, " :baserestrictcost %.2f :outerjoinset ",
                                         node->baserestrictcost);
        _outIntList(str, node->outerjoinset);
-
        appendStringInfo(str, " :joininfo ");
        _outNode(str, node->joininfo);
-
        appendStringInfo(str, " :innerjoin ");
        _outNode(str, node->innerjoin);
 }
@@ -977,21 +998,21 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
 {
        appendStringInfo(str, " RTE :relname ");
        _outToken(str, node->relname);
-       appendStringInfo(str, " :relid %u :alias ",
+       appendStringInfo(str, " :relid %u ",
                                         node->relid);
+       appendStringInfo(str, " :subquery ");
+       _outNode(str, node->subquery);
+       appendStringInfo(str, " :alias ");
        _outNode(str, node->alias);
        appendStringInfo(str, " :eref ");
        _outNode(str, node->eref);
-       appendStringInfo(str, " :inh %s :inFromCl %s :skipAcl %s",
+       appendStringInfo(str, " :inh %s :inFromCl %s :checkForRead %s"
+                                        " :checkForWrite %s :checkAsUser %u",
                                         node->inh ? "true" : "false",
                                         node->inFromCl ? "true" : "false",
-                                        node->skipAcl ? "true" : "false");
-}
-
-static void
-_outRowMark(StringInfo str, RowMark *node)
-{
-       appendStringInfo(str, " ROWMARK :rti %u :info %u", node->rti, node->info);
+                                        node->checkForRead ? "true" : "false",
+                                        node->checkForWrite ? "true" : "false",
+                                        node->checkAsUser);
 }
 
 /*
@@ -1151,8 +1172,8 @@ _outRestrictInfo(StringInfo str, RestrictInfo *node)
        appendStringInfo(str, " RESTRICTINFO :clause ");
        _outNode(str, node->clause);
 
-       appendStringInfo(str, " :isjoinqual %s :subclauseindices ",
-                                        node->isjoinqual ? "true" : "false");
+       appendStringInfo(str, " :ispusheddown %s :subclauseindices ",
+                                        node->ispusheddown ? "true" : "false");
        _outNode(str, node->subclauseindices);
 
        appendStringInfo(str, " :mergejoinoperator %u ", node->mergejoinoperator);
@@ -1492,6 +1513,9 @@ _outNode(StringInfo str, void *obj)
                        case T_TidScan:
                                _outTidScan(str, obj);
                                break;
+                       case T_SubqueryScan:
+                               _outSubqueryScan(str, obj);
+                               break;
                        case T_Material:
                                _outMaterial(str, obj);
                                break;
@@ -1555,6 +1579,9 @@ _outNode(StringInfo str, void *obj)
                        case T_RangeTblRef:
                                _outRangeTblRef(str, obj);
                                break;
+                       case T_FromExpr:
+                               _outFromExpr(str, obj);
+                               break;
                        case T_JoinExpr:
                                _outJoinExpr(str, obj);
                                break;
@@ -1573,9 +1600,6 @@ _outNode(StringInfo str, void *obj)
                        case T_RangeTblEntry:
                                _outRangeTblEntry(str, obj);
                                break;
-                       case T_RowMark:
-                               _outRowMark(str, obj);
-                               break;
                        case T_Path:
                                _outPath(str, obj);
                                break;
index 3f48cb0..a0417f8 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.41 2000/09/25 18:14:55 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.42 2000/09/29 18:21:29 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -39,7 +39,6 @@ print(void *obj)
        s = nodeToString(obj);
        printf("%s\n", s);
        fflush(stdout);
-       return;
 }
 
 /*
@@ -132,10 +131,15 @@ print_rt(List *rtable)
        {
                RangeTblEntry *rte = lfirst(l);
 
-               printf("%d\t%s(%s)\t%u\t%d\t%s\n",
-                          i, rte->relname, rte->eref->relname, rte->relid,
-                          rte->inFromCl,
-                          (rte->inh ? "inh" : ""));
+               if (rte->relname)
+                       printf("%d\t%s (%s)\t%u",
+                                  i, rte->relname, rte->eref->relname, rte->relid);
+               else
+                       printf("%d\t[subquery] (%s)\t",
+                                  i, rte->eref->relname);
+               printf("\t%s\t%s\n",
+                          (rte->inh ? "inh" : ""),
+                          (rte->inFromCl ? "inFromCl" : ""));
                i++;
        }
 }
@@ -286,7 +290,7 @@ plannode_type(Plan *p)
 {
        switch (nodeTag(p))
        {
-                       case T_Plan:
+               case T_Plan:
                        return "PLAN";
                        break;
                case T_Result:
@@ -304,6 +308,12 @@ plannode_type(Plan *p)
                case T_IndexScan:
                        return "INDEXSCAN";
                        break;
+               case T_TidScan:
+                       return "TIDSCAN";
+                       break;
+               case T_SubqueryScan:
+                       return "SUBQUERYSCAN";
+                       break;
                case T_Join:
                        return "JOIN";
                        break;
@@ -334,9 +344,6 @@ plannode_type(Plan *p)
                case T_Group:
                        return "GROUP";
                        break;
-               case T_TidScan:
-                       return "TIDSCAN";
-                       break;
                default:
                        return "UNKNOWN";
                        break;
@@ -372,10 +379,10 @@ print_plan_recursive(Plan *p, Query *parsetree, int indentLevel, char *label)
        }
        else if (IsA(p, IndexScan))
        {
-               StrNCpy(extraInfo,
-                  ((RangeTblEntry *) (nth(((IndexScan *) p)->scan.scanrelid - 1,
-                                                                  parsetree->rtable)))->relname,
-                               NAMEDATALEN);
+               RangeTblEntry *rte;
+
+               rte = rt_fetch(((IndexScan *) p)->scan.scanrelid, parsetree->rtable);
+               StrNCpy(extraInfo, rte->relname, NAMEDATALEN);
        }
        else
                extraInfo[0] = '\0';
@@ -386,7 +393,7 @@ print_plan_recursive(Plan *p, Query *parsetree, int indentLevel, char *label)
        print_plan_recursive(p->lefttree, parsetree, indentLevel + 3, "l: ");
        print_plan_recursive(p->righttree, parsetree, indentLevel + 3, "r: ");
 
-       if (nodeTag(p) == T_Append)
+       if (IsA(p, Append))
        {
                List       *lst;
                int                     whichplan = 0;
index 00a6407..da0ba22 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.96 2000/09/12 21:06:49 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.97 2000/09/29 18:21:29 tgl Exp $
  *
  * NOTES
  *       Most of the read functions for plan nodes are tested. (In fact, they
@@ -70,13 +70,16 @@ _readQuery()
        local_node->commandType = atoi(token);
 
        token = lsptok(NULL, &length);          /* skip :utility */
-       /* we can't get create or index here, can we? */
-
-       token = lsptok(NULL, &length);          /* get the notify name if any */
+       token = lsptok(NULL, &length);
        if (length == 0)
                local_node->utilityStmt = NULL;
        else
        {
+               /*
+                * Hack to make up for lack of readfuncs for utility-stmt nodes
+                *
+                * we can't get create or index here, can we?
+                */
                NotifyStmt *n = makeNode(NotifyStmt);
 
                n->relname = debackslash(token, length);
@@ -110,11 +113,13 @@ _readQuery()
        token = lsptok(NULL, &length);          /* get unionall */
        local_node->unionall = (token[0] == 't') ? true : false;
 
-       token = lsptok(NULL, &length);          /* skip :distinctClause */
-       local_node->distinctClause = nodeRead(true);
+       token = lsptok(NULL, &length);          /* skip the :hasAggs */
+       token = lsptok(NULL, &length);          /* get hasAggs */
+       local_node->hasAggs = (token[0] == 't') ? true : false;
 
-       token = lsptok(NULL, &length);          /* skip :sortClause */
-       local_node->sortClause = nodeRead(true);
+       token = lsptok(NULL, &length);          /* skip the :hasSubLinks */
+       token = lsptok(NULL, &length);          /* get hasSubLinks */
+       local_node->hasSubLinks = (token[0] == 't') ? true : false;
 
        token = lsptok(NULL, &length);          /* skip :rtable */
        local_node->rtable = nodeRead(true);
@@ -125,8 +130,14 @@ _readQuery()
        token = lsptok(NULL, &length);          /* skip :targetlist */
        local_node->targetList = nodeRead(true);
 
-       token = lsptok(NULL, &length);          /* skip :qual */
-       local_node->qual = nodeRead(true);
+       token = lsptok(NULL, &length);          /* skip :rowMarks */
+       local_node->rowMarks = toIntList(nodeRead(true));
+
+       token = lsptok(NULL, &length);          /* skip :distinctClause */
+       local_node->distinctClause = nodeRead(true);
+
+       token = lsptok(NULL, &length);          /* skip :sortClause */
+       local_node->sortClause = nodeRead(true);
 
        token = lsptok(NULL, &length);          /* skip :groupClause */
        local_node->groupClause = nodeRead(true);
@@ -134,20 +145,11 @@ _readQuery()
        token = lsptok(NULL, &length);          /* skip :havingQual */
        local_node->havingQual = nodeRead(true);
 
-       token = lsptok(NULL, &length);          /* skip the :hasAggs */
-       token = lsptok(NULL, &length);          /* get hasAggs */
-       local_node->hasAggs = (token[0] == 't') ? true : false;
-
-       token = lsptok(NULL, &length);          /* skip the :hasSubLinks */
-       token = lsptok(NULL, &length);          /* get hasSubLinks */
-       local_node->hasSubLinks = (token[0] == 't') ? true : false;
-
-       token = lsptok(NULL, &length);          /* skip :unionClause */
-       local_node->unionClause = nodeRead(true);
-
        token = lsptok(NULL, &length);          /* skip :intersectClause */
        local_node->intersectClause = nodeRead(true);
 
+       token = lsptok(NULL, &length);          /* skip :unionClause */
+       local_node->unionClause = nodeRead(true);
 
        token = lsptok(NULL, &length);          /* skip :limitOffset */
        local_node->limitOffset = nodeRead(true);
@@ -155,9 +157,6 @@ _readQuery()
        token = lsptok(NULL, &length);          /* skip :limitCount */
        local_node->limitCount = nodeRead(true);
 
-       token = lsptok(NULL, &length);          /* skip :rowMark */
-       local_node->rowMark = nodeRead(true);
-
        return local_node;
 }
 
@@ -562,6 +561,29 @@ _readTidScan()
 }
 
 /* ----------------
+ *             _readSubqueryScan
+ *
+ *     SubqueryScan is a subclass of Scan
+ * ----------------
+ */
+static SubqueryScan *
+_readSubqueryScan()
+{
+       SubqueryScan  *local_node;
+       char       *token;
+       int                     length;
+
+       local_node = makeNode(SubqueryScan);
+
+       _getScan((Scan *) local_node);
+
+       token = lsptok(NULL, &length);                  /* eat :subplan */
+       local_node->subplan = nodeRead(true);   /* now read it */
+
+       return local_node;
+}
+
+/* ----------------
  *             _readSort
  *
  *     Sort is a subclass of Plan
@@ -1182,6 +1204,30 @@ _readRangeTblRef()
 }
 
 /* ----------------
+ *             _readFromExpr
+ *
+ *     FromExpr is a subclass of Node
+ * ----------------
+ */
+static FromExpr *
+_readFromExpr()
+{
+       FromExpr   *local_node;
+       char       *token;
+       int                     length;
+
+       local_node = makeNode(FromExpr);
+
+       token = lsptok(NULL, &length);          /* eat :fromlist */
+       local_node->fromlist = nodeRead(true);  /* now read it */
+
+       token = lsptok(NULL, &length);          /* eat :quals */
+       local_node->quals = nodeRead(true);     /* now read it */
+
+       return local_node;
+}
+
+/* ----------------
  *             _readJoinExpr
  *
  *     JoinExpr is a subclass of Node
@@ -1293,22 +1339,6 @@ _readRelOptInfo()
        token = lsptok(NULL, &length);          /* now read it */
        local_node->width = atoi(token);
 
-       token = lsptok(NULL, &length);          /* get :indexed */
-       token = lsptok(NULL, &length);          /* now read it */
-
-       if (!strncmp(token, "true", 4))
-               local_node->indexed = true;
-       else
-               local_node->indexed = false;
-
-       token = lsptok(NULL, &length);          /* get :pages */
-       token = lsptok(NULL, &length);          /* now read it */
-       local_node->pages = atol(token);
-
-       token = lsptok(NULL, &length);          /* get :tuples */
-       token = lsptok(NULL, &length);          /* now read it */
-       local_node->tuples = atof(token);
-
        token = lsptok(NULL, &length);          /* get :targetlist */
        local_node->targetlist = nodeRead(true);        /* now read it */
 
@@ -1325,6 +1355,25 @@ _readRelOptInfo()
        token = lsptok(NULL, &length);          /* get :pruneable */
        local_node->pruneable = (token[0] == 't') ? true : false;
 
+       token = lsptok(NULL, &length);          /* get :issubquery */
+       token = lsptok(NULL, &length);          /* now read it */
+       local_node->issubquery = (token[0] == 't') ? true : false;
+
+       token = lsptok(NULL, &length);          /* get :indexed */
+       token = lsptok(NULL, &length);          /* now read it */
+       local_node->indexed = (token[0] == 't') ? true : false;
+
+       token = lsptok(NULL, &length);          /* get :pages */
+       token = lsptok(NULL, &length);          /* now read it */
+       local_node->pages = atol(token);
+
+       token = lsptok(NULL, &length);          /* get :tuples */
+       token = lsptok(NULL, &length);          /* now read it */
+       local_node->tuples = atof(token);
+
+       token = lsptok(NULL, &length);          /* get :subplan */
+       local_node->subplan = nodeRead(true); /* now read it */
+
        token = lsptok(NULL, &length);          /* get :baserestrictinfo */
        local_node->baserestrictinfo = nodeRead(true); /* now read it */
 
@@ -1409,6 +1458,9 @@ _readRangeTblEntry()
        token = lsptok(NULL, &length);          /* get :relid */
        local_node->relid = strtoul(token, NULL, 10);
 
+       token = lsptok(NULL, &length);          /* eat :subquery */
+       local_node->subquery = nodeRead(true);  /* now read it */
+
        token = lsptok(NULL, &length);          /* eat :alias */
        local_node->alias = nodeRead(true);     /* now read it */
 
@@ -1423,27 +1475,17 @@ _readRangeTblEntry()
        token = lsptok(NULL, &length);          /* get :inFromCl */
        local_node->inFromCl = (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;
-
-       return local_node;
-}
-
-static RowMark *
-_readRowMark()
-{
-       RowMark    *local_node = makeNode(RowMark);
-       char       *token;
-       int                     length;
+       token = lsptok(NULL, &length);          /* eat :checkForRead */
+       token = lsptok(NULL, &length);          /* get :checkForRead */
+       local_node->checkForRead = (token[0] == 't') ? true : false;
 
-       token = lsptok(NULL, &length);          /* eat :rti */
-       token = lsptok(NULL, &length);          /* get :rti */
-       local_node->rti = strtoul(token, NULL, 10);
+       token = lsptok(NULL, &length);          /* eat :checkForWrite */
+       token = lsptok(NULL, &length);          /* get :checkForWrite */
+       local_node->checkForWrite = (token[0] == 't') ? true : false;
 
-       token = lsptok(NULL, &length);          /* eat :info */
-       token = lsptok(NULL, &length);          /* get :info */
-       local_node->info = strtoul(token, NULL, 10);
+       token = lsptok(NULL, &length);          /* eat :checkAsUser */
+       token = lsptok(NULL, &length);          /* get :checkAsUser */
+       local_node->checkAsUser = strtoul(token, NULL, 10);
 
        return local_node;
 }
@@ -1768,9 +1810,9 @@ _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);          /* get :ispusheddown */
        token = lsptok(NULL, &length);          /* now read it */
-       local_node->isjoinqual = (token[0] == 't') ? true : false;
+       local_node->ispusheddown = (token[0] == 't') ? true : false;
 
        token = lsptok(NULL, &length);          /* get :subclauseindices */
        local_node->subclauseindices = nodeRead(true);          /* now read it */
@@ -1879,6 +1921,8 @@ parsePlanString(void)
                return_value = _readIndexScan();
        else if (length == 7 && strncmp(token, "TIDSCAN", length) == 0)
                return_value = _readTidScan();
+       else if (length == 12 && strncmp(token, "SUBQUERYSCAN", length) == 0)
+               return_value = _readSubqueryScan();
        else if (length == 4 && strncmp(token, "SORT", length) == 0)
                return_value = _readSort();
        else if (length == 6 && strncmp(token, "AGGREG", length) == 0)
@@ -1891,6 +1935,8 @@ parsePlanString(void)
                return_value = _readRelabelType();
        else if (length == 11 && strncmp(token, "RANGETBLREF", length) == 0)
                return_value = _readRangeTblRef();
+       else if (length == 8 && strncmp(token, "FROMEXPR", length) == 0)
+               return_value = _readFromExpr();
        else if (length == 8 && strncmp(token, "JOINEXPR", length) == 0)
                return_value = _readJoinExpr();
        else if (length == 3 && strncmp(token, "AGG", length) == 0)
@@ -1953,10 +1999,8 @@ parsePlanString(void)
                return_value = _readCaseExpr();
        else if (length == 4 && strncmp(token, "WHEN", length) == 0)
                return_value = _readCaseWhen();
-       else if (length == 7 && strncmp(token, "ROWMARK", length) == 0)
-               return_value = _readRowMark();
        else
-               elog(ERROR, "badly formatted planstring \"%.10s\"...\n", token);
+               elog(ERROR, "badly formatted planstring \"%.10s\"...", token);
 
        return (Node *) return_value;
 }
index 38901ed..f0113df 100644 (file)
@@ -9,23 +9,63 @@ stuff.  /geqo is the separate "genetic optimization" planner --- it does
 a semi-random search through the join tree space, rather than exhaustively
 considering all possible join trees.  (But each join considered by /geqo
 is given to /path to create paths for, so we consider all possible
-implementation paths for each specific join even in GEQO mode.)
+implementation paths for each specific join pair even in GEQO mode.)
+
+
+Paths and Join Pairs
+--------------------
+
+During the planning/optimizing process, we build "Path" trees representing
+the different ways of doing a query.  We select the cheapest Path that
+generates the desired relation and turn it into a Plan to pass to the
+executor.  (There is pretty much a one-to-one correspondence between the
+Path and Plan trees, but Path nodes omit info that won't be needed during
+planning, and include info needed for planning that won't be needed by the
+executor.)
+
+The optimizer builds a RelOptInfo structure for each base relation used in
+the query.  Base rels are either primitive tables, or subquery subselects
+that are planned via a separate recursive invocation of the planner.  A
+RelOptInfo is also built for each join relation that is considered during
+planning.  A join rel is simply a combination of base rels.  There is only
+one join RelOptInfo for any given set of baserels --- for example, the join
+{A B C} is represented by the same RelOptInfo no matter whether we build it
+by joining A and B first and then adding C, or joining B and C first and
+then adding A, etc.  These different means of building the joinrel are
+represented as Paths.  For each RelOptInfo we build a list of Paths that
+represent plausible ways to implement the scan or join of that relation.
+Once we've considered all the plausible Paths for a rel, we select the one
+that is cheapest according to the planner's cost estimates.  The final plan
+is derived from the cheapest Path for the RelOptInfo that includes all the
+base rels of the query.
+
+Possible Paths for a primitive table relation include plain old sequential
+scan, plus index scans for any indexes that exist on the table.  A subquery
+base relation just has one Path, a "SubqueryScan" path (which links to the
+subplan that was built by a recursive invocation of the planner).
+
+Joins always occur using two RelOptInfos.  One is outer, the other inner.
+Outers drive lookups of values in the inner.  In a nested loop, lookups of
+values in the inner occur by scanning the inner path once per outer tuple
+to find each matching inner row.  In a mergejoin, inner and outer rows are
+ordered, and are accessed in order, so only one scan is required to perform
+the entire join: both inner and outer paths are scanned in-sync.  (There's
+not a lot of difference between inner and outer in a mergejoin...)  In a
+hashjoin, the inner is scanned first and all its rows are entered in a
+hashtable, then the outer is scanned and for each row we lookup the join
+key in the hashtable.
+
+A Path for a join relation is actually a tree structure, with the top
+Path node representing the join method.  It has left and right subpaths
+that represent the scan or join methods used for the two input relations.
 
 
 Join Tree Construction
 ----------------------
 
 The optimizer generates optimal query plans by doing a more-or-less
-exhaustive search through the ways of executing the query.  During
-the planning/optimizing process, we build "Path" trees representing
-the different ways of doing a query.  We select the cheapest Path
-that generates the desired relation and turn it into a Plan to pass
-to the executor.  (There is pretty much a one-to-one correspondence
-between the Path and Plan trees, but Path nodes omit info that won't
-be needed during planning, and include info needed for planning that
-won't be needed by the executor.)
-
-The best Path tree is found by a recursive process:
+exhaustive search through the ways of executing the query.  The best Path
+tree is found by a recursive process:
 
 1) Take each base relation in the query, and make a RelOptInfo structure
 for it.  Find each potentially useful way of accessing the relation,
@@ -44,46 +84,40 @@ 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
 single join relation.
 
-2) Consider joining each RelOptInfo to each other RelOptInfo specified in
-its RelOptInfo.joininfo, and generate a Path for each possible join method.
-(If we have a RelOptInfo with no join clauses, we have no choice but to
-generate a clauseless Cartesian-product join; so we consider joining that
-rel to each other available rel.  But in the presence of join clauses we
-will only consider joins that use available join clauses.)
-
-At this stage each input RelOptInfo is a single relation, so we are joining
-every relation to the other relations as joined in the WHERE clause.  We
-generate a new "join" RelOptInfo for each possible combination of two
-"base" RelOptInfos, and put all the plausible paths for that combination
-into the join RelOptInfo's pathlist.  (As before, we keep only the cheapest
-alternative that generates any one sort ordering of the result.)
-
-Joins always occur using two RelOptInfos.  One is outer, the other inner.
-Outers drive lookups of values in the inner.  In a nested loop, lookups of
-values in the inner occur by scanning the inner path once per outer tuple
-to find each matching inner row.  In a mergejoin, inner and outer rows are
-ordered, and are accessed in order, so only one scan is required to perform
-the entire join: both inner and outer paths are scanned in-sync.  (There's
-not a lot of difference between inner and outer in a mergejoin...)  In a
-hashjoin, the inner is scanned first and all its rows are entered in a
-hashtable, then the outer is scanned and for each row we lookup the join
-key in the hashtable.
-
-A Path for a join relation is actually a tree structure, with the top
-Path node representing the join method.  It has left and right subpaths
-that represent the scan methods used for the two input relations.
-
-3) If we only had two base relations, we are done: we just pick the
-cheapest path for the join RelOptInfo.  If we had more than two, we now
+2) If the query's FROM clause contains explicit JOIN clauses, we join
+those pairs of relations in exactly the tree structure indicated by the
+JOIN clauses.  (This is absolutely necessary when dealing with outer JOINs.
+For inner JOINs we have more flexibility in theory, but don't currently
+exploit it in practice.)  For each such join pair, we generate a Path
+for each feasible join method, and select the cheapest Path.  Note that
+the JOIN clause structure determines the join Path structure, but it
+doesn't constrain the join implementation method at each join (nestloop,
+merge, hash), nor does it say which rel is considered outer or inner at
+each join.  We consider all these possibilities in building Paths.
+
+3) At the top level of the FROM clause we will have a list of relations
+that are either base rels or joinrels constructed per JOIN directives.
+We can join these rels together in any order the planner sees fit.
+The standard (non-GEQO) planner does this as follows:
+
+Consider joining each RelOptInfo to each other RelOptInfo specified in its
+RelOptInfo.joininfo, and generate a Path for each possible join method for
+each such pair.  (If we have a RelOptInfo with no join clauses, we have no
+choice but to generate a clauseless Cartesian-product join; so we consider
+joining that rel to each other available rel.  But in the presence of join
+clauses we will only consider joins that use available join clauses.)
+
+If we only had two relations in the FROM list, we are done: we just pick
+the cheapest path for the join RelOptInfo.  If we had more than two, we now
 need to consider ways of joining join RelOptInfos to each other to make
-join RelOptInfos that represent more than two base relations.
+join RelOptInfos that represent more than two FROM items.
 
 The join tree is constructed using a "dynamic programming" algorithm:
 in the first pass (already described) we consider ways to create join rels
-representing exactly two base relations.  The second pass considers ways
-to make join rels that represent exactly three base relations; the next pass,
-four relations, etc.  The last pass considers how to make the final join
-relation that includes all base rels --- obviously there can be only one
+representing exactly two FROM items.  The second pass considers ways
+to make join rels that represent exactly three FROM items; the next pass,
+four items, etc.  The last pass considers how to make the final join
+relation that includes all FROM items --- obviously there can be only one
 join rel at this top level, whereas there can be more than one join rel
 at lower levels.  At each level we use joins that follow available join
 clauses, if possible, just as described for the first level.
@@ -114,32 +148,45 @@ For example:
     {1 2 3 4}
 
 We consider left-handed plans (the outer rel of an upper join is a joinrel,
-but the inner is always a base rel); right-handed plans (outer rel is always
-a base rel); and bushy plans (both inner and outer can be joins themselves).
-For example, when building {1 2 3 4} we consider joining {1 2 3} to {4}
-(left-handed), {4} to {1 2 3} (right-handed), and {1 2} to {3 4} (bushy),
-among other choices.  Although the jointree scanning code produces these
-potential join combinations one at a time, all the ways to produce the
-same set of joined base rels will share the same RelOptInfo, so the paths
-produced from different join combinations that produce equivalent joinrels
-will compete in add_path.
+but the inner is always a single FROM item); right-handed plans (outer rel
+is always a single item); and bushy plans (both inner and outer can be
+joins themselves).  For example, when building {1 2 3 4} we consider
+joining {1 2 3} to {4} (left-handed), {4} to {1 2 3} (right-handed), and
+{1 2} to {3 4} (bushy), among other choices.  Although the jointree
+scanning code produces these potential join combinations one at a time,
+all the ways to produce the same set of joined base rels will share the
+same RelOptInfo, so the paths produced from different join combinations
+that produce equivalent joinrels will compete in add_path.
 
 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).
+
+Pulling up subqueries
+---------------------
+
+As we described above, a subquery appearing in the range table is planned
+independently and treated as a "black box" during planning of the outer
+query.  This is necessary when the subquery uses features such as
+aggregates, GROUP, or DISTINCT.  But if the subquery is just a simple
+scan or join, treating the subquery as a black box may produce a poor plan
+compared to considering it as part of the entire plan search space.
+Therefore, at the start of the planning process the planner looks for
+simple subqueries and pulls them up into the main query's jointree.
+
+Pulling up a subquery may result in FROM-list joins appearing below the top
+of the join tree.  Each FROM-list is planned using the dynamic-programming
+search method described above.
+
+If pulling up a subquery produces a FROM-list as a direct child of another
+FROM-list (with no explicit JOIN directives between), then we can merge the
+two FROM-lists together.  Once that's done, the subquery is an absolutely
+integral part of the outer query and will not constrain the join tree
+search space at all.  However, that could result in unpleasant growth of
+planning time, since the dynamic-programming search has runtime exponential
+in the number of FROM-items considered.  Therefore, we don't merge
+FROM-lists if the result would have too many FROM-items in one list.
 
 
 Optimizer Functions
@@ -151,6 +198,7 @@ planner()
  set up for recursive handling of subqueries
  do final cleanup after planning.
 -subquery_planner()
+ pull up subqueries from rangetable, if possible
  simplify constant expressions
  canonicalize qual
      Attempt to reduce WHERE clause to either CNF or DNF canonical form.
@@ -167,13 +215,14 @@ planner()
   preprocess target list
   handle GROUP BY, HAVING, aggregates, ORDER BY, DISTINCT
 --query_planner()
-   pull out constants from target list
-   get a target list that only contains column names, no expressions
-   if none, then return
+   pull out constant quals, which can be used to gate execution of the
+       whole plan (if any are found, we make a top-level Result node
+       to do the gating)
+   make a simplified target list that only contains Vars, no expressions
 ---subplanner()
     make list of base relations used in query
     split up the qual into restrictions (a=1) and joins (b=c)
-    find relation clauses that can do merge sort and hash joins
+    find qual clauses that enable merge and hash joins
 ----make_one_rel()
      set_base_rel_pathlist()
       find scan and all index paths for each base relation
@@ -188,10 +237,11 @@ planner()
       Back at make_one_rel_by_joins(), apply set_cheapest() to extract the
       cheapest path for each newly constructed joinrel.
       Loop back if this wasn't the top join level.
-   do group(GROUP)
-   do aggregate
-   put back constants
-   re-flatten target list
+   Back at query_planner:
+   put back constant quals and non-simplified target list
+ Back at union_planner:
+ do grouping(GROUP)
+ do aggregates
  make unique(DISTINCT)
  make sort(ORDER BY)
 
index 755c331..3428015 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: geqo_main.c,v 1.24 2000/09/19 18:42:33 tgl Exp $
+ * $Id: geqo_main.c,v 1.25 2000/09/29 18:21:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -245,9 +245,9 @@ geqo(Query *root, int number_of_rels, List *initial_rels)
        best_tour = (Gene *) pool->data[0].string;
 
 /* root->join_rel_list will be modified during this ! */
-       best_rel = (RelOptInfo *) gimme_tree(root, initial_rels,
-                                                                                best_tour, pool->string_length,
-                                                                                0, NULL);
+       best_rel = gimme_tree(root, initial_rels,
+                                                 best_tour, pool->string_length,
+                                                 0, NULL);
 
 /* DBG: show the query plan
 print_plan(best_plan, root);
index be4a5ca..8ab2aee 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.64 2000/09/19 18:42:34 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.65 2000/09/29 18:21:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -19,6 +19,9 @@
 #include "optimizer/geqo.h"
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
+#include "optimizer/plancat.h"
+#include "optimizer/planner.h"
+#include "parser/parsetree.h"
 
 
 bool           enable_geqo = true;
@@ -26,7 +29,6 @@ int                   geqo_rels = DEFAULT_GEQO_RELS;
 
 
 static void set_base_rel_pathlist(Query *root);
-static List *build_jointree_rels(Query *root);
 static RelOptInfo *make_one_rel_by_joins(Query *root, int levels_needed,
                                                                                 List *initial_rels);
 
@@ -44,20 +46,7 @@ static void debug_print_rel(Query *root, RelOptInfo *rel);
 RelOptInfo *
 make_one_rel(Query *root)
 {
-       int                     levels_needed;
-       List       *initial_rels;
-
-       /*
-        * 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->jointree);
-
-       if (levels_needed <= 0)
-               return NULL;                    /* nothing to do? */
+       RelOptInfo *rel;
 
        /*
         * Generate access paths for the base rels.
@@ -65,27 +54,18 @@ make_one_rel(Query *root)
        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.
+        * Generate access paths for the entire join tree.
         */
-       initial_rels = build_jointree_rels(root);
+       Assert(root->jointree != NULL && IsA(root->jointree, FromExpr));
 
-       if (levels_needed == 1)
-       {
-               /*
-                * Single jointree node, so we're done.
-                */
-               return (RelOptInfo *) lfirst(initial_rels);
-       }
-       else
-       {
+       rel = make_fromexpr_rel(root, root->jointree);
 
-               /*
-                * Generate join tree.
-                */
-               return make_one_rel_by_joins(root, levels_needed, initial_rels);
-       }
+       /*
+        * The result should join all the query's rels.
+        */
+       Assert(length(rel->relids) == length(root->base_rel_list));
+
+       return rel;
 }
 
 /*
@@ -102,36 +82,67 @@ set_base_rel_pathlist(Query *root)
        foreach(rellist, root->base_rel_list)
        {
                RelOptInfo *rel = (RelOptInfo *) lfirst(rellist);
-               List       *indices = find_relation_indices(root, rel);
+               RangeTblEntry *rte;
 
-               /* Mark rel with estimated output rows, width, etc */
-               set_baserel_size_estimates(root, rel);
+               Assert(length(rel->relids) == 1); /* better be base rel */
+               rte = rt_fetch(lfirsti(rel->relids), root->rtable);
 
-               /*
-                * Generate paths and add them to the rel's pathlist.
-                *
-                * Note: add_path() will discard any paths that are dominated by
-                * another available path, keeping only those paths that are
-                * superior along at least one dimension of cost or sortedness.
-                */
+               if (rel->issubquery)
+               {
+                       /* Subquery --- generate a separate plan for it */
 
-               /* Consider sequential scan */
-               add_path(rel, create_seqscan_path(rel));
+                       /*
+                        * XXX for now, we just apply any restrict clauses that came
+                        * from the outer query as qpquals of the SubqueryScan node.
+                        * Later, think about pushing them down into the subquery itself.
+                        */
 
-               /* Consider TID scans */
-               create_tidscan_paths(root, rel);
+                       /* Generate the plan for the subquery */
+                       rel->subplan = planner(rte->subquery);
 
-               /* Consider index paths for both simple and OR index clauses */
-               create_index_paths(root, rel, indices,
-                                                  rel->baserestrictinfo,
-                                                  rel->joininfo);
+                       /* Copy number of output rows from subplan */
+                       rel->tuples = rel->subplan->plan_rows;
 
-               /*
-                * Note: create_or_index_paths depends on create_index_paths to
-                * have marked OR restriction clauses with relevant indices; this
-                * is why it doesn't need to be given the list of indices.
-                */
-               create_or_index_paths(root, rel, rel->baserestrictinfo);
+                       /* Mark rel with estimated output rows, width, etc */
+                       set_baserel_size_estimates(root, rel);
+
+                       /* Generate appropriate path */
+                       add_path(rel, create_subqueryscan_path(rel));
+               }
+               else
+               {
+                       /* Plain relation */
+                       List       *indices = find_secondary_indexes(rte->relid);
+
+                       /* Mark rel with estimated output rows, width, etc */
+                       set_baserel_size_estimates(root, rel);
+
+                       /*
+                        * Generate paths and add them to the rel's pathlist.
+                        *
+                        * Note: add_path() will discard any paths that are dominated by
+                        * another available path, keeping only those paths that are
+                        * superior along at least one dimension of cost or sortedness.
+                        */
+
+                       /* Consider sequential scan */
+                       add_path(rel, create_seqscan_path(rel));
+
+                       /* Consider TID scans */
+                       create_tidscan_paths(root, rel);
+
+                       /* Consider index paths for both simple and OR index clauses */
+                       create_index_paths(root, rel, indices,
+                                                          rel->baserestrictinfo,
+                                                          rel->joininfo);
+
+                       /*
+                        * Note: create_or_index_paths depends on create_index_paths to
+                        * have marked OR restriction clauses with relevant indices; this
+                        * is why it doesn't need to be given the list of indices.
+                        */
+                       create_or_index_paths(root, rel, rel->baserestrictinfo);
+               }
 
                /* Now find the cheapest of the paths for this rel */
                set_cheapest(rel);
@@ -139,26 +150,57 @@ 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.
+ * make_fromexpr_rel
+ *       Build access paths for a FromExpr jointree node.
  */
-static List *
-build_jointree_rels(Query *root)
+RelOptInfo *
+make_fromexpr_rel(Query *root, FromExpr *from)
 {
-       List       *rels = NIL;
+       int                     levels_needed;
+       List       *initial_rels = NIL;
        List       *jt;
 
-       foreach(jt, root->jointree)
+       /*
+        * Count the number of child jointree nodes.  This is the depth
+        * of the dynamic-programming algorithm we must employ to consider
+        * all ways of joining the child nodes.
+        */
+       levels_needed = length(from->fromlist);
+
+       if (levels_needed <= 0)
+               return NULL;                    /* nothing to do? */
+
+       /*
+        * Construct a list of rels corresponding to the child jointree nodes.
+        * This may contain both base rels and rels constructed according to
+        * explicit JOIN directives.
+        */
+       foreach(jt, from->fromlist)
        {
                Node       *jtnode = (Node *) lfirst(jt);
 
-               rels = lappend(rels, make_rel_from_jointree(root, jtnode));
+               initial_rels = lappend(initial_rels,
+                                                          make_jointree_rel(root, jtnode));
+       }
+
+       if (levels_needed == 1)
+       {
+               /*
+                * Single jointree node, so we're done.
+                */
+               return (RelOptInfo *) lfirst(initial_rels);
+       }
+       else
+       {
+               /*
+                * Consider the different orders in which we could join the rels,
+                * using either GEQO or regular optimizer.
+                */
+               if (enable_geqo && levels_needed >= geqo_rels)
+                       return geqo(root, levels_needed, initial_rels);
+               else
+                       return make_one_rel_by_joins(root, levels_needed, initial_rels);
        }
-       return rels;
 }
 
 /*
@@ -182,14 +224,6 @@ make_one_rel_by_joins(Query *root, int levels_needed, List *initial_rels)
        int                     lev;
        RelOptInfo *rel;
 
-       /*******************************************
-        * genetic query optimizer entry point     *
-        *        <utesch@aut.tu-freiberg.de>              *
-        * rest will be skipped in case of GEQO    *
-        *******************************************/
-       if (enable_geqo && levels_needed >= geqo_rels)
-               return geqo(root, levels_needed, initial_rels);
-
        /*
         * We employ a simple "dynamic programming" algorithm: we first find
         * all ways to build joins of two jointree items, then all ways to
@@ -243,12 +277,11 @@ make_one_rel_by_joins(Query *root, int levels_needed, List *initial_rels)
        }
 
        /*
-        * We should have a single rel at the final level,
-        * representing the join of all the base rels.
+        * We should have a single rel at the final level.
         */
        Assert(length(joinitems[levels_needed]) == 1);
+
        rel = (RelOptInfo *) lfirst(joinitems[levels_needed]);
-       Assert(length(rel->relids) == length(root->base_rel_list));
 
        return rel;
 }
index 0f26dc7..025f011 100644 (file)
@@ -42,7 +42,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.62 2000/06/18 22:44:06 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.63 2000/09/29 18:21:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -115,6 +115,7 @@ cost_seqscan(Path *path, RelOptInfo *baserel)
 
        /* Should only be applied to base relations */
        Assert(length(baserel->relids) == 1);
+       Assert(!baserel->issubquery);
 
        if (!enable_seqscan)
                startup_cost += disable_cost;
@@ -223,6 +224,7 @@ cost_index(Path *path, Query *root,
        /* Should only be applied to base relations */
        Assert(IsA(baserel, RelOptInfo) &&IsA(index, IndexOptInfo));
        Assert(length(baserel->relids) == 1);
+       Assert(!baserel->issubquery);
 
        if (!enable_indexscan && !is_injoin)
                startup_cost += disable_cost;
index 55bbd5f..5ed42ac 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.96 2000/09/15 18:45:25 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.97 2000/09/29 18:21:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -464,7 +464,7 @@ extract_or_indexqual_conditions(RelOptInfo *rel,
        else
        {
                /* we assume the caller passed a valid indexable qual */
-               quals = lcons(orsubclause, NIL);
+               quals = makeList1(orsubclause);
        }
 
        return expand_indexqual_conditions(quals);
@@ -1504,7 +1504,7 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
                        RestrictInfo *clause = (RestrictInfo *) lfirst(temp);
 
                        indexquals = lappend(indexquals, clause->clause);
-                       if (! clause->isjoinqual)
+                       if (clause->ispusheddown)
                                alljoinquals = false;
                }
 
@@ -1516,8 +1516,8 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
                 * therefore, both indexid and indexqual should be single-element
                 * lists.
                 */
-               pathnode->indexid = lconsi(index->indexoid, NIL);
-               pathnode->indexqual = lcons(indexquals, NIL);
+               pathnode->indexid = makeListi1(index->indexoid);
+               pathnode->indexqual = makeList1(indexquals);
 
                /* We don't actually care what order the index scans in ... */
                pathnode->indexscandir = NoMovementScanDirection;
@@ -1541,7 +1541,7 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
                 */
                pathnode->rows = rel->tuples *
                        restrictlist_selectivity(root,
-                                                                        LispUnion(rel->baserestrictinfo,
+                                                                        set_union(rel->baserestrictinfo,
                                                                                           clausegroup),
                                                                         lfirsti(rel->relids));
                /* Like costsize.c, force estimate to be at least one row */
@@ -2034,7 +2034,7 @@ prefix_quals(Var *leftop, Oid expr_op,
                con = string_to_const(prefix, datatype);
                op = makeOper(oproid, InvalidOid, BOOLOID);
                expr = make_opclause(op, leftop, (Var *) con);
-               result = lcons(expr, NIL);
+               result = makeList1(expr);
                return result;
        }
 
@@ -2049,7 +2049,7 @@ prefix_quals(Var *leftop, Oid expr_op,
        con = string_to_const(prefix, datatype);
        op = makeOper(oproid, InvalidOid, BOOLOID);
        expr = make_opclause(op, leftop, (Var *) con);
-       result = lcons(expr, NIL);
+       result = makeList1(expr);
 
        /*
         * If we can create a string larger than the prefix, say "x <
index 367e1ac..d8bfe62 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.56 2000/09/12 21:06:53 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.57 2000/09/29 18:21:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -643,10 +643,10 @@ hash_inner_and_outer(Query *root,
                        continue;                       /* not hashjoinable */
 
                /*
-                * If processing an outer join, only use explicit join clauses for
+                * If processing an outer join, only use its own join clauses for
                 * hashing.  For inner joins we need not be so picky.
                 */
-               if (isouterjoin && !restrictinfo->isjoinqual)
+               if (isouterjoin && restrictinfo->ispusheddown)
                        continue;
 
                clause = restrictinfo->clause;
@@ -665,7 +665,7 @@ hash_inner_and_outer(Query *root,
                        continue;                       /* no good for these input relations */
 
                /* always a one-element list of hash clauses */
-               hashclauses = lcons(restrictinfo, NIL);
+               hashclauses = makeList1(restrictinfo);
 
                /* estimate disbursion of inner var for costing purposes */
                innerdisbursion = estimate_disbursion(root, inner);
@@ -820,7 +820,7 @@ select_mergejoin_clauses(RelOptInfo *joinrel,
                                   *right;
 
                /*
-                * If processing an outer join, only use explicit join clauses in the
+                * If processing an outer join, only use its own 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
@@ -832,7 +832,7 @@ select_mergejoin_clauses(RelOptInfo *joinrel,
                 */
                if (isouterjoin)
                {
-                       if (!restrictinfo->isjoinqual)
+                       if (restrictinfo->ispusheddown)
                                continue;
                        switch (jointype)
                        {
index 3cab2da..74cc036 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.47 2000/09/12 21:06:53 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.48 2000/09/29 18:21:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -307,13 +307,13 @@ make_rels_by_clauseless_joins(Query *root,
 
 
 /*
- * make_rel_from_jointree
+ * make_jointree_rel
  *             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)
+make_jointree_rel(Query *root, Node *jtnode)
 {
        if (IsA(jtnode, RangeTblRef))
        {
@@ -321,6 +321,13 @@ make_rel_from_jointree(Query *root, Node *jtnode)
 
                return get_base_rel(root, varno);
        }
+       else if (IsA(jtnode, FromExpr))
+       {
+               FromExpr   *f = (FromExpr *) jtnode;
+
+               /* Recurse back to multi-way-join planner */
+               return make_fromexpr_rel(root, f);
+       }
        else if (IsA(jtnode, JoinExpr))
        {
                JoinExpr   *j = (JoinExpr *) jtnode;
@@ -329,8 +336,8 @@ make_rel_from_jointree(Query *root, Node *jtnode)
                                   *rrel;
 
                /* Recurse */
-               lrel = make_rel_from_jointree(root, j->larg);
-               rrel = make_rel_from_jointree(root, j->rarg);
+               lrel = make_jointree_rel(root, j->larg);
+               rrel = make_jointree_rel(root, j->rarg);
 
                /* Make this join rel */
                rel = make_join_rel(root, lrel, rrel, j->jointype);
@@ -346,7 +353,7 @@ make_rel_from_jointree(Query *root, Node *jtnode)
                return rel;
        }
        else
-               elog(ERROR, "make_rel_from_jointree: unexpected node type %d",
+               elog(ERROR, "make_jointree_rel: unexpected node type %d",
                         nodeTag(jtnode));
        return NULL;                            /* keep compiler quiet */
 }
index c6eccdd..820492b 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.25 2000/09/12 21:06:53 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.26 2000/09/29 18:21:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -122,7 +122,7 @@ add_equijoined_keys(Query *root, RestrictInfo *restrictinfo)
                                newset = lcons(item1, lcons(item2, NIL));
 
                        /* Found a set to merge into our new set */
-                       newset = LispUnion(newset, curset);
+                       newset = set_union(newset, curset);
 
                        /*
                         * Remove old set from equi_key_list.  NOTE this does not
index 96dc332..cc308b4 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.96 2000/09/12 21:06:53 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.97 2000/09/29 18:21:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -41,6 +41,8 @@ static IndexScan *create_indexscan_node(Query *root, IndexPath *best_path,
                                          List *tlist, List *scan_clauses);
 static TidScan *create_tidscan_node(TidPath *best_path, List *tlist,
                                        List *scan_clauses);
+static SubqueryScan *create_subqueryscan_node(Path *best_path,
+                                         List *tlist, List *scan_clauses);
 static NestLoop *create_nestloop_node(NestPath *best_path, List *tlist,
                                                                          List *joinclauses, List *otherclauses,
                                                                          Plan *outer_node, List *outer_tlist,
@@ -66,6 +68,8 @@ static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
                           ScanDirection indexscandir);
 static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid,
                         List *tideval);
+static SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual,
+                                                                          Index scanrelid, Plan *subplan);
 static NestLoop *make_nestloop(List *tlist,
                                                           List *joinclauses, List *otherclauses,
                                                           Plan *lefttree, Plan *righttree,
@@ -110,6 +114,7 @@ create_plan(Query *root, Path *best_path)
                case T_IndexScan:
                case T_SeqScan:
                case T_TidScan:
+               case T_SubqueryScan:
                        plan_node = (Plan *) create_scan_node(root, best_path, tlist);
                        break;
                case T_HashJoin:
@@ -164,7 +169,9 @@ create_scan_node(Query *root, Path *best_path, List *tlist)
        switch (best_path->pathtype)
        {
                case T_SeqScan:
-                       node = (Scan *) create_seqscan_node(best_path, tlist, scan_clauses);
+                       node = (Scan *) create_seqscan_node(best_path,
+                                                                                               tlist,
+                                                                                               scan_clauses);
                        break;
 
                case T_IndexScan:
@@ -180,6 +187,12 @@ create_scan_node(Query *root, Path *best_path, List *tlist)
                                                                                                scan_clauses);
                        break;
 
+               case T_SubqueryScan:
+                       node = (Scan *) create_subqueryscan_node(best_path,
+                                                                                                        tlist,
+                                                                                                        scan_clauses);
+                       break;
+
                default:
                        elog(ERROR, "create_scan_node: unknown node type: %d",
                                 best_path->pathtype);
@@ -301,6 +314,7 @@ create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses)
 
        /* there should be exactly one base rel involved... */
        Assert(length(best_path->parent->relids) == 1);
+       Assert(! best_path->parent->issubquery);
 
        scan_relid = (Index) lfirsti(best_path->parent->relids);
 
@@ -342,6 +356,8 @@ create_indexscan_node(Query *root,
 
        /* there should be exactly one base rel involved... */
        Assert(length(best_path->path.parent->relids) == 1);
+       Assert(! best_path->path.parent->issubquery);
+
        baserelid = lfirsti(best_path->path.parent->relids);
 
        /* check to see if any of the indices are lossy */
@@ -391,8 +407,7 @@ create_indexscan_node(Query *root,
                                                                make_ands_explicit(lfirst(orclause)));
                indxqual_expr = make_orclause(orclauses);
 
-               qpqual = set_difference(scan_clauses,
-                                                               lcons(indxqual_expr, NIL));
+               qpqual = set_difference(scan_clauses, makeList1(indxqual_expr));
 
                if (lossy)
                        qpqual = lappend(qpqual, copyObject(indxqual_expr));
@@ -449,6 +464,7 @@ create_tidscan_node(TidPath *best_path, List *tlist, List *scan_clauses)
 
        /* there should be exactly one base rel involved... */
        Assert(length(best_path->path.parent->relids) == 1);
+       Assert(! best_path->path.parent->issubquery);
 
        scan_relid = (Index) lfirsti(best_path->path.parent->relids);
 
@@ -465,6 +481,34 @@ create_tidscan_node(TidPath *best_path, List *tlist, List *scan_clauses)
        return scan_node;
 }
 
+/*
+ * create_subqueryscan_node
+ *      Returns a subqueryscan node for the base relation scanned by 'best_path'
+ *      with restriction clauses 'scan_clauses' and targetlist 'tlist'.
+ */
+static SubqueryScan *
+create_subqueryscan_node(Path *best_path, List *tlist, List *scan_clauses)
+{
+       SubqueryScan *scan_node;
+       Index           scan_relid;
+
+       /* there should be exactly one base rel involved... */
+       Assert(length(best_path->parent->relids) == 1);
+       /* and it must be a subquery */
+       Assert(best_path->parent->issubquery);
+
+       scan_relid = (Index) lfirsti(best_path->parent->relids);
+
+       scan_node = make_subqueryscan(tlist,
+                                                                 scan_clauses,
+                                                                 scan_relid,
+                                                                 best_path->parent->subplan);
+
+       copy_path_costsize(&scan_node->scan.plan, best_path);
+
+       return scan_node;
+}
+
 /*****************************************************************************
  *
  *     JOIN METHODS
@@ -1162,6 +1206,28 @@ make_tidscan(List *qptlist,
        return node;
 }
 
+static SubqueryScan *
+make_subqueryscan(List *qptlist,
+                                 List *qpqual,
+                                 Index scanrelid,
+                                 Plan *subplan)
+{
+       SubqueryScan *node = makeNode(SubqueryScan);
+       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->subplan = subplan;
+       node->scan.scanstate = (CommonScanState *) NULL;
+
+       return node;
+}
+
 
 static NestLoop *
 make_nestloop(List *tlist,
@@ -1405,7 +1471,11 @@ make_agg(List *tlist, List *qual, Plan *lefttree)
         * mode, so it didn't reduce its row count already.)
         */
        if (IsA(lefttree, Group))
+       {
                plan->plan_rows *= 0.1;
+               if (plan->plan_rows < 1)
+                       plan->plan_rows = 1;
+       }
        else
        {
                plan->plan_rows = 1;
@@ -1447,7 +1517,11 @@ make_group(List *tlist,
         * --- bogus, but how to do better?
         */
        if (!tuplePerGroup)
+       {
                plan->plan_rows *= 0.1;
+               if (plan->plan_rows < 1)
+                       plan->plan_rows = 1;
+       }
 
        plan->state = (EState *) NULL;
        plan->qual = NULL;
@@ -1489,6 +1563,8 @@ make_unique(List *tlist, Plan *lefttree, List *distinctList)
         * 10% as many tuples out as in.
         */
        plan->plan_rows *= 0.1;
+       if (plan->plan_rows < 1)
+               plan->plan_rows = 1;
 
        plan->state = (EState *) NULL;
        plan->targetlist = tlist;
index bf728ca..acee58b 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.50 2000/09/12 21:06:54 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.51 2000/09/29 18:21:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 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 distribute_qual_to_rels(Query *root, Node *clause,
+                                                                       bool ispusheddown,
+                                                                       bool isouterjoin,
+                                                                       Relids qualscope);
 static void add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
                                          Relids join_relids);
 static void add_vars_to_targetlist(Query *root, List *vars);
@@ -93,15 +94,13 @@ add_vars_to_targetlist(Query *root, List *vars)
  *       If we have a relation listed in the join tree that does not appear
  *       in the target list nor qualifications, we must add it to the base
  *       relation list so that it can be processed.  For instance,
+ *                     select count(*) from foo;
+ *       would fail to scan foo if this routine were not called.  More subtly,
  *                     select f.x from foo f, foo f2
  *       is a join of f and f2.  Note that if we have
  *                     select foo.x from foo f
  *       this also gets turned into a join (between foo as foo and foo as f).
  *
- *       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
@@ -115,34 +114,24 @@ add_missing_rels_to_query(Query *root, Node *jtnode)
 
        if (jtnode == NULL)
                return NIL;
-       if (IsA(jtnode, List))
+       if (IsA(jtnode, RangeTblRef))
        {
-               List       *l;
+               int                     varno = ((RangeTblRef *) jtnode)->rtindex;
+               /* This call to get_base_rel does the primary work... */
+               RelOptInfo *rel = get_base_rel(root, varno);
 
-               foreach(l, (List *) jtnode)
-               {
-                       result = nconc(result,
-                                                  add_missing_rels_to_query(root, lfirst(l)));
-               }
+               result = makeList1(rel);
        }
-       else if (IsA(jtnode, RangeTblRef))
+       else if (IsA(jtnode, FromExpr))
        {
-               int                     varno = ((RangeTblRef *) jtnode)->rtindex;
-               RelOptInfo *rel = get_base_rel(root, varno);
+               FromExpr   *f = (FromExpr *) jtnode;
+               List       *l;
 
-               /*
-                * If the rel isn't otherwise referenced, give it a dummy
-                * targetlist consisting of its own OID.
-                */
-               if (rel->targetlist == NIL)
+               foreach(l, f->fromlist)
                {
-                       Var                *var = makeVar(varno, ObjectIdAttributeNumber,
-                                                                         OIDOID, -1, 0);
-
-                       add_var_to_tlist(rel, var);
+                       result = nconc(result,
+                                                  add_missing_rels_to_query(root, lfirst(l)));
                }
-
-               result = lcons(rel, NIL);
        }
        else if (IsA(jtnode, JoinExpr))
        {
@@ -167,58 +156,74 @@ add_missing_rels_to_query(Query *root, Node *jtnode)
 
 
 /*
- * 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().
+ * distribute_quals_to_rels
+ *       Recursively scan the query's join tree for WHERE and JOIN/ON qual
+ *       clauses, and add these to the appropriate RestrictInfo and JoinInfo
+ *       lists belonging to base RelOptInfos.  New base rel entries are created
+ *       as needed.  Also, base RelOptInfos are marked with outerjoinset
+ *       information, to aid in proper positioning of qual clauses that appear
+ *       above outer joins.
  *
  * 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.
+ * available.  However, we cannot push a qual down into the nullable side(s)
+ * of an outer join since the qual might eliminate matching rows and cause a
+ * NULL row to be incorrectly emitted by the join.  Therefore, rels appearing
+ * within the nullable side(s) 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
+ * To ease the calculation of these values, distribute_quals_to_rels() returns
  * the list of Relids involved in its own level of join.  This is just an
  * internal convenience; no outside callers pay attention to the result.
  */
 Relids
-add_join_quals_to_rels(Query *root, Node *jtnode)
+distribute_quals_to_rels(Query *root, Node *jtnode)
 {
        Relids          result = NIL;
 
        if (jtnode == NULL)
                return result;
-       if (IsA(jtnode, List))
+       if (IsA(jtnode, RangeTblRef))
        {
+               int                     varno = ((RangeTblRef *) jtnode)->rtindex;
+
+               /* No quals to deal with, just return correct result */
+               result = makeListi1(varno);
+       }
+       else if (IsA(jtnode, FromExpr))
+       {
+               FromExpr   *f = (FromExpr *) jtnode;
                List       *l;
+               List       *qual;
 
                /*
+                * First, recurse to handle child joins.
+                *
                 * Note: we assume it's impossible to see same RT index from more
-                * than one subtree, so nconc() is OK rather than LispUnioni().
+                * than one subtree, so nconc() is OK rather than set_unioni().
                 */
-               foreach(l, (List *) jtnode)
+               foreach(l, f->fromlist)
+               {
                        result = nconc(result,
-                                                  add_join_quals_to_rels(root, lfirst(l)));
-       }
-       else if (IsA(jtnode, RangeTblRef))
-       {
-               int                     varno = ((RangeTblRef *) jtnode)->rtindex;
+                                                  distribute_quals_to_rels(root, lfirst(l)));
+               }
 
-               /* No quals to deal with, just return correct result */
-               result = lconsi(varno, NIL);
+               /*
+                * Now process the top-level quals.  These are always marked as
+                * "pushed down", since they clearly didn't come from a JOIN expr.
+                */
+               foreach(qual, (List *) f->quals)
+                       distribute_qual_to_rels(root, (Node *) lfirst(qual),
+                                                                       true, false, result);
        }
        else if (IsA(jtnode, JoinExpr))
        {
                JoinExpr   *j = (JoinExpr *) jtnode;
                Relids          leftids,
-                                       rightids,
-                                       outerjoinids;
+                                       rightids;
+               bool            isouterjoin;
                List       *qual;
 
                /*
@@ -228,15 +233,15 @@ add_join_quals_to_rels(Query *root, Node *jtnode)
                 * 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.
+                * quals and quals above us in the join tree.
                 * 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);
+               leftids = distribute_quals_to_rels(root, j->larg);
+               rightids = distribute_quals_to_rels(root, j->rarg);
 
                result = nconc(listCopy(leftids), rightids);
 
-               outerjoinids = NIL;
+               isouterjoin = false;
                switch (j->jointype)
                {
                        case JOIN_INNER:
@@ -244,15 +249,15 @@ add_join_quals_to_rels(Query *root, Node *jtnode)
                                break;
                        case JOIN_LEFT:
                                mark_baserels_for_outer_join(root, rightids, result);
-                               outerjoinids = result;
+                               isouterjoin = true;
                                break;
                        case JOIN_FULL:
                                mark_baserels_for_outer_join(root, result, result);
-                               outerjoinids = result;
+                               isouterjoin = true;
                                break;
                        case JOIN_RIGHT:
                                mark_baserels_for_outer_join(root, leftids, result);
-                               outerjoinids = result;
+                               isouterjoin = true;
                                break;
                        case JOIN_UNION:
                                /*
@@ -262,17 +267,18 @@ add_join_quals_to_rels(Query *root, Node *jtnode)
                                elog(ERROR, "UNION JOIN is not implemented yet");
                                break;
                        default:
-                               elog(ERROR, "add_join_quals_to_rels: unsupported join type %d",
+                               elog(ERROR,
+                                        "distribute_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);
+                       distribute_qual_to_rels(root, (Node *) lfirst(qual),
+                                                                       false, isouterjoin, result);
        }
        else
-               elog(ERROR, "add_join_quals_to_rels: unexpected node type %d",
+               elog(ERROR, "distribute_quals_to_rels: unexpected node type %d",
                         nodeTag(jtnode));
        return result;
 }
@@ -301,25 +307,7 @@ mark_baserels_for_outer_join(Query *root, Relids rels, Relids 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 root->base_rel_list.
- *
- * 'clauses': the list of clauses in the cnfify'd query qualification.
- */
-void
-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),
-                                                                        false, NIL);
-}
-
-/*
- * add_restrict_and_join_to_rel
+ * distribute_qual_to_rels
  *       Add clause information to either the 'RestrictInfo' or 'JoinInfo' field
  *       (depending on whether the clause is a join) of each base relation
  *       mentioned in the clause.      A RestrictInfo node is created and added to
@@ -327,20 +315,21 @@ add_restrict_and_join_to_rels(Query *root, List *clauses)
  *       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.)
+ * 'clause': the qual clause to be distributed
+ * 'ispusheddown': if TRUE, force the clause to be marked 'ispusheddown'
+ *             (this indicates the clause came from a FromExpr, not a JoinExpr)
+ * 'isouterjoin': TRUE if the qual came from an OUTER JOIN's ON-clause
+ * 'qualscope': list of baserels the qual's syntactic scope covers
+ *
+ * 'qualscope' identifies what level of JOIN the qual came from.  For a top
+ * level qual (WHERE qual), qualscope lists all baserel ids and in addition
+ * 'ispusheddown' will be TRUE.
  */
 static void
-add_restrict_and_join_to_rel(Query *root, Node *clause,
-                                                        bool isjoinqual,
-                                                        Relids outerjoinrelids)
+distribute_qual_to_rels(Query *root, Node *clause,
+                                               bool ispusheddown,
+                                               bool isouterjoin,
+                                               Relids qualscope)
 {
        RestrictInfo *restrictinfo = makeNode(RestrictInfo);
        Relids          relids;
@@ -348,7 +337,6 @@ add_restrict_and_join_to_rel(Query *root, Node *clause,
        bool            can_be_equijoin;
 
        restrictinfo->clause = (Expr *) clause;
-       restrictinfo->isjoinqual = isjoinqual;
        restrictinfo->subclauseindices = NIL;
        restrictinfo->mergejoinoperator = InvalidOid;
        restrictinfo->left_sortop = InvalidOid;
@@ -361,17 +349,40 @@ 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.
+        * Cross-check: clause should contain no relids not within its scope.
+        * Otherwise the parser messed up.
         */
-       if (outerjoinrelids)
+       if (! is_subseti(relids, qualscope))
+               elog(ERROR, "JOIN qualification may not refer to other relations");
+
+       /*
+        * If the clause is variable-free, we force it to be evaluated at its
+        * original syntactic level.  Note that this should not happen for
+        * top-level clauses, because query_planner() special-cases them.  But
+        * it will happen for variable-free JOIN/ON clauses.  We don't have to
+        * be real smart about such a case, we just have to be correct.
+        */
+       if (relids == NIL)
+               relids = qualscope;
+
+       /*
+        * For an outer-join qual, pretend that the clause references all rels
+        * appearing within its syntactic scope, even if it really doesn't.
+        * This ensures that the clause will be evaluated exactly at the level
+        * of joining corresponding to the outer join.
+        *
+        * For a non-outer-join qual, we can evaluate the qual as soon as
+        * (1) we have all the rels it mentions, and (2) we are at or above any
+        * outer joins that can null any of these rels and are below the syntactic
+        * location of the given qual.  To enforce the latter, scan the base rels
+        * listed in relids, and merge their outer-join lists into the clause's
+        * own reference list.  At the time we are called, the outerjoinset list
+        * of each baserel will show exactly those outer joins that are below the
+        * qual in the join tree.
+        */
+       if (isouterjoin)
        {
-               /* 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;
+               relids = qualscope;
                can_be_equijoin = false;
        }
        else
@@ -379,15 +390,16 @@ add_restrict_and_join_to_rel(Query *root, Node *clause,
                Relids          newrelids = relids;
                List       *relid;
 
-               /* We rely on LispUnioni to be nondestructive of its input lists... */
+               /* We rely on set_unioni 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)
+                       if (rel->outerjoinset &&
+                               ! is_subseti(rel->outerjoinset, relids))
                        {
-                               newrelids = LispUnioni(newrelids, rel->outerjoinset);
+                               newrelids = set_unioni(newrelids, rel->outerjoinset);
                                /*
                                 * Because application of the qual will be delayed by outer
                                 * join, we mustn't assume its vars are equal everywhere.
@@ -396,8 +408,19 @@ add_restrict_and_join_to_rel(Query *root, Node *clause,
                        }
                }
                relids = newrelids;
+               /* Should still be a subset of current scope ... */
+               Assert(is_subseti(relids, qualscope));
        }
 
+       /*
+        * Mark the qual as "pushed down" if it can be applied at a level below
+        * its original syntactic level.  This allows us to distinguish original
+        * JOIN/ON quals from higher-level quals pushed down to the same joinrel.
+        * A qual originating from WHERE is always considered "pushed down".
+        */
+       restrictinfo->ispusheddown = ispusheddown || !sameseti(relids,
+                                                                                                                  qualscope);
+
        if (length(relids) == 1)
        {
 
@@ -454,10 +477,9 @@ add_restrict_and_join_to_rel(Query *root, Node *clause,
        {
                /*
                 * 'clause' references no rels, and therefore we have no place to
-                * attach it.  This means query_planner() screwed up --- it should
-                * treat variable-less clauses separately.
+                * attach it.  Shouldn't get here if callers are working properly.
                 */
-               elog(ERROR, "add_restrict_and_join_to_rel: can't cope with variable-free clause");
+               elog(ERROR, "distribute_qual_to_rels: can't cope with variable-free clause");
        }
 
        /*
@@ -557,7 +579,7 @@ process_implied_equality(Query *root, Node *item1, Node *item2,
        else
        {
                JoinInfo   *joininfo = find_joininfo_node(rel1,
-                                                                                                 lconsi(irel2, NIL));
+                                                                                                 makeListi1(irel2));
 
                restrictlist = joininfo->jinfo_restrictinfo;
        }
@@ -612,10 +634,20 @@ process_implied_equality(Query *root, Node *item1, Node *item2,
        clause->oper = (Node *) makeOper(oprid(eq_operator), /* opno */
                                                                         InvalidOid, /* opid */
                                                                         BOOLOID); /* operator result type */
-       clause->args = lcons(item1, lcons(item2, NIL));
+       clause->args = makeList2(item1, item2);
 
-       add_restrict_and_join_to_rel(root, (Node *) clause,
-                                                                false, NIL);
+       /*
+        * Note: we mark the qual "pushed down" to ensure that it can never be
+        * taken for an original JOIN/ON clause.  We also claim it is an outer-
+        * join clause, which it isn't, but that keeps distribute_qual_to_rels
+        * from examining the outerjoinsets of the relevant rels (which are no
+        * longer of interest, but could keep the qual from being pushed down
+        * to where it should be).  It'll also save a useless call to
+        * add_equijoined keys...
+        */
+       distribute_qual_to_rels(root, (Node *) clause,
+                                                       true, true,
+                                                       pull_varnos((Node *) clause));
 }
 
 
index 1fcbe64..29cfccf 100644 (file)
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.59 2000/09/12 21:06:54 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.60 2000/09/29 18:21:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -32,8 +32,8 @@
 #include "utils/memutils.h"
 
 
-static Plan *subplanner(Query *root, List *flat_tlist, List *qual,
-                  double tuple_fraction);
+static Plan *subplanner(Query *root, List *flat_tlist,
+                                               double tuple_fraction);
 
 
 /*--------------------
@@ -75,46 +75,36 @@ query_planner(Query *root,
                          List *tlist,
                          double tuple_fraction)
 {
-       List       *normal_qual;
-       List       *noncachable_qual;
-       List       *constant_qual;
+       List       *constant_quals;
        List       *var_only_tlist;
        Plan       *subplan;
 
        /*
-        * If the query contains no relation references at all, it must be
-        * something like "SELECT 2+2;".  Build a trivial "Result" plan.
+        * If the query has an empty join tree, then it's something easy like
+        * "SELECT 2+2;" or "INSERT ... VALUES()".  Fall through quickly.
         */
-       if (root->rtable == NIL)
+       if (root->jointree->fromlist == NIL)
        {
-               /* If it's not a select, it should have had a target relation... */
-               if (root->commandType != CMD_SELECT)
-                       elog(ERROR, "Empty range table for non-SELECT query");
-
                root->query_pathkeys = NIL;             /* signal unordered result */
 
                /* Make childless Result node to evaluate given tlist. */
-               return (Plan *) make_result(tlist, root->qual, (Plan *) NULL);
+               return (Plan *) make_result(tlist, root->jointree->quals,
+                                                                       (Plan *) NULL);
        }
 
        /*
-        * Pull out any non-variable qual clauses so these can be put in a
+        * Pull out any non-variable WHERE clauses so these can be put in a
         * toplevel "Result" node, where they will gate execution of the whole
         * plan (the Result will not invoke its descendant plan unless the
         * quals are true).  Note that any *really* non-variable quals will
         * have been optimized away by eval_const_expressions().  What we're
         * mostly interested in here is quals that depend only on outer-level
         * vars, although if the qual reduces to "WHERE FALSE" this path will
-        * also be taken.  We also need a special case for quals that contain
-        * 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 plan, so that they get evaluated once per potential
-        * output tuple.
+        * also be taken.
         */
-       normal_qual = pull_constant_clauses((List *) root->qual,
-                                                                               &noncachable_qual,
-                                                                               &constant_qual);
+       root->jointree->quals = (Node *)
+               pull_constant_clauses((List *) root->jointree->quals,
+                                                         &constant_quals);
 
        /*
         * Create a target list that consists solely of (resdom var) target
@@ -132,18 +122,12 @@ query_planner(Query *root,
        /*
         * Choose the best access path and build a plan for it.
         */
-       subplan = subplanner(root, var_only_tlist, normal_qual, tuple_fraction);
-
-       /*
-        * Handle the noncachable quals.
-        */
-       if (noncachable_qual)
-               subplan->qual = nconc(subplan->qual, noncachable_qual);
+       subplan = subplanner(root, var_only_tlist, tuple_fraction);
 
        /*
         * Build a result node to control the plan if we have constant quals.
         */
-       if (constant_qual)
+       if (constant_quals)
        {
 
                /*
@@ -151,7 +135,7 @@ query_planner(Query *root,
                 * originally requested tlist.
                 */
                subplan = (Plan *) make_result(tlist,
-                                                                          (Node *) constant_qual,
+                                                                          (Node *) constant_quals,
                                                                           subplan);
        }
        else
@@ -175,7 +159,6 @@ query_planner(Query *root,
  *      for processing a single level of attributes.
  *
  * flat_tlist is the flattened target list
- * qual is the qualification to be satisfied (restrict and join quals only)
  * tuple_fraction is the fraction of tuples we expect will be retrieved
  *
  * See query_planner() comments about the interpretation of tuple_fraction.
@@ -185,7 +168,6 @@ query_planner(Query *root,
 static Plan *
 subplanner(Query *root,
                   List *flat_tlist,
-                  List *qual,
                   double tuple_fraction)
 {
        List       *joined_rels;
@@ -210,9 +192,8 @@ subplanner(Query *root,
        root->equi_key_list = NIL;
 
        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);
+
+       (void) distribute_quals_to_rels(root, (Node *) root->jointree);
 
        /*
         * Make sure we have RelOptInfo nodes for all relations to be joined.
@@ -270,26 +251,7 @@ subplanner(Query *root,
        final_rel = make_one_rel(root);
 
        if (!final_rel)
-       {
-
-               /*
-                * 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 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 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 */
-
-               /* Make childless Result node to evaluate given tlist. */
-               resultplan = (Plan *) make_result(flat_tlist, (Node *) qual,
-                                                                                 (Plan *) NULL);
-               goto plan_built;
-       }
+               elog(ERROR, "subplanner: failed to construct a relation");
 
 #ifdef NOT_USED                                        /* fix xfunc */
 
@@ -395,7 +357,10 @@ plan_built:
 
        /*
         * Must copy the completed plan tree and its pathkeys out of temporary
-        * context.
+        * context.  We also have to copy the rtable in case it contains any
+        * subqueries.  (If it does, they'll have been modified during the
+        * recursive invocation of planner.c, and hence will contain substructure
+        * allocated in my temporary context...)
         */
        MemoryContextSwitchTo(oldcxt);
 
@@ -403,6 +368,8 @@ plan_built:
 
        root->query_pathkeys = copyObject(root->query_pathkeys);
 
+       root->rtable = copyObject(root->rtable);
+
        /*
         * Now we can release the Path storage.
         */
index d6e2330..9376281 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.90 2000/09/25 18:09:28 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.91 2000/09/29 18:21:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "optimizer/subselect.h"
 #include "optimizer/tlist.h"
 #include "optimizer/var.h"
+#include "parser/parsetree.h"
 #include "parser/parse_expr.h"
+#include "rewrite/rewriteManip.h"
 #include "utils/lsyscache.h"
 
 
-static void preprocess_join_conditions(Query *parse, Node *jtnode);
+/* Expression kind codes for preprocess_expression */
+#define EXPRKIND_TARGET        0
+#define EXPRKIND_WHERE 1
+#define EXPRKIND_HAVING        2
+
+
+static Node *pull_up_subqueries(Query *parse, Node *jtnode);
+static bool is_simple_subquery(Query *subquery);
+static void resolvenew_in_jointree(Node *jtnode, int varno, List *subtlist);
+static Node *preprocess_jointree(Query *parse, Node *jtnode);
+static Node *preprocess_expression(Query *parse, Node *expr, int kind);
+static void preprocess_qual_conditions(Query *parse, Node *jtnode);
 static List *make_subplanTargetList(Query *parse, List *tlist,
                                           AttrNumber **groupColIdx);
 static Plan *make_groupplan(List *group_tlist, bool tuplePerGroup,
@@ -52,8 +65,9 @@ planner(Query *parse)
        int                     save_PlannerPlanId;
 
        /*
-        * The planner can be called recursively (an example is when
-        * eval_const_expressions tries to simplify an SQL function).
+        * The outer planner can be called recursively, for example to process
+        * a subquery in the rangetable.  (A less obvious example occurs when
+        * eval_const_expressions tries to simplify an SQL function.)
         * So, global state variables must be saved and restored.
         *
         * (Perhaps these should be moved into the Query structure instead?)
@@ -72,7 +86,7 @@ planner(Query *parse)
        /* this should go away sometime soon */
        transformKeySetQuery(parse);
 
-       /* primary planning entry point (may recurse for subplans) */
+       /* primary planning entry point (may recurse for sublinks) */
        result_plan = subquery_planner(parse, -1.0 /* default case */ );
 
        Assert(PlannerQueryLevel == 1);
@@ -127,6 +141,18 @@ Plan *
 subquery_planner(Query *parse, double tuple_fraction)
 {
        /*
+        * Check to see if any subqueries in the rangetable can be merged into
+        * this query.
+        */
+       parse->jointree = (FromExpr *)
+               pull_up_subqueries(parse, (Node *) parse->jointree);
+       /*
+        * If so, we may have created opportunities to simplify the jointree.
+        */
+       parse->jointree = (FromExpr *)
+               preprocess_jointree(parse, (Node *) parse->jointree);
+
+       /*
         * A HAVING clause without aggregates is equivalent to a WHERE clause
         * (except it can only refer to grouped fields).  If there are no aggs
         * anywhere in the query, then we don't want to create an Agg plan
@@ -135,150 +161,413 @@ subquery_planner(Query *parse, double tuple_fraction)
         */
        if (parse->havingQual != NULL && !parse->hasAggs)
        {
-               if (parse->qual == NULL)
-                       parse->qual = parse->havingQual;
-               else
-                       parse->qual = (Node *) make_andclause(lappend(lcons(parse->qual,
-                                                                                                                               NIL),
-                                                                                                        parse->havingQual));
+               parse->jointree->quals = make_and_qual(parse->jointree->quals,
+                                                                                          parse->havingQual);
                parse->havingQual = NULL;
        }
 
        /*
-        * Simplify constant expressions in targetlist and quals.
-        *
-        * Note that at this point the qual has not yet been converted to
-        * implicit-AND form, so we can apply eval_const_expressions directly.
-        * Also note that we need to do this before SS_process_sublinks,
-        * because that routine inserts bogus "Const" nodes.
+        * Do preprocessing on targetlist and quals.
         */
        parse->targetList = (List *)
-               eval_const_expressions((Node *) parse->targetList);
-       parse->qual = eval_const_expressions(parse->qual);
-       parse->havingQual = eval_const_expressions(parse->havingQual);
+               preprocess_expression(parse, (Node *) parse->targetList,
+                                                         EXPRKIND_TARGET);
+
+       preprocess_qual_conditions(parse, (Node *) parse->jointree);
+
+       parse->havingQual = preprocess_expression(parse, parse->havingQual,
+                                                                                         EXPRKIND_HAVING);
 
        /*
-        * Canonicalize the qual, and convert it to implicit-AND format.
-        *
-        * XXX Is there any value in re-applying eval_const_expressions after
-        * canonicalize_qual?
+        * Do the main planning (potentially recursive)
         */
-       parse->qual = (Node *) canonicalize_qual((Expr *) parse->qual, true);
-
-#ifdef OPTIMIZER_DEBUG
-       printf("After canonicalize_qual()\n");
-       pprint(parse->qual);
-#endif
+       return union_planner(parse, tuple_fraction);
 
        /*
-        * Ditto for the havingQual
+        * XXX should any more of union_planner's activity be moved here?
+        *
+        * That would take careful study of the interactions with prepunion.c,
+        * but I suspect it would pay off in simplicity and avoidance of
+        * wasted cycles.
         */
-       parse->havingQual = (Node *) canonicalize_qual((Expr *) parse->havingQual,
-                                                                                                  true);
+}
 
-       /* Expand SubLinks to SubPlans */
-       if (parse->hasSubLinks)
+/*
+ * pull_up_subqueries
+ *             Look for subqueries in the rangetable that can be pulled up into
+ *             the parent query.  If the subquery has no special features like
+ *             grouping/aggregation then we can merge it into the parent's jointree.
+ *
+ * A tricky aspect of this code is that if we pull up a subquery we have
+ * to replace Vars that reference the subquery's outputs throughout the
+ * parent query, including quals attached to jointree nodes above the one
+ * we are currently processing!  We handle this by being careful not to
+ * change the jointree structure while recursing: no nodes other than
+ * subquery RangeTblRef entries will be replaced.  Also, we can't turn
+ * ResolveNew loose on the whole jointree, because it'll return a mutated
+ * copy of the tree; we have to invoke it just on the quals, instead.
+ */
+static Node *
+pull_up_subqueries(Query *parse, Node *jtnode)
+{
+       if (jtnode == NULL)
+               return NULL;
+       if (IsA(jtnode, RangeTblRef))
        {
-               parse->targetList = (List *)
-                       SS_process_sublinks((Node *) parse->targetList);
-               parse->qual = SS_process_sublinks(parse->qual);
-               parse->havingQual = SS_process_sublinks(parse->havingQual);
+               int                     varno = ((RangeTblRef *) jtnode)->rtindex;
+               RangeTblEntry *rte = rt_fetch(varno, parse->rtable);
+               Query      *subquery = rte->subquery;
 
-               if (parse->groupClause != NIL || parse->hasAggs)
+               /*
+                * Is this a subquery RTE, and if so, is the subquery simple enough
+                * to pull up?  (If not, do nothing at this node.)
+                */
+               if (subquery && is_simple_subquery(subquery))
                {
+                       int             rtoffset;
+                       Node   *subjointree;
+                       List   *subtlist;
 
                        /*
-                        * Check for ungrouped variables passed to subplans. Note we
-                        * do NOT do this for subplans in WHERE; it's legal there
-                        * because WHERE is evaluated pre-GROUP.
-                        *
-                        * An interesting fine point: if we reassigned a HAVING qual into
-                        * WHERE above, then we will accept references to ungrouped
-                        * vars from subplans in the HAVING qual.  This is not
-                        * entirely consistent, but it doesn't seem particularly
-                        * harmful...
+                        * First, recursively pull up the subquery's subqueries,
+                        * so that this routine's processing is complete for its
+                        * jointree and rangetable.
+                        */
+                       subquery->jointree = (FromExpr *)
+                               pull_up_subqueries(subquery, (Node *) subquery->jointree);
+                       /*
+                        * Append the subquery's rangetable to mine (currently,
+                        * no adjustments will be needed in the subquery's rtable).
+                        */
+                       rtoffset = length(parse->rtable);
+                       parse->rtable = nconc(parse->rtable, subquery->rtable);
+                       /*
+                        * Make copies of the subquery's jointree and targetlist
+                        * with varnos adjusted to match the merged rangetable.
+                        */
+                       subjointree = copyObject(subquery->jointree);
+                       OffsetVarNodes(subjointree, rtoffset, 0);
+                       subtlist = copyObject(subquery->targetList);
+                       OffsetVarNodes((Node *) subtlist, rtoffset, 0);
+                       /*
+                        * Replace all of the top query's references to the subquery's
+                        * outputs with copies of the adjusted subtlist items, being
+                        * careful not to replace any of the jointree structure.
                         */
-                       check_subplans_for_ungrouped_vars((Node *) parse->targetList,
-                                                                                         parse);
-                       check_subplans_for_ungrouped_vars(parse->havingQual, parse);
+                       parse->targetList = (List *)
+                               ResolveNew((Node *) parse->targetList,
+                                                  varno, 0, subtlist, CMD_SELECT, 0);
+                       resolvenew_in_jointree((Node *) parse->jointree, varno, subtlist);
+                       parse->havingQual =
+                               ResolveNew(parse->havingQual,
+                                                  varno, 0, subtlist, CMD_SELECT, 0);
+                       /*
+                        * Miscellaneous housekeeping.
+                        */
+                       parse->hasSubLinks |= subquery->hasSubLinks;
+                       /*
+                        * Return the adjusted subquery jointree to replace the
+                        * RangeTblRef entry in my jointree.
+                        */
+                       return subjointree;
                }
        }
-
-       /* Replace uplevel vars with Param nodes */
-       if (PlannerQueryLevel > 1)
+       else if (IsA(jtnode, FromExpr))
        {
-               parse->targetList = (List *)
-                       SS_replace_correlation_vars((Node *) parse->targetList);
-               parse->qual = SS_replace_correlation_vars(parse->qual);
-               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);
+               FromExpr   *f = (FromExpr *) jtnode;
+               List       *l;
 
-       /* Do the main planning (potentially recursive) */
+               foreach(l, f->fromlist)
+               {
+                       lfirst(l) = pull_up_subqueries(parse, lfirst(l));
+               }
+       }
+       else if (IsA(jtnode, JoinExpr))
+       {
+               JoinExpr   *j = (JoinExpr *) jtnode;
 
-       return union_planner(parse, tuple_fraction);
+               j->larg = pull_up_subqueries(parse, j->larg);
+               j->rarg = pull_up_subqueries(parse, j->rarg);
+       }
+       else
+               elog(ERROR, "pull_up_subqueries: unexpected node type %d",
+                        nodeTag(jtnode));
+       return jtnode;
+}
 
+/*
+ * is_simple_subquery
+ *       Check a subquery in the range table to see if it's simple enough
+ *       to pull up into the parent query.
+ */
+static bool
+is_simple_subquery(Query *subquery)
+{
        /*
-        * XXX should any more of union_planner's activity be moved here?
-        *
-        * That would take careful study of the interactions with prepunion.c,
-        * but I suspect it would pay off in simplicity and avoidance of
-        * wasted cycles.
+        * Let's just make sure it's a valid subselect ...
+        */
+       if (!IsA(subquery, Query) ||
+               subquery->commandType != CMD_SELECT ||
+               subquery->resultRelation != 0 ||
+               subquery->into != NULL ||
+               subquery->isPortal)
+               elog(ERROR, "is_simple_subquery: subquery is bogus");
+       /*
+        * Also check for currently-unsupported features.
+        */
+       if (subquery->rowMarks)
+               elog(ERROR, "FOR UPDATE is not supported in subselects");
+       if (subquery->limitOffset || subquery->limitCount)
+               elog(ERROR, "LIMIT is not supported in subselects");
+       /*
+        * Can't currently pull up a union query.  Maybe after querytree redesign.
         */
+       if (subquery->unionClause)
+               return false;
+       /*
+        * Can't pull up a subquery involving grouping, aggregation, or sorting.
+        */
+       if (subquery->hasAggs ||
+               subquery->groupClause ||
+               subquery->havingQual ||
+               subquery->sortClause ||
+               subquery->distinctClause)
+               return false;
+       /*
+        * Hack: don't try to pull up a subquery with an empty jointree.
+        * query_planner() will correctly generate a Result plan for a
+        * jointree that's totally empty, but I don't think the right things
+        * happen if an empty FromExpr appears lower down in a jointree.
+        * Not worth working hard on this, just to collapse SubqueryScan/Result
+        * into Result...
+        */
+       if (subquery->jointree->fromlist == NIL)
+               return false;
+
+       return true;
 }
 
 /*
- * preprocess_join_conditions
- *             Recursively scan the query's jointree and do subquery_planner's
- *             qual preprocessing work on each ON condition found therein.
+ * Helper routine for pull_up_subqueries: do ResolveNew on every expression
+ * in the jointree, without changing the jointree structure itself.  Ugly,
+ * but there's no other way...
  */
 static void
-preprocess_join_conditions(Query *parse, Node *jtnode)
+resolvenew_in_jointree(Node *jtnode, int varno, List *subtlist)
 {
        if (jtnode == NULL)
                return;
-       if (IsA(jtnode, List))
+       if (IsA(jtnode, RangeTblRef))
        {
+               /* nothing to do here */
+       }
+       else if (IsA(jtnode, FromExpr))
+       {
+               FromExpr   *f = (FromExpr *) jtnode;
                List       *l;
 
-               foreach(l, (List *) jtnode)
-                       preprocess_join_conditions(parse, lfirst(l));
+               foreach(l, f->fromlist)
+                       resolvenew_in_jointree(lfirst(l), varno, subtlist);
+               f->quals = ResolveNew(f->quals,
+                                                         varno, 0, subtlist, CMD_SELECT, 0);
        }
-       else if (IsA(jtnode, RangeTblRef))
+       else if (IsA(jtnode, JoinExpr))
        {
-               /* nothing to do here */
+               JoinExpr   *j = (JoinExpr *) jtnode;
+
+               resolvenew_in_jointree(j->larg, varno, subtlist);
+               resolvenew_in_jointree(j->rarg, varno, subtlist);
+               j->quals = ResolveNew(j->quals,
+                                                         varno, 0, subtlist, CMD_SELECT, 0);
+               /* We don't bother to update the colvars list, since it won't be
+                * used again ...
+                */
+       }
+       else
+               elog(ERROR, "resolvenew_in_jointree: unexpected node type %d",
+                        nodeTag(jtnode));
+}
+
+/*
+ * preprocess_jointree
+ *             Attempt to simplify a query's jointree.
+ *
+ * If we succeed in pulling up a subquery then we might form a jointree
+ * in which a FromExpr is a direct child of another FromExpr.  In that
+ * case we can consider collapsing the two FromExprs into one.  This is
+ * an optional conversion, since the planner will work correctly either
+ * way.  But we may find a better plan (at the cost of more planning time)
+ * if we merge the two nodes.
+ *
+ * NOTE: don't try to do this in the same jointree scan that does subquery
+ * pullup!  Since we're changing the jointree structure here, that wouldn't
+ * work reliably --- see comments for pull_up_subqueries().
+ */
+static Node *
+preprocess_jointree(Query *parse, Node *jtnode)
+{
+       if (jtnode == NULL)
+               return NULL;
+       if (IsA(jtnode, RangeTblRef))
+       {
+               /* nothing to do here... */
+       }
+       else if (IsA(jtnode, FromExpr))
+       {
+               FromExpr   *f = (FromExpr *) jtnode;
+               List       *newlist = NIL;
+               List       *l;
+
+               foreach(l, f->fromlist)
+               {
+                       Node   *child = (Node *) lfirst(l);
+
+                       /* Recursively simplify the child... */
+                       child = preprocess_jointree(parse, child);
+                       /* Now, is it a FromExpr? */
+                       if (child && IsA(child, FromExpr))
+                       {
+                               /*
+                                * Yes, so do we want to merge it into parent?  Always do so
+                                * if child has just one element (since that doesn't make the
+                                * parent's list any longer).  Otherwise we have to be careful
+                                * about the increase in planning time caused by combining the
+                                * two join search spaces into one.  Our heuristic is to merge
+                                * if the merge will produce a join list no longer than
+                                * GEQO_RELS/2.  (Perhaps need an additional user parameter?)
+                                */
+                               FromExpr   *subf = (FromExpr *) child;
+                               int                     childlen = length(subf->fromlist);
+                               int                     myothers = length(newlist) + length(lnext(l));
+
+                               if (childlen <= 1 || (childlen+myothers) <= geqo_rels/2)
+                               {
+                                       newlist = nconc(newlist, subf->fromlist);
+                                       f->quals = make_and_qual(f->quals, subf->quals);
+                               }
+                               else
+                                       newlist = lappend(newlist, child);
+                       }
+                       else
+                               newlist = lappend(newlist, child);
+               }
+               f->fromlist = newlist;
        }
        else if (IsA(jtnode, JoinExpr))
        {
                JoinExpr   *j = (JoinExpr *) jtnode;
 
-               preprocess_join_conditions(parse, j->larg);
-               preprocess_join_conditions(parse, j->rarg);
+               /* Can't usefully change the JoinExpr, but recurse on children */
+               j->larg = preprocess_jointree(parse, j->larg);
+               j->rarg = preprocess_jointree(parse, j->rarg);
+       }
+       else
+               elog(ERROR, "preprocess_jointree: unexpected node type %d",
+                        nodeTag(jtnode));
+       return jtnode;
+}
 
-               /* Simplify constant expressions */
-               j->quals = eval_const_expressions(j->quals);
+/*
+ * preprocess_expression
+ *             Do subquery_planner's preprocessing work for an expression,
+ *             which can be a targetlist, a WHERE clause (including JOIN/ON
+ *             conditions), or a HAVING clause.
+ */
+static Node *
+preprocess_expression(Query *parse, Node *expr, int kind)
+{
+       /*
+        * Simplify constant expressions.
+        *
+        * Note that at this point quals have not yet been converted to
+        * implicit-AND form, so we can apply eval_const_expressions directly.
+        * Also note that we need to do this before SS_process_sublinks,
+        * because that routine inserts bogus "Const" nodes.
+        */
+       expr = eval_const_expressions(expr);
 
-               /* Canonicalize the qual, and convert it to implicit-AND format */
-               j->quals = (Node *) canonicalize_qual((Expr *) j->quals, true);
+       /*
+        * If it's a qual or havingQual, canonicalize it, and convert it
+        * to implicit-AND format.
+        *
+        * XXX Is there any value in re-applying eval_const_expressions after
+        * canonicalize_qual?
+        */
+       if (kind != EXPRKIND_TARGET)
+       {
+               expr = (Node *) canonicalize_qual((Expr *) expr, true);
+
+#ifdef OPTIMIZER_DEBUG
+               printf("After canonicalize_qual()\n");
+               pprint(expr);
+#endif
+       }
 
+       if (parse->hasSubLinks)
+       {
                /* Expand SubLinks to SubPlans */
-               if (parse->hasSubLinks)
+               expr = SS_process_sublinks(expr);
+
+               if (kind != EXPRKIND_WHERE &&
+                       (parse->groupClause != NIL || parse->hasAggs))
                {
-                       j->quals = SS_process_sublinks(j->quals);
                        /*
-                        * ON conditions, like WHERE clauses, are evaluated pre-GROUP;
-                        * so we allow ungrouped vars in them.
+                        * Check for ungrouped variables passed to subplans.  Note we
+                        * do NOT do this for subplans in WHERE (or JOIN/ON); it's legal
+                        * there because WHERE is evaluated pre-GROUP.
+                        *
+                        * An interesting fine point: if subquery_planner reassigned a
+                        * HAVING qual into WHERE, then we will accept references to
+                        * ungrouped vars from subplans in the HAVING qual.  This is not
+                        * entirely consistent, but it doesn't seem particularly
+                        * harmful...
                         */
+                       check_subplans_for_ungrouped_vars(expr, parse);
                }
+       }
+
+       /* Replace uplevel vars with Param nodes */
+       if (PlannerQueryLevel > 1)
+               expr = SS_replace_correlation_vars(expr);
 
-               /* Replace uplevel vars with Param nodes */
-               if (PlannerQueryLevel > 1)
-                       j->quals = SS_replace_correlation_vars(j->quals);
+       return expr;
+}
+
+/*
+ * preprocess_qual_conditions
+ *             Recursively scan the query's jointree and do subquery_planner's
+ *             preprocessing work on each qual condition found therein.
+ */
+static void
+preprocess_qual_conditions(Query *parse, Node *jtnode)
+{
+       if (jtnode == NULL)
+               return;
+       if (IsA(jtnode, RangeTblRef))
+       {
+               /* nothing to do here */
+       }
+       else if (IsA(jtnode, FromExpr))
+       {
+               FromExpr   *f = (FromExpr *) jtnode;
+               List       *l;
+
+               foreach(l, f->fromlist)
+                       preprocess_qual_conditions(parse, lfirst(l));
+
+               f->quals = preprocess_expression(parse, f->quals, EXPRKIND_WHERE);
+       }
+       else if (IsA(jtnode, JoinExpr))
+       {
+               JoinExpr   *j = (JoinExpr *) jtnode;
+
+               preprocess_qual_conditions(parse, j->larg);
+               preprocess_qual_conditions(parse, j->rarg);
+
+               j->quals = preprocess_expression(parse, j->quals, EXPRKIND_WHERE);
        }
        else
-               elog(ERROR, "preprocess_join_conditions: unexpected node type %d",
+               elog(ERROR, "preprocess_qual_conditions: unexpected node type %d",
                         nodeTag(jtnode));
 }
 
@@ -309,7 +598,6 @@ union_planner(Query *parse,
                          double tuple_fraction)
 {
        List       *tlist = parse->targetList;
-       List       *rangetable = parse->rtable;
        Plan       *result_plan = (Plan *) NULL;
        AttrNumber *groupColIdx = NULL;
        List       *current_pathkeys = NIL;
@@ -342,7 +630,7 @@ union_planner(Query *parse,
                sort_pathkeys = make_pathkeys_for_sortclauses(parse->sortClause,
                                                                                                          tlist);
        }
-       else if (find_inheritable_rt_entry(rangetable,
+       else if (find_inheritable_rt_entry(parse->rtable,
                                                                           &rt_index, &inheritors))
        {
                List       *sub_tlist;
@@ -373,7 +661,7 @@ union_planner(Query *parse,
                                                                          parse->resultRelation,
                                                                          parse->rtable);
 
-               if (parse->rowMark != NULL)
+               if (parse->rowMarks)
                        elog(ERROR, "SELECT FOR UPDATE is not supported for inherit queries");
 
                /*
@@ -401,33 +689,35 @@ union_planner(Query *parse,
                                                                          parse->rtable);
 
                /*
-                * Add row-mark targets for UPDATE (should this be done in
-                * preprocess_targetlist?)
+                * Add TID targets for rels selected FOR UPDATE (should this be
+                * done in preprocess_targetlist?).  The executor uses the TID
+                * to know which rows to lock, much as for UPDATE or DELETE.
                 */
-               if (parse->rowMark != NULL)
+               if (parse->rowMarks)
                {
                        List       *l;
 
-                       foreach(l, parse->rowMark)
+                       foreach(l, parse->rowMarks)
                        {
-                               RowMark    *rowmark = (RowMark *) lfirst(l);
-                               TargetEntry *ctid;
+                               Index           rti = lfirsti(l);
+                               char       *resname;
                                Resdom     *resdom;
                                Var                *var;
-                               char       *resname;
-
-                               if (!(rowmark->info & ROW_MARK_FOR_UPDATE))
-                                       continue;
+                               TargetEntry *ctid;
 
                                resname = (char *) palloc(32);
-                               sprintf(resname, "ctid%u", rowmark->rti);
+                               sprintf(resname, "ctid%u", rti);
                                resdom = makeResdom(length(tlist) + 1,
                                                                        TIDOID,
                                                                        -1,
                                                                        resname,
                                                                        true);
 
-                               var = makeVar(rowmark->rti, -1, TIDOID, -1, 0);
+                               var = makeVar(rti,
+                                                         SelfItemPointerAttributeNumber,
+                                                         TIDOID,
+                                                         -1,
+                                                         0);
 
                                ctid = makeTargetEntry(resdom, (Node *) var);
                                tlist = lappend(tlist, ctid);
index d30636c..ad1b47a 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.65 2000/09/12 21:06:54 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.66 2000/09/29 18:21:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -102,6 +102,11 @@ set_plan_references(Plan *plan)
                        fix_expr_references(plan, (Node *) plan->targetlist);
                        fix_expr_references(plan, (Node *) plan->qual);
                        break;
+               case T_SubqueryScan:
+                       fix_expr_references(plan, (Node *) plan->targetlist);
+                       fix_expr_references(plan, (Node *) plan->qual);
+                       /* No need to recurse into the subplan, it's fixed already */
+                       break;
                case T_NestLoop:
                        set_join_references((Join *) plan);
                        fix_expr_references(plan, (Node *) plan->targetlist);
index 24a0aae..182d138 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.41 2000/09/12 21:06:54 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.42 2000/09/29 18:21:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -453,19 +453,6 @@ make_subplan(SubLink *slink)
        return result;
 }
 
-/* this oughta be merged with LispUnioni */
-
-static List *
-set_unioni(List *l1, List *l2)
-{
-       if (l1 == NULL)
-               return l2;
-       if (l2 == NULL)
-               return l1;
-
-       return nconc(l1, set_differencei(l2, l1));
-}
-
 /*
  * finalize_primnode: build lists of subplans and params appearing
  * in the given expression tree.  NOTE: items are added to lists passed in,
@@ -680,6 +667,7 @@ SS_finalize_plan(Plan *plan)
 
                case T_Agg:
                case T_SeqScan:
+               case T_SubqueryScan:
                case T_Material:
                case T_Sort:
                case T_Unique:
index a28e329..6016628 100644 (file)
@@ -85,19 +85,14 @@ transformKeySetQuery(Query *origNode)
        /*************************/
        /* Qualify where clause */
        /*************************/
-       if (!inspectOrNode((Expr *) origNode->qual) || TotalExpr < 9)
+       if (!inspectOrNode((Expr *) origNode->jointree->quals) || TotalExpr < 9)
                return;
 
        /* Copy essential elements into a union node */
-       while (((Expr *) origNode->qual)->opType == OR_EXPR)
+       while (((Expr *) origNode->jointree->quals)->opType == OR_EXPR)
        {
                Query      *unionNode = makeNode(Query);
-
-               /* Pull up Expr =  */
-               unionNode->qual = lsecond(((Expr *) origNode->qual)->args);
-
-               /* Pull up balance of tree      */
-               origNode->qual = lfirst(((Expr *) origNode->qual)->args);
+               List       *qualargs = ((Expr *) origNode->jointree->quals)->args;
 
                unionNode->commandType = origNode->commandType;
                unionNode->resultRelation = origNode->resultRelation;
@@ -107,9 +102,16 @@ transformKeySetQuery(Query *origNode)
                Node_Copy(origNode, unionNode, distinctClause);
                Node_Copy(origNode, unionNode, sortClause);
                Node_Copy(origNode, unionNode, rtable);
+               origNode->jointree->quals = NULL; /* avoid unnecessary copying */
                Node_Copy(origNode, unionNode, jointree);
                Node_Copy(origNode, unionNode, targetList);
 
+               /* Pull up Expr =  */
+               unionNode->jointree->quals = lsecond(qualargs);
+
+               /* Pull up balance of tree      */
+               origNode->jointree->quals = lfirst(qualargs);
+
                origNode->unionClause = lappend(origNode->unionClause, unionNode);
        }
        return;
index d284dd5..d3df886 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.52 2000/09/12 21:06:57 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.53 2000/09/29 18:21:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -411,7 +411,7 @@ find_all_inheritors(Oid parentrel)
                 * there can't be any cycles in the inheritance graph anyway.)
                 */
                currentchildren = set_differencei(currentchildren, examined_relids);
-               unexamined_relids = LispUnioni(unexamined_relids, currentchildren);
+               unexamined_relids = set_unioni(unexamined_relids, currentchildren);
        }
 
        return examined_relids;
index c616010..471cfdf 100644 (file)
@@ -4,7 +4,7 @@
 #    Makefile for optimizer/util
 #
 # IDENTIFICATION
-#    $Header: /cvsroot/pgsql/src/backend/optimizer/util/Makefile,v 1.13 2000/08/31 16:10:14 petere Exp $
+#    $Header: /cvsroot/pgsql/src/backend/optimizer/util/Makefile,v 1.14 2000/09/29 18:21:23 tgl Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -12,7 +12,7 @@ subdir = src/backend/optimizer/util
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = restrictinfo.o clauses.o indexnode.o plancat.o \
+OBJS = restrictinfo.o clauses.o plancat.o \
        joininfo.o pathnode.o relnode.o tlist.o var.o
 
 all: SUBSYS.o
index 9f371ff..06c67da 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.75 2000/09/25 18:14:55 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.76 2000/09/29 18:21:23 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -119,9 +119,9 @@ make_opclause(Oper *op, Var *leftop, Var *rightop)
        expr->opType = OP_EXPR;
        expr->oper = (Node *) op;
        if (rightop)
-               expr->args = lcons(leftop, lcons(rightop, NIL));
+               expr->args = makeList2(leftop, rightop);
        else
-               expr->args = lcons(leftop, NIL);
+               expr->args = makeList1(leftop);
        return expr;
 }
 
@@ -264,7 +264,7 @@ make_notclause(Expr *notclause)
        expr->typeOid = BOOLOID;
        expr->opType = NOT_EXPR;
        expr->oper = NULL;
-       expr->args = lcons(notclause, NIL);
+       expr->args = makeList1(notclause);
        return expr;
 }
 
@@ -303,7 +303,6 @@ and_clause(Node *clause)
  * make_andclause
  *
  * Create an 'and' clause given its arguments in a list.
- *
  */
 Expr *
 make_andclause(List *andclauses)
@@ -318,6 +317,23 @@ make_andclause(List *andclauses)
 }
 
 /*
+ * make_and_qual
+ *
+ * Variant of make_andclause for ANDing two qual conditions together.
+ * Qual conditions have the property that a NULL nodetree is interpreted
+ * as 'true'.
+ */
+Node *
+make_and_qual(Node *qual1, Node *qual2)
+{
+       if (qual1 == NULL)
+               return qual2;
+       if (qual2 == NULL)
+               return qual1;
+       return (Node *) make_andclause(makeList2(qual1, qual2));
+}
+
+/*
  * Sometimes (such as in the result of canonicalize_qual or the input of
  * ExecQual), we use lists of expression nodes with implicit AND semantics.
  *
@@ -356,7 +372,7 @@ make_ands_implicit(Expr *clause)
                         DatumGetBool(((Const *) clause)->constvalue))
                return NIL;                             /* constant TRUE input -> NIL list */
        else
-               return lcons(clause, NIL);
+               return makeList1(clause);
 }
 
 
@@ -676,49 +692,32 @@ is_pseudo_constant_clause(Node *clause)
        return false;
 }
 
-/*----------
+/*
  * pull_constant_clauses
  *             Scan through a list of qualifications and separate "constant" quals
  *             from those that are not.
  *
- * The input qual list is divided into three parts:
- *     * The function's return value is a list of all those quals that contain
- *       variable(s) of the current query level.  (These quals will become
- *       restrict and join quals.)
- *     * *noncachableQual receives a list of quals that have no Vars, yet
- *       cannot be treated as constants because they contain noncachable
- *       function calls.  (Example: WHERE random() < 0.5)
- *     * *constantQual receives a list of the remaining quals, which can be
- *       treated as constants for any one scan of the current query level.
- *       (They are really only pseudo-constant, since they may contain
- *       Params or outer-level Vars.)
- *----------
+ * Returns a list of the pseudo-constant clauses in constantQual and the
+ * remaining quals as the return value.
  */
 List *
-pull_constant_clauses(List *quals,
-                                         List **noncachableQual,
-                                         List **constantQual)
+pull_constant_clauses(List *quals, List **constantQual)
 {
-       List       *q;
-       List       *normqual = NIL;
-       List       *noncachequal = NIL;
        List       *constqual = NIL;
+       List       *restqual = NIL;
+       List       *q;
 
        foreach(q, quals)
        {
-               Node       *qual = (Node *) lfirst(q);
+               Node   *qual = (Node *) lfirst(q);
 
-               if (contain_var_clause(qual))
-                       normqual = lappend(normqual, qual);
-               else if (contain_noncachable_functions(qual))
-                       noncachequal = lappend(noncachequal, qual);
-               else
+               if (is_pseudo_constant_clause(qual))
                        constqual = lappend(constqual, qual);
+               else
+                       restqual = lappend(restqual, qual);
        }
-
-       *noncachableQual = noncachequal;
        *constantQual = constqual;
-       return normqual;
+       return restqual;
 }
 
 
@@ -1636,9 +1635,9 @@ 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.)  Also, RangeTblRef and
- * JoinExpr nodes are handled, so that qual expressions in a jointree can be
- * processed without additional code.
+ * chooses to process TargetEntry nodes specially.)  Also, RangeTblRef,
+ * FromExpr, 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).
@@ -1801,6 +1800,16 @@ expression_tree_walker(Node *node,
                        break;
                case T_TargetEntry:
                        return walker(((TargetEntry *) node)->expr, context);
+               case T_FromExpr:
+                       {
+                               FromExpr    *from = (FromExpr *) node;
+
+                               if (walker(from->fromlist, context))
+                                       return true;
+                               if (walker(from->quals, context))
+                                       return true;
+                       }
+                       break;
                case T_JoinExpr:
                        {
                                JoinExpr    *join = (JoinExpr *) node;
@@ -1844,14 +1853,12 @@ query_tree_walker(Query *query,
 
        if (walker((Node *) query->targetList, context))
                return true;
-       if (walker(query->qual, context))
+       if (walker((Node *) query->jointree, 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
+        * XXX for subselect-in-FROM, may need to examine rtable as well?
         */
        return false;
 }
@@ -2126,6 +2133,17 @@ expression_tree_mutator(Node *node,
                                return (Node *) newnode;
                        }
                        break;
+               case T_FromExpr:
+                       {
+                               FromExpr *from = (FromExpr *) node;
+                               FromExpr *newnode;
+
+                               FLATCOPY(newnode, from, FromExpr);
+                               MUTATE(newnode->fromlist, from->fromlist, List *);
+                               MUTATE(newnode->quals, from->quals, Node *);
+                               return (Node *) newnode;
+                       }
+                       break;
                case T_JoinExpr:
                        {
                                JoinExpr *join = (JoinExpr *) node;
diff --git a/src/backend/optimizer/util/indexnode.c b/src/backend/optimizer/util/indexnode.c
deleted file mode 100644 (file)
index e8d97aa..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * indexnode.c
- *       Routines to find all indices on a relation
- *
- * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/Attic/indexnode.c,v 1.22 2000/01/26 05:56:40 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres.h"
-
-#include "optimizer/pathnode.h"
-#include "optimizer/plancat.h"
-
-
-/*
- * find_relation_indices
- *       Returns a list of index nodes containing appropriate information for
- *       each (secondary) index defined on a relation.
- *
- */
-List *
-find_relation_indices(Query *root, RelOptInfo *rel)
-{
-       if (rel->indexed)
-               return find_secondary_indexes(root, lfirsti(rel->relids));
-       else
-               return NIL;
-}
index fc73bb2..737b4db 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.65 2000/09/12 21:06:58 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.66 2000/09/29 18:21:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,6 +16,7 @@
 
 #include "postgres.h"
 
+#include "nodes/plannodes.h"
 #include "optimizer/cost.h"
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
@@ -272,7 +273,6 @@ add_path(RelOptInfo *parent_rel, Path *new_path)
  * create_seqscan_path
  *       Creates a path corresponding to a sequential scan, returning the
  *       pathnode.
- *
  */
 Path *
 create_seqscan_path(RelOptInfo *rel)
@@ -343,8 +343,8 @@ create_index_path(Query *root,
         * We are making a pathnode for a single-scan indexscan; therefore,
         * both indexid and indexqual should be single-element lists.
         */
-       pathnode->indexid = lconsi(index->indexoid, NIL);
-       pathnode->indexqual = lcons(indexquals, NIL);
+       pathnode->indexid = makeListi1(index->indexoid);
+       pathnode->indexqual = makeList1(indexquals);
 
        pathnode->indexscandir = indexscandir;
 
@@ -391,6 +391,27 @@ create_tidscan_path(RelOptInfo *rel, List *tideval)
 }
 
 /*
+ * create_subqueryscan_path
+ *       Creates a path corresponding to a sequential scan of a subquery,
+ *       returning the pathnode.
+ */
+Path *
+create_subqueryscan_path(RelOptInfo *rel)
+{
+       Path       *pathnode = makeNode(Path);
+
+       pathnode->pathtype = T_SubqueryScan;
+       pathnode->parent = rel;
+       pathnode->pathkeys = NIL;       /* for now, assume unordered result */
+
+       /* just copy the subplan's cost estimates */
+       pathnode->startup_cost = rel->subplan->startup_cost;
+       pathnode->total_cost = rel->subplan->total_cost;
+
+       return pathnode;
+}
+
+/*
  * create_nestloop_path
  *       Creates a pathnode corresponding to a nestloop join between two
  *       relations.
index 750a463..0d32e82 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.60 2000/07/27 23:16:04 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.61 2000/09/29 18:21:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -25,7 +25,6 @@
 #include "catalog/pg_inherits.h"
 #include "catalog/pg_index.h"
 #include "optimizer/plancat.h"
-#include "parser/parsetree.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
 #include "utils/relcache.h"
 /*
  * relation_info -
  *       Retrieves catalog information for a given relation.
- *       Given the rangetable index of the relation, return the following info:
+ *       Given the Oid of the relation, return the following info:
  *                             whether the relation has secondary indices
  *                             number of pages
  *                             number of tuples
  */
 void
-relation_info(Query *root, Index relid,
+relation_info(Oid relationObjectId,
                          bool *hasindex, long *pages, double *tuples)
 {
-       Oid                     relationObjectId = getrelid(relid, root->rtable);
        HeapTuple       relationTuple;
        Form_pg_class relation;
 
@@ -69,19 +67,18 @@ relation_info(Query *root, Index relid,
 /*
  * find_secondary_indexes
  *       Creates a list of IndexOptInfo nodes containing information for each
- *       secondary index defined on the given relation.
+ *       secondary index defined on the specified relation.
  *
- * 'relid' is the RT index of the relation for which indices are being located
+ * 'relationObjectId' is the OID of the relation for which indices are wanted
  *
  * Returns a list of new IndexOptInfo nodes.
  */
 List *
-find_secondary_indexes(Query *root, Index relid)
+find_secondary_indexes(Oid relationObjectId)
 {
        List       *indexinfos = NIL;
        List       *indexoidlist,
                           *indexoidscan;
-       Oid                     indrelid = getrelid(relid, root->rtable);
        Relation        relation;
 
        /*
@@ -89,12 +86,12 @@ find_secondary_indexes(Query *root, Index relid)
         * a cached list of OID indexes for each relation.  So, get that list
         * and then use the syscache to obtain pg_index entries.
         */
-       relation = heap_open(indrelid, AccessShareLock);
+       relation = heap_open(relationObjectId, AccessShareLock);
        indexoidlist = RelationGetIndexList(relation);
 
        foreach(indexoidscan, indexoidlist)
        {
-               Oid             indexoid = lfirsti(indexoidscan);
+               Oid                     indexoid = lfirsti(indexoidscan);
                HeapTuple       indexTuple;
                Form_pg_index index;
                IndexOptInfo *info;
index 87e8759..86258b0 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.28 2000/09/12 21:06:58 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.29 2000/09/29 18:21:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -19,6 +19,7 @@
 #include "optimizer/pathnode.h"
 #include "optimizer/plancat.h"
 #include "optimizer/tlist.h"
+#include "parser/parsetree.h"
 
 
 static List *new_join_tlist(List *tlist, int first_resdomno);
@@ -44,6 +45,7 @@ get_base_rel(Query *root, int relid)
 {
        List       *baserels;
        RelOptInfo *rel;
+       Oid                     relationObjectId;
 
        foreach(baserels, root->base_rel_list)
        {
@@ -59,7 +61,7 @@ get_base_rel(Query *root, int relid)
 
        /* No existing RelOptInfo for this base rel, so make a new one */
        rel = makeNode(RelOptInfo);
-       rel->relids = lconsi(relid, NIL);
+       rel->relids = makeListi1(relid);
        rel->rows = 0;
        rel->width = 0;
        rel->targetlist = NIL;
@@ -67,18 +69,31 @@ get_base_rel(Query *root, int relid)
        rel->cheapest_startup_path = NULL;
        rel->cheapest_total_path = NULL;
        rel->pruneable = true;
+       rel->issubquery = false;
        rel->indexed = false;
        rel->pages = 0;
        rel->tuples = 0;
+       rel->subplan = NULL;
        rel->baserestrictinfo = NIL;
        rel->baserestrictcost = 0;
        rel->outerjoinset = NIL;
        rel->joininfo = NIL;
        rel->innerjoin = NIL;
 
-       /* Retrieve relation statistics from the system catalogs. */
-       relation_info(root, relid,
-                                 &rel->indexed, &rel->pages, &rel->tuples);
+       /* Check rtable to see if it's a plain relation or a subquery */
+       relationObjectId = getrelid(relid, root->rtable);
+
+       if (relationObjectId != InvalidOid)
+       {
+               /* Plain relation --- retrieve statistics from the system catalogs */
+               relation_info(relationObjectId,
+                                         &rel->indexed, &rel->pages, &rel->tuples);
+       }
+       else
+       {
+               /* subquery --- mark it as such for later processing */
+               rel->issubquery = true;
+       }
 
        root->base_rel_list = lcons(rel, root->base_rel_list);
 
@@ -174,9 +189,11 @@ get_join_rel(Query *root,
        joinrel->cheapest_startup_path = NULL;
        joinrel->cheapest_total_path = NULL;
        joinrel->pruneable = true;
+       joinrel->issubquery = false;
        joinrel->indexed = false;
        joinrel->pages = 0;
        joinrel->tuples = 0;
+       joinrel->subplan = NULL;
        joinrel->baserestrictinfo = NIL;
        joinrel->baserestrictcost = 0;
        joinrel->outerjoinset = NIL;
@@ -310,7 +327,7 @@ build_joinrel_restrictlist(RelOptInfo *joinrel,
         * We must eliminate duplicates, since we will see the same clauses
         * arriving from both input relations...
         */
-       return LispUnion(subbuild_joinrel_restrictlist(joinrel,
+       return set_union(subbuild_joinrel_restrictlist(joinrel,
                                                                                                   outer_rel->joininfo),
                                         subbuild_joinrel_restrictlist(joinrel,
                                                                                                   inner_rel->joininfo));
@@ -396,7 +413,7 @@ subbuild_joinrel_joinlist(RelOptInfo *joinrel,
 
                        new_joininfo = find_joininfo_node(joinrel, new_unjoined_relids);
                        new_joininfo->jinfo_restrictinfo =
-                               LispUnion(new_joininfo->jinfo_restrictinfo,
+                               set_union(new_joininfo->jinfo_restrictinfo,
                                                  joininfo->jinfo_restrictinfo);
                }
        }
index adbfd88..318c789 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.11 2000/09/12 21:06:58 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.12 2000/09/29 18:21:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -59,7 +59,7 @@ get_actual_clauses(List *restrictinfo_list)
  * get_actual_join_clauses
  *
  * Extract clauses from 'restrictinfo_list', separating those that
- * came from JOIN/ON conditions from those that didn't.
+ * syntactically match the join level from those that were pushed down.
  */
 void
 get_actual_join_clauses(List *restrictinfo_list,
@@ -74,9 +74,9 @@ get_actual_join_clauses(List *restrictinfo_list,
        {
                RestrictInfo *clause = (RestrictInfo *) lfirst(temp);
 
-               if (clause->isjoinqual)
-                       *joinquals = lappend(*joinquals, clause->clause);
-               else
+               if (clause->ispusheddown)
                        *otherquals = lappend(*otherquals, clause->clause);
+               else
+                       *joinquals = lappend(*joinquals, clause->clause);
        }
 }
index 888f8f8..169075c 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.158 2000/09/25 12:58:46 momjian Exp $
+ *     $Id: analyze.c,v 1.159 2000/09/29 18:21:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -56,6 +56,7 @@ static void transformColumnType(ParseState *pstate, ColumnDef *column);
 static void transformFkeyCheckAttrs(FkConstraint *fkconstraint);
 
 static void release_pstate_resources(ParseState *pstate);
+static FromExpr *makeFromExpr(List *fromlist, Node *quals);
 
 /* kluge to return extra info from transformCreateStmt() */
 static List *extras_before;
@@ -289,6 +290,7 @@ static Query *
 transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
 {
        Query      *qry = makeNode(Query);
+       Node       *qual;
 
        qry->commandType = CMD_DELETE;
 
@@ -299,17 +301,17 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
        qry->distinctClause = NIL;
 
        /* fix where clause */
-       qry->qual = transformWhereClause(pstate, stmt->whereClause);
+       qual = transformWhereClause(pstate, stmt->whereClause);
 
-       /* done building the rtable */
+       /* done building the range table and jointree */
        qry->rtable = pstate->p_rtable;
-       qry->jointree = pstate->p_jointree;
+       qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
        qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
 
        qry->hasSubLinks = pstate->p_hasSubLinks;
        qry->hasAggs = pstate->p_hasAggs;
        if (pstate->p_hasAggs)
-               parseCheckAggregates(pstate, qry);
+               parseCheckAggregates(pstate, qry, qual);
 
        return (Query *) qry;
 }
@@ -322,6 +324,7 @@ static Query *
 transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 {
        Query      *qry = makeNode(Query);
+       Node       *qual;
        List       *icolumns;
        List       *attrnos;
        List       *attnos;
@@ -348,7 +351,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 
        qry->targetList = transformTargetList(pstate, stmt->targetList);
 
-       qry->qual = transformWhereClause(pstate, stmt->whereClause);
+       qual = transformWhereClause(pstate, stmt->whereClause);
 
        /*
         * Initial processing of HAVING clause is just like WHERE clause.
@@ -371,7 +374,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
        qry->hasSubLinks = pstate->p_hasSubLinks;
        qry->hasAggs = pstate->p_hasAggs;
        if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
-               parseCheckAggregates(pstate, qry);
+               parseCheckAggregates(pstate, qry, qual);
 
        /*
         * The INSERT INTO ... SELECT ... could have a UNION in child, so
@@ -393,13 +396,13 @@ 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.)  Note that the INSERT target is NOT added to the
-        * join tree, since we don't want to join over it.
+        * joinlist, since we don't want to join over it.
         */
        setTargetTable(pstate, stmt->relname, false, false);
 
-       /* now the range table will not change */
+       /* now the range table and jointree will not change */
        qry->rtable = pstate->p_rtable;
-       qry->jointree = pstate->p_jointree;
+       qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
        qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
 
        /* Prepare to assign non-conflicting resnos to resjunk attributes */
@@ -715,7 +718,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
                                        snamenode->val.val.str = qstring;
                                        funccallnode = makeNode(FuncCall);
                                        funccallnode->funcname = "nextval";
-                                       funccallnode->args = lcons(snamenode, NIL);
+                                       funccallnode->args = makeList1(snamenode);
                                        funccallnode->agg_star = false;
                                        funccallnode->agg_distinct = false;
 
@@ -748,7 +751,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
                                        elog(NOTICE, "CREATE TABLE will create implicit sequence '%s' for SERIAL column '%s.%s'",
                                          sequence->seqname, stmt->relname, column->colname);
 
-                                       blist = lcons(sequence, NIL);
+                                       blist = makeList1(sequence);
                                }
 
                                /* Process column constraints, if any... */
@@ -776,7 +779,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
                                                id->isRel = false;
 
                                                fkconstraint = (FkConstraint *) constraint;
-                                               fkconstraint->fk_attrs = lappend(NIL, id);
+                                               fkconstraint->fk_attrs = makeList1(id);
 
                                                fkconstraints = lappend(fkconstraints, constraint);
                                                continue;
@@ -815,7 +818,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
                                                        {
                                                                key = makeNode(Ident);
                                                                key->name = pstrdup(column->colname);
-                                                               constraint->keys = lcons(key, NIL);
+                                                               constraint->keys = makeList1(key);
                                                        }
                                                        dlist = lappend(dlist, constraint);
                                                        break;
@@ -827,7 +830,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
                                                        {
                                                                key = makeNode(Ident);
                                                                key->name = pstrdup(column->colname);
-                                                               constraint->keys = lcons(key, NIL);
+                                                               constraint->keys = makeList1(key);
                                                        }
                                                        dlist = lappend(dlist, constraint);
                                                        break;
@@ -1453,8 +1456,11 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
        newrte = addRangeTableEntry(pstate, stmt->object->relname,
                                                                makeAttr("*NEW*", NULL),
                                                                false, true);
+       /* Must override addRangeTableEntry's default access-check flags */
+       oldrte->checkForRead = false;
+       newrte->checkForRead = false;
        /*
-        * They must be in the jointree too for lookup purposes, but only add
+        * They must be in the joinlist 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.
@@ -1464,17 +1470,17 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
        switch (stmt->event)
        {
                case CMD_SELECT:
-                       addRTEtoJoinTree(pstate, oldrte);
+                       addRTEtoJoinList(pstate, oldrte);
                        break;
                case CMD_UPDATE:
-                       addRTEtoJoinTree(pstate, oldrte);
-                       addRTEtoJoinTree(pstate, newrte);
+                       addRTEtoJoinList(pstate, oldrte);
+                       addRTEtoJoinList(pstate, newrte);
                        break;
                case CMD_INSERT:
-                       addRTEtoJoinTree(pstate, newrte);
+                       addRTEtoJoinList(pstate, newrte);
                        break;
                case CMD_DELETE:
-                       addRTEtoJoinTree(pstate, oldrte);
+                       addRTEtoJoinList(pstate, oldrte);
                        break;
                default:
                        elog(ERROR, "transformRuleStmt: unexpected event type %d",
@@ -1504,9 +1510,9 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
 
                nothing_qry->commandType = CMD_NOTHING;
                nothing_qry->rtable = pstate->p_rtable;
-               nothing_qry->jointree = NIL; /* no join actually wanted */
+               nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */
 
-               stmt->actions = lappend(NIL, nothing_qry);
+               stmt->actions = makeList1(nothing_qry);
        }
        else
        {
@@ -1526,7 +1532,7 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
                         * 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
+                        * actions.  We don't need to add them to the joinlist for
                         * qualified-name lookup, either (see qualifiedNameToVar()).
                         */
                        oldrte = addRangeTableEntry(sub_pstate, stmt->object->relname,
@@ -1535,6 +1541,8 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
                        newrte = addRangeTableEntry(sub_pstate, stmt->object->relname,
                                                                                makeAttr("*NEW*", NULL),
                                                                                false, false);
+                       oldrte->checkForRead = false;
+                       newrte->checkForRead = false;
 
                        /* Transform the rule action statement */
                        sub_qry = transformStmt(sub_pstate, lfirst(actions));
@@ -1581,8 +1589,8 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
                         */
                        if (has_old)
                        {
-                               addRTEtoJoinTree(sub_pstate, oldrte);
-                               sub_qry->jointree = sub_pstate->p_jointree;
+                               addRTEtoJoinList(sub_pstate, oldrte);
+                               sub_qry->jointree->fromlist = sub_pstate->p_joinlist;
                        }
 
                        lfirst(actions) = sub_qry;
@@ -1605,6 +1613,7 @@ static Query *
 transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 {
        Query      *qry = makeNode(Query);
+       Node       *qual;
 
        qry->commandType = CMD_SELECT;
 
@@ -1617,7 +1626,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 
        qry->targetList = transformTargetList(pstate, stmt->targetList);
 
-       qry->qual = transformWhereClause(pstate, stmt->whereClause);
+       qual = transformWhereClause(pstate, stmt->whereClause);
 
        /*
         * Initial processing of HAVING clause is just like WHERE clause.
@@ -1641,7 +1650,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
        qry->hasSubLinks = pstate->p_hasSubLinks;
        qry->hasAggs = pstate->p_hasAggs;
        if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
-               parseCheckAggregates(pstate, qry);
+               parseCheckAggregates(pstate, qry, qual);
 
        /*
         * The INSERT INTO ... SELECT ... could have a UNION in child, so
@@ -1657,7 +1666,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
        qry->intersectClause = stmt->intersectClause;
 
        qry->rtable = pstate->p_rtable;
-       qry->jointree = pstate->p_jointree;
+       qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
 
        if (stmt->forUpdate != NULL)
                transformForUpdate(qry, stmt->forUpdate);
@@ -1674,6 +1683,7 @@ static Query *
 transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
 {
        Query      *qry = makeNode(Query);
+       Node       *qual;
        List       *origTargetList;
        List       *tl;
 
@@ -1683,22 +1693,27 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
        /*
         * the FROM clause is non-standard SQL syntax. We used to be able to
         * do this with REPLACE in POSTQUEL so we keep the feature.
+        *
+        * Note: it's critical here that we process FROM before adding the
+        * target table to the rtable --- otherwise, if the target is also
+        * used in FROM, we'd fail to notice that it should be marked
+        * checkForRead as well as checkForWrite.  See setTargetTable().
         */
        makeRangeTable(pstate, stmt->fromClause);
        setTargetTable(pstate, stmt->relname, stmt->inh, true);
 
        qry->targetList = transformTargetList(pstate, stmt->targetList);
 
-       qry->qual = transformWhereClause(pstate, stmt->whereClause);
+       qual = transformWhereClause(pstate, stmt->whereClause);
 
        qry->rtable = pstate->p_rtable;
-       qry->jointree = pstate->p_jointree;
+       qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
        qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
 
        qry->hasSubLinks = pstate->p_hasSubLinks;
        qry->hasAggs = pstate->p_hasAggs;
        if (pstate->p_hasAggs)
-               parseCheckAggregates(pstate, qry);
+               parseCheckAggregates(pstate, qry, qual);
 
        /*
         * Now we are done with SELECT-like processing, and can get on with
@@ -2083,7 +2098,7 @@ A_Expr_to_Expr(Node *ptr, bool *intersect_present)
 
                                                        expr->typeOid = BOOLOID;
                                                        expr->opType = OR_EXPR;
-                                                       expr->args = makeList(lexpr, rexpr, -1);
+                                                       expr->args = makeList2(lexpr, rexpr);
                                                        result = (Node *) expr;
                                                        break;
                                                }
@@ -2095,7 +2110,7 @@ A_Expr_to_Expr(Node *ptr, bool *intersect_present)
 
                                                        expr->typeOid = BOOLOID;
                                                        expr->opType = AND_EXPR;
-                                                       expr->args = makeList(lexpr, rexpr, -1);
+                                                       expr->args = makeList2(lexpr, rexpr);
                                                        result = (Node *) expr;
                                                        break;
                                                }
@@ -2106,7 +2121,7 @@ A_Expr_to_Expr(Node *ptr, bool *intersect_present)
 
                                                        expr->typeOid = BOOLOID;
                                                        expr->opType = NOT_EXPR;
-                                                       expr->args = makeList(rexpr, -1);
+                                                       expr->args = makeList1(rexpr);
                                                        result = (Node *) expr;
                                                        break;
                                                }
@@ -2122,7 +2137,7 @@ A_Expr_to_Expr(Node *ptr, bool *intersect_present)
 void
 CheckSelectForUpdate(Query *qry)
 {
-       if (qry->unionClause != NULL)
+       if (qry->unionClause || qry->intersectClause)
                elog(ERROR, "SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT clause");
        if (qry->distinctClause != NIL)
                elog(ERROR, "SELECT FOR UPDATE is not allowed with DISTINCT clause");
@@ -2135,61 +2150,70 @@ CheckSelectForUpdate(Query *qry)
 static void
 transformForUpdate(Query *qry, List *forUpdate)
 {
-       List       *rowMark = NULL;
-       RowMark    *newrm;
+       List       *rowMarks = NIL;
        List       *l;
+       List       *rt;
        Index           i;
 
        CheckSelectForUpdate(qry);
 
-       if (lfirst(forUpdate) == NULL)          /* all tables */
+       if (lfirst(forUpdate) == NULL)
        {
-               i = 1;
-               foreach(l, qry->rtable)
+               /* all tables used in query */
+               i = 0;
+               foreach(rt, qry->rtable)
                {
-                       newrm = makeNode(RowMark);
-                       newrm->rti = i++;
-                       newrm->info = ROW_MARK_FOR_UPDATE | ROW_ACL_FOR_UPDATE;
-                       rowMark = lappend(rowMark, newrm);
+                       RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
+
+                       ++i;
+                       if (rte->subquery)
+                       {
+                               /* FOR UPDATE of subquery is propagated to subquery's rels */
+                               transformForUpdate(rte->subquery, makeList1(NULL));
+                       }
+                       else
+                       {
+                               rowMarks = lappendi(rowMarks, i);
+                               rte->checkForWrite = true;
+                       }
                }
-               qry->rowMark = nconc(qry->rowMark, rowMark);
-               return;
        }
-
-       foreach(l, forUpdate)
+       else
        {
-               char       *relname = lfirst(l);
-               List       *l2;
-
-               i = 1;
-               foreach(l2, qry->rtable)
+               /* just the named tables */
+               foreach(l, forUpdate)
                {
-                       if (strcmp(((RangeTblEntry *) lfirst(l2))->eref->relname, relname) == 0)
+                       char       *relname = lfirst(l);
+
+                       i = 0;
+                       foreach(rt, qry->rtable)
                        {
-                               List       *l3;
+                               RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
 
-                               foreach(l3, rowMark)
+                               ++i;
+                               if (strcmp(rte->eref->relname, relname) == 0)
                                {
-                                       if (((RowMark *) lfirst(l3))->rti == i)         /* duplicate */
-                                               break;
-                               }
-                               if (l3 == NULL)
-                               {
-                                       newrm = makeNode(RowMark);
-                                       newrm->rti = i;
-                                       newrm->info = ROW_MARK_FOR_UPDATE | ROW_ACL_FOR_UPDATE;
-                                       rowMark = lappend(rowMark, newrm);
+                                       if (rte->subquery)
+                                       {
+                                               /* propagate to subquery */
+                                               transformForUpdate(rte->subquery, makeList1(NULL));
+                                       }
+                                       else
+                                       {
+                                               if (!intMember(i, rowMarks)) /* avoid duplicates */
+                                                       rowMarks = lappendi(rowMarks, i);
+                                               rte->checkForWrite = true;
+                                       }
+                                       break;
                                }
-                               break;
                        }
-                       i++;
+                       if (rt == NIL)
+                               elog(ERROR, "FOR UPDATE: relation \"%s\" not found in FROM clause",
+                                        relname);
                }
-               if (l2 == NULL)
-                       elog(ERROR, "FOR UPDATE: relation \"%s\" not found in FROM clause",
-                                relname);
        }
 
-       qry->rowMark = rowMark;
+       qry->rowMarks = rowMarks;
 }
 
 
@@ -2452,6 +2476,17 @@ transformConstraintAttrs(List *constraintList)
        }
 }
 
+/* Build a FromExpr node */
+static FromExpr *
+makeFromExpr(List *fromlist, Node *quals)
+{
+       FromExpr *f = makeNode(FromExpr);
+
+       f->fromlist = fromlist;
+       f->quals = quals;
+       return f;
+}
+
 /*
  * Special handling of type definition for a column
  */
index 93aa912..31aea15 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.192 2000/09/25 18:38:39 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.193 2000/09/29 18:21:36 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -403,7 +403,7 @@ stmtmulti:  stmtmulti ';' stmt
                                }
                | stmt
                                { if ($1 != (Node *)NULL)
-                                       $$ = lcons($1,NIL);
+                                       $$ = makeList1($1);
                                  else
                                        $$ = NIL;
                                }
@@ -575,11 +575,11 @@ user_createuser_clause:  CREATEUSER                               { $$ = +1; }
 
 user_list:  user_list ',' UserId
                                {
-                                       $$ = lcons((void*)makeString($3), $1);
+                                       $$ = lappend($1, makeString($3));
                                }
                        | UserId
                                {
-                                       $$ = lcons((void*)makeString($1), NIL);
+                                       $$ = makeList1(makeString($1));
                                }
                ;
 
@@ -721,7 +721,7 @@ SessionList:  SessionList ',' SessionClause
                                }
                | SessionClause
                                {
-                                       $$ = lcons($1, NIL);
+                                       $$ = makeList1($1);
                                }
                ;
 
@@ -937,7 +937,7 @@ constraints_set_list:       ALL
 
 constraints_set_namelist:      IDENT
                                {
-                                       $$ = lappend(NIL, $1);
+                                       $$ = makeList1($1);
                                }
                | constraints_set_namelist ',' IDENT
                                {
@@ -1193,11 +1193,11 @@ OptTableElementList:  OptTableElementList ',' OptTableElement
                        | OptTableElement
                                {
                                        if ($1 != NULL)
-                                               $$ = lcons($1, NIL);
+                                               $$ = makeList1($1);
                                        else
-                                               $$ = NULL;
+                                               $$ = NIL;
                                }
-                       | /*EMPTY*/                                                     { $$ = NULL; }
+                       | /*EMPTY*/                                                     { $$ = NIL; }
                ;
 
 OptTableElement:  columnDef                                            { $$ = $1; }
@@ -1551,7 +1551,7 @@ OptCreateAs:  '(' CreateAsList ')'                                { $$ = $2; }
                ;
 
 CreateAsList:  CreateAsList ',' CreateAsElement        { $$ = lappend($1, $3); }
-                       | CreateAsElement                                       { $$ = lcons($1, NIL); }
+                       | CreateAsElement                                       { $$ = makeList1($1); }
                ;
 
 CreateAsElement:  ColId
@@ -1783,7 +1783,7 @@ TriggerForType:  ROW                                              { $$ = TRUE; }
                ;
 
 TriggerFuncArgs:  TriggerFuncArg
-                               { $$ = lcons($1, NIL); }
+                               { $$ = makeList1($1); }
                        | TriggerFuncArgs ',' TriggerFuncArg
                                { $$ = lappend($1, $3); }
                        | /*EMPTY*/
@@ -1899,7 +1899,7 @@ def_name:  PROCEDURE                                              { $$ = "procedure"; }
 definition:  '(' def_list ')'                          { $$ = $2; }
                ;
 
-def_list:  def_elem                                                    { $$ = lcons($1, NIL); }
+def_list:  def_elem                                                    { $$ = makeList1($1); }
                | def_list ',' def_elem                         { $$ = lappend($1, $3); }
                ;
 
@@ -2361,11 +2361,11 @@ access_method_clause:  USING access_method              { $$ = $2; }
                ;
 
 index_params:  index_list                                              { $$ = $1; }
-               | func_index                                                    { $$ = lcons($1,NIL); }
+               | func_index                                                    { $$ = makeList1($1); }
                ;
 
 index_list:  index_list ',' index_elem                 { $$ = lappend($1, $3); }
-               | index_elem                                                    { $$ = lcons($1, NIL); }
+               | index_elem                                                    { $$ = makeList1($1); }
                ;
 
 func_index:  func_name '(' name_list ')' opt_class
@@ -2486,9 +2486,9 @@ func_args:  '(' func_args_list ')'                                { $$ = $2; }
                ;
 
 func_args_list:  func_arg
-                               {       $$ = lcons(makeString($1->name),NIL); }
+                               {       $$ = makeList1(makeString($1->name)); }
                | func_args_list ',' func_arg
-                               {       $$ = lappend($1,makeString($3->name)); }
+                               {       $$ = lappend($1, makeString($3->name)); }
                ;
 
 /* Would be nice to use the full Typename production for these fields,
@@ -2539,9 +2539,9 @@ opt_arg:  IN
                ;
 
 func_as: Sconst
-                               {   $$ = lcons(makeString($1),NIL); }
+                               {   $$ = makeList1(makeString($1)); }
                | Sconst ',' Sconst
-                               {       $$ = lappend(lcons(makeString($1),NIL), makeString($3)); }
+                               {       $$ = makeList2(makeString($1), makeString($3)); }
                ;
 
 func_return:  SimpleTypename
@@ -2631,11 +2631,11 @@ oper_argtypes:  name
                                   elog(ERROR,"parser: argument type missing (use NONE for unary operators)");
                                }
                | name ',' name
-                               { $$ = makeList(makeString($1), makeString($3), -1); }
+                               { $$ = makeList2(makeString($1), makeString($3)); }
                | NONE ',' name                 /* left unary */
-                               { $$ = makeList(NULL, makeString($3), -1); }
+                               { $$ = makeList2(NULL, makeString($3)); }
                | name ',' NONE                 /* right unary */
-                               { $$ = makeList(makeString($1), NULL, -1); }
+                               { $$ = makeList2(makeString($1), NULL); }
                ;
 
 
@@ -2724,22 +2724,22 @@ RuleStmt:  CREATE RULE name AS
                ;
 
 RuleActionList:  NOTHING                               { $$ = NIL; }
-               | SelectStmt                                    { $$ = lcons($1, NIL); }
-               | RuleActionStmt                                { $$ = lcons($1, NIL); }
+               | SelectStmt                                    { $$ = makeList1($1); }
+               | RuleActionStmt                                { $$ = makeList1($1); }
                | '[' RuleActionMulti ']'               { $$ = $2; }
                | '(' RuleActionMulti ')'               { $$ = $2; } 
                ;
 
 /* the thrashing around here is to discard "empty" statements... */
 RuleActionMulti:  RuleActionMulti ';' RuleActionStmtOrEmpty
-                               { if ($3 != (Node *)NULL)
+                               { if ($3 != (Node *) NULL)
                                        $$ = lappend($1, $3);
                                  else
                                        $$ = $1;
                                }
                | RuleActionStmtOrEmpty
-                               { if ($1 != (Node *)NULL)
-                                       $$ = lcons($1,NIL);
+                               { if ($1 != (Node *) NULL)
+                                       $$ = makeList1($1);
                                  else
                                        $$ = NIL;
                                }
@@ -2761,7 +2761,7 @@ event_object:  relation_name '.' attr_name
                                        $$ = makeNode(Attr);
                                        $$->relname = $1;
                                        $$->paramNo = NULL;
-                                       $$->attrs = lcons(makeString($3), NIL);
+                                       $$->attrs = makeList1(makeString($3));
                                        $$->indirection = NIL;
                                }
                | relation_name
@@ -2910,10 +2910,8 @@ ViewStmt:  CREATE VIEW name opt_column_list AS SelectStmt
                                        n->viewname = $3;
                                        n->aliases = $4;
                                        n->query = (Query *)$6;
-                                       if (((SelectStmt *)n->query)->sortClause != NULL)
-                                               elog(ERROR,"ORDER BY and DISTINCT on views are not implemented");
                                        if (((SelectStmt *)n->query)->unionClause != NULL)
-                                               elog(ERROR,"UNION on views is not implemented");
+                                               elog(ERROR,"UNION in views is not implemented");
                                        if (((SelectStmt *)n->query)->forUpdate != NULL)
                                                elog(ERROR, "SELECT FOR UPDATE is not allowed in CREATE VIEW");
                                        $$ = (Node *)n;
@@ -3092,9 +3090,9 @@ opt_va_list:  '(' va_list ')'                                     { $$ = $2; }
                ;
 
 va_list:  name
-                               { $$=lcons($1,NIL); }
+                               { $$ = makeList1($1); }
                | va_list ',' name
-                               { $$=lappend($1,$3); }
+                               { $$ = lappend($1, $3); }
                ;
 
 
@@ -3240,7 +3238,7 @@ opt_column_list:  '(' columnList ')'                      { $$ = $2; }
 columnList:  columnList ',' columnElem
                                { $$ = lappend($1, $3); }
                | columnElem
-                               { $$ = lcons($1, NIL); }
+                               { $$ = makeList1($1); }
                ;
 
 columnElem:  ColId opt_indirection
@@ -3364,7 +3362,7 @@ opt_cursor:  BINARY                                               { $$ = TRUE; }
  *****************************************************************************/
 
 /* A complete SELECT statement looks like this.  Note sort, for_update,
- * and limit clauses can only appear once, not in each subselect.
+ * and limit clauses can only appear once, not in each set operation.
  * 
  * The rule returns a SelectStmt Node having the set operations attached to 
  * unionClause and intersectClause (NIL if no set operations were present)
@@ -3584,7 +3582,7 @@ opt_all:  ALL                                                                     { $$ = TRUE; }
 /* We use (NIL) as a placeholder to indicate that all target expressions
  * should be placed in the DISTINCT list during parsetree analysis.
  */
-opt_distinct:  DISTINCT                                                        { $$ = lcons(NIL,NIL); }
+opt_distinct:  DISTINCT                                                        { $$ = makeList1(NIL); }
                | DISTINCT ON '(' expr_list ')'                 { $$ = $4; }
                | ALL                                                                   { $$ = NIL; }
                | /*EMPTY*/                                                             { $$ = NIL; }
@@ -3594,7 +3592,7 @@ sort_clause:  ORDER BY sortby_list                                { $$ = $3; }
                | /*EMPTY*/                                                             { $$ = NIL; }
                ;
 
-sortby_list:  sortby                                                   { $$ = lcons($1, NIL); }
+sortby_list:  sortby                                                   { $$ = makeList1($1); }
                | sortby_list ',' sortby                                { $$ = lappend($1, $3); }
                ;
 
@@ -3614,17 +3612,17 @@ OptUseOp:  USING all_Op                                                 { $$ = $2; }
 
 
 opt_select_limit:      LIMIT select_limit_value ',' select_offset_value
-                       { $$ = lappend(lappend(NIL, $4), $2); }
+                       { $$ = makeList2($4, $2); }
                | LIMIT select_limit_value OFFSET select_offset_value
-                       { $$ = lappend(lappend(NIL, $4), $2); }
+                       { $$ = makeList2($4, $2); }
                | LIMIT select_limit_value
-                       { $$ = lappend(lappend(NIL, NULL), $2); }
+                       { $$ = makeList2(NULL, $2); }
                | OFFSET select_offset_value LIMIT select_limit_value
-                       { $$ = lappend(lappend(NIL, $2), $4); }
+                       { $$ = makeList2($2, $4); }
                | OFFSET select_offset_value
-                       { $$ = lappend(lappend(NIL, $2), NULL); }
+                       { $$ = makeList2($2, NULL); }
                | /* EMPTY */
-                       { $$ = lappend(lappend(NIL, NULL), NULL); }
+                       { $$ = makeList2(NULL, NULL); }
                ;
 
 select_limit_value:  Iconst
@@ -3704,9 +3702,9 @@ opt_inh_star:  '*'                                                                { $$ = TRUE; }
 relation_name_list:  name_list;
 
 name_list:  name
-                               {       $$ = lcons(makeString($1),NIL); }
+                               {       $$ = makeList1(makeString($1)); }
                | name_list ',' name
-                               {       $$ = lappend($1,makeString($3)); }
+                               {       $$ = lappend($1, makeString($3)); }
                ;
 
 group_clause:  GROUP BY expr_list                              { $$ = $3; }
@@ -3726,7 +3724,7 @@ for_update_clause:  FOR UPDATE update_list                { $$ = $3; }
                ;
 
 update_list:  OF va_list                                               { $$ = $2; }
-               | /* EMPTY */                                                   { $$ = lcons(NULL, NULL); }
+               | /* EMPTY */                                                   { $$ = makeList1(NULL); }
                ;
 
 /*****************************************************************************
@@ -3742,7 +3740,7 @@ from_clause:  FROM from_list                                      { $$ = $2; }
                ;
 
 from_list:  from_list ',' table_ref                            { $$ = lappend($1, $3); }
-               | table_ref                                                             { $$ = lcons($1, NIL); }
+               | table_ref                                                             { $$ = makeList1($1); }
                ;
 
 /*
@@ -4001,10 +3999,10 @@ Typename:  SimpleTypename opt_array_bounds
                                }
                ;
 
-opt_array_bounds:      '[' ']' opt_array_bounds
-                               {  $$ = lcons(makeInteger(-1), $3); }
-               | '[' Iconst ']' opt_array_bounds
-                               {  $$ = lcons(makeInteger($2), $4); }
+opt_array_bounds:      opt_array_bounds '[' ']'
+                               {  $$ = lappend($1, makeInteger(-1)); }
+               | opt_array_bounds '[' Iconst ']'
+                               {  $$ = lappend($1, makeInteger($3)); }
                | /*EMPTY*/
                                {  $$ = NIL; }
                ;
@@ -4296,7 +4294,7 @@ opt_timezone:  WITH TIME ZONE                                     { $$ = TRUE; }
                | /*EMPTY*/                                                             { $$ = FALSE; }
                ;
 
-opt_interval:  datetime                                                        { $$ = lcons($1, NIL); }
+opt_interval:  datetime                                                        { $$ = makeList1($1); }
                | YEAR_P TO MONTH_P                                             { $$ = NIL; }
                | DAY_P TO HOUR_P                                               { $$ = NIL; }
                | DAY_P TO MINUTE_P                                             { $$ = NIL; }
@@ -4403,7 +4401,7 @@ row_list:  row_list ',' a_expr
                                }
                | a_expr
                                {
-                                       $$ = lcons($1, NIL);
+                                       $$ = makeList1($1);
                                }
                ;
 
@@ -4524,7 +4522,7 @@ a_expr:  c_expr
                                {
                                        FuncCall *n = makeNode(FuncCall);
                                        n->funcname = "like_escape";
-                                       n->args = makeList($3, $5, -1);
+                                       n->args = makeList2($3, $5);
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
                                        $$ = makeA_Expr(OP, "~~", $1, (Node *) n);
@@ -4535,7 +4533,7 @@ a_expr:  c_expr
                                {
                                        FuncCall *n = makeNode(FuncCall);
                                        n->funcname = "like_escape";
-                                       n->args = makeList($4, $6, -1);
+                                       n->args = makeList2($4, $6);
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
                                        $$ = makeA_Expr(OP, "!~~", $1, (Node *) n);
@@ -4546,7 +4544,7 @@ a_expr:  c_expr
                                {
                                        FuncCall *n = makeNode(FuncCall);
                                        n->funcname = "like_escape";
-                                       n->args = makeList($3, $5, -1);
+                                       n->args = makeList2($3, $5);
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
                                        $$ = makeA_Expr(OP, "~~*", $1, (Node *) n);
@@ -4557,7 +4555,7 @@ a_expr:  c_expr
                                {
                                        FuncCall *n = makeNode(FuncCall);
                                        n->funcname = "like_escape";
-                                       n->args = makeList($4, $6, -1);
+                                       n->args = makeList2($4, $6);
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
                                        $$ = makeA_Expr(OP, "!~~*", $1, (Node *) n);
@@ -4634,7 +4632,7 @@ a_expr:  c_expr
                                        if (IsA($4, SubLink))
                                        {
                                                        SubLink *n = (SubLink *)$4;
-                                                       n->lefthand = lcons($1, NIL);
+                                                       n->lefthand = makeList1($1);
                                                        n->oper = (List *) makeA_Expr(OP, "=", NULL, NULL);
                                                        n->useor = FALSE;
                                                        n->subLinkType = ANY_SUBLINK;
@@ -4661,7 +4659,7 @@ a_expr:  c_expr
                                        if (IsA($5, SubLink))
                                        {
                                                SubLink *n = (SubLink *)$5;
-                                               n->lefthand = lcons($1, NIL);
+                                               n->lefthand = makeList1($1);
                                                n->oper = (List *) makeA_Expr(OP, "<>", NULL, NULL);
                                                n->useor = FALSE;
                                                n->subLinkType = ALL_SUBLINK;
@@ -4685,7 +4683,7 @@ a_expr:  c_expr
                | a_expr all_Op sub_type '(' SubSelect ')'
                                {
                                        SubLink *n = makeNode(SubLink);
-                                       n->lefthand = lcons($1, NIL);
+                                       n->lefthand = makeList1($1);
                                        n->oper = (List *) makeA_Expr(OP, $2, NULL, NULL);
                                        n->useor = FALSE; /* doesn't matter since only one col */
                                        n->subLinkType = $3;
@@ -4840,7 +4838,7 @@ c_expr:  attr
                                        star->val.type = T_Integer;
                                        star->val.val.ival = 1;
                                        n->funcname = $1;
-                                       n->args = lcons(star, NIL);
+                                       n->args = makeList1(star);
                                        n->agg_star = TRUE;
                                        n->agg_distinct = FALSE;
                                        $$ = (Node *)n;
@@ -4873,7 +4871,7 @@ c_expr:  attr
                                        t->typmod = -1;
 
                                        n->funcname = xlateSqlType("date");
-                                       n->args = lcons(s, NIL);
+                                       n->args = makeList1(s);
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
 
@@ -4898,7 +4896,7 @@ c_expr:  attr
                                        t->typmod = -1;
 
                                        n->funcname = xlateSqlType("time");
-                                       n->args = lcons(s, NIL);
+                                       n->args = makeList1(s);
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
 
@@ -4923,7 +4921,7 @@ c_expr:  attr
                                        t->typmod = -1;
 
                                        n->funcname = xlateSqlType("time");
-                                       n->args = lcons(s, NIL);
+                                       n->args = makeList1(s);
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
 
@@ -4952,7 +4950,7 @@ c_expr:  attr
                                        t->typmod = -1;
 
                                        n->funcname = xlateSqlType("timestamp");
-                                       n->args = lcons(s, NIL);
+                                       n->args = makeList1(s);
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
 
@@ -4977,7 +4975,7 @@ c_expr:  attr
                                        t->typmod = -1;
 
                                        n->funcname = xlateSqlType("timestamp");
-                                       n->args = lcons(s, NIL);
+                                       n->args = makeList1(s);
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
 
@@ -5104,26 +5102,26 @@ c_expr:  attr
  * Supporting nonterminals for expressions.
  */
 
-opt_indirection:  '[' a_expr ']' opt_indirection
+opt_indirection:       opt_indirection '[' a_expr ']'
                                {
                                        A_Indices *ai = makeNode(A_Indices);
                                        ai->lidx = NULL;
-                                       ai->uidx = $2;
-                                       $$ = lcons(ai, $4);
+                                       ai->uidx = $3;
+                                       $$ = lappend($1, ai);
                                }
-               | '[' a_expr ':' a_expr ']' opt_indirection
+               | opt_indirection '[' a_expr ':' a_expr ']'
                                {
                                        A_Indices *ai = makeNode(A_Indices);
-                                       ai->lidx = $2;
-                                       ai->uidx = $4;
-                                       $$ = lcons(ai, $6);
+                                       ai->lidx = $3;
+                                       ai->uidx = $5;
+                                       $$ = lappend($1, ai);
                                }
                | /*EMPTY*/
                                {       $$ = NIL; }
                ;
 
 expr_list:  a_expr
-                               { $$ = lcons($1, NIL); }
+                               { $$ = makeList1($1); }
                | expr_list ',' a_expr
                                { $$ = lappend($1, $3); }
                | expr_list USING a_expr
@@ -5135,7 +5133,7 @@ extract_list:  extract_arg FROM a_expr
                                        A_Const *n = makeNode(A_Const);
                                        n->val.type = T_String;
                                        n->val.val.str = $1;
-                                       $$ = makeList((Node *)n, $3, -1);
+                                       $$ = makeList2((Node *) n, $3);
                                }
                | /*EMPTY*/
                                {       $$ = NIL; }
@@ -5149,7 +5147,7 @@ extract_arg:  datetime                                            { $$ = $1; }
 /* position_list uses b_expr not a_expr to avoid conflict with general IN */
 
 position_list:  b_expr IN b_expr
-                               {       $$ = makeList($3, $1, -1); }
+                               {       $$ = makeList2($3, $1); }
                | /*EMPTY*/
                                {       $$ = NIL; }
                ;
@@ -5169,7 +5167,7 @@ substr_from:  FROM expr_list
                                        A_Const *n = makeNode(A_Const);
                                        n->val.type = T_Integer;
                                        n->val.val.ival = 1;
-                                       $$ = lcons((Node *)n,NIL);
+                                       $$ = makeList1((Node *)n);
                                }
                ;
 
@@ -5198,7 +5196,7 @@ in_expr:  SubSelect
                ;
 
 in_expr_nodes:  a_expr
-                               {       $$ = lcons($1, NIL); }
+                               {       $$ = makeList1($1); }
                | in_expr_nodes ',' a_expr
                                {       $$ = lappend($1, $3); }
                ;
@@ -5236,7 +5234,7 @@ case_expr:  CASE case_arg when_clause_list case_default END_TRANS
                                        w->result = (Node *)n;
 */
                                        w->expr = makeA_Expr(OP, "=", $3, $5);
-                                       c->args = lcons(w, NIL);
+                                       c->args = makeList1(w);
                                        c->defresult = $3;
                                        $$ = (Node *)c;
                                }
@@ -5259,7 +5257,7 @@ case_expr:  CASE case_arg when_clause_list case_default END_TRANS
 when_clause_list:  when_clause_list when_clause
                                { $$ = lappend($1, $2); }
                | when_clause
-                               { $$ = lcons($1, NIL); }
+                               { $$ = makeList1($1); }
                ;
 
 when_clause:  WHEN a_expr THEN a_expr
@@ -5300,7 +5298,7 @@ attr:  relation_name '.' attrs opt_indirection
                ;
 
 attrs:   attr_name
-                               { $$ = lcons(makeString($1), NIL); }
+                               { $$ = makeList1(makeString($1)); }
                | attrs '.' attr_name
                                { $$ = lappend($1, makeString($3)); }
                | attrs '.' '*'
@@ -5319,7 +5317,7 @@ attrs:      attr_name
 target_list:  target_list ',' target_el
                                {       $$ = lappend($1, $3);  }
                | target_el
-                               {       $$ = lcons($1, NIL);  }
+                               {       $$ = makeList1($1);  }
                ;
 
 /* AS is not optional because shift/red conflict with unary ops */
@@ -5342,7 +5340,7 @@ target_el:  a_expr AS ColLabel
                                        Attr *att = makeNode(Attr);
                                        att->relname = $1;
                                        att->paramNo = NULL;
-                                       att->attrs = lcons(makeString("*"), NIL);
+                                       att->attrs = makeList1(makeString("*"));
                                        att->indirection = NIL;
                                        $$ = makeNode(ResTarget);
                                        $$->name = NULL;
@@ -5368,7 +5366,7 @@ target_el:  a_expr AS ColLabel
 update_target_list:  update_target_list ',' update_target_el
                                {       $$ = lappend($1,$3);  }
                | update_target_el
-                               {       $$ = lcons($1, NIL);  }
+                               {       $$ = makeList1($1);  }
                ;
 
 update_target_el:  ColId opt_indirection '=' a_expr
index c3ac417..e7d8fe8 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.41 2000/09/25 18:14:54 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.42 2000/09/29 18:21:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -129,10 +129,13 @@ check_ungrouped_columns_walker(Node *node,
  *     Ideally this should be done earlier, but it's difficult to distinguish
  *     aggregates from plain functions at the grammar level.  So instead we
  *     check here.  This function should be called after the target list and
- *     qualifications are finalized.
+ *     qualifications are finalized.  BUT: in some cases we want to call this
+ *     routine before we've assembled the joinlist and qual into a FromExpr.
+ *     So, rather than looking at qry->jointree, look at pstate->p_joinlist
+ *     and the explicitly-passed qual.
  */
 void
-parseCheckAggregates(ParseState *pstate, Query *qry)
+parseCheckAggregates(ParseState *pstate, Query *qry, Node *qual)
 {
        List       *groupClauses = NIL;
        List       *tl;
@@ -141,18 +144,16 @@ parseCheckAggregates(ParseState *pstate, Query *qry)
        Assert(pstate->p_hasAggs || qry->groupClause || qry->havingQual);
 
        /*
-        * Aggregates must never appear in WHERE clauses. (Note this check
-        * should appear first to deliver an appropriate error message;
-        * otherwise we are likely to complain about some innocent variable in
-        * the target list, which is outright misleading if the problem is in
-        * WHERE.)
+        * Aggregates must never appear in WHERE or JOIN/ON clauses.
+        *
+        * (Note this check should appear first to deliver an appropriate error
+        * message; otherwise we are likely to complain about some innocent
+        * variable in the target list, which is outright misleading if the
+        * problem is in WHERE.)
         */
-       if (contain_agg_clause(qry->qual))
+       if (contain_agg_clause(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))
+       if (contain_agg_clause((Node *) pstate->p_joinlist))
                elog(ERROR, "Aggregates not allowed in JOIN conditions");
 
        /*
index 87041d3..cc849eb 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.67 2000/09/17 22:21:27 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.68 2000/09/29 18:21:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -64,7 +64,7 @@ static bool exprIsInSortList(Node *expr, List *sortList, List *targetList);
  * 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
+ * Note: we assume that pstate's p_rtable and p_joinlist lists were
  * initialized to NIL when the pstate was created.  We will add onto
  * any entries already present --- this is needed for rule processing!
  */
@@ -75,7 +75,7 @@ makeRangeTable(ParseState *pstate, List *frmList)
 
        /*
         * The grammar will have produced a list of RangeVars, RangeSubselects,
-        * and/or JoinExprs. Transform each one, and then add it to the join tree.
+        * and/or JoinExprs. Transform each one, and then add it to the joinlist.
         */
        foreach(fl, frmList)
        {
@@ -83,7 +83,7 @@ makeRangeTable(ParseState *pstate, List *frmList)
                List       *containedRels;
 
                n = transformFromClauseItem(pstate, n, &containedRels);
-               pstate->p_jointree = lappend(pstate->p_jointree, n);
+               pstate->p_joinlist = lappend(pstate->p_joinlist, n);
        }
 }
 
@@ -92,7 +92,7 @@ makeRangeTable(ParseState *pstate, List *frmList)
  *       Add the target relation of INSERT/UPDATE/DELETE to the range table,
  *       and make the special links to it in the ParseState.
  *
- *       inJoinSet says whether to add the target to the join tree.
+ *       inJoinSet says whether to add the target to the join list.
  *       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.
@@ -106,15 +106,32 @@ setTargetTable(ParseState *pstate, char *relname, bool inh, bool inJoinSet)
        if (refnameRangeTablePosn(pstate, relname, NULL) == 0)
        {
                rte = addRangeTableEntry(pstate, relname, NULL, inh, false);
+               /*
+                * Since the rel wasn't in the rangetable already, it's not being
+                * read; override addRangeTableEntry's default checkForRead.
+                *
+                * If we find an explicit reference to the rel later during
+                * parse analysis, scanRTEForColumn will change checkForRead
+                * to 'true' again.  That can't happen for INSERT but it is
+                * possible for UPDATE and DELETE.
+                */
+               rte->checkForRead = false;
        }
        else
        {
                rte = refnameRangeTableEntry(pstate, relname);
+               /*
+                * Since the rel was in the rangetable already, it's being read
+                * as well as written.  Therefore, leave checkForRead true.
+                */
                /* XXX what if pre-existing entry has wrong inh setting? */
        }
 
+       /* Mark target table as requiring write access. */
+       rte->checkForWrite = true;
+
        if (inJoinSet)
-               addRTEtoJoinTree(pstate, rte);
+               addRTEtoJoinList(pstate, rte);
 
        /* This could only happen for multi-action rules */
        if (pstate->p_target_relation != NULL)
@@ -242,22 +259,22 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j,
                                          List *containedRels)
 {
        Node       *result;
-       List       *sv_jointree;
+       List       *sv_joinlist;
        List       *clause_varnos,
                           *l;
 
        /*
         * This is a tad tricky, for two reasons.  First, at the point where
         * we're called, the two subtrees of the JOIN node aren't yet part of
-        * the pstate's jointree, which means that transformExpr() won't resolve
+        * the pstate's joinlist, which means that transformExpr() won't resolve
         * unqualified references to their columns correctly.  We fix this in a
-        * slightly klugy way: temporarily make the pstate's jointree consist of
+        * slightly klugy way: temporarily make the pstate's joinlist consist of
         * just those two subtrees (which creates exactly the namespace the ON
         * clause should see).  This is OK only because the ON clause can't
-        * legally alter the jointree by causing relation refs to be added.
+        * legally alter the joinlist by causing relation refs to be added.
         */
-       sv_jointree = pstate->p_jointree;
-       pstate->p_jointree = lcons(j->larg, lcons(j->rarg, NIL));
+       sv_joinlist = pstate->p_joinlist;
+       pstate->p_joinlist = makeList2(j->larg, j->rarg);
 
        /* This part is just like transformWhereClause() */
        result = transformExpr(pstate, j->quals, EXPR_COLUMN_FIRST);
@@ -267,12 +284,12 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j,
                         typeidTypeName(exprType(result)));
        }
 
-       pstate->p_jointree = sv_jointree;
+       pstate->p_joinlist = sv_joinlist;
 
        /*
         * Second, we need to check that the ON condition doesn't refer to any
         * rels outside the input subtrees of the JOIN.  It could do that despite
-        * our hack on the jointree if it uses fully-qualified names.  So, grovel
+        * our hack on the joinlist if it uses fully-qualified names.  So, grovel
         * through the transformed clause and make sure there are no bogus
         * references.
         */
@@ -312,7 +329,7 @@ transformTableEntry(ParseState *pstate, RangeVar *r)
        rte = addRangeTableEntry(pstate, relname, r->name, r->inh, true);
 
        /*
-        * We create a RangeTblRef, but we do not add it to the jointree here.
+        * We create a RangeTblRef, but we do not add it to the joinlist here.
         * makeRangeTable will do so, if we are at top level of the FROM clause.
         */
        rtr = makeNode(RangeTblRef);
@@ -333,6 +350,16 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
        SelectStmt *subquery = (SelectStmt *) r->subquery;
        List       *parsetrees;
        Query      *query;
+       RangeTblEntry *rte;
+       RangeTblRef *rtr;
+
+       /*
+        * We require user to supply an alias for a subselect, per SQL92.
+        * To relax this, we'd have to be prepared to gin up a unique alias
+        * for an unlabeled subselect.
+        */
+       if (r->name == NULL)
+               elog(ERROR, "sub-select in FROM must have an alias");
 
        /*
         * subquery node might not be SelectStmt if user wrote something like
@@ -347,7 +374,7 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
         * 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);
+       parsetrees = parse_analyze(makeList1(subquery), NULL);
 
        /*
         * Check that we got something reasonable.  Some of these conditions
@@ -362,13 +389,24 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
 
        if (query->commandType != CMD_SELECT)
                elog(ERROR, "Expected SELECT query from subselect in FROM");
-       if (query->resultRelation != 0 || query->into != NULL)
+       if (query->resultRelation != 0 || query->into != NULL || query->isPortal)
                elog(ERROR, "Subselect in FROM may not have SELECT INTO");
 
+       /*
+        * OK, build an RTE for the subquery.
+        */
+       rte = addRangeTableEntryForSubquery(pstate, query, r->name, true);
 
-       elog(ERROR, "Subselect in FROM not done yet");
+       /*
+        * We create a RangeTblRef, but we do not add it to the joinlist 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 NULL;
+       return rtr;
 }
 
 
@@ -376,12 +414,12 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
  * 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.
+ *       transformed item ready to include in the joinlist.
  *       This routine can recurse to handle SQL92 JOIN expressions.
  *
- *       Aside from the primary return value (the transformed jointree item)
+ *       Aside from the primary return value (the transformed joinlist item)
  *       this routine also returns an integer list of the rangetable indexes
- *       of all the base relations represented in the jointree item.  This
+ *       of all the base relations represented in the joinlist item.  This
  *       list is needed for checking JOIN/ON conditions in higher levels.
  */
 static Node *
@@ -393,7 +431,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
                RangeTblRef *rtr;
 
                rtr = transformTableEntry(pstate, (RangeVar *) n);
-               *containedRels = lconsi(rtr->rtindex, NIL);
+               *containedRels = makeListi1(rtr->rtindex);
                return (Node *) rtr;
        }
        else if (IsA(n, RangeSubselect))
@@ -402,7 +440,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
                RangeTblRef *rtr;
 
                rtr = transformRangeSubselect(pstate, (RangeSubselect *) n);
-               *containedRels = lconsi(rtr->rtindex, NIL);
+               *containedRels = makeListi1(rtr->rtindex);
                return (Node *) rtr;
        }
        else if (IsA(n, JoinExpr))
@@ -599,7 +637,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
                                                a->lexpr = l_colvar;
                                                w->expr = (Node *) a;
                                                w->result = l_colvar;
-                                               c->args = lcons(w, NIL);
+                                               c->args = makeList1(w);
                                                c->defresult = r_colvar;
                                                colvar = transformExpr(pstate, (Node *) c,
                                                                                           EXPR_COLUMN_FIRST);
@@ -641,17 +679,17 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
                 * 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.
+                * the pstate's joinlist, so they have to be scanned separately.
                 */
                if (j->alias)
                {
-                       /* Check against previously created RTEs and jointree entries */
+                       /* Check against previously created RTEs and joinlist 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))
+                       if (scanJoinListForRefname(j->larg, j->alias->relname) ||
+                               scanJoinListForRefname(j->rarg, j->alias->relname))
                                elog(ERROR, "Table name \"%s\" specified more than once",
                                         j->alias->relname);
                        /*
index a033ff4..591fdab 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.83 2000/09/12 21:07:02 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.84 2000/09/29 18:21:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -175,7 +175,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
 
                                                        result = ParseFuncOrColumn(pstate,
                                                                                                           "nullvalue",
-                                                                                                          lcons(lexpr, NIL),
+                                                                                                          makeList1(lexpr),
                                                                                                           false, false,
                                                                                                           precedence);
                                                }
@@ -188,7 +188,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
 
                                                        result = ParseFuncOrColumn(pstate,
                                                                                                           "nonnullvalue",
-                                                                                                          lcons(lexpr, NIL),
+                                                                                                          makeList1(lexpr),
                                                                                                           false, false,
                                                                                                           precedence);
                                                }
@@ -213,7 +213,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
 
                                                        expr->typeOid = BOOLOID;
                                                        expr->opType = AND_EXPR;
-                                                       expr->args = makeList(lexpr, rexpr, -1);
+                                                       expr->args = makeList2(lexpr, rexpr);
                                                        result = (Node *) expr;
                                                }
                                                break;
@@ -235,7 +235,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
                                                                         typeidTypeName(exprType(rexpr)), typeidTypeName(BOOLOID));
                                                        expr->typeOid = BOOLOID;
                                                        expr->opType = OR_EXPR;
-                                                       expr->args = makeList(lexpr, rexpr, -1);
+                                                       expr->args = makeList2(lexpr, rexpr);
                                                        result = (Node *) expr;
                                                }
                                                break;
@@ -251,7 +251,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
                                                                         typeidTypeName(exprType(rexpr)), typeidTypeName(BOOLOID));
                                                        expr->typeOid = BOOLOID;
                                                        expr->opType = NOT_EXPR;
-                                                       expr->args = makeList(rexpr, -1);
+                                                       expr->args = makeList1(rexpr);
                                                        result = (Node *) expr;
                                                }
                                                break;
@@ -294,7 +294,8 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
                                        break;
                                }
                                pstate->p_hasSubLinks = true;
-                               qtrees = parse_analyze(lcons(sublink->subselect, NIL), pstate);
+                               qtrees = parse_analyze(makeList1(sublink->subselect),
+                                                                          pstate);
                                if (length(qtrees) != 1)
                                        elog(ERROR, "Bad query in subselect");
                                qtree = (Query *) lfirst(qtrees);
index 1f19b1b..b4324b0 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.90 2000/09/12 21:07:02 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.91 2000/09/29 18:21:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -76,7 +76,7 @@ ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr, int precedence)
                                                                                                        EXPR_RELATION_FIRST);
 
                retval = ParseFuncOrColumn(pstate, strVal(lfirst(attr->attrs)),
-                                                                  lcons(param, NIL),
+                                                                  makeList1(param),
                                                                   false, false,
                                                                   precedence);
        }
@@ -87,7 +87,7 @@ ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr, int precedence)
                ident->name = attr->relname;
                ident->isRel = TRUE;
                retval = ParseFuncOrColumn(pstate, strVal(lfirst(attr->attrs)),
-                                                                  lcons(ident, NIL),
+                                                                  makeList1(ident),
                                                                   false, false,
                                                                   precedence);
        }
@@ -96,7 +96,7 @@ ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr, int precedence)
        foreach(mutator_iter, lnext(attr->attrs))
        {
                retval = ParseFuncOrColumn(pstate, strVal(lfirst(mutator_iter)),
-                                                                  lcons(retval, NIL),
+                                                                  makeList1(retval),
                                                                   false, false,
                                                                   precedence);
        }
@@ -447,6 +447,9 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
                         * but has varattno == 0 to signal that the whole tuple is the
                         * argument.
                         */
+                       if (rte->relname == NULL)
+                               elog(ERROR,
+                                        "function applied to tuple is not supported for subSELECTs");
                        toid = typeTypeId(typenameType(rte->relname));
 
                        /* replace it in the arg list */
index 85a5606..2ff4d9c 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.46 2000/09/12 21:07:02 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.47 2000/09/29 18:21:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -152,11 +152,11 @@ make_op(char *opname, Node *ltree, Node *rtree)
        result->oper = (Node *) newop;
 
        if (!left)
-               result->args = lcons(right, NIL);
+               result->args = makeList1(right);
        else if (!right)
-               result->args = lcons(left, NIL);
+               result->args = makeList1(left);
        else
-               result->args = lcons(left, lcons(right, NIL));
+               result->args = makeList2(left, right);
 
        return result;
 }      /* make_op() */
@@ -171,8 +171,8 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno)
 {
        int                     vnum,
                                sublevels_up;
-       Oid                     vartypeid;
-       int32           type_mod;
+       Oid                     vartypeid = 0;
+       int32           type_mod = 0;
 
        vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
 
@@ -197,8 +197,22 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno)
        else
        {
                /* Subselect RTE --- get type info from subselect's tlist */
-               elog(ERROR, "make_var: subselect in FROM not implemented yet");
-               vartypeid = type_mod = 0;
+               List       *tlistitem;
+
+               foreach(tlistitem, rte->subquery->targetList)
+               {
+                       TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
+
+                       if (te->resdom->resjunk || te->resdom->resno != attrno)
+                               continue;
+                       vartypeid = te->resdom->restype;
+                       type_mod = te->resdom->restypmod;
+                       break;
+               }
+               /* falling off end of list shouldn't happen... */
+               if (tlistitem == NIL)
+                       elog(ERROR, "Subquery %s does not have attribute %d",
+                                rte->eref->relname, attrno);
        }
 
        return makeVar(vnum, attrno, vartypeid, type_mod, sublevels_up);
index baae0a5..3fccd95 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.48 2000/09/25 18:14:54 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.49 2000/09/29 18:21:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -26,7 +26,6 @@
 #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"
 
@@ -98,7 +97,7 @@ refnameRangeOrJoinEntry(ParseState *pstate,
 
                /*
                 * Check the rangetable for RTEs; if no match, recursively scan
-                * the jointree for join tables.  We assume that no duplicate
+                * the joinlist for join tables.  We assume that no duplicate
                 * entries have been made in any one nesting level.
                 */
                foreach(temp, pstate->p_rtable)
@@ -109,7 +108,7 @@ refnameRangeOrJoinEntry(ParseState *pstate,
                                return (Node *) rte;
                }
 
-               join = scanJoinTreeForRefname((Node *) pstate->p_jointree, refname);
+               join = scanJoinListForRefname((Node *) pstate->p_joinlist, refname);
                if (join)
                        return (Node *) join;
 
@@ -122,9 +121,14 @@ refnameRangeOrJoinEntry(ParseState *pstate,
        return NULL;
 }
 
-/* Recursively search a jointree for a joinexpr with given refname */
+/*
+ * Recursively search a joinlist for a joinexpr with given refname
+ *
+ * Note that during parse analysis, we don't expect to find a FromExpr node
+ * in p_joinlist; its top level is just a bare List.
+ */
 JoinExpr *
-scanJoinTreeForRefname(Node *jtnode, char *refname)
+scanJoinListForRefname(Node *jtnode, char *refname)
 {
        JoinExpr   *result = NULL;
 
@@ -136,7 +140,7 @@ scanJoinTreeForRefname(Node *jtnode, char *refname)
 
                foreach(l, (List *) jtnode)
                {
-                       result = scanJoinTreeForRefname(lfirst(l), refname);
+                       result = scanJoinListForRefname(lfirst(l), refname);
                        if (result)
                                break;
                }
@@ -151,12 +155,12 @@ scanJoinTreeForRefname(Node *jtnode, char *refname)
 
                if (j->alias && strcmp(j->alias->relname, refname) == 0)
                        return j;
-               result = scanJoinTreeForRefname(j->larg, refname);
+               result = scanJoinListForRefname(j->larg, refname);
                if (! result)
-                       result = scanJoinTreeForRefname(j->rarg, refname);
+                       result = scanJoinListForRefname(j->rarg, refname);
        }
        else
-               elog(ERROR, "scanJoinTreeForRefname: unexpected node type %d",
+               elog(ERROR, "scanJoinListForRefname: unexpected node type %d",
                         nodeTag(jtnode));
        return result;
 }
@@ -261,6 +265,9 @@ RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up)
  *       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.
+ *
+ * Side effect: if we find a match, mark the RTE as requiring read access.
+ * See comments in setTargetTable().
  */
 static Node *
 scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
@@ -281,6 +288,7 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
                        if (result)
                                elog(ERROR, "Column reference \"%s\" is ambiguous", colname);
                        result = (Node *) make_var(pstate, rte, attnum);
+                       rte->checkForRead = true;
                }
        }
 
@@ -299,7 +307,10 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
        {
                attnum = specialAttNum(colname);
                if (attnum != InvalidAttrNumber)
+               {
                        result = (Node *) make_var(pstate, rte, attnum);
+                       rte->checkForRead = true;
+               }
        }
 
        return result;
@@ -310,6 +321,11 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
  *       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.
+ *
+ * NOTE: unlike scanRTEForColumn, there's no need to worry about forcing
+ * checkForRead true for the referenced tables.  This is so because a join
+ * expression can only appear in a FROM clause, and any table named in
+ * FROM will be marked checkForRead from the beginning.
  */
 static Node *
 scanJoinForColumn(JoinExpr *join, char *colname, int sublevels_up)
@@ -359,7 +375,7 @@ colnameToVar(ParseState *pstate, char *colname)
                 * those, ignore RTEs that are marked as not inFromCl and not
                 * the query's target relation.
                 */
-               foreach(jt, pstate->p_jointree)
+               foreach(jt, pstate->p_joinlist)
                {
                        Node   *jtnode = (Node *) lfirst(jt);
                        Node   *newresult = NULL;
@@ -446,8 +462,9 @@ qualifiedNameToVar(ParseState *pstate, char *refname, char *colname,
 }
 
 /*
- * Add an entry to the pstate's range table (p_rtable), unless the
- * specified refname is already present, in which case raise error.
+ * Add an entry for a relation to the pstate's range table (p_rtable).
+ *
+ * If the specified refname is already present, raise error.
  *
  * If pstate is NULL, we just build an RTE and return it without worrying
  * about membership in an rtable list.
@@ -481,6 +498,7 @@ addRangeTableEntry(ParseState *pstate,
 
        rte->relname = relname;
        rte->alias = alias;
+       rte->subquery = NULL;
 
        /*
         * Get the rel's OID.  This access also ensures that we have an
@@ -492,7 +510,7 @@ addRangeTableEntry(ParseState *pstate,
        rte->relid = RelationGetRelid(rel);
        maxattrs = RelationGetNumberOfAttributes(rel);
 
-       eref = alias ? copyObject(alias) : makeAttr(refname, NULL);
+       eref = alias ? (Attr *) copyObject(alias) : makeAttr(refname, NULL);
        numaliases = length(eref->attrs);
 
        if (maxattrs < numaliases)
@@ -515,12 +533,18 @@ addRangeTableEntry(ParseState *pstate,
         * 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.
+        * - this RTE should be checked for read/write access rights.
+        *
+        * The initial default on access checks is always check-for-READ-access,
+        * which is the right thing for all except target tables.
         *----------
         */
        rte->inh = inh;
        rte->inFromCl = inFromCl;
-       rte->skipAcl = false;           /* always starts out false */
+       rte->checkForRead = true;
+       rte->checkForWrite = false;
+
+       rte->checkAsUser = InvalidOid; /* not set-uid by default, either */
 
        /*
         * Add completed RTE to range table list.
@@ -532,17 +556,105 @@ addRangeTableEntry(ParseState *pstate,
 }
 
 /*
- * Add the given RTE as a top-level entry in the pstate's join tree,
+ * Add an entry for a subquery to the pstate's range table (p_rtable).
+ *
+ * This is just like addRangeTableEntry() except that it makes a subquery RTE.
+ * Note that an alias clause *must* be supplied.
+ */
+RangeTblEntry *
+addRangeTableEntryForSubquery(ParseState *pstate,
+                                                         Query *subquery,
+                                                         Attr *alias,
+                                                         bool inFromCl)
+{
+       char       *refname = alias->relname;
+       RangeTblEntry *rte;
+       Attr       *eref;
+       int                     numaliases;
+       int                     varattno;
+       List       *tlistitem;
+
+       /* Check for conflicting RTE or jointable alias (at level 0 only) */
+       if (pstate != NULL)
+       {
+               Node   *rteorjoin = refnameRangeOrJoinEntry(pstate, refname, NULL);
+
+               if (rteorjoin)
+                       elog(ERROR, "Table name \"%s\" specified more than once",
+                                refname);
+       }
+
+       rte = makeNode(RangeTblEntry);
+
+       rte->relname = NULL;
+       rte->relid = InvalidOid;
+       rte->subquery = subquery;
+       rte->alias = alias;
+
+       eref = copyObject(alias);
+       numaliases = length(eref->attrs);
+
+       /* fill in any unspecified alias columns */
+       varattno = 0;
+       foreach(tlistitem, subquery->targetList)
+       {
+               TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
+
+               if (te->resdom->resjunk)
+                       continue;
+               varattno++;
+               Assert(varattno == te->resdom->resno);
+               if (varattno > numaliases)
+               {
+                       char       *attrname;
+
+                       attrname = pstrdup(te->resdom->resname);
+                       eref->attrs = lappend(eref->attrs, makeString(attrname));
+               }
+       }
+       if (varattno < numaliases)
+               elog(ERROR, "Table \"%s\" has %d columns available but %d columns specified",
+                        refname, varattno, numaliases);
+
+       rte->eref = eref;
+
+       /*----------
+        * Flags:
+        * - this RTE should be expanded to include descendant tables,
+        * - this RTE is in the FROM clause,
+        * - this RTE should be checked for read/write access rights.
+        *
+        * Subqueries are never checked for access rights.
+        *----------
+        */
+       rte->inh = false;                       /* never true for subqueries */
+       rte->inFromCl = inFromCl;
+       rte->checkForRead = false;
+       rte->checkForWrite = false;
+
+       rte->checkAsUser = InvalidOid;
+
+       /*
+        * Add completed RTE to range table list.
+        */
+       if (pstate != NULL)
+               pstate->p_rtable = lappend(pstate->p_rtable, rte);
+
+       return rte;
+}
+
+/*
+ * Add the given RTE as a top-level entry in the pstate's join list,
  * unless there already is an entry for it.
  */
 void
-addRTEtoJoinTree(ParseState *pstate, RangeTblEntry *rte)
+addRTEtoJoinList(ParseState *pstate, RangeTblEntry *rte)
 {
        int                     rtindex = RTERangeTablePosn(pstate, rte, NULL);
        List       *jt;
        RangeTblRef *rtr;
 
-       foreach(jt, pstate->p_jointree)
+       foreach(jt, pstate->p_joinlist)
        {
                Node       *n = (Node *) lfirst(jt);
 
@@ -556,7 +668,7 @@ addRTEtoJoinTree(ParseState *pstate, RangeTblEntry *rte)
        /* Not present, so add it */
        rtr = makeNode(RangeTblRef);
        rtr->rtindex = rtindex;
-       pstate->p_jointree = lappend(pstate->p_jointree, rtr);
+       pstate->p_joinlist = lappend(pstate->p_joinlist, rtr);
 }
 
 /*
@@ -570,7 +682,7 @@ addImplicitRTE(ParseState *pstate, char *relname)
        RangeTblEntry *rte;
 
        rte = addRangeTableEntry(pstate, relname, NULL, false, false);
-       addRTEtoJoinTree(pstate, rte);
+       addRTEtoJoinList(pstate, rte);
        warnAutoRange(pstate, relname);
 
        return rte;
@@ -590,11 +702,9 @@ void
 expandRTE(ParseState *pstate, RangeTblEntry *rte,
                  List **colnames, List **colvars)
 {
-       Relation        rel;
-       int                     varattno,
-                               maxattrs,
-                               rtindex,
-                               sublevels_up;
+       int                     rtindex,
+                               sublevels_up,
+                               varattno;
 
        if (colnames)
                *colnames = NIL;
@@ -604,43 +714,88 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
        /* Need the RT index of the entry for creating Vars */
        rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
 
-       rel = heap_open(rte->relid, AccessShareLock);
+       if (rte->relname)
+       {
+               /* Ordinary relation RTE */
+               Relation        rel;
+               int                     maxattrs;
 
-       maxattrs = RelationGetNumberOfAttributes(rel);
+               rel = heap_openr(rte->relname, AccessShareLock);
 
-       for (varattno = 0; varattno < maxattrs; varattno++)
-       {
-               Form_pg_attribute attr = rel->rd_att->attrs[varattno];
+               maxattrs = RelationGetNumberOfAttributes(rel);
+
+               for (varattno = 0; varattno < maxattrs; varattno++)
+               {
+                       Form_pg_attribute attr = rel->rd_att->attrs[varattno];
 
 #ifdef _DROP_COLUMN_HACK__
-               if (COLUMN_IS_DROPPED(attr))
-                       continue;
+                       if (COLUMN_IS_DROPPED(attr))
+                               continue;
 #endif  /* _DROP_COLUMN_HACK__ */
 
-               if (colnames)
-               {
-                       char       *label;
+                       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 (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);
+                       }
                }
 
-               if (colvars)
+               heap_close(rel, AccessShareLock);
+       }
+       else
+       {
+               /* Subquery RTE */
+               List       *aliasp = rte->eref->attrs;
+               List       *tlistitem;
+
+               varattno = 0;
+               foreach(tlistitem, rte->subquery->targetList)
                {
-                       Var                *varnode;
+                       TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
+
+                       if (te->resdom->resjunk)
+                               continue;
+                       varattno++;
+                       Assert(varattno == te->resdom->resno);
+
+                       if (colnames)
+                       {
+                               /* Assume there is one alias per target item */
+                               char       *label = strVal(lfirst(aliasp));
 
-                       varnode = makeVar(rtindex, attr->attnum,
-                                                         attr->atttypid, attr->atttypmod,
-                                                         sublevels_up);
+                               *colnames = lappend(*colnames, makeString(pstrdup(label)));
+                               aliasp = lnext(aliasp);
+                       }
 
-                       *colvars = lappend(*colvars, varnode);
+                       if (colvars)
+                       {
+                               Var                *varnode;
+
+                               varnode = makeVar(rtindex, varattno,
+                                                                 te->resdom->restype,
+                                                                 te->resdom->restypmod,
+                                                                 sublevels_up);
+
+                               *colvars = lappend(*colvars, varnode);
+                       }
                }
        }
-
-       heap_close(rel, AccessShareLock);
 }
 
 /*
index b8e1570..c353c06 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.62 2000/09/12 21:07:02 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.63 2000/09/29 18:21:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -386,7 +386,7 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
  * 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.
+ * which by now has been transformed into a joinlist.
  */
 static List *
 ExpandAllTables(ParseState *pstate)
@@ -395,10 +395,10 @@ ExpandAllTables(ParseState *pstate)
        List       *jt;
 
        /* SELECT *; */
-       if (pstate->p_jointree == NIL)
+       if (pstate->p_joinlist == NIL)
                elog(ERROR, "Wildcard with no tables specified not allowed");
 
-       foreach(jt, pstate->p_jointree)
+       foreach(jt, pstate->p_joinlist)
        {
                Node       *n = (Node *) lfirst(jt);
 
index aca50c5..81d8219 100644 (file)
@@ -4,7 +4,7 @@
 #    Makefile for rewrite
 #
 # IDENTIFICATION
-#    $Header: /cvsroot/pgsql/src/backend/rewrite/Makefile,v 1.13 2000/08/31 16:10:27 petere Exp $
+#    $Header: /cvsroot/pgsql/src/backend/rewrite/Makefile,v 1.14 2000/09/29 18:21:24 tgl Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -13,7 +13,7 @@ top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
 OBJS = rewriteRemove.o rewriteDefine.o \
-       rewriteHandler.o rewriteManip.o rewriteSupport.o locks.o
+       rewriteHandler.o rewriteManip.o rewriteSupport.o
 
 all: SUBSYS.o
 
diff --git a/src/backend/rewrite/locks.c b/src/backend/rewrite/locks.c
deleted file mode 100644 (file)
index 78fdad8..0000000
+++ /dev/null
@@ -1,268 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * locks.c
- *
- * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/rewrite/Attic/locks.c,v 1.32 2000/09/12 21:07:02 tgl Exp $
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres.h"
-
-#include "access/heapam.h"
-#include "catalog/pg_shadow.h"
-#include "optimizer/clauses.h"
-#include "rewrite/locks.h"
-#include "parser/parsetree.h"
-#include "utils/acl.h"
-#include "utils/syscache.h"
-
-
-/*
- * thisLockWasTriggered
- *
- * walk the tree, if there we find a varnode,
- * we check the varattno against the attnum
- * if we find at least one such match, we return true
- * otherwise, we return false
- *
- * XXX this should be unified with attribute_used()
- */
-
-typedef struct
-{
-       int                     varno;
-       int                     attnum;
-       int                     sublevels_up;
-} thisLockWasTriggered_context;
-
-static bool
-thisLockWasTriggered_walker(Node *node,
-                                                       thisLockWasTriggered_context *context)
-{
-       if (node == NULL)
-               return false;
-       if (IsA(node, Var))
-       {
-               Var                *var = (Var *) node;
-
-               if (var->varlevelsup == context->sublevels_up &&
-                       var->varno == context->varno &&
-                       (var->varattno == context->attnum || context->attnum == -1))
-                       return true;
-               return false;
-       }
-       if (IsA(node, Query))
-       {
-               /* Recurse into subselects */
-               bool            result;
-
-               context->sublevels_up++;
-               result = query_tree_walker((Query *) node, thisLockWasTriggered_walker,
-                                                                  (void *) context);
-               context->sublevels_up--;
-               return result;
-       }
-       return expression_tree_walker(node, thisLockWasTriggered_walker,
-                                                                 (void *) context);
-}
-
-static bool
-thisLockWasTriggered(int varno,
-                                        int attnum,
-                                        Query *parsetree)
-{
-       thisLockWasTriggered_context context;
-
-       context.varno = varno;
-       context.attnum = attnum;
-       context.sublevels_up = 0;
-
-       return thisLockWasTriggered_walker((Node *) parsetree, &context);
-}
-
-/*
- * matchLocks -
- *       match the list of locks and returns the matching rules
- */
-List *
-matchLocks(CmdType event,
-                  RuleLock *rulelocks,
-                  int varno,
-                  Query *parsetree)
-{
-       List       *real_locks = NIL;
-       int                     nlocks;
-       int                     i;
-
-       Assert(rulelocks != NULL);      /* we get called iff there is some lock */
-       Assert(parsetree != NULL);
-
-       if (parsetree->commandType != CMD_SELECT)
-       {
-               if (parsetree->resultRelation != varno)
-                       return NULL;
-       }
-
-       nlocks = rulelocks->numLocks;
-
-       for (i = 0; i < nlocks; i++)
-       {
-               RewriteRule *oneLock = rulelocks->rules[i];
-
-               if (oneLock->event == event)
-               {
-                       if (parsetree->commandType != CMD_SELECT ||
-                               thisLockWasTriggered(varno,
-                                                                        oneLock->attrno,
-                                                                        parsetree))
-                               real_locks = lappend(real_locks, oneLock);
-               }
-       }
-
-       checkLockPerms(real_locks, parsetree, varno);
-
-       return real_locks;
-}
-
-
-/*
- * Check the access permissions of tables that are referred to by a rule.
- * We want to check the access permissions using the userid of the rule's
- * owner, *not* of the current user (the one accessing the rule).  So, we
- * do the permission check here and set skipAcl = TRUE in each of the rule's
- * RTEs, to prevent the executor from running another check with the current
- * user's ID.
- *
- * XXX This routine is called before the rule's query tree has been copied
- * out of the relcache entry where it is kept.  Therefore, when we set
- * skipAcl = TRUE, we are destructively modifying the relcache entry for
- * the event relation!  This seems fairly harmless because the relcache
- * querytree is only used as a source for the rewriter, but it's a tad
- * unclean anyway.
- *
- * Note that we must check permissions every time, even if skipAcl was
- * already set TRUE by a prior call.  This ensures that we enforce the
- * current permission settings for each referenced table, even if they
- * have changed since the relcache entry was loaded.
- */
-
-typedef struct
-{
-       Oid                     evowner;
-} checkLockPerms_context;
-
-static bool
-checkLockPerms_walker(Node *node,
-                                         checkLockPerms_context *context)
-{
-       if (node == NULL)
-               return false;
-       if (IsA(node, Query))
-       {
-               Query      *qry = (Query *) node;
-               int                     rtablength = length(qry->rtable);
-               int                     i;
-
-               /* Check all the RTEs in this query node, except OLD and NEW */
-               for (i = 1; i <= rtablength; i++)
-               {
-                       RangeTblEntry *rte = rt_fetch(i, qry->rtable);
-                       int32           reqperm;
-                       int32           aclcheck_res;
-
-                       if (strcmp(rte->eref->relname, "*NEW*") == 0)
-                               continue;
-                       if (strcmp(rte->eref->relname, "*OLD*") == 0)
-                               continue;
-
-                       if (i == qry->resultRelation)
-                               switch (qry->commandType)
-                               {
-                                       case CMD_INSERT:
-                                               reqperm = ACL_AP;
-                                               break;
-                                       default:
-                                               reqperm = ACL_WR;
-                                               break;
-                               }
-                       else
-                               reqperm = ACL_RD;
-
-                       aclcheck_res = pg_aclcheck(rte->relname,
-                                                                          context->evowner,
-                                                                          reqperm);
-                       if (aclcheck_res != ACLCHECK_OK)
-                               elog(ERROR, "%s: %s",
-                                        rte->relname,
-                                        aclcheck_error_strings[aclcheck_res]);
-
-                       /*
-                        * Mark RTE to prevent executor from checking again with the
-                        * current user's ID...
-                        */
-                       rte->skipAcl = true;
-               }
-
-               /* If there are sublinks, search for them and check their RTEs */
-               if (qry->hasSubLinks)
-                       return query_tree_walker(qry, checkLockPerms_walker,
-                                                                        (void *) context);
-               return false;
-       }
-       return expression_tree_walker(node, checkLockPerms_walker,
-                                                                 (void *) context);
-}
-
-void
-checkLockPerms(List *locks, Query *parsetree, int rt_index)
-{
-       RangeTblEntry *rte;
-       Relation        ev_rel;
-       HeapTuple       usertup;
-       Form_pg_shadow userform;
-       checkLockPerms_context context;
-       List       *l;
-
-       if (locks == NIL)
-               return;                                 /* nothing to check */
-
-       /*
-        * Get the userid of the rule's event relation owner
-        */
-       rte = rt_fetch(rt_index, parsetree->rtable);
-       ev_rel = heap_openr(rte->relname, AccessShareLock);
-       usertup = SearchSysCacheTuple(SHADOWSYSID,
-                                                         ObjectIdGetDatum(ev_rel->rd_rel->relowner),
-                                                                 0, 0, 0);
-       if (!HeapTupleIsValid(usertup))
-               elog(ERROR, "cache lookup for userid %d failed",
-                        ev_rel->rd_rel->relowner);
-       userform = (Form_pg_shadow) GETSTRUCT(usertup);
-       context.evowner = userform->usesysid;
-       heap_close(ev_rel, AccessShareLock);
-
-       /*
-        * Check all the locks that should get fired on this query
-        */
-       foreach(l, locks)
-       {
-               RewriteRule *onelock = (RewriteRule *) lfirst(l);
-               List       *action;
-
-               /*
-                * In each lock check every action.  We must scan the action
-                * recursively in case there are any sub-queries within it.
-                */
-               foreach(action, onelock->actions)
-               {
-                       Query      *query = (Query *) lfirst(action);
-
-                       checkLockPerms_walker((Node *) query, &context);
-               }
-       }
-}
index da0d84a..e06896d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.52 2000/09/12 20:38:09 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.53 2000/09/29 18:21:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
 #include "access/heapam.h"
-#include "utils/builtins.h"
 #include "catalog/catname.h"
 #include "catalog/indexing.h"
 #include "catalog/pg_rewrite.h"
+#include "commands/view.h"
+#include "miscadmin.h"
+#include "optimizer/clauses.h"
 #include "parser/parse_relation.h"
 #include "rewrite/rewriteDefine.h"
 #include "rewrite/rewriteSupport.h"
-#include "utils/syscache.h"
 #include "storage/smgr.h"
-#include "commands/view.h"
+#include "utils/builtins.h"
+
+
+static void setRuleCheckAsUser(Query *qry, Oid userid);
+static bool setRuleCheckAsUser_walker(Node *node, Oid *context);
 
 
 /*
@@ -52,7 +57,7 @@ InsertRule(char *rulname,
        Oid                     rewriteObjectId;
 
        if (IsDefinedRewriteRule(rulname))
-               elog(ERROR, "Attempt to insert rule '%s' failed: already exists",
+               elog(ERROR, "Attempt to insert rule \"%s\" failed: already exists",
                         rulname);
 
        /* ----------------
@@ -217,10 +222,7 @@ DefineQueryRewrite(RuleStmt *stmt)
         */
        if (event_type == CMD_SELECT)
        {
-               TargetEntry *tle;
-               Resdom     *resdom;
-               Form_pg_attribute attr;
-               char       *attname;
+               List       *tllist;
                int                     i;
                char            *expected_name;
 
@@ -245,6 +247,10 @@ DefineQueryRewrite(RuleStmt *stmt)
                query = (Query *) lfirst(action);
                if (!is_instead || query->commandType != CMD_SELECT)
                        elog(ERROR, "only instead-select rules currently supported on select");
+
+               /*
+                * ... there can be no rule qual, ...
+                */
                if (event_qual != NULL)
                        elog(ERROR, "event qualifications not supported for rules on select");
 
@@ -252,26 +258,36 @@ DefineQueryRewrite(RuleStmt *stmt)
                 * ... the targetlist of the SELECT action must exactly match the
                 * event relation, ...
                 */
-               if (event_relation->rd_att->natts != length(query->targetList))
-                       elog(ERROR, "select rules target list must match event relations structure");
-
-               for (i = 1; i <= event_relation->rd_att->natts; i++)
+               i = 0;
+               foreach(tllist, query->targetList)
                {
-                       tle = (TargetEntry *) nth(i - 1, query->targetList);
-                       resdom = tle->resdom;
+                       TargetEntry *tle = (TargetEntry *) lfirst(tllist);
+                       Resdom     *resdom = tle->resdom;
+                       Form_pg_attribute attr;
+                       char       *attname;
+
+                       if (resdom->resjunk)
+                               continue;
+                       i++;
+                       if (i > event_relation->rd_att->natts)
+                               elog(ERROR, "select rule's target list has too many entries");
+
                        attr = event_relation->rd_att->attrs[i - 1];
                        attname = NameStr(attr->attname);
 
                        if (strcmp(resdom->resname, attname) != 0)
-                               elog(ERROR, "select rules target entry %d has different column name from %s", i, attname);
+                               elog(ERROR, "select rule's target entry %d has different column name from %s", i, attname);
 
                        if (attr->atttypid != resdom->restype)
-                               elog(ERROR, "select rules target entry %d has different type from attribute %s", i, attname);
+                               elog(ERROR, "select rule's target entry %d has different type from attribute %s", i, attname);
 
                        if (attr->atttypmod != resdom->restypmod)
-                               elog(ERROR, "select rules target entry %d has different size from attribute %s", i, attname);
+                               elog(ERROR, "select rule's target entry %d has different size from attribute %s", i, attname);
                }
 
+               if (i != event_relation->rd_att->natts)
+                       elog(ERROR, "select rule's target list has too few entries");
+
                /*
                 * ... there must not be another ON SELECT rule already ...
                 */
@@ -283,7 +299,7 @@ DefineQueryRewrite(RuleStmt *stmt)
 
                                rule = event_relation->rd_rules->rules[i];
                                if (rule->event == CMD_SELECT)
-                                       elog(ERROR, "%s is already a view",
+                                       elog(ERROR, "\"%s\" is already a view",
                                                 RelationGetRelationName(event_relation));
                        }
                }
@@ -295,24 +311,12 @@ DefineQueryRewrite(RuleStmt *stmt)
                        elog(ERROR, "LIMIT clause not supported in views");
 
                /*
-                * DISTINCT on view is not supported
-                */
-               if (query->distinctClause != NIL)
-                       elog(ERROR, "DISTINCT not supported in views");
-
-               /*
-                * ORDER BY in view is not supported
-                */
-               if (query->sortClause != NIL)
-                       elog(ERROR, "ORDER BY not supported in views");
-
-               /*
                 * ... and finally the rule must be named _RETviewname.
                 */
                expected_name = MakeRetrieveViewRuleName(event_obj->relname);
                if (strcmp(expected_name, stmt->rulename) != 0)
                {
-                       elog(ERROR, "view rule for %s must be named %s",
+                       elog(ERROR, "view rule for \"%s\" must be named \"%s\"",
                                 event_obj->relname, expected_name);
                }
                pfree(expected_name);
@@ -342,7 +346,7 @@ DefineQueryRewrite(RuleStmt *stmt)
        }
 
        /*
-        * This rule is allowed - install it.
+        * This rule is allowed - prepare to install it.
         */
        if (eslot_string == NULL)
        {
@@ -360,13 +364,21 @@ DefineQueryRewrite(RuleStmt *stmt)
                                 eslot_string, event_qual, &action,
                                 is_instead, event_attype);
 
+       /*
+        * We want the rule's table references to be checked as though by
+        * the rule owner, not the user referencing the rule.  Therefore,
+        * scan through the rule's rtables and set the checkAsUser field
+        * on all rtable entries (except *OLD* and *NEW*).
+        */
+       foreach(l, action)
+       {
+               query = (Query *) lfirst(l);
+               setRuleCheckAsUser(query, GetUserId());
+       }
+
        /* discard rule if it's null action and not INSTEAD; it's a no-op */
        if (action != NIL || is_instead)
        {
-               Relation        relationRelation;
-               HeapTuple       tuple;
-               Relation        idescs[Num_pg_class_indices];
-
                event_qualP = nodeToString(event_qual);
                actionP = nodeToString(action);
 
@@ -386,34 +398,8 @@ DefineQueryRewrite(RuleStmt *stmt)
                 * Important side effect: an SI notice is broadcast to force all
                 * backends (including me!) to update relcache entries with the new
                 * rule.
-                *
-                * NOTE : Used to call setRelhasrulesInRelation. The code
-                * was inlined so that two updates were not needed. mhh 31-aug-2000
                 */
-
-               /*
-                * Find the tuple to update in pg_class, using syscache for the lookup.
-                */
-               relationRelation = heap_openr(RelationRelationName, RowExclusiveLock);
-               tuple = SearchSysCacheTupleCopy(RELOID,
-                                                                               ObjectIdGetDatum(ev_relid),
-                                                                               0, 0, 0);
-               Assert(HeapTupleIsValid(tuple));
-
-               /* Do the update */
-               ((Form_pg_class) GETSTRUCT(tuple))->relhasrules = true;
-               if (RelisBecomingView)
-                       ((Form_pg_class) GETSTRUCT(tuple))->relkind = RELKIND_VIEW;
-
-               heap_update(relationRelation, &tuple->t_self, tuple, NULL);
-
-               /* Keep the catalog indices up to date */
-               CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs);
-               CatalogIndexInsert(idescs, Num_pg_class_indices, relationRelation, tuple);
-               CatalogCloseIndices(Num_pg_class_indices, idescs);
-
-               heap_freetuple(tuple);
-               heap_close(relationRelation, RowExclusiveLock);
+               SetRelationRuleStatus(ev_relid, true, RelisBecomingView);
        }
 
        /*
@@ -427,3 +413,60 @@ DefineQueryRewrite(RuleStmt *stmt)
        /* Close rel, but keep lock till commit... */
        heap_close(event_relation, NoLock);
 }
+
+/*
+ * setRuleCheckAsUser
+ *             Recursively scan a query and set the checkAsUser field to the
+ *             given userid in all rtable entries except *OLD* and *NEW*.
+ */
+static void
+setRuleCheckAsUser(Query *qry, Oid userid)
+{
+       List       *l;
+
+       /* Set all the RTEs in this query node, except OLD and NEW */
+       foreach(l, qry->rtable)
+       {
+               RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
+
+               if (strcmp(rte->eref->relname, "*NEW*") == 0)
+                       continue;
+               if (strcmp(rte->eref->relname, "*OLD*") == 0)
+                       continue;
+
+               if (rte->subquery)
+               {
+                       /*
+                        * Recurse into subquery in FROM
+                        */
+                       setRuleCheckAsUser(rte->subquery, userid);
+               }
+               else
+               {
+                       rte->checkAsUser = userid;
+               }
+       }
+
+       /* If there are sublinks, search for them and process their RTEs */
+       if (qry->hasSubLinks)
+               query_tree_walker(qry, setRuleCheckAsUser_walker, (void *) &userid);
+}
+
+/*
+ * Expression-tree walker to find sublink queries
+ */
+static bool
+setRuleCheckAsUser_walker(Node *node, Oid *context)
+{
+       if (node == NULL)
+               return false;
+       if (IsA(node, Query))
+       {
+               Query      *qry = (Query *) node;
+
+               setRuleCheckAsUser(qry, *context);
+               return false;
+       }
+       return expression_tree_walker(node, setRuleCheckAsUser_walker,
+                                                                 (void *) context);
+}
index 49dfae5..5d1e3a4 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.80 2000/09/12 21:07:03 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.81 2000/09/29 18:21:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "parser/parse_target.h"
 #include "parser/parsetree.h"
 #include "parser/parse_type.h"
-#include "rewrite/locks.h"
 #include "rewrite/rewriteManip.h"
-#include "utils/acl.h"
 #include "utils/lsyscache.h"
 
 
 extern void CheckSelectForUpdate(Query *rule_action);  /* in analyze.c */
 
 
-/* macros borrowed from expression_tree_mutator */
-
-#define FLATCOPY(newnode, node, nodetype)  \
-       ( (newnode) = makeNode(nodetype), \
-         memcpy((newnode), (node), sizeof(nodetype)) )
-
-#define MUTATE(newfield, oldfield, fieldtype, mutator, context)  \
-               ( (newfield) = (fieldtype) mutator((Node *) (oldfield), (context)) )
-
-
 static RewriteInfo *gatherRewriteMeta(Query *parsetree,
                                  Query *rule_action,
                                  Node *rule_qual,
                                  int rt_index,
                                  CmdType event,
                                  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);
+static List *adjustJoinTreeList(Query *parsetree, int rt_index, bool *found);
+static List *matchLocks(CmdType event, RuleLock *rulelocks,
+                                               int varno, Query *parsetree);
 static Query *fireRIRrules(Query *parsetree);
 static Query *Except_Intersect_Rewrite(Query *parsetree);
 static void check_targetlists_are_compatible(List *prev_target,
@@ -134,15 +118,16 @@ gatherRewriteMeta(Query *parsetree,
                 * 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);
+                       List   *newjointree = adjustJoinTreeList(parsetree,
+                                                                                                        rt_index,
+                                                                                                        &found);
 
-                       info->rule_action->jointree = nconc(newjointree,
-                                                                                               info->rule_action->jointree);
+                       info->rule_action->jointree->fromlist =
+                               nconc(newjointree,
+                                         info->rule_action->jointree->fromlist);
                }
 
                /*
@@ -175,13 +160,15 @@ gatherRewriteMeta(Query *parsetree,
 /*
  * 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
+ * within join items; this is OK because we are only expecting to find it
+ * as an UPDATE or DELETE target relation, which will be at the top level
+ * of the join).  Returns modified jointree list --- original list
  * is not changed.  *found is set to indicate if we found the rt_index.
  */
 static List *
-adjustJoinTree(Query *parsetree, int rt_index, bool *found)
+adjustJoinTreeList(Query *parsetree, int rt_index, bool *found)
 {
-       List       *newjointree = listCopy(parsetree->jointree);
+       List       *newjointree = listCopy(parsetree->jointree->fromlist);
        List       *jjt;
 
        *found = false;
@@ -201,559 +188,147 @@ adjustJoinTree(Query *parsetree, int rt_index, bool *found)
 
 
 /*
- * modifyAggrefChangeVarnodes -
- *     Change the var nodes in a sublink created for an aggregate column
- *     used in the qualification to point to the correct local RTE.
- *
- * XXX if we still need this after redoing querytree design, it should
- * be combined with ChangeVarNodes, which is the same thing except for
- * not having the option to adjust the vars' varlevelsup.
- *
- * 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
- * earlier to ensure that no unwanted side-effects occur!
+ * matchLocks -
+ *       match the list of locks and returns the matching rules
  */
-
-typedef struct
+static List *
+matchLocks(CmdType event,
+                  RuleLock *rulelocks,
+                  int varno,
+                  Query *parsetree)
 {
-       int                     rt_index;
-       int                     new_index;
-       int                     sublevels_up;
-       int                     new_sublevels_up;
-} modifyAggrefChangeVarnodes_context;
+       List       *real_locks = NIL;
+       int                     nlocks;
+       int                     i;
 
-static bool
-modifyAggrefChangeVarnodes_walker(Node *node,
-                                                        modifyAggrefChangeVarnodes_context *context)
-{
-       if (node == NULL)
-               return false;
-       if (IsA(node, Var))
-       {
-               Var                *var = (Var *) node;
+       Assert(rulelocks != NULL);      /* we get called iff there is some lock */
+       Assert(parsetree != NULL);
 
-               if (var->varlevelsup == context->sublevels_up &&
-                       var->varno == context->rt_index)
-               {
-                       var->varno = context->new_index;
-                       var->varnoold = context->new_index;
-                       var->varlevelsup = context->new_sublevels_up;
-               }
-               return false;
-       }
-       if (IsA(node, Query))
+       if (parsetree->commandType != CMD_SELECT)
        {
-               /* 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;
+               if (parsetree->resultRelation != varno)
+                       return NIL;
        }
-       return expression_tree_walker(node, modifyAggrefChangeVarnodes_walker,
-                                                                 (void *) context);
-}
 
-static bool
-modifyAggrefChangeVarnodes(Query *query, int rt_index, int new_index,
-                                                  int sublevels_up, int new_sublevels_up)
-{
-       modifyAggrefChangeVarnodes_context context;
-
-       context.rt_index = rt_index;
-       context.new_index = new_index;
-       context.sublevels_up = sublevels_up;
-       context.new_sublevels_up = new_sublevels_up;
-       return query_tree_walker(query, modifyAggrefChangeVarnodes_walker,
-                                                        (void *) &context);
-}
+       nlocks = rulelocks->numLocks;
 
-
-/*
- * modifyAggrefDropQual -
- *     remove the pure aggref clause from a qualification
- *
- * targetNode is an Aggref node somewhere within the given expression tree.
- * Find the boolean operator that's presumably somewhere above it, and replace
- * that whole operator expression with a constant TRUE.  (This is NOT really
- * quite the right thing, but it handles simple cases. This whole set of
- * Aggref-in-qual routines needs to be thrown away when we can do subselects
- * in FROM.)
- *
- * The return tree is a modified copy of the given tree; the given tree
- * is not altered.
- *
- * Note: we don't recurse into subselects looking for targetNode; that's
- * not necessary in the current usage, since in fact targetNode will be
- * within the same select level as the given toplevel node.
- */
-static Node *
-modifyAggrefDropQual(Node *node, Node *targetNode)
-{
-       if (node == NULL)
-               return NULL;
-       if (node == targetNode)
-       {
-               /* Oops, it's not inside an Expr we can rearrange... */
-               elog(ERROR, "Cannot handle aggregate function inserted at this place in WHERE clause");
-       }
-       if (IsA(node, Expr))
+       for (i = 0; i < nlocks; i++)
        {
-               Expr       *expr = (Expr *) node;
-               List       *i;
+               RewriteRule *oneLock = rulelocks->rules[i];
 
-               foreach(i, expr->args)
+               if (oneLock->event == event)
                {
-                       if (((Node *) lfirst(i)) == targetNode)
-                       {
-                               /* Found the parent expression containing the Aggref */
-                               if (expr->typeOid != BOOLOID)
-                                       elog(ERROR,
-                                                "aggregate function in qual must be argument of boolean operator");
-                               return (Node *) makeConst(BOOLOID, 1, (Datum) true,
-                                                                                 false, true, false, false);
-                       }
+                       if (parsetree->commandType != CMD_SELECT ||
+                               (oneLock->attrno == -1 ?
+                                rangeTableEntry_used((Node *) parsetree, varno, 0) :
+                                attribute_used((Node *) parsetree,
+                                                               varno, oneLock->attrno, 0)))
+                               real_locks = lappend(real_locks, oneLock);
                }
-               /* else this isn't the expr we want, keep going */
        }
-       return expression_tree_mutator(node, modifyAggrefDropQual,
-                                                                  (void *) targetNode);
+
+       return real_locks;
 }
 
-/*
- * modifyAggrefMakeSublink -
- *     Create a sublink node for a qualification expression that
- *     uses an aggregate column of a view
- */
-static SubLink *
-modifyAggrefMakeSublink(Aggref *aggref, Query *parsetree)
-{
-       List       *aggVarNos;
 
-       /* rte points to old structure: */
-       RangeTblEntry *rte;
+static Query *
+ApplyRetrieveRule(Query *parsetree,
+                                 RewriteRule *rule,
+                                 int rt_index,
+                                 bool relation_level,
+                                 Relation relation,
+                                 bool relIsUsed)
+{
+       Query      *rule_action;
+       RangeTblEntry *rte,
+                          *subrte;
+       List       *l;
 
-       /* these point to newly-created structures: */
-       Query      *subquery;
-       SubLink    *sublink;
-       TargetEntry *tle;
-       Resdom     *resdom;
-       RangeTblRef *rtr;
-
-       aggVarNos = pull_varnos(aggref->target);
-       if (length(aggVarNos) != 1)
-               elog(ERROR, "rewrite: aggregates of views only allowed on single tables for now");
-       rte = rt_fetch(lfirsti(aggVarNos), parsetree->rtable);
-
-       resdom = makeNode(Resdom);
-       resdom->resno = 1;
-       resdom->restype = aggref->aggtype;
-       resdom->restypmod = -1;
-       resdom->resname = pstrdup("<noname>");
-       resdom->reskey = 0;
-       resdom->reskeyop = 0;
-       resdom->resjunk = false;
-
-       tle = makeNode(TargetEntry);
-       tle->resdom = resdom;
-       tle->expr = copyObject(aggref);         /* make a modifiable copy! */
-
-       subquery = makeNode(Query);
-
-       sublink = makeNode(SubLink);
-       sublink->subLinkType = EXPR_SUBLINK;
-       sublink->useor = false;
-       sublink->lefthand = NIL;
-       sublink->oper = NIL;
-       sublink->subselect = (Node *) subquery;
-
-       subquery->commandType = CMD_SELECT;
-       subquery->utilityStmt = NULL;
-       subquery->resultRelation = 0;
-       subquery->into = NULL;
-       subquery->isPortal = FALSE;
-       subquery->isBinary = FALSE;
-       subquery->isTemp = FALSE;
-       subquery->unionall = FALSE;
-       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);
+       if (length(rule->actions) != 1)
+               elog(ERROR, "ApplyRetrieveRule: expected just one rule action");
+       if (rule->qual != NULL)
+               elog(ERROR, "ApplyRetrieveRule: can't handle qualified ON SELECT rule");
+       if (! relation_level)
+               elog(ERROR, "ApplyRetrieveRule: can't handle per-attribute ON SELECT rule");
 
        /*
-        * If there are still aggs in the subselect's qual, give up. Recursing
-        * would be a bad idea --- we'd likely produce an infinite recursion.
-        * This whole technique is a crock, really...
+        * Make a modifiable copy of the view query, and recursively expand
+        * any view references inside it.
         */
-       if (checkExprHasAggs(subquery->qual))
-               elog(ERROR, "Cannot handle multiple aggregate functions in WHERE clause");
-       subquery->groupClause = NIL;
-       subquery->havingQual = NULL;
-       subquery->hasAggs = TRUE;
-       subquery->hasSubLinks = checkExprHasSubLink(subquery->qual);
-       subquery->unionClause = NULL;
+       rule_action = copyObject(lfirst(rule->actions));
 
-       /* Increment all varlevelsup fields in the new subquery */
-       IncrementVarSublevelsUp((Node *) subquery, 1, 0);
+       rule_action = fireRIRrules(rule_action);
 
        /*
-        * Replace references to the target table with correct local varno, 1.
-        * Note that because of previous line, these references have
-        * varlevelsup = 1, which must be changed to 0.
+        * VIEWs are really easy --- just plug the view query in as a subselect,
+        * replacing the relation's original RTE.
         */
-       modifyAggrefChangeVarnodes(subquery,
-                                                          lfirsti(aggVarNos), 1,
-                                                          1, 0);
+       rte = rt_fetch(rt_index, parsetree->rtable);
 
-       return sublink;
-}
-
-
-/*
- * modifyAggrefQual -
- *     Search for qualification expressions that contain aggregate
- *     functions and substitute them by sublinks. These expressions
- *     originally come from qualifications that use aggregate columns
- *     of a view.
- *
- *     The return value is a modified copy of the given expression tree.
- */
-static Node *
-modifyAggrefQual(Node *node, Query *parsetree)
-{
-       if (node == NULL)
-               return NULL;
-       if (IsA(node, Aggref))
-       {
-               SubLink    *sub = modifyAggrefMakeSublink((Aggref *) node, parsetree);
-
-               parsetree->hasSubLinks = true;
-               return (Node *) sub;
-       }
+       rte->relname = NULL;
+       rte->relid = InvalidOid;
+       rte->subquery = rule_action;
+       rte->inh = false;                       /* must not be set for a subquery */
 
        /*
-        * Otherwise, fall through and copy the expr normally.
-        *
-        * We do NOT recurse into subselects in this routine.  It's sufficient to
-        * get rid of aggregates that are in the qual expression proper.
+        * We move the view's permission check data down to its rangetable.
+        * The checks will actually be done against the *OLD* entry therein.
         */
-       return expression_tree_mutator(node, modifyAggrefQual,
-                                                                  (void *) parsetree);
-}
+       subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable);
+       Assert(subrte->relid == relation->rd_id);
+       subrte->checkForRead = rte->checkForRead;
+       subrte->checkForWrite = rte->checkForWrite;
 
-
-static Node *
-FindMatchingTLEntry(List *tlist, char *e_attname)
-{
-       List       *i;
-
-       foreach(i, tlist)
-       {
-               TargetEntry *tle = lfirst(i);
-               char       *resname;
-
-               resname = tle->resdom->resname;
-               if (!strcmp(e_attname, resname))
-                       return (tle->expr);
-       }
-       return NULL;
-}
-
-
-static Node *
-make_null(Oid type)
-{
-       Const      *c = makeNode(Const);
-
-       c->consttype = type;
-       c->constlen = get_typlen(type);
-       c->constvalue = PointerGetDatum(NULL);
-       c->constisnull = true;
-       c->constbyval = get_typbyval(type);
-       return (Node *) c;
-}
-
-
-/*
- * apply_RIR_view
- *     Replace Vars matching a given RT index with copies of TL expressions.
- */
-
-typedef struct
-{
-       int                     rt_index;
-       int                     sublevels_up;
-       RangeTblEntry *rte;
-       List       *tlist;
-       int                *modified;
-} apply_RIR_view_context;
-
-static Node *
-apply_RIR_view_mutator(Node *node,
-                                          apply_RIR_view_context *context)
-{
-       if (node == NULL)
-               return NULL;
-       if (IsA(node, Var))
-       {
-               Var                *var = (Var *) node;
-
-               if (var->varlevelsup == context->sublevels_up &&
-                       var->varno == context->rt_index)
-               {
-                       Node       *expr;
-
-                       if (var->varattno < 0)
-                               elog(ERROR, "system column %s not available - %s is a view",
-                                        get_attname(context->rte->relid, var->varattno),
-                                        context->rte->relname);
-
-                       expr = FindMatchingTLEntry(context->tlist,
-                                                                          get_attname(context->rte->relid,
-                                                                                                  var->varattno));
-                       if (expr == NULL)
-                       {
-                               /* XXX shouldn't this be an error condition? */
-                               return make_null(var->vartype);
-                       }
-
-                       /* Make a copy of the tlist item to return */
-                       expr = copyObject(expr);
-                       /* Adjust varlevelsup if tlist item is from higher query level */
-                       if (var->varlevelsup > 0)
-                               IncrementVarSublevelsUp(expr, var->varlevelsup, 0);
-
-                       *(context->modified) = true;
-                       return (Node *) expr;
-               }
-               /* otherwise fall through to copy the var normally */
-       }
+       rte->checkForRead = false;      /* no permission check on subquery itself */
+       rte->checkForWrite = false;
 
        /*
-        * Since expression_tree_mutator won't touch subselects, we have to
-        * handle them specially.
+        * FOR UPDATE of view?
         */
-       if (IsA(node, SubLink))
-       {
-               SubLink    *sublink = (SubLink *) node;
-               SubLink    *newnode;
-
-               FLATCOPY(newnode, sublink, SubLink);
-               MUTATE(newnode->lefthand, sublink->lefthand, List *,
-                          apply_RIR_view_mutator, context);
-               context->sublevels_up++;
-               MUTATE(newnode->subselect, sublink->subselect, Node *,
-                          apply_RIR_view_mutator, context);
-               context->sublevels_up--;
-               return (Node *) newnode;
-       }
-       if (IsA(node, Query))
+       if (intMember(rt_index, parsetree->rowMarks))
        {
-               Query      *query = (Query *) node;
-               Query      *newnode;
-
-               FLATCOPY(newnode, query, Query);
-               MUTATE(newnode->targetList, query->targetList, List *,
-                          apply_RIR_view_mutator, context);
-               MUTATE(newnode->qual, query->qual, 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,
-                                                                  (void *) context);
-}
-
-static Node *
-apply_RIR_view(Node *node, int rt_index, RangeTblEntry *rte, List *tlist,
-                          int *modified, int sublevels_up)
-{
-       apply_RIR_view_context context;
-
-       context.rt_index = rt_index;
-       context.sublevels_up = sublevels_up;
-       context.rte = rte;
-       context.tlist = tlist;
-       context.modified = modified;
-
-       return apply_RIR_view_mutator(node, &context);
-}
+               Index           innerrti = 1;
 
+               CheckSelectForUpdate(rule_action);
 
-static Query *
-ApplyRetrieveRule(Query *parsetree,
-                                 RewriteRule *rule,
-                                 int rt_index,
-                                 int relation_level,
-                                 Relation relation,
-                                 bool relIsUsed)
-{
-       Query      *rule_action = NULL;
-       Node       *rule_qual;
-       List       *rtable,
-                          *addedrtable,
-                          *l;
-       int                     nothing,
-                               rt_length;
-       int                     modified = false;
-       int                     badsql = false;
-
-       rule_qual = rule->qual;
-       if (rule->actions)
-       {
-               if (length(rule->actions) > 1)  /* ??? because we don't handle
-                                                                                * rules with more than one
-                                                                                * action? -ay */
-
-                       return parsetree;
-               rule_action = copyObject(lfirst(rule->actions));
-               nothing = FALSE;
-       }
-       else
-               nothing = TRUE;
-
-       rtable = copyObject(parsetree->rtable);
-       rt_length = length(rtable); /* original length, not counting rule */
-
-       addedrtable = copyObject(rule_action->rtable);
-
-       /*
-        * 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 (relIsUsed)
-       {
                /*
-                * 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 ...)
+                * Remove the view from the list of rels that will actually be
+                * marked FOR UPDATE by the executor.  It will still be access-
+                * checked for write access, though.
                 */
-               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);
-       parsetree->rtable = rtable;
-
-       /* FOR UPDATE of view... */
-       foreach(l, parsetree->rowMark)
-       {
-               if (((RowMark *) lfirst(l))->rti == rt_index)
-                       break;
-       }
-       if (l != NULL)                          /* oh, hell -:) */
-       {
-               RowMark    *newrm;
-               Index           rti = 1;
-               List       *l2;
-
-               CheckSelectForUpdate(rule_action);
+               parsetree->rowMarks = lremovei(rt_index, parsetree->rowMarks);
 
                /*
-                * We believe that rt_index is VIEW - nothing should be marked for
-                * VIEW, but ACL check must be done. As for real tables of VIEW -
-                * their rows must be marked, but we have to skip ACL check for
-                * them.
+                * Set up the view's referenced tables as if FOR UPDATE.
                 */
-               ((RowMark *) lfirst(l))->info &= ~ROW_MARK_FOR_UPDATE;
-
-               foreach(l2, rule_action->rtable)
+               foreach(l, rule_action->rtable)
                {
+                       subrte = (RangeTblEntry *) lfirst(l);
 
                        /*
-                        * RTable of VIEW has two entries of VIEW itself - we use
-                        * relid to skip them.
+                        * RTable of VIEW has two entries of VIEW itself - skip them!
+                        * Also keep hands off of sub-subqueries.
                         */
-                       if (relation->rd_id != ((RangeTblEntry *) lfirst(l2))->relid)
+                       if (innerrti != PRS2_OLD_VARNO && innerrti != PRS2_NEW_VARNO &&
+                               subrte->relid != InvalidOid)
                        {
-                               newrm = makeNode(RowMark);
-                               newrm->rti = rti + rt_length;
-                               newrm->info = ROW_MARK_FOR_UPDATE;
-                               lnext(l) = lcons(newrm, lnext(l));
-                               l = lnext(l);
+                               if (!intMember(innerrti, rule_action->rowMarks))
+                                       rule_action->rowMarks = lappendi(rule_action->rowMarks,
+                                                                                                        innerrti);
+                               subrte->checkForWrite = true;
                        }
-                       rti++;
+                       innerrti++;
                }
        }
 
-       rule_action->rtable = rtable;
-       OffsetVarNodes((Node *) rule_qual, rt_length, 0);
-       OffsetVarNodes((Node *) rule_action, rt_length, 0);
-
-       ChangeVarNodes((Node *) rule_qual,
-                                  PRS2_OLD_VARNO + rt_length, rt_index, 0);
-       ChangeVarNodes((Node *) rule_action,
-                                  PRS2_OLD_VARNO + rt_length, rt_index, 0);
-
-       if (relation_level)
-       {
-               RangeTblEntry *rte = rt_fetch(rt_index, rtable);
-
-               parsetree = (Query *) apply_RIR_view((Node *) parsetree,
-                                                                                        rt_index, rte,
-                                                                                        rule_action->targetList,
-                                                                                        &modified, 0);
-               rule_action = (Query *) apply_RIR_view((Node *) rule_action,
-                                                                                          rt_index, rte,
-                                                                                          rule_action->targetList,
-                                                                                          &modified, 0);
-               /* always apply quals of relation-level rules, whether we found a
-                * var to substitute or not.
-                */
-               modified = true;
-       }
-       else
-       {
-               HandleRIRAttributeRule(parsetree, rtable, rule_action->targetList,
-                                                          rt_index, rule->attrno, &modified, &badsql);
-               /* quals will be inserted only if we found uses of the attribute */
-       }
-       if (modified && !badsql)
-       {
-               AddQual(parsetree, rule_action->qual);
-               AddGroupClause(parsetree, rule_action->groupClause,
-                                          rule_action->targetList);
-               AddHavingQual(parsetree, rule_action->havingQual);
-               parsetree->hasAggs = (rule_action->hasAggs || parsetree->hasAggs);
-               parsetree->hasSubLinks = (rule_action->hasSubLinks || parsetree->hasSubLinks);
-       }
-
        return parsetree;
 }
 
 
 /*
- * fireRIRonSubselect -
- *     Apply fireRIRrules() to each subselect found in the given tree.
+ * fireRIRonSubLink -
+ *     Apply fireRIRrules() to each SubLink (subselect in expression) found
+ *     in the given tree.
  *
  * 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
@@ -764,7 +339,7 @@ ApplyRetrieveRule(Query *parsetree,
  * the SubLink's subselect link with the possibly-rewritten subquery.
  */
 static bool
-fireRIRonSubselect(Node *node, void *context)
+fireRIRonSubLink(Node *node, void *context)
 {
        if (node == NULL)
                return false;
@@ -778,9 +353,9 @@ fireRIRonSubselect(Node *node, void *context)
        }
        /*
         * Do NOT recurse into Query nodes, because fireRIRrules already
-        * processed subselects for us.
+        * processed subselects of subselects for us.
         */
-       return expression_tree_walker(node, fireRIRonSubselect,
+       return expression_tree_walker(node, fireRIRonSubLink,
                                                                  (void *) context);
 }
 
@@ -798,7 +373,6 @@ fireRIRrules(Query *parsetree)
        List       *locks;
        RuleLock   *rules;
        RewriteRule *rule;
-       RewriteRule RIRonly;
        bool            relIsUsed;
        int                     i;
        List       *l;
@@ -815,6 +389,17 @@ fireRIRrules(Query *parsetree)
                rte = rt_fetch(rt_index, parsetree->rtable);
 
                /*
+                * A subquery RTE can't have associated rules, so there's nothing
+                * to do to this level of the query, but we must recurse into the
+                * subquery to expand any rule references in it.
+                */
+               if (rte->subquery)
+               {
+                       rte->subquery = fireRIRrules(rte->subquery);
+                       continue;
+               }
+
+               /*
                 * 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
@@ -856,26 +441,16 @@ fireRIRrules(Query *parsetree)
                }
 
                /*
-                * Check permissions
-                */
-               checkLockPerms(locks, parsetree, rt_index);
-
-               /*
                 * Now apply them
                 */
                foreach(l, locks)
                {
                        rule = lfirst(l);
 
-                       RIRonly.event = rule->event;
-                       RIRonly.attrno = rule->attrno;
-                       RIRonly.qual = rule->qual;
-                       RIRonly.actions = rule->actions;
-
                        parsetree = ApplyRetrieveRule(parsetree,
-                                                                                 &RIRonly,
+                                                                                 rule,
                                                                                  rt_index,
-                                                                                 RIRonly.attrno == -1,
+                                                                                 rule->attrno == -1,
                                                                                  rel,
                                                                                  relIsUsed);
                }
@@ -883,11 +458,30 @@ fireRIRrules(Query *parsetree)
                heap_close(rel, AccessShareLock);
        }
 
-       if (parsetree->hasAggs)
-               parsetree->qual = modifyAggrefQual(parsetree->qual, parsetree);
+       /*
+        * Recurse into sublink subqueries, too.
+        */
+       if (parsetree->hasSubLinks)
+               query_tree_walker(parsetree, fireRIRonSubLink, NULL);
 
+       /*
+        * If the query was marked having aggregates, check if this is
+        * still true after rewriting.  Ditto for sublinks.  Note there
+        * should be no aggs in the qual at this point.  (Does this code
+        * still do anything useful?  The view-becomes-subselect-in-FROM
+        * approach doesn't look like it could remove aggs or sublinks...)
+        */
+       if (parsetree->hasAggs)
+       {
+               parsetree->hasAggs = checkExprHasAggs((Node *) parsetree);
+               if (parsetree->hasAggs)
+                       if (checkExprHasAggs((Node *) parsetree->jointree))
+                               elog(ERROR, "fireRIRrules: failed to remove aggs from qual");
+       }
        if (parsetree->hasSubLinks)
-               query_tree_walker(parsetree, fireRIRonSubselect, NULL);
+       {
+               parsetree->hasSubLinks = checkExprHasSubLink((Node *) parsetree);
+       }
 
        return parsetree;
 }
@@ -919,8 +513,7 @@ orderRules(List *locks)
                else
                        regular = lappend(regular, rule_lock);
        }
-       regular = nconc(regular, instead_qualified);
-       return nconc(regular, instead_rules);
+       return nconc(nconc(regular, instead_qualified), instead_rules);
 }
 
 
@@ -944,20 +537,20 @@ CopyAndAddQual(Query *parsetree,
        {
                List       *rtable;
                int                     rt_length;
-               List       *jointree;
+               List       *jointreelist;
 
                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,
+               jointreelist = copyObject(rule_action->jointree->fromlist);
+               OffsetVarNodes((Node *) jointreelist, rt_length, 0);
+               ChangeVarNodes((Node *) jointreelist, PRS2_OLD_VARNO + rt_length,
                                           rt_index, 0);
-               new_tree->jointree = nconc(new_tree->jointree, jointree);
+               new_tree->jointree->fromlist = nconc(new_tree->jointree->fromlist,
+                                                                                        jointreelist);
        }
        /* XXX -- where current doesn't work for instead nothing.... yet */
        AddNotQual(new_tree, new_qual);
@@ -995,6 +588,7 @@ fireRules(Query *parsetree,
                return NIL;
 
        locks = orderRules(locks);      /* real instead rules last */
+
        foreach(i, locks)
        {
                RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
@@ -1002,49 +596,11 @@ fireRules(Query *parsetree,
                List       *actions;
                List       *r;
 
-               /*
-                * Instead rules change the resultRelation of the query. So the
-                * permission checks on the initial resultRelation would never be
-                * done (this is normally done in the executor deep down). So we
-                * must do it here. The result relations resulting from earlier
-                * rewrites are already checked against the rules eventrelation
-                * owner (during matchLocks) and have the skipAcl flag set.
-                */
-               if (rule_lock->isInstead &&
-                       parsetree->commandType != CMD_SELECT)
-               {
-                       RangeTblEntry *rte;
-                       int32           acl_rc;
-                       int32           reqperm;
-
-                       switch (parsetree->commandType)
-                       {
-                               case CMD_INSERT:
-                                       reqperm = ACL_AP;
-                                       break;
-                               default:
-                                       reqperm = ACL_WR;
-                                       break;
-                       }
-
-                       rte = rt_fetch(parsetree->resultRelation, parsetree->rtable);
-                       if (!rte->skipAcl)
-                       {
-                               acl_rc = pg_aclcheck(rte->relname,
-                                                                        GetUserId(), reqperm);
-                               if (acl_rc != ACLCHECK_OK)
-                               {
-                                       elog(ERROR, "%s: %s",
-                                                rte->relname,
-                                                aclcheck_error_strings[acl_rc]);
-                               }
-                       }
-               }
-
                /* multiple rule action time */
                *instead_flag = rule_lock->isInstead;
                event_qual = rule_lock->qual;
                actions = rule_lock->actions;
+
                if (event_qual != NULL && *instead_flag)
                {
                        Query      *qual_product;
@@ -1065,7 +621,7 @@ fireRules(Query *parsetree,
                        if (*qual_products == NIL)
                                qual_product = parsetree;
                        else
-                               qual_product = (Query *) nth(0, *qual_products);
+                               qual_product = (Query *) lfirst(*qual_products);
 
                        MemSet(&qual_info, 0, sizeof(qual_info));
                        qual_info.event = qual_product->commandType;
@@ -1083,7 +639,7 @@ fireRules(Query *parsetree,
                        if (event == CMD_INSERT || event == CMD_UPDATE)
                                FixNew(&qual_info, qual_product);
 
-                       *qual_products = lappend(NIL, qual_product);
+                       *qual_products = makeList1(qual_product);
                }
 
                foreach(r, actions)
@@ -1141,10 +697,10 @@ fireRules(Query *parsetree,
                         * splitting into two queries one w/rule_qual, one w/NOT
                         * rule_qual. Also add user query qual onto rule action
                         */
-                       AddQual(info->rule_action, parsetree->qual);
-
                        AddQual(info->rule_action, info->rule_qual);
 
+                       AddQual(info->rule_action, parsetree->jointree->quals);
+
                        /*--------------------------------------------------
                         * Step 2:
                         *        Rewrite new.attribute w/ right hand side of target-list
@@ -1183,10 +739,10 @@ RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products)
 {
        CmdType         event;
        List       *product_queries = NIL;
-       int                     result_relation = 0;
+       int                     result_relation;
        RangeTblEntry *rt_entry;
-       Relation        rt_entry_relation = NULL;
-       RuleLock   *rt_entry_locks = NULL;
+       Relation        rt_entry_relation;
+       RuleLock   *rt_entry_locks;
 
        Assert(parsetree != NULL);
 
@@ -1251,7 +807,7 @@ deepRewriteQuery(Query *parsetree)
 {
        List       *n;
        List       *rewritten = NIL;
-       List       *result = NIL;
+       List       *result;
        bool            instead;
        List       *qual_products = NIL;
 
@@ -1267,7 +823,7 @@ deepRewriteQuery(Query *parsetree)
        foreach(n, result)
        {
                Query      *pt = lfirst(n);
-               List       *newstuff = NIL;
+               List       *newstuff;
 
                newstuff = deepRewriteQuery(pt);
                if (newstuff != NIL)
@@ -1343,23 +899,6 @@ BasicQueryRewrite(Query *parsetree)
        foreach(l, querylist)
        {
                query = fireRIRrules((Query *) lfirst(l));
-
-               /*
-                * If the query was marked having aggregates, check if this is
-                * still true after rewriting.  Ditto for sublinks.  Note there
-                * should be no aggs in the qual at this point.
-                */
-               if (query->hasAggs)
-               {
-                       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);
-               }
                results = lappend(results, query);
        }
 
@@ -1498,7 +1037,8 @@ check_targetlists_are_compatible(List *prev_target, List *current_target)
        }
 }
 
-/* Rewrites UNION INTERSECT and EXCEPT queries to semantiacally equivalent
+/*
+ * Rewrites UNION INTERSECT and EXCEPT queries to semantically equivalent
  * queries that use IN and NOT IN subselects.
  *
  * The operator tree is attached to 'intersectClause' (see rule
@@ -1516,7 +1056,8 @@ check_targetlists_are_compatible(List *prev_target, List *current_target)
  * (sortClause etc) are attached to the new top Node (Note that the
  * new top Node can differ from the parsetree given as argument because of
  * the translation to DNF. That's why we have to remember the sortClause
- * and so on!) */
+ * and so on!)
+ */
 static Query *
 Except_Intersect_Rewrite(Query *parsetree)
 {
@@ -1829,11 +1370,12 @@ Except_Intersect_Rewrite(Query *parsetree)
        return result;
 }
 
-/* Create a list of nodes that are either Query nodes of NOT Expr
+/*
+ * Create a list of nodes that are either Query nodes of NOT Expr
  * nodes followed by a Query node. The tree given in ptr contains at
  * least one non negated Query node. This node is attached to the
- * beginning of the list */
-
+ * beginning of the list.
+ */
 static void
 create_intersect_list(Node *ptr, List **intersect_list)
 {
@@ -1864,10 +1406,12 @@ create_intersect_list(Node *ptr, List **intersect_list)
        }
 }
 
-/* The nodes given in 'tree' are still 'raw' so 'cook' them using parse_analyze().
- * The node given in first_select has already been cooked, so don't transform
- * it again but return a pointer to the previously cooked version given in 'parsetree'
- * instead. */
+/*
+ * The nodes given in 'tree' are still 'raw' so 'cook' them using
+ * parse_analyze().  The node given in first_select has already been cooked,
+ * so don't transform it again but return a pointer to the previously cooked
+ * version given in 'parsetree' instead.
+ */
 static Node *
 intersect_tree_analyze(Node *tree, Node *first_select, Node *parsetree)
 {
@@ -1886,7 +1430,7 @@ intersect_tree_analyze(Node *tree, Node *first_select, Node *parsetree)
                else
                {
                        /* transform the 'raw' nodes to 'cooked' Query nodes */
-                       List       *qtree = parse_analyze(lcons(tree, NIL), NULL);
+                       List       *qtree = parse_analyze(makeList1(tree), NULL);
 
                        result = (Node *) lfirst(qtree);
                }
index e83ac05..0b07b5e 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.48 2000/09/12 21:07:04 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.49 2000/09/29 18:21:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -473,8 +473,7 @@ attribute_used(Node *node, int rt_index, int attno, int sublevels_up)
 void
 AddQual(Query *parsetree, Node *qual)
 {
-       Node       *copy,
-                          *old;
+       Node       *copy;
 
        if (qual == NULL)
                return;
@@ -482,11 +481,8 @@ AddQual(Query *parsetree, Node *qual)
        /* INTERSECT want's the original, but we need to copy - Jan */
        copy = copyObject(qual);
 
-       old = parsetree->qual;
-       if (old == NULL)
-               parsetree->qual = copy;
-       else
-               parsetree->qual = (Node *) make_andclause(makeList(old, copy, -1));
+       parsetree->jointree->quals = make_and_qual(parsetree->jointree->quals,
+                                                                                          copy);
 
        /*
         * Make sure query is marked correctly if added qual has sublinks or
@@ -504,8 +500,7 @@ AddQual(Query *parsetree, Node *qual)
 void
 AddHavingQual(Query *parsetree, Node *havingQual)
 {
-       Node       *copy,
-                          *old;
+       Node       *copy;
 
        if (havingQual == NULL)
                return;
@@ -513,11 +508,8 @@ AddHavingQual(Query *parsetree, Node *havingQual)
        /* INTERSECT want's the original, but we need to copy - Jan */
        copy = copyObject(havingQual);
 
-       old = parsetree->havingQual;
-       if (old == NULL)
-               parsetree->havingQual = copy;
-       else
-               parsetree->havingQual = (Node *) make_andclause(makeList(old, copy, -1));
+       parsetree->havingQual = make_and_qual(parsetree->havingQual,
+                                                                                 copy);
 
        /*
         * Make sure query is marked correctly if added qual has sublinks or
@@ -560,45 +552,7 @@ AddNotQual(Query *parsetree, Node *qual)
 }
 
 
-/*
- * Add all expressions used by the given GroupClause list to the
- * parsetree's targetlist and groupclause list.
- *
- * tlist is the old targetlist associated with the input groupclauses.
- *
- * XXX shouldn't we be checking to see if there are already matching
- * entries in parsetree->targetlist?
- */
-void
-AddGroupClause(Query *parsetree, List *group_by, List *tlist)
-{
-       List       *l;
-
-       foreach(l, group_by)
-       {
-               GroupClause *groupclause = (GroupClause *) copyObject(lfirst(l));
-               TargetEntry *tle = get_sortgroupclause_tle(groupclause, tlist);
-
-               /* copy the groupclause's TLE from the old tlist */
-               tle = (TargetEntry *) copyObject(tle);
-
-               /*
-                * The ressortgroupref number in the old tlist might be already
-                * taken in the new tlist, so force assignment of a new number.
-                */
-               tle->resdom->ressortgroupref = 0;
-               groupclause->tleSortGroupRef =
-                       assignSortGroupRef(tle, parsetree->targetList);
-
-               /* Also need to set the resno and mark it resjunk. */
-               tle->resdom->resno = length(parsetree->targetList) + 1;
-               tle->resdom->resjunk = true;
-
-               parsetree->targetList = lappend(parsetree->targetList, tle);
-               parsetree->groupClause = lappend(parsetree->groupClause, groupclause);
-       }
-}
-
+/* Build a NULL constant expression of the given type */
 static Node *
 make_null(Oid type)
 {
@@ -612,28 +566,6 @@ make_null(Oid type)
        return (Node *) c;
 }
 
-#ifdef NOT_USED
-void
-FixResdomTypes(List *tlist)
-{
-       List       *i;
-
-       foreach(i, tlist)
-       {
-               TargetEntry *tle = lfirst(i);
-
-               if (nodeTag(tle->expr) == T_Var)
-               {
-                       Var                *var = (Var *) tle->expr;
-
-                       tle->resdom->restype = var->vartype;
-                       tle->resdom->restypmod = var->vartypmod;
-               }
-       }
-}
-
-#endif
-
 /* Find a targetlist entry by resno */
 static Node *
 FindMatchingNew(List *tlist, int attno)
@@ -650,6 +582,8 @@ FindMatchingNew(List *tlist, int attno)
        return NULL;
 }
 
+#ifdef NOT_USED
+
 /* Find a targetlist entry by resname */
 static Node *
 FindMatchingTLEntry(List *tlist, char *e_attname)
@@ -662,25 +596,31 @@ FindMatchingTLEntry(List *tlist, char *e_attname)
                char       *resname;
 
                resname = tle->resdom->resname;
-               if (!strcmp(e_attname, resname))
+               if (strcmp(e_attname, resname) == 0)
                        return tle->expr;
        }
        return NULL;
 }
 
+#endif
+
 
 /*
  * ResolveNew - replace Vars with corresponding items from a targetlist
  *
- * Vars matching info->new_varno and sublevels_up are replaced by the
+ * Vars matching target_varno and sublevels_up are replaced by the
  * entry with matching resno from targetlist, if there is one.
+ * If not, we either change the unmatched Var's varno to update_varno
+ * (when event == CMD_UPDATE) or replace it with a constant NULL.
  */
 
 typedef struct
 {
-       RewriteInfo *info;
-       List       *targetlist;
+       int                     target_varno;
        int                     sublevels_up;
+       List       *targetlist;
+       int                     event;
+       int                     update_varno;
 } ResolveNew_context;
 
 static Node *
@@ -694,7 +634,7 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context)
                int                     this_varno = (int) var->varno;
                int                     this_varlevelsup = (int) var->varlevelsup;
 
-               if (this_varno == context->info->new_varno &&
+               if (this_varno == context->target_varno &&
                        this_varlevelsup == context->sublevels_up)
                {
                        Node       *n = FindMatchingNew(context->targetlist,
@@ -702,13 +642,13 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context)
 
                        if (n == NULL)
                        {
-                               if (context->info->event == CMD_UPDATE)
+                               if (context->event == CMD_UPDATE)
                                {
                                        /* For update, just change unmatched var's varno */
-                                       n = copyObject(node);
-                                       ((Var *) n)->varno = context->info->current_varno;
-                                       ((Var *) n)->varnoold = context->info->current_varno;
-                                       return n;
+                                       var = (Var *) copyObject(node);
+                                       var->varno = context->update_varno;
+                                       var->varnoold = context->update_varno;
+                                       return (Node *) var;
                                }
                                else
                                {
@@ -755,54 +695,68 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context)
                FLATCOPY(newnode, query, Query);
                MUTATE(newnode->targetList, query->targetList, List *,
                           ResolveNew_mutator, context);
-               MUTATE(newnode->qual, query->qual, Node *,
+               MUTATE(newnode->jointree, query->jointree, FromExpr *,
                           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,
                                                                   (void *) context);
 }
 
-static Node *
-ResolveNew(Node *node, RewriteInfo *info, List *targetlist,
-                  int sublevels_up)
+Node *
+ResolveNew(Node *node, int target_varno, int sublevels_up,
+                  List *targetlist, int event, int update_varno)
 {
        ResolveNew_context context;
 
-       context.info = info;
-       context.targetlist = targetlist;
+       context.target_varno = target_varno;
        context.sublevels_up = sublevels_up;
+       context.targetlist = targetlist;
+       context.event = event;
+       context.update_varno = update_varno;
 
+       /*
+        * Note: if an entire Query is passed, the right things will happen,
+        * because ResolveNew_mutator increments sublevels_up when it sees
+        * a SubLink, not a Query.
+        */
        return ResolveNew_mutator(node, &context);
 }
 
+/*
+ * Alternate interface to ResolveNew: substitute Vars in info->rule_action
+ * with targetlist items from the parsetree's targetlist.
+ */
 void
 FixNew(RewriteInfo *info, Query *parsetree)
 {
+       ResolveNew_context context;
+
+       context.target_varno = info->new_varno;
+       context.sublevels_up = 0;
+       context.targetlist = parsetree->targetList;
+       context.event = info->event;
+       context.update_varno = info->current_varno;
+
        info->rule_action->targetList = (List *)
-               ResolveNew((Node *) info->rule_action->targetList,
-                                  info, parsetree->targetList, 0);
-       info->rule_action->qual = ResolveNew(info->rule_action->qual,
-                                                                                info, parsetree->targetList, 0);
-       info->rule_action->havingQual = ResolveNew(info->rule_action->havingQual,
-                                                                                          info, parsetree->targetList, 0);
-       info->rule_action->jointree = (List *)
-               ResolveNew((Node *) info->rule_action->jointree,
-                                  info, parsetree->targetList, 0);
+               ResolveNew_mutator((Node *) info->rule_action->targetList, &context);
+       info->rule_action->jointree = (FromExpr *)
+               ResolveNew_mutator((Node *) info->rule_action->jointree, &context);
+       info->rule_action->havingQual =
+               ResolveNew_mutator(info->rule_action->havingQual, &context);
 }
 
+
+#ifdef NOT_USED
+
 /*
  * HandleRIRAttributeRule
  *     Replace Vars matching a given RT index with copies of TL expressions.
  *
  * Handles 'on retrieve to relation.attribute
  *                     do instead retrieve (attribute = expression) w/qual'
- *
- * XXX Why is this not unified with apply_RIR_view()?
  */
 
 typedef struct
@@ -897,12 +851,12 @@ HandleRIRAttributeRule_mutator(Node *node,
                FLATCOPY(newnode, query, Query);
                MUTATE(newnode->targetList, query->targetList, List *,
                           HandleRIRAttributeRule_mutator, context);
-               MUTATE(newnode->qual, query->qual, Node *,
+               MUTATE(newnode->jointree, query->jointree, FromExpr *,
                           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,
@@ -931,13 +885,12 @@ HandleRIRAttributeRule(Query *parsetree,
        parsetree->targetList = (List *)
                HandleRIRAttributeRule_mutator((Node *) parsetree->targetList,
                                                                           &context);
-       parsetree->qual =
-               HandleRIRAttributeRule_mutator(parsetree->qual,
+       parsetree->jointree = (FromExpr *)
+               HandleRIRAttributeRule_mutator((Node *) parsetree->jointree,
                                                                           &context);
        parsetree->havingQual =
                HandleRIRAttributeRule_mutator(parsetree->havingQual,
                                                                           &context);
-       parsetree->jointree = (List *)
-               HandleRIRAttributeRule_mutator((Node *) parsetree->jointree,
-                                                                          &context);
 }
+
+#endif /* NOT_USED */
index 2af7785..ba409f4 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteRemove.c,v 1.39 2000/09/12 04:49:09 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteRemove.c,v 1.40 2000/09/29 18:21:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,14 +40,14 @@ RewriteGetRuleEventRel(char *rulename)
                                                           PointerGetDatum(rulename),
                                                           0, 0, 0);
        if (!HeapTupleIsValid(htup))
-               elog(ERROR, "Rule or view '%s' not found",
-                 ((!strncmp(rulename, "_RET", 4)) ? (rulename + 4) : rulename));
+               elog(ERROR, "Rule or view \"%s\" not found",
+                 ((strncmp(rulename, "_RET", 4) == 0) ? (rulename + 4) : rulename));
        eventrel = ((Form_pg_rewrite) GETSTRUCT(htup))->ev_class;
        htup = SearchSysCacheTuple(RELOID,
                                                           PointerGetDatum(eventrel),
                                                           0, 0, 0);
        if (!HeapTupleIsValid(htup))
-               elog(ERROR, "Class '%u' not found", eventrel);
+               elog(ERROR, "Relation %u not found", eventrel);
 
        return NameStr(((Form_pg_class) GETSTRUCT(htup))->relname);
 }
@@ -85,7 +85,7 @@ RemoveRewriteRule(char *ruleName)
        if (!HeapTupleIsValid(tuple))
        {
                heap_close(RewriteRelation, RowExclusiveLock);
-               elog(ERROR, "Rule '%s' not found\n", ruleName);
+               elog(ERROR, "Rule \"%s\" not found", ruleName);
        }
 
        /*
@@ -105,7 +105,7 @@ RemoveRewriteRule(char *ruleName)
 
        /* do not allow the removal of a view's SELECT rule */
        if (event_relation->rd_rel->relkind == RELKIND_VIEW &&
-                       ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_type == '1' )
+               ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_type == '1' )
                elog(ERROR, "Cannot remove a view's SELECT rule");
 
        hasMoreRules = event_relation->rd_rules != NULL &&
@@ -133,7 +133,7 @@ RemoveRewriteRule(char *ruleName)
         * new rule set.  Therefore, must do this even if relhasrules is
         * still true!
         */
-       setRelhasrulesInRelation(eventRelationOid, hasMoreRules);
+       SetRelationRuleStatus(eventRelationOid, hasMoreRules, false);
 
        /* Close rel, but keep lock till commit... */
        heap_close(event_relation, NoLock);
index fe87f56..264021d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteSupport.c,v 1.43 2000/06/30 07:04:23 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteSupport.c,v 1.44 2000/09/29 18:21:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -34,10 +34,11 @@ IsDefinedRewriteRule(char *ruleName)
 }
 
 /*
- * setRelhasrulesInRelation
- *             Set the value of the relation's relhasrules field in pg_class.
+ * SetRelationRuleStatus
+ *             Set the value of the relation's relhasrules field in pg_class;
+ *             if the relation is becoming a view, also adjust its relkind.
  *
- * NOTE: caller should be holding an appropriate lock on the relation.
+ * NOTE: caller must be holding an appropriate lock on the relation.
  *
  * NOTE: an important side-effect of this operation is that an SI invalidation
  * message is sent out to all backends --- including me --- causing relcache
@@ -47,7 +48,8 @@ IsDefinedRewriteRule(char *ruleName)
  * an SI message in that case.
  */
 void
-setRelhasrulesInRelation(Oid relationId, bool relhasrules)
+SetRelationRuleStatus(Oid relationId, bool relHasRules,
+                                         bool relIsBecomingView)
 {
        Relation        relationRelation;
        HeapTuple       tuple;
@@ -63,7 +65,10 @@ setRelhasrulesInRelation(Oid relationId, bool relhasrules)
        Assert(HeapTupleIsValid(tuple));
 
        /* Do the update */
-       ((Form_pg_class) GETSTRUCT(tuple))->relhasrules = relhasrules;
+       ((Form_pg_class) GETSTRUCT(tuple))->relhasrules = relHasRules;
+       if (relIsBecomingView)
+               ((Form_pg_class) GETSTRUCT(tuple))->relkind = RELKIND_VIEW;
+
        heap_update(relationRelation, &tuple->t_self, tuple, NULL);
 
        /* Keep the catalog indices up to date */
index 2eaef85..64d0c3a 100644 (file)
@@ -3,7 +3,7 @@
  *                             back to source text
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.63 2000/09/25 18:14:54 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.64 2000/09/29 18:21:37 tgl Exp $
  *
  *       This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -714,7 +714,7 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc)
                query = (Query *) lfirst(actions);
 
                context.buf = buf;
-               context.rangetables = lcons(query->rtable, NIL);
+               context.rangetables = makeList1(query->rtable);
                context.varprefix = (length(query->rtable) != 1);
 
                get_rule_expr(qual, &context);
@@ -892,6 +892,9 @@ get_select_query_def(Query *query, deparse_context *context)
                TargetEntry *tle = (TargetEntry *) lfirst(l);
                bool            tell_as = false;
 
+               if (tle->resdom->resjunk)
+                       continue;                       /* ignore junk entries */
+
                appendStringInfo(buf, sep);
                sep = ", ";
 
@@ -922,10 +925,10 @@ get_select_query_def(Query *query, deparse_context *context)
        get_from_clause(query, context);
 
        /* Add the WHERE clause if given */
-       if (query->qual != NULL)
+       if (query->jointree->quals != NULL)
        {
                appendStringInfo(buf, " WHERE ");
-               get_rule_expr(query->qual, context);
+               get_rule_expr(query->jointree->quals, context);
        }
 
        /* Add the GROUP BY CLAUSE */
@@ -999,6 +1002,9 @@ get_insert_query_def(Query *query, deparse_context *context)
        {
                TargetEntry *tle = (TargetEntry *) lfirst(l);
 
+               if (tle->resdom->resjunk)
+                       continue;                       /* ignore junk entries */
+
                appendStringInfo(buf, sep);
                sep = ", ";
                appendStringInfo(buf, "%s", quote_identifier(tle->resdom->resname));
@@ -1006,7 +1012,7 @@ get_insert_query_def(Query *query, deparse_context *context)
        appendStringInfo(buf, ") ");
 
        /* Add the VALUES or the SELECT */
-       if (rt_constonly && query->qual == NULL)
+       if (rt_constonly && query->jointree->quals == NULL)
        {
                appendStringInfo(buf, "VALUES (");
                sep = "";
@@ -1014,6 +1020,9 @@ get_insert_query_def(Query *query, deparse_context *context)
                {
                        TargetEntry *tle = (TargetEntry *) lfirst(l);
 
+                       if (tle->resdom->resjunk)
+                               continue;               /* ignore junk entries */
+
                        appendStringInfo(buf, sep);
                        sep = ", ";
                        get_tle_expr(tle, context);
@@ -1034,7 +1043,6 @@ get_update_query_def(Query *query, deparse_context *context)
 {
        StringInfo      buf = context->buf;
        char       *sep;
-       TargetEntry *tle;
        RangeTblEntry *rte;
        List       *l;
 
@@ -1051,7 +1059,10 @@ get_update_query_def(Query *query, deparse_context *context)
        sep = "";
        foreach(l, query->targetList)
        {
-               tle = (TargetEntry *) lfirst(l);
+               TargetEntry *tle = (TargetEntry *) lfirst(l);
+
+               if (tle->resdom->resjunk)
+                       continue;                       /* ignore junk entries */
 
                appendStringInfo(buf, sep);
                sep = ", ";
@@ -1070,10 +1081,10 @@ get_update_query_def(Query *query, deparse_context *context)
        get_from_clause(query, context);
 
        /* Finally add a WHERE clause if given */
-       if (query->qual != NULL)
+       if (query->jointree->quals != NULL)
        {
                appendStringInfo(buf, " WHERE ");
-               get_rule_expr(query->qual, context);
+               get_rule_expr(query->jointree->quals, context);
        }
 }
 
@@ -1098,10 +1109,10 @@ get_delete_query_def(Query *query, deparse_context *context)
                                         quote_identifier(rte->relname));
 
        /* Add a WHERE clause if given */
-       if (query->qual != NULL)
+       if (query->jointree->quals != NULL)
        {
                appendStringInfo(buf, " WHERE ");
-               get_rule_expr(query->qual, context);
+               get_rule_expr(query->jointree->quals, context);
        }
 }
 
@@ -1747,11 +1758,13 @@ get_from_clause(Query *query, deparse_context *context)
        /*
         * 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.
+        * (These can only appear at the top level of the jointree, so it's
+        * sufficient to check here.)
         * Also ignore the rule pseudo-RTEs for NEW and OLD.
         */
        sep = " FROM ";
 
-       foreach(l, query->jointree)
+       foreach(l, query->jointree->fromlist)
        {
                Node   *jtnode = (Node *) lfirst(l);
 
@@ -1784,9 +1797,21 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
                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->relname)
+               {
+                       /* Normal relation RTE */
+                       appendStringInfo(buf, "%s%s",
+                                                        only_marker(rte),
+                                                        quote_identifier(rte->relname));
+               }
+               else
+               {
+                       /* Subquery RTE */
+                       Assert(rte->subquery != NULL);
+                       appendStringInfoChar(buf, '(');
+                       get_query_def(rte->subquery, buf, NIL);
+                       appendStringInfoChar(buf, ')');
+               }
                if (rte->alias != NULL)
                {
                        appendStringInfo(buf, " %s",
index ab9ee67..c64b5a9 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.47 2000/09/19 18:18:01 petere Exp $
+ * $Id: catversion.h,v 1.48 2000/09/29 18:21:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     200009191
+#define CATALOG_VERSION_NO     200009281
 
 #endif
diff --git a/src/include/executor/nodeSubqueryscan.h b/src/include/executor/nodeSubqueryscan.h
new file mode 100644 (file)
index 0000000..c582384
--- /dev/null
@@ -0,0 +1,25 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeSubqueryscan.h
+ *
+ *
+ *
+ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: nodeSubqueryscan.h,v 1.1 2000/09/29 18:21:38 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NODESUBQUERYSCAN_H
+#define NODESUBQUERYSCAN_H
+
+#include "nodes/plannodes.h"
+
+extern TupleTableSlot *ExecSubqueryScan(SubqueryScan *node);
+extern void ExecEndSubqueryScan(SubqueryScan *node);
+extern bool ExecInitSubqueryScan(SubqueryScan *node, EState *estate, Plan *parent);
+extern int     ExecCountSlotsSubqueryScan(SubqueryScan *node);
+extern void ExecSubqueryReScan(SubqueryScan *node, ExprContext *exprCtxt, Plan *parent);
+
+#endif  /* NODESUBQUERYSCAN_H */
index 83ed6c5..65d35a2 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.49 2000/09/12 21:07:10 tgl Exp $
+ * $Id: execnodes.h,v 1.50 2000/09/29 18:21:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -453,6 +453,24 @@ typedef struct TidScanState
        HeapTupleData tss_htup;
 } TidScanState;
 
+/* ----------------
+ *      SubqueryScanState information
+ *
+ *             SubqueryScanState is used for scanning a sub-query in the range table.
+ *             The sub-query will have its own EState, which we save here.
+ *             ScanTupleSlot references the current output tuple of the sub-query.
+ *
+ *             SubQueryDesc       queryDesc for sub-query
+ *             SubEState                  exec state for sub-query
+ * ----------------
+ */
+typedef struct SubqueryScanState
+{
+       CommonScanState csstate;        /* its first field is NodeTag */
+       struct QueryDesc *sss_SubQueryDesc;
+       EState     *sss_SubEState;
+} SubqueryScanState;
+
 /* ----------------------------------------------------------------
  *                              Join State Information
  * ----------------------------------------------------------------
index f3929d8..c725164 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.76 2000/09/12 21:07:10 tgl Exp $
+ * $Id: nodes.h,v 1.77 2000/09/29 18:21:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -49,6 +49,7 @@ typedef enum NodeTag
        T_Group,
        T_SubPlan,
        T_TidScan,
+       T_SubqueryScan,
 
        /*---------------------
         * TAGS FOR PRIMITIVE NODES (primnodes.h)
@@ -69,6 +70,7 @@ typedef enum NodeTag
        T_Iter,
        T_RelabelType,
        T_RangeTblRef,
+       T_FromExpr,
        T_JoinExpr,
 
        /*---------------------
@@ -118,6 +120,7 @@ typedef enum NodeTag
        T_UniqueState,
        T_HashState,
        T_TidScanState,
+       T_SubqueryScanState,
 
        /*---------------------
         * TAGS FOR MEMORY NODES (memnodes.h)
@@ -222,7 +225,7 @@ typedef enum NodeTag
        T_oldJoinExprXXX,                       /* not used anymore; this tag# is available */
        T_CaseExpr,
        T_CaseWhen,
-       T_RowMark,
+       T_RowMarkXXX,                           /* not used anymore; this tag# is available */
        T_FkConstraint,
 
        /*---------------------
index 440a760..ab80540 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.113 2000/09/12 21:07:10 tgl Exp $
+ * $Id: parsenodes.h,v 1.114 2000/09/29 18:21:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -50,11 +50,12 @@ typedef struct Query
        bool            hasSubLinks;    /* has subquery SubLink */
 
        List       *rtable;                     /* list of range table entries */
-       List       *jointree;           /* table join tree (from the FROM clause) */
+       FromExpr   *jointree;           /* table join tree (FROM and WHERE clauses) */
 
        List       *targetList;         /* target list (of TargetEntry) */
-       Node       *qual;                       /* qualifications applied to tuples */
-       List       *rowMark;            /* list of RowMark entries */
+
+       List       *rowMarks;           /* integer list of RT indexes of relations
+                                                                * that are selected FOR UPDATE */
 
        List       *distinctClause; /* a list of SortClause's */
 
@@ -1087,7 +1088,7 @@ typedef struct RangeSubselect
 {
        NodeTag         type;
        Node       *subquery;           /* the untransformed sub-select clause */
-       Attr       *name;                       /* optional table alias & column aliases */
+       Attr       *name;                       /* table alias & optional column aliases */
 } RangeSubselect;
 
 /*
@@ -1141,15 +1142,22 @@ typedef struct TargetEntry
  * RangeTblEntry -
  *       A range table is a List of RangeTblEntry nodes.
  *
- *       Some of the following are only used in one of
- *       the parsing, optimizing, execution stages.
+ *       Currently we use the same node type for both plain relation references
+ *       and sub-selects in the FROM clause.  It might be cleaner to abstract
+ *       the common fields into a "superclass" nodetype.
  *
  *       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.
+ *       real or aliases).  Note that system columns (OID etc) are not included
+ *       in the column list.
+ *       eref->relname is required to be present, and should generally be used
+ *       to identify the RTE for error messages etc.
+ *
+ *       inh is TRUE for relation references that should be expanded to include
+ *       inheritance children, if the rel has any.  This *must* be FALSE for
+ *       subquery RTEs.
  *
  *       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
@@ -1160,18 +1168,37 @@ 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 '*'.
+ *
+ *       checkForRead, checkForWrite, and checkAsUser control run-time access
+ *       permissions checks.  A rel will be checked for read or write access
+ *       (or both, or neither) per checkForRead and checkForWrite.  If
+ *       checkAsUser is not InvalidOid, then do the permissions checks using
+ *       the access rights of that user, not the current effective user ID.
+ *       (This allows rules to act as setuid gateways.)
  *--------------------
  */
 typedef struct RangeTblEntry
 {
        NodeTag         type;
+       /*
+        * Fields valid for a plain relation RTE (else NULL/zero):
+        */
        char       *relname;            /* real name of the relation */
        Oid                     relid;                  /* OID of the relation */
+       /*
+        * Fields valid for a subquery RTE (else NULL):
+        */
+       Query      *subquery;           /* the sub-query */
+       /*
+        * Fields valid in all RTEs:
+        */
        Attr       *alias;                      /* user-written alias clause, if any */
        Attr       *eref;                       /* expanded reference names */
        bool            inh;                    /* inheritance requested? */
        bool            inFromCl;               /* present in FROM clause */
-       bool            skipAcl;                /* skip ACL check in executor */
+       bool            checkForRead;   /* check rel for read access */
+       bool            checkForWrite;  /* check rel for write access */
+       Oid                     checkAsUser;    /* if not zero, check access as this user */
 } RangeTblEntry;
 
 /*
@@ -1206,14 +1233,4 @@ typedef struct SortClause
  */
 typedef SortClause GroupClause;
 
-#define ROW_MARK_FOR_UPDATE            (1 << 0)
-#define ROW_ACL_FOR_UPDATE             (1 << 1)
-
-typedef struct RowMark
-{
-       NodeTag         type;
-       Index           rti;                    /* index in Query->rtable */
-       bits8           info;                   /* as above */
-} RowMark;
-
 #endif  /* PARSENODES_H */
index 4e0bcfc..953f6ed 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.19 2000/09/12 21:07:10 tgl Exp $
+ * $Id: pg_list.h,v 1.20 2000/09/29 18:21:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -92,6 +92,18 @@ typedef struct List
 #define foreach(_elt_,_list_)  \
        for(_elt_=(_list_); _elt_!=NIL; _elt_=lnext(_elt_))
 
+/*
+ * Convenience macros for building fixed-length lists
+ */
+#define makeList1(x1)                          lcons(x1, NIL)
+#define makeList2(x1,x2)                       lcons(x1, makeList1(x2))
+#define makeList3(x1,x2,x3)                    lcons(x1, makeList2(x2,x3))
+#define makeList4(x1,x2,x3,x4)         lcons(x1, makeList3(x2,x3,x4))
+
+#define makeListi1(x1)                         lconsi(x1, NIL)
+#define makeListi2(x1,x2)                      lconsi(x1, makeListi1(x2))
+#define makeListi3(x1,x2,x3)           lconsi(x1, makeListi2(x2,x3))
+#define makeListi4(x1,x2,x3,x4)                lconsi(x1, makeListi3(x2,x3,x4))
 
 /*
  * function prototypes in nodes/list.c
@@ -106,11 +118,11 @@ extern bool intMember(int datum, List *list);
 extern Value *makeInteger(long i);
 extern Value *makeFloat(char *numericStr);
 extern Value *makeString(char *str);
-extern List *makeList(void *elem,...);
 extern List *lappend(List *list, void *datum);
 extern List *lappendi(List *list, int datum);
 extern List *lremove(void *elem, List *list);
 extern List *LispRemove(void *elem, List *list);
+extern List *lremovei(int elem, List *list);
 extern List *ltruncate(int n, List *list);
 
 extern void *nth(int n, List *l);
@@ -120,8 +132,8 @@ extern void set_nth(List *l, int n, void *elem);
 extern List *set_difference(List *list1, List *list2);
 extern List *set_differencei(List *list1, List *list2);
 extern List *lreverse(List *l);
-extern List *LispUnion(List *list1, List *list2);
-extern List *LispUnioni(List *list1, List *list2);
+extern List *set_union(List *list1, List *list2);
+extern List *set_unioni(List *list1, List *list2);
 
 extern bool sameseti(List *list1, List *list2);
 extern bool nonoverlap_setsi(List *list1, List *list2);
index cf93b9d..ca5727f 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.42 2000/09/12 21:07:10 tgl Exp $
+ * $Id: plannodes.h,v 1.43 2000/09/29 18:21:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,6 +31,7 @@
  *
  *             Scan ***                                CommonScanState                 scanstate;
  *             IndexScan                               IndexScanState                  indxstate;
+ *             SubqueryScan                    SubqueryScanState               subquerystate;
  *
  *               (*** nodes which inherit Scan also inherit scanstate)
  *
@@ -202,6 +203,26 @@ typedef struct TidScan
        TidScanState *tidstate;
 } TidScan;
 
+/* ----------------
+ *             subquery scan node
+ *
+ * SubqueryScan is for scanning the output of a sub-query in the range table.
+ * We need a special plan node above the sub-query's plan as a place to switch
+ * execution contexts.  Although we are not scanning a physical relation,
+ * we make this a descendant of Scan anyway for code-sharing purposes.
+ *
+ * Note: we store the sub-plan in the type-specific subplan field, not in
+ * the generic lefttree field as you might expect.  This is because we do
+ * not want plan-tree-traversal routines to recurse into the subplan without
+ * knowing that they are changing Query contexts.
+ * ----------------
+ */
+typedef struct SubqueryScan
+{
+       Scan            scan;
+       Plan       *subplan;
+} SubqueryScan;
+
 /*
  * ==========
  * Join nodes
index bc17773..62b65fe 100644 (file)
@@ -10,7 +10,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: primnodes.h,v 1.48 2000/09/12 21:07:10 tgl Exp $
+ * $Id: primnodes.h,v 1.49 2000/09/29 18:21:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -495,24 +495,32 @@ typedef struct RelabelType
  *
  * 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.
+ * or qualified join.  Also, FromExpr nodes can appear to denote an
+ * ordinary cross-product join ("FROM foo, bar, baz WHERE ...").
+ * FromExpr is like a JoinExpr of jointype JOIN_INNER, except that it
+ * may have any number of child nodes, not just two.  Also, there is an
+ * implementation-defined difference: the planner is allowed to join the
+ * children of a FromExpr using whatever join order seems good to it.
+ * At present, JoinExpr nodes are always joined in exactly the order
+ * implied by the jointree structure (except the planner may choose to
+ * swap inner and outer members of a join pair).
+ *
+ * NOTE: the top level of a Query's jointree is always a FromExpr.
+ * Even if the jointree contains no rels, there will be a FromExpr.
  *
  * 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.
+ * *in addition to* the query's main WHERE clause, which appears as the
+ * qual of the top-level FromExpr.  The reason for associating quals with
+ * specific nodes in the jointree is that the position of a qual is critical
+ * when outer joins are present.  (If we enforce a qual too soon or too late,
+ * that may cause the outer join to produce the wrong set of NULL-extended
+ * rows.)  If all joins are inner joins then all the qual positions are
+ * semantically 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.
+ * during the parse analysis phase.  Also, the top-level FromExpr is added
+ * during parse analysis; the grammar regards FROM and WHERE as separate.
  * ----------------------------------------------------------------
  */
 
@@ -561,4 +569,20 @@ typedef struct JoinExpr
        List       *colvars;            /* output column nodes (list of expressions) */
 } JoinExpr;
 
+/*----------
+ * FromExpr - represents a FROM ... WHERE ... construct
+ *
+ * This is both more flexible than a JoinExpr (it can have any number of
+ * children, including zero) and less so --- we don't need to deal with
+ * aliases and so on.  The output column set is implicitly just the union
+ * of the outputs of the children.
+ *----------
+ */
+typedef struct FromExpr
+{
+       NodeTag         type;
+       List       *fromlist;           /* List of join subtrees */
+       Node       *quals;                      /* qualifiers on join, if any */
+} FromExpr;
+
 #endif  /* PRIMNODES_H */
index 767e2e1..1c864f2 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.48 2000/09/12 21:07:10 tgl Exp $
+ * $Id: relation.h,v 1.49 2000/09/29 18:21:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -35,10 +35,20 @@ typedef enum CostSelector
        STARTUP_COST, TOTAL_COST
 } CostSelector;
 
-/*
+/*----------
  * RelOptInfo
  *             Per-relation information for planning/optimization
  *
+ *             For planning purposes, a "base rel" is either a plain relation (a
+ *             table) or the output of a sub-SELECT that appears in the range table.
+ *             In either case it is uniquely identified by an RT index.  A "joinrel"
+ *             is the joining of two or more base rels.  A joinrel is identified by
+ *             the set of RT indexes for its component baserels.
+ *
+ *             Note that there is only one joinrel for any given set of component
+ *             baserels, no matter what order we assemble them in; so an unordered
+ *             set is the right datatype to identify it with.
+ *
  *             Parts of this data structure are specific to various scan and join
  *             mechanisms.  It didn't seem worth creating new node types for them.
  *
@@ -61,9 +71,16 @@ typedef enum CostSelector
  *
  *      * If the relation is a base relation it will have these fields set:
  *
- *             indexed - true if the relation has secondary indices
- *             pages - number of disk pages in relation
+ *             issubquery - true if baserel is a subquery RTE rather than a table
+ *             indexed - true if the relation has secondary indices (always false
+ *                               if it's a subquery)
+ *             pages - number of disk pages in relation (zero if a subquery)
  *             tuples - number of tuples in relation (not considering restrictions)
+ *             subplan - plan for subquery (NULL if it's a plain table)
+ *
+ *             Note: for a subquery, tuples and subplan are not set immediately
+ *             upon creation of the RelOptInfo object; they are filled in when
+ *             set_base_rel_pathlist processes the object.
  *
  *      * The presence of the remaining fields depends on the restrictions
  *             and joins that the relation participates in:
@@ -101,6 +118,7 @@ typedef enum CostSelector
  * 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
@@ -122,10 +140,12 @@ typedef struct RelOptInfo
        struct Path *cheapest_total_path;
        bool            pruneable;
 
-       /* statistics from pg_class (only valid if it's a base rel!) */
+       /* information about a base rel (not set for join rels!) */
+       bool            issubquery;
        bool            indexed;
        long            pages;
        double          tuples;
+       struct Plan *subplan;
 
        /* used by various scans and joins: */
        List       *baserestrictinfo;           /* RestrictInfo structures (if
@@ -272,7 +292,8 @@ typedef struct Path
  * 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.
+ * This flag is TRUE iff all the indexquals came from non-pushed-down
+ * JOIN/ON conditions, which means the path is safe to use for an outer join.
  *
  * '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
@@ -375,10 +396,10 @@ typedef struct HashPath
  * Restriction clause info.
  *
  * We create one of these for each AND sub-clause of a restriction condition
- * (WHERE clause).     Since the restriction clauses are logically ANDed, we
- * can use any one of them or any subset of them to filter out tuples,
- * without having to evaluate the rest.  The RestrictInfo node itself stores
- * data used by the optimizer while choosing the best query plan.
+ * (WHERE or JOIN/ON clause).  Since the restriction clauses are logically
+ * ANDed, we can use any one of them or any subset of them to filter out
+ * tuples, without having to evaluate the rest.  The RestrictInfo node itself
+ * stores data used by the optimizer while choosing the best query plan.
  *
  * If a restriction clause references a single base relation, it will appear
  * in the baserestrictinfo list of the RelOptInfo for that base rel.
@@ -405,6 +426,31 @@ typedef struct HashPath
  * sequence we use.  So, these clauses cannot be associated directly with
  * the join RelOptInfo, but must be kept track of on a per-join-path basis.
  *
+ * When dealing with outer joins we have to be very careful about pushing qual
+ * clauses up and down the tree.  An outer join's own JOIN/ON conditions must
+ * be evaluated exactly at that join node, and any quals appearing in WHERE or
+ * in a JOIN above the outer join cannot be pushed down below the outer join.
+ * Otherwise the outer join will produce wrong results because it will see the
+ * wrong sets of input rows.  All quals are stored as RestrictInfo nodes
+ * during planning, but there's a flag to indicate whether a qual has been
+ * pushed down to a lower level than its original syntactic placement in the
+ * join tree would suggest.  If an outer join prevents us from pushing a qual
+ * down to its "natural" semantic level (the level associated with just the
+ * base rels used in the qual) then the qual will appear in JoinInfo lists
+ * that reference more than just the base rels it actually uses.  By
+ * pretending that the qual references all the rels appearing in the outer
+ * join, we prevent it from being evaluated below the outer join's joinrel.
+ * When we do form the outer join's joinrel, we still need to distinguish
+ * those quals that are actually in that join's JOIN/ON condition from those
+ * that appeared higher in the tree and were pushed down to the join rel
+ * because they used no other rels.  That's what the ispusheddown flag is for;
+ * it tells us that a qual came from a point above the join of the specific
+ * set of base rels that it uses (or that the JoinInfo structures claim it
+ * uses).  A clause that originally came from WHERE will *always* have its
+ * ispusheddown flag set; a clause that came from an INNER JOIN condition,
+ * but doesn't use all the rels being joined, will also have ispusheddown set
+ * because it will get attached to some lower joinrel.
+ *
  * In general, the referenced clause might be arbitrarily complex.     The
  * kinds of clauses we can handle as indexscan quals, mergejoin clauses,
  * or hashjoin clauses are fairly limited --- the code for each kind of
@@ -415,16 +461,6 @@ typedef struct HashPath
  * 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
@@ -433,7 +469,7 @@ typedef struct RestrictInfo
 
        Expr       *clause;                     /* the represented clause of WHERE or JOIN */
 
-       bool            isjoinqual;             /* TRUE if clause came from JOIN/ON */
+       bool            ispusheddown;   /* TRUE if clause was pushed down in level */
 
        /* only used if clause is an OR clause: */
        List       *subclauseindices;           /* indexes matching subclauses */
index 62bb401..881940f 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.39 2000/09/12 21:07:11 tgl Exp $
+ * $Id: clauses.h,v 1.40 2000/09/29 18:21:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -43,6 +43,7 @@ extern Expr *get_notclausearg(Expr *notclause);
 
 extern bool and_clause(Node *clause);
 extern Expr *make_andclause(List *andclauses);
+extern Node *make_and_qual(Node *qual1, Node *qual2);
 extern Expr *make_ands_explicit(List *andclauses);
 extern List *make_ands_implicit(Expr *clause);
 
@@ -56,10 +57,7 @@ extern void check_subplans_for_ungrouped_vars(Node *clause, Query *query);
 extern bool contain_noncachable_functions(Node *clause);
 
 extern bool is_pseudo_constant_clause(Node *clause);
-
-extern List *pull_constant_clauses(List *quals,
-                                                                  List **noncachableQual,
-                                                                  List **constantQual);
+extern List *pull_constant_clauses(List *quals, List **constantQual);
 
 extern void clause_get_relids_vars(Node *clause, Relids *relids, List **vars);
 extern int     NumRelids(Node *clause);
index 0bf57ef..e5810a0 100644 (file)
@@ -1,13 +1,13 @@
 /*-------------------------------------------------------------------------
  *
  * pathnode.h
- *       prototypes for pathnode.c, indexnode.c, relnode.c.
+ *       prototypes for pathnode.c, relnode.c.
  *
  *
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pathnode.h,v 1.28 2000/09/12 21:07:11 tgl Exp $
+ * $Id: pathnode.h,v 1.29 2000/09/29 18:21:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -32,6 +32,7 @@ extern IndexPath *create_index_path(Query *root, RelOptInfo *rel,
                                  List *restriction_clauses,
                                  ScanDirection indexscandir);
 extern TidPath *create_tidscan_path(RelOptInfo *rel, List *tideval);
+extern Path *create_subqueryscan_path(RelOptInfo *rel);
 
 extern NestPath *create_nestloop_path(RelOptInfo *joinrel,
                                                                          JoinType jointype,
@@ -66,9 +67,4 @@ extern RelOptInfo *get_join_rel(Query *root, RelOptInfo *outer_rel,
                         RelOptInfo *inner_rel,
                         List **restrictlist_ptr);
 
-/*
- * prototypes for indexnode.h
- */
-extern List *find_relation_indices(Query *root, RelOptInfo *rel);
-
 #endif  /* PATHNODE_H */
index 35eb319..327e63d 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.47 2000/09/12 21:07:11 tgl Exp $
+ * $Id: paths.h,v 1.48 2000/09/29 18:21:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -28,6 +28,7 @@ extern bool enable_geqo;
 extern int     geqo_rels;
 
 extern RelOptInfo *make_one_rel(Query *root);
+extern RelOptInfo *make_fromexpr_rel(Query *root, FromExpr *from);
 
 /*
  * indxpath.c
@@ -77,7 +78,7 @@ extern List *make_rels_by_clause_joins(Query *root,
 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);
+extern RelOptInfo *make_jointree_rel(Query *root, Node *jtnode);
 
 /*
  * pathkeys.c
index a5c09f4..c599114 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: plancat.h,v 1.19 2000/06/09 03:17:11 tgl Exp $
+ * $Id: plancat.h,v 1.20 2000/09/29 18:21:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "nodes/relation.h"
 
 
-extern void relation_info(Query *root, Index relid,
-                         bool *hasindex, long *pages, double *tuples);
+extern void relation_info(Oid relationObjectId,
+                                                 bool *hasindex, long *pages, double *tuples);
 
-extern List *find_secondary_indexes(Query *root, Index relid);
+extern List *find_secondary_indexes(Oid relationObjectId);
 
 extern List *find_inheritance_children(Oid inhparent);
 
index 43c9397..e46d0bd 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.44 2000/09/12 21:07:11 tgl Exp $
+ * $Id: planmain.h,v 1.45 2000/09/29 18:21:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,8 +40,7 @@ extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
  * prototypes for plan/initsplan.c
  */
 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 Relids distribute_quals_to_rels(Query *root, Node *jtnode);
 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);
index 2edc2ff..cf85ec4 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_agg.h,v 1.15 2000/04/12 17:16:45 momjian Exp $
+ * $Id: parse_agg.h,v 1.16 2000/09/29 18:21:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,7 +17,7 @@
 #include "parser/parse_node.h"
 
 extern void AddAggToParseState(ParseState *pstate, Aggref *aggref);
-extern void parseCheckAggregates(ParseState *pstate, Query *qry);
+extern void parseCheckAggregates(ParseState *pstate, Query *qry, Node *qual);
 extern Aggref *ParseAgg(ParseState *pstate, char *aggname, Oid basetype,
                 List *args, bool agg_star, bool agg_distinct,
                 int precedence);
index 002391d..22dd797 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.21 2000/09/12 21:07:12 tgl Exp $
+ * $Id: parse_node.h,v 1.22 2000/09/29 18:21:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,7 +23,8 @@ typedef struct ParseState
 {
        struct ParseState *parentParseState; /* stack link */
        List       *p_rtable;           /* range table so far */
-       List       *p_jointree;         /* join tree so far */
+       List       *p_joinlist;         /* join items so far (will become
+                                                                * FromExpr node's fromlist) */
        int                     p_last_resno;   /* last targetlist resno assigned */
        bool            p_hasAggs;
        bool            p_hasSubLinks;
index 7c7a048..981d428 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.19 2000/09/12 21:07:12 tgl Exp $
+ * $Id: parse_relation.h,v 1.20 2000/09/29 18:21:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -27,7 +27,7 @@ extern int refnameRangeTablePosn(ParseState *pstate,
 extern int RTERangeTablePosn(ParseState *pstate,
                                                         RangeTblEntry *rte,
                                                         int *sublevels_up);
-extern JoinExpr *scanJoinTreeForRefname(Node *jtnode, char *refname);
+extern JoinExpr *scanJoinListForRefname(Node *jtnode, char *refname);
 extern Node *colnameToVar(ParseState *pstate, char *colname);
 extern Node *qualifiedNameToVar(ParseState *pstate, char *refname,
                                                                char *colname, bool implicitRTEOK);
@@ -36,7 +36,11 @@ extern RangeTblEntry *addRangeTableEntry(ParseState *pstate,
                                                                                 Attr *alias,
                                                                                 bool inh,
                                                                                 bool inFromCl);
-extern void addRTEtoJoinTree(ParseState *pstate, RangeTblEntry *rte);
+extern RangeTblEntry *addRangeTableEntryForSubquery(ParseState *pstate,
+                                                                                                       Query *subquery,
+                                                                                                       Attr *alias,
+                                                                                                       bool inFromCl);
+extern void addRTEtoJoinList(ParseState *pstate, RangeTblEntry *rte);
 extern RangeTblEntry *addImplicitRTE(ParseState *pstate, char *relname);
 extern void expandRTE(ParseState *pstate, RangeTblEntry *rte,
                                          List **colnames, List **colvars);
diff --git a/src/include/rewrite/locks.h b/src/include/rewrite/locks.h
deleted file mode 100644 (file)
index bcc8863..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * locks.h
- *
- *
- *
- * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * $Id: locks.h,v 1.13 2000/01/26 05:58:30 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-#ifndef LOCKS_H
-#define LOCKS_H
-
-#include "nodes/parsenodes.h"
-#include "rewrite/prs2lock.h"
-
-extern List *matchLocks(CmdType event, RuleLock *rulelocks, int varno,
-                  Query *parsetree);
-extern void checkLockPerms(List *locks, Query *parsetree, int rt_index);
-
-#endif  /* LOCKS_H */
index c41519a..2cfbf0e 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.22 2000/09/12 21:07:15 tgl Exp $
+ * $Id: rewriteManip.h,v 1.23 2000/09/29 18:21:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,15 +31,12 @@ extern bool attribute_used(Node *node, int rt_index, int attno,
 extern void AddQual(Query *parsetree, Node *qual);
 extern void AddHavingQual(Query *parsetree, Node *havingQual);
 extern void AddNotQual(Query *parsetree, Node *qual);
-extern void AddGroupClause(Query *parsetree, List *group_by, List *tlist);
 
 extern bool checkExprHasAggs(Node *node);
 extern bool checkExprHasSubLink(Node *node);
 
+extern Node *ResolveNew(Node *node, int target_varno, int sublevels_up,
+                                               List *targetlist, int event, int update_varno);
 extern void FixNew(RewriteInfo *info, Query *parsetree);
 
-extern void HandleRIRAttributeRule(Query *parsetree, List *rtable,
-                                          List *targetlist, int rt_index,
-                                          int attr_num, int *modified, int *badsql);
-
 #endif  /* REWRITEMANIP_H */
index 589f424..39605df 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: rewriteSupport.h,v 1.12 2000/06/30 07:04:04 tgl Exp $
+ * $Id: rewriteSupport.h,v 1.13 2000/09/29 18:21:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,6 +16,7 @@
 
 extern int     IsDefinedRewriteRule(char *ruleName);
 
-extern void setRelhasrulesInRelation(Oid relationId, bool relhasrules);
+extern void SetRelationRuleStatus(Oid relationId, bool relHasRules,
+                                                                 bool relIsBecomingView);
 
 #endif  /* REWRITESUPPORT_H */
index 66a5c3a..ca030be 100644 (file)
@@ -221,7 +221,7 @@ drop rule 314159;
 ERROR:  parser: parse error at or near "314159"
 -- no such rule 
 drop rule nonesuch;
-ERROR:  Rule or view 'nonesuch' not found
+ERROR:  Rule or view "nonesuch" not found
 -- bad keyword 
 drop tuple rule nonesuch;
 ERROR:  parser: parse error at or near "tuple"