Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple assignment for UPDATE SET #7968

Merged
merged 7 commits into from
Jun 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/include/duckdb/parser/transformer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,9 @@ class Transformer {
unique_ptr<ParsedExpression> TransformParamRef(duckdb_libpgquery::PGParamRef &node);
unique_ptr<ParsedExpression> TransformNamedArg(duckdb_libpgquery::PGNamedArgExpr &root);

//! Transform multi assignment reference into an Expression
unique_ptr<ParsedExpression> TransformMultiAssignRef(duckdb_libpgquery::PGMultiAssignRef &root);

unique_ptr<ParsedExpression> TransformSQLValueFunction(duckdb_libpgquery::PGSQLValueFunction &node);

unique_ptr<ParsedExpression> TransformSubquery(duckdb_libpgquery::PGSubLink &root);
Expand Down
3 changes: 2 additions & 1 deletion src/parser/transform/expression/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ add_library_unity(
transform_operator.cpp
transform_param_ref.cpp
transform_positional_reference.cpp
transform_subquery.cpp)
transform_subquery.cpp
transform_multi_assign_reference.cpp)
set(ALL_OBJECT_FILES
${ALL_OBJECT_FILES} $<TARGET_OBJECTS:duckdb_transformer_expression>
PARENT_SCOPE)
2 changes: 2 additions & 0 deletions src/parser/transform/expression/transform_expression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ unique_ptr<ParsedExpression> Transformer::TransformExpression(duckdb_libpgquery:
return TransformStarExpression(PGCast<duckdb_libpgquery::PGAStar>(node));
case duckdb_libpgquery::T_PGBooleanTest:
return TransformBooleanTest(PGCast<duckdb_libpgquery::PGBooleanTest>(node));
case duckdb_libpgquery::T_PGMultiAssignRef:
return TransformMultiAssignRef(PGCast<duckdb_libpgquery::PGMultiAssignRef>(node));

default:
throw NotImplementedException("Expression type %s (%d)", NodetypeToString(node.type), (int)node.type);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#include "duckdb/parser/parsed_expression.hpp"
#include "duckdb/parser/transformer.hpp"

namespace duckdb {

unique_ptr<ParsedExpression> Transformer::TransformMultiAssignRef(duckdb_libpgquery::PGMultiAssignRef &root) {
// Multi assignment for the ROW function
if (root.source->type == duckdb_libpgquery::T_PGFuncCall) {
auto func = PGCast<duckdb_libpgquery::PGFuncCall>(*root.source);

// Explicitly only allow ROW function
char const *function_name =
PGPointerCast<duckdb_libpgquery::PGValue>(func.funcname->tail->data.ptr_value)->val.str;
if (function_name == nullptr || strlen(function_name) != 3 || strncmp(function_name, "row", 3) != 0) {
return TransformExpression(root.source);
}

// Too many columns (ie. (x, y) = (1, 2, 3) )
if (root.ncolumns < func.args->length) {
throw ParserException(
"Could not perform multiple assignment, target only expects %d values, %d were provided", root.ncolumns,
func.args->length);
}

// Get the expression corresponding with the current column
idx_t idx = 1;
auto list = func.args->head;
while (list && idx < static_cast<idx_t>(root.colno)) {
list = list->next;
++idx;
}

// Not enough columns (ie. (x, y, z) = (1, 2) )
if (!list) {
throw ParserException(
"Could not perform multiple assignment, target expects %d values, only %d were provided", root.ncolumns,
func.args->length);
}
return TransformExpression(reinterpret_cast<duckdb_libpgquery::PGNode *>(list->data.ptr_value));
nickgerrets marked this conversation as resolved.
Show resolved Hide resolved
}
return TransformExpression(root.source);
}

} // namespace duckdb
81 changes: 81 additions & 0 deletions test/sql/update/test_multiple_assignment.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# name: test/sql/update/test_multiple_assignment.test
# description: Update with multiple assignment
# group: [update]

nickgerrets marked this conversation as resolved.
Show resolved Hide resolved
# create table and insert some values
# ---------------------------------------------------
statement ok
CREATE TABLE tbl (key INT, fruit VARCHAR, cost INT);

statement ok
INSERT INTO tbl VALUES (1, 'apple', 2), (2, 'orange', 3);

# simple case
# ---------------------------------------------------
statement ok
UPDATE tbl SET (key, fruit, cost) = (1, 'pear', 2);

query III
SELECT * FROM tbl;
----
1 pear 2
1 pear 2

# ROW function
# ---------------------------------------------------
statement ok
UPDATE tbl SET (key, fruit, cost) = (2, 'apple', 3);

query III
SELECT * FROM tbl;
----
2 apple 3
2 apple 3

# 1 value
# ---------------------------------------------------
statement ok
UPDATE tbl SET (key, fruit, cost) = 3;

query III
SELECT * FROM tbl;
----
3 3 3
3 3 3

# too few values
# ---------------------------------------------------
statement error
UPDATE tbl SET (key, fruit, cost) = (1, 2);
----
Parser Error: Could not perform multiple assignment, target expects 3 values, only 2 were provided

# too many values
# ---------------------------------------------------
statement error
UPDATE tbl SET (key, fruit, cost) = (1, 2, 3, 4);
----
Parser Error: Could not perform multiple assignment, target only expects 3 values, 4 were provided

# functions should still work
# ---------------------------------------------------
statement ok
UPDATE tbl SET (key, fruit, cost) = ADD(key, cost);

query III
SELECT * FROM tbl;
----
6 6 6
6 6 6

# empty lists
# ---------------------------------------------------
statement error
UPDATE tbl SET () = (key, fruit);
----
Parser Error: syntax error at or near ")"

statement error
UPDATE tbl SET (key, fruit) = ();
----
Parser Error: syntax error at or near ")"