From a4a92127622e3ecdac4f195e91cee839a78220e4 Mon Sep 17 00:00:00 2001 From: Tender Wang Date: Thu, 29 Oct 2026 08:31:04 +0800 Subject: [PATCH v2] Fix MERGE into partitioned tables with SubPlans in UPDATE actions When tuple routing creates a ResultRelInfo for a partition during MERGE, ExecInitPartitionInfo() builds partition-specific state for each MERGE action. Unlike the corresponding code in ExecInitMerge(), it passed a NULL parent PlanState when building an UPDATE projection. This fails if the UPDATE target list contains a SubPlan, because ExecInitSubPlanExpr() needs the parent PlanState to initialize the SubPlan and attach its state to the parent's subPlan list, resulting in the error "SubPlan found with no parent plan". Pass the ModifyTableState's PlanState to ExecBuildUpdateProjection(), as ExecInitMerge() already does. --- src/backend/executor/execPartition.c | 2 +- src/test/regress/expected/merge.out | 15 +++++++++++++++ src/test/regress/sql/merge.sql | 11 +++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 4ec69f2bf99..a7ec585763e 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -1069,7 +1069,7 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, RelationGetDescr(leaf_part_rri->ri_RelationDesc), econtext, leaf_part_rri->ri_newTupleSlot, - NULL); + &mtstate->ps); break; case CMD_DELETE: case CMD_NOTHING: diff --git a/src/test/regress/expected/merge.out b/src/test/regress/expected/merge.out index 9cb1d87066a..17586383153 100644 --- a/src/test/regress/expected/merge.out +++ b/src/test/regress/expected/merge.out @@ -2307,6 +2307,21 @@ MERGE INTO pa_target t WHEN MATCHED THEN UPDATE SET tid = tid - 1; ERROR: new row violates row-level security policy for table "pa_target" +ROLLBACK; +-- Test a SubPlan in a partition-specific MERGE UPDATE projection +BEGIN; +MERGE INTO pa_target t +USING (SELECT true AS c OFFSET 0) s ON t.tid IS NULL +WHEN MATCHED THEN + UPDATE SET val = (SELECT NULL WHERE s.c) +WHEN NOT MATCHED THEN + INSERT VALUES (1, 0, 'subplan test'); +SELECT * FROM pa_target WHERE val = 'subplan test'; + tid | balance | val +-----+---------+-------------- + 1 | 0 | subplan test +(1 row) + ROLLBACK; DROP TABLE pa_source; DROP TABLE pa_target CASCADE; diff --git a/src/test/regress/sql/merge.sql b/src/test/regress/sql/merge.sql index 2660b19f238..ddc2ac68495 100644 --- a/src/test/regress/sql/merge.sql +++ b/src/test/regress/sql/merge.sql @@ -1432,6 +1432,17 @@ MERGE INTO pa_target t UPDATE SET tid = tid - 1; ROLLBACK; +-- Test a SubPlan in a partition-specific MERGE UPDATE projection +BEGIN; +MERGE INTO pa_target t +USING (SELECT true AS c OFFSET 0) s ON t.tid IS NULL +WHEN MATCHED THEN + UPDATE SET val = (SELECT NULL WHERE s.c) +WHEN NOT MATCHED THEN + INSERT VALUES (1, 0, 'subplan test'); +SELECT * FROM pa_target WHERE val = 'subplan test'; +ROLLBACK; + DROP TABLE pa_source; DROP TABLE pa_target CASCADE; -- 2.43.0