Attached v3 is cleaned up and includes a pg_walinspect docs update as well as some edited comments in rmgr_utils.c
On Mon, Mar 27, 2023 at 6:27 PM Peter Geoghegan <p...@bowt.ie> wrote: > > On Mon, Mar 27, 2023 at 2:29 PM Melanie Plageman > <melanieplage...@gmail.com> wrote: > > I went to add dedup records and noticed that since the actual > > BTDedupInterval struct is what is put in the xlog, I would need access > > to that type from nbtdesc.c, however, including nbtree.h doesn't seem to > > work because it includes files that cannot be included in frontend code. > > I suppose that the BTDedupInterval struct could have just as easily > gone in nbtxlog.h, next to xl_btree_dedup. There might have been a > moment where I thought about doing it that way, but I guess I found it > slightly preferable to use that symbol name (BTDedupInterval) rather > than (say) xl_btree_dedup_interval in places like the nearby > BTDedupStateData struct. > > Actually, I suppose that it's hard to make that alternative work, at > least without > including nbtxlog.h in nbtree.h. Which sounds wrong. > > > I, of course, could make some local struct in nbtdesc.c which has an > > OffsetNumber and a uint16, since the BTDedupInterval is pretty > > straightforward, but that seems a bit annoying. > > I'm probably missing something obvious, but is there a better way to do > > this? > > It was probably just one of those cases where I settled on the > arrangement that looked least odd overall. Not a particularly > principled approach. But the approach that I'm going to take once more > here. ;-) > > All of the available alternatives are annoying in roughly the same > way, though perhaps to varying degrees. All except one: I'm okay with > just not adding coverage for deduplication records, for the time being > -- just seeing the number of intervals alone is relatively informative > with deduplication records, unlike (say) nbtree delete records. I'm > also okay with having coverage for dedup records if you feel it's > worth having. Your call. > > If we're going to have coverage for deduplication records then it > seems to me that we have to have a struct in nbtxlog.h for your code > to work off of. It also seems likely that we'll want to use that same > struct within nbtxlog.c. What's less clear is what that means for the > BTDedupInterval struct. I don't think that we should include nbtxlog.h > in nbtree.h, nor should we do the converse. > > I guess maybe two identical structs would be okay. BTDedupInterval, > and xl_btree_dedup_interval, with the former still used in nbtdedup.c, > and the latter used through a pointer at the point that nbtxlog.c > reads a dedup record. Then maybe at a sizeof() static assert beside > the existing btree_xlog_dedup() assertions that check that the dedup > state interval array matches the array taken from the WAL record. > That's still a bit weird, but I find it preferable to any alternative > that I can think of. I've omitted enhancements for the dedup record type for now. > > On another note, I've thought about how to include some example output > > in docs, and, for example we could modify the example output in the > > pgwalinspect docs which includes a PRUNE record already for > > pg_get_wal_record_info() docs. We'd probably just want to keep it short. > > Yeah. Perhaps a PRUNE record for one of the system catalogs whose > relfilenode is relatively recognizable. Say pg_class. It probably > doesn't matter that much, but there is perhaps some small value in > picking an example that is relatively easy to recreate later on (or to > approximately recreate). I'm certainly not insisting on that, though. I've added such an example to pg_walinspect docs. On Tue, Mar 21, 2023 at 6:37 PM Peter Geoghegan <p...@bowt.ie> wrote: > > On Mon, Mar 13, 2023 at 6:41 PM Peter Geoghegan <p...@bowt.ie> wrote: > > There are several different things that seem important to me > > personally. These are in tension with each other, to a degree. These > > are: > > > > 1. Like Andres, I'd really like to have some way of inspecting things > > like heapam PRUNE, VACUUM, and FREEZE_PAGE records in significant > > detail. These record types happen to be very important in general, and > > the ability to see detailed information about the WAL record would > > definitely help with some debugging scenarios. I've really missed > > stuff like this while debugging serious issues under time pressure. > > One problem that I often run into when performing analysis of VACUUM > using pg_walinspect is the issue of *who* pruned which heap page, for > any given PRUNE record. Was it VACUUM/autovacuum, or was it > opportunistic pruning? There is no way of knowing for sure right now. > You *cannot* rely on an xid of 0 as an indicator of a given PRUNE > record coming from VACUUM; it could just have been an opportunistic > prune operation that happened to take place when a SELECT query ran, > before any XID was ever allocated. > > I think that we should do something like the attached, to completely > avoid this ambiguity. This patch adds a new XLOG_HEAP2 bit that's > similar to XLOG_HEAP_INIT_PAGE -- XLOG_HEAP2_BYVACUUM. This allows all > XLOG_HEAP2 record types to indicate that they took place during > VACUUM, by XOR'ing the flag with the record type/info when > XLogInsert() is called. For now this is only used by PRUNE records. > Tools like pg_walinspect will report a separate "Heap2/PRUNE+BYVACUUM" > record_type, as well as the unadorned Heap2/PRUNE record_type, which > we'll now know must have been opportunistic pruning. > > The approach of using a bit in the style of the heapam init bit makes > sense to me, because the bit is available, and works in a way that is > minimally invasive. Also, one can imagine needing to resolve a similar > ambiguity in the future, when (say) opportunistic freezing is added. > > I think that it makes sense to treat this within the scope of > Melanie's ongoing work to improve the instrumentation of these records > -- meaning that it's in scope for Postgres 16. Admittedly this is a > slightly creative interpretation, so if others disagree then I won't > argue. This is quite a small patch, though, which makes debugging > significantly easier. I think that there could be a great deal of > utility in being able to easily "pair up" corresponding > "Heap2/PRUNE+BYVACUUM" and "Heap2/VACUUM" records in debugging > scenarios. I can imagine linking these to "Heap2/FREEZE_PAGE" and > "Heap2/VISIBLE" records, too, since they're all closely related record > types. I really like this idea and would find it useful. I reviewed the patch and tried it out and it worked for me and code looked fine as well. I didn't include it in the attached patchset because I don't feel confident enough in my own understanding of any potential implications of splitting up these record types to definitively endorse it. But, if someone else felt comfortable with it, I would like to see it in the tree. - Melanie
From b584717e9de72c1ac084698453667cb301551e36 Mon Sep 17 00:00:00 2001 From: Peter Geoghegan <p...@bowt.ie> Date: Tue, 21 Mar 2023 14:40:43 -0700 Subject: [PATCH v1] Record which PRUNE records are from VACUUM. --- src/include/access/heapam.h | 2 +- src/include/access/heapam_xlog.h | 8 +++++++- src/backend/access/heap/pruneheap.c | 25 +++++++++++++++---------- src/backend/access/rmgrdesc/heapdesc.c | 3 +++ 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h index faf502651..2eedd7efd 100644 --- a/src/include/access/heapam.h +++ b/src/include/access/heapam.h @@ -289,7 +289,7 @@ extern int heap_page_prune(Relation relation, Buffer buffer, TransactionId old_snap_xmin, TimestampTz old_snap_ts, int *nnewlpdead, - OffsetNumber *off_loc); + OffsetNumber *off_loc_vacuum); extern void heap_page_prune_execute(Buffer buffer, OffsetNumber *redirected, int nredirected, OffsetNumber *nowdead, int ndead, diff --git a/src/include/access/heapam_xlog.h b/src/include/access/heapam_xlog.h index a2c67d1cd..3372d1938 100644 --- a/src/include/access/heapam_xlog.h +++ b/src/include/access/heapam_xlog.h @@ -48,7 +48,7 @@ * We ran out of opcodes, so heapam.c now has a second RmgrId. These opcodes * are associated with RM_HEAP2_ID, but are not logically different from * the ones above associated with RM_HEAP_ID. XLOG_HEAP_OPMASK applies to - * these, too. + * these, too. The BYVACUUM bit is used in place of RM_HEAP_ID's init bit. */ #define XLOG_HEAP2_REWRITE 0x00 #define XLOG_HEAP2_PRUNE 0x10 @@ -59,6 +59,12 @@ #define XLOG_HEAP2_LOCK_UPDATED 0x60 #define XLOG_HEAP2_NEW_CID 0x70 +/* + * RM_HEAP2_ID operations that take place during VACUUM set the BYVACUUM bit + * as instrumentation. Only set in XLOG_HEAP2_PRUNE records, for now. + */ +#define XLOG_HEAP2_BYVACUUM 0x80 + /* * xl_heap_insert/xl_heap_multi_insert flag values, 8 bits are available. */ diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c index 4e65cbcad..5f4ef384e 100644 --- a/src/backend/access/heap/pruneheap.c +++ b/src/backend/access/heap/pruneheap.c @@ -257,8 +257,8 @@ heap_page_prune_opt(Relation relation, Buffer buffer) * Sets *nnewlpdead for caller, indicating the number of items that were * newly set LP_DEAD during prune operation. * - * off_loc is the offset location required by the caller to use in error - * callback. + * off_loc_vacuum is the offset location required by VACUUM caller to use in + * error callback. Opportunistic pruning caller should pass NULL. * * Returns the number of tuples deleted from the page during this call. */ @@ -268,7 +268,7 @@ heap_page_prune(Relation relation, Buffer buffer, TransactionId old_snap_xmin, TimestampTz old_snap_ts, int *nnewlpdead, - OffsetNumber *off_loc) + OffsetNumber *off_loc_vacuum) { int ndeleted = 0; Page page = BufferGetPage(buffer); @@ -345,8 +345,8 @@ heap_page_prune(Relation relation, Buffer buffer, * Set the offset number so that we can display it along with any * error that occurred while processing this tuple. */ - if (off_loc) - *off_loc = offnum; + if (off_loc_vacuum) + *off_loc_vacuum = offnum; prstate.htsv[offnum] = heap_prune_satisfies_vacuum(&prstate, &tup, buffer); @@ -364,8 +364,8 @@ heap_page_prune(Relation relation, Buffer buffer, continue; /* see preceding loop */ - if (off_loc) - *off_loc = offnum; + if (off_loc_vacuum) + *off_loc_vacuum = offnum; /* Nothing to do if slot is empty or already dead */ itemid = PageGetItemId(page, offnum); @@ -377,8 +377,8 @@ heap_page_prune(Relation relation, Buffer buffer, } /* Clear the offset information once we have processed the given page. */ - if (off_loc) - *off_loc = InvalidOffsetNumber; + if (off_loc_vacuum) + *off_loc_vacuum = InvalidOffsetNumber; /* Any error while applying the changes is critical */ START_CRIT_SECTION(); @@ -416,6 +416,7 @@ heap_page_prune(Relation relation, Buffer buffer, if (RelationNeedsWAL(relation)) { xl_heap_prune xlrec; + uint8 info = XLOG_HEAP2_PRUNE; XLogRecPtr recptr; xlrec.snapshotConflictHorizon = prstate.snapshotConflictHorizon; @@ -445,7 +446,11 @@ heap_page_prune(Relation relation, Buffer buffer, XLogRegisterBufData(0, (char *) prstate.nowunused, prstate.nunused * sizeof(OffsetNumber)); - recptr = XLogInsert(RM_HEAP2_ID, XLOG_HEAP2_PRUNE); + /* Set bit indicating pruning by VACUUM where appropriate */ + if (off_loc_vacuum) + info |= XLOG_HEAP2_BYVACUUM; + + recptr = XLogInsert(RM_HEAP2_ID, info); PageSetLSN(BufferGetPage(buffer), recptr); } diff --git a/src/backend/access/rmgrdesc/heapdesc.c b/src/backend/access/rmgrdesc/heapdesc.c index 628f7e821..9231b7411 100644 --- a/src/backend/access/rmgrdesc/heapdesc.c +++ b/src/backend/access/rmgrdesc/heapdesc.c @@ -235,6 +235,9 @@ heap2_identify(uint8 info) case XLOG_HEAP2_PRUNE: id = "PRUNE"; break; + case XLOG_HEAP2_PRUNE | XLOG_HEAP2_BYVACUUM: + id = "PRUNE+BYVACUUM"; + break; case XLOG_HEAP2_VACUUM: id = "VACUUM"; break; -- 2.39.2
From 5b33b8fa323db65f3cf3b0dcc343071a7975b649 Mon Sep 17 00:00:00 2001 From: Melanie Plageman <melanieplage...@gmail.com> Date: Wed, 25 Jan 2023 08:38:38 -0500 Subject: [PATCH v3 1/2] Add rmgr_desc utilities - Begin to standardize format of rmgr_desc output, adding a new rmgr_utils file with comments about expected format. - Add helper which prints arrays in a standard format and use it in heapdesc to print out offset arrays, relid arrays, freeze plan arrays, and redirect arrays. Suggested by Andres Freund Discussion: https://postgr.es/m/flat/20230109215842.fktuhesvayno6o4g%40awork3.anarazel.de --- doc/src/sgml/pgwalinspect.sgml | 14 +- src/backend/access/rmgrdesc/Makefile | 1 + src/backend/access/rmgrdesc/heapdesc.c | 152 +++++++++++++++---- src/backend/access/rmgrdesc/meson.build | 1 + src/backend/access/rmgrdesc/rmgrdesc_utils.c | 82 ++++++++++ src/bin/pg_waldump/Makefile | 2 +- src/include/access/rmgrdesc_utils.h | 29 ++++ 7 files changed, 245 insertions(+), 36 deletions(-) create mode 100644 src/backend/access/rmgrdesc/rmgrdesc_utils.c create mode 100644 src/include/access/rmgrdesc_utils.h diff --git a/doc/src/sgml/pgwalinspect.sgml b/doc/src/sgml/pgwalinspect.sgml index d9ed8f0a9a..7fb089e83a 100644 --- a/doc/src/sgml/pgwalinspect.sgml +++ b/doc/src/sgml/pgwalinspect.sgml @@ -110,18 +110,18 @@ block_ref | blkref #0: rel 1663/5/60221 fork main blk 2 Returns one row per WAL record. For example: <screen> postgres=# SELECT * FROM pg_get_wal_records_info('0/1E913618', '0/1E913740') LIMIT 1; --[ RECORD 1 ]----+-------------------------------------------------------------- +-[ RECORD 1 ]----+------------------------------------------------------------------------------------------------------------------------ start_lsn | 0/1E913618 end_lsn | 0/1E913650 prev_lsn | 0/1E9135A0 xid | 0 -resource_manager | Standby -record_type | RUNNING_XACTS -record_length | 50 -main_data_length | 24 +resource_manager | Heap2 +record_type | PRUNE +record_length | 61 +main_data_length | 9 fpi_length | 0 -description | nextXid 33775 latestCompletedXid 33774 oldestRunningXid 33775 -block_ref | +description | snapshotConflictHorizon: 754, nredirected: 1, ndead: 1, nunused: 0, redirected: [ 1->5, 6->5 ], dead: [ 6 ], unused: [] +block_ref | blkref #0: rel 1663/5/1259 fork main blk 1 </screen> </para> <para> diff --git a/src/backend/access/rmgrdesc/Makefile b/src/backend/access/rmgrdesc/Makefile index f88d72fd86..cd95eec37f 100644 --- a/src/backend/access/rmgrdesc/Makefile +++ b/src/backend/access/rmgrdesc/Makefile @@ -23,6 +23,7 @@ OBJS = \ nbtdesc.o \ relmapdesc.o \ replorigindesc.o \ + rmgrdesc_utils.o \ seqdesc.o \ smgrdesc.o \ spgdesc.o \ diff --git a/src/backend/access/rmgrdesc/heapdesc.c b/src/backend/access/rmgrdesc/heapdesc.c index 628f7e8215..1041db239f 100644 --- a/src/backend/access/rmgrdesc/heapdesc.c +++ b/src/backend/access/rmgrdesc/heapdesc.c @@ -15,20 +15,53 @@ #include "postgres.h" #include "access/heapam_xlog.h" +#include "access/rmgrdesc_utils.h" + static void out_infobits(StringInfo buf, uint8 infobits) { + if ((infobits & XLHL_XMAX_IS_MULTI) == 0 && + (infobits & XLHL_XMAX_LOCK_ONLY) == 0 && + (infobits & XLHL_XMAX_EXCL_LOCK) == 0 && + (infobits & XLHL_XMAX_KEYSHR_LOCK) == 0 && + (infobits & XLHL_KEYS_UPDATED) == 0) + return; + + appendStringInfoString(buf, ", infobits: ["); + if (infobits & XLHL_XMAX_IS_MULTI) - appendStringInfoString(buf, "IS_MULTI "); + appendStringInfoString(buf, " IS_MULTI"); if (infobits & XLHL_XMAX_LOCK_ONLY) - appendStringInfoString(buf, "LOCK_ONLY "); + appendStringInfoString(buf, ", LOCK_ONLY"); if (infobits & XLHL_XMAX_EXCL_LOCK) - appendStringInfoString(buf, "EXCL_LOCK "); + appendStringInfoString(buf, ", EXCL_LOCK"); if (infobits & XLHL_XMAX_KEYSHR_LOCK) - appendStringInfoString(buf, "KEYSHR_LOCK "); + appendStringInfoString(buf, ", KEYSHR_LOCK"); if (infobits & XLHL_KEYS_UPDATED) - appendStringInfoString(buf, "KEYS_UPDATED "); + appendStringInfoString(buf, ", KEYS_UPDATED"); + + appendStringInfoString(buf, " ]"); +} + +static void +plan_elem_desc(StringInfo buf, void *restrict plan, void *restrict data) +{ + xl_heap_freeze_plan *new_plan = (xl_heap_freeze_plan *) plan; + OffsetNumber **offsets = data; + + appendStringInfo(buf, "{ xmax: %u, infomask: %u, infomask2: %u, ntuples: %u", + new_plan->xmax, new_plan->t_infomask, + new_plan->t_infomask2, + new_plan->ntuples); + + appendStringInfoString(buf, ", offsets:"); + array_desc(buf, *offsets, sizeof(OffsetNumber), new_plan->ntuples, + &offset_elem_desc, NULL); + + *offsets += new_plan->ntuples; + + appendStringInfo(buf, " }"); } void @@ -42,14 +75,15 @@ heap_desc(StringInfo buf, XLogReaderState *record) { xl_heap_insert *xlrec = (xl_heap_insert *) rec; - appendStringInfo(buf, "off %u flags 0x%02X", xlrec->offnum, + appendStringInfo(buf, "off: %u, flags: 0x%02X", + xlrec->offnum, xlrec->flags); } else if (info == XLOG_HEAP_DELETE) { xl_heap_delete *xlrec = (xl_heap_delete *) rec; - appendStringInfo(buf, "off %u flags 0x%02X ", + appendStringInfo(buf, "off: %u, flags: 0x%02X", xlrec->offnum, xlrec->flags); out_infobits(buf, xlrec->infobits_set); @@ -58,12 +92,12 @@ heap_desc(StringInfo buf, XLogReaderState *record) { xl_heap_update *xlrec = (xl_heap_update *) rec; - appendStringInfo(buf, "off %u xmax %u flags 0x%02X ", + appendStringInfo(buf, "off: %u, xmax: %u, flags: 0x%02X", xlrec->old_offnum, xlrec->old_xmax, xlrec->flags); out_infobits(buf, xlrec->old_infobits_set); - appendStringInfo(buf, "; new off %u xmax %u", + appendStringInfo(buf, ", new off: %u, xmax %u", xlrec->new_offnum, xlrec->new_xmax); } @@ -71,39 +105,41 @@ heap_desc(StringInfo buf, XLogReaderState *record) { xl_heap_update *xlrec = (xl_heap_update *) rec; - appendStringInfo(buf, "off %u xmax %u flags 0x%02X ", + appendStringInfo(buf, "off: %u, xmax: %u, flags: 0x%02X", xlrec->old_offnum, xlrec->old_xmax, xlrec->flags); out_infobits(buf, xlrec->old_infobits_set); - appendStringInfo(buf, "; new off %u xmax %u", + appendStringInfo(buf, ", new off: %u, xmax: %u", xlrec->new_offnum, xlrec->new_xmax); } else if (info == XLOG_HEAP_TRUNCATE) { xl_heap_truncate *xlrec = (xl_heap_truncate *) rec; - int i; + appendStringInfoString(buf, "flags: ["); if (xlrec->flags & XLH_TRUNCATE_CASCADE) - appendStringInfoString(buf, "cascade "); + appendStringInfoString(buf, " CASCADE"); if (xlrec->flags & XLH_TRUNCATE_RESTART_SEQS) - appendStringInfoString(buf, "restart_seqs "); - appendStringInfo(buf, "nrelids %u relids", xlrec->nrelids); - for (i = 0; i < xlrec->nrelids; i++) - appendStringInfo(buf, " %u", xlrec->relids[i]); + appendStringInfoString(buf, ", RESTART_SEQS"); + appendStringInfoString(buf, " ]"); + + appendStringInfo(buf, ", nrelids: %u", xlrec->nrelids); + appendStringInfoString(buf, ", relids:"); + array_desc(buf, xlrec->relids, sizeof(Oid), xlrec->nrelids, &relid_desc, NULL); } else if (info == XLOG_HEAP_CONFIRM) { xl_heap_confirm *xlrec = (xl_heap_confirm *) rec; - appendStringInfo(buf, "off %u", xlrec->offnum); + appendStringInfo(buf, "off: %u", xlrec->offnum); } else if (info == XLOG_HEAP_LOCK) { xl_heap_lock *xlrec = (xl_heap_lock *) rec; - appendStringInfo(buf, "off %u: xid %u: flags 0x%02X ", + appendStringInfo(buf, "off: %u, xid: %u, flags: 0x%02X", xlrec->offnum, xlrec->locking_xid, xlrec->flags); out_infobits(buf, xlrec->infobits_set); } @@ -111,9 +147,10 @@ heap_desc(StringInfo buf, XLogReaderState *record) { xl_heap_inplace *xlrec = (xl_heap_inplace *) rec; - appendStringInfo(buf, "off %u", xlrec->offnum); + appendStringInfo(buf, "off: %u", xlrec->offnum); } } + void heap2_desc(StringInfo buf, XLogReaderState *record) { @@ -125,43 +162,102 @@ heap2_desc(StringInfo buf, XLogReaderState *record) { xl_heap_prune *xlrec = (xl_heap_prune *) rec; - appendStringInfo(buf, "snapshotConflictHorizon %u nredirected %u ndead %u", + appendStringInfo(buf, "snapshotConflictHorizon: %u, nredirected: %u, ndead: %u", 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); + + appendStringInfoString(buf, ", redirected:"); + array_desc(buf, redirected, sizeof(OffsetNumber) * 2, nredirected * 2, &redirect_elem_desc, NULL); + appendStringInfoString(buf, ", dead:"); + array_desc(buf, nowdead, sizeof(OffsetNumber), xlrec->ndead, &offset_elem_desc, NULL); + appendStringInfoString(buf, ", unused:"); + array_desc(buf, nowunused, sizeof(OffsetNumber), nunused, &offset_elem_desc, NULL); + } } else if (info == XLOG_HEAP2_VACUUM) { xl_heap_vacuum *xlrec = (xl_heap_vacuum *) rec; - appendStringInfo(buf, "nunused %u", xlrec->nunused); + appendStringInfo(buf, "nunused: %u", xlrec->nunused); + + if (!XLogRecHasBlockImage(record, 0)) + { + OffsetNumber *nowunused; + + nowunused = (OffsetNumber *) XLogRecGetBlockData(record, 0, NULL); + + appendStringInfoString(buf, ", unused:"); + array_desc(buf, nowunused, sizeof(OffsetNumber), xlrec->nunused, &offset_elem_desc, NULL); + } } else if (info == XLOG_HEAP2_FREEZE_PAGE) { xl_heap_freeze_page *xlrec = (xl_heap_freeze_page *) rec; - appendStringInfo(buf, "snapshotConflictHorizon %u nplans %u", + appendStringInfo(buf, "snapshotConflictHorizon: %u, nplans: %u", xlrec->snapshotConflictHorizon, xlrec->nplans); + + if (!XLogRecHasBlockImage(record, 0)) + { + xl_heap_freeze_plan *plans = (xl_heap_freeze_plan *) XLogRecGetBlockData(record, 0, NULL); + OffsetNumber *offsets = (OffsetNumber *) &plans[xlrec->nplans]; + + appendStringInfoString(buf, ", plans:"); + array_desc(buf, + plans, + sizeof(xl_heap_freeze_plan), + xlrec->nplans, + &plan_elem_desc, + &offsets); + + } } else if (info == XLOG_HEAP2_VISIBLE) { xl_heap_visible *xlrec = (xl_heap_visible *) rec; - appendStringInfo(buf, "snapshotConflictHorizon %u flags 0x%02X", + appendStringInfo(buf, "snapshotConflictHorizon: %u, flags: 0x%02X", xlrec->snapshotConflictHorizon, xlrec->flags); } 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, + appendStringInfo(buf, "ntuples: %d, flags: 0x%02X", xlrec->ntuples, xlrec->flags); + appendStringInfoString(buf, ", offsets:"); + if (!XLogRecHasBlockImage(record, 0) && !isinit) + array_desc(buf, xlrec->offsets, sizeof(OffsetNumber), xlrec->ntuples, &offset_elem_desc, NULL); } else if (info == XLOG_HEAP2_LOCK_UPDATED) { xl_heap_lock_updated *xlrec = (xl_heap_lock_updated *) rec; - appendStringInfo(buf, "off %u: xmax %u: flags 0x%02X ", + appendStringInfo(buf, "off: %u, xmax: %u, flags: 0x%02X", xlrec->offnum, xlrec->xmax, xlrec->flags); out_infobits(buf, xlrec->infobits_set); } @@ -169,13 +265,13 @@ heap2_desc(StringInfo buf, XLogReaderState *record) { xl_heap_new_cid *xlrec = (xl_heap_new_cid *) rec; - appendStringInfo(buf, "rel %u/%u/%u; tid %u/%u", + appendStringInfo(buf, "rel: %u/%u/%u, tid: %u/%u", xlrec->target_locator.spcOid, xlrec->target_locator.dbOid, xlrec->target_locator.relNumber, ItemPointerGetBlockNumber(&(xlrec->target_tid)), ItemPointerGetOffsetNumber(&(xlrec->target_tid))); - appendStringInfo(buf, "; cmin: %u, cmax: %u, combo: %u", + appendStringInfo(buf, ", cmin: %u, cmax: %u, combo: %u", xlrec->cmin, xlrec->cmax, xlrec->combocid); } } diff --git a/src/backend/access/rmgrdesc/meson.build b/src/backend/access/rmgrdesc/meson.build index 166cee67b6..f76e87e2d7 100644 --- a/src/backend/access/rmgrdesc/meson.build +++ b/src/backend/access/rmgrdesc/meson.build @@ -16,6 +16,7 @@ rmgr_desc_sources = files( 'nbtdesc.c', 'relmapdesc.c', 'replorigindesc.c', + 'rmgrdesc_utils.c', 'seqdesc.c', 'smgrdesc.c', 'spgdesc.c', diff --git a/src/backend/access/rmgrdesc/rmgrdesc_utils.c b/src/backend/access/rmgrdesc/rmgrdesc_utils.c new file mode 100644 index 0000000000..2bfd11cb6e --- /dev/null +++ b/src/backend/access/rmgrdesc/rmgrdesc_utils.c @@ -0,0 +1,82 @@ +/*------------------------------------------------------------------------- + * + * rmgrdesc_utils.c + * support functions for rmgrdesc + * + * Portions Copyright (c) 2023, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * src/backend/access/rmgrdesc/rmgrdesc_utils.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "access/rmgrdesc_utils.h" + +/* + * The expected formatting of a desc function for an xlog record is as follows: + * member1_name: member1_value, member2_name: member2_value + * If the value is a list, please use + * member3_name: [ member3_list_value1, member3_list_value2 ] + * + * The first item appended to the string should not be prepended by any spaces + * or comma, however all subsequent appends to the string are responsible for + * prepending themselves with a comma followed by a space. + * + * Arrays should have a space between the opening square bracket and first + * element and between the last element and closing brace. + * + * Flags should be in all caps. + * + * For lists/arrays of items, the number of those items should be listed at the + * beginning with all of the other numbers. + * + * List punctuation should still be included even if there are 0 items. + * + * Composite objects in a list should be surrounded with { }. + */ + +void +array_desc(StringInfo buf, void *array, size_t elem_size, int count, + void (*elem_desc) (StringInfo buf, void *restrict elem, void *restrict data), + void *restrict data) +{ + if (count == 0) + { + appendStringInfoString(buf, " []"); + return; + } + appendStringInfo(buf, " ["); + for (int i = 0; i < count; i++) + { + if (i > 0) + appendStringInfoString(buf, ","); + appendStringInfoString(buf, " "); + + elem_desc(buf, (char *) array + elem_size * i, data); + } + appendStringInfoString(buf, " ]"); +} + +void +offset_elem_desc(StringInfo buf, void *restrict offset, void *restrict data) +{ + appendStringInfo(buf, "%u", *(OffsetNumber *) offset); +} + +void +redirect_elem_desc(StringInfo buf, void *restrict offset, void *restrict data) +{ + OffsetNumber *new_offset = (OffsetNumber *) offset; + + appendStringInfo(buf, "%u->%u", new_offset[0], new_offset[1]); +} + +void +relid_desc(StringInfo buf, void *restrict relid, void *restrict data) +{ + appendStringInfo(buf, "%u", *(Oid *) relid); +} diff --git a/src/bin/pg_waldump/Makefile b/src/bin/pg_waldump/Makefile index d6459e17c7..61c9de470c 100644 --- a/src/bin/pg_waldump/Makefile +++ b/src/bin/pg_waldump/Makefile @@ -18,7 +18,7 @@ OBJS = \ override CPPFLAGS := -DFRONTEND $(CPPFLAGS) -RMGRDESCSOURCES = $(sort $(notdir $(wildcard $(top_srcdir)/src/backend/access/rmgrdesc/*desc.c))) +RMGRDESCSOURCES = $(sort $(notdir $(wildcard $(top_srcdir)/src/backend/access/rmgrdesc/*desc.c) $(top_srcdir)/src/backend/access/rmgrdesc/rmgrdesc_utils.c)) RMGRDESCOBJS = $(patsubst %.c,%.o,$(RMGRDESCSOURCES)) diff --git a/src/include/access/rmgrdesc_utils.h b/src/include/access/rmgrdesc_utils.h new file mode 100644 index 0000000000..763ae81859 --- /dev/null +++ b/src/include/access/rmgrdesc_utils.h @@ -0,0 +1,29 @@ +/*------------------------------------------------------------------------- + * + * rmgrdesc_utils.h + * helper utilities for rmgrdesc + * + * Copyright (c) 2023 PostgreSQL Global Development Group + * + * src/include/access/rmgrdesc_utils.h + * + *------------------------------------------------------------------------- + */ +#ifndef RMGRDESC_UTILS_H_ +#define RMGRDESC_UTILS_H_ + +#include "storage/off.h" +#include "access/heapam_xlog.h" + +extern void array_desc(StringInfo buf, void *array, size_t elem_size, int count, + void (*elem_desc) (StringInfo buf, void *restrict elem, void *restrict data), + void *restrict data); + +extern void offset_elem_desc(StringInfo buf, void *restrict offset, void *restrict data); + +extern void redirect_elem_desc(StringInfo buf, void *restrict offset, void *restrict data); + +extern void relid_desc(StringInfo buf, void *restrict relid, void *restrict data); + + +#endif /* RMGRDESC_UTILS_H */ -- 2.37.2