diff options
author | Peter Geoghegan <pg@bowt.ie> | 2024-04-06 11:47:10 -0400 |
---|---|---|
committer | Peter Geoghegan <pg@bowt.ie> | 2024-04-06 11:47:10 -0400 |
commit | 5bf748b86bc6786a3fc57fc7ce296c37da6564b0 (patch) | |
tree | cdf5b28c807516e1b25c716beb77f7592a1c941b /src/test | |
parent | ddd9e43a92417dd0c2b60822d6e75862c73b139a (diff) |
Enhance nbtree ScalarArrayOp execution.
Commit 9e8da0f7 taught nbtree to handle ScalarArrayOpExpr quals
natively. This works by pushing down the full context (the array keys)
to the nbtree index AM, enabling it to execute multiple primitive index
scans that the planner treats as one continuous index scan/index path.
This earlier enhancement enabled nbtree ScalarArrayOp index-only scans.
It also allowed scans with ScalarArrayOp quals to return ordered results
(with some notable restrictions, described further down).
Take this general approach a lot further: teach nbtree SAOP index scans
to decide how to execute ScalarArrayOp scans (when and where to start
the next primitive index scan) based on physical index characteristics.
This can be far more efficient. All SAOP scans will now reliably avoid
duplicative leaf page accesses (just like any other nbtree index scan).
SAOP scans whose array keys are naturally clustered together now require
far fewer index descents, since we'll reliably avoid starting a new
primitive scan just to get to a later offset from the same leaf page.
The scan's arrays now advance using binary searches for the array
element that best matches the next tuple's attribute value. Required
scan key arrays (i.e. arrays from scan keys that can terminate the scan)
ratchet forward in lockstep with the index scan. Non-required arrays
(i.e. arrays from scan keys that can only exclude non-matching tuples)
"advance" without the process ever rolling over to a higher-order array.
Naturally, only required SAOP scan keys trigger skipping over leaf pages
(non-required arrays cannot safely end or start primitive index scans).
Consequently, even index scans of a composite index with a high-order
inequality scan key (which we'll mark required) and a low-order SAOP
scan key (which we won't mark required) now avoid repeating leaf page
accesses -- that benefit isn't limited to simpler equality-only cases.
In general, all nbtree index scans now output tuples as if they were one
continuous index scan -- even scans that mix a high-order inequality
with lower-order SAOP equalities reliably output tuples in index order.
This allows us to remove a couple of special cases that were applied
when building index paths with SAOP clauses during planning.
Bugfix commit 807a40c5 taught the planner to avoid generating unsafe
path keys: path keys on a multicolumn index path, with a SAOP clause on
any attribute beyond the first/most significant attribute. These cases
are now all safe, so we go back to generating path keys without regard
for the presence of SAOP clauses (just like with any other clause type).
Affected queries can now exploit scan output order in all the usual ways
(e.g., certain "ORDER BY ... LIMIT n" queries can now terminate early).
Also undo changes from follow-up bugfix commit a4523c5a, which taught
the planner to produce alternative index paths, with path keys, but
without low-order SAOP index quals (filter quals were used instead).
We'll no longer generate these alternative paths, since they can no
longer offer any meaningful advantages over standard index qual paths.
Affected queries thereby avoid all of the disadvantages that come from
using filter quals within index scan nodes. They can avoid extra heap
page accesses from using filter quals to exclude non-matching tuples
(index quals will never have that problem). They can also skip over
irrelevant sections of the index in more cases (though only when nbtree
determines that starting another primitive scan actually makes sense).
There is a theoretical risk that removing restrictions on SAOP index
paths from the planner will break compatibility with amcanorder-based
index AMs maintained as extensions. Such an index AM could have the
same limitations around ordered SAOP scans as nbtree had up until now.
Adding a pro forma incompatibility item about the issue to the Postgres
17 release notes seems like a good idea.
Author: Peter Geoghegan <pg@bowt.ie>
Author: Matthias van de Meent <boekewurm+postgres@gmail.com>
Reviewed-By: Heikki Linnakangas <hlinnaka@iki.fi>
Reviewed-By: Matthias van de Meent <boekewurm+postgres@gmail.com>
Reviewed-By: Tomas Vondra <tomas.vondra@enterprisedb.com>
Discussion: https://postgr.es/m/CAH2-Wz=ksvN_sjcnD1+Bt-WtifRA5ok48aDYnq3pkKhxgMQpcw@mail.gmail.com
Diffstat (limited to 'src/test')
-rw-r--r-- | src/test/regress/expected/btree_index.out | 52 | ||||
-rw-r--r-- | src/test/regress/expected/create_index.out | 179 | ||||
-rw-r--r-- | src/test/regress/expected/join.out | 5 | ||||
-rw-r--r-- | src/test/regress/expected/select_parallel.out | 25 | ||||
-rw-r--r-- | src/test/regress/sql/btree_index.sql | 15 | ||||
-rw-r--r-- | src/test/regress/sql/create_index.sql | 64 | ||||
-rw-r--r-- | src/test/regress/sql/select_parallel.sql | 8 |
7 files changed, 321 insertions, 27 deletions
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out index 8311a03c3df..510646cbce7 100644 --- a/src/test/regress/expected/btree_index.out +++ b/src/test/regress/expected/btree_index.out @@ -190,6 +190,58 @@ select hundred, twenty from tenk1 where hundred <= 48 order by hundred desc limi (1 row) -- +-- Add coverage for ScalarArrayOp btree quals with pivot tuple constants +-- +explain (costs off) +select distinct hundred from tenk1 where hundred in (47, 48, 72, 82); + QUERY PLAN +------------------------------------------------------------------ + Unique + -> Index Only Scan using tenk1_hundred on tenk1 + Index Cond: (hundred = ANY ('{47,48,72,82}'::integer[])) +(3 rows) + +select distinct hundred from tenk1 where hundred in (47, 48, 72, 82); + hundred +--------- + 47 + 48 + 72 + 82 +(4 rows) + +explain (costs off) +select distinct hundred from tenk1 where hundred in (47, 48, 72, 82) order by hundred desc; + QUERY PLAN +------------------------------------------------------------------ + Unique + -> Index Only Scan Backward using tenk1_hundred on tenk1 + Index Cond: (hundred = ANY ('{47,48,72,82}'::integer[])) +(3 rows) + +select distinct hundred from tenk1 where hundred in (47, 48, 72, 82) order by hundred desc; + hundred +--------- + 82 + 72 + 48 + 47 +(4 rows) + +explain (costs off) +select thousand from tenk1 where thousand in (364, 366,380) and tenthous = 200000; + QUERY PLAN +--------------------------------------------------------------------------------------- + Index Only Scan using tenk1_thous_tenthous on tenk1 + Index Cond: ((thousand = ANY ('{364,366,380}'::integer[])) AND (tenthous = 200000)) +(2 rows) + +select thousand from tenk1 where thousand in (364, 366,380) and tenthous = 200000; + thousand +---------- +(0 rows) + +-- -- Check correct optimization of LIKE (special index operator support) -- for both indexscan and bitmapscan cases -- diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out index 70ab47a92f2..cf6eac57349 100644 --- a/src/test/regress/expected/create_index.out +++ b/src/test/regress/expected/create_index.out @@ -1698,6 +1698,12 @@ SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique1 > 500; 0 (1 row) +SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IN (-1, 0, 1); + count +------- + 1 +(1 row) + DROP INDEX onek_nulltest; CREATE UNIQUE INDEX onek_nulltest ON onek_with_null (unique2 desc nulls last,unique1); SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL; @@ -1910,7 +1916,7 @@ SELECT count(*) FROM dupindexcols (1 row) -- --- Check ordering of =ANY indexqual results (bug in 9.2.0) +-- Check that index scans with =ANY indexquals return rows in index order -- explain (costs off) SELECT unique1 FROM tenk1 @@ -1932,16 +1938,16 @@ ORDER BY unique1; 42 (3 rows) +-- Non-required array scan key on "tenthous": explain (costs off) SELECT thousand, tenthous FROM tenk1 WHERE thousand < 2 AND tenthous IN (1001,3000) ORDER BY thousand; - QUERY PLAN -------------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------------------------- Index Only Scan using tenk1_thous_tenthous on tenk1 - Index Cond: (thousand < 2) - Filter: (tenthous = ANY ('{1001,3000}'::integer[])) -(3 rows) + Index Cond: ((thousand < 2) AND (tenthous = ANY ('{1001,3000}'::integer[]))) +(2 rows) SELECT thousand, tenthous FROM tenk1 WHERE thousand < 2 AND tenthous IN (1001,3000) @@ -1952,29 +1958,166 @@ ORDER BY thousand; 1 | 1001 (2 rows) -SET enable_indexonlyscan = OFF; +-- Non-required array scan key on "tenthous", backward scan: explain (costs off) SELECT thousand, tenthous FROM tenk1 WHERE thousand < 2 AND tenthous IN (1001,3000) -ORDER BY thousand; - QUERY PLAN --------------------------------------------------------------------------------------- - Sort - Sort Key: thousand - -> Index Scan using tenk1_thous_tenthous on tenk1 - Index Cond: ((thousand < 2) AND (tenthous = ANY ('{1001,3000}'::integer[]))) -(4 rows) +ORDER BY thousand DESC, tenthous DESC; + QUERY PLAN +-------------------------------------------------------------------------------- + Index Only Scan Backward using tenk1_thous_tenthous on tenk1 + Index Cond: ((thousand < 2) AND (tenthous = ANY ('{1001,3000}'::integer[]))) +(2 rows) SELECT thousand, tenthous FROM tenk1 WHERE thousand < 2 AND tenthous IN (1001,3000) -ORDER BY thousand; +ORDER BY thousand DESC, tenthous DESC; thousand | tenthous ----------+---------- - 0 | 3000 1 | 1001 + 0 | 3000 +(2 rows) + +-- +-- Check elimination of redundant and contradictory index quals +-- +explain (costs off) +SELECT unique1 FROM tenk1 WHERE unique1 IN (1, 42, 7) and unique1 = ANY('{7, 8, 9}'); + QUERY PLAN +---------------------------------------------------------------------------------------------------- + Index Only Scan using tenk1_unique1 on tenk1 + Index Cond: ((unique1 = ANY ('{1,42,7}'::integer[])) AND (unique1 = ANY ('{7,8,9}'::integer[]))) +(2 rows) + +SELECT unique1 FROM tenk1 WHERE unique1 IN (1, 42, 7) and unique1 = ANY('{7, 8, 9}'); + unique1 +--------- + 7 +(1 row) + +explain (costs off) +SELECT unique1 FROM tenk1 WHERE unique1 = ANY('{7, 14, 22}') and unique1 = ANY('{33, 44}'::bigint[]); + QUERY PLAN +---------------------------------------------------------------------------------------------------- + Index Only Scan using tenk1_unique1 on tenk1 + Index Cond: ((unique1 = ANY ('{7,14,22}'::integer[])) AND (unique1 = ANY ('{33,44}'::bigint[]))) +(2 rows) + +SELECT unique1 FROM tenk1 WHERE unique1 = ANY('{7, 14, 22}') and unique1 = ANY('{33, 44}'::bigint[]); + unique1 +--------- +(0 rows) + +explain (costs off) +SELECT unique1 FROM tenk1 WHERE unique1 IN (1, 42, 7) and unique1 = 1; + QUERY PLAN +--------------------------------------------------------------------------- + Index Only Scan using tenk1_unique1 on tenk1 + Index Cond: ((unique1 = ANY ('{1,42,7}'::integer[])) AND (unique1 = 1)) +(2 rows) + +SELECT unique1 FROM tenk1 WHERE unique1 IN (1, 42, 7) and unique1 = 1; + unique1 +--------- + 1 +(1 row) + +explain (costs off) +SELECT unique1 FROM tenk1 WHERE unique1 IN (1, 42, 7) and unique1 = 12345; + QUERY PLAN +------------------------------------------------------------------------------- + Index Only Scan using tenk1_unique1 on tenk1 + Index Cond: ((unique1 = ANY ('{1,42,7}'::integer[])) AND (unique1 = 12345)) +(2 rows) + +SELECT unique1 FROM tenk1 WHERE unique1 IN (1, 42, 7) and unique1 = 12345; + unique1 +--------- +(0 rows) + +explain (costs off) +SELECT unique1 FROM tenk1 WHERE unique1 IN (1, 42, 7) and unique1 >= 42; + QUERY PLAN +----------------------------------------------------------------------------- + Index Only Scan using tenk1_unique1 on tenk1 + Index Cond: ((unique1 = ANY ('{1,42,7}'::integer[])) AND (unique1 >= 42)) +(2 rows) + +SELECT unique1 FROM tenk1 WHERE unique1 IN (1, 42, 7) and unique1 >= 42; + unique1 +--------- + 42 +(1 row) + +explain (costs off) +SELECT unique1 FROM tenk1 WHERE unique1 IN (1, 42, 7) and unique1 > 42; + QUERY PLAN +---------------------------------------------------------------------------- + Index Only Scan using tenk1_unique1 on tenk1 + Index Cond: ((unique1 = ANY ('{1,42,7}'::integer[])) AND (unique1 > 42)) +(2 rows) + +SELECT unique1 FROM tenk1 WHERE unique1 IN (1, 42, 7) and unique1 > 42; + unique1 +--------- +(0 rows) + +explain (costs off) +SELECT unique1 FROM tenk1 WHERE unique1 > 9996 and unique1 >= 9999; + QUERY PLAN +-------------------------------------------------------- + Index Only Scan using tenk1_unique1 on tenk1 + Index Cond: ((unique1 > 9996) AND (unique1 >= 9999)) +(2 rows) + +SELECT unique1 FROM tenk1 WHERE unique1 > 9996 and unique1 >= 9999; + unique1 +--------- + 9999 +(1 row) + +explain (costs off) +SELECT unique1 FROM tenk1 WHERE unique1 < 3 and unique1 <= 3; + QUERY PLAN +-------------------------------------------------- + Index Only Scan using tenk1_unique1 on tenk1 + Index Cond: ((unique1 < 3) AND (unique1 <= 3)) +(2 rows) + +SELECT unique1 FROM tenk1 WHERE unique1 < 3 and unique1 <= 3; + unique1 +--------- + 0 + 1 + 2 +(3 rows) + +explain (costs off) +SELECT unique1 FROM tenk1 WHERE unique1 < 3 and unique1 < (-1)::bigint; + QUERY PLAN +------------------------------------------------------------ + Index Only Scan using tenk1_unique1 on tenk1 + Index Cond: ((unique1 < 3) AND (unique1 < '-1'::bigint)) (2 rows) -RESET enable_indexonlyscan; +SELECT unique1 FROM tenk1 WHERE unique1 < 3 and unique1 < (-1)::bigint; + unique1 +--------- +(0 rows) + +explain (costs off) +SELECT unique1 FROM tenk1 WHERE unique1 IN (1, 42, 7) and unique1 < (-1)::bigint; + QUERY PLAN +-------------------------------------------------------------------------------------- + Index Only Scan using tenk1_unique1 on tenk1 + Index Cond: ((unique1 = ANY ('{1,42,7}'::integer[])) AND (unique1 < '-1'::bigint)) +(2 rows) + +SELECT unique1 FROM tenk1 WHERE unique1 IN (1, 42, 7) and unique1 < (-1)::bigint; + unique1 +--------- +(0 rows) + -- -- Check elimination of constant-NULL subexpressions -- diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out index 63cddac0d63..8b640c2fc2f 100644 --- a/src/test/regress/expected/join.out +++ b/src/test/regress/expected/join.out @@ -8880,10 +8880,9 @@ where j1.id1 % 1000 = 1 and j2.id1 % 1000 = 1 and j2.id1 >= any (array[1,5]); Merge Cond: (j1.id1 = j2.id1) Join Filter: (j2.id2 = j1.id2) -> Index Scan using j1_id1_idx on j1 - -> Index Only Scan using j2_pkey on j2 + -> Index Scan using j2_id1_idx on j2 Index Cond: (id1 >= ANY ('{1,5}'::integer[])) - Filter: ((id1 % 1000) = 1) -(7 rows) +(6 rows) select * from j1 inner join j2 on j1.id1 = j2.id1 and j1.id2 = j2.id2 diff --git a/src/test/regress/expected/select_parallel.out b/src/test/regress/expected/select_parallel.out index 4ffc5b4c563..87273fa635e 100644 --- a/src/test/regress/expected/select_parallel.out +++ b/src/test/regress/expected/select_parallel.out @@ -361,6 +361,7 @@ alter table tenk2 reset (parallel_workers); -- test parallel index scans. set enable_seqscan to off; set enable_bitmapscan to off; +set random_page_cost = 2; explain (costs off) select count((unique1)) from tenk1 where hundred > 1; QUERY PLAN @@ -379,6 +380,30 @@ select count((unique1)) from tenk1 where hundred > 1; 9800 (1 row) +-- Parallel ScalarArrayOp index scan +explain (costs off) + select count((unique1)) from tenk1 + where hundred = any ((select array_agg(i) from generate_series(1, 100, 15) i)::int[]); + QUERY PLAN +--------------------------------------------------------------------- + Finalize Aggregate + InitPlan 1 + -> Aggregate + -> Function Scan on generate_series i + -> Gather + Workers Planned: 4 + -> Partial Aggregate + -> Parallel Index Scan using tenk1_hundred on tenk1 + Index Cond: (hundred = ANY ((InitPlan 1).col1)) +(9 rows) + +select count((unique1)) from tenk1 +where hundred = any ((select array_agg(i) from generate_series(1, 100, 15) i)::int[]); + count +------- + 700 +(1 row) + -- test parallel index-only scans. explain (costs off) select count(*) from tenk1 where thousand > 95; diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql index ef843542347..0d2a33f3705 100644 --- a/src/test/regress/sql/btree_index.sql +++ b/src/test/regress/sql/btree_index.sql @@ -136,6 +136,21 @@ select hundred, twenty from tenk1 where hundred <= 48 order by hundred desc limi select hundred, twenty from tenk1 where hundred <= 48 order by hundred desc limit 1; -- +-- Add coverage for ScalarArrayOp btree quals with pivot tuple constants +-- +explain (costs off) +select distinct hundred from tenk1 where hundred in (47, 48, 72, 82); +select distinct hundred from tenk1 where hundred in (47, 48, 72, 82); + +explain (costs off) +select distinct hundred from tenk1 where hundred in (47, 48, 72, 82) order by hundred desc; +select distinct hundred from tenk1 where hundred in (47, 48, 72, 82) order by hundred desc; + +explain (costs off) +select thousand from tenk1 where thousand in (364, 366,380) and tenthous = 200000; +select thousand from tenk1 where thousand in (364, 366,380) and tenthous = 200000; + +-- -- Check correct optimization of LIKE (special index operator support) -- for both indexscan and bitmapscan cases -- diff --git a/src/test/regress/sql/create_index.sql b/src/test/regress/sql/create_index.sql index d49ce9f3007..e296891cab8 100644 --- a/src/test/regress/sql/create_index.sql +++ b/src/test/regress/sql/create_index.sql @@ -668,6 +668,7 @@ SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NOT NULL; SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL AND unique1 > 500; SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique1 > 500; +SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IN (-1, 0, 1); DROP INDEX onek_nulltest; @@ -753,7 +754,7 @@ SELECT count(*) FROM dupindexcols WHERE f1 BETWEEN 'WA' AND 'ZZZ' and id < 1000 and f1 ~<~ 'YX'; -- --- Check ordering of =ANY indexqual results (bug in 9.2.0) +-- Check that index scans with =ANY indexquals return rows in index order -- explain (costs off) @@ -765,6 +766,7 @@ SELECT unique1 FROM tenk1 WHERE unique1 IN (1,42,7) ORDER BY unique1; +-- Non-required array scan key on "tenthous": explain (costs off) SELECT thousand, tenthous FROM tenk1 WHERE thousand < 2 AND tenthous IN (1001,3000) @@ -774,18 +776,68 @@ SELECT thousand, tenthous FROM tenk1 WHERE thousand < 2 AND tenthous IN (1001,3000) ORDER BY thousand; -SET enable_indexonlyscan = OFF; - +-- Non-required array scan key on "tenthous", backward scan: explain (costs off) SELECT thousand, tenthous FROM tenk1 WHERE thousand < 2 AND tenthous IN (1001,3000) -ORDER BY thousand; +ORDER BY thousand DESC, tenthous DESC; SELECT thousand, tenthous FROM tenk1 WHERE thousand < 2 AND tenthous IN (1001,3000) -ORDER BY thousand; +ORDER BY thousand DESC, tenthous DESC; + +-- +-- Check elimination of redundant and contradictory index quals +-- +explain (costs off) +SELECT unique1 FROM tenk1 WHERE unique1 IN (1, 42, 7) and unique1 = ANY('{7, 8, 9}'); + +SELECT unique1 FROM tenk1 WHERE unique1 IN (1, 42, 7) and unique1 = ANY('{7, 8, 9}'); + +explain (costs off) +SELECT unique1 FROM tenk1 WHERE unique1 = ANY('{7, 14, 22}') and unique1 = ANY('{33, 44}'::bigint[]); + +SELECT unique1 FROM tenk1 WHERE unique1 = ANY('{7, 14, 22}') and unique1 = ANY('{33, 44}'::bigint[]); + +explain (costs off) +SELECT unique1 FROM tenk1 WHERE unique1 IN (1, 42, 7) and unique1 = 1; + +SELECT unique1 FROM tenk1 WHERE unique1 IN (1, 42, 7) and unique1 = 1; + +explain (costs off) +SELECT unique1 FROM tenk1 WHERE unique1 IN (1, 42, 7) and unique1 = 12345; + +SELECT unique1 FROM tenk1 WHERE unique1 IN (1, 42, 7) and unique1 = 12345; + +explain (costs off) +SELECT unique1 FROM tenk1 WHERE unique1 IN (1, 42, 7) and unique1 >= 42; + +SELECT unique1 FROM tenk1 WHERE unique1 IN (1, 42, 7) and unique1 >= 42; + +explain (costs off) +SELECT unique1 FROM tenk1 WHERE unique1 IN (1, 42, 7) and unique1 > 42; + +SELECT unique1 FROM tenk1 WHERE unique1 IN (1, 42, 7) and unique1 > 42; + +explain (costs off) +SELECT unique1 FROM tenk1 WHERE unique1 > 9996 and unique1 >= 9999; + +SELECT unique1 FROM tenk1 WHERE unique1 > 9996 and unique1 >= 9999; + +explain (costs off) +SELECT unique1 FROM tenk1 WHERE unique1 < 3 and unique1 <= 3; + +SELECT unique1 FROM tenk1 WHERE unique1 < 3 and unique1 <= 3; + +explain (costs off) +SELECT unique1 FROM tenk1 WHERE unique1 < 3 and unique1 < (-1)::bigint; + +SELECT unique1 FROM tenk1 WHERE unique1 < 3 and unique1 < (-1)::bigint; + +explain (costs off) +SELECT unique1 FROM tenk1 WHERE unique1 IN (1, 42, 7) and unique1 < (-1)::bigint; -RESET enable_indexonlyscan; +SELECT unique1 FROM tenk1 WHERE unique1 IN (1, 42, 7) and unique1 < (-1)::bigint; -- -- Check elimination of constant-NULL subexpressions diff --git a/src/test/regress/sql/select_parallel.sql b/src/test/regress/sql/select_parallel.sql index c43a5b21191..20376c03fae 100644 --- a/src/test/regress/sql/select_parallel.sql +++ b/src/test/regress/sql/select_parallel.sql @@ -137,11 +137,19 @@ alter table tenk2 reset (parallel_workers); -- test parallel index scans. set enable_seqscan to off; set enable_bitmapscan to off; +set random_page_cost = 2; explain (costs off) select count((unique1)) from tenk1 where hundred > 1; select count((unique1)) from tenk1 where hundred > 1; +-- Parallel ScalarArrayOp index scan +explain (costs off) + select count((unique1)) from tenk1 + where hundred = any ((select array_agg(i) from generate_series(1, 100, 15) i)::int[]); +select count((unique1)) from tenk1 +where hundred = any ((select array_agg(i) from generate_series(1, 100, 15) i)::int[]); + -- test parallel index-only scans. explain (costs off) select count(*) from tenk1 where thousand > 95; |