]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Fix RETURNING OLD with BEFORE UPDATE trigger and concurrent update.
authorDean Rasheed <dean.a.rasheed@gmail.com>
Wed, 8 Jul 2026 19:46:25 +0000 (20:46 +0100)
committerDean Rasheed <dean.a.rasheed@gmail.com>
Wed, 8 Jul 2026 19:46:25 +0000 (20:46 +0100)
When executing an UPDATE with a RETURNING clause on a table with a
BEFORE UPDATE row trigger, the computation of the OLD values in the
RETURNING list was incorrect if the target tuple was concurrently
updated by another session, at isolation level READ COMMITTED.

The problem was that the trigger code would lock the target tuple,
waiting for the other session to commit, and then fetch the updated
target tuple, but ExecUpdate() would not realise that the target tuple
had changed, and use the outdated target tuple for computing OLD
values. Fix by having ExecUpdate() check the TM_FailureData from
trigger execution and re-fetch the target tuple if necessary.

Re-fetching the target tuple like this is a little inefficient, but
probably negligible compared to the trigger execution and update. A
better long-term fix might be to move the EPQ code out of trigger.c,
and let ExecUpdate() handle it, like ExecMergeMatched() does, but that
would likely mean changing the trigger API, which seems a bit much for
back-patching.

Backpatch to v18, where support for RETURNING OLD/NEW was added.

Bug: #19536
Reported-by: Jonas Boberg <bobergj@gmail.com>
Diagnosed-by: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Author: Dean Rasheed <dean.a.rasheed@gmail.com>
Reviewed-by: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Discussion: https://postgr.es/m/19536-73ce5847e6c0e7b1@postgresql.org
Backpatch-through: 18

src/backend/executor/nodeModifyTable.c
src/test/isolation/expected/eval-plan-qual-trigger.out
src/test/isolation/expected/merge-match-recheck.out
src/test/isolation/specs/eval-plan-qual-trigger.spec
src/test/isolation/specs/merge-match-recheck.spec

