1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-14 08:21:07 +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

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: analyze.c,v 1.158 2000/09/25 12:58:46 momjian Exp $
* $Id: analyze.c,v 1.159 2000/09/29 18:21:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -56,6 +56,7 @@ static void transformColumnType(ParseState *pstate, ColumnDef *column);
static void transformFkeyCheckAttrs(FkConstraint *fkconstraint);
static void release_pstate_resources(ParseState *pstate);
static FromExpr *makeFromExpr(List *fromlist, Node *quals);
/* kluge to return extra info from transformCreateStmt() */
static List *extras_before;
@ -289,6 +290,7 @@ static Query *
transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
{
Query *qry = makeNode(Query);
Node *qual;
qry->commandType = CMD_DELETE;
@ -299,17 +301,17 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
qry->distinctClause = NIL;
/* fix where clause */
qry->qual = transformWhereClause(pstate, stmt->whereClause);
qual = transformWhereClause(pstate, stmt->whereClause);
/* done building the rtable */
/* done building the range table and jointree */
qry->rtable = pstate->p_rtable;
qry->jointree = pstate->p_jointree;
qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
qry->hasSubLinks = pstate->p_hasSubLinks;
qry->hasAggs = pstate->p_hasAggs;
if (pstate->p_hasAggs)
parseCheckAggregates(pstate, qry);
parseCheckAggregates(pstate, qry, qual);
return (Query *) qry;
}
@ -322,6 +324,7 @@ static Query *
transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
{
Query *qry = makeNode(Query);
Node *qual;
List *icolumns;
List *attrnos;
List *attnos;
@ -348,7 +351,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
qry->targetList = transformTargetList(pstate, stmt->targetList);
qry->qual = transformWhereClause(pstate, stmt->whereClause);
qual = transformWhereClause(pstate, stmt->whereClause);
/*
* Initial processing of HAVING clause is just like WHERE clause.
@ -371,7 +374,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
qry->hasSubLinks = pstate->p_hasSubLinks;
qry->hasAggs = pstate->p_hasAggs;
if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
parseCheckAggregates(pstate, qry);
parseCheckAggregates(pstate, qry, qual);
/*
* The INSERT INTO ... SELECT ... could have a UNION in child, so
@ -393,13 +396,13 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
* In particular, it's time to add the INSERT target to the rangetable.
* (We didn't want it there until now since it shouldn't be visible in
* the SELECT part.) Note that the INSERT target is NOT added to the
* join tree, since we don't want to join over it.
* joinlist, since we don't want to join over it.
*/
setTargetTable(pstate, stmt->relname, false, false);
/* now the range table will not change */
/* now the range table and jointree will not change */
qry->rtable = pstate->p_rtable;
qry->jointree = pstate->p_jointree;
qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
/* Prepare to assign non-conflicting resnos to resjunk attributes */
@ -715,7 +718,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
snamenode->val.val.str = qstring;
funccallnode = makeNode(FuncCall);
funccallnode->funcname = "nextval";
funccallnode->args = lcons(snamenode, NIL);
funccallnode->args = makeList1(snamenode);
funccallnode->agg_star = false;
funccallnode->agg_distinct = false;
@ -748,7 +751,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
elog(NOTICE, "CREATE TABLE will create implicit sequence '%s' for SERIAL column '%s.%s'",
sequence->seqname, stmt->relname, column->colname);
blist = lcons(sequence, NIL);
blist = makeList1(sequence);
}
/* Process column constraints, if any... */
@ -776,7 +779,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
id->isRel = false;
fkconstraint = (FkConstraint *) constraint;
fkconstraint->fk_attrs = lappend(NIL, id);
fkconstraint->fk_attrs = makeList1(id);
fkconstraints = lappend(fkconstraints, constraint);
continue;
@ -815,7 +818,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
{
key = makeNode(Ident);
key->name = pstrdup(column->colname);
constraint->keys = lcons(key, NIL);
constraint->keys = makeList1(key);
}
dlist = lappend(dlist, constraint);
break;
@ -827,7 +830,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
{
key = makeNode(Ident);
key->name = pstrdup(column->colname);
constraint->keys = lcons(key, NIL);
constraint->keys = makeList1(key);
}
dlist = lappend(dlist, constraint);
break;
@ -1453,8 +1456,11 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
newrte = addRangeTableEntry(pstate, stmt->object->relname,
makeAttr("*NEW*", NULL),
false, true);
/* Must override addRangeTableEntry's default access-check flags */
oldrte->checkForRead = false;
newrte->checkForRead = false;
/*
* They must be in the jointree too for lookup purposes, but only add
* They must be in the joinlist too for lookup purposes, but only add
* the one(s) that are relevant for the current kind of rule. In an
* UPDATE rule, quals must refer to OLD.field or NEW.field to be
* unambiguous, but there's no need to be so picky for INSERT & DELETE.
@ -1464,17 +1470,17 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
switch (stmt->event)
{
case CMD_SELECT:
addRTEtoJoinTree(pstate, oldrte);
addRTEtoJoinList(pstate, oldrte);
break;
case CMD_UPDATE:
addRTEtoJoinTree(pstate, oldrte);
addRTEtoJoinTree(pstate, newrte);
addRTEtoJoinList(pstate, oldrte);
addRTEtoJoinList(pstate, newrte);
break;
case CMD_INSERT:
addRTEtoJoinTree(pstate, newrte);
addRTEtoJoinList(pstate, newrte);
break;
case CMD_DELETE:
addRTEtoJoinTree(pstate, oldrte);
addRTEtoJoinList(pstate, oldrte);
break;
default:
elog(ERROR, "transformRuleStmt: unexpected event type %d",
@ -1504,9 +1510,9 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
nothing_qry->commandType = CMD_NOTHING;
nothing_qry->rtable = pstate->p_rtable;
nothing_qry->jointree = NIL; /* no join actually wanted */
nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */
stmt->actions = lappend(NIL, nothing_qry);
stmt->actions = makeList1(nothing_qry);
}
else
{
@ -1526,7 +1532,7 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
* Set up OLD/NEW in the rtable for this statement. The entries
* are marked not inFromCl because we don't want them to be
* referred to by unqualified field names nor "*" in the rule
* actions. We don't need to add them to the jointree for
* actions. We don't need to add them to the joinlist for
* qualified-name lookup, either (see qualifiedNameToVar()).
*/
oldrte = addRangeTableEntry(sub_pstate, stmt->object->relname,
@ -1535,6 +1541,8 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
newrte = addRangeTableEntry(sub_pstate, stmt->object->relname,
makeAttr("*NEW*", NULL),
false, false);
oldrte->checkForRead = false;
newrte->checkForRead = false;
/* Transform the rule action statement */
sub_qry = transformStmt(sub_pstate, lfirst(actions));
@ -1581,8 +1589,8 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
*/
if (has_old)
{
addRTEtoJoinTree(sub_pstate, oldrte);
sub_qry->jointree = sub_pstate->p_jointree;
addRTEtoJoinList(sub_pstate, oldrte);
sub_qry->jointree->fromlist = sub_pstate->p_joinlist;
}
lfirst(actions) = sub_qry;
@ -1605,6 +1613,7 @@ static Query *
transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
{
Query *qry = makeNode(Query);
Node *qual;
qry->commandType = CMD_SELECT;
@ -1617,7 +1626,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
qry->targetList = transformTargetList(pstate, stmt->targetList);
qry->qual = transformWhereClause(pstate, stmt->whereClause);
qual = transformWhereClause(pstate, stmt->whereClause);
/*
* Initial processing of HAVING clause is just like WHERE clause.
@ -1641,7 +1650,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
qry->hasSubLinks = pstate->p_hasSubLinks;
qry->hasAggs = pstate->p_hasAggs;
if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
parseCheckAggregates(pstate, qry);
parseCheckAggregates(pstate, qry, qual);
/*
* The INSERT INTO ... SELECT ... could have a UNION in child, so
@ -1657,7 +1666,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
qry->intersectClause = stmt->intersectClause;
qry->rtable = pstate->p_rtable;
qry->jointree = pstate->p_jointree;
qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
if (stmt->forUpdate != NULL)
transformForUpdate(qry, stmt->forUpdate);
@ -1674,6 +1683,7 @@ static Query *
transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
{
Query *qry = makeNode(Query);
Node *qual;
List *origTargetList;
List *tl;
@ -1683,22 +1693,27 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
/*
* the FROM clause is non-standard SQL syntax. We used to be able to
* do this with REPLACE in POSTQUEL so we keep the feature.
*
* Note: it's critical here that we process FROM before adding the
* target table to the rtable --- otherwise, if the target is also
* used in FROM, we'd fail to notice that it should be marked
* checkForRead as well as checkForWrite. See setTargetTable().
*/
makeRangeTable(pstate, stmt->fromClause);
setTargetTable(pstate, stmt->relname, stmt->inh, true);
qry->targetList = transformTargetList(pstate, stmt->targetList);
qry->qual = transformWhereClause(pstate, stmt->whereClause);
qual = transformWhereClause(pstate, stmt->whereClause);
qry->rtable = pstate->p_rtable;
qry->jointree = pstate->p_jointree;
qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
qry->hasSubLinks = pstate->p_hasSubLinks;
qry->hasAggs = pstate->p_hasAggs;
if (pstate->p_hasAggs)
parseCheckAggregates(pstate, qry);
parseCheckAggregates(pstate, qry, qual);
/*
* Now we are done with SELECT-like processing, and can get on with
@ -2083,7 +2098,7 @@ A_Expr_to_Expr(Node *ptr, bool *intersect_present)
expr->typeOid = BOOLOID;
expr->opType = OR_EXPR;
expr->args = makeList(lexpr, rexpr, -1);
expr->args = makeList2(lexpr, rexpr);
result = (Node *) expr;
break;
}
@ -2095,7 +2110,7 @@ A_Expr_to_Expr(Node *ptr, bool *intersect_present)
expr->typeOid = BOOLOID;
expr->opType = AND_EXPR;
expr->args = makeList(lexpr, rexpr, -1);
expr->args = makeList2(lexpr, rexpr);
result = (Node *) expr;
break;
}
@ -2106,7 +2121,7 @@ A_Expr_to_Expr(Node *ptr, bool *intersect_present)
expr->typeOid = BOOLOID;
expr->opType = NOT_EXPR;
expr->args = makeList(rexpr, -1);
expr->args = makeList1(rexpr);
result = (Node *) expr;
break;
}
@ -2122,7 +2137,7 @@ A_Expr_to_Expr(Node *ptr, bool *intersect_present)
void
CheckSelectForUpdate(Query *qry)
{
if (qry->unionClause != NULL)
if (qry->unionClause || qry->intersectClause)
elog(ERROR, "SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT clause");
if (qry->distinctClause != NIL)
elog(ERROR, "SELECT FOR UPDATE is not allowed with DISTINCT clause");
@ -2135,61 +2150,70 @@ CheckSelectForUpdate(Query *qry)
static void
transformForUpdate(Query *qry, List *forUpdate)
{
List *rowMark = NULL;
RowMark *newrm;
List *rowMarks = NIL;
List *l;
List *rt;
Index i;
CheckSelectForUpdate(qry);
if (lfirst(forUpdate) == NULL) /* all tables */
if (lfirst(forUpdate) == NULL)
{
i = 1;
foreach(l, qry->rtable)
/* all tables used in query */
i = 0;
foreach(rt, qry->rtable)
{
newrm = makeNode(RowMark);
newrm->rti = i++;
newrm->info = ROW_MARK_FOR_UPDATE | ROW_ACL_FOR_UPDATE;
rowMark = lappend(rowMark, newrm);
}
qry->rowMark = nconc(qry->rowMark, rowMark);
return;
}
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
foreach(l, forUpdate)
{
char *relname = lfirst(l);
List *l2;
i = 1;
foreach(l2, qry->rtable)
{
if (strcmp(((RangeTblEntry *) lfirst(l2))->eref->relname, relname) == 0)
++i;
if (rte->subquery)
{
List *l3;
foreach(l3, rowMark)
{
if (((RowMark *) lfirst(l3))->rti == i) /* duplicate */
break;
}
if (l3 == NULL)
{
newrm = makeNode(RowMark);
newrm->rti = i;
newrm->info = ROW_MARK_FOR_UPDATE | ROW_ACL_FOR_UPDATE;
rowMark = lappend(rowMark, newrm);
}
break;
/* FOR UPDATE of subquery is propagated to subquery's rels */
transformForUpdate(rte->subquery, makeList1(NULL));
}
else
{
rowMarks = lappendi(rowMarks, i);
rte->checkForWrite = true;
}
i++;
}
if (l2 == NULL)
elog(ERROR, "FOR UPDATE: relation \"%s\" not found in FROM clause",
relname);
}
else
{
/* just the named tables */
foreach(l, forUpdate)
{
char *relname = lfirst(l);
i = 0;
foreach(rt, qry->rtable)
{
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
++i;
if (strcmp(rte->eref->relname, relname) == 0)
{
if (rte->subquery)
{
/* propagate to subquery */
transformForUpdate(rte->subquery, makeList1(NULL));
}
else
{
if (!intMember(i, rowMarks)) /* avoid duplicates */
rowMarks = lappendi(rowMarks, i);
rte->checkForWrite = true;
}
break;
}
}
if (rt == NIL)
elog(ERROR, "FOR UPDATE: relation \"%s\" not found in FROM clause",
relname);
}
}
qry->rowMark = rowMark;
qry->rowMarks = rowMarks;
}
@ -2452,6 +2476,17 @@ transformConstraintAttrs(List *constraintList)
}
}
/* Build a FromExpr node */
static FromExpr *
makeFromExpr(List *fromlist, Node *quals)
{
FromExpr *f = makeNode(FromExpr);
f->fromlist = fromlist;
f->quals = quals;
return f;
}
/*
* Special handling of type definition for a column
*/

