diff options
Diffstat (limited to 'src/backend/access/rmgrdesc')
22 files changed, 2908 insertions, 0 deletions
diff --git a/src/backend/access/rmgrdesc/Makefile b/src/backend/access/rmgrdesc/Makefile new file mode 100644 index 0000000..f88d72f --- /dev/null +++ b/src/backend/access/rmgrdesc/Makefile @@ -0,0 +1,34 @@ +# +# Makefile for the rmgr descriptor routines +# +# src/backend/access/rmgrdesc/Makefile +# + +subdir = src/backend/access/rmgrdesc +top_builddir = ../../../.. +include $(top_builddir)/src/Makefile.global + +OBJS = \ + brindesc.o \ + clogdesc.o \ + committsdesc.o \ + dbasedesc.o \ + genericdesc.o \ + gindesc.o \ + gistdesc.o \ + hashdesc.o \ + heapdesc.o \ + logicalmsgdesc.o \ + mxactdesc.o \ + nbtdesc.o \ + relmapdesc.o \ + replorigindesc.o \ + seqdesc.o \ + smgrdesc.o \ + spgdesc.o \ + standbydesc.o \ + tblspcdesc.o \ + xactdesc.o \ + xlogdesc.o + +include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/access/rmgrdesc/brindesc.c b/src/backend/access/rmgrdesc/brindesc.c new file mode 100644 index 0000000..f05607e --- /dev/null +++ b/src/backend/access/rmgrdesc/brindesc.c @@ -0,0 +1,107 @@ +/*------------------------------------------------------------------------- + * + * brindesc.c + * rmgr descriptor routines for BRIN indexes + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/access/rmgrdesc/brindesc.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/brin_xlog.h" + +void +brin_desc(StringInfo buf, XLogReaderState *record) +{ + char *rec = XLogRecGetData(record); + uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK; + + info &= XLOG_BRIN_OPMASK; + if (info == XLOG_BRIN_CREATE_INDEX) + { + xl_brin_createidx *xlrec = (xl_brin_createidx *) rec; + + appendStringInfo(buf, "v%d pagesPerRange %u", + xlrec->version, xlrec->pagesPerRange); + } + else if (info == XLOG_BRIN_INSERT) + { + xl_brin_insert *xlrec = (xl_brin_insert *) rec; + + appendStringInfo(buf, "heapBlk %u pagesPerRange %u offnum %u", + xlrec->heapBlk, + xlrec->pagesPerRange, + xlrec->offnum); + } + else if (info == XLOG_BRIN_UPDATE) + { + xl_brin_update *xlrec = (xl_brin_update *) rec; + + appendStringInfo(buf, "heapBlk %u pagesPerRange %u old offnum %u, new offnum %u", + xlrec->insert.heapBlk, + xlrec->insert.pagesPerRange, + xlrec->oldOffnum, + xlrec->insert.offnum); + } + else if (info == XLOG_BRIN_SAMEPAGE_UPDATE) + { + xl_brin_samepage_update *xlrec = (xl_brin_samepage_update *) rec; + + appendStringInfo(buf, "offnum %u", xlrec->offnum); + } + else if (info == XLOG_BRIN_REVMAP_EXTEND) + { + xl_brin_revmap_extend *xlrec = (xl_brin_revmap_extend *) rec; + + appendStringInfo(buf, "targetBlk %u", xlrec->targetBlk); + } + else if (info == XLOG_BRIN_DESUMMARIZE) + { + xl_brin_desummarize *xlrec = (xl_brin_desummarize *) rec; + + appendStringInfo(buf, "pagesPerRange %u, heapBlk %u, page offset %u", + xlrec->pagesPerRange, xlrec->heapBlk, xlrec->regOffset); + } +} + +const char * +brin_identify(uint8 info) +{ + const char *id = NULL; + + switch (info & ~XLR_INFO_MASK) + { + case XLOG_BRIN_CREATE_INDEX: + id = "CREATE_INDEX"; + break; + case XLOG_BRIN_INSERT: + id = "INSERT"; + break; + case XLOG_BRIN_INSERT | XLOG_BRIN_INIT_PAGE: + id = "INSERT+INIT"; + break; + case XLOG_BRIN_UPDATE: + id = "UPDATE"; + break; + case XLOG_BRIN_UPDATE | XLOG_BRIN_INIT_PAGE: + id = "UPDATE+INIT"; + break; + case XLOG_BRIN_SAMEPAGE_UPDATE: + id = "SAMEPAGE_UPDATE"; + break; + case XLOG_BRIN_REVMAP_EXTEND: + id = "REVMAP_EXTEND"; + break; + case XLOG_BRIN_DESUMMARIZE: + id = "DESUMMARIZE"; + break; + } + + return id; +} diff --git a/src/backend/access/rmgrdesc/clogdesc.c b/src/backend/access/rmgrdesc/clogdesc.c new file mode 100644 index 0000000..8751373 --- /dev/null +++ b/src/backend/access/rmgrdesc/clogdesc.c @@ -0,0 +1,59 @@ +/*------------------------------------------------------------------------- + * + * clogdesc.c + * rmgr descriptor routines for access/transam/clog.c + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/access/rmgrdesc/clogdesc.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/clog.h" + + +void +clog_desc(StringInfo buf, XLogReaderState *record) +{ + char *rec = XLogRecGetData(record); + uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK; + + if (info == CLOG_ZEROPAGE) + { + int pageno; + + memcpy(&pageno, rec, sizeof(int)); + appendStringInfo(buf, "page %d", pageno); + } + else if (info == CLOG_TRUNCATE) + { + xl_clog_truncate xlrec; + + memcpy(&xlrec, rec, sizeof(xl_clog_truncate)); + appendStringInfo(buf, "page %d; oldestXact %u", + xlrec.pageno, xlrec.oldestXact); + } +} + +const char * +clog_identify(uint8 info) +{ + const char *id = NULL; + + switch (info & ~XLR_INFO_MASK) + { + case CLOG_ZEROPAGE: + id = "ZEROPAGE"; + break; + case CLOG_TRUNCATE: + id = "TRUNCATE"; + break; + } + + return id; +} diff --git a/src/backend/access/rmgrdesc/committsdesc.c b/src/backend/access/rmgrdesc/committsdesc.c new file mode 100644 index 0000000..3a65538 --- /dev/null +++ b/src/backend/access/rmgrdesc/committsdesc.c @@ -0,0 +1,55 @@ +/*------------------------------------------------------------------------- + * + * committsdesc.c + * rmgr descriptor routines for access/transam/commit_ts.c + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/access/rmgrdesc/committsdesc.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/commit_ts.h" +#include "utils/timestamp.h" + + +void +commit_ts_desc(StringInfo buf, XLogReaderState *record) +{ + char *rec = XLogRecGetData(record); + uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK; + + if (info == COMMIT_TS_ZEROPAGE) + { + int pageno; + + memcpy(&pageno, rec, sizeof(int)); + appendStringInfo(buf, "%d", pageno); + } + else if (info == COMMIT_TS_TRUNCATE) + { + xl_commit_ts_truncate *trunc = (xl_commit_ts_truncate *) rec; + + appendStringInfo(buf, "pageno %d, oldestXid %u", + trunc->pageno, trunc->oldestXid); + } +} + +const char * +commit_ts_identify(uint8 info) +{ + switch (info) + { + case COMMIT_TS_ZEROPAGE: + return "ZEROPAGE"; + case COMMIT_TS_TRUNCATE: + return "TRUNCATE"; + default: + return NULL; + } +} diff --git a/src/backend/access/rmgrdesc/dbasedesc.c b/src/backend/access/rmgrdesc/dbasedesc.c new file mode 100644 index 0000000..523d0b3 --- /dev/null +++ b/src/backend/access/rmgrdesc/dbasedesc.c @@ -0,0 +1,75 @@ +/*------------------------------------------------------------------------- + * + * dbasedesc.c + * rmgr descriptor routines for commands/dbcommands.c + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/access/rmgrdesc/dbasedesc.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "commands/dbcommands_xlog.h" +#include "lib/stringinfo.h" + + +void +dbase_desc(StringInfo buf, XLogReaderState *record) +{ + char *rec = XLogRecGetData(record); + uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK; + + if (info == XLOG_DBASE_CREATE_FILE_COPY) + { + xl_dbase_create_file_copy_rec *xlrec = + (xl_dbase_create_file_copy_rec *) rec; + + appendStringInfo(buf, "copy dir %u/%u to %u/%u", + xlrec->src_tablespace_id, xlrec->src_db_id, + xlrec->tablespace_id, xlrec->db_id); + } + else if (info == XLOG_DBASE_CREATE_WAL_LOG) + { + xl_dbase_create_wal_log_rec *xlrec = + (xl_dbase_create_wal_log_rec *) rec; + + appendStringInfo(buf, "create dir %u/%u", + xlrec->tablespace_id, xlrec->db_id); + } + else if (info == XLOG_DBASE_DROP) + { + xl_dbase_drop_rec *xlrec = (xl_dbase_drop_rec *) rec; + int i; + + appendStringInfoString(buf, "dir"); + for (i = 0; i < xlrec->ntablespaces; i++) + appendStringInfo(buf, " %u/%u", + xlrec->tablespace_ids[i], xlrec->db_id); + } +} + +const char * +dbase_identify(uint8 info) +{ + const char *id = NULL; + + switch (info & ~XLR_INFO_MASK) + { + case XLOG_DBASE_CREATE_FILE_COPY: + id = "CREATE_FILE_COPY"; + break; + case XLOG_DBASE_CREATE_WAL_LOG: + id = "CREATE_WAL_LOG"; + break; + case XLOG_DBASE_DROP: + id = "DROP"; + break; + } + + return id; +} diff --git a/src/backend/access/rmgrdesc/genericdesc.c b/src/backend/access/rmgrdesc/genericdesc.c new file mode 100644 index 0000000..877beb5 --- /dev/null +++ b/src/backend/access/rmgrdesc/genericdesc.c @@ -0,0 +1,56 @@ +/*------------------------------------------------------------------------- + * + * genericdesc.c + * rmgr descriptor routines for access/transam/generic_xlog.c + * + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/backend/access/rmgrdesc/genericdesc.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/generic_xlog.h" +#include "lib/stringinfo.h" +#include "storage/relfilenode.h" + +/* + * Description of generic xlog record: write page regions that this record + * overrides. + */ +void +generic_desc(StringInfo buf, XLogReaderState *record) +{ + Pointer ptr = XLogRecGetData(record), + end = ptr + XLogRecGetDataLen(record); + + while (ptr < end) + { + OffsetNumber offset, + length; + + memcpy(&offset, ptr, sizeof(offset)); + ptr += sizeof(offset); + memcpy(&length, ptr, sizeof(length)); + ptr += sizeof(length); + ptr += length; + + if (ptr < end) + appendStringInfo(buf, "offset %u, length %u; ", offset, length); + else + appendStringInfo(buf, "offset %u, length %u", offset, length); + } +} + +/* + * Identification of generic xlog record: we don't distinguish any subtypes + * inside generic xlog records. + */ +const char * +generic_identify(uint8 info) +{ + return "Generic"; +} diff --git a/src/backend/access/rmgrdesc/gindesc.c b/src/backend/access/rmgrdesc/gindesc.c new file mode 100644 index 0000000..57f7bce --- /dev/null +++ b/src/backend/access/rmgrdesc/gindesc.c @@ -0,0 +1,218 @@ +/*------------------------------------------------------------------------- + * + * gindesc.c + * rmgr descriptor routines for access/transam/gin/ginxlog.c + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/access/rmgrdesc/gindesc.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/ginxlog.h" +#include "access/xlogutils.h" +#include "lib/stringinfo.h" +#include "storage/relfilenode.h" + +static void +desc_recompress_leaf(StringInfo buf, ginxlogRecompressDataLeaf *insertData) +{ + int i; + char *walbuf = ((char *) insertData) + sizeof(ginxlogRecompressDataLeaf); + + appendStringInfo(buf, " %d segments:", (int) insertData->nactions); + + for (i = 0; i < insertData->nactions; i++) + { + uint8 a_segno = *((uint8 *) (walbuf++)); + uint8 a_action = *((uint8 *) (walbuf++)); + uint16 nitems = 0; + int newsegsize = 0; + + if (a_action == GIN_SEGMENT_INSERT || + a_action == GIN_SEGMENT_REPLACE) + { + newsegsize = SizeOfGinPostingList((GinPostingList *) walbuf); + walbuf += SHORTALIGN(newsegsize); + } + + if (a_action == GIN_SEGMENT_ADDITEMS) + { + memcpy(&nitems, walbuf, sizeof(uint16)); + walbuf += sizeof(uint16); + walbuf += nitems * sizeof(ItemPointerData); + } + + switch (a_action) + { + case GIN_SEGMENT_ADDITEMS: + appendStringInfo(buf, " %d (add %d items)", a_segno, nitems); + break; + case GIN_SEGMENT_DELETE: + appendStringInfo(buf, " %d (delete)", a_segno); + break; + case GIN_SEGMENT_INSERT: + appendStringInfo(buf, " %d (insert)", a_segno); + break; + case GIN_SEGMENT_REPLACE: + appendStringInfo(buf, " %d (replace)", a_segno); + break; + default: + appendStringInfo(buf, " %d unknown action %d ???", a_segno, a_action); + /* cannot decode unrecognized actions further */ + return; + } + } +} + +void +gin_desc(StringInfo buf, XLogReaderState *record) +{ + char *rec = XLogRecGetData(record); + uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK; + + switch (info) + { + case XLOG_GIN_CREATE_PTREE: + /* no further information */ + break; + case XLOG_GIN_INSERT: + { + ginxlogInsert *xlrec = (ginxlogInsert *) rec; + + appendStringInfo(buf, "isdata: %c isleaf: %c", + (xlrec->flags & GIN_INSERT_ISDATA) ? 'T' : 'F', + (xlrec->flags & GIN_INSERT_ISLEAF) ? 'T' : 'F'); + if (!(xlrec->flags & GIN_INSERT_ISLEAF)) + { + char *payload = rec + sizeof(ginxlogInsert); + BlockNumber leftChildBlkno; + BlockNumber rightChildBlkno; + + leftChildBlkno = BlockIdGetBlockNumber((BlockId) payload); + payload += sizeof(BlockIdData); + rightChildBlkno = BlockIdGetBlockNumber((BlockId) payload); + payload += sizeof(BlockNumber); + appendStringInfo(buf, " children: %u/%u", + leftChildBlkno, rightChildBlkno); + } + if (XLogRecHasBlockImage(record, 0)) + { + if (XLogRecBlockImageApply(record, 0)) + appendStringInfoString(buf, " (full page image)"); + else + appendStringInfoString(buf, " (full page image, for WAL verification)"); + } + else + { + char *payload = XLogRecGetBlockData(record, 0, NULL); + + if (!(xlrec->flags & GIN_INSERT_ISDATA)) + appendStringInfo(buf, " isdelete: %c", + (((ginxlogInsertEntry *) payload)->isDelete) ? 'T' : 'F'); + else if (xlrec->flags & GIN_INSERT_ISLEAF) + desc_recompress_leaf(buf, (ginxlogRecompressDataLeaf *) payload); + else + { + ginxlogInsertDataInternal *insertData = + (ginxlogInsertDataInternal *) payload; + + appendStringInfo(buf, " pitem: %u-%u/%u", + PostingItemGetBlockNumber(&insertData->newitem), + ItemPointerGetBlockNumber(&insertData->newitem.key), + ItemPointerGetOffsetNumber(&insertData->newitem.key)); + } + } + } + break; + case XLOG_GIN_SPLIT: + { + ginxlogSplit *xlrec = (ginxlogSplit *) rec; + + appendStringInfo(buf, "isrootsplit: %c", + (((ginxlogSplit *) rec)->flags & GIN_SPLIT_ROOT) ? 'T' : 'F'); + appendStringInfo(buf, " isdata: %c isleaf: %c", + (xlrec->flags & GIN_INSERT_ISDATA) ? 'T' : 'F', + (xlrec->flags & GIN_INSERT_ISLEAF) ? 'T' : 'F'); + } + break; + case XLOG_GIN_VACUUM_PAGE: + /* no further information */ + break; + case XLOG_GIN_VACUUM_DATA_LEAF_PAGE: + { + if (XLogRecHasBlockImage(record, 0)) + { + if (XLogRecBlockImageApply(record, 0)) + appendStringInfoString(buf, " (full page image)"); + else + appendStringInfoString(buf, " (full page image, for WAL verification)"); + } + else + { + ginxlogVacuumDataLeafPage *xlrec = + (ginxlogVacuumDataLeafPage *) XLogRecGetBlockData(record, 0, NULL); + + desc_recompress_leaf(buf, &xlrec->data); + } + } + break; + case XLOG_GIN_DELETE_PAGE: + /* no further information */ + break; + case XLOG_GIN_UPDATE_META_PAGE: + /* no further information */ + break; + case XLOG_GIN_INSERT_LISTPAGE: + /* no further information */ + break; + case XLOG_GIN_DELETE_LISTPAGE: + appendStringInfo(buf, "ndeleted: %d", + ((ginxlogDeleteListPages *) rec)->ndeleted); + break; + } +} + +const char * +gin_identify(uint8 info) +{ + const char *id = NULL; + + switch (info & ~XLR_INFO_MASK) + { + case XLOG_GIN_CREATE_PTREE: + id = "CREATE_PTREE"; + break; + case XLOG_GIN_INSERT: + id = "INSERT"; + break; + case XLOG_GIN_SPLIT: + id = "SPLIT"; + break; + case XLOG_GIN_VACUUM_PAGE: + id = "VACUUM_PAGE"; + break; + case XLOG_GIN_VACUUM_DATA_LEAF_PAGE: + id = "VACUUM_DATA_LEAF_PAGE"; + break; + case XLOG_GIN_DELETE_PAGE: + id = "DELETE_PAGE"; + break; + case XLOG_GIN_UPDATE_META_PAGE: + id = "UPDATE_META_PAGE"; + break; + case XLOG_GIN_INSERT_LISTPAGE: + id = "INSERT_LISTPAGE"; + break; + case XLOG_GIN_DELETE_LISTPAGE: + id = "DELETE_LISTPAGE"; + break; + } + + return id; +} diff --git a/src/backend/access/rmgrdesc/gistdesc.c b/src/backend/access/rmgrdesc/gistdesc.c new file mode 100644 index 0000000..d0c8e24 --- /dev/null +++ b/src/backend/access/rmgrdesc/gistdesc.c @@ -0,0 +1,116 @@ +/*------------------------------------------------------------------------- + * + * gistdesc.c + * rmgr descriptor routines for access/gist/gistxlog.c + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/access/rmgrdesc/gistdesc.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/gistxlog.h" +#include "lib/stringinfo.h" +#include "storage/relfilenode.h" + +static void +out_gistxlogPageUpdate(StringInfo buf, gistxlogPageUpdate *xlrec) +{ +} + +static void +out_gistxlogPageReuse(StringInfo buf, gistxlogPageReuse *xlrec) +{ + appendStringInfo(buf, "rel %u/%u/%u; blk %u; latestRemovedXid %u:%u", + xlrec->node.spcNode, xlrec->node.dbNode, + xlrec->node.relNode, xlrec->block, + EpochFromFullTransactionId(xlrec->latestRemovedFullXid), + XidFromFullTransactionId(xlrec->latestRemovedFullXid)); +} + +static void +out_gistxlogDelete(StringInfo buf, gistxlogDelete *xlrec) +{ + appendStringInfo(buf, "delete: latestRemovedXid %u, nitems: %u", + xlrec->latestRemovedXid, xlrec->ntodelete); +} + +static void +out_gistxlogPageSplit(StringInfo buf, gistxlogPageSplit *xlrec) +{ + appendStringInfo(buf, "page_split: splits to %d pages", + xlrec->npage); +} + +static void +out_gistxlogPageDelete(StringInfo buf, gistxlogPageDelete *xlrec) +{ + appendStringInfo(buf, "deleteXid %u:%u; downlink %u", + EpochFromFullTransactionId(xlrec->deleteXid), + XidFromFullTransactionId(xlrec->deleteXid), + xlrec->downlinkOffset); +} + +void +gist_desc(StringInfo buf, XLogReaderState *record) +{ + char *rec = XLogRecGetData(record); + uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK; + + switch (info) + { + case XLOG_GIST_PAGE_UPDATE: + out_gistxlogPageUpdate(buf, (gistxlogPageUpdate *) rec); + break; + case XLOG_GIST_PAGE_REUSE: + out_gistxlogPageReuse(buf, (gistxlogPageReuse *) rec); + break; + case XLOG_GIST_DELETE: + out_gistxlogDelete(buf, (gistxlogDelete *) rec); + break; + case XLOG_GIST_PAGE_SPLIT: + out_gistxlogPageSplit(buf, (gistxlogPageSplit *) rec); + break; + case XLOG_GIST_PAGE_DELETE: + out_gistxlogPageDelete(buf, (gistxlogPageDelete *) rec); + break; + case XLOG_GIST_ASSIGN_LSN: + /* No details to write out */ + break; + } +} + +const char * +gist_identify(uint8 info) +{ + const char *id = NULL; + + switch (info & ~XLR_INFO_MASK) + { + case XLOG_GIST_PAGE_UPDATE: + id = "PAGE_UPDATE"; + break; + case XLOG_GIST_DELETE: + id = "DELETE"; + break; + case XLOG_GIST_PAGE_REUSE: + id = "PAGE_REUSE"; + break; + case XLOG_GIST_PAGE_SPLIT: + id = "PAGE_SPLIT"; + break; + case XLOG_GIST_PAGE_DELETE: + id = "PAGE_DELETE"; + break; + case XLOG_GIST_ASSIGN_LSN: + id = "ASSIGN_LSN"; + break; + } + + return id; +} diff --git a/src/backend/access/rmgrdesc/hashdesc.c b/src/backend/access/rmgrdesc/hashdesc.c new file mode 100644 index 0000000..ef443bd --- /dev/null +++ b/src/backend/access/rmgrdesc/hashdesc.c @@ -0,0 +1,172 @@ +/*------------------------------------------------------------------------- + * + * hashdesc.c + * rmgr descriptor routines for access/hash/hash.c + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/access/rmgrdesc/hashdesc.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/hash_xlog.h" + +void +hash_desc(StringInfo buf, XLogReaderState *record) +{ + char *rec = XLogRecGetData(record); + uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK; + + switch (info) + { + case XLOG_HASH_INIT_META_PAGE: + { + xl_hash_init_meta_page *xlrec = (xl_hash_init_meta_page *) rec; + + appendStringInfo(buf, "num_tuples %g, fillfactor %d", + xlrec->num_tuples, xlrec->ffactor); + break; + } + case XLOG_HASH_INIT_BITMAP_PAGE: + { + xl_hash_init_bitmap_page *xlrec = (xl_hash_init_bitmap_page *) rec; + + appendStringInfo(buf, "bmsize %d", xlrec->bmsize); + break; + } + case XLOG_HASH_INSERT: + { + xl_hash_insert *xlrec = (xl_hash_insert *) rec; + + appendStringInfo(buf, "off %u", xlrec->offnum); + break; + } + case XLOG_HASH_ADD_OVFL_PAGE: + { + xl_hash_add_ovfl_page *xlrec = (xl_hash_add_ovfl_page *) rec; + + appendStringInfo(buf, "bmsize %d, bmpage_found %c", + xlrec->bmsize, (xlrec->bmpage_found) ? 'T' : 'F'); + break; + } + case XLOG_HASH_SPLIT_ALLOCATE_PAGE: + { + xl_hash_split_allocate_page *xlrec = (xl_hash_split_allocate_page *) rec; + + appendStringInfo(buf, "new_bucket %u, meta_page_masks_updated %c, issplitpoint_changed %c", + xlrec->new_bucket, + (xlrec->flags & XLH_SPLIT_META_UPDATE_MASKS) ? 'T' : 'F', + (xlrec->flags & XLH_SPLIT_META_UPDATE_SPLITPOINT) ? 'T' : 'F'); + break; + } + case XLOG_HASH_SPLIT_COMPLETE: + { + xl_hash_split_complete *xlrec = (xl_hash_split_complete *) rec; + + appendStringInfo(buf, "old_bucket_flag %u, new_bucket_flag %u", + xlrec->old_bucket_flag, xlrec->new_bucket_flag); + break; + } + case XLOG_HASH_MOVE_PAGE_CONTENTS: + { + xl_hash_move_page_contents *xlrec = (xl_hash_move_page_contents *) rec; + + appendStringInfo(buf, "ntups %d, is_primary %c", + xlrec->ntups, + xlrec->is_prim_bucket_same_wrt ? 'T' : 'F'); + break; + } + case XLOG_HASH_SQUEEZE_PAGE: + { + xl_hash_squeeze_page *xlrec = (xl_hash_squeeze_page *) rec; + + appendStringInfo(buf, "prevblkno %u, nextblkno %u, ntups %d, is_primary %c", + xlrec->prevblkno, + xlrec->nextblkno, + xlrec->ntups, + xlrec->is_prim_bucket_same_wrt ? 'T' : 'F'); + break; + } + case XLOG_HASH_DELETE: + { + xl_hash_delete *xlrec = (xl_hash_delete *) rec; + + appendStringInfo(buf, "clear_dead_marking %c, is_primary %c", + xlrec->clear_dead_marking ? 'T' : 'F', + xlrec->is_primary_bucket_page ? 'T' : 'F'); + break; + } + case XLOG_HASH_UPDATE_META_PAGE: + { + xl_hash_update_meta_page *xlrec = (xl_hash_update_meta_page *) rec; + + appendStringInfo(buf, "ntuples %g", + xlrec->ntuples); + break; + } + case XLOG_HASH_VACUUM_ONE_PAGE: + { + xl_hash_vacuum_one_page *xlrec = (xl_hash_vacuum_one_page *) rec; + + appendStringInfo(buf, "ntuples %d, latestRemovedXid %u", + xlrec->ntuples, + xlrec->latestRemovedXid); + break; + } + } +} + +const char * +hash_identify(uint8 info) +{ + const char *id = NULL; + + switch (info & ~XLR_INFO_MASK) + { + case XLOG_HASH_INIT_META_PAGE: + id = "INIT_META_PAGE"; + break; + case XLOG_HASH_INIT_BITMAP_PAGE: + id = "INIT_BITMAP_PAGE"; + break; + case XLOG_HASH_INSERT: + id = "INSERT"; + break; + case XLOG_HASH_ADD_OVFL_PAGE: + id = "ADD_OVFL_PAGE"; + break; + case XLOG_HASH_SPLIT_ALLOCATE_PAGE: + id = "SPLIT_ALLOCATE_PAGE"; + break; + case XLOG_HASH_SPLIT_PAGE: + id = "SPLIT_PAGE"; + break; + case XLOG_HASH_SPLIT_COMPLETE: + id = "SPLIT_COMPLETE"; + break; + case XLOG_HASH_MOVE_PAGE_CONTENTS: + id = "MOVE_PAGE_CONTENTS"; + break; + case XLOG_HASH_SQUEEZE_PAGE: + id = "SQUEEZE_PAGE"; + break; + case XLOG_HASH_DELETE: + id = "DELETE"; + break; + case XLOG_HASH_SPLIT_CLEANUP: + id = "SPLIT_CLEANUP"; + break; + case XLOG_HASH_UPDATE_META_PAGE: + id = "UPDATE_META_PAGE"; + break; + case XLOG_HASH_VACUUM_ONE_PAGE: + id = "VACUUM_ONE_PAGE"; + } + + return id; +} diff --git a/src/backend/access/rmgrdesc/heapdesc.c b/src/backend/access/rmgrdesc/heapdesc.c new file mode 100644 index 0000000..6238085 --- /dev/null +++ b/src/backend/access/rmgrdesc/heapdesc.c @@ -0,0 +1,265 @@ +/*------------------------------------------------------------------------- + * + * heapdesc.c + * rmgr descriptor routines for access/heap/heapam.c + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/access/rmgrdesc/heapdesc.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/heapam_xlog.h" + +static void +out_infobits(StringInfo buf, uint8 infobits) +{ + if (infobits & XLHL_XMAX_IS_MULTI) + appendStringInfoString(buf, "IS_MULTI "); + if (infobits & XLHL_XMAX_LOCK_ONLY) + appendStringInfoString(buf, "LOCK_ONLY "); + if (infobits & XLHL_XMAX_EXCL_LOCK) + appendStringInfoString(buf, "EXCL_LOCK "); + if (infobits & XLHL_XMAX_KEYSHR_LOCK) + appendStringInfoString(buf, "KEYSHR_LOCK "); + if (infobits & XLHL_KEYS_UPDATED) + appendStringInfoString(buf, "KEYS_UPDATED "); +} + +void +heap_desc(StringInfo buf, XLogReaderState *record) +{ + char *rec = XLogRecGetData(record); + uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK; + + info &= XLOG_HEAP_OPMASK; + if (info == XLOG_HEAP_INSERT) + { + xl_heap_insert *xlrec = (xl_heap_insert *) rec; + + 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 ", + xlrec->offnum, + xlrec->flags); + out_infobits(buf, xlrec->infobits_set); + } + else if (info == XLOG_HEAP_UPDATE) + { + xl_heap_update *xlrec = (xl_heap_update *) rec; + + 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", + xlrec->new_offnum, + xlrec->new_xmax); + } + else if (info == XLOG_HEAP_HOT_UPDATE) + { + xl_heap_update *xlrec = (xl_heap_update *) rec; + + 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", + xlrec->new_offnum, + xlrec->new_xmax); + } + else if (info == XLOG_HEAP_TRUNCATE) + { + xl_heap_truncate *xlrec = (xl_heap_truncate *) rec; + int i; + + if (xlrec->flags & XLH_TRUNCATE_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]); + } + else if (info == XLOG_HEAP_CONFIRM) + { + xl_heap_confirm *xlrec = (xl_heap_confirm *) rec; + + 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 ", + xlrec->offnum, xlrec->locking_xid, xlrec->flags); + out_infobits(buf, xlrec->infobits_set); + } + else if (info == XLOG_HEAP_INPLACE) + { + xl_heap_inplace *xlrec = (xl_heap_inplace *) rec; + + appendStringInfo(buf, "off %u", xlrec->offnum); + } +} +void +heap2_desc(StringInfo buf, XLogReaderState *record) +{ + char *rec = XLogRecGetData(record); + uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK; + + info &= XLOG_HEAP_OPMASK; + if (info == XLOG_HEAP2_PRUNE) + { + xl_heap_prune *xlrec = (xl_heap_prune *) rec; + + appendStringInfo(buf, "latestRemovedXid %u nredirected %u ndead %u", + xlrec->latestRemovedXid, + xlrec->nredirected, + xlrec->ndead); + } + else if (info == XLOG_HEAP2_VACUUM) + { + xl_heap_vacuum *xlrec = (xl_heap_vacuum *) rec; + + appendStringInfo(buf, "nunused %u", xlrec->nunused); + } + else if (info == XLOG_HEAP2_FREEZE_PAGE) + { + xl_heap_freeze_page *xlrec = (xl_heap_freeze_page *) rec; + + appendStringInfo(buf, "cutoff xid %u ntuples %u", + xlrec->cutoff_xid, xlrec->ntuples); + } + else if (info == XLOG_HEAP2_VISIBLE) + { + xl_heap_visible *xlrec = (xl_heap_visible *) rec; + + appendStringInfo(buf, "cutoff xid %u flags 0x%02X", + xlrec->cutoff_xid, xlrec->flags); + } + else if (info == XLOG_HEAP2_MULTI_INSERT) + { + xl_heap_multi_insert *xlrec = (xl_heap_multi_insert *) rec; + + appendStringInfo(buf, "%d tuples flags 0x%02X", xlrec->ntuples, + xlrec->flags); + } + 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 ", + xlrec->offnum, xlrec->xmax, xlrec->flags); + out_infobits(buf, xlrec->infobits_set); + } + else if (info == XLOG_HEAP2_NEW_CID) + { + xl_heap_new_cid *xlrec = (xl_heap_new_cid *) rec; + + appendStringInfo(buf, "rel %u/%u/%u; tid %u/%u", + xlrec->target_node.spcNode, + xlrec->target_node.dbNode, + xlrec->target_node.relNode, + ItemPointerGetBlockNumber(&(xlrec->target_tid)), + ItemPointerGetOffsetNumber(&(xlrec->target_tid))); + appendStringInfo(buf, "; cmin: %u, cmax: %u, combo: %u", + xlrec->cmin, xlrec->cmax, xlrec->combocid); + } +} + +const char * +heap_identify(uint8 info) +{ + const char *id = NULL; + + switch (info & ~XLR_INFO_MASK) + { + case XLOG_HEAP_INSERT: + id = "INSERT"; + break; + case XLOG_HEAP_INSERT | XLOG_HEAP_INIT_PAGE: + id = "INSERT+INIT"; + break; + case XLOG_HEAP_DELETE: + id = "DELETE"; + break; + case XLOG_HEAP_UPDATE: + id = "UPDATE"; + break; + case XLOG_HEAP_UPDATE | XLOG_HEAP_INIT_PAGE: + id = "UPDATE+INIT"; + break; + case XLOG_HEAP_HOT_UPDATE: + id = "HOT_UPDATE"; + break; + case XLOG_HEAP_HOT_UPDATE | XLOG_HEAP_INIT_PAGE: + id = "HOT_UPDATE+INIT"; + break; + case XLOG_HEAP_TRUNCATE: + id = "TRUNCATE"; + break; + case XLOG_HEAP_CONFIRM: + id = "HEAP_CONFIRM"; + break; + case XLOG_HEAP_LOCK: + id = "LOCK"; + break; + case XLOG_HEAP_INPLACE: + id = "INPLACE"; + break; + } + + return id; +} + +const char * +heap2_identify(uint8 info) +{ + const char *id = NULL; + + switch (info & ~XLR_INFO_MASK) + { + case XLOG_HEAP2_PRUNE: + id = "PRUNE"; + break; + case XLOG_HEAP2_VACUUM: + id = "VACUUM"; + break; + case XLOG_HEAP2_FREEZE_PAGE: + id = "FREEZE_PAGE"; + break; + case XLOG_HEAP2_VISIBLE: + id = "VISIBLE"; + break; + case XLOG_HEAP2_MULTI_INSERT: + id = "MULTI_INSERT"; + break; + case XLOG_HEAP2_MULTI_INSERT | XLOG_HEAP_INIT_PAGE: + id = "MULTI_INSERT+INIT"; + break; + case XLOG_HEAP2_LOCK_UPDATED: + id = "LOCK_UPDATED"; + break; + case XLOG_HEAP2_NEW_CID: + id = "NEW_CID"; + break; + case XLOG_HEAP2_REWRITE: + id = "REWRITE"; + break; + } + + return id; +} diff --git a/src/backend/access/rmgrdesc/logicalmsgdesc.c b/src/backend/access/rmgrdesc/logicalmsgdesc.c new file mode 100644 index 0000000..08e03aa --- /dev/null +++ b/src/backend/access/rmgrdesc/logicalmsgdesc.c @@ -0,0 +1,52 @@ +/*------------------------------------------------------------------------- + * + * logicalmsgdesc.c + * rmgr descriptor routines for replication/logical/message.c + * + * Portions Copyright (c) 2015-2022, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * src/backend/access/rmgrdesc/logicalmsgdesc.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "replication/message.h" + +void +logicalmsg_desc(StringInfo buf, XLogReaderState *record) +{ + char *rec = XLogRecGetData(record); + uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK; + + if (info == XLOG_LOGICAL_MESSAGE) + { + xl_logical_message *xlrec = (xl_logical_message *) rec; + char *prefix = xlrec->message; + char *message = xlrec->message + xlrec->prefix_size; + char *sep = ""; + + Assert(prefix[xlrec->prefix_size - 1] == '\0'); + + appendStringInfo(buf, "%s, prefix \"%s\"; payload (%zu bytes): ", + xlrec->transactional ? "transactional" : "non-transactional", + prefix, xlrec->message_size); + /* Write message payload as a series of hex bytes */ + for (int cnt = 0; cnt < xlrec->message_size; cnt++) + { + appendStringInfo(buf, "%s%02X", sep, (unsigned char) message[cnt]); + sep = " "; + } + } +} + +const char * +logicalmsg_identify(uint8 info) +{ + if ((info & ~XLR_INFO_MASK) == XLOG_LOGICAL_MESSAGE) + return "MESSAGE"; + + return NULL; +} diff --git a/src/backend/access/rmgrdesc/mxactdesc.c b/src/backend/access/rmgrdesc/mxactdesc.c new file mode 100644 index 0000000..7076be2 --- /dev/null +++ b/src/backend/access/rmgrdesc/mxactdesc.c @@ -0,0 +1,105 @@ +/*------------------------------------------------------------------------- + * + * mxactdesc.c + * rmgr descriptor routines for access/transam/multixact.c + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/access/rmgrdesc/mxactdesc.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/multixact.h" + +static void +out_member(StringInfo buf, MultiXactMember *member) +{ + appendStringInfo(buf, "%u ", member->xid); + switch (member->status) + { + case MultiXactStatusForKeyShare: + appendStringInfoString(buf, "(keysh) "); + break; + case MultiXactStatusForShare: + appendStringInfoString(buf, "(sh) "); + break; + case MultiXactStatusForNoKeyUpdate: + appendStringInfoString(buf, "(fornokeyupd) "); + break; + case MultiXactStatusForUpdate: + appendStringInfoString(buf, "(forupd) "); + break; + case MultiXactStatusNoKeyUpdate: + appendStringInfoString(buf, "(nokeyupd) "); + break; + case MultiXactStatusUpdate: + appendStringInfoString(buf, "(upd) "); + break; + default: + appendStringInfoString(buf, "(unk) "); + break; + } +} + +void +multixact_desc(StringInfo buf, XLogReaderState *record) +{ + char *rec = XLogRecGetData(record); + uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK; + + if (info == XLOG_MULTIXACT_ZERO_OFF_PAGE || + info == XLOG_MULTIXACT_ZERO_MEM_PAGE) + { + int pageno; + + memcpy(&pageno, rec, sizeof(int)); + appendStringInfo(buf, "%d", pageno); + } + else if (info == XLOG_MULTIXACT_CREATE_ID) + { + xl_multixact_create *xlrec = (xl_multixact_create *) rec; + int i; + + appendStringInfo(buf, "%u offset %u nmembers %d: ", xlrec->mid, + xlrec->moff, xlrec->nmembers); + for (i = 0; i < xlrec->nmembers; i++) + out_member(buf, &xlrec->members[i]); + } + else if (info == XLOG_MULTIXACT_TRUNCATE_ID) + { + xl_multixact_truncate *xlrec = (xl_multixact_truncate *) rec; + + appendStringInfo(buf, "offsets [%u, %u), members [%u, %u)", + xlrec->startTruncOff, xlrec->endTruncOff, + xlrec->startTruncMemb, xlrec->endTruncMemb); + } +} + +const char * +multixact_identify(uint8 info) +{ + const char *id = NULL; + + switch (info & ~XLR_INFO_MASK) + { + case XLOG_MULTIXACT_ZERO_OFF_PAGE: + id = "ZERO_OFF_PAGE"; + break; + case XLOG_MULTIXACT_ZERO_MEM_PAGE: + id = "ZERO_MEM_PAGE"; + break; + case XLOG_MULTIXACT_CREATE_ID: + id = "CREATE_ID"; + break; + case XLOG_MULTIXACT_TRUNCATE_ID: + id = "TRUNCATE_ID"; + break; + } + + return id; +} diff --git a/src/backend/access/rmgrdesc/nbtdesc.c b/src/backend/access/rmgrdesc/nbtdesc.c new file mode 100644 index 0000000..dfbbf4e --- /dev/null +++ b/src/backend/access/rmgrdesc/nbtdesc.c @@ -0,0 +1,178 @@ +/*------------------------------------------------------------------------- + * + * nbtdesc.c + * rmgr descriptor routines for access/nbtree/nbtxlog.c + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/access/rmgrdesc/nbtdesc.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/nbtxlog.h" + +void +btree_desc(StringInfo buf, XLogReaderState *record) +{ + char *rec = XLogRecGetData(record); + uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK; + + switch (info) + { + case XLOG_BTREE_INSERT_LEAF: + case XLOG_BTREE_INSERT_UPPER: + case XLOG_BTREE_INSERT_META: + case XLOG_BTREE_INSERT_POST: + { + xl_btree_insert *xlrec = (xl_btree_insert *) rec; + + appendStringInfo(buf, "off %u", xlrec->offnum); + break; + } + case XLOG_BTREE_SPLIT_L: + case XLOG_BTREE_SPLIT_R: + { + xl_btree_split *xlrec = (xl_btree_split *) rec; + + appendStringInfo(buf, "level %u, firstrightoff %d, newitemoff %d, postingoff %d", + xlrec->level, xlrec->firstrightoff, + xlrec->newitemoff, xlrec->postingoff); + break; + } + case XLOG_BTREE_DEDUP: + { + xl_btree_dedup *xlrec = (xl_btree_dedup *) rec; + + appendStringInfo(buf, "nintervals %u", xlrec->nintervals); + break; + } + case XLOG_BTREE_VACUUM: + { + xl_btree_vacuum *xlrec = (xl_btree_vacuum *) rec; + + appendStringInfo(buf, "ndeleted %u; nupdated %u", + xlrec->ndeleted, xlrec->nupdated); + break; + } + case XLOG_BTREE_DELETE: + { + xl_btree_delete *xlrec = (xl_btree_delete *) rec; + + appendStringInfo(buf, "latestRemovedXid %u; ndeleted %u; nupdated %u", + xlrec->latestRemovedXid, xlrec->ndeleted, xlrec->nupdated); + break; + } + case XLOG_BTREE_MARK_PAGE_HALFDEAD: + { + xl_btree_mark_page_halfdead *xlrec = (xl_btree_mark_page_halfdead *) rec; + + appendStringInfo(buf, "topparent %u; leaf %u; left %u; right %u", + xlrec->topparent, xlrec->leafblk, xlrec->leftblk, xlrec->rightblk); + break; + } + case XLOG_BTREE_UNLINK_PAGE_META: + case XLOG_BTREE_UNLINK_PAGE: + { + xl_btree_unlink_page *xlrec = (xl_btree_unlink_page *) rec; + + appendStringInfo(buf, "left %u; right %u; level %u; safexid %u:%u; ", + xlrec->leftsib, xlrec->rightsib, xlrec->level, + EpochFromFullTransactionId(xlrec->safexid), + XidFromFullTransactionId(xlrec->safexid)); + appendStringInfo(buf, "leafleft %u; leafright %u; leaftopparent %u", + xlrec->leafleftsib, xlrec->leafrightsib, + xlrec->leaftopparent); + break; + } + case XLOG_BTREE_NEWROOT: + { + xl_btree_newroot *xlrec = (xl_btree_newroot *) rec; + + appendStringInfo(buf, "lev %u", xlrec->level); + break; + } + case XLOG_BTREE_REUSE_PAGE: + { + xl_btree_reuse_page *xlrec = (xl_btree_reuse_page *) rec; + + appendStringInfo(buf, "rel %u/%u/%u; latestRemovedXid %u:%u", + xlrec->node.spcNode, xlrec->node.dbNode, + xlrec->node.relNode, + EpochFromFullTransactionId(xlrec->latestRemovedFullXid), + XidFromFullTransactionId(xlrec->latestRemovedFullXid)); + break; + } + case XLOG_BTREE_META_CLEANUP: + { + xl_btree_metadata *xlrec; + + xlrec = (xl_btree_metadata *) XLogRecGetBlockData(record, 0, + NULL); + appendStringInfo(buf, "last_cleanup_num_delpages %u", + xlrec->last_cleanup_num_delpages); + break; + } + } +} + +const char * +btree_identify(uint8 info) +{ + const char *id = NULL; + + switch (info & ~XLR_INFO_MASK) + { + case XLOG_BTREE_INSERT_LEAF: + id = "INSERT_LEAF"; + break; + case XLOG_BTREE_INSERT_UPPER: + id = "INSERT_UPPER"; + break; + case XLOG_BTREE_INSERT_META: + id = "INSERT_META"; + break; + case XLOG_BTREE_SPLIT_L: + id = "SPLIT_L"; + break; + case XLOG_BTREE_SPLIT_R: + id = "SPLIT_R"; + break; + case XLOG_BTREE_INSERT_POST: + id = "INSERT_POST"; + break; + case XLOG_BTREE_DEDUP: + id = "DEDUP"; + break; + case XLOG_BTREE_VACUUM: + id = "VACUUM"; + break; + case XLOG_BTREE_DELETE: + id = "DELETE"; + break; + case XLOG_BTREE_MARK_PAGE_HALFDEAD: + id = "MARK_PAGE_HALFDEAD"; + break; + case XLOG_BTREE_UNLINK_PAGE: + id = "UNLINK_PAGE"; + break; + case XLOG_BTREE_UNLINK_PAGE_META: + id = "UNLINK_PAGE_META"; + break; + case XLOG_BTREE_NEWROOT: + id = "NEWROOT"; + break; + case XLOG_BTREE_REUSE_PAGE: + id = "REUSE_PAGE"; + break; + case XLOG_BTREE_META_CLEANUP: + id = "META_CLEANUP"; + break; + } + + return id; +} diff --git a/src/backend/access/rmgrdesc/relmapdesc.c b/src/backend/access/rmgrdesc/relmapdesc.c new file mode 100644 index 0000000..43d63eb --- /dev/null +++ b/src/backend/access/rmgrdesc/relmapdesc.c @@ -0,0 +1,47 @@ +/*------------------------------------------------------------------------- + * + * relmapdesc.c + * rmgr descriptor routines for utils/cache/relmapper.c + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/access/rmgrdesc/relmapdesc.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "utils/relmapper.h" + +void +relmap_desc(StringInfo buf, XLogReaderState *record) +{ + char *rec = XLogRecGetData(record); + uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK; + + if (info == XLOG_RELMAP_UPDATE) + { + xl_relmap_update *xlrec = (xl_relmap_update *) rec; + + appendStringInfo(buf, "database %u tablespace %u size %d", + xlrec->dbid, xlrec->tsid, xlrec->nbytes); + } +} + +const char * +relmap_identify(uint8 info) +{ + const char *id = NULL; + + switch (info & ~XLR_INFO_MASK) + { + case XLOG_RELMAP_UPDATE: + id = "UPDATE"; + break; + } + + return id; +} diff --git a/src/backend/access/rmgrdesc/replorigindesc.c b/src/backend/access/rmgrdesc/replorigindesc.c new file mode 100644 index 0000000..e3213b1 --- /dev/null +++ b/src/backend/access/rmgrdesc/replorigindesc.c @@ -0,0 +1,62 @@ +/*------------------------------------------------------------------------- + * + * replorigindesc.c + * rmgr descriptor routines for replication/logical/origin.c + * + * Portions Copyright (c) 2015-2022, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * src/backend/access/rmgrdesc/replorigindesc.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "replication/origin.h" + +void +replorigin_desc(StringInfo buf, XLogReaderState *record) +{ + char *rec = XLogRecGetData(record); + uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK; + + switch (info) + { + case XLOG_REPLORIGIN_SET: + { + xl_replorigin_set *xlrec; + + xlrec = (xl_replorigin_set *) rec; + + appendStringInfo(buf, "set %u; lsn %X/%X; force: %d", + xlrec->node_id, + LSN_FORMAT_ARGS(xlrec->remote_lsn), + xlrec->force); + break; + } + case XLOG_REPLORIGIN_DROP: + { + xl_replorigin_drop *xlrec; + + xlrec = (xl_replorigin_drop *) rec; + + appendStringInfo(buf, "drop %u", xlrec->node_id); + break; + } + } +} + +const char * +replorigin_identify(uint8 info) +{ + switch (info) + { + case XLOG_REPLORIGIN_SET: + return "SET"; + case XLOG_REPLORIGIN_DROP: + return "DROP"; + default: + return NULL; + } +} diff --git a/src/backend/access/rmgrdesc/seqdesc.c b/src/backend/access/rmgrdesc/seqdesc.c new file mode 100644 index 0000000..d9b1e60 --- /dev/null +++ b/src/backend/access/rmgrdesc/seqdesc.c @@ -0,0 +1,46 @@ +/*------------------------------------------------------------------------- + * + * seqdesc.c + * rmgr descriptor routines for commands/sequence.c + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/access/rmgrdesc/seqdesc.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "commands/sequence.h" + + +void +seq_desc(StringInfo buf, XLogReaderState *record) +{ + char *rec = XLogRecGetData(record); + uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK; + xl_seq_rec *xlrec = (xl_seq_rec *) rec; + + if (info == XLOG_SEQ_LOG) + appendStringInfo(buf, "rel %u/%u/%u", + xlrec->node.spcNode, xlrec->node.dbNode, + xlrec->node.relNode); +} + +const char * +seq_identify(uint8 info) +{ + const char *id = NULL; + + switch (info & ~XLR_INFO_MASK) + { + case XLOG_SEQ_LOG: + id = "LOG"; + break; + } + + return id; +} diff --git a/src/backend/access/rmgrdesc/smgrdesc.c b/src/backend/access/rmgrdesc/smgrdesc.c new file mode 100644 index 0000000..7547813 --- /dev/null +++ b/src/backend/access/rmgrdesc/smgrdesc.c @@ -0,0 +1,61 @@ +/*------------------------------------------------------------------------- + * + * smgrdesc.c + * rmgr descriptor routines for catalog/storage.c + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/access/rmgrdesc/smgrdesc.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "catalog/storage_xlog.h" + + +void +smgr_desc(StringInfo buf, XLogReaderState *record) +{ + char *rec = XLogRecGetData(record); + uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK; + + if (info == XLOG_SMGR_CREATE) + { + xl_smgr_create *xlrec = (xl_smgr_create *) rec; + char *path = relpathperm(xlrec->rnode, xlrec->forkNum); + + appendStringInfoString(buf, path); + pfree(path); + } + else if (info == XLOG_SMGR_TRUNCATE) + { + xl_smgr_truncate *xlrec = (xl_smgr_truncate *) rec; + char *path = relpathperm(xlrec->rnode, MAIN_FORKNUM); + + appendStringInfo(buf, "%s to %u blocks flags %d", path, + xlrec->blkno, xlrec->flags); + pfree(path); + } +} + +const char * +smgr_identify(uint8 info) +{ + const char *id = NULL; + + switch (info & ~XLR_INFO_MASK) + { + case XLOG_SMGR_CREATE: + id = "CREATE"; + break; + case XLOG_SMGR_TRUNCATE: + id = "TRUNCATE"; + break; + } + + return id; +} diff --git a/src/backend/access/rmgrdesc/spgdesc.c b/src/backend/access/rmgrdesc/spgdesc.c new file mode 100644 index 0000000..d5d921a --- /dev/null +++ b/src/backend/access/rmgrdesc/spgdesc.c @@ -0,0 +1,164 @@ +/*------------------------------------------------------------------------- + * + * spgdesc.c + * rmgr descriptor routines for access/spgist/spgxlog.c + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/access/rmgrdesc/spgdesc.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/spgxlog.h" + +void +spg_desc(StringInfo buf, XLogReaderState *record) +{ + char *rec = XLogRecGetData(record); + uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK; + + switch (info) + { + case XLOG_SPGIST_ADD_LEAF: + { + spgxlogAddLeaf *xlrec = (spgxlogAddLeaf *) rec; + + appendStringInfo(buf, "off: %u, headoff: %u, parentoff: %u, nodeI: %u", + xlrec->offnumLeaf, xlrec->offnumHeadLeaf, + xlrec->offnumParent, xlrec->nodeI); + if (xlrec->newPage) + appendStringInfoString(buf, " (newpage)"); + if (xlrec->storesNulls) + appendStringInfoString(buf, " (nulls)"); + } + break; + case XLOG_SPGIST_MOVE_LEAFS: + { + spgxlogMoveLeafs *xlrec = (spgxlogMoveLeafs *) rec; + + appendStringInfo(buf, "nmoves: %u, parentoff: %u, nodeI: %u", + xlrec->nMoves, + xlrec->offnumParent, xlrec->nodeI); + if (xlrec->newPage) + appendStringInfoString(buf, " (newpage)"); + if (xlrec->replaceDead) + appendStringInfoString(buf, " (replacedead)"); + if (xlrec->storesNulls) + appendStringInfoString(buf, " (nulls)"); + } + break; + case XLOG_SPGIST_ADD_NODE: + { + spgxlogAddNode *xlrec = (spgxlogAddNode *) rec; + + appendStringInfo(buf, "off: %u, newoff: %u, parentBlk: %d, " + "parentoff: %u, nodeI: %u", + xlrec->offnum, + xlrec->offnumNew, + xlrec->parentBlk, + xlrec->offnumParent, + xlrec->nodeI); + if (xlrec->newPage) + appendStringInfoString(buf, " (newpage)"); + } + break; + case XLOG_SPGIST_SPLIT_TUPLE: + { + spgxlogSplitTuple *xlrec = (spgxlogSplitTuple *) rec; + + appendStringInfo(buf, "prefixoff: %u, postfixoff: %u", + xlrec->offnumPrefix, + xlrec->offnumPostfix); + if (xlrec->newPage) + appendStringInfoString(buf, " (newpage)"); + if (xlrec->postfixBlkSame) + appendStringInfoString(buf, " (same)"); + } + break; + case XLOG_SPGIST_PICKSPLIT: + { + spgxlogPickSplit *xlrec = (spgxlogPickSplit *) rec; + + appendStringInfo(buf, "ndelete: %u, ninsert: %u, inneroff: %u, " + "parentoff: %u, nodeI: %u", + xlrec->nDelete, xlrec->nInsert, + xlrec->offnumInner, + xlrec->offnumParent, xlrec->nodeI); + if (xlrec->innerIsParent) + appendStringInfoString(buf, " (innerIsParent)"); + if (xlrec->storesNulls) + appendStringInfoString(buf, " (nulls)"); + if (xlrec->isRootSplit) + appendStringInfoString(buf, " (isRootSplit)"); + } + break; + case XLOG_SPGIST_VACUUM_LEAF: + { + spgxlogVacuumLeaf *xlrec = (spgxlogVacuumLeaf *) rec; + + appendStringInfo(buf, "ndead: %u, nplaceholder: %u, nmove: %u, nchain: %u", + xlrec->nDead, xlrec->nPlaceholder, + xlrec->nMove, xlrec->nChain); + } + break; + case XLOG_SPGIST_VACUUM_ROOT: + { + spgxlogVacuumRoot *xlrec = (spgxlogVacuumRoot *) rec; + + appendStringInfo(buf, "ndelete: %u", + xlrec->nDelete); + } + break; + case XLOG_SPGIST_VACUUM_REDIRECT: + { + spgxlogVacuumRedirect *xlrec = (spgxlogVacuumRedirect *) rec; + + appendStringInfo(buf, "ntoplaceholder: %u, firstplaceholder: %u, newestredirectxid: %u", + xlrec->nToPlaceholder, + xlrec->firstPlaceholder, + xlrec->newestRedirectXid); + } + break; + } +} + +const char * +spg_identify(uint8 info) +{ + const char *id = NULL; + + switch (info & ~XLR_INFO_MASK) + { + case XLOG_SPGIST_ADD_LEAF: + id = "ADD_LEAF"; + break; + case XLOG_SPGIST_MOVE_LEAFS: + id = "MOVE_LEAFS"; + break; + case XLOG_SPGIST_ADD_NODE: + id = "ADD_NODE"; + break; + case XLOG_SPGIST_SPLIT_TUPLE: + id = "SPLIT_TUPLE"; + break; + case XLOG_SPGIST_PICKSPLIT: + id = "PICKSPLIT"; + break; + case XLOG_SPGIST_VACUUM_LEAF: + id = "VACUUM_LEAF"; + break; + case XLOG_SPGIST_VACUUM_ROOT: + id = "VACUUM_ROOT"; + break; + case XLOG_SPGIST_VACUUM_REDIRECT: + id = "VACUUM_REDIRECT"; + break; + } + + return id; +} diff --git a/src/backend/access/rmgrdesc/standbydesc.c b/src/backend/access/rmgrdesc/standbydesc.c new file mode 100644 index 0000000..2dba39e --- /dev/null +++ b/src/backend/access/rmgrdesc/standbydesc.c @@ -0,0 +1,135 @@ +/*------------------------------------------------------------------------- + * + * standbydesc.c + * rmgr descriptor routines for storage/ipc/standby.c + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/access/rmgrdesc/standbydesc.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "storage/standbydefs.h" + +static void +standby_desc_running_xacts(StringInfo buf, xl_running_xacts *xlrec) +{ + int i; + + appendStringInfo(buf, "nextXid %u latestCompletedXid %u oldestRunningXid %u", + xlrec->nextXid, + xlrec->latestCompletedXid, + xlrec->oldestRunningXid); + if (xlrec->xcnt > 0) + { + appendStringInfo(buf, "; %d xacts:", xlrec->xcnt); + for (i = 0; i < xlrec->xcnt; i++) + appendStringInfo(buf, " %u", xlrec->xids[i]); + } + + if (xlrec->subxid_overflow) + appendStringInfoString(buf, "; subxid ovf"); +} + +void +standby_desc(StringInfo buf, XLogReaderState *record) +{ + char *rec = XLogRecGetData(record); + uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK; + + if (info == XLOG_STANDBY_LOCK) + { + xl_standby_locks *xlrec = (xl_standby_locks *) rec; + int i; + + for (i = 0; i < xlrec->nlocks; i++) + appendStringInfo(buf, "xid %u db %u rel %u ", + xlrec->locks[i].xid, xlrec->locks[i].dbOid, + xlrec->locks[i].relOid); + } + else if (info == XLOG_RUNNING_XACTS) + { + xl_running_xacts *xlrec = (xl_running_xacts *) rec; + + standby_desc_running_xacts(buf, xlrec); + } + else if (info == XLOG_INVALIDATIONS) + { + xl_invalidations *xlrec = (xl_invalidations *) rec; + + standby_desc_invalidations(buf, xlrec->nmsgs, xlrec->msgs, + xlrec->dbId, xlrec->tsId, + xlrec->relcacheInitFileInval); + } +} + +const char * +standby_identify(uint8 info) +{ + const char *id = NULL; + + switch (info & ~XLR_INFO_MASK) + { + case XLOG_STANDBY_LOCK: + id = "LOCK"; + break; + case XLOG_RUNNING_XACTS: + id = "RUNNING_XACTS"; + break; + case XLOG_INVALIDATIONS: + id = "INVALIDATIONS"; + break; + } + + return id; +} + +/* + * This routine is used by both standby_desc and xact_desc, because + * transaction commits and XLOG_INVALIDATIONS messages contain invalidations; + * it seems pointless to duplicate the code. + */ +void +standby_desc_invalidations(StringInfo buf, + int nmsgs, SharedInvalidationMessage *msgs, + Oid dbId, Oid tsId, + bool relcacheInitFileInval) +{ + int i; + + /* Do nothing if there are no invalidation messages */ + if (nmsgs <= 0) + return; + + if (relcacheInitFileInval) + appendStringInfo(buf, "; relcache init file inval dbid %u tsid %u", + dbId, tsId); + + appendStringInfoString(buf, "; inval msgs:"); + for (i = 0; i < nmsgs; i++) + { + SharedInvalidationMessage *msg = &msgs[i]; + + if (msg->id >= 0) + appendStringInfo(buf, " catcache %d", msg->id); + else if (msg->id == SHAREDINVALCATALOG_ID) + appendStringInfo(buf, " catalog %u", msg->cat.catId); + else if (msg->id == SHAREDINVALRELCACHE_ID) + appendStringInfo(buf, " relcache %u", msg->rc.relId); + /* not expected, but print something anyway */ + else if (msg->id == SHAREDINVALSMGR_ID) + appendStringInfoString(buf, " smgr"); + /* not expected, but print something anyway */ + else if (msg->id == SHAREDINVALRELMAP_ID) + appendStringInfo(buf, " relmap db %u", msg->rm.dbId); + else if (msg->id == SHAREDINVALSNAPSHOT_ID) + appendStringInfo(buf, " snapshot %u", msg->sn.relId); + else + appendStringInfo(buf, " unrecognized id %d", msg->id); + } +} diff --git a/src/backend/access/rmgrdesc/tblspcdesc.c b/src/backend/access/rmgrdesc/tblspcdesc.c new file mode 100644 index 0000000..ed94b6e --- /dev/null +++ b/src/backend/access/rmgrdesc/tblspcdesc.c @@ -0,0 +1,56 @@ +/*------------------------------------------------------------------------- + * + * tblspcdesc.c + * rmgr descriptor routines for commands/tablespace.c + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/access/rmgrdesc/tblspcdesc.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "commands/tablespace.h" + + +void +tblspc_desc(StringInfo buf, XLogReaderState *record) +{ + char *rec = XLogRecGetData(record); + uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK; + + if (info == XLOG_TBLSPC_CREATE) + { + xl_tblspc_create_rec *xlrec = (xl_tblspc_create_rec *) rec; + + appendStringInfo(buf, "%u \"%s\"", xlrec->ts_id, xlrec->ts_path); + } + else if (info == XLOG_TBLSPC_DROP) + { + xl_tblspc_drop_rec *xlrec = (xl_tblspc_drop_rec *) rec; + + appendStringInfo(buf, "%u", xlrec->ts_id); + } +} + +const char * +tblspc_identify(uint8 info) +{ + const char *id = NULL; + + switch (info & ~XLR_INFO_MASK) + { + case XLOG_TBLSPC_CREATE: + id = "CREATE"; + break; + case XLOG_TBLSPC_DROP: + id = "DROP"; + break; + } + + return id; +} diff --git a/src/backend/access/rmgrdesc/xactdesc.c b/src/backend/access/rmgrdesc/xactdesc.c new file mode 100644 index 0000000..90b6ac2 --- /dev/null +++ b/src/backend/access/rmgrdesc/xactdesc.c @@ -0,0 +1,514 @@ +/*------------------------------------------------------------------------- + * + * xactdesc.c + * rmgr descriptor routines for access/transam/xact.c + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/access/rmgrdesc/xactdesc.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/transam.h" +#include "access/xact.h" +#include "replication/origin.h" +#include "storage/sinval.h" +#include "storage/standbydefs.h" +#include "utils/timestamp.h" + +/* + * Parse the WAL format of an xact commit and abort records into an easier to + * understand format. + * + * This routines are in xactdesc.c because they're accessed in backend (when + * replaying WAL) and frontend (pg_waldump) code. This file is the only xact + * specific one shared between both. They're complicated enough that + * duplication would be bothersome. + */ + +void +ParseCommitRecord(uint8 info, xl_xact_commit *xlrec, xl_xact_parsed_commit *parsed) +{ + char *data = ((char *) xlrec) + MinSizeOfXactCommit; + + memset(parsed, 0, sizeof(*parsed)); + + parsed->xinfo = 0; /* default, if no XLOG_XACT_HAS_INFO is + * present */ + + parsed->xact_time = xlrec->xact_time; + + if (info & XLOG_XACT_HAS_INFO) + { + xl_xact_xinfo *xl_xinfo = (xl_xact_xinfo *) data; + + parsed->xinfo = xl_xinfo->xinfo; + + data += sizeof(xl_xact_xinfo); + } + + if (parsed->xinfo & XACT_XINFO_HAS_DBINFO) + { + xl_xact_dbinfo *xl_dbinfo = (xl_xact_dbinfo *) data; + + parsed->dbId = xl_dbinfo->dbId; + parsed->tsId = xl_dbinfo->tsId; + + data += sizeof(xl_xact_dbinfo); + } + + if (parsed->xinfo & XACT_XINFO_HAS_SUBXACTS) + { + xl_xact_subxacts *xl_subxacts = (xl_xact_subxacts *) data; + + parsed->nsubxacts = xl_subxacts->nsubxacts; + parsed->subxacts = xl_subxacts->subxacts; + + data += MinSizeOfXactSubxacts; + data += parsed->nsubxacts * sizeof(TransactionId); + } + + if (parsed->xinfo & XACT_XINFO_HAS_RELFILENODES) + { + xl_xact_relfilenodes *xl_relfilenodes = (xl_xact_relfilenodes *) data; + + parsed->nrels = xl_relfilenodes->nrels; + parsed->xnodes = xl_relfilenodes->xnodes; + + data += MinSizeOfXactRelfilenodes; + data += xl_relfilenodes->nrels * sizeof(RelFileNode); + } + + if (parsed->xinfo & XACT_XINFO_HAS_DROPPED_STATS) + { + xl_xact_stats_items *xl_drops = (xl_xact_stats_items *) data; + + parsed->nstats = xl_drops->nitems; + parsed->stats = xl_drops->items; + + data += MinSizeOfXactStatsItems; + data += xl_drops->nitems * sizeof(xl_xact_stats_item); + } + + if (parsed->xinfo & XACT_XINFO_HAS_INVALS) + { + xl_xact_invals *xl_invals = (xl_xact_invals *) data; + + parsed->nmsgs = xl_invals->nmsgs; + parsed->msgs = xl_invals->msgs; + + data += MinSizeOfXactInvals; + data += xl_invals->nmsgs * sizeof(SharedInvalidationMessage); + } + + if (parsed->xinfo & XACT_XINFO_HAS_TWOPHASE) + { + xl_xact_twophase *xl_twophase = (xl_xact_twophase *) data; + + parsed->twophase_xid = xl_twophase->xid; + + data += sizeof(xl_xact_twophase); + + if (parsed->xinfo & XACT_XINFO_HAS_GID) + { + strlcpy(parsed->twophase_gid, data, sizeof(parsed->twophase_gid)); + data += strlen(data) + 1; + } + } + + /* Note: no alignment is guaranteed after this point */ + + if (parsed->xinfo & XACT_XINFO_HAS_ORIGIN) + { + xl_xact_origin xl_origin; + + /* no alignment is guaranteed, so copy onto stack */ + memcpy(&xl_origin, data, sizeof(xl_origin)); + + parsed->origin_lsn = xl_origin.origin_lsn; + parsed->origin_timestamp = xl_origin.origin_timestamp; + + data += sizeof(xl_xact_origin); + } +} + +void +ParseAbortRecord(uint8 info, xl_xact_abort *xlrec, xl_xact_parsed_abort *parsed) +{ + char *data = ((char *) xlrec) + MinSizeOfXactAbort; + + memset(parsed, 0, sizeof(*parsed)); + + parsed->xinfo = 0; /* default, if no XLOG_XACT_HAS_INFO is + * present */ + + parsed->xact_time = xlrec->xact_time; + + if (info & XLOG_XACT_HAS_INFO) + { + xl_xact_xinfo *xl_xinfo = (xl_xact_xinfo *) data; + + parsed->xinfo = xl_xinfo->xinfo; + + data += sizeof(xl_xact_xinfo); + } + + if (parsed->xinfo & XACT_XINFO_HAS_DBINFO) + { + xl_xact_dbinfo *xl_dbinfo = (xl_xact_dbinfo *) data; + + parsed->dbId = xl_dbinfo->dbId; + parsed->tsId = xl_dbinfo->tsId; + + data += sizeof(xl_xact_dbinfo); + } + + if (parsed->xinfo & XACT_XINFO_HAS_SUBXACTS) + { + xl_xact_subxacts *xl_subxacts = (xl_xact_subxacts *) data; + + parsed->nsubxacts = xl_subxacts->nsubxacts; + parsed->subxacts = xl_subxacts->subxacts; + + data += MinSizeOfXactSubxacts; + data += parsed->nsubxacts * sizeof(TransactionId); + } + + if (parsed->xinfo & XACT_XINFO_HAS_RELFILENODES) + { + xl_xact_relfilenodes *xl_relfilenodes = (xl_xact_relfilenodes *) data; + + parsed->nrels = xl_relfilenodes->nrels; + parsed->xnodes = xl_relfilenodes->xnodes; + + data += MinSizeOfXactRelfilenodes; + data += xl_relfilenodes->nrels * sizeof(RelFileNode); + } + + if (parsed->xinfo & XACT_XINFO_HAS_DROPPED_STATS) + { + xl_xact_stats_items *xl_drops = (xl_xact_stats_items *) data; + + parsed->nstats = xl_drops->nitems; + parsed->stats = xl_drops->items; + + data += MinSizeOfXactStatsItems; + data += xl_drops->nitems * sizeof(xl_xact_stats_item); + } + + if (parsed->xinfo & XACT_XINFO_HAS_TWOPHASE) + { + xl_xact_twophase *xl_twophase = (xl_xact_twophase *) data; + + parsed->twophase_xid = xl_twophase->xid; + + data += sizeof(xl_xact_twophase); + + if (parsed->xinfo & XACT_XINFO_HAS_GID) + { + strlcpy(parsed->twophase_gid, data, sizeof(parsed->twophase_gid)); + data += strlen(data) + 1; + } + } + + /* Note: no alignment is guaranteed after this point */ + + if (parsed->xinfo & XACT_XINFO_HAS_ORIGIN) + { + xl_xact_origin xl_origin; + + /* no alignment is guaranteed, so copy onto stack */ + memcpy(&xl_origin, data, sizeof(xl_origin)); + + parsed->origin_lsn = xl_origin.origin_lsn; + parsed->origin_timestamp = xl_origin.origin_timestamp; + + data += sizeof(xl_xact_origin); + } +} + +/* + * ParsePrepareRecord + */ +void +ParsePrepareRecord(uint8 info, xl_xact_prepare *xlrec, xl_xact_parsed_prepare *parsed) +{ + char *bufptr; + + bufptr = ((char *) xlrec) + MAXALIGN(sizeof(xl_xact_prepare)); + + memset(parsed, 0, sizeof(*parsed)); + + parsed->xact_time = xlrec->prepared_at; + parsed->origin_lsn = xlrec->origin_lsn; + parsed->origin_timestamp = xlrec->origin_timestamp; + parsed->twophase_xid = xlrec->xid; + parsed->dbId = xlrec->database; + parsed->nsubxacts = xlrec->nsubxacts; + parsed->nrels = xlrec->ncommitrels; + parsed->nabortrels = xlrec->nabortrels; + parsed->nmsgs = xlrec->ninvalmsgs; + + strncpy(parsed->twophase_gid, bufptr, xlrec->gidlen); + bufptr += MAXALIGN(xlrec->gidlen); + + parsed->subxacts = (TransactionId *) bufptr; + bufptr += MAXALIGN(xlrec->nsubxacts * sizeof(TransactionId)); + + parsed->xnodes = (RelFileNode *) bufptr; + bufptr += MAXALIGN(xlrec->ncommitrels * sizeof(RelFileNode)); + + parsed->abortnodes = (RelFileNode *) bufptr; + bufptr += MAXALIGN(xlrec->nabortrels * sizeof(RelFileNode)); + + parsed->stats = (xl_xact_stats_item *) bufptr; + bufptr += MAXALIGN(xlrec->ncommitstats * sizeof(xl_xact_stats_item)); + + parsed->abortstats = (xl_xact_stats_item *) bufptr; + bufptr += MAXALIGN(xlrec->nabortstats * sizeof(xl_xact_stats_item)); + + parsed->msgs = (SharedInvalidationMessage *) bufptr; + bufptr += MAXALIGN(xlrec->ninvalmsgs * sizeof(SharedInvalidationMessage)); +} + +static void +xact_desc_relations(StringInfo buf, char *label, int nrels, + RelFileNode *xnodes) +{ + int i; + + if (nrels > 0) + { + appendStringInfo(buf, "; %s:", label); + for (i = 0; i < nrels; i++) + { + char *path = relpathperm(xnodes[i], MAIN_FORKNUM); + + appendStringInfo(buf, " %s", path); + pfree(path); + } + } +} + +static void +xact_desc_subxacts(StringInfo buf, int nsubxacts, TransactionId *subxacts) +{ + int i; + + if (nsubxacts > 0) + { + appendStringInfoString(buf, "; subxacts:"); + for (i = 0; i < nsubxacts; i++) + appendStringInfo(buf, " %u", subxacts[i]); + } +} + +static void +xact_desc_stats(StringInfo buf, const char *label, + int ndropped, xl_xact_stats_item *dropped_stats) +{ + int i; + + if (ndropped > 0) + { + appendStringInfo(buf, "; %sdropped stats:", label); + for (i = 0; i < ndropped; i++) + { + appendStringInfo(buf, " %d/%u/%u", + dropped_stats[i].kind, + dropped_stats[i].dboid, + dropped_stats[i].objoid); + } + } +} + +static void +xact_desc_commit(StringInfo buf, uint8 info, xl_xact_commit *xlrec, RepOriginId origin_id) +{ + xl_xact_parsed_commit parsed; + + ParseCommitRecord(info, xlrec, &parsed); + + /* If this is a prepared xact, show the xid of the original xact */ + if (TransactionIdIsValid(parsed.twophase_xid)) + appendStringInfo(buf, "%u: ", parsed.twophase_xid); + + appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time)); + + xact_desc_relations(buf, "rels", parsed.nrels, parsed.xnodes); + xact_desc_subxacts(buf, parsed.nsubxacts, parsed.subxacts); + xact_desc_stats(buf, "", parsed.nstats, parsed.stats); + + standby_desc_invalidations(buf, parsed.nmsgs, parsed.msgs, parsed.dbId, + parsed.tsId, + XactCompletionRelcacheInitFileInval(parsed.xinfo)); + + if (XactCompletionApplyFeedback(parsed.xinfo)) + appendStringInfoString(buf, "; apply_feedback"); + + if (XactCompletionForceSyncCommit(parsed.xinfo)) + appendStringInfoString(buf, "; sync"); + + if (parsed.xinfo & XACT_XINFO_HAS_ORIGIN) + { + appendStringInfo(buf, "; origin: node %u, lsn %X/%X, at %s", + origin_id, + LSN_FORMAT_ARGS(parsed.origin_lsn), + timestamptz_to_str(parsed.origin_timestamp)); + } +} + +static void +xact_desc_abort(StringInfo buf, uint8 info, xl_xact_abort *xlrec, RepOriginId origin_id) +{ + xl_xact_parsed_abort parsed; + + ParseAbortRecord(info, xlrec, &parsed); + + /* If this is a prepared xact, show the xid of the original xact */ + if (TransactionIdIsValid(parsed.twophase_xid)) + appendStringInfo(buf, "%u: ", parsed.twophase_xid); + + appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time)); + + xact_desc_relations(buf, "rels", parsed.nrels, parsed.xnodes); + xact_desc_subxacts(buf, parsed.nsubxacts, parsed.subxacts); + + if (parsed.xinfo & XACT_XINFO_HAS_ORIGIN) + { + appendStringInfo(buf, "; origin: node %u, lsn %X/%X, at %s", + origin_id, + LSN_FORMAT_ARGS(parsed.origin_lsn), + timestamptz_to_str(parsed.origin_timestamp)); + } + + xact_desc_stats(buf, "", parsed.nstats, parsed.stats); +} + +static void +xact_desc_prepare(StringInfo buf, uint8 info, xl_xact_prepare *xlrec, RepOriginId origin_id) +{ + xl_xact_parsed_prepare parsed; + + ParsePrepareRecord(info, xlrec, &parsed); + + appendStringInfo(buf, "gid %s: ", parsed.twophase_gid); + appendStringInfoString(buf, timestamptz_to_str(parsed.xact_time)); + + xact_desc_relations(buf, "rels(commit)", parsed.nrels, parsed.xnodes); + xact_desc_relations(buf, "rels(abort)", parsed.nabortrels, + parsed.abortnodes); + xact_desc_stats(buf, "commit ", parsed.nstats, parsed.stats); + xact_desc_stats(buf, "abort ", parsed.nabortstats, parsed.abortstats); + xact_desc_subxacts(buf, parsed.nsubxacts, parsed.subxacts); + + standby_desc_invalidations(buf, parsed.nmsgs, parsed.msgs, parsed.dbId, + parsed.tsId, xlrec->initfileinval); + + /* + * Check if the replication origin has been set in this record in the same + * way as PrepareRedoAdd(). + */ + if (origin_id != InvalidRepOriginId) + appendStringInfo(buf, "; origin: node %u, lsn %X/%X, at %s", + origin_id, + LSN_FORMAT_ARGS(parsed.origin_lsn), + timestamptz_to_str(parsed.origin_timestamp)); +} + +static void +xact_desc_assignment(StringInfo buf, xl_xact_assignment *xlrec) +{ + int i; + + appendStringInfoString(buf, "subxacts:"); + + for (i = 0; i < xlrec->nsubxacts; i++) + appendStringInfo(buf, " %u", xlrec->xsub[i]); +} + +void +xact_desc(StringInfo buf, XLogReaderState *record) +{ + char *rec = XLogRecGetData(record); + uint8 info = XLogRecGetInfo(record) & XLOG_XACT_OPMASK; + + if (info == XLOG_XACT_COMMIT || info == XLOG_XACT_COMMIT_PREPARED) + { + xl_xact_commit *xlrec = (xl_xact_commit *) rec; + + xact_desc_commit(buf, XLogRecGetInfo(record), xlrec, + XLogRecGetOrigin(record)); + } + else if (info == XLOG_XACT_ABORT || info == XLOG_XACT_ABORT_PREPARED) + { + xl_xact_abort *xlrec = (xl_xact_abort *) rec; + + xact_desc_abort(buf, XLogRecGetInfo(record), xlrec, + XLogRecGetOrigin(record)); + } + else if (info == XLOG_XACT_PREPARE) + { + xl_xact_prepare *xlrec = (xl_xact_prepare *) rec; + + xact_desc_prepare(buf, XLogRecGetInfo(record), xlrec, + XLogRecGetOrigin(record)); + } + else if (info == XLOG_XACT_ASSIGNMENT) + { + xl_xact_assignment *xlrec = (xl_xact_assignment *) rec; + + /* + * Note that we ignore the WAL record's xid, since we're more + * interested in the top-level xid that issued the record and which + * xids are being reported here. + */ + appendStringInfo(buf, "xtop %u: ", xlrec->xtop); + xact_desc_assignment(buf, xlrec); + } + else if (info == XLOG_XACT_INVALIDATIONS) + { + xl_xact_invals *xlrec = (xl_xact_invals *) rec; + + standby_desc_invalidations(buf, xlrec->nmsgs, xlrec->msgs, InvalidOid, + InvalidOid, false); + } +} + +const char * +xact_identify(uint8 info) +{ + const char *id = NULL; + + switch (info & XLOG_XACT_OPMASK) + { + case XLOG_XACT_COMMIT: + id = "COMMIT"; + break; + case XLOG_XACT_PREPARE: + id = "PREPARE"; + break; + case XLOG_XACT_ABORT: + id = "ABORT"; + break; + case XLOG_XACT_COMMIT_PREPARED: + id = "COMMIT_PREPARED"; + break; + case XLOG_XACT_ABORT_PREPARED: + id = "ABORT_PREPARED"; + break; + case XLOG_XACT_ASSIGNMENT: + id = "ASSIGNMENT"; + break; + case XLOG_XACT_INVALIDATIONS: + id = "INVALIDATION"; + break; + } + + return id; +} diff --git a/src/backend/access/rmgrdesc/xlogdesc.c b/src/backend/access/rmgrdesc/xlogdesc.c new file mode 100644 index 0000000..fefc563 --- /dev/null +++ b/src/backend/access/rmgrdesc/xlogdesc.c @@ -0,0 +1,331 @@ +/*------------------------------------------------------------------------- + * + * xlogdesc.c + * rmgr descriptor routines for access/transam/xlog.c + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/access/rmgrdesc/xlogdesc.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/transam.h" +#include "access/xlog.h" +#include "access/xlog_internal.h" +#include "catalog/pg_control.h" +#include "utils/guc.h" +#include "utils/timestamp.h" + +/* + * GUC support + */ +const struct config_enum_entry wal_level_options[] = { + {"minimal", WAL_LEVEL_MINIMAL, false}, + {"replica", WAL_LEVEL_REPLICA, false}, + {"archive", WAL_LEVEL_REPLICA, true}, /* deprecated */ + {"hot_standby", WAL_LEVEL_REPLICA, true}, /* deprecated */ + {"logical", WAL_LEVEL_LOGICAL, false}, + {NULL, 0, false} +}; + +void +xlog_desc(StringInfo buf, XLogReaderState *record) +{ + char *rec = XLogRecGetData(record); + uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK; + + if (info == XLOG_CHECKPOINT_SHUTDOWN || + info == XLOG_CHECKPOINT_ONLINE) + { + CheckPoint *checkpoint = (CheckPoint *) rec; + + appendStringInfo(buf, "redo %X/%X; " + "tli %u; prev tli %u; fpw %s; xid %u:%u; oid %u; multi %u; offset %u; " + "oldest xid %u in DB %u; oldest multi %u in DB %u; " + "oldest/newest commit timestamp xid: %u/%u; " + "oldest running xid %u; %s", + LSN_FORMAT_ARGS(checkpoint->redo), + checkpoint->ThisTimeLineID, + checkpoint->PrevTimeLineID, + checkpoint->fullPageWrites ? "true" : "false", + EpochFromFullTransactionId(checkpoint->nextXid), + XidFromFullTransactionId(checkpoint->nextXid), + checkpoint->nextOid, + checkpoint->nextMulti, + checkpoint->nextMultiOffset, + checkpoint->oldestXid, + checkpoint->oldestXidDB, + checkpoint->oldestMulti, + checkpoint->oldestMultiDB, + checkpoint->oldestCommitTsXid, + checkpoint->newestCommitTsXid, + checkpoint->oldestActiveXid, + (info == XLOG_CHECKPOINT_SHUTDOWN) ? "shutdown" : "online"); + } + else if (info == XLOG_NEXTOID) + { + Oid nextOid; + + memcpy(&nextOid, rec, sizeof(Oid)); + appendStringInfo(buf, "%u", nextOid); + } + else if (info == XLOG_RESTORE_POINT) + { + xl_restore_point *xlrec = (xl_restore_point *) rec; + + appendStringInfoString(buf, xlrec->rp_name); + } + else if (info == XLOG_FPI || info == XLOG_FPI_FOR_HINT) + { + /* no further information to print */ + } + else if (info == XLOG_BACKUP_END) + { + XLogRecPtr startpoint; + + memcpy(&startpoint, rec, sizeof(XLogRecPtr)); + appendStringInfo(buf, "%X/%X", LSN_FORMAT_ARGS(startpoint)); + } + else if (info == XLOG_PARAMETER_CHANGE) + { + xl_parameter_change xlrec; + const char *wal_level_str; + const struct config_enum_entry *entry; + + memcpy(&xlrec, rec, sizeof(xl_parameter_change)); + + /* Find a string representation for wal_level */ + wal_level_str = "?"; + for (entry = wal_level_options; entry->name; entry++) + { + if (entry->val == xlrec.wal_level) + { + wal_level_str = entry->name; + break; + } + } + + appendStringInfo(buf, "max_connections=%d max_worker_processes=%d " + "max_wal_senders=%d max_prepared_xacts=%d " + "max_locks_per_xact=%d wal_level=%s " + "wal_log_hints=%s track_commit_timestamp=%s", + xlrec.MaxConnections, + xlrec.max_worker_processes, + xlrec.max_wal_senders, + xlrec.max_prepared_xacts, + xlrec.max_locks_per_xact, + wal_level_str, + xlrec.wal_log_hints ? "on" : "off", + xlrec.track_commit_timestamp ? "on" : "off"); + } + else if (info == XLOG_FPW_CHANGE) + { + bool fpw; + + memcpy(&fpw, rec, sizeof(bool)); + appendStringInfoString(buf, fpw ? "true" : "false"); + } + else if (info == XLOG_END_OF_RECOVERY) + { + xl_end_of_recovery xlrec; + + memcpy(&xlrec, rec, sizeof(xl_end_of_recovery)); + appendStringInfo(buf, "tli %u; prev tli %u; time %s", + xlrec.ThisTimeLineID, xlrec.PrevTimeLineID, + timestamptz_to_str(xlrec.end_time)); + } + else if (info == XLOG_OVERWRITE_CONTRECORD) + { + xl_overwrite_contrecord xlrec; + + memcpy(&xlrec, rec, sizeof(xl_overwrite_contrecord)); + appendStringInfo(buf, "lsn %X/%X; time %s", + LSN_FORMAT_ARGS(xlrec.overwritten_lsn), + timestamptz_to_str(xlrec.overwrite_time)); + } +} + +const char * +xlog_identify(uint8 info) +{ + const char *id = NULL; + + switch (info & ~XLR_INFO_MASK) + { + case XLOG_CHECKPOINT_SHUTDOWN: + id = "CHECKPOINT_SHUTDOWN"; + break; + case XLOG_CHECKPOINT_ONLINE: + id = "CHECKPOINT_ONLINE"; + break; + case XLOG_NOOP: + id = "NOOP"; + break; + case XLOG_NEXTOID: + id = "NEXTOID"; + break; + case XLOG_SWITCH: + id = "SWITCH"; + break; + case XLOG_BACKUP_END: + id = "BACKUP_END"; + break; + case XLOG_PARAMETER_CHANGE: + id = "PARAMETER_CHANGE"; + break; + case XLOG_RESTORE_POINT: + id = "RESTORE_POINT"; + break; + case XLOG_FPW_CHANGE: + id = "FPW_CHANGE"; + break; + case XLOG_END_OF_RECOVERY: + id = "END_OF_RECOVERY"; + break; + case XLOG_OVERWRITE_CONTRECORD: + id = "OVERWRITE_CONTRECORD"; + break; + case XLOG_FPI: + id = "FPI"; + break; + case XLOG_FPI_FOR_HINT: + id = "FPI_FOR_HINT"; + break; + } + + return id; +} + +/* + * Returns a string giving information about all the blocks in an + * XLogRecord. + */ +void +XLogRecGetBlockRefInfo(XLogReaderState *record, bool pretty, + bool detailed_format, StringInfo buf, + uint32 *fpi_len) +{ + int block_id; + + Assert(record != NULL); + + if (detailed_format && pretty) + appendStringInfoChar(buf, '\n'); + + for (block_id = 0; block_id <= XLogRecMaxBlockId(record); block_id++) + { + RelFileNode rnode; + ForkNumber forknum; + BlockNumber blk; + + if (!XLogRecGetBlockTagExtended(record, block_id, + &rnode, &forknum, &blk, NULL)) + continue; + + if (detailed_format) + { + /* Get block references in detailed format. */ + + if (pretty) + appendStringInfoChar(buf, '\t'); + else if (block_id > 0) + appendStringInfoChar(buf, ' '); + + appendStringInfo(buf, + "blkref #%d: rel %u/%u/%u fork %s blk %u", + block_id, + rnode.spcNode, rnode.dbNode, rnode.relNode, + forkNames[forknum], + blk); + + if (XLogRecHasBlockImage(record, block_id)) + { + uint8 bimg_info = XLogRecGetBlock(record, block_id)->bimg_info; + + /* Calculate the amount of FPI data in the record. */ + if (fpi_len) + *fpi_len += XLogRecGetBlock(record, block_id)->bimg_len; + + if (BKPIMAGE_COMPRESSED(bimg_info)) + { + const char *method; + + if ((bimg_info & BKPIMAGE_COMPRESS_PGLZ) != 0) + method = "pglz"; + else if ((bimg_info & BKPIMAGE_COMPRESS_LZ4) != 0) + method = "lz4"; + else if ((bimg_info & BKPIMAGE_COMPRESS_ZSTD) != 0) + method = "zstd"; + else + method = "unknown"; + + appendStringInfo(buf, + " (FPW%s); hole: offset: %u, length: %u, " + "compression saved: %u, method: %s", + XLogRecBlockImageApply(record, block_id) ? + "" : " for WAL verification", + XLogRecGetBlock(record, block_id)->hole_offset, + XLogRecGetBlock(record, block_id)->hole_length, + BLCKSZ - + XLogRecGetBlock(record, block_id)->hole_length - + XLogRecGetBlock(record, block_id)->bimg_len, + method); + } + else + { + appendStringInfo(buf, + " (FPW%s); hole: offset: %u, length: %u", + XLogRecBlockImageApply(record, block_id) ? + "" : " for WAL verification", + XLogRecGetBlock(record, block_id)->hole_offset, + XLogRecGetBlock(record, block_id)->hole_length); + } + } + + if (pretty) + appendStringInfoChar(buf, '\n'); + } + else + { + /* Get block references in short format. */ + + if (forknum != MAIN_FORKNUM) + { + appendStringInfo(buf, + ", blkref #%d: rel %u/%u/%u fork %s blk %u", + block_id, + rnode.spcNode, rnode.dbNode, rnode.relNode, + forkNames[forknum], + blk); + } + else + { + appendStringInfo(buf, + ", blkref #%d: rel %u/%u/%u blk %u", + block_id, + rnode.spcNode, rnode.dbNode, rnode.relNode, + blk); + } + + if (XLogRecHasBlockImage(record, block_id)) + { + /* Calculate the amount of FPI data in the record. */ + if (fpi_len) + *fpi_len += XLogRecGetBlock(record, block_id)->bimg_len; + + if (XLogRecBlockImageApply(record, block_id)) + appendStringInfo(buf, " FPW"); + else + appendStringInfo(buf, " FPW for WAL verification"); + } + } + } + + if (!detailed_format && pretty) + appendStringInfoChar(buf, '\n'); +} |