mirror of
https://github.com/postgres/postgres.git
synced 2026-04-10 19:47:07 -04:00
As coded, the planner logic that calculates the number of parallel workers to use for a parallel index build uses expressions and predicates from the relcache, which are flattened for the planner by eval_const_expressions(). As reported in the bug, an immutable parallel-unsafe function flattened in the relcache would become a Const, which would be considered as parallel-safe, even if the predicate or the expressions including the function are not safe in parallel workers. Depending on the expressions or predicate used, this could cause the parallel build to fail. Tests are included that check parallel index builds with parallel-unsafe predicate and expressions. Two routines are added to lsyscache.h to be able to retrieve expressions and predicate of an index from its pg_index data. Reported-by: Alexander Lakhin Author: Tender Wang Reviewed-by: Jian He, Michael Paquier Discussion: https://postgr.es/m/CAHewXN=UaAaNn9ruHDH3Os8kxLVmtWqbssnf=dZN_s9=evHUFA@mail.gmail.com Backpatch-through: 12
207 lines
7 KiB
PL/PgSQL
207 lines
7 KiB
PL/PgSQL
--
|
|
-- BTREE_INDEX
|
|
-- test retrieval of min/max keys for each index
|
|
--
|
|
|
|
SELECT b.*
|
|
FROM bt_i4_heap b
|
|
WHERE b.seqno < 1;
|
|
|
|
SELECT b.*
|
|
FROM bt_i4_heap b
|
|
WHERE b.seqno >= 9999;
|
|
|
|
SELECT b.*
|
|
FROM bt_i4_heap b
|
|
WHERE b.seqno = 4500;
|
|
|
|
SELECT b.*
|
|
FROM bt_name_heap b
|
|
WHERE b.seqno < '1'::name;
|
|
|
|
SELECT b.*
|
|
FROM bt_name_heap b
|
|
WHERE b.seqno >= '9999'::name;
|
|
|
|
SELECT b.*
|
|
FROM bt_name_heap b
|
|
WHERE b.seqno = '4500'::name;
|
|
|
|
SELECT b.*
|
|
FROM bt_txt_heap b
|
|
WHERE b.seqno < '1'::text;
|
|
|
|
SELECT b.*
|
|
FROM bt_txt_heap b
|
|
WHERE b.seqno >= '9999'::text;
|
|
|
|
SELECT b.*
|
|
FROM bt_txt_heap b
|
|
WHERE b.seqno = '4500'::text;
|
|
|
|
SELECT b.*
|
|
FROM bt_f8_heap b
|
|
WHERE b.seqno < '1'::float8;
|
|
|
|
SELECT b.*
|
|
FROM bt_f8_heap b
|
|
WHERE b.seqno >= '9999'::float8;
|
|
|
|
SELECT b.*
|
|
FROM bt_f8_heap b
|
|
WHERE b.seqno = '4500'::float8;
|
|
|
|
--
|
|
-- 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;
|
|
select proname from pg_proc where proname like E'RI\\_FKey%del' order by 1;
|
|
explain (costs off)
|
|
select proname from pg_proc where proname ilike '00%foo' order by 1;
|
|
select proname from pg_proc where proname ilike '00%foo' order by 1;
|
|
explain (costs off)
|
|
select proname from pg_proc where proname ilike 'ri%foo' order by 1;
|
|
|
|
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;
|
|
select proname from pg_proc where proname like E'RI\\_FKey%del' order by 1;
|
|
explain (costs off)
|
|
select proname from pg_proc where proname ilike '00%foo' order by 1;
|
|
select proname from pg_proc where proname ilike '00%foo' order by 1;
|
|
explain (costs off)
|
|
select proname from pg_proc where proname ilike 'ri%foo' order by 1;
|
|
|
|
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';
|
|
select * from btree_bpchar where f1 like 'foo';
|
|
explain (costs off)
|
|
select * from btree_bpchar where f1 like 'foo%';
|
|
select * from btree_bpchar where f1 like 'foo%';
|
|
-- these do match the index:
|
|
explain (costs off)
|
|
select * from btree_bpchar where f1::bpchar like 'foo';
|
|
select * from btree_bpchar where f1::bpchar like 'foo';
|
|
explain (costs off)
|
|
select * from btree_bpchar where f1::bpchar like 'foo%';
|
|
select * from btree_bpchar where f1::bpchar like 'foo%';
|
|
|
|
-- 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));
|
|
|
|
-- 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);
|
|
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);
|
|
DROP TABLE btree_part;
|
|
|
|
-- Test with index expression and predicate that include a parallel unsafe
|
|
-- function.
|
|
CREATE FUNCTION para_unsafe_f() RETURNS int IMMUTABLE PARALLEL UNSAFE
|
|
AS $$
|
|
BEGIN
|
|
RETURN 0;
|
|
EXCEPTION WHEN OTHERS THEN
|
|
RETURN 1;
|
|
END$$ LANGUAGE plpgsql;
|
|
|
|
CREATE TABLE btree_para_bld(i int);
|
|
ALTER TABLE btree_para_bld SET (parallel_workers = 4);
|
|
SET max_parallel_maintenance_workers TO 4;
|
|
-- With parallel-unsafe expression
|
|
CREATE INDEX ON btree_para_bld((i + para_unsafe_f()));
|
|
-- With parallel-unsafe predicate
|
|
CREATE INDEX ON btree_para_bld(i) WHERE i > para_unsafe_f();
|
|
|
|
RESET max_parallel_maintenance_workers;
|
|
DROP TABLE btree_para_bld;
|
|
DROP FUNCTION para_unsafe_f;
|