index c333d7139fae0723d70ace7cf18f82f14e66890b..b9781eb3b95ba7a9c9078e8995384d22ba598026 100644 (file)
@@ -2765,9 +2765,28 @@ ExecUpdate(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
         * Prepare for the update.  This includes BEFORE ROW triggers, so we're
         * done if it says we are.
         */
+       context->tmfd.traversed = false;
        if (!ExecUpdatePrologue(context, resultRelInfo, tupleid, oldtuple, slot, NULL))
                return NULL;
 
+       /*
+        * If the target tuple was concurrently updated, the trigger code will
+        * have done EPQ and updated tupleid, following the update chain.  In this
+        * case, we must fetch the most recent version of old tuple for the
+        * benefit of RETURNING.  Technically, we could get away with not doing
+        * this, if there is no RETURNING clause, or it doesn't refer to OLD, but
+        * it seems preferable to always ensure that the contents of oldSlot are
+        * correct.
+        */
+       if (context->tmfd.traversed)
+       {
+               if (!table_tuple_fetch_row_version(resultRelInfo->ri_RelationDesc,
+                                                                                  tupleid,
+                                                                                  SnapshotAny,
+                                                                                  oldSlot))
+                       elog(ERROR, "failed to re-fetch tuple updated during trigger execution");
+       }
+
        /* INSTEAD OF ROW UPDATE Triggers */
        if (resultRelInfo->ri_TrigDesc &&
                resultRelInfo->ri_TrigDesc->trig_update_instead_row)
index f6714c2e599a1bf7d57847cc70bd7a5f59ffc07f..eca8606f60fa4d04990f2f8632355264f1600776 100644 (file)
@@ -61,11 +61,11 @@ step s2_upd_a_data:
     WHERE
         noisy_oper('upd', key, '=', 'key-a') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *;
+    RETURNING *, new.data = old.data || '-ups2' AS check_old_and_new;
 
-key  |data              
------+------------------
-key-a|val-a-s1-ups1-ups2
+key  |data              |check_old_and_new
+-----+------------------+-----------------
+key-a|val-a-s1-ups1-ups2|t                
 (1 row)
 
 step s2_c: COMMIT;
@@ -132,11 +132,11 @@ step s2_upd_a_data:
     WHERE
         noisy_oper('upd', key, '=', 'key-a') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *;
+    RETURNING *, new.data = old.data || '-ups2' AS check_old_and_new;
 
-key  |data         
------+-------------
-key-a|val-a-s1-ups2
+key  |data         |check_old_and_new
+-----+-------------+-----------------
+key-a|val-a-s1-ups2|t                
 (1 row)
 
 step s2_c: COMMIT;
@@ -205,11 +205,11 @@ step s2_del_a:
     WHERE
         noisy_oper('upd', key, '=', 'key-a') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *
+    RETURNING *, data = old.data AS check_old;
 
-key  |data         
------+-------------
-key-a|val-a-s1-ups1
+key  |data         |check_old
+-----+-------------+---------
+key-a|val-a-s1-ups1|t        
 (1 row)
 
 step s2_c: COMMIT;
@@ -277,11 +277,11 @@ step s2_del_a:
     WHERE
         noisy_oper('upd', key, '=', 'key-a') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *
+    RETURNING *, data = old.data AS check_old;
 
-key  |data    
------+--------
-key-a|val-a-s1
+key  |data    |check_old
+-----+--------+---------
+key-a|val-a-s1|t        
 (1 row)
 
 step s2_c: COMMIT;
@@ -343,7 +343,7 @@ step s2_upd_a_data:
     WHERE
         noisy_oper('upd', key, '=', 'key-a') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *;
+    RETURNING *, new.data = old.data || '-ups2' AS check_old_and_new;
  <waiting ...>
 step s1_c: COMMIT;
 s2: NOTICE:  upd: text key-a = text key-a: t
@@ -352,9 +352,9 @@ s2: NOTICE:  trigger: name rep_b_u; when: BEFORE; lev: ROWs; op: UPDATE; old: (k
 s2: NOTICE:  upd: text key-b = text key-a: f
 s2: NOTICE:  trigger: name rep_a_u; when: AFTER; lev: ROWs; op: UPDATE; old: (key-a,val-a-s1-ups1) new: (key-a,val-a-s1-ups1-ups2)
 step s2_upd_a_data: <... completed>
-key  |data              
------+------------------
-key-a|val-a-s1-ups1-ups2
+key  |data              |check_old_and_new
+-----+------------------+-----------------
+key-a|val-a-s1-ups1-ups2|t                
 (1 row)
 
 step s2_c: COMMIT;
@@ -417,16 +417,16 @@ step s2_upd_a_data:
     WHERE
         noisy_oper('upd', key, '=', 'key-a') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *;
+    RETURNING *, new.data = old.data || '-ups2' AS check_old_and_new;
  <waiting ...>
 step s1_r: ROLLBACK;
 s2: NOTICE:  trigger: name rep_b_u; when: BEFORE; lev: ROWs; op: UPDATE; old: (key-a,val-a-s1) new: (key-a,val-a-s1-ups2)
 s2: NOTICE:  upd: text key-b = text key-a: f
 s2: NOTICE:  trigger: name rep_a_u; when: AFTER; lev: ROWs; op: UPDATE; old: (key-a,val-a-s1) new: (key-a,val-a-s1-ups2)
 step s2_upd_a_data: <... completed>
-key  |data         
------+-------------
-key-a|val-a-s1-ups2
+key  |data         |check_old_and_new
+-----+-------------+-----------------
+key-a|val-a-s1-ups2|t                
 (1 row)
 
 step s2_c: COMMIT;
@@ -491,7 +491,7 @@ step s2_upd_a_data:
     WHERE
         noisy_oper('upd', key, '=', 'key-a') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *;
+    RETURNING *, new.data = old.data || '-ups2' AS check_old_and_new;
  <waiting ...>
 step s1_c: COMMIT;
 s2: NOTICE:  upd: text key-a = text key-a: t
@@ -500,9 +500,9 @@ s2: NOTICE:  trigger: name rep_b_u; when: BEFORE; lev: ROWs; op: UPDATE; old: (k
 s2: NOTICE:  upd: text key-b = text key-a: f
 s2: NOTICE:  trigger: name rep_a_u; when: AFTER; lev: ROWs; op: UPDATE; old: (key-a,val-a-s1-ups1) new: (key-a,val-a-s1-ups1-ups2)
 step s2_upd_a_data: <... completed>
-key  |data              
------+------------------
-key-a|val-a-s1-ups1-ups2
+key  |data              |check_old_and_new
+-----+------------------+-----------------
+key-a|val-a-s1-ups1-ups2|t                
 (1 row)
 
 step s2_c: COMMIT;
@@ -567,16 +567,16 @@ step s2_upd_a_data:
     WHERE
         noisy_oper('upd', key, '=', 'key-a') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *;
+    RETURNING *, new.data = old.data || '-ups2' AS check_old_and_new;
  <waiting ...>
 step s1_r: ROLLBACK;
 s2: NOTICE:  trigger: name rep_b_u; when: BEFORE; lev: ROWs; op: UPDATE; old: (key-a,val-a-s1) new: (key-a,val-a-s1-ups2)
 s2: NOTICE:  upd: text key-b = text key-a: f
 s2: NOTICE:  trigger: name rep_a_u; when: AFTER; lev: ROWs; op: UPDATE; old: (key-a,val-a-s1) new: (key-a,val-a-s1-ups2)
 step s2_upd_a_data: <... completed>
-key  |data         
------+-------------
-key-a|val-a-s1-ups2
+key  |data         |check_old_and_new
+-----+-------------+-----------------
+key-a|val-a-s1-ups2|t                
 (1 row)
 
 step s2_c: COMMIT;
@@ -641,13 +641,13 @@ step s2_upd_a_data:
     WHERE
         noisy_oper('upd', key, '=', 'key-a') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *;
+    RETURNING *, new.data = old.data || '-ups2' AS check_old_and_new;
  <waiting ...>
 step s1_c: COMMIT;
 s2: NOTICE:  upd: text key-b = text key-a: f
 step s2_upd_a_data: <... completed>
-key|data
----+----
+key|data|check_old_and_new
+---+----+-----------------
 (0 rows)
 
 step s2_c: COMMIT;
@@ -711,16 +711,16 @@ step s2_upd_a_data:
     WHERE
         noisy_oper('upd', key, '=', 'key-a') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *;
+    RETURNING *, new.data = old.data || '-ups2' AS check_old_and_new;
  <waiting ...>
 step s1_r: ROLLBACK;
 s2: NOTICE:  trigger: name rep_b_u; when: BEFORE; lev: ROWs; op: UPDATE; old: (key-a,val-a-s1) new: (key-a,val-a-s1-ups2)
 s2: NOTICE:  upd: text key-b = text key-a: f
 s2: NOTICE:  trigger: name rep_a_u; when: AFTER; lev: ROWs; op: UPDATE; old: (key-a,val-a-s1) new: (key-a,val-a-s1-ups2)
 step s2_upd_a_data: <... completed>
-key  |data         
------+-------------
-key-a|val-a-s1-ups2
+key  |data         |check_old_and_new
+-----+-------------+-----------------
+key-a|val-a-s1-ups2|t                
 (1 row)
 
 step s2_c: COMMIT;
@@ -873,7 +873,7 @@ step s2_upsert_a_data:
         WHERE
             noisy_oper('upd', trigtest.key, '=', 'key-a') AND
             noisy_oper('upk', trigtest.data, '<>', 'mismatch')
-    RETURNING *;
+    RETURNING *, new.data = old.data || '-upserts2' AS check_old_and_new;
  <waiting ...>
 step s1_c: COMMIT;
 s2: NOTICE:  upd: text key-a = text key-a: t
@@ -881,9 +881,9 @@ s2: NOTICE:  upk: text val-a-s1-ups1 <> text mismatch: t
 s2: NOTICE:  trigger: name rep_b_u; when: BEFORE; lev: ROWs; op: UPDATE; old: (key-a,val-a-s1-ups1) new: (key-a,val-a-s1-ups1-upserts2)
 s2: NOTICE:  trigger: name rep_a_u; when: AFTER; lev: ROWs; op: UPDATE; old: (key-a,val-a-s1-ups1) new: (key-a,val-a-s1-ups1-upserts2)
 step s2_upsert_a_data: <... completed>
-key  |data                  
------+----------------------
-key-a|val-a-s1-ups1-upserts2
+key  |data                  |check_old_and_new
+-----+----------------------+-----------------
+key-a|val-a-s1-ups1-upserts2|t                
 (1 row)
 
 step s2_c: COMMIT;
@@ -955,7 +955,7 @@ step s2_upsert_a_data:
         WHERE
             noisy_oper('upd', trigtest.key, '=', 'key-a') AND
             noisy_oper('upk', trigtest.data, '<>', 'mismatch')
-    RETURNING *;
+    RETURNING *, new.data = old.data || '-upserts2' AS check_old_and_new;
  <waiting ...>
 step s1_c: COMMIT;
 s2: NOTICE:  upd: text key-a = text key-a: t
@@ -963,9 +963,9 @@ s2: NOTICE:  upk: text val-a-s1-ups1 <> text mismatch: t
 s2: NOTICE:  trigger: name rep_b_u; when: BEFORE; lev: ROWs; op: UPDATE; old: (key-a,val-a-s1-ups1) new: (key-a,val-a-s1-ups1-upserts2)
 s2: NOTICE:  trigger: name rep_a_u; when: AFTER; lev: ROWs; op: UPDATE; old: (key-a,val-a-s1-ups1) new: (key-a,val-a-s1-ups1-upserts2)
 step s2_upsert_a_data: <... completed>
-key  |data                  
------+----------------------
-key-a|val-a-s1-ups1-upserts2
+key  |data                  |check_old_and_new
+-----+----------------------+-----------------
+key-a|val-a-s1-ups1-upserts2|t                
 (1 row)
 
 step s2_c: COMMIT;
@@ -1012,7 +1012,7 @@ step s2_upsert_a_data:
         WHERE
             noisy_oper('upd', trigtest.key, '=', 'key-a') AND
             noisy_oper('upk', trigtest.data, '<>', 'mismatch')
-    RETURNING *;
+    RETURNING *, new.data = old.data || '-upserts2' AS check_old_and_new;
  <waiting ...>
 step s1_c: COMMIT;
 s2: NOTICE:  upd: text key-a = text key-a: t
@@ -1020,9 +1020,9 @@ s2: NOTICE:  upk: text val-a-s1 <> text mismatch: t
 s2: NOTICE:  trigger: name rep_b_u; when: BEFORE; lev: ROWs; op: UPDATE; old: (key-a,val-a-s1) new: (key-a,val-a-s1-upserts2)
 s2: NOTICE:  trigger: name rep_a_u; when: AFTER; lev: ROWs; op: UPDATE; old: (key-a,val-a-s1) new: (key-a,val-a-s1-upserts2)
 step s2_upsert_a_data: <... completed>
-key  |data             
------+-----------------
-key-a|val-a-s1-upserts2
+key  |data             |check_old_and_new
+-----+-----------------+-----------------
+key-a|val-a-s1-upserts2|t                
 (1 row)
 
 step s2_c: COMMIT;
@@ -1068,14 +1068,14 @@ step s2_upsert_a_data:
         WHERE
             noisy_oper('upd', trigtest.key, '=', 'key-a') AND
             noisy_oper('upk', trigtest.data, '<>', 'mismatch')
-    RETURNING *;
+    RETURNING *, new.data = old.data || '-upserts2' AS check_old_and_new;
  <waiting ...>
 step s1_r: ROLLBACK;
 s2: NOTICE:  trigger: name rep_a_i; when: AFTER; lev: ROWs; op: INSERT; old: <NULL> new: (key-a,val-a-upss2)
 step s2_upsert_a_data: <... completed>
-key  |data       
------+-----------
-key-a|val-a-upss2
+key  |data       |check_old_and_new
+-----+-----------+-----------------
+key-a|val-a-upss2|                 
 (1 row)
 
 step s2_c: COMMIT;
@@ -1137,7 +1137,7 @@ step s2_upsert_a_data:
         WHERE
             noisy_oper('upd', trigtest.key, '=', 'key-a') AND
             noisy_oper('upk', trigtest.data, '<>', 'mismatch')
-    RETURNING *;
+    RETURNING *, new.data = old.data || '-upserts2' AS check_old_and_new;
  <waiting ...>
 step s1_c: COMMIT;
 s2: NOTICE:  upd: text key-a = text key-a: t
@@ -1145,9 +1145,9 @@ s2: NOTICE:  upk: text val-a-s1-ups1 <> text mismatch: t
 s2: NOTICE:  trigger: name rep_b_u; when: BEFORE; lev: ROWs; op: UPDATE; old: (key-a,val-a-s1-ups1) new: (key-a,val-a-s1-ups1-upserts2)
 s2: NOTICE:  trigger: name rep_a_u; when: AFTER; lev: ROWs; op: UPDATE; old: (key-a,val-a-s1-ups1) new: (key-a,val-a-s1-ups1-upserts2)
 step s2_upsert_a_data: <... completed>
-key  |data                  
------+----------------------
-key-a|val-a-s1-ups1-upserts2
+key  |data                  |check_old_and_new
+-----+----------------------+-----------------
+key-a|val-a-s1-ups1-upserts2|t                
 (1 row)
 
 step s2_c: COMMIT;
@@ -1209,14 +1209,14 @@ step s2_upsert_a_data:
         WHERE
             noisy_oper('upd', trigtest.key, '=', 'key-a') AND
             noisy_oper('upk', trigtest.data, '<>', 'mismatch')
-    RETURNING *;
+    RETURNING *, new.data = old.data || '-upserts2' AS check_old_and_new;
  <waiting ...>
 step s1_r: ROLLBACK;
 s2: NOTICE:  trigger: name rep_a_i; when: AFTER; lev: ROWs; op: INSERT; old: <NULL> new: (key-a,val-a-upss2)
 step s2_upsert_a_data: <... completed>
-key  |data       
------+-----------
-key-a|val-a-upss2
+key  |data       |check_old_and_new
+-----+-----------+-----------------
+key-a|val-a-upss2|                 
 (1 row)
 
 step s2_c: COMMIT;
@@ -1276,7 +1276,7 @@ step s2_upd_a_data:
     WHERE
         noisy_oper('upd', key, '=', 'key-a') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *;
+    RETURNING *, new.data = old.data || '-ups2' AS check_old_and_new;
  <waiting ...>
 step s1_c: COMMIT;
 s2: NOTICE:  upd: text key-a = text key-a: t
@@ -1284,9 +1284,9 @@ s2: NOTICE:  upk: text val-a-s1-ups1 <> text mismatch: t
 s2: NOTICE:  upd: text key-b = text key-a: f
 s2: NOTICE:  trigger: name rep_a_u; when: AFTER; lev: ROWs; op: UPDATE; old: (key-a,val-a-s1-ups1) new: (key-a,val-a-s1-ups1-ups2)
 step s2_upd_a_data: <... completed>
-key  |data              
------+------------------
-key-a|val-a-s1-ups1-ups2
+key  |data              |check_old_and_new
+-----+------------------+-----------------
+key-a|val-a-s1-ups1-ups2|t                
 (1 row)
 
 step s2_c: COMMIT;
@@ -1347,15 +1347,15 @@ step s2_upd_a_data:
     WHERE
         noisy_oper('upd', key, '=', 'key-a') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *;
+    RETURNING *, new.data = old.data || '-ups2' AS check_old_and_new;
  <waiting ...>
 step s1_r: ROLLBACK;
 s2: NOTICE:  upd: text key-b = text key-a: f
 s2: NOTICE:  trigger: name rep_a_u; when: AFTER; lev: ROWs; op: UPDATE; old: (key-a,val-a-s1) new: (key-a,val-a-s1-ups2)
 step s2_upd_a_data: <... completed>
-key  |data         
------+-------------
-key-a|val-a-s1-ups2
+key  |data         |check_old_and_new
+-----+-------------+-----------------
+key-a|val-a-s1-ups2|t                
 (1 row)
 
 step s2_c: COMMIT;
@@ -1417,7 +1417,7 @@ step s2_del_a:
     WHERE
         noisy_oper('upd', key, '=', 'key-a') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *
+    RETURNING *, data = old.data AS check_old;
  <waiting ...>
 step s1_c: COMMIT;
 s2: NOTICE:  upd: text key-a = text key-a: t
@@ -1425,9 +1425,9 @@ s2: NOTICE:  upk: text val-a-s1-ups1 <> text mismatch: t
 s2: NOTICE:  upd: text key-b = text key-a: f
 s2: NOTICE:  trigger: name rep_a_d; when: AFTER; lev: ROWs; op: DELETE; old: (key-a,val-a-s1-ups1) new: <NULL>
 step s2_del_a: <... completed>
-key  |data         
------+-------------
-key-a|val-a-s1-ups1
+key  |data         |check_old
+-----+-------------+---------
+key-a|val-a-s1-ups1|t        
 (1 row)
 
 step s2_c: COMMIT;
@@ -1488,15 +1488,15 @@ step s2_del_a:
     WHERE
         noisy_oper('upd', key, '=', 'key-a') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *
+    RETURNING *, data = old.data AS check_old;
  <waiting ...>
 step s1_r: ROLLBACK;
 s2: NOTICE:  upd: text key-b = text key-a: f
 s2: NOTICE:  trigger: name rep_a_d; when: AFTER; lev: ROWs; op: DELETE; old: (key-a,val-a-s1) new: <NULL>
 step s2_del_a: <... completed>
-key  |data    
------+--------
-key-a|val-a-s1
+key  |data    |check_old
+-----+--------+---------
+key-a|val-a-s1|t        
 (1 row)
 
 step s2_c: COMMIT;
@@ -1557,13 +1557,13 @@ step s2_upd_a_data:
     WHERE
         noisy_oper('upd', key, '=', 'key-a') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *;
+    RETURNING *, new.data = old.data || '-ups2' AS check_old_and_new;
  <waiting ...>
 step s1_c: COMMIT;
 s2: NOTICE:  upd: text key-b = text key-a: f
 step s2_upd_a_data: <... completed>
-key|data
----+----
+key|data|check_old_and_new
+---+----+-----------------
 (0 rows)
 
 step s2_c: COMMIT;
@@ -1624,15 +1624,15 @@ step s2_upd_a_data:
     WHERE
         noisy_oper('upd', key, '=', 'key-a') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *;
+    RETURNING *, new.data = old.data || '-ups2' AS check_old_and_new;
  <waiting ...>
 step s1_r: ROLLBACK;
 s2: NOTICE:  upd: text key-b = text key-a: f
 s2: NOTICE:  trigger: name rep_a_u; when: AFTER; lev: ROWs; op: UPDATE; old: (key-a,val-a-s1) new: (key-a,val-a-s1-ups2)
 step s2_upd_a_data: <... completed>
-key  |data         
------+-------------
-key-a|val-a-s1-ups2
+key  |data         |check_old_and_new
+-----+-------------+-----------------
+key-a|val-a-s1-ups2|t                
 (1 row)
 
 step s2_c: COMMIT;
@@ -1693,13 +1693,13 @@ step s2_del_a:
     WHERE
         noisy_oper('upd', key, '=', 'key-a') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *
+    RETURNING *, data = old.data AS check_old;
  <waiting ...>
 step s1_c: COMMIT;
 s2: NOTICE:  upd: text key-b = text key-a: f
 step s2_del_a: <... completed>
-key|data
----+----
+key|data|check_old
+---+----+---------
 (0 rows)
 
 step s2_c: COMMIT;
@@ -1759,15 +1759,15 @@ step s2_del_a:
     WHERE
         noisy_oper('upd', key, '=', 'key-a') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *
+    RETURNING *, data = old.data AS check_old;
  <waiting ...>
 step s1_r: ROLLBACK;
 s2: NOTICE:  upd: text key-b = text key-a: f
 s2: NOTICE:  trigger: name rep_a_d; when: AFTER; lev: ROWs; op: DELETE; old: (key-a,val-a-s1) new: <NULL>
 step s2_del_a: <... completed>
-key  |data    
------+--------
-key-a|val-a-s1
+key  |data    |check_old
+-----+--------+---------
+key-a|val-a-s1|t        
 (1 row)
 
 step s2_c: COMMIT;
@@ -1829,14 +1829,14 @@ step s2_upd_a_data:
     WHERE
         noisy_oper('upd', key, '=', 'key-a') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *;
+    RETURNING *, new.data = old.data || '-ups2' AS check_old_and_new;
  <waiting ...>
 step s1_c: COMMIT;
 s2: NOTICE:  upd: text key-b = text key-a: f
 s2: NOTICE:  upd: text key-c = text key-a: f
 step s2_upd_a_data: <... completed>
-key|data
----+----
+key|data|check_old_and_new
+---+----+-----------------
 (0 rows)
 
 step s2_c: COMMIT;
@@ -1899,16 +1899,16 @@ step s2_upd_a_data:
     WHERE
         noisy_oper('upd', key, '=', 'key-a') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *;
+    RETURNING *, new.data = old.data || '-ups2' AS check_old_and_new;
  <waiting ...>
 step s1_r: ROLLBACK;
 s2: NOTICE:  trigger: name rep_b_u; when: BEFORE; lev: ROWs; op: UPDATE; old: (key-a,val-a-s1) new: (key-a,val-a-s1-ups2)
 s2: NOTICE:  upd: text key-c = text key-a: f
 s2: NOTICE:  trigger: name rep_a_u; when: AFTER; lev: ROWs; op: UPDATE; old: (key-a,val-a-s1) new: (key-a,val-a-s1-ups2)
 step s2_upd_a_data: <... completed>
-key  |data         
------+-------------
-key-a|val-a-s1-ups2
+key  |data         |check_old_and_new
+-----+-------------+-----------------
+key-a|val-a-s1-ups2|t                
 (1 row)
 
 step s2_c: COMMIT;
@@ -2038,7 +2038,7 @@ step s2_upd_all_data:
     WHERE
         noisy_oper('upd', key, '<>', 'mismatch') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *;
+    RETURNING *, new.data = old.data || '-ups2' AS check_old_and_new;
  <waiting ...>
 step s1_c: COMMIT;
 s2: NOTICE:  upd: text key-b <> text mismatch: t
@@ -2050,10 +2050,10 @@ s2: NOTICE:  trigger: name rep_b_u; when: BEFORE; lev: ROWs; op: UPDATE; old: (k
 s2: NOTICE:  trigger: name rep_a_u; when: AFTER; lev: ROWs; op: UPDATE; old: (key-b,val-a-s1-tobs1) new: (key-b,val-a-s1-tobs1-ups2)
 s2: NOTICE:  trigger: name rep_a_u; when: AFTER; lev: ROWs; op: UPDATE; old: (key-c,val-c-s1) new: (key-c,val-c-s1-ups2)
 step s2_upd_all_data: <... completed>
-key  |data               
------+-------------------
-key-b|val-a-s1-tobs1-ups2
-key-c|val-c-s1-ups2      
+key  |data               |check_old_and_new
+-----+-------------------+-----------------
+key-b|val-a-s1-tobs1-ups2|t                
+key-c|val-c-s1-ups2      |t                
 (2 rows)
 
 step s2_c: COMMIT;
@@ -2118,13 +2118,13 @@ step s2_upd_a_data:
     WHERE
         noisy_oper('upd', key, '=', 'key-a') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *;
+    RETURNING *, new.data = old.data || '-ups2' AS check_old_and_new;
  <waiting ...>
 step s1_c: COMMIT;
 s2: NOTICE:  upd: text key-c = text key-a: f
 step s2_upd_a_data: <... completed>
-key|data
----+----
+key|data|check_old_and_new
+---+----+-----------------
 (0 rows)
 
 step s2_c: COMMIT;
@@ -2188,16 +2188,16 @@ step s2_upd_a_data:
     WHERE
         noisy_oper('upd', key, '=', 'key-a') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *;
+    RETURNING *, new.data = old.data || '-ups2' AS check_old_and_new;
  <waiting ...>
 step s1_r: ROLLBACK;
 s2: NOTICE:  trigger: name rep_b_u; when: BEFORE; lev: ROWs; op: UPDATE; old: (key-a,val-a-s1) new: (key-a,val-a-s1-ups2)
 s2: NOTICE:  upd: text key-c = text key-a: f
 s2: NOTICE:  trigger: name rep_a_u; when: AFTER; lev: ROWs; op: UPDATE; old: (key-a,val-a-s1) new: (key-a,val-a-s1-ups2)
 step s2_upd_a_data: <... completed>
-key  |data         
------+-------------
-key-a|val-a-s1-ups2
+key  |data         |check_old_and_new
+-----+-------------+-----------------
+key-a|val-a-s1-ups2|t                
 (1 row)
 
 step s2_c: COMMIT;
@@ -2260,13 +2260,13 @@ step s2_del_a:
     WHERE
         noisy_oper('upd', key, '=', 'key-a') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *
+    RETURNING *, data = old.data AS check_old;
  <waiting ...>
 step s1_c: COMMIT;
 s2: NOTICE:  upd: text key-c = text key-a: f
 step s2_del_a: <... completed>
-key|data
----+----
+key|data|check_old
+---+----+---------
 (0 rows)
 
 step s2_c: COMMIT;
@@ -2328,16 +2328,16 @@ step s2_del_a:
     WHERE
         noisy_oper('upd', key, '=', 'key-a') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *
+    RETURNING *, data = old.data AS check_old;
  <waiting ...>
 step s1_r: ROLLBACK;
 s2: NOTICE:  trigger: name rep_b_d; when: BEFORE; lev: ROWs; op: DELETE; old: (key-a,val-a-s1) new: <NULL>
 s2: NOTICE:  upd: text key-c = text key-a: f
 s2: NOTICE:  trigger: name rep_a_d; when: AFTER; lev: ROWs; op: DELETE; old: (key-a,val-a-s1) new: <NULL>
 step s2_del_a: <... completed>
-key  |data    
------+--------
-key-a|val-a-s1
+key  |data    |check_old
+-----+--------+---------
+key-a|val-a-s1|t        
 (1 row)
 
 step s2_c: COMMIT;
@@ -2507,7 +2507,7 @@ step s2_upd_a_data:
     WHERE
         noisy_oper('upd', key, '=', 'key-a') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *;
+    RETURNING *, new.data = old.data || '-ups2' AS check_old_and_new;
  <waiting ...>
 step s1_c: COMMIT;
 step s2_upd_a_data: <... completed>
@@ -2572,16 +2572,16 @@ step s2_upd_a_data:
     WHERE
         noisy_oper('upd', key, '=', 'key-a') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *;
+    RETURNING *, new.data = old.data || '-ups2' AS check_old_and_new;
  <waiting ...>
 step s1_r: ROLLBACK;
 s2: NOTICE:  trigger: name rep_b_u; when: BEFORE; lev: ROWs; op: UPDATE; old: (key-a,val-a-s1) new: (key-a,val-a-s1-ups2)
 s2: NOTICE:  upd: text key-b = text key-a: f
 s2: NOTICE:  trigger: name rep_a_u; when: AFTER; lev: ROWs; op: UPDATE; old: (key-a,val-a-s1) new: (key-a,val-a-s1-ups2)
 step s2_upd_a_data: <... completed>
-key  |data         
------+-------------
-key-a|val-a-s1-ups2
+key  |data         |check_old_and_new
+-----+-------------+-----------------
+key-a|val-a-s1-ups2|t                
 (1 row)
 
 step s2_c: COMMIT;
@@ -2646,7 +2646,7 @@ step s2_upd_a_data:
     WHERE
         noisy_oper('upd', key, '=', 'key-a') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *;
+    RETURNING *, new.data = old.data || '-ups2' AS check_old_and_new;
  <waiting ...>
 step s1_c: COMMIT;
 step s2_upd_a_data: <... completed>
@@ -2712,16 +2712,16 @@ step s2_upd_a_data:
     WHERE
         noisy_oper('upd', key, '=', 'key-a') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *;
+    RETURNING *, new.data = old.data || '-ups2' AS check_old_and_new;
  <waiting ...>
 step s1_r: ROLLBACK;
 s2: NOTICE:  trigger: name rep_b_u; when: BEFORE; lev: ROWs; op: UPDATE; old: (key-a,val-a-s1) new: (key-a,val-a-s1-ups2)
 s2: NOTICE:  upd: text key-b = text key-a: f
 s2: NOTICE:  trigger: name rep_a_u; when: AFTER; lev: ROWs; op: UPDATE; old: (key-a,val-a-s1) new: (key-a,val-a-s1-ups2)
 step s2_upd_a_data: <... completed>
-key  |data         
------+-------------
-key-a|val-a-s1-ups2
+key  |data         |check_old_and_new
+-----+-------------+-----------------
+key-a|val-a-s1-ups2|t                
 (1 row)
 
 step s2_c: COMMIT;
index 4250b85af2d3cf3dd9fdd3d3157e3fefe7a5a8cd..10ef2ad2fcd5ce26c0d200e9a3412665780640d8 100644 (file)
@@ -197,15 +197,23 @@ step merge_bal:
   MERGE INTO target t
   USING (SELECT 1 as key) s
   ON s.key = t.key
+  WHEN MATCHED AND balance < 0 THEN
+    DELETE
   WHEN MATCHED AND balance < 100 THEN
        UPDATE SET balance = balance * 2, val = t.val || ' when1'
   WHEN MATCHED AND balance < 200 THEN
        UPDATE SET balance = balance * 4, val = t.val || ' when2'
   WHEN MATCHED AND balance < 300 THEN
-       UPDATE SET balance = balance * 8, val = t.val || ' when3';
+       UPDATE SET balance = balance * 8, val = t.val || ' when3'
+  RETURNING t.key, old.balance AS old_balance, new.balance AS new_balance, t.status, t.val;
  <waiting ...>
 step c2: COMMIT;
 step merge_bal: <... completed>
+key|old_balance|new_balance|status|val                               
+---+-----------+-----------+------+----------------------------------
+  1|         50|        100|s1    |setup updated by update_bal1 when1
+(1 row)
+
 step select1: SELECT * FROM target;
 key|balance|status|val                               
 ---+-------+------+----------------------------------
@@ -220,15 +228,23 @@ step merge_bal_pa:
   MERGE INTO target_pa t
   USING (SELECT 1 as key) s
   ON s.key = t.key
+  WHEN MATCHED AND balance < 0 THEN
+    DELETE
   WHEN MATCHED AND balance < 100 THEN
        UPDATE SET balance = balance * 2, val = t.val || ' when1'
   WHEN MATCHED AND balance < 200 THEN
        UPDATE SET balance = balance * 4, val = t.val || ' when2'
   WHEN MATCHED AND balance < 300 THEN
-       UPDATE SET balance = balance * 8, val = t.val || ' when3';
+       UPDATE SET balance = balance * 8, val = t.val || ' when3'
+  RETURNING t.key, old.balance AS old_balance, new.balance AS new_balance, t.status, t.val;
  <waiting ...>
 step c2: COMMIT;
 step merge_bal_pa: <... completed>
+key|old_balance|new_balance|status|val                                  
+---+-----------+-----------+------+-------------------------------------
+  1|         50|        100|s1    |setup updated by update_bal1_pa when1
+(1 row)
+
 step select1_pa: SELECT * FROM target_pa;
 key|balance|status|val                                  
 ---+-------+------+-------------------------------------
@@ -245,22 +261,24 @@ step merge_bal_tg:
     MERGE INTO target_tg t
     USING (SELECT 1 as key) s
     ON s.key = t.key
+    WHEN MATCHED AND balance < 0 THEN
+      DELETE
     WHEN MATCHED AND balance < 100 THEN
       UPDATE SET balance = balance * 2, val = t.val || ' when1'
     WHEN MATCHED AND balance < 200 THEN
       UPDATE SET balance = balance * 4, val = t.val || ' when2'
     WHEN MATCHED AND balance < 300 THEN
       UPDATE SET balance = balance * 8, val = t.val || ' when3'
-    RETURNING t.*
+    RETURNING t.key, old.balance AS old_balance, new.balance AS new_balance, t.status, t.val
   )
   SELECT * FROM t;
  <waiting ...>
 step c2: COMMIT;
 s1: NOTICE:  Update: (1,50,s1,"setup updated by update_bal1_tg") -> (1,100,s1,"setup updated by update_bal1_tg when1")
 step merge_bal_tg: <... completed>
-key|balance|status|val                                  
----+-------+------+-------------------------------------
-  1|    100|s1    |setup updated by update_bal1_tg when1
+key|old_balance|new_balance|status|val                                  
+---+-----------+-----------+------+-------------------------------------
+  1|         50|        100|s1    |setup updated by update_bal1_tg when1
 (1 row)
 
 step select1_tg: SELECT * FROM target_tg;
@@ -278,15 +296,23 @@ step merge_bal:
   MERGE INTO target t
   USING (SELECT 1 as key) s
   ON s.key = t.key
+  WHEN MATCHED AND balance < 0 THEN
+    DELETE
   WHEN MATCHED AND balance < 100 THEN
        UPDATE SET balance = balance * 2, val = t.val || ' when1'
   WHEN MATCHED AND balance < 200 THEN
        UPDATE SET balance = balance * 4, val = t.val || ' when2'
   WHEN MATCHED AND balance < 300 THEN
-       UPDATE SET balance = balance * 8, val = t.val || ' when3';
+       UPDATE SET balance = balance * 8, val = t.val || ' when3'
+  RETURNING t.key, old.balance AS old_balance, new.balance AS new_balance, t.status, t.val;
  <waiting ...>
 step c2: COMMIT;
 step merge_bal: <... completed>
+key|old_balance|new_balance|status|val                                              
+---+-----------+-----------+------+-------------------------------------------------
+  1|         70|        140|s1    |setup updated by update1 updated by update6 when1
+(1 row)
+
 step select1: SELECT * FROM target;
 key|balance|status|val                                              
 ---+-------+------+-------------------------------------------------
@@ -302,15 +328,23 @@ step merge_bal_pa:
   MERGE INTO target_pa t
   USING (SELECT 1 as key) s
   ON s.key = t.key
+  WHEN MATCHED AND balance < 0 THEN
+    DELETE
   WHEN MATCHED AND balance < 100 THEN
        UPDATE SET balance = balance * 2, val = t.val || ' when1'
   WHEN MATCHED AND balance < 200 THEN
        UPDATE SET balance = balance * 4, val = t.val || ' when2'
   WHEN MATCHED AND balance < 300 THEN
-       UPDATE SET balance = balance * 8, val = t.val || ' when3';
+       UPDATE SET balance = balance * 8, val = t.val || ' when3'
+  RETURNING t.key, old.balance AS old_balance, new.balance AS new_balance, t.status, t.val;
  <waiting ...>
 step c2: COMMIT;
 step merge_bal_pa: <... completed>
+key|old_balance|new_balance|status|val                                                    
+---+-----------+-----------+------+-------------------------------------------------------
+  1|         70|        140|s1    |setup updated by update1_pa updated by update6_pa when1
+(1 row)
+
 step select1_pa: SELECT * FROM target_pa;
 key|balance|status|val                                                    
 ---+-------+------+-------------------------------------------------------
@@ -329,22 +363,24 @@ step merge_bal_tg:
     MERGE INTO target_tg t
     USING (SELECT 1 as key) s
     ON s.key = t.key
+    WHEN MATCHED AND balance < 0 THEN
+      DELETE
     WHEN MATCHED AND balance < 100 THEN
       UPDATE SET balance = balance * 2, val = t.val || ' when1'
     WHEN MATCHED AND balance < 200 THEN
       UPDATE SET balance = balance * 4, val = t.val || ' when2'
     WHEN MATCHED AND balance < 300 THEN
       UPDATE SET balance = balance * 8, val = t.val || ' when3'
-    RETURNING t.*
+    RETURNING t.key, old.balance AS old_balance, new.balance AS new_balance, t.status, t.val
   )
   SELECT * FROM t;
  <waiting ...>
 step c2: COMMIT;
 s1: NOTICE:  Update: (1,70,s1,"setup updated by update1_tg updated by update6_tg") -> (1,140,s1,"setup updated by update1_tg updated by update6_tg when1")
 step merge_bal_tg: <... completed>
-key|balance|status|val                                                    
----+-------+------+-------------------------------------------------------
-  1|    140|s1    |setup updated by update1_tg updated by update6_tg when1
+key|old_balance|new_balance|status|val                                                    
+---+-----------+-----------+------+-------------------------------------------------------
+  1|         70|        140|s1    |setup updated by update1_tg updated by update6_tg when1
 (1 row)
 
 step select1_tg: SELECT * FROM target_tg;
@@ -355,6 +391,105 @@ key|balance|status|val
 
 step c1: COMMIT;
 
+starting permutation: update6 update6 merge_bal c2 select1 c1
+step update6: UPDATE target t SET balance = balance - 100, val = t.val || ' updated by update6' WHERE t.key = 1;
+step update6: UPDATE target t SET balance = balance - 100, val = t.val || ' updated by update6' WHERE t.key = 1;
+step merge_bal: 
+  MERGE INTO target t
+  USING (SELECT 1 as key) s
+  ON s.key = t.key
+  WHEN MATCHED AND balance < 0 THEN
+    DELETE
+  WHEN MATCHED AND balance < 100 THEN
+       UPDATE SET balance = balance * 2, val = t.val || ' when1'
+  WHEN MATCHED AND balance < 200 THEN
+       UPDATE SET balance = balance * 4, val = t.val || ' when2'
+  WHEN MATCHED AND balance < 300 THEN
+       UPDATE SET balance = balance * 8, val = t.val || ' when3'
+  RETURNING t.key, old.balance AS old_balance, new.balance AS new_balance, t.status, t.val;
+ <waiting ...>
+step c2: COMMIT;
+step merge_bal: <... completed>
+key|old_balance|new_balance|status|val                                        
+---+-----------+-----------+------+-------------------------------------------
+  1|        -40|           |s1    |setup updated by update6 updated by update6
+(1 row)
+
+step select1: SELECT * FROM target;
+key|balance|status|val
+---+-------+------+---
+(0 rows)
+
+step c1: COMMIT;
+
+starting permutation: update6_pa update6_pa merge_bal_pa c2 select1_pa c1
+step update6_pa: UPDATE target_pa t SET balance = balance - 100, val = t.val || ' updated by update6_pa' WHERE t.key = 1;
+step update6_pa: UPDATE target_pa t SET balance = balance - 100, val = t.val || ' updated by update6_pa' WHERE t.key = 1;
+step merge_bal_pa: 
+  MERGE INTO target_pa t
+  USING (SELECT 1 as key) s
+  ON s.key = t.key
+  WHEN MATCHED AND balance < 0 THEN
+    DELETE
+  WHEN MATCHED AND balance < 100 THEN
+       UPDATE SET balance = balance * 2, val = t.val || ' when1'
+  WHEN MATCHED AND balance < 200 THEN
+       UPDATE SET balance = balance * 4, val = t.val || ' when2'
+  WHEN MATCHED AND balance < 300 THEN
+       UPDATE SET balance = balance * 8, val = t.val || ' when3'
+  RETURNING t.key, old.balance AS old_balance, new.balance AS new_balance, t.status, t.val;
+ <waiting ...>
+step c2: COMMIT;
+step merge_bal_pa: <... completed>
+key|old_balance|new_balance|status|val                                              
+---+-----------+-----------+------+-------------------------------------------------
+  1|        -40|           |s1    |setup updated by update6_pa updated by update6_pa
+(1 row)
+
+step select1_pa: SELECT * FROM target_pa;
+key|balance|status|val
+---+-------+------+---
+(0 rows)
+
+step c1: COMMIT;
+
+starting permutation: update6_tg update6_tg merge_bal_tg c2 select1_tg c1
+s2: NOTICE:  Update: (1,160,s1,setup) -> (1,60,s1,"setup updated by update6_tg")
+step update6_tg: UPDATE target_tg t SET balance = balance - 100, val = t.val || ' updated by update6_tg' WHERE t.key = 1;
+s2: NOTICE:  Update: (1,60,s1,"setup updated by update6_tg") -> (1,-40,s1,"setup updated by update6_tg updated by update6_tg")
+step update6_tg: UPDATE target_tg t SET balance = balance - 100, val = t.val || ' updated by update6_tg' WHERE t.key = 1;
+step merge_bal_tg: 
+  WITH t AS (
+    MERGE INTO target_tg t
+    USING (SELECT 1 as key) s
+    ON s.key = t.key
+    WHEN MATCHED AND balance < 0 THEN
+      DELETE
+    WHEN MATCHED AND balance < 100 THEN
+      UPDATE SET balance = balance * 2, val = t.val || ' when1'
+    WHEN MATCHED AND balance < 200 THEN
+      UPDATE SET balance = balance * 4, val = t.val || ' when2'
+    WHEN MATCHED AND balance < 300 THEN
+      UPDATE SET balance = balance * 8, val = t.val || ' when3'
+    RETURNING t.key, old.balance AS old_balance, new.balance AS new_balance, t.status, t.val
+  )
+  SELECT * FROM t;
+ <waiting ...>
+step c2: COMMIT;
+s1: NOTICE:  Delete: (1,-40,s1,"setup updated by update6_tg updated by update6_tg")
+step merge_bal_tg: <... completed>
+key|old_balance|new_balance|status|val                                              
+---+-----------+-----------+------+-------------------------------------------------
+  1|        -40|           |s1    |setup updated by update6_tg updated by update6_tg
+(1 row)
+
+step select1_tg: SELECT * FROM target_tg;
+key|balance|status|val
+---+-------+------+---
+(0 rows)
+
+step c1: COMMIT;
+
 starting permutation: update7 update6 merge_bal c2 select1 c1
 step update7: UPDATE target t SET balance = 350, val = t.val || ' updated by update7' WHERE t.key = 1;
 step update6: UPDATE target t SET balance = balance - 100, val = t.val || ' updated by update6' WHERE t.key = 1;
@@ -362,15 +497,23 @@ step merge_bal:
   MERGE INTO target t
   USING (SELECT 1 as key) s
   ON s.key = t.key
+  WHEN MATCHED AND balance < 0 THEN
+    DELETE
   WHEN MATCHED AND balance < 100 THEN
        UPDATE SET balance = balance * 2, val = t.val || ' when1'
   WHEN MATCHED AND balance < 200 THEN
        UPDATE SET balance = balance * 4, val = t.val || ' when2'
   WHEN MATCHED AND balance < 300 THEN
-       UPDATE SET balance = balance * 8, val = t.val || ' when3';
+       UPDATE SET balance = balance * 8, val = t.val || ' when3'
+  RETURNING t.key, old.balance AS old_balance, new.balance AS new_balance, t.status, t.val;
  <waiting ...>
 step c2: COMMIT;
 step merge_bal: <... completed>
+key|old_balance|new_balance|status|val                                              
+---+-----------+-----------+------+-------------------------------------------------
+  1|        250|       2000|s1    |setup updated by update7 updated by update6 when3
+(1 row)
+
 step select1: SELECT * FROM target;
 key|balance|status|val                                              
 ---+-------+------+-------------------------------------------------
@@ -385,12 +528,15 @@ step merge_bal_pa:
   MERGE INTO target_pa t
   USING (SELECT 1 as key) s
   ON s.key = t.key
+  WHEN MATCHED AND balance < 0 THEN
+    DELETE
   WHEN MATCHED AND balance < 100 THEN
        UPDATE SET balance = balance * 2, val = t.val || ' when1'
   WHEN MATCHED AND balance < 200 THEN
        UPDATE SET balance = balance * 4, val = t.val || ' when2'
   WHEN MATCHED AND balance < 300 THEN
-       UPDATE SET balance = balance * 8, val = t.val || ' when3';
+       UPDATE SET balance = balance * 8, val = t.val || ' when3'
+  RETURNING t.key, old.balance AS old_balance, new.balance AS new_balance, t.status, t.val;
  <waiting ...>
 step c2: COMMIT;
 step merge_bal_pa: <... completed>
@@ -404,12 +550,15 @@ step merge_bal_pa:
   MERGE INTO target_pa t
   USING (SELECT 1 as key) s
   ON s.key = t.key
+  WHEN MATCHED AND balance < 0 THEN
+    DELETE
   WHEN MATCHED AND balance < 100 THEN
        UPDATE SET balance = balance * 2, val = t.val || ' when1'
   WHEN MATCHED AND balance < 200 THEN
        UPDATE SET balance = balance * 4, val = t.val || ' when2'
   WHEN MATCHED AND balance < 300 THEN
-       UPDATE SET balance = balance * 8, val = t.val || ' when3';
+       UPDATE SET balance = balance * 8, val = t.val || ' when3'
+  RETURNING t.key, old.balance AS old_balance, new.balance AS new_balance, t.status, t.val;
  <waiting ...>
 step c2: COMMIT;
 step merge_bal_pa: <... completed>
index 232b3e27652a1f19d282d7df696e07792a31be50..575fbe010e61fe597b7f61a65b31fc91fec767b1 100644 (file)
@@ -120,14 +120,14 @@ step s2_del_a {
     WHERE
         noisy_oper('upd', key, '=', 'key-a') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *
+    RETURNING *, data = old.data AS check_old;
 }
 step s2_upd_a_data {
     UPDATE trigtest SET data = data || '-ups2'
     WHERE
         noisy_oper('upd', key, '=', 'key-a') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *;
+    RETURNING *, new.data = old.data || '-ups2' AS check_old_and_new;
 }
 step s2_upd_b_data {
     UPDATE trigtest SET data = data || '-ups2'
@@ -141,7 +141,7 @@ step s2_upd_all_data {
     WHERE
         noisy_oper('upd', key, '<>', 'mismatch') AND
         noisy_oper('upk', data, '<>', 'mismatch')
-    RETURNING *;
+    RETURNING *, new.data = old.data || '-ups2' AS check_old_and_new;
 }
 step s2_upsert_a_data {
     INSERT INTO trigtest VALUES ('key-a', 'val-a-upss2')
@@ -150,7 +150,7 @@ step s2_upsert_a_data {
         WHERE
             noisy_oper('upd', trigtest.key, '=', 'key-a') AND
             noisy_oper('upk', trigtest.data, '<>', 'mismatch')
-    RETURNING *;
+    RETURNING *, new.data = old.data || '-upserts2' AS check_old_and_new;
 }
 
 session s3
index 6e7a776d17e5aab8821a36fb707ffff3e3e86b1e..6054fcade269e8c294d61c97ddd06853a8acabba 100644 (file)
@@ -10,7 +10,7 @@ setup
   INSERT INTO target VALUES (1, 160, 's1', 'setup');
 
   CREATE TABLE target_pa (key int, balance integer, status text, val text) PARTITION BY RANGE (balance);
-  CREATE TABLE target_pa1 PARTITION OF target_pa FOR VALUES FROM (0) TO (200);
+  CREATE TABLE target_pa1 PARTITION OF target_pa FOR VALUES FROM (-100) TO (200);
   CREATE TABLE target_pa2 PARTITION OF target_pa FOR VALUES FROM (200) TO (1000);
   INSERT INTO target_pa VALUES (1, 160, 's1', 'setup');
 
@@ -78,24 +78,30 @@ step "merge_bal"
   MERGE INTO target t
   USING (SELECT 1 as key) s
   ON s.key = t.key
+  WHEN MATCHED AND balance < 0 THEN
+    DELETE
   WHEN MATCHED AND balance < 100 THEN
        UPDATE SET balance = balance * 2, val = t.val || ' when1'
   WHEN MATCHED AND balance < 200 THEN
        UPDATE SET balance = balance * 4, val = t.val || ' when2'
   WHEN MATCHED AND balance < 300 THEN
-       UPDATE SET balance = balance * 8, val = t.val || ' when3';
+       UPDATE SET balance = balance * 8, val = t.val || ' when3'
+  RETURNING t.key, old.balance AS old_balance, new.balance AS new_balance, t.status, t.val;
 }
 step "merge_bal_pa"
 {
   MERGE INTO target_pa t
   USING (SELECT 1 as key) s
   ON s.key = t.key
+  WHEN MATCHED AND balance < 0 THEN
+    DELETE
   WHEN MATCHED AND balance < 100 THEN
        UPDATE SET balance = balance * 2, val = t.val || ' when1'
   WHEN MATCHED AND balance < 200 THEN
        UPDATE SET balance = balance * 4, val = t.val || ' when2'
   WHEN MATCHED AND balance < 300 THEN
-       UPDATE SET balance = balance * 8, val = t.val || ' when3';
+       UPDATE SET balance = balance * 8, val = t.val || ' when3'
+  RETURNING t.key, old.balance AS old_balance, new.balance AS new_balance, t.status, t.val;
 }
 step "merge_bal_tg"
 {
@@ -103,13 +109,15 @@ step "merge_bal_tg"
     MERGE INTO target_tg t
     USING (SELECT 1 as key) s
     ON s.key = t.key
+    WHEN MATCHED AND balance < 0 THEN
+      DELETE
     WHEN MATCHED AND balance < 100 THEN
       UPDATE SET balance = balance * 2, val = t.val || ' when1'
     WHEN MATCHED AND balance < 200 THEN
       UPDATE SET balance = balance * 4, val = t.val || ' when2'
     WHEN MATCHED AND balance < 300 THEN
       UPDATE SET balance = balance * 8, val = t.val || ' when3'
-    RETURNING t.*
+    RETURNING t.key, old.balance AS old_balance, new.balance AS new_balance, t.status, t.val
   )
   SELECT * FROM t;
 }
@@ -190,6 +198,11 @@ permutation "update1" "update6" "merge_bal" "c2" "select1" "c1"
 permutation "update1_pa" "update6_pa" "merge_bal_pa" "c2" "select1_pa" "c1"
 permutation "update1_tg" "update6_tg" "merge_bal_tg" "c2" "select1_tg" "c1"
 
+# merge_bal sees row concurrently updated twice and rechecks WHEN conditions, different check passes, and row is deleted
+permutation "update6" "update6" "merge_bal" "c2" "select1" "c1"
+permutation "update6_pa" "update6_pa" "merge_bal_pa" "c2" "select1_pa" "c1"
+permutation "update6_tg" "update6_tg" "merge_bal_tg" "c2" "select1_tg" "c1"
+
 # merge_bal sees row concurrently updated twice, first update would cause all checks to fail, second update causes different check to pass, so final balance = 2000
 permutation "update7" "update6" "merge_bal" "c2" "select1" "c1"