From 75eb867c3f91c085dbd9be856db90e4f72ccd86f Mon Sep 17 00:00:00 2001 From: Amit Langote Date: Wed, 15 Oct 2025 23:22:43 -0400 Subject: [PATCH v2] Fix EPQ crash from missing partition directory in EState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EvalPlanQualStart() failed to propagate es_partition_directory into the child EState used for EPQ rechecks. When execution time partition pruning ran during the EPQ scan, executor code dereferenced a NULL partition directory and crashed. Before commit d47cbf474, CreatePartitionPruneState(), called from ExecInitNode(), created es_partition_directory on demand. After that refactor it initializes only the init time pruning context, while ExecInitNode() initializes the exec time context. Since EvalPlanQualStart() runs only ExecInitNode() and not CreatePartitionPruneState(), the EPQ estate can be left with a NULL es_partition_directory unless copied from the parent. Fix by borrowing the parent estate’s es_partition_directory in EvalPlanQualStart(), and by clearing that field in EvalPlanQualEnd() so the parent remains responsible for freeing the directory. Add an isolation test permutation that triggers EPQ with execution- time partition pruning, the case that reproduces this crash. Bug: #19078 Reported-by: Yuri Zamyatin Diagnosed-by: David Rowley Author: David Rowley Co-authored-by: Amit Langote Discussion: https://postgr.es/m/19078-dfd62f840a2c0766@postgresql.org --- src/backend/executor/execMain.c | 10 ++++++++++ src/test/isolation/expected/eval-plan-qual.out | 7 +++++++ src/test/isolation/specs/eval-plan-qual.spec | 2 ++ 3 files changed, 19 insertions(+) diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 831c55ce787..713e926329c 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -3093,6 +3093,9 @@ EvalPlanQualStart(EPQState *epqstate, Plan *planTree) rcestate->es_part_prune_states = parentestate->es_part_prune_states; rcestate->es_part_prune_results = parentestate->es_part_prune_results; + /* We'll also borrow the es_partition_directory from the parent state */ + rcestate->es_partition_directory = parentestate->es_partition_directory; + /* * Initialize private state information for each SubPlan. We must do this * before running ExecInitNode on the main query tree, since @@ -3210,6 +3213,13 @@ EvalPlanQualEnd(EPQState *epqstate) MemoryContextSwitchTo(oldcontext); + /* + * NULLify the partition directory before freeing the executor state. + * Since EvalPlanQualStart() just borrowed the parent EState's directory, + * we'd better leave it up to the parent to delete it. + */ + estate->es_partition_directory = NULL; + FreeExecutorState(estate); /* Mark EPQState idle */ diff --git a/src/test/isolation/expected/eval-plan-qual.out b/src/test/isolation/expected/eval-plan-qual.out index 60eca44b4e3..05fffe0d570 100644 --- a/src/test/isolation/expected/eval-plan-qual.out +++ b/src/test/isolation/expected/eval-plan-qual.out @@ -1466,3 +1466,10 @@ step s2pp3: EXECUTE epd(1); step c1: COMMIT; step s2pp3: <... completed> step c2: COMMIT; + +starting permutation: s1pp1 s2pp4 c1 c2 +step s1pp1: UPDATE another_parttbl SET b = b + 1 WHERE a = 1; +step s2pp4: DELETE FROM another_parttbl WHERE a = (SELECT 1); +step c1: COMMIT; +step s2pp4: <... completed> +step c2: COMMIT; diff --git a/src/test/isolation/specs/eval-plan-qual.spec b/src/test/isolation/specs/eval-plan-qual.spec index 64afffb1d83..80e1e6bb307 100644 --- a/src/test/isolation/specs/eval-plan-qual.spec +++ b/src/test/isolation/specs/eval-plan-qual.spec @@ -316,6 +316,7 @@ step r2 { ROLLBACK; } step s2pp1 { SET plan_cache_mode TO force_generic_plan; } step s2pp2 { PREPARE epd AS DELETE FROM another_parttbl WHERE a = $1; } step s2pp3 { EXECUTE epd(1); } +step s2pp4 { DELETE FROM another_parttbl WHERE a = (SELECT 1); } session s3 setup { BEGIN ISOLATION LEVEL READ COMMITTED; } @@ -423,3 +424,4 @@ permutation sys1 sysmerge2 c1 c2 # Exercise run-time partition pruning code in an EPQ recheck permutation s1pp1 s2pp1 s2pp2 s2pp3 c1 c2 +permutation s1pp1 s2pp4 c1 c2 -- 2.47.3