PostgreSQL Source Code  git master
verify_heapam.c File Reference
#include "postgres.h"
#include "access/detoast.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "access/heaptoast.h"
#include "access/multixact.h"
#include "access/toast_internals.h"
#include "access/visibilitymap.h"
#include "catalog/pg_am.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "storage/bufmgr.h"
#include "storage/procarray.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
Include dependency graph for verify_heapam.c:

Go to the source code of this file.

Data Structures

struct  HeapCheckContext
 

Macros

#define HEAPCHECK_RELATION_COLS   4
 

Typedefs

typedef enum XidBoundsViolation XidBoundsViolation
 
typedef enum XidCommitStatus XidCommitStatus
 
typedef enum SkipPages SkipPages
 
typedef struct HeapCheckContext HeapCheckContext
 

Enumerations

enum  XidBoundsViolation {
  XID_INVALID, XID_IN_FUTURE, XID_PRECEDES_CLUSTERMIN, XID_PRECEDES_RELMIN,
  XID_BOUNDS_OK
}
 
enum  XidCommitStatus { XID_COMMITTED, XID_IN_PROGRESS, XID_ABORTED }
 
enum  SkipPages { SKIP_PAGES_ALL_FROZEN, SKIP_PAGES_ALL_VISIBLE, SKIP_PAGES_NONE }
 

Functions

 PG_FUNCTION_INFO_V1 (verify_heapam)
 
static void sanity_check_relation (Relation rel)
 
static void check_tuple (HeapCheckContext *ctx)
 
static void check_toast_tuple (HeapTuple toasttup, HeapCheckContext *ctx)
 
static bool check_tuple_attribute (HeapCheckContext *ctx)
 
static bool check_tuple_header_and_visibilty (HeapTupleHeader tuphdr, HeapCheckContext *ctx)
 
static void report_corruption (HeapCheckContext *ctx, char *msg)
 
static TupleDesc verify_heapam_tupdesc (void)
 
static FullTransactionId FullTransactionIdFromXidAndCtx (TransactionId xid, const HeapCheckContext *ctx)
 
static void update_cached_xid_range (HeapCheckContext *ctx)
 
static void update_cached_mxid_range (HeapCheckContext *ctx)
 
static XidBoundsViolation check_mxid_in_range (MultiXactId mxid, HeapCheckContext *ctx)
 
static XidBoundsViolation check_mxid_valid_in_rel (MultiXactId mxid, HeapCheckContext *ctx)
 
static XidBoundsViolation get_xid_status (TransactionId xid, HeapCheckContext *ctx, XidCommitStatus *status)
 
Datum verify_heapam (PG_FUNCTION_ARGS)
 
static bool fxid_in_cached_range (FullTransactionId fxid, const HeapCheckContext *ctx)
 

Macro Definition Documentation

◆ HEAPCHECK_RELATION_COLS

#define HEAPCHECK_RELATION_COLS   4

Definition at line 31 of file verify_heapam.c.

Referenced by report_corruption(), and verify_heapam_tupdesc().

Typedef Documentation

◆ HeapCheckContext

◆ SkipPages

typedef enum SkipPages SkipPages

◆ XidBoundsViolation

◆ XidCommitStatus

Enumeration Type Documentation

◆ SkipPages

enum SkipPages
Enumerator
SKIP_PAGES_ALL_FROZEN 
SKIP_PAGES_ALL_VISIBLE 
SKIP_PAGES_NONE 

Definition at line 53 of file verify_heapam.c.

◆ XidBoundsViolation

Enumerator
XID_INVALID 
XID_IN_FUTURE 
XID_PRECEDES_CLUSTERMIN 
XID_PRECEDES_RELMIN 
XID_BOUNDS_OK 

Definition at line 37 of file verify_heapam.c.

◆ XidCommitStatus

Enumerator
XID_COMMITTED 
XID_IN_PROGRESS 
XID_ABORTED 

Definition at line 46 of file verify_heapam.c.

Function Documentation

◆ check_mxid_in_range()

static XidBoundsViolation check_mxid_in_range ( MultiXactId  mxid,
HeapCheckContext ctx 
)
static

Definition at line 1342 of file verify_heapam.c.

References MultiXactIdPrecedes(), MultiXactIdPrecedesOrEquals(), HeapCheckContext::next_mxact, HeapCheckContext::oldest_mxact, HeapCheckContext::relminmxid, TransactionIdIsValid, XID_BOUNDS_OK, XID_IN_FUTURE, XID_INVALID, XID_PRECEDES_CLUSTERMIN, and XID_PRECEDES_RELMIN.

Referenced by check_mxid_valid_in_rel().

1343 {
1344  if (!TransactionIdIsValid(mxid))
1345  return XID_INVALID;
1346  if (MultiXactIdPrecedes(mxid, ctx->relminmxid))
1347  return XID_PRECEDES_RELMIN;
1348  if (MultiXactIdPrecedes(mxid, ctx->oldest_mxact))
1349  return XID_PRECEDES_CLUSTERMIN;
1350  if (MultiXactIdPrecedesOrEquals(ctx->next_mxact, mxid))
1351  return XID_IN_FUTURE;
1352  return XID_BOUNDS_OK;
1353 }
MultiXactId oldest_mxact
Definition: verify_heapam.c:80
MultiXactId next_mxact
Definition: verify_heapam.c:79
bool MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2)
Definition: multixact.c:3156
bool MultiXactIdPrecedesOrEquals(MultiXactId multi1, MultiXactId multi2)
Definition: multixact.c:3170
#define TransactionIdIsValid(xid)
Definition: transam.h:41
TransactionId relminmxid
Definition: verify_heapam.c:92

◆ check_mxid_valid_in_rel()

static XidBoundsViolation check_mxid_valid_in_rel ( MultiXactId  mxid,
HeapCheckContext ctx 
)
static

Definition at line 1364 of file verify_heapam.c.

References check_mxid_in_range(), update_cached_mxid_range(), and XID_BOUNDS_OK.

Referenced by check_tuple().

1365 {
1366  XidBoundsViolation result;
1367 
1368  result = check_mxid_in_range(mxid, ctx);
1369  if (result == XID_BOUNDS_OK)
1370  return XID_BOUNDS_OK;
1371 
1372  /* The range may have advanced. Recheck. */
1374  return check_mxid_in_range(mxid, ctx);
1375 }
static XidBoundsViolation check_mxid_in_range(MultiXactId mxid, HeapCheckContext *ctx)
static void update_cached_mxid_range(HeapCheckContext *ctx)
XidBoundsViolation
Definition: verify_heapam.c:37

◆ check_toast_tuple()

static void check_toast_tuple ( HeapTuple  toasttup,
HeapCheckContext ctx 
)
static

Definition at line 829 of file verify_heapam.c.

References HeapCheckContext::attrsize, HeapCheckContext::chunkno, DatumGetInt32, DatumGetPointer, HeapCheckContext::endchunk, fastgetattr, header(), psprintf(), pstrdup(), RelationData::rd_att, report_corruption(), TOAST_MAX_CHUNK_SIZE, HeapCheckContext::toast_rel, HeapCheckContext::totalchunks, VARATT_IS_EXTENDED, VARATT_IS_SHORT, VARHDRSZ, VARHDRSZ_SHORT, VARSIZE, and VARSIZE_SHORT.

Referenced by check_tuple_attribute().

