-- Format violations raise ERRCODE_INDEX_CORRUPTED (XX002) with a precise detail CREATE EXTENSION pg_sift_test CASCADE; NOTICE: installing required extension "pg_sift" CREATE FUNCTION expect(q text) RETURNS text LANGUAGE plpgsql AS $$ DECLARE d text; h text; BEGIN EXECUTE q; RETURN 'ok'; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS d = PG_EXCEPTION_DETAIL, h = PG_EXCEPTION_HINT; RETURN regexp_replace(SQLSTATE || ': ' || SQLERRM || coalesce(' | ' || nullif(d, ''), '') || coalesce(' | ' || nullif(h, ''), ''), 'pg_sift [0-9]+(\.[0-9]+)+', 'pg_sift '); END $$; -- Fresh index: segment 1 = 20000 bytes (header block 5, data 2-4), segment 2 = 100 bytes CREATE TABLE t (v int); INSERT INTO t VALUES (20000), (100); CREATE FUNCTION fresh() RETURNS void LANGUAGE sql AS $$ REINDEX INDEX t_idx $$; CREATE INDEX t_idx ON t USING sift_store_test (v); SELECT gen, header, size, nextents FROM sift._test_segments('t_idx'); gen | header | size | nextents -----+--------+-------+---------- 1 | 5 | 20000 | 1 2 | 7 | 100 | 1 (2 rows) SELECT expect($$ SELECT sift._test_check('t_idx') $$); expect -------- ok (1 row) -- Data page type in the special space SELECT sift._test_poke('t_idx', 3, 8176, 9, 2); _test_poke ------------ (1 row) SELECT expect($$ SELECT sift._test_seg_read('t_idx', 1) $$); expect ----------------------------------------------------------------------------------------------------------------- XX002: sift index "t_idx" is corrupted | Block 3: expected page type 4 generation 1, found type 9 generation 1. (1 row) -- Data page generation SELECT fresh(); fresh ------- (1 row) SELECT sift._test_poke('t_idx', 4, 8184, 77, 8); _test_poke ------------ (1 row) SELECT expect($$ SELECT sift._test_seg_read('t_idx', 1) $$); expect ------------------------------------------------------------------------------------------------------------------ XX002: sift index "t_idx" is corrupted | Block 4: expected page type 4 generation 1, found type 4 generation 77. (1 row) -- Truncated page: pd_lower below the bytes the stream needs SELECT fresh(); fresh ------- (1 row) SELECT sift._test_poke('t_idx', 4, 12, 1000, 2); _test_poke ------------ (1 row) SELECT expect($$ SELECT sift._test_seg_read('t_idx', 1) $$); expect -------------------------------------------------------------------------------------------------- XX002: sift index "t_idx" is corrupted | Read past the 976 written bytes of block 4 (segment 1). (1 row) -- Broken layout: pd_upper moved SELECT fresh(); fresh ------- (1 row) SELECT sift._test_poke('t_idx', 2, 14, 8000, 2); _test_poke ------------ (1 row) SELECT expect($$ SELECT sift._test_seg_read('t_idx', 1, 2) $$); expect --------------------------------------------------------------------------------------------------------------------- XX002: sift index "t_idx" is corrupted | Block 2 has an invalid page layout (lower 8176, upper 8000, special 8176). (1 row) -- Segment header type SELECT fresh(); fresh ------- (1 row) SELECT sift._test_poke('t_idx', 5, 8176, 4, 2); _test_poke ------------ (1 row) SELECT expect($$ SELECT sift._test_seg_read('t_idx', 1) $$); expect ----------------------------------------------------------------------------------------------------------------- XX002: sift index "t_idx" is corrupted | Block 5: expected page type 3 generation 1, found type 4 generation 1. (1 row) -- Metapage magic, format version, high-water mark SELECT fresh(); fresh ------- (1 row) SELECT sift._test_poke('t_idx', 0, 24, 0, 4); _test_poke ------------ (1 row) SELECT expect($$ SELECT sift._test_seg_read('t_idx', 1) $$); expect ------------------------------------------------------------------------------------- XX002: sift index "t_idx" is corrupted | Bad metapage (magic 0x00000000, 64 bytes). (1 row) -- the used bytes of the metapage (pd_lower at offset 12, the data at 24): the 64 of a build without the failure note, the 272 with it and the 400 with the analyzer configuration are read, others are not SELECT fresh(); fresh ------- (1 row) SELECT sift._test_poke('t_idx', 0, 12, 24 + 40, 2); _test_poke ------------ (1 row) SELECT expect($$ SELECT sift._test_check('t_idx') $$); expect ------------------------------------------------------------------------------------- XX002: sift index "t_idx" is corrupted | Bad metapage (magic 0x53494654, 40 bytes). (1 row) SELECT fresh(); fresh ------- (1 row) SELECT sift._test_poke('t_idx', 0, 12, 24 + 500, 2); _test_poke ------------ (1 row) SELECT expect($$ SELECT sift._test_check('t_idx') $$); expect -------------------------------------------------------------------------------------- XX002: sift index "t_idx" is corrupted | Bad metapage (magic 0x53494654, 500 bytes). (1 row) SELECT fresh(); fresh ------- (1 row) SELECT sift._test_poke('t_idx', 0, 12, 24 + 272, 2); _test_poke ------------ (1 row) SELECT sift._test_check('t_idx') >= 0 AS note_size_is_read; note_size_is_read ------------------- t (1 row) SELECT fresh(); fresh ------- (1 row) SELECT sift._test_poke('t_idx', 0, 12, 24 + 400, 2); _test_poke ------------ (1 row) SELECT sift._test_check('t_idx') >= 0 AS config_size_is_read; config_size_is_read --------------------- t (1 row) SELECT fresh(); fresh ------- (1 row) SELECT sift._test_poke('t_idx', 0, 28, 2, 2); _test_poke ------------ (1 row) SELECT expect($$ SELECT sift._test_check('t_idx') $$); expect ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 55000: sift index "t_idx" has on-disk format 2.2, this build (pg_sift ) reads formats 1.0 to 1.6 | Install the pg_sift release that wrote the index on every node that reads it, or REINDEX it. (1 row) SELECT fresh(); fresh ------- (1 row) SELECT sift._test_poke('t_idx', 0, 30, 7, 2); _test_poke ------------ (1 row) SELECT expect($$ SELECT sift._test_check('t_idx') $$); expect ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 55000: sift index "t_idx" has on-disk format 1.7, this build (pg_sift ) reads formats 1.0 to 1.6 | Install the pg_sift release that wrote the index on every node that reads it, or REINDEX it. (1 row) SELECT fresh(); fresh ------- (1 row) SELECT sift._test_poke('t_idx', 0, 30, 0, 2); _test_poke ------------ (1 row) SELECT sift._test_check('t_idx') >= 0 AS format_1_0_is_read; format_1_0_is_read -------------------- t (1 row) SELECT fresh(); fresh ------- (1 row) SELECT sift._test_poke('t_idx', 0, 30, 1, 2); _test_poke ------------ (1 row) SELECT sift._test_check('t_idx') >= 0 AS format_1_1_is_read; format_1_1_is_read -------------------- t (1 row) SELECT fresh(); fresh ------- (1 row) SELECT sift._test_poke('t_idx', 0, 30, 3, 2); _test_poke ------------ (1 row) SELECT sift._test_check('t_idx') >= 0 AS format_1_3_is_read; format_1_3_is_read -------------------- t (1 row) SELECT fresh(); fresh ------- (1 row) SELECT sift._test_poke('t_idx', 0, 76, 1, 4); _test_poke ------------ (1 row) SELECT expect($$ SELECT sift._test_check('t_idx') $$); expect ------------------------------------------------------------------------------------------- XX002: sift index "t_idx" is corrupted | High-water mark 1 is below the first data block. (1 row) SELECT fresh(); fresh ------- (1 row) SELECT sift._test_poke('t_idx', 0, 76, 100, 4); _test_poke ------------ (1 row) SELECT expect($$ SELECT sift._test_check('t_idx') $$); expect ------------------------------------------------------------------------------------------------ XX002: sift index "t_idx" is corrupted | Relation has 8 blocks, below the high-water mark 100. (1 row) -- Directory: segment count and entry generation SELECT fresh(); fresh ------- (1 row) SELECT sift._test_poke('t_idx', 1, 28, 129, 2); _test_poke ------------ (1 row) SELECT expect($$ SELECT sift._test_check('t_idx') $$); expect ------------------------------------------------------------------------------------- XX002: sift index "t_idx" is corrupted | Directory holds 129 segments in 120 bytes. (1 row) SELECT fresh(); fresh ------- (1 row) SELECT sift._test_poke('t_idx', 1, 32 + 56, 1, 8); _test_poke ------------ (1 row) SELECT expect($$ SELECT sift._test_check('t_idx') $$); expect --------------------------------------------------------------------------------------- XX002: sift index "t_idx" is corrupted | Generation 1 appears twice in the directory. (1 row) -- Segment header extent past the high-water mark, and overlapping another segment SELECT fresh(); fresh ------- (1 row) SELECT sift._test_poke('t_idx', 5, 160, 50, 4); _test_poke ------------ (1 row) SELECT expect($$ SELECT sift._test_check('t_idx') $$); expect ---------------------------------------------------------------------------------------------------------------------------- XX002: sift index "t_idx" is corrupted | The segment extent of generation 1 (blocks 2+50) lies past the high-water mark 8. (1 row) SELECT fresh(); fresh ------- (1 row) SELECT sift._test_poke('t_idx', 5, 156, 4, 4); _test_poke ------------ (1 row) SELECT expect($$ SELECT sift._test_check('t_idx') $$); expect -------------------------------------------------------------------------------------------------------- XX002: sift index "t_idx" is corrupted | Block 5 is referenced twice (segment extent of generation 1). (1 row) -- Free list: a record covering a live block is caught by check and by a release SELECT fresh(); fresh ------- (1 row) SELECT sift._test_seg_write('t_idx', 1, 1) AS g \gset SELECT sift._test_seg_drop('t_idx', :g); _test_seg_drop ---------------- 2 (1 row) SELECT key, value FROM sift._test_info('t_idx') WHERE key IN ('freelist_pages', 'free_extents'); key | value ----------------+------- freelist_pages | 1 free_extents | 2 (2 rows) SELECT value AS fl FROM sift._test_info('t_idx') WHERE key = 'freelist_head' \gset SELECT (sift._test_page('t_idx', :fl)).type AS fl_type; fl_type --------- 8 (1 row) SELECT sift._test_poke('t_idx', :fl, 32, 5, 4); _test_poke ------------ (1 row) SELECT expect($$ SELECT sift._test_check('t_idx') $$); expect ------------------------------------------------------------------------------------- XX002: sift index "t_idx" is corrupted | Block 5 is referenced twice (free extent). (1 row) SELECT expect($$ SELECT sift._test_seg_drop('t_idx', 1) $$); expect --------------------------------------------------------------------- XX002: sift index "t_idx" is corrupted | Block 5 is released twice. (1 row) -- Free list record past the high-water mark, and a chain pointing at itself SELECT sift._test_poke('t_idx', :fl, 32, 5000, 4); _test_poke ------------ (1 row) SELECT expect($$ SELECT sift._test_alloc('t_idx', 1, 1) $$); expect ------------------------------------------------------------------------------------------------------------- XX002: sift index "t_idx" is corrupted | Free list block 24 has record 5000+14 past the high-water mark 25. (1 row) SELECT sift._test_poke('t_idx', :fl, 28, 0, 4), sift._test_poke('t_idx', :fl, 12, 32, 2); _test_poke | _test_poke ------------+------------ | (1 row) SELECT sift._test_poke('t_idx', :fl, 24, :fl, 4); _test_poke ------------ (1 row) SELECT expect($$ SELECT sift._test_check('t_idx') $$); expect ----------------------------------------------------------------------------------------- XX002: sift index "t_idx" is corrupted | Block 24 is referenced twice (free list page). (1 row) SELECT expect($$ SELECT sift._test_alloc('t_idx', 1, 1) $$); expect ------------------------------------------------------------------------------------------- XX002: sift index "t_idx" is corrupted | Free list chain reaches block 24 after 25 pages. (1 row) -- A fresh build is clean again SELECT fresh(); fresh ------- (1 row) SELECT expect($$ SELECT sift._test_check('t_idx') $$); expect -------- ok (1 row) DROP TABLE t; DROP FUNCTION expect(text), fresh(); DROP EXTENSION pg_sift_test; DROP EXTENSION pg_sift;