View File

@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.192 2000/09/25 18:38:39 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.193 2000/09/29 18:21:36 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -403,7 +403,7 @@ stmtmulti: stmtmulti ';' stmt
}
| stmt
{ if ($1 != (Node *)NULL)
$$ = lcons($1,NIL);
$$ = makeList1($1);
else
$$ = NIL;
}
@ -575,11 +575,11 @@ user_createuser_clause: CREATEUSER { $$ = +1; }
user_list: user_list ',' UserId
{
$$ = lcons((void*)makeString($3), $1);
$$ = lappend($1, makeString($3));
}
| UserId
{
$$ = lcons((void*)makeString($1), NIL);
$$ = makeList1(makeString($1));
}
;
@ -721,7 +721,7 @@ SessionList: SessionList ',' SessionClause
}
| SessionClause
{
$$ = lcons($1, NIL);
$$ = makeList1($1);
}
;
@ -937,7 +937,7 @@ constraints_set_list: ALL
constraints_set_namelist: IDENT
{
$$ = lappend(NIL, $1);
$$ = makeList1($1);
}
| constraints_set_namelist ',' IDENT
{
@ -1193,11 +1193,11 @@ OptTableElementList: OptTableElementList ',' OptTableElement
| OptTableElement
{
if ($1 != NULL)
$$ = lcons($1, NIL);
$$ = makeList1($1);
else
$$ = NULL;
$$ = NIL;
}
| /*EMPTY*/ { $$ = NULL; }
| /*EMPTY*/ { $$ = NIL; }
;
OptTableElement: columnDef { $$ = $1; }
@ -1551,7 +1551,7 @@ OptCreateAs: '(' CreateAsList ')' { $$ = $2; }
;
CreateAsList: CreateAsList ',' CreateAsElement { $$ = lappend($1, $3); }
| CreateAsElement { $$ = lcons($1, NIL); }
| CreateAsElement { $$ = makeList1($1); }
;
CreateAsElement: ColId
@ -1783,7 +1783,7 @@ TriggerForType: ROW { $$ = TRUE; }
;
TriggerFuncArgs: TriggerFuncArg
{ $$ = lcons($1, NIL); }
{ $$ = makeList1($1); }
| TriggerFuncArgs ',' TriggerFuncArg
{ $$ = lappend($1, $3); }
| /*EMPTY*/
@ -1899,7 +1899,7 @@ def_name: PROCEDURE { $$ = "procedure"; }
definition: '(' def_list ')' { $$ = $2; }
;
def_list: def_elem { $$ = lcons($1, NIL); }
def_list: def_elem { $$ = makeList1($1); }
| def_list ',' def_elem { $$ = lappend($1, $3); }
;
@ -2361,11 +2361,11 @@ access_method_clause: USING access_method { $$ = $2; }
;
index_params: index_list { $$ = $1; }
| func_index { $$ = lcons($1,NIL); }
| func_index { $$ = makeList1($1); }
;
index_list: index_list ',' index_elem { $$ = lappend($1, $3); }
| index_elem { $$ = lcons($1, NIL); }
| index_elem { $$ = makeList1($1); }
;
func_index: func_name '(' name_list ')' opt_class
@ -2486,9 +2486,9 @@ func_args: '(' func_args_list ')' { $$ = $2; }
;
func_args_list: func_arg
{ $$ = lcons(makeString($1->name),NIL); }
{ $$ = makeList1(makeString($1->name)); }
| func_args_list ',' func_arg
{ $$ = lappend($1,makeString($3->name)); }
{ $$ = lappend($1, makeString($3->name)); }
;
/* Would be nice to use the full Typename production for these fields,
@ -2539,9 +2539,9 @@ opt_arg: IN
;
func_as: Sconst
{ $$ = lcons(makeString($1),NIL); }
{ $$ = makeList1(makeString($1)); }
| Sconst ',' Sconst
{ $$ = lappend(lcons(makeString($1),NIL), makeString($3)); }
{ $$ = makeList2(makeString($1), makeString($3)); }
;
func_return: SimpleTypename
@ -2631,11 +2631,11 @@ oper_argtypes: name
elog(ERROR,"parser: argument type missing (use NONE for unary operators)");
}
| name ',' name
{ $$ = makeList(makeString($1), makeString($3), -1); }
{ $$ = makeList2(makeString($1), makeString($3)); }
| NONE ',' name /* left unary */
{ $$ = makeList(NULL, makeString($3), -1); }
{ $$ = makeList2(NULL, makeString($3)); }
| name ',' NONE /* right unary */
{ $$ = makeList(makeString($1), NULL, -1); }
{ $$ = makeList2(makeString($1), NULL); }
;
@ -2724,22 +2724,22 @@ RuleStmt: CREATE RULE name AS
;
RuleActionList: NOTHING { $$ = NIL; }
| SelectStmt { $$ = lcons($1, NIL); }
| RuleActionStmt { $$ = lcons($1, NIL); }
| SelectStmt { $$ = makeList1($1); }
| RuleActionStmt { $$ = makeList1($1); }
| '[' RuleActionMulti ']' { $$ = $2; }
| '(' RuleActionMulti ')' { $$ = $2; }
;
/* the thrashing around here is to discard "empty" statements... */
RuleActionMulti: RuleActionMulti ';' RuleActionStmtOrEmpty
{ if ($3 != (Node *)NULL)
{ if ($3 != (Node *) NULL)
$$ = lappend($1, $3);
else
$$ = $1;
}
| RuleActionStmtOrEmpty
{ if ($1 != (Node *)NULL)
$$ = lcons($1,NIL);
{ if ($1 != (Node *) NULL)
$$ = makeList1($1);
else
$$ = NIL;
}
@ -2761,7 +2761,7 @@ event_object: relation_name '.' attr_name
$$ = makeNode(Attr);
$$->relname = $1;
$$->paramNo = NULL;
$$->attrs = lcons(makeString($3), NIL);
$$->attrs = makeList1(makeString($3));
$$->indirection = NIL;
}
| relation_name
@ -2910,10 +2910,8 @@ ViewStmt: CREATE VIEW name opt_column_list AS SelectStmt
n->viewname = $3;
n->aliases = $4;
n->query = (Query *)$6;
if (((SelectStmt *)n->query)->sortClause != NULL)
elog(ERROR,"ORDER BY and DISTINCT on views are not implemented");
if (((SelectStmt *)n->query)->unionClause != NULL)
elog(ERROR,"UNION on views is not implemented");
elog(ERROR,"UNION in views is not implemented");
if (((SelectStmt *)n->query)->forUpdate != NULL)
elog(ERROR, "SELECT FOR UPDATE is not allowed in CREATE VIEW");
$$ = (Node *)n;
@ -3092,9 +3090,9 @@ opt_va_list: '(' va_list ')' { $$ = $2; }
;
va_list: name
{ $$=lcons($1,NIL); }
{ $$ = makeList1($1); }
| va_list ',' name
{ $$=lappend($1,$3); }
{ $$ = lappend($1, $3); }
;
@ -3240,7 +3238,7 @@ opt_column_list: '(' columnList ')' { $$ = $2; }
columnList: columnList ',' columnElem
{ $$ = lappend($1, $3); }
| columnElem
{ $$ = lcons($1, NIL); }
{ $$ = makeList1($1); }
;
columnElem: ColId opt_indirection
@ -3364,7 +3362,7 @@ opt_cursor: BINARY { $$ = TRUE; }
*****************************************************************************/
/* A complete SELECT statement looks like this. Note sort, for_update,
* and limit clauses can only appear once, not in each subselect.
* and limit clauses can only appear once, not in each set operation.
*
* The rule returns a SelectStmt Node having the set operations attached to
* unionClause and intersectClause (NIL if no set operations were present)
@ -3584,7 +3582,7 @@ opt_all: ALL { $$ = TRUE; }
/* We use (NIL) as a placeholder to indicate that all target expressions
* should be placed in the DISTINCT list during parsetree analysis.
*/
opt_distinct: DISTINCT { $$ = lcons(NIL,NIL); }
opt_distinct: DISTINCT { $$ = makeList1(NIL); }
| DISTINCT ON '(' expr_list ')' { $$ = $4; }
| ALL { $$ = NIL; }
| /*EMPTY*/ { $$ = NIL; }
@ -3594,7 +3592,7 @@ sort_clause: ORDER BY sortby_list { $$ = $3; }
| /*EMPTY*/ { $$ = NIL; }
;
sortby_list: sortby { $$ = lcons($1, NIL); }
sortby_list: sortby { $$ = makeList1($1); }
| sortby_list ',' sortby { $$ = lappend($1, $3); }
;
@ -3614,17 +3612,17 @@ OptUseOp: USING all_Op { $$ = $2; }
opt_select_limit: LIMIT select_limit_value ',' select_offset_value
{ $$ = lappend(lappend(NIL, $4), $2); }
{ $$ = makeList2($4, $2); }
| LIMIT select_limit_value OFFSET select_offset_value
{ $$ = lappend(lappend(NIL, $4), $2); }
{ $$ = makeList2($4, $2); }
| LIMIT select_limit_value
{ $$ = lappend(lappend(NIL, NULL), $2); }
{ $$ = makeList2(NULL, $2); }
| OFFSET select_offset_value LIMIT select_limit_value
{ $$ = lappend(lappend(NIL, $2), $4); }
{ $$ = makeList2($2, $4); }
| OFFSET select_offset_value
{ $$ = lappend(lappend(NIL, $2), NULL); }
{ $$ = makeList2($2, NULL); }
| /* EMPTY */
{ $$ = lappend(lappend(NIL, NULL), NULL); }
{ $$ = makeList2(NULL, NULL); }
;
select_limit_value: Iconst
@ -3704,9 +3702,9 @@ opt_inh_star: '*' { $$ = TRUE; }
relation_name_list: name_list;
name_list: name
{ $$ = lcons(makeString($1),NIL); }
{ $$ = makeList1(makeString($1)); }
| name_list ',' name
{ $$ = lappend($1,makeString($3)); }
{ $$ = lappend($1, makeString($3)); }
;
group_clause: GROUP BY expr_list { $$ = $3; }
@ -3726,7 +3724,7 @@ for_update_clause: FOR UPDATE update_list { $$ = $3; }
;
update_list: OF va_list { $$ = $2; }
| /* EMPTY */ { $$ = lcons(NULL, NULL); }
| /* EMPTY */ { $$ = makeList1(NULL); }
;
/*****************************************************************************
@ -3742,7 +3740,7 @@ from_clause: FROM from_list { $$ = $2; }
;
from_list: from_list ',' table_ref { $$ = lappend($1, $3); }
| table_ref { $$ = lcons($1, NIL); }
| table_ref { $$ = makeList1($1); }
;
/*
@ -4001,10 +3999,10 @@ Typename: SimpleTypename opt_array_bounds
}
;
opt_array_bounds: '[' ']' opt_array_bounds
{ $$ = lcons(makeInteger(-1), $3); }
| '[' Iconst ']' opt_array_bounds
{ $$ = lcons(makeInteger($2), $4); }
opt_array_bounds: opt_array_bounds '[' ']'
{ $$ = lappend($1, makeInteger(-1)); }
| opt_array_bounds '[' Iconst ']'
{ $$ = lappend($1, makeInteger($3)); }
| /*EMPTY*/
{ $$ = NIL; }
;
@ -4296,7 +4294,7 @@ opt_timezone: WITH TIME ZONE { $$ = TRUE; }
| /*EMPTY*/ { $$ = FALSE; }
;
opt_interval: datetime { $$ = lcons($1, NIL); }
opt_interval: datetime { $$ = makeList1($1); }
| YEAR_P TO MONTH_P { $$ = NIL; }
| DAY_P TO HOUR_P { $$ = NIL; }
| DAY_P TO MINUTE_P { $$ = NIL; }
@ -4403,7 +4401,7 @@ row_list: row_list ',' a_expr
}
| a_expr
{
$$ = lcons($1, NIL);
$$ = makeList1($1);
}
;
@ -4524,7 +4522,7 @@ a_expr: c_expr
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "like_escape";
n->args = makeList($3, $5, -1);
n->args = makeList2($3, $5);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
$$ = makeA_Expr(OP, "~~", $1, (Node *) n);
@ -4535,7 +4533,7 @@ a_expr: c_expr
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "like_escape";
n->args = makeList($4, $6, -1);
n->args = makeList2($4, $6);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
$$ = makeA_Expr(OP, "!~~", $1, (Node *) n);
@ -4546,7 +4544,7 @@ a_expr: c_expr
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "like_escape";
n->args = makeList($3, $5, -1);
n->args = makeList2($3, $5);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
$$ = makeA_Expr(OP, "~~*", $1, (Node *) n);
@ -4557,7 +4555,7 @@ a_expr: c_expr
{
FuncCall *n = makeNode(FuncCall);
n->funcname = "like_escape";
n->args = makeList($4, $6, -1);
n->args = makeList2($4, $6);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
$$ = makeA_Expr(OP, "!~~*", $1, (Node *) n);
@ -4634,7 +4632,7 @@ a_expr: c_expr
if (IsA($4, SubLink))
{
SubLink *n = (SubLink *)$4;
n->lefthand = lcons($1, NIL);
n->lefthand = makeList1($1);
n->oper = (List *) makeA_Expr(OP, "=", NULL, NULL);
n->useor = FALSE;
n->subLinkType = ANY_SUBLINK;
@ -4661,7 +4659,7 @@ a_expr: c_expr
if (IsA($5, SubLink))
{
SubLink *n = (SubLink *)$5;
n->lefthand = lcons($1, NIL);
n->lefthand = makeList1($1);
n->oper = (List *) makeA_Expr(OP, "<>", NULL, NULL);
n->useor = FALSE;
n->subLinkType = ALL_SUBLINK;
@ -4685,7 +4683,7 @@ a_expr: c_expr
| a_expr all_Op sub_type '(' SubSelect ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = lcons($1, NIL);
n->lefthand = makeList1($1);
n->oper = (List *) makeA_Expr(OP, $2, NULL, NULL);
n->useor = FALSE; /* doesn't matter since only one col */
n->subLinkType = $3;
@ -4840,7 +4838,7 @@ c_expr: attr
star->val.type = T_Integer;
star->val.val.ival = 1;
n->funcname = $1;
n->args = lcons(star, NIL);
n->args = makeList1(star);
n->agg_star = TRUE;
n->agg_distinct = FALSE;
$$ = (Node *)n;
@ -4873,7 +4871,7 @@ c_expr: attr
t->typmod = -1;
n->funcname = xlateSqlType("date");
n->args = lcons(s, NIL);
n->args = makeList1(s);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
@ -4898,7 +4896,7 @@ c_expr: attr
t->typmod = -1;
n->funcname = xlateSqlType("time");
n->args = lcons(s, NIL);
n->args = makeList1(s);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
@ -4923,7 +4921,7 @@ c_expr: attr
t->typmod = -1;
n->funcname = xlateSqlType("time");
n->args = lcons(s, NIL);
n->args = makeList1(s);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
@ -4952,7 +4950,7 @@ c_expr: attr
t->typmod = -1;
n->funcname = xlateSqlType("timestamp");
n->args = lcons(s, NIL);
n->args = makeList1(s);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
@ -4977,7 +4975,7 @@ c_expr: attr
t->typmod = -1;
n->funcname = xlateSqlType("timestamp");
n->args = lcons(s, NIL);
n->args = makeList1(s);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
@ -5104,26 +5102,26 @@ c_expr: attr
* Supporting nonterminals for expressions.
*/
opt_indirection: '[' a_expr ']' opt_indirection
opt_indirection: opt_indirection '[' a_expr ']'
{
A_Indices *ai = makeNode(A_Indices);
ai->lidx = NULL;
ai->uidx = $2;
$$ = lcons(ai, $4);
ai->uidx = $3;
$$ = lappend($1, ai);
}
| '[' a_expr ':' a_expr ']' opt_indirection
| opt_indirection '[' a_expr ':' a_expr ']'
{
A_Indices *ai = makeNode(A_Indices);
ai->lidx = $2;
ai->uidx = $4;
$$ = lcons(ai, $6);
ai->lidx = $3;
ai->uidx = $5;
$$ = lappend($1, ai);
}
| /*EMPTY*/
{ $$ = NIL; }
;
expr_list: a_expr
{ $$ = lcons($1, NIL); }
{ $$ = makeList1($1); }
| expr_list ',' a_expr
{ $$ = lappend($1, $3); }
| expr_list USING a_expr
@ -5135,7 +5133,7 @@ extract_list: extract_arg FROM a_expr
A_Const *n = makeNode(A_Const);
n->val.type = T_String;
n->val.val.str = $1;
$$ = makeList((Node *)n, $3, -1);
$$ = makeList2((Node *) n, $3);
}
| /*EMPTY*/
{ $$ = NIL; }
@ -5149,7 +5147,7 @@ extract_arg: datetime { $$ = $1; }
/* position_list uses b_expr not a_expr to avoid conflict with general IN */
position_list: b_expr IN b_expr
{ $$ = makeList($3, $1, -1); }
{ $$ = makeList2($3, $1); }
| /*EMPTY*/
{ $$ = NIL; }
;
@ -5169,7 +5167,7 @@ substr_from: FROM expr_list
A_Const *n = makeNode(A_Const);
n->val.type = T_Integer;
n->val.val.ival = 1;
$$ = lcons((Node *)n,NIL);
$$ = makeList1((Node *)n);
}
;
@ -5198,7 +5196,7 @@ in_expr: SubSelect
;
in_expr_nodes: a_expr
{ $$ = lcons($1, NIL); }
{ $$ = makeList1($1); }
| in_expr_nodes ',' a_expr
{ $$ = lappend($1, $3); }
;
@ -5236,7 +5234,7 @@ case_expr: CASE case_arg when_clause_list case_default END_TRANS
w->result = (Node *)n;
*/
w->expr = makeA_Expr(OP, "=", $3, $5);
c->args = lcons(w, NIL);
c->args = makeList1(w);
c->defresult = $3;
$$ = (Node *)c;
}
@ -5259,7 +5257,7 @@ case_expr: CASE case_arg when_clause_list case_default END_TRANS
when_clause_list: when_clause_list when_clause
{ $$ = lappend($1, $2); }
| when_clause
{ $$ = lcons($1, NIL); }
{ $$ = makeList1($1); }
;
when_clause: WHEN a_expr THEN a_expr
@ -5300,7 +5298,7 @@ attr: relation_name '.' attrs opt_indirection
;
attrs: attr_name
{ $$ = lcons(makeString($1), NIL); }
{ $$ = makeList1(makeString($1)); }
| attrs '.' attr_name
{ $$ = lappend($1, makeString($3)); }
| attrs '.' '*'
@ -5319,7 +5317,7 @@ attrs: attr_name
target_list: target_list ',' target_el
{ $$ = lappend($1, $3); }
| target_el
{ $$ = lcons($1, NIL); }
{ $$ = makeList1($1); }
;
/* AS is not optional because shift/red conflict with unary ops */
@ -5342,7 +5340,7 @@ target_el: a_expr AS ColLabel
Attr *att = makeNode(Attr);
att->relname = $1;
att->paramNo = NULL;
att->attrs = lcons(makeString("*"), NIL);
att->attrs = makeList1(makeString("*"));
att->indirection = NIL;
$$ = makeNode(ResTarget);
$$->name = NULL;
@ -5368,7 +5366,7 @@ target_el: a_expr AS ColLabel
update_target_list: update_target_list ',' update_target_el
{ $$ = lappend($1,$3); }
| update_target_el
{ $$ = lcons($1, NIL); }
{ $$ = makeList1($1); }
;
update_target_el: ColId opt_indirection '=' a_expr

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.41 2000/09/25 18:14:54 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.42 2000/09/29 18:21:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -129,10 +129,13 @@ check_ungrouped_columns_walker(Node *node,
* Ideally this should be done earlier, but it's difficult to distinguish
* aggregates from plain functions at the grammar level. So instead we
* check here. This function should be called after the target list and
* qualifications are finalized.
* qualifications are finalized. BUT: in some cases we want to call this
* routine before we've assembled the joinlist and qual into a FromExpr.
* So, rather than looking at qry->jointree, look at pstate->p_joinlist
* and the explicitly-passed qual.
*/
void
parseCheckAggregates(ParseState *pstate, Query *qry)
parseCheckAggregates(ParseState *pstate, Query *qry, Node *qual)
{
List *groupClauses = NIL;
List *tl;
@ -141,18 +144,16 @@ parseCheckAggregates(ParseState *pstate, Query *qry)
Assert(pstate->p_hasAggs || qry->groupClause || qry->havingQual);
/*
* Aggregates must never appear in WHERE clauses. (Note this check
* should appear first to deliver an appropriate error message;
* otherwise we are likely to complain about some innocent variable in
* the target list, which is outright misleading if the problem is in
* WHERE.)
* Aggregates must never appear in WHERE or JOIN/ON clauses.
*
* (Note this check should appear first to deliver an appropriate error
* message; otherwise we are likely to complain about some innocent
* variable in the target list, which is outright misleading if the
* problem is in WHERE.)
*/
if (contain_agg_clause(qry->qual))
if (contain_agg_clause(qual))
elog(ERROR, "Aggregates not allowed in WHERE clause");
/*
* ON-conditions in JOIN expressions are like WHERE clauses.
*/
if (contain_agg_clause((Node *) qry->jointree))
if (contain_agg_clause((Node *) pstate->p_joinlist))
elog(ERROR, "Aggregates not allowed in JOIN conditions");
/*

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);
/*

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.83 2000/09/12 21:07:02 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.84 2000/09/29 18:21:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -175,7 +175,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
result = ParseFuncOrColumn(pstate,
"nullvalue",
lcons(lexpr, NIL),
makeList1(lexpr),
false, false,
precedence);
}
@ -188,7 +188,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
result = ParseFuncOrColumn(pstate,
"nonnullvalue",
lcons(lexpr, NIL),
makeList1(lexpr),
false, false,
precedence);
}
@ -213,7 +213,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
expr->typeOid = BOOLOID;
expr->opType = AND_EXPR;
expr->args = makeList(lexpr, rexpr, -1);
expr->args = makeList2(lexpr, rexpr);
result = (Node *) expr;
}
break;
@ -235,7 +235,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
typeidTypeName(exprType(rexpr)), typeidTypeName(BOOLOID));
expr->typeOid = BOOLOID;
expr->opType = OR_EXPR;
expr->args = makeList(lexpr, rexpr, -1);
expr->args = makeList2(lexpr, rexpr);
result = (Node *) expr;
}
break;
@ -251,7 +251,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
typeidTypeName(exprType(rexpr)), typeidTypeName(BOOLOID));
expr->typeOid = BOOLOID;
expr->opType = NOT_EXPR;
expr->args = makeList(rexpr, -1);
expr->args = makeList1(rexpr);
result = (Node *) expr;
}
break;
@ -294,7 +294,8 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
break;
}
pstate->p_hasSubLinks = true;
qtrees = parse_analyze(lcons(sublink->subselect, NIL), pstate);
qtrees = parse_analyze(makeList1(sublink->subselect),
pstate);
if (length(qtrees) != 1)
elog(ERROR, "Bad query in subselect");
qtree = (Query *) lfirst(qtrees);

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.90 2000/09/12 21:07:02 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.91 2000/09/29 18:21:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -76,7 +76,7 @@ ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr, int precedence)
EXPR_RELATION_FIRST);
retval = ParseFuncOrColumn(pstate, strVal(lfirst(attr->attrs)),
lcons(param, NIL),
makeList1(param),
false, false,
precedence);
}
@ -87,7 +87,7 @@ ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr, int precedence)
ident->name = attr->relname;
ident->isRel = TRUE;
retval = ParseFuncOrColumn(pstate, strVal(lfirst(attr->attrs)),
lcons(ident, NIL),
makeList1(ident),
false, false,
precedence);
}
@ -96,7 +96,7 @@ ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr, int precedence)
foreach(mutator_iter, lnext(attr->attrs))
{
retval = ParseFuncOrColumn(pstate, strVal(lfirst(mutator_iter)),
lcons(retval, NIL),
makeList1(retval),
false, false,
precedence);
}
@ -447,6 +447,9 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
* but has varattno == 0 to signal that the whole tuple is the
* argument.
*/
if (rte->relname == NULL)
elog(ERROR,
"function applied to tuple is not supported for subSELECTs");
toid = typeTypeId(typenameType(rte->relname));
/* replace it in the arg list */

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.46 2000/09/12 21:07:02 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.47 2000/09/29 18:21:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -152,11 +152,11 @@ make_op(char *opname, Node *ltree, Node *rtree)
result->oper = (Node *) newop;
if (!left)
result->args = lcons(right, NIL);
result->args = makeList1(right);
else if (!right)
result->args = lcons(left, NIL);
result->args = makeList1(left);
else
result->args = lcons(left, lcons(right, NIL));
result->args = makeList2(left, right);
return result;
} /* make_op() */
@ -171,8 +171,8 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno)
{
int vnum,
sublevels_up;
Oid vartypeid;
int32 type_mod;
Oid vartypeid = 0;
int32 type_mod = 0;
vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
@ -197,8 +197,22 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno)
else
{
/* Subselect RTE --- get type info from subselect's tlist */
elog(ERROR, "make_var: subselect in FROM not implemented yet");
vartypeid = type_mod = 0;
List *tlistitem;
foreach(tlistitem, rte->subquery->targetList)
{
TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
if (te->resdom->resjunk || te->resdom->resno != attrno)
continue;
vartypeid = te->resdom->restype;
type_mod = te->resdom->restypmod;
break;
}
/* falling off end of list shouldn't happen... */
if (tlistitem == NIL)
elog(ERROR, "Subquery %s does not have attribute %d",
rte->eref->relname, attrno);
}
return makeVar(vnum, attrno, vartypeid, type_mod, sublevels_up);

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.48 2000/09/25 18:14:54 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.49 2000/09/29 18:21:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -26,7 +26,6 @@
#include "parser/parse_relation.h"
#include "parser/parse_type.h"
#include "rewrite/rewriteManip.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
@ -98,7 +97,7 @@ refnameRangeOrJoinEntry(ParseState *pstate,
/*
* Check the rangetable for RTEs; if no match, recursively scan
* the jointree for join tables. We assume that no duplicate
* the joinlist for join tables. We assume that no duplicate
* entries have been made in any one nesting level.
*/
foreach(temp, pstate->p_rtable)
@ -109,7 +108,7 @@ refnameRangeOrJoinEntry(ParseState *pstate,
return (Node *) rte;
}
join = scanJoinTreeForRefname((Node *) pstate->p_jointree, refname);
join = scanJoinListForRefname((Node *) pstate->p_joinlist, refname);
if (join)
return (Node *) join;
@ -122,9 +121,14 @@ refnameRangeOrJoinEntry(ParseState *pstate,
return NULL;
}
/* Recursively search a jointree for a joinexpr with given refname */
/*
* Recursively search a joinlist for a joinexpr with given refname
*
* Note that during parse analysis, we don't expect to find a FromExpr node
* in p_joinlist; its top level is just a bare List.
*/
JoinExpr *
scanJoinTreeForRefname(Node *jtnode, char *refname)
scanJoinListForRefname(Node *jtnode, char *refname)
{
JoinExpr *result = NULL;
@ -136,7 +140,7 @@ scanJoinTreeForRefname(Node *jtnode, char *refname)
foreach(l, (List *) jtnode)
{
result = scanJoinTreeForRefname(lfirst(l), refname);
result = scanJoinListForRefname(lfirst(l), refname);
if (result)
break;
}
@ -151,12 +155,12 @@ scanJoinTreeForRefname(Node *jtnode, char *refname)
if (j->alias && strcmp(j->alias->relname, refname) == 0)
return j;
result = scanJoinTreeForRefname(j->larg, refname);
result = scanJoinListForRefname(j->larg, refname);
if (! result)
result = scanJoinTreeForRefname(j->rarg, refname);
result = scanJoinListForRefname(j->rarg, refname);
}
else
elog(ERROR, "scanJoinTreeForRefname: unexpected node type %d",
elog(ERROR, "scanJoinListForRefname: unexpected node type %d",
nodeTag(jtnode));
return result;
}
@ -261,6 +265,9 @@ RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up)
* Search the column names of a single RTE for the given name.
* If found, return an appropriate Var node, else return NULL.
* If the name proves ambiguous within this RTE, raise error.
*
* Side effect: if we find a match, mark the RTE as requiring read access.
* See comments in setTargetTable().
*/
static Node *
scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
@ -281,6 +288,7 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
if (result)
elog(ERROR, "Column reference \"%s\" is ambiguous", colname);
result = (Node *) make_var(pstate, rte, attnum);
rte->checkForRead = true;
}
}
@ -299,7 +307,10 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
{
attnum = specialAttNum(colname);
if (attnum != InvalidAttrNumber)
{
result = (Node *) make_var(pstate, rte, attnum);
rte->checkForRead = true;
}
}
return result;
@ -310,6 +321,11 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
* Search the column names of a single join table for the given name.
* If found, return an appropriate Var node or expression, else return NULL.
* If the name proves ambiguous within this jointable, raise error.
*
* NOTE: unlike scanRTEForColumn, there's no need to worry about forcing
* checkForRead true for the referenced tables. This is so because a join
* expression can only appear in a FROM clause, and any table named in
* FROM will be marked checkForRead from the beginning.
*/
static Node *
scanJoinForColumn(JoinExpr *join, char *colname, int sublevels_up)
@ -359,7 +375,7 @@ colnameToVar(ParseState *pstate, char *colname)
* those, ignore RTEs that are marked as not inFromCl and not
* the query's target relation.
*/
foreach(jt, pstate->p_jointree)
foreach(jt, pstate->p_joinlist)
{
Node *jtnode = (Node *) lfirst(jt);
Node *newresult = NULL;
@ -446,8 +462,9 @@ qualifiedNameToVar(ParseState *pstate, char *refname, char *colname,
}
/*
* Add an entry to the pstate's range table (p_rtable), unless the
* specified refname is already present, in which case raise error.
* Add an entry for a relation to the pstate's range table (p_rtable).
*
* If the specified refname is already present, raise error.
*
* If pstate is NULL, we just build an RTE and return it without worrying
* about membership in an rtable list.
@ -481,6 +498,7 @@ addRangeTableEntry(ParseState *pstate,
rte->relname = relname;
rte->alias = alias;
rte->subquery = NULL;
/*
* Get the rel's OID. This access also ensures that we have an
@ -492,7 +510,7 @@ addRangeTableEntry(ParseState *pstate,
rte->relid = RelationGetRelid(rel);
maxattrs = RelationGetNumberOfAttributes(rel);
eref = alias ? copyObject(alias) : makeAttr(refname, NULL);
eref = alias ? (Attr *) copyObject(alias) : makeAttr(refname, NULL);
numaliases = length(eref->attrs);
if (maxattrs < numaliases)
@ -515,12 +533,18 @@ addRangeTableEntry(ParseState *pstate,
* Flags:
* - this RTE should be expanded to include descendant tables,
* - this RTE is in the FROM clause,
* - this RTE should not be checked for access rights.
* - this RTE should be checked for read/write access rights.
*
* The initial default on access checks is always check-for-READ-access,
* which is the right thing for all except target tables.
*----------
*/
rte->inh = inh;
rte->inFromCl = inFromCl;
rte->skipAcl = false; /* always starts out false */
rte->checkForRead = true;
rte->checkForWrite = false;
rte->checkAsUser = InvalidOid; /* not set-uid by default, either */
/*
* Add completed RTE to range table list.
@ -532,17 +556,105 @@ addRangeTableEntry(ParseState *pstate,
}
/*
* Add the given RTE as a top-level entry in the pstate's join tree,
* Add an entry for a subquery to the pstate's range table (p_rtable).
*
* This is just like addRangeTableEntry() except that it makes a subquery RTE.
* Note that an alias clause *must* be supplied.
*/
RangeTblEntry *
addRangeTableEntryForSubquery(ParseState *pstate,
Query *subquery,
Attr *alias,
bool inFromCl)
{
char *refname = alias->relname;
RangeTblEntry *rte;
Attr *eref;
int numaliases;
int varattno;
List *tlistitem;
/* Check for conflicting RTE or jointable alias (at level 0 only) */
if (pstate != NULL)
{
Node *rteorjoin = refnameRangeOrJoinEntry(pstate, refname, NULL);
if (rteorjoin)
elog(ERROR, "Table name \"%s\" specified more than once",
refname);
}
rte = makeNode(RangeTblEntry);
rte->relname = NULL;
rte->relid = InvalidOid;
rte->subquery = subquery;
rte->alias = alias;
eref = copyObject(alias);
numaliases = length(eref->attrs);
/* fill in any unspecified alias columns */
varattno = 0;
foreach(tlistitem, subquery->targetList)
{
TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
if (te->resdom->resjunk)
continue;
varattno++;
Assert(varattno == te->resdom->resno);
if (varattno > numaliases)
{
char *attrname;
attrname = pstrdup(te->resdom->resname);
eref->attrs = lappend(eref->attrs, makeString(attrname));
}
}
if (varattno < numaliases)
elog(ERROR, "Table \"%s\" has %d columns available but %d columns specified",
refname, varattno, numaliases);
rte->eref = eref;
/*----------
* Flags:
* - this RTE should be expanded to include descendant tables,
* - this RTE is in the FROM clause,
* - this RTE should be checked for read/write access rights.
*
* Subqueries are never checked for access rights.
*----------
*/
rte->inh = false; /* never true for subqueries */
rte->inFromCl = inFromCl;
rte->checkForRead = false;
rte->checkForWrite = false;
rte->checkAsUser = InvalidOid;
/*
* Add completed RTE to range table list.
*/
if (pstate != NULL)
pstate->p_rtable = lappend(pstate->p_rtable, rte);
return rte;
}
/*
* Add the given RTE as a top-level entry in the pstate's join list,
* unless there already is an entry for it.
*/
void
addRTEtoJoinTree(ParseState *pstate, RangeTblEntry *rte)
addRTEtoJoinList(ParseState *pstate, RangeTblEntry *rte)
{
int rtindex = RTERangeTablePosn(pstate, rte, NULL);
List *jt;
RangeTblRef *rtr;
foreach(jt, pstate->p_jointree)
foreach(jt, pstate->p_joinlist)
{
Node *n = (Node *) lfirst(jt);
@ -556,7 +668,7 @@ addRTEtoJoinTree(ParseState *pstate, RangeTblEntry *rte)
/* Not present, so add it */
rtr = makeNode(RangeTblRef);
rtr->rtindex = rtindex;
pstate->p_jointree = lappend(pstate->p_jointree, rtr);
pstate->p_joinlist = lappend(pstate->p_joinlist, rtr);
}
/*
@ -570,7 +682,7 @@ addImplicitRTE(ParseState *pstate, char *relname)
RangeTblEntry *rte;
rte = addRangeTableEntry(pstate, relname, NULL, false, false);
addRTEtoJoinTree(pstate, rte);
addRTEtoJoinList(pstate, rte);
warnAutoRange(pstate, relname);
return rte;
@ -590,11 +702,9 @@ void
expandRTE(ParseState *pstate, RangeTblEntry *rte,
List **colnames, List **colvars)
{
Relation rel;
int varattno,
maxattrs,
rtindex,
sublevels_up;
int rtindex,
sublevels_up,
varattno;
if (colnames)
*colnames = NIL;
@ -604,43 +714,88 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
/* Need the RT index of the entry for creating Vars */
rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
rel = heap_open(rte->relid, AccessShareLock);
maxattrs = RelationGetNumberOfAttributes(rel);
for (varattno = 0; varattno < maxattrs; varattno++)
if (rte->relname)
{
Form_pg_attribute attr = rel->rd_att->attrs[varattno];
/* Ordinary relation RTE */
Relation rel;
int maxattrs;
rel = heap_openr(rte->relname, AccessShareLock);
maxattrs = RelationGetNumberOfAttributes(rel);
for (varattno = 0; varattno < maxattrs; varattno++)
{
Form_pg_attribute attr = rel->rd_att->attrs[varattno];
#ifdef _DROP_COLUMN_HACK__
if (COLUMN_IS_DROPPED(attr))
continue;
if (COLUMN_IS_DROPPED(attr))
continue;
#endif /* _DROP_COLUMN_HACK__ */
if (colnames)
{
char *label;
if (colnames)
{
char *label;
if (varattno < length(rte->eref->attrs))
label = strVal(nth(varattno, rte->eref->attrs));
else
label = NameStr(attr->attname);
*colnames = lappend(*colnames, makeString(pstrdup(label)));
if (varattno < length(rte->eref->attrs))
label = strVal(nth(varattno, rte->eref->attrs));
else
label = NameStr(attr->attname);
*colnames = lappend(*colnames, makeString(pstrdup(label)));
}
if (colvars)
{
Var *varnode;
varnode = makeVar(rtindex, attr->attnum,
attr->atttypid, attr->atttypmod,
sublevels_up);
*colvars = lappend(*colvars, varnode);
}
}
if (colvars)
heap_close(rel, AccessShareLock);
}
else
{
/* Subquery RTE */
List *aliasp = rte->eref->attrs;
List *tlistitem;
varattno = 0;
foreach(tlistitem, rte->subquery->targetList)
{
Var *varnode;
TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
varnode = makeVar(rtindex, attr->attnum,
attr->atttypid, attr->atttypmod,
sublevels_up);
if (te->resdom->resjunk)
continue;
varattno++;
Assert(varattno == te->resdom->resno);
*colvars = lappend(*colvars, varnode);
if (colnames)
{
/* Assume there is one alias per target item */
char *label = strVal(lfirst(aliasp));
*colnames = lappend(*colnames, makeString(pstrdup(label)));
aliasp = lnext(aliasp);
}
if (colvars)
{
Var *varnode;
varnode = makeVar(rtindex, varattno,
te->resdom->restype,
te->resdom->restypmod,
sublevels_up);
*colvars = lappend(*colvars, varnode);
}
}
}
heap_close(rel, AccessShareLock);
}
/*

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.62 2000/09/12 21:07:02 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.63 2000/09/29 18:21:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -386,7 +386,7 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
* Turns '*' (in the target list) into a list of targetlist entries.
*
* tlist entries are generated for each relation appearing in the FROM list,
* which by now has been expanded into a join tree.
* which by now has been transformed into a joinlist.
*/
static List *
ExpandAllTables(ParseState *pstate)
@ -395,10 +395,10 @@ ExpandAllTables(ParseState *pstate)
List *jt;
/* SELECT *; */
if (pstate->p_jointree == NIL)
if (pstate->p_joinlist == NIL)
elog(ERROR, "Wildcard with no tables specified not allowed");
foreach(jt, pstate->p_jointree)
foreach(jt, pstate->p_joinlist)
{
Node *n = (Node *) lfirst(jt);