| -- |
| -- Test GIN indexes. |
| -- |
| -- There are other tests to test different GIN opclasses. This is for testing |
| -- GIN itself. |
| |
| -- Create and populate a test table with a GIN index. |
| create table gin_test_tbl(i int4[]) with (autovacuum_enabled = off); |
| create index gin_test_idx on gin_test_tbl using gin (i) |
| with (fastupdate = on, gin_pending_list_limit = 4096); |
| insert into gin_test_tbl select array[1, 2, g] from generate_series(1, 20000) g; |
| insert into gin_test_tbl select array[1, 3, g] from generate_series(1, 1000) g; |
| |
| select gin_clean_pending_list('gin_test_idx')>10 as many; -- flush the fastupdate buffers |
| |
| insert into gin_test_tbl select array[3, 1, g] from generate_series(1, 1000) g; |
| |
| vacuum gin_test_tbl; -- flush the fastupdate buffers |
| |
| select gin_clean_pending_list('gin_test_idx'); -- nothing to flush |
| |
| -- Test vacuuming |
| delete from gin_test_tbl where i @> array[2]; |
| vacuum gin_test_tbl; |
| |
| -- Disable fastupdate, and do more insertions. With fastupdate enabled, most |
| -- insertions (by flushing the list pages) cause page splits. Without |
| -- fastupdate, we get more churn in the GIN data leaf pages, and exercise the |
| -- recompression codepaths. |
| alter index gin_test_idx set (fastupdate = off); |
| |
| insert into gin_test_tbl select array[1, 2, g] from generate_series(1, 1000) g; |
| insert into gin_test_tbl select array[1, 3, g] from generate_series(1, 1000) g; |
| |
| delete from gin_test_tbl where i @> array[2]; |
| vacuum gin_test_tbl; |
| |
| -- Test for "rare && frequent" searches |
| explain (costs off) |
| select count(*) from gin_test_tbl where i @> array[1, 999]; |
| |
| select count(*) from gin_test_tbl where i @> array[1, 999]; |
| |
| -- Very weak test for gin_fuzzy_search_limit |
| set gin_fuzzy_search_limit = 1000; |
| |
| explain (costs off) |
| select count(*) > 0 as ok from gin_test_tbl where i @> array[1]; |
| |
| select count(*) > 0 as ok from gin_test_tbl where i @> array[1]; |
| |
| reset gin_fuzzy_search_limit; |
| |
| -- Test optimization of empty queries |
| create temp table t_gin_test_tbl(i int4[], j int4[]); |
| create index on t_gin_test_tbl using gin (i, j); |
| insert into t_gin_test_tbl |
| values |
| (null, null), |
| ('{}', null), |
| ('{1}', null), |
| ('{1,2}', null), |
| (null, '{}'), |
| (null, '{10}'), |
| ('{1,2}', '{10}'), |
| ('{2}', '{10}'), |
| ('{1,3}', '{}'), |
| ('{1,1}', '{10}'); |
| |
| set enable_seqscan = off; |
| explain (costs off) |
| select * from t_gin_test_tbl where array[0] <@ i; |
| select * from t_gin_test_tbl where array[0] <@ i; |
| select * from t_gin_test_tbl where array[0] <@ i and '{}'::int4[] <@ j; |
| |
| explain (costs off) |
| select * from t_gin_test_tbl where i @> '{}'; |
| select * from t_gin_test_tbl where i @> '{}'; |
| |
| create function explain_query_json(query_sql text) |
| returns table (explain_line json) |
| language plpgsql as |
| $$ |
| begin |
| set enable_seqscan = off; |
| set enable_bitmapscan = on; |
| return query execute 'EXPLAIN (ANALYZE, FORMAT json) ' || query_sql; |
| end; |
| $$; |
| |
| create function execute_text_query_index(query_sql text) |
| returns setof text |
| language plpgsql |
| as |
| $$ |
| begin |
| set enable_seqscan = off; |
| set enable_bitmapscan = on; |
| return query execute query_sql; |
| end; |
| $$; |
| |
| create function execute_text_query_heap(query_sql text) |
| returns setof text |
| language plpgsql |
| as |
| $$ |
| begin |
| set enable_seqscan = on; |
| set enable_bitmapscan = off; |
| return query execute query_sql; |
| end; |
| $$; |
| |
| -- check number of rows returned by index and removed by recheck |
| select |
| query, |
| js->0->'Plan'->'Plans'->0->'Actual Rows' as "return by index", |
| js->0->'Plan'->'Rows Removed by Index Recheck' as "removed by recheck", |
| (res_index = res_heap) as "match" |
| from |
| (values |
| ($$ i @> '{}' $$), |
| ($$ j @> '{}' $$), |
| ($$ i @> '{}' and j @> '{}' $$), |
| ($$ i @> '{1}' $$), |
| ($$ i @> '{1}' and j @> '{}' $$), |
| ($$ i @> '{1}' and i @> '{}' and j @> '{}' $$), |
| ($$ j @> '{10}' $$), |
| ($$ j @> '{10}' and i @> '{}' $$), |
| ($$ j @> '{10}' and j @> '{}' and i @> '{}' $$), |
| ($$ i @> '{1}' and j @> '{10}' $$) |
| ) q(query), |
| lateral explain_query_json($$select * from t_gin_test_tbl where $$ || query) js, |
| lateral execute_text_query_index($$select string_agg((i, j)::text, ' ') from t_gin_test_tbl where $$ || query) res_index, |
| lateral execute_text_query_heap($$select string_agg((i, j)::text, ' ') from t_gin_test_tbl where $$ || query) res_heap; |
| |
| reset enable_seqscan; |
| reset enable_bitmapscan; |
| |
| -- re-purpose t_gin_test_tbl to test scans involving posting trees |
| insert into t_gin_test_tbl select array[1, g, g/10], array[2, g, g/10] |
| from generate_series(1, 20000) g; |
| |
| select gin_clean_pending_list('t_gin_test_tbl_i_j_idx') is not null; |
| |
| analyze t_gin_test_tbl; |
| |
| set enable_seqscan = off; |
| set enable_bitmapscan = on; |
| |
| explain (costs off) |
| select count(*) from t_gin_test_tbl where j @> array[50]; |
| select count(*) from t_gin_test_tbl where j @> array[50]; |
| explain (costs off) |
| select count(*) from t_gin_test_tbl where j @> array[2]; |
| select count(*) from t_gin_test_tbl where j @> array[2]; |
| explain (costs off) |
| select count(*) from t_gin_test_tbl where j @> '{}'::int[]; |
| select count(*) from t_gin_test_tbl where j @> '{}'::int[]; |
| |
| -- test vacuuming of posting trees |
| delete from t_gin_test_tbl where j @> array[2]; |
| vacuum t_gin_test_tbl; |
| |
| select count(*) from t_gin_test_tbl where j @> array[50]; |
| select count(*) from t_gin_test_tbl where j @> array[2]; |
| select count(*) from t_gin_test_tbl where j @> '{}'::int[]; |
| |
| reset enable_seqscan; |
| reset enable_bitmapscan; |
| |
| drop table t_gin_test_tbl; |