Skip to content

Commit 50529e5

Browse files
committed
amcheck: Improve some confusing reports about TOAST problems.
Don't phrase reports in terms of the number of tuples thus-far returned by the index scan, but rather in terms of the chunk_seq values found inside the tuples. Patch by me, reviewed by Mark Dilger. Discussion: http://postgr.es/m/CA+TgmoZUONCkdcdR778EKuE+f1r5Obieu63db2OgMPHaEvEPTQ@mail.gmail.com
1 parent f68970e commit 50529e5

File tree

1 file changed

+40
-35
lines changed

1 file changed

+40
-35
lines changed

contrib/amcheck/verify_heapam.c

Lines changed: 40 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,8 @@ typedef struct HeapCheckContext
150150
static void sanity_check_relation(Relation rel);
151151
static void check_tuple(HeapCheckContext *ctx);
152152
static void check_toast_tuple(HeapTuple toasttup, HeapCheckContext *ctx,
153-
ToastedAttribute *ta, int32 chunkno,
154-
int32 endchunk);
153+
ToastedAttribute *ta, int32 *expected_chunk_seq,
154+
uint32 extsize);
155155

156156
static bool check_tuple_attribute(HeapCheckContext *ctx);
157157
static void check_toasted_attribute(HeapCheckContext *ctx,
@@ -1159,37 +1159,51 @@ check_tuple_visibility(HeapCheckContext *ctx)
11591159
* each toast tuple being checked against where we are in the sequence, as well
11601160
* as each toast tuple having its varlena structure sanity checked.
11611161
*
1162-
* Returns whether the toast tuple passed the corruption checks.
1162+
* On entry, *expected_chunk_seq should be the chunk_seq value that we expect
1163+
* to find in toasttup. On exit, it will be updated to the value the next call
1164+
* to this function should expect to see.
11631165
*/
11641166
static void
11651167
check_toast_tuple(HeapTuple toasttup, HeapCheckContext *ctx,
1166-
ToastedAttribute *ta, int32 chunkno, int32 endchunk)
1168+
ToastedAttribute *ta, int32 *expected_chunk_seq,
1169+
uint32 extsize)
11671170
{
1168-
int32 curchunk;
1171+
int32 chunk_seq;
1172+
int32 last_chunk_seq = (extsize - 1) / TOAST_MAX_CHUNK_SIZE;
11691173
Pointer chunk;
11701174
bool isnull;
11711175
int32 chunksize;
11721176
int32 expected_size;
11731177

1174-
/*
1175-
* Have a chunk, extract the sequence number and the data
1176-
*/
1177-
curchunk = DatumGetInt32(fastgetattr(toasttup, 2,
1178-
ctx->toast_rel->rd_att, &isnull));
1178+
/* Sanity-check the sequence number. */
1179+
chunk_seq = DatumGetInt32(fastgetattr(toasttup, 2,
1180+
ctx->toast_rel->rd_att, &isnull));
11791181
if (isnull)
11801182
{
11811183
report_toast_corruption(ctx, ta,
11821184
psprintf("toast value %u has toast chunk with null sequence number",
11831185
ta->toast_pointer.va_valueid));
11841186
return;
11851187
}
1188+
if (chunk_seq != *expected_chunk_seq)
1189+
{
1190+
/* Either the TOAST index is corrupt, or we don't have all chunks. */
1191+
report_toast_corruption(ctx, ta,
1192+
psprintf("toast value %u index scan returned chunk %d when expecting chunk %d",
1193+
ta->toast_pointer.va_valueid,
1194+
chunk_seq, *expected_chunk_seq));
1195+
}
1196+
*expected_chunk_seq = chunk_seq + 1;
1197+
1198+
/* Sanity-check the chunk data. */
11861199
chunk = DatumGetPointer(fastgetattr(toasttup, 3,
11871200
ctx->toast_rel->rd_att, &isnull));
11881201
if (isnull)
11891202
{
11901203
report_toast_corruption(ctx, ta,
11911204
psprintf("toast value %u chunk %d has null data",
1192-
ta->toast_pointer.va_valueid, chunkno));
1205+
ta->toast_pointer.va_valueid,
1206+
chunk_seq));
11931207
return;
11941208
}
11951209
if (!VARATT_IS_EXTENDED(chunk))
@@ -1209,40 +1223,31 @@ check_toast_tuple(HeapTuple toasttup, HeapCheckContext *ctx,
12091223
report_toast_corruption(ctx, ta,
12101224
psprintf("toast value %u chunk %d has invalid varlena header %0x",
12111225
ta->toast_pointer.va_valueid,
1212-
chunkno, header));
1226+
chunk_seq, header));
12131227
return;
12141228
}
12151229

