1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-16 06:01:02 +03:00

Subselects in FROM clause, per ISO syntax: FROM (SELECT ...) [AS] alias.

(Don't forget that an alias is required.)  Views reimplemented as expanding
to subselect-in-FROM.  Grouping, aggregates, DISTINCT in views actually
work now (he says optimistically).  No UNION support in subselects/views
yet, but I have some ideas about that.  Rule-related permissions checking
moved out of rewriter and into executor.
INITDB REQUIRED!
This commit is contained in:
Tom Lane
2000-09-29 18:21:41 +00:00
parent 6f64c2e54a
commit 3a94e789f5
77 changed files with 3176 additions and 2661 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.67 2000/09/17 22:21:27 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.68 2000/09/29 18:21:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -64,7 +64,7 @@ static bool exprIsInSortList(Node *expr, List *sortList, List *targetList);
* 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_jointree lists were
* 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!
*/
@ -75,7 +75,7 @@ makeRangeTable(ParseState *pstate, List *frmList)
/*
* The grammar will have produced a list of RangeVars, RangeSubselects,
* and/or JoinExprs. Transform each one, and then add it to the join tree.
* and/or JoinExprs. Transform each one, and then add it to the joinlist.
*/
foreach(fl, frmList)
{
@ -83,7 +83,7 @@ makeRangeTable(ParseState *pstate, List *frmList)
List *containedRels;
n = transformFromClauseItem(pstate, n, &containedRels);
pstate->p_jointree = lappend(pstate->p_jointree, n);
pstate->p_joinlist = lappend(pstate->p_joinlist, n);
}
}
@ -92,7 +92,7 @@ makeRangeTable(ParseState *pstate, List *frmList)
* 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 tree.
* 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.
@ -106,15 +106,32 @@ setTargetTable(ParseState *pstate, char *relname, bool inh, bool inJoinSet)
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.
*/
/* XXX what if pre-existing entry has wrong inh setting? */
}
/* Mark target table as requiring write access. */
rte->checkForWrite = true;
if (inJoinSet)
addRTEtoJoinTree(pstate, rte);
addRTEtoJoinList(pstate, rte);
/* This could only happen for multi-action rules */
if (pstate->p_target_relation != NULL)
@ -242,22 +259,22 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j,
List *containedRels)
{
Node *result;
List *sv_jointree;
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 jointree, which means that transformExpr() won't resolve
* 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 jointree consist of
* 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 jointree by causing relation refs to be added.
* legally alter the joinlist by causing relation refs to be added.
*/
sv_jointree = pstate->p_jointree;
pstate->p_jointree = lcons(j->larg, lcons(j->rarg, NIL));
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);
@ -267,12 +284,12 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j,
typeidTypeName(exprType(result)));
}
pstate->p_jointree = sv_jointree;
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 jointree if it uses fully-qualified names. So, grovel
* 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.
*/
@ -312,7 +329,7 @@ transformTableEntry(ParseState *pstate, RangeVar *r)
rte = addRangeTableEntry(pstate, relname, r->name, r->inh, true);
/*
* We create a RangeTblRef, but we do not add it to the jointree here.
* 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);
@ -333,6 +350,16 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
SelectStmt *subquery = (SelectStmt *) r->subquery;
List *parsetrees;
Query *query;
RangeTblEntry *rte;
RangeTblRef *rtr;
/*
* We require user to supply an alias for a subselect, per SQL92.
* To relax this, we'd have to be prepared to gin up a unique alias
* for an unlabeled subselect.
*/
if (r->name == NULL)
elog(ERROR, "sub-select in FROM must have an alias");
/*
* subquery node might not be SelectStmt if user wrote something like
@ -347,7 +374,7 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
* Analyze and transform the subquery as if it were an independent
* statement (we do NOT want it to see the outer query as a parent).
*/
parsetrees = parse_analyze(lcons(subquery, NIL), NULL);
parsetrees = parse_analyze(makeList1(subquery), NULL);
/*
* Check that we got something reasonable. Some of these conditions
@ -362,13 +389,24 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
if (query->commandType != CMD_SELECT)
elog(ERROR, "Expected SELECT query from subselect in FROM");
if (query->resultRelation != 0 || query->into != NULL)
if (query->resultRelation != 0 || query->into != NULL || query->isPortal)
elog(ERROR, "Subselect in FROM may not have SELECT INTO");
/*
* OK, build an RTE for the subquery.
*/
rte = addRangeTableEntryForSubquery(pstate, query, r->name, true);
elog(ERROR, "Subselect in FROM not done yet");
/*
* 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 */
rtr->rtindex = length(pstate->p_rtable);
Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));
return NULL;
return rtr;
}
@ -376,12 +414,12 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
* 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 jointree list.
* 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 jointree item)
* Aside from the primary return value (the transformed joinlist item)
* this routine also returns an integer list of the rangetable indexes
* of all the base relations represented in the jointree item. This
* of all the base relations represented in the joinlist item. This
* list is needed for checking JOIN/ON conditions in higher levels.
*/
static Node *
@ -393,7 +431,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
RangeTblRef *rtr;
rtr = transformTableEntry(pstate, (RangeVar *) n);
*containedRels = lconsi(rtr->rtindex, NIL);
*containedRels = makeListi1(rtr->rtindex);
return (Node *) rtr;
}
else if (IsA(n, RangeSubselect))
@ -402,7 +440,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
RangeTblRef *rtr;
rtr = transformRangeSubselect(pstate, (RangeSubselect *) n);
*containedRels = lconsi(rtr->rtindex, NIL);
*containedRels = makeListi1(rtr->rtindex);
return (Node *) rtr;
}
else if (IsA(n, JoinExpr))
@ -599,7 +637,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
a->lexpr = l_colvar;
w->expr = (Node *) a;
w->result = l_colvar;
c->args = lcons(w, NIL);
c->args = makeList1(w);
c->defresult = r_colvar;
colvar = transformExpr(pstate, (Node *) c,
EXPR_COLUMN_FIRST);
@ -641,17 +679,17 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
* 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 jointree, so they have to be scanned separately.
* the pstate's joinlist, so they have to be scanned separately.
*/
if (j->alias)
{
/* Check against previously created RTEs and jointree entries */
/* 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 (scanJoinTreeForRefname(j->larg, j->alias->relname) ||
scanJoinTreeForRefname(j->rarg, j->alias->relname))
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);
/*