@@ -150,8 +150,8 @@ typedef struct HeapCheckContext
150
150
static void sanity_check_relation (Relation rel );
151
151
static void check_tuple (HeapCheckContext * ctx );
152
152
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 );
155
155
156
156
static bool check_tuple_attribute (HeapCheckContext * ctx );
157
157
static void check_toasted_attribute (HeapCheckContext * ctx ,
@@ -1159,37 +1159,51 @@ check_tuple_visibility(HeapCheckContext *ctx)
1159
1159
* each toast tuple being checked against where we are in the sequence, as well
1160
1160
* as each toast tuple having its varlena structure sanity checked.
1161
1161
*
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.
1163
1165
*/
1164
1166
static void
1165
1167
check_toast_tuple (HeapTuple toasttup , HeapCheckContext * ctx ,
1166
- ToastedAttribute * ta , int32 chunkno , int32 endchunk )
1168
+ ToastedAttribute * ta , int32 * expected_chunk_seq ,
1169
+ uint32 extsize )
1167
1170
{
1168
- int32 curchunk ;
1171
+ int32 chunk_seq ;
1172
+ int32 last_chunk_seq = (extsize - 1 ) / TOAST_MAX_CHUNK_SIZE ;
1169
1173
Pointer chunk ;
1170
1174
bool isnull ;
1171
1175
int32 chunksize ;
1172
1176
int32 expected_size ;
1173
1177
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 ));
1179
1181
if (isnull )
1180
1182
{
1181
1183
report_toast_corruption (ctx , ta ,
1182
1184
psprintf ("toast value %u has toast chunk with null sequence number" ,
1183
1185
ta -> toast_pointer .va_valueid ));
1184
1186
return ;
1185
1187
}
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. */
1186
1199
chunk = DatumGetPointer (fastgetattr (toasttup , 3 ,
1187
1200
ctx -> toast_rel -> rd_att , & isnull ));
1188
1201
if (isnull )
1189
1202
{
1190
1203
report_toast_corruption (ctx , ta ,
1191
1204
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 ));
1193
1207
return ;
1194
1208
}
1195
1209
if (!VARATT_IS_EXTENDED (chunk ))
@@ -1209,40 +1223,31 @@ check_toast_tuple(HeapTuple toasttup, HeapCheckContext *ctx,
1209
1223
report_toast_corruption (ctx , ta ,
1210
1224
psprintf ("toast value %u chunk %d has invalid varlena header %0x" ,
1211
1225
ta -> toast_pointer .va_valueid ,
1212
- chunkno , header ));
1226
+ chunk_seq , header ));
1213
1227
return ;
1214
1228
}
1215
1229
1216
1230
/*
1217
1231
* Some checks on the data we've found
1218
1232
*/
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 )
1228
1234
{
1229
1235
report_toast_corruption (ctx , ta ,
1230
1236
psprintf ("toast value %u chunk %d follows last expected chunk %d" ,
1231
1237
ta -> toast_pointer .va_valueid ,
1232
- chunkno , endchunk ));
1238
+ chunk_seq , last_chunk_seq ));
1233
1239
return ;
1234
1240
}
1235
1241
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 );
1238
1244
1239
1245
if (chunksize != expected_size )
1240
1246
report_toast_corruption (ctx , ta ,
1241
1247
psprintf ("toast value %u chunk %d has size %u, but expected size %u" ,
1242
1248
ta -> toast_pointer .va_valueid ,
1243
- chunkno , chunksize , expected_size ));
1249
+ chunk_seq , chunksize , expected_size ));
1244
1250
}
1245
-
1246
1251
/*
1247
1252
* Check the current attribute as tracked in ctx, recording any corruption
1248
1253
* found in ctx->tupstore.
@@ -1436,10 +1441,12 @@ check_toasted_attribute(HeapCheckContext *ctx, ToastedAttribute *ta)
1436
1441
SysScanDesc toastscan ;
1437
1442
bool found_toasttup ;
1438
1443
HeapTuple toasttup ;
1439
- int32 chunkno ;
1440
- int32 endchunk ;
1444
+ uint32 extsize ;
1445
+ int32 expected_chunk_seq = 0 ;
1446
+ int32 last_chunk_seq ;
1441
1447
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 ;
1443
1450
1444
1451
/*
1445
1452
* 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)
1458
1465
ctx -> valid_toast_index ,
1459
1466
& SnapshotToast , 1 ,
1460
1467
& toastkey );
1461
- chunkno = 0 ;
1462
1468
found_toasttup = false;
1463
1469
while ((toasttup =
1464
1470
systable_getnext_ordered (toastscan ,
1465
1471
ForwardScanDirection )) != NULL )
1466
1472
{
1467
1473
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 );
1470
1475
}
1471
1476
systable_endscan_ordered (toastscan );
1472
1477
1473
1478
if (!found_toasttup )
1474
1479
report_toast_corruption (ctx , ta ,
1475
1480
psprintf ("toast value %u not found in toast table" ,
1476
1481
ta -> toast_pointer .va_valueid ));
1477
- else if (chunkno != ( endchunk + 1 ) )
1482
+ else if (expected_chunk_seq <= last_chunk_seq )
1478
1483
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" ,
1480
1485
ta -> toast_pointer .va_valueid ,
1481
- ( endchunk + 1 ), chunkno ));
1486
+ last_chunk_seq , expected_chunk_seq ));
1482
1487
}
1483
1488
1484
1489
/*
0 commit comments