/* The number of columns in tuples returned by verify_heapam */
#define HEAPCHECK_RELATION_COLS 4
+/* The largest valid toast va_rawsize */
+#define VARLENA_SIZE_LIMIT 0x3FFFFFFF
+
/*
* Despite the name, we use this for reporting problems with both XIDs and
* MXIDs.
*/
VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
+ /* Toasted attributes too large to be untoasted should never be stored */
+ if (toast_pointer.va_rawsize > VARLENA_SIZE_LIMIT)
+ report_corruption(ctx,
+ psprintf("toast value %u rawsize %u exceeds limit %u",
+ toast_pointer.va_valueid,
+ toast_pointer.va_rawsize,
+ VARLENA_SIZE_LIMIT));
+
+ if (VARATT_IS_COMPRESSED(&toast_pointer))
+ {
+ ToastCompressionId cmid;
+ bool valid = false;
+
+ /* Compression should never expand the attribute */
+ if (VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer) > toast_pointer.va_rawsize - VARHDRSZ)
+ report_corruption(ctx,
+ psprintf("toast value %u external size %u exceeds maximum expected for rawsize %u",
+ toast_pointer.va_valueid,
+ VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer),
+ toast_pointer.va_rawsize));
+
+ /* Compressed attributes should have a valid compression method */
+ cmid = TOAST_COMPRESS_METHOD(&toast_pointer);
+ switch (cmid)
+ {
+ /* List of all valid compression method IDs */
+ case TOAST_PGLZ_COMPRESSION_ID:
+ case TOAST_LZ4_COMPRESSION_ID:
+ valid = true;
+ break;
+
+ /* Recognized but invalid compression method ID */
+ case TOAST_INVALID_COMPRESSION_ID:
+ break;
+
+ /* Intentionally no default here */
+ }
+ if (!valid)
+ report_corruption(ctx,
+ psprintf("toast value %u has invalid compression method id %d",
+ toast_pointer.va_valueid, cmid));
+ }
+
/* The tuple header better claim to contain toasted values */
if (!(infomask & HEAP_HASEXTERNAL))
{
my $relpath = "$pgdata/$rel";
# Insert data and freeze public.test
-use constant ROWCOUNT => 16;
+use constant ROWCOUNT => 18;
$node->safe_psql(
'postgres', qq(
INSERT INTO public.test (a, b, c)
$node->start;
# Ok, Xids and page layout look ok. We can run corruption tests.
-plan tests => 19;
+plan tests => 21;
# Check that pg_amcheck runs against the uncorrupted table without error.
$node->command_ok(
push @expected,
qr/${header}multitransaction ID 4 equals or exceeds next valid multitransaction ID 1/;
}
- elsif ($offnum == 15) # Last offnum must equal ROWCOUNT
+ elsif ($offnum == 15)
{
# Set both HEAP_XMAX_COMMITTED and HEAP_XMAX_IS_MULTI
$tup->{t_infomask} |= HEAP_XMAX_COMMITTED;
push @expected,
qr/${header}multitransaction ID 4000000000 precedes relation minimum multitransaction ID threshold 1/;
}
+ elsif ($offnum == 16)
+ {
+ # Set raw size too large
+ $tup->{c_va_rawsize} = 1073741824;
+
+ $header = header(0, $offnum, 2);
+ push @expected,
+ qr/${header}toast value \d+ rawsize 1073741824 exceeds limit 1073741823/;
+ }
+ elsif ($offnum == 17) # Last offnum should equal ROWCOUNT-1
+ {
+ # Set raw size too small.
+ $tup->{c_va_rawsize} = 9998;
+
+ $header = header(0, $offnum, 2);
+ push @expected,
+ qr/${header}toast value \d+ external size 10000 exceeds maximum expected for rawsize 9998/;
+ }
write_tuple($file, $offset, $tup);
}
close($file)