Fix MERGE with DO NOTHING actions into a partitioned table.
authorDean Rasheed <[email protected]>
Sat, 29 Mar 2025 09:58:40 +0000 (09:58 +0000)
committerDean Rasheed <[email protected]>
Sat, 29 Mar 2025 09:58:40 +0000 (09:58 +0000)
ExecInitPartitionInfo() duplicates much of the logic in
ExecInitMerge(), except that it failed to handle DO NOTHING
actions. This would cause an "unknown action in MERGE WHEN clause"
error if a MERGE with any DO NOTHING actions attempted to insert into
a partition not already initialised by ExecInitModifyTable().

Bug: #18871
Reported-by: Alexander Lakhin <[email protected]>
Author: Tender Wang <[email protected]>
Reviewed-by: Gurjeet Singh <[email protected]>
Discussion: https://postgr.es/m/18871-b44e3c96de3bd2e8%40postgresql.org
Backpatch-through: 15

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

index 84ccd7d457de910aeb18937fdcff289ca9140b3b..0374476ffad9ea8f13518cd71aaab34e427dc0dd 100644 (file)
@@ -876,7 +876,7 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
     * reference and make copy for this relation, converting stuff that
     * references attribute numbers to match this relation's.
     *
-    * This duplicates much of the logic in ExecInitMerge(), so something
+    * This duplicates much of the logic in ExecInitMerge(), so if something
     * changes there, look here too.
     */
    if (node && node->operation == CMD_MERGE)
@@ -956,6 +956,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
                                                  NULL);
                    break;
                case CMD_DELETE:
+               case CMD_NOTHING:
+                   /* Nothing to do */
                    break;
 
                default:
index 87c820276a8f79b0214e2479ff0dc2a3a130fc1d..309e27f8b5faa57b942b7195af9314b4b06a9f4b 100644 (file)
@@ -3784,7 +3784,7 @@ ExecInitMerge(ModifyTableState *mtstate, EState *estate)
                case CMD_NOTHING:
                    break;
                default:
-                   elog(ERROR, "unknown operation");
+                   elog(ERROR, "unknown action in MERGE WHEN clause");
                    break;
            }
        }
index c8ecc8b6b4abba697b936bb005cd695ee02c2a9e..bcd296682975cec557531b1cee1237bf70d3ebbc 100644 (file)
@@ -2060,6 +2060,23 @@ SELECT * FROM pa_target ORDER BY tid;
   15 |    1500 | initial
 (8 rows)
 
+ROLLBACK;
+-- bug #18871: ExecInitPartitionInfo()'s handling of DO NOTHING actions
+BEGIN;
+TRUNCATE pa_target;
+MERGE INTO pa_target t
+  USING (VALUES (10, 100)) AS s(sid, delta)
+  ON t.tid = s.sid
+  WHEN NOT MATCHED THEN
+    INSERT VALUES (1, 10, 'inserted by merge')
+  WHEN MATCHED THEN
+    DO NOTHING;
+SELECT * FROM pa_target ORDER BY tid, val;
+ tid | balance |        val        
+-----+---------+-------------------
+   1 |      10 | inserted by merge
+(1 row)
+
 ROLLBACK;
 DROP TABLE pa_target CASCADE;
 -- The target table is partitioned in the same way, but this time by attaching
index 07b6295b3bae7969036e7262d65911b0c4786269..f7a19c0e7dd7d99fdb085ed25da97007622cb96a 100644 (file)
@@ -1271,6 +1271,19 @@ MERGE INTO pa_target t
 SELECT * FROM pa_target ORDER BY tid;
 ROLLBACK;
 
+-- bug #18871: ExecInitPartitionInfo()'s handling of DO NOTHING actions
+BEGIN;
+TRUNCATE pa_target;
+MERGE INTO pa_target t
+  USING (VALUES (10, 100)) AS s(sid, delta)
+  ON t.tid = s.sid
+  WHEN NOT MATCHED THEN
+    INSERT VALUES (1, 10, 'inserted by merge')
+  WHEN MATCHED THEN
+    DO NOTHING;
+SELECT * FROM pa_target ORDER BY tid, val;
+ROLLBACK;
+
 DROP TABLE pa_target CASCADE;
 
 -- The target table is partitioned in the same way, but this time by attaching