From 18e8f06c9dc0e87a74681a28b023dd8a8b9c5a6b Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 13 Feb 2003 20:45:22 +0000 Subject: [PATCH] Arrange to give error when a SetOp member statement refers to a variable 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 | 16 +++++++- src/backend/parser/parse_clause.c | 69 ++++++++++++++++---------------- src/test/regress/expected/rangefuncs.out | 1 - 3 files changed, 49 insertions(+), 37 deletions(-) diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index f198ceb813..0736572f0f 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -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. diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index a68442b69a..d65df553ac 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -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 @@ -430,6 +418,25 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r) 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. */ rte = addRangeTableEntryForSubquery(pstate, query, r->alias, true); @@ -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 diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out index e24c7fe05c..029881ec5a 100644 --- a/src/test/regress/expected/rangefuncs.out +++ b/src/test/regress/expected/rangefuncs.out @@ -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; -- 2.11.0