29#include "utils/fmgroids.h"
35#define HEAPCHECK_RELATION_COLS 4
38#define VARLENA_SIZE_LIMIT 0x3FFFFFFF
158 bool *xmin_commit_status_ok,
170 bool *xmin_commit_status_ok,
238 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
239 errmsg(
"relation cannot be null")));
244 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
245 errmsg(
"on_error_stop cannot be null")));
250 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
251 errmsg(
"check_toast cannot be null")));
256 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
257 errmsg(
"skip cannot be null")));
267 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
268 errmsg(
"invalid skip option"),
269 errhint(
"Valid skip options are \"all-visible\", \"all-frozen\", and \"none\".")));
273 ctx.toasted_attributes =
NIL;
299 if (!RELKIND_HAS_TABLE_AM(ctx.rel->rd_rel->relkind) &&
300 ctx.rel->rd_rel->relkind != RELKIND_SEQUENCE)
302 (
errcode(ERRCODE_WRONG_OBJECT_TYPE),
303 errmsg(
"cannot check relation \"%s\"",
311 if (ctx.rel->rd_rel->relkind != RELKIND_SEQUENCE &&
312 ctx.rel->rd_rel->relam != HEAP_TABLE_AM_OID)
314 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
315 errmsg(
"only heap AM is supported")));
322 if (ctx.rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
326 (
errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
327 errmsg(
"cannot verify unlogged relation \"%s\" during recovery, skipping",
352 if (fb < 0 || fb >= nblocks)
354 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
355 errmsg(
"starting block number must be between 0 and %u",
360 last_block = nblocks - 1;
365 if (lb < 0 || lb >= nblocks)
367 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
368 errmsg(
"ending block number must be between 0 and %u",
374 if (ctx.rel->rd_rel->reltoastrelid && check_toast)
379 ctx.toast_rel =
table_open(ctx.rel->rd_rel->reltoastrelid,
383 &(ctx.toast_indexes),
384 &(ctx.num_toast_indexes));
385 ctx.valid_toast_index = ctx.toast_indexes[offset];
393 ctx.toast_rel = NULL;
394 ctx.toast_indexes = NULL;
395 ctx.num_toast_indexes = 0;
400 ctx.relfrozenxid = ctx.rel->rd_rel->relfrozenxid;
402 ctx.relminmxid = ctx.rel->rd_rel->relminmxid;
405 ctx.oldest_xid = ctx.relfrozenxid;
407 for (ctx.blkno = first_block; ctx.blkno <= last_block; ctx.blkno++)
455 lp_valid[ctx.offnum] =
false;
456 xmin_commit_status_ok[ctx.offnum] =
false;
475 psprintf(
"line pointer redirection to item at offset %u precedes minimum offset %u",
480 if (rdoffnum > maxoff)
483 psprintf(
"line pointer redirection to item at offset %u exceeds maximum offset %u",
499 psprintf(
"redirected line pointer points to an unused item at offset %u",
500 (
unsigned) rdoffnum));
506 psprintf(
"redirected line pointer points to a dead item at offset %u",
507 (
unsigned) rdoffnum));
513 psprintf(
"redirected line pointer points to another redirected line pointer at offset %u",
514 (
unsigned) rdoffnum));
523 lp_valid[ctx.offnum] =
true;
524 successor[ctx.offnum] = rdoffnum;
532 if (ctx.lp_off !=
MAXALIGN(ctx.lp_off))
535 psprintf(
"line pointer to page offset %u is not maximally aligned",
542 psprintf(
"line pointer length %u is less than the minimum tuple header size %u",
547 if (ctx.lp_off + ctx.lp_len > BLCKSZ)
550 psprintf(
"line pointer to page offset %u with length %u ends beyond maximum page offset %u",
558 lp_valid[ctx.offnum] =
true;
564 &xmin_commit_status_ok[ctx.offnum],
565 &xmin_commit_status[ctx.offnum]);
574 if (nextblkno == ctx.blkno && nextoffnum != ctx.offnum &&
576 successor[ctx.offnum] = nextoffnum;
627 psprintf(
"redirected line pointer points to a non-heap-only tuple at offset %u",
628 (
unsigned) nextoffnum));
635 psprintf(
"redirect line pointer points to offset %u, but offset %u also points there",
636 (
unsigned) nextoffnum, (
unsigned) predecessor[nextoffnum]));
644 predecessor[nextoffnum] = ctx.offnum;
668 psprintf(
"tuple points to new version at offset %u, but offset %u also points there",
669 (
unsigned) nextoffnum, (
unsigned) predecessor[nextoffnum]));
677 predecessor[nextoffnum] = ctx.offnum;
692 psprintf(
"non-heap-only update produced a heap-only tuple at offset %u",
693 (
unsigned) nextoffnum));
699 psprintf(
"heap-only update produced a non-heap only tuple at offset %u",
700 (
unsigned) nextoffnum));
714 if (xmin_commit_status_ok[ctx.offnum] &&
716 xmin_commit_status_ok[nextoffnum] &&
721 psprintf(
"tuple with in-progress xmin %u was updated to produce a tuple at offset %u with committed xmin %u",
722 (
unsigned) curr_xmin,
723 (
unsigned) ctx.offnum,
724 (
unsigned) next_xmin));
731 if (xmin_commit_status_ok[ctx.offnum] &&
733 xmin_commit_status_ok[nextoffnum])
737 psprintf(
"tuple with aborted xmin %u was updated to produce a tuple at offset %u with in-progress xmin %u",
738 (
unsigned) curr_xmin,
739 (
unsigned) ctx.offnum,
740 (
unsigned) next_xmin));
743 psprintf(
"tuple with aborted xmin %u was updated to produce a tuple at offset %u with committed xmin %u",
744 (
unsigned) curr_xmin,
745 (
unsigned) ctx.offnum,
746 (
unsigned) next_xmin));
758 ctx.offnum <= maxoff;
761 if (xmin_commit_status_ok[ctx.offnum] &&
777 psprintf(
"tuple is root of chain but is marked as heap-only tuple"));
788 if (ctx.toasted_attributes !=
NIL)
792 foreach(cell, ctx.toasted_attributes)
795 ctx.toasted_attributes =
NIL;
798 if (on_error_stop && ctx.is_corrupt)
806 if (ctx.toast_indexes)
913 unsigned expected_hoff;
918 psprintf(
"data begins at offset %u beyond the tuple length %u",
927 pstrdup(
"multixact should not be marked committed"));
941 psprintf(
"tuple has been HOT updated, but xmax is 0"));
954 psprintf(
"tuple is heap only, but not the result of an update"));
967 psprintf(
"tuple data should begin at byte %u, but actually begins at byte %u (1 attribute, has nulls)",
971 psprintf(
"tuple data should begin at byte %u, but actually begins at byte %u (%u attributes, has nulls)",
973 else if (ctx->
natts == 1)
975 psprintf(
"tuple data should begin at byte %u, but actually begins at byte %u (1 attribute, no nulls)",
979 psprintf(
"tuple data should begin at byte %u, but actually begins at byte %u (%u attributes, no nulls)",
1032 *xmin_commit_status_ok =
false;
1042 *xmin_commit_status_ok =
true;
1043 *xmin_commit_status = xmin_status;
1047 psprintf(
"xmin %u equals or exceeds next valid transaction ID %u:%u",
1054 psprintf(
"xmin %u precedes oldest valid transaction ID %u:%u",
1061 psprintf(
"xmin %u precedes relation freeze threshold %u:%u",
1084 pstrdup(
"old-style VACUUM FULL transaction ID for moved off tuple is invalid"));
1088 psprintf(
"old-style VACUUM FULL transaction ID %u for moved off tuple equals or exceeds next valid transaction ID %u:%u",
1095 psprintf(
"old-style VACUUM FULL transaction ID %u for moved off tuple precedes relation freeze threshold %u:%u",
1102 psprintf(
"old-style VACUUM FULL transaction ID %u for moved off tuple precedes oldest valid transaction ID %u:%u",
1111 switch (xvac_status)
1115 psprintf(
"old-style VACUUM FULL transaction ID %u for moved off tuple matches our current transaction ID",
1120 psprintf(
"old-style VACUUM FULL transaction ID %u for moved off tuple appears to be in progress",
1153 pstrdup(
"old-style VACUUM FULL transaction ID for moved in tuple is invalid"));
1157 psprintf(
"old-style VACUUM FULL transaction ID %u for moved in tuple equals or exceeds next valid transaction ID %u:%u",
1164 psprintf(
"old-style VACUUM FULL transaction ID %u for moved in tuple precedes relation freeze threshold %u:%u",
1171 psprintf(
"old-style VACUUM FULL transaction ID %u for moved in tuple precedes oldest valid transaction ID %u:%u",
1180 switch (xvac_status)
1184 psprintf(
"old-style VACUUM FULL transaction ID %u for moved in tuple matches our current transaction ID",
1189 psprintf(
"old-style VACUUM FULL transaction ID %u for moved in tuple appears to be in progress",
1256 pstrdup(
"multitransaction ID is invalid"));
1260 psprintf(
"multitransaction ID %u precedes relation minimum multitransaction ID threshold %u",
1265 psprintf(
"multitransaction ID %u precedes oldest valid multitransaction ID threshold %u",
1270 psprintf(
"multitransaction ID %u equals or exceeds next valid multitransaction ID %u",
1314 pstrdup(
"update xid is invalid"));
1318 psprintf(
"update xid %u equals or exceeds next valid transaction ID %u:%u",
1325 psprintf(
"update xid %u precedes relation freeze threshold %u:%u",
1332 psprintf(
"update xid %u precedes oldest valid transaction ID %u:%u",
1341 switch (xmax_status)
1383 psprintf(
"xmax %u equals or exceeds next valid transaction ID %u:%u",
1390 psprintf(
"xmax %u precedes relation freeze threshold %u:%u",
1397 psprintf(
"xmax %u precedes oldest valid transaction ID %u:%u",
1410 switch (xmax_status)
1471 int32 expected_size;
1479 psprintf(
"toast value %u has toast chunk with null sequence number",
1483 if (chunk_seq != *expected_chunk_seq)
1487 psprintf(
"toast value %u index scan returned chunk %d when expecting chunk %d",
1489 chunk_seq, *expected_chunk_seq));
1491 *expected_chunk_seq = chunk_seq + 1;
1499 psprintf(
"toast value %u chunk %d has null data",
1519 psprintf(
"toast value %u chunk %d has invalid varlena header %0x",
1521 chunk_seq, header));
1528 if (chunk_seq > last_chunk_seq)
1531 psprintf(
"toast value %u chunk %d follows last expected chunk %d",
1533 chunk_seq, last_chunk_seq));
1540 if (chunksize != expected_size)
1542 psprintf(
"toast value %u chunk %d has size %u, but expected size %u",
1544 chunk_seq, chunksize, expected_size));
1585 psprintf(
"attribute with length %u starts at offset %u beyond total tuple length %u",
1597 if (thisatt->
attlen != -1)
1605 psprintf(
"attribute with length %u ends at offset %u beyond total tuple length %u",
1637 psprintf(
"toasted attribute has unexpected TOAST tag %u",
1651 psprintf(
"attribute with length %u ends at offset %u beyond total tuple length %u",
1686 psprintf(
"toast value %u rawsize %d exceeds limit %d",
1714 psprintf(
"toast value %u has invalid compression method id %d",
1722 psprintf(
"toast value %u is external but tuple header flag HEAP_HASEXTERNAL not set",
1731 psprintf(
"toast value %u is external but relation has no toast relation",
1772 bool found_toasttup;
1775 int32 expected_chunk_seq = 0;
1776 int32 last_chunk_seq;
1797 found_toasttup =
false;
1802 found_toasttup =
true;
1807 if (!found_toasttup)
1809 psprintf(
"toast value %u not found in toast table",
1811 else if (expected_chunk_seq <= last_chunk_seq)
1813 psprintf(
"toast value %u was expected to end at chunk %d, but ended while expecting chunk %d",
1815 last_chunk_seq, expected_chunk_seq));
1842 xmin_commit_status))
1853 psprintf(
"number of attributes %u exceeds maximum expected for table %u",
static Datum values[MAXATTR]
void ReleaseBuffer(Buffer buffer)
void UnlockReleaseBuffer(Buffer buffer)
void LockBuffer(Buffer buffer, int mode)
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
#define BUFFER_LOCK_SHARE
#define RelationGetNumberOfBlocks(reln)
static Page BufferGetPage(Buffer buffer)
static Item PageGetItem(const PageData *page, const ItemIdData *itemId)
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
static OffsetNumber PageGetMaxOffsetNumber(const PageData *page)
#define CStringGetTextDatum(s)
#define Assert(condition)
TransactionId MultiXactId
#define VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr)
int errhint(const char *fmt,...)
int errcode(int sqlerrcode)
int errmsg(const char *fmt,...)
#define ereport(elevel,...)
Datum Int64GetDatum(int64 X)
#define PG_GETARG_TEXT_PP(n)
#define PG_GETARG_INT64(n)
#define PG_GETARG_BOOL(n)
BufferAccessStrategy GetAccessStrategy(BufferAccessStrategyType btype)
void InitMaterializedSRF(FunctionCallInfo fcinfo, bits32 flags)
SysScanDesc systable_beginscan_ordered(Relation heapRelation, Relation indexRelation, Snapshot snapshot, int nkeys, ScanKey key)
void systable_endscan_ordered(SysScanDesc sysscan)
HeapTuple systable_getnext_ordered(SysScanDesc sysscan, ScanDirection direction)
TransactionId HeapTupleGetUpdateXid(const HeapTupleHeaderData *tup)
#define TOAST_MAX_CHUNK_SIZE
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
HeapTupleHeaderData * HeapTupleHeader
#define HeapTupleHeaderGetNatts(tup)
#define SizeofHeapTupleHeader
static bool HEAP_XMAX_IS_LOCKED_ONLY(uint16 infomask)
static int BITMAPLEN(int NATTS)
static bool HeapTupleHeaderXminInvalid(const HeapTupleHeaderData *tup)
static TransactionId HeapTupleHeaderGetXvac(const HeapTupleHeaderData *tup)
static TransactionId HeapTupleHeaderGetRawXmax(const HeapTupleHeaderData *tup)
static bool HeapTupleHeaderIsHeapOnly(const HeapTupleHeaderData *tup)
#define HEAP_XMAX_IS_MULTI
#define HEAP_XMAX_COMMITTED
static TransactionId HeapTupleHeaderGetXmin(const HeapTupleHeaderData *tup)
static bool HeapTupleHeaderIsHotUpdated(const HeapTupleHeaderData *tup)
#define HEAP_XMAX_INVALID
static TransactionId HeapTupleHeaderGetUpdateXid(const HeapTupleHeaderData *tup)
static Datum fastgetattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
static bool HeapTupleHeaderXminCommitted(const HeapTupleHeaderData *tup)
if(TABLE==NULL||TABLE_index==NULL)
#define ItemIdGetLength(itemId)
#define ItemIdIsNormal(itemId)
#define ItemIdGetOffset(itemId)
#define ItemIdGetRedirect(itemId)
#define ItemIdIsDead(itemId)
#define ItemIdIsUsed(itemId)
#define ItemIdIsRedirected(itemId)
static OffsetNumber ItemPointerGetOffsetNumber(const ItemPointerData *pointer)
static BlockNumber ItemPointerGetBlockNumber(const ItemPointerData *pointer)
List * lappend(List *list, void *datum)
void list_free_deep(List *list)
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
void LWLockRelease(LWLock *lock)
char * pstrdup(const char *in)
void pfree(void *pointer)
void * palloc0(Size size)
#define CHECK_FOR_INTERRUPTS()
void ReadMultiXactIdRange(MultiXactId *oldest, MultiXactId *next)
bool MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2)
bool MultiXactIdPrecedesOrEquals(MultiXactId multi1, MultiXactId multi2)
#define InvalidOffsetNumber
#define OffsetNumberNext(offsetNumber)
#define FirstOffsetNumber
static const struct exclude_list_item skip[]
int errdetail_relkind_not_supported(char relkind)
int pg_strcasecmp(const char *s1, const char *s2)
static Datum ObjectIdGetDatum(Oid X)
static Pointer DatumGetPointer(Datum X)
static Datum Int32GetDatum(int32 X)
static int32 DatumGetInt32(Datum X)
bool TransactionIdIsInProgress(TransactionId xid)
char * psprintf(const char *fmt,...)
#define RelationGetDescr(relation)
#define RelationGetRelationName(relation)
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Snapshot GetTransactionSnapshot(void)
void relation_close(Relation relation, LOCKMODE lockmode)
Relation relation_open(Oid relationId, LOCKMODE lockmode)
#define BTEqualStrategyNumber
Tuplestorestate * tupstore
bool tuple_could_be_pruned
FullTransactionId oldest_fxid
BufferAccessStrategy bstrategy
TransactionId relfrozenxid
FullTransactionId next_fxid
List * toasted_attributes
FullTransactionId relfrozenfxid
Relation valid_toast_index
XidCommitStatus cached_status
Tuplestorestate * setResult
struct varatt_external toast_pointer
FullTransactionId nextXid
TransactionId oldestClogXid
void table_close(Relation relation, LOCKMODE lockmode)
Relation table_open(Oid relationId, LOCKMODE lockmode)
@ TOAST_INVALID_COMPRESSION_ID
@ TOAST_LZ4_COMPRESSION_ID
@ TOAST_PGLZ_COMPRESSION_ID
void toast_close_indexes(Relation *toastidxs, int num_indexes, LOCKMODE lock)
int toast_open_indexes(Relation toastrel, LOCKMODE lock, Relation **toastidxs, int *num_indexes)
Snapshot get_toast_snapshot(void)
#define TOAST_COMPRESS_METHOD(ptr)
bool TransactionIdDidCommit(TransactionId transactionId)
bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
#define FullTransactionIdIsNormal(x)
#define FrozenTransactionId
#define InvalidTransactionId
#define FullTransactionIdPrecedesOrEquals(a, b)
#define EpochFromFullTransactionId(x)
#define U64FromFullTransactionId(x)
static FullTransactionId FullTransactionIdFromU64(uint64 value)
#define TransactionIdEquals(id1, id2)
#define BootstrapTransactionId
#define XidFromFullTransactionId(x)
#define FirstNormalTransactionId
#define TransactionIdIsValid(xid)
static FullTransactionId FullTransactionIdFromEpochAndXid(uint32 epoch, TransactionId xid)
#define TransactionIdIsNormal(xid)
#define FirstNormalFullTransactionId
#define FullTransactionIdPrecedes(a, b)
static CompactAttribute * TupleDescCompactAttr(TupleDesc tupdesc, int i)
void tuplestore_puttuple(Tuplestorestate *state, HeapTuple tuple)
#define att_nominal_alignby(cur_offset, attalignby)
static bool att_isnull(int ATT, const bits8 *BITS)
#define att_addlength_pointer(cur_offset, attlen, attptr)
#define att_pointer_alignby(cur_offset, attalignby, attlen, attptr)
#define VARSIZE_SHORT(PTR)
#define VARATT_IS_EXTENDED(PTR)
#define VARATT_IS_SHORT(PTR)
#define VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer)
#define VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer)
#define VARTAG_EXTERNAL(PTR)
#define VARATT_IS_EXTERNAL(PTR)
char * text_to_cstring(const text *t)
TransamVariablesData * TransamVariables
static XidBoundsViolation check_mxid_valid_in_rel(MultiXactId mxid, HeapCheckContext *ctx)
static void check_toasted_attribute(HeapCheckContext *ctx, ToastedAttribute *ta)
PG_FUNCTION_INFO_V1(verify_heapam)
#define VARLENA_SIZE_LIMIT
static XidBoundsViolation get_xid_status(TransactionId xid, HeapCheckContext *ctx, XidCommitStatus *status)
@ XID_PRECEDES_CLUSTERMIN
static void report_corruption_internal(Tuplestorestate *tupstore, TupleDesc tupdesc, BlockNumber blkno, OffsetNumber offnum, AttrNumber attnum, char *msg)
static bool check_tuple_visibility(HeapCheckContext *ctx, bool *xmin_commit_status_ok, XidCommitStatus *xmin_commit_status)
#define HEAPCHECK_RELATION_COLS
struct HeapCheckContext HeapCheckContext
static void check_toast_tuple(HeapTuple toasttup, HeapCheckContext *ctx, ToastedAttribute *ta, int32 *expected_chunk_seq, uint32 extsize)
static bool fxid_in_cached_range(FullTransactionId fxid, const HeapCheckContext *ctx)
static XidBoundsViolation check_mxid_in_range(MultiXactId mxid, HeapCheckContext *ctx)
static void update_cached_mxid_range(HeapCheckContext *ctx)
static void update_cached_xid_range(HeapCheckContext *ctx)
static FullTransactionId FullTransactionIdFromXidAndCtx(TransactionId xid, const HeapCheckContext *ctx)
static void check_tuple(HeapCheckContext *ctx, bool *xmin_commit_status_ok, XidCommitStatus *xmin_commit_status)
struct ToastedAttribute ToastedAttribute
static bool check_tuple_header(HeapCheckContext *ctx)
static void report_toast_corruption(HeapCheckContext *ctx, ToastedAttribute *ta, char *msg)
static bool check_tuple_attribute(HeapCheckContext *ctx)
static void report_corruption(HeapCheckContext *ctx, char *msg)
Datum verify_heapam(PG_FUNCTION_ARGS)
uint8 visibilitymap_get_status(Relation rel, BlockNumber heapBlk, Buffer *vmbuf)
#define VISIBILITYMAP_ALL_FROZEN
#define VISIBILITYMAP_ALL_VISIBLE
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
bool RecoveryInProgress(void)