]> granicus.if.org Git - postgresql/commitdiff
Fix order of shutdown processing when CTEs contain inter-references.
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 26 Feb 2011 04:53:34 +0000 (23:53 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 26 Feb 2011 04:53:34 +0000 (23:53 -0500)
We need ExecutorEnd to run the ModifyTable nodes to completion in
reverse order of initialization, not forward order.  Easily done
by constructing the list back-to-front.

src/backend/executor/nodeModifyTable.c
src/test/regress/expected/with.out
src/test/regress/sql/with.sql

index cf32dc569037f710ce6c43c4c93ee3a10cabe085..f10f70a17d3d773b6f3911686cf581611fba6915 100644 (file)
@@ -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;
 }
index a82ae13797782244794758c615e424d3552bc948..3491ce42b5e2c7973f5662dfdb2dd38610f13b06 100644 (file)
@@ -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);
index f5d5ebe1594ce5f8363586bab3396887f422f765..cf036ca285968f7ac387c101b5a0bf49fdcb5a84 100644 (file)
@@ -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;