Skip to content

Commit 3df84ec

Browse files
x4mCommitfest Bot
authored andcommitted
Add gist_index_check() function to verify GiST index
This function traverses GiST with a depth-fisrt search and checks that all downlink tuples are included into parent tuple keyspace. This traverse takes lock of any page until some discapency found. To re-check suspicious pair of parent and child tuples it aqcuires locks on both parent and child pages in the same order as page split does. Author: Andrey Borodin <[email protected]> Author: Heikki Linnakangas <[email protected]> Reviewed-By: José Villanova <[email protected]> Reviewed-By: Aleksander Alekseev <[email protected]> Reviewed-By: Nikolay Samokhvalov <[email protected]> Reviewed-By: Andres Freund <[email protected]> Reviewed-By: Tomas Vondra <[email protected]> Discussion: https://postgr.es/m/45AC9B0A-2B45-40EE-B08F-BDCF5739D1E1%40yandex-team.ru
1 parent 30651d7 commit 3df84ec

File tree

8 files changed

+911
-3
lines changed

8 files changed

+911
-3
lines changed

contrib/amcheck/Makefile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,17 @@ MODULE_big = amcheck
44
OBJS = \
55
$(WIN32RES) \
66
verify_common.o \
7+
verify_gist.o \
78
verify_gin.o \
89
verify_heapam.o \
910
verify_nbtree.o
1011

1112
EXTENSION = amcheck
1213
DATA = amcheck--1.2--1.3.sql amcheck--1.1--1.2.sql amcheck--1.0--1.1.sql amcheck--1.0.sql \
13-
amcheck--1.3--1.4.sql amcheck--1.4--1.5.sql
14+
amcheck--1.3--1.4.sql amcheck--1.4--1.5.sql amcheck--1.5--1.6.sql
1415
PGFILEDESC = "amcheck - function for verifying relation integrity"
1516

16-
REGRESS = check check_btree check_gin check_heap
17+
REGRESS = check check_btree check_gin check_gist check_heap
1718

1819
EXTRA_INSTALL = contrib/pg_walinspect
1920
TAP_TESTS = 1
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/* contrib/amcheck/amcheck--1.5--1.6.sql */
2+
3+
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
4+
\echo Use "ALTER EXTENSION amcheck UPDATE TO '1.6'" to load this file. \quit
5+
6+
7+
-- gist_index_check()
8+
--
9+
CREATE FUNCTION gist_index_check(index regclass, heapallindexed boolean)
10+
RETURNS VOID
11+
AS 'MODULE_PATHNAME', 'gist_index_check'
12+
LANGUAGE C STRICT;
13+
14+
REVOKE ALL ON FUNCTION gist_index_check(regclass,boolean) FROM PUBLIC;

