diff options
Diffstat (limited to 'src/backend/parser')
| -rw-r--r-- | src/backend/parser/analyze.c | 179 | ||||
| -rw-r--r-- | src/backend/parser/gram.y | 164 | ||||
| -rw-r--r-- | src/backend/parser/parse_agg.c | 27 | ||||
| -rw-r--r-- | src/backend/parser/parse_clause.c | 96 | ||||
| -rw-r--r-- | src/backend/parser/parse_expr.c | 15 | ||||
| -rw-r--r-- | src/backend/parser/parse_func.c | 11 | ||||
| -rw-r--r-- | src/backend/parser/parse_node.c | 30 | ||||
| -rw-r--r-- | src/backend/parser/parse_relation.c | 253 | ||||
| -rw-r--r-- | src/backend/parser/parse_target.c | 8 |
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); |
