Fix MERGE with DO NOTHING actions into a partitioned table.

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
REL_15_STABLE
Dean Rasheed 6 months ago
parent 0e86bad380
commit 14a33d3f0a
  1. 4
      src/backend/executor/execPartition.c
  2. 2
      src/backend/executor/nodeModifyTable.c
  3. 17
      src/test/regress/expected/merge.out
  4. 13
      src/test/regress/sql/merge.sql

@ -871,7 +871,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)
@ -941,6 +941,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
NULL);
break;
case CMD_DELETE:
case CMD_NOTHING:
/* Nothing to do */
break;
default:

@ -3424,7 +3424,7 @@ ExecInitMerge(ModifyTableState *mtstate, EState *estate)
case CMD_NOTHING:
break;
default:
elog(ERROR, "unknown operation");
elog(ERROR, "unknown action in MERGE WHEN clause");
break;
}
}

@ -1698,6 +1698,23 @@ SELECT * FROM pa_target ORDER BY tid;
14 | 140 | inserted by merge
(14 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

@ -1060,6 +1060,19 @@ SELECT merge_func();
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

Loading…
Cancel
Save