contrib/amcheck/amcheck.control

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# amcheck extension
22
comment = 'functions for verifying relation integrity'
3-
default_version = '1.5'
3+
default_version = '1.6'
44
module_pathname = '$libdir/amcheck'
55
relocatable = true
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
SELECT setseed(1);
2+
setseed
3+
---------
4+
5+
(1 row)
6+
7+
-- Test that index built with bulk load is correct
8+
CREATE TABLE gist_check AS SELECT point(random(),s) c, random() p FROM generate_series(1,10000) s;
9+
CREATE INDEX gist_check_idx1 ON gist_check USING gist(c);
10+
CREATE INDEX gist_check_idx2 ON gist_check USING gist(c) INCLUDE(p);
11+
SELECT gist_index_check('gist_check_idx1', false);
12+
gist_index_check
13+
------------------
14+
15+
(1 row)
16+
17+
SELECT gist_index_check('gist_check_idx2', false);
18+
gist_index_check
19+
------------------
20+
21+
(1 row)
22+
23+
SELECT gist_index_check('gist_check_idx1', true);
24+
gist_index_check
25+
------------------
26+
27+
(1 row)
28+
29+
SELECT gist_index_check('gist_check_idx2', true);
30+
gist_index_check
31+
------------------
32+
33+
(1 row)
34+
35+
-- Test that index is correct after inserts
36+
INSERT INTO gist_check SELECT point(random(),s) c, random() p FROM generate_series(1,10000) s;
37+
SELECT gist_index_check('gist_check_idx1', false);
38+
gist_index_check
39+
------------------
40+
41+
(1 row)
42+
43+
SELECT gist_index_check('gist_check_idx2', false);
44+
gist_index_check
45+
------------------
46+
47+
(1 row)
48+
49+
SELECT gist_index_check('gist_check_idx1', true);
50+
gist_index_check
51+
------------------
52+
53+
(1 row)
54+
55+
SELECT gist_index_check('gist_check_idx2', true);
56+
gist_index_check
57+
------------------
58+
59+
(1 row)
60+
61+
-- Test that index is correct after vacuuming
62+
DELETE FROM gist_check WHERE c[1] < 5000; -- delete clustered data
63+
DELETE FROM gist_check WHERE c[1]::int % 2 = 0; -- delete scattered data
64+
-- We need two passes through the index and one global vacuum to actually
65+
-- reuse page
66+
VACUUM gist_check;
67+
VACUUM;
68+
SELECT gist_index_check('gist_check_idx1', false);
69+
gist_index_check
70+
------------------
71+
72+
(1 row)
73+
74+
SELECT gist_index_check('gist_check_idx2', false);
75+
gist_index_check
76+
------------------
77+
78+
(1 row)
79+
80+
SELECT gist_index_check('gist_check_idx1', true);
81+
gist_index_check
82+
------------------
83+
84+
(1 row)
85+
86+
SELECT gist_index_check('gist_check_idx2', true);
87+
gist_index_check
88+
------------------
89+
90+
(1 row)
91+
92+
-- Test that index is correct after reusing pages
93+
INSERT INTO gist_check SELECT point(random(),s) c, random() p FROM generate_series(1,10000) s;
94+
SELECT gist_index_check('gist_check_idx1', false);
95+
gist_index_check
96+
------------------
97+
98+
(1 row)
99+
100+
SELECT gist_index_check('gist_check_idx2', false);
101+
gist_index_check
102+
------------------
103+
104+
(1 row)
105+
106+
SELECT gist_index_check('gist_check_idx1', true);
107+
gist_index_check
108+
------------------
109+
110+
(1 row)
111+
112+
SELECT gist_index_check('gist_check_idx2', true);
113+
gist_index_check
114+
------------------
115+
116+
(1 row)
117+
118+
-- cleanup
119+
DROP TABLE gist_check;
120+
--
121+
-- Similar to BUG #15597
122+
--
123+
CREATE TABLE toast_bug(c point,buggy text);
124+
ALTER TABLE toast_bug ALTER COLUMN buggy SET STORAGE extended;
125+
CREATE INDEX toasty ON toast_bug USING gist(c) INCLUDE(buggy);
126+
-- pg_attribute entry for toasty.buggy (the index) will have plain storage:
127+
UPDATE pg_attribute SET attstorage = 'p'
128+
WHERE attrelid = 'toasty'::regclass AND attname = 'buggy';
129+
-- Whereas pg_attribute entry for toast_bug.buggy (the table) still has extended storage:
130+
SELECT attstorage FROM pg_attribute
131+
WHERE attrelid = 'toast_bug'::regclass AND attname = 'buggy';
132+
attstorage
133+
------------
134+
x
135+
(1 row)
136+
137+
-- Insert compressible heap tuple (comfortably exceeds TOAST_TUPLE_THRESHOLD):
138+
INSERT INTO toast_bug SELECT point(0,0), repeat('a', 2200);
139+
-- Should not get false positive report of corruption:
140+
SELECT gist_index_check('toasty', true);
141+
gist_index_check
142+
------------------
143+
144+
(1 row)
145+

contrib/amcheck/meson.build

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ amcheck_sources = files(
55
'verify_gin.c',
66
'verify_heapam.c',
77
'verify_nbtree.c',
8+
'verify_gist.c',
89
)
910

