]> granicus.if.org Git - postgresql/commitdiff
Use the correct tuplestore read pointer in a NamedTuplestoreScan.
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 27 Feb 2018 20:56:51 +0000 (15:56 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 27 Feb 2018 20:56:51 +0000 (15:56 -0500)
Tom Kazimiers reported that transition tables don't work correctly when
they are scanned by more than one executor node.  That's because commit
18ce3a4ab allocated separate read pointers for each executor node, as it
must, but failed to make them active at the appropriate times.  Repair.

Thomas Munro

Discussion: https://postgr.es/m/20180224034748.bixarv6632vbxgeb%40dewberry.localdomain

src/backend/executor/nodeNamedtuplestorescan.c
src/test/regress/expected/plpgsql.out
src/test/regress/sql/plpgsql.sql

index 3a65b9f5dc9d7556da71b35582e42d335093da23..feb681390f31430a9bb4c64f3d54f5ee1cf4c78c 100644 (file)
@@ -40,6 +40,7 @@ NamedTuplestoreScanNext(NamedTuplestoreScanState *node)
         * Get the next tuple from tuplestore. Return NULL if no more tuples.
         */
        slot = node->ss.ss_ScanTupleSlot;
+       tuplestore_select_read_pointer(node->relation, node->readptr);
        (void) tuplestore_gettupleslot(node->relation, true, false, slot);
        return slot;
 }
@@ -116,6 +117,7 @@ ExecInitNamedTuplestoreScan(NamedTuplestoreScan *node, EState *estate, int eflag
         * The new read pointer copies its position from read pointer 0, which
         * could be anywhere, so explicitly rewind it.
         */
+       tuplestore_select_read_pointer(scanstate->relation, scanstate->readptr);
        tuplestore_rescan(scanstate->relation);
 
        /*
index 36ecedac5e1abd6f922e81d0b6c76f00a7d820f8..0162102a19615a40e7c33007c69ae7baae5eefa4 100644 (file)
@@ -5985,6 +5985,28 @@ LINE 1: SELECT (SELECT string_agg(id || '=' || name, ',') FROM d)
 QUERY:  SELECT (SELECT string_agg(id || '=' || name, ',') FROM d)
 CONTEXT:  PL/pgSQL function alter_table_under_transition_tables_upd_func() line 3 at RAISE
 --
+-- Test multiple reference to a transition table
+--
+CREATE TABLE multi_test (i int);
+INSERT INTO multi_test VALUES (1);
+CREATE OR REPLACE FUNCTION multi_test_trig() RETURNS trigger
+LANGUAGE plpgsql AS $$
+BEGIN
+    RAISE NOTICE 'count = %', (SELECT COUNT(*) FROM new_test);
+    RAISE NOTICE 'count union = %',
+      (SELECT COUNT(*)
+       FROM (SELECT * FROM new_test UNION ALL SELECT * FROM new_test) ss);
+    RETURN NULL;
+END$$;
+CREATE TRIGGER my_trigger AFTER UPDATE ON multi_test
+  REFERENCING NEW TABLE AS new_test OLD TABLE as old_test
+  FOR EACH STATEMENT EXECUTE PROCEDURE multi_test_trig();
+UPDATE multi_test SET i = i;
+NOTICE:  count = 1
+NOTICE:  count union = 2
+DROP TABLE multi_test;
+DROP FUNCTION multi_test_trig();
+--
 -- Check type parsing and record fetching from partitioned tables
 --
 CREATE TABLE partitioned_table (a int, b text) PARTITION BY LIST (a);
index d025605b1b1c534f94fc5932bbe636ea511aef69..070a3a4596ead607c9124770c7800ee53eb97830 100644 (file)
@@ -4773,6 +4773,32 @@ ALTER TABLE alter_table_under_transition_tables
 UPDATE alter_table_under_transition_tables
   SET id = id;
 
+--
+-- Test multiple reference to a transition table
+--
+
+CREATE TABLE multi_test (i int);
+INSERT INTO multi_test VALUES (1);
+
+CREATE OR REPLACE FUNCTION multi_test_trig() RETURNS trigger
+LANGUAGE plpgsql AS $$
+BEGIN
+    RAISE NOTICE 'count = %', (SELECT COUNT(*) FROM new_test);
+    RAISE NOTICE 'count union = %',
+      (SELECT COUNT(*)
+       FROM (SELECT * FROM new_test UNION ALL SELECT * FROM new_test) ss);
+    RETURN NULL;
+END$$;
+
+CREATE TRIGGER my_trigger AFTER UPDATE ON multi_test
+  REFERENCING NEW TABLE AS new_test OLD TABLE as old_test
+  FOR EACH STATEMENT EXECUTE PROCEDURE multi_test_trig();
+
+UPDATE multi_test SET i = i;
+
+DROP TABLE multi_test;
+DROP FUNCTION multi_test_trig();
+
 --
 -- Check type parsing and record fetching from partitioned tables
 --