Hi, A couple times when investigating data corruption issues, the last time just yesterday in [1], I needed to see the offsets affected by PRUNE and VACUUM records. As that's probably not just me, I think we should make that change in-tree.
The attached patch adds details to XLOG_HEAP2_PRUNE, XLOG_HEAP2_VACUUM, XLOG_HEAP2_FREEZE_PAGE. The biggest issue I have with the patch is that it's very hard to figure out what punctuation to use where ;). The existing code is very inconsistent. I chose to include infomask[2] for the different freeze plans mainly because it looks odd to see different plans without a visible reason. But I'm not sure that's the right choice. Greetings, Andres Freund [1] https://postgr.es/m/CANtu0ojby3eBdMXfs4QmS%2BK1avBc7NcRq_Ot5bnzrbwM%2BuQ55w%40mail.gmail.com
>From 2668f69edc5345a45b850eb4f33321f8bc18cb6e Mon Sep 17 00:00:00 2001 From: Andres Freund <and...@anarazel.de> Date: Mon, 9 Jan 2023 13:49:09 -0800 Subject: [PATCH v3] heapdesc: Include information about offset arrays --- src/backend/access/rmgrdesc/heapdesc.c | 94 ++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/src/backend/access/rmgrdesc/heapdesc.c b/src/backend/access/rmgrdesc/heapdesc.c index 628f7e82156..f1c4a0e4c64 100644 --- a/src/backend/access/rmgrdesc/heapdesc.c +++ b/src/backend/access/rmgrdesc/heapdesc.c @@ -114,6 +114,37 @@ heap_desc(StringInfo buf, XLogReaderState *record) appendStringInfo(buf, "off %u", xlrec->offnum); } } + +static void +print_offset_array(StringInfo buf, const char *label, OffsetNumber *offsets, int count) +{ + if (count == 0) + return; + appendStringInfo(buf, "%s [", label); + for (int i = 0; i < count; i++) + { + if (i > 0) + appendStringInfoString(buf, ", "); + appendStringInfo(buf, "%u", offsets[i]); + } + appendStringInfoString(buf, "]"); +} + +static void +print_redirect_array(StringInfo buf, const char *label, OffsetNumber *offsets, int count) +{ + if (count == 0) + return; + appendStringInfo(buf, "%s [", label); + for (int i = 0; i < count; i++) + { + if (i > 0) + appendStringInfoString(buf, ", "); + appendStringInfo(buf, "%u->%u", offsets[i * 2], offsets[i * 2 + 1]); + } + appendStringInfoString(buf, "]"); +} + void heap2_desc(StringInfo buf, XLogReaderState *record) { @@ -129,12 +160,48 @@ heap2_desc(StringInfo buf, XLogReaderState *record) xlrec->snapshotConflictHorizon, xlrec->nredirected, xlrec->ndead); + + + if (!XLogRecHasBlockImage(record, 0)) + { + OffsetNumber *end; + OffsetNumber *redirected; + OffsetNumber *nowdead; + OffsetNumber *nowunused; + int nredirected; + int nunused; + Size datalen; + + redirected = (OffsetNumber *) XLogRecGetBlockData(record, 0, &datalen); + + nredirected = xlrec->nredirected; + end = (OffsetNumber *) ((char *) redirected + datalen); + nowdead = redirected + (nredirected * 2); + nowunused = nowdead + xlrec->ndead; + nunused = (end - nowunused); + Assert(nunused >= 0); + + appendStringInfo(buf, " nunused %u", nunused); + print_offset_array(buf, ", unused:", nowunused, nunused); + print_redirect_array(buf, ", redirected:", redirected, nredirected); + print_offset_array(buf, ", dead:", nowdead, xlrec->ndead); + } } else if (info == XLOG_HEAP2_VACUUM) { xl_heap_vacuum *xlrec = (xl_heap_vacuum *) rec; appendStringInfo(buf, "nunused %u", xlrec->nunused); + + if (!XLogRecHasBlockImage(record, 0)) + { + OffsetNumber *nowunused; + Size datalen; + + nowunused = (OffsetNumber *) XLogRecGetBlockData(record, 0, &datalen); + + print_offset_array(buf, " unused:", nowunused, xlrec->nunused); + } } else if (info == XLOG_HEAP2_FREEZE_PAGE) { @@ -142,6 +209,30 @@ heap2_desc(StringInfo buf, XLogReaderState *record) appendStringInfo(buf, "snapshotConflictHorizon %u nplans %u", xlrec->snapshotConflictHorizon, xlrec->nplans); + + if (!XLogRecHasBlockImage(record, 0)) + { + OffsetNumber *offsets; + xl_heap_freeze_plan *plans; + int curoff = 0; + + plans = (xl_heap_freeze_plan *) XLogRecGetBlockData(record, 0, NULL); + offsets = (OffsetNumber *) ((char *) plans + + (xlrec->nplans * + sizeof(xl_heap_freeze_plan))); + + for (int p = 0; p < xlrec->nplans; p++) + { + xl_heap_freeze_plan plan; + + memcpy(&plan, &plans[p], sizeof(xl_heap_freeze_plan)); + + appendStringInfo(buf, "; plan #%d: xmax %u infomask %u infomask2 %u", + p, plan.xmax, plan.t_infomask, plan.t_infomask2); + print_offset_array(buf, ", offsets", offsets + curoff, plan.ntuples); + curoff += plan.ntuples; + } + } } else if (info == XLOG_HEAP2_VISIBLE) { @@ -153,9 +244,12 @@ heap2_desc(StringInfo buf, XLogReaderState *record) else if (info == XLOG_HEAP2_MULTI_INSERT) { xl_heap_multi_insert *xlrec = (xl_heap_multi_insert *) rec; + bool isinit = (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE) != 0; appendStringInfo(buf, "%d tuples flags 0x%02X", xlrec->ntuples, xlrec->flags); + if (!XLogRecHasBlockImage(record, 0) && !isinit) + print_offset_array(buf, " offsets", xlrec->offsets, xlrec->ntuples); } else if (info == XLOG_HEAP2_LOCK_UPDATED) { -- 2.38.0