/* Values for iterating over tuples within a page */
OffsetNumber offnum;
ItemId itemid;
uint16 lp_len;
uint16 lp_off;
HeapTupleHeader tuphdr; int natts;
/* Values for iterating over attributes within the tuple */
uint32 offset; /* offset in tuple data */
AttrNumber attnum;
/* True if tuple's xmax makes it eligible for pruning */ bool tuple_could_be_pruned;
/* *ListofToastedAttributestructsfortoastedattributeswhicharenot *eligibleforpruningandshouldbechecked
*/
List *toasted_attributes;
/* Whether verify_heapam has yet encountered any corrupt tuples */ bool is_corrupt;
/* The descriptor and tuplestore for verify_heapam's result tuples */
TupleDesc tupdesc;
Tuplestorestate *tupstore;
} HeapCheckContext;
/* Early exit if the relation is empty */
nblocks = RelationGetNumberOfBlocks(ctx.rel); if (!nblocks)
{
relation_close(ctx.rel, AccessShareLock);
PG_RETURN_NULL();
}
/* Validate block numbers, or handle nulls. */ if (PG_ARGISNULL(4))
first_block = 0; else
{
int64 fb = PG_GETARG_INT64(4);
if (fb < 0 || fb >= nblocks)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("starting block number must be between 0 and %u",
nblocks - 1)));
first_block = (BlockNumber) fb;
} if (PG_ARGISNULL(5))
last_block = nblocks - 1; else
{
int64 lb = PG_GETARG_INT64(5);
if (lb < 0 || lb >= nblocks)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("ending block number must be between 0 and %u",
nblocks - 1)));
last_block = (BlockNumber) lb;
}
/* Optionally open the toast relation, if any. */ if (ctx.rel->rd_rel->reltoastrelid && check_toast)
{ int offset;
if (TransactionIdIsNormal(ctx.relfrozenxid))
ctx.oldest_xid = ctx.relfrozenxid;
/* Now that `ctx` is set up, set up the read stream */
stream_skip_data.range.current_blocknum = first_block;
stream_skip_data.range.last_exclusive = last_block + 1;
stream_skip_data.skip_option = skip_option;
stream_skip_data.rel = ctx.rel;
stream_skip_data.vmbuffer = &vmbuffer;
if (rdoffnum < FirstOffsetNumber)
{
report_corruption(&ctx,
psprintf("line pointer redirection to item at offset %u precedes minimum offset %u",
(unsigned) rdoffnum,
(unsigned) FirstOffsetNumber)); continue;
} if (rdoffnum > maxoff)
{
report_corruption(&ctx,
psprintf("line pointer redirection to item at offset %u exceeds maximum offset %u",
(unsigned) rdoffnum,
(unsigned) maxoff)); continue;
}
/* *Sincewe'vecheckedthatthisredirectpointstoaline *pointerbetweenFirstOffsetNumberandmaxoff,itshouldnow *besafetofetchthereferencedlinepointer.Weexpectit *tobeLP_NORMAL;ifnot,that'scorruption.
*/
rditem = PageGetItemId(ctx.page, rdoffnum); if (!ItemIdIsUsed(rditem))
{
report_corruption(&ctx,
psprintf("redirected line pointer points to an unused item at offset %u",
(unsigned) rdoffnum)); continue;
} elseif (ItemIdIsDead(rditem))
{
report_corruption(&ctx,
psprintf("redirected line pointer points to a dead item at offset %u",
(unsigned) rdoffnum)); continue;
} elseif (ItemIdIsRedirected(rditem))
{
report_corruption(&ctx,
psprintf("redirected line pointer points to another redirected line pointer at offset %u",
(unsigned) rdoffnum)); continue;
}
/* Sanity-check the line pointer's offset and length values */
ctx.lp_len = ItemIdGetLength(ctx.itemid);
ctx.lp_off = ItemIdGetOffset(ctx.itemid);
if (ctx.lp_off != MAXALIGN(ctx.lp_off))
{
report_corruption(&ctx,
psprintf("line pointer to page offset %u is not maximally aligned",
ctx.lp_off)); continue;
} if (ctx.lp_len < MAXALIGN(SizeofHeapTupleHeader))
{
report_corruption(&ctx,
psprintf("line pointer length %u is less than the minimum tuple header size %u",
ctx.lp_len,
(unsigned) MAXALIGN(SizeofHeapTupleHeader))); continue;
} if (ctx.lp_off + ctx.lp_len > BLCKSZ)
{
report_corruption(&ctx,
psprintf("line pointer to page offset %u with length %u ends beyond maximum page offset %u",
ctx.lp_off,
ctx.lp_len,
(unsigned) BLCKSZ)); continue;
}
/* It should be safe to examine the tuple's header, at least */
lp_valid[ctx.offnum] = true;
ctx.tuphdr = (HeapTupleHeader) PageGetItem(ctx.page, ctx.itemid);
ctx.natts = HeapTupleHeaderGetNatts(ctx.tuphdr);
/* Ok, ready to check this next tuple */
check_tuple(&ctx,
&xmin_commit_status_ok[ctx.offnum],
&xmin_commit_status[ctx.offnum]);
/* We have two valid line pointers that we can examine. */
curr_lp = PageGetItemId(ctx.page, ctx.offnum);
next_lp = PageGetItemId(ctx.page, nextoffnum);
/* Handle the cases where the current line pointer is a redirect. */ if (ItemIdIsRedirected(curr_lp))
{ /* *Weshouldnothavesetsuccessor[ctx.offnum]toavalue *otherthanInvalidOffsetNumberunlessthatlinepointeris *LP_NORMAL.
*/
Assert(ItemIdIsNormal(next_lp));
/* Can only redirect to a HOT tuple. */
next_htup = (HeapTupleHeader) PageGetItem(ctx.page, next_lp); if (!HeapTupleHeaderIsHeapOnly(next_htup))
{
report_corruption(&ctx,
psprintf("redirected line pointer points to a non-heap-only tuple at offset %u",
(unsigned) nextoffnum));
}
/* HOT chains should not intersect. */ if (predecessor[nextoffnum] != InvalidOffsetNumber)
{
report_corruption(&ctx,
psprintf("redirect line pointer points to offset %u, but offset %u also points there",
(unsigned) nextoffnum, (unsigned) predecessor[nextoffnum])); continue;
}
/* HOT chains should not intersect. */ if (predecessor[nextoffnum] != InvalidOffsetNumber)
{
report_corruption(&ctx,
psprintf("tuple points to new version at offset %u, but offset %u also points there",
(unsigned) nextoffnum, (unsigned) predecessor[nextoffnum])); continue;
}
/* *IfthecurrenttupleismarkedasHOT-updated,thenthenext *tupleshouldbemarkedasaheap-onlytuple.Conversely,ifthe *currenttupleisn'tmarkedasHOT-updated,thenthenexttuple *shouldn'tbemarkedasaheap-onlytuple. * *NB:Can'tuseHeapTupleHeaderIsHotUpdated()asitchecksif *hintbitsindicatexmin/xmaxaborted.
*/ if (!(curr_htup->t_infomask2 & HEAP_HOT_UPDATED) &&
HeapTupleHeaderIsHeapOnly(next_htup))
{
report_corruption(&ctx,
psprintf("non-heap-only update produced a heap-only tuple at offset %u",
(unsigned) nextoffnum));
} if ((curr_htup->t_infomask2 & HEAP_HOT_UPDATED) &&
!HeapTupleHeaderIsHeapOnly(next_htup))
{
report_corruption(&ctx,
psprintf("heap-only update produced a non-heap only tuple at offset %u",
(unsigned) nextoffnum));
}
/* *Ifthecurrenttuple'sxminisstillinprogressbutthe *successortuple'sxminiscommitted,that'scorruption. * *NB:Werecheckthecommitstatusofthecurrenttuple'sxmin *here,becauseitmighthavecommittedafterwecheckeditand *beforewecheckedthecommitstatusofthesuccessortuple's *xmin.Thisshouldbesafebecausethexminitselfcan'thave *changed,onlyitscommitstatus.
*/
curr_xmin = HeapTupleHeaderGetXmin(curr_htup); if (xmin_commit_status_ok[ctx.offnum] &&
xmin_commit_status[ctx.offnum] == XID_IN_PROGRESS &&
xmin_commit_status_ok[nextoffnum] &&
xmin_commit_status[nextoffnum] == XID_COMMITTED &&
TransactionIdIsInProgress(curr_xmin))
{
report_corruption(&ctx,
psprintf("tuple with in-progress xmin %u was updated to produce a tuple at offset %u with committed xmin %u",
(unsigned) curr_xmin,
(unsigned) ctx.offnum,
(unsigned) next_xmin));
}
/* *Ifthecurrenttuple'sxminisabortedbutthesuccessor *tuple'sxminisin-progressorcommitted,that'scorruption.
*/ if (xmin_commit_status_ok[ctx.offnum] &&
xmin_commit_status[ctx.offnum] == XID_ABORTED &&
xmin_commit_status_ok[nextoffnum])
{ if (xmin_commit_status[nextoffnum] == XID_IN_PROGRESS)
report_corruption(&ctx,
psprintf("tuple with aborted xmin %u was updated to produce a tuple at offset %u with in-progress xmin %u",
(unsigned) curr_xmin,
(unsigned) ctx.offnum,
(unsigned) next_xmin)); elseif (xmin_commit_status[nextoffnum] == XID_COMMITTED)
report_corruption(&ctx,
psprintf("tuple with aborted xmin %u was updated to produce a tuple at offset %u with committed xmin %u",
(unsigned) curr_xmin,
(unsigned) ctx.offnum,
(unsigned) next_xmin));
}
}
curr_lp = PageGetItemId(ctx.page, ctx.offnum); if (!ItemIdIsRedirected(curr_lp))
{
HeapTupleHeader curr_htup;
curr_htup = (HeapTupleHeader)
PageGetItem(ctx.page, curr_lp); if (HeapTupleHeaderIsHeapOnly(curr_htup))
report_corruption(&ctx,
psprintf("tuple is root of chain but is marked as heap-only tuple"));
}
}
}
/* clean up */
UnlockReleaseBuffer(ctx.buffer);
/* *Checkanytoastpointersfromthepagewhoselockwejustreleased
*/ if (ctx.toasted_attributes != NIL)
{
ListCell *cell;
if (vmbuffer != InvalidBuffer)
ReleaseBuffer(vmbuffer);
/* Close the associated toast table and indexes, if any. */ if (ctx.toast_indexes)
toast_close_indexes(ctx.toast_indexes, ctx.num_toast_indexes,
AccessShareLock); if (ctx.toast_rel)
table_close(ctx.toast_rel, AccessShareLock);
/* Close the main relation */
relation_close(ctx.rel, AccessShareLock);
if (ctx->tuphdr->t_hoff > ctx->lp_len)
{
report_corruption(ctx,
psprintf("data begins at offset %u beyond the tuple length %u",
ctx->tuphdr->t_hoff, ctx->lp_len));
result = false;
}
if ((ctx->tuphdr->t_infomask & HEAP_XMAX_COMMITTED) &&
(ctx->tuphdr->t_infomask & HEAP_XMAX_IS_MULTI))
{
report_corruption(ctx,
pstrdup("multixact should not be marked committed"));
if (!TransactionIdIsValid(curr_xmax) &&
HeapTupleHeaderIsHotUpdated(tuphdr))
{
report_corruption(ctx,
psprintf("tuple has been HOT updated, but xmax is 0"));
if (HeapTupleHeaderIsHeapOnly(tuphdr) &&
((tuphdr->t_infomask & HEAP_UPDATED) == 0))
{
report_corruption(ctx,
psprintf("tuple is heap only, but not the result of an update"));
/* Here again, we can still perform further checks. */
}
if (infomask & HEAP_HASNULL)
expected_hoff = MAXALIGN(SizeofHeapTupleHeader + BITMAPLEN(ctx->natts)); else
expected_hoff = MAXALIGN(SizeofHeapTupleHeader); if (ctx->tuphdr->t_hoff != expected_hoff)
{ if ((infomask & HEAP_HASNULL) && ctx->natts == 1)
report_corruption(ctx,
psprintf("tuple data should begin at byte %u, but actually begins at byte %u (1 attribute, has nulls)",
expected_hoff, ctx->tuphdr->t_hoff)); elseif ((infomask & HEAP_HASNULL))
report_corruption(ctx,
psprintf("tuple data should begin at byte %u, but actually begins at byte %u (%u attributes, has nulls)",
expected_hoff, ctx->tuphdr->t_hoff, ctx->natts)); elseif (ctx->natts == 1)
report_corruption(ctx,
psprintf("tuple data should begin at byte %u, but actually begins at byte %u (1 attribute, no nulls)",
expected_hoff, ctx->tuphdr->t_hoff)); else
report_corruption(ctx,
psprintf("tuple data should begin at byte %u, but actually begins at byte %u (%u attributes, no nulls)",
expected_hoff, ctx->tuphdr->t_hoff, ctx->natts));
result = false;
}
ctx->tuple_could_be_pruned = true; /* have not yet proven otherwise */
*xmin_commit_status_ok = false; /* have not yet proven otherwise */
/* If xmin is normal, it should be within valid range */
xmin = HeapTupleHeaderGetXmin(tuphdr); switch (get_xid_status(xmin, ctx, &xmin_status))
{ case XID_INVALID: /* Could be the result of a speculative insertion that aborted. */ returnfalse; case XID_BOUNDS_OK:
*xmin_commit_status_ok = true;
*xmin_commit_status = xmin_status; break; case XID_IN_FUTURE:
report_corruption(ctx,
psprintf("xmin %u equals or exceeds next valid transaction ID %u:%u",
xmin,
EpochFromFullTransactionId(ctx->next_fxid),
XidFromFullTransactionId(ctx->next_fxid))); returnfalse; case XID_PRECEDES_CLUSTERMIN:
report_corruption(ctx,
psprintf("xmin %u precedes oldest valid transaction ID %u:%u",
xmin,
EpochFromFullTransactionId(ctx->oldest_fxid),
XidFromFullTransactionId(ctx->oldest_fxid))); returnfalse; case XID_PRECEDES_RELMIN:
report_corruption(ctx,
psprintf("xmin %u precedes relation freeze threshold %u:%u",
xmin,
EpochFromFullTransactionId(ctx->relfrozenfxid),
XidFromFullTransactionId(ctx->relfrozenfxid))); returnfalse;
}
/* *Hasinsertingtransactioncommitted?
*/ if (!HeapTupleHeaderXminCommitted(tuphdr))
{ if (HeapTupleHeaderXminInvalid(tuphdr)) returnfalse; /* inserter aborted, don't check */ /* Used by pre-9.0 binary upgrades */ elseif (tuphdr->t_infomask & HEAP_MOVED_OFF)
{
xvac = HeapTupleHeaderGetXvac(tuphdr);
switch (get_xid_status(xvac, ctx, &xvac_status))
{ case XID_INVALID:
report_corruption(ctx,
pstrdup("old-style VACUUM FULL transaction ID for moved off tuple is invalid")); returnfalse; case XID_IN_FUTURE:
report_corruption(ctx,
psprintf("old-style VACUUM FULL transaction ID %u for moved off tuple equals or exceeds next valid transaction ID %u:%u",
xvac,
EpochFromFullTransactionId(ctx->next_fxid),
XidFromFullTransactionId(ctx->next_fxid))); returnfalse; case XID_PRECEDES_RELMIN:
report_corruption(ctx,
psprintf("old-style VACUUM FULL transaction ID %u for moved off tuple precedes relation freeze threshold %u:%u",
xvac,
EpochFromFullTransactionId(ctx->relfrozenfxid),
XidFromFullTransactionId(ctx->relfrozenfxid))); returnfalse; case XID_PRECEDES_CLUSTERMIN:
report_corruption(ctx,
psprintf("old-style VACUUM FULL transaction ID %u for moved off tuple precedes oldest valid transaction ID %u:%u",
xvac,
EpochFromFullTransactionId(ctx->oldest_fxid),
XidFromFullTransactionId(ctx->oldest_fxid))); returnfalse; case XID_BOUNDS_OK: break;
}
switch (xvac_status)
{ case XID_IS_CURRENT_XID:
report_corruption(ctx,
psprintf("old-style VACUUM FULL transaction ID %u for moved off tuple matches our current transaction ID",
xvac)); returnfalse; case XID_IN_PROGRESS:
report_corruption(ctx,
psprintf("old-style VACUUM FULL transaction ID %u for moved off tuple appears to be in progress",
xvac)); returnfalse;
switch (get_xid_status(xvac, ctx, &xvac_status))
{ case XID_INVALID:
report_corruption(ctx,
pstrdup("old-style VACUUM FULL transaction ID for moved in tuple is invalid")); returnfalse; case XID_IN_FUTURE:
report_corruption(ctx,
psprintf("old-style VACUUM FULL transaction ID %u for moved in tuple equals or exceeds next valid transaction ID %u:%u",
xvac,
EpochFromFullTransactionId(ctx->next_fxid),
XidFromFullTransactionId(ctx->next_fxid))); returnfalse; case XID_PRECEDES_RELMIN:
report_corruption(ctx,
psprintf("old-style VACUUM FULL transaction ID %u for moved in tuple precedes relation freeze threshold %u:%u",
xvac,
EpochFromFullTransactionId(ctx->relfrozenfxid),
XidFromFullTransactionId(ctx->relfrozenfxid))); returnfalse; case XID_PRECEDES_CLUSTERMIN:
report_corruption(ctx,
psprintf("old-style VACUUM FULL transaction ID %u for moved in tuple precedes oldest valid transaction ID %u:%u",
xvac,
EpochFromFullTransactionId(ctx->oldest_fxid),
XidFromFullTransactionId(ctx->oldest_fxid))); returnfalse; case XID_BOUNDS_OK: break;
}
switch (xvac_status)
{ case XID_IS_CURRENT_XID:
report_corruption(ctx,
psprintf("old-style VACUUM FULL transaction ID %u for moved in tuple matches our current transaction ID",
xvac)); returnfalse; case XID_IN_PROGRESS:
report_corruption(ctx,
psprintf("old-style VACUUM FULL transaction ID %u for moved in tuple appears to be in progress",
xvac)); returnfalse;
if (tuphdr->t_infomask & HEAP_XMAX_IS_MULTI)
{ /* *Wealreadycheckedabovethatthismultixactiswithinlimitsfor *thistable.Nowchecktheupdatexidfromthismultixact.
*/
xmax = HeapTupleGetUpdateXid(tuphdr); switch (get_xid_status(xmax, ctx, &xmax_status))
{ case XID_INVALID: /* not LOCKED_ONLY, so it has to have an xmax */
report_corruption(ctx,
pstrdup("update xid is invalid")); returntrue; case XID_IN_FUTURE:
report_corruption(ctx,
psprintf("update xid %u equals or exceeds next valid transaction ID %u:%u",
xmax,
EpochFromFullTransactionId(ctx->next_fxid),
XidFromFullTransactionId(ctx->next_fxid))); returntrue; case XID_PRECEDES_RELMIN:
report_corruption(ctx,
psprintf("update xid %u precedes relation freeze threshold %u:%u",
xmax,
EpochFromFullTransactionId(ctx->relfrozenfxid),
XidFromFullTransactionId(ctx->relfrozenfxid))); returntrue; case XID_PRECEDES_CLUSTERMIN:
report_corruption(ctx,
psprintf("update xid %u precedes oldest valid transaction ID %u:%u",
xmax,
EpochFromFullTransactionId(ctx->oldest_fxid),
XidFromFullTransactionId(ctx->oldest_fxid))); returntrue; case XID_BOUNDS_OK: break;
}
switch (xmax_status)
{ case XID_IS_CURRENT_XID: case XID_IN_PROGRESS:
/* *Thedeleteisinprogress,soitcannotbevisibletoour *snapshot.
*/
ctx->tuple_could_be_pruned = false; break; case XID_COMMITTED:
/* Sanity-check the sequence number. */
chunk_seq = DatumGetInt32(fastgetattr(toasttup, 2,
ctx->toast_rel->rd_att, &isnull)); if (isnull)
{
report_toast_corruption(ctx, ta,
psprintf("toast value %u has toast chunk with null sequence number",
ta->toast_pointer.va_valueid)); return;
} if (chunk_seq != *expected_chunk_seq)
{ /* Either the TOAST index is corrupt, or we don't have all chunks. */
report_toast_corruption(ctx, ta,
psprintf("toast value %u index scan returned chunk %d when expecting chunk %d",
ta->toast_pointer.va_valueid,
chunk_seq, *expected_chunk_seq));
}
*expected_chunk_seq = chunk_seq + 1;
/* Sanity-check the chunk data. */
chunk = DatumGetPointer(fastgetattr(toasttup, 3,
ctx->toast_rel->rd_att, &isnull)); if (isnull)
{
report_toast_corruption(ctx, ta,
psprintf("toast value %u chunk %d has null data",
ta->toast_pointer.va_valueid,
chunk_seq)); return;
} if (!VARATT_IS_EXTENDED(chunk))
chunksize = VARSIZE(chunk) - VARHDRSZ; elseif (VARATT_IS_SHORT(chunk))
{ /* *couldhappenduetoheap_form_tupledoingitsthing
*/
chunksize = VARSIZE_SHORT(chunk) - VARHDRSZ_SHORT;
} else
{ /* should never happen */
uint32 header = ((varattrib_4b *) chunk)->va_4byte.va_header;
report_toast_corruption(ctx, ta,
psprintf("toast value %u chunk %d has invalid varlena header %0x",
ta->toast_pointer.va_valueid,
chunk_seq, header)); return;
}
/* *Somechecksonthedatawe'vefound
*/ if (chunk_seq > last_chunk_seq)
{
report_toast_corruption(ctx, ta,
psprintf("toast value %u chunk %d follows last expected chunk %d",
ta->toast_pointer.va_valueid,
chunk_seq, last_chunk_seq)); return;
}
if (chunksize != expected_size)
report_toast_corruption(ctx, ta,
psprintf("toast value %u chunk %d has size %u, but expected size %u",
ta->toast_pointer.va_valueid,
chunk_seq, chunksize, expected_size));
}
if (va_tag != VARTAG_ONDISK)
{
report_corruption(ctx,
psprintf("toasted attribute has unexpected TOAST tag %u",
va_tag)); /* We can't know where the next attribute begins */ returnfalse;
}
}
/* Ok, should be safe now */
ctx->offset = att_addlength_pointer(ctx->offset, thisatt->attlen,
tp + ctx->offset);
if (ctx->tuphdr->t_hoff + ctx->offset > ctx->lp_len)
{
report_corruption(ctx,
psprintf("attribute with length %u ends at offset %u beyond total tuple length %u",
thisatt->attlen,
ctx->tuphdr->t_hoff + ctx->offset,
ctx->lp_len));
/* Toasted attributes too large to be untoasted should never be stored */ if (toast_pointer.va_rawsize > VARLENA_SIZE_LIMIT)
report_corruption(ctx,
psprintf("toast value %u rawsize %d exceeds limit %d",
toast_pointer.va_valueid,
toast_pointer.va_rawsize,
VARLENA_SIZE_LIMIT));
if (VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer))
{
ToastCompressionId cmid; bool valid = false;
/* Compressed attributes should have a valid compression method */
cmid = TOAST_COMPRESS_METHOD(&toast_pointer); switch (cmid)
{ /* List of all valid compression method IDs */ case TOAST_PGLZ_COMPRESSION_ID: case TOAST_LZ4_COMPRESSION_ID:
valid = true; break;
/* Recognized but invalid compression method ID */ case TOAST_INVALID_COMPRESSION_ID: break;
/* Intentionally no default here */
} if (!valid)
report_corruption(ctx,
psprintf("toast value %u has invalid compression method id %d",
toast_pointer.va_valueid, cmid));
}
/* The tuple header better claim to contain toasted values */ if (!(infomask & HEAP_HASEXTERNAL))
{
report_corruption(ctx,
psprintf("toast value %u is external but tuple header flag HEAP_HASEXTERNAL not set",
toast_pointer.va_valueid)); returntrue;
}
/* The relation better have a toast table */ if (!ctx->rel->rd_rel->reltoastrelid)
{
report_corruption(ctx,
psprintf("toast value %u is external but relation has no toast relation",
toast_pointer.va_valueid)); returntrue;
}
/* If we were told to skip toast checking, then we're done. */ if (ctx->toast_rel == NULL) returntrue;
/* *Ifthistupleiseligibletobepruned,wecannotcheckthetoast. *Otherwise,wepushacopyofthetoasttuplesowecancheckitafter *releasingthemaintablebufferlock.
*/ if (!ctx->tuple_could_be_pruned)
{
ToastedAttribute *ta;
ta = (ToastedAttribute *) palloc0(sizeof(ToastedAttribute));
if (!found_toasttup)
report_toast_corruption(ctx, ta,
psprintf("toast value %u not found in toast table",
ta->toast_pointer.va_valueid)); elseif (expected_chunk_seq <= last_chunk_seq)
report_toast_corruption(ctx, ta,
psprintf("toast value %u was expected to end at chunk %d, but ended while expecting chunk %d",
ta->toast_pointer.va_valueid,
last_chunk_seq, expected_chunk_seq));
}
/* And compute alternate versions of the same */
ctx->next_xid = XidFromFullTransactionId(ctx->next_fxid);
ctx->oldest_fxid = FullTransactionIdFromXidAndCtx(ctx->oldest_xid, ctx);
}
/* Quick check for special xids */ if (!TransactionIdIsValid(xid)) return XID_INVALID; elseif (xid == BootstrapTransactionId || xid == FrozenTransactionId)
{ if (status != NULL)
*status = XID_COMMITTED; return XID_BOUNDS_OK;
}
/* Check if the xid is within bounds */
fxid = FullTransactionIdFromXidAndCtx(xid, ctx); if (!fxid_in_cached_range(fxid, ctx))
{ /* *Wemayhavebeencheckingagainststalevalues.Updatethecached *rangetobesure,andsincewereliedonthecachedrangewhenwe *performedthefullxidconversion,reconvert.
*/
update_cached_xid_range(ctx);
fxid = FullTransactionIdFromXidAndCtx(xid, ctx);
}
if (FullTransactionIdPrecedesOrEquals(ctx->next_fxid, fxid)) return XID_IN_FUTURE; if (FullTransactionIdPrecedes(fxid, ctx->oldest_fxid)) return XID_PRECEDES_CLUSTERMIN; if (FullTransactionIdPrecedes(fxid, ctx->relfrozenfxid)) return XID_PRECEDES_RELMIN;
/* Early return if the caller does not request clog checking */ if (status == NULL) return XID_BOUNDS_OK;
/* Early return if we just checked this xid in a prior call */ if (xid == ctx->cached_xid)
{
*status = ctx->cached_status; return XID_BOUNDS_OK;
}
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.73Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-08-08)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.