| -- Objective of these tests is to ensure if IndexScan is being picked up when order by clause has columns that match |
| -- prefix of any existing btree indices. This is for queries with both order by and a limit. |
| -- Tests for queries with order by and limit on B-tree indices. |
| CREATE TABLE test_index_with_orderby_limit (a int, b int, c float, d int); |
| CREATE INDEX index_a on test_index_with_orderby_limit using btree(a); |
| CREATE INDEX index_ab on test_index_with_orderby_limit using btree(a, b); |
| CREATE INDEX index_bda on test_index_with_orderby_limit using btree(b, d, a); |
| CREATE INDEX index_c on test_index_with_orderby_limit using hash(c); |
| INSERT INTO test_index_with_orderby_limit select i, i-2, i/3, i+1 from generate_series(1,10000) i; |
| ANALYZE test_index_with_orderby_limit; |
| -- should use index scan |
| explain (costs off) select a from test_index_with_orderby_limit order by a limit 10; |
| QUERY PLAN |
| ----------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> Limit |
| -> Index Scan using index_a on test_index_with_orderby_limit |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select a from test_index_with_orderby_limit order by a limit 10; |
| a |
| ---- |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
| 10 |
| (10 rows) |
| |
| -- order by using a hash indexed column, should use SeqScan |
| explain (costs off) select c from test_index_with_orderby_limit order by c limit 10; |
| QUERY PLAN |
| ------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: c |
| -> Limit |
| -> Sort |
| Sort Key: c |
| -> Seq Scan on test_index_with_orderby_limit |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| select c from test_index_with_orderby_limit order by c limit 10; |
| c |
| --- |
| 0 |
| 0 |
| 1 |
| 1 |
| 1 |
| 2 |
| 2 |
| 2 |
| 3 |
| 3 |
| (10 rows) |
| |
| -- should use index scan |
| explain (costs off) select b from test_index_with_orderby_limit order by b limit 10; |
| QUERY PLAN |
| ------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b |
| -> Limit |
| -> Index Scan using index_bda on test_index_with_orderby_limit |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select b from test_index_with_orderby_limit order by b limit 10; |
| b |
| ---- |
| -1 |
| 0 |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| (10 rows) |
| |
| -- should use index scan |
| explain (costs off) select a, b from test_index_with_orderby_limit order by a, b limit 10; |
| QUERY PLAN |
| ------------------------------------------------------------------------------ |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a, b |
| -> Limit |
| -> Index Scan using index_ab on test_index_with_orderby_limit |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select a, b from test_index_with_orderby_limit order by a, b limit 10; |
| a | b |
| ----+---- |
| 1 | -1 |
| 2 | 0 |
| 3 | 1 |
| 4 | 2 |
| 5 | 3 |
| 6 | 4 |
| 7 | 5 |
| 8 | 6 |
| 9 | 7 |
| 10 | 8 |
| (10 rows) |
| |
| -- should use index scan |
| explain (costs off) select b, d from test_index_with_orderby_limit order by b, d limit 10; |
| QUERY PLAN |
| ------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b, d |
| -> Limit |
| -> Index Scan using index_bda on test_index_with_orderby_limit |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select b, d from test_index_with_orderby_limit order by b, d limit 10; |
| b | d |
| ----+---- |
| -1 | 2 |
| 0 | 3 |
| 1 | 4 |
| 2 | 5 |
| 3 | 6 |
| 4 | 7 |
| 5 | 8 |
| 6 | 9 |
| 7 | 10 |
| 8 | 11 |
| (10 rows) |
| |
| -- should use seq scan |
| explain (costs off) select d, b from test_index_with_orderby_limit order by d, b limit 10; |
| QUERY PLAN |
| ------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: d, b |
| -> Limit |
| -> Sort |
| Sort Key: d, b |
| -> Seq Scan on test_index_with_orderby_limit |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| select d, b from test_index_with_orderby_limit order by d, b limit 10; |
| d | b |
| ----+---- |
| 2 | -1 |
| 3 | 0 |
| 4 | 1 |
| 5 | 2 |
| 6 | 3 |
| 7 | 4 |
| 8 | 5 |
| 9 | 6 |
| 10 | 7 |
| 11 | 8 |
| (10 rows) |
| |
| -- should use seq scan |
| explain (costs off) select d, a from test_index_with_orderby_limit order by d, a limit 10; |
| QUERY PLAN |
| ------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: d, a |
| -> Limit |
| -> Sort |
| Sort Key: d, a |
| -> Seq Scan on test_index_with_orderby_limit |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| select d, a from test_index_with_orderby_limit order by d, a limit 10; |
| d | a |
| ----+---- |
| 2 | 1 |
| 3 | 2 |
| 4 | 3 |
| 5 | 4 |
| 6 | 5 |
| 7 | 6 |
| 8 | 7 |
| 9 | 8 |
| 10 | 9 |
| 11 | 10 |
| (10 rows) |
| |
| -- should use seq scan |
| explain (costs off) select a, c from test_index_with_orderby_limit order by a, c limit 10; |
| QUERY PLAN |
| ------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a, c |
| -> Limit |
| -> Sort |
| Sort Key: a, c |
| -> Seq Scan on test_index_with_orderby_limit |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| select a, c from test_index_with_orderby_limit order by a, c limit 10; |
| a | c |
| ----+--- |
| 1 | 0 |
| 2 | 0 |
| 3 | 1 |
| 4 | 1 |
| 5 | 1 |
| 6 | 2 |
| 7 | 2 |
| 8 | 2 |
| 9 | 3 |
| 10 | 3 |
| (10 rows) |
| |
| -- should use index scan |
| explain (costs off) select b, d, a from test_index_with_orderby_limit order by b, d, a limit 10; |
| QUERY PLAN |
| ------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b, d, a |
| -> Limit |
| -> Index Scan using index_bda on test_index_with_orderby_limit |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select b, d, a from test_index_with_orderby_limit order by b, d, a limit 10; |
| b | d | a |
| ----+----+---- |
| -1 | 2 | 1 |
| 0 | 3 | 2 |
| 1 | 4 | 3 |
| 2 | 5 | 4 |
| 3 | 6 | 5 |
| 4 | 7 | 6 |
| 5 | 8 | 7 |
| 6 | 9 | 8 |
| 7 | 10 | 9 |
| 8 | 11 | 10 |
| (10 rows) |
| |
| -- should use seq scan |
| explain (costs off) select b, d, c from test_index_with_orderby_limit order by b, d, c limit 10; |
| QUERY PLAN |
| ------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b, d, c |
| -> Limit |
| -> Sort |
| Sort Key: b, d, c |
| -> Seq Scan on test_index_with_orderby_limit |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| select b, d, c from test_index_with_orderby_limit order by b, d, c limit 10; |
| b | d | c |
| ----+----+--- |
| -1 | 2 | 0 |
| 0 | 3 | 0 |
| 1 | 4 | 1 |
| 2 | 5 | 1 |
| 3 | 6 | 1 |
| 4 | 7 | 2 |
| 5 | 8 | 2 |
| 6 | 9 | 2 |
| 7 | 10 | 3 |
| 8 | 11 | 3 |
| (10 rows) |
| |
| -- should use seq scan |
| explain (costs off) select c, b, a from test_index_with_orderby_limit order by c, b, a limit 10; |
| QUERY PLAN |
| ------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: c, b, a |
| -> Limit |
| -> Sort |
| Sort Key: c, b, a |
| -> Seq Scan on test_index_with_orderby_limit |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| select c, b, a from test_index_with_orderby_limit order by c, b, a limit 10; |
| c | b | a |
| ---+----+---- |
| 0 | -1 | 1 |
| 0 | 0 | 2 |
| 1 | 1 | 3 |
| 1 | 2 | 4 |
| 1 | 3 | 5 |
| 2 | 4 | 6 |
| 2 | 5 | 7 |
| 2 | 6 | 8 |
| 3 | 7 | 9 |
| 3 | 8 | 10 |
| (10 rows) |
| |
| -- with offset and without limit |
| explain (costs off) select a from test_index_with_orderby_limit order by a offset 9990; |
| QUERY PLAN |
| ----------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> Index Scan using index_a on test_index_with_orderby_limit |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (5 rows) |
| |
| select a from test_index_with_orderby_limit order by a offset 9990; |
| a |
| ------- |
| 9991 |
| 9992 |
| 9993 |
| 9994 |
| 9995 |
| 9996 |
| 9997 |
| 9998 |
| 9999 |
| 10000 |
| (10 rows) |
| |
| -- limit value in subquery |
| explain (costs off) select a from test_index_with_orderby_limit order by a limit (select min(a) from test_index_with_orderby_limit); |
| QUERY PLAN |
| ---------------------------------------------------------------------------------------------------------------------------- |
| Limit |
| InitPlan 2 (returns $1) (slice2) |
| -> Result |
| InitPlan 1 (returns $0) (slice3) |
| -> Limit |
| -> Gather Motion 3:1 (slice4; segments: 3) |
| Merge Key: test_index_with_orderby_limit_1.a |
| -> Index Only Scan using index_a on test_index_with_orderby_limit test_index_with_orderby_limit_1 |
| Index Cond: (a IS NOT NULL) |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: test_index_with_orderby_limit.a |
| -> Limit |
| -> Index Only Scan using index_a on test_index_with_orderby_limit |
| Optimizer: Postgres query optimizer |
| (14 rows) |
| |
| select a from test_index_with_orderby_limit order by a limit (select min(a) from test_index_with_orderby_limit); |
| a |
| --- |
| 1 |
| (1 row) |
| |
| -- offset value in a subquery |
| explain (costs off) select c from test_index_with_orderby_limit order by c offset (select 9990); |
| QUERY PLAN |
| ------------------------------------------------------------- |
| Limit |
| InitPlan 1 (returns $0) (slice2) |
| -> Result |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: test_index_with_orderby_limit.c |
| -> Sort |
| Sort Key: test_index_with_orderby_limit.c |
| -> Seq Scan on test_index_with_orderby_limit |
| Optimizer: Postgres query optimizer |
| (9 rows) |
| |
| select c from test_index_with_orderby_limit order by c offset (select 9990); |
| c |
| ------ |
| 3330 |
| 3330 |
| 3331 |
| 3331 |
| 3331 |
| 3332 |
| 3332 |
| 3332 |
| 3333 |
| 3333 |
| (10 rows) |
| |
| -- order by opposite to index sort direction |
| explain (costs off) select a from test_index_with_orderby_limit order by a desc limit 10; |
| QUERY PLAN |
| -------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> Limit |
| -> Index Scan Backward using index_a on test_index_with_orderby_limit |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select a from test_index_with_orderby_limit order by a desc limit 10; |
| a |
| ------- |
| 10000 |
| 9999 |
| 9998 |
| 9997 |
| 9996 |
| 9995 |
| 9994 |
| 9993 |
| 9992 |
| 9991 |
| (10 rows) |
| |
| -- order by opposite to nulls direction in index |
| explain (costs off) select a from test_index_with_orderby_limit order by a NULLS FIRST limit 10; |
| QUERY PLAN |
| ------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> Limit |
| -> Sort |
| Sort Key: a NULLS FIRST |
| -> Seq Scan on test_index_with_orderby_limit |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| select a from test_index_with_orderby_limit order by a NULLS FIRST limit 10; |
| a |
| ---- |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
| 10 |
| (10 rows) |
| |
| -- order by desc with nulls last |
| explain (costs off) select a from test_index_with_orderby_limit order by a desc NULLS LAST limit 10; |
| QUERY PLAN |
| ------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> Limit |
| -> Sort |
| Sort Key: a DESC NULLS LAST |
| -> Seq Scan on test_index_with_orderby_limit |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| select a from test_index_with_orderby_limit order by a desc NULLS LAST limit 10; |
| a |
| ------- |
| 10000 |
| 9999 |
| 9998 |
| 9997 |
| 9996 |
| 9995 |
| 9994 |
| 9993 |
| 9992 |
| 9991 |
| (10 rows) |
| |
| -- order by as sum of two columns, uses SeqScan with Sort |
| explain (costs off) select a, b from test_index_with_orderby_limit order by a+b, c limit 3; |
| QUERY PLAN |
| ------------------------------------------------------------------------- |
| Result |
| -> Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: ((a + b)), c |
| -> Limit |
| -> Sort |
| Sort Key: ((a + b)), c |
| -> Seq Scan on test_index_with_orderby_limit |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (9 rows) |
| |
| select a, b from test_index_with_orderby_limit order by a+b, c limit 3; |
| a | b |
| ---+---- |
| 1 | -1 |
| 2 | 0 |
| 3 | 1 |
| (3 rows) |
| |
| explain (costs off) select a+b as sum from test_index_with_orderby_limit order by sum limit 3; |
| QUERY PLAN |
| ------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: ((a + b)) |
| -> Limit |
| -> Sort |
| Sort Key: ((a + b)) |
| -> Seq Scan on test_index_with_orderby_limit |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| select a+b as sum from test_index_with_orderby_limit order by sum limit 3; |
| sum |
| ----- |
| 0 |
| 2 |
| 4 |
| (3 rows) |
| |
| -- order by using column number |
| explain (costs off) select a from test_index_with_orderby_limit order by 1 limit 3; |
| QUERY PLAN |
| ----------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> Limit |
| -> Index Scan using index_a on test_index_with_orderby_limit |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select a from test_index_with_orderby_limit order by 1 limit 3; |
| a |
| --- |
| 1 |
| 2 |
| 3 |
| (3 rows) |
| |
| -- check if index-only scan is leveraged when required |
| set optimizer_enable_indexscan to off; |
| -- project only columns in the Index |
| explain (costs off) select b from test_index_with_orderby_limit order by b limit 10; |
| QUERY PLAN |
| ------------------------------------------------------------------------------------ |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b |
| -> Limit |
| -> Index Only Scan using index_bda on test_index_with_orderby_limit |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select b from test_index_with_orderby_limit order by b limit 10; |
| b |
| ---- |
| -1 |
| 0 |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| (10 rows) |
| |
| -- re-enable indexscan |
| reset optimizer_enable_indexscan; |
| DROP TABLE test_index_with_orderby_limit; |
| -- Test Case: Test on a regular table with mixed data type columns. |
| -- Purpose: Validate if IndexScan with correct scan direction is used on expected index for queries with order by and limit. |
| CREATE TABLE zoo (a int, b text, c float, d int, e text, f int); |
| INSERT INTO zoo select i, CONCAT('col_b', i)::text, i/3.2, i+1, CONCAT('col_e', i)::text, i+3 from generate_series(1,10000) i; |
| -- Inserting nulls to verify results match when index key specifies nulls first or desc |
| INSERT INTO zoo values (null, null, null, null, null); |
| ANALYZE zoo; |
| -- Positive tests: Validate if IndexScan Forward/Backward is chosen. |
| -- single col index with default order |
| CREATE INDEX dir_index_a on zoo using btree(a); |
| -- Validate if 'dir_index_a' is used for order by cols matching/commutative to the index cols |
| -- Expected to use Forward IndexScan |
| explain (costs off) select a from zoo order by a limit 3; |
| QUERY PLAN |
| ---------------------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> Limit |
| -> Index Scan using dir_index_a on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select a from zoo order by a limit 3; |
| a |
| --- |
| 1 |
| 2 |
| 3 |
| (3 rows) |
| |
| -- Expected to use Backward IndexScan |
| explain (costs off) select a from zoo order by a desc limit 3; |
| QUERY PLAN |
| ------------------------------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> Limit |
| -> Index Scan Backward using dir_index_a on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select a from zoo order by a desc limit 3; |
| a |
| ------- |
| |
| 10000 |
| 9999 |
| (3 rows) |
| |
| -- single col index with reverse order |
| CREATE INDEX dir_index_b on zoo using btree(b desc); |
| -- Validate if 'dir_index_b' is used for order by cols matching/commutative to the index cols |
| -- Expected to use Forward IndexScan |
| explain (costs off) select b from zoo order by b desc limit 3; |
| QUERY PLAN |
| ---------------------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b |
| -> Limit |
| -> Index Scan using dir_index_b on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select b from zoo order by b desc limit 3; |
| b |
| ----------- |
| |
| col_b9999 |
| col_b9998 |
| (3 rows) |
| |
| -- Expected to use Backward IndexScan |
| explain (costs off) select b from zoo order by b limit 3; |
| QUERY PLAN |
| ------------------------------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b |
| -> Limit |
| -> Index Scan Backward using dir_index_b on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select b from zoo order by b limit 3; |
| b |
| ---------- |
| col_b1 |
| col_b10 |
| col_b100 |
| (3 rows) |
| |
| -- single col index with opp nulls direction |
| CREATE INDEX dir_index_c on zoo using btree(c nulls first); |
| -- Validate if 'dir_index_c' is used for order by cols matching/commutative to the index cols |
| -- Expected to use Forward IndexScan |
| explain (costs off) select c from zoo order by c nulls first limit 3; |
| QUERY PLAN |
| ---------------------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: c |
| -> Limit |
| -> Index Scan using dir_index_c on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select c from zoo order by c nulls first limit 3; |
| c |
| -------- |
| |
| 0.3125 |
| 0.625 |
| (3 rows) |
| |
| -- Expected to use Backward IndexScan |
| explain (costs off) select c from zoo order by c desc nulls last limit 3; |
| QUERY PLAN |
| ------------------------------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: c |
| -> Limit |
| -> Index Scan Backward using dir_index_c on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select c from zoo order by c desc nulls last limit 3; |
| c |
| ----------- |
| 3125 |
| 3124.6875 |
| 3124.375 |
| (3 rows) |
| |
| -- multi col index all with all index keys asc |
| CREATE INDEX dir_index_bcd on zoo using btree(b,c,d); |
| -- Inserting rows with duplicate values to ensure results are sorted correctly for order by on multiple columns |
| INSERT INTO zoo(b,c) values('col_b1',-1); |
| INSERT INTO zoo(b,c) values('col_b9999',10000); |
| -- Validate if 'dir_index_bcd' is used for order by cols matching/commutative to the index cols |
| -- Testing various permutations of order by columns that are expected to choose Forward IndexScan |
| explain (costs off) select b,c,d from zoo order by b,c,d limit 3; |
| QUERY PLAN |
| --------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b, c, d |
| -> Limit |
| -> Index Scan using dir_index_bcd on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select b,c,d from zoo order by b,c,d limit 3; |
| b | c | d |
| ---------+--------+---- |
| col_b1 | -1 | |
| col_b1 | 0.3125 | 2 |
| col_b10 | 3.125 | 11 |
| (3 rows) |
| |
| explain (costs off) select b,c from zoo order by b,c limit 3; |
| QUERY PLAN |
| --------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b, c |
| -> Limit |
| -> Index Scan using dir_index_bcd on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select b,c from zoo order by b,c limit 3; |
| b | c |
| ---------+-------- |
| col_b1 | -1 |
| col_b1 | 0.3125 |
| col_b10 | 3.125 |
| (3 rows) |
| |
| -- Testing various permutations of order by columns that are expected to choose Backward IndexScan |
| explain (costs off) select b,c,d from zoo order by b desc,c desc,d desc limit 4; |
| QUERY PLAN |
| ------------------------------------------------------------------ |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b, c, d |
| -> Limit |
| -> Index Scan Backward using dir_index_bcd on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select b,c,d from zoo order by b desc,c desc,d desc limit 4; |
| b | c | d |
| -----------+-----------+------- |
| | | |
| col_b9999 | 10000 | |
| col_b9999 | 3124.6875 | 10000 |
| col_b9998 | 3124.375 | 9999 |
| (4 rows) |
| |
| explain (costs off) select b,c from zoo order by b desc,c desc limit 4; |
| QUERY PLAN |
| ------------------------------------------------------------------ |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b, c |
| -> Limit |
| -> Index Scan Backward using dir_index_bcd on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select b,c from zoo order by b desc,c desc limit 4; |
| b | c |
| -----------+----------- |
| | |
| col_b9999 | 10000 |
| col_b9999 | 3124.6875 |
| col_b9998 | 3124.375 |
| (4 rows) |
| |
| -- Delete duplicate rows |
| delete from zoo where b='col_b1' and c=-1; |
| delete from zoo where b='col_b9999' and c=10000; |
| -- multi col index all with all index keys desc |
| CREATE INDEX dir_index_fde on zoo using btree(f desc,d desc,e desc); |
| -- Inserting rows with duplicate values to ensure results are sorted correctly for order by on multiple columns |
| INSERT INTO zoo(f,d) values(4,-1); |
| INSERT INTO zoo(f,d) values(10003,-1); |
| -- Validate if 'dir_index_fde' is used for order by cols matching/commutative to the index cols |
| -- Testing various permutations of order by columns that are expected to choose Forward IndexScan |
| explain (costs off) select f,d,e from zoo order by f desc,d desc,e desc limit 4; |
| QUERY PLAN |
| --------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: f, d, e |
| -> Limit |
| -> Index Scan using dir_index_fde on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select f,d,e from zoo order by f desc,d desc,e desc limit 4; |
| f | d | e |
| -------+-------+------------ |
| | | |
| 10003 | 10001 | col_e10000 |
| 10003 | -1 | |
| 10002 | 10000 | col_e9999 |
| (4 rows) |
| |
| explain (costs off) select f,d from zoo order by f desc,d desc limit 4; |
| QUERY PLAN |
| --------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: f, d |
| -> Limit |
| -> Index Scan using dir_index_fde on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select f,d from zoo order by f desc,d desc limit 4; |
| f | d |
| -------+------- |
| | |
| 10003 | 10001 |
| 10003 | -1 |
| 10002 | 10000 |
| (4 rows) |
| |
| -- Testing various permutations of order by columns that are expected to choose Backward IndexScan |
| explain (costs off) select f,d,e from zoo order by f,d,e limit 3; |
| QUERY PLAN |
| ------------------------------------------------------------------ |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: f, d, e |
| -> Limit |
| -> Index Scan Backward using dir_index_fde on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select f,d,e from zoo order by f,d,e limit 3; |
| f | d | e |
| ---+----+-------- |
| 4 | -1 | |
| 4 | 2 | col_e1 |
| 5 | 3 | col_e2 |
| (3 rows) |
| |
| explain (costs off) select f,d from zoo order by f,d limit 3; |
| QUERY PLAN |
| ------------------------------------------------------------------ |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: f, d |
| -> Limit |
| -> Index Scan Backward using dir_index_fde on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select f,d from zoo order by f,d limit 3; |
| f | d |
| ---+---- |
| 4 | -1 |
| 4 | 2 |
| 5 | 3 |
| (3 rows) |
| |
| -- Delete duplicate rows |
| delete from zoo where f=4 and d=-1; |
| delete from zoo where f=10003 and d=-1; |
| -- multi col index with mixed index keys properties |
| CREATE INDEX dir_index_eda on zoo using btree(e, d desc nulls last,a); |
| -- Inserting rows with duplicate values to ensure results are sorted correctly for order by on multiple columns |
| INSERT INTO zoo(d,e) values(9999,'col_e9999'); |
| INSERT INTO zoo(d,e) values(1,'col_e1'); |
| -- Validate if 'dir_index_eda' is used for order by cols matching/commutative to the index cols |
| -- Testing various permutations of order by columns that are expected to choose Forward IndexScan |
| explain (costs off) select e,d,a from zoo order by e, d desc nulls last,a limit 3; |
| QUERY PLAN |
| --------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: e, d, a |
| -> Limit |
| -> Index Scan using dir_index_eda on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select e,d,a from zoo order by e, d desc nulls last,a limit 3; |
| e | d | a |
| ---------+----+---- |
| col_e1 | 2 | 1 |
| col_e1 | 1 | |
| col_e10 | 11 | 10 |
| (3 rows) |
| |
| explain (costs off) select e,d from zoo order by e, d desc nulls last limit 3; |
| QUERY PLAN |
| --------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: e, d |
| -> Limit |
| -> Index Scan using dir_index_eda on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select e,d from zoo order by e, d desc nulls last limit 3; |
| e | d |
| ---------+---- |
| col_e1 | 2 |
| col_e1 | 1 |
| col_e10 | 11 |
| (3 rows) |
| |
| -- Testing various permutations of order by columns that are expected to choose Backward IndexScan |
| explain (costs off) select e,d,a from zoo order by e desc,d nulls first,a desc limit 4; |
| QUERY PLAN |
| ------------------------------------------------------------------ |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: e, d, a |
| -> Limit |
| -> Index Scan Backward using dir_index_eda on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select e,d,a from zoo order by e desc,d nulls first,a desc limit 4; |
| e | d | a |
| -----------+-------+------ |
| | | |
| col_e9999 | 9999 | |
| col_e9999 | 10000 | 9999 |
| col_e9998 | 9999 | 9998 |
| (4 rows) |
| |
| explain (costs off) select e,d from zoo order by e desc,d nulls first limit 4; |
| QUERY PLAN |
| ------------------------------------------------------------------ |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: e, d |
| -> Limit |
| -> Index Scan Backward using dir_index_eda on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select e,d from zoo order by e desc,d nulls first limit 4; |
| e | d |
| -----------+------- |
| | |
| col_e9999 | 9999 |
| col_e9999 | 10000 |
| col_e9998 | 9999 |
| (4 rows) |
| |
| -- Delete duplicate rows |
| delete from zoo where d=9999 and e='col_e9999'; |
| delete from zoo where d=1 and e='col_e1'; |
| -- Covering index with descending and one include column |
| CREATE INDEX dir_covering_index_db ON zoo(d desc) INCLUDE (b); |
| -- Validate if IndexScan is chosen and on covering index |
| -- Expected to use Forward IndexScan |
| explain (costs off) select d from zoo order by d desc limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: d |
| -> Limit |
| -> Index Scan using dir_covering_index_db on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select d from zoo order by d desc limit 3; |
| d |
| ------- |
| |
| 10001 |
| 10000 |
| (3 rows) |
| |
| -- Expected to use Backward IndexScan |
| explain (costs off) select d from zoo order by d limit 3; |
| QUERY PLAN |
| ----------------------------------------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: d |
| -> Limit |
| -> Index Scan Backward using dir_covering_index_db on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select d from zoo order by d limit 3; |
| d |
| --- |
| 2 |
| 3 |
| 4 |
| (3 rows) |
| |
| -- Validate if Backward IndexScan is chosen for query with offset and without limit |
| explain (costs off) select e,d,a from zoo order by e desc,d nulls first,a desc offset 9990; |
| QUERY PLAN |
| --------------------------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: e, d, a |
| -> Index Scan Backward using dir_index_eda on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (5 rows) |
| |
| select e,d,a from zoo order by e desc,d nulls first,a desc offset 9997; |
| e | d | a |
| -----------+------+------ |
| col_e1000 | 1001 | 1000 |
| col_e100 | 101 | 100 |
| col_e10 | 11 | 10 |
| col_e1 | 2 | 1 |
| (4 rows) |
| |
| -- Validate if Backward IndexScan is chosen for query with offset value in subquery |
| -- ORCA_FEATURE_NOT_SUPPORTED: ORCA doesn't support limit or offset values specified as part of a subquery |
| explain (costs off) select c from zoo order by c desc nulls last offset (select 9997); |
| QUERY PLAN |
| ------------------------------------------------ |
| Limit |
| InitPlan 1 (returns $0) (slice2) |
| -> Result |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: zoo.c |
| -> Sort |
| Sort Key: zoo.c DESC NULLS LAST |
| -> Seq Scan on zoo |
| Optimizer: Postgres query optimizer |
| (9 rows) |
| |
| select c from zoo order by c desc nulls last offset (select 9997); |
| c |
| -------- |
| 0.9375 |
| 0.625 |
| 0.3125 |
| |
| (4 rows) |
| |
| -- Validate if Backward IndexScan is chosen for query with limit value in subquery |
| -- ORCA_FEATURE_NOT_SUPPORTED: ORCA doesn't support limit or offset values specified as part of a subquery |
| explain (costs off) select c from zoo order by c desc nulls last limit (select 3); |
| QUERY PLAN |
| ------------------------------------------------------------------------------------------------------------------ |
| Limit |
| InitPlan 1 (returns $0) (slice2) |
| -> Result |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: zoo.c |
| -> Limit |
| -> Index Only Scan Backward using dir_index_c on zoo |
| Optimizer: Postgres query optimizer |
| (8 rows) |
| |
| select c from zoo order by c desc nulls last limit (select 3); |
| c |
| ----------- |
| 3125 |
| 3124.6875 |
| 3124.375 |
| (3 rows) |
| |
| -- Negative tests: Validate if a SeqScan is chosen if order by cols directions do not matching indices keys directions. |
| -- Expected to choose SeqScan with Sort |
| -- Testing various permutations that are not matching keys in 'dir_index_a' |
| explain (costs off) select a from zoo order by a nulls first limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> Limit |
| -> Sort |
| Sort Key: a NULLS FIRST |
| -> Seq Scan on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| select a from zoo order by a nulls first limit 3; |
| a |
| --- |
| |
| 1 |
| 2 |
| (3 rows) |
| |
| explain (costs off) select a from zoo order by a desc nulls last limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> Limit |
| -> Sort |
| Sort Key: a DESC NULLS LAST |
| -> Seq Scan on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| select a from zoo order by a desc nulls last limit 3; |
| a |
| ------- |
| 10000 |
| 9999 |
| 9998 |
| (3 rows) |
| |
| -- Testing various permutations that are not matching keys in 'dir_index_b' |
| explain (costs off) select b from zoo order by b nulls first limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b |
| -> Limit |
| -> Sort |
| Sort Key: b NULLS FIRST |
| -> Seq Scan on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| select b from zoo order by b nulls first limit 3; |
| b |
| --------- |
| |
| col_b1 |
| col_b10 |
| (3 rows) |
| |
| explain (costs off) select b from zoo order by b desc nulls last limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b |
| -> Limit |
| -> Sort |
| Sort Key: b DESC NULLS LAST |
| -> Seq Scan on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| select b from zoo order by b desc nulls last limit 3; |
| b |
| ----------- |
| col_b9999 |
| col_b9998 |
| col_b9997 |
| (3 rows) |
| |
| -- Testing various permutations that are not matching keys in 'dir_index_c' |
| explain (costs off) select c from zoo order by c limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: c |
| -> Limit |
| -> Sort |
| Sort Key: c |
| -> Seq Scan on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| select c from zoo order by c limit 3; |
| c |
| -------- |
| 0.3125 |
| 0.625 |
| 0.9375 |
| (3 rows) |
| |
| explain (costs off) select c from zoo order by c desc limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: c |
| -> Limit |
| -> Sort |
| Sort Key: c DESC |
| -> Seq Scan on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| select c from zoo order by c desc limit 3; |
| c |
| ----------- |
| |
| 3125 |
| 3124.6875 |
| (3 rows) |
| |
| -- Testing various permutations that are not matching keys in 'dir_index_bcd' |
| explain (costs off) select b,c,d from zoo order by b ,c desc,d desc limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b, c, d |
| -> Limit |
| -> Sort |
| Sort Key: b, c DESC, d DESC |
| -> Seq Scan on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| select b,c,d from zoo order by b ,c desc,d desc limit 3; |
| b | c | d |
| ----------+--------+----- |
| col_b1 | 0.3125 | 2 |
| col_b10 | 3.125 | 11 |
| col_b100 | 31.25 | 101 |
| (3 rows) |
| |
| explain (costs off) select b,c,d from zoo order by b ,c ,d desc limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b, c, d |
| -> Limit |
| -> Sort |
| Sort Key: b, c, d DESC |
| -> Seq Scan on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| select b,c,d from zoo order by b ,c ,d desc limit 3; |
| b | c | d |
| ----------+--------+----- |
| col_b1 | 0.3125 | 2 |
| col_b10 | 3.125 | 11 |
| col_b100 | 31.25 | 101 |
| (3 rows) |
| |
| explain (costs off) select b,c,d from zoo order by b desc, c ,d desc limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b, c, d |
| -> Limit |
| -> Sort |
| Sort Key: b DESC, c, d DESC |
| -> Seq Scan on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| select b,c,d from zoo order by b desc, c ,d desc limit 3; |
| b | c | d |
| -----------+-----------+------- |
| | | |
| col_b9999 | 3124.6875 | 10000 |
| col_b9998 | 3124.375 | 9999 |
| (3 rows) |
| |
| -- Testing various permutations that are not matching keys in 'dir_index_fde' |
| explain (costs off) select f,d,e from zoo order by f ,d desc,e desc limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: f, d, e |
| -> Limit |
| -> Sort |
| Sort Key: f, d DESC, e DESC |
| -> Seq Scan on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| select f,d,e from zoo order by f ,d desc,e desc limit 3; |
| f | d | e |
| ---+---+-------- |
| 4 | 2 | col_e1 |
| 5 | 3 | col_e2 |
| 6 | 4 | col_e3 |
| (3 rows) |
| |
| explain (costs off) select f,d,e from zoo order by f,d ,e desc limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: f, d, e |
| -> Limit |
| -> Sort |
| Sort Key: f, d, e DESC |
| -> Seq Scan on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| select f,d,e from zoo order by f,d ,e desc limit 3; |
| f | d | e |
| ---+---+-------- |
| 4 | 2 | col_e1 |
| 5 | 3 | col_e2 |
| 6 | 4 | col_e3 |
| (3 rows) |
| |
| explain (costs off) select f,d,e from zoo order by f desc, d ,e desc limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: f, d, e |
| -> Limit |
| -> Sort |
| Sort Key: f DESC, d, e DESC |
| -> Seq Scan on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| select f,d,e from zoo order by f desc, d ,e desc limit 3; |
| f | d | e |
| -------+-------+------------ |
| | | |
| 10003 | 10001 | col_e10000 |
| 10002 | 10000 | col_e9999 |
| (3 rows) |
| |
| -- Testing various permutations that are not matching keys in 'dir_index_eda' |
| explain (costs off) select e,d,a from zoo order by e, d desc,a desc limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: e, d, a |
| -> Limit |
| -> Sort |
| Sort Key: e, d DESC, a DESC |
| -> Seq Scan on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| select e,d,a from zoo order by e, d desc,a desc limit 3; |
| e | d | a |
| ----------+-----+----- |
| col_e1 | 2 | 1 |
| col_e10 | 11 | 10 |
| col_e100 | 101 | 100 |
| (3 rows) |
| |
| explain (costs off) select e,d,a from zoo order by e desc,d desc,a desc limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: e, d, a |
| -> Limit |
| -> Sort |
| Sort Key: e DESC, d DESC, a DESC |
| -> Seq Scan on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| select e,d,a from zoo order by e desc,d desc,a desc limit 3; |
| e | d | a |
| -----------+-------+------ |
| | | |
| col_e9999 | 10000 | 9999 |
| col_e9998 | 9999 | 9998 |
| (3 rows) |
| |
| explain (costs off) select e,d,a from zoo order by e ,d ,a limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: e, d, a |
| -> Limit |
| -> Sort |
| Sort Key: e, d, a |
| -> Seq Scan on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| select e,d,a from zoo order by e ,d ,a limit 3; |
| e | d | a |
| ----------+-----+----- |
| col_e1 | 2 | 1 |
| col_e10 | 11 | 10 |
| col_e100 | 101 | 100 |
| (3 rows) |
| |
| -- Testing various permutations of order by on non-index columns. Expected to choose SeqScan with Sort |
| explain (costs off) select d, f from zoo order by d, f limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: d, f |
| -> Limit |
| -> Sort |
| Sort Key: d, f |
| -> Seq Scan on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| select d, f from zoo order by d, f limit 3; |
| d | f |
| ---+--- |
| 2 | 4 |
| 3 | 5 |
| 4 | 6 |
| (3 rows) |
| |
| explain (costs off) select a,e from zoo order by a,e limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a, e |
| -> Limit |
| -> Sort |
| Sort Key: a, e |
| -> Seq Scan on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| select a,e from zoo order by a,e limit 3; |
| a | e |
| ---+-------- |
| 1 | col_e1 |
| 2 | col_e2 |
| 3 | col_e3 |
| (3 rows) |
| |
| explain (costs off) select d,a from zoo order by d,a desc limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: d, a |
| -> Limit |
| -> Sort |
| Sort Key: d, a DESC |
| -> Seq Scan on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| select d,a from zoo order by d,a desc limit 3; |
| d | a |
| ---+--- |
| 2 | 1 |
| 3 | 2 |
| 4 | 3 |
| (3 rows) |
| |
| explain (costs off) select d,c from zoo order by d desc,c limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: d, c |
| -> Limit |
| -> Sort |
| Sort Key: d DESC, c |
| -> Seq Scan on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| select d,c from zoo order by d desc,c limit 3; |
| d | c |
| -------+----------- |
| | |
| 10001 | 3125 |
| 10000 | 3124.6875 |
| (3 rows) |
| |
| -- Validate if SeqScan is chosen if order by cols also have the Included Column of covering index |
| explain (costs off) select e,b from zoo order by e, b limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: e, b |
| -> Limit |
| -> Sort |
| Sort Key: e, b |
| -> Seq Scan on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| select e,b from zoo order by e,b limit 3; |
| e | b |
| ----------+---------- |
| col_e1 | col_b1 |
| col_e10 | col_b10 |
| col_e100 | col_b100 |
| (3 rows) |
| |
| -- Purpose: Validate if IndexOnlyScan Forward/Backward is chosen when required for queries with order by and limit |
| -- Vacuum table to Ensure IndexOnlyScan is chosen |
| vacuum zoo; |
| -- Testing various permutations of order by columns that are expected to choose IndexOnlyScan Forward |
| explain (costs off) select b from zoo order by b desc limit 3; |
| QUERY PLAN |
| --------------------------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b |
| -> Limit |
| -> Index Only Scan using dir_index_b on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select b from zoo order by b desc limit 3; |
| b |
| ----------- |
| |
| col_b9999 |
| col_b9998 |
| (3 rows) |
| |
| explain (costs off) select e,d,a from zoo order by e, d desc nulls last limit 3; |
| QUERY PLAN |
| ----------------------------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: e, d |
| -> Limit |
| -> Index Only Scan using dir_index_eda on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select e,d,a from zoo order by e, d desc nulls last limit 3; |
| e | d | a |
| ----------+-----+----- |
| col_e1 | 2 | 1 |
| col_e10 | 11 | 10 |
| col_e100 | 101 | 100 |
| (3 rows) |
| |
| explain (costs off) select b,c,d from zoo order by b, c, d limit 3; |
| QUERY PLAN |
| ----------------------------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b, c, d |
| -> Limit |
| -> Index Only Scan using dir_index_bcd on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select b,c,d from zoo order by b, c, d limit 3; |
| b | c | d |
| ----------+--------+----- |
| col_b1 | 0.3125 | 2 |
| col_b10 | 3.125 | 11 |
| col_b100 | 31.25 | 101 |
| (3 rows) |
| |
| explain (costs off) select f,d from zoo order by f desc,d desc,e desc limit 3; |
| QUERY PLAN |
| ----------------------------------------------------------------------------------------------------------------- |
| Result |
| -> Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: f, d, e |
| -> Limit |
| -> Index Only Scan using dir_index_fde on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (7 rows) |
| |
| select f,d from zoo order by f desc,d desc,e desc limit 3; |
| f | d |
| -------+------- |
| | |
| 10003 | 10001 |
| 10002 | 10000 |
| (3 rows) |
| |
| -- Testing various permutations of order by columns that are expected to choose IndexOnlyScan Backward |
| explain (costs off) select e,d,a from zoo order by e desc,d nulls first limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: e, d |
| -> Limit |
| -> Index Only Scan Backward using dir_index_eda on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select e,d,a from zoo order by e desc,d nulls first limit 3; |
| e | d | a |
| -----------+-------+------ |
| | | |
| col_e9999 | 10000 | 9999 |
| col_e9998 | 9999 | 9998 |
| (3 rows) |
| |
| explain (costs off) select e,d,a from zoo order by e desc,d nulls first,a desc limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: e, d, a |
| -> Limit |
| -> Index Only Scan Backward using dir_index_eda on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select e,d,a from zoo order by e desc,d nulls first,a desc limit 3; |
| e | d | a |
| -----------+-------+------ |
| | | |
| col_e9999 | 10000 | 9999 |
| col_e9998 | 9999 | 9998 |
| (3 rows) |
| |
| explain (costs off) select b,c from zoo order by b desc, c desc, d desc limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------------------------------------------------------- |
| Result |
| -> Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b, c, d |
| -> Limit |
| -> Index Only Scan Backward using dir_index_bcd on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (7 rows) |
| |
| select b,c from zoo order by b desc, c desc, d desc limit 3; |
| b | c |
| -----------+----------- |
| | |
| col_b9999 | 3124.6875 |
| col_b9998 | 3124.375 |
| (3 rows) |
| |
| explain (costs off) select f,d from zoo order by f, d limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: f, d |
| -> Limit |
| -> Index Only Scan Backward using dir_index_fde on zoo |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select f,d from zoo order by f, d limit 3; |
| f | d |
| ---+--- |
| 4 | 2 |
| 5 | 3 |
| 6 | 4 |
| (3 rows) |
| |
| -- Clean Up |
| DROP TABLE zoo; |
| -- Test Case: Test on Leaf Partition of a partition table with mixed data type columns. |
| -- Purpose: Validate if IndexScan/IndexOnlyScan with correct scan direction is used on expected index for queries with order by and limit. |
| CREATE TABLE test_partition_table(a int, b int, c float, d text, e numeric, f int) DISTRIBUTED BY (a) PARTITION BY range(a); |
| CREATE TABLE partition1 PARTITION OF test_partition_table FOR VALUES FROM (1) TO (3000); |
| NOTICE: table has parent, setting distribution columns to match parent table |
| CREATE TABLE partition2 PARTITION OF test_partition_table FOR VALUES FROM (3000) TO (6000); |
| NOTICE: table has parent, setting distribution columns to match parent table |
| CREATE TABLE partition3 PARTITION OF test_partition_table FOR VALUES FROM (6000) TO (9000); |
| NOTICE: table has parent, setting distribution columns to match parent table |
| -- single col index with opp direction on partition column |
| CREATE INDEX part_index_ac on test_partition_table using btree(a desc, c); |
| INSERT INTO test_partition_table SELECT i, i+3, i/4.2, concat('sample_text ',i), i/5, i from generate_series(1,8998) i; |
| -- Inserting nulls to verify results match when index key specifies nulls first or desc |
| INSERT INTO test_partition_table values (8999, null, null, null, null, null); |
| ANALYZE test_partition_table; |
| -- Validate if IndexScan Forward/Backward are used for order by on partition2 table(as this is a regular table and don't |
| -- have further partitions) |
| explain(costs off) select a, c from partition2 order by a desc, c limit 3; |
| QUERY PLAN |
| --------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a, c |
| -> Limit |
| -> Index Scan using partition2_a_c_idx on partition2 |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select a, c from partition2 order by a desc, c limit 3; |
| a | c |
| ------+-------------------- |
| 5999 | 1428.3333333333333 |
| 5998 | 1428.095238095238 |
| 5997 | 1427.857142857143 |
| (3 rows) |
| |
| explain(costs off) select a, c from partition2 order by a, c desc limit 3; |
| QUERY PLAN |
| ------------------------------------------------------------------------------ |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a, c |
| -> Limit |
| -> Index Scan Backward using partition2_a_c_idx on partition2 |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select a, c from partition2 order by a, c desc limit 3; |
| a | c |
| ------+------------------- |
| 3000 | 714.2857142857143 |
| 3001 | 714.5238095238095 |
| 3002 | 714.7619047619048 |
| (3 rows) |
| |
| -- Purpose: Validate if IndexOnlyScan Forward/Backward is chosen when required for queries with order by and limit |
| -- Vacuum table to ensure IndexOnlyScans are chosen |
| vacuum test_partition_table; |
| -- Expected to use IndexOnlyScan Forward |
| explain(costs off) select a, c from partition2 order by a desc, c limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a, c |
| -> Limit |
| -> Index Only Scan using partition2_a_c_idx on partition2 |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select a, c from partition2 order by a desc, c limit 3; |
| a | c |
| ------+-------------------- |
| 5999 | 1428.3333333333333 |
| 5998 | 1428.095238095238 |
| 5997 | 1427.857142857143 |
| (3 rows) |
| |
| -- Expected to use IndexOnlyScan Backward |
| explain(costs off) select a, c from partition2 order by a, c desc limit 3; |
| QUERY PLAN |
| ----------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a, c |
| -> Limit |
| -> Index Only Scan Backward using partition2_a_c_idx on partition2 |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select a, c from partition2 order by a, c desc limit 3; |
| a | c |
| ------+------------------- |
| 3000 | 714.2857142857143 |
| 3001 | 714.5238095238095 |
| 3002 | 714.7619047619048 |
| (3 rows) |
| |
| -- Clean Up |
| DROP TABLE test_partition_table; |
| -- Test Case: Test on a Replicated table with mixed data type columns. |
| -- Purpose: Validate if Forward/Backward IndexScan works on Replicated table |
| CREATE TABLE test_replicated_table(a int, b int, c float, d text, e numeric) DISTRIBUTED REPLICATED; |
| -- multi col index with mixed index keys properties |
| CREATE INDEX rep_index_eda on test_replicated_table using btree(e desc nulls last, d,a desc); |
| INSERT INTO test_replicated_table SELECT i, i+3, i/4.2, concat('sample_text ',i), i/5 from generate_series(1,100) i; |
| -- Inserting nulls to verify results match when index key specifies nulls first or desc |
| INSERT INTO test_replicated_table values (null, null, null, null, null); |
| -- Positive tests: Validate if IndexScan Forward/Backward is chosen. |
| -- Validate if 'rep_index_eda' is used for order by matching to the index |
| explain(costs off) select e,d,a from test_replicated_table order by e desc nulls last, d, a desc limit 3; |
| QUERY PLAN |
| --------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 1:1 (slice1; segments: 1) |
| Merge Key: e, d, a |
| -> Index Scan using rep_index_eda on test_replicated_table |
| Optimizer: GPORCA |
| (5 rows) |
| |
| select e,d,a from test_replicated_table order by e desc nulls last, d, a desc limit 3; |
| e | d | a |
| ----+-----------------+----- |
| 20 | sample_text 100 | 100 |
| 19 | sample_text 95 | 95 |
| 19 | sample_text 96 | 96 |
| (3 rows) |
| |
| -- Validate if 'rep_index_eda' is used for order by commutative to the index |
| explain(costs off) select e,d,a from test_replicated_table order by e nulls first, d desc, a limit 3; |
| QUERY PLAN |
| ------------------------------------------------------------------------------ |
| Limit |
| -> Gather Motion 1:1 (slice1; segments: 1) |
| Merge Key: e, d, a |
| -> Index Scan Backward using rep_index_eda on test_replicated_table |
| Optimizer: GPORCA |
| (5 rows) |
| |
| select e,d,a from test_replicated_table order by e nulls first, d desc, a limit 3; |
| e | d | a |
| ---+---------------+--- |
| | | |
| 0 | sample_text 4 | 4 |
| 0 | sample_text 3 | 3 |
| (3 rows) |
| |
| -- Negative tests: Validate if a SeqScan is chosen for order by cols not matching any indices. Expected to choose SeqScan with Sort |
| explain(costs off) select d,a from test_replicated_table order by d,a desc limit 3; |
| QUERY PLAN |
| ----------------------------------------------------- |
| Limit |
| -> Gather Motion 1:1 (slice1; segments: 1) |
| Merge Key: d, a |
| -> Sort |
| Sort Key: d, a DESC |
| -> Seq Scan on test_replicated_table |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (7 rows) |
| |
| select d,a from test_replicated_table order by d,a desc limit 3; |
| d | a |
| -----------------+----- |
| sample_text 1 | 1 |
| sample_text 10 | 10 |
| sample_text 100 | 100 |
| (3 rows) |
| |
| -- Clean Up |
| DROP TABLE test_replicated_table; |
| -- Test Case: Test on AO table with mixed data type columns. |
| -- IndexOnlyScans are supported but IndexScans aren't supported on AO tables |
| CREATE TABLE test_ao_table(a int, b int, c float, d text, e numeric) WITH (appendonly=true) DISTRIBUTED BY (a); |
| -- multi col index with mixed index keys properties |
| CREATE INDEX ao_index_eda on test_ao_table using btree(e desc nulls last, d,a desc); |
| INSERT INTO test_ao_table SELECT i, i+3, i/4.2, concat('sample_text ',i), i/5 from generate_series(1,100) i; |
| -- Expected to choose IndexOnlyScan Forward |
| explain(costs off) select e,d,a from test_ao_table order by e desc nulls last, d, a desc limit 3; |
| QUERY PLAN |
| ----------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: e, d, a |
| -> Index Only Scan using ao_index_eda on test_ao_table |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (5 rows) |
| |
| select e,d,a from test_ao_table order by e desc nulls last, d, a desc limit 3; |
| e | d | a |
| ----+-----------------+----- |
| 20 | sample_text 100 | 100 |
| 19 | sample_text 95 | 95 |
| 19 | sample_text 96 | 96 |
| (3 rows) |
| |
| -- Expected to choose IndexOnlyScan Backward |
| explain(costs off) select e,d,a from test_ao_table order by e nulls first, d desc, a limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: e, d, a |
| -> Index Only Scan Backward using ao_index_eda on test_ao_table |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (5 rows) |
| |
| select e,d,a from test_ao_table order by e nulls first, d desc, a limit 3; |
| e | d | a |
| ---+---------------+--- |
| 0 | sample_text 4 | 4 |
| 0 | sample_text 3 | 3 |
| 0 | sample_text 2 | 2 |
| (3 rows) |
| |
| -- Expected to choose SeqScan with a Sort as IndexOnlyScan doesn't support, since it selects all columns |
| explain(costs off) select * from test_ao_table order by e desc nulls last, d, a desc limit 3; |
| QUERY PLAN |
| ------------------------------------------------------ |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: e, d, a |
| -> Sort |
| Sort Key: e DESC NULLS LAST, d, a DESC |
| -> Seq Scan on test_ao_table |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (7 rows) |
| |
| select * from test_ao_table order by e desc nulls last, d, a desc limit 3; |
| a | b | c | d | e |
| -----+-----+--------------------+-----------------+---- |
| 100 | 103 | 23.80952380952381 | sample_text 100 | 20 |
| 95 | 98 | 22.61904761904762 | sample_text 95 | 19 |
| 96 | 99 | 22.857142857142858 | sample_text 96 | 19 |
| (3 rows) |
| |
| -- Clean Up |
| DROP TABLE test_ao_table; |
| -- Test Case: Test on table with all other types of indexes apart from btree(bitmap, hash, brin, spgist, gist, gin) |
| -- Purpose: Evaluate if Forward/Backward IndexScan works on query with order by and limit, with other type of indices |
| -- Note: No other index type apart from btree support IndexScans |
| CREATE TABLE test_multi_index_types_table(a int, b int, c float, d text, e tsquery, f tsvector); |
| -- create a bitmap index |
| create index bitmap_a on test_multi_index_types_table using bitmap(a); |
| -- create a hash index |
| create index hash_b on test_multi_index_types_table using hash(b); |
| -- create a brin index |
| create index brin_c on test_multi_index_types_table using brin(c); |
| -- create a spgist index |
| create index spgist_d on test_multi_index_types_table using spgist(d); |
| -- create a gin index |
| create index gist_e on test_multi_index_types_table using gist(e); |
| -- create a gin index |
| create index gin_f on test_multi_index_types_table using gin(f); |
| -- All of the below queries are expected to use SeqScan with a Sort as only btree index supports IndexScan |
| explain(costs off) select a from test_multi_index_types_table order by a limit 3; |
| QUERY PLAN |
| ------------------------------------------------------------ |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> Sort |
| Sort Key: a |
| -> Seq Scan on test_multi_index_types_table |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (7 rows) |
| |
| explain(costs off) select b from test_multi_index_types_table order by b limit 3; |
| QUERY PLAN |
| ------------------------------------------------------------ |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b |
| -> Sort |
| Sort Key: b |
| -> Seq Scan on test_multi_index_types_table |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (7 rows) |
| |
| explain(costs off) select c from test_multi_index_types_table order by c limit 3; |
| QUERY PLAN |
| ------------------------------------------------------------ |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: c |
| -> Sort |
| Sort Key: c |
| -> Seq Scan on test_multi_index_types_table |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (7 rows) |
| |
| explain(costs off) select d from test_multi_index_types_table order by d limit 3; |
| QUERY PLAN |
| ------------------------------------------------------------ |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: d |
| -> Sort |
| Sort Key: d |
| -> Seq Scan on test_multi_index_types_table |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (7 rows) |
| |
| explain(costs off) select e from test_multi_index_types_table order by e limit 3; |
| QUERY PLAN |
| ------------------------------------------------------------ |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: e |
| -> Sort |
| Sort Key: e |
| -> Seq Scan on test_multi_index_types_table |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (7 rows) |
| |
| explain(costs off) select f from test_multi_index_types_table order by f limit 3; |
| QUERY PLAN |
| ------------------------------------------------------------ |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: f |
| -> Sort |
| Sort Key: f |
| -> Seq Scan on test_multi_index_types_table |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (7 rows) |
| |
| -- Clean Up |
| DROP TABLE test_multi_index_types_table; |
| -- Purpose: Test Forward/Backward IndexScan over views |
| create table test_on_views(a int, b int, c float); |
| INSERT INTO test_on_views SELECT i+3, i, i/4.2 from generate_series(1,100) i; |
| -- create a index on column b |
| create index view_index on test_on_views using btree(b); |
| analyze test_on_views; |
| -- create view |
| create view test_view as select b from test_on_views; |
| -- Expected to use IndexScan Forward |
| explain(costs off) select * from test_view order by b limit 3; |
| QUERY PLAN |
| ---------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b |
| -> Limit |
| -> Index Scan using view_index on test_on_views |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select * from test_view order by b limit 3; |
| b |
| --- |
| 1 |
| 2 |
| 3 |
| (3 rows) |
| |
| -- Expected to use IndexScan Backwards |
| explain(costs off) select * from test_view order by b desc limit 3; |
| QUERY PLAN |
| ------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b |
| -> Limit |
| -> Index Scan Backward using view_index on test_on_views |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select * from test_view order by b desc limit 3; |
| b |
| ----- |
| 100 |
| 99 |
| 98 |
| (3 rows) |
| |
| -- Clean Up |
| DROP VIEW test_view; |
| DROP TABLE test_on_views; |
| -- Purpose: Test Forward/Backward IndexScan over partial indices |
| -- ORCA_FEATURE_NOT_SUPPORTED: partial indexes are not supported |
| create table test_on_partial_indices(a int, b int, c float); |
| -- create a partial index on column b |
| create index partial_index on test_on_partial_indices(b desc) where b<54; |
| analyze test_on_partial_indices; |
| -- Expected to use SeqScan with Sort |
| explain(costs off) select b from test_on_partial_indices order by b desc limit 3; |
| QUERY PLAN |
| ------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b |
| -> Sort |
| Sort Key: b DESC |
| -> Seq Scan on test_on_partial_indices |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (7 rows) |
| |
| -- Clean Up |
| DROP TABLE test_on_partial_indices; |
| -- Purpose: Test Forward/Backward IndexScan over primary key |
| create table test_on_pk_column(a int primary key , b int, c float); |
| INSERT INTO test_on_pk_column SELECT i+3, i, i/4.2 from generate_series(1,100) i; |
| analyze test_on_pk_column; |
| -- Expected to use Forward IndexScan |
| explain(costs off) select a from test_on_pk_column order by a limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> Limit |
| -> Index Scan using test_on_pk_column_pkey on test_on_pk_column |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select a from test_on_pk_column order by a limit 3; |
| a |
| --- |
| 4 |
| 5 |
| 6 |
| (3 rows) |
| |
| -- Expected to use Backward IndexScan |
| explain(costs off) select a from test_on_pk_column order by a desc limit 3; |
| QUERY PLAN |
| ----------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> Limit |
| -> Index Scan Backward using test_on_pk_column_pkey on test_on_pk_column |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select a from test_on_pk_column order by a desc limit 3; |
| a |
| ----- |
| 103 |
| 102 |
| 101 |
| (3 rows) |
| |
| -- Clean Up |
| DROP TABLE test_on_pk_column; |
| -- Purpose: Test Forward/Backward IndexScan over column with unique constraint |
| create table test_on_unique_column(a int, b int unique, c float); |
| INSERT INTO test_on_unique_column SELECT i+3, i, i/4.2 from generate_series(1,100) i; |
| analyze test_on_unique_column; |
| -- Expected to use Forward IndexScan |
| explain(costs off) select a from test_on_unique_column order by b limit 3; |
| QUERY PLAN |
| ----------------------------------------------------------------------------------------------- |
| Result |
| -> Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b |
| -> Limit |
| -> Index Scan using test_on_unique_column_b_key on test_on_unique_column |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (7 rows) |
| |
| select a from test_on_unique_column order by b limit 3; |
| a |
| --- |
| 4 |
| 5 |
| 6 |
| (3 rows) |
| |
| -- Expected to use Backward IndexScan |
| explain(costs off) select a from test_on_unique_column order by b desc limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------------------------------------- |
| Result |
| -> Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b |
| -> Limit |
| -> Index Scan Backward using test_on_unique_column_b_key on test_on_unique_column |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (7 rows) |
| |
| select a from test_on_unique_column order by b desc limit 3; |
| a |
| ----- |
| 103 |
| 102 |
| 101 |
| (3 rows) |
| |
| -- Clean Up |
| DROP TABLE test_on_unique_column; |
| -- Purpose: Test Forward/Backward IndexScan with order by on Index Expressions |
| -- ORCA_FEATURE_NOT_SUPPORTED: Indexes on Expressions are not supported by ORCA |
| create table test_on_index_expressions(a int, b int, c float); |
| CREATE INDEX expr_index_a on test_on_index_expressions using btree(a); |
| analyze test_on_index_expressions; |
| -- Expected to use SeqScan with Sort |
| explain(costs off) select a,b from test_on_index_expressions order by a*b desc limit 3; |
| QUERY PLAN |
| --------------------------------------------------------------- |
| Result |
| -> Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: ((a * b)) |
| -> Sort |
| Sort Key: ((a * b)) DESC |
| -> Seq Scan on test_on_index_expressions |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| -- Expected to use SeqScan with Sort |
| explain(costs off) select a from test_on_index_expressions order by a|2 limit 3; |
| QUERY PLAN |
| --------------------------------------------------------------- |
| Result |
| -> Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: ((a | 2)) |
| -> Sort |
| Sort Key: ((a | 2)) |
| -> Seq Scan on test_on_index_expressions |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| -- Expected to use SeqScan with Sort |
| explain(costs off) select a from test_on_index_expressions order by a is not null desc limit 3; |
| QUERY PLAN |
| --------------------------------------------------------------- |
| Result |
| -> Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: ((NOT (a IS NULL))) |
| -> Sort |
| Sort Key: ((NOT (a IS NULL))) DESC |
| -> Seq Scan on test_on_index_expressions |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| -- Expected to use SeqScan with Sort |
| explain(costs off) select a from test_on_index_expressions order by a>3 limit 3; |
| QUERY PLAN |
| --------------------------------------------------------------- |
| Result |
| -> Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: ((a > 3)) |
| -> Sort |
| Sort Key: ((a > 3)) |
| -> Seq Scan on test_on_index_expressions |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| -- define a simple multiplication function |
| CREATE OR REPLACE FUNCTION multiply_by_two(integer) |
| RETURNS INTEGER |
| LANGUAGE 'plpgsql' |
| AS $$ |
| BEGIN |
| RETURN $1 * 2; |
| END; |
| $$; |
| -- Order by using multiplication function. Expected to use SeqScan with Sort |
| explain(costs off) select a from test_on_index_expressions order by multiply_by_two(a) limit 3; |
| QUERY PLAN |
| --------------------------------------------------------------- |
| Result |
| -> Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: (multiply_by_two(a)) |
| -> Sort |
| Sort Key: (multiply_by_two(a)) |
| -> Seq Scan on test_on_index_expressions |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| -- Clean Up |
| DROP FUNCTION multiply_by_two; |
| DROP TABLE test_on_index_expressions; |
| -- Purpose: Test Forward/Backward IndexScan with order by on custom data type |
| -- create a custom type |
| CREATE TYPE custom_data_type AS ( |
| name VARCHAR, |
| age INTEGER); |
| create table test_on_custom_data_type(a int, b float, c custom_data_type); |
| create index index_on_custom_type on test_on_custom_data_type using btree(c); |
| insert into test_on_custom_data_type select i, i/3, (concat('person', i), i)::custom_data_type from generate_series(1,100)i; |
| analyze test_on_custom_data_type; |
| -- Expected to use Forward IndexScan |
| explain(costs off) select c from test_on_custom_data_type order by c limit 3; |
| QUERY PLAN |
| ------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: c |
| -> Limit |
| -> Index Scan using index_on_custom_type on test_on_custom_data_type |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select c from test_on_custom_data_type order by c limit 3; |
| c |
| ----------------- |
| (person1,1) |
| (person10,10) |
| (person100,100) |
| (3 rows) |
| |
| -- Expected to use Backward IndexScan |
| explain(costs off) select c from test_on_custom_data_type order by c desc limit 3; |
| QUERY PLAN |
| ---------------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: c |
| -> Limit |
| -> Index Scan Backward using index_on_custom_type on test_on_custom_data_type |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| select c from test_on_custom_data_type order by c desc limit 3; |
| c |
| --------------- |
| (person99,99) |
| (person98,98) |
| (person97,97) |
| (3 rows) |
| |
| -- Clean Up |
| DROP TABLE test_on_custom_data_type; |
| DROP TYPE custom_data_type; |
| -- Purpose: Test DynamicIndexScan on partition table with composite partition columns |
| -- ORCA_FEATURE_NOT_SUPPORTED: Composite partition keys are not supported in ORCA |
| CREATE TABLE tbl_range (id int, col1 int, col2 int, col3 int) PARTITION BY RANGE (col1, col2); |
| CREATE TABLE p1 PARTITION OF tbl_range FOR VALUES FROM (0, 0) TO (100, 100); |
| NOTICE: table has parent, setting distribution columns to match parent table |
| CREATE TABLE p2 PARTITION OF tbl_range FOR VALUES FROM (100, 100) TO (200, 200); |
| NOTICE: table has parent, setting distribution columns to match parent table |
| CREATE TABLE p3 PARTITION OF tbl_range FOR VALUES FROM (200, 200) TO (300, 300); |
| NOTICE: table has parent, setting distribution columns to match parent table |
| CREATE INDEX idx_on_tbl_range ON tbl_range using btree(col1, col2); |
| ANALYZE tbl_range; |
| -- Demonstrate that Planner could use IndexScan with MergeAppend for these cases |
| set enable_seqscan to off; |
| explain(costs off) select * from tbl_range order by col1, col2 limit 3; |
| QUERY PLAN |
| ----------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: tbl_range.col1, tbl_range.col2 |
| -> Limit |
| -> Append |
| -> Index Scan using p1_col1_col2_idx on p1 tbl_range_1 |
| -> Index Scan using p2_col1_col2_idx on p2 tbl_range_2 |
| -> Index Scan using p3_col1_col2_idx on p3 tbl_range_3 |
| Optimizer: Postgres query optimizer |
| (9 rows) |
| |
| explain(costs off) select * from tbl_range order by col1 limit 3; |
| QUERY PLAN |
| ----------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: tbl_range.col1 |
| -> Limit |
| -> Append |
| -> Index Scan using p1_col1_col2_idx on p1 tbl_range_1 |
| -> Index Scan using p2_col1_col2_idx on p2 tbl_range_2 |
| -> Index Scan using p3_col1_col2_idx on p3 tbl_range_3 |
| Optimizer: Postgres query optimizer |
| (9 rows) |
| |
| -- Clean Up |
| DROP TABLE tbl_range; |
| -- Purpose: Test DynamicIndexScan on multi-level partition table |
| -- ORCA_FEATURE_NOT_SUPPORTED: Multi-level partitioned tables are not supported in ORCA |
| CREATE TABLE sales_data(year int, geo varchar(2), impressions integer, sales integer) PARTITION BY RANGE (year); |
| CREATE TABLE sales_data_18_20 PARTITION OF sales_data FOR VALUES FROM (2018) TO (2020) PARTITION BY LIST (geo); |
| NOTICE: table has parent, setting distribution columns to match parent table |
| CREATE TABLE sales_data_UK_18_20 PARTITION OF sales_data_18_20 FOR VALUES IN ('UK'); |
| NOTICE: table has parent, setting distribution columns to match parent table |
| CREATE TABLE sales_data_US_18_20 PARTITION OF sales_data_18_20 FOR VALUES IN ('US'); |
| NOTICE: table has parent, setting distribution columns to match parent table |
| CREATE INDEX idx_on_year ON sales_data USING btree(year desc); |
| ANALYZE sales_data; |
| explain(costs off) select * from sales_data order by year desc limit 3; |
| QUERY PLAN |
| ----------------------------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: sales_data_1.year |
| -> Limit |
| -> Merge Append |
| Sort Key: sales_data_1.year DESC |
| -> Index Scan using sales_data_uk_18_20_year_idx on sales_data_uk_18_20 sales_data_1 |
| -> Index Scan using sales_data_us_18_20_year_idx on sales_data_us_18_20 sales_data_2 |
| Optimizer: Postgres query optimizer |
| (9 rows) |
| |
| -- Clean Up |
| DROP TABLE sales_data; |
| -- Purpose: Test DynamicIndexScan on a hash partitioned table |
| -- ORCA_FEATURE_NOT_SUPPORTED: ORCA do not support hash partitioning |
| CREATE TABLE tbl_hash (id int, col1 int, col2 int, col3 int) PARTITION BY HASH (col1); |
| CREATE TABLE p1 PARTITION OF tbl_hash FOR VALUES WITH (MODULUS 100, REMAINDER 20); |
| NOTICE: table has parent, setting distribution columns to match parent table |
| CREATE TABLE p2 PARTITION OF tbl_hash FOR VALUES WITH (MODULUS 100, REMAINDER 30); |
| NOTICE: table has parent, setting distribution columns to match parent table |
| CREATE INDEX idx_on_tbl_hash ON tbl_hash using btree(col1); |
| ANALYZE tbl_hash; |
| explain(costs off) select * from tbl_hash order by col1 limit 3; |
| QUERY PLAN |
| ----------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: tbl_hash.col1 |
| -> Limit |
| -> Merge Append |
| Sort Key: tbl_hash.col1 |
| -> Index Scan using p1_col1_idx on p1 tbl_hash_1 |
| -> Index Scan using p2_col1_idx on p2 tbl_hash_2 |
| Optimizer: Postgres query optimizer |
| (9 rows) |
| |
| -- Clean Up |
| DROP TABLE tbl_hash; |
| -- Purpose: Test DynamicIndexScan on a list partitioned table |
| -- Currently, ORCA doesn't support DynamicIndex(Only)Scan on partition tables. |
| CREATE TABLE sales_data(year int, geo varchar(2), impressions integer, sales integer) PARTITION BY LIST (geo); |
| CREATE TABLE sales_data_UK PARTITION OF sales_data FOR VALUES IN ('UK'); |
| NOTICE: table has parent, setting distribution columns to match parent table |
| CREATE TABLE sales_data_AU PARTITION OF sales_data FOR VALUES IN ('AU'); |
| NOTICE: table has parent, setting distribution columns to match parent table |
| CREATE INDEX idx_on_geo ON sales_data using btree(geo); |
| ANALYZE sales_data; |
| -- Expected to use DynamicSeqScan with Sort |
| explain(costs off) select * from sales_data order by geo limit 3; |
| QUERY PLAN |
| ---------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: geo |
| -> Sort |
| Sort Key: geo |
| -> Dynamic Seq Scan on sales_data |
| Number of partitions to scan: 2 (out of 2) |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| -- Clean Up |
| reset enable_seqscan; |
| DROP TABLE sales_data; |
| -- Purpose: Test DynamicIndexScan on partition table with holes and a default partition |
| -- Currently, ORCA doesn't support DynamicIndex(Only)Scan on partition tables. |
| CREATE TABLE tbl_range (id int, col1 int, col2 int, col3 int) PARTITION BY RANGE (col1); |
| CREATE TABLE p1 PARTITION OF tbl_range FOR VALUES FROM (100) TO (200); |
| NOTICE: table has parent, setting distribution columns to match parent table |
| CREATE TABLE p2 PARTITION OF tbl_range FOR VALUES FROM (200) TO (300); |
| NOTICE: table has parent, setting distribution columns to match parent table |
| CREATE TABLE p3 PARTITION OF tbl_range DEFAULT; |
| NOTICE: table has parent, setting distribution columns to match parent table |
| CREATE INDEX idx_on_tbl_range ON tbl_range using btree(col1); |
| INSERT INTO tbl_range select i-1,i,i*3,i/2 FROM generate_series(0, 400) i; |
| ANALYZE tbl_range; |
| -- Demonstrate that Planner could use IndexScan with MergeAppend for these cases |
| set enable_seqscan to off; |
| explain(costs off) select * from tbl_range order by col1 limit 3; |
| QUERY PLAN |
| ---------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: col1 |
| -> Limit |
| -> Sort |
| Sort Key: col1 |
| -> Dynamic Seq Scan on tbl_range |
| Number of partitions to scan: 3 (out of 3) |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (9 rows) |
| |
| select * from tbl_range order by col1 limit 3; |
| id | col1 | col2 | col3 |
| ----+------+------+------ |
| -1 | 0 | 0 | 0 |
| 0 | 1 | 3 | 0 |
| 1 | 2 | 6 | 1 |
| (3 rows) |
| |
| -- Clean Up |
| DROP TABLE tbl_range; |
| -- Purpose: This section includes tests on general table where backward index scan could be used, but is not used currently since |
| -- those cases are not supported as part of this initial addition of backward index support. |
| CREATE TABLE test_yet_unsupported_backwrd_idxscan_cases (a int, b text, c float, d int, e int); |
| -- single col index with default order |
| CREATE INDEX index_a on test_yet_unsupported_backwrd_idxscan_cases using btree(a); |
| -- single col index with reverse order |
| CREATE INDEX index_b on test_yet_unsupported_backwrd_idxscan_cases using btree(b desc); |
| CREATE INDEX index_cd on test_yet_unsupported_backwrd_idxscan_cases using btree(c, d); |
| -- Inserting data to demonstrate that Planner chooses IndexScans for these cases |
| INSERT INTO test_yet_unsupported_backwrd_idxscan_cases select i, concat('sample_text', i), i/3.3, i,i-2 from generate_series(1,10000)i; |
| ANALYZE test_yet_unsupported_backwrd_idxscan_cases; |
| -- Cases with just order by without limit |
| explain(costs off) select a from test_yet_unsupported_backwrd_idxscan_cases order by a desc; |
| QUERY PLAN |
| -------------------------------------------------------------------- |
| Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> Sort |
| Sort Key: a DESC |
| -> Seq Scan on test_yet_unsupported_backwrd_idxscan_cases |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| explain(costs off) select c,d from test_yet_unsupported_backwrd_idxscan_cases order by c desc, d desc; |
| QUERY PLAN |
| -------------------------------------------------------------------- |
| Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: c, d |
| -> Sort |
| Sort Key: c DESC, d DESC |
| -> Seq Scan on test_yet_unsupported_backwrd_idxscan_cases |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| -- Since col a is asc in index, max(a) could use a backward index scan |
| explain(costs off) select max(a) from test_yet_unsupported_backwrd_idxscan_cases; |
| QUERY PLAN |
| --------------------------------------------------------------------------------------------------------- |
| Aggregate |
| -> Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> Limit |
| -> Index Scan Backward using index_a on test_yet_unsupported_backwrd_idxscan_cases |
| Index Cond: (a IS NOT NULL) |
| Optimizer: GPORCA |
| (8 rows) |
| |
| -- Cases with a predicate and order by (with/without limit). Order by columns commutating index column |
| explain(costs off) select * from test_yet_unsupported_backwrd_idxscan_cases where a>997 order by c desc, d desc; |
| QUERY PLAN |
| ------------------------------------------------------------------------------------ |
| Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: c, d |
| -> Sort |
| Sort Key: c DESC, d DESC |
| -> Index Scan using index_a on test_yet_unsupported_backwrd_idxscan_cases |
| Index Cond: (a > 997) |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (7 rows) |
| |
| explain(costs off) select * from test_yet_unsupported_backwrd_idxscan_cases where a>997 order by c desc, d desc limit 3; |
| QUERY PLAN |
| ------------------------------------------------------------------------------------------------ |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: c, d |
| -> Limit |
| -> Sort |
| Sort Key: c DESC, d DESC |
| -> Index Scan using index_a on test_yet_unsupported_backwrd_idxscan_cases |
| Index Cond: (a > 997) |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (9 rows) |
| |
| -- Cases with group by, order by (with/without limit). Order by cols commutating index column |
| explain(costs off) select a, sum(d) from test_yet_unsupported_backwrd_idxscan_cases group by a order by a desc; |
| QUERY PLAN |
| -------------------------------------------------------------------------- |
| Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> GroupAggregate |
| Group Key: a |
| -> Sort |
| Sort Key: a DESC |
| -> Seq Scan on test_yet_unsupported_backwrd_idxscan_cases |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| explain(costs off) select a, sum(d) from test_yet_unsupported_backwrd_idxscan_cases group by a order by a desc limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> Limit |
| -> GroupAggregate |
| Group Key: a |
| -> Sort |
| Sort Key: a DESC |
| -> Seq Scan on test_yet_unsupported_backwrd_idxscan_cases |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (10 rows) |
| |
| -- Case with group by, order by and a having clause (with/without limit). Order by cols commutating index. |
| explain(costs off) select a, sum(d) from test_yet_unsupported_backwrd_idxscan_cases group by a having a>30 order by a desc; |
| QUERY PLAN |
| ------------------------------------------------------------------------------------------ |
| Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> GroupAggregate |
| Group Key: a |
| -> Sort |
| Sort Key: a DESC |
| -> Index Scan using index_a on test_yet_unsupported_backwrd_idxscan_cases |
| Index Cond: (a > 30) |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (9 rows) |
| |
| explain(costs off) select a, sum(d) from test_yet_unsupported_backwrd_idxscan_cases group by a having a>30 order by a desc; |
| QUERY PLAN |
| ------------------------------------------------------------------------------------------ |
| Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> GroupAggregate |
| Group Key: a |
| -> Sort |
| Sort Key: a DESC |
| -> Index Scan using index_a on test_yet_unsupported_backwrd_idxscan_cases |
| Index Cond: (a > 30) |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (9 rows) |
| |
| -- Case with ordering via over() using window aggregates (with/without limit): rank(), row_number(), percent_rank() etc... |
| explain(costs off) select c,d, rank() over (order by c desc, d desc) from test_yet_unsupported_backwrd_idxscan_cases; |
| QUERY PLAN |
| -------------------------------------------------------------------------- |
| WindowAgg |
| Order By: c, d |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: c, d |
| -> Sort |
| Sort Key: c DESC, d DESC |
| -> Seq Scan on test_yet_unsupported_backwrd_idxscan_cases |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| explain(costs off) select c,d, rank() over (order by c desc, d desc) from test_yet_unsupported_backwrd_idxscan_cases limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------------- |
| Limit |
| -> WindowAgg |
| Order By: c, d |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: c, d |
| -> Sort |
| Sort Key: c DESC, d DESC |
| -> Seq Scan on test_yet_unsupported_backwrd_idxscan_cases |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (9 rows) |
| |
| -- Case with distinct and order by (with/without limit) |
| explain(costs off) select distinct(a) from test_yet_unsupported_backwrd_idxscan_cases order by a desc; |
| QUERY PLAN |
| -------------------------------------------------------------------------- |
| Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> GroupAggregate |
| Group Key: a |
| -> Sort |
| Sort Key: a DESC |
| -> Seq Scan on test_yet_unsupported_backwrd_idxscan_cases |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| explain(costs off) select distinct(a) from test_yet_unsupported_backwrd_idxscan_cases order by a desc limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> Limit |
| -> GroupAggregate |
| Group Key: a |
| -> Sort |
| Sort Key: a DESC |
| -> Seq Scan on test_yet_unsupported_backwrd_idxscan_cases |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (10 rows) |
| |
| -- Order by within a CTE without limit |
| explain(costs off) with sorted_by_cd as (select c,d from test_yet_unsupported_backwrd_idxscan_cases order by c desc, d desc) select c from sorted_by_cd; |
| QUERY PLAN |
| -------------------------------------------------------------- |
| Gather Motion 3:1 (slice1; segments: 3) |
| -> Seq Scan on test_yet_unsupported_backwrd_idxscan_cases |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (3 rows) |
| |
| -- Order by within a CTE, with limit outside CTE expression |
| explain(costs off) with sorted_by_cd as (select c,d from test_yet_unsupported_backwrd_idxscan_cases order by c desc, d desc) select c from sorted_by_cd limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| -> Limit |
| -> Seq Scan on test_yet_unsupported_backwrd_idxscan_cases |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (5 rows) |
| |
| -- Clean Up |
| DROP TABLE test_yet_unsupported_backwrd_idxscan_cases; |
| -- NL Joins can utilize IndexScan's sort property, but currently ORCA doesn't |
| -- generate IndexScan alternatives for NL joins. This tests the case where |
| -- IndexScan's order property could be used for joining two tables |
| CREATE TABLE employee(id int, name text, dep_id int, salary int); |
| CREATE TABLE department(dep_id int, dep_name text); |
| CREATE INDEX index_salary on employee using btree(salary); |
| ANALYZE employee; |
| ANALYZE department; |
| -- Forcing planner, ORCA to use a NL join |
| set enable_hashjoin to off; |
| set optimizer_enable_hashjoin to off; |
| set enable_seqscan to off; |
| -- Planner uses NL Join with IndexScan Backwards and the sort property of index 'index_salary', |
| -- but ORCA, since it doesn't generate IndexScan alternative uses NL join with a Sort operator. |
| explain(costs off) select e.id, e.name, e.salary, d.dep_name from employee e join department d on e.id=d.dep_id order by e.salary desc; |
| QUERY PLAN |
| -------------------------------------------------------------- |
| Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: e.salary |
| -> Sort |
| Sort Key: e.salary DESC |
| -> Nested Loop |
| Join Filter: (e.id = d.dep_id) |
| -> Seq Scan on employee e |
| -> Seq Scan on department d |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (9 rows) |
| |
| explain(costs off) select e.id, e.name, e.salary, d.dep_name from employee e join department d on e.id=d.dep_id order by e.salary desc limit 3; |
| QUERY PLAN |
| -------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: e.salary |
| -> Sort |
| Sort Key: e.salary DESC |
| -> Nested Loop |
| Join Filter: (e.id = d.dep_id) |
| -> Seq Scan on employee e |
| -> Seq Scan on department d |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (10 rows) |
| |
| -- Clean up |
| reset enable_hashjoin; |
| reset optimizer_enable_hashjoin; |
| reset enable_seqscan; |
| DROP TABLE employee; |
| DROP TABLE department; |
| -- Union all of two tables with order by on their indexed column uses |
| -- IndexScan's sort property with MergeAppend node in Planner. However in ORCA |
| -- we don't generate IndexScan alternative for union all, also we don't support |
| -- MergeAppend. But documenting this case for reference |
| CREATE TABLE table1(a int, b int); |
| CREATE TABLE table2(a int, b int); |
| CREATE INDEX tab1_idx on table1 using btree(b); |
| CREATE INDEX tab2_idx on table2 using btree(b); |
| -- inserting data and disabling seq_scan to avoid Planner generating a plan with |
| -- Sort operator and Append node instead of IndexScan with MergeAppend |
| set enable_seqscan to off; |
| INSERT INTO table1 select i, i from generate_series(1,99)i; |
| INSERT INTO table2 select i, i from generate_series(1,99)i; |
| ANALYZE table1; |
| ANALYZE table2; |
| explain(costs off) select b from table1 union all select b from table2 order by b desc; |
| QUERY PLAN |
| ------------------------------------------ |
| Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: table1.b |
| -> Sort |
| Sort Key: table1.b DESC |
| -> Append |
| -> Seq Scan on table1 |
| -> Seq Scan on table2 |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| reset enable_seqscan; |
| DROP TABLE table1; |
| DROP TABLE table2; |
| -- Purpose: This section includes tests on partition table where backward |
| -- DynamicIndexScan could be used, but is not used currently since ORCA currently |
| -- doesn't support DynamicIndex(Only)Scan on partition tables. |
| CREATE TABLE test_partition_table(a int, b text, c float, d int, e int) DISTRIBUTED BY (a) PARTITION BY range(a) (start (0) end(10000) every(2000)); |
| -- single col index on partition column |
| CREATE INDEX part_index_a on test_partition_table using btree(a); |
| -- multi col indices |
| CREATE INDEX part_index_cd on test_partition_table using btree(c,d); |
| CREATE INDEX part_index_bcd on test_partition_table using btree(b,c,d); |
| CREATE INDEX part_index_cad on test_partition_table using btree(c,a,d); |
| -- Inserting data to demonstrate that Planner chooses IndexScans for these cases |
| INSERT INTO test_partition_table select i, concat('sample_text', i), i/3.3, i,i-2 from generate_series(1,9999)i; |
| ANALYZE test_partition_table; |
| -- Currently ORCA doesn't support DynamicIndex(Only)Scans for order by on partition tables, |
| -- even if a btree index exists matching/commutative to order by cols. ORCA uses DynamicSeqScan with sort. |
| explain(costs off) select c,d from test_partition_table order by c, d limit 3; |
| QUERY PLAN |
| ---------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: c, d |
| -> Limit |
| -> Sort |
| Sort Key: c, d |
| -> Dynamic Seq Scan on test_partition_table |
| Number of partitions to scan: 5 (out of 5) |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (9 rows) |
| |
| explain(costs off) select c,a,d from test_partition_table order by c,a,d limit 3; |
| QUERY PLAN |
| ---------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: c, a, d |
| -> Limit |
| -> Sort |
| Sort Key: c, a, d |
| -> Dynamic Seq Scan on test_partition_table |
| Number of partitions to scan: 5 (out of 5) |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (9 rows) |
| |
| explain(costs off) select b,c,d from test_partition_table order by b desc,c desc,d desc limit 3; |
| QUERY PLAN |
| ---------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b, c, d |
| -> Limit |
| -> Sort |
| Sort Key: b DESC, c DESC, d DESC |
| -> Dynamic Seq Scan on test_partition_table |
| Number of partitions to scan: 5 (out of 5) |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (9 rows) |
| |
| -- Cases with just order by without limit |
| explain(costs off) select a from test_partition_table order by a desc; |
| QUERY PLAN |
| ---------------------------------------------------------- |
| Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> Sort |
| Sort Key: a DESC |
| -> Dynamic Seq Scan on test_partition_table |
| Number of partitions to scan: 5 (out of 5) |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (7 rows) |
| |
| -- Since col a is asc in index, max(a) could use a backward index scan |
| explain(costs off) select max(a) from test_partition_table; |
| QUERY PLAN |
| ---------------------------------------------------------------- |
| Finalize Aggregate |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| -> Partial Aggregate |
| -> Dynamic Seq Scan on test_partition_table |
| Number of partitions to scan: 5 (out of 5) |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| -- Cases with a predicate and order by (with/without limit). Order by columns commutating index column |
| explain(costs off) select * from test_partition_table where a BETWEEN 40 and 4000 or c not between 4000 and 6000 order by a desc; |
| QUERY PLAN |
| ------------------------------------------------------------------------------------------------------------------------- |
| Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> Sort |
| Sort Key: a DESC |
| -> Dynamic Seq Scan on test_partition_table |
| Number of partitions to scan: 5 (out of 5) |
| Filter: (((a >= 40) AND (a <= 4000)) OR (c < '4000'::double precision) OR (c > '6000'::double precision)) |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (8 rows) |
| |
| explain(costs off) select * from test_partition_table where a>7 order by a desc limit 4; |
| QUERY PLAN |
| --------------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> Limit |
| -> Sort |
| Sort Key: a DESC |
| -> Dynamic Index Scan on part_index_a on test_partition_table |
| Index Cond: (a > 7) |
| Number of partitions to scan: 5 (out of 5) |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (10 rows) |
| |
| -- Cases with group by, order by (with/without limit). Order by cols commutating index column |
| explain(costs off) select a, sum(d) from test_partition_table group by a order by a desc; |
| QUERY PLAN |
| ---------------------------------------------------------------- |
| Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> GroupAggregate |
| Group Key: a |
| -> Sort |
| Sort Key: a DESC |
| -> Dynamic Seq Scan on test_partition_table |
| Number of partitions to scan: 5 (out of 5) |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (9 rows) |
| |
| explain(costs off) select a, sum(d) from test_partition_table group by a order by a desc limit 3; |
| QUERY PLAN |
| ---------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> Limit |
| -> GroupAggregate |
| Group Key: a |
| -> Sort |
| Sort Key: a DESC |
| -> Dynamic Seq Scan on test_partition_table |
| Number of partitions to scan: 5 (out of 5) |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (11 rows) |
| |
| -- Case with group by, order by and a having clause (with/without limit). Order by cols commutating index. |
| explain(costs off) select a, sum(d) from test_partition_table group by a having a>30 order by a desc; |
| QUERY PLAN |
| --------------------------------------------------------------------------------- |
| Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> GroupAggregate |
| Group Key: a |
| -> Sort |
| Sort Key: a DESC |
| -> Dynamic Index Scan on part_index_a on test_partition_table |
| Index Cond: (a > 30) |
| Number of partitions to scan: 5 (out of 5) |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (10 rows) |
| |
| explain(costs off) select a, sum(d) from test_partition_table group by a having a>30 order by a desc; |
| QUERY PLAN |
| --------------------------------------------------------------------------------- |
| Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> GroupAggregate |
| Group Key: a |
| -> Sort |
| Sort Key: a DESC |
| -> Dynamic Index Scan on part_index_a on test_partition_table |
| Index Cond: (a > 30) |
| Number of partitions to scan: 5 (out of 5) |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (10 rows) |
| |
| -- Case with ordering via over() using window aggregates (with/without limit): rank(), row_number(), percent_rank() etc... |
| explain(costs off) select a, rank() over (order by a desc) from test_partition_table; |
| QUERY PLAN |
| ---------------------------------------------------------------- |
| WindowAgg |
| Order By: a |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> Sort |
| Sort Key: a DESC |
| -> Dynamic Seq Scan on test_partition_table |
| Number of partitions to scan: 5 (out of 5) |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (9 rows) |
| |
| explain(costs off) select a, rank() over (order by a desc) from test_partition_table limit 3; |
| QUERY PLAN |
| ---------------------------------------------------------------------- |
| Limit |
| -> WindowAgg |
| Order By: a |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> Sort |
| Sort Key: a DESC |
| -> Dynamic Seq Scan on test_partition_table |
| Number of partitions to scan: 5 (out of 5) |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (10 rows) |
| |
| -- Case with distinct and order by (with/without limit) |
| explain(costs off) select distinct(a) from test_partition_table order by a desc; |
| QUERY PLAN |
| ---------------------------------------------------------------- |
| Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> GroupAggregate |
| Group Key: a |
| -> Sort |
| Sort Key: a DESC |
| -> Dynamic Seq Scan on test_partition_table |
| Number of partitions to scan: 5 (out of 5) |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (9 rows) |
| |
| explain(costs off) select distinct(a) from test_partition_table order by a desc limit 3; |
| QUERY PLAN |
| ---------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: a |
| -> Limit |
| -> GroupAggregate |
| Group Key: a |
| -> Sort |
| Sort Key: a DESC |
| -> Dynamic Seq Scan on test_partition_table |
| Number of partitions to scan: 5 (out of 5) |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (11 rows) |
| |
| -- Order by within a CTE without limit |
| explain(costs off) with sorted_by_a as (select a from test_partition_table order by a) select a from sorted_by_a; |
| QUERY PLAN |
| ---------------------------------------------------- |
| Gather Motion 3:1 (slice1; segments: 3) |
| -> Dynamic Seq Scan on test_partition_table |
| Number of partitions to scan: 5 (out of 5) |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (4 rows) |
| |
| -- Order by within a CTE, with limit outside CTE expression |
| explain(costs off) with sorted_by_a as (select a from test_partition_table order by a) select a from sorted_by_a limit 3; |
| QUERY PLAN |
| ---------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| -> Limit |
| -> Dynamic Seq Scan on test_partition_table |
| Number of partitions to scan: 5 (out of 5) |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (6 rows) |
| |
| -- Case where DynamicIndexOnlyScan Backwards could be picked, but ORCA fails to produce DynamicIndexOnlyScan alternative. |
| -- This is because, in FCoverIndex() function while determining output columns for the query we also consider partition |
| -- column as part of the output cols(though query doesn't project it) since partition colum is always marked as Used. |
| -- Due to this, for every index that doesn't include partition column as its key, index cols doesn't match |
| -- the output cols and hence alternative isn't added. This issue has to be fixed after adding support for MergeAppend. |
| vacuum test_partition_table; |
| explain(costs off) select b,c,d from test_partition_table order by b desc, c desc, d desc; |
| QUERY PLAN |
| ---------------------------------------------------------- |
| Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b, c, d |
| -> Sort |
| Sort Key: b DESC, c DESC, d DESC |
| -> Dynamic Seq Scan on test_partition_table |
| Number of partitions to scan: 5 (out of 5) |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (7 rows) |
| |
| -- Clean Up |
| DROP TABLE test_partition_table; |
| -- NL Joins can utilize IndexScan's sort property, but currently ORCA doesn't |
| -- generate DynamicIndexScan alternatives for NL joins. This tests the case |
| -- where IndexScan's order property could be used for joining two partition |
| -- tables |
| CREATE TABLE part_employee(id int, name text, dep_id int, salary int) PARTITION BY range(id) (start (0) end(10000) every(2000)); |
| CREATE TABLE part_department(dep_id int, dep_name text) PARTITION BY range(dep_id) (start (0) end(10000) every(2000)); |
| CREATE INDEX part_index_id on part_employee using btree(id); |
| ANALYZE part_employee; |
| ANALYZE part_department; |
| -- Forcing planner, ORCA to use a NL join |
| set enable_hashjoin to off; |
| set optimizer_enable_hashjoin to off; |
| set enable_seqscan to off; |
| -- Planner uses NL Join with IndexScan Backwards and the sort property of index |
| -- 'index_salary', but ORCA since it doesn't generate DynamicIndexScan |
| -- alternative uses NL join with a Sort operator. |
| explain(costs off) select e.id, e.name, e.salary, d.dep_name from part_employee e join part_department d on e.id=d.dep_id order by e.id desc; |
| QUERY PLAN |
| --------------------------------------------------------------------------- |
| Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: e.id |
| -> Sort |
| Sort Key: e.id DESC |
| -> Nested Loop |
| Join Filter: true |
| -> Dynamic Seq Scan on part_department d |
| Number of partitions to scan: 5 (out of 5) |
| -> Dynamic Index Scan on part_index_id on part_employee e |
| Index Cond: (id = d.dep_id) |
| Number of partitions to scan: 5 (out of 5) |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (12 rows) |
| |
| explain(costs off) select e.id, e.name, e.salary, d.dep_name from part_employee e join part_department d on e.id=d.dep_id order by e.id desc limit 3; |
| QUERY PLAN |
| --------------------------------------------------------------------------------- |
| Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: e.id |
| -> Sort |
| Sort Key: e.id DESC |
| -> Nested Loop |
| Join Filter: true |
| -> Dynamic Seq Scan on part_department d |
| Number of partitions to scan: 5 (out of 5) |
| -> Dynamic Index Scan on part_index_id on part_employee e |
| Index Cond: (id = d.dep_id) |
| Number of partitions to scan: 5 (out of 5) |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (13 rows) |
| |
| -- Clean up |
| reset enable_hashjoin; |
| reset optimizer_enable_hashjoin; |
| reset enable_seqscan; |
| DROP TABLE part_employee; |
| DROP TABLE part_department; |
| -- Union all of two partition tables with order by on their indexed column uses |
| -- IndexScan's sort property with MergeAppend node in Planner. However in ORCA |
| -- we don't generate DynamicIndexScan alternative for union all, also we don't |
| -- support MergeAppend. But documenting this case for reference |
| CREATE TABLE part_table1(a int, b int) PARTITION BY range(a) (start (0) end(100) every(20)); |
| CREATE TABLE part_table2(a int, b int) PARTITION BY range(a) (start (0) end(100) every(20)); |
| CREATE INDEX part_tab1_idx on part_table1 using btree(a); |
| CREATE INDEX part_tab2_idx on part_table2 using btree(a); |
| -- inserting data and disabling seq_scan to avoid Planner generating a plan with |
| -- Sort operator and Append node instead of IndexScan with MergeAppend |
| set enable_seqscan to off; |
| INSERT INTO part_table1 select i, i from generate_series(1,99)i; |
| INSERT INTO part_table2 select i, i from generate_series(1,99)i; |
| ANALYZE part_table1; |
| ANALYZE part_table2; |
| explain(costs off) select a from part_table1 union all select a from part_table2 order by a desc; |
| QUERY PLAN |
| ---------------------------------------------------------------- |
| Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: part_table1.a |
| -> Sort |
| Sort Key: part_table1.a DESC |
| -> Append |
| -> Dynamic Seq Scan on part_table1 |
| Number of partitions to scan: 5 (out of 5) |
| -> Dynamic Seq Scan on part_table2 |
| Number of partitions to scan: 5 (out of 5) |
| Optimizer: Pivotal Optimizer (GPORCA) |
| (10 rows) |
| |
| -- Clean Up |
| reset enable_seqscan; |
| DROP TABLE part_table1; |
| DROP TABLE part_table2; |
| -- Purpose: This section includes tests related to min(), max() aggregates optimization. |
| CREATE TABLE min_max_aggregates(a int, b int, c int); |
| CREATE INDEX multi_key_index_b_c on min_max_aggregates using btree(b DESC, c); |
| ANALYZE min_max_aggregates; |
| -- Testing min/max functions when table doesn't have any tuples to |
| -- ensure they return 1 NULL row indicating no min or max value exists. |
| -- This test is eligible for optimization. |
| explain(costs off) select min(b) from min_max_aggregates; |
| QUERY PLAN |
| --------------------------------------------------------------------------------------- |
| Aggregate |
| -> Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b |
| -> Index Scan Backward using multi_key_index_b_c on min_max_aggregates |
| Index Cond: (b IS NOT NULL) |
| Optimizer: GPORCA |
| (7 rows) |
| |
| select min(b) from min_max_aggregates; |
| min |
| ----- |
| |
| (1 row) |
| |
| explain(costs off) select max(b) from min_max_aggregates; |
| QUERY PLAN |
| ------------------------------------------------------------------------------ |
| Aggregate |
| -> Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b |
| -> Index Scan using multi_key_index_b_c on min_max_aggregates |
| Index Cond: (b IS NOT NULL) |
| Optimizer: GPORCA |
| (7 rows) |
| |
| select max(b) from min_max_aggregates; |
| max |
| ----- |
| |
| (1 row) |
| |
| INSERT INTO min_max_aggregates select i, i, i from generate_series(1,100)i; |
| INSERT INTO min_max_aggregates values(null, null, null); |
| -- Creating this index to show that both 'multi_key_index_b_c' and |
| -- 'single_key_b_desc' are eligible for min/max on column 'b' and |
| -- ORCA picks the lower cost index |
| CREATE INDEX single_key_b_desc on min_max_aggregates using btree(b DESC); |
| ANALYZE min_max_aggregates; |
| -- This query is eligible for optimization and it leverages Backward |
| -- IndexScan as column 'b' in 'single_key_b_desc' is sorted in |
| -- descending order and therefore, minimum value can be found at |
| -- the bottom of the index |
| explain(costs off) select min(b) from min_max_aggregates; |
| QUERY PLAN |
| ------------------------------------------------------------------------------------------- |
| Aggregate |
| -> Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b |
| -> Limit |
| -> Index Scan Backward using single_key_b_desc on min_max_aggregates |
| Index Cond: (b IS NOT NULL) |
| Optimizer: GPORCA |
| (8 rows) |
| |
| select min(b) from min_max_aggregates; |
| min |
| ----- |
| 1 |
| (1 row) |
| |
| -- This query is eligible for optimization and it leverages Forward |
| -- IndexScan as column 'b' in 'single_key_b_desc' is sorted in |
| -- descending order and therefore, maximum value can be found |
| -- at the top of the index |
| explain(costs off) select max(b) from min_max_aggregates; |
| QUERY PLAN |
| ---------------------------------------------------------------------------------- |
| Aggregate |
| -> Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b |
| -> Limit |
| -> Index Scan using single_key_b_desc on min_max_aggregates |
| Index Cond: (b IS NOT NULL) |
| Optimizer: GPORCA |
| (8 rows) |
| |
| select max(b) from min_max_aggregates; |
| max |
| ----- |
| 100 |
| (1 row) |
| |
| DROP INDEX single_key_b_desc; |
| -- Test min/max optimization behavior on index with key direction ASC |
| CREATE INDEX single_key_b_asc on min_max_aggregates using btree(b); |
| ANALYZE min_max_aggregates; |
| -- This query is eligible for optimization and it leverages Forward |
| -- IndexScan as column 'b' in 'single_key_b_asc' is sorted in |
| -- ascending order and therefore, minimum value can be found at the |
| -- top of the index |
| explain(costs off) select min(b) from min_max_aggregates; |
| QUERY PLAN |
| --------------------------------------------------------------------------------- |
| Aggregate |
| -> Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b |
| -> Limit |
| -> Index Scan using single_key_b_asc on min_max_aggregates |
| Index Cond: (b IS NOT NULL) |
| Optimizer: GPORCA |
| (8 rows) |
| |
| select min(b) from min_max_aggregates; |
| min |
| ----- |
| 1 |
| (1 row) |
| |
| -- This query is eligible for optimization and it leverages Backward |
| -- IndexScan as column 'b' in 'single_key_b_asc' is sorted in |
| -- ascending order and therefore, maximum value can be found |
| -- at the bottom of the index |
| explain(costs off) select max(b) from min_max_aggregates; |
| QUERY PLAN |
| ------------------------------------------------------------------------------------------ |
| Aggregate |
| -> Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b |
| -> Limit |
| -> Index Scan Backward using single_key_b_asc on min_max_aggregates |
| Index Cond: (b IS NOT NULL) |
| Optimizer: GPORCA |
| (8 rows) |
| |
| select max(b) from min_max_aggregates; |
| max |
| ----- |
| 100 |
| (1 row) |
| |
| DROP INDEX single_key_b_asc; |
| -- This query is not eligible for optimization as min/max aggregates |
| -- are applied on non-leading keys in the index. |
| explain(costs off) select min(c) from min_max_aggregates; |
| QUERY PLAN |
| -------------------------------------------------- |
| Finalize Aggregate |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| -> Partial Aggregate |
| -> Seq Scan on min_max_aggregates |
| Optimizer: GPORCA |
| (5 rows) |
| |
| explain(costs off) select max(c) from min_max_aggregates; |
| QUERY PLAN |
| -------------------------------------------------- |
| Finalize Aggregate |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| -> Partial Aggregate |
| -> Seq Scan on min_max_aggregates |
| Optimizer: GPORCA |
| (5 rows) |
| |
| -- This query is not eligible for optimization as min/max aggregates |
| -- are applied on non index column. |
| explain(costs off) select min(a) from min_max_aggregates; |
| QUERY PLAN |
| -------------------------------------------------- |
| Finalize Aggregate |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| -> Partial Aggregate |
| -> Seq Scan on min_max_aggregates |
| Optimizer: GPORCA |
| (5 rows) |
| |
| explain(costs off) select max(a) from min_max_aggregates; |
| QUERY PLAN |
| -------------------------------------------------- |
| Finalize Aggregate |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| -> Partial Aggregate |
| -> Seq Scan on min_max_aggregates |
| Optimizer: GPORCA |
| (5 rows) |
| |
| -- Test min/max on a constant. This query is not eligible for |
| -- optimization as it is not necessary for min/max on constants |
| explain(costs off) select min(100) from min_max_aggregates; |
| QUERY PLAN |
| -------------------------------------------------- |
| Finalize Aggregate |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| -> Partial Aggregate |
| -> Seq Scan on min_max_aggregates |
| Optimizer: GPORCA |
| (5 rows) |
| |
| explain(costs off) select max(100) from min_max_aggregates; |
| QUERY PLAN |
| -------------------------------------------------- |
| Finalize Aggregate |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| -> Partial Aggregate |
| -> Seq Scan on min_max_aggregates |
| Optimizer: GPORCA |
| (5 rows) |
| |
| -- Test min/max optimization behavior with empty group by. This query |
| -- is eligible for optimization as it doesn't specify any grouping columns |
| explain(costs off) select min(b) from min_max_aggregates group by (); |
| QUERY PLAN |
| --------------------------------------------------------------------------------------------- |
| Aggregate |
| -> Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b |
| -> Limit |
| -> Index Scan Backward using multi_key_index_b_c on min_max_aggregates |
| Index Cond: (b IS NOT NULL) |
| Optimizer: GPORCA |
| (8 rows) |
| |
| select min(b) from min_max_aggregates group by (); |
| min |
| ----- |
| 1 |
| (1 row) |
| |
| explain(costs off) select max(b) from min_max_aggregates group by (); |
| QUERY PLAN |
| ------------------------------------------------------------------------------------ |
| Aggregate |
| -> Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b |
| -> Limit |
| -> Index Scan using multi_key_index_b_c on min_max_aggregates |
| Index Cond: (b IS NOT NULL) |
| Optimizer: GPORCA |
| (8 rows) |
| |
| select max(b) from min_max_aggregates group by (); |
| max |
| ----- |
| 100 |
| (1 row) |
| |
| -- Test min/max with non-empty group by. This query is not eligible for |
| -- optimization as it has grouping columns. This check is made while |
| -- computing xform promise by validating size of grouping columns and |
| -- there by avoiding application of transform |
| explain(costs off) select min(b) from min_max_aggregates group by b; |
| QUERY PLAN |
| ------------------------------------------------------------------ |
| Gather Motion 3:1 (slice1; segments: 3) |
| -> GroupAggregate |
| Group Key: b |
| -> Sort |
| Sort Key: b |
| -> Redistribute Motion 3:3 (slice2; segments: 3) |
| Hash Key: b |
| -> Seq Scan on min_max_aggregates |
| Optimizer: GPORCA |
| (9 rows) |
| |
| explain(costs off) select max(b) from min_max_aggregates group by b; |
| QUERY PLAN |
| ------------------------------------------------------------------ |
| Gather Motion 3:1 (slice1; segments: 3) |
| -> GroupAggregate |
| Group Key: b |
| -> Sort |
| Sort Key: b |
| -> Redistribute Motion 3:3 (slice2; segments: 3) |
| Hash Key: b |
| -> Seq Scan on min_max_aggregates |
| Optimizer: GPORCA |
| (9 rows) |
| |
| -- Test min/max optimization with CTE |
| -- Test min/max optimization when used in CTE producer. This query is |
| -- eligible for optimization as producer computes min aggregate on a |
| -- btree index key |
| explain (costs off) with cte_producer as (select min(b) as min_b from min_max_aggregates) select min_b from cte_producer; |
| QUERY PLAN |
| --------------------------------------------------------------------------------------------- |
| Aggregate |
| -> Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b |
| -> Limit |
| -> Index Scan Backward using multi_key_index_b_c on min_max_aggregates |
| Index Cond: (b IS NOT NULL) |
| Optimizer: GPORCA |
| (8 rows) |
| |
| with cte_producer as (select min(b) as min_b from min_max_aggregates) select min_b from cte_producer; |
| min_b |
| ------- |
| 1 |
| (1 row) |
| |
| -- Test min/max optimization when used in CTE consumer. This query is |
| -- eligible for optimization as CTE consumer computes min aggregate on a |
| -- btree index key projected by CTE producer |
| explain (costs off) with cte_consumer as (select b as col_b from min_max_aggregates) select min(col_b) from cte_consumer; |
| QUERY PLAN |
| --------------------------------------------------------------------------------------------- |
| Aggregate |
| -> Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b |
| -> Limit |
| -> Index Scan Backward using multi_key_index_b_c on min_max_aggregates |
| Index Cond: (b IS NOT NULL) |
| Optimizer: GPORCA |
| (8 rows) |
| |
| with cte_consumer as (select b as col_b from min_max_aggregates) select min(col_b) from cte_consumer; |
| min |
| ----- |
| 1 |
| (1 row) |
| |
| -- Test min/max optimization when used in CTE consumer. This query is not |
| -- eligible for optimization, because the subquery projects 'col_b' as 'b/2' |
| -- upon which the min is computed, but none of the indices store values |
| -- of column 'b/2' so that IndexScan could be used on that index |
| explain (costs off) with cte_consumer as (select b/2 as col_b from min_max_aggregates) select min(col_b) from cte_consumer; |
| QUERY PLAN |
| -------------------------------------------------- |
| Finalize Aggregate |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| -> Partial Aggregate |
| -> Seq Scan on min_max_aggregates |
| Optimizer: GPORCA |
| (5 rows) |
| |
| -- Test min/max optimization with Casts |
| -- Case where result of min/max is casted to a different data type. |
| -- This query is eligible for optimization as casting happens after |
| -- aggregation. |
| explain (costs off) select min(b)::int8 from min_max_aggregates; |
| QUERY PLAN |
| --------------------------------------------------------------------------------------------- |
| Aggregate |
| -> Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b |
| -> Limit |
| -> Index Scan Backward using multi_key_index_b_c on min_max_aggregates |
| Index Cond: (b IS NOT NULL) |
| Optimizer: GPORCA |
| (8 rows) |
| |
| select min(b)::int8 from min_max_aggregates; |
| min |
| ----- |
| 1 |
| (1 row) |
| |
| -- Case where min/max is computed on a column casted to a different data type. |
| -- This query is eligible for optimization as column type int4 and casted |
| -- type int8 belong to same opfamily |
| explain (costs off) select min(b::int8) from min_max_aggregates; |
| QUERY PLAN |
| --------------------------------------------------------------------------------------------- |
| Aggregate |
| -> Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b |
| -> Limit |
| -> Index Scan Backward using multi_key_index_b_c on min_max_aggregates |
| Index Cond: (b IS NOT NULL) |
| Optimizer: GPORCA |
| (8 rows) |
| |
| select min(b::int8) from min_max_aggregates; |
| min |
| ----- |
| 1 |
| (1 row) |
| |
| -- This query is not eligible for optimization as there is no operator |
| -- that handles comparison of a int4 and varchar type |
| explain (costs off) select min(b::varchar) from min_max_aggregates; |
| QUERY PLAN |
| -------------------------------------------------- |
| Finalize Aggregate |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| -> Partial Aggregate |
| -> Seq Scan on min_max_aggregates |
| Optimizer: GPORCA |
| (5 rows) |
| |
| -- Cases with more than one min/max aggregates in query |
| -- These queries aren't eligible for optimization because the transform's |
| -- pattern only contains a single Scalar Project Element that matches only |
| -- a single aggregate function whereas, these queries have more than one |
| -- aggregate function. Support for these queries is beyond the scope of |
| -- the current PR |
| explain(costs off) select min(b), max(b) from min_max_aggregates; |
| QUERY PLAN |
| -------------------------------------------------- |
| Finalize Aggregate |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| -> Partial Aggregate |
| -> Seq Scan on min_max_aggregates |
| Optimizer: GPORCA |
| (5 rows) |
| |
| explain(costs off) select min(a) + max(a) from min_max_aggregates; |
| QUERY PLAN |
| -------------------------------------------------- |
| Finalize Aggregate |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| -> Partial Aggregate |
| -> Seq Scan on min_max_aggregates |
| Optimizer: GPORCA |
| (5 rows) |
| |
| -- Clean Up |
| drop table min_max_aggregates; |
| -- Test min/max optimization with union all, subqueries, joins and outer references |
| CREATE TABLE table1 (a int, b int, c int); |
| CREATE INDEX t1_c_idx on table1 using btree(c); |
| CREATE TABLE table2 (a int, b int, c int); |
| CREATE INDEX t2_c_idx on table2 using btree(c); |
| INSERT INTO table1 select i, i, i from generate_series(1,100) i; |
| INSERT INTO table2 select i, i, i from generate_series(1,100) i; |
| ANALYZE table1; |
| ANALYZE table2; |
| -- Test min/max optimization when used in subqueries |
| -- This query is eligible for optimization as subquery computes max |
| -- aggregate on a btree index key |
| explain (costs off) select b from table1 where c = (select max(c) from table1); |
| QUERY PLAN |
| ---------------------------------------------------------------------------------------- |
| Gather Motion 3:1 (slice1; segments: 3) |
| -> Nested Loop |
| Join Filter: true |
| -> Broadcast Motion 1:3 (slice2) |
| -> Aggregate |
| -> Limit |
| -> Gather Motion 3:1 (slice3; segments: 3) |
| Merge Key: table1.c |
| -> Limit |
| -> Index Scan Backward using t1_c_idx on table1 |
| Index Cond: (c IS NOT NULL) |
| -> Index Scan using t1_c_idx on table1 table1_1 |
| Index Cond: (c = (max(table1.c))) |
| Optimizer: GPORCA |
| (14 rows) |
| |
| select b from table1 where c = (select max(c) from table1); |
| b |
| ----- |
| 100 |
| (1 row) |
| |
| -- Test min/max optimization in a subquery along with a predicate. |
| -- This query isn't eligible to use optimization because predicate's |
| -- pattern has a Select over LogicalGet and it doesn't match with the |
| -- transform's pattern |
| explain (costs off) select b, (select min(c) from table1 where table1.a > 5) as min_c from table2; |
| QUERY PLAN |
| ------------------------------------------------------------------------ |
| Gather Motion 3:1 (slice1; segments: 3) |
| -> Nested Loop Left Join |
| Join Filter: true |
| -> Seq Scan on table2 |
| -> Materialize |
| -> Broadcast Motion 1:3 (slice2) |
| -> Finalize Aggregate |
| -> Gather Motion 3:1 (slice3; segments: 3) |
| -> Partial Aggregate |
| -> Seq Scan on table1 |
| Filter: (a > 5) |
| Optimizer: GPORCA |
| (12 rows) |
| |
| explain (costs off) select b, (select min(c) from table1 where table1.b = table2.b) as min_c from table2; |
| QUERY PLAN |
| ------------------------------------------------------------------------ |
| Gather Motion 3:1 (slice1; segments: 3) |
| -> Hash Left Join |
| Hash Cond: (table2.b = table1.b) |
| -> Redistribute Motion 3:3 (slice2; segments: 3) |
| Hash Key: table2.b |
| -> Seq Scan on table2 |
| -> Hash |
| -> HashAggregate |
| Group Key: table1.b |
| -> Redistribute Motion 3:3 (slice3; segments: 3) |
| Hash Key: table1.b |
| -> Seq Scan on table1 |
| Optimizer: GPORCA |
| (13 rows) |
| |
| -- Test min/max optimization on result of union all present as part of |
| -- subquery. This query is not eligible to use min/max optimization as |
| -- it performs min/max on result of union all, and not directly on a table's |
| -- column due to which there is mismatch in query and transform's pattern. |
| -- The query pattern has LogicalUnionAll as first child of GbAgg whereas |
| -- transforms pattern has LogicalGet. |
| explain (costs off) select max(c) from (select c from table1 union all select c from table2) subquery; |
| QUERY PLAN |
| ------------------------------------------------ |
| Finalize Aggregate |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| -> Partial Aggregate |
| -> Append |
| -> Seq Scan on table1 |
| -> Seq Scan on table2 |
| Optimizer: GPORCA |
| (7 rows) |
| |
| -- Test min/max optimization on outer references. |
| -- This query is eligible to use optimization as it only has single |
| -- aggregate function on an index column |
| explain (costs off) select (select b from table1 t1_alias where t1_alias.a = min(t1.c)) as min_val_for_c from table1 t1; |
| QUERY PLAN |
| -------------------------------------------------------------- |
| Aggregate |
| -> Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: t1.c |
| -> Limit |
| -> Index Scan using t1_c_idx on table1 t1 |
| Index Cond: (c IS NOT NULL) |
| SubPlan 1 |
| -> Result |
| Filter: (t1_alias.a = min(t1.c)) |
| -> Materialize |
| -> Gather Motion 3:1 (slice2; segments: 3) |
| -> Seq Scan on table1 t1_alias |
| Optimizer: GPORCA |
| (14 rows) |
| |
| -- This query uses more than one aggregate function, and is not eligible |
| -- to use optimization. This is because, query's pattern doesn't |
| -- match the transforms pattern of a single Scalar Project Element. |
| explain (costs off) select min(t1.c) as min_c, |
| (select b from table1 t1_alias where t1_alias.c = max(t1.c)) as b_val |
| from table1 t1; |
| QUERY PLAN |
| -------------------------------------------------------------- |
| Finalize Aggregate |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| -> Partial Aggregate |
| -> Seq Scan on table1 t1 |
| SubPlan 1 |
| -> Result |
| Filter: (t1_alias.c = max(t1.c)) |
| -> Materialize |
| -> Gather Motion 3:1 (slice2; segments: 3) |
| -> Seq Scan on table1 t1_alias |
| Optimizer: GPORCA |
| (11 rows) |
| |
| -- Test min/max optimization used as part of projected columns in join. |
| -- This query is not eligible to use the optimization as it involves |
| -- join result which is not guaranteed to be sorted, unless it is an NL join. |
| explain (costs off) select min(table1.c) from table1 join table2 on table1.a=table2.a; |
| QUERY PLAN |
| ------------------------------------------------------ |
| Finalize Aggregate |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| -> Partial Aggregate |
| -> Hash Join |
| Hash Cond: (table1.a = table2.a) |
| -> Seq Scan on table1 |
| -> Hash |
| -> Seq Scan on table2 |
| Optimizer: GPORCA |
| (9 rows) |
| |
| -- Clean up |
| drop table table1; |
| drop table table2; |
| -- Purpose: This section tests IS NULL/IS NOT NULL predicate on btree and non-index columns |
| CREATE TABLE test_nulltype_predicates(a int, b int); |
| CREATE INDEX index_b on test_nulltype_predicates using btree(b DESC); |
| INSERT INTO test_nulltype_predicates select i, i from generate_series(1,3)i; |
| INSERT INTO test_nulltype_predicates values(null, null); |
| ANALYZE test_nulltype_predicates; |
| -- Tests with IS NULL on btree index columns |
| explain(costs off) select * from test_nulltype_predicates where b is null; |
| QUERY PLAN |
| ------------------------------------------------------------ |
| Gather Motion 3:1 (slice1; segments: 3) |
| -> Index Scan using index_b on test_nulltype_predicates |
| Index Cond: (b IS NULL) |
| Optimizer: GPORCA |
| (4 rows) |
| |
| select * from test_nulltype_predicates where b is null; |
| a | b |
| ---+--- |
| | |
| (1 row) |
| |
| -- Tests with IS NULL on non-index columns |
| explain(costs off) select * from test_nulltype_predicates where a is null; |
| QUERY PLAN |
| -------------------------------------------- |
| Gather Motion 1:1 (slice1; segments: 1) |
| -> Seq Scan on test_nulltype_predicates |
| Filter: (a IS NULL) |
| Optimizer: GPORCA |
| (4 rows) |
| |
| -- Tests with IS NOT NULL on btree index columns |
| explain(costs off) select * from test_nulltype_predicates where b is not null; |
| QUERY PLAN |
| ------------------------------------------------------------ |
| Gather Motion 3:1 (slice1; segments: 3) |
| -> Index Scan using index_b on test_nulltype_predicates |
| Index Cond: (b IS NOT NULL) |
| Optimizer: GPORCA |
| (4 rows) |
| |
| select * from test_nulltype_predicates where b is not null; |
| a | b |
| ---+--- |
| 1 | 1 |
| 3 | 3 |
| 2 | 2 |
| (3 rows) |
| |
| -- Tests with IS NOT NULL on non-index columns |
| explain(costs off) select * from test_nulltype_predicates where a is not null; |
| QUERY PLAN |
| -------------------------------------------- |
| Gather Motion 3:1 (slice1; segments: 3) |
| -> Seq Scan on test_nulltype_predicates |
| Filter: (NOT (a IS NULL)) |
| Optimizer: GPORCA |
| (4 rows) |
| |
| -- Clean Up |
| drop table test_nulltype_predicates; |
| -- Purpose: Test min/max optimization on AO table. |
| -- IndexOnlyScans are supported but IndexScans aren't supported on AO tables |
| CREATE TABLE test_ao_table(a int, b int) WITH (appendonly=true) DISTRIBUTED BY (a); |
| CREATE INDEX ao_index_b on test_ao_table using btree(b desc); |
| INSERT INTO test_ao_table SELECT i, i from generate_series(1,100) i; |
| ANALYZE test_ao_table; |
| -- Test max() aggregate. This query is eligible to use optimization |
| explain(costs off) select max(b) from test_ao_table; |
| QUERY PLAN |
| --------------------------------------------------------------------------- |
| Aggregate |
| -> Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b |
| -> Limit |
| -> Index Only Scan using ao_index_b on test_ao_table |
| Index Cond: (b IS NOT NULL) |
| Optimizer: GPORCA |
| (8 rows) |
| |
| select max(b) from test_ao_table; |
| max |
| ----- |
| 100 |
| (1 row) |
| |
| -- Test min() aggregate. This query is eligible to use optimization |
| explain(costs off) select min(b) from test_ao_table; |
| QUERY PLAN |
| ------------------------------------------------------------------------------------ |
| Aggregate |
| -> Limit |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| Merge Key: b |
| -> Limit |
| -> Index Only Scan Backward using ao_index_b on test_ao_table |
| Index Cond: (b IS NOT NULL) |
| Optimizer: GPORCA |
| (8 rows) |
| |
| select min(b) from test_ao_table; |
| min |
| ----- |
| 1 |
| (1 row) |
| |
| -- Clean Up |
| drop table test_ao_table; |
| -- Purpose: Test min/max optimization on partition tables. |
| CREATE TABLE test_partition_table(a int, b int) DISTRIBUTED BY (a) PARTITION BY range(b); |
| CREATE TABLE partition1 PARTITION OF test_partition_table FOR VALUES FROM (1) TO (3); |
| NOTICE: table has parent, setting distribution columns to match parent table |
| CREATE TABLE partition2 PARTITION OF test_partition_table FOR VALUES FROM (3) TO (6); |
| NOTICE: table has parent, setting distribution columns to match parent table |
| CREATE TABLE default_partition PARTITION OF test_partition_table DEFAULT; |
| NOTICE: table has parent, setting distribution columns to match parent table |
| CREATE INDEX part_index_b on test_partition_table using btree(b desc); |
| INSERT INTO test_partition_table SELECT i, i from generate_series(1,4) i; |
| -- Insert into default partition to show partition pruning |
| -- for IS NULL conditions |
| INSERT INTO test_partition_table values (0, NULL); |
| ANALYZE test_partition_table; |
| -- Test min/max aggregate on partition tables. This query is not |
| -- eligible for optimization because the query's pattern has LogicalDynamicGet |
| -- as first child of LogicalGbAgg whereas transform's pattern has LogicalGet. |
| -- Support for these queries is beyond the scope of the current PR. |
| explain(costs off) select max(b) from test_partition_table; |
| QUERY PLAN |
| ---------------------------------------------------------------- |
| Finalize Aggregate |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| -> Partial Aggregate |
| -> Dynamic Seq Scan on test_partition_table |
| Number of partitions to scan: 3 (out of 3) |
| Optimizer: GPORCA |
| (6 rows) |
| |
| explain(costs off) select min(b) from test_partition_table; |
| QUERY PLAN |
| ---------------------------------------------------------------- |
| Finalize Aggregate |
| -> Gather Motion 3:1 (slice1; segments: 3) |
| -> Partial Aggregate |
| -> Dynamic Seq Scan on test_partition_table |
| Number of partitions to scan: 3 (out of 3) |
| Optimizer: GPORCA |
| (6 rows) |
| |
| -- Test IS NULL, IS NOT NULL on partition table btree index column. |
| -- For IS NULL predicate on partition column, pruning happens |
| -- whereas, for IS NOT NULL it doesn't because the Non null values |
| -- could be in all of the partitions |
| explain(costs off) select * from test_partition_table where b is null; |
| QUERY PLAN |
| ------------------------------------------------------------------ |
| Gather Motion 3:1 (slice1; segments: 3) |
| -> Dynamic Index Scan on part_index_b on test_partition_table |
| Index Cond: (b IS NULL) |
| Number of partitions to scan: 1 (out of 3) |
| Optimizer: GPORCA |
| (5 rows) |
| |
| select * from test_partition_table where b is null; |
| a | b |
| ---+--- |
| 0 | |
| (1 row) |
| |
| explain(costs off) select * from test_partition_table where b is not null; |
| QUERY PLAN |
| ------------------------------------------------------------------ |
| Gather Motion 3:1 (slice1; segments: 3) |
| -> Dynamic Index Scan on part_index_b on test_partition_table |
| Index Cond: (b IS NOT NULL) |
| Number of partitions to scan: 3 (out of 3) |
| Optimizer: GPORCA |
| (5 rows) |
| |
| select * from test_partition_table where b is not null; |
| a | b |
| ---+--- |
| 2 | 2 |
| 4 | 4 |
| 3 | 3 |
| 1 | 1 |
| (4 rows) |
| |
| -- Clean Up |
| drop table test_partition_table; |