CREATE TABLE parent(ts timestamptz, i int, n numeric, s text) PARTITION BY RANGE (ts); -- row partitions CREATE TABLE p0 PARTITION OF parent FOR VALUES FROM ('2020-01-01') TO ('2020-02-01'); CREATE TABLE p1 PARTITION OF parent FOR VALUES FROM ('2020-02-01') TO ('2020-03-01'); CREATE TABLE p2 PARTITION OF parent FOR VALUES FROM ('2020-03-01') TO ('2020-04-01'); CREATE TABLE p3 PARTITION OF parent FOR VALUES FROM ('2020-04-01') TO ('2020-05-01'); INSERT INTO parent SELECT '2020-01-15', 10, 100, 'one thousand' FROM generate_series(1,100000); INSERT INTO parent SELECT '2020-02-15', 20, 200, 'two thousand' FROM generate_series(1,100000); INSERT INTO parent SELECT '2020-03-15', 30, 300, 'three thousand' FROM generate_series(1,100000); INSERT INTO parent SELECT '2020-04-15', 30, 300, 'three thousand' FROM generate_series(1,100000); -- run parallel plans SET force_parallel_mode = regress; SET min_parallel_table_scan_size = 1; SET parallel_tuple_cost = 0; SET max_parallel_workers = 4; SET max_parallel_workers_per_gather = 4; EXPLAIN (costs off) SELECT count(*), sum(i), min(i), max(i) FROM parent; QUERY PLAN --------------------------------------------------------------------- Finalize Aggregate -> Gather Workers Planned: 4 -> Partial Aggregate -> Parallel Append -> Parallel Seq Scan on p0 -> Parallel Seq Scan on p1 -> Parallel Seq Scan on p2 -> Parallel Seq Scan on p3 (9 rows) SELECT count(*), sum(i), min(i), max(i) FROM parent; count | sum | min | max --------------------------------------------------------------------- 400000 | 9000000 | 10 | 30 (1 row) -- set older partitions as columnar SELECT alter_table_set_access_method('p0','columnar'); NOTICE: creating a new table for public.p0 NOTICE: moving the data of public.p0 NOTICE: dropping the old public.p0 NOTICE: renaming the new table to public.p0 alter_table_set_access_method --------------------------------------------------------------------- (1 row) SELECT alter_table_set_access_method('p1','columnar'); NOTICE: creating a new table for public.p1 NOTICE: moving the data of public.p1 NOTICE: dropping the old public.p1 NOTICE: renaming the new table to public.p1 alter_table_set_access_method --------------------------------------------------------------------- (1 row) SELECT alter_table_set_access_method('p3','columnar'); NOTICE: creating a new table for public.p3 NOTICE: moving the data of public.p3 NOTICE: dropping the old public.p3 NOTICE: renaming the new table to public.p3 alter_table_set_access_method --------------------------------------------------------------------- (1 row) -- should also be parallel plan EXPLAIN (costs off) SELECT count(*), sum(i), min(i), max(i) FROM parent; QUERY PLAN --------------------------------------------------------------------- Finalize Aggregate -> Gather Workers Planned: 4 -> Partial Aggregate -> Parallel Append -> Custom Scan (ColumnarScan) on p3 Columnar Projected Columns: i -> Custom Scan (ColumnarScan) on p0 Columnar Projected Columns: i -> Custom Scan (ColumnarScan) on p1 Columnar Projected Columns: i -> Parallel Seq Scan on p2 (12 rows) SELECT count(*), sum(i), min(i), max(i) FROM parent; count | sum | min | max --------------------------------------------------------------------- 400000 | 9000000 | 10 | 30 (1 row) -- and also parallel without custom scan SET columnar.enable_custom_scan = FALSE; EXPLAIN (costs off) SELECT count(*), sum(i), min(i), max(i) FROM parent; QUERY PLAN --------------------------------------------------------------------- Finalize Aggregate -> Gather Workers Planned: 4 -> Partial Aggregate -> Parallel Append -> Seq Scan on p3 -> Seq Scan on p0 -> Seq Scan on p1 -> Parallel Seq Scan on p2 (9 rows) SELECT count(*), sum(i), min(i), max(i) FROM parent; count | sum | min | max --------------------------------------------------------------------- 400000 | 9000000 | 10 | 30 (1 row) SET columnar.enable_custom_scan TO DEFAULT; SET force_parallel_mode TO DEFAULT; SET min_parallel_table_scan_size TO DEFAULT; SET parallel_tuple_cost TO DEFAULT; SET max_parallel_workers TO DEFAULT; SET max_parallel_workers_per_gather TO DEFAULT; CREATE INDEX parent_btree ON parent (n); ANALYZE parent; -- will use columnar custom scan on columnar partitions but index -- scan on heap partition EXPLAIN (costs off) SELECT count(*), sum(i), min(i), max(i) FROM parent WHERE ts > '2020-02-20' AND n < 5; QUERY PLAN --------------------------------------------------------------------- Aggregate -> Append -> Custom Scan (ColumnarScan) on p1 Filter: ((ts > 'Thu Feb 20 00:00:00 2020 PST'::timestamp with time zone) AND (n < '5'::numeric)) Columnar Projected Columns: ts, i, n Columnar Chunk Group Filters: ((ts > 'Thu Feb 20 00:00:00 2020 PST'::timestamp with time zone) AND (n < '5'::numeric)) -> Index Scan using p2_n_idx on p2 Index Cond: (n < '5'::numeric) Filter: (ts > 'Thu Feb 20 00:00:00 2020 PST'::timestamp with time zone) -> Custom Scan (ColumnarScan) on p3 Filter: ((ts > 'Thu Feb 20 00:00:00 2020 PST'::timestamp with time zone) AND (n < '5'::numeric)) Columnar Projected Columns: ts, i, n Columnar Chunk Group Filters: ((ts > 'Thu Feb 20 00:00:00 2020 PST'::timestamp with time zone) AND (n < '5'::numeric)) (13 rows) BEGIN; SET LOCAL columnar.enable_custom_scan TO 'OFF'; -- now that we disabled columnar custom scan, will use seq scan on columnar -- partitions since index scan is more expensive than seq scan too EXPLAIN (costs off) SELECT count(*), sum(i), min(i), max(i) FROM parent WHERE ts > '2020-02-20' AND n < 5; QUERY PLAN --------------------------------------------------------------------- Aggregate -> Append -> Seq Scan on p1 Filter: ((ts > 'Thu Feb 20 00:00:00 2020 PST'::timestamp with time zone) AND (n < '5'::numeric)) -> Index Scan using p2_n_idx on p2 Index Cond: (n < '5'::numeric) Filter: (ts > 'Thu Feb 20 00:00:00 2020 PST'::timestamp with time zone) -> Seq Scan on p3 Filter: ((ts > 'Thu Feb 20 00:00:00 2020 PST'::timestamp with time zone) AND (n < '5'::numeric)) (9 rows) ROLLBACK; DROP TABLE parent; -- -- Test inheritance -- CREATE TABLE i_row(i int); INSERT INTO i_row VALUES(100); CREATE TABLE i_col(i int) USING columnar; INSERT INTO i_col VALUES(200); CREATE TABLE ij_row_row(j int) INHERITS(i_row); INSERT INTO ij_row_row VALUES(300, 1000); CREATE TABLE ij_row_col(j int) INHERITS(i_row) USING columnar; INSERT INTO ij_row_col VALUES(400, 2000); CREATE TABLE ij_col_row(j int) INHERITS(i_col); INSERT INTO ij_col_row VALUES(500, 3000); CREATE TABLE ij_col_col(j int) INHERITS(i_col) USING columnar; INSERT INTO ij_col_col VALUES(600, 4000); EXPLAIN (costs off) SELECT * FROM i_row; QUERY PLAN --------------------------------------------------------------------- Append -> Seq Scan on i_row -> Seq Scan on ij_row_row -> Custom Scan (ColumnarScan) on ij_row_col Columnar Projected Columns: i (5 rows) SELECT * FROM i_row; i --------------------------------------------------------------------- 100 300 400 (3 rows) EXPLAIN (costs off) SELECT * FROM ONLY i_row; QUERY PLAN --------------------------------------------------------------------- Seq Scan on i_row (1 row) SELECT * FROM ONLY i_row; i --------------------------------------------------------------------- 100 (1 row) EXPLAIN (costs off) SELECT * FROM i_col; QUERY PLAN --------------------------------------------------------------------- Append -> Custom Scan (ColumnarScan) on i_col Columnar Projected Columns: i -> Seq Scan on ij_col_row -> Custom Scan (ColumnarScan) on ij_col_col Columnar Projected Columns: i (6 rows) SELECT * FROM i_col; i --------------------------------------------------------------------- 200 500 600 (3 rows) EXPLAIN (costs off) SELECT * FROM ONLY i_col; QUERY PLAN --------------------------------------------------------------------- Custom Scan (ColumnarScan) on i_col Columnar Projected Columns: i (2 rows) SELECT * FROM ONLY i_col; i --------------------------------------------------------------------- 200 (1 row) EXPLAIN (costs off) SELECT * FROM ij_row_row; QUERY PLAN --------------------------------------------------------------------- Seq Scan on ij_row_row (1 row) SELECT * FROM ij_row_row; i | j --------------------------------------------------------------------- 300 | 1000 (1 row) EXPLAIN (costs off) SELECT * FROM ij_row_col; QUERY PLAN --------------------------------------------------------------------- Custom Scan (ColumnarScan) on ij_row_col Columnar Projected Columns: i, j (2 rows) SELECT * FROM ij_row_col; i | j --------------------------------------------------------------------- 400 | 2000 (1 row) EXPLAIN (costs off) SELECT * FROM ij_col_row; QUERY PLAN --------------------------------------------------------------------- Seq Scan on ij_col_row (1 row) SELECT * FROM ij_col_row; i | j --------------------------------------------------------------------- 500 | 3000 (1 row) EXPLAIN (costs off) SELECT * FROM ij_col_col; QUERY PLAN --------------------------------------------------------------------- Custom Scan (ColumnarScan) on ij_col_col Columnar Projected Columns: i, j (2 rows) SELECT * FROM ij_col_col; i | j --------------------------------------------------------------------- 600 | 4000 (1 row) SET columnar.enable_custom_scan = FALSE; EXPLAIN (costs off) SELECT * FROM i_row; QUERY PLAN --------------------------------------------------------------------- Append -> Seq Scan on i_row -> Seq Scan on ij_row_row -> Seq Scan on ij_row_col (4 rows) SELECT * FROM i_row; i --------------------------------------------------------------------- 100 300 400 (3 rows) EXPLAIN (costs off) SELECT * FROM ONLY i_row; QUERY PLAN --------------------------------------------------------------------- Seq Scan on i_row (1 row) SELECT * FROM ONLY i_row; i --------------------------------------------------------------------- 100 (1 row) EXPLAIN (costs off) SELECT * FROM i_col; QUERY PLAN --------------------------------------------------------------------- Append -> Seq Scan on i_col -> Seq Scan on ij_col_row -> Seq Scan on ij_col_col (4 rows) SELECT * FROM i_col; i --------------------------------------------------------------------- 200 500 600 (3 rows) EXPLAIN (costs off) SELECT * FROM ONLY i_col; QUERY PLAN --------------------------------------------------------------------- Seq Scan on i_col (1 row) SELECT * FROM ONLY i_col; i --------------------------------------------------------------------- 200 (1 row) EXPLAIN (costs off) SELECT * FROM ij_row_row; QUERY PLAN --------------------------------------------------------------------- Seq Scan on ij_row_row (1 row) SELECT * FROM ij_row_row; i | j --------------------------------------------------------------------- 300 | 1000 (1 row) EXPLAIN (costs off) SELECT * FROM ij_row_col; QUERY PLAN --------------------------------------------------------------------- Seq Scan on ij_row_col (1 row) SELECT * FROM ij_row_col; i | j --------------------------------------------------------------------- 400 | 2000 (1 row) EXPLAIN (costs off) SELECT * FROM ij_col_row; QUERY PLAN --------------------------------------------------------------------- Seq Scan on ij_col_row (1 row) SELECT * FROM ij_col_row; i | j --------------------------------------------------------------------- 500 | 3000 (1 row) EXPLAIN (costs off) SELECT * FROM ij_col_col; QUERY PLAN --------------------------------------------------------------------- Seq Scan on ij_col_col (1 row) SELECT * FROM ij_col_col; i | j --------------------------------------------------------------------- 600 | 4000 (1 row) SET columnar.enable_custom_scan TO DEFAULT; -- remove the child table from the inheritance hierarchy table ALTER TABLE ij_row_row NO INHERIT i_row; DROP TABLE ij_row_row; DROP TABLE i_row CASCADE; NOTICE: drop cascades to table ij_row_col DROP TABLE i_col CASCADE; NOTICE: drop cascades to 2 other objects DETAIL: drop cascades to table ij_col_row drop cascades to table ij_col_col -- -- https://github.com/citusdata/citus/issues/5257 -- set default_table_access_method to columnar; CREATE TABLE prt1 (a int, b int, c varchar) PARTITION BY RANGE(a); CREATE TABLE prt1_p1 PARTITION OF prt1 FOR VALUES FROM (0) TO (250); CREATE TABLE prt1_p3 PARTITION OF prt1 FOR VALUES FROM (500) TO (600); CREATE TABLE prt1_p2 PARTITION OF prt1 FOR VALUES FROM (250) TO (500); INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 2 = 0; CREATE TABLE prt2 (a int, b int, c varchar) PARTITION BY RANGE(b); CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES FROM (0) TO (250); CREATE TABLE prt2_p2 PARTITION OF prt2 FOR VALUES FROM (250) TO (500); CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES FROM (500) TO (600); INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 3 = 0; SET enable_partitionwise_join to true; EXPLAIN (costs off, timing off, summary off) SELECT * FROM prt1 t1 LEFT JOIN LATERAL (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.b) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b) ) ss ON t1.a = ss.t2a WHERE t1.b = 0 ORDER BY t1.a; QUERY PLAN --------------------------------------------------------------------- Sort Sort Key: t1.a -> Append -> Nested Loop Left Join -> Custom Scan (ColumnarScan) on prt1_p1 t1 Filter: (b = 0) Columnar Projected Columns: a, b, c Columnar Chunk Group Filters: (b = 0) -> Hash Join Hash Cond: (t2.a = t3.b) -> Custom Scan (ColumnarScan) on prt1_p1 t2 Filter: (t1.a = a) Columnar Projected Columns: a Columnar Chunk Group Filters: (t1.a = a) -> Hash -> Custom Scan (ColumnarScan) on prt2_p1 t3 Columnar Projected Columns: a, b -> Nested Loop Left Join -> Custom Scan (ColumnarScan) on prt1_p2 t1_1 Filter: (b = 0) Columnar Projected Columns: a, b, c Columnar Chunk Group Filters: (b = 0) -> Hash Join Hash Cond: (t2_1.a = t3_1.b) -> Custom Scan (ColumnarScan) on prt1_p2 t2_1 Filter: (t1_1.a = a) Columnar Projected Columns: a Columnar Chunk Group Filters: (t1_1.a = a) -> Hash -> Custom Scan (ColumnarScan) on prt2_p2 t3_1 Columnar Projected Columns: a, b -> Nested Loop Left Join -> Custom Scan (ColumnarScan) on prt1_p3 t1_2 Filter: (b = 0) Columnar Projected Columns: a, b, c Columnar Chunk Group Filters: (b = 0) -> Hash Join Hash Cond: (t2_2.a = t3_2.b) -> Custom Scan (ColumnarScan) on prt1_p3 t2_2 Filter: (t1_2.a = a) Columnar Projected Columns: a Columnar Chunk Group Filters: (t1_2.a = a) -> Hash -> Custom Scan (ColumnarScan) on prt2_p3 t3_2 Columnar Projected Columns: a, b (45 rows) SELECT * FROM prt1 t1 LEFT JOIN LATERAL (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.b) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b) ) ss ON t1.a = ss.t2a WHERE t1.b = 0 ORDER BY t1.a; a | b | c | t2a | t3a | least --------------------------------------------------------------------- 0 | 0 | 0000 | 0 | 0 | 0 50 | 0 | 0050 | | | 100 | 0 | 0100 | | | 150 | 0 | 0150 | 150 | 0 | 150 200 | 0 | 0200 | | | 250 | 0 | 0250 | | | 300 | 0 | 0300 | 300 | 0 | 300 350 | 0 | 0350 | | | 400 | 0 | 0400 | | | 450 | 0 | 0450 | 450 | 0 | 450 500 | 0 | 0500 | | | 550 | 0 | 0550 | | | (12 rows) set default_table_access_method to default; SET enable_partitionwise_join to default; DROP TABLE prt1; DROP TABLE prt2;