Commit 50529e5b authored by Robert Haas's avatar Robert Haas

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
parent f68970e3
...@@ -150,8 +150,8 @@ typedef struct HeapCheckContext ...@@ -150,8 +150,8 @@ typedef struct HeapCheckContext
static void sanity_check_relation(Relation rel); static void sanity_check_relation(Relation rel);
static void check_tuple(HeapCheckContext *ctx); static void check_tuple(HeapCheckContext *ctx);
static void check_toast_tuple(HeapTuple toasttup, HeapCheckContext *ctx, static void check_toast_tuple(HeapTuple toasttup, HeapCheckContext *ctx,
ToastedAttribute *ta, int32 chunkno, ToastedAttribute *ta, int32 *expected_chunk_seq,
int32 endchunk); uint32 extsize);
static bool check_tuple_attribute(HeapCheckContext *ctx); static bool check_tuple_attribute(HeapCheckContext *ctx);
static void check_toasted_attribute(HeapCheckContext *ctx, static void check_toasted_attribute(HeapCheckContext *ctx,
...@@ -1159,23 +1159,25 @@ check_tuple_visibility(HeapCheckContext *ctx) ...@@ -1159,23 +1159,25 @@ check_tuple_visibility(HeapCheckContext *ctx)
* each toast tuple being checked against where we are in the sequence, as well * each toast tuple being checked against where we are in the sequence, as well
* as each toast tuple having its varlena structure sanity checked. * as each toast tuple having its varlena structure sanity checked.
* *
* Returns whether the toast tuple passed the corruption checks. * On entry, *expected_chunk_seq should be the chunk_seq value that we expect
* to find in toasttup. On exit, it will be updated to the value the next call
* to this function should expect to see.
*/ */
static void static void
check_toast_tuple(HeapTuple toasttup, HeapCheckContext *ctx, check_toast_tuple(HeapTuple toasttup, HeapCheckContext *ctx,
ToastedAttribute *ta, int32 chunkno, int32 endchunk) ToastedAttribute *ta, int32 *expected_chunk_seq,
uint32 extsize)
{ {
int32 curchunk; int32 chunk_seq;
int32 last_chunk_seq = (extsize - 1) / TOAST_MAX_CHUNK_SIZE;
Pointer chunk; Pointer chunk;
bool isnull; bool isnull;
int32 chunksize; int32 chunksize;
int32 expected_size; int32 expected_size;
/* /* Sanity-check the sequence number. */
* Have a chunk, extract the sequence number and the data chunk_seq = DatumGetInt32(fastgetattr(toasttup, 2,
*/ ctx->toast_rel->rd_att, &isnull));
curchunk = DatumGetInt32(fastgetattr(toasttup, 2,
ctx->toast_rel->rd_att, &isnull));
if (isnull) if (isnull)
{ {
report_toast_corruption(ctx, ta, report_toast_corruption(ctx, ta,
...@@ -1183,13 +1185,25 @@ check_toast_tuple(HeapTuple toasttup, HeapCheckContext *ctx, ...@@ -1183,13 +1185,25 @@ check_toast_tuple(HeapTuple toasttup, HeapCheckContext *ctx,
ta->toast_pointer.va_valueid)); ta->toast_pointer.va_valueid));
return; return;
} }
if (chunk_seq != *expected_chunk_seq)
{
/* Either the TOAST index is corrupt, or we don't have all chunks. */
report_toast_corruption(ctx, ta,
psprintf("toast value %u index scan returned chunk %d when expecting chunk %d",
ta->toast_pointer.va_valueid,
chunk_seq, *expected_chunk_seq));
}
*expected_chunk_seq = chunk_seq + 1;
/* Sanity-check the chunk data. */
chunk = DatumGetPointer(fastgetattr(toasttup, 3, chunk = DatumGetPointer(fastgetattr(toasttup, 3,
ctx->toast_rel->rd_att, &isnull)); ctx->toast_rel->rd_att, &isnull));
if (isnull) if (isnull)
{ {
report_toast_corruption(ctx, ta, report_toast_corruption(ctx, ta,
psprintf("toast value %u chunk %d has null data", psprintf("toast value %u chunk %d has null data",
ta->toast_pointer.va_valueid, chunkno)); ta->toast_pointer.va_valueid,
chunk_seq));
return; return;
} }
if (!VARATT_IS_EXTENDED(chunk)) if (!VARATT_IS_EXTENDED(chunk))
...@@ -1209,40 +1223,31 @@ check_toast_tuple(HeapTuple toasttup, HeapCheckContext *ctx, ...@@ -1209,40 +1223,31 @@ check_toast_tuple(HeapTuple toasttup, HeapCheckContext *ctx,
report_toast_corruption(ctx, ta, report_toast_corruption(ctx, ta,
psprintf("toast value %u chunk %d has invalid varlena header %0x", psprintf("toast value %u chunk %d has invalid varlena header %0x",
ta->toast_pointer.va_valueid, ta->toast_pointer.va_valueid,
chunkno, header)); chunk_seq, header));
return; return;
} }
/* /*
* Some checks on the data we've found * Some checks on the data we've found
*/ */
if (curchunk != chunkno) if (chunk_seq > last_chunk_seq)
{
report_toast_corruption(ctx, ta,
psprintf("toast value %u chunk %d has sequence number %d, but expected sequence number %d",
ta->toast_pointer.va_valueid,
chunkno, curchunk, chunkno));
return;
}
if (chunkno > endchunk)
{ {
report_toast_corruption(ctx, ta, report_toast_corruption(ctx, ta,
psprintf("toast value %u chunk %d follows last expected chunk %d", psprintf("toast value %u chunk %d follows last expected chunk %d",
ta->toast_pointer.va_valueid, ta->toast_pointer.va_valueid,
chunkno, endchunk)); chunk_seq, last_chunk_seq));
return; return;
} }
expected_size = curchunk < endchunk ? TOAST_MAX_CHUNK_SIZE expected_size = chunk_seq < last_chunk_seq ? TOAST_MAX_CHUNK_SIZE
: VARATT_EXTERNAL_GET_EXTSIZE(ta->toast_pointer) - (endchunk * TOAST_MAX_CHUNK_SIZE); : extsize - (last_chunk_seq * TOAST_MAX_CHUNK_SIZE);
if (chunksize != expected_size) if (chunksize != expected_size)
report_toast_corruption(ctx, ta, report_toast_corruption(ctx, ta,
psprintf("toast value %u chunk %d has size %u, but expected size %u", psprintf("toast value %u chunk %d has size %u, but expected size %u",
ta->toast_pointer.va_valueid, ta->toast_pointer.va_valueid,
chunkno, chunksize, expected_size)); chunk_seq, chunksize, expected_size));
} }
/* /*
* Check the current attribute as tracked in ctx, recording any corruption * Check the current attribute as tracked in ctx, recording any corruption
* found in ctx->tupstore. * found in ctx->tupstore.
...@@ -1436,10 +1441,12 @@ check_toasted_attribute(HeapCheckContext *ctx, ToastedAttribute *ta) ...@@ -1436,10 +1441,12 @@ check_toasted_attribute(HeapCheckContext *ctx, ToastedAttribute *ta)
SysScanDesc toastscan; SysScanDesc toastscan;
bool found_toasttup; bool found_toasttup;
HeapTuple toasttup; HeapTuple toasttup;
int32 chunkno; uint32 extsize;
int32 endchunk; int32 expected_chunk_seq = 0;
int32 last_chunk_seq;
endchunk = (VARATT_EXTERNAL_GET_EXTSIZE(ta->toast_pointer) - 1) / TOAST_MAX_CHUNK_SIZE; extsize = VARATT_EXTERNAL_GET_EXTSIZE(ta->toast_pointer);
last_chunk_seq = (extsize - 1) / TOAST_MAX_CHUNK_SIZE;
/* /*
* Setup a scan key to find chunks in toast table with matching va_valueid * Setup a scan key to find chunks in toast table with matching va_valueid
...@@ -1458,15 +1465,13 @@ check_toasted_attribute(HeapCheckContext *ctx, ToastedAttribute *ta) ...@@ -1458,15 +1465,13 @@ check_toasted_attribute(HeapCheckContext *ctx, ToastedAttribute *ta)
ctx->valid_toast_index, ctx->valid_toast_index,
&SnapshotToast, 1, &SnapshotToast, 1,
&toastkey); &toastkey);
chunkno = 0;
found_toasttup = false; found_toasttup = false;
while ((toasttup = while ((toasttup =
systable_getnext_ordered(toastscan, systable_getnext_ordered(toastscan,
ForwardScanDirection)) != NULL) ForwardScanDirection)) != NULL)
{ {
found_toasttup = true; found_toasttup = true;
check_toast_tuple(toasttup, ctx, ta, chunkno, endchunk); check_toast_tuple(toasttup, ctx, ta, &expected_chunk_seq, extsize);
chunkno++;
} }
systable_endscan_ordered(toastscan); systable_endscan_ordered(toastscan);
...@@ -1474,11 +1479,11 @@ check_toasted_attribute(HeapCheckContext *ctx, ToastedAttribute *ta) ...@@ -1474,11 +1479,11 @@ check_toasted_attribute(HeapCheckContext *ctx, ToastedAttribute *ta)
report_toast_corruption(ctx, ta, report_toast_corruption(ctx, ta,
psprintf("toast value %u not found in toast table", psprintf("toast value %u not found in toast table",
ta->toast_pointer.va_valueid)); ta->toast_pointer.va_valueid));
else if (chunkno != (endchunk + 1)) else if (expected_chunk_seq <= last_chunk_seq)
report_toast_corruption(ctx, ta, report_toast_corruption(ctx, ta,
psprintf("toast value %u was expected to end at chunk %d, but ended at chunk %d", psprintf("toast value %u was expected to end at chunk %d, but ended while expecting chunk %d",
ta->toast_pointer.va_valueid, ta->toast_pointer.va_valueid,
(endchunk + 1), chunkno)); last_chunk_seq, expected_chunk_seq));
} }
/* /*
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment