From e2ee89a7e36641bd0386a8c7da5e5ebfebc78f92 Mon Sep 17 00:00:00 2001 From: amitlan Date: Thu, 26 Jan 2023 21:10:19 +0900 Subject: [PATCH v1] Don't elide Append/MergeAppend if run-time pruning present --- src/backend/optimizer/plan/setrefs.c | 27 +++--- src/test/regress/expected/partition_prune.out | 87 ++++++++++++------- src/test/regress/sql/partition_prune.sql | 8 ++ 3 files changed, 80 insertions(+), 42 deletions(-) diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 85ba9d1ca1..4e582afb0a 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -1708,13 +1708,14 @@ set_append_references(PlannerInfo *root, /* * See if it's safe to get rid of the Append entirely. For this to be - * safe, there must be only one child plan and that child plan's parallel - * awareness must match the Append's. The reason for the latter is that - * if the Append is parallel aware and the child is not, then the calling - * plan may execute the non-parallel aware child multiple times. (If you + * safe, there must be only one child plan and no run-time pruning info + * must have been set. Also, the only child plan's parallel awareness + * must match the Append's. The reason for the latter is that if the + * Append is parallel aware and the child is not, then the calling plan + * may execute the non-parallel aware child multiple times. (If you * change these rules, update create_append_path to match.) */ - if (list_length(aplan->appendplans) == 1) + if (list_length(aplan->appendplans) == 1 && aplan->part_prune_index < 0) { Plan *p = (Plan *) linitial(aplan->appendplans); @@ -1773,15 +1774,15 @@ set_mergeappend_references(PlannerInfo *root, } /* - * See if it's safe to get rid of the MergeAppend entirely. For this to - * be safe, there must be only one child plan and that child plan's - * parallel awareness must match the MergeAppend's. The reason for the - * latter is that if the MergeAppend is parallel aware and the child is - * not, then the calling plan may execute the non-parallel aware child - * multiple times. (If you change these rules, update - * create_merge_append_path to match.) + * See if it's safe to get rid of the MergeAppend entirely. For this to be + * safe, there must be only one child plan and no run-time pruning info + * must have been set. Also, the only child plan's parallel awareness must + * match the MergeAppend's. The reason for the latter is that if the + * MergeAppend is parallel aware and the child is not, then the calling + * plan may execute the non-parallel aware child multiple times. (If you + * change these rules, update create_merge_append_path to match.) */ - if (list_length(mplan->mergeplans) == 1) + if (list_length(mplan->mergeplans) == 1 && mplan->part_prune_index < 0) { Plan *p = (Plan *) linitial(mplan->mergeplans); diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out index 7555764c77..9554559215 100644 --- a/src/test/regress/expected/partition_prune.out +++ b/src/test/regress/expected/partition_prune.out @@ -1935,6 +1935,30 @@ explain (analyze, costs off, summary off, timing off) select * from list_part wh (13 rows) rollback; +-- Test the case where an Append with only one run-time prunable partition +-- must not be elided +explain (analyze, costs off, summary off, timing off) + select * from list_part where a > 3 and a = (select 4); + QUERY PLAN +------------------------------------------------------------------ + Append (actual rows=1 loops=1) + InitPlan 1 (returns $0) + -> Result (actual rows=1 loops=1) + -> Seq Scan on list_part4 list_part_1 (actual rows=1 loops=1) + Filter: ((a > 3) AND (a = $0)) +(5 rows) + +explain (analyze, costs off, summary off, timing off) + select * from list_part where a > 3 and a = (select 5); + QUERY PLAN +----------------------------------------------------------- + Append (actual rows=0 loops=1) + InitPlan 1 (returns $0) + -> Result (actual rows=1 loops=1) + -> Seq Scan on list_part4 list_part_1 (never executed) + Filter: ((a > 3) AND (a = $0)) +(5 rows) + drop table list_part; -- Parallel append -- Parallel queries won't necessarily get as many workers as the planner @@ -2791,11 +2815,12 @@ prepare part_abc_q1 (int, int, int) as select * from part_abc where a = $1 and b = $2 and c = $3; -- Single partition should be scanned. explain (analyze, costs off, summary off, timing off) execute part_abc_q1 (1, 2, 3); - QUERY PLAN ----------------------------------------------------------- - Seq Scan on part_abc_p1 part_abc (actual rows=0 loops=1) - Filter: ((a = $1) AND (b = $2) AND (c = $3)) -(2 rows) + QUERY PLAN +------------------------------------------------------------------ + Append (actual rows=0 loops=1) + -> Seq Scan on part_abc_p1 part_abc_1 (actual rows=0 loops=1) + Filter: ((a = $1) AND (b = $2) AND (c = $3)) +(3 rows) deallocate part_abc_q1; drop table part_abc; @@ -3609,13 +3634,14 @@ create table listp2 partition of listp for values in(2) partition by list(b); create table listp2_10 partition of listp2 for values in (10); explain (analyze, costs off, summary off, timing off) select * from listp where a = (select 2) and b <> 10; - QUERY PLAN --------------------------------------------------- - Seq Scan on listp1 listp (actual rows=0 loops=1) - Filter: ((b <> 10) AND (a = $0)) + QUERY PLAN +--------------------------------------------------- + Append (actual rows=0 loops=1) InitPlan 1 (returns $0) - -> Result (never executed) -(4 rows) + -> Result (actual rows=1 loops=1) + -> Seq Scan on listp1 listp_1 (never executed) + Filter: ((b <> 10) AND (a = $0)) +(5 rows) -- -- check that a partition directly accessed in a query is excluded with @@ -3674,8 +3700,8 @@ alter table listp_12_1 set (parallel_workers = 0); -- Ensure that listp_12_2 is not scanned. (The nested Append is not seen in -- the plan as it's pulled in setref.c due to having just a single subnode). select explain_parallel_append('select * from listp where a = (select 1);'); - explain_parallel_append ----------------------------------------------------------------------- + explain_parallel_append +---------------------------------------------------------------------------- Gather (actual rows=N loops=N) Workers Planned: 2 Params Evaluated: $0 @@ -3683,11 +3709,12 @@ select explain_parallel_append('select * from listp where a = (select 1);'); InitPlan 1 (returns $0) -> Result (actual rows=N loops=N) -> Parallel Append (actual rows=N loops=N) - -> Seq Scan on listp_12_1 listp_1 (actual rows=N loops=N) - Filter: (a = $0) - -> Parallel Seq Scan on listp_12_2 listp_2 (never executed) - Filter: (a = $0) -(11 rows) + -> Parallel Append (actual rows=N loops=N) + -> Seq Scan on listp_12_1 listp_2 (actual rows=N loops=N) + Filter: (a = $0) + -> Parallel Seq Scan on listp_12_2 listp_3 (never executed) + Filter: (a = $0) +(12 rows) -- Like the above but throw some more complexity at the planner by adding -- a UNION ALL. We expect both sides of the union not to scan the @@ -3696,8 +3723,8 @@ select explain_parallel_append( 'select * from listp where a = (select 1) union all select * from listp where a = (select 2);'); - explain_parallel_append ------------------------------------------------------------------------------------ + explain_parallel_append +----------------------------------------------------------------------------------------- Append (actual rows=N loops=N) -> Gather (actual rows=N loops=N) Workers Planned: 2 @@ -3706,10 +3733,11 @@ select * from listp where a = (select 2);'); InitPlan 1 (returns $0) -> Result (actual rows=N loops=N) -> Parallel Append (actual rows=N loops=N) - -> Seq Scan on listp_12_1 listp_1 (actual rows=N loops=N) - Filter: (a = $0) - -> Parallel Seq Scan on listp_12_2 listp_2 (never executed) - Filter: (a = $0) + -> Parallel Append (actual rows=N loops=N) + -> Seq Scan on listp_12_1 listp_2 (actual rows=N loops=N) + Filter: (a = $0) + -> Parallel Seq Scan on listp_12_2 listp_3 (never executed) + Filter: (a = $0) -> Gather (actual rows=N loops=N) Workers Planned: 2 Params Evaluated: $1 @@ -3717,11 +3745,12 @@ select * from listp where a = (select 2);'); InitPlan 2 (returns $1) -> Result (actual rows=N loops=N) -> Parallel Append (actual rows=N loops=N) - -> Seq Scan on listp_12_1 listp_4 (never executed) - Filter: (a = $1) - -> Parallel Seq Scan on listp_12_2 listp_5 (actual rows=N loops=N) - Filter: (a = $1) -(23 rows) + -> Parallel Append (actual rows=N loops=N) + -> Seq Scan on listp_12_1 listp_6 (never executed) + Filter: (a = $1) + -> Parallel Seq Scan on listp_12_2 listp_7 (actual rows=N loops=N) + Filter: (a = $1) +(25 rows) drop table listp; reset parallel_tuple_cost; diff --git a/src/test/regress/sql/partition_prune.sql b/src/test/regress/sql/partition_prune.sql index d70bd8610c..560aa0ccc0 100644 --- a/src/test/regress/sql/partition_prune.sql +++ b/src/test/regress/sql/partition_prune.sql @@ -439,6 +439,14 @@ explain (analyze, costs off, summary off, timing off) select * from list_part wh rollback; +-- Test the case where an Append with only one run-time prunable partition +-- must not be elided +explain (analyze, costs off, summary off, timing off) + select * from list_part where a > 3 and a = (select 4); +explain (analyze, costs off, summary off, timing off) + select * from list_part where a > 3 and a = (select 5); + + drop table list_part; -- Parallel append -- 2.35.3