summaryrefslogtreecommitdiff
path: root/src/backend/parser
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser')
-rw-r--r--src/backend/parser/analyze.c179
-rw-r--r--src/backend/parser/gram.y164
-rw-r--r--src/backend/parser/parse_agg.c27
-rw-r--r--src/backend/parser/parse_clause.c96
-rw-r--r--src/backend/parser/parse_expr.c15
-rw-r--r--src/backend/parser/parse_func.c11
-rw-r--r--src/backend/parser/parse_node.c30
-rw-r--r--src/backend/parser/parse_relation.c253
-rw-r--r--src/backend/parser/parse_target.c8
9 files changed, 514 insertions, 269 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 888f8f8e14..169075c47e 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -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);
+ RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
+
+ ++i;
+ if (rte->subquery)
+ {
+ /* FOR UPDATE of subquery is propagated to subquery's rels */
+ transformForUpdate(rte->subquery, makeList1(NULL));
+ }
+ else
+ {
+ rowMarks = lappendi(rowMarks, i);
+ rte->checkForWrite = true;
+ }
}
- qry->rowMark = nconc(qry->rowMark, rowMark);
- return;
}
-
- foreach(l, forUpdate)
+ else
{
- char *relname = lfirst(l);
- List *l2;
-
- i = 1;
- foreach(l2, qry->rtable)
+ /* just the named tables */
+ foreach(l, forUpdate)
{
- if (strcmp(((RangeTblEntry *) lfirst(l2))->eref->relname, relname) == 0)
+ char *relname = lfirst(l);
+
+ i = 0;
+ foreach(rt, qry->rtable)
{
- List *l3;
+ RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
- foreach(l3, rowMark)
+ ++i;
+ if (strcmp(rte->eref->relname, relname) == 0)
{
- 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);
+ 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;
}
- break;
}
- i++;
+ if (rt == NIL)
+ elog(ERROR, "FOR UPDATE: relation \"%s\" not found in FROM clause",
+ relname);
}
- if (l2 == NULL)
- 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
*/
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 93aa912f45..31aea152fa 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -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
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index c3ac417365..e7d8fe8d3b 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -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");
/*
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 87041d3b6e..cc849ebf07 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -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);
/*
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index a033ff4be2..591fdab878 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -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);
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 1f19b1b949..b4324b0b6a 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -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 */
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index 85a56067bd..2ff4d9c990 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -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);
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index baae0a578c..3fccd95cb1 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -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);
+ if (rte->relname)
+ {
+ /* Ordinary relation RTE */
+ Relation rel;
+ int maxattrs;
- maxattrs = RelationGetNumberOfAttributes(rel);
+ rel = heap_openr(rte->relname, AccessShareLock);
- for (varattno = 0; varattno < maxattrs; varattno++)
- {
- Form_pg_attribute attr = rel->rd_att->attrs[varattno];
+ 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);
+
+ if (te->resdom->resjunk)
+ continue;
+ varattno++;
+ Assert(varattno == te->resdom->resno);
+
+ if (colnames)
+ {
+ /* Assume there is one alias per target item */
+ char *label = strVal(lfirst(aliasp));
- varnode = makeVar(rtindex, attr->attnum,
- attr->atttypid, attr->atttypmod,
- sublevels_up);
+ *colnames = lappend(*colnames, makeString(pstrdup(label)));
+ aliasp = lnext(aliasp);
+ }
- *colvars = lappend(*colvars, varnode);
+ if (colvars)
+ {
+ Var *varnode;
+
+ varnode = makeVar(rtindex, varattno,
+ te->resdom->restype,
+ te->resdom->restypmod,
+ sublevels_up);
+
+ *colvars = lappend(*colvars, varnode);
+ }
}
}
-
- heap_close(rel, AccessShareLock);
}
/*
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index b8e1570985..c353c064fd 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -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);