diff options
Diffstat (limited to 'src/backend/parser')
-rw-r--r-- | src/backend/parser/analyze.c | 474 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 107 | ||||
-rw-r--r-- | src/backend/parser/parse_expr.c | 60 | ||||
-rw-r--r-- | src/backend/parser/parse_node.c | 4 | ||||
-rw-r--r-- | src/backend/parser/parse_relation.c | 123 | ||||
-rw-r--r-- | src/backend/parser/parse_target.c | 159 | ||||
-rw-r--r-- | src/backend/parser/parse_type.c | 3 |
7 files changed, 734 insertions, 196 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index b086afe8ca2..4f7001b6f1a 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.340 2006/07/14 14:52:21 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.341 2006/08/02 01:59:46 joe Exp $ * *------------------------------------------------------------------------- */ @@ -98,10 +98,13 @@ static Query *transformViewStmt(ParseState *pstate, ViewStmt *stmt, static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt); static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt, List **extras_before, List **extras_after); +static List *transformInsertRow(ParseState *pstate, List *exprlist, + List *stmtcols, List *icolumns, List *attrnos); static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt); static Query *transformRuleStmt(ParseState *query, RuleStmt *stmt, List **extras_before, List **extras_after); static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt); +static Query *transformValuesClause(ParseState *pstate, SelectStmt *stmt); static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt); static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt); static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt); @@ -367,12 +370,16 @@ transformStmt(ParseState *pstate, Node *parseTree, break; case T_SelectStmt: - if (((SelectStmt *) parseTree)->op == SETOP_NONE) - result = transformSelectStmt(pstate, - (SelectStmt *) parseTree); - else - result = transformSetOperationStmt(pstate, - (SelectStmt *) parseTree); + { + SelectStmt *n = (SelectStmt *) parseTree; + + if (n->valuesLists) + result = transformValuesClause(pstate, n); + else if (n->op == SETOP_NONE) + result = transformSelectStmt(pstate, n); + else + result = transformSetOperationStmt(pstate, n); + } break; case T_DeclareCursorStmt: @@ -510,20 +517,31 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt, List **extras_before, List **extras_after) { Query *qry = makeNode(Query); - Query *selectQuery = NULL; + SelectStmt *selectStmt = (SelectStmt *) stmt->selectStmt; + List *exprList = NIL; + bool isGeneralSelect; List *sub_rtable; List *sub_relnamespace; List *sub_varnamespace; List *icolumns; List *attrnos; + RangeTblEntry *rte; + RangeTblRef *rtr; ListCell *icols; ListCell *attnos; - ListCell *tl; + ListCell *lc; qry->commandType = CMD_INSERT; pstate->p_is_insert = true; /* + * We have three cases to deal with: DEFAULT VALUES (selectStmt == NULL), + * VALUES list, or general SELECT input. We special-case VALUES, both + * for efficiency and so we can handle DEFAULT specifications. + */ + isGeneralSelect = (selectStmt && selectStmt->valuesLists == NIL); + + /* * If a non-nil rangetable/namespace was passed in, and we are doing * INSERT/SELECT, arrange to pass the rangetable/namespace down to the * SELECT. This can only happen if we are inside a CREATE RULE, and in @@ -532,7 +550,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt, * The SELECT's joinlist is not affected however. We must do this before * adding the target table to the INSERT's rtable. */ - if (stmt->selectStmt) + if (isGeneralSelect) { sub_rtable = pstate->p_rtable; pstate->p_rtable = NIL; @@ -557,10 +575,23 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt, qry->resultRelation = setTargetTable(pstate, stmt->relation, false, false, ACL_INSERT); + /* Validate stmt->cols list, or build default list if no list given */ + icolumns = checkInsertTargets(pstate, stmt->cols, &attrnos); + Assert(list_length(icolumns) == list_length(attrnos)); + /* - * Is it INSERT ... SELECT or INSERT ... VALUES? + * Determine which variant of INSERT we have. */ - if (stmt->selectStmt) + if (selectStmt == NULL) + { + /* + * We have INSERT ... DEFAULT VALUES. We can handle this case by + * emitting an empty targetlist --- all columns will be defaulted + * when the planner expands the targetlist. + */ + exprList = NIL; + } + else if (isGeneralSelect) { /* * We make the sub-pstate a child of the outer pstate so that it can @@ -570,8 +601,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt, * see. */ ParseState *sub_pstate = make_parsestate(pstate); - RangeTblEntry *rte; - RangeTblRef *rtr; + Query *selectQuery; /* * Process the source SELECT. @@ -617,8 +647,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt, pstate->p_joinlist = lappend(pstate->p_joinlist, rtr); /*---------- - * Generate a targetlist for the INSERT that selects all the - * non-resjunk columns from the subquery. (We need this to be + * Generate an expression list for the INSERT that selects all the + * non-resjunk columns from the subquery. (INSERT's tlist must be * separate from the subquery's tlist because we may add columns, * insert datatype coercions, etc.) * @@ -630,10 +660,10 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt, * INSERT INTO foo SELECT 'bar', ... FROM baz *---------- */ - qry->targetList = NIL; - foreach(tl, selectQuery->targetList) + exprList = NIL; + foreach(lc, selectQuery->targetList) { - TargetEntry *tle = (TargetEntry *) lfirst(tl); + TargetEntry *tle = (TargetEntry *) lfirst(lc); Expr *expr; if (tle->resjunk) @@ -648,83 +678,221 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt, exprType((Node *) tle->expr), exprTypmod((Node *) tle->expr), 0); - tle = makeTargetEntry(expr, - (AttrNumber) pstate->p_next_resno++, - tle->resname, - false); - qry->targetList = lappend(qry->targetList, tle); + exprList = lappend(exprList, expr); } + + /* Prepare row for assignment to target table */ + exprList = transformInsertRow(pstate, exprList, + stmt->cols, + icolumns, attrnos); } - else + else if (list_length(selectStmt->valuesLists) > 1) { /* - * For INSERT ... VALUES, transform the given list of values to form a - * targetlist for the INSERT. + * Process INSERT ... VALUES with multiple VALUES sublists. + * We generate a VALUES RTE holding the transformed expression + * lists, and build up a targetlist containing Vars that reference + * the VALUES RTE. */ - qry->targetList = transformTargetList(pstate, stmt->targetList); + List *exprsLists = NIL; + int sublist_length = -1; + + foreach(lc, selectStmt->valuesLists) + { + List *sublist = (List *) lfirst(lc); + + /* Do basic expression transformation (same as a ROW() expr) */ + sublist = transformExpressionList(pstate, sublist); + + /* + * All the sublists must be the same length, *after* transformation + * (which might expand '*' into multiple items). The VALUES RTE + * can't handle anything different. + */ + if (sublist_length < 0) + { + /* Remember post-transformation length of first sublist */ + sublist_length = list_length(sublist); + } + else if (sublist_length != list_length(sublist)) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("VALUES lists must all be the same length"))); + } + + /* Prepare row for assignment to target table */ + sublist = transformInsertRow(pstate, sublist, + stmt->cols, + icolumns, attrnos); + + exprsLists = lappend(exprsLists, sublist); + } + + /* + * There mustn't have been any table references in the expressions, + * else strange things would happen, like Cartesian products of + * those tables with the VALUES list ... + */ + if (pstate->p_joinlist != NIL) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("VALUES must not contain table references"))); + + /* + * Another thing we can't currently support is NEW/OLD references + * in rules --- seems we'd need something like SQL99's LATERAL + * construct to ensure that the values would be available while + * evaluating the VALUES RTE. This is a shame. FIXME + */ + if (contain_vars_of_level((Node *) exprsLists, 0)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("VALUES must not contain OLD or NEW references"))); + + /* + * Generate the VALUES RTE + */ + rte = addRangeTableEntryForValues(pstate, exprsLists, NULL, true); + rtr = makeNode(RangeTblRef); + /* assume new rte is at end */ + rtr->rtindex = list_length(pstate->p_rtable); + Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable)); + pstate->p_joinlist = lappend(pstate->p_joinlist, rtr); + + /* + * Generate list of Vars referencing the RTE + */ + expandRTE(rte, rtr->rtindex, 0, false, NULL, &exprList); } + else + { + /*---------- + * Process INSERT ... VALUES with a single VALUES sublist. + * We treat this separately for efficiency and for historical + * compatibility --- specifically, allowing table references, + * such as + * INSERT INTO foo VALUES(bar.*) + * + * The sublist is just computed directly as the Query's targetlist, + * with no VALUES RTE. So it works just like SELECT without FROM. + *---------- + */ + List *valuesLists = selectStmt->valuesLists; - /* - * Now we are done with SELECT-like processing, and can get on with - * transforming the target list to match the INSERT target columns. - */ + Assert(list_length(valuesLists) == 1); - /* Prepare to assign non-conflicting resnos to resjunk attributes */ - if (pstate->p_next_resno <= pstate->p_target_relation->rd_rel->relnatts) - pstate->p_next_resno = pstate->p_target_relation->rd_rel->relnatts + 1; + /* Do basic expression transformation (same as a ROW() expr) */ + exprList = transformExpressionList(pstate, + (List *) linitial(valuesLists)); - /* Validate stmt->cols list, or build default list if no list given */ - icolumns = checkInsertTargets(pstate, stmt->cols, &attrnos); + /* Prepare row for assignment to target table */ + exprList = transformInsertRow(pstate, exprList, + stmt->cols, + icolumns, attrnos); + } /* - * Prepare columns for assignment to target table. + * Generate query's target list using the computed list of expressions. */ + qry->targetList = NIL; icols = list_head(icolumns); attnos = list_head(attrnos); - foreach(tl, qry->targetList) + foreach(lc, exprList) { - TargetEntry *tle = (TargetEntry *) lfirst(tl); + Expr *expr = (Expr *) lfirst(lc); ResTarget *col; - - if (icols == NULL || attnos == NULL) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("INSERT has more expressions than target columns"))); + TargetEntry *tle; col = (ResTarget *) lfirst(icols); Assert(IsA(col, ResTarget)); - Assert(!tle->resjunk); - updateTargetListEntry(pstate, tle, col->name, lfirst_int(attnos), - col->indirection, col->location); + tle = makeTargetEntry(expr, + (AttrNumber) lfirst_int(attnos), + col->name, + false); + qry->targetList = lappend(qry->targetList, tle); icols = lnext(icols); attnos = lnext(attnos); } - /* - * Ensure that the targetlist has the same number of entries that were - * present in the columns list. Don't do the check unless an explicit - * columns list was given, though. - */ - if (stmt->cols != NIL && (icols != NULL || attnos != NULL)) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("INSERT has more target columns than expressions"))); - /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; - qry->hasAggs = pstate->p_hasAggs; + /* aggregates not allowed (but subselects are okay) */ if (pstate->p_hasAggs) - parseCheckAggregates(pstate, qry); + ereport(ERROR, + (errcode(ERRCODE_GROUPING_ERROR), + errmsg("cannot use aggregate function in VALUES"))); return qry; } /* + * Prepare an INSERT row for assignment to the target table. + * + * The row might be either a VALUES row, or variables referencing a + * sub-SELECT output. + */ +static List * +transformInsertRow(ParseState *pstate, List *exprlist, + List *stmtcols, List *icolumns, List *attrnos) +{ + List *result; + ListCell *lc; + ListCell *icols; + ListCell *attnos; + + /* + * Check length of expr list. It must not have more expressions than + * there are target columns. We allow fewer, but only if no explicit + * columns list was given (the remaining columns are implicitly + * defaulted). Note we must check this *after* transformation because + * that could expand '*' into multiple items. + */ + if (list_length(exprlist) > list_length(icolumns)) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("INSERT has more expressions than target columns"))); + if (stmtcols != NIL && + list_length(exprlist) < list_length(icolumns)) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("INSERT has more target columns than expressions"))); + + /* + * Prepare columns for assignment to target table. + */ + result = NIL; + icols = list_head(icolumns); + attnos = list_head(attrnos); + foreach(lc, exprlist) + { + Expr *expr = (Expr *) lfirst(lc); + ResTarget *col; + + col = (ResTarget *) lfirst(icols); + Assert(IsA(col, ResTarget)); + + expr = transformAssignedExpr(pstate, expr, + col->name, + lfirst_int(attnos), + col->indirection, + col->location); + + result = lappend(result, expr); + + icols = lnext(icols); + attnos = lnext(attnos); + } + + return result; +} + +/* * transformCreateStmt - * transforms the "create table" statement * SQL92 allows constraints to be scattered all over, so thumb through @@ -1934,6 +2102,187 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) } /* + * transformValuesClause - + * transforms a VALUES clause that's being used as a standalone SELECT + * + * We build a Query containing a VALUES RTE, rather as if one had written + * SELECT * FROM (VALUES ...) + */ +static Query * +transformValuesClause(ParseState *pstate, SelectStmt *stmt) +{ + Query *qry = makeNode(Query); + List *exprsLists = NIL; + List **coltype_lists = NULL; + Oid *coltypes = NULL; + int sublist_length = -1; + List *newExprsLists; + RangeTblEntry *rte; + RangeTblRef *rtr; + ListCell *lc; + ListCell *lc2; + int i; + + qry->commandType = CMD_SELECT; + + /* Most SELECT stuff doesn't apply in a VALUES clause */ + Assert(stmt->distinctClause == NIL); + Assert(stmt->into == NULL); + Assert(stmt->intoColNames == NIL); + Assert(stmt->targetList == NIL); + Assert(stmt->fromClause == NIL); + Assert(stmt->whereClause == NULL); + Assert(stmt->groupClause == NIL); + Assert(stmt->havingClause == NULL); + Assert(stmt->op == SETOP_NONE); + + /* + * For each row of VALUES, transform the raw expressions and gather + * type information. This is also a handy place to reject DEFAULT + * nodes, which the grammar allows for simplicity. + */ + foreach(lc, stmt->valuesLists) + { + List *sublist = (List *) lfirst(lc); + + /* Do basic expression transformation (same as a ROW() expr) */ + sublist = transformExpressionList(pstate, sublist); + + /* + * All the sublists must be the same length, *after* transformation + * (which might expand '*' into multiple items). The VALUES RTE + * can't handle anything different. + */ + if (sublist_length < 0) + { + /* Remember post-transformation length of first sublist */ + sublist_length = list_length(sublist); + /* and allocate arrays for column-type info */ + coltype_lists = (List **) palloc0(sublist_length * sizeof(List *)); + coltypes = (Oid *) palloc0(sublist_length * sizeof(Oid)); + } + else if (sublist_length != list_length(sublist)) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("VALUES lists must all be the same length"))); + } + + exprsLists = lappend(exprsLists, sublist); + + i = 0; + foreach(lc2, sublist) + { + Node *col = (Node *) lfirst(lc2); + + if (IsA(col, SetToDefault)) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("DEFAULT can only appear in a VALUES list within INSERT"))); + coltype_lists[i] = lappend_oid(coltype_lists[i], exprType(col)); + i++; + } + } + + /* + * Now resolve the common types of the columns, and coerce everything + * to those types. + */ + for (i = 0; i < sublist_length; i++) + { + coltypes[i] = select_common_type(coltype_lists[i], "VALUES"); + } + + newExprsLists = NIL; + foreach(lc, exprsLists) + { + List *sublist = (List *) lfirst(lc); + List *newsublist = NIL; + + i = 0; + foreach(lc2, sublist) + { + Node *col = (Node *) lfirst(lc2); + + col = coerce_to_common_type(pstate, col, coltypes[i], "VALUES"); + newsublist = lappend(newsublist, col); + i++; + } + + newExprsLists = lappend(newExprsLists, newsublist); + } + + /* + * Generate the VALUES RTE + */ + rte = addRangeTableEntryForValues(pstate, newExprsLists, NULL, true); + rtr = makeNode(RangeTblRef); + /* assume new rte is at end */ + rtr->rtindex = list_length(pstate->p_rtable); + Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable)); + pstate->p_joinlist = lappend(pstate->p_joinlist, rtr); + + /* + * Generate a targetlist as though expanding "*" + */ + Assert(pstate->p_next_resno == 1); + qry->targetList = expandRelAttrs(pstate, rte, rtr->rtindex, 0); + + /* + * The grammar does allow attaching ORDER BY, LIMIT, and FOR UPDATE + * to a VALUES, so cope. + */ + qry->sortClause = transformSortClause(pstate, + stmt->sortClause, + &qry->targetList, + true /* fix unknowns */ ); + + qry->limitOffset = transformLimitClause(pstate, stmt->limitOffset, + "OFFSET"); + qry->limitCount = transformLimitClause(pstate, stmt->limitCount, + "LIMIT"); + + if (stmt->lockingClause) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("SELECT FOR UPDATE/SHARE cannot be applied to VALUES"))); + + /* + * There mustn't have been any table references in the expressions, + * else strange things would happen, like Cartesian products of + * those tables with the VALUES list. We have to check this after + * parsing ORDER BY et al since those could insert more junk. + */ + if (list_length(pstate->p_joinlist) != 1) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("VALUES must not contain table references"))); + + /* + * Another thing we can't currently support is NEW/OLD references + * in rules --- seems we'd need something like SQL99's LATERAL + * construct to ensure that the values would be available while + * evaluating the VALUES RTE. This is a shame. FIXME + */ + if (contain_vars_of_level((Node *) newExprsLists, 0)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("VALUES must not contain OLD or NEW references"))); + + qry->rtable = pstate->p_rtable; + qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); + + qry->hasSubLinks = pstate->p_hasSubLinks; + /* aggregates not allowed (but subselects are okay) */ + if (pstate->p_hasAggs) + ereport(ERROR, + (errcode(ERRCODE_GROUPING_ERROR), + errmsg("cannot use aggregate function in VALUES"))); + + return qry; +} + +/* * transformSetOperationsStmt - * transforms a set-operations tree * @@ -2931,6 +3280,11 @@ transformLockingClause(Query *qry, LockingClause *lc) (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("SELECT FOR UPDATE/SHARE cannot be applied to a function"))); break; + case RTE_VALUES: + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("SELECT FOR UPDATE/SHARE cannot be applied to VALUES"))); + break; default: elog(ERROR, "unrecognized RTE type: %d", (int) rte->rtekind); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 434f05bc4d8..4586c77f8a1 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.553 2006/07/31 01:16:37 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.554 2006/08/02 01:59:46 joe Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -173,7 +173,7 @@ static void doNegateFloat(Value *v); DropOwnedStmt ReassignOwnedStmt %type <node> select_no_parens select_with_parens select_clause - simple_select + simple_select values_clause %type <node> alter_column_default opclass_item alter_using %type <ival> add_drop @@ -238,7 +238,7 @@ static void doNegateFloat(Value *v); qualified_name_list any_name any_name_list any_operator expr_list attrs target_list update_target_list insert_column_list - insert_target_list def_list indirection opt_indirection + values_list def_list indirection opt_indirection group_clause TriggerFuncArgs select_limit opt_select_limit opclass_item_list transaction_mode_list_or_empty @@ -299,7 +299,7 @@ static void doNegateFloat(Value *v); %type <list> when_clause_list %type <ival> sub_type %type <list> OptCreateAs CreateAsList -%type <node> CreateAsElement +%type <node> CreateAsElement values_item %type <value> NumericOnly FloatOnly IntegerOnly %type <alias> alias_clause %type <sortby> sortby @@ -308,7 +308,7 @@ static void doNegateFloat(Value *v); %type <jexpr> joined_table %type <range> relation_expr %type <range> relation_expr_opt_alias -%type <target> target_el insert_target_el update_target_el insert_column_item +%type <target> target_el update_target_el insert_column_item %type <typnam> Typename SimpleTypename ConstTypename GenericType Numeric opt_float @@ -5342,40 +5342,23 @@ InsertStmt: ; insert_rest: - VALUES '(' insert_target_list ')' - { - $$ = makeNode(InsertStmt); - $$->cols = NIL; - $$->targetList = $3; - $$->selectStmt = NULL; - } - | DEFAULT VALUES - { - $$ = makeNode(InsertStmt); - $$->cols = NIL; - $$->targetList = NIL; - $$->selectStmt = NULL; - } - | SelectStmt + SelectStmt { $$ = makeNode(InsertStmt); $$->cols = NIL; - $$->targetList = NIL; $$->selectStmt = $1; } - | '(' insert_column_list ')' VALUES '(' insert_target_list ')' + | '(' insert_column_list ')' SelectStmt { $$ = makeNode(InsertStmt); $$->cols = $2; - $$->targetList = $6; - $$->selectStmt = NULL; + $$->selectStmt = $4; } - | '(' insert_column_list ')' SelectStmt + | DEFAULT VALUES { $$ = makeNode(InsertStmt); - $$->cols = $2; - $$->targetList = NIL; - $$->selectStmt = $4; + $$->cols = NIL; + $$->selectStmt = NULL; } ; @@ -5629,6 +5612,7 @@ simple_select: n->havingClause = $8; $$ = (Node *)n; } + | values_clause { $$ = $1; } | select_clause UNION opt_all select_clause { $$ = makeSetOp(SETOP_UNION, $3, $1, $4); @@ -5848,6 +5832,32 @@ locked_rels_list: | /* EMPTY */ { $$ = NIL; } ; + +values_clause: + VALUES '(' values_list ')' + { + SelectStmt *n = makeNode(SelectStmt); + n->valuesLists = list_make1($3); + $$ = (Node *) n; + } + | values_clause ',' '(' values_list ')' + { + SelectStmt *n = (SelectStmt *) $1; + n->valuesLists = lappend(n->valuesLists, $4); + $$ = (Node *) n; + } + ; + +values_list: values_item { $$ = list_make1($1); } + | values_list ',' values_item { $$ = lappend($1, $3); } + ; + +values_item: + a_expr { $$ = (Node *) $1; } + | DEFAULT { $$ = (Node *) makeNode(SetToDefault); } + ; + + /***************************************************************************** * * clauses common to all Optimizable Stmts: @@ -5937,10 +5947,17 @@ table_ref: relation_expr * However, it does seem like a good idea to emit * an error message that's better than "syntax error". */ - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("subquery in FROM must have an alias"), - errhint("For example, FROM (SELECT ...) [AS] foo."))); + if (IsA($1, SelectStmt) && + ((SelectStmt *) $1)->valuesLists) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("VALUES in FROM must have an alias"), + errhint("For example, FROM (VALUES ...) [AS] foo."))); + else + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subquery in FROM must have an alias"), + errhint("For example, FROM (SELECT ...) [AS] foo."))); $$ = NULL; } | select_with_parens alias_clause @@ -8176,30 +8193,6 @@ update_target_el: ; -insert_target_list: - insert_target_el { $$ = list_make1($1); } - | insert_target_list ',' insert_target_el { $$ = lappend($1, $3); } - ; - -insert_target_el: - a_expr - { - $$ = makeNode(ResTarget); - $$->name = NULL; - $$->indirection = NIL; - $$->val = (Node *)$1; - $$->location = @1; - } - | DEFAULT - { - $$ = makeNode(ResTarget); - $$->name = NULL; - $$->indirection = NIL; - $$->val = (Node *) makeNode(SetToDefault); - $$->location = @1; - } - ; - /***************************************************************************** * @@ -8656,7 +8649,6 @@ unreserved_keyword: | VACUUM | VALID | VALIDATOR - | VALUES | VARYING | VIEW | VOLATILE @@ -8715,6 +8707,7 @@ col_name_keyword: | TIMESTAMP | TREAT | TRIM + | VALUES | VARCHAR ; diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 11ef7e4f1e9..6a7117e98bf 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.195 2006/07/14 14:52:22 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.196 2006/08/02 01:59:46 joe Exp $ * *------------------------------------------------------------------------- */ @@ -1281,56 +1281,9 @@ static Node * transformRowExpr(ParseState *pstate, RowExpr *r) { RowExpr *newr = makeNode(RowExpr); - List *newargs = NIL; - ListCell *arg; /* Transform the field expressions */ - foreach(arg, r->args) - { - Node *e = (Node *) lfirst(arg); - Node *newe; - - /* - * Check for "something.*". Depending on the complexity of the - * "something", the star could appear as the last name in ColumnRef, - * or as the last indirection item in A_Indirection. - */ - if (IsA(e, ColumnRef)) - { - ColumnRef *cref = (ColumnRef *) e; - - if (strcmp(strVal(llast(cref->fields)), "*") == 0) - { - /* It is something.*, expand into multiple items */ - newargs = list_concat(newargs, - ExpandColumnRefStar(pstate, cref, - false)); - continue; - } - } - else if (IsA(e, A_Indirection)) - { - A_Indirection *ind = (A_Indirection *) e; - Node *lastitem = llast(ind->indirection); - - if (IsA(lastitem, String) && - strcmp(strVal(lastitem), "*") == 0) - { - /* It is something.*, expand into multiple items */ - newargs = list_concat(newargs, - ExpandIndirectionStar(pstate, ind, - false)); - continue; - } - } - - /* - * Not "something.*", so transform as a single expression - */ - newe = transformExpr(pstate, e); - newargs = lappend(newargs, newe); - } - newr->args = newargs; + newr->args = transformExpressionList(pstate, r->args); /* Barring later casting, we consider the type RECORD */ newr->row_typeid = RECORDOID; @@ -1526,6 +1479,15 @@ transformWholeRowRef(ParseState *pstate, char *schemaname, char *relname, sublevels_up); } break; + case RTE_VALUES: + toid = RECORDOID; + /* returns composite; same as relation case */ + result = (Node *) makeVar(vnum, + InvalidAttrNumber, + toid, + -1, + sublevels_up); + break; default: /* diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c index f1b6a45e7fc..9c1b58704e9 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_node.c,v 1.93 2006/07/14 14:52:22 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_node.c,v 1.94 2006/08/02 01:59:47 joe Exp $ * *------------------------------------------------------------------------- */ @@ -258,7 +258,7 @@ transformArraySubscripts(ParseState *pstate, /* * If doing an array store, coerce the source value to the right type. - * (This should agree with the coercion done by updateTargetListEntry.) + * (This should agree with the coercion done by transformAssignedExpr.) */ if (assignFrom != NULL) { diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 10f71712ff6..e9896be6349 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.123 2006/04/30 18:30:39 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.124 2006/08/02 01:59:47 joe Exp $ * *------------------------------------------------------------------------- */ @@ -941,6 +941,75 @@ addRangeTableEntryForFunction(ParseState *pstate, } /* + * Add an entry for a VALUES list to the pstate's range table (p_rtable). + * + * This is much like addRangeTableEntry() except that it makes a values RTE. + */ +RangeTblEntry * +addRangeTableEntryForValues(ParseState *pstate, + List *exprs, + Alias *alias, + bool inFromCl) +{ + RangeTblEntry *rte = makeNode(RangeTblEntry); + char *refname = alias ? alias->aliasname : pstrdup("*VALUES*"); + Alias *eref; + int numaliases; + int numcolumns; + + rte->rtekind = RTE_VALUES; + rte->relid = InvalidOid; + rte->subquery = NULL; + rte->values_lists = exprs; + rte->alias = alias; + + eref = alias ? copyObject(alias) : makeAlias(refname, NIL); + + /* fill in any unspecified alias columns */ + numcolumns = list_length((List *) linitial(exprs)); + numaliases = list_length(eref->colnames); + while (numaliases < numcolumns) + { + char attrname[64]; + + numaliases++; + snprintf(attrname, sizeof(attrname), "column%d", numaliases); + eref->colnames = lappend(eref->colnames, + makeString(pstrdup(attrname))); + } + if (numcolumns < numaliases) + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), + errmsg("VALUES lists \"%s\" have %d columns available but %d columns specified", + refname, numcolumns, 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 appropriate access rights. + * + * Subqueries are never checked for access rights. + *---------- + */ + rte->inh = false; /* never true for values RTEs */ + rte->inFromCl = inFromCl; + rte->requiredPerms = 0; + rte->checkAsUser = InvalidOid; + + /* + * Add completed RTE to pstate's range table list, but not to join list + * nor namespace --- caller must do that if appropriate. + */ + if (pstate != NULL) + pstate->p_rtable = lappend(pstate->p_rtable, rte); + + return rte; +} + +/* * Add an entry for a join to the pstate's range table (p_rtable). * * This is much like addRangeTableEntry() except that it makes a join RTE. @@ -1233,6 +1302,41 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, } } break; + case RTE_VALUES: + { + /* Values RTE */ + ListCell *aliasp_item = list_head(rte->eref->colnames); + ListCell *lc; + + varattno = 0; + foreach(lc, (List *) linitial(rte->values_lists)) + { + Node *col = (Node *) lfirst(lc); + + varattno++; + if (colnames) + { + /* Assume there is one alias per column */ + char *label = strVal(lfirst(aliasp_item)); + + *colnames = lappend(*colnames, + makeString(pstrdup(label))); + aliasp_item = lnext(aliasp_item); + } + + if (colvars) + { + Var *varnode; + + varnode = makeVar(rtindex, varattno, + exprType(col), + exprTypmod(col), + sublevels_up); + *colvars = lappend(*colvars, varnode); + } + } + } + break; case RTE_JOIN: { /* Join RTE */ @@ -1569,6 +1673,20 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, } } break; + case RTE_VALUES: + { + /* Values RTE --- get type info from first sublist */ + List *collist = (List *) linitial(rte->values_lists); + Node *col; + + if (attnum < 1 || attnum > list_length(collist)) + elog(ERROR, "values list %s does not have attribute %d", + rte->eref->aliasname, attnum); + col = (Node *) list_nth(collist, attnum-1); + *vartype = exprType(col); + *vartypmod = exprTypmod(col); + } + break; case RTE_JOIN: { /* @@ -1619,7 +1737,8 @@ get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum) } break; case RTE_SUBQUERY: - /* Subselect RTEs never have dropped columns */ + case RTE_VALUES: + /* Subselect and Values RTEs never have dropped columns */ result = false; break; case RTE_JOIN: diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 6f2e44f6e12..9258acccfbc 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.146 2006/07/14 14:52:22 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.147 2006/08/02 01:59:47 joe Exp $ * *------------------------------------------------------------------------- */ @@ -42,7 +42,11 @@ static Node *transformAssignmentIndirection(ParseState *pstate, ListCell *indirection, Node *rhs, int location); +static List *ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, + bool targetlist); static List *ExpandAllTables(ParseState *pstate); +static List *ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind, + bool targetlist); static int FigureColnameInternal(Node *node, char **name); @@ -152,6 +156,69 @@ transformTargetList(ParseState *pstate, List *targetlist) /* + * transformExpressionList() + * + * This is the identical transformation to transformTargetList, except that + * the input list elements are bare expressions without ResTarget decoration, + * and the output elements are likewise just expressions without TargetEntry + * decoration. We use this for ROW() and VALUES() constructs. + */ +List * +transformExpressionList(ParseState *pstate, List *exprlist) +{ + List *result = NIL; + ListCell *lc; + + foreach(lc, exprlist) + { + Node *e = (Node *) lfirst(lc); + + /* + * Check for "something.*". Depending on the complexity of the + * "something", the star could appear as the last name in ColumnRef, + * or as the last indirection item in A_Indirection. + */ + if (IsA(e, ColumnRef)) + { + ColumnRef *cref = (ColumnRef *) e; + + if (strcmp(strVal(llast(cref->fields)), "*") == 0) + { + /* It is something.*, expand into multiple items */ + result = list_concat(result, + ExpandColumnRefStar(pstate, cref, + false)); + continue; + } + } + else if (IsA(e, A_Indirection)) + { + A_Indirection *ind = (A_Indirection *) e; + Node *lastitem = llast(ind->indirection); + + if (IsA(lastitem, String) && + strcmp(strVal(lastitem), "*") == 0) + { + /* It is something.*, expand into multiple items */ + result = list_concat(result, + ExpandIndirectionStar(pstate, ind, + false)); + continue; + } + } + + /* + * Not "something.*", so transform as a single expression + */ + result = lappend(result, + transformExpr(pstate, e)); + } + + return result; +} + + +/* * markTargetListOrigins() * Mark targetlist columns that are simple Vars with the source * table's OID and column number. @@ -229,6 +296,7 @@ markTargetListOrigin(ParseState *pstate, TargetEntry *tle, break; case RTE_SPECIAL: case RTE_FUNCTION: + case RTE_VALUES: /* not a simple relation, leave it unmarked */ break; } @@ -236,23 +304,26 @@ markTargetListOrigin(ParseState *pstate, TargetEntry *tle, /* - * updateTargetListEntry() - * This is used in INSERT and UPDATE statements only. It prepares a - * TargetEntry for assignment to a column of the target table. + * transformAssignedExpr() + * This is used in INSERT and UPDATE statements only. It prepares an + * expression for assignment to a column of the target table. * This includes coercing the given value to the target column's type * (if necessary), and dealing with any subfield names or subscripts - * attached to the target column itself. + * attached to the target column itself. The input expression has + * already been through transformExpr(). * * pstate parse state - * tle target list entry to be modified + * expr expression to be modified * colname target column name (ie, name of attribute to be assigned to) * attrno target attribute number * indirection subscripts/field names for target column, if any - * location error cursor position (should point at column name), or -1 + * location error cursor position, or -1 + * + * Returns the modified expression. */ -void -updateTargetListEntry(ParseState *pstate, - TargetEntry *tle, +Expr * +transformAssignedExpr(ParseState *pstate, + Expr *expr, char *colname, int attrno, List *indirection, @@ -281,9 +352,9 @@ updateTargetListEntry(ParseState *pstate, * or array element with DEFAULT, since there can't be any default for * portions of a column. */ - if (tle->expr && IsA(tle->expr, SetToDefault)) + if (expr && IsA(expr, SetToDefault)) { - SetToDefault *def = (SetToDefault *) tle->expr; + SetToDefault *def = (SetToDefault *) expr; def->typeId = attrtype; def->typeMod = attrtypmod; @@ -303,7 +374,7 @@ updateTargetListEntry(ParseState *pstate, } /* Now we can use exprType() safely. */ - type_id = exprType((Node *) tle->expr); + type_id = exprType((Node *) expr); /* * If there is indirection on the target column, prepare an array or @@ -334,7 +405,7 @@ updateTargetListEntry(ParseState *pstate, attrno); } - tle->expr = (Expr *) + expr = (Expr *) transformAssignmentIndirection(pstate, colVar, colname, @@ -342,7 +413,7 @@ updateTargetListEntry(ParseState *pstate, attrtype, attrtypmod, list_head(indirection), - (Node *) tle->expr, + (Node *) expr, location); } else @@ -351,13 +422,13 @@ updateTargetListEntry(ParseState *pstate, * For normal non-qualified target column, do type checking and * coercion. */ - tle->expr = (Expr *) + expr = (Expr *) coerce_to_target_type(pstate, - (Node *) tle->expr, type_id, + (Node *) expr, type_id, attrtype, attrtypmod, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST); - if (tle->expr == NULL) + if (expr == NULL) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("column \"%s\" is of type %s" @@ -369,6 +440,41 @@ updateTargetListEntry(ParseState *pstate, parser_errposition(pstate, location))); } + return expr; +} + + +/* + * updateTargetListEntry() + * This is used in UPDATE statements only. It prepares an UPDATE + * TargetEntry for assignment to a column of the target table. + * This includes coercing the given value to the target column's type + * (if necessary), and dealing with any subfield names or subscripts + * attached to the target column itself. + * + * pstate parse state + * tle target list entry to be modified + * colname target column name (ie, name of attribute to be assigned to) + * attrno target attribute number + * indirection subscripts/field names for target column, if any + * location error cursor position (should point at column name), or -1 + */ +void +updateTargetListEntry(ParseState *pstate, + TargetEntry *tle, + char *colname, + int attrno, + List *indirection, + int location) +{ + /* Fix up expression as needed */ + tle->expr = transformAssignedExpr(pstate, + tle->expr, + colname, + attrno, + indirection, + location); + /* * Set the resno to identify the target column --- the rewriter and * planner depend on this. We also set the resname to identify the target @@ -379,6 +485,7 @@ updateTargetListEntry(ParseState *pstate, tle->resname = colname; } + /* * Process indirection (field selection or subscripting) of the target * column in INSERT/UPDATE. This routine recurses for multiple levels @@ -701,9 +808,10 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos) * This handles the case where '*' appears as the last or only name in a * ColumnRef. The code is shared between the case of foo.* at the top level * in a SELECT target list (where we want TargetEntry nodes in the result) - * and foo.* in a ROW() construct (where we want just bare expressions). + * and foo.* in a ROW() or VALUES() construct (where we want just bare + * expressions). */ -List * +static List * ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, bool targetlist) { @@ -836,9 +944,9 @@ ExpandAllTables(ParseState *pstate) * This handles the case where '*' appears as the last item in A_Indirection. * The code is shared between the case of foo.* at the top level in a SELECT * target list (where we want TargetEntry nodes in the result) and foo.* in - * a ROW() construct (where we want just bare expressions). + * a ROW() or VALUES() construct (where we want just bare expressions). */ -List * +static List * ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind, bool targetlist) { @@ -996,11 +1104,12 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup) { case RTE_RELATION: case RTE_SPECIAL: + case RTE_VALUES: /* - * This case should not occur: a column of a table shouldn't have - * type RECORD. Fall through and fail (most likely) at the - * bottom. + * This case should not occur: a column of a table or values list + * shouldn't have type RECORD. Fall through and fail + * (most likely) at the bottom. */ break; case RTE_SUBQUERY: diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c index b6eac7417f8..a12aea6c38a 100644 --- a/src/backend/parser/parse_type.c +++ b/src/backend/parser/parse_type.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.82 2006/07/14 14:52:22 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.83 2006/08/02 01:59:47 joe Exp $ * *------------------------------------------------------------------------- */ @@ -426,6 +426,7 @@ parseTypeString(const char *str, Oid *type_id, int32 *typmod) stmt->whereClause != NULL || stmt->groupClause != NIL || stmt->havingClause != NULL || + stmt->valuesLists != NIL || stmt->sortClause != NIL || stmt->limitOffset != NULL || stmt->limitCount != NULL || |