From 0a2e08b85986b14a26cf6226c6fb1e5094b3a173 Mon Sep 17 00:00:00 2001 From: Amit Langote Date: Tue, 10 Feb 2026 22:00:32 +0900 Subject: [PATCH v5 3/6] Add test for partition lock behavior with generic cached plans Add a regression test that inspects pg_locks to verify which child partitions are locked when executing a prepared statement that uses a generic cached plan. Two cases are tested: one with enable_partition_pruning on and one with it off. Currently both cases lock all child partitions, because GetCachedPlan() acquires execution locks on every relation in the plan regardless of pruning. A subsequent commit that adds pruning-aware locking will update the expected output for the pruning-enabled case, showing that only the surviving partition is locked. --- src/test/regress/expected/partition_prune.out | 83 +++++++++++++++++++ src/test/regress/sql/partition_prune.sql | 55 ++++++++++++ 2 files changed, 138 insertions(+) diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out index deacdd75807..39dab8fcc05 100644 --- a/src/test/regress/expected/partition_prune.out +++ b/src/test/regress/expected/partition_prune.out @@ -4824,3 +4824,86 @@ select min(a) over (partition by a order by a) from part_abc where a >= stable_o drop view part_abc_view; drop table part_abc; +-- +-- Verify that pruning-aware locking skips pruned partitions +-- when reusing a generic cached plan. +-- +set plan_cache_mode to force_generic_plan; +create table prunelock_p (a int) partition by list (a); +create table prunelock_p1 partition of prunelock_p for values in (1); +create table prunelock_p2 partition of prunelock_p for values in (2); +create table prunelock_p3 partition of prunelock_p for values in (3); +prepare prunelock_q (int) as select * from prunelock_p where a = $1; +-- Force generic plan creation +explain (costs off) execute prunelock_q(1); + QUERY PLAN +---------------------------------------------- + Append + Subplans Removed: 2 + -> Seq Scan on prunelock_p1 prunelock_p_1 + Filter: (a = $1) +(4 rows) + +-- Execute and check which child partitions are locked +begin; +execute prunelock_q(1); + a +--- +(0 rows) + +select c.relname + from pg_locks l + join pg_class c on c.oid = l.relation + where l.pid = pg_backend_pid() + and c.relname like 'prunelock_p_' + order by c.relname; + relname +-------------- + prunelock_p1 + prunelock_p2 + prunelock_p3 +(3 rows) + +commit; +deallocate prunelock_q; +-- Turn pruning off +set enable_partition_pruning to off; +prepare prunelock_q (int) as select * from prunelock_p where a = $1; +-- Force generic plan creation +explain (costs off) execute prunelock_q(1); + QUERY PLAN +---------------------------------------------- + Append + -> Seq Scan on prunelock_p1 prunelock_p_1 + Filter: (a = $1) + -> Seq Scan on prunelock_p2 prunelock_p_2 + Filter: (a = $1) + -> Seq Scan on prunelock_p3 prunelock_p_3 + Filter: (a = $1) +(7 rows) + +-- Execute and check which child partitions are locked +begin; +execute prunelock_q(1); + a +--- +(0 rows) + +select c.relname + from pg_locks l + join pg_class c on c.oid = l.relation + where l.pid = pg_backend_pid() + and c.relname like 'prunelock_p_' + order by c.relname; + relname +-------------- + prunelock_p1 + prunelock_p2 + prunelock_p3 +(3 rows) + +commit; +deallocate prunelock_q; +drop table prunelock_p; +reset plan_cache_mode; +reset enable_partition_pruning; diff --git a/src/test/regress/sql/partition_prune.sql b/src/test/regress/sql/partition_prune.sql index d93c0c03bab..229c5eb370c 100644 --- a/src/test/regress/sql/partition_prune.sql +++ b/src/test/regress/sql/partition_prune.sql @@ -1447,3 +1447,58 @@ select min(a) over (partition by a order by a) from part_abc where a >= stable_o drop view part_abc_view; drop table part_abc; + +-- +-- Verify that pruning-aware locking skips pruned partitions +-- when reusing a generic cached plan. +-- +set plan_cache_mode to force_generic_plan; + +create table prunelock_p (a int) partition by list (a); +create table prunelock_p1 partition of prunelock_p for values in (1); +create table prunelock_p2 partition of prunelock_p for values in (2); +create table prunelock_p3 partition of prunelock_p for values in (3); + +prepare prunelock_q (int) as select * from prunelock_p where a = $1; + +-- Force generic plan creation +explain (costs off) execute prunelock_q(1); + +-- Execute and check which child partitions are locked +begin; +execute prunelock_q(1); + +select c.relname + from pg_locks l + join pg_class c on c.oid = l.relation + where l.pid = pg_backend_pid() + and c.relname like 'prunelock_p_' + order by c.relname; +commit; + +deallocate prunelock_q; + +-- Turn pruning off +set enable_partition_pruning to off; + +prepare prunelock_q (int) as select * from prunelock_p where a = $1; + +-- Force generic plan creation +explain (costs off) execute prunelock_q(1); + +-- Execute and check which child partitions are locked +begin; +execute prunelock_q(1); + +select c.relname + from pg_locks l + join pg_class c on c.oid = l.relation + where l.pid = pg_backend_pid() + and c.relname like 'prunelock_p_' + order by c.relname; +commit; + +deallocate prunelock_q; +drop table prunelock_p; +reset plan_cache_mode; +reset enable_partition_pruning; -- 2.47.3