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:
@ -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
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
||||
/*
|
||||
|
@ -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);
|
||||
/*
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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);
|
||||
|
||||
|
Reference in New Issue
Block a user