=== Applying patches on top of PostgreSQL commit ID 901ed9b352b41f034e17bc540725082a488fce31 === /etc/rc.d/jail: WARNING: Per-jail configuration via jail_* variables is obsolete. Please consider migrating to /etc/jail.conf. Sat May 9 15:37:28 UTC 2026 On branch cf/6216 nothing to commit, working tree clean === using 'git am' to apply patch ./v11-0001-Move-execution-lock-acquisition-out-of-GetCached.patch === Applying: Move execution lock acquisition out of GetCachedPlan() === using 'git am' to apply patch ./v11-0002-Refactor-executor-s-initial-partition-pruning-se.patch === Applying: Refactor executor's initial partition pruning setup === using 'git am' to apply patch ./v11-0003-Introduce-ExecutorPrep-and-refactor-executor-sta.patch === Applying: Introduce ExecutorPrep and refactor executor startup Using index info to reconstruct a base tree... M src/backend/executor/execMain.c M src/include/executor/execdesc.h Falling back to patching base and 3-way merge... Auto-merging src/include/executor/execdesc.h Auto-merging src/backend/executor/execMain.c === using 'git am' to apply patch ./v11-0004-Use-pruning-aware-locking-for-single-statement-c.patch === Applying: Use pruning-aware locking for single-statement cached plans error: sha1 information is lacking or useless (src/backend/tcop/pquery.c). error: could not build fake ancestor hint: Use 'git am --show-current-patch=diff' to see the failed patch Patch failed at 0001 Use pruning-aware locking for single-statement cached plans When you have resolved this problem, run "git am --continue". If you prefer to skip this patch, run "git am --skip" instead. To restore the original branch and stop patching, run "git am --abort". === using patch(1) to apply patch ./v11-0004-Use-pruning-aware-locking-for-single-statement-c.patch === patching file src/backend/commands/explain.c Hunk #1 succeeded at 377 (offset 3 lines). Hunk #2 succeeded at 502 (offset 3 lines). Hunk #3 succeeded at 534 (offset 5 lines). Hunk #4 succeeded at 549 (offset 5 lines). patching file src/backend/commands/prepare.c patching file src/backend/executor/execMain.c Hunk #1 succeeded at 333 (offset 9 lines). Hunk #2 succeeded at 509 (offset 9 lines). patching file src/backend/executor/nodeModifyTable.c Hunk #1 succeeded at 5132 (offset 20 lines). Hunk #2 succeeded at 5147 (offset 20 lines). patching file src/backend/optimizer/plan/planner.c Hunk #1 succeeded at 675 (offset 18 lines). patching file src/backend/optimizer/plan/setrefs.c patching file src/backend/tcop/pquery.c Hunk #2 succeeded at 495 (offset 1 line). Hunk #3 succeeded at 514 (offset 1 line). Hunk #4 succeeded at 560 (offset 1 line). Hunk #5 succeeded at 616 (offset 1 line). Hunk #6 succeeded at 1833 (offset 1 line). Hunk #7 succeeded at 1874 (offset 1 line). patching file src/backend/utils/cache/plancache.c patching file src/include/commands/explain.h patching file src/include/executor/executor.h patching file src/include/nodes/pathnodes.h patching file src/include/nodes/plannodes.h patching file src/include/utils/plancache.h patching file src/test/regress/expected/partition_prune.out Hunk #1 FAILED at 4824. 1 out of 1 hunk FAILED -- saving rejects to file src/test/regress/expected/partition_prune.out.rej patching file src/test/regress/expected/plancache.out Hunk #1 succeeded at 402 (offset 4 lines). patching file src/test/regress/sql/partition_prune.sql Hunk #1 FAILED at 1447. 1 out of 1 hunk FAILED -- saving rejects to file src/test/regress/sql/partition_prune.sql.rej patching file src/test/regress/sql/plancache.sql Hunk #1 succeeded at 228 (offset 5 lines). Unstaged changes after reset: M src/backend/commands/explain.c M src/backend/commands/prepare.c M src/backend/executor/execMain.c M src/backend/executor/nodeModifyTable.c M src/backend/optimizer/plan/planner.c M src/backend/optimizer/plan/setrefs.c M src/backend/tcop/pquery.c M src/backend/utils/cache/plancache.c M src/include/commands/explain.h M src/include/executor/executor.h M src/include/nodes/pathnodes.h M src/include/nodes/plannodes.h M src/include/utils/plancache.h M src/test/regress/expected/plancache.out M src/test/regress/sql/plancache.sql Removing src/test/regress/expected/partition_prune.out.rej Removing src/test/regress/sql/partition_prune.sql.rej === using 'git apply' to apply patch ./v11-0004-Use-pruning-aware-locking-for-single-statement-c.patch === Applied patch to 'src/backend/commands/explain.c' cleanly. Applied patch to 'src/backend/commands/prepare.c' cleanly. Applied patch to 'src/backend/executor/execMain.c' cleanly. Applied patch to 'src/backend/executor/nodeModifyTable.c' cleanly. Applied patch to 'src/backend/optimizer/plan/planner.c' cleanly. Applied patch to 'src/backend/optimizer/plan/setrefs.c' cleanly. error: repository lacks the necessary blob to perform 3-way merge. Falling back to direct application... Applied patch to 'src/backend/utils/cache/plancache.c' cleanly. Applied patch to 'src/include/commands/explain.h' cleanly. Applied patch to 'src/include/executor/executor.h' cleanly. Applied patch to 'src/include/nodes/pathnodes.h' cleanly. Applied patch to 'src/include/nodes/plannodes.h' cleanly. Applied patch to 'src/include/utils/plancache.h' cleanly. Applied patch to 'src/test/regress/expected/partition_prune.out' with conflicts. Applied patch to 'src/test/regress/expected/plancache.out' cleanly. Applied patch to 'src/test/regress/sql/partition_prune.sql' with conflicts. Applied patch to 'src/test/regress/sql/plancache.sql' cleanly. U src/test/regress/expected/partition_prune.out U src/test/regress/sql/partition_prune.sql diff --cc src/test/regress/expected/partition_prune.out index 849049f9c51,61781389d2f..00000000000 --- a/src/test/regress/expected/partition_prune.out +++ b/src/test/regress/expected/partition_prune.out @@@ -4825,134 -4825,186 +4825,320 @@@ select min(a) over (partition by a orde drop view part_abc_view; drop table part_abc; -- ++<<<<<<< ours +-- Check that operands wrapped in PlaceHolderVars are matched to partition +-- keys, allowing partition pruning to occur. PlaceHolderVars can be +-- introduced when a subquery's output is used with grouping sets. +-- +create table phv_part (a int, b text) partition by list (a); +create table phv_part_1 partition of phv_part for values in (1); +create table phv_part_2 partition of phv_part for values in (2); +create table phv_part_null partition of phv_part for values in (null); +insert into phv_part values (1, 'one'), (2, 'two'), (null, 'null'); +-- OpExpr: PHV-wrapped operand matched via equal() +explain (costs off) +select * from (select a, b from phv_part) t + where a = 1 + group by grouping sets (a, b); + QUERY PLAN +--------------------------------------- + MixedAggregate + Hash Key: phv_part.b + Group Key: phv_part.a + -> Seq Scan on phv_part_1 phv_part + Filter: (a = 1) +(5 rows) + +select * from (select a, b from phv_part) t + where a = 1 + group by grouping sets (a, b); + a | b +---+----- + 1 | + | one +(2 rows) + +-- OpExpr with RelabelType: PHV wrapped around a casted column +explain (costs off) +select * from (select a::oid as x, b from phv_part) t + where x::int = 1 + group by grouping sets (x, b); + QUERY PLAN +------------------------------------------- + HashAggregate + Hash Key: (phv_part.a)::oid + Hash Key: phv_part.b + -> Seq Scan on phv_part_1 phv_part + Filter: (((a)::oid)::integer = 1) +(5 rows) + +select * from (select a::oid as x, b from phv_part) t + where x::int = 1 + group by grouping sets (x, b); + x | b +---+----- + 1 | + | one +(2 rows) + +-- ScalarArrayOpExpr: IN clause with PHV-wrapped operand +explain (costs off) +select * from (select a, b from phv_part) t + where a in (1, null) + group by grouping sets (a, b); + QUERY PLAN +--------------------------------------------------- + HashAggregate + Hash Key: phv_part.a + Hash Key: phv_part.b + -> Seq Scan on phv_part_1 phv_part + Filter: (a = ANY ('{1,NULL}'::integer[])) +(5 rows) + +select * from (select a, b from phv_part) t + where a in (1, null) + group by grouping sets (a, b); + a | b +---+----- + 1 | + | one +(2 rows) + +-- NullTest: IS NULL with PHV-wrapped operand +explain (costs off) +select * from (select a, b from phv_part) t + where a is null + group by grouping sets (a, b); + QUERY PLAN +------------------------------------------ + HashAggregate + Hash Key: phv_part.a + Hash Key: phv_part.b + -> Seq Scan on phv_part_null phv_part + Filter: (a IS NULL) +(5 rows) + +select * from (select a, b from phv_part) t + where a is null + group by grouping sets (a, b); + a | b +---+------ + | + | null +(2 rows) + +drop table phv_part; +-- BooleanTest: IS TRUE with PHV-wrapped boolean partition key +create table phv_boolpart (a bool, b text) partition by list (a); +create table phv_boolpart_t partition of phv_boolpart for values in (true); +create table phv_boolpart_f partition of phv_boolpart for values in (false); +create table phv_boolpart_null partition of phv_boolpart default; +insert into phv_boolpart values (true, 'yes'), (false, 'no'), (null, 'unknown'); +explain (costs off) +select * from (select a, b from phv_boolpart) t + where a is true + group by grouping sets (a, b); + QUERY PLAN +----------------------------------------------- + HashAggregate + Hash Key: phv_boolpart.a + Hash Key: phv_boolpart.b + -> Seq Scan on phv_boolpart_t phv_boolpart + Filter: (a IS TRUE) +(5 rows) + +select * from (select a, b from phv_boolpart) t + where a is true + group by grouping sets (a, b); + a | b +---+----- + t | + | yes +(2 rows) + +drop table phv_boolpart; ++======= + -- 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 + (1 row) + + 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; + reset enable_partition_pruning; + -- + -- Verify firstResultRels handling with multiple ModifyTable nodes + -- (writable CTEs) targeting a partitioned table. When a pruning + -- parameter matches no partition, all result relations are pruned + -- and the executor must still find a usable first result relation + -- for each ModifyTable node. + -- + prepare prunelock_mt_q (int, int) as + with upd1 as (update prunelock_p set a = a), + upd2 as (update prunelock_p set a = a where a = $2) + update prunelock_p set a = a where a = $1; + -- Force generic plan creation + explain (costs off) execute prunelock_mt_q(1, 2); + QUERY PLAN + ------------------------------------------------------------ + Update on prunelock_p + Update on prunelock_p1 prunelock_p_1 + CTE upd1 + -> Update on prunelock_p prunelock_p_3 + Update on prunelock_p1 prunelock_p_4 + Update on prunelock_p2 prunelock_p_5 + Update on prunelock_p3 prunelock_p_6 + -> Append + -> Seq Scan on prunelock_p1 prunelock_p_4 + -> Seq Scan on prunelock_p2 prunelock_p_5 + -> Seq Scan on prunelock_p3 prunelock_p_6 + CTE upd2 + -> Update on prunelock_p prunelock_p_7 + Update on prunelock_p2 prunelock_p_8 + -> Append + Subplans Removed: 2 + -> Seq Scan on prunelock_p2 prunelock_p_8 + Filter: (a = $2) + -> Append + Subplans Removed: 2 + -> Seq Scan on prunelock_p1 prunelock_p_1 + Filter: (a = $1) + (22 rows) + + -- All partitions pruned: value 4 matches no partition, so each + -- ModifyTable must still initialize correctly with no matching + -- result relations. + explain (costs off) execute prunelock_mt_q(4, 5); + QUERY PLAN + ------------------------------------------------------------ + Update on prunelock_p + CTE upd1 + -> Update on prunelock_p prunelock_p_2 + Update on prunelock_p1 prunelock_p_3 + Update on prunelock_p2 prunelock_p_4 + Update on prunelock_p3 prunelock_p_5 + -> Append + -> Seq Scan on prunelock_p1 prunelock_p_3 + -> Seq Scan on prunelock_p2 prunelock_p_4 + -> Seq Scan on prunelock_p3 prunelock_p_5 + CTE upd2 + -> Update on prunelock_p prunelock_p_6 + -> Append + Subplans Removed: 3 + -> Append + Subplans Removed: 3 + (16 rows) + + deallocate prunelock_mt_q; + drop table prunelock_p; + -- + -- Verify that pruning-aware locking falls back to locking all + -- partitions for multi-statement CachedPlans. Rule rewriting can + -- expand a single statement into multiple PlannedStmts, and later + -- statements must not have their pruning evaluated before earlier + -- ones have executed, since CCI between statements can change what + -- pruning expressions see. + -- + create table prune_config (val int); + insert into prune_config values (1); + create table multistmt_pt (a int, b int) partition by list (a); + create table multistmt_pt_1 partition of multistmt_pt for values in (1); + create table multistmt_pt_2 partition of multistmt_pt for values in (2); + insert into multistmt_pt values (1, 0), (2, 0); + create function get_prune_val() returns int as $$ + select val from prune_config; + $$ language sql stable; + create rule config_upd_rule as on update to multistmt_pt + do also update prune_config set val = 2; + set plan_cache_mode to force_generic_plan; + prepare multi_q as update multistmt_pt set b = b + 1 where a = get_prune_val(); + -- first execute creates the generic plan + execute multi_q; + -- reset for the real test + update prune_config set val = 1; + update multistmt_pt set b = 0; + -- second execute reuses the plan; pruning-aware locking kicks in + execute multi_q; + select * from multistmt_pt order by a; + a | b + ---+--- + 1 | 0 + 2 | 1 + (2 rows) + + deallocate multi_q; + drop rule config_upd_rule on multistmt_pt; + drop function get_prune_val; + drop table multistmt_pt, prune_config; + reset plan_cache_mode; ++>>>>>>> theirs diff --cc src/test/regress/sql/partition_prune.sql index 359a9208056,692415a8d9f..00000000000 --- a/src/test/regress/sql/partition_prune.sql +++ b/src/test/regress/sql/partition_prune.sql @@@ -1449,72 -1449,117 +1449,189 @@@ drop view part_abc_view drop table part_abc; -- ++<<<<<<< ours +-- Check that operands wrapped in PlaceHolderVars are matched to partition +-- keys, allowing partition pruning to occur. PlaceHolderVars can be +-- introduced when a subquery's output is used with grouping sets. +-- +create table phv_part (a int, b text) partition by list (a); +create table phv_part_1 partition of phv_part for values in (1); +create table phv_part_2 partition of phv_part for values in (2); +create table phv_part_null partition of phv_part for values in (null); +insert into phv_part values (1, 'one'), (2, 'two'), (null, 'null'); + +-- OpExpr: PHV-wrapped operand matched via equal() +explain (costs off) +select * from (select a, b from phv_part) t + where a = 1 + group by grouping sets (a, b); + +select * from (select a, b from phv_part) t + where a = 1 + group by grouping sets (a, b); + +-- OpExpr with RelabelType: PHV wrapped around a casted column +explain (costs off) +select * from (select a::oid as x, b from phv_part) t + where x::int = 1 + group by grouping sets (x, b); + +select * from (select a::oid as x, b from phv_part) t + where x::int = 1 + group by grouping sets (x, b); + +-- ScalarArrayOpExpr: IN clause with PHV-wrapped operand +explain (costs off) +select * from (select a, b from phv_part) t + where a in (1, null) + group by grouping sets (a, b); + +select * from (select a, b from phv_part) t + where a in (1, null) + group by grouping sets (a, b); + +-- NullTest: IS NULL with PHV-wrapped operand +explain (costs off) +select * from (select a, b from phv_part) t + where a is null + group by grouping sets (a, b); + +select * from (select a, b from phv_part) t + where a is null + group by grouping sets (a, b); + +drop table phv_part; + +-- BooleanTest: IS TRUE with PHV-wrapped boolean partition key +create table phv_boolpart (a bool, b text) partition by list (a); +create table phv_boolpart_t partition of phv_boolpart for values in (true); +create table phv_boolpart_f partition of phv_boolpart for values in (false); +create table phv_boolpart_null partition of phv_boolpart default; +insert into phv_boolpart values (true, 'yes'), (false, 'no'), (null, 'unknown'); + +explain (costs off) +select * from (select a, b from phv_boolpart) t + where a is true + group by grouping sets (a, b); + +select * from (select a, b from phv_boolpart) t + where a is true + group by grouping sets (a, b); + +drop table phv_boolpart; ++======= + -- 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; + reset enable_partition_pruning; + + -- + -- Verify firstResultRels handling with multiple ModifyTable nodes + -- (writable CTEs) targeting a partitioned table. When a pruning + -- parameter matches no partition, all result relations are pruned + -- and the executor must still find a usable first result relation + -- for each ModifyTable node. + -- + prepare prunelock_mt_q (int, int) as + with upd1 as (update prunelock_p set a = a), + upd2 as (update prunelock_p set a = a where a = $2) + update prunelock_p set a = a where a = $1; + + -- Force generic plan creation + explain (costs off) execute prunelock_mt_q(1, 2); + + -- All partitions pruned: value 4 matches no partition, so each + -- ModifyTable must still initialize correctly with no matching + -- result relations. + explain (costs off) execute prunelock_mt_q(4, 5); + + deallocate prunelock_mt_q; + drop table prunelock_p; + + -- + -- Verify that pruning-aware locking falls back to locking all + -- partitions for multi-statement CachedPlans. Rule rewriting can + -- expand a single statement into multiple PlannedStmts, and later + -- statements must not have their pruning evaluated before earlier + -- ones have executed, since CCI between statements can change what + -- pruning expressions see. + -- + create table prune_config (val int); + insert into prune_config values (1); + + create table multistmt_pt (a int, b int) partition by list (a); + create table multistmt_pt_1 partition of multistmt_pt for values in (1); + create table multistmt_pt_2 partition of multistmt_pt for values in (2); + insert into multistmt_pt values (1, 0), (2, 0); + + create function get_prune_val() returns int as $$ + select val from prune_config; + $$ language sql stable; + + create rule config_upd_rule as on update to multistmt_pt + do also update prune_config set val = 2; + + set plan_cache_mode to force_generic_plan; + prepare multi_q as update multistmt_pt set b = b + 1 where a = get_prune_val(); + -- first execute creates the generic plan + execute multi_q; + -- reset for the real test + update prune_config set val = 1; + update multistmt_pt set b = 0; + -- second execute reuses the plan; pruning-aware locking kicks in + execute multi_q; + select * from multistmt_pt order by a; + + deallocate multi_q; + drop rule config_upd_rule on multistmt_pt; + drop function get_prune_val; + drop table multistmt_pt, prune_config; + reset plan_cache_mode; ++>>>>>>> theirs