12161230
/*
12171231
* Some checks on the data we've found
12181232
*/
1219-
if (curchunk != chunkno)
1220-
{
1221-
report_toast_corruption(ctx, ta,
1222-
psprintf("toast value %u chunk %d has sequence number %d, but expected sequence number %d",
1223-
ta->toast_pointer.va_valueid,
1224-
chunkno, curchunk, chunkno));
1225-
return;
1226-
}
1227-
if (chunkno > endchunk)
1233+
if (chunk_seq > last_chunk_seq)
12281234
{
12291235
report_toast_corruption(ctx, ta,
12301236
psprintf("toast value %u chunk %d follows last expected chunk %d",
12311237
ta->toast_pointer.va_valueid,
1232-
chunkno, endchunk));
1238+
chunk_seq, last_chunk_seq));
12331239
return;
12341240
}
12351241

1236-
expected_size = curchunk < endchunk ? TOAST_MAX_CHUNK_SIZE
1237-
: VARATT_EXTERNAL_GET_EXTSIZE(ta->toast_pointer) - (endchunk * TOAST_MAX_CHUNK_SIZE);
1242+
expected_size = chunk_seq < last_chunk_seq ? TOAST_MAX_CHUNK_SIZE
1243+
: extsize - (last_chunk_seq * TOAST_MAX_CHUNK_SIZE);
12381244

12391245
if (chunksize != expected_size)
12401246
report_toast_corruption(ctx, ta,
12411247
psprintf("toast value %u chunk %d has size %u, but expected size %u",
12421248
ta->toast_pointer.va_valueid,
1243-
chunkno, chunksize, expected_size));
1249+
chunk_seq, chunksize, expected_size));
12441250
}
1245-
12461251
/*
12471252
* Check the current attribute as tracked in ctx, recording any corruption
12481253
* found in ctx->tupstore.
@@ -1436,10 +1441,12 @@ check_toasted_attribute(HeapCheckContext *ctx, ToastedAttribute *ta)
14361441
SysScanDesc toastscan;
14371442
bool found_toasttup;
14381443
HeapTuple toasttup;
1439-
int32 chunkno;
1440-
int32 endchunk;
1444+
uint32 extsize;
1445+
int32 expected_chunk_seq = 0;
1446+
int32 last_chunk_seq;
14411447

1442-
endchunk = (VARATT_EXTERNAL_GET_EXTSIZE(ta->toast_pointer) - 1) / TOAST_MAX_CHUNK_SIZE;
1448+
extsize = VARATT_EXTERNAL_GET_EXTSIZE(ta->toast_pointer);
1449+
last_chunk_seq = (extsize - 1) / TOAST_MAX_CHUNK_SIZE;
14431450

14441451
/*
14451452
* Setup a scan key to find chunks in toast table with matching va_valueid
@@ -1458,27 +1465,25 @@ check_toasted_attribute(HeapCheckContext *ctx, ToastedAttribute *ta)
14581465
ctx->valid_toast_index,
14591466
&SnapshotToast, 1,
14601467
&toastkey);
1461-
chunkno = 0;
14621468
found_toasttup = false;
14631469
while ((toasttup =
14641470
systable_getnext_ordered(toastscan,
14651471
ForwardScanDirection)) != NULL)
14661472
{
14671473
found_toasttup = true;
1468-
check_toast_tuple(toasttup, ctx, ta, chunkno, endchunk);
1469-
chunkno++;
1474+
check_toast_tuple(toasttup, ctx, ta, &expected_chunk_seq, extsize);
14701475
}
14711476
systable_endscan_ordered(toastscan);
14721477

14731478
if (!found_toasttup)
14741479
report_toast_corruption(ctx, ta,
14751480
psprintf("toast value %u not found in toast table",
14761481
ta->toast_pointer.va_valueid));
1477-
else if (chunkno != (endchunk + 1))
1482+
else if (expected_chunk_seq <= last_chunk_seq)
14781483
report_toast_corruption(ctx, ta,
1479-
psprintf("toast value %u was expected to end at chunk %d, but ended at chunk %d",
1484+
psprintf("toast value %u was expected to end at chunk %d, but ended while expecting chunk %d",
14801485
ta->toast_pointer.va_valueid,
1481-
(endchunk + 1), chunkno));
1486+
last_chunk_seq, expected_chunk_seq));
14821487
}
14831488

14841489
/*

0 commit comments

Comments
 (0)