v9_diffs
application/octet-stream
diff --git a/contrib/amcheck/verify_heapam.c b/contrib/amcheck/verify_heapam.c
index f5e68906b2..b7ea745964 100644
--- a/contrib/amcheck/verify_heapam.c
+++ b/contrib/amcheck/verify_heapam.c
@@ -113,6 +113,7 @@ verify_heapam(PG_FUNCTION_ARGS)
FullTransactionId nextFullXid;
Buffer vmbuffer = InvalidBuffer;
Oid relid;
+ bool fatal = false;
bool on_error_stop;
bool skip_all_frozen = false;
bool skip_all_visible = false;
@@ -208,6 +209,7 @@ verify_heapam(PG_FUNCTION_ARGS)
else
{
/* Main relation has no associated toast relation */
+ ctx.toastrel = NULL;
ctx.toast_indexes = NULL;
ctx.num_toast_indexes = 0;
}
@@ -234,15 +236,25 @@ verify_heapam(PG_FUNCTION_ARGS)
confess(&ctx, psprintf("relfrozenxid %u precedes global "
"oldest valid xid %u ",
ctx.relfrozenxid, ctx.oldestValidXid));
- PG_RETURN_NULL();
+ fatal = true;
}
-
- if (TransactionIdIsNormal(ctx.relminmxid) &&
+ else if (TransactionIdIsNormal(ctx.relminmxid) &&
TransactionIdPrecedes(ctx.relminmxid, ctx.oldestValidXid))
{
confess(&ctx, psprintf("relfrozenxid %u precedes global "
"oldest valid xid %u ",
ctx.relfrozenxid, ctx.oldestValidXid));
+ fatal = true;
+ }
+
+ if (fatal)
+ {
+ if (ctx.toast_indexes)
+ toast_close_indexes(ctx.toast_indexes, ctx.num_toast_indexes,
+ ShareUpdateExclusiveLock);
+ if (ctx.toastrel)
+ table_close(ctx.toastrel, ShareUpdateExclusiveLock);
+ relation_close(ctx.rel, ShareUpdateExclusiveLock);
PG_RETURN_NULL();
}
@@ -264,6 +276,7 @@ verify_heapam(PG_FUNCTION_ARGS)
{
int32 mapbits;
OffsetNumber maxoff;
+ PageHeader ph;
/* Optionally skip over all-frozen or all-visible blocks */
if (skip_all_frozen || skip_all_visible)
@@ -281,6 +294,7 @@ verify_heapam(PG_FUNCTION_ARGS)
RBM_NORMAL, ctx.bstrategy);
LockBuffer(ctx.buffer, BUFFER_LOCK_SHARE);
ctx.page = BufferGetPage(ctx.buffer);
+ ph = (PageHeader) ctx.page;
/* We must unlock the page from the prior iteration, if any */
Assert(ctx.blkno == InvalidBlockNumber || ctx.buffer != InvalidBuffer);
@@ -302,12 +316,33 @@ verify_heapam(PG_FUNCTION_ARGS)
{
ctx.itemid = PageGetItemId(ctx.page, ctx.offnum);
- /* Skip over unused/dead/redirected line pointers */
- if (!ItemIdIsUsed(ctx.itemid) ||
- ItemIdIsDead(ctx.itemid) ||
- ItemIdIsRedirected(ctx.itemid))
+ /* Skip over unused/dead line pointers */
+ if (!ItemIdIsUsed(ctx.itemid) || ItemIdIsDead(ctx.itemid))
continue;
+ /*
+ * If this line pointer has been redirected, check that it redirects
+ * to a valid offset within the line pointer array.
+ */
+ if (ItemIdIsRedirected(ctx.itemid))
+ {
+ uint16 redirect = ItemIdGetRedirect(ctx.itemid);
+ if (redirect <= SizeOfPageHeaderData || redirect >= ph->pd_lower)
+ {
+ confess(&ctx, psprintf(
+ "Invalid redirect line pointer offset %u out of bounds",
+ (unsigned) redirect));
+ continue;
+ }
+ if ((redirect - SizeOfPageHeaderData) % sizeof(uint16))
+ {
+ confess(&ctx, psprintf(
+ "Invalid redirect line pointer offset %u bad alignment",
+ (unsigned) redirect));
+ }
+ continue;
+ }
+
/* Set up context information about this next tuple */
ctx.lp_len = ItemIdGetLength(ctx.itemid);
ctx.tuphdr = (HeapTupleHeader) PageGetItem(ctx.page, ctx.itemid);
@@ -637,7 +672,7 @@ check_toast_tuple(HeapTuple toasttup, HeapCheckContext * ctx)
{
/* should never happen */
confess(ctx,
- pstrdup("toast chunk is neither short nor extended"));
+ pstrdup("corrupt toast chunk va_header"));
return;
}
@@ -972,16 +1007,29 @@ check_tuple(HeapCheckContext * ctx)
* length nulls bitmap field t_bits does not overflow the allowed space.
* We don't know if the corruption is in the natts field or the infomask
* bit HEAP_HASNULL.
+ *
+ * If the tuple does not have nulls, check that no space has been
+ * reserved for the null bitmap.
*/
- if (infomask & HEAP_HASNULL &&
- SizeofHeapTupleHeader + BITMAPLEN(ctx->natts) > ctx->tuphdr->t_hoff)
+ if (infomask & HEAP_HASNULL)
{
- confess(ctx, psprintf("SizeofHeapTupleHeader + "
- "BITMAPLEN(natts) > t_hoff "
- "(%u + %u > %u)",
- (unsigned) SizeofHeapTupleHeader,
- BITMAPLEN(ctx->natts),
- ctx->tuphdr->t_hoff));
+ if (SizeofHeapTupleHeader + BITMAPLEN(ctx->natts) > ctx->tuphdr->t_hoff)
+ {
+ confess(ctx, psprintf("SizeofHeapTupleHeader + "
+ "BITMAPLEN(natts) > t_hoff "
+ "(%u + %u > %u)",
+ (unsigned) SizeofHeapTupleHeader,
+ BITMAPLEN(ctx->natts),
+ ctx->tuphdr->t_hoff));
+ fatal = true;
+ }
+ }
+ else if (MAXALIGN(ctx->tuphdr->t_hoff) != MAXALIGN(SizeofHeapTupleHeader))
+ {
+ confess(ctx,
+ psprintf("t_hoff = %u in tuple without nulls (expected %u)",
+ (unsigned) MAXALIGN(ctx->tuphdr->t_hoff),
+ (unsigned) MAXALIGN(SizeofHeapTupleHeader)));
fatal = true;
}
@@ -1031,6 +1079,4 @@ check_tuple(HeapCheckContext * ctx)
if (!check_tuple_attribute(ctx))
break;
}
- ctx->offset = -1;
- ctx->attnum = -1;
}
diff --git a/contrib/pg_amcheck/t/004_verify_heapam.pl b/contrib/pg_amcheck/t/004_verify_heapam.pl
index b2c1f36928..08bce6e68e 100644
--- a/contrib/pg_amcheck/t/004_verify_heapam.pl
+++ b/contrib/pg_amcheck/t/004_verify_heapam.pl
@@ -381,8 +381,11 @@ is ($result,
0|2|8064|1|58|||tuple xmin = 4026531839 precedes relation relfrozenxid = $relfrozenxid
0|3|8000|1|58|||tuple xmax = 4026531839 precedes relation relfrozenxid = $relfrozenxid
0|4|7936|1|58|||t_hoff > lp_len (152 > 58)
+0|4|7936|1|58|||t_hoff = 152 in tuple without nulls (expected 24)
0|5|7872|1|58|||t_hoff not max-aligned (27)
+0|5|7872|1|58|||t_hoff = 32 in tuple without nulls (expected 24)
0|6|7808|1|58|||t_hoff < SizeofHeapTupleHeader (16 < 23)
+0|6|7808|1|58|||t_hoff = 16 in tuple without nulls (expected 24)
0|7|7744|1|58|||t_hoff < SizeofHeapTupleHeader (21 < 23)
0|7|7744|1|58|||t_hoff not max-aligned (21)
0|8|7680|1|58|||relation natts < tuple natts (3 < 2047)