26 #include "utils/fmgroids.h"
31 #define HEAPCHECK_RELATION_COLS 4
34 #define VARLENA_SIZE_LIMIT 0x3FFFFFFF
154 bool *xmin_commit_status_ok,
166 bool *xmin_commit_status_ok,
234 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
235 errmsg(
"relation cannot be null")));
240 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
241 errmsg(
"on_error_stop cannot be null")));
246 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
247 errmsg(
"check_toast cannot be null")));
252 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
253 errmsg(
"skip cannot be null")));
263 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
264 errmsg(
"invalid skip option"),
265 errhint(
"Valid skip options are \"all-visible\", \"all-frozen\", and \"none\".")));
269 ctx.toasted_attributes =
NIL;
295 if (!RELKIND_HAS_TABLE_AM(ctx.rel->rd_rel->relkind) &&
296 ctx.rel->rd_rel->relkind != RELKIND_SEQUENCE)
298 (
errcode(ERRCODE_WRONG_OBJECT_TYPE),
299 errmsg(
"cannot check relation \"%s\"",
307 if (ctx.rel->rd_rel->relkind != RELKIND_SEQUENCE &&
308 ctx.rel->rd_rel->relam != HEAP_TABLE_AM_OID)
310 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
311 errmsg(
"only heap AM is supported")));
318 if (ctx.rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
322 (
errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
323 errmsg(
"cannot verify unlogged relation \"%s\" during recovery, skipping",
348 if (fb < 0 || fb >= nblocks)
350 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
351 errmsg(
"starting block number must be between 0 and %u",
356 last_block = nblocks - 1;
361 if (lb < 0 || lb >= nblocks)
363 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
364 errmsg(
"ending block number must be between 0 and %u",
370 if (ctx.rel->rd_rel->reltoastrelid && check_toast)
375 ctx.toast_rel =
table_open(ctx.rel->rd_rel->reltoastrelid,
379 &(ctx.toast_indexes),
380 &(ctx.num_toast_indexes));
381 ctx.valid_toast_index = ctx.toast_indexes[offset];
389 ctx.toast_rel = NULL;
390 ctx.toast_indexes = NULL;
391 ctx.num_toast_indexes = 0;
396 ctx.relfrozenxid = ctx.rel->rd_rel->relfrozenxid;
398 ctx.relminmxid = ctx.rel->rd_rel->relminmxid;
401 ctx.oldest_xid = ctx.relfrozenxid;
403 for (ctx.blkno = first_block; ctx.blkno <= last_block; ctx.blkno++)
451 lp_valid[ctx.offnum] =
false;
452 xmin_commit_status_ok[ctx.offnum] =
false;
471 psprintf(
"line pointer redirection to item at offset %u precedes minimum offset %u",
476 if (rdoffnum > maxoff)
479 psprintf(
"line pointer redirection to item at offset %u exceeds maximum offset %u",
495 psprintf(
"redirected line pointer points to an unused item at offset %u",
496 (
unsigned) rdoffnum));
502 psprintf(
"redirected line pointer points to a dead item at offset %u",
503 (
unsigned) rdoffnum));
509 psprintf(
"redirected line pointer points to another redirected line pointer at offset %u",
510 (
unsigned) rdoffnum));
519 lp_valid[ctx.offnum] =
true;
520 successor[ctx.offnum] = rdoffnum;
528 if (ctx.lp_off !=
MAXALIGN(ctx.lp_off))
531 psprintf(
"line pointer to page offset %u is not maximally aligned",
538 psprintf(
"line pointer length %u is less than the minimum tuple header size %u",
543 if (ctx.lp_off + ctx.lp_len > BLCKSZ)
546 psprintf(
"line pointer to page offset %u with length %u ends beyond maximum page offset %u",
554 lp_valid[ctx.offnum] =
true;
560 &xmin_commit_status_ok[ctx.offnum],
561 &xmin_commit_status[ctx.offnum]);
570 if (nextblkno == ctx.blkno && nextoffnum != ctx.offnum &&
572 successor[ctx.offnum] = nextoffnum;
623 psprintf(
"redirected line pointer points to a non-heap-only tuple at offset %u",
624 (
unsigned) nextoffnum));
631 psprintf(
"redirect line pointer points to offset %u, but offset %u also points there",
632 (
unsigned) nextoffnum, (
unsigned) predecessor[nextoffnum]));
640 predecessor[nextoffnum] = ctx.offnum;
664 psprintf(
"tuple points to new version at offset %u, but offset %u also points there",
665 (
unsigned) nextoffnum, (
unsigned) predecessor[nextoffnum]));
673 predecessor[nextoffnum] = ctx.offnum;
688 psprintf(
"non-heap-only update produced a heap-only tuple at offset %u",
689 (
unsigned) nextoffnum));
695 psprintf(
"heap-only update produced a non-heap only tuple at offset %u",
696 (
unsigned) nextoffnum));
710 if (xmin_commit_status_ok[ctx.offnum] &&
712 xmin_commit_status_ok[nextoffnum] &&
717 psprintf(
"tuple with in-progress xmin %u was updated to produce a tuple at offset %u with committed xmin %u",
718 (
unsigned) curr_xmin,
719 (
unsigned) ctx.offnum,
720 (
unsigned) next_xmin));
727 if (xmin_commit_status_ok[ctx.offnum] &&
729 xmin_commit_status_ok[nextoffnum])
733 psprintf(
"tuple with aborted xmin %u was updated to produce a tuple at offset %u with in-progress xmin %u",
734 (
unsigned) curr_xmin,
735 (
unsigned) ctx.offnum,
736 (
unsigned) next_xmin));
739 psprintf(
"tuple with aborted xmin %u was updated to produce a tuple at offset %u with committed xmin %u",
740 (
unsigned) curr_xmin,
741 (
unsigned) ctx.offnum,
742 (
unsigned) next_xmin));
754 ctx.offnum <= maxoff;
757 if (xmin_commit_status_ok[ctx.offnum] &&
773 psprintf(
"tuple is root of chain but is marked as heap-only tuple"));
784 if (ctx.toasted_attributes !=
NIL)
788 foreach(cell, ctx.toasted_attributes)
791 ctx.toasted_attributes =
NIL;
794 if (on_error_stop && ctx.is_corrupt)
802 if (ctx.toast_indexes)
909 unsigned expected_hoff;
914 psprintf(
"data begins at offset %u beyond the tuple length %u",
923 pstrdup(
"multixact should not be marked committed"));
937 psprintf(
"tuple has been HOT updated, but xmax is 0"));
950 psprintf(
"tuple is heap only, but not the result of an update"));
963 psprintf(
"tuple data should begin at byte %u, but actually begins at byte %u (1 attribute, has nulls)",
967 psprintf(
"tuple data should begin at byte %u, but actually begins at byte %u (%u attributes, has nulls)",
969 else if (ctx->
natts == 1)
971 psprintf(
"tuple data should begin at byte %u, but actually begins at byte %u (1 attribute, no nulls)",
975 psprintf(
"tuple data should begin at byte %u, but actually begins at byte %u (%u attributes, no nulls)",
1028 *xmin_commit_status_ok =
false;
1038 *xmin_commit_status_ok =
true;
1039 *xmin_commit_status = xmin_status;
1043 psprintf(
"xmin %u equals or exceeds next valid transaction ID %u:%u",
1050 psprintf(
"xmin %u precedes oldest valid transaction ID %u:%u",
1057 psprintf(
"xmin %u precedes relation freeze threshold %u:%u",
1080 pstrdup(
"old-style VACUUM FULL transaction ID for moved off tuple is invalid"));
1084 psprintf(
"old-style VACUUM FULL transaction ID %u for moved off tuple equals or exceeds next valid transaction ID %u:%u",
1091 psprintf(
"old-style VACUUM FULL transaction ID %u for moved off tuple precedes relation freeze threshold %u:%u",
1098 psprintf(
"old-style VACUUM FULL transaction ID %u for moved off tuple precedes oldest valid transaction ID %u:%u",
1107 switch (xvac_status)
1111 psprintf(
"old-style VACUUM FULL transaction ID %u for moved off tuple matches our current transaction ID",
1116 psprintf(
"old-style VACUUM FULL transaction ID %u for moved off tuple appears to be in progress",
1149 pstrdup(
"old-style VACUUM FULL transaction ID for moved in tuple is invalid"));
1153 psprintf(
"old-style VACUUM FULL transaction ID %u for moved in tuple equals or exceeds next valid transaction ID %u:%u",
1160 psprintf(
"old-style VACUUM FULL transaction ID %u for moved in tuple precedes relation freeze threshold %u:%u",
1167 psprintf(
"old-style VACUUM FULL transaction ID %u for moved in tuple precedes oldest valid transaction ID %u:%u",
1176 switch (xvac_status)
1180 psprintf(
"old-style VACUUM FULL transaction ID %u for moved in tuple matches our current transaction ID",
1185 psprintf(
"old-style VACUUM FULL transaction ID %u for moved in tuple appears to be in progress",
1252 pstrdup(
"multitransaction ID is invalid"));
1256 psprintf(
"multitransaction ID %u precedes relation minimum multitransaction ID threshold %u",
1261 psprintf(
"multitransaction ID %u precedes oldest valid multitransaction ID threshold %u",
1266 psprintf(
"multitransaction ID %u equals or exceeds next valid multitransaction ID %u",
1310 pstrdup(
"update xid is invalid"));
1314 psprintf(
"update xid %u equals or exceeds next valid transaction ID %u:%u",
1321 psprintf(
"update xid %u precedes relation freeze threshold %u:%u",
1328 psprintf(
"update xid %u precedes oldest valid transaction ID %u:%u",
1337 switch (xmax_status)
1379 psprintf(
"xmax %u equals or exceeds next valid transaction ID %u:%u",
1386 psprintf(
"xmax %u precedes relation freeze threshold %u:%u",
1393 psprintf(
"xmax %u precedes oldest valid transaction ID %u:%u",
1406 switch (xmax_status)
1467 int32 expected_size;
1475 psprintf(
"toast value %u has toast chunk with null sequence number",
1479 if (chunk_seq != *expected_chunk_seq)
1483 psprintf(
"toast value %u index scan returned chunk %d when expecting chunk %d",
1485 chunk_seq, *expected_chunk_seq));
1487 *expected_chunk_seq = chunk_seq + 1;
1495 psprintf(
"toast value %u chunk %d has null data",
1515 psprintf(
"toast value %u chunk %d has invalid varlena header %0x",
1517 chunk_seq, header));
1524 if (chunk_seq > last_chunk_seq)
1527 psprintf(
"toast value %u chunk %d follows last expected chunk %d",
1529 chunk_seq, last_chunk_seq));
1536 if (chunksize != expected_size)
1538 psprintf(
"toast value %u chunk %d has size %u, but expected size %u",
1540 chunk_seq, chunksize, expected_size));
1581 psprintf(
"attribute with length %u starts at offset %u beyond total tuple length %u",
1593 if (thisatt->attlen != -1)
1601 psprintf(
"attribute with length %u ends at offset %u beyond total tuple length %u",
1633 psprintf(
"toasted attribute has unexpected TOAST tag %u",
1647 psprintf(
"attribute with length %u ends at offset %u beyond total tuple length %u",
1682 psprintf(
"toast value %u rawsize %d exceeds limit %d",
1710 psprintf(
"toast value %u has invalid compression method id %d",
1718 psprintf(
"toast value %u is external but tuple header flag HEAP_HASEXTERNAL not set",
1727 psprintf(
"toast value %u is external but relation has no toast relation",
1769 bool found_toasttup;
1772 int32 expected_chunk_seq = 0;
1773 int32 last_chunk_seq;
1795 found_toasttup =
false;
1800 found_toasttup =
true;
1805 if (!found_toasttup)
1807 psprintf(
"toast value %u not found in toast table",
1809 else if (expected_chunk_seq <= last_chunk_seq)
1811 psprintf(
"toast value %u was expected to end at chunk %d, but ended while expecting chunk %d",
1813 last_chunk_seq, expected_chunk_seq));
1840 xmin_commit_status))
1851 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(Page page, ItemId itemId)
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
static OffsetNumber PageGetMaxOffsetNumber(Page 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(HeapTupleHeader tuple)
#define TOAST_MAX_CHUNK_SIZE
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
HeapTupleHeaderData * HeapTupleHeader
#define HEAP_XMAX_IS_LOCKED_ONLY(infomask)
#define HeapTupleHeaderGetNatts(tup)
#define SizeofHeapTupleHeader
#define HeapTupleHeaderIsHeapOnly(tup)
#define HeapTupleHeaderGetXvac(tup)
#define HeapTupleHeaderGetXmin(tup)
#define HEAP_XMAX_IS_MULTI
#define HEAP_XMAX_COMMITTED
#define HEAP_XMAX_INVALID
#define HeapTupleHeaderXminCommitted(tup)
#define HeapTupleHeaderGetRawXmax(tup)
#define HeapTupleHeaderGetUpdateXid(tup)
static Datum fastgetattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
#define HeapTupleHeaderIsHotUpdated(tup)
#define HeapTupleHeaderXminInvalid(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
FormData_pg_attribute * Form_pg_attribute
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)
void init_toast_snapshot(Snapshot toast_snapshot)
int toast_open_indexes(Relation toastrel, LOCKMODE lock, Relation **toastidxs, int *num_indexes)
#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)
#define TupleDescAttr(tupdesc, i)
void tuplestore_puttuple(Tuplestorestate *state, HeapTuple tuple)
#define att_align_pointer(cur_offset, attalign, attlen, attptr)
#define att_align_nominal(cur_offset, attalign)
static bool att_isnull(int ATT, const bits8 *BITS)
#define att_addlength_pointer(cur_offset, 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)