diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 86fa0f0deaa..d89e6c370d9 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -2116,6 +2116,8 @@ CreatePartitionPruneState(EState *estate, PartitionPruneInfo *pruneinfo, prunestate->other_subplans = bms_copy(pruneinfo->other_subplans); prunestate->do_initial_prune = false; /* may be set below */ prunestate->do_exec_prune = false; /* may be set below */ + prunestate->initialized = false; /* set in + * InitExecPartitionPruneContexts */ prunestate->num_partprunedata = n_part_hierarchies; /* @@ -2461,6 +2463,16 @@ InitExecPartitionPruneContexts(PartitionPruneState *prunestate, Assert(parent_plan != NULL); estate = parent_plan->state; + /* + * PartitionPruneStates are sometimes shared and this one may have been + * initialized already. Sharing of states occurs for EPQ, for example. + * See EvalPlanQualStart(). + */ + if (prunestate->initialized) + return; + + prunestate->initialized = true; + /* * No need to fix subplans maps if initial pruning didn't eliminate any * subplans. diff --git a/src/include/executor/execPartition.h b/src/include/executor/execPartition.h index 82063ec2a16..001ab051b5a 100644 --- a/src/include/executor/execPartition.h +++ b/src/include/executor/execPartition.h @@ -113,6 +113,8 @@ typedef struct PartitionPruningData * startup (at any hierarchy level). * do_exec_prune true if pruning should be performed during * executor run (at any hierarchy level). + * initialized true if InitExecPartitionPruneContexts has been called + * on this PartitionPruneState * num_partprunedata Number of items in "partprunedata" array. * partprunedata Array of PartitionPruningData pointers for the plan's * partitioned relation(s), one for each partitioning @@ -126,6 +128,7 @@ typedef struct PartitionPruneState MemoryContext prune_context; bool do_initial_prune; bool do_exec_prune; + bool initialized; int num_partprunedata; PartitionPruningData *partprunedata[FLEXIBLE_ARRAY_MEMBER]; } PartitionPruneState; diff --git a/src/test/isolation/expected/eval-plan-qual-partition-prune.out b/src/test/isolation/expected/eval-plan-qual-partition-prune.out new file mode 100644 index 00000000000..4ec0ad036f7 --- /dev/null +++ b/src/test/isolation/expected/eval-plan-qual-partition-prune.out @@ -0,0 +1,30 @@ +Parsed test spec with 2 sessions + +starting permutation: s1_begin s1_update s2_update s1_commit s2_select +step s1_begin: BEGIN; +step s1_update: UPDATE lk SET n = n + 1 WHERE id = 1; +step s2_update: + WITH locked AS ( + SELECT * FROM lk WHERE id = 1 FOR UPDATE + ), upd AS ( + UPDATE lp SET b = lp.b + 1 + (SELECT count(*) FROM locked) + FROM lp lp2 WHERE lp2.a = lp.a + RETURNING lp.* + ) + SELECT * FROM upd ORDER BY a; + +step s1_commit: COMMIT; +step s2_update: <... completed> +a|b +-+- +1|3 +2|3 +(2 rows) + +step s2_select: SELECT * FROM lp ORDER BY a; +a|b +-+- +1|3 +2|3 +(2 rows) + diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule index f1676a961f9..bf9a2037c70 100644 --- a/src/test/isolation/isolation_schedule +++ b/src/test/isolation/isolation_schedule @@ -43,6 +43,7 @@ test: fk-fastpath-null-key test: subxid-overflow test: eval-plan-qual test: eval-plan-qual-trigger +test: eval-plan-qual-partition-prune test: inplace-inval test: intra-grant-inplace test: intra-grant-inplace-db diff --git a/src/test/isolation/specs/eval-plan-qual-partition-prune.spec b/src/test/isolation/specs/eval-plan-qual-partition-prune.spec new file mode 100644 index 00000000000..234483e8735 --- /dev/null +++ b/src/test/isolation/specs/eval-plan-qual-partition-prune.spec @@ -0,0 +1,43 @@ +# Test run-time partition pruning after an EPQ recheck + +setup +{ + CREATE TABLE lp (a int PRIMARY KEY, b int) PARTITION BY LIST (a); + CREATE TABLE lp1 PARTITION OF lp FOR VALUES IN (1); + CREATE TABLE lp2 PARTITION OF lp FOR VALUES IN (2); + INSERT INTO lp VALUES (1, 1), (2, 1); + CREATE TABLE lk (id int PRIMARY KEY, n int); + INSERT INTO lk VALUES (1, 0); +} + +teardown +{ + DROP TABLE lp, lk; +} + +session s1 +step s1_begin { BEGIN; } +step s1_update { UPDATE lk SET n = n + 1 WHERE id = 1; } +step s1_commit { COMMIT; } + +session s2 +setup +{ + SET enable_hashjoin = off; + SET enable_mergejoin = off; + SET enable_seqscan = off; +} +step s2_update +{ + WITH locked AS ( + SELECT * FROM lk WHERE id = 1 FOR UPDATE + ), upd AS ( + UPDATE lp SET b = lp.b + 1 + (SELECT count(*) FROM locked) + FROM lp lp2 WHERE lp2.a = lp.a + RETURNING lp.* + ) + SELECT * FROM upd ORDER BY a; +} +step s2_select { SELECT * FROM lp ORDER BY a; } + +permutation s1_begin s1_update s2_update s1_commit s2_select