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)
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