1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-09 06:21:09 +03:00

Add support for multi-row VALUES clauses as part of INSERT statements

(e.g. "INSERT ... VALUES (...), (...), ...") and elsewhere as allowed
by the spec. (e.g. similar to a FROM clause subselect). initdb required.
Joe Conway and Tom Lane.
This commit is contained in:
Joe Conway
2006-08-02 01:59:48 +00:00
parent d307c428cb
commit 9caafda579
40 changed files with 1877 additions and 313 deletions

View File

@@ -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,19 +517,30 @@ 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
@@ -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,82 +678,220 @@ 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 if (list_length(selectStmt->valuesLists) > 1)
{
/*
* 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.
*/
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
{
/*
* For INSERT ... VALUES, transform the given list of values to form a
* targetlist for the INSERT.
/*----------
* 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.
*----------
*/
qry->targetList = transformTargetList(pstate, stmt->targetList);
List *valuesLists = selectStmt->valuesLists;
Assert(list_length(valuesLists) == 1);
/* Do basic expression transformation (same as a ROW() expr) */
exprList = transformExpressionList(pstate,
(List *) linitial(valuesLists));
/* Prepare row for assignment to target table */
exprList = transformInsertRow(pstate, exprList,
stmt->cols,
icolumns, attrnos);
}
/*
* Now we are done with SELECT-like processing, and can get on with
* transforming the target list to match the INSERT target columns.
*/
/* 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;
/* Validate stmt->cols list, or build default list if no list given */
icolumns = checkInsertTargets(pstate, stmt->cols, &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
@@ -1933,6 +2101,187 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
return qry;
}
/*
* 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);

View File

@@ -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,41 +5342,24 @@ InsertStmt:
;
insert_rest:
VALUES '(' insert_target_list ')'
SelectStmt
{
$$ = makeNode(InsertStmt);
$$->cols = NIL;
$$->targetList = $3;
$$->selectStmt = NULL;
}
| DEFAULT VALUES
{
$$ = makeNode(InsertStmt);
$$->cols = NIL;
$$->targetList = NIL;
$$->selectStmt = NULL;
}
| SelectStmt
{
$$ = makeNode(InsertStmt);
$$->cols = NIL;
$$->targetList = NIL;
$$->selectStmt = $1;
}
| '(' insert_column_list ')' VALUES '(' insert_target_list ')'
{
$$ = makeNode(InsertStmt);
$$->cols = $2;
$$->targetList = $6;
$$->selectStmt = NULL;
}
| '(' insert_column_list ')' SelectStmt
{
$$ = makeNode(InsertStmt);
$$->cols = $2;
$$->targetList = NIL;
$$->selectStmt = $4;
}
| DEFAULT VALUES
{
$$ = makeNode(InsertStmt);
$$->cols = NIL;
$$->selectStmt = NULL;
}
;
insert_column_list:
@@ -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
;

View File

@@ -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:
/*

View File

@@ -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)
{

View File

@@ -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 $
*
*-------------------------------------------------------------------------
*/
@@ -940,6 +940,75 @@ addRangeTableEntryForFunction(ParseState *pstate,
return rte;
}
/*
* 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).
*
@@ -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:

View File

@@ -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);
@@ -151,6 +155,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
@@ -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:

View File

@@ -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 ||