mirror of
https://github.com/postgres/postgres.git
synced 2025-08-05 07:41:25 +03:00
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.
This commit is contained in:
@@ -6,7 +6,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 "commands/prepare.h"
|
||||||
#include "nodes/makefuncs.h"
|
#include "nodes/makefuncs.h"
|
||||||
#include "optimizer/clauses.h"
|
#include "optimizer/clauses.h"
|
||||||
|
#include "optimizer/var.h"
|
||||||
#include "parser/analyze.h"
|
#include "parser/analyze.h"
|
||||||
#include "parser/gramparse.h"
|
#include "parser/gramparse.h"
|
||||||
#include "parser/parsetree.h"
|
#include "parser/parsetree.h"
|
||||||
@@ -1982,6 +1983,19 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
|
|||||||
|
|
||||||
Assert(length(selectList) == 1);
|
Assert(length(selectList) == 1);
|
||||||
selectQuery = (Query *) lfirst(selectList);
|
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.
|
* Make the leaf query be a subquery in the top-level rangetable.
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 *
|
static RangeTblRef *
|
||||||
transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
|
transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
|
||||||
{
|
{
|
||||||
List *save_namespace;
|
|
||||||
List *parsetrees;
|
List *parsetrees;
|
||||||
Query *query;
|
Query *query;
|
||||||
RangeTblEntry *rte;
|
RangeTblEntry *rte;
|
||||||
@@ -398,21 +397,10 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
|
|||||||
elog(ERROR, "sub-select in FROM must have an alias");
|
elog(ERROR, "sub-select in FROM must have an alias");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Analyze and transform the subquery. This is a bit tricky because
|
* Analyze and transform the subquery.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
save_namespace = pstate->p_namespace;
|
|
||||||
pstate->p_namespace = NIL;
|
|
||||||
|
|
||||||
parsetrees = parse_analyze(r->subquery, pstate);
|
parsetrees = parse_analyze(r->subquery, pstate);
|
||||||
|
|
||||||
pstate->p_namespace = save_namespace;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check that we got something reasonable. Some of these conditions
|
* Check that we got something reasonable. Some of these conditions
|
||||||
* are probably impossible given restrictions of the grammar, but
|
* 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)
|
if (query->resultRelation != 0 || query->into != NULL || query->isPortal)
|
||||||
elog(ERROR, "Subselect in FROM may not have SELECT INTO");
|
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.
|
* OK, build an RTE for the subquery.
|
||||||
*/
|
*/
|
||||||
@@ -455,7 +462,6 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
|
|||||||
{
|
{
|
||||||
Node *funcexpr;
|
Node *funcexpr;
|
||||||
char *funcname;
|
char *funcname;
|
||||||
List *save_namespace;
|
|
||||||
RangeTblEntry *rte;
|
RangeTblEntry *rte;
|
||||||
RangeTblRef *rtr;
|
RangeTblRef *rtr;
|
||||||
|
|
||||||
@@ -464,31 +470,24 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
|
|||||||
funcname = strVal(llast(((FuncCall *) r->funccallnode)->funcname));
|
funcname = strVal(llast(((FuncCall *) r->funccallnode)->funcname));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Transform the raw FuncCall node. This is a bit tricky because we
|
* Transform the raw FuncCall node.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
save_namespace = pstate->p_namespace;
|
|
||||||
pstate->p_namespace = NIL;
|
|
||||||
|
|
||||||
funcexpr = transformExpr(pstate, r->funccallnode);
|
funcexpr = transformExpr(pstate, r->funccallnode);
|
||||||
|
|
||||||
pstate->p_namespace = save_namespace;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We still need to check that the function parameters don't refer to
|
* The function parameters cannot make use of any variables from other
|
||||||
* any other rels. That could happen despite our hack on the
|
* FROM items. (Compare to transformRangeSubselect(); the coding is
|
||||||
* namespace if fully-qualified names are used. So, check there are
|
* different though because we didn't parse as a sub-select with its own
|
||||||
* no local Var references in the transformed expression. (Outer
|
* level of namespace.)
|
||||||
* references are OK, and are ignored here.)
|
*
|
||||||
|
* 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)))
|
if (pstate->p_namespace)
|
||||||
elog(ERROR, "FROM function expression may not refer to other relations of same query level");
|
{
|
||||||
|
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
|
* Disallow aggregate functions in the expression. (No reason to
|
||||||
|
@@ -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;
|
CREATE FUNCTION foot(int) returns setof foo2 as 'SELECT * FROM foo2 WHERE fooid = $1;' LANGUAGE SQL;
|
||||||
-- supposed to fail with ERROR
|
-- supposed to fail with ERROR
|
||||||
select * from foo2, foot(foo2.fooid) z where foo2.f2 = z.f2;
|
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
|
ERROR: FROM function expression may not refer to other relations of same query level
|
||||||
-- function in subselect
|
-- 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;
|
select * from foo2 where f2 in (select f2 from foot(foo2.fooid) z where z.fooid = foo2.fooid) ORDER BY 1,2;
|
||||||
|
Reference in New Issue
Block a user