/*------------------------------------------------------------------------- * * heapdesc.c * rmgr descriptor routines for access/heap/heapam.c * * Portions Copyright (c) 1996-2023, 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" #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"); 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"); 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 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; appendStringInfoString(buf, "flags: ["); if (xlrec->flags & XLH_TRUNCATE_CASCADE) appendStringInfoString(buf, " CASCADE"); if (xlrec->flags & XLH_TRUNCATE_RESTART_SEQS) appendStringInfoString(buf, ", RESTART_SEQS"); appendStringInfoString(buf, " ]"); appendStringInfo(buf, ", nrelids: %u", xlrec->nrelids); appendStringInfoString(buf, ", relids:"); array_desc(buf, xlrec->relids, sizeof(Oid), xlrec->nrelids, &oid_elem_desc, NULL); } 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, "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); 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", 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", 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, "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", 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_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", 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; }