mirror of
https://github.com/postgres/postgres.git
synced 2025-12-19 17:02:53 +03:00
Add nbtree skip scan optimization.
Teach nbtree multi-column index scans to opportunistically skip over
irrelevant sections of the index given a query with no "=" conditions on
one or more prefix index columns. When nbtree is passed input scan keys
derived from a predicate "WHERE b = 5", new nbtree preprocessing steps
output "WHERE a = ANY(<every possible 'a' value>) AND b = 5" scan keys.
That is, preprocessing generates a "skip array" (and an output scan key)
for the omitted prefix column "a", which makes it safe to mark the scan
key on "b" as required to continue the scan. The scan is therefore able
to repeatedly reposition itself by applying both the "a" and "b" keys.
A skip array has "elements" that are generated procedurally and on
demand, but otherwise works just like a regular ScalarArrayOp array.
Preprocessing can freely add a skip array before or after any input
ScalarArrayOp arrays. Index scans with a skip array decide when and
where to reposition the scan using the same approach as any other scan
with array keys. This design builds on the design for array advancement
and primitive scan scheduling added to Postgres 17 by commit 5bf748b8.
Testing has shown that skip scans of an index with a low cardinality
skipped prefix column can be multiple orders of magnitude faster than an
equivalent full index scan (or sequential scan). In general, the
cardinality of the scan's skipped column(s) limits the number of leaf
pages that can be skipped over.
The core B-Tree operator classes on most discrete types generate their
array elements with the help of their own custom skip support routine.
This infrastructure gives nbtree a way to generate the next required
array element by incrementing (or decrementing) the current array value.
It can reduce the number of index descents in cases where the next
possible indexable value frequently turns out to be the next value
stored in the index. Opclasses that lack a skip support routine fall
back on having nbtree "increment" (or "decrement") a skip array's
current element by setting the NEXT (or PRIOR) scan key flag, without
directly changing the scan key's sk_argument. These sentinel values
behave just like any other value from an array -- though they can never
locate equal index tuples (they can only locate the next group of index
tuples containing the next set of non-sentinel values that the scan's
arrays need to advance to).
A skip array's range is constrained by "contradictory" inequality keys.
For example, a skip array on "x" will only generate the values 1 and 2
given a qual such as "WHERE x BETWEEN 1 AND 2 AND y = 66". Such a skip
array qual usually has near-identical performance characteristics to a
comparable SAOP qual "WHERE x = ANY('{1, 2}') AND y = 66". However,
improved performance isn't guaranteed. Much depends on physical index
characteristics.
B-Tree preprocessing is optimistic about skipping working out: it
applies static, generic rules when determining where to generate skip
arrays, which assumes that the runtime overhead of maintaining skip
arrays will pay for itself -- or lead to only a modest performance loss.
As things stand, these assumptions are much too optimistic: skip array
maintenance will lead to unacceptable regressions with unsympathetic
queries (queries whose scan can't skip over many irrelevant leaf pages).
An upcoming commit will address the problems in this area by enhancing
_bt_readpage's approach to saving cycles on scan key evaluation, making
it work in a way that directly considers the needs of = array keys
(particularly = skip array keys).
Author: Peter Geoghegan <pg@bowt.ie>
Reviewed-By: Masahiro Ikeda <masahiro.ikeda@nttdata.com>
Reviewed-By: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Reviewed-By: Matthias van de Meent <boekewurm+postgres@gmail.com>
Reviewed-By: Tomas Vondra <tomas@vondra.me>
Reviewed-By: Aleksander Alekseev <aleksander@timescale.com>
Reviewed-By: Alena Rybakina <a.rybakina@postgrespro.ru>
Discussion: https://postgr.es/m/CAH2-Wzmn1YsLzOGgjAQZdn1STSG_y8qP__vggTaPAYXJP+G4bw@mail.gmail.com
This commit is contained in:
@@ -362,9 +362,9 @@ ERROR: invalid operator number 0, must be between 1 and 5
|
||||
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 1 < ; -- operator without argument types
|
||||
ERROR: operator argument types must be specified in ALTER OPERATOR FAMILY
|
||||
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- invalid options parsing function
|
||||
ERROR: invalid function number 0, must be between 1 and 5
|
||||
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 6 btint42cmp(int4, int2); -- function number should be between 1 and 5
|
||||
ERROR: invalid function number 6, must be between 1 and 5
|
||||
ERROR: invalid function number 0, must be between 1 and 6
|
||||
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 7 btint42cmp(int4, int2); -- function number should be between 1 and 6
|
||||
ERROR: invalid function number 7, must be between 1 and 6
|
||||
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD STORAGE invalid_storage; -- Ensure STORAGE is not a part of ALTER OPERATOR FAMILY
|
||||
ERROR: STORAGE cannot be specified in ALTER OPERATOR FAMILY
|
||||
DROP OPERATOR FAMILY alt_opf4 USING btree;
|
||||
@@ -505,6 +505,10 @@ ALTER OPERATOR FAMILY alt_opf18 USING btree ADD
|
||||
ALTER OPERATOR FAMILY alt_opf18 USING btree
|
||||
ADD FUNCTION 4 (int4, int2) btequalimage(oid);
|
||||
ERROR: ordering equal image functions must not be cross-type
|
||||
-- Should fail. Not allowed to have cross-type skip support function.
|
||||
ALTER OPERATOR FAMILY alt_opf18 USING btree
|
||||
ADD FUNCTION 6 (int4, int2) btint4skipsupport(internal);
|
||||
ERROR: btree skip support functions must not be cross-type
|
||||
ALTER OPERATOR FAMILY alt_opf18 USING btree DROP FUNCTION 2 (int4, int4);
|
||||
ERROR: function 2(integer,integer) does not exist in operator family "alt_opf18"
|
||||
DROP OPERATOR FAMILY alt_opf18 USING btree;
|
||||
|
||||
@@ -581,6 +581,47 @@ alter table btree_tall_tbl alter COLUMN t set storage plain;
|
||||
create index btree_tall_idx on btree_tall_tbl (t, id) with (fillfactor = 10);
|
||||
insert into btree_tall_tbl select g, repeat('x', 250)
|
||||
from generate_series(1, 130) g;
|
||||
insert into btree_tall_tbl select g, NULL
|
||||
from generate_series(50, 60) g;
|
||||
--
|
||||
-- Test for skip scan with type that lacks skip support (text)
|
||||
--
|
||||
set enable_seqscan to false;
|
||||
set enable_bitmapscan to false;
|
||||
-- Forwards scan
|
||||
SELECT id FROM btree_tall_tbl WHERE id = 55 ORDER BY t, id;
|
||||
id
|
||||
----
|
||||
55
|
||||
55
|
||||
(2 rows)
|
||||
|
||||
explain (costs off)
|
||||
SELECT id FROM btree_tall_tbl WHERE id = 55 ORDER BY t, id;
|
||||
QUERY PLAN
|
||||
--------------------------------------------------------
|
||||
Index Only Scan using btree_tall_idx on btree_tall_tbl
|
||||
Index Cond: (id = 55)
|
||||
(2 rows)
|
||||
|
||||
-- Backwards scan
|
||||
SELECT id FROM btree_tall_tbl WHERE id = 55 ORDER BY t DESC, id DESC;
|
||||
id
|
||||
----
|
||||
55
|
||||
55
|
||||
(2 rows)
|
||||
|
||||
explain (costs off)
|
||||
SELECT id FROM btree_tall_tbl WHERE id = 55 ORDER BY t DESC, id DESC;
|
||||
QUERY PLAN
|
||||
-----------------------------------------------------------------
|
||||
Index Only Scan Backward using btree_tall_idx on btree_tall_tbl
|
||||
Index Cond: (id = 55)
|
||||
(2 rows)
|
||||
|
||||
reset enable_seqscan;
|
||||
reset enable_bitmapscan;
|
||||
--
|
||||
-- Test for multilevel page deletion
|
||||
--
|
||||
|
||||
@@ -1637,7 +1637,9 @@ DROP TABLE syscol_table;
|
||||
-- Tests for IS NULL/IS NOT NULL with b-tree indexes
|
||||
--
|
||||
CREATE TABLE onek_with_null AS SELECT unique1, unique2 FROM onek;
|
||||
INSERT INTO onek_with_null (unique1,unique2) VALUES (NULL, -1), (NULL, NULL);
|
||||
INSERT INTO onek_with_null(unique1, unique2)
|
||||
VALUES (NULL, -1), (NULL, 2_147_483_647), (NULL, NULL),
|
||||
(100, NULL), (500, NULL);
|
||||
CREATE UNIQUE INDEX onek_nulltest ON onek_with_null (unique2,unique1);
|
||||
SET enable_seqscan = OFF;
|
||||
SET enable_indexscan = ON;
|
||||
@@ -1645,7 +1647,7 @@ SET enable_bitmapscan = ON;
|
||||
SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL;
|
||||
count
|
||||
-------
|
||||
2
|
||||
3
|
||||
(1 row)
|
||||
|
||||
SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NULL;
|
||||
@@ -1657,13 +1659,13 @@ SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NULL;
|
||||
SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL;
|
||||
count
|
||||
-------
|
||||
1000
|
||||
1002
|
||||
(1 row)
|
||||
|
||||
SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NOT NULL;
|
||||
count
|
||||
-------
|
||||
1
|
||||
2
|
||||
(1 row)
|
||||
|
||||
SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL AND unique1 > 500;
|
||||
@@ -1678,12 +1680,18 @@ SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique1 > 500;
|
||||
0
|
||||
(1 row)
|
||||
|
||||
SELECT unique1, unique2 FROM onek_with_null WHERE unique1 = 500 ORDER BY unique2 DESC, unique1 DESC LIMIT 1;
|
||||
unique1 | unique2
|
||||
---------+---------
|
||||
500 |
|
||||
(1 row)
|
||||
|
||||
DROP INDEX onek_nulltest;
|
||||
CREATE UNIQUE INDEX onek_nulltest ON onek_with_null (unique2 desc,unique1);
|
||||
SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL;
|
||||
count
|
||||
-------
|
||||
2
|
||||
3
|
||||
(1 row)
|
||||
|
||||
SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NULL;
|
||||
@@ -1695,13 +1703,13 @@ SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NULL;
|
||||
SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL;
|
||||
count
|
||||
-------
|
||||
1000
|
||||
1002
|
||||
(1 row)
|
||||
|
||||
SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NOT NULL;
|
||||
count
|
||||
-------
|
||||
1
|
||||
2
|
||||
(1 row)
|
||||
|
||||
SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL AND unique1 > 500;
|
||||
@@ -1722,12 +1730,18 @@ SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IN (-1, 0,
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT unique1, unique2 FROM onek_with_null WHERE unique1 = 500 ORDER BY unique2 DESC, unique1 DESC LIMIT 1;
|
||||
unique1 | unique2
|
||||
---------+---------
|
||||
500 |
|
||||
(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;
|
||||
count
|
||||
-------
|
||||
2
|
||||
3
|
||||
(1 row)
|
||||
|
||||
SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NULL;
|
||||
@@ -1739,13 +1753,13 @@ SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NULL;
|
||||
SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL;
|
||||
count
|
||||
-------
|
||||
1000
|
||||
1002
|
||||
(1 row)
|
||||
|
||||
SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NOT NULL;
|
||||
count
|
||||
-------
|
||||
1
|
||||
2
|
||||
(1 row)
|
||||
|
||||
SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL AND unique1 > 500;
|
||||
@@ -1760,12 +1774,18 @@ SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique1 > 500;
|
||||
0
|
||||
(1 row)
|
||||
|
||||
SELECT unique1, unique2 FROM onek_with_null WHERE unique1 = 500 ORDER BY unique2 DESC, unique1 DESC LIMIT 1;
|
||||
unique1 | unique2
|
||||
---------+---------
|
||||
500 |
|
||||
(1 row)
|
||||
|
||||
DROP INDEX onek_nulltest;
|
||||
CREATE UNIQUE INDEX onek_nulltest ON onek_with_null (unique2 nulls first,unique1);
|
||||
SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL;
|
||||
count
|
||||
-------
|
||||
2
|
||||
3
|
||||
(1 row)
|
||||
|
||||
SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NULL;
|
||||
@@ -1777,13 +1797,13 @@ SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NULL;
|
||||
SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL;
|
||||
count
|
||||
-------
|
||||
1000
|
||||
1002
|
||||
(1 row)
|
||||
|
||||
SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NOT NULL;
|
||||
count
|
||||
-------
|
||||
1
|
||||
2
|
||||
(1 row)
|
||||
|
||||
SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL AND unique1 > 500;
|
||||
@@ -1798,6 +1818,12 @@ SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique1 > 500;
|
||||
0
|
||||
(1 row)
|
||||
|
||||
SELECT unique1, unique2 FROM onek_with_null WHERE unique1 = 500 ORDER BY unique2 DESC, unique1 DESC LIMIT 1;
|
||||
unique1 | unique2
|
||||
---------+---------
|
||||
500 |
|
||||
(1 row)
|
||||
|
||||
DROP INDEX onek_nulltest;
|
||||
-- Check initial-positioning logic too
|
||||
CREATE UNIQUE INDEX onek_nulltest ON onek_with_null (unique2);
|
||||
@@ -1829,20 +1855,24 @@ SELECT unique1, unique2 FROM onek_with_null WHERE unique2 >= 0
|
||||
(2 rows)
|
||||
|
||||
SELECT unique1, unique2 FROM onek_with_null
|
||||
ORDER BY unique2 DESC LIMIT 2;
|
||||
unique1 | unique2
|
||||
---------+---------
|
||||
|
|
||||
278 | 999
|
||||
(2 rows)
|
||||
ORDER BY unique2 DESC LIMIT 5;
|
||||
unique1 | unique2
|
||||
---------+------------
|
||||
500 |
|
||||
100 |
|
||||
|
|
||||
| 2147483647
|
||||
278 | 999
|
||||
(5 rows)
|
||||
|
||||
SELECT unique1, unique2 FROM onek_with_null WHERE unique2 >= -1
|
||||
ORDER BY unique2 DESC LIMIT 2;
|
||||
unique1 | unique2
|
||||
---------+---------
|
||||
278 | 999
|
||||
0 | 998
|
||||
(2 rows)
|
||||
ORDER BY unique2 DESC LIMIT 3;
|
||||
unique1 | unique2
|
||||
---------+------------
|
||||
| 2147483647
|
||||
278 | 999
|
||||
0 | 998
|
||||
(3 rows)
|
||||
|
||||
SELECT unique1, unique2 FROM onek_with_null WHERE unique2 < 999
|
||||
ORDER BY unique2 DESC LIMIT 2;
|
||||
@@ -2247,7 +2277,8 @@ SELECT count(*) FROM dupindexcols
|
||||
(1 row)
|
||||
|
||||
--
|
||||
-- Check that index scans with =ANY indexquals return rows in index order
|
||||
-- Check that index scans with SAOP array and/or skip array indexquals
|
||||
-- return rows in index order
|
||||
--
|
||||
explain (costs off)
|
||||
SELECT unique1 FROM tenk1
|
||||
@@ -2269,7 +2300,7 @@ ORDER BY unique1;
|
||||
42
|
||||
(3 rows)
|
||||
|
||||
-- Non-required array scan key on "tenthous":
|
||||
-- Skip array on "thousand", SAOP array on "tenthous":
|
||||
explain (costs off)
|
||||
SELECT thousand, tenthous FROM tenk1
|
||||
WHERE thousand < 2 AND tenthous IN (1001,3000)
|
||||
@@ -2289,7 +2320,7 @@ ORDER BY thousand;
|
||||
1 | 1001
|
||||
(2 rows)
|
||||
|
||||
-- Non-required array scan key on "tenthous", backward scan:
|
||||
-- Skip array on "thousand", SAOP array on "tenthous", backward scan:
|
||||
explain (costs off)
|
||||
SELECT thousand, tenthous FROM tenk1
|
||||
WHERE thousand < 2 AND tenthous IN (1001,3000)
|
||||
@@ -2309,6 +2340,25 @@ ORDER BY thousand DESC, tenthous DESC;
|
||||
0 | 3000
|
||||
(2 rows)
|
||||
|
||||
explain (costs off)
|
||||
SELECT thousand, tenthous FROM tenk1
|
||||
WHERE thousand > 995 and tenthous in (998, 999)
|
||||
ORDER BY thousand desc;
|
||||
QUERY PLAN
|
||||
--------------------------------------------------------------------------------
|
||||
Index Only Scan Backward using tenk1_thous_tenthous on tenk1
|
||||
Index Cond: ((thousand > 995) AND (tenthous = ANY ('{998,999}'::integer[])))
|
||||
(2 rows)
|
||||
|
||||
SELECT thousand, tenthous FROM tenk1
|
||||
WHERE thousand > 995 and tenthous in (998, 999)
|
||||
ORDER BY thousand desc;
|
||||
thousand | tenthous
|
||||
----------+----------
|
||||
999 | 999
|
||||
998 | 998
|
||||
(2 rows)
|
||||
|
||||
--
|
||||
-- Check elimination of redundant and contradictory index quals
|
||||
--
|
||||
@@ -2339,6 +2389,45 @@ SELECT unique1 FROM tenk1 WHERE unique1 = ANY('{7, 14, 22}') and unique1 = ANY('
|
||||
---------
|
||||
(0 rows)
|
||||
|
||||
explain (costs off)
|
||||
SELECT unique1 FROM tenk1 WHERE unique1 = ANY(NULL);
|
||||
QUERY PLAN
|
||||
-------------------------------------------------
|
||||
Index Only Scan using tenk1_unique1 on tenk1
|
||||
Index Cond: (unique1 = ANY (NULL::integer[]))
|
||||
(2 rows)
|
||||
|
||||
SELECT unique1 FROM tenk1 WHERE unique1 = ANY(NULL);
|
||||
unique1
|
||||
---------
|
||||
(0 rows)
|
||||
|
||||
explain (costs off)
|
||||
SELECT unique1 FROM tenk1 WHERE unique1 = ANY('{NULL,NULL,NULL}');
|
||||
QUERY PLAN
|
||||
---------------------------------------------------------------
|
||||
Index Only Scan using tenk1_unique1 on tenk1
|
||||
Index Cond: (unique1 = ANY ('{NULL,NULL,NULL}'::integer[]))
|
||||
(2 rows)
|
||||
|
||||
SELECT unique1 FROM tenk1 WHERE unique1 = ANY('{NULL,NULL,NULL}');
|
||||
unique1
|
||||
---------
|
||||
(0 rows)
|
||||
|
||||
explain (costs off)
|
||||
SELECT unique1 FROM tenk1 WHERE unique1 IS NULL AND unique1 IS NULL;
|
||||
QUERY PLAN
|
||||
---------------------------------------------------------
|
||||
Index Only Scan using tenk1_unique1 on tenk1
|
||||
Index Cond: ((unique1 IS NULL) AND (unique1 IS NULL))
|
||||
(2 rows)
|
||||
|
||||
SELECT unique1 FROM tenk1 WHERE unique1 IS NULL AND unique1 IS NULL;
|
||||
unique1
|
||||
---------
|
||||
(0 rows)
|
||||
|
||||
explain (costs off)
|
||||
SELECT unique1 FROM tenk1 WHERE unique1 IN (1, 42, 7) and unique1 = 1;
|
||||
QUERY PLAN
|
||||
@@ -2462,6 +2551,44 @@ SELECT unique1 FROM tenk1 WHERE (thousand, tenthous) > (NULL, 5);
|
||||
---------
|
||||
(0 rows)
|
||||
|
||||
-- Skip array redundancy (pair of redundant low_compare inequalities)
|
||||
explain (costs off)
|
||||
SELECT thousand, tenthous FROM tenk1
|
||||
WHERE thousand > -1 and thousand >= 0 AND tenthous = 3000
|
||||
ORDER BY thousand;
|
||||
QUERY PLAN
|
||||
--------------------------------------------------------------------------------------
|
||||
Index Only Scan using tenk1_thous_tenthous on tenk1
|
||||
Index Cond: ((thousand > '-1'::integer) AND (thousand >= 0) AND (tenthous = 3000))
|
||||
(2 rows)
|
||||
|
||||
SELECT thousand, tenthous FROM tenk1
|
||||
WHERE thousand > -1 and thousand >= 0 AND tenthous = 3000
|
||||
ORDER BY thousand;
|
||||
thousand | tenthous
|
||||
----------+----------
|
||||
0 | 3000
|
||||
(1 row)
|
||||
|
||||
-- Skip array redundancy (pair of redundant high_compare inequalities)
|
||||
explain (costs off)
|
||||
SELECT thousand, tenthous FROM tenk1
|
||||
WHERE thousand < 3 and thousand <= 2 AND tenthous = 1001
|
||||
ORDER BY thousand;
|
||||
QUERY PLAN
|
||||
--------------------------------------------------------------------------
|
||||
Index Only Scan using tenk1_thous_tenthous on tenk1
|
||||
Index Cond: ((thousand < 3) AND (thousand <= 2) AND (tenthous = 1001))
|
||||
(2 rows)
|
||||
|
||||
SELECT thousand, tenthous FROM tenk1
|
||||
WHERE thousand < 3 and thousand <= 2 AND tenthous = 1001
|
||||
ORDER BY thousand;
|
||||
thousand | tenthous
|
||||
----------+----------
|
||||
1 | 1001
|
||||
(1 row)
|
||||
|
||||
--
|
||||
-- Check elimination of constant-NULL subexpressions
|
||||
--
|
||||
|
||||
@@ -5332,9 +5332,10 @@ Function | in_range(time without time zone,time without time zone,i
|
||||
btree | uuid_ops | uuid | uuid | 1 | uuid_cmp
|
||||
btree | uuid_ops | uuid | uuid | 2 | uuid_sortsupport
|
||||
btree | uuid_ops | uuid | uuid | 4 | btequalimage
|
||||
btree | uuid_ops | uuid | uuid | 6 | uuid_skipsupport
|
||||
hash | uuid_ops | uuid | uuid | 1 | uuid_hash
|
||||
hash | uuid_ops | uuid | uuid | 2 | uuid_hash_extended
|
||||
(5 rows)
|
||||
(6 rows)
|
||||
|
||||
-- check \dconfig
|
||||
set work_mem = 10240;
|
||||
|
||||
@@ -310,7 +310,7 @@ ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 6 < (int4, int2); -- ope
|
||||
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 5
|
||||
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 1 < ; -- operator without argument types
|
||||
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- invalid options parsing function
|
||||
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 6 btint42cmp(int4, int2); -- function number should be between 1 and 5
|
||||
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 7 btint42cmp(int4, int2); -- function number should be between 1 and 6
|
||||
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD STORAGE invalid_storage; -- Ensure STORAGE is not a part of ALTER OPERATOR FAMILY
|
||||
DROP OPERATOR FAMILY alt_opf4 USING btree;
|
||||
|
||||
@@ -444,6 +444,9 @@ ALTER OPERATOR FAMILY alt_opf18 USING btree ADD
|
||||
-- Should fail. Not allowed to have cross-type equalimage function.
|
||||
ALTER OPERATOR FAMILY alt_opf18 USING btree
|
||||
ADD FUNCTION 4 (int4, int2) btequalimage(oid);
|
||||
-- Should fail. Not allowed to have cross-type skip support function.
|
||||
ALTER OPERATOR FAMILY alt_opf18 USING btree
|
||||
ADD FUNCTION 6 (int4, int2) btint4skipsupport(internal);
|
||||
ALTER OPERATOR FAMILY alt_opf18 USING btree DROP FUNCTION 2 (int4, int4);
|
||||
DROP OPERATOR FAMILY alt_opf18 USING btree;
|
||||
|
||||
|
||||
@@ -327,6 +327,27 @@ alter table btree_tall_tbl alter COLUMN t set storage plain;
|
||||
create index btree_tall_idx on btree_tall_tbl (t, id) with (fillfactor = 10);
|
||||
insert into btree_tall_tbl select g, repeat('x', 250)
|
||||
from generate_series(1, 130) g;
|
||||
insert into btree_tall_tbl select g, NULL
|
||||
from generate_series(50, 60) g;
|
||||
|
||||
--
|
||||
-- Test for skip scan with type that lacks skip support (text)
|
||||
--
|
||||
set enable_seqscan to false;
|
||||
set enable_bitmapscan to false;
|
||||
|
||||
-- Forwards scan
|
||||
SELECT id FROM btree_tall_tbl WHERE id = 55 ORDER BY t, id;
|
||||
explain (costs off)
|
||||
SELECT id FROM btree_tall_tbl WHERE id = 55 ORDER BY t, id;
|
||||
|
||||
-- Backwards scan
|
||||
SELECT id FROM btree_tall_tbl WHERE id = 55 ORDER BY t DESC, id DESC;
|
||||
explain (costs off)
|
||||
SELECT id FROM btree_tall_tbl WHERE id = 55 ORDER BY t DESC, id DESC;
|
||||
|
||||
reset enable_seqscan;
|
||||
reset enable_bitmapscan;
|
||||
|
||||
--
|
||||
-- Test for multilevel page deletion
|
||||
|
||||
@@ -650,7 +650,9 @@ DROP TABLE syscol_table;
|
||||
--
|
||||
|
||||
CREATE TABLE onek_with_null AS SELECT unique1, unique2 FROM onek;
|
||||
INSERT INTO onek_with_null (unique1,unique2) VALUES (NULL, -1), (NULL, NULL);
|
||||
INSERT INTO onek_with_null(unique1, unique2)
|
||||
VALUES (NULL, -1), (NULL, 2_147_483_647), (NULL, NULL),
|
||||
(100, NULL), (500, NULL);
|
||||
CREATE UNIQUE INDEX onek_nulltest ON onek_with_null (unique2,unique1);
|
||||
|
||||
SET enable_seqscan = OFF;
|
||||
@@ -663,6 +665,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 unique1, unique2 FROM onek_with_null WHERE unique1 = 500 ORDER BY unique2 DESC, unique1 DESC LIMIT 1;
|
||||
|
||||
DROP INDEX onek_nulltest;
|
||||
|
||||
@@ -675,6 +678,7 @@ SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NOT NUL
|
||||
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);
|
||||
SELECT unique1, unique2 FROM onek_with_null WHERE unique1 = 500 ORDER BY unique2 DESC, unique1 DESC LIMIT 1;
|
||||
|
||||
DROP INDEX onek_nulltest;
|
||||
|
||||
@@ -686,6 +690,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 unique1, unique2 FROM onek_with_null WHERE unique1 = 500 ORDER BY unique2 DESC, unique1 DESC LIMIT 1;
|
||||
|
||||
DROP INDEX onek_nulltest;
|
||||
|
||||
@@ -697,6 +702,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 unique1, unique2 FROM onek_with_null WHERE unique1 = 500 ORDER BY unique2 DESC, unique1 DESC LIMIT 1;
|
||||
|
||||
DROP INDEX onek_nulltest;
|
||||
|
||||
@@ -716,9 +722,9 @@ SELECT unique1, unique2 FROM onek_with_null WHERE unique2 >= 0
|
||||
ORDER BY unique2 LIMIT 2;
|
||||
|
||||
SELECT unique1, unique2 FROM onek_with_null
|
||||
ORDER BY unique2 DESC LIMIT 2;
|
||||
ORDER BY unique2 DESC LIMIT 5;
|
||||
SELECT unique1, unique2 FROM onek_with_null WHERE unique2 >= -1
|
||||
ORDER BY unique2 DESC LIMIT 2;
|
||||
ORDER BY unique2 DESC LIMIT 3;
|
||||
SELECT unique1, unique2 FROM onek_with_null WHERE unique2 < 999
|
||||
ORDER BY unique2 DESC LIMIT 2;
|
||||
|
||||
@@ -852,7 +858,8 @@ SELECT count(*) FROM dupindexcols
|
||||
WHERE f1 BETWEEN 'WA' AND 'ZZZ' and id < 1000 and f1 ~<~ 'YX';
|
||||
|
||||
--
|
||||
-- Check that index scans with =ANY indexquals return rows in index order
|
||||
-- Check that index scans with SAOP array and/or skip array indexquals
|
||||
-- return rows in index order
|
||||
--
|
||||
|
||||
explain (costs off)
|
||||
@@ -864,7 +871,7 @@ SELECT unique1 FROM tenk1
|
||||
WHERE unique1 IN (1,42,7)
|
||||
ORDER BY unique1;
|
||||
|
||||
-- Non-required array scan key on "tenthous":
|
||||
-- Skip array on "thousand", SAOP array on "tenthous":
|
||||
explain (costs off)
|
||||
SELECT thousand, tenthous FROM tenk1
|
||||
WHERE thousand < 2 AND tenthous IN (1001,3000)
|
||||
@@ -874,7 +881,7 @@ SELECT thousand, tenthous FROM tenk1
|
||||
WHERE thousand < 2 AND tenthous IN (1001,3000)
|
||||
ORDER BY thousand;
|
||||
|
||||
-- Non-required array scan key on "tenthous", backward scan:
|
||||
-- Skip array on "thousand", SAOP array on "tenthous", backward scan:
|
||||
explain (costs off)
|
||||
SELECT thousand, tenthous FROM tenk1
|
||||
WHERE thousand < 2 AND tenthous IN (1001,3000)
|
||||
@@ -884,6 +891,15 @@ SELECT thousand, tenthous FROM tenk1
|
||||
WHERE thousand < 2 AND tenthous IN (1001,3000)
|
||||
ORDER BY thousand DESC, tenthous DESC;
|
||||
|
||||
explain (costs off)
|
||||
SELECT thousand, tenthous FROM tenk1
|
||||
WHERE thousand > 995 and tenthous in (998, 999)
|
||||
ORDER BY thousand desc;
|
||||
|
||||
SELECT thousand, tenthous FROM tenk1
|
||||
WHERE thousand > 995 and tenthous in (998, 999)
|
||||
ORDER BY thousand desc;
|
||||
|
||||
--
|
||||
-- Check elimination of redundant and contradictory index quals
|
||||
--
|
||||
@@ -897,6 +913,21 @@ SELECT unique1 FROM tenk1 WHERE unique1 = ANY('{7, 14, 22}') and unique1 = ANY('
|
||||
|
||||
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 = ANY(NULL);
|
||||
|
||||
SELECT unique1 FROM tenk1 WHERE unique1 = ANY(NULL);
|
||||
|
||||
explain (costs off)
|
||||
SELECT unique1 FROM tenk1 WHERE unique1 = ANY('{NULL,NULL,NULL}');
|
||||
|
||||
SELECT unique1 FROM tenk1 WHERE unique1 = ANY('{NULL,NULL,NULL}');
|
||||
|
||||
explain (costs off)
|
||||
SELECT unique1 FROM tenk1 WHERE unique1 IS NULL AND unique1 IS NULL;
|
||||
|
||||
SELECT unique1 FROM tenk1 WHERE unique1 IS NULL AND unique1 IS NULL;
|
||||
|
||||
explain (costs off)
|
||||
SELECT unique1 FROM tenk1 WHERE unique1 IN (1, 42, 7) and unique1 = 1;
|
||||
|
||||
@@ -942,6 +973,26 @@ SELECT unique1 FROM tenk1 WHERE (thousand, tenthous) > (NULL, 5);
|
||||
|
||||
SELECT unique1 FROM tenk1 WHERE (thousand, tenthous) > (NULL, 5);
|
||||
|
||||
-- Skip array redundancy (pair of redundant low_compare inequalities)
|
||||
explain (costs off)
|
||||
SELECT thousand, tenthous FROM tenk1
|
||||
WHERE thousand > -1 and thousand >= 0 AND tenthous = 3000
|
||||
ORDER BY thousand;
|
||||
|
||||
SELECT thousand, tenthous FROM tenk1
|
||||
WHERE thousand > -1 and thousand >= 0 AND tenthous = 3000
|
||||
ORDER BY thousand;
|
||||
|
||||
-- Skip array redundancy (pair of redundant high_compare inequalities)
|
||||
explain (costs off)
|
||||
SELECT thousand, tenthous FROM tenk1
|
||||
WHERE thousand < 3 and thousand <= 2 AND tenthous = 1001
|
||||
ORDER BY thousand;
|
||||
|
||||
SELECT thousand, tenthous FROM tenk1
|
||||
WHERE thousand < 3 and thousand <= 2 AND tenthous = 1001
|
||||
ORDER BY thousand;
|
||||
|
||||
--
|
||||
-- Check elimination of constant-NULL subexpressions
|
||||
--
|
||||
|
||||
Reference in New Issue
Block a user