Skip to content

Commit cdd1a43

Browse files
committed
amcheck: Fix parent key check in gin_index_check()
The checks introduced by commit 14ffaec did not get the parent key checks quite right, missing some data corruption cases. In particular: * The "rightlink" check was not working as intended, because rightlink is a BlockNumber, and InvalidBlockNumber is 0xFFFFFFFF, so !GinPageGetOpaque(page)->rightlink almost always evaluates to false (except for rightlink=0). So in most cases parenttup was left NULL, preventing any checks against parent. * Use GinGetDownlink() to retrieve child blkno to avoid triggering Assert, same as the core GIN code. Issues reported by Arseniy Mukhin, along with a proposed patch. Review by Andrey M. Borodin, cleanup and improvements by me. Author: Arseniy Mukhin <arseniy.mukhin.dev@gmail.com> Reviewed-by: Andrey M. Borodin <x4mmm@yandex-team.ru> Discussion: https://postgr.es/m/CAE7r3MJ611B9TE=YqBBncewp7-k64VWs+sjk7XF6fJUX77uFBA@mail.gmail.com
1 parent 0b54b39 commit cdd1a43

File tree

2 files changed

+82
-4
lines changed

2 files changed

+82
-4
lines changed

contrib/amcheck/t/006_verify_gin.pl

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
invalid_entry_order_leaf_page_test();
3535
invalid_entry_order_inner_page_test();
3636
invalid_entry_columns_order_test();
37+
inconsistent_with_parent_key__parent_key_corrupted_test();
38+
inconsistent_with_parent_key__child_key_corrupted_test();
3739

3840
sub invalid_entry_order_leaf_page_test
3941
{
@@ -159,6 +161,82 @@ sub invalid_entry_columns_order_test
159161
like($stderr, qr/$expected/);
160162
}
161163

164+
sub inconsistent_with_parent_key__parent_key_corrupted_test
165+
{
166+
my $relname = "test";
167+
my $indexname = "test_gin_idx";
168+
169+
# fill the table until we have a split
170+
$node->safe_psql(
171+
'postgres', qq(
172+
DROP TABLE IF EXISTS $relname;
173+
CREATE TABLE $relname (a text[]);
174+
INSERT INTO $relname (a) VALUES (('{' || 'llllllllll' || random_string($filler_size) ||'}')::text[]);
175+
INSERT INTO $relname (a) VALUES (('{' || 'mmmmmmmmmm' || random_string($filler_size) ||'}')::text[]);
176+
INSERT INTO $relname (a) VALUES (('{' || 'nnnnnnnnnn' || random_string($filler_size) ||'}')::text[]);
177+
INSERT INTO $relname (a) VALUES (('{' || 'xxxxxxxxxx' || random_string($filler_size) ||'}')::text[]);
178+
INSERT INTO $relname (a) VALUES (('{' || 'yyyyyyyyyy' || random_string($filler_size) ||'}')::text[]);
179+
CREATE INDEX $indexname ON $relname USING gin (a);
180+
));
181+
my $relpath = relation_filepath($indexname);
182+
183+
$node->stop;
184+
185+
my $blkno = 1; # root
186+
187+
# we have nnnnnnnnnn... as parent key in the root, so replace it with something smaller then child's keys
188+
string_replace_block(
189+
$relpath,
190+
'nnnnnnnnnn',
191+
'aaaaaaaaaa',
192+
$blkno
193+
);
194+
195+
$node->start;
196+
197+
my ($result, $stdout, $stderr) = $node->psql('postgres', qq(SELECT gin_index_check('$indexname')));
198+
my $expected = "index \"$indexname\" has inconsistent records on page 3 offset 3";
199+
like($stderr, qr/$expected/);
200+
}
201+
202+
sub inconsistent_with_parent_key__child_key_corrupted_test
203+
{
204+
my $relname = "test";
205+
my $indexname = "test_gin_idx";
206+
207+
# fill the table until we have a split
208+
$node->safe_psql(
209+
'postgres', qq(
210+
DROP TABLE IF EXISTS $relname;
211+
CREATE TABLE $relname (a text[]);
212+
INSERT INTO $relname (a) VALUES (('{' || 'llllllllll' || random_string($filler_size) ||'}')::text[]);
213+
INSERT INTO $relname (a) VALUES (('{' || 'mmmmmmmmmm' || random_string($filler_size) ||'}')::text[]);
214+
INSERT INTO $relname (a) VALUES (('{' || 'nnnnnnnnnn' || random_string($filler_size) ||'}')::text[]);
215+
INSERT INTO $relname (a) VALUES (('{' || 'xxxxxxxxxx' || random_string($filler_size) ||'}')::text[]);
216+
INSERT INTO $relname (a) VALUES (('{' || 'yyyyyyyyyy' || random_string($filler_size) ||'}')::text[]);
217+
CREATE INDEX $indexname ON $relname USING gin (a);
218+
));
219+
my $relpath = relation_filepath($indexname);
220+
221+
$node->stop;
222+
223+
my $blkno = 3; # leaf
224+
225+
# we have nnnnnnnnnn... as parent key in the root, so replace child key with something bigger
226+
string_replace_block(
227+
$relpath,
228+
'nnnnnnnnnn',
229+
'pppppppppp',
230+
$blkno
231+
);
232+
233+
$node->start;
234+
235+
my ($result, $stdout, $stderr) = $node->psql('postgres', qq(SELECT gin_index_check('$indexname')));
236+
my $expected = "index \"$indexname\" has inconsistent records on page 3 offset 3";
237+
like($stderr, qr/$expected/);
238+
}
239+
162240
# Returns the filesystem path for the named relation.
163241
sub relation_filepath
164242
{

contrib/amcheck/verify_gin.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -608,10 +608,10 @@ gin_check_parent_keys_consistency(Relation rel,
608608
ptr = (GinScanItem *) palloc(sizeof(GinScanItem));
609609
ptr->depth = stack->depth + 1;
610610
/* last tuple in layer has no high key */
611-
if (i != maxoff && !GinPageGetOpaque(page)->rightlink)
612-
ptr->parenttup = CopyIndexTuple(idxtuple);
613-
else
611+
if (i == maxoff && rightlink == InvalidBlockNumber)
614612
ptr->parenttup = NULL;
613+
else
614+
ptr->parenttup = CopyIndexTuple(idxtuple);
615615
ptr->parentblk = stack->blkno;
616616
ptr->blkno = GinGetDownlink(idxtuple);
617617
ptr->next = stack->next;
@@ -748,7 +748,7 @@ gin_refind_parent(Relation rel, BlockNumber parentblkno,
748748
ItemId p_iid = PageGetItemIdCareful(rel, parentblkno, parentpage, o);
749749
IndexTuple itup = (IndexTuple) PageGetItem(parentpage, p_iid);
750750

751-
if (ItemPointerGetBlockNumber(&(itup->t_tid)) == childblkno)
751+
if (GinGetDownlink(itup) == childblkno)
752752
{
753753
/* Found it! Make copy and return it */
754754
result = CopyIndexTuple(itup);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy