diff --git a/src/backend/distributed/deparser/ruleutils_15.c b/src/backend/distributed/deparser/ruleutils_15.c index 9004f7bbc..88db9662f 100644 --- a/src/backend/distributed/deparser/ruleutils_15.c +++ b/src/backend/distributed/deparser/ruleutils_15.c @@ -3509,6 +3509,9 @@ get_update_query_targetlist_def(Query *query, List *targetList, SubLink *cur_ma_sublink; List *ma_sublinks; + AttrNumber previous_attnum = InvalidAttrNumber; + int paramid_increment = 0; + /* * Prepare to deal with MULTIEXPR assignments: collect the source SubLinks * into a list. We expect them to appear, in ID order, in resjunk tlist @@ -3631,11 +3634,48 @@ get_update_query_targetlist_def(Query *query, List *targetList, */ if (cur_ma_sublink != NULL) { + AttrNumber attnum = InvalidAttrNumber; + if (IsA(expr, Param)) + { + Param *param = (Param *) expr; + attnum = param->paramid + paramid_increment; + } + else if (IsA(expr, FuncExpr)) + { + FuncExpr *func = (FuncExpr *) expr; + ListCell *lc; + + /* Iterate through the arguments of the FuncExpr */ + foreach(lc, func->args) + { + Node *arg = (Node *) lfirst(lc); + + /* Check if the argument is a PARAM node */ + if (IsA(arg, Param)) + { + Param *param = (Param *) arg; + attnum = param->paramid + paramid_increment; + + break; /* Exit loop once we find the PARAM node */ + } + } + } + + if (previous_attnum >= attnum) + ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg( + "cannot plan distributed UPDATE SET (..) = (SELECT ...) query when not sorted by physical order"), + errhint("Sort the columns on the left side by physical order."))); + + previous_attnum = attnum; + if (--remaining_ma_columns > 0) continue; /* not the last column of multiassignment */ + appendStringInfoChar(buf, ')'); expr = (Node *) cur_ma_sublink; cur_ma_sublink = NULL; + paramid_increment = previous_attnum; } appendStringInfoString(buf, " = "); diff --git a/src/backend/distributed/deparser/ruleutils_16.c b/src/backend/distributed/deparser/ruleutils_16.c index 65bbd1720..027fe3242 100644 --- a/src/backend/distributed/deparser/ruleutils_16.c +++ b/src/backend/distributed/deparser/ruleutils_16.c @@ -3525,6 +3525,9 @@ get_update_query_targetlist_def(Query *query, List *targetList, SubLink *cur_ma_sublink; List *ma_sublinks; + AttrNumber previous_attnum = InvalidAttrNumber; + int paramid_increment = 0; + /* * Prepare to deal with MULTIEXPR assignments: collect the source SubLinks * into a list. We expect them to appear, in ID order, in resjunk tlist @@ -3647,11 +3650,48 @@ get_update_query_targetlist_def(Query *query, List *targetList, */ if (cur_ma_sublink != NULL) { + AttrNumber attnum = InvalidAttrNumber; + if (IsA(expr, Param)) + { + Param *param = (Param *) expr; + attnum = param->paramid + paramid_increment; + } + else if (IsA(expr, FuncExpr)) + { + FuncExpr *func = (FuncExpr *) expr; + ListCell *lc; + + /* Iterate through the arguments of the FuncExpr */ + foreach(lc, func->args) + { + Node *arg = (Node *) lfirst(lc); + + /* Check if the argument is a PARAM node */ + if (IsA(arg, Param)) + { + Param *param = (Param *) arg; + attnum = param->paramid + paramid_increment; + + break; /* Exit loop once we find the PARAM node */ + } + } + } + + if (previous_attnum >= attnum) + ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg( + "cannot plan distributed UPDATE SET (..) = (SELECT ...) query when not sorted by physical order"), + errhint("Sort the columns on the left side by physical order."))); + + previous_attnum = attnum; + if (--remaining_ma_columns > 0) continue; /* not the last column of multiassignment */ + appendStringInfoChar(buf, ')'); expr = (Node *) cur_ma_sublink; cur_ma_sublink = NULL; + paramid_increment = previous_attnum; } appendStringInfoString(buf, " = "); diff --git a/src/backend/distributed/deparser/ruleutils_17.c b/src/backend/distributed/deparser/ruleutils_17.c index f0710e684..64ac077cb 100644 --- a/src/backend/distributed/deparser/ruleutils_17.c +++ b/src/backend/distributed/deparser/ruleutils_17.c @@ -3542,6 +3542,9 @@ get_update_query_targetlist_def(Query *query, List *targetList, SubLink *cur_ma_sublink; List *ma_sublinks; + AttrNumber previous_attnum = InvalidAttrNumber; + int paramid_increment = 0; + /* * Prepare to deal with MULTIEXPR assignments: collect the source SubLinks * into a list. We expect them to appear, in ID order, in resjunk tlist @@ -3664,11 +3667,48 @@ get_update_query_targetlist_def(Query *query, List *targetList, */ if (cur_ma_sublink != NULL) { + AttrNumber attnum = InvalidAttrNumber; + if (IsA(expr, Param)) + { + Param *param = (Param *) expr; + attnum = param->paramid + paramid_increment; + } + else if (IsA(expr, FuncExpr)) + { + FuncExpr *func = (FuncExpr *) expr; + ListCell *lc; + + /* Iterate through the arguments of the FuncExpr */ + foreach(lc, func->args) + { + Node *arg = (Node *) lfirst(lc); + + /* Check if the argument is a PARAM node */ + if (IsA(arg, Param)) + { + Param *param = (Param *) arg; + attnum = param->paramid + paramid_increment; + + break; /* Exit loop once we find the PARAM node */ + } + } + } + + if (previous_attnum >= attnum) + ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg( + "cannot plan distributed UPDATE SET (..) = (SELECT ...) query when not sorted by physical order"), + errhint("Sort the columns on the left side by physical order."))); + + previous_attnum = attnum; + if (--remaining_ma_columns > 0) continue; /* not the last column of multiassignment */ + appendStringInfoChar(buf, ')'); expr = (Node *) cur_ma_sublink; cur_ma_sublink = NULL; + paramid_increment = previous_attnum; } appendStringInfoString(buf, " = "); diff --git a/src/test/regress/expected/indirections.out b/src/test/regress/expected/indirections.out index a9ef5078a..20bd73d2b 100644 --- a/src/test/regress/expected/indirections.out +++ b/src/test/regress/expected/indirections.out @@ -103,103 +103,113 @@ SELECT * FROM test_dist_indirection_new ORDER BY id; -- now UPDATEs UPDATE test_ref_indirection SET (col_bool, col_date, col_int, col_text) - = (SELECT true, '1970-12-31'::date, 1, 'ok') + = (SELECT true, '1970-01-01'::date, 1, 'ok') RETURNING *; id | col_bool | col_date | col_int | col_text --------------------------------------------------------------------- - 1 | t | 12-31-1970 | 1 | ok - 2 | t | 12-31-1970 | 1 | ok - 3 | t | 12-31-1970 | 1 | ok - 4 | t | 12-31-1970 | 1 | ok + 1 | t | 01-01-1970 | 1 | ok + 2 | t | 01-01-1970 | 1 | ok + 3 | t | 01-01-1970 | 1 | ok + 4 | t | 01-01-1970 | 1 | ok (4 rows) UPDATE test_dist_indirection SET (col_bool, col_date, col_int, col_text) - = (SELECT true, '1970-12-31'::date, 1, 'ok') + = (SELECT true, '1970-01-01'::date, 1, 'ok') RETURNING *; id | col_bool | col_date | col_int | col_text --------------------------------------------------------------------- - 1 | t | 12-31-1970 | 1 | ok - 2 | t | 12-31-1970 | 1 | ok - 3 | t | 12-31-1970 | 1 | ok - 4 | t | 12-31-1970 | 1 | ok + 1 | t | 01-01-1970 | 1 | ok + 2 | t | 01-01-1970 | 1 | ok + 3 | t | 01-01-1970 | 1 | ok + 4 | t | 01-01-1970 | 1 | ok (4 rows) UPDATE test_ref_indirection - SET (col_bool, col_date) = (select true, '1970-06-06'::date) + SET (col_bool, col_date) = (select true, '1970-01-01'::date) , (col_int, col_text) = (select 1, 'still ok') RETURNING *; id | col_bool | col_date | col_int | col_text --------------------------------------------------------------------- - 1 | t | 06-06-1970 | 1 | still ok - 2 | t | 06-06-1970 | 1 | still ok - 3 | t | 06-06-1970 | 1 | still ok - 4 | t | 06-06-1970 | 1 | still ok + 1 | t | 01-01-1970 | 1 | still ok + 2 | t | 01-01-1970 | 1 | still ok + 3 | t | 01-01-1970 | 1 | still ok + 4 | t | 01-01-1970 | 1 | still ok (4 rows) UPDATE test_dist_indirection - SET (col_bool, col_date) = (select true, '1970-06-06'::date) + SET (col_bool, col_date) = (select true, '1970-01-01'::date) , (col_int, col_text) = (select 1, 'still ok') RETURNING *; id | col_bool | col_date | col_int | col_text --------------------------------------------------------------------- - 1 | t | 06-06-1970 | 1 | still ok - 2 | t | 06-06-1970 | 1 | still ok - 3 | t | 06-06-1970 | 1 | still ok - 4 | t | 06-06-1970 | 1 | still ok + 1 | t | 01-01-1970 | 1 | still ok + 2 | t | 01-01-1970 | 1 | still ok + 3 | t | 01-01-1970 | 1 | still ok + 4 | t | 01-01-1970 | 1 | still ok +(4 rows) + +UPDATE test_ref_indirection + SET (col_bool, col_int) = (select true, 1) + , (col_text) = (select 'ok') +RETURNING *; + id | col_bool | col_date | col_int | col_text +--------------------------------------------------------------------- + 1 | t | 01-01-1970 | 1 | ok + 2 | t | 01-01-1970 | 1 | ok + 3 | t | 01-01-1970 | 1 | ok + 4 | t | 01-01-1970 | 1 | ok (4 rows) SELECT * FROM test_ref_indirection ORDER BY id; id | col_bool | col_date | col_int | col_text --------------------------------------------------------------------- - 1 | t | 06-06-1970 | 1 | still ok - 2 | t | 06-06-1970 | 1 | still ok - 3 | t | 06-06-1970 | 1 | still ok - 4 | t | 06-06-1970 | 1 | still ok + 1 | t | 01-01-1970 | 1 | ok + 2 | t | 01-01-1970 | 1 | ok + 3 | t | 01-01-1970 | 1 | ok + 4 | t | 01-01-1970 | 1 | ok (4 rows) SELECT * FROM test_dist_indirection ORDER BY id; id | col_bool | col_date | col_int | col_text --------------------------------------------------------------------- - 1 | t | 06-06-1970 | 1 | still ok - 2 | t | 06-06-1970 | 1 | still ok - 3 | t | 06-06-1970 | 1 | still ok - 4 | t | 06-06-1970 | 1 | still ok + 1 | t | 01-01-1970 | 1 | still ok + 2 | t | 01-01-1970 | 1 | still ok + 3 | t | 01-01-1970 | 1 | still ok + 4 | t | 01-01-1970 | 1 | still ok (4 rows) -- but those should not: --- TODO wrong ERROR UPDATE test_ref_indirection SET (col_date, col_text, col_int, col_bool) - = (SELECT '1970-12-31'::date, 'not ok', 2, false) + = (SELECT '1970-06-06'::date, 'not ok', 2, false) RETURNING *; -ERROR: column "col_bool" is of type boolean but expression is of type date -HINT: You will need to rewrite or cast the expression. -CONTEXT: while executing command on localhost:xxxxx --- TODO wrong ERROR +ERROR: cannot plan distributed UPDATE SET (..) = (SELECT ...) query when not sorted by physical order +HINT: Sort the columns on the left side by physical order. UPDATE test_dist_indirection SET (col_date, col_text, col_int, col_bool) - = (SELECT '1970-12-31'::date, 'not ok', 2, false) + = (SELECT '1970-06-06'::date, 'not ok', 2, false) RETURNING *; -ERROR: column "col_bool" is of type boolean but expression is of type date -HINT: You will need to rewrite or cast the expression. -CONTEXT: while executing command on localhost:xxxxx --- TODO wrong ERROR +ERROR: cannot plan distributed UPDATE SET (..) = (SELECT ...) query when not sorted by physical order +HINT: Sort the columns on the left side by physical order. +UPDATE test_ref_indirection + SET (col_int, col_text) = (select 2, 'not ok') + , (col_bool) = (select false) +RETURNING *; +ERROR: cannot plan distributed UPDATE SET (..) = (SELECT ...) query when not sorted by physical order +HINT: Sort the columns on the left side by physical order. UPDATE test_ref_indirection SET (col_int, col_date) = (select 2, '1970-06-06'::date) , (col_text, col_bool) = (select 'not ok', false) RETURNING *; -ERROR: column "col_bool" is of type boolean but expression is of type integer -HINT: You will need to rewrite or cast the expression. -CONTEXT: while executing command on localhost:xxxxx --- TODO wrong ERROR +ERROR: cannot plan distributed UPDATE SET (..) = (SELECT ...) query when not sorted by physical order +HINT: Sort the columns on the left side by physical order. UPDATE test_dist_indirection SET (col_int, col_date) = (select 2, '1970-06-06'::date) , (col_text, col_bool) = (select 'not ok', false) RETURNING *; -ERROR: column "col_bool" is of type boolean but expression is of type integer -HINT: You will need to rewrite or cast the expression. -CONTEXT: while executing command on localhost:xxxxx +ERROR: cannot plan distributed UPDATE SET (..) = (SELECT ...) query when not sorted by physical order +HINT: Sort the columns on the left side by physical order. -- -- more restrictive ones, just in case we miss a wrong value -- @@ -209,10 +219,10 @@ UPDATE test_ref_indirection RETURNING *; id | col_bool | col_date | col_int | col_text --------------------------------------------------------------------- - 1 | t | 06-06-1970 | 1 | ok - 2 | t | 06-06-1970 | 1 | ok - 3 | t | 06-06-1970 | 1 | ok - 4 | t | 06-06-1970 | 1 | ok + 1 | t | 01-01-1970 | 1 | ok + 2 | t | 01-01-1970 | 1 | ok + 3 | t | 01-01-1970 | 1 | ok + 4 | t | 01-01-1970 | 1 | ok (4 rows) UPDATE test_dist_indirection @@ -220,10 +230,10 @@ UPDATE test_dist_indirection RETURNING *; id | col_bool | col_date | col_int | col_text --------------------------------------------------------------------- - 1 | t | 06-06-1970 | 1 | ok - 2 | t | 06-06-1970 | 1 | ok - 3 | t | 06-06-1970 | 1 | ok - 4 | t | 06-06-1970 | 1 | ok + 1 | t | 01-01-1970 | 1 | ok + 2 | t | 01-01-1970 | 1 | ok + 3 | t | 01-01-1970 | 1 | ok + 4 | t | 01-01-1970 | 1 | ok (4 rows) UPDATE test_ref_indirection @@ -232,7 +242,7 @@ WHERE id = 1 RETURNING *; id | col_bool | col_date | col_int | col_text --------------------------------------------------------------------- - 1 | t | 06-06-1970 | 1 | ok + 1 | t | 01-01-1970 | 1 | ok (1 row) UPDATE test_dist_indirection @@ -241,60 +251,51 @@ WHERE id = 1 RETURNING *; id | col_bool | col_date | col_int | col_text --------------------------------------------------------------------- - 1 | t | 06-06-1970 | 1 | ok + 1 | t | 01-01-1970 | 1 | ok (1 row) SELECT * FROM test_ref_indirection ORDER BY id; id | col_bool | col_date | col_int | col_text --------------------------------------------------------------------- - 1 | t | 06-06-1970 | 1 | ok - 2 | t | 06-06-1970 | 1 | ok - 3 | t | 06-06-1970 | 1 | ok - 4 | t | 06-06-1970 | 1 | ok + 1 | t | 01-01-1970 | 1 | ok + 2 | t | 01-01-1970 | 1 | ok + 3 | t | 01-01-1970 | 1 | ok + 4 | t | 01-01-1970 | 1 | ok (4 rows) SELECT * FROM test_dist_indirection ORDER BY id; id | col_bool | col_date | col_int | col_text --------------------------------------------------------------------- - 1 | t | 06-06-1970 | 1 | ok - 2 | t | 06-06-1970 | 1 | ok - 3 | t | 06-06-1970 | 1 | ok - 4 | t | 06-06-1970 | 1 | ok + 1 | t | 01-01-1970 | 1 | ok + 2 | t | 01-01-1970 | 1 | ok + 3 | t | 01-01-1970 | 1 | ok + 4 | t | 01-01-1970 | 1 | ok (4 rows) -- those should not --- TODO wrong ERROR UPDATE test_ref_indirection SET (col_text, col_bool) = (SELECT 'not ok', false) RETURNING *; -ERROR: column "col_bool" is of type boolean but expression is of type text -HINT: You will need to rewrite or cast the expression. -CONTEXT: while executing command on localhost:xxxxx --- TODO wrong ERROR +ERROR: cannot plan distributed UPDATE SET (..) = (SELECT ...) query when not sorted by physical order +HINT: Sort the columns on the left side by physical order. UPDATE test_dist_indirection SET (col_text, col_bool) = (SELECT 'not ok', false) RETURNING *; -ERROR: column "col_bool" is of type boolean but expression is of type text -HINT: You will need to rewrite or cast the expression. -CONTEXT: while executing command on localhost:xxxxx --- TODO wrong ERROR +ERROR: cannot plan distributed UPDATE SET (..) = (SELECT ...) query when not sorted by physical order +HINT: Sort the columns on the left side by physical order. UPDATE test_ref_indirection SET (col_text, col_bool) = (SELECT 'not ok', false) WHERE id = 2 RETURNING *; -ERROR: column "col_bool" is of type boolean but expression is of type text -HINT: You will need to rewrite or cast the expression. -CONTEXT: while executing command on localhost:xxxxx --- TODO wrong ERROR +ERROR: cannot plan distributed UPDATE SET (..) = (SELECT ...) query when not sorted by physical order +HINT: Sort the columns on the left side by physical order. UPDATE test_dist_indirection SET (col_text, col_bool) = (SELECT 'not ok', false) WHERE id = 2 RETURNING *; -ERROR: column "col_bool" is of type boolean but expression is of type text -HINT: You will need to rewrite or cast the expression. -CONTEXT: while executing command on localhost:xxxxx +ERROR: cannot plan distributed UPDATE SET (..) = (SELECT ...) query when not sorted by physical order +HINT: Sort the columns on the left side by physical order. -- several updates in CTE shoult not work --- TODO wrong ERROR with qq3 as ( update test_ref_indirection SET (col_text, col_bool) @@ -310,10 +311,8 @@ qq4 as ( returning * ) select * from qq3 union all select * from qq4; -ERROR: column "col_bool" is of type boolean but expression is of type text -HINT: You will need to rewrite or cast the expression. -CONTEXT: while executing command on localhost:xxxxx --- TODO wrong ERROR +ERROR: cannot plan distributed UPDATE SET (..) = (SELECT ...) query when not sorted by physical order +HINT: Sort the columns on the left side by physical order. with qq3 as ( update test_dist_indirection SET (col_text, col_bool) @@ -329,9 +328,8 @@ qq4 as ( returning * ) select * from qq3 union all select * from qq4; -ERROR: column "col_bool" is of type boolean but expression is of type text -HINT: You will need to rewrite or cast the expression. -CONTEXT: while executing command on localhost:xxxxx +ERROR: cannot plan distributed UPDATE SET (..) = (SELECT ...) query when not sorted by physical order +HINT: Sort the columns on the left side by physical order. DROP TABLE test_dist_indirection; DROP TABLE test_dist_indirection_new; DROP TABLE test_ref_indirection; @@ -351,6 +349,8 @@ SELECT create_reference_table('indirections.update_test'); UPDATE update_test SET (b,a) = (select a,b from update_test where b = 41 and c = 'car') WHERE a = 100 AND b = 20; +ERROR: cannot plan distributed UPDATE SET (..) = (SELECT ...) query when not sorted by physical order +HINT: Sort the columns on the left side by physical order. -- https://github.com/citusdata/citus/pull/5692 DROP SCHEMA indirections CASCADE; NOTICE: drop cascades to 2 other objects diff --git a/src/test/regress/sql/indirections.sql b/src/test/regress/sql/indirections.sql index fd8119ff5..73ff78b71 100644 --- a/src/test/regress/sql/indirections.sql +++ b/src/test/regress/sql/indirections.sql @@ -65,43 +65,48 @@ SELECT * FROM test_dist_indirection_new ORDER BY id; -- now UPDATEs UPDATE test_ref_indirection SET (col_bool, col_date, col_int, col_text) - = (SELECT true, '1970-12-31'::date, 1, 'ok') + = (SELECT true, '1970-01-01'::date, 1, 'ok') RETURNING *; UPDATE test_dist_indirection SET (col_bool, col_date, col_int, col_text) - = (SELECT true, '1970-12-31'::date, 1, 'ok') + = (SELECT true, '1970-01-01'::date, 1, 'ok') RETURNING *; UPDATE test_ref_indirection - SET (col_bool, col_date) = (select true, '1970-06-06'::date) + SET (col_bool, col_date) = (select true, '1970-01-01'::date) , (col_int, col_text) = (select 1, 'still ok') RETURNING *; UPDATE test_dist_indirection - SET (col_bool, col_date) = (select true, '1970-06-06'::date) + SET (col_bool, col_date) = (select true, '1970-01-01'::date) , (col_int, col_text) = (select 1, 'still ok') RETURNING *; +UPDATE test_ref_indirection + SET (col_bool, col_int) = (select true, 1) + , (col_text) = (select 'ok') +RETURNING *; + SELECT * FROM test_ref_indirection ORDER BY id; SELECT * FROM test_dist_indirection ORDER BY id; -- but those should not: --- TODO wrong ERROR UPDATE test_ref_indirection SET (col_date, col_text, col_int, col_bool) - = (SELECT '1970-12-31'::date, 'not ok', 2, false) + = (SELECT '1970-06-06'::date, 'not ok', 2, false) RETURNING *; --- TODO wrong ERROR UPDATE test_dist_indirection SET (col_date, col_text, col_int, col_bool) - = (SELECT '1970-12-31'::date, 'not ok', 2, false) + = (SELECT '1970-06-06'::date, 'not ok', 2, false) RETURNING *; --- TODO wrong ERROR +UPDATE test_ref_indirection + SET (col_int, col_text) = (select 2, 'not ok') + , (col_bool) = (select false) +RETURNING *; UPDATE test_ref_indirection SET (col_int, col_date) = (select 2, '1970-06-06'::date) , (col_text, col_bool) = (select 'not ok', false) RETURNING *; --- TODO wrong ERROR UPDATE test_dist_indirection SET (col_int, col_date) = (select 2, '1970-06-06'::date) , (col_text, col_bool) = (select 'not ok', false) @@ -131,28 +136,23 @@ SELECT * FROM test_ref_indirection ORDER BY id; SELECT * FROM test_dist_indirection ORDER BY id; -- those should not --- TODO wrong ERROR UPDATE test_ref_indirection SET (col_text, col_bool) = (SELECT 'not ok', false) RETURNING *; --- TODO wrong ERROR UPDATE test_dist_indirection SET (col_text, col_bool) = (SELECT 'not ok', false) RETURNING *; --- TODO wrong ERROR UPDATE test_ref_indirection SET (col_text, col_bool) = (SELECT 'not ok', false) WHERE id = 2 RETURNING *; --- TODO wrong ERROR UPDATE test_dist_indirection SET (col_text, col_bool) = (SELECT 'not ok', false) WHERE id = 2 RETURNING *; -- several updates in CTE shoult not work --- TODO wrong ERROR with qq3 as ( update test_ref_indirection SET (col_text, col_bool) @@ -168,7 +168,6 @@ qq4 as ( returning * ) select * from qq3 union all select * from qq4; --- TODO wrong ERROR with qq3 as ( update test_dist_indirection SET (col_text, col_bool)