diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 1dbf0ffff9e..9a1c0992bfe 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1335,8 +1335,18 @@ ExecInsert(ModifyTableContext *context, if (resultRelInfo->ri_WithCheckOptions != NIL) ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo, slot, estate); - /* Process RETURNING if present */ - if (resultRelInfo->ri_projectReturning) + /* + * Process RETURNING if present. + * + * If this is an UPDATE/DELETE ... FOR PORTION OF, we do not return the + * leftover rows inserted by ExecForPortionOfLeftovers(). Note that we + * must check mtstate->operation here, because we *do* want to process the + * newly inserted row of a cross-partition UPDATE with a FOR PORTION OF + * clause (ExecCrossPartitionUpdate() leaves mtstate->operation set to + * CMD_UPDATE, whereas ExecForPortionOfLeftovers() sets it to CMD_INSERT). + */ + if (resultRelInfo->ri_projectReturning && + !(node->forPortionOf && mtstate->operation == CMD_INSERT)) { TupleTableSlot *oldSlot = NULL; diff --git a/src/test/regress/expected/for_portion_of.out b/src/test/regress/expected/for_portion_of.out index 3c592f90e70..a6cb1ba8380 100644 --- a/src/test/regress/expected/for_portion_of.out +++ b/src/test/regress/expected/for_portion_of.out @@ -920,14 +920,23 @@ SELECT * FROM for_portion_of_test ORDER BY id, valid_at; \set QUIET true -- UPDATE ... RETURNING returns only the updated values -- (not the inserted side values, which are added by a separate "statement"): +CREATE FUNCTION fpo_returning_row(text) +RETURNS text LANGUAGE plpgsql AS +$$ +BEGIN + RAISE NOTICE 'RETURNING %', $1; + RETURN $1; +END; +$$; UPDATE for_portion_of_test FOR PORTION OF valid_at FROM '2018-02-01' TO '2018-02-15' SET name = 'three^3' WHERE id = '[3,4)' - RETURNING *; - id | valid_at | name --------+-------------------------+--------- - [3,4) | [2018-02-01,2018-02-15) | three^3 + RETURNING *, fpo_returning_row(for_portion_of_test::text); +NOTICE: RETURNING ("[3,4)","[2018-02-01,2018-02-15)",three^3) + id | valid_at | name | fpo_returning_row +-------+-------------------------+---------+--------------------------------------------- + [3,4) | [2018-02-01,2018-02-15) | three^3 | ("[3,4)","[2018-02-01,2018-02-15)",three^3) (1 row) -- UPDATE ... RETURNING supports NEW and OLD valid_at @@ -975,10 +984,11 @@ DELETE FROM for_portion_of_test WHERE id = '[99,100)'; DELETE FROM for_portion_of_test FOR PORTION OF valid_at FROM '2018-02-02' TO '2018-02-03' WHERE id = '[3,4)' - RETURNING *; - id | valid_at | name --------+-------------------------+--------- - [3,4) | [2018-02-01,2018-02-10) | three^3 + RETURNING *, fpo_returning_row(for_portion_of_test::text); +NOTICE: RETURNING ("[3,4)","[2018-02-01,2018-02-10)",three^3) + id | valid_at | name | fpo_returning_row +-------+-------------------------+---------+--------------------------------------------- + [3,4) | [2018-02-01,2018-02-10) | three^3 | ("[3,4)","[2018-02-01,2018-02-10)",three^3) (1 row) -- DELETE FOR PORTION OF in a PL/pgSQL function @@ -2137,7 +2147,14 @@ UPDATE temporal_partitioned FOR PORTION OF valid_at FROM '2000-03-01' TO '2000-0 UPDATE temporal_partitioned FOR PORTION OF valid_at FROM '2000-06-01' TO '2000-07-01' SET name = 'one^2', id = '[4,5)' - WHERE id = '[1,2)'; + WHERE id = '[1,2)' + RETURNING id, valid_at, name, fpo_returning_row(temporal_partitioned::text); +NOTICE: RETURNING ("[4,5)","[2000-06-01,2000-07-01)",one^2,30) + id | valid_at | name | fpo_returning_row +-------+-------------------------+-------+---------------------------------------------- + [4,5) | [2000-06-01,2000-07-01) | one^2 | ("[4,5)","[2000-06-01,2000-07-01)",one^2,30) +(1 row) + -- Move from partition 3 to partition 1 UPDATE temporal_partitioned FOR PORTION OF valid_at FROM '2000-06-01' TO '2000-07-01' SET name = 'three^2', @@ -2199,6 +2216,7 @@ SELECT * FROM temporal_partitioned_5 ORDER BY id, valid_at; five | [2000-07-01,2010-01-01) | [5,6) | 3471 (4 rows) +DROP FUNCTION fpo_returning_row; DROP TABLE temporal_partitioned; -- UPDATE/DELETE FOR PORTION OF with RULEs CREATE TABLE fpo_rule (f1 bigint, f2 int4range); diff --git a/src/test/regress/sql/for_portion_of.sql b/src/test/regress/sql/for_portion_of.sql index 5f7b04fdf6b..955cf666d66 100644 --- a/src/test/regress/sql/for_portion_of.sql +++ b/src/test/regress/sql/for_portion_of.sql @@ -590,11 +590,19 @@ SELECT * FROM for_portion_of_test ORDER BY id, valid_at; -- UPDATE ... RETURNING returns only the updated values -- (not the inserted side values, which are added by a separate "statement"): +CREATE FUNCTION fpo_returning_row(text) +RETURNS text LANGUAGE plpgsql AS +$$ +BEGIN + RAISE NOTICE 'RETURNING %', $1; + RETURN $1; +END; +$$; UPDATE for_portion_of_test FOR PORTION OF valid_at FROM '2018-02-01' TO '2018-02-15' SET name = 'three^3' WHERE id = '[3,4)' - RETURNING *; + RETURNING *, fpo_returning_row(for_portion_of_test::text); -- UPDATE ... RETURNING supports NEW and OLD valid_at UPDATE for_portion_of_test @@ -629,7 +637,7 @@ DELETE FROM for_portion_of_test WHERE id = '[99,100)'; DELETE FROM for_portion_of_test FOR PORTION OF valid_at FROM '2018-02-02' TO '2018-02-03' WHERE id = '[3,4)' - RETURNING *; + RETURNING *, fpo_returning_row(for_portion_of_test::text); -- DELETE FOR PORTION OF in a PL/pgSQL function INSERT INTO for_portion_of_test (id, valid_at, name) VALUES @@ -1439,7 +1447,8 @@ UPDATE temporal_partitioned FOR PORTION OF valid_at FROM '2000-03-01' TO '2000-0 UPDATE temporal_partitioned FOR PORTION OF valid_at FROM '2000-06-01' TO '2000-07-01' SET name = 'one^2', id = '[4,5)' - WHERE id = '[1,2)'; + WHERE id = '[1,2)' + RETURNING id, valid_at, name, fpo_returning_row(temporal_partitioned::text); -- Move from partition 3 to partition 1 UPDATE temporal_partitioned FOR PORTION OF valid_at FROM '2000-06-01' TO '2000-07-01' @@ -1460,6 +1469,7 @@ SELECT * FROM temporal_partitioned_1 ORDER BY id, valid_at; SELECT * FROM temporal_partitioned_3 ORDER BY id, valid_at; SELECT * FROM temporal_partitioned_5 ORDER BY id, valid_at; +DROP FUNCTION fpo_returning_row; DROP TABLE temporal_partitioned; -- UPDATE/DELETE FOR PORTION OF with RULEs