489 lines
16 KiB
Plaintext
489 lines
16 KiB
Plaintext
--
|
|
-- BTREE_INDEX
|
|
--
|
|
-- directory paths are passed to us in environment variables
|
|
\getenv abs_srcdir PG_ABS_SRCDIR
|
|
CREATE TABLE bt_i4_heap (
|
|
seqno int4,
|
|
random int4
|
|
);
|
|
CREATE TABLE bt_name_heap (
|
|
seqno name,
|
|
random int4
|
|
);
|
|
CREATE TABLE bt_txt_heap (
|
|
seqno text,
|
|
random int4
|
|
);
|
|
CREATE TABLE bt_f8_heap (
|
|
seqno float8,
|
|
random int4
|
|
);
|
|
\set filename :abs_srcdir '/data/desc.data'
|
|
COPY bt_i4_heap FROM :'filename';
|
|
\set filename :abs_srcdir '/data/hash.data'
|
|
COPY bt_name_heap FROM :'filename';
|
|
\set filename :abs_srcdir '/data/desc.data'
|
|
COPY bt_txt_heap FROM :'filename';
|
|
\set filename :abs_srcdir '/data/hash.data'
|
|
COPY bt_f8_heap FROM :'filename';
|
|
ANALYZE bt_i4_heap;
|
|
ANALYZE bt_name_heap;
|
|
ANALYZE bt_txt_heap;
|
|
ANALYZE bt_f8_heap;
|
|
--
|
|
-- BTREE ascending/descending cases
|
|
--
|
|
-- we load int4/text from pure descending data (each key is a new
|
|
-- low key) and name/f8 from pure ascending data (each key is a new
|
|
-- high key). we had a bug where new low keys would sometimes be
|
|
-- "lost".
|
|
--
|
|
CREATE INDEX bt_i4_index ON bt_i4_heap USING btree (seqno int4_ops);
|
|
CREATE INDEX bt_name_index ON bt_name_heap USING btree (seqno name_ops);
|
|
CREATE INDEX bt_txt_index ON bt_txt_heap USING btree (seqno text_ops);
|
|
CREATE INDEX bt_f8_index ON bt_f8_heap USING btree (seqno float8_ops);
|
|
--
|
|
-- test retrieval of min/max keys for each index
|
|
--
|
|
SELECT b.*
|
|
FROM bt_i4_heap b
|
|
WHERE b.seqno < 1;
|
|
seqno | random
|
|
-------+------------
|
|
0 | 1935401906
|
|
(1 row)
|
|
|
|
SELECT b.*
|
|
FROM bt_i4_heap b
|
|
WHERE b.seqno >= 9999;
|
|
seqno | random
|
|
-------+------------
|
|
9999 | 1227676208
|
|
(1 row)
|
|
|
|
SELECT b.*
|
|
FROM bt_i4_heap b
|
|
WHERE b.seqno = 4500;
|
|
seqno | random
|
|
-------+------------
|
|
4500 | 2080851358
|
|
(1 row)
|
|
|
|
SELECT b.*
|
|
FROM bt_name_heap b
|
|
WHERE b.seqno < '1'::name;
|
|
seqno | random
|
|
-------+------------
|
|
0 | 1935401906
|
|
(1 row)
|
|
|
|
SELECT b.*
|
|
FROM bt_name_heap b
|
|
WHERE b.seqno >= '9999'::name;
|
|
seqno | random
|
|
-------+------------
|
|
9999 | 1227676208
|
|
(1 row)
|
|
|
|
SELECT b.*
|
|
FROM bt_name_heap b
|
|
WHERE b.seqno = '4500'::name;
|
|
seqno | random
|
|
-------+------------
|
|
4500 | 2080851358
|
|
(1 row)
|
|
|
|
SELECT b.*
|
|
FROM bt_txt_heap b
|
|
WHERE b.seqno < '1'::text;
|
|
seqno | random
|
|
-------+------------
|
|
0 | 1935401906
|
|
(1 row)
|
|
|
|
SELECT b.*
|
|
FROM bt_txt_heap b
|
|
WHERE b.seqno >= '9999'::text;
|
|
seqno | random
|
|
-------+------------
|
|
9999 | 1227676208
|
|
(1 row)
|
|
|
|
SELECT b.*
|
|
FROM bt_txt_heap b
|
|
WHERE b.seqno = '4500'::text;
|
|
seqno | random
|
|
-------+------------
|
|
4500 | 2080851358
|
|
(1 row)
|
|
|
|
SELECT b.*
|
|
FROM bt_f8_heap b
|
|
WHERE b.seqno < '1'::float8;
|
|
seqno | random
|
|
-------+------------
|
|
0 | 1935401906
|
|
(1 row)
|
|
|
|
SELECT b.*
|
|
FROM bt_f8_heap b
|
|
WHERE b.seqno >= '9999'::float8;
|
|
seqno | random
|
|
-------+------------
|
|
9999 | 1227676208
|
|
(1 row)
|
|
|
|
SELECT b.*
|
|
FROM bt_f8_heap b
|
|
WHERE b.seqno = '4500'::float8;
|
|
seqno | random
|
|
-------+------------
|
|
4500 | 2080851358
|
|
(1 row)
|
|
|
|
--
|
|
-- Add coverage for optimization of backwards scan index descents
|
|
--
|
|
-- Here we expect _bt_search to descend straight to a leaf page containing a
|
|
-- non-pivot tuple with the value '47', which comes last (after 11 similar
|
|
-- non-pivot tuples). Query execution should only need to visit a single
|
|
-- leaf page here.
|
|
--
|
|
-- Test case relies on tenk1_hundred index having a leaf page whose high key
|
|
-- is '(48, -inf)'. We use a low cardinality index to make our test case less
|
|
-- sensitive to implementation details that may change in the future.
|
|
set enable_seqscan to false;
|
|
set enable_indexscan to true;
|
|
set enable_bitmapscan to false;
|
|
explain (costs off)
|
|
select hundred, twenty from tenk1 where hundred < 48 order by hundred desc limit 1;
|
|
QUERY PLAN
|
|
--------------------------------------------------------
|
|
Limit
|
|
-> Index Scan Backward using tenk1_hundred on tenk1
|
|
Index Cond: (hundred < 48)
|
|
(3 rows)
|
|
|
|
select hundred, twenty from tenk1 where hundred < 48 order by hundred desc limit 1;
|
|
hundred | twenty
|
|
---------+--------
|
|
47 | 7
|
|
(1 row)
|
|
|
|
-- This variant of the query need only return a single tuple located to the immediate
|
|
-- right of the '(48, -inf)' high key. It also only needs to scan one single
|
|
-- leaf page (the right sibling of the page scanned by the last test case):
|
|
explain (costs off)
|
|
select hundred, twenty from tenk1 where hundred <= 48 order by hundred desc limit 1;
|
|
QUERY PLAN
|
|
--------------------------------------------------------
|
|
Limit
|
|
-> Index Scan Backward using tenk1_hundred on tenk1
|
|
Index Cond: (hundred <= 48)
|
|
(3 rows)
|
|
|
|
select hundred, twenty from tenk1 where hundred <= 48 order by hundred desc limit 1;
|
|
hundred | twenty
|
|
---------+--------
|
|
48 | 8
|
|
(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
|
|
--
|
|
set enable_seqscan to false;
|
|
set enable_indexscan to true;
|
|
set enable_bitmapscan to false;
|
|
explain (costs off)
|
|
select proname from pg_proc where proname like E'RI\\_FKey%del' order by 1;
|
|
QUERY PLAN
|
|
------------------------------------------------------------------------------
|
|
Index Only Scan using pg_proc_proname_args_nsp_index on pg_proc
|
|
Index Cond: ((proname >= 'RI_FKey'::text) AND (proname < 'RI_FKez'::text))
|
|
Filter: (proname ~~ 'RI\_FKey%del'::text)
|
|
(3 rows)
|
|
|
|
select proname from pg_proc where proname like E'RI\\_FKey%del' order by 1;
|
|
proname
|
|
------------------------
|
|
RI_FKey_cascade_del
|
|
RI_FKey_noaction_del
|
|
RI_FKey_restrict_del
|
|
RI_FKey_setdefault_del
|
|
RI_FKey_setnull_del
|
|
(5 rows)
|
|
|
|
explain (costs off)
|
|
select proname from pg_proc where proname ilike '00%foo' order by 1;
|
|
QUERY PLAN
|
|
--------------------------------------------------------------------
|
|
Index Only Scan using pg_proc_proname_args_nsp_index on pg_proc
|
|
Index Cond: ((proname >= '00'::text) AND (proname < '01'::text))
|
|
Filter: (proname ~~* '00%foo'::text)
|
|
(3 rows)
|
|
|
|
select proname from pg_proc where proname ilike '00%foo' order by 1;
|
|
proname
|
|
---------
|
|
(0 rows)
|
|
|
|
explain (costs off)
|
|
select proname from pg_proc where proname ilike 'ri%foo' order by 1;
|
|
QUERY PLAN
|
|
-----------------------------------------------------------------
|
|
Index Only Scan using pg_proc_proname_args_nsp_index on pg_proc
|
|
Filter: (proname ~~* 'ri%foo'::text)
|
|
(2 rows)
|
|
|
|
set enable_indexscan to false;
|
|
set enable_bitmapscan to true;
|
|
explain (costs off)
|
|
select proname from pg_proc where proname like E'RI\\_FKey%del' order by 1;
|
|
QUERY PLAN
|
|
------------------------------------------------------------------------------------------
|
|
Sort
|
|
Sort Key: proname
|
|
-> Bitmap Heap Scan on pg_proc
|
|
Filter: (proname ~~ 'RI\_FKey%del'::text)
|
|
-> Bitmap Index Scan on pg_proc_proname_args_nsp_index
|
|
Index Cond: ((proname >= 'RI_FKey'::text) AND (proname < 'RI_FKez'::text))
|
|
(6 rows)
|
|
|
|
select proname from pg_proc where proname like E'RI\\_FKey%del' order by 1;
|
|
proname
|
|
------------------------
|
|
RI_FKey_cascade_del
|
|
RI_FKey_noaction_del
|
|
RI_FKey_restrict_del
|
|
RI_FKey_setdefault_del
|
|
RI_FKey_setnull_del
|
|
(5 rows)
|
|
|
|
explain (costs off)
|
|
select proname from pg_proc where proname ilike '00%foo' order by 1;
|
|
QUERY PLAN
|
|
--------------------------------------------------------------------------------
|
|
Sort
|
|
Sort Key: proname
|
|
-> Bitmap Heap Scan on pg_proc
|
|
Filter: (proname ~~* '00%foo'::text)
|
|
-> Bitmap Index Scan on pg_proc_proname_args_nsp_index
|
|
Index Cond: ((proname >= '00'::text) AND (proname < '01'::text))
|
|
(6 rows)
|
|
|
|
select proname from pg_proc where proname ilike '00%foo' order by 1;
|
|
proname
|
|
---------
|
|
(0 rows)
|
|
|
|
explain (costs off)
|
|
select proname from pg_proc where proname ilike 'ri%foo' order by 1;
|
|
QUERY PLAN
|
|
-----------------------------------------------------------------
|
|
Index Only Scan using pg_proc_proname_args_nsp_index on pg_proc
|
|
Filter: (proname ~~* 'ri%foo'::text)
|
|
(2 rows)
|
|
|
|
reset enable_seqscan;
|
|
reset enable_indexscan;
|
|
reset enable_bitmapscan;
|
|
-- Also check LIKE optimization with binary-compatible cases
|
|
create temp table btree_bpchar (f1 text collate "C");
|
|
create index on btree_bpchar(f1 bpchar_ops) WITH (deduplicate_items=on);
|
|
insert into btree_bpchar values ('foo'), ('fool'), ('bar'), ('quux');
|
|
-- doesn't match index:
|
|
explain (costs off)
|
|
select * from btree_bpchar where f1 like 'foo';
|
|
QUERY PLAN
|
|
-------------------------------
|
|
Seq Scan on btree_bpchar
|
|
Filter: (f1 ~~ 'foo'::text)
|
|
(2 rows)
|
|
|
|
select * from btree_bpchar where f1 like 'foo';
|
|
f1
|
|
-----
|
|
foo
|
|
(1 row)
|
|
|
|
explain (costs off)
|
|
select * from btree_bpchar where f1 like 'foo%';
|
|
QUERY PLAN
|
|
--------------------------------
|
|
Seq Scan on btree_bpchar
|
|
Filter: (f1 ~~ 'foo%'::text)
|
|
(2 rows)
|
|
|
|
select * from btree_bpchar where f1 like 'foo%';
|
|
f1
|
|
------
|
|
foo
|
|
fool
|
|
(2 rows)
|
|
|
|
-- these do match the index:
|
|
explain (costs off)
|
|
select * from btree_bpchar where f1::bpchar like 'foo';
|
|
QUERY PLAN
|
|
----------------------------------------------------
|
|
Bitmap Heap Scan on btree_bpchar
|
|
Filter: ((f1)::bpchar ~~ 'foo'::text)
|
|
-> Bitmap Index Scan on btree_bpchar_f1_idx
|
|
Index Cond: ((f1)::bpchar = 'foo'::bpchar)
|
|
(4 rows)
|
|
|
|
select * from btree_bpchar where f1::bpchar like 'foo';
|
|
f1
|
|
-----
|
|
foo
|
|
(1 row)
|
|
|
|
explain (costs off)
|
|
select * from btree_bpchar where f1::bpchar like 'foo%';
|
|
QUERY PLAN
|
|
------------------------------------------------------------------------------------------
|
|
Bitmap Heap Scan on btree_bpchar
|
|
Filter: ((f1)::bpchar ~~ 'foo%'::text)
|
|
-> Bitmap Index Scan on btree_bpchar_f1_idx
|
|
Index Cond: (((f1)::bpchar >= 'foo'::bpchar) AND ((f1)::bpchar < 'fop'::bpchar))
|
|
(4 rows)
|
|
|
|
select * from btree_bpchar where f1::bpchar like 'foo%';
|
|
f1
|
|
------
|
|
foo
|
|
fool
|
|
(2 rows)
|
|
|
|
-- get test coverage for "single value" deduplication strategy:
|
|
insert into btree_bpchar select 'foo' from generate_series(1,1500);
|
|
--
|
|
-- Perform unique checking, with and without the use of deduplication
|
|
--
|
|
CREATE TABLE dedup_unique_test_table (a int) WITH (autovacuum_enabled=false);
|
|
CREATE UNIQUE INDEX dedup_unique ON dedup_unique_test_table (a) WITH (deduplicate_items=on);
|
|
CREATE UNIQUE INDEX plain_unique ON dedup_unique_test_table (a) WITH (deduplicate_items=off);
|
|
-- Generate enough garbage tuples in index to ensure that even the unique index
|
|
-- with deduplication enabled has to check multiple leaf pages during unique
|
|
-- checking (at least with a BLCKSZ of 8192 or less)
|
|
DO $$
|
|
BEGIN
|
|
FOR r IN 1..1350 LOOP
|
|
DELETE FROM dedup_unique_test_table;
|
|
INSERT INTO dedup_unique_test_table SELECT 1;
|
|
END LOOP;
|
|
END$$;
|
|
-- Exercise the LP_DEAD-bit-set tuple deletion code with a posting list tuple.
|
|
-- The implementation prefers deleting existing items to merging any duplicate
|
|
-- tuples into a posting list, so we need an explicit test to make sure we get
|
|
-- coverage (note that this test also assumes BLCKSZ is 8192 or less):
|
|
DROP INDEX plain_unique;
|
|
DELETE FROM dedup_unique_test_table WHERE a = 1;
|
|
INSERT INTO dedup_unique_test_table SELECT i FROM generate_series(0,450) i;
|
|
--
|
|
-- Test B-tree fast path (cache rightmost leaf page) optimization.
|
|
--
|
|
-- First create a tree that's at least three levels deep (i.e. has one level
|
|
-- between the root and leaf levels). The text inserted is long. It won't be
|
|
-- TOAST compressed because we use plain storage in the table. Only a few
|
|
-- index tuples fit on each internal page, allowing us to get a tall tree with
|
|
-- few pages. (A tall tree is required to trigger caching.)
|
|
--
|
|
-- The text column must be the leading column in the index, since suffix
|
|
-- truncation would otherwise truncate tuples on internal pages, leaving us
|
|
-- with a short tree.
|
|
create table btree_tall_tbl(id int4, t text);
|
|
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;
|
|
--
|
|
-- Test for multilevel page deletion
|
|
--
|
|
CREATE TABLE delete_test_table (a bigint, b bigint, c bigint, d bigint);
|
|
INSERT INTO delete_test_table SELECT i, 1, 2, 3 FROM generate_series(1,80000) i;
|
|
ALTER TABLE delete_test_table ADD PRIMARY KEY (a,b,c,d);
|
|
-- Delete most entries, and vacuum, deleting internal pages and creating "fast
|
|
-- root"
|
|
DELETE FROM delete_test_table WHERE a < 79990;
|
|
VACUUM delete_test_table;
|
|
--
|
|
-- Test B-tree insertion with a metapage update (XLOG_BTREE_INSERT_META
|
|
-- WAL record type). This happens when a "fast root" page is split. This
|
|
-- also creates coverage for nbtree FSM page recycling.
|
|
--
|
|
-- The vacuum above should've turned the leaf page into a fast root. We just
|
|
-- need to insert some rows to cause the fast root page to split.
|
|
INSERT INTO delete_test_table SELECT i, 1, 2, 3 FROM generate_series(1,1000) i;
|
|
-- Test unsupported btree opclass parameters
|
|
create index on btree_tall_tbl (id int4_ops(foo=1));
|
|
ERROR: operator class int4_ops has no options
|
|
-- Test case of ALTER INDEX with abuse of column names for indexes.
|
|
-- This grammar is not officially supported, but the parser allows it.
|
|
CREATE INDEX btree_tall_idx2 ON btree_tall_tbl (id);
|
|
ALTER INDEX btree_tall_idx2 ALTER COLUMN id SET (n_distinct=100);
|
|
ERROR: ALTER action ALTER COLUMN ... SET cannot be performed on relation "btree_tall_idx2"
|
|
DETAIL: This operation is not supported for indexes.
|
|
DROP INDEX btree_tall_idx2;
|
|
-- Partitioned index
|
|
CREATE TABLE btree_part (id int4) PARTITION BY RANGE (id);
|
|
CREATE INDEX btree_part_idx ON btree_part(id);
|
|
ALTER INDEX btree_part_idx ALTER COLUMN id SET (n_distinct=100);
|
|
ERROR: ALTER action ALTER COLUMN ... SET cannot be performed on relation "btree_part_idx"
|
|
DETAIL: This operation is not supported for partitioned indexes.
|
|
DROP TABLE btree_part;
|