Show more detail in heapam rmgr descriptions.
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 <melanieplageman@gmail.com> Reviewed-By: Peter Geoghegan <pg@bowt.ie> Discussion: https://postgr.es/m/flat/20230109215842.fktuhesvayno6o4g%40awork3.anarazel.de
This commit is contained in:
parent
76c111a7f1
commit
7d8219a444
@ -71,19 +71,19 @@
|
||||
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
|
||||
|
@ -23,6 +23,7 @@ OBJS = \
|
||||
nbtdesc.o \
|
||||
relmapdesc.o \
|
||||
replorigindesc.o \
|
||||
rmgrdesc_utils.o \
|
||||
seqdesc.o \
|
||||
smgrdesc.o \
|
||||
spgdesc.o \
|
||||
|
@ -15,20 +15,52 @@
|
||||
#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);
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ rmgr_desc_sources = files(
|
||||
'nbtdesc.c',
|
||||
'relmapdesc.c',
|
||||
'replorigindesc.c',
|
||||
'rmgrdesc_utils.c',
|
||||
'seqdesc.c',
|
||||
'smgrdesc.c',
|
||||
'spgdesc.c',
|
||||
|
84
src/backend/access/rmgrdesc/rmgrdesc_utils.c
Normal file
84
src/backend/access/rmgrdesc/rmgrdesc_utils.c
Normal file
@ -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);
|
||||
}
|
@ -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))
|
||||
|
||||
|
||||
|
22
src/include/access/rmgrdesc_utils.h
Normal file
22
src/include/access/rmgrdesc_utils.h
Normal file
@ -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 */
|
Loading…
x
Reference in New Issue
Block a user