diff options
Diffstat (limited to 'src/test/modules/gin/expected/gin_incomplete_splits.out')
-rw-r--r-- | src/test/modules/gin/expected/gin_incomplete_splits.out | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/src/test/modules/gin/expected/gin_incomplete_splits.out b/src/test/modules/gin/expected/gin_incomplete_splits.out new file mode 100644 index 00000000000..9a60f8c128a --- /dev/null +++ b/src/test/modules/gin/expected/gin_incomplete_splits.out @@ -0,0 +1,180 @@ +-- +-- The test uses a GIN index over int[]. The table contains arrays +-- with integers from 1 to :next_i. Each integer occurs exactly once, +-- no gaps or duplicates, although the index does contain some +-- duplicate elements because some of the inserting transactions are +-- rolled back during the test. The exact contents of the table depend +-- on the physical layout of the index, which in turn depends at least +-- on the block size, so instead of check for the exact contents, we +-- check those invariants. :next_i psql variable is maintained at all +-- times to hold the last inserted integer + 1. +-- +-- This uses injection points to cause errors that leave some page +-- splits in "incomplete" state +create extension injection_points; +-- Use the index for all the queries +set enable_seqscan=off; +-- Print a NOTICE whenever an incomplete split gets fixed +SELECT injection_points_attach('gin-finish-incomplete-split', 'notice'); + injection_points_attach +------------------------- + +(1 row) + +-- +-- First create the test table and some helper functions +-- +create table gin_incomplete_splits(i int4[]) with (autovacuum_enabled = off); +create index on gin_incomplete_splits using gin (i) with (fastupdate = off); +-- Creates an array with all integers from $1 (inclusive) $2 (exclusive) +create function range_array(int, int) returns int[] language sql immutable as $$ + select array_agg(g) from generate_series($1, $2 - 1) g +$$; +-- Inserts an array with 'n' rows to the test table. Pass :next_i as +-- the first argument, returns the new value for :next_i. +create function insert_n(first_i int, n int) returns int language plpgsql as $$ +begin + insert into gin_incomplete_splits values (range_array(first_i, first_i+n)); + return first_i + n; +end; +$$; +-- Inserts to the table until an insert fails. Like insert_n(), returns the +-- new value for :next_i. +create function insert_until_fail(next_i int, step int default 1) returns int language plpgsql as $$ +declare + i integer; +begin + -- Insert arrays with 'step' elements each, until an error occurs. + loop + begin + select insert_n(next_i, step) into next_i; + exception when others then + raise notice 'failed with: %', sqlerrm; + exit; + end; + + -- The caller is expected to set an injection point that eventuall + -- causes an error. But bail out if still no error after 10000 + -- attempts, so that we don't get stuck in an infinite loop. + i := i + 1; + if i = 10000 then + raise 'no error on inserts after '; + end if; + end loop; + + return next_i; +end; +$$; +-- Check the invariants. +create function verify(next_i int) returns bool language plpgsql as $$ +declare + a integer[]; + elem integer; + c integer; +begin + -- Perform a scan over the trailing part of the index, where the + -- possible incomplete splits are. (We don't check the whole table, + -- because that'd be pretty slow.) + c := 0; + -- Find all arrays that overlap with the last 200 inserted integers. Or + -- the next 100, which shouldn't exist. + for a in select i from gin_incomplete_splits where i && range_array(next_i - 200, next_i + 100) + loop + -- count all elements in those arrays in the window. + foreach elem in ARRAY a loop + if elem >= next_i then + raise 'unexpected value % in array', elem; + end if; + if elem >= next_i - 200 then + c := c + 1; + end if; + end loop; + end loop; + if c <> 200 then + raise 'unexpected count % ', c; + end if; + + return true; +end; +$$; +-- Insert one array to get started. +select insert_n(1, 1000) as next_i +\gset +select verify(:next_i); + verify +-------- + t +(1 row) + +-- +-- Test incomplete leaf split +-- +SELECT injection_points_attach('gin-leave-leaf-split-incomplete', 'error'); + injection_points_attach +------------------------- + +(1 row) + +select insert_until_fail(:next_i) as next_i +\gset +NOTICE: failed with: error triggered for injection point gin-leave-leaf-split-incomplete +SELECT injection_points_detach('gin-leave-leaf-split-incomplete'); + injection_points_detach +------------------------- + +(1 row) + +-- Verify that a scan works even though there's an incomplete split +select verify(:next_i); + verify +-------- + t +(1 row) + +-- Insert some more rows, finishing the split +select insert_n(:next_i, 10) as next_i +\gset +NOTICE: notice triggered for injection point gin-finish-incomplete-split +-- Verify that a scan still works +select verify(:next_i); + verify +-------- + t +(1 row) + +-- +-- Test incomplete internal page split +-- +SELECT injection_points_attach('gin-leave-internal-split-incomplete', 'error'); + injection_points_attach +------------------------- + +(1 row) + +select insert_until_fail(:next_i, 100) as next_i +\gset +NOTICE: failed with: error triggered for injection point gin-leave-internal-split-incomplete +SELECT injection_points_detach('gin-leave-internal-split-incomplete'); + injection_points_detach +------------------------- + +(1 row) + + -- Verify that a scan works even though there's an incomplete split +select verify(:next_i); + verify +-------- + t +(1 row) + +-- Insert some more rows, finishing the split +select insert_n(:next_i, 10) as next_i +\gset +NOTICE: notice triggered for injection point gin-finish-incomplete-split +-- Verify that a scan still works +select verify(:next_i); + verify +-------- + t +(1 row) + |