diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2014-06-18 13:22:25 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2014-06-18 13:22:34 -0400 |
commit | 8f889b1083f38f4f5b3bd3512008a3f60e939244 (patch) | |
tree | 68c2e242c88245ea0d3b9329e1e27c78a8e70eaf /src/backend/rewrite/rewriteManip.c | |
parent | 230ba02d855de7fac31bfb6af25ebd4ae052640b (diff) |
Implement UPDATE tab SET (col1,col2,...) = (SELECT ...), ...
This SQL-standard feature allows a sub-SELECT yielding multiple columns
(but only one row) to be used to compute the new values of several columns
to be updated. While the same results can be had with an independent
sub-SELECT per column, such a workaround can require a great deal of
duplicated computation.
The standard actually says that the source for a multi-column assignment
could be any row-valued expression. The implementation used here is
tightly tied to our existing sub-SELECT support and can't handle other
cases; the Bison grammar would have some issues with them too. However,
I don't feel too bad about this since other cases can be converted into
sub-SELECTs. For instance, "SET (a,b,c) = row_valued_function(x)" could
be written "SET (a,b,c) = (SELECT * FROM row_valued_function(x))".
Diffstat (limited to 'src/backend/rewrite/rewriteManip.c')
-rw-r--r-- | src/backend/rewrite/rewriteManip.c | 35 |
1 files changed, 35 insertions, 0 deletions
diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c index bcf3bd9243a..fb203146b13 100644 --- a/src/backend/rewrite/rewriteManip.c +++ b/src/backend/rewrite/rewriteManip.c @@ -281,6 +281,26 @@ checkExprHasSubLink_walker(Node *node, void *context) return expression_tree_walker(node, checkExprHasSubLink_walker, context); } +/* + * Check for MULTIEXPR Param within expression tree + * + * We intentionally don't descend into SubLinks: only Params at the current + * query level are of interest. + */ +static bool +contains_multiexpr_param(Node *node, void *context) +{ + if (node == NULL) + return false; + if (IsA(node, Param)) + { + if (((Param *) node)->paramkind == PARAM_MULTIEXPR) + return true; /* abort the tree traversal and return true */ + return false; + } + return expression_tree_walker(node, contains_multiexpr_param, context); +} + /* * OffsetVarNodes - adjust Vars when appending one query's RT to another @@ -1370,6 +1390,21 @@ ReplaceVarsFromTargetList_callback(Var *var, if (var->varlevelsup > 0) IncrementVarSublevelsUp(newnode, var->varlevelsup, 0); + /* + * Check to see if the tlist item contains a PARAM_MULTIEXPR Param, + * and throw error if so. This case could only happen when expanding + * an ON UPDATE rule's NEW variable and the referenced tlist item in + * the original UPDATE command is part of a multiple assignment. There + * seems no practical way to handle such cases without multiple + * evaluation of the multiple assignment's sub-select, which would + * create semantic oddities that users of rules would probably prefer + * not to cope with. So treat it as an unimplemented feature. + */ + if (contains_multiexpr_param(newnode, NULL)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("NEW variables in ON UPDATE rules cannot reference columns that are part of a multiple assignment in the subject UPDATE command"))); + return newnode; } } |