Show more detail in heapam rmgr descriptions.
authorPeter Geoghegan <[email protected]>
Fri, 7 Apr 2023 23:08:52 +0000 (16:08 -0700)
committerPeter Geoghegan <[email protected]>
Fri, 7 Apr 2023 23:08:52 +0000 (16:08 -0700)
Add helper functions that output arrays in a standard format, and use
the functions inside heapdesc routines.  This allows tools like
pg_walinspect to show a detailed description of the page offset number
arrays for records like PRUNE and VACUUM (unless there was an FPI).

Also document the conventions that desc routines should follow.  Only
the heapdesc routines follow the conventions for now, so they're just
guidelines for the time being.

Based on a suggestion from Andres Freund.

Author: Melanie Plageman <[email protected]>
Reviewed-By: Peter Geoghegan <[email protected]>
Discussion: https://postgr.es/m/flat/20230109215842.fktuhesvayno6o4g%40awork3.anarazel.de

doc/src/sgml/pgwalinspect.sgml
src/backend/access/rmgrdesc/Makefile
src/backend/access/rmgrdesc/heapdesc.c
src/backend/access/rmgrdesc/meson.build
src/backend/access/rmgrdesc/rmgrdesc_utils.c [new file with mode: 0644]
src/bin/pg_waldump/Makefile
src/include/access/rmgrdesc_utils.h [new file with mode: 0644]

index d9ed8f0a9a4609568d1a47c5cef0651f412663cc..b3712be0097d03a2ca1e5da24e7bbbd2f475170b 100644 (file)
       after the <replaceable>in_lsn</replaceable> argument.  For
       example:
 <screen>
-postgres=# SELECT * FROM pg_get_wal_record_info('0/1E826E98');
--[ RECORD 1 ]----+----------------------------------------------------
-start_lsn        | 0/1E826F20
-end_lsn          | 0/1E826F60
-prev_lsn         | 0/1E826C80
+postgres=# SELECT * FROM pg_get_wal_record_info('0/E84F5E8');
+-[ RECORD 1 ]----+--------------------------------------------------
+start_lsn        | 0/E84F5E8
+end_lsn          | 0/E84F620
+prev_lsn         | 0/E84F5A8
 xid              | 0
 resource_manager | Heap2
-record_type      | PRUNE
-record_length    | 58
-main_data_length | 8
+record_type      | VACUUM
+record_length    | 50
+main_data_length | 2
 fpi_length       | 0
-description      | snapshotConflictHorizon 33748 nredirected 0 ndead 2
-block_ref        | blkref #0: rel 1663/5/60221 fork main blk 2
+description      | nunused: 1, unused: [ 22 ]
+block_ref        | blkref #0: rel 1663/16389/20884 fork main blk 126
 </screen>
      </para>
      <para>
@@ -144,7 +144,7 @@ block_ref        |
       references.  Returns one row per block reference per WAL record.
       For example:
 <screen>
-postgres=# SELECT * FROM pg_get_wal_block_info('0/10E9D80', '0/10E9DC0') LIMIT 1;
+postgres=# SELECT * FROM pg_get_wal_block_info('0/10E9D80', '0/10E9DC0');
 -[ RECORD 1 ]-----+-----------------------------------
 start_lsn         | 0/10E9D80
 end_lsn           | 0/10E9DC0
index f88d72fd862aa0f041bfb4159dfa985cad5c6d7b..cd95eec37f148cb051e872f07aef384126a42a90 100644 (file)
@@ -23,6 +23,7 @@ OBJS = \
    nbtdesc.o \
    relmapdesc.o \
    replorigindesc.o \
+   rmgrdesc_utils.o \
    seqdesc.o \
    smgrdesc.o \
    spgdesc.o \
index 628f7e8215600d4b7bed9594640c11704abdd58f..6dfd7d7d1305b66bba4e69c44e9cc124c0951047 100644 (file)
 #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 *plan, void *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 +74,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 +91,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 +104,42 @@ 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,105 @@ 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, &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;
+           OffsetNumber *offsets;
+
+           plans = (xl_heap_freeze_plan *) XLogRecGetBlockData(record, 0, NULL);
+           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 +268,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);
    }
 }
index 166cee67b6e037efa7eaafedcc9784631920c757..f76e87e2d7ddf0347aeb09d0bd61c0ef7845f54a 100644 (file)
@@ -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 (file)
index 0000000..180f64b
--- /dev/null
@@ -0,0 +1,84 @@
+/*-------------------------------------------------------------------------
+ *
+ * rmgrdesc_utils.c
+ *   Support functions for rmgrdesc routines
+ *
+ * Copyright (c) 2023, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ *   src/backend/access/rmgrdesc/rmgrdesc_utils.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/rmgrdesc_utils.h"
+#include "storage/off.h"
+
+/*
+ * Guidelines for formatting desc functions:
+ *
+ * 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 *elem, void *data),
+          void *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 *offset, void *data)
+{
+   appendStringInfo(buf, "%u", *(OffsetNumber *) offset);
+}
+
+void
+redirect_elem_desc(StringInfo buf, void *offset, void *data)
+{
+   OffsetNumber *new_offset = (OffsetNumber *) offset;
+
+   appendStringInfo(buf, "%u->%u", new_offset[0], new_offset[1]);
+}
+
+void
+relid_desc(StringInfo buf, void *relid, void *data)
+{
+   appendStringInfo(buf, "%u", *(Oid *) relid);
+}
index d6459e17c73593286cb0a5d807ea4fde4a4f1a29..0ecf582039dd21a1764d3bf68cc88a7ddd094b7c 100644 (file)
@@ -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)))
 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 (file)
index 0000000..aa6d029
--- /dev/null
@@ -0,0 +1,22 @@
+/*-------------------------------------------------------------------------
+ *
+ * rmgrdesc_utils.h
+ *   Support functions for rmgrdesc routines
+ *
+ * Copyright (c) 2023, PostgreSQL Global Development Group
+ *
+ * src/include/access/rmgrdesc_utils.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef RMGRDESC_UTILS_H_
+#define RMGRDESC_UTILS_H_
+
+extern void array_desc(StringInfo buf, void *array, size_t elem_size, int count,
+                      void (*elem_desc) (StringInfo buf, void *elem, void *data),
+                      void *data);
+extern void offset_elem_desc(StringInfo buf, void *offset, void *data);
+extern void redirect_elem_desc(StringInfo buf, void *offset, void *data);
+extern void relid_desc(StringInfo buf, void *relid, void *data);
+
+#endif                         /* RMGRDESC_UTILS_H */