[ WITH [ RECURSIVE ] <replaceable class="parameter">with_query</replaceable> [, ...] ]
UPDATE [ ONLY ] <replaceable class="PARAMETER">table_name</replaceable> [ * ] [ [ AS ] <replaceable class="parameter">alias</replaceable> ]
SET { <replaceable class="PARAMETER">column_name</replaceable> = { <replaceable class="PARAMETER">expression</replaceable> | DEFAULT } |
- ( <replaceable class="PARAMETER">column_name</replaceable> [, ...] ) = ( { <replaceable class="PARAMETER">expression</replaceable> | DEFAULT } [, ...] ) |
+ ( <replaceable class="PARAMETER">column_name</replaceable> [, ...] ) = [ ROW ] ( { <replaceable class="PARAMETER">expression</replaceable> | DEFAULT } [, ...] ) |
( <replaceable class="PARAMETER">column_name</replaceable> [, ...] ) = ( <replaceable class="PARAMETER">sub-SELECT</replaceable> )
} [, ...]
[ FROM <replaceable class="PARAMETER">from_list</replaceable> ]
<para>
According to the standard, the source value for a parenthesized sub-list of
- column names can be any row-valued expression yielding the correct number
- of columns. <productname>PostgreSQL</productname> only allows the source
- value to be a parenthesized list of expressions (a row constructor) or a
- sub-<literal>SELECT</>. An individual column's updated value can be
- specified as <literal>DEFAULT</> in the row-constructor case, but not
- inside a sub-<literal>SELECT</>.
+ target column names can be any row-valued expression yielding the correct
+ number of columns. <productname>PostgreSQL</productname> only allows the
+ source value to be a <link linkend="sql-syntax-row-constructors">row
+ constructor</link> or a sub-<literal>SELECT</>. An individual column's
+ updated value can be specified as <literal>DEFAULT</> in the
+ row-constructor case, but not inside a sub-<literal>SELECT</>.
</para>
</refsect1>
</refentry>
{
List *sublist = (List *) lfirst(lc);
- /* Do basic expression transformation (same as a ROW() expr) */
- sublist = transformExpressionList(pstate, sublist, EXPR_KIND_VALUES);
+ /*
+ * Do basic expression transformation (same as a ROW() expr, but
+ * allow SetToDefault at top level)
+ */
+ sublist = transformExpressionList(pstate, sublist,
+ EXPR_KIND_VALUES, true);
/*
* All the sublists must be the same length, *after*
Assert(list_length(valuesLists) == 1);
Assert(selectStmt->intoClause == NULL);
- /* Do basic expression transformation (same as a ROW() expr) */
+ /*
+ * Do basic expression transformation (same as a ROW() expr, but allow
+ * SetToDefault at top level)
+ */
exprList = transformExpressionList(pstate,
(List *) linitial(valuesLists),
- EXPR_KIND_VALUES);
+ EXPR_KIND_VALUES,
+ true);
/* Prepare row for assignment to target table */
exprList = transformInsertRow(pstate, exprList,
}
/*
- * For each row of VALUES, transform the raw expressions. This is also a
- * handy place to reject DEFAULT nodes, which the grammar allows for
- * simplicity.
+ * For each row of VALUES, transform the raw expressions.
*
* Note that the intermediate representation we build is column-organized
* not row-organized. That simplifies the type and collation processing
{
List *sublist = (List *) lfirst(lc);
- /* Do basic expression transformation (same as a ROW() expr) */
- sublist = transformExpressionList(pstate, sublist, EXPR_KIND_VALUES);
+ /*
+ * Do basic expression transformation (same as a ROW() expr, but here
+ * we disallow SetToDefault)
+ */
+ sublist = transformExpressionList(pstate, sublist,
+ EXPR_KIND_VALUES, false);
/*
* All the sublists must be the same length, *after* transformation
exprLocation((Node *) sublist))));
}
- /* Check for DEFAULT and build per-column expression lists */
+ /* Build per-column expression lists */
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"),
- parser_errposition(pstate, exprLocation(col))));
colexprs[i] = lappend(colexprs[i], col);
i++;
}
qualified_name_list any_name any_name_list type_name_list
any_operator expr_list attrs
target_list opt_target_list insert_column_list set_target_list
- set_clause_list set_clause multiple_set_clause
- ctext_expr_list ctext_row def_list operator_def_list indirection opt_indirection
+ set_clause_list set_clause
+ def_list operator_def_list indirection opt_indirection
reloption_list group_clause TriggerFuncArgs select_limit
opt_select_limit opclass_item_list opclass_drop_list
opclass_purpose opt_opfamily transaction_mode_list_or_empty
%type <node> case_expr case_arg when_clause case_default
%type <list> when_clause_list
%type <ival> sub_type
-%type <node> ctext_expr
%type <value> NumericOnly
%type <list> NumericOnly_list
%type <alias> alias_clause opt_alias_clause
%type <range> relation_expr
%type <range> relation_expr_opt_alias
%type <node> tablesample_clause opt_repeatable_clause
-%type <target> target_el single_set_clause set_target insert_column_item
+%type <target> target_el set_target insert_column_item
%type <str> generic_option_name
%type <node> generic_option_arg
;
set_clause:
- single_set_clause { $$ = list_make1($1); }
- | multiple_set_clause { $$ = $1; }
- ;
-
-single_set_clause:
- set_target '=' ctext_expr
- {
- $$ = $1;
- $$->val = (Node *) $3;
- }
- ;
-
-/*
- * Ideally, we'd accept any row-valued a_expr as RHS of a multiple_set_clause.
- * However, per SQL spec the row-constructor case must allow DEFAULT as a row
- * member, and it's pretty unclear how to do that (unless perhaps we allow
- * DEFAULT in any a_expr and let parse analysis sort it out later?). For the
- * moment, the planner/executor only support a subquery as a multiassignment
- * source anyhow, so we need only accept ctext_row and subqueries here.
- */
-multiple_set_clause:
- '(' set_target_list ')' '=' ctext_row
+ set_target '=' a_expr
{
- ListCell *col_cell;
- ListCell *val_cell;
-
- /*
- * Break the ctext_row apart, merge individual expressions
- * into the destination ResTargets. This is semantically
- * equivalent to, and much cheaper to process than, the
- * general case.
- */
- if (list_length($2) != list_length($5))
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("number of columns does not match number of values"),
- parser_errposition(@5)));
- forboth(col_cell, $2, val_cell, $5)
- {
- ResTarget *res_col = (ResTarget *) lfirst(col_cell);
- Node *res_val = (Node *) lfirst(val_cell);
-
- res_col->val = res_val;
- }
-
- $$ = $2;
+ $1->val = (Node *) $3;
+ $$ = list_make1($1);
}
- | '(' set_target_list ')' '=' select_with_parens
+ | '(' set_target_list ')' '=' a_expr
{
- SubLink *sl = makeNode(SubLink);
int ncolumns = list_length($2);
int i = 1;
ListCell *col_cell;
- /* First, convert bare SelectStmt into a SubLink */
- sl->subLinkType = MULTIEXPR_SUBLINK;
- sl->subLinkId = 0; /* will be assigned later */
- sl->testexpr = NULL;
- sl->operName = NIL;
- sl->subselect = $5;
- sl->location = @5;
-
/* Create a MultiAssignRef source for each target */
foreach(col_cell, $2)
{
ResTarget *res_col = (ResTarget *) lfirst(col_cell);
MultiAssignRef *r = makeNode(MultiAssignRef);
- r->source = (Node *) sl;
+ r->source = (Node *) $5;
r->colno = i;
r->ncolumns = ncolumns;
res_col->val = (Node *) r;
;
+/*
+ * We should allow ROW '(' expr_list ')' too, but that seems to require
+ * making VALUES a fully reserved word, which will probably break more apps
+ * than allowing the noise-word is worth.
+ */
values_clause:
- VALUES ctext_row
+ VALUES '(' expr_list ')'
{
SelectStmt *n = makeNode(SelectStmt);
- n->valuesLists = list_make1($2);
+ n->valuesLists = list_make1($3);
$$ = (Node *) n;
}
- | values_clause ',' ctext_row
+ | values_clause ',' '(' expr_list ')'
{
SelectStmt *n = (SelectStmt *) $1;
- n->valuesLists = lappend(n->valuesLists, $3);
+ n->valuesLists = lappend(n->valuesLists, $4);
$$ = (Node *) n;
}
;
list_make1($1), @2),
@2);
}
+ | DEFAULT
+ {
+ /*
+ * The SQL spec only allows DEFAULT in "contextually typed
+ * expressions", but for us, it's easier to allow it in
+ * any a_expr and then throw error during parse analysis
+ * if it's in an inappropriate context. This way also
+ * lets us say something smarter than "syntax error".
+ */
+ SetToDefault *n = makeNode(SetToDefault);
+ /* parse analysis will fill in the rest */
+ n->location = @1;
+ $$ = (Node *)n;
+ }
;
/*
| /*EMPTY*/
;
-/*
- * The SQL spec defines "contextually typed value expressions" and
- * "contextually typed row value constructors", which for our purposes
- * are the same as "a_expr" and "row" except that DEFAULT can appear at
- * the top level.
- */
-
-ctext_expr:
- a_expr { $$ = (Node *) $1; }
- | DEFAULT
- {
- SetToDefault *n = makeNode(SetToDefault);
- n->location = @1;
- $$ = (Node *) n;
- }
- ;
-
-ctext_expr_list:
- ctext_expr { $$ = list_make1($1); }
- | ctext_expr_list ',' ctext_expr { $$ = lappend($1, $3); }
- ;
-
-/*
- * We should allow ROW '(' ctext_expr_list ')' too, but that seems to require
- * making VALUES a fully reserved word, which will probably break more apps
- * than allowing the noise-word is worth.
- */
-ctext_row: '(' ctext_expr_list ')' { $$ = $2; }
- ;
-
/*****************************************************************************
*
static Node *transformSubLink(ParseState *pstate, SubLink *sublink);
static Node *transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
Oid array_type, Oid element_type, int32 typmod);
-static Node *transformRowExpr(ParseState *pstate, RowExpr *r);
+static Node *transformRowExpr(ParseState *pstate, RowExpr *r, bool allowDefault);
static Node *transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c);
static Node *transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m);
static Node *transformSQLValueFunction(ParseState *pstate,
break;
case T_RowExpr:
- result = transformRowExpr(pstate, (RowExpr *) expr);
+ result = transformRowExpr(pstate, (RowExpr *) expr, false);
break;
case T_CoalesceExpr:
break;
/*
- * CaseTestExpr and SetToDefault don't require any processing;
- * they are only injected into parse trees in fully-formed state.
+ * In all places where DEFAULT is legal, the caller should have
+ * processed it rather than passing it to transformExpr().
+ */
+ case T_SetToDefault:
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("DEFAULT is not allowed in this context"),
+ parser_errposition(pstate,
+ ((SetToDefault *) expr)->location)));
+ break;
+
+ /*
+ * CaseTestExpr doesn't require any processing; it is only
+ * injected into parse trees in a fully-formed state.
*
* Ordinarily we should not see a Var here, but it is convenient
* for transformJoinUsingClause() to create untransformed operator
* references, which seems expensively pointless. So allow it.
*/
case T_CaseTestExpr:
- case T_SetToDefault:
case T_Var:
{
result = (Node *) expr;
transformMultiAssignRef(ParseState *pstate, MultiAssignRef *maref)
{
SubLink *sublink;
+ RowExpr *rexpr;
Query *qtree;
TargetEntry *tle;
- Param *param;
/* We should only see this in first-stage processing of UPDATE tlists */
Assert(pstate->p_expr_kind == EXPR_KIND_UPDATE_SOURCE);
/* We only need to transform the source if this is the first column */
if (maref->colno == 1)
{
- sublink = (SubLink *) transformExprRecurse(pstate, maref->source);
- /* Currently, the grammar only allows a SubLink as source */
- Assert(IsA(sublink, SubLink));
- Assert(sublink->subLinkType == MULTIEXPR_SUBLINK);
- qtree = (Query *) sublink->subselect;
- Assert(IsA(qtree, Query));
-
- /* Check subquery returns required number of columns */
- if (count_nonjunk_tlist_entries(qtree->targetList) != maref->ncolumns)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("number of columns does not match number of values"),
- parser_errposition(pstate, sublink->location)));
-
/*
- * Build a resjunk tlist item containing the MULTIEXPR SubLink, and
- * add it to pstate->p_multiassign_exprs, whence it will later get
- * appended to the completed targetlist. We needn't worry about
- * selecting a resno for it; transformUpdateStmt will do that.
+ * For now, we only allow EXPR SubLinks and RowExprs as the source of
+ * an UPDATE multiassignment. This is sufficient to cover interesting
+ * cases; at worst, someone would have to write (SELECT * FROM expr)
+ * to expand a composite-returning expression of another form.
*/
- tle = makeTargetEntry((Expr *) sublink, 0, NULL, true);
- pstate->p_multiassign_exprs = lappend(pstate->p_multiassign_exprs, tle);
+ if (IsA(maref->source, SubLink) &&
+ ((SubLink *) maref->source)->subLinkType == EXPR_SUBLINK)
+ {
+ /* Relabel it as a MULTIEXPR_SUBLINK */
+ sublink = (SubLink *) maref->source;
+ sublink->subLinkType = MULTIEXPR_SUBLINK;
+ /* And transform it */
+ sublink = (SubLink *) transformExprRecurse(pstate,
+ (Node *) sublink);
+
+ qtree = (Query *) sublink->subselect;
+ Assert(IsA(qtree, Query));
+
+ /* Check subquery returns required number of columns */
+ if (count_nonjunk_tlist_entries(qtree->targetList) != maref->ncolumns)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("number of columns does not match number of values"),
+ parser_errposition(pstate, sublink->location)));
- /*
- * Assign a unique-within-this-targetlist ID to the MULTIEXPR SubLink.
- * We can just use its position in the p_multiassign_exprs list.
- */
- sublink->subLinkId = list_length(pstate->p_multiassign_exprs);
+ /*
+ * Build a resjunk tlist item containing the MULTIEXPR SubLink,
+ * and add it to pstate->p_multiassign_exprs, whence it will later
+ * get appended to the completed targetlist. We needn't worry
+ * about selecting a resno for it; transformUpdateStmt will do
+ * that.
+ */
+ tle = makeTargetEntry((Expr *) sublink, 0, NULL, true);
+ pstate->p_multiassign_exprs = lappend(pstate->p_multiassign_exprs,
+ tle);
+
+ /*
+ * Assign a unique-within-this-targetlist ID to the MULTIEXPR
+ * SubLink. We can just use its position in the
+ * p_multiassign_exprs list.
+ */
+ sublink->subLinkId = list_length(pstate->p_multiassign_exprs);
+ }
+ else if (IsA(maref->source, RowExpr))
+ {
+ /* Transform the RowExpr, allowing SetToDefault items */
+ rexpr = (RowExpr *) transformRowExpr(pstate,
+ (RowExpr *) maref->source,
+ true);
+
+ /* Check it returns required number of columns */
+ if (list_length(rexpr->args) != maref->ncolumns)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("number of columns does not match number of values"),
+ parser_errposition(pstate, rexpr->location)));
+
+ /*
+ * Temporarily append it to p_multiassign_exprs, so we can get it
+ * back when we come back here for additional columns.
+ */
+ tle = makeTargetEntry((Expr *) rexpr, 0, NULL, true);
+ pstate->p_multiassign_exprs = lappend(pstate->p_multiassign_exprs,
+ tle);
+ }
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("source for a multiple-column UPDATE item must be a sub-SELECT or ROW() expression"),
+ parser_errposition(pstate, exprLocation(maref->source))));
}
else
{
/*
* Second or later column in a multiassignment. Re-fetch the
- * transformed query, which we assume is still the last entry in
- * p_multiassign_exprs.
+ * transformed SubLink or RowExpr, which we assume is still the last
+ * entry in p_multiassign_exprs.
*/
Assert(pstate->p_multiassign_exprs != NIL);
tle = (TargetEntry *) llast(pstate->p_multiassign_exprs);
+ }
+
+ /*
+ * Emit the appropriate output expression for the current column
+ */
+ if (IsA(tle->expr, SubLink))
+ {
+ Param *param;
+
sublink = (SubLink *) tle->expr;
- Assert(IsA(sublink, SubLink));
Assert(sublink->subLinkType == MULTIEXPR_SUBLINK);
qtree = (Query *) sublink->subselect;
Assert(IsA(qtree, Query));
+
+ /* Build a Param representing the current subquery output column */
+ tle = (TargetEntry *) list_nth(qtree->targetList, maref->colno - 1);
+ Assert(!tle->resjunk);
+
+ param = makeNode(Param);
+ param->paramkind = PARAM_MULTIEXPR;
+ param->paramid = (sublink->subLinkId << 16) | maref->colno;
+ param->paramtype = exprType((Node *) tle->expr);
+ param->paramtypmod = exprTypmod((Node *) tle->expr);
+ param->paramcollid = exprCollation((Node *) tle->expr);
+ param->location = exprLocation((Node *) tle->expr);
+
+ return (Node *) param;
}
- /* Build a Param representing the appropriate subquery output column */
- tle = (TargetEntry *) list_nth(qtree->targetList, maref->colno - 1);
- Assert(!tle->resjunk);
+ if (IsA(tle->expr, RowExpr))
+ {
+ Node *result;
+
+ rexpr = (RowExpr *) tle->expr;
- param = makeNode(Param);
- param->paramkind = PARAM_MULTIEXPR;
- param->paramid = (sublink->subLinkId << 16) | maref->colno;
- param->paramtype = exprType((Node *) tle->expr);
- param->paramtypmod = exprTypmod((Node *) tle->expr);
- param->paramcollid = exprCollation((Node *) tle->expr);
- param->location = exprLocation((Node *) tle->expr);
+ /* Just extract and return the next element of the RowExpr */
+ result = (Node *) list_nth(rexpr->args, maref->colno - 1);
+
+ /*
+ * If we're at the last column, delete the RowExpr from
+ * p_multiassign_exprs; we don't need it anymore, and don't want it in
+ * the finished UPDATE tlist.
+ */
+ if (maref->colno == maref->ncolumns)
+ pstate->p_multiassign_exprs =
+ list_delete_ptr(pstate->p_multiassign_exprs, tle);
+
+ return result;
+ }
- return (Node *) param;
+ elog(ERROR, "unexpected expr type in multiassign list");
+ return NULL; /* keep compiler quiet */
}
static Node *
}
static Node *
-transformRowExpr(ParseState *pstate, RowExpr *r)
+transformRowExpr(ParseState *pstate, RowExpr *r, bool allowDefault)
{
RowExpr *newr;
char fname[16];
newr = makeNode(RowExpr);
/* Transform the field expressions */
- newr->args = transformExpressionList(pstate, r->args, pstate->p_expr_kind);
+ newr->args = transformExpressionList(pstate, r->args,
+ pstate->p_expr_kind, allowDefault);
/* Barring later casting, we consider the type RECORD */
newr->row_typeid = RECORDOID;
{
/* Transform the node if caller didn't do it already */
if (expr == NULL)
- expr = transformExpr(pstate, node, exprKind);
+ {
+ /*
+ * If it's a SetToDefault node and we should allow that, pass it
+ * through unmodified. (transformExpr will throw the appropriate
+ * error if we're disallowing it.)
+ */
+ if (exprKind == EXPR_KIND_UPDATE_SOURCE && IsA(node, SetToDefault))
+ expr = node;
+ else
+ expr = transformExpr(pstate, node, exprKind);
+ }
if (colname == NULL && !resjunk)
{
* 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.
+ *
+ * exprKind is not enough to tell us whether to allow SetToDefault, so
+ * an additional flag is needed for that.
*/
List *
transformExpressionList(ParseState *pstate, List *exprlist,
- ParseExprKind exprKind)
+ ParseExprKind exprKind, bool allowDefault)
{
List *result = NIL;
ListCell *lc;
}
/*
- * Not "something.*", so transform as a single expression
+ * Not "something.*", so transform as a single expression. If it's a
+ * SetToDefault node and we should allow that, pass it through
+ * unmodified. (transformExpr will throw the appropriate error if
+ * we're disallowing it.)
*/
- result = lappend(result,
- transformExpr(pstate, e, exprKind));
+ if (allowDefault && IsA(e, SetToDefault))
+ /* do nothing */ ;
+ else
+ e = transformExpr(pstate, e, exprKind);
+
+ result = lappend(result, e);
}
/* Shouldn't have any multiassign items here */
extern List *transformTargetList(ParseState *pstate, List *targetlist,
ParseExprKind exprKind);
extern List *transformExpressionList(ParseState *pstate, List *exprlist,
- ParseExprKind exprKind);
+ ParseExprKind exprKind, bool allowDefault);
extern void markTargetListOrigins(ParseState *pstate, List *targetlist);
extern TargetEntry *transformTargetEntry(ParseState *pstate,
Node *node, Node *expr, ParseExprKind exprKind,
| |
(4 rows)
--- these should work, but don't yet:
-UPDATE update_test SET (a,b) = (v.*) FROM (VALUES(21, 100)) AS v(i, j)
+-- *-expansion should work in this context:
+UPDATE update_test SET (a,b) = ROW(v.*) FROM (VALUES(21, 100)) AS v(i, j)
WHERE update_test.a = v.i;
-ERROR: number of columns does not match number of values
-LINE 1: UPDATE update_test SET (a,b) = (v.*) FROM (VALUES(21, 100)) ...
- ^
-UPDATE update_test SET (a,b) = ROW(v.*) FROM (VALUES(21, 101)) AS v(i, j)
+-- you might expect this to work, but syntactically it's not a RowExpr:
+UPDATE update_test SET (a,b) = (v.*) FROM (VALUES(21, 101)) AS v(i, j)
WHERE update_test.a = v.i;
-ERROR: syntax error at or near "ROW"
-LINE 1: UPDATE update_test SET (a,b) = ROW(v.*) FROM (VALUES(21, 101...
- ^
+ERROR: source for a multiple-column UPDATE item must be a sub-SELECT or ROW() expression
+LINE 1: UPDATE update_test SET (a,b) = (v.*) FROM (VALUES(21, 101)) ...
+ ^
-- if an alias for the target table is specified, don't allow references
-- to the original table name
UPDATE update_test AS t SET b = update_test.b + 10 WHERE t.a = 10;
SELECT a, b, char_length(c) FROM update_test;
a | b | char_length
----+-----+-------------
- 21 | 101 |
| |
+ 21 | 100 |
41 | 12 | 10000
42 | 12 | 10000
(4 rows)
UPDATE update_test SET (b,a) = (select a+1,b from update_test where a = 1000)
WHERE a = 11;
SELECT * FROM update_test;
--- these should work, but don't yet:
-UPDATE update_test SET (a,b) = (v.*) FROM (VALUES(21, 100)) AS v(i, j)
+-- *-expansion should work in this context:
+UPDATE update_test SET (a,b) = ROW(v.*) FROM (VALUES(21, 100)) AS v(i, j)
WHERE update_test.a = v.i;
-UPDATE update_test SET (a,b) = ROW(v.*) FROM (VALUES(21, 101)) AS v(i, j)
+-- you might expect this to work, but syntactically it's not a RowExpr:
+UPDATE update_test SET (a,b) = (v.*) FROM (VALUES(21, 101)) AS v(i, j)
WHERE update_test.a = v.i;
-- if an alias for the target table is specified, don't allow references