mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
Use Append rather than MergeAppend for scanning ordered partitions.
If we need ordered output from a scan of a partitioned table, but the ordering matches the partition ordering, then we don't need to use a MergeAppend to combine the pre-ordered per-partition scan results: a plain Append will produce the same results. This both saves useless comparison work inside the MergeAppend proper, and allows us to start returning tuples after istarting up just the first child node not all of them. However, all is not peaches and cream, because if some of the child nodes have high startup costs then there will be big discontinuities in the tuples-returned-versus-elapsed-time curve. The planner's cost model cannot handle that (yet, anyway). If we model the Append's startup cost as being just the first child's startup cost, we may drastically underestimate the cost of fetching slightly more tuples than are available from the first child. Since we've had bad experiences with over-optimistic choices of "fast start" plans for ORDER BY LIMIT queries, that seems scary. As a klugy workaround, set the startup cost estimate for an ordered Append to be the sum of its children's startup costs (as MergeAppend would). This doesn't really describe reality, but it's less likely to cause a bad plan choice than an underestimated startup cost would. In practice, the cases where we really care about this optimization will have child plans that are IndexScans with zero startup cost, so that the overly conservative estimate is still just zero. David Rowley, reviewed by Julien Rouhaud and Antonin Houska Discussion: https://postgr.es/m/CAKJS1f-hAqhPLRk_RaSFTgYxd=Tz5hA7kQ2h4-DhJufQk8TGuw@mail.gmail.com
This commit is contained in:
@ -2037,7 +2037,6 @@ explain (costs off) select * from mcrparted where a = 20 and c > 20; -- scans mc
|
||||
Filter: ((c > 20) AND (a = 20))
|
||||
(9 rows)
|
||||
|
||||
drop table mcrparted;
|
||||
-- check that partitioned table Appends cope with being referenced in
|
||||
-- subplans
|
||||
create table parted_minmax (a int, b varchar(16)) partition by range (a);
|
||||
@ -2065,3 +2064,255 @@ select min(a), max(a) from parted_minmax where b = '12345';
|
||||
(1 row)
|
||||
|
||||
drop table parted_minmax;
|
||||
-- Test code that uses Append nodes in place of MergeAppend when the
|
||||
-- partition ordering matches the desired ordering.
|
||||
create index mcrparted_a_abs_c_idx on mcrparted (a, abs(b), c);
|
||||
-- MergeAppend must be used when a default partition exists
|
||||
explain (costs off) select * from mcrparted order by a, abs(b), c;
|
||||
QUERY PLAN
|
||||
-------------------------------------------------------------------
|
||||
Merge Append
|
||||
Sort Key: mcrparted0.a, (abs(mcrparted0.b)), mcrparted0.c
|
||||
-> Index Scan using mcrparted0_a_abs_c_idx on mcrparted0
|
||||
-> Index Scan using mcrparted1_a_abs_c_idx on mcrparted1
|
||||
-> Index Scan using mcrparted2_a_abs_c_idx on mcrparted2
|
||||
-> Index Scan using mcrparted3_a_abs_c_idx on mcrparted3
|
||||
-> Index Scan using mcrparted4_a_abs_c_idx on mcrparted4
|
||||
-> Index Scan using mcrparted5_a_abs_c_idx on mcrparted5
|
||||
-> Index Scan using mcrparted_def_a_abs_c_idx on mcrparted_def
|
||||
(9 rows)
|
||||
|
||||
drop table mcrparted_def;
|
||||
-- Append is used for a RANGE partitioned table with no default
|
||||
-- and no subpartitions
|
||||
explain (costs off) select * from mcrparted order by a, abs(b), c;
|
||||
QUERY PLAN
|
||||
-------------------------------------------------------------
|
||||
Append
|
||||
-> Index Scan using mcrparted0_a_abs_c_idx on mcrparted0
|
||||
-> Index Scan using mcrparted1_a_abs_c_idx on mcrparted1
|
||||
-> Index Scan using mcrparted2_a_abs_c_idx on mcrparted2
|
||||
-> Index Scan using mcrparted3_a_abs_c_idx on mcrparted3
|
||||
-> Index Scan using mcrparted4_a_abs_c_idx on mcrparted4
|
||||
-> Index Scan using mcrparted5_a_abs_c_idx on mcrparted5
|
||||
(7 rows)
|
||||
|
||||
-- Append is used with subpaths in reverse order with backwards index scans
|
||||
explain (costs off) select * from mcrparted order by a desc, abs(b) desc, c desc;
|
||||
QUERY PLAN
|
||||
----------------------------------------------------------------------
|
||||
Append
|
||||
-> Index Scan Backward using mcrparted5_a_abs_c_idx on mcrparted5
|
||||
-> Index Scan Backward using mcrparted4_a_abs_c_idx on mcrparted4
|
||||
-> Index Scan Backward using mcrparted3_a_abs_c_idx on mcrparted3
|
||||
-> Index Scan Backward using mcrparted2_a_abs_c_idx on mcrparted2
|
||||
-> Index Scan Backward using mcrparted1_a_abs_c_idx on mcrparted1
|
||||
-> Index Scan Backward using mcrparted0_a_abs_c_idx on mcrparted0
|
||||
(7 rows)
|
||||
|
||||
-- check that Append plan is used containing a MergeAppend for sub-partitions
|
||||
-- that are unordered.
|
||||
drop table mcrparted5;
|
||||
create table mcrparted5 partition of mcrparted for values from (20, 20, 20) to (maxvalue, maxvalue, maxvalue) partition by list (a);
|
||||
create table mcrparted5a partition of mcrparted5 for values in(20);
|
||||
create table mcrparted5_def partition of mcrparted5 default;
|
||||
explain (costs off) select * from mcrparted order by a, abs(b), c;
|
||||
QUERY PLAN
|
||||
---------------------------------------------------------------------------
|
||||
Append
|
||||
-> Index Scan using mcrparted0_a_abs_c_idx on mcrparted0
|
||||
-> Index Scan using mcrparted1_a_abs_c_idx on mcrparted1
|
||||
-> Index Scan using mcrparted2_a_abs_c_idx on mcrparted2
|
||||
-> Index Scan using mcrparted3_a_abs_c_idx on mcrparted3
|
||||
-> Index Scan using mcrparted4_a_abs_c_idx on mcrparted4
|
||||
-> Merge Append
|
||||
Sort Key: mcrparted5a.a, (abs(mcrparted5a.b)), mcrparted5a.c
|
||||
-> Index Scan using mcrparted5a_a_abs_c_idx on mcrparted5a
|
||||
-> Index Scan using mcrparted5_def_a_abs_c_idx on mcrparted5_def
|
||||
(10 rows)
|
||||
|
||||
drop table mcrparted5_def;
|
||||
-- check that an Append plan is used and the sub-partitions are flattened
|
||||
-- into the main Append when the sub-partition is unordered but contains
|
||||
-- just a single sub-partition.
|
||||
explain (costs off) select a, abs(b) from mcrparted order by a, abs(b), c;
|
||||
QUERY PLAN
|
||||
---------------------------------------------------------------
|
||||
Append
|
||||
-> Index Scan using mcrparted0_a_abs_c_idx on mcrparted0
|
||||
-> Index Scan using mcrparted1_a_abs_c_idx on mcrparted1
|
||||
-> Index Scan using mcrparted2_a_abs_c_idx on mcrparted2
|
||||
-> Index Scan using mcrparted3_a_abs_c_idx on mcrparted3
|
||||
-> Index Scan using mcrparted4_a_abs_c_idx on mcrparted4
|
||||
-> Index Scan using mcrparted5a_a_abs_c_idx on mcrparted5a
|
||||
(7 rows)
|
||||
|
||||
-- check that Append is used when the sub-partitioned tables are pruned
|
||||
-- during planning.
|
||||
explain (costs off) select * from mcrparted where a < 20 order by a, abs(b), c;
|
||||
QUERY PLAN
|
||||
-------------------------------------------------------------
|
||||
Append
|
||||
-> Index Scan using mcrparted0_a_abs_c_idx on mcrparted0
|
||||
Index Cond: (a < 20)
|
||||
-> Index Scan using mcrparted1_a_abs_c_idx on mcrparted1
|
||||
Index Cond: (a < 20)
|
||||
-> Index Scan using mcrparted2_a_abs_c_idx on mcrparted2
|
||||
Index Cond: (a < 20)
|
||||
-> Index Scan using mcrparted3_a_abs_c_idx on mcrparted3
|
||||
Index Cond: (a < 20)
|
||||
(9 rows)
|
||||
|
||||
create table mclparted (a int) partition by list(a);
|
||||
create table mclparted1 partition of mclparted for values in(1);
|
||||
create table mclparted2 partition of mclparted for values in(2);
|
||||
create index on mclparted (a);
|
||||
-- Ensure an Append is used for a list partition with an order by.
|
||||
explain (costs off) select * from mclparted order by a;
|
||||
QUERY PLAN
|
||||
------------------------------------------------------------
|
||||
Append
|
||||
-> Index Only Scan using mclparted1_a_idx on mclparted1
|
||||
-> Index Only Scan using mclparted2_a_idx on mclparted2
|
||||
(3 rows)
|
||||
|
||||
-- Ensure a MergeAppend is used when a partition exists with interleaved
|
||||
-- datums in the partition bound.
|
||||
create table mclparted3_5 partition of mclparted for values in(3,5);
|
||||
create table mclparted4 partition of mclparted for values in(4);
|
||||
explain (costs off) select * from mclparted order by a;
|
||||
QUERY PLAN
|
||||
----------------------------------------------------------------
|
||||
Merge Append
|
||||
Sort Key: mclparted1.a
|
||||
-> Index Only Scan using mclparted1_a_idx on mclparted1
|
||||
-> Index Only Scan using mclparted2_a_idx on mclparted2
|
||||
-> Index Only Scan using mclparted3_5_a_idx on mclparted3_5
|
||||
-> Index Only Scan using mclparted4_a_idx on mclparted4
|
||||
(6 rows)
|
||||
|
||||
drop table mclparted;
|
||||
-- Ensure subplans which don't have a path with the correct pathkeys get
|
||||
-- sorted correctly.
|
||||
drop index mcrparted_a_abs_c_idx;
|
||||
create index on mcrparted1 (a, abs(b), c);
|
||||
create index on mcrparted2 (a, abs(b), c);
|
||||
create index on mcrparted3 (a, abs(b), c);
|
||||
create index on mcrparted4 (a, abs(b), c);
|
||||
explain (costs off) select * from mcrparted where a < 20 order by a, abs(b), c limit 1;
|
||||
QUERY PLAN
|
||||
-------------------------------------------------------------------------
|
||||
Limit
|
||||
-> Append
|
||||
-> Sort
|
||||
Sort Key: mcrparted0.a, (abs(mcrparted0.b)), mcrparted0.c
|
||||
-> Seq Scan on mcrparted0
|
||||
Filter: (a < 20)
|
||||
-> Index Scan using mcrparted1_a_abs_c_idx on mcrparted1
|
||||
Index Cond: (a < 20)
|
||||
-> Index Scan using mcrparted2_a_abs_c_idx on mcrparted2
|
||||
Index Cond: (a < 20)
|
||||
-> Index Scan using mcrparted3_a_abs_c_idx on mcrparted3
|
||||
Index Cond: (a < 20)
|
||||
(12 rows)
|
||||
|
||||
set enable_bitmapscan = 0;
|
||||
-- Ensure Append node can be used when the partition is ordered by some
|
||||
-- pathkeys which were deemed redundant.
|
||||
explain (costs off) select * from mcrparted where a = 10 order by a, abs(b), c;
|
||||
QUERY PLAN
|
||||
-------------------------------------------------------------
|
||||
Append
|
||||
-> Index Scan using mcrparted1_a_abs_c_idx on mcrparted1
|
||||
Index Cond: (a = 10)
|
||||
-> Index Scan using mcrparted2_a_abs_c_idx on mcrparted2
|
||||
Index Cond: (a = 10)
|
||||
(5 rows)
|
||||
|
||||
reset enable_bitmapscan;
|
||||
drop table mcrparted;
|
||||
-- Ensure LIST partitions allow an Append to be used instead of a MergeAppend
|
||||
create table bool_lp (b bool) partition by list(b);
|
||||
create table bool_lp_true partition of bool_lp for values in(true);
|
||||
create table bool_lp_false partition of bool_lp for values in(false);
|
||||
create index on bool_lp (b);
|
||||
explain (costs off) select * from bool_lp order by b;
|
||||
QUERY PLAN
|
||||
------------------------------------------------------------------
|
||||
Append
|
||||
-> Index Only Scan using bool_lp_false_b_idx on bool_lp_false
|
||||
-> Index Only Scan using bool_lp_true_b_idx on bool_lp_true
|
||||
(3 rows)
|
||||
|
||||
drop table bool_lp;
|
||||
-- Ensure const bool quals can be properly detected as redundant
|
||||
create table bool_rp (b bool, a int) partition by range(b,a);
|
||||
create table bool_rp_false_1k partition of bool_rp for values from (false,0) to (false,1000);
|
||||
create table bool_rp_true_1k partition of bool_rp for values from (true,0) to (true,1000);
|
||||
create table bool_rp_false_2k partition of bool_rp for values from (false,1000) to (false,2000);
|
||||
create table bool_rp_true_2k partition of bool_rp for values from (true,1000) to (true,2000);
|
||||
create index on bool_rp (b,a);
|
||||
explain (costs off) select * from bool_rp where b = true order by b,a;
|
||||
QUERY PLAN
|
||||
------------------------------------------------------------------------
|
||||
Append
|
||||
-> Index Only Scan using bool_rp_true_1k_b_a_idx on bool_rp_true_1k
|
||||
Index Cond: (b = true)
|
||||
-> Index Only Scan using bool_rp_true_2k_b_a_idx on bool_rp_true_2k
|
||||
Index Cond: (b = true)
|
||||
(5 rows)
|
||||
|
||||
explain (costs off) select * from bool_rp where b = false order by b,a;
|
||||
QUERY PLAN
|
||||
--------------------------------------------------------------------------
|
||||
Append
|
||||
-> Index Only Scan using bool_rp_false_1k_b_a_idx on bool_rp_false_1k
|
||||
Index Cond: (b = false)
|
||||
-> Index Only Scan using bool_rp_false_2k_b_a_idx on bool_rp_false_2k
|
||||
Index Cond: (b = false)
|
||||
(5 rows)
|
||||
|
||||
explain (costs off) select * from bool_rp where b = true order by a;
|
||||
QUERY PLAN
|
||||
------------------------------------------------------------------------
|
||||
Append
|
||||
-> Index Only Scan using bool_rp_true_1k_b_a_idx on bool_rp_true_1k
|
||||
Index Cond: (b = true)
|
||||
-> Index Only Scan using bool_rp_true_2k_b_a_idx on bool_rp_true_2k
|
||||
Index Cond: (b = true)
|
||||
(5 rows)
|
||||
|
||||
explain (costs off) select * from bool_rp where b = false order by a;
|
||||
QUERY PLAN
|
||||
--------------------------------------------------------------------------
|
||||
Append
|
||||
-> Index Only Scan using bool_rp_false_1k_b_a_idx on bool_rp_false_1k
|
||||
Index Cond: (b = false)
|
||||
-> Index Only Scan using bool_rp_false_2k_b_a_idx on bool_rp_false_2k
|
||||
Index Cond: (b = false)
|
||||
(5 rows)
|
||||
|
||||
drop table bool_rp;
|
||||
-- Ensure an Append scan is chosen when the partition order is a subset of
|
||||
-- the required order.
|
||||
create table range_parted (a int, b int, c int) partition by range(a, b);
|
||||
create table range_parted1 partition of range_parted for values from (0,0) to (10,10);
|
||||
create table range_parted2 partition of range_parted for values from (10,10) to (20,20);
|
||||
create index on range_parted (a,b,c);
|
||||
explain (costs off) select * from range_parted order by a,b,c;
|
||||
QUERY PLAN
|
||||
----------------------------------------------------------------------
|
||||
Append
|
||||
-> Index Only Scan using range_parted1_a_b_c_idx on range_parted1
|
||||
-> Index Only Scan using range_parted2_a_b_c_idx on range_parted2
|
||||
(3 rows)
|
||||
|
||||
explain (costs off) select * from range_parted order by a desc,b desc,c desc;
|
||||
QUERY PLAN
|
||||
-------------------------------------------------------------------------------
|
||||
Append
|
||||
-> Index Only Scan Backward using range_parted2_a_b_c_idx on range_parted2
|
||||
-> Index Only Scan Backward using range_parted1_a_b_c_idx on range_parted1
|
||||
(3 rows)
|
||||
|
||||
drop table range_parted;
|
||||
|
@ -3078,14 +3078,14 @@ drop table boolp;
|
||||
--
|
||||
set enable_seqscan = off;
|
||||
set enable_sort = off;
|
||||
create table ma_test (a int) partition by range (a);
|
||||
create table ma_test (a int, b int) partition by range (a);
|
||||
create table ma_test_p1 partition of ma_test for values from (0) to (10);
|
||||
create table ma_test_p2 partition of ma_test for values from (10) to (20);
|
||||
create table ma_test_p3 partition of ma_test for values from (20) to (30);
|
||||
insert into ma_test select x from generate_series(0,29) t(x);
|
||||
create index on ma_test (a);
|
||||
insert into ma_test select x,x from generate_series(0,29) t(x);
|
||||
create index on ma_test (b);
|
||||
analyze ma_test;
|
||||
prepare mt_q1 (int) as select * from ma_test where a >= $1 and a % 10 = 5 order by a;
|
||||
prepare mt_q1 (int) as select a from ma_test where a >= $1 and a % 10 = 5 order by b;
|
||||
-- Execute query 5 times to allow choose_custom_plan
|
||||
-- to start considering a generic plan.
|
||||
execute mt_q1(0);
|
||||
@ -3132,17 +3132,15 @@ explain (analyze, costs off, summary off, timing off) execute mt_q1(15);
|
||||
QUERY PLAN
|
||||
-------------------------------------------------------------------------------
|
||||
Merge Append (actual rows=2 loops=1)
|
||||
Sort Key: ma_test_p2.a
|
||||
Sort Key: ma_test_p2.b
|
||||
Subplans Removed: 1
|
||||
-> Index Scan using ma_test_p2_a_idx on ma_test_p2 (actual rows=1 loops=1)
|
||||
Index Cond: (a >= $1)
|
||||
Filter: ((a % 10) = 5)
|
||||
Rows Removed by Filter: 4
|
||||
-> Index Scan using ma_test_p3_a_idx on ma_test_p3 (actual rows=1 loops=1)
|
||||
Index Cond: (a >= $1)
|
||||
Filter: ((a % 10) = 5)
|
||||
-> Index Scan using ma_test_p2_b_idx on ma_test_p2 (actual rows=1 loops=1)
|
||||
Filter: ((a >= $1) AND ((a % 10) = 5))
|
||||
Rows Removed by Filter: 9
|
||||
(11 rows)
|
||||
-> Index Scan using ma_test_p3_b_idx on ma_test_p3 (actual rows=1 loops=1)
|
||||
Filter: ((a >= $1) AND ((a % 10) = 5))
|
||||
Rows Removed by Filter: 9
|
||||
(9 rows)
|
||||
|
||||
execute mt_q1(15);
|
||||
a
|
||||
@ -3155,13 +3153,12 @@ explain (analyze, costs off, summary off, timing off) execute mt_q1(25);
|
||||
QUERY PLAN
|
||||
-------------------------------------------------------------------------------
|
||||
Merge Append (actual rows=1 loops=1)
|
||||
Sort Key: ma_test_p3.a
|
||||
Sort Key: ma_test_p3.b
|
||||
Subplans Removed: 2
|
||||
-> Index Scan using ma_test_p3_a_idx on ma_test_p3 (actual rows=1 loops=1)
|
||||
Index Cond: (a >= $1)
|
||||
Filter: ((a % 10) = 5)
|
||||
Rows Removed by Filter: 4
|
||||
(7 rows)
|
||||
-> Index Scan using ma_test_p3_b_idx on ma_test_p3 (actual rows=1 loops=1)
|
||||
Filter: ((a >= $1) AND ((a % 10) = 5))
|
||||
Rows Removed by Filter: 9
|
||||
(6 rows)
|
||||
|
||||
execute mt_q1(25);
|
||||
a
|
||||
@ -3174,12 +3171,11 @@ explain (analyze, costs off, summary off, timing off) execute mt_q1(35);
|
||||
QUERY PLAN
|
||||
------------------------------------------------------------------------
|
||||
Merge Append (actual rows=0 loops=1)
|
||||
Sort Key: ma_test_p1.a
|
||||
Sort Key: ma_test_p1.b
|
||||
Subplans Removed: 2
|
||||
-> Index Scan using ma_test_p1_a_idx on ma_test_p1 (never executed)
|
||||
Index Cond: (a >= $1)
|
||||
Filter: ((a % 10) = 5)
|
||||
(6 rows)
|
||||
-> Index Scan using ma_test_p1_b_idx on ma_test_p1 (never executed)
|
||||
Filter: ((a >= $1) AND ((a % 10) = 5))
|
||||
(5 rows)
|
||||
|
||||
execute mt_q1(35);
|
||||
a
|
||||
@ -3188,23 +3184,23 @@ execute mt_q1(35);
|
||||
|
||||
deallocate mt_q1;
|
||||
-- ensure initplan params properly prune partitions
|
||||
explain (analyze, costs off, summary off, timing off) select * from ma_test where a >= (select min(a) from ma_test_p2) order by a;
|
||||
explain (analyze, costs off, summary off, timing off) select * from ma_test where a >= (select min(b) from ma_test_p2) order by b;
|
||||
QUERY PLAN
|
||||
------------------------------------------------------------------------------------------------------------
|
||||
Merge Append (actual rows=20 loops=1)
|
||||
Sort Key: ma_test_p1.a
|
||||
Sort Key: ma_test_p1.b
|
||||
InitPlan 2 (returns $1)
|
||||
-> Result (actual rows=1 loops=1)
|
||||
InitPlan 1 (returns $0)
|
||||
-> Limit (actual rows=1 loops=1)
|
||||
-> Index Scan using ma_test_p2_a_idx on ma_test_p2 ma_test_p2_1 (actual rows=1 loops=1)
|
||||
Index Cond: (a IS NOT NULL)
|
||||
-> Index Scan using ma_test_p1_a_idx on ma_test_p1 (never executed)
|
||||
Index Cond: (a >= $1)
|
||||
-> Index Scan using ma_test_p2_a_idx on ma_test_p2 (actual rows=10 loops=1)
|
||||
Index Cond: (a >= $1)
|
||||
-> Index Scan using ma_test_p3_a_idx on ma_test_p3 (actual rows=10 loops=1)
|
||||
Index Cond: (a >= $1)
|
||||
-> Index Scan using ma_test_p2_b_idx on ma_test_p2 ma_test_p2_1 (actual rows=1 loops=1)
|
||||
Index Cond: (b IS NOT NULL)
|
||||
-> Index Scan using ma_test_p1_b_idx on ma_test_p1 (never executed)
|
||||
Filter: (a >= $1)
|
||||
-> Index Scan using ma_test_p2_b_idx on ma_test_p2 (actual rows=10 loops=1)
|
||||
Filter: (a >= $1)
|
||||
-> Index Scan using ma_test_p3_b_idx on ma_test_p3 (actual rows=10 loops=1)
|
||||
Filter: (a >= $1)
|
||||
(14 rows)
|
||||
|
||||
reset enable_seqscan;
|
||||
|
@ -715,7 +715,6 @@ explain (costs off) select * from mcrparted where abs(b) = 5; -- scans all parti
|
||||
explain (costs off) select * from mcrparted where a > -1; -- scans all partitions
|
||||
explain (costs off) select * from mcrparted where a = 20 and abs(b) = 10 and c > 10; -- scans mcrparted4
|
||||
explain (costs off) select * from mcrparted where a = 20 and c > 20; -- scans mcrparted3, mcrparte4, mcrparte5, mcrparted_def
|
||||
drop table mcrparted;
|
||||
|
||||
-- check that partitioned table Appends cope with being referenced in
|
||||
-- subplans
|
||||
@ -726,3 +725,111 @@ insert into parted_minmax values (1,'12345');
|
||||
explain (costs off) select min(a), max(a) from parted_minmax where b = '12345';
|
||||
select min(a), max(a) from parted_minmax where b = '12345';
|
||||
drop table parted_minmax;
|
||||
|
||||
-- Test code that uses Append nodes in place of MergeAppend when the
|
||||
-- partition ordering matches the desired ordering.
|
||||
|
||||
create index mcrparted_a_abs_c_idx on mcrparted (a, abs(b), c);
|
||||
|
||||
-- MergeAppend must be used when a default partition exists
|
||||
explain (costs off) select * from mcrparted order by a, abs(b), c;
|
||||
|
||||
drop table mcrparted_def;
|
||||
|
||||
-- Append is used for a RANGE partitioned table with no default
|
||||
-- and no subpartitions
|
||||
explain (costs off) select * from mcrparted order by a, abs(b), c;
|
||||
|
||||
-- Append is used with subpaths in reverse order with backwards index scans
|
||||
explain (costs off) select * from mcrparted order by a desc, abs(b) desc, c desc;
|
||||
|
||||
-- check that Append plan is used containing a MergeAppend for sub-partitions
|
||||
-- that are unordered.
|
||||
drop table mcrparted5;
|
||||
create table mcrparted5 partition of mcrparted for values from (20, 20, 20) to (maxvalue, maxvalue, maxvalue) partition by list (a);
|
||||
create table mcrparted5a partition of mcrparted5 for values in(20);
|
||||
create table mcrparted5_def partition of mcrparted5 default;
|
||||
|
||||
explain (costs off) select * from mcrparted order by a, abs(b), c;
|
||||
|
||||
drop table mcrparted5_def;
|
||||
|
||||
-- check that an Append plan is used and the sub-partitions are flattened
|
||||
-- into the main Append when the sub-partition is unordered but contains
|
||||
-- just a single sub-partition.
|
||||
explain (costs off) select a, abs(b) from mcrparted order by a, abs(b), c;
|
||||
|
||||
-- check that Append is used when the sub-partitioned tables are pruned
|
||||
-- during planning.
|
||||
explain (costs off) select * from mcrparted where a < 20 order by a, abs(b), c;
|
||||
|
||||
create table mclparted (a int) partition by list(a);
|
||||
create table mclparted1 partition of mclparted for values in(1);
|
||||
create table mclparted2 partition of mclparted for values in(2);
|
||||
create index on mclparted (a);
|
||||
|
||||
-- Ensure an Append is used for a list partition with an order by.
|
||||
explain (costs off) select * from mclparted order by a;
|
||||
|
||||
-- Ensure a MergeAppend is used when a partition exists with interleaved
|
||||
-- datums in the partition bound.
|
||||
create table mclparted3_5 partition of mclparted for values in(3,5);
|
||||
create table mclparted4 partition of mclparted for values in(4);
|
||||
|
||||
explain (costs off) select * from mclparted order by a;
|
||||
|
||||
drop table mclparted;
|
||||
|
||||
-- Ensure subplans which don't have a path with the correct pathkeys get
|
||||
-- sorted correctly.
|
||||
drop index mcrparted_a_abs_c_idx;
|
||||
create index on mcrparted1 (a, abs(b), c);
|
||||
create index on mcrparted2 (a, abs(b), c);
|
||||
create index on mcrparted3 (a, abs(b), c);
|
||||
create index on mcrparted4 (a, abs(b), c);
|
||||
|
||||
explain (costs off) select * from mcrparted where a < 20 order by a, abs(b), c limit 1;
|
||||
|
||||
set enable_bitmapscan = 0;
|
||||
-- Ensure Append node can be used when the partition is ordered by some
|
||||
-- pathkeys which were deemed redundant.
|
||||
explain (costs off) select * from mcrparted where a = 10 order by a, abs(b), c;
|
||||
reset enable_bitmapscan;
|
||||
|
||||
drop table mcrparted;
|
||||
|
||||
-- Ensure LIST partitions allow an Append to be used instead of a MergeAppend
|
||||
create table bool_lp (b bool) partition by list(b);
|
||||
create table bool_lp_true partition of bool_lp for values in(true);
|
||||
create table bool_lp_false partition of bool_lp for values in(false);
|
||||
create index on bool_lp (b);
|
||||
|
||||
explain (costs off) select * from bool_lp order by b;
|
||||
|
||||
drop table bool_lp;
|
||||
|
||||
-- Ensure const bool quals can be properly detected as redundant
|
||||
create table bool_rp (b bool, a int) partition by range(b,a);
|
||||
create table bool_rp_false_1k partition of bool_rp for values from (false,0) to (false,1000);
|
||||
create table bool_rp_true_1k partition of bool_rp for values from (true,0) to (true,1000);
|
||||
create table bool_rp_false_2k partition of bool_rp for values from (false,1000) to (false,2000);
|
||||
create table bool_rp_true_2k partition of bool_rp for values from (true,1000) to (true,2000);
|
||||
create index on bool_rp (b,a);
|
||||
explain (costs off) select * from bool_rp where b = true order by b,a;
|
||||
explain (costs off) select * from bool_rp where b = false order by b,a;
|
||||
explain (costs off) select * from bool_rp where b = true order by a;
|
||||
explain (costs off) select * from bool_rp where b = false order by a;
|
||||
|
||||
drop table bool_rp;
|
||||
|
||||
-- Ensure an Append scan is chosen when the partition order is a subset of
|
||||
-- the required order.
|
||||
create table range_parted (a int, b int, c int) partition by range(a, b);
|
||||
create table range_parted1 partition of range_parted for values from (0,0) to (10,10);
|
||||
create table range_parted2 partition of range_parted for values from (10,10) to (20,20);
|
||||
create index on range_parted (a,b,c);
|
||||
|
||||
explain (costs off) select * from range_parted order by a,b,c;
|
||||
explain (costs off) select * from range_parted order by a desc,b desc,c desc;
|
||||
|
||||
drop table range_parted;
|
||||
|
@ -775,15 +775,15 @@ drop table boolp;
|
||||
--
|
||||
set enable_seqscan = off;
|
||||
set enable_sort = off;
|
||||
create table ma_test (a int) partition by range (a);
|
||||
create table ma_test (a int, b int) partition by range (a);
|
||||
create table ma_test_p1 partition of ma_test for values from (0) to (10);
|
||||
create table ma_test_p2 partition of ma_test for values from (10) to (20);
|
||||
create table ma_test_p3 partition of ma_test for values from (20) to (30);
|
||||
insert into ma_test select x from generate_series(0,29) t(x);
|
||||
create index on ma_test (a);
|
||||
insert into ma_test select x,x from generate_series(0,29) t(x);
|
||||
create index on ma_test (b);
|
||||
|
||||
analyze ma_test;
|
||||
prepare mt_q1 (int) as select * from ma_test where a >= $1 and a % 10 = 5 order by a;
|
||||
prepare mt_q1 (int) as select a from ma_test where a >= $1 and a % 10 = 5 order by b;
|
||||
|
||||
-- Execute query 5 times to allow choose_custom_plan
|
||||
-- to start considering a generic plan.
|
||||
@ -804,7 +804,7 @@ execute mt_q1(35);
|
||||
deallocate mt_q1;
|
||||
|
||||
-- ensure initplan params properly prune partitions
|
||||
explain (analyze, costs off, summary off, timing off) select * from ma_test where a >= (select min(a) from ma_test_p2) order by a;
|
||||
explain (analyze, costs off, summary off, timing off) select * from ma_test where a >= (select min(b) from ma_test_p2) order by b;
|
||||
|
||||
reset enable_seqscan;
|
||||
reset enable_sort;
|
||||
|
Reference in New Issue
Block a user