mirror of
https://github.com/postgres/postgres.git
synced 2025-07-27 12:41:57 +03:00
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
This commit is contained in:
@ -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,22 +1159,24 @@ 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,
|
||||||
*/
|
|
||||||
curchunk = DatumGetInt32(fastgetattr(toasttup, 2,
|
|
||||||
ctx->toast_rel->rd_att, &isnull));
|
ctx->toast_rel->rd_att, &isnull));
|
||||||
if (isnull)
|
if (isnull)
|
||||||
{
|
{
|
||||||
@ -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,
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Reference in New Issue
Block a user