
Add a new, optional, capability to bt_index_check() and bt_index_parent_check(): check that each heap tuple that should have an index entry does in fact have one. The extra checking is performed at the end of the existing nbtree checks. This is implemented by using a Bloom filter data structure. The implementation performs set membership tests within a callback (the same type of callback that each index AM registers for CREATE INDEX). The Bloom filter is populated during the initial index verification scan. Reusing the CREATE INDEX infrastructure allows the new verification option to automatically benefit from the heap consistency checks that CREATE INDEX already performs. CREATE INDEX does thorough sanity checking of HOT chains, so the new check actually manages to detect problems in heap-only tuples. Author: Peter Geoghegan Reviewed-By: Pavan Deolasee, Andres Freund Discussion: https://postgr.es/m/CAH2-Wzm5VmG7cu1N-H=nnS57wZThoSDQU+F5dewx3o84M+jY=g@mail.gmail.com
65 lines
2.2 KiB
PL/PgSQL
65 lines
2.2 KiB
PL/PgSQL
-- minimal test, basically just verifying that amcheck
|
|
CREATE TABLE bttest_a(id int8);
|
|
CREATE TABLE bttest_b(id int8);
|
|
|
|
INSERT INTO bttest_a SELECT * FROM generate_series(1, 100000);
|
|
INSERT INTO bttest_b SELECT * FROM generate_series(100000, 1, -1);
|
|
|
|
CREATE INDEX bttest_a_idx ON bttest_a USING btree (id);
|
|
CREATE INDEX bttest_b_idx ON bttest_b USING btree (id);
|
|
|
|
CREATE ROLE bttest_role;
|
|
|
|
-- verify permissions are checked (error due to function not callable)
|
|
SET ROLE bttest_role;
|
|
SELECT bt_index_check('bttest_a_idx'::regclass);
|
|
SELECT bt_index_parent_check('bttest_a_idx'::regclass);
|
|
RESET ROLE;
|
|
|
|
-- we, intentionally, don't check relation permissions - it's useful
|
|
-- to run this cluster-wide with a restricted account, and as tested
|
|
-- above explicit permission has to be granted for that.
|
|
GRANT EXECUTE ON FUNCTION bt_index_check(regclass) TO bttest_role;
|
|
GRANT EXECUTE ON FUNCTION bt_index_parent_check(regclass) TO bttest_role;
|
|
GRANT EXECUTE ON FUNCTION bt_index_check(regclass, boolean) TO bttest_role;
|
|
GRANT EXECUTE ON FUNCTION bt_index_parent_check(regclass, boolean) TO bttest_role;
|
|
SET ROLE bttest_role;
|
|
SELECT bt_index_check('bttest_a_idx');
|
|
SELECT bt_index_parent_check('bttest_a_idx');
|
|
RESET ROLE;
|
|
|
|
-- verify plain tables are rejected (error)
|
|
SELECT bt_index_check('bttest_a');
|
|
SELECT bt_index_parent_check('bttest_a');
|
|
|
|
-- verify non-existing indexes are rejected (error)
|
|
SELECT bt_index_check(17);
|
|
SELECT bt_index_parent_check(17);
|
|
|
|
-- verify wrong index types are rejected (error)
|
|
BEGIN;
|
|
CREATE INDEX bttest_a_brin_idx ON bttest_a USING brin(id);
|
|
SELECT bt_index_parent_check('bttest_a_brin_idx');
|
|
ROLLBACK;
|
|
|
|
-- normal check outside of xact
|
|
SELECT bt_index_check('bttest_a_idx');
|
|
-- more expansive tests
|
|
SELECT bt_index_check('bttest_a_idx', true);
|
|
SELECT bt_index_parent_check('bttest_b_idx', true);
|
|
|
|
BEGIN;
|
|
SELECT bt_index_check('bttest_a_idx');
|
|
SELECT bt_index_parent_check('bttest_b_idx');
|
|
-- make sure we don't have any leftover locks
|
|
SELECT * FROM pg_locks
|
|
WHERE relation = ANY(ARRAY['bttest_a', 'bttest_a_idx', 'bttest_b', 'bttest_b_idx']::regclass[])
|
|
AND pid = pg_backend_pid();
|
|
COMMIT;
|
|
|
|
-- cleanup
|
|
DROP TABLE bttest_a;
|
|
DROP TABLE bttest_b;
|
|
DROP OWNED BY bttest_role; -- permissions
|
|
DROP ROLE bttest_role;
|