diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2012-08-07 19:02:54 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2012-08-07 19:02:54 -0400 |
commit | 5ebaaa49445eb1ba7b299bbea3a477d4e4c0430b (patch) | |
tree | 1a72c939a655e9acbba1c71a1831dd38ee41db95 /src/backend/parser/analyze.c | |
parent | 5078be480412790e4f1b2aeda04f8c65fc7a3b93 (diff) | |
download | postgresql-5ebaaa49445eb1ba7b299bbea3a477d4e4c0430b.tar.gz postgresql-5ebaaa49445eb1ba7b299bbea3a477d4e4c0430b.zip |
Implement SQL-standard LATERAL subqueries.
This patch implements the standard syntax of LATERAL attached to a
sub-SELECT in FROM, and also allows LATERAL attached to a function in FROM,
since set-returning function calls are expected to be one of the principal
use-cases.
The main change here is a rewrite of the mechanism for keeping track of
which relations are visible for column references while the FROM clause is
being scanned. The parser "namespace" lists are no longer lists of bare
RTEs, but are lists of ParseNamespaceItem structs, which carry an RTE
pointer as well as some visibility-controlling flags. Aside from
supporting LATERAL correctly, this lets us get rid of the ancient hacks
that required rechecking subqueries and JOIN/ON and function-in-FROM
expressions for invalid references after they were initially parsed.
Invalid column references are now always correctly detected on sight.
In passing, remove assorted parser error checks that are now dead code by
virtue of our having gotten rid of add_missing_from, as well as some
comments that are obsolete for the same reason. (It was mainly
add_missing_from that caused so much fudging here in the first place.)
The planner support for this feature is very minimal, and will be improved
in future patches. It works well enough for testing purposes, though.
catversion bump forced due to new field in RangeTblEntry.
Diffstat (limited to 'src/backend/parser/analyze.c')
-rw-r--r-- | src/backend/parser/analyze.c | 72 |
1 files changed, 14 insertions, 58 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 263edb5a7a6..1a112cd9a4e 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -533,6 +533,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) rte = addRangeTableEntryForSubquery(pstate, selectQuery, makeAlias("*SELECT*", NIL), + false, false); rtr = makeNode(RangeTblRef); /* assume new rte is at end */ @@ -652,18 +653,6 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) collations = lappend_oid(collations, InvalidOid); /* - * There mustn't have been any table references in the expressions, - * else strange things would happen, like Cartesian products of those - * tables with the VALUES list ... - */ - if (pstate->p_joinlist != NIL) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("VALUES must not contain table references"), - parser_errposition(pstate, - locate_var_of_level((Node *) exprsLists, 0)))); - - /* * Another thing we can't currently support is NEW/OLD references in * rules --- seems we'd need something like SQL99's LATERAL construct * to ensure that the values would be available while evaluating the @@ -1067,7 +1056,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) List **colexprs = NULL; int sublist_length = -1; RangeTblEntry *rte; - RangeTblRef *rtr; + int rtindex; ListCell *lc; ListCell *lc2; int i; @@ -1215,19 +1204,17 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) */ rte = addRangeTableEntryForValues(pstate, exprsLists, collations, NULL, true); - rtr = makeNode(RangeTblRef); + addRTEtoQuery(pstate, rte, true, true, true); + /* assume new rte is at end */ - rtr->rtindex = list_length(pstate->p_rtable); - Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable)); - pstate->p_joinlist = lappend(pstate->p_joinlist, rtr); - pstate->p_relnamespace = lappend(pstate->p_relnamespace, rte); - pstate->p_varnamespace = lappend(pstate->p_varnamespace, rte); + rtindex = list_length(pstate->p_rtable); + Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); /* * Generate a targetlist as though expanding "*" */ Assert(pstate->p_next_resno == 1); - qry->targetList = expandRelAttrs(pstate, rte, rtr->rtindex, 0, -1); + qry->targetList = expandRelAttrs(pstate, rte, rtindex, 0, -1); /* * The grammar allows attaching ORDER BY, LIMIT, and FOR UPDATE to a @@ -1250,19 +1237,6 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) errmsg("SELECT FOR UPDATE/SHARE cannot be applied to VALUES"))); /* - * There mustn't have been any table references in the expressions, else - * strange things would happen, like Cartesian products of those tables - * with the VALUES list. We have to check this after parsing ORDER BY et - * al since those could insert more junk. - */ - if (list_length(pstate->p_joinlist) != 1) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("VALUES must not contain table references"), - parser_errposition(pstate, - locate_var_of_level((Node *) exprsLists, 0)))); - - /* * Another thing we can't currently support is NEW/OLD references in rules * --- seems we'd need something like SQL99's LATERAL construct to ensure * that the values would be available while evaluating the VALUES RTE. @@ -1477,10 +1451,12 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) false); sv_relnamespace = pstate->p_relnamespace; - pstate->p_relnamespace = NIL; /* no qualified names allowed */ - sv_varnamespace = pstate->p_varnamespace; - pstate->p_varnamespace = list_make1(jrte); + pstate->p_relnamespace = NIL; + pstate->p_varnamespace = NIL; + + /* add jrte to varnamespace only */ + addRTEtoQuery(pstate, jrte, false, false, true); /* * For now, we don't support resjunk sort clauses on the output of a @@ -1577,7 +1553,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, /* * If an internal node of a set-op tree has ORDER BY, LIMIT, FOR UPDATE, * or WITH clauses attached, we need to treat it like a leaf node to - * generate an independent sub-Query tree. Otherwise, it can be + * generate an independent sub-Query tree. Otherwise, it can be * represented by a SetOperationStmt node underneath the parent Query. */ if (stmt->op == SETOP_NONE) @@ -1652,6 +1628,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, rte = addRangeTableEntryForSubquery(pstate, selectQuery, makeAlias(selectName, NIL), + false, false); /* @@ -2074,7 +2051,6 @@ transformReturningList(ParseState *pstate, List *returningList) int save_next_resno; bool save_hasAggs; bool save_hasWindowFuncs; - int length_rtable; if (returningList == NIL) return NIL; /* nothing to do */ @@ -2092,7 +2068,6 @@ transformReturningList(ParseState *pstate, List *returningList) pstate->p_hasAggs = false; save_hasWindowFuncs = pstate->p_hasWindowFuncs; pstate->p_hasWindowFuncs = false; - length_rtable = list_length(pstate->p_rtable); /* transform RETURNING identically to a SELECT targetlist */ rlist = transformTargetList(pstate, returningList); @@ -2113,25 +2088,6 @@ transformReturningList(ParseState *pstate, List *returningList) parser_errposition(pstate, locate_windowfunc((Node *) rlist)))); - /* no new relation references please */ - if (list_length(pstate->p_rtable) != length_rtable) - { - int vlocation = -1; - int relid; - - /* try to locate such a reference to point to */ - for (relid = length_rtable + 1; relid <= list_length(pstate->p_rtable); relid++) - { - vlocation = locate_var_of_relation((Node *) rlist, relid, 0); - if (vlocation >= 0) - break; - } - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("RETURNING cannot contain references to other relations"), - parser_errposition(pstate, vlocation))); - } - /* mark column origins */ markTargetListOrigins(pstate, rlist); |