830 {
831  int32 curchunk;
832  Pointer chunk;
833  bool isnull;
834  int32 chunksize;
835  int32 expected_size;
836 
837  /*
838  * Have a chunk, extract the sequence number and the data
839  */
840  curchunk = DatumGetInt32(fastgetattr(toasttup, 2,
841  ctx->toast_rel->rd_att, &isnull));
842  if (isnull)
843  {
844  report_corruption(ctx,
845  pstrdup("toast chunk sequence number is null"));
846  return;
847  }
848  chunk = DatumGetPointer(fastgetattr(toasttup, 3,
849  ctx->toast_rel->rd_att, &isnull));
850  if (isnull)
851  {
852  report_corruption(ctx,
853  pstrdup("toast chunk data is null"));
854  return;
855  }
856  if (!VARATT_IS_EXTENDED(chunk))
857  chunksize = VARSIZE(chunk) - VARHDRSZ;
858  else if (VARATT_IS_SHORT(chunk))
859  {
860  /*
861  * could happen due to heap_form_tuple doing its thing
862  */
863  chunksize = VARSIZE_SHORT(chunk) - VARHDRSZ_SHORT;
864  }
865  else
866  {
867  /* should never happen */
868  uint32 header = ((varattrib_4b *) chunk)->va_4byte.va_header;
869 
870  report_corruption(ctx,
871  psprintf("corrupt extended toast chunk has invalid varlena header: %0x (sequence number %d)",
872  header, curchunk));
873  return;
874  }
875 
876  /*
877  * Some checks on the data we've found
878  */
879  if (curchunk != ctx->chunkno)
880  {
881  report_corruption(ctx,
882  psprintf("toast chunk sequence number %u does not match the expected sequence number %u",
883  curchunk, ctx->chunkno));
884  return;
885  }
886  if (curchunk > ctx->endchunk)
887  {
888  report_corruption(ctx,
889  psprintf("toast chunk sequence number %u exceeds the end chunk sequence number %u",
890  curchunk, ctx->endchunk));
891  return;
892  }
893 
894  expected_size = curchunk < ctx->totalchunks - 1 ? TOAST_MAX_CHUNK_SIZE
895  : ctx->attrsize - ((ctx->totalchunks - 1) * TOAST_MAX_CHUNK_SIZE);
896  if (chunksize != expected_size)
897  {
898  report_corruption(ctx,
899  psprintf("toast chunk size %u differs from the expected size %u",
900  chunksize, expected_size));
901  return;
902  }
903 }
#define fastgetattr(tup, attnum, tupleDesc, isnull)
Definition: htup_details.h:712
#define TOAST_MAX_CHUNK_SIZE
Definition: heaptoast.h:84
#define DatumGetInt32(X)
Definition: postgres.h:472
#define VARHDRSZ_SHORT
Definition: postgres.h:268
#define VARSIZE(PTR)
Definition: postgres.h:303
Relation toast_rel
Definition: verify_heapam.c:93
#define VARHDRSZ
Definition: c.h:627
char * pstrdup(const char *in)
Definition: mcxt.c:1187
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
static void report_corruption(HeapCheckContext *ctx, char *msg)
signed int int32
Definition: c.h:429
char * Pointer
Definition: c.h:418
#define VARATT_IS_SHORT(PTR)
Definition: postgres.h:326
unsigned int uint32
Definition: c.h:441
#define VARSIZE_SHORT(PTR)
Definition: postgres.h:305
TupleDesc rd_att
Definition: rel.h:111
static void header(const char *fmt,...) pg_attribute_printf(1
Definition: pg_regress.c:210
#define VARATT_IS_EXTENDED(PTR)
Definition: postgres.h:327
#define DatumGetPointer(X)
Definition: postgres.h:549

◆ check_tuple()

static void check_tuple ( HeapCheckContext ctx)
static

Definition at line 1121 of file verify_heapam.c.

References HeapCheckContext::attnum, check_mxid_valid_in_rel(), check_tuple_attribute(), check_tuple_header_and_visibilty(), EpochFromFullTransactionId, fatal, get_xid_status(), HEAP_XMAX_IS_MULTI, HeapTupleHeaderGetRawXmax, HeapTupleHeaderGetXmin, HeapCheckContext::natts, HeapCheckContext::next_fxid, HeapCheckContext::next_mxact, HeapCheckContext::offset, HeapCheckContext::oldest_fxid, HeapCheckContext::oldest_mxact, psprintf(), pstrdup(), HeapCheckContext::rel, RelationGetDescr, HeapCheckContext::relfrozenfxid, HeapCheckContext::relminmxid, report_corruption(), HeapTupleHeaderData::t_infomask, HeapCheckContext::tuphdr, XID_BOUNDS_OK, XID_IN_FUTURE, XID_INVALID, XID_PRECEDES_CLUSTERMIN, XID_PRECEDES_RELMIN, and XidFromFullTransactionId.

Referenced by verify_heapam().

1122 {
1123  TransactionId xmin;
1124  TransactionId xmax;
1125  bool fatal = false;
1126  uint16 infomask = ctx->tuphdr->t_infomask;
1127 
1128  /* If xmin is normal, it should be within valid range */
1129  xmin = HeapTupleHeaderGetXmin(ctx->tuphdr);
1130  switch (get_xid_status(xmin, ctx, NULL))
1131  {
1132  case XID_INVALID:
1133  case XID_BOUNDS_OK:
1134  break;
1135  case XID_IN_FUTURE:
1136  report_corruption(ctx,
1137  psprintf("xmin %u equals or exceeds next valid transaction ID %u:%u",
1138  xmin,
1141  fatal = true;
1142  break;
1144  report_corruption(ctx,
1145  psprintf("xmin %u precedes oldest valid transaction ID %u:%u",
1146  xmin,
1149  fatal = true;
1150  break;
1151  case XID_PRECEDES_RELMIN:
1152  report_corruption(ctx,
1153  psprintf("xmin %u precedes relation freeze threshold %u:%u",
1154  xmin,
1157  fatal = true;
1158  break;
1159  }
1160 
1161  xmax = HeapTupleHeaderGetRawXmax(ctx->tuphdr);
1162 
1163  if (infomask & HEAP_XMAX_IS_MULTI)
1164  {
1165  /* xmax is a multixact, so it should be within valid MXID range */
1166  switch (check_mxid_valid_in_rel(xmax, ctx))
1167  {
1168  case XID_INVALID:
1169  report_corruption(ctx,
1170  pstrdup("multitransaction ID is invalid"));
1171  fatal = true;
1172  break;
1173  case XID_PRECEDES_RELMIN:
1174  report_corruption(ctx,
1175  psprintf("multitransaction ID %u precedes relation minimum multitransaction ID threshold %u",
1176  xmax, ctx->relminmxid));
1177  fatal = true;
1178  break;
1180  report_corruption(ctx,
1181  psprintf("multitransaction ID %u precedes oldest valid multitransaction ID threshold %u",
1182  xmax, ctx->oldest_mxact));
1183  fatal = true;
1184  break;
1185  case XID_IN_FUTURE:
1186  report_corruption(ctx,
1187  psprintf("multitransaction ID %u equals or exceeds next valid multitransaction ID %u",
1188  xmax,
1189  ctx->next_mxact));
1190  fatal = true;
1191  break;
1192  case XID_BOUNDS_OK:
1193  break;
1194  }
1195  }
1196  else
1197  {
1198  /*
1199  * xmax is not a multixact and is normal, so it should be within the
1200  * valid XID range.
1201  */
1202  switch (get_xid_status(xmax, ctx, NULL))
1203  {
1204  case XID_INVALID:
1205  case XID_BOUNDS_OK:
1206  break;
1207  case XID_IN_FUTURE:
1208  report_corruption(ctx,
1209  psprintf("xmax %u equals or exceeds next valid transaction ID %u:%u",
1210  xmax,
1213  fatal = true;
1214  break;
1216  report_corruption(ctx,
1217  psprintf("xmax %u precedes oldest valid transaction ID %u:%u",
1218  xmax,
1221  fatal = true;
1222  break;
1223  case XID_PRECEDES_RELMIN:
1224  report_corruption(ctx,
1225  psprintf("xmax %u precedes relation freeze threshold %u:%u",
1226  xmax,
1229  fatal = true;
1230  }
1231  }
1232 
1233  /*
1234  * Cannot process tuple data if tuple header was corrupt, as the offsets
1235  * within the page cannot be trusted, leaving too much risk of reading
1236  * garbage if we continue.
1237  *
1238  * We also cannot process the tuple if the xmin or xmax were invalid
1239  * relative to relfrozenxid or relminmxid, as clog entries for the xids
1240  * may already be gone.
1241  */
1242  if (fatal)
1243  return;
1244 
1245  /*
1246  * Check various forms of tuple header corruption. If the header is too
1247  * corrupt to continue checking, or if the tuple is not visible to anyone,
1248  * we cannot continue with other checks.
1249  */
1250  if (!check_tuple_header_and_visibilty(ctx->tuphdr, ctx))
1251  return;
1252 
1253  /*
1254  * The tuple is visible, so it must be compatible with the current version
1255  * of the relation descriptor. It might have fewer columns than are
1256  * present in the relation descriptor, but it cannot have more.
1257  */
1258  if (RelationGetDescr(ctx->rel)->natts < ctx->natts)
1259  {
1260  report_corruption(ctx,
1261  psprintf("number of attributes %u exceeds maximum expected for table %u",
1262  ctx->natts,
1263  RelationGetDescr(ctx->rel)->natts));
1264  return;
1265  }
1266 
1267  /*
1268  * Check each attribute unless we hit corruption that confuses what to do
1269  * next, at which point we abort further attribute checks for this tuple.
1270  * Note that we don't abort for all types of corruption, only for those
1271  * types where we don't know how to continue.
1272  */
1273  ctx->offset = 0;
1274  for (ctx->attnum = 0; ctx->attnum < ctx->natts; ctx->attnum++)
1275  if (!check_tuple_attribute(ctx))
1276  break; /* cannot continue */
1277 
1278  /* revert attnum to -1 until we again examine individual attributes */
1279  ctx->attnum = -1;
1280 }
FullTransactionId oldest_fxid
Definition: verify_heapam.c:73
HeapTupleHeader tuphdr
uint32 TransactionId
Definition: c.h:587
#define RelationGetDescr(relation)
Definition: rel.h:483
FullTransactionId next_fxid
Definition: verify_heapam.c:70
MultiXactId oldest_mxact
Definition: verify_heapam.c:80
char * pstrdup(const char *in)
Definition: mcxt.c:1187
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
static void report_corruption(HeapCheckContext *ctx, char *msg)
AttrNumber attnum
MultiXactId next_mxact
Definition: verify_heapam.c:79
#define XidFromFullTransactionId(x)
Definition: transam.h:48
#define HeapTupleHeaderGetRawXmax(tup)
Definition: htup_details.h:375
unsigned short uint16
Definition: c.h:440
static bool check_tuple_attribute(HeapCheckContext *ctx)
static bool check_tuple_header_and_visibilty(HeapTupleHeader tuphdr, HeapCheckContext *ctx)
static XidBoundsViolation get_xid_status(TransactionId xid, HeapCheckContext *ctx, XidCommitStatus *status)
FullTransactionId relfrozenfxid
Definition: verify_heapam.c:91
#define EpochFromFullTransactionId(x)
Definition: transam.h:47
#define HEAP_XMAX_IS_MULTI
Definition: htup_details.h:208
#define HeapTupleHeaderGetXmin(tup)
Definition: htup_details.h:313
#define fatal(...)
TransactionId relminmxid
Definition: verify_heapam.c:92
static XidBoundsViolation check_mxid_valid_in_rel(MultiXactId mxid, HeapCheckContext *ctx)

◆ check_tuple_attribute()

static bool check_tuple_attribute ( HeapCheckContext ctx)
static

Definition at line 926 of file verify_heapam.c.

References att_addlength_pointer, att_align_nominal, att_align_pointer, att_isnull, HeapCheckContext::attnum, HeapCheckContext::attrsize, BTEqualStrategyNumber, check_toast_tuple(), HeapCheckContext::chunkno, DatumGetPointer, HeapCheckContext::endchunk, fetchatt, ForwardScanDirection, HEAP_HASEXTERNAL, HEAP_HASNULL, init_toast_snapshot(), HeapCheckContext::lp_len, ObjectIdGetDatum, HeapCheckContext::offset, psprintf(), RelationData::rd_rel, HeapCheckContext::rel, RelationGetDescr, report_corruption(), ScanKeyInit(), systable_beginscan_ordered(), systable_endscan_ordered(), systable_getnext_ordered(), HeapTupleHeaderData::t_bits, HeapTupleHeaderData::t_hoff, HeapTupleHeaderData::t_infomask, TOAST_MAX_CHUNK_SIZE, HeapCheckContext::toast_rel, HeapCheckContext::totalchunks, HeapCheckContext::tuphdr, TupleDescAttr, varatt_external::va_extsize, varatt_external::va_valueid, HeapCheckContext::valid_toast_index, VARATT_EXTERNAL_GET_POINTER, VARATT_IS_EXTERNAL, VARTAG_EXTERNAL, and VARTAG_ONDISK.

Referenced by check_tuple().

927 {
928  struct varatt_external toast_pointer;
929  ScanKeyData toastkey;
930  SysScanDesc toastscan;
931  SnapshotData SnapshotToast;
932  HeapTuple toasttup;
933  bool found_toasttup;
934  Datum attdatum;
935  struct varlena *attr;
936  char *tp; /* pointer to the tuple data */
937  uint16 infomask;
938  Form_pg_attribute thisatt;
939 
940  infomask = ctx->tuphdr->t_infomask;
941  thisatt = TupleDescAttr(RelationGetDescr(ctx->rel), ctx->attnum);
942 
943  tp = (char *) ctx->tuphdr + ctx->tuphdr->t_hoff;
944 
945  if (ctx->tuphdr->t_hoff + ctx->offset > ctx->lp_len)
946  {
947  report_corruption(ctx,
948  psprintf("attribute %u with length %u starts at offset %u beyond total tuple length %u",
949  ctx->attnum,
950  thisatt->attlen,
951  ctx->tuphdr->t_hoff + ctx->offset,
952  ctx->lp_len));
953  return false;
954  }
955 
956  /* Skip null values */
957  if (infomask & HEAP_HASNULL && att_isnull(ctx->attnum, ctx->tuphdr->t_bits))
958  return true;
959 
960  /* Skip non-varlena values, but update offset first */
961  if (thisatt->attlen != -1)
962  {
963  ctx->offset = att_align_nominal(ctx->offset, thisatt->attalign);
964  ctx->offset = att_addlength_pointer(ctx->offset, thisatt->attlen,
965  tp + ctx->offset);
966  if (ctx->tuphdr->t_hoff + ctx->offset > ctx->lp_len)
967  {
968  report_corruption(ctx,
969  psprintf("attribute %u with length %u ends at offset %u beyond total tuple length %u",
970  ctx->attnum,
971  thisatt->attlen,
972  ctx->tuphdr->t_hoff + ctx->offset,
973  ctx->lp_len));
974  return false;
975  }
976  return true;
977  }
978 
979  /* Ok, we're looking at a varlena attribute. */
980  ctx->offset = att_align_pointer(ctx->offset, thisatt->attalign, -1,
981  tp + ctx->offset);
982 
983  /* Get the (possibly corrupt) varlena datum */
984  attdatum = fetchatt(thisatt, tp + ctx->offset);
985 
986  /*
987  * We have the datum, but we cannot decode it carelessly, as it may still
988  * be corrupt.
989  */
990 
991  /*
992  * Check that VARTAG_SIZE won't hit a TrapMacro on a corrupt va_tag before
993  * risking a call into att_addlength_pointer
994  */
995  if (VARATT_IS_EXTERNAL(tp + ctx->offset))
996  {
997  uint8 va_tag = VARTAG_EXTERNAL(tp + ctx->offset);
998 
999  if (va_tag != VARTAG_ONDISK)
1000  {
1001  report_corruption(ctx,
1002  psprintf("toasted attribute %u has unexpected TOAST tag %u",
1003  ctx->attnum,
1004  va_tag));
1005  /* We can't know where the next attribute begins */
1006  return false;
1007  }
1008  }
1009 
1010  /* Ok, should be safe now */
1011  ctx->offset = att_addlength_pointer(ctx->offset, thisatt->attlen,
1012  tp + ctx->offset);
1013 
1014  if (ctx->tuphdr->t_hoff + ctx->offset > ctx->lp_len)
1015  {
1016  report_corruption(ctx,
1017  psprintf("attribute %u with length %u ends at offset %u beyond total tuple length %u",
1018  ctx->attnum,
1019  thisatt->attlen,
1020  ctx->tuphdr->t_hoff + ctx->offset,
1021  ctx->lp_len));
1022 
1023  return false;
1024  }
1025 
1026  /*
1027  * heap_deform_tuple would be done with this attribute at this point,
1028  * having stored it in values[], and would continue to the next attribute.
1029  * We go further, because we need to check if the toast datum is corrupt.
1030  */
1031 
1032  attr = (struct varlena *) DatumGetPointer(attdatum);
1033 
1034  /*
1035  * Now we follow the logic of detoast_external_attr(), with the same
1036  * caveats about being paranoid about corruption.
1037  */
1038 
1039  /* Skip values that are not external */
1040  if (!VARATT_IS_EXTERNAL(attr))
1041  return true;
1042 
1043  /* It is external, and we're looking at a page on disk */
1044 
1045  /* The tuple header better claim to contain toasted values */
1046  if (!(infomask & HEAP_HASEXTERNAL))
1047  {
1048  report_corruption(ctx,
1049  psprintf("attribute %u is external but tuple header flag HEAP_HASEXTERNAL not set",
1050  ctx->attnum));
1051  return true;
1052  }
1053 
1054  /* The relation better have a toast table */
1055  if (!ctx->rel->rd_rel->reltoastrelid)
1056  {
1057  report_corruption(ctx,
1058  psprintf("attribute %u is external but relation has no toast relation",
1059  ctx->attnum));
1060  return true;
1061  }
1062 
1063  /* If we were told to skip toast checking, then we're done. */
1064  if (ctx->toast_rel == NULL)
1065  return true;
1066 
1067  /*
1068  * Must copy attr into toast_pointer for alignment considerations
1069  */
1070  VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
1071 
1072  ctx->attrsize = toast_pointer.va_extsize;
1073  ctx->endchunk = (ctx->attrsize - 1) / TOAST_MAX_CHUNK_SIZE;
1074  ctx->totalchunks = ctx->endchunk + 1;
1075 
1076  /*
1077  * Setup a scan key to find chunks in toast table with matching va_valueid
1078  */
1079  ScanKeyInit(&toastkey,
1080  (AttrNumber) 1,
1081  BTEqualStrategyNumber, F_OIDEQ,
1082  ObjectIdGetDatum(toast_pointer.va_valueid));
1083 
1084  /*
1085  * Check if any chunks for this toasted object exist in the toast table,
1086  * accessible via the index.
1087  */
1088  init_toast_snapshot(&SnapshotToast);
1089  toastscan = systable_beginscan_ordered(ctx->toast_rel,
1090  ctx->valid_toast_index,
1091  &SnapshotToast, 1,
1092  &toastkey);
1093  ctx->chunkno = 0;
1094  found_toasttup = false;
1095  while ((toasttup =
1096  systable_getnext_ordered(toastscan,
1097  ForwardScanDirection)) != NULL)
1098  {
1099  found_toasttup = true;
1100  check_toast_tuple(toasttup, ctx);
1101  ctx->chunkno++;
1102  }
1103  if (ctx->chunkno != (ctx->endchunk + 1))
1104  report_corruption(ctx,
1105  psprintf("final toast chunk number %u differs from expected value %u",
1106  ctx->chunkno, (ctx->endchunk + 1)));
1107  if (!found_toasttup)
1108  report_corruption(ctx,
1109  psprintf("toasted value for attribute %u missing from toast table",
1110  ctx->attnum));
1111  systable_endscan_ordered(toastscan);
1112 
1113  return true;
1114 }
#define TOAST_MAX_CHUNK_SIZE
Definition: heaptoast.h:84
#define att_align_nominal(cur_offset, attalign)
Definition: tupmacs.h:148
HeapTupleHeader tuphdr
bits8 t_bits[FLEXIBLE_ARRAY_MEMBER]
Definition: htup_details.h:177
#define RelationGetDescr(relation)
Definition: rel.h:483
#define VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr)
Definition: detoast.h:32
#define att_isnull(ATT, BITS)
Definition: tupmacs.h:25
void init_toast_snapshot(Snapshot toast_snapshot)
Relation toast_rel
Definition: verify_heapam.c:93
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
unsigned char uint8
Definition: c.h:439
static void report_corruption(HeapCheckContext *ctx, char *msg)
AttrNumber attnum
HeapTuple systable_getnext_ordered(SysScanDesc sysscan, ScanDirection direction)
Definition: genam.c:707
Relation valid_toast_index
Definition: verify_heapam.c:95
Form_pg_class rd_rel
Definition: rel.h:110
#define VARTAG_EXTERNAL(PTR)
Definition: postgres.h:308
#define fetchatt(A, T)
Definition: tupmacs.h:41
#define VARATT_IS_EXTERNAL(PTR)
Definition: postgres.h:313
unsigned short uint16
Definition: c.h:440
#define HEAP_HASNULL
Definition: htup_details.h:189
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:197
#define att_addlength_pointer(cur_offset, attlen, attptr)
Definition: tupmacs.h:176
static void check_toast_tuple(HeapTuple toasttup, HeapCheckContext *ctx)
uintptr_t Datum
Definition: postgres.h:367
#define att_align_pointer(cur_offset, attalign, attlen, attptr)
Definition: tupmacs.h:126
void systable_endscan_ordered(SysScanDesc sysscan)
Definition: genam.c:732
#define DatumGetPointer(X)
Definition: postgres.h:549
SysScanDesc systable_beginscan_ordered(Relation heapRelation, Relation indexRelation, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:642
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
#define HEAP_HASEXTERNAL
Definition: htup_details.h:191
Definition: c.h:621
int16 AttrNumber
Definition: attnum.h:21
#define BTEqualStrategyNumber
Definition: stratnum.h:31

◆ check_tuple_header_and_visibilty()

static bool check_tuple_header_and_visibilty ( HeapTupleHeader  tuphdr,
HeapCheckContext ctx 
)
static

Definition at line 598 of file verify_heapam.c.

References BITMAPLEN, EpochFromFullTransactionId, get_xid_status(), HEAP_HASNULL, HEAP_MOVED_IN, HEAP_MOVED_OFF, HEAP_XMAX_COMMITTED, HEAP_XMAX_INVALID, HEAP_XMAX_IS_LOCKED_ONLY, HEAP_XMAX_IS_MULTI, HeapTupleGetUpdateXid(), HeapTupleHeaderGetRawXmin, HeapTupleHeaderGetXvac, HeapTupleHeaderXminCommitted, HeapTupleHeaderXminInvalid, HeapCheckContext::lp_len, MAXALIGN, HeapCheckContext::natts, HeapCheckContext::next_fxid, HeapCheckContext::oldest_fxid, psprintf(), pstrdup(), HeapCheckContext::relfrozenfxid, report_corruption(), SizeofHeapTupleHeader, status(), HeapTupleHeaderData::t_hoff, HeapTupleHeaderData::t_infomask, HeapCheckContext::tuphdr, XID_ABORTED, XID_BOUNDS_OK, XID_COMMITTED, XID_IN_FUTURE, XID_IN_PROGRESS, XID_INVALID, XID_PRECEDES_CLUSTERMIN, XID_PRECEDES_RELMIN, and XidFromFullTransactionId.

Referenced by check_tuple().

599 {
600  uint16 infomask = tuphdr->t_infomask;
601  bool header_garbled = false;
602  unsigned expected_hoff;
603 
604  if (ctx->tuphdr->t_hoff > ctx->lp_len)
605  {
606  report_corruption(ctx,
607  psprintf("data begins at offset %u beyond the tuple length %u",
608  ctx->tuphdr->t_hoff, ctx->lp_len));
609  header_garbled = true;
610  }
611 
612  if ((ctx->tuphdr->t_infomask & HEAP_XMAX_COMMITTED) &&
614  {
615  report_corruption(ctx,
616  pstrdup("multixact should not be marked committed"));
617 
618  /*
619  * This condition is clearly wrong, but we do not consider the header
620  * garbled, because we don't rely on this property for determining if
621  * the tuple is visible or for interpreting other relevant header
622  * fields.
623  */
624  }
625 
626  if (infomask & HEAP_HASNULL)
627  expected_hoff = MAXALIGN(SizeofHeapTupleHeader + BITMAPLEN(ctx->natts));
628  else
629  expected_hoff = MAXALIGN(SizeofHeapTupleHeader);
630  if (ctx->tuphdr->t_hoff != expected_hoff)
631  {
632  if ((infomask & HEAP_HASNULL) && ctx->natts == 1)
633  report_corruption(ctx,
634  psprintf("tuple data should begin at byte %u, but actually begins at byte %u (1 attribute, has nulls)",
635  expected_hoff, ctx->tuphdr->t_hoff));
636  else if ((infomask & HEAP_HASNULL))
637  report_corruption(ctx,
638  psprintf("tuple data should begin at byte %u, but actually begins at byte %u (%u attributes, has nulls)",
639  expected_hoff, ctx->tuphdr->t_hoff, ctx->natts));
640  else if (ctx->natts == 1)
641  report_corruption(ctx,
642  psprintf("tuple data should begin at byte %u, but actually begins at byte %u (1 attribute, no nulls)",
643  expected_hoff, ctx->tuphdr->t_hoff));
644  else
645  report_corruption(ctx,
646  psprintf("tuple data should begin at byte %u, but actually begins at byte %u (%u attributes, no nulls)",
647  expected_hoff, ctx->tuphdr->t_hoff, ctx->natts));
648  header_garbled = true;
649  }
650 
651  if (header_garbled)
652  return false; /* checking of this tuple should not continue */
653 
654  /*
655  * Ok, we can examine the header for tuple visibility purposes, though we
656  * still need to be careful about a few remaining types of header
657  * corruption. This logic roughly follows that of
658  * HeapTupleSatisfiesVacuum. Where possible the comments indicate which
659  * HTSV_Result we think that function might return for this tuple.
660  */
661  if (!HeapTupleHeaderXminCommitted(tuphdr))
662  {
663  TransactionId raw_xmin = HeapTupleHeaderGetRawXmin(tuphdr);
664 
665  if (HeapTupleHeaderXminInvalid(tuphdr))
666  return false; /* HEAPTUPLE_DEAD */
667  /* Used by pre-9.0 binary upgrades */
668  else if (infomask & HEAP_MOVED_OFF ||
669  infomask & HEAP_MOVED_IN)
670  {
672  TransactionId xvac = HeapTupleHeaderGetXvac(tuphdr);
673 
674  switch (get_xid_status(xvac, ctx, &status))
675  {
676  case XID_INVALID:
677  report_corruption(ctx,
678  pstrdup("old-style VACUUM FULL transaction ID is invalid"));
679  return false; /* corrupt */
680  case XID_IN_FUTURE:
681  report_corruption(ctx,
682  psprintf("old-style VACUUM FULL transaction ID %u equals or exceeds next valid transaction ID %u:%u",
683  xvac,
686  return false; /* corrupt */
687  case XID_PRECEDES_RELMIN:
688  report_corruption(ctx,
689  psprintf("old-style VACUUM FULL transaction ID %u precedes relation freeze threshold %u:%u",
690  xvac,
693  return false; /* corrupt */
694  break;
696  report_corruption(ctx,
697  psprintf("old-style VACUUM FULL transaction ID %u precedes oldest valid transaction ID %u:%u",
698  xvac,
701  return false; /* corrupt */
702  break;
703  case XID_BOUNDS_OK:
704  switch (status)
705  {
706  case XID_IN_PROGRESS:
707  return true; /* HEAPTUPLE_DELETE_IN_PROGRESS */
708  case XID_COMMITTED:
709  case XID_ABORTED:
710  return false; /* HEAPTUPLE_DEAD */
711  }
712  }
713  }
714  else
715  {
717 
718  switch (get_xid_status(raw_xmin, ctx, &status))
719  {
720  case XID_INVALID:
721  report_corruption(ctx,
722  pstrdup("raw xmin is invalid"));
723  return false;
724  case XID_IN_FUTURE:
725  report_corruption(ctx,
726  psprintf("raw xmin %u equals or exceeds next valid transaction ID %u:%u",
727  raw_xmin,
730  return false; /* corrupt */
731  case XID_PRECEDES_RELMIN:
732  report_corruption(ctx,
733  psprintf("raw xmin %u precedes relation freeze threshold %u:%u",
734  raw_xmin,
737  return false; /* corrupt */
739  report_corruption(ctx,
740  psprintf("raw xmin %u precedes oldest valid transaction ID %u:%u",
741  raw_xmin,
744  return false; /* corrupt */
745  case XID_BOUNDS_OK:
746  switch (status)
747  {
748  case XID_COMMITTED:
749  break;
750  case XID_IN_PROGRESS:
751  return true; /* insert or delete in progress */
752  case XID_ABORTED:
753  return false; /* HEAPTUPLE_DEAD */
754  }
755  }
756  }
757  }
758 
759  if (!(infomask & HEAP_XMAX_INVALID) && !HEAP_XMAX_IS_LOCKED_ONLY(infomask))
760  {
761  if (infomask & HEAP_XMAX_IS_MULTI)
762  {
764  TransactionId xmax = HeapTupleGetUpdateXid(tuphdr);
765 
766  switch (get_xid_status(xmax, ctx, &status))
767  {
768  /* not LOCKED_ONLY, so it has to have an xmax */
769  case XID_INVALID:
770  report_corruption(ctx,
771  pstrdup("xmax is invalid"));
772  return false; /* corrupt */
773  case XID_IN_FUTURE:
774  report_corruption(ctx,
775  psprintf("xmax %u equals or exceeds next valid transaction ID %u:%u",
776  xmax,
779  return false; /* corrupt */
780  case XID_PRECEDES_RELMIN:
781  report_corruption(ctx,
782  psprintf("xmax %u precedes relation freeze threshold %u:%u",
783  xmax,
786  return false; /* corrupt */
788  report_corruption(ctx,
789  psprintf("xmax %u precedes oldest valid transaction ID %u:%u",
790  xmax,
793  return false; /* corrupt */
794  case XID_BOUNDS_OK:
795  switch (status)
796  {
797  case XID_IN_PROGRESS:
798  return true; /* HEAPTUPLE_DELETE_IN_PROGRESS */
799  case XID_COMMITTED:
800  case XID_ABORTED:
801  return false; /* HEAPTUPLE_RECENTLY_DEAD or
802  * HEAPTUPLE_DEAD */
803  }
804  }
805 
806  /* Ok, the tuple is live */
807  }
808  else if (!(infomask & HEAP_XMAX_COMMITTED))
809  return true; /* HEAPTUPLE_DELETE_IN_PROGRESS or
810  * HEAPTUPLE_LIVE */
811  else
812  return false; /* HEAPTUPLE_RECENTLY_DEAD or HEAPTUPLE_DEAD */
813  }
814  return true; /* not dead */
815 }
#define SizeofHeapTupleHeader
Definition: htup_details.h:184
FullTransactionId oldest_fxid
Definition: verify_heapam.c:73
HeapTupleHeader tuphdr
uint32 TransactionId
Definition: c.h:587
TransactionId HeapTupleGetUpdateXid(HeapTupleHeader tuple)
Definition: heapam.c:6838
FullTransactionId next_fxid
Definition: verify_heapam.c:70
char * pstrdup(const char *in)
Definition: mcxt.c:1187
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
static void report_corruption(HeapCheckContext *ctx, char *msg)
#define BITMAPLEN(NATTS)
Definition: htup_details.h:547
#define HeapTupleHeaderXminInvalid(tup)
Definition: htup_details.h:329
#define HEAP_XMAX_COMMITTED
Definition: htup_details.h:206
#define XidFromFullTransactionId(x)
Definition: transam.h:48
unsigned short uint16
Definition: c.h:440
#define HEAP_HASNULL
Definition: htup_details.h:189
#define HEAP_XMAX_INVALID
Definition: htup_details.h:207
#define HeapTupleHeaderXminCommitted(tup)
Definition: htup_details.h:324
static XidBoundsViolation get_xid_status(TransactionId xid, HeapCheckContext *ctx, XidCommitStatus *status)
#define HeapTupleHeaderGetXvac(tup)
Definition: htup_details.h:415
#define HEAP_MOVED_IN
Definition: htup_details.h:213
#define HEAP_XMAX_IS_LOCKED_ONLY(infomask)
Definition: htup_details.h:230
FullTransactionId relfrozenfxid
Definition: verify_heapam.c:91
#define EpochFromFullTransactionId(x)
Definition: transam.h:47
#define HEAP_XMAX_IS_MULTI
Definition: htup_details.h:208
#define HEAP_MOVED_OFF
Definition: htup_details.h:210
XidCommitStatus
Definition: verify_heapam.c:46
#define MAXALIGN(LEN)
Definition: c.h:757
#define HeapTupleHeaderGetRawXmin(tup)
Definition: htup_details.h:308
static void static void status(const char *fmt,...) pg_attribute_printf(1
Definition: pg_regress.c:227

◆ FullTransactionIdFromXidAndCtx()

static FullTransactionId FullTransactionIdFromXidAndCtx ( TransactionId  xid,
const HeapCheckContext ctx 
)
static

Definition at line 1288 of file verify_heapam.c.

References epoch, EpochFromFullTransactionId, FullTransactionIdFromEpochAndXid(), HeapCheckContext::next_fxid, HeapCheckContext::next_xid, and TransactionIdIsNormal.

Referenced by get_xid_status(), update_cached_xid_range(), and verify_heapam().

1289 {
1290  uint32 epoch;
1291 
1292  if (!TransactionIdIsNormal(xid))
1293  return FullTransactionIdFromEpochAndXid(0, xid);
1294  epoch = EpochFromFullTransactionId(ctx->next_fxid);
1295  if (xid > ctx->next_xid)
1296  epoch--;
1297  return FullTransactionIdFromEpochAndXid(epoch, xid);
1298 }
FullTransactionId next_fxid
Definition: verify_heapam.c:70
unsigned int uint32
Definition: c.h:441
TransactionId next_xid
Definition: verify_heapam.c:71
#define EpochFromFullTransactionId(x)
Definition: transam.h:47
static FullTransactionId FullTransactionIdFromEpochAndXid(uint32 epoch, TransactionId xid)
Definition: transam.h:71
static const unsigned __int64 epoch
Definition: gettimeofday.c:34
#define TransactionIdIsNormal(xid)
Definition: transam.h:42

◆ fxid_in_cached_range()

static bool fxid_in_cached_range ( FullTransactionId  fxid,
const HeapCheckContext ctx 
)
inlinestatic

Definition at line 1331 of file verify_heapam.c.

References FullTransactionIdPrecedes, FullTransactionIdPrecedesOrEquals, HeapCheckContext::next_fxid, and HeapCheckContext::oldest_fxid.

Referenced by get_xid_status().

1332 {
1333  return (FullTransactionIdPrecedesOrEquals(ctx->oldest_fxid, fxid) &&
1334  FullTransactionIdPrecedes(fxid, ctx->next_fxid));
1335 }
FullTransactionId oldest_fxid
Definition: verify_heapam.c:73
FullTransactionId next_fxid
Definition: verify_heapam.c:70
#define FullTransactionIdPrecedesOrEquals(a, b)
Definition: transam.h:52
#define FullTransactionIdPrecedes(a, b)
Definition: transam.h:51

◆ get_xid_status()

static XidBoundsViolation get_xid_status ( TransactionId  xid,
HeapCheckContext ctx,
XidCommitStatus status 
)
static

Definition at line 1396 of file verify_heapam.c.

References BootstrapTransactionId, HeapCheckContext::cached_status, HeapCheckContext::cached_xid, FrozenTransactionId, FullTransactionIdFromXidAndCtx(), FullTransactionIdPrecedes, FullTransactionIdPrecedesOrEquals, fxid_in_cached_range(), LW_SHARED, LWLockAcquire(), LWLockRelease(), HeapCheckContext::next_fxid, HeapCheckContext::oldest_fxid, VariableCacheData::oldestClogXid, HeapCheckContext::relfrozenfxid, ShmemVariableCache, status(), TransactionIdDidAbort(), TransactionIdDidCommit(), TransactionIdIsCurrentTransactionId(), TransactionIdIsValid, update_cached_xid_range(), XID_ABORTED, XID_BOUNDS_OK, XID_COMMITTED, XID_IN_FUTURE, XID_IN_PROGRESS, XID_INVALID, XID_PRECEDES_CLUSTERMIN, and XID_PRECEDES_RELMIN.

Referenced by check_tuple(), and check_tuple_header_and_visibilty().

1398 {
1399  FullTransactionId fxid;
1400  FullTransactionId clog_horizon;
1401 
1402  /* Quick check for special xids */
1403  if (!TransactionIdIsValid(xid))
1404  return XID_INVALID;
1405  else if (xid == BootstrapTransactionId || xid == FrozenTransactionId)
1406  {
1407  if (status != NULL)
1408  *status = XID_COMMITTED;
1409  return XID_BOUNDS_OK;
1410  }
1411 
1412  /* Check if the xid is within bounds */
1413  fxid = FullTransactionIdFromXidAndCtx(xid, ctx);
1414  if (!fxid_in_cached_range(fxid, ctx))
1415  {
1416  /*
1417  * We may have been checking against stale values. Update the cached
1418  * range to be sure, and since we relied on the cached range when we
1419  * performed the full xid conversion, reconvert.
1420  */
1422  fxid = FullTransactionIdFromXidAndCtx(xid, ctx);
1423  }
1424 
1426  return XID_IN_FUTURE;
1427  if (FullTransactionIdPrecedes(fxid, ctx->oldest_fxid))
1428  return XID_PRECEDES_CLUSTERMIN;
1429  if (FullTransactionIdPrecedes(fxid, ctx->relfrozenfxid))
1430  return XID_PRECEDES_RELMIN;
1431 
1432  /* Early return if the caller does not request clog checking */
1433  if (status == NULL)
1434  return XID_BOUNDS_OK;
1435 
1436  /* Early return if we just checked this xid in a prior call */
1437  if (xid == ctx->cached_xid)
1438  {
1439  *status = ctx->cached_status;
1440  return XID_BOUNDS_OK;
1441  }
1442 
1443  *status = XID_COMMITTED;
1444  LWLockAcquire(XactTruncationLock, LW_SHARED);
1445  clog_horizon =
1447  ctx);
1448  if (FullTransactionIdPrecedesOrEquals(clog_horizon, fxid))
1449  {
1452  else if (TransactionIdDidCommit(xid))
1453  *status = XID_COMMITTED;
1454  else if (TransactionIdDidAbort(xid))
1455  *status = XID_ABORTED;
1456  else
1458  }
1459  LWLockRelease(XactTruncationLock);
1460  ctx->cached_xid = xid;
1461  ctx->cached_status = *status;
1462  return XID_BOUNDS_OK;
1463 }
FullTransactionId oldest_fxid
Definition: verify_heapam.c:73
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition: xact.c:869
FullTransactionId next_fxid
Definition: verify_heapam.c:70
TransactionId cached_xid
Definition: verify_heapam.c:85
bool TransactionIdDidCommit(TransactionId transactionId)
Definition: transam.c:125
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1808
static void update_cached_xid_range(HeapCheckContext *ctx)
#define FullTransactionIdPrecedesOrEquals(a, b)
Definition: transam.h:52
#define BootstrapTransactionId
Definition: transam.h:32
TransactionId oldestClogXid
Definition: transam.h:246
VariableCache ShmemVariableCache
Definition: varsup.c:34
bool TransactionIdDidAbort(TransactionId transactionId)
Definition: transam.c:181
FullTransactionId relfrozenfxid
Definition: verify_heapam.c:91
#define FrozenTransactionId
Definition: transam.h:33
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1206
static bool fxid_in_cached_range(FullTransactionId fxid, const HeapCheckContext *ctx)
XidCommitStatus cached_status
Definition: verify_heapam.c:86
#define FullTransactionIdPrecedes(a, b)
Definition: transam.h:51
#define TransactionIdIsValid(xid)
Definition: transam.h:41
static FullTransactionId FullTransactionIdFromXidAndCtx(TransactionId xid, const HeapCheckContext *ctx)
static void static void status(const char *fmt,...) pg_attribute_printf(1
Definition: pg_regress.c:227

◆ PG_FUNCTION_INFO_V1()

PG_FUNCTION_INFO_V1 ( verify_heapam  )

◆ report_corruption()

static void report_corruption ( HeapCheckContext ctx,
char *  msg 
)
static

Definition at line 508 of file verify_heapam.c.

References HeapCheckContext::attnum, HeapCheckContext::blkno, CStringGetTextDatum, heap_form_tuple(), HEAPCHECK_RELATION_COLS, Int32GetDatum, Int64GetDatum(), HeapCheckContext::is_corrupt, MemSet, HeapCheckContext::offnum, pfree(), HeapCheckContext::tupdesc, tuplestore_puttuple(), HeapCheckContext::tupstore, and values.

Referenced by check_toast_tuple(), check_tuple(), check_tuple_attribute(), check_tuple_header_and_visibilty(), and verify_heapam().

509 {
511  bool nulls[HEAPCHECK_RELATION_COLS];
512  HeapTuple tuple;
513 
514  MemSet(values, 0, sizeof(values));
515  MemSet(nulls, 0, sizeof(nulls));
516  values[0] = Int64GetDatum(ctx->blkno);
517  values[1] = Int32GetDatum(ctx->offnum);
518  values[2] = Int32GetDatum(ctx->attnum);
519  nulls[2] = (ctx->attnum < 0);
520  values[3] = CStringGetTextDatum(msg);
521 
522  /*
523  * In principle, there is nothing to prevent a scan over a large, highly
524  * corrupted table from using work_mem worth of memory building up the
525  * tuplestore. That's ok, but if we also leak the msg argument memory
526  * until the end of the query, we could exceed work_mem by more than a
527  * trivial amount. Therefore, free the msg argument each time we are
528  * called rather than waiting for our current memory context to be freed.
529  */
530  pfree(msg);
531 
532  tuple = heap_form_tuple(ctx->tupdesc, values, nulls);
533  tuplestore_puttuple(ctx->tupstore, tuple);
534  ctx->is_corrupt = true;
535 }
BlockNumber blkno
Definition: verify_heapam.c:99
#define HEAPCHECK_RELATION_COLS
Definition: verify_heapam.c:31
#define MemSet(start, val, len)
Definition: c.h:1008
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1020
AttrNumber attnum
void pfree(void *pointer)
Definition: mcxt.c:1057
void tuplestore_puttuple(Tuplestorestate *state, HeapTuple tuple)
Definition: tuplestore.c:730
Datum Int64GetDatum(int64 X)
Definition: fmgr.c:1700
OffsetNumber offnum
uintptr_t Datum
Definition: postgres.h:367
Tuplestorestate * tupstore
static Datum values[MAXATTR]
Definition: bootstrap.c:165
#define Int32GetDatum(X)
Definition: postgres.h:479
#define CStringGetTextDatum(s)
Definition: builtins.h:82

◆ sanity_check_relation()

static void sanity_check_relation ( Relation  rel)
static

Definition at line 485 of file verify_heapam.c.

References ereport, errcode(), errmsg(), ERROR, RelationData::rd_rel, and RelationGetRelationName.

Referenced by verify_heapam().

486 {
487  if (rel->rd_rel->relkind != RELKIND_RELATION &&
488  rel->rd_rel->relkind != RELKIND_MATVIEW &&
489  rel->rd_rel->relkind != RELKIND_TOASTVALUE)
490  ereport(ERROR,
491  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
492  errmsg("\"%s\" is not a table, materialized view, or TOAST table",
493  RelationGetRelationName(rel))));
494  if (rel->rd_rel->relam != HEAP_TABLE_AM_OID)
495  ereport(ERROR,
496  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
497  errmsg("only heap AM is supported")));
498 }
int errcode(int sqlerrcode)
Definition: elog.c:694
Form_pg_class rd_rel
Definition: rel.h:110
#define ERROR
Definition: elog.h:45
#define RelationGetRelationName(relation)
Definition: rel.h:491
#define ereport(elevel,...)
Definition: elog.h:155
int errmsg(const char *fmt,...)
Definition: elog.c:905

◆ update_cached_mxid_range()

static void update_cached_mxid_range ( HeapCheckContext ctx)
static

Definition at line 1321 of file verify_heapam.c.

References HeapCheckContext::next_mxact, HeapCheckContext::oldest_mxact, and ReadMultiXactIdRange().

Referenced by check_mxid_valid_in_rel(), and verify_heapam().

1322 {
1324 }
MultiXactId oldest_mxact
Definition: verify_heapam.c:80
MultiXactId next_mxact
Definition: verify_heapam.c:79
void ReadMultiXactIdRange(MultiXactId *oldest, MultiXactId *next)
Definition: multixact.c:743

◆ update_cached_xid_range()

static void update_cached_xid_range ( HeapCheckContext ctx)
static

Definition at line 1304 of file verify_heapam.c.

References FullTransactionIdFromXidAndCtx(), LW_SHARED, LWLockAcquire(), LWLockRelease(), HeapCheckContext::next_fxid, HeapCheckContext::next_xid, VariableCacheData::nextXid, HeapCheckContext::oldest_fxid, HeapCheckContext::oldest_xid, VariableCacheData::oldestXid, ShmemVariableCache, and XidFromFullTransactionId.

Referenced by get_xid_status(), and verify_heapam().

1305 {
1306  /* Make cached copies */
1307  LWLockAcquire(XidGenLock, LW_SHARED);
1310  LWLockRelease(XidGenLock);
1311 
1312  /* And compute alternate versions of the same */
1315 }
TransactionId oldest_xid
Definition: verify_heapam.c:72
FullTransactionId oldest_fxid
Definition: verify_heapam.c:73
FullTransactionId next_fxid
Definition: verify_heapam.c:70
TransactionId oldestXid
Definition: transam.h:215
FullTransactionId nextXid
Definition: transam.h:213
#define XidFromFullTransactionId(x)
Definition: transam.h:48
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1808
VariableCache ShmemVariableCache
Definition: varsup.c:34
TransactionId next_xid
Definition: verify_heapam.c:71
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1206
static FullTransactionId FullTransactionIdFromXidAndCtx(TransactionId xid, const HeapCheckContext *ctx)

◆ verify_heapam()

Datum verify_heapam ( PG_FUNCTION_ARGS  )

Definition at line 186 of file verify_heapam.c.

References AccessShareLock, ReturnSetInfo::allowedModes, BAS_BULKREAD, BUFFER_LOCK_SHARE, BufferGetPage, check_tuple(), ReturnSetInfo::econtext, ExprContext::ecxt_per_query_memory, ereport, errcode(), errhint(), errmsg(), ERROR, fb(), FirstOffsetNumber, FullTransactionIdFromXidAndCtx(), GetAccessStrategy(), HeapTupleHeaderGetNatts, InvalidBuffer, InvalidTransactionId, IsA, ItemIdGetLength, ItemIdGetOffset, ItemIdGetRedirect, ItemIdIsDead, ItemIdIsRedirected, ItemIdIsUsed, LockBuffer(), MAIN_FORKNUM, MAXALIGN, MemoryContextSwitchTo(), HeapCheckContext::offset, OffsetNumberNext, PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, PG_ARGISNULL, PG_GETARG_BOOL, PG_GETARG_INT64, PG_GETARG_OID, PG_GETARG_TEXT_PP, PG_RETURN_NULL, pg_strcasecmp(), psprintf(), RBM_NORMAL, ReadBufferExtended(), relation_close(), relation_open(), RelationGetNumberOfBlocks, ReleaseBuffer(), report_corruption(), ReturnSetInfo::returnMode, sanity_check_relation(), ReturnSetInfo::setDesc, ReturnSetInfo::setResult, SFRM_Materialize, SFRM_Materialize_Random, SizeofHeapTupleHeader, skip, SKIP_PAGES_ALL_FROZEN, SKIP_PAGES_ALL_VISIBLE, SKIP_PAGES_NONE, table_close(), table_open(), text_to_cstring(), toast_close_indexes(), toast_open_indexes(), TransactionIdIsNormal, tuplestore_begin_heap(), UnlockReleaseBuffer(), update_cached_mxid_range(), update_cached_xid_range(), verify_heapam_tupdesc(), VISIBILITYMAP_ALL_FROZEN, VISIBILITYMAP_ALL_VISIBLE, visibilitymap_get_status(), and work_mem.

187 {
188  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
189  MemoryContext old_context;
190  bool random_access;
191  HeapCheckContext ctx;
192  Buffer vmbuffer = InvalidBuffer;
193  Oid relid;
194  bool on_error_stop;
195  bool check_toast;
196  SkipPages skip_option = SKIP_PAGES_NONE;
197  BlockNumber first_block;
198  BlockNumber last_block;
199  BlockNumber nblocks;
200  const char *skip;
201 
202  /* Check to see if caller supports us returning a tuplestore */
203  if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
204  ereport(ERROR,
205  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
206  errmsg("set-valued function called in context that cannot accept a set")));
207  if (!(rsinfo->allowedModes & SFRM_Materialize))
208  ereport(ERROR,
209  (errcode(ERRCODE_SYNTAX_ERROR),
210  errmsg("materialize mode required, but it is not allowed in this context")));
211 
212  /* Check supplied arguments */
213  if (PG_ARGISNULL(0))
214  ereport(ERROR,
215  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
216  errmsg("relation cannot be null")));
217  relid = PG_GETARG_OID(0);
218 
219  if (PG_ARGISNULL(1))
220  ereport(ERROR,
221  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
222  errmsg("on_error_stop cannot be null")));
223  on_error_stop = PG_GETARG_BOOL(1);
224 
225  if (PG_ARGISNULL(2))
226  ereport(ERROR,
227  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
228  errmsg("check_toast cannot be null")));
229  check_toast = PG_GETARG_BOOL(2);
230 
231  if (PG_ARGISNULL(3))
232  ereport(ERROR,
233  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
234  errmsg("skip cannot be null")));
236  if (pg_strcasecmp(skip, "all-visible") == 0)
237  skip_option = SKIP_PAGES_ALL_VISIBLE;
238  else if (pg_strcasecmp(skip, "all-frozen") == 0)
239  skip_option = SKIP_PAGES_ALL_FROZEN;
240  else if (pg_strcasecmp(skip, "none") == 0)
241  skip_option = SKIP_PAGES_NONE;
242  else
243  ereport(ERROR,
244  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
245  errmsg("invalid skip option"),
246  errhint("Valid skip options are \"all-visible\", \"all-frozen\", and \"none\".")));
247 
248  memset(&ctx, 0, sizeof(HeapCheckContext));
249  ctx.cached_xid = InvalidTransactionId;
250 
251  /*
252  * If we report corruption when not examining some individual attribute,
253  * we need attnum to be reported as NULL. Set that up before any
254  * corruption reporting might happen.
255  */
256  ctx.attnum = -1;
257 
258  /* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
259  old_context = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
260  random_access = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0;
261  ctx.tupdesc = verify_heapam_tupdesc();
262  ctx.tupstore = tuplestore_begin_heap(random_access, false, work_mem);
263  rsinfo->returnMode = SFRM_Materialize;
264  rsinfo->setResult = ctx.tupstore;
265  rsinfo->setDesc = ctx.tupdesc;
266  MemoryContextSwitchTo(old_context);
267 
268  /* Open relation, check relkind and access method, and check privileges */
269  ctx.rel = relation_open(relid, AccessShareLock);
270  sanity_check_relation(ctx.rel);
271 
272  /* Early exit if the relation is empty */
273  nblocks = RelationGetNumberOfBlocks(ctx.rel);
274  if (!nblocks)
275  {
277  PG_RETURN_NULL();
278  }
279 
280  ctx.bstrategy = GetAccessStrategy(BAS_BULKREAD);
281  ctx.buffer = InvalidBuffer;
282  ctx.page = NULL;
283 
284  /* Validate block numbers, or handle nulls. */
285  if (PG_ARGISNULL(4))
286  first_block = 0;
287  else
288  {
289  int64 fb = PG_GETARG_INT64(4);
290 
291  if (fb < 0 || fb >= nblocks)
292  ereport(ERROR,
293  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
294  errmsg("starting block number must be between 0 and %u",
295  nblocks - 1)));
296  first_block = (BlockNumber) fb;
297  }
298  if (PG_ARGISNULL(5))
299  last_block = nblocks - 1;
300  else
301  {
302  int64 lb = PG_GETARG_INT64(5);
303 
304  if (lb < 0 || lb >= nblocks)
305  ereport(ERROR,
306  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
307  errmsg("ending block number must be between 0 and %u",
308  nblocks - 1)));
309  last_block = (BlockNumber) lb;
310  }
311 
312  /* Optionally open the toast relation, if any. */
313  if (ctx.rel->rd_rel->reltoastrelid && check_toast)
314  {
315  int offset;
316 
317  /* Main relation has associated toast relation */
318  ctx.toast_rel = table_open(ctx.rel->rd_rel->reltoastrelid,
320  offset = toast_open_indexes(ctx.toast_rel,
322  &(ctx.toast_indexes),
323  &(ctx.num_toast_indexes));
324  ctx.valid_toast_index = ctx.toast_indexes[offset];
325  }
326  else
327  {
328  /*
329  * Main relation has no associated toast relation, or we're
330  * intentionally skipping it.
331  */
332  ctx.toast_rel = NULL;
333  ctx.toast_indexes = NULL;
334  ctx.num_toast_indexes = 0;
335  }
336 
339  ctx.relfrozenxid = ctx.rel->rd_rel->relfrozenxid;
340  ctx.relfrozenfxid = FullTransactionIdFromXidAndCtx(ctx.relfrozenxid, &ctx);
341  ctx.relminmxid = ctx.rel->rd_rel->relminmxid;
342 
343  if (TransactionIdIsNormal(ctx.relfrozenxid))
344  ctx.oldest_xid = ctx.relfrozenxid;
345 
346  for (ctx.blkno = first_block; ctx.blkno <= last_block; ctx.blkno++)
347  {
348  OffsetNumber maxoff;
349 
350  /* Optionally skip over all-frozen or all-visible blocks */
351  if (skip_option != SKIP_PAGES_NONE)
352  {
353  int32 mapbits;
354 
355  mapbits = (int32) visibilitymap_get_status(ctx.rel, ctx.blkno,
356  &vmbuffer);
357  if (skip_option == SKIP_PAGES_ALL_FROZEN)
358  {
359  if ((mapbits & VISIBILITYMAP_ALL_FROZEN) != 0)
360  continue;
361  }
362 
363  if (skip_option == SKIP_PAGES_ALL_VISIBLE)
364  {
365  if ((mapbits & VISIBILITYMAP_ALL_VISIBLE) != 0)
366  continue;
367  }
368  }
369 
370  /* Read and lock the next page. */
371  ctx.buffer = ReadBufferExtended(ctx.rel, MAIN_FORKNUM, ctx.blkno,
372  RBM_NORMAL, ctx.bstrategy);
373  LockBuffer(ctx.buffer, BUFFER_LOCK_SHARE);
374  ctx.page = BufferGetPage(ctx.buffer);
375 
376  /* Perform tuple checks */
377  maxoff = PageGetMaxOffsetNumber(ctx.page);
378  for (ctx.offnum = FirstOffsetNumber; ctx.offnum <= maxoff;
379  ctx.offnum = OffsetNumberNext(ctx.offnum))
380  {
381  ctx.itemid = PageGetItemId(ctx.page, ctx.offnum);
382 
383  /* Skip over unused/dead line pointers */
384  if (!ItemIdIsUsed(ctx.itemid) || ItemIdIsDead(ctx.itemid))
385  continue;
386 
387  /*
388  * If this line pointer has been redirected, check that it
389  * redirects to a valid offset within the line pointer array
390  */
391  if (ItemIdIsRedirected(ctx.itemid))
392  {
393  OffsetNumber rdoffnum = ItemIdGetRedirect(ctx.itemid);
394  ItemId rditem;
395 
396  if (rdoffnum < FirstOffsetNumber)
397  {
398  report_corruption(&ctx,
399  psprintf("line pointer redirection to item at offset %u precedes minimum offset %u",
400  (unsigned) rdoffnum,
401  (unsigned) FirstOffsetNumber));
402  continue;
403  }
404  if (rdoffnum > maxoff)
405  {
406  report_corruption(&ctx,
407  psprintf("line pointer redirection to item at offset %u exceeds maximum offset %u",
408  (unsigned) rdoffnum,
409  (unsigned) maxoff));
410  continue;
411  }
412  rditem = PageGetItemId(ctx.page, rdoffnum);
413  if (!ItemIdIsUsed(rditem))
414  report_corruption(&ctx,
415  psprintf("line pointer redirection to unused item at offset %u",
416  (unsigned) rdoffnum));
417  continue;
418  }
419 
420  /* Sanity-check the line pointer's offset and length values */
421  ctx.lp_len = ItemIdGetLength(ctx.itemid);
422  ctx.lp_off = ItemIdGetOffset(ctx.itemid);
423 
424  if (ctx.lp_off != MAXALIGN(ctx.lp_off))
425  {
426  report_corruption(&ctx,
427  psprintf("line pointer to page offset %u is not maximally aligned",
428  ctx.lp_off));
429  continue;
430  }
431  if (ctx.lp_len < MAXALIGN(SizeofHeapTupleHeader))
432  {
433  report_corruption(&ctx,
434  psprintf("line pointer length %u is less than the minimum tuple header size %u",
435  ctx.lp_len,
436  (unsigned) MAXALIGN(SizeofHeapTupleHeader)));
437  continue;
438  }
439  if (ctx.lp_off + ctx.lp_len > BLCKSZ)
440  {
441  report_corruption(&ctx,
442  psprintf("line pointer to page offset %u with length %u ends beyond maximum page offset %u",
443  ctx.lp_off,
444  ctx.lp_len,
445  (unsigned) BLCKSZ));
446  continue;
447  }
448 
449  /* It should be safe to examine the tuple's header, at least */
450  ctx.tuphdr = (HeapTupleHeader) PageGetItem(ctx.page, ctx.itemid);
451  ctx.natts = HeapTupleHeaderGetNatts(ctx.tuphdr);
452 
453  /* Ok, ready to check this next tuple */
454  check_tuple(&ctx);
455  }
456 
457  /* clean up */
458  UnlockReleaseBuffer(ctx.buffer);
459 
460  if (on_error_stop && ctx.is_corrupt)
461  break;
462  }
463 
464  if (vmbuffer != InvalidBuffer)
465  ReleaseBuffer(vmbuffer);
466 
467  /* Close the associated toast table and indexes, if any. */
468  if (ctx.toast_indexes)
469  toast_close_indexes(ctx.toast_indexes, ctx.num_toast_indexes,
471  if (ctx.toast_rel)
472  table_close(ctx.toast_rel, AccessShareLock);
473 
474  /* Close the main relation */
476 
477  PG_RETURN_NULL();
478 }
BufferAccessStrategy GetAccessStrategy(BufferAccessStrategyType btype)
Definition: freelist.c:542
#define SizeofHeapTupleHeader
Definition: htup_details.h:184
#define IsA(nodeptr, _type_)
Definition: nodes.h:584
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:167
int errhint(const char *fmt,...)
Definition: elog.c:1152
#define ItemIdIsRedirected(itemId)
Definition: itemid.h:106
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define ItemIdGetRedirect(itemId)
Definition: itemid.h:78
#define VISIBILITYMAP_ALL_FROZEN
Definition: visibilitymap.h:27
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition: bufmgr.c:666
static const struct exclude_list_item skip[]
Definition: pg_checksums.c:112
#define ItemIdIsUsed(itemId)
Definition: itemid.h:92
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
#define AccessShareLock
Definition: lockdefs.h:36
#define InvalidBuffer
Definition: buf.h:25
int errcode(int sqlerrcode)
Definition: elog.c:694
static void report_corruption(HeapCheckContext *ctx, char *msg)
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3700
#define PG_GETARG_BOOL(n)
Definition: fmgr.h:274
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:36
unsigned int Oid
Definition: postgres_ext.h:31
static int fb(int x)
Definition: preproc-init.c:92
#define ItemIdIsDead(itemId)
Definition: itemid.h:113
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
signed int int32
Definition: c.h:429
#define PG_GETARG_TEXT_PP(n)
Definition: fmgr.h:309
uint16 OffsetNumber
Definition: off.h:24
static void update_cached_xid_range(HeapCheckContext *ctx)
#define ItemIdGetLength(itemId)
Definition: itemid.h:59
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3723
#define ERROR
Definition: elog.h:45
#define HeapTupleHeaderGetNatts(tup)
Definition: htup_details.h:531
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:48
static void sanity_check_relation(Relation rel)
#define PG_GETARG_OID(n)
Definition: fmgr.h:275
#define FirstOffsetNumber
Definition: off.h:27
void toast_close_indexes(Relation *toastidxs, int num_indexes, LOCKMODE lock)
#define InvalidTransactionId
Definition: transam.h:31
#define ItemIdGetOffset(itemId)
Definition: itemid.h:65
static void update_cached_mxid_range(HeapCheckContext *ctx)
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
static void check_tuple(HeapCheckContext *ctx)
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
Tuplestorestate * tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
Definition: tuplestore.c:318
int toast_open_indexes(Relation toastrel, LOCKMODE lock, Relation **toastidxs, int *num_indexes)
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3939
int work_mem
Definition: globals.c:122
#define RelationGetNumberOfBlocks(reln)
Definition: bufmgr.h:211
SkipPages
Definition: verify_heapam.c:53
#define ereport(elevel,...)
Definition: elog.h:155
int allowedModes
Definition: execnodes.h:304
SetFunctionReturnMode returnMode
Definition: execnodes.h:306
#define PG_ARGISNULL(n)
Definition: fmgr.h:209
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:206
static TupleDesc verify_heapam_tupdesc(void)
#define OffsetNumberNext(offsetNumber)
Definition: off.h:52
#define MAXALIGN(LEN)
Definition: c.h:757
MemoryContext ecxt_per_query_memory
Definition: execnodes.h:232
#define VISIBILITYMAP_ALL_VISIBLE
Definition: visibilitymap.h:26
Tuplestorestate * setResult
Definition: execnodes.h:309
char * text_to_cstring(const text *t)
Definition: varlena.c:222
ExprContext * econtext
Definition: execnodes.h:302
TupleDesc setDesc
Definition: execnodes.h:310
int errmsg(const char *fmt,...)
Definition: elog.c:905
uint8 visibilitymap_get_status(Relation rel, BlockNumber heapBlk, Buffer *buf)
#define BUFFER_LOCK_SHARE
Definition: bufmgr.h:97
static FullTransactionId FullTransactionIdFromXidAndCtx(TransactionId xid, const HeapCheckContext *ctx)
#define TransactionIdIsNormal(xid)
Definition: transam.h:42
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
#define PG_GETARG_INT64(n)
Definition: fmgr.h:283
int Buffer
Definition: buf.h:23
#define PG_RETURN_NULL()
Definition: fmgr.h:345
#define PageGetItem(page, itemId)
Definition: bufpage.h:340

◆ verify_heapam_tupdesc()

static TupleDesc verify_heapam_tupdesc ( void  )
static

Definition at line 542 of file verify_heapam.c.

References Assert, BlessTupleDesc(), CreateTemplateTupleDesc(), HEAPCHECK_RELATION_COLS, HeapCheckContext::tupdesc, and TupleDescInitEntry().

Referenced by verify_heapam().

543 {
544  TupleDesc tupdesc;
545  AttrNumber a = 0;
546 
548  TupleDescInitEntry(tupdesc, ++a, "blkno", INT8OID, -1, 0);
549  TupleDescInitEntry(tupdesc, ++a, "offnum", INT4OID, -1, 0);
550  TupleDescInitEntry(tupdesc, ++a, "attnum", INT4OID, -1, 0);
551  TupleDescInitEntry(tupdesc, ++a, "msg", TEXTOID, -1, 0);
553 
554  return BlessTupleDesc(tupdesc);
555 }
TupleDesc CreateTemplateTupleDesc(int natts)
Definition: tupdesc.c:44
#define HEAPCHECK_RELATION_COLS
Definition: verify_heapam.c:31
TupleDesc BlessTupleDesc(TupleDesc tupdesc)
Definition: execTuples.c:2052
void TupleDescInitEntry(TupleDesc desc, AttrNumber attributeNumber, const char *attributeName, Oid oidtypeid, int32 typmod, int attdim)
Definition: tupdesc.c:603
#define Assert(condition)
Definition: c.h:804
int16 AttrNumber
Definition: attnum.h:21