Fix MERGE with DO NOTHING actions into a partitioned table.
authorDean Rasheed <dean.a.rasheed@gmail.com>
Sat, 29 Mar 2025 09:50:14 +0000 (09:50 +0000)
committerDean Rasheed <dean.a.rasheed@gmail.com>
Sat, 29 Mar 2025 09:50:14 +0000 (09:50 +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 <exclusion@gmail.com>
Author: Tender Wang <tndrwang@gmail.com>
Reviewed-by: Gurjeet Singh <gurjeet@singh.im>
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 7651886229114a380d9299172cdd14b15ca01821..a3602b816871e0ac43867a9d229d2ad31eaaab82 100644 (file)
@@ -872,7 +872,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)
@@ -952,6 +952,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
                                                  NULL);
                    break;
                case CMD_DELETE:
+               case CMD_NOTHING:
+                   /* Nothing to do */
                    break;
 
                default:
index 02be41845accdae2a41ba75d8eda1c803a2c86f6..d84e6dba45ac4b85c687c238ff1e9e607a63e3b3 100644 (file)
@@ -3600,7 +3600,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 521d70a8919868098907bca4198a7ac9b89b538e..e9c9b15d3c80dc0519d6873a95a4b1d5458141a1 100644 (file)
@@ -2058,6 +2058,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 5ddcca84f829268a06fe4ece76688419b13496f2..556777e4f4b51a5274bf46eb4f3baeb5ab2a34b8 100644 (file)
@@ -1269,6 +1269,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