diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index cf32dc5690..f10f70a17d 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1147,11 +1147,14 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) * Lastly, if this is not the primary (canSetTag) ModifyTable node, add it * to estate->es_auxmodifytables so that it will be run to completion by * ExecPostprocessPlan. (It'd actually work fine to add the primary - * ModifyTable node too, but there's no need.) + * ModifyTable node too, but there's no need.) Note the use of lcons + * not lappend: we need later-initialized ModifyTable nodes to be shut + * down before earlier ones. This ensures that we don't throw away + * RETURNING rows that need to be seen by a later CTE subplan. */ if (!mtstate->canSetTag) - estate->es_auxmodifytables = lappend(estate->es_auxmodifytables, - mtstate); + estate->es_auxmodifytables = lcons(mtstate, + estate->es_auxmodifytables); return mtstate; } diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index a82ae13797..3491ce42b5 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -1543,6 +1543,82 @@ SELECT * FROM y; -400 (22 rows) +-- check that run to completion happens in proper ordering +TRUNCATE TABLE y; +INSERT INTO y SELECT generate_series(1, 3); +CREATE TEMPORARY TABLE yy (a INTEGER); +WITH RECURSIVE t1 AS ( + INSERT INTO y SELECT * FROM y RETURNING * +), t2 AS ( + INSERT INTO yy SELECT * FROM t1 RETURNING * +) +SELECT 1; + ?column? +---------- + 1 +(1 row) + +SELECT * FROM y; + a +--- + 1 + 2 + 3 + 1 + 2 + 3 +(6 rows) + +SELECT * FROM yy; + a +--- + 1 + 2 + 3 +(3 rows) + +WITH RECURSIVE t1 AS ( + INSERT INTO yy SELECT * FROM t2 RETURNING * +), t2 AS ( + INSERT INTO y SELECT * FROM y RETURNING * +) +SELECT 1; + ?column? +---------- + 1 +(1 row) + +SELECT * FROM y; + a +--- + 1 + 2 + 3 + 1 + 2 + 3 + 1 + 2 + 3 + 1 + 2 + 3 +(12 rows) + +SELECT * FROM yy; + a +--- + 1 + 2 + 3 + 1 + 2 + 3 + 1 + 2 + 3 +(9 rows) + -- triggers TRUNCATE TABLE y; INSERT INTO y SELECT generate_series(1, 10); diff --git a/src/test/regress/sql/with.sql b/src/test/regress/sql/with.sql index f5d5ebe159..cf036ca285 100644 --- a/src/test/regress/sql/with.sql +++ b/src/test/regress/sql/with.sql @@ -641,6 +641,32 @@ SELECT * FROM t LIMIT 10; SELECT * FROM y; +-- check that run to completion happens in proper ordering + +TRUNCATE TABLE y; +INSERT INTO y SELECT generate_series(1, 3); +CREATE TEMPORARY TABLE yy (a INTEGER); + +WITH RECURSIVE t1 AS ( + INSERT INTO y SELECT * FROM y RETURNING * +), t2 AS ( + INSERT INTO yy SELECT * FROM t1 RETURNING * +) +SELECT 1; + +SELECT * FROM y; +SELECT * FROM yy; + +WITH RECURSIVE t1 AS ( + INSERT INTO yy SELECT * FROM t2 RETURNING * +), t2 AS ( + INSERT INTO y SELECT * FROM y RETURNING * +) +SELECT 1; + +SELECT * FROM y; +SELECT * FROM yy; + -- triggers TRUNCATE TABLE y;