Planned patch for join scope rules (long) - Mailing list pgsql-patches

From Tom Lane
Subject Planned patch for join scope rules (long)
Date
Msg-id 11798.982106611@sss.pgh.pa.us
Whole thread Raw
List pgsql-patches
Attached are the diffs for changes I intend to make soon; I still have
some more work to do, but I'm throwing these out now for comment.

These changes respond to Tom Lockhart's point that we do not currently
implement scoping correctly in join trees.  As I now read the SQL92
spec, it says that an aliased JOIN clause --- that is,

    ( <joined table> ) AS <alias>

is a <subquery> and therefore table and column names appearing within it
are not visible to the surrounding query.  So, for example,

    SELECT a.* FROM (table1 a CROSS JOIN table2 b) AS z;

is incorrect because the refname a is not visible outside z; on the
other hand

    SELECT a.* FROM (table1 a CROSS JOIN table2 b);

is valid because there is no <subquery>.  Also,

    SELECT a.* FROM (table1 a CROSS JOIN table2 b) AS z, table3 a;

is legal: there is no conflict between the two refnames a because they
have different scopes.  (Without the AS z, it'd be erroneous.)

The most reasonable way to deal with this seems to be to decouple
reference name scoping from the rangetable and join list structures.
Accordingly, the attached patch creates a third datastructure
"p_namespace" that's maintained within a ParseState.  The representation
is the same as for the jointree but the contents may be different from
the jointree at any instant.  This cleans things up in several places
that previously did rather ugly hacking on the rtable and jointree ---
now they hack the namespace, which should have less chance of unexpected
side-effects.  I've also been able to simplify the handling of
INSERT/UPDATE/DELETE target tables.

These changes do not affect on-disk datastructures, only parser internal
structures, so no initdb will be needed.

What remains to be done is to fix ruleutils.c so that it produces
correct reverse-listings of parsetrees.  This is not trivial --- the
existing approach no longer gives correct answers, because we cannot
assume that refnames associated with RTEs are unique or even accessible.
I believe I can make this happen without changing the on-disk
representation of parsetrees, but it's gonna be a tad painful.
Something I'd like to do fairly soon in the 7.2 cycle is to change the
parsetree representation so that Var nodes store the original refname
and colname used to access the attribute.  This will let ruleutils
simply spit that info out, rather than indulging in major pushups to
deduce the correct values.  (Or maybe we should abandon ruleutils
entirely, and store the textual definitions of rules and views...)

Comments anyone?

            regards, tom lane


*** src/backend/catalog/heap.c.orig    Mon Feb 12 15:07:21 2001
--- src/backend/catalog/heap.c    Tue Feb 13 16:28:53 2001
***************
*** 1764,1772 ****
       * sole rangetable entry.  We need a ParseState for transformExpr.
       */
      pstate = make_parsestate(NULL);
-     makeRangeTable(pstate, NULL);
      rte = addRangeTableEntry(pstate, relname, NULL, false, true);
!     addRTEtoJoinList(pstate, rte);

      /*
       * Process column default expressions.
--- 1764,1771 ----
       * sole rangetable entry.  We need a ParseState for transformExpr.
       */
      pstate = make_parsestate(NULL);
      rte = addRangeTableEntry(pstate, relname, NULL, false, true);
!     addRTEtoQuery(pstate, rte, true, true);

      /*
       * Process column default expressions.
*** src/backend/commands/command.c.orig    Sun Jan 28 19:39:20 2001
--- src/backend/commands/command.c    Tue Feb 13 16:29:14 2001
***************
*** 1136,1145 ****
                       * the expression we can pass to ExecQual
                       */
                      pstate = make_parsestate(NULL);
-                     makeRangeTable(pstate, NULL);
                      rte = addRangeTableEntry(pstate, relationName, NULL,
                                               false, true);
!                     addRTEtoJoinList(pstate, rte);

                      /* Convert the A_EXPR in raw_expr into an EXPR */
                      expr = transformExpr(pstate, constr->raw_expr,
--- 1136,1144 ----
                       * the expression we can pass to ExecQual
                       */
                      pstate = make_parsestate(NULL);
                      rte = addRangeTableEntry(pstate, relationName, NULL,
                                               false, true);
!                     addRTEtoQuery(pstate, rte, true, true);

                      /* Convert the A_EXPR in raw_expr into an EXPR */
                      expr = transformExpr(pstate, constr->raw_expr,
*** src/backend/parser/analyze.c.orig    Sat Jan 27 02:23:48 2001
--- src/backend/parser/analyze.c    Tue Feb 13 16:28:16 2001
***************
*** 257,267 ****

      qry->commandType = CMD_DELETE;

!     /* set up a range table */
!     lockTargetTable(pstate, stmt->relname);
!     makeRangeTable(pstate, NIL);
!     setTargetTable(pstate, stmt->relname,
!                    interpretInhOption(stmt->inhOpt), true);

      qry->distinctClause = NIL;

--- 257,266 ----

      qry->commandType = CMD_DELETE;

!     /* set up range table with just the result rel */
!     qry->resultRelation = setTargetTable(pstate, stmt->relname,
!                                          interpretInhOption(stmt->inhOpt),
!                                          true);

      qry->distinctClause = NIL;

***************
*** 271,277 ****
      /* done building the range table and jointree */
      qry->rtable = pstate->p_rtable;
      qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
-     qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);

      qry->hasSubLinks = pstate->p_hasSubLinks;
      qry->hasAggs = pstate->p_hasAggs;
--- 270,275 ----
***************
*** 289,294 ****
--- 287,294 ----
  transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
  {
      Query       *qry = makeNode(Query);
+     List       *sub_rtable;
+     List       *sub_namespace;
      List       *icolumns;
      List       *attrnos;
      List       *attnos;
***************
*** 300,310 ****
      pstate->p_is_insert = true;

      /*
!      * Must get write lock on target table before scanning SELECT,
       * else we will grab the wrong kind of initial lock if the target
!      * table is also mentioned in the SELECT part.
       */
!     lockTargetTable(pstate, stmt->relname);

      /*
       * Is it INSERT ... SELECT or INSERT ... VALUES?
--- 300,334 ----
      pstate->p_is_insert = true;

      /*
!      * If a non-nil rangetable/namespace was passed in, and we are doing
!      * INSERT/SELECT, arrange to pass the rangetable/namespace down to the
!      * SELECT.  This can only happen if we are inside a CREATE RULE,
!      * and in that case we want the rule's OLD and NEW rtable entries to
!      * appear as part of the SELECT's rtable, not as outer references for
!      * it.  (Kluge!)  The SELECT's joinlist is not affected however.
!      * We must do this before adding the target table to the INSERT's rtable.
!      */
!     if (stmt->selectStmt)
!     {
!         sub_rtable = pstate->p_rtable;
!         pstate->p_rtable = NIL;
!         sub_namespace = pstate->p_namespace;
!         pstate->p_namespace = NIL;
!     }
!     else
!     {
!         sub_rtable = NIL;        /* not used, but keep compiler quiet */
!         sub_namespace = NIL;
!     }
!
!     /*
!      * Must get write lock on INSERT target table before scanning SELECT,
       * else we will grab the wrong kind of initial lock if the target
!      * table is also mentioned in the SELECT part.  Note that the target
!      * table is not added to the joinlist or namespace.
       */
!     qry->resultRelation = setTargetTable(pstate, stmt->relname,
!                                          false, false);

      /*
       * Is it INSERT ... SELECT or INSERT ... VALUES?
***************
*** 323,337 ****
           * otherwise the behavior of SELECT within INSERT might be different
           * from a stand-alone SELECT. (Indeed, Postgres up through 6.5 had
           * bugs of just that nature...)
-          *
-          * If a non-nil rangetable was passed in, pass it down to the SELECT.
-          * This can only happen if we are inside a CREATE RULE, and in that
-          * case we want the rule's OLD and NEW rtable entries to appear as
-          * part of the SELECT's rtable, not as outer references for it.
           */
!         sub_pstate->p_rtable = pstate->p_rtable;
!         pstate->p_rtable = NIL;
          selectQuery = transformStmt(sub_pstate, stmt->selectStmt);
          release_pstate_resources(sub_pstate);
          pfree(sub_pstate);

--- 347,358 ----
           * otherwise the behavior of SELECT within INSERT might be different
           * from a stand-alone SELECT. (Indeed, Postgres up through 6.5 had
           * bugs of just that nature...)
           */
!         sub_pstate->p_rtable = sub_rtable;
!         sub_pstate->p_namespace = sub_namespace;
!
          selectQuery = transformStmt(sub_pstate, stmt->selectStmt);
+
          release_pstate_resources(sub_pstate);
          pfree(sub_pstate);

***************
*** 341,347 ****
              elog(ERROR, "INSERT ... SELECT may not specify INTO");
          /*
           * Make the source be a subquery in the INSERT's rangetable,
!          * and add it to the joinlist.
           */
          rte = addRangeTableEntryForSubquery(pstate,
                                              selectQuery,
--- 362,368 ----
              elog(ERROR, "INSERT ... SELECT may not specify INTO");
          /*
           * Make the source be a subquery in the INSERT's rangetable,
!          * and add it to the INSERT's joinlist.
           */
          rte = addRangeTableEntryForSubquery(pstate,
                                              selectQuery,
***************
*** 400,412 ****
      /*
       * Now we are done with SELECT-like processing, and can get on with
       * transforming the target list to match the INSERT target columns.
-      *
-      * 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
-      * joinlist, since we don't want to join over it.
       */
-     setTargetTable(pstate, stmt->relname, false, false);

      /* Prepare to assign non-conflicting resnos to resjunk attributes */
      if (pstate->p_last_resno <= pstate->p_target_relation->rd_rel->relnatts)
--- 421,427 ----
***************
*** 495,501 ****
      /* done building the range table and jointree */
      qry->rtable = pstate->p_rtable;
      qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
-     qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);

      qry->hasSubLinks = pstate->p_hasSubLinks;
      qry->hasAggs = pstate->p_hasAggs;
--- 510,515 ----
***************
*** 1565,1591 ****
      oldrte->checkForRead = false;
      newrte->checkForRead = false;
      /*
!      * 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.
       * (Note we marked the RTEs "inFromCl = true" above to allow unqualified
!      * references to their fields.)
       */
      switch (stmt->event)
      {
          case CMD_SELECT:
!             addRTEtoJoinList(pstate, oldrte);
              break;
          case CMD_UPDATE:
!             addRTEtoJoinList(pstate, oldrte);
!             addRTEtoJoinList(pstate, newrte);
              break;
          case CMD_INSERT:
!             addRTEtoJoinList(pstate, newrte);
              break;
          case CMD_DELETE:
!             addRTEtoJoinList(pstate, oldrte);
              break;
          default:
              elog(ERROR, "transformRuleStmt: unexpected event type %d",
--- 1579,1605 ----
      oldrte->checkForRead = false;
      newrte->checkForRead = false;
      /*
!      * They must be in the namespace too for lookup purposes, but only add
       * the one(s) that are relevant for the current kind of rule.  In an
       * UPDATE rule, quals must refer to OLD.field or NEW.field to be
       * unambiguous, but there's no need to be so picky for INSERT & DELETE.
       * (Note we marked the RTEs "inFromCl = true" above to allow unqualified
!      * references to their fields.)  We do not add them to the joinlist.
       */
      switch (stmt->event)
      {
          case CMD_SELECT:
!             addRTEtoQuery(pstate, oldrte, false, true);
              break;
          case CMD_UPDATE:
!             addRTEtoQuery(pstate, oldrte, false, true);
!             addRTEtoQuery(pstate, newrte, false, true);
              break;
          case CMD_INSERT:
!             addRTEtoQuery(pstate, newrte, false, true);
              break;
          case CMD_DELETE:
!             addRTEtoQuery(pstate, oldrte, false, true);
              break;
          default:
              elog(ERROR, "transformRuleStmt: unexpected event type %d",
***************
*** 1638,1645 ****
               * 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 joinlist for
!              * qualified-name lookup, either (see qualifiedNameToVar()).
               */
              oldrte = addRangeTableEntry(sub_pstate, stmt->object->relname,
                                          makeAttr("*OLD*", NULL),
--- 1652,1660 ----
               * 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 must add them to the namespace, however, or they
!              * won't be accessible at all.  We decide later whether to put
!              * them in the joinlist.
               */
              oldrte = addRangeTableEntry(sub_pstate, stmt->object->relname,
                                          makeAttr("*OLD*", NULL),
***************
*** 1649,1654 ****
--- 1664,1671 ----
                                          false, false);
              oldrte->checkForRead = false;
              newrte->checkForRead = false;
+             addRTEtoQuery(sub_pstate, oldrte, false, true);
+             addRTEtoQuery(sub_pstate, newrte, false, true);

              /* Transform the rule action statement */
              top_subqry = transformStmt(sub_pstate, lfirst(actions));
***************
*** 1712,1721 ****
               */
              if (has_old || (has_new && stmt->event == CMD_UPDATE))
              {
!                 /* hack so we can use addRTEtoJoinList() */
                  sub_pstate->p_rtable = sub_qry->rtable;
                  sub_pstate->p_joinlist = sub_qry->jointree->fromlist;
!                 addRTEtoJoinList(sub_pstate, oldrte);
                  sub_qry->jointree->fromlist = sub_pstate->p_joinlist;
              }

--- 1729,1738 ----
               */
              if (has_old || (has_new && stmt->event == CMD_UPDATE))
              {
!                 /* hack so we can use addRTEtoQuery() */
                  sub_pstate->p_rtable = sub_qry->rtable;
                  sub_pstate->p_joinlist = sub_qry->jointree->fromlist;
!                 addRTEtoQuery(sub_pstate, oldrte, true, false);
                  sub_qry->jointree->fromlist = sub_pstate->p_joinlist;
              }

***************
*** 1779,1786 ****
      /* make FOR UPDATE clause available to addRangeTableEntry */
      pstate->p_forUpdate = stmt->forUpdate;

!     /* set up a range table */
!     makeRangeTable(pstate, stmt->fromClause);

      /* transform targetlist and WHERE */
      qry->targetList = transformTargetList(pstate, stmt->targetList);
--- 1796,1803 ----
      /* make FOR UPDATE clause available to addRangeTableEntry */
      pstate->p_forUpdate = stmt->forUpdate;

!     /* process the FROM clause */
!     transformFromClause(pstate, stmt->fromClause);

      /* transform targetlist and WHERE */
      qry->targetList = transformTargetList(pstate, stmt->targetList);
***************
*** 2055,2061 ****
      if (isLeaf)
      {
          /* Process leaf SELECT */
-         List   *save_rtable;
          List   *selectList;
          Query  *selectQuery;
          char    selectName[32];
--- 2072,2077 ----
***************
*** 2063,2078 ****
          RangeTblRef *rtr;

          /*
!          * Transform SelectStmt into a Query.  We do not want any previously
!          * transformed leaf queries to be visible in the outer context of
!          * this sub-query, so temporarily make the top-level pstate have an
!          * empty rtable.  (We needn't do the same with the joinlist because
!          * we aren't entering anything in the top-level joinlist.)
           */
-         save_rtable = pstate->p_rtable;
-         pstate->p_rtable = NIL;
          selectList = parse_analyze((Node *) stmt, pstate);
-         pstate->p_rtable = save_rtable;

          Assert(length(selectList) == 1);
          selectQuery = (Query *) lfirst(selectList);
--- 2079,2091 ----
          RangeTblRef *rtr;

          /*
!          * Transform SelectStmt into a Query.
!          *
!          * Note: previously transformed sub-queries don't affect the parsing
!          * of this sub-query, because they are not in the toplevel pstate's
!          * namespace list.
           */
          selectList = parse_analyze((Node *) stmt, pstate);

          Assert(length(selectList) == 1);
          selectQuery = (Query *) lfirst(selectList);
***************
*** 2202,2220 ****
      qry->commandType = CMD_UPDATE;
      pstate->p_is_update = true;

      /*
       * 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().
!      */
!     lockTargetTable(pstate, stmt->relname);
!     makeRangeTable(pstate, stmt->fromClause);
!     setTargetTable(pstate, stmt->relname,
!                    interpretInhOption(stmt->inhOpt), true);

      qry->targetList = transformTargetList(pstate, stmt->targetList);

--- 2215,2229 ----
      qry->commandType = CMD_UPDATE;
      pstate->p_is_update = true;

+     qry->resultRelation = setTargetTable(pstate, stmt->relname,
+                                          interpretInhOption(stmt->inhOpt),
+                                          true);
+
      /*
       * 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.
!      */
!     transformFromClause(pstate, stmt->fromClause);

      qry->targetList = transformTargetList(pstate, stmt->targetList);

***************
*** 2222,2228 ****

      qry->rtable = pstate->p_rtable;
      qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
-     qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);

      qry->hasSubLinks = pstate->p_hasSubLinks;
      qry->hasAggs = pstate->p_hasAggs;
--- 2231,2236 ----
*** src/backend/parser/parse_clause.c.orig    Wed Jan 24 16:00:42 2001
--- src/backend/parser/parse_clause.c    Tue Feb 13 16:50:14 2001
***************
*** 58,83 ****


  /*
!  * makeRangeTable -
!  *      Build the initial range table from the FROM clause.
   *
!  * The range table constructed here may grow as we transform the expressions
!  * in the query's quals and target list. (Note that this happens because in
!  * POSTQUEL, we allow references to relations not specified in the
!  * from-clause.  PostgreSQL keeps this extension to standard SQL.)
   *
!  * Note: we assume that pstate's p_rtable and p_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!
   */
  void
! makeRangeTable(ParseState *pstate, List *frmList)
  {
      List       *fl;

      /*
       * The grammar will have produced a list of RangeVars, RangeSubselects,
!      * and/or JoinExprs. Transform each one, and then add it to the joinlist.
       */
      foreach(fl, frmList)
      {
--- 58,87 ----


  /*
!  * transformFromClause -
!  *      Process the FROM clause and add items to the query's range table,
!  *      joinlist, and namespace.
   *
!  * Note: we assume that pstate's p_rtable, p_joinlist, and p_namespace lists
!  * were initialized to NIL when the pstate was created.  We will add onto
!  * any entries already present --- this is needed for rule processing, as
!  * well as for UPDATE and DELETE.
   *
!  * The range table may grow still further when we transform the expressions
!  * in the query's quals and target list. (This is possible because in
!  * POSTQUEL, we allowed references to relations not specified in the
!  * from-clause.  PostgreSQL keeps this extension to standard SQL.)
   */
  void
! transformFromClause(ParseState *pstate, List *frmList)
  {
      List       *fl;

      /*
       * The grammar will have produced a list of RangeVars, RangeSubselects,
!      * and/or JoinExprs. Transform each one (possibly adding entries to the
!      * rtable), check for duplicate refnames, and then add it to the joinlist
!      * and namespace.
       */
      foreach(fl, frmList)
      {
***************
*** 85,111 ****
          List       *containedRels;

          n = transformFromClauseItem(pstate, n, &containedRels);
          pstate->p_joinlist = lappend(pstate->p_joinlist, n);
      }
  }

  /*
!  * lockTargetTable
!  *      Find the target relation of INSERT/UPDATE/DELETE and acquire write
!  *      lock on it.  This must be done before building the range table,
!  *      in case the target is also mentioned as a source relation --- we
!  *      want to be sure to grab the write lock before any read lock.
   *
!  * The ParseState's link to the target relcache entry is also set here.
   */
! void
! lockTargetTable(ParseState *pstate, char *relname)
  {
      /* Close old target; this could only happen for multi-action rules */
      if (pstate->p_target_relation != NULL)
          heap_close(pstate->p_target_relation, NoLock);
-     pstate->p_target_relation = NULL;
-     pstate->p_target_rangetblentry = NULL; /* setTargetTable will set this */

      /*
       * Open target rel and grab suitable lock (which we will hold till
--- 89,129 ----
          List       *containedRels;

          n = transformFromClauseItem(pstate, n, &containedRels);
+         checkNameSpaceConflicts(pstate, (Node *) pstate->p_namespace, n);
          pstate->p_joinlist = lappend(pstate->p_joinlist, n);
+         pstate->p_namespace = lappend(pstate->p_namespace, n);
      }
  }

  /*
!  * setTargetTable
!  *      Add the target relation of INSERT/UPDATE/DELETE to the range table,
!  *      and make the special links to it in the ParseState.
!  *
!  *      We also open the target relation and acquire a write lock on it.
!  *      This must be done before processing the FROM list, in case the target
!  *      is also mentioned as a source relation --- we want to be sure to grab
!  *      the write lock before any read lock.
   *
!  *      If alsoSource is true, add the target to the query's joinlist and
!  *      namespace.  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.  (NOTE: we do not bother
!  *      to check for namespace conflict; we assume that the namespace was
!  *      initially empty in these cases.)
!  *
!  *      Returns the rangetable index of the target relation.
   */
! int
! setTargetTable(ParseState *pstate, char *relname,
!                bool inh, bool alsoSource)
  {
+     RangeTblEntry *rte;
+     int            rtindex;
+
      /* Close old target; this could only happen for multi-action rules */
      if (pstate->p_target_relation != NULL)
          heap_close(pstate->p_target_relation, NoLock);

      /*
       * Open target rel and grab suitable lock (which we will hold till
***************
*** 115,176 ****
       * but *not* release the lock.
       */
      pstate->p_target_relation = heap_openr(relname, RowExclusiveLock);
- }

! /*
!  * setTargetTable
!  *      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 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.
!  */
! void
! setTargetTable(ParseState *pstate, char *relname, bool inh, bool inJoinSet)
! {
!     RangeTblEntry *rte;

!     /* look for relname only at current nesting level... */
!     if (refnameRangeTablePosn(pstate, relname, NULL) == 0)
!     {
!         rte = addRangeTableEntry(pstate, relname, 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.
!          *
!          * Force inh to the desired setting for the target (XXX is this
!          * reasonable?  It's *necessary* that INSERT target not be marked
!          * inheritable, but otherwise not too clear what to do if conflict?)
!          */
!         rte->inh = inh;
!     }

!     /* Mark target table as requiring write access. */
      rte->checkForWrite = true;

!     if (inJoinSet)
!         addRTEtoJoinList(pstate, rte);
!
!     /* lockTargetTable should have been called earlier */
!     Assert(pstate->p_target_relation != NULL);

!     pstate->p_target_rangetblentry = rte;
  }

  /*
--- 133,168 ----
       * but *not* release the lock.
       */
      pstate->p_target_relation = heap_openr(relname, RowExclusiveLock);

!     /*
!      * Now build an RTE.
!      */
!     rte = addRangeTableEntry(pstate, relname, NULL, inh, false);
!     pstate->p_target_rangetblentry = rte;

!     /* assume new rte is at end */
!     rtindex = length(pstate->p_rtable);
!     Assert(rte == rt_fetch(rtindex, pstate->p_rtable));

!     /*
!      * Override addRangeTableEntry's default checkForRead, and instead
!      * mark target table as requiring write access.
!      *
!      * 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;
      rte->checkForWrite = true;

!     /*
!      * If UPDATE/DELETE, add table to joinlist and namespace.
!      */
!     if (alsoSource)
!         addRTEtoQuery(pstate, rte, true, true);

!     return rtindex;
  }

  /*
***************
*** 313,334 ****
                        List *containedRels)
  {
      Node       *result;
!     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 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 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 joinlist by causing relation refs to be added.
       */
!     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);
--- 305,325 ----
                        List *containedRels)
  {
      Node       *result;
!     List       *save_namespace;
      List       *clause_varnos,
                 *l;

      /*
!      * This is a tad tricky, for two reasons.  First, the namespace that
!      * the join expression should see is just the two subtrees of the JOIN
!      * plus any outer references from upper pstate levels.  So, temporarily
!      * set this pstate's namespace accordingly.  (We need not check for
!      * refname conflicts, because transformFromClauseItem() already did.)
!      * NOTE: this code is OK only because the ON clause can't legally alter
!      * the namespace by causing implicit relation refs to be added.
       */
!     save_namespace = pstate->p_namespace;
!     pstate->p_namespace = makeList2(j->larg, j->rarg);

      /* This part is just like transformWhereClause() */
      result = transformExpr(pstate, j->quals, EXPR_COLUMN_FIRST);
***************
*** 338,351 ****
               typeidTypeName(exprType(result)));
      }

!     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 joinlist if it uses fully-qualified names.  So, grovel
       * through the transformed clause and make sure there are no bogus
!      * references.
       */
      clause_varnos = pull_varnos(result);
      foreach(l, clause_varnos)
--- 329,342 ----
               typeidTypeName(exprType(result)));
      }

!     pstate->p_namespace = save_namespace;

      /*
       * 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 namespace if it uses fully-qualified names.  So, grovel
       * through the transformed clause and make sure there are no bogus
!      * references.  (Outer references are OK, and are ignored here.)
       */
      clause_varnos = pull_varnos(result);
      foreach(l, clause_varnos)
***************
*** 384,391 ****
                               interpretInhOption(r->inhOpt), true);

      /*
!      * 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 */
--- 375,382 ----
                               interpretInhOption(r->inhOpt), true);

      /*
!      * We create a RangeTblRef, but we do not add it to the joinlist or
!      * namespace; our caller must do that if appropriate.
       */
      rtr = makeNode(RangeTblRef);
      /* assume new rte is at end */
***************
*** 402,409 ****
  static RangeTblRef *
  transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
  {
!     List       *save_rtable;
!     List       *save_joinlist;
      List       *parsetrees;
      Query       *query;
      RangeTblEntry *rte;
--- 393,399 ----
  static RangeTblRef *
  transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
  {
!     List       *save_namespace;
      List       *parsetrees;
      Query       *query;
      RangeTblEntry *rte;
***************
*** 424,438 ****
       * does not include other FROM items).  But it does need to be able to
       * see any further-up parent states, so we can't just pass a null parent
       * pstate link.  So, temporarily make the current query level have an
!      * empty rtable and joinlist.
       */
!     save_rtable = pstate->p_rtable;
!     save_joinlist = pstate->p_joinlist;
!     pstate->p_rtable = NIL;
!     pstate->p_joinlist = NIL;
      parsetrees = parse_analyze(r->subquery, pstate);
!     pstate->p_rtable = save_rtable;
!     pstate->p_joinlist = save_joinlist;

      /*
       * Check that we got something reasonable.  Some of these conditions
--- 414,425 ----
       * does not include other FROM items).  But it does need to be able to
       * see any further-up parent states, so we can't just pass a null parent
       * pstate link.  So, temporarily make the current query level have an
!      * empty namespace.
       */
!     save_namespace = pstate->p_namespace;
!     pstate->p_namespace = NIL;
      parsetrees = parse_analyze(r->subquery, pstate);
!     pstate->p_namespace = save_namespace;

      /*
       * Check that we got something reasonable.  Some of these conditions
***************
*** 456,463 ****
      rte = addRangeTableEntryForSubquery(pstate, query, r->name, true);

      /*
!      * 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 */
--- 443,450 ----
      rte = addRangeTableEntryForSubquery(pstate, query, r->name, true);

      /*
!      * We create a RangeTblRef, but we do not add it to the joinlist or
!      * namespace; our caller must do that if appropriate.
       */
      rtr = makeNode(RangeTblRef);
      /* assume new rte is at end */
***************
*** 472,478 ****
   * 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 joinlist.
   *      This routine can recurse to handle SQL92 JOIN expressions.
   *
   *      Aside from the primary return value (the transformed joinlist item)
--- 459,465 ----
   * 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 joinlist and namespace.
   *      This routine can recurse to handle SQL92 JOIN expressions.
   *
   *      Aside from the primary return value (the transformed joinlist item)
***************
*** 526,531 ****
--- 513,525 ----
          *containedRels = nconc(l_containedRels, r_containedRels);

          /*
+          * Check for conflicting refnames in left and right subtrees.  Must
+          * do this because higher levels will assume I hand back a self-
+          * consistent namespace subtree.
+          */
+         checkNameSpaceConflicts(pstate, j->larg, j->rarg);
+
+         /*
           * Extract column name and var lists from both subtrees
           */
          if (IsA(j->larg, JoinExpr))
***************
*** 733,755 ****

          /*
           * Process alias (AS clause), if any.
-          *
-          * The given table alias must be unique in the current nesting level,
-          * ie it cannot match any RTE refname or jointable alias.  This is
-          * a bit painful to check because my own child joins are not yet in
-          * the pstate's joinlist, so they have to be scanned separately.
           */
          if (j->alias)
          {
-             /* 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 (scanJoinListForRefname(j->larg, j->alias->relname) ||
-                 scanJoinListForRefname(j->rarg, j->alias->relname))
-                 elog(ERROR, "Table name \"%s\" specified more than once",
-                      j->alias->relname);
              /*
               * If a column alias list is specified, substitute the alias
               * names into my output-column list
--- 727,735 ----
*** src/backend/parser/parse_expr.c.orig    Wed Jan 24 16:00:42 2001
--- src/backend/parser/parse_expr.c    Tue Feb 13 17:29:26 2001
***************
*** 541,547 ****
  {
      if (indirection == NIL)
          return basenode;
!     return (Node *) transformArraySubscripts(pstate, basenode,
                                               indirection, false, NULL);
  }

--- 541,548 ----
  {
      if (indirection == NIL)
          return basenode;
!     return (Node *) transformArraySubscripts(pstate,
!                                              basenode, exprType(basenode),
                                               indirection, false, NULL);
  }

***************
*** 558,570 ****
  transformIdent(ParseState *pstate, Ident *ident, int precedence)
  {
      Node       *result = NULL;

      /*
       * try to find the ident as a relation ... but not if subscripts
       * appear
       */
      if (ident->indirection == NIL &&
!         refnameRangeTableEntry(pstate, ident->name) != NULL)
      {
          ident->isRel = TRUE;
          result = (Node *) ident;
--- 559,572 ----
  transformIdent(ParseState *pstate, Ident *ident, int precedence)
  {
      Node       *result = NULL;
+     int            sublevels_up;

      /*
       * try to find the ident as a relation ... but not if subscripts
       * appear
       */
      if (ident->indirection == NIL &&
!         refnameRangeOrJoinEntry(pstate, ident->name, &sublevels_up) != NULL)
      {
          ident->isRel = TRUE;
          result = (Node *) ident;
*** src/backend/parser/parse_func.c.orig    Wed Jan 24 16:00:43 2001
--- src/backend/parser/parse_func.c    Tue Feb 13 16:28:26 2001
***************
*** 427,432 ****
--- 427,433 ----
          {
              RangeTblEntry *rte;
              int            vnum;
+             Node       *rteorjoin;
              int            sublevels_up;

              /*
***************
*** 434,442 ****
               */
              refname = ((Ident *) arg)->name;

!             rte = refnameRangeTableEntry(pstate, refname);
!             if (rte == NULL)
                  rte = addImplicitRTE(pstate, refname);

              vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);

--- 435,463 ----
               */
              refname = ((Ident *) arg)->name;

!             rteorjoin = refnameRangeOrJoinEntry(pstate, refname,
!                                                 &sublevels_up);
!
!             if (rteorjoin == NULL)
!             {
                  rte = addImplicitRTE(pstate, refname);
+             }
+             else if (IsA(rteorjoin, RangeTblEntry))
+             {
+                 rte = (RangeTblEntry *) rteorjoin;
+             }
+             else if (IsA(rteorjoin, JoinExpr))
+             {
+                 elog(ERROR,
+                      "function applied to tuple is not supported for joins");
+                 rte = NULL;        /* keep compiler quiet */
+             }
+             else
+             {
+                 elog(ERROR, "ParseFuncOrColumn: unexpected node type %d",
+                      nodeTag(rteorjoin));
+                 rte = NULL;        /* keep compiler quiet */
+             }

              vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);

*** src/backend/parser/parse_node.c.orig    Wed Jan 24 16:00:43 2001
--- src/backend/parser/parse_node.c    Tue Feb 13 17:29:29 2001
***************
*** 229,248 ****
   *
   * pstate        Parse state
   * arrayBase    Already-transformed expression for the array as a whole
   * indirection    Untransformed list of subscripts (must not be NIL)
   * forceSlice    If true, treat subscript as array slice in all cases
   * assignFrom    NULL for array fetch, else transformed expression for source.
   */
! ArrayRef   *
  transformArraySubscripts(ParseState *pstate,
                           Node *arrayBase,
                           List *indirection,
                           bool forceSlice,
                           Node *assignFrom)
  {
!     Oid            typearray,
!                 typeelement,
!                 typeresult;
      HeapTuple    type_tuple_array,
                  type_tuple_element;
      Form_pg_type type_struct_array,
--- 229,250 ----
   *
   * pstate        Parse state
   * arrayBase    Already-transformed expression for the array as a whole
+  *                (may be NULL if we are handling an INSERT)
+  * arrayType    OID of array's datatype
   * indirection    Untransformed list of subscripts (must not be NIL)
   * forceSlice    If true, treat subscript as array slice in all cases
   * assignFrom    NULL for array fetch, else transformed expression for source.
   */
! ArrayRef *
  transformArraySubscripts(ParseState *pstate,
                           Node *arrayBase,
+                          Oid arrayType,
                           List *indirection,
                           bool forceSlice,
                           Node *assignFrom)
  {
!     Oid            elementType,
!                 resultType;
      HeapTuple    type_tuple_array,
                  type_tuple_element;
      Form_pg_type type_struct_array,
***************
*** 254,281 ****
      ArrayRef   *aref;

      /* Get the type tuple for the array */
-     typearray = exprType(arrayBase);
-
      type_tuple_array = SearchSysCache(TYPEOID,
!                                       ObjectIdGetDatum(typearray),
                                        0, 0, 0);
      if (!HeapTupleIsValid(type_tuple_array))
          elog(ERROR, "transformArraySubscripts: Cache lookup failed for array type %u",
!              typearray);
      type_struct_array = (Form_pg_type) GETSTRUCT(type_tuple_array);

!     typeelement = type_struct_array->typelem;
!     if (typeelement == InvalidOid)
          elog(ERROR, "transformArraySubscripts: type %s is not an array",
               NameStr(type_struct_array->typname));

      /* Get the type tuple for the array element type */
      type_tuple_element = SearchSysCache(TYPEOID,
!                                         ObjectIdGetDatum(typeelement),
                                          0, 0, 0);
      if (!HeapTupleIsValid(type_tuple_element))
          elog(ERROR, "transformArraySubscripts: Cache lookup failed for array element type %u",
!              typeelement);
      type_struct_element = (Form_pg_type) GETSTRUCT(type_tuple_element);

      /*
--- 256,281 ----
      ArrayRef   *aref;

      /* Get the type tuple for the array */
      type_tuple_array = SearchSysCache(TYPEOID,
!                                       ObjectIdGetDatum(arrayType),
                                        0, 0, 0);
      if (!HeapTupleIsValid(type_tuple_array))
          elog(ERROR, "transformArraySubscripts: Cache lookup failed for array type %u",
!              arrayType);
      type_struct_array = (Form_pg_type) GETSTRUCT(type_tuple_array);

!     elementType = type_struct_array->typelem;
!     if (elementType == InvalidOid)
          elog(ERROR, "transformArraySubscripts: type %s is not an array",
               NameStr(type_struct_array->typname));

      /* Get the type tuple for the array element type */
      type_tuple_element = SearchSysCache(TYPEOID,
!                                         ObjectIdGetDatum(elementType),
                                          0, 0, 0);
      if (!HeapTupleIsValid(type_tuple_element))
          elog(ERROR, "transformArraySubscripts: Cache lookup failed for array element type %u",
!              elementType);
      type_struct_element = (Form_pg_type) GETSTRUCT(type_tuple_element);

      /*
***************
*** 308,316 ****
       * array type if we are fetching a slice or storing.
       */
      if (isSlice || assignFrom != NULL)
!         typeresult = typearray;
      else
!         typeresult = typeelement;

      /*
       * Transform the subscript expressions.
--- 308,316 ----
       * array type if we are fetching a slice or storing.
       */
      if (isSlice || assignFrom != NULL)
!         resultType = arrayType;
      else
!         resultType = elementType;

      /*
       * Transform the subscript expressions.
***************
*** 359,365 ****
      if (assignFrom != NULL)
      {
          Oid            typesource = exprType(assignFrom);
!         Oid            typeneeded = isSlice ? typearray : typeelement;

          if (typesource != InvalidOid)
          {
--- 359,365 ----
      if (assignFrom != NULL)
      {
          Oid            typesource = exprType(assignFrom);
!         Oid            typeneeded = isSlice ? arrayType : elementType;

          if (typesource != InvalidOid)
          {
***************
*** 385,391 ****
      aref = makeNode(ArrayRef);
      aref->refattrlength = type_struct_array->typlen;
      aref->refelemlength = type_struct_element->typlen;
!     aref->refelemtype = typeresult;        /* XXX should save element type
                                           * too */
      aref->refelembyval = type_struct_element->typbyval;
      aref->refupperindexpr = upperIndexpr;
--- 385,391 ----
      aref = makeNode(ArrayRef);
      aref->refattrlength = type_struct_array->typlen;
      aref->refelemlength = type_struct_element->typlen;
!     aref->refelemtype = resultType;        /* XXX should save element type
                                           * too */
      aref->refelembyval = type_struct_element->typbyval;
      aref->refupperindexpr = upperIndexpr;
*** src/backend/parser/parse_relation.c.orig    Wed Jan 24 16:00:43 2001
--- src/backend/parser/parse_relation.c    Tue Feb 13 16:28:29 2001
***************
*** 12,21 ****
   *
   *-------------------------------------------------------------------------
   */
- #include <ctype.h>
-
  #include "postgres.h"

  #include "access/heapam.h"
  #include "access/htup.h"
  #include "catalog/pg_type.h"
--- 12,21 ----
   *
   *-------------------------------------------------------------------------
   */
  #include "postgres.h"

+ #include <ctype.h>
+
  #include "access/heapam.h"
  #include "access/htup.h"
  #include "catalog/pg_type.h"
***************
*** 30,35 ****
--- 30,37 ----
  #include "utils/lsyscache.h"


+ static Node *scanNameSpaceForRefname(ParseState *pstate, Node *nsnode,
+                                      char *refname);
  static Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte,
                                char *colname);
  static Node *scanJoinForColumn(JoinExpr *join, char *colname,
***************
*** 93,117 ****

      while (pstate != NULL)
      {
!         List       *temp;
!         JoinExpr   *join;

!         /*
!          * Check the rangetable for RTEs; if no match, recursively scan
!          * the joinlist for join tables.  We assume that no duplicate
!          * entries have been made in any one nesting level.
!          */
!         foreach(temp, pstate->p_rtable)
!         {
!             RangeTblEntry *rte = lfirst(temp);
!
!             if (strcmp(rte->eref->relname, refname) == 0)
!                 return (Node *) rte;
!         }
!
!         join = scanJoinListForRefname((Node *) pstate->p_joinlist, refname);
!         if (join)
!             return (Node *) join;

          pstate = pstate->parentParseState;
          if (sublevels_up)
--- 95,107 ----

      while (pstate != NULL)
      {
!         Node       *rte;

!         rte = scanNameSpaceForRefname(pstate,
!                                       (Node *) pstate->p_namespace,
!                                       refname);
!         if (rte)
!             return rte;

          pstate = pstate->parentParseState;
          if (sublevels_up)
***************
*** 123,230 ****
  }

  /*
!  * 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 *
! scanJoinListForRefname(Node *jtnode, char *refname)
  {
!     JoinExpr   *result = NULL;

!     if (jtnode == NULL)
          return NULL;
!     if (IsA(jtnode, List))
      {
!         List       *l;

!         foreach(l, (List *) jtnode)
!         {
!             result = scanJoinListForRefname(lfirst(l), refname);
!             if (result)
!                 break;
!         }
      }
!     else if (IsA(jtnode, RangeTblRef))
      {
!         /* ignore ... */
      }
!     else if (IsA(jtnode, JoinExpr))
      {
!         JoinExpr   *j = (JoinExpr *) jtnode;

!         if (j->alias && strcmp(j->alias->relname, refname) == 0)
!             return j;
!         result = scanJoinListForRefname(j->larg, refname);
!         if (! result)
!             result = scanJoinListForRefname(j->rarg, refname);
      }
      else
!         elog(ERROR, "scanJoinListForRefname: unexpected node type %d",
!              nodeTag(jtnode));
      return result;
  }

! /*
!  * given refname, return a pointer to the range table entry.
!  *
!  * NOTE that this routine will ONLY find RTEs, not join tables.
!  */
! RangeTblEntry *
! refnameRangeTableEntry(ParseState *pstate, char *refname)
  {
!     List       *temp;
!
!     while (pstate != NULL)
!     {
!         foreach(temp, pstate->p_rtable)
!         {
!             RangeTblEntry *rte = lfirst(temp);
!
!             if (strcmp(rte->eref->relname, refname) == 0)
!                 return rte;
!         }
!         pstate = pstate->parentParseState;
!     }
!     return NULL;
  }

  /*
!  * given refname, return RT index (starting with 1) of the relation,
!  * and optionally get its nesting depth (0 = current).    If sublevels_up
!  * is NULL, only consider rels at the current nesting level.
!  * A zero result means name not found.
   *
!  * NOTE that this routine will ONLY find RTEs, not join tables.
   */
! int
! refnameRangeTablePosn(ParseState *pstate, char *refname, int *sublevels_up)
  {
!     int            index;
!     List       *temp;
!
!     if (sublevels_up)
!         *sublevels_up = 0;

!     while (pstate != NULL)
      {
!         index = 1;
!         foreach(temp, pstate->p_rtable)
          {
!             RangeTblEntry *rte = lfirst(temp);

!             if (strcmp(rte->eref->relname, refname) == 0)
!                 return index;
!             index++;
          }
-         pstate = pstate->parentParseState;
-         if (sublevels_up)
-             (*sublevels_up)++;
-         else
-             break;
      }
!     return 0;
  }

  /*
--- 113,241 ----
  }

  /*
!  * Recursively search a namespace for an RTE or joinexpr with given refname.
!  *
!  * The top level of p_namespace is a list, and we recurse into any joins
!  * that are not subqueries.  It is also possible to pass an individual
!  * join subtree (useful when checking for name conflicts within a scope).
   *
!  * Note: we do not worry about the possibility of multiple matches;
!  * we assume the code that built the namespace checked for duplicates.
   */
! static Node *
! scanNameSpaceForRefname(ParseState *pstate, Node *nsnode,
!                         char *refname)
  {
!     Node       *result = NULL;

!     if (nsnode == NULL)
          return NULL;
!     if (IsA(nsnode, RangeTblRef))
      {
!         int            varno = ((RangeTblRef *) nsnode)->rtindex;
!         RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);

!         if (strcmp(rte->eref->relname, refname) == 0)
!             result = (Node *) rte;
      }
!     else if (IsA(nsnode, JoinExpr))
      {
!         JoinExpr   *j = (JoinExpr *) nsnode;
!
!         if (j->alias)
!         {
!             if (strcmp(j->alias->relname, refname) == 0)
!                 return (Node *) j; /* matched a join alias */
!             /*
!              * Tables within an aliased join are invisible from outside
!              * the join, according to the scope rules of SQL92 (the join
!              * is considered a subquery).  So, stop here.
!              */
!             return NULL;
!         }
!         result = scanNameSpaceForRefname(pstate, j->larg, refname);
!         if (! result)
!             result = scanNameSpaceForRefname(pstate, j->rarg, refname);
      }
!     else if (IsA(nsnode, List))
      {
!         List       *l;

!         foreach(l, (List *) nsnode)
!         {
!             result = scanNameSpaceForRefname(pstate, lfirst(l), refname);
!             if (result)
!                 break;
!         }
      }
      else
!         elog(ERROR, "scanNameSpaceForRefname: unexpected node type %d",
!              nodeTag(nsnode));
      return result;
  }

! /* Convenience subroutine for checkNameSpaceConflicts */
! static void
! scanNameSpaceForConflict(ParseState *pstate, Node *nsnode,
!                          char *refname)
  {
!     if (scanNameSpaceForRefname(pstate, nsnode, refname) != NULL)
!         elog(ERROR, "Table name \"%s\" specified more than once", refname);
  }

  /*
!  * Recursively check for refname conflicts between two namespaces or
!  * namespace subtrees.  Raise an error if any is found.
!  *
!  * Works by recursively scanning namespace1 in the same way that
!  * scanNameSpaceForRefname does, and then looking in namespace2 for
!  * a match to each refname found in namespace1.
   *
!  * Note: we assume that each given argument does not contain conflicts
!  * itself; we just want to know if the two can be merged together.
   */
! void
! checkNameSpaceConflicts(ParseState *pstate, Node *namespace1,
!                         Node *namespace2)
  {
!     if (namespace1 == NULL)
!         return;
!     if (IsA(namespace1, RangeTblRef))
!     {
!         int            varno = ((RangeTblRef *) namespace1)->rtindex;
!         RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);

!         scanNameSpaceForConflict(pstate, namespace2, rte->eref->relname);
!     }
!     else if (IsA(namespace1, JoinExpr))
      {
!         JoinExpr   *j = (JoinExpr *) namespace1;
!
!         if (j->alias)
          {
!             scanNameSpaceForConflict(pstate, namespace2, j->alias->relname);
!             /*
!              * Tables within an aliased join are invisible from outside
!              * the join, according to the scope rules of SQL92 (the join
!              * is considered a subquery).  So, stop here.
!              */
!             return;
!         }
!         checkNameSpaceConflicts(pstate, j->larg, namespace2);
!         checkNameSpaceConflicts(pstate, j->rarg, namespace2);
!     }
!     else if (IsA(namespace1, List))
!     {
!         List       *l;

!         foreach(l, (List *) namespace1)
!         {
!             checkNameSpaceConflicts(pstate, lfirst(l), namespace2);
          }
      }
!     else
!         elog(ERROR, "checkNameSpaceConflicts: unexpected node type %d",
!              nodeTag(namespace1));
  }

  /*
***************
*** 257,262 ****
--- 268,274 ----
          else
              break;
      }
+
      elog(ERROR, "RTERangeTablePosn: RTE not found (internal error)");
      return 0;                    /* keep compiler quiet */
  }
***************
*** 369,389 ****

      while (pstate != NULL)
      {
!         List       *jt;

          /*
!          * We want to look only at top-level jointree items, and even for
           * those, ignore RTEs that are marked as not inFromCl and not
           * the query's target relation.
           */
!         foreach(jt, pstate->p_joinlist)
          {
!             Node   *jtnode = (Node *) lfirst(jt);
              Node   *newresult = NULL;

!             if (IsA(jtnode, RangeTblRef))
              {
!                 int            varno = ((RangeTblRef *) jtnode)->rtindex;
                  RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);

                  if (! rte->inFromCl &&
--- 381,401 ----

      while (pstate != NULL)
      {
!         List       *ns;

          /*
!          * We need to look only at top-level namespace items, and even for
           * those, ignore RTEs that are marked as not inFromCl and not
           * the query's target relation.
           */
!         foreach(ns, pstate->p_namespace)
          {
!             Node   *nsnode = (Node *) lfirst(ns);
              Node   *newresult = NULL;

!             if (IsA(nsnode, RangeTblRef))
              {
!                 int            varno = ((RangeTblRef *) nsnode)->rtindex;
                  RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);

                  if (! rte->inFromCl &&
***************
*** 393,407 ****
                  /* use orig_pstate here to get the right sublevels_up */
                  newresult = scanRTEForColumn(orig_pstate, rte, colname);
              }
!             else if (IsA(jtnode, JoinExpr))
              {
!                 JoinExpr   *j = (JoinExpr *) jtnode;

                  newresult = scanJoinForColumn(j, colname, levels_up);
              }
              else
                  elog(ERROR, "colnameToVar: unexpected node type %d",
!                      nodeTag(jtnode));

              if (newresult)
              {
--- 405,419 ----
                  /* use orig_pstate here to get the right sublevels_up */
                  newresult = scanRTEForColumn(orig_pstate, rte, colname);
              }
!             else if (IsA(nsnode, JoinExpr))
              {
!                 JoinExpr   *j = (JoinExpr *) nsnode;

                  newresult = scanJoinForColumn(j, colname, levels_up);
              }
              else
                  elog(ERROR, "colnameToVar: unexpected node type %d",
!                      nodeTag(nsnode));

              if (newresult)
              {
***************
*** 451,457 ****
                                    colname);
      else if (IsA(rteorjoin, JoinExpr))
          result = scanJoinForColumn((JoinExpr *) rteorjoin,
!                                   colname, sublevels_up);
      else
      {
          elog(ERROR, "qualifiedNameToVar: unexpected node type %d",
--- 463,469 ----
                                    colname);
      else if (IsA(rteorjoin, JoinExpr))
          result = scanJoinForColumn((JoinExpr *) rteorjoin,
!                                    colname, sublevels_up);
      else
      {
          elog(ERROR, "qualifiedNameToVar: unexpected node type %d",
***************
*** 465,474 ****
  /*
   * 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.
   */
  RangeTblEntry *
  addRangeTableEntry(ParseState *pstate,
--- 477,487 ----
  /*
   * Add an entry for a relation to the pstate's range table (p_rtable).
   *
!  * If pstate is NULL, we just build an RTE and return it without adding it
!  * to an rtable list.
   *
!  * Note: formerly this checked for refname conflicts, but that's wrong.
!  * Caller is responsible for checking for conflicts in the appropriate scope.
   */
  RangeTblEntry *
  addRangeTableEntry(ParseState *pstate,
***************
*** 477,503 ****
                     bool inh,
                     bool inFromCl)
  {
      char       *refname = alias ? alias->relname : relname;
      LOCKMODE    lockmode;
      Relation    rel;
-     RangeTblEntry *rte;
      Attr       *eref;
      int            maxattrs;
      int            numaliases;
      int            varattno;

-     /* 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 = relname;
      rte->alias = alias;
      rte->subquery = NULL;
--- 490,504 ----
                     bool inh,
                     bool inFromCl)
  {
+     RangeTblEntry *rte = makeNode(RangeTblEntry);
      char       *refname = alias ? alias->relname : relname;
      LOCKMODE    lockmode;
      Relation    rel;
      Attr       *eref;
      int            maxattrs;
      int            numaliases;
      int            varattno;

      rte->relname = relname;
      rte->alias = alias;
      rte->subquery = NULL;
***************
*** 559,565 ****
      rte->checkAsUser = InvalidOid; /* not set-uid by default, either */

      /*
!      * Add completed RTE to range table list.
       */
      if (pstate != NULL)
          pstate->p_rtable = lappend(pstate->p_rtable, rte);
--- 560,567 ----
      rte->checkAsUser = InvalidOid; /* not set-uid by default, either */

      /*
!      * Add completed RTE to pstate's range table list, but not to join list
!      * nor namespace --- caller must do that if appropriate.
       */
      if (pstate != NULL)
          pstate->p_rtable = lappend(pstate->p_rtable, rte);
***************
*** 579,603 ****
                                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;
--- 581,593 ----
                                Attr *alias,
                                bool inFromCl)
  {
+     RangeTblEntry *rte = makeNode(RangeTblEntry);
      char       *refname = alias->relname;
      Attr       *eref;
      int            numaliases;
      int            varattno;
      List       *tlistitem;

      rte->relname = NULL;
      rte->relid = InvalidOid;
      rte->subquery = subquery;
***************
*** 647,653 ****
      rte->checkAsUser = InvalidOid;

      /*
!      * Add completed RTE to range table list.
       */
      if (pstate != NULL)
          pstate->p_rtable = lappend(pstate->p_rtable, rte);
--- 637,644 ----
      rte->checkAsUser = InvalidOid;

      /*
!      * Add completed RTE to pstate's range table list, but not to join list
!      * nor namespace --- caller must do that if appropriate.
       */
      if (pstate != NULL)
          pstate->p_rtable = lappend(pstate->p_rtable, rte);
***************
*** 691,727 ****
  }

  /*
!  * Add the given RTE as a top-level entry in the pstate's join list,
!  * unless there already is an entry for it.
   */
  void
! addRTEtoJoinList(ParseState *pstate, RangeTblEntry *rte)
  {
      int            rtindex = RTERangeTablePosn(pstate, rte, NULL);
!     List       *jt;
!     RangeTblRef *rtr;
!
!     foreach(jt, pstate->p_joinlist)
!     {
!         Node       *n = (Node *) lfirst(jt);

-         if (IsA(n, RangeTblRef))
-         {
-             if (rtindex == ((RangeTblRef *) n)->rtindex)
-                 return;            /* it's already being joined to */
-         }
-     }
-
-     /* Not present, so add it */
-     rtr = makeNode(RangeTblRef);
      rtr->rtindex = rtindex;
!     pstate->p_joinlist = lappend(pstate->p_joinlist, rtr);
  }

  /*
   * Add a POSTQUEL-style implicit RTE.
   *
!  * We assume caller has already checked that there is no such RTE now.
   */
  RangeTblEntry *
  addImplicitRTE(ParseState *pstate, char *relname)
--- 682,711 ----
  }

  /*
!  * Add the given RTE as a top-level entry in the pstate's join list
!  * and/or name space list.  (We assume caller has checked for any
!  * namespace conflict.)
   */
  void
! addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte,
!               bool addToJoinList, bool addToNameSpace)
  {
      int            rtindex = RTERangeTablePosn(pstate, rte, NULL);
!     RangeTblRef *rtr = makeNode(RangeTblRef);

      rtr->rtindex = rtindex;
!
!     if (addToJoinList)
!         pstate->p_joinlist = lappend(pstate->p_joinlist, rtr);
!     if (addToNameSpace)
!         pstate->p_namespace = lappend(pstate->p_namespace, rtr);
  }

  /*
   * Add a POSTQUEL-style implicit RTE.
   *
!  * We assume caller has already checked that there is no RTE or join with
!  * a conflicting name.
   */
  RangeTblEntry *
  addImplicitRTE(ParseState *pstate, char *relname)
***************
*** 729,735 ****
      RangeTblEntry *rte;

      rte = addRangeTableEntry(pstate, relname, NULL, false, false);
!     addRTEtoJoinList(pstate, rte);
      warnAutoRange(pstate, relname);

      return rte;
--- 713,719 ----
      RangeTblEntry *rte;

      rte = addRangeTableEntry(pstate, relname, NULL, false, false);
!     addRTEtoQuery(pstate, rte, true, true);
      warnAutoRange(pstate, relname);

      return rte;
***************
*** 1088,1091 ****
               pstate->parentParseState != NULL ? " in subquery" : "",
               refname);
  }
-
--- 1072,1074 ----
*** src/backend/parser/parse_target.c.orig    Wed Jan 24 16:00:43 2001
--- src/backend/parser/parse_target.c    Tue Feb 13 17:29:34 2001
***************
*** 212,240 ****
       */
      if (indirection)
      {
-         Attr       *att = makeAttr(pstrdup(RelationGetRelationName(rd)),
-                                    colname);
          Node       *arrayBase;
          ArrayRef   *aref;

-         arrayBase = ParseNestedFuncOrColumn(pstate, att, EXPR_COLUMN_FIRST);
-         aref = transformArraySubscripts(pstate, arrayBase,
-                                         indirection,
-                                         pstate->p_is_insert,
-                                         tle->expr);
          if (pstate->p_is_insert)
          {
-
              /*
               * The command is INSERT INTO table (arraycol[subscripts]) ...
               * so there is not really a source array value to work with.
               * Let the executor do something reasonable, if it can. Notice
!              * that we forced transformArraySubscripts to treat the
!              * subscripting op as an array-slice op above, so the source
!              * data will have been coerced to array type.
               */
!             aref->refexpr = NULL;        /* signal there is no source array */
          }
          tle->expr = (Node *) aref;
      }
      else
--- 212,248 ----
       */
      if (indirection)
      {
          Node       *arrayBase;
          ArrayRef   *aref;

          if (pstate->p_is_insert)
          {
              /*
               * The command is INSERT INTO table (arraycol[subscripts]) ...
               * so there is not really a source array value to work with.
               * Let the executor do something reasonable, if it can. Notice
!              * that we force transformArraySubscripts to treat the
!              * subscripting op as an array-slice op below, so the source
!              * data will have been coerced to the array type.
!              */
!             arrayBase = NULL;    /* signal there is no source array */
!         }
!         else
!         {
!             /*
!              * Build a Var for the array to be updated.
               */
!             arrayBase = (Node *) make_var(pstate,
!                                           pstate->p_target_rangetblentry,
!                                           attrno);
          }
+
+         aref = transformArraySubscripts(pstate,
+                                         arrayBase,
+                                         attrtype,
+                                         indirection,
+                                         pstate->p_is_insert,
+                                         tle->expr);
          tle->expr = (Node *) aref;
      }
      else
***************
*** 385,406 ****
  /* ExpandAllTables()
   * 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 transformed into a joinlist.
   */
  static List *
  ExpandAllTables(ParseState *pstate)
  {
      List       *target = NIL;
!     List       *jt;

!     /* SELECT *; */
!     if (pstate->p_joinlist == NIL)
!         elog(ERROR, "Wildcard with no tables specified not allowed");
!
!     foreach(jt, pstate->p_joinlist)
      {
!         Node       *n = (Node *) lfirst(jt);

          if (IsA(n, RangeTblRef))
          {
--- 393,411 ----
  /* ExpandAllTables()
   * Turns '*' (in the target list) into a list of targetlist entries.
   *
!  * tlist entries are generated for each relation appearing at the top level
!  * of the query's namespace, except for RTEs marked not inFromCl.  (These
!  * may include NEW/OLD pseudo-entries, implicit RTEs, etc.)
   */
  static List *
  ExpandAllTables(ParseState *pstate)
  {
      List       *target = NIL;
!     List       *ns;

!     foreach(ns, pstate->p_namespace)
      {
!         Node       *n = (Node *) lfirst(ns);

          if (IsA(n, RangeTblRef))
          {
***************
*** 430,435 ****
--- 435,444 ----
              elog(ERROR, "ExpandAllTables: unexpected node (internal error)"
                   "\n\t%s", nodeToString(n));
      }
+
+     /* Check for SELECT *; */
+     if (target == NIL)
+         elog(ERROR, "Wildcard with no tables specified not allowed");

      return target;
  }
*** src/include/nodes/primnodes.h.orig    Wed Jan 24 16:01:34 2001
--- src/include/nodes/primnodes.h    Tue Feb 13 16:26:54 2001
***************
*** 499,508 ****
   * are not equivalent to ON() since they also affect the output column list.
   *
   * alias is an Attr node representing the AS alias-clause attached to the
!  * join expression, or NULL if no clause.  During parse analysis, colnames
!  * is filled with a list of String nodes giving the column names (real or
!  * alias) of the output of the join, and colvars is filled with a list of
!  * expressions that can be copied to reference the output columns.
   *----------
   */
  typedef struct JoinExpr
--- 499,512 ----
   * are not equivalent to ON() since they also affect the output column list.
   *
   * alias is an Attr node representing the AS alias-clause attached to the
!  * join expression, or NULL if no clause.  NB: presence or absence of the
!  * alias has a critical impact on semantics, because a join with an alias
!  * restricts visibility of the tables/columns inside it.
!  *
!  * During parse analysis, colnames is filled with a list of String nodes
!  * giving the column names (real or alias) of the output of the join,
!  * and colvars is filled with a list of expressions that can be copied to
!  * reference the output columns.
   *----------
   */
  typedef struct JoinExpr
*** src/include/parser/parse_clause.h.orig    Wed Jan 24 16:01:35 2001
--- src/include/parser/parse_clause.h    Tue Feb 13 16:27:44 2001
***************
*** 16,25 ****

  #include "parser/parse_node.h"

! extern void makeRangeTable(ParseState *pstate, List *frmList);
! extern void lockTargetTable(ParseState *pstate, char *relname);
! extern void setTargetTable(ParseState *pstate, char *relname,
!                            bool inh, bool inJoinSet);
  extern bool interpretInhOption(InhOption inhOpt);
  extern Node *transformWhereClause(ParseState *pstate, Node *where);
  extern List *transformGroupClause(ParseState *pstate, List *grouplist,
--- 16,24 ----

  #include "parser/parse_node.h"

! extern void transformFromClause(ParseState *pstate, List *frmList);
! extern int setTargetTable(ParseState *pstate, char *relname,
!                           bool inh, bool alsoSource);
  extern bool interpretInhOption(InhOption inhOpt);
  extern Node *transformWhereClause(ParseState *pstate, Node *where);
  extern List *transformGroupClause(ParseState *pstate, List *grouplist,
*** src/include/parser/parse_node.h.orig    Wed Jan 24 16:01:36 2001
--- src/include/parser/parse_node.h    Tue Feb 13 17:29:10 2001
***************
*** 18,23 ****
--- 18,37 ----

  /*
   * State information used during parse analysis
+  *
+  * p_rtable: list of RTEs that will become the rangetable of the query.
+  * Note that neither relname nor refname of these entries are necessarily
+  * unique; searching the rtable by name is a bad idea.
+  *
+  * p_joinlist: list of join items (RangeTblRef and JoinExpr nodes) that
+  * will become the fromlist of the query's top-level FromExpr node.
+  *
+  * p_namespace: list of join items that represents the current namespace
+  * for table and column lookup.  This may be just a subset of the rtable +
+  * joinlist, and/or may contain entries that are not yet added to the main
+  * joinlist.  Note that an RTE that is present in p_namespace, but does not
+  * have its inFromCl flag set, is accessible only with an explicit qualifier;
+  * lookups of unqualified column names should ignore it.
   */
  typedef struct ParseState
  {
***************
*** 25,30 ****
--- 39,45 ----
      List       *p_rtable;        /* range table so far */
      List       *p_joinlist;        /* join items so far (will become
                                   * FromExpr node's fromlist) */
+     List       *p_namespace;    /* current lookup namespace (join items) */
      int            p_last_resno;    /* last targetlist resno assigned */
      List       *p_forUpdate;    /* FOR UPDATE clause, if any (see gram.y) */
      bool        p_hasAggs;
***************
*** 42,47 ****
--- 57,63 ----
  extern Var *make_var(ParseState *pstate, RangeTblEntry *rte, int attrno);
  extern ArrayRef *transformArraySubscripts(ParseState *pstate,
                           Node *arrayBase,
+                          Oid arrayType,
                           List *indirection,
                           bool forceSlice,
                           Node *assignFrom);
*** src/include/parser/parse_relation.h.orig    Wed Jan 24 16:01:36 2001
--- src/include/parser/parse_relation.h    Tue Feb 13 16:27:41 2001
***************
*** 19,33 ****
  extern Node *refnameRangeOrJoinEntry(ParseState *pstate,
                                       char *refname,
                                       int *sublevels_up);
! extern RangeTblEntry *refnameRangeTableEntry(ParseState *pstate,
!                                              char *refname);
! extern int refnameRangeTablePosn(ParseState *pstate,
!                                  char *refname,
!                                  int *sublevels_up);
  extern int RTERangeTablePosn(ParseState *pstate,
                               RangeTblEntry *rte,
                               int *sublevels_up);
- 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);
--- 19,29 ----
  extern Node *refnameRangeOrJoinEntry(ParseState *pstate,
                                       char *refname,
                                       int *sublevels_up);
! extern void checkNameSpaceConflicts(ParseState *pstate, Node *namespace1,
!                                     Node *namespace2);
  extern int RTERangeTablePosn(ParseState *pstate,
                               RangeTblEntry *rte,
                               int *sublevels_up);
  extern Node *colnameToVar(ParseState *pstate, char *colname);
  extern Node *qualifiedNameToVar(ParseState *pstate, char *refname,
                                  char *colname, bool implicitRTEOK);
***************
*** 40,46 ****
                                                      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);
--- 36,43 ----
                                                      Query *subquery,
                                                      Attr *alias,
                                                      bool inFromCl);
! extern void addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte,
!                           bool addToJoinList, bool addToNameSpace);
  extern RangeTblEntry *addImplicitRTE(ParseState *pstate, char *relname);
  extern void expandRTE(ParseState *pstate, RangeTblEntry *rte,
                        List **colnames, List **colvars);
*** src/tools/backend/index.html.orig    Fri Mar 17 18:17:23 2000
--- src/tools/backend/index.html    Tue Feb 13 16:29:42 2001
***************
*** 67,73 ****
  HREF="../../include/nodes/parsenodes.h"> RangeTableEntry,</A> and they
  are linked together to form the <I>range table</I> of the query, which
  is generated by <A HREF="../../backend/parser/parse_clause.c">
! makeRangeTable().</A>  Query.rtable holds the query's range table.<P>


  Certain queries, like <I>SELECT,</I> return columns of data.  Other
--- 67,73 ----
  HREF="../../include/nodes/parsenodes.h"> RangeTableEntry,</A> and they
  are linked together to form the <I>range table</I> of the query, which
  is generated by <A HREF="../../backend/parser/parse_clause.c">
! transformFromClause().</A>  Query.rtable holds the query's range table.<P>


  Certain queries, like <I>SELECT,</I> return columns of data.  Other

pgsql-patches by date:

Previous
From: Bruce Momjian
Date:
Subject: Re: timestamp fix for AIX and IRIX as discussed
Next
From: Marko Kreen
Date:
Subject: pgcrypto update