]> granicus.if.org Git - postgresql/commitdiff
Arrange to give error when a SetOp member statement refers to a variable
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 13 Feb 2003 20:45:22 +0000 (20:45 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 13 Feb 2003 20:45:22 +0000 (20:45 +0000)
of the containing query (which really can only happen in a rule context).
Per example from Brandon Craig Rhodes.  Also, make the error message
more specific for the similar case with sub-select in FROM.  The revised
coding should be easier to adapt to SQL99's LATERAL(), when we get around
to supporting that.

src/backend/parser/analyze.c
src/backend/parser/parse_clause.c
src/test/regress/expected/rangefuncs.out

index f198ceb813f54bf6b3df5d4e5debc37583c6a7ca..0736572f0f99a8858eaf14fc1b943bf7d35a91c0 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.262 2003/02/11 04:13:06 tgl Exp $
+ *     $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.263 2003/02/13 20:45:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,6 +23,7 @@
 #include "commands/prepare.h"
 #include "nodes/makefuncs.h"
 #include "optimizer/clauses.h"
+#include "optimizer/var.h"
 #include "parser/analyze.h"
 #include "parser/gramparse.h"
 #include "parser/parsetree.h"
@@ -1982,6 +1983,19 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
 
                Assert(length(selectList) == 1);
                selectQuery = (Query *) lfirst(selectList);
+               Assert(IsA(selectQuery, Query));
+
+               /*
+                * Check for bogus references to Vars on the current query level
+                * (but upper-level references are okay).
+                * Normally this can't happen because the namespace will be empty,
+                * but it could happen if we are inside a rule.
+                */
+               if (pstate->p_namespace)
+               {
+                       if (contain_vars_of_level((Node *) selectQuery, 1))
+                               elog(ERROR, "UNION/INTERSECT/EXCEPT member statement may not refer to other relations of same query level");
+               }
 
                /*
                 * Make the leaf query be a subquery in the top-level rangetable.
index a68442b69a91d74f1387d1e91aae14092bf884b8..d65df553acf2606a8643e4afe46ff14b1eb7d65c 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.108 2003/02/13 05:53:46 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.109 2003/02/13 20:45:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -383,7 +383,6 @@ transformTableEntry(ParseState *pstate, RangeVar *r)
 static RangeTblRef *
 transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
 {
-       List       *save_namespace;
        List       *parsetrees;
        Query      *query;
        RangeTblEntry *rte;
@@ -398,21 +397,10 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
                elog(ERROR, "sub-select in FROM must have an alias");
 
        /*
-        * Analyze and transform the subquery.  This is a bit tricky because
-        * we don't want the subquery to be able to see any FROM items already
-        * created in the current query (per SQL92, the scope of a FROM item
-        * 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.
+        * Analyze and transform the subquery.
         */
-       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
         * are probably impossible given restrictions of the grammar, but
@@ -429,6 +417,25 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
        if (query->resultRelation != 0 || query->into != NULL || query->isPortal)
                elog(ERROR, "Subselect in FROM may not have SELECT INTO");
 
+       /*
+        * The subquery cannot make use of any variables from FROM items created
+        * earlier in the current query.  Per SQL92, the scope of a FROM item
+        * does not include other FROM items.  Formerly we hacked the namespace
+        * so that the other variables weren't even visible, but it seems more
+        * useful to leave them visible and give a specific error message.
+        *
+        * XXX this will need further work to support SQL99's LATERAL() feature,
+        * wherein such references would indeed be legal.
+        *
+        * We can skip groveling through the subquery if there's not anything
+        * visible in the current query.  Also note that outer references are OK.
+        */
+       if (pstate->p_namespace)
+       {
+               if (contain_vars_of_level((Node *) query, 1))
+                       elog(ERROR, "Subselect in FROM may not refer to other relations of same query level");
+       }
+
        /*
         * OK, build an RTE for the subquery.
         */
@@ -455,7 +462,6 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
 {
        Node       *funcexpr;
        char       *funcname;
-       List       *save_namespace;
        RangeTblEntry *rte;
        RangeTblRef *rtr;
 
@@ -464,31 +470,24 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
        funcname = strVal(llast(((FuncCall *) r->funccallnode)->funcname));
 
        /*
-        * Transform the raw FuncCall node.  This is a bit tricky because we
-        * don't want the function expression to be able to see any FROM items
-        * already created in the current query (compare to
-        * transformRangeSubselect). But it does need to be able to see any
-        * further-up parent states. So, temporarily make the current query
-        * level have an empty namespace. NOTE: this code is OK only because
-        * the expression can't legally alter the namespace by causing
-        * implicit relation refs to be added.
+        * Transform the raw FuncCall node.
         */
-       save_namespace = pstate->p_namespace;
-       pstate->p_namespace = NIL;
-
        funcexpr = transformExpr(pstate, r->funccallnode);
 
-       pstate->p_namespace = save_namespace;
-
        /*
-        * We still need to check that the function parameters don't refer to
-        * any other rels.      That could happen despite our hack on the
-        * namespace if fully-qualified names are used.  So, check there are
-        * no local Var references in the transformed expression.  (Outer
-        * references are OK, and are ignored here.)
+        * The function parameters cannot make use of any variables from other
+        * FROM items.  (Compare to transformRangeSubselect(); the coding is
+        * different though because we didn't parse as a sub-select with its own
+        * level of namespace.)
+        *
+        * XXX this will need further work to support SQL99's LATERAL() feature,
+        * wherein such references would indeed be legal.
         */
-       if (!bms_is_empty(pull_varnos(funcexpr)))
-               elog(ERROR, "FROM function expression may not refer to other relations of same query level");
+       if (pstate->p_namespace)
+       {
+               if (contain_vars_of_level(funcexpr, 0))
+                       elog(ERROR, "FROM function expression may not refer to other relations of same query level");
+       }
 
        /*
         * Disallow aggregate functions in the expression.      (No reason to
index e24c7fe05cd70e0bbe95e3fa2761052a2f858ae3..029881ec5a46e87703621c849cb7228cb7b6a3fe 100644 (file)
@@ -18,7 +18,6 @@ INSERT INTO foo2 VALUES(1, 111);
 CREATE FUNCTION foot(int) returns setof foo2 as 'SELECT * FROM foo2 WHERE fooid = $1;' LANGUAGE SQL;
 -- supposed to fail with ERROR
 select * from foo2, foot(foo2.fooid) z where foo2.f2 = z.f2;
-NOTICE:  Adding missing FROM-clause entry for table "foo2"
 ERROR:  FROM function expression may not refer to other relations of same query level
 -- function in subselect
 select * from foo2 where f2 in (select f2 from foot(foo2.fooid) z where z.fooid = foo2.fooid) ORDER BY 1,2;