1011
if host_system == 'windows'
@@ -27,6 +28,7 @@ install_data(
2728
'amcheck--1.2--1.3.sql',
2829
'amcheck--1.3--1.4.sql',
2930
'amcheck--1.4--1.5.sql',
31+
'amcheck--1.5--1.6.sql',
3032
kwargs: contrib_data_args,
3133
)
3234

@@ -39,6 +41,7 @@ tests += {
3941
'check',
4042
'check_btree',
4143
'check_gin',
44+
'check_gist',
4245
'check_heap',
4346
],
4447
},

contrib/amcheck/sql/check_gist.sql

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
2+
SELECT setseed(1);
3+
4+
-- Test that index built with bulk load is correct
5+
CREATE TABLE gist_check AS SELECT point(random(),s) c, random() p FROM generate_series(1,10000) s;
6+
CREATE INDEX gist_check_idx1 ON gist_check USING gist(c);
7+
CREATE INDEX gist_check_idx2 ON gist_check USING gist(c) INCLUDE(p);
8+
SELECT gist_index_check('gist_check_idx1', false);
9+
SELECT gist_index_check('gist_check_idx2', false);
10+
SELECT gist_index_check('gist_check_idx1', true);
11+
SELECT gist_index_check('gist_check_idx2', true);
12+
13+
-- Test that index is correct after inserts
14+
INSERT INTO gist_check SELECT point(random(),s) c, random() p FROM generate_series(1,10000) s;
15+
SELECT gist_index_check('gist_check_idx1', false);
16+
SELECT gist_index_check('gist_check_idx2', false);
17+
SELECT gist_index_check('gist_check_idx1', true);
18+
SELECT gist_index_check('gist_check_idx2', true);
19+
20+
-- Test that index is correct after vacuuming
21+
DELETE FROM gist_check WHERE c[1] < 5000; -- delete clustered data
22+
DELETE FROM gist_check WHERE c[1]::int % 2 = 0; -- delete scattered data
23+
24+
-- We need two passes through the index and one global vacuum to actually
25+
-- reuse page
26+
VACUUM gist_check;
27+
VACUUM;
28+
29+
SELECT gist_index_check('gist_check_idx1', false);
30+
SELECT gist_index_check('gist_check_idx2', false);
31+
SELECT gist_index_check('gist_check_idx1', true);
32+
SELECT gist_index_check('gist_check_idx2', true);
33+
34+
35+
-- Test that index is correct after reusing pages
36+
INSERT INTO gist_check SELECT point(random(),s) c, random() p FROM generate_series(1,10000) s;
37+
SELECT gist_index_check('gist_check_idx1', false);
38+
SELECT gist_index_check('gist_check_idx2', false);
39+
SELECT gist_index_check('gist_check_idx1', true);
40+
SELECT gist_index_check('gist_check_idx2', true);
41+
-- cleanup
42+
DROP TABLE gist_check;
43+
44+
--
45+
-- Similar to BUG #15597
46+
--
47+
CREATE TABLE toast_bug(c point,buggy text);
48+
ALTER TABLE toast_bug ALTER COLUMN buggy SET STORAGE extended;
49+
CREATE INDEX toasty ON toast_bug USING gist(c) INCLUDE(buggy);
50+
51+
-- pg_attribute entry for toasty.buggy (the index) will have plain storage:
52+
UPDATE pg_attribute SET attstorage = 'p'
53+
WHERE attrelid = 'toasty'::regclass AND attname = 'buggy';
54+
55+
-- Whereas pg_attribute entry for toast_bug.buggy (the table) still has extended storage:
56+
SELECT attstorage FROM pg_attribute
57+
WHERE attrelid = 'toast_bug'::regclass AND attname = 'buggy';
58+
59+
-- Insert compressible heap tuple (comfortably exceeds TOAST_TUPLE_THRESHOLD):
60+
INSERT INTO toast_bug SELECT point(0,0), repeat('a', 2200);
61+
-- Should not get false positive report of corruption:
62+
SELECT gist_index_check('toasty', true);

0 commit comments

Comments
 (0)