LCOV - code coverage report
Current view: top level - src/bin/pg_waldump - heapdesc.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 228 254 89.8 %
Date: 2025-10-31 00:18:18 Functions: 8 8 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * heapdesc.c
       4             :  *    rmgr descriptor routines for access/heap/heapam.c
       5             :  *
       6             :  * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/access/rmgrdesc/heapdesc.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : #include "postgres.h"
      16             : 
      17             : #include "access/heapam_xlog.h"
      18             : #include "access/rmgrdesc_utils.h"
      19             : #include "access/visibilitymapdefs.h"
      20             : #include "storage/standbydefs.h"
      21             : 
      22             : /*
      23             :  * NOTE: "keyname" argument cannot have trailing spaces or punctuation
      24             :  * characters
      25             :  */
      26             : static void
      27      124122 : infobits_desc(StringInfo buf, uint8 infobits, const char *keyname)
      28             : {
      29      124122 :     appendStringInfo(buf, "%s: [", keyname);
      30             : 
      31             :     Assert(buf->data[buf->len - 1] != ' ');
      32             : 
      33      124122 :     if (infobits & XLHL_XMAX_IS_MULTI)
      34           0 :         appendStringInfoString(buf, "IS_MULTI, ");
      35      124122 :     if (infobits & XLHL_XMAX_LOCK_ONLY)
      36       61000 :         appendStringInfoString(buf, "LOCK_ONLY, ");
      37      124122 :     if (infobits & XLHL_XMAX_EXCL_LOCK)
      38       61000 :         appendStringInfoString(buf, "EXCL_LOCK, ");
      39      124122 :     if (infobits & XLHL_XMAX_KEYSHR_LOCK)
      40           0 :         appendStringInfoString(buf, "KEYSHR_LOCK, ");
      41      124122 :     if (infobits & XLHL_KEYS_UPDATED)
      42         430 :         appendStringInfoString(buf, "KEYS_UPDATED, ");
      43             : 
      44      124122 :     if (buf->data[buf->len - 1] == ' ')
      45             :     {
      46             :         /* Truncate-away final unneeded ", "  */
      47             :         Assert(buf->data[buf->len - 2] == ',');
      48       61430 :         buf->len -= 2;
      49       61430 :         buf->data[buf->len] = '\0';
      50             :     }
      51             : 
      52      124122 :     appendStringInfoChar(buf, ']');
      53      124122 : }
      54             : 
      55             : static void
      56          12 : truncate_flags_desc(StringInfo buf, uint8 flags)
      57             : {
      58          12 :     appendStringInfoString(buf, "flags: [");
      59             : 
      60          12 :     if (flags & XLH_TRUNCATE_CASCADE)
      61           0 :         appendStringInfoString(buf, "CASCADE, ");
      62          12 :     if (flags & XLH_TRUNCATE_RESTART_SEQS)
      63           0 :         appendStringInfoString(buf, "RESTART_SEQS, ");
      64             : 
      65          12 :     if (buf->data[buf->len - 1] == ' ')
      66             :     {
      67             :         /* Truncate-away final unneeded ", "  */
      68             :         Assert(buf->data[buf->len - 2] == ',');
      69           0 :         buf->len -= 2;
      70           0 :         buf->data[buf->len] = '\0';
      71             :     }
      72             : 
      73          12 :     appendStringInfoChar(buf, ']');
      74          12 : }
      75             : 
      76             : static void
      77        1232 : plan_elem_desc(StringInfo buf, void *plan, void *data)
      78             : {
      79        1232 :     xlhp_freeze_plan *new_plan = (xlhp_freeze_plan *) plan;
      80        1232 :     OffsetNumber **offsets = data;
      81             : 
      82        1232 :     appendStringInfo(buf, "{ xmax: %u, infomask: %u, infomask2: %u, ntuples: %u",
      83             :                      new_plan->xmax,
      84        1232 :                      new_plan->t_infomask, new_plan->t_infomask2,
      85        1232 :                      new_plan->ntuples);
      86             : 
      87        1232 :     appendStringInfoString(buf, ", offsets:");
      88        1232 :     array_desc(buf, *offsets, sizeof(OffsetNumber), new_plan->ntuples,
      89             :                &offset_elem_desc, NULL);
      90             : 
      91        1232 :     *offsets += new_plan->ntuples;
      92             : 
      93        1232 :     appendStringInfoString(buf, " }");
      94        1232 : }
      95             : 
      96             : 
      97             : /*
      98             :  * Given a MAXALIGNed buffer returned by XLogRecGetBlockData() and pointed to
      99             :  * by cursor and any xl_heap_prune flags, deserialize the arrays of
     100             :  * OffsetNumbers contained in an XLOG_HEAP2_PRUNE_* record.
     101             :  *
     102             :  * This is in heapdesc.c so it can be shared between heap2_redo and heap2_desc
     103             :  * code, the latter of which is used in frontend (pg_waldump) code.
     104             :  */
     105             : void
     106        3064 : heap_xlog_deserialize_prune_and_freeze(char *cursor, uint16 flags,
     107             :                                        int *nplans, xlhp_freeze_plan **plans,
     108             :                                        OffsetNumber **frz_offsets,
     109             :                                        int *nredirected, OffsetNumber **redirected,
     110             :                                        int *ndead, OffsetNumber **nowdead,
     111             :                                        int *nunused, OffsetNumber **nowunused)
     112             : {
     113        3064 :     if (flags & XLHP_HAS_FREEZE_PLANS)
     114             :     {
     115        1028 :         xlhp_freeze_plans *freeze_plans = (xlhp_freeze_plans *) cursor;
     116             : 
     117        1028 :         *nplans = freeze_plans->nplans;
     118             :         Assert(*nplans > 0);
     119        1028 :         *plans = freeze_plans->plans;
     120             : 
     121        1028 :         cursor += offsetof(xlhp_freeze_plans, plans);
     122        1028 :         cursor += sizeof(xlhp_freeze_plan) * *nplans;
     123             :     }
     124             :     else
     125             :     {
     126        2036 :         *nplans = 0;
     127        2036 :         *plans = NULL;
     128             :     }
     129             : 
     130        3064 :     if (flags & XLHP_HAS_REDIRECTIONS)
     131             :     {
     132         704 :         xlhp_prune_items *subrecord = (xlhp_prune_items *) cursor;
     133             : 
     134         704 :         *nredirected = subrecord->ntargets;
     135             :         Assert(*nredirected > 0);
     136         704 :         *redirected = &subrecord->data[0];
     137             : 
     138         704 :         cursor += offsetof(xlhp_prune_items, data);
     139         704 :         cursor += sizeof(OffsetNumber[2]) * *nredirected;
     140             :     }
     141             :     else
     142             :     {
     143        2360 :         *nredirected = 0;
     144        2360 :         *redirected = NULL;
     145             :     }
     146             : 
     147        3064 :     if (flags & XLHP_HAS_DEAD_ITEMS)
     148             :     {
     149         816 :         xlhp_prune_items *subrecord = (xlhp_prune_items *) cursor;
     150             : 
     151         816 :         *ndead = subrecord->ntargets;
     152             :         Assert(*ndead > 0);
     153         816 :         *nowdead = subrecord->data;
     154             : 
     155         816 :         cursor += offsetof(xlhp_prune_items, data);
     156         816 :         cursor += sizeof(OffsetNumber) * *ndead;
     157             :     }
     158             :     else
     159             :     {
     160        2248 :         *ndead = 0;
     161        2248 :         *nowdead = NULL;
     162             :     }
     163             : 
     164        3064 :     if (flags & XLHP_HAS_NOW_UNUSED_ITEMS)
     165             :     {
     166         756 :         xlhp_prune_items *subrecord = (xlhp_prune_items *) cursor;
     167             : 
     168         756 :         *nunused = subrecord->ntargets;
     169             :         Assert(*nunused > 0);
     170         756 :         *nowunused = subrecord->data;
     171             : 
     172         756 :         cursor += offsetof(xlhp_prune_items, data);
     173         756 :         cursor += sizeof(OffsetNumber) * *nunused;
     174             :     }
     175             :     else
     176             :     {
     177        2308 :         *nunused = 0;
     178        2308 :         *nowunused = NULL;
     179             :     }
     180             : 
     181        3064 :     *frz_offsets = (OffsetNumber *) cursor;
     182        3064 : }
     183             : 
     184             : void
     185      270860 : heap_desc(StringInfo buf, XLogReaderState *record)
     186             : {
     187      270860 :     char       *rec = XLogRecGetData(record);
     188      270860 :     uint8       info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
     189             : 
     190      270860 :     info &= XLOG_HEAP_OPMASK;
     191      270860 :     if (info == XLOG_HEAP_INSERT)
     192             :     {
     193      144098 :         xl_heap_insert *xlrec = (xl_heap_insert *) rec;
     194             : 
     195      144098 :         appendStringInfo(buf, "off: %u, flags: 0x%02X",
     196      144098 :                          xlrec->offnum,
     197      144098 :                          xlrec->flags);
     198             :     }
     199      126762 :     else if (info == XLOG_HEAP_DELETE)
     200             :     {
     201         430 :         xl_heap_delete *xlrec = (xl_heap_delete *) rec;
     202             : 
     203         430 :         appendStringInfo(buf, "xmax: %u, off: %u, ",
     204         430 :                          xlrec->xmax, xlrec->offnum);
     205         430 :         infobits_desc(buf, xlrec->infobits_set, "infobits");
     206         430 :         appendStringInfo(buf, ", flags: 0x%02X", xlrec->flags);
     207             :     }
     208      126332 :     else if (info == XLOG_HEAP_UPDATE)
     209             :     {
     210       61056 :         xl_heap_update *xlrec = (xl_heap_update *) rec;
     211             : 
     212       61056 :         appendStringInfo(buf, "old_xmax: %u, old_off: %u, ",
     213       61056 :                          xlrec->old_xmax, xlrec->old_offnum);
     214       61056 :         infobits_desc(buf, xlrec->old_infobits_set, "old_infobits");
     215       61056 :         appendStringInfo(buf, ", flags: 0x%02X, new_xmax: %u, new_off: %u",
     216       61056 :                          xlrec->flags, xlrec->new_xmax, xlrec->new_offnum);
     217             :     }
     218       65276 :     else if (info == XLOG_HEAP_HOT_UPDATE)
     219             :     {
     220        1636 :         xl_heap_update *xlrec = (xl_heap_update *) rec;
     221             : 
     222        1636 :         appendStringInfo(buf, "old_xmax: %u, old_off: %u, ",
     223        1636 :                          xlrec->old_xmax, xlrec->old_offnum);
     224        1636 :         infobits_desc(buf, xlrec->old_infobits_set, "old_infobits");
     225        1636 :         appendStringInfo(buf, ", flags: 0x%02X, new_xmax: %u, new_off: %u",
     226        1636 :                          xlrec->flags, xlrec->new_xmax, xlrec->new_offnum);
     227             :     }
     228       63640 :     else if (info == XLOG_HEAP_TRUNCATE)
     229             :     {
     230          12 :         xl_heap_truncate *xlrec = (xl_heap_truncate *) rec;
     231             : 
     232          12 :         truncate_flags_desc(buf, xlrec->flags);
     233          12 :         appendStringInfo(buf, ", nrelids: %u", xlrec->nrelids);
     234          12 :         appendStringInfoString(buf, ", relids:");
     235          12 :         array_desc(buf, xlrec->relids, sizeof(Oid), xlrec->nrelids,
     236             :                    &oid_elem_desc, NULL);
     237             :     }
     238       63628 :     else if (info == XLOG_HEAP_CONFIRM)
     239             :     {
     240           0 :         xl_heap_confirm *xlrec = (xl_heap_confirm *) rec;
     241             : 
     242           0 :         appendStringInfo(buf, "off: %u", xlrec->offnum);
     243             :     }
     244       63628 :     else if (info == XLOG_HEAP_LOCK)
     245             :     {
     246       61000 :         xl_heap_lock *xlrec = (xl_heap_lock *) rec;
     247             : 
     248       61000 :         appendStringInfo(buf, "xmax: %u, off: %u, ",
     249       61000 :                          xlrec->xmax, xlrec->offnum);
     250       61000 :         infobits_desc(buf, xlrec->infobits_set, "infobits");
     251       61000 :         appendStringInfo(buf, ", flags: 0x%02X", xlrec->flags);
     252             :     }
     253        2628 :     else if (info == XLOG_HEAP_INPLACE)
     254             :     {
     255        2628 :         xl_heap_inplace *xlrec = (xl_heap_inplace *) rec;
     256             : 
     257        2628 :         appendStringInfo(buf, "off: %u", xlrec->offnum);
     258        2628 :         standby_desc_invalidations(buf, xlrec->nmsgs, xlrec->msgs,
     259             :                                    xlrec->dbId, xlrec->tsId,
     260        2628 :                                    xlrec->relcacheInitFileInval);
     261             :     }
     262      270860 : }
     263             : 
     264             : void
     265       16560 : heap2_desc(StringInfo buf, XLogReaderState *record)
     266             : {
     267       16560 :     char       *rec = XLogRecGetData(record);
     268       16560 :     uint8       info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
     269             : 
     270       16560 :     info &= XLOG_HEAP_OPMASK;
     271       16560 :     if (info == XLOG_HEAP2_PRUNE_ON_ACCESS ||
     272       14068 :         info == XLOG_HEAP2_PRUNE_VACUUM_SCAN ||
     273             :         info == XLOG_HEAP2_PRUNE_VACUUM_CLEANUP)
     274        3064 :     {
     275        3064 :         xl_heap_prune *xlrec = (xl_heap_prune *) rec;
     276             : 
     277        3064 :         if (xlrec->flags & XLHP_HAS_CONFLICT_HORIZON)
     278             :         {
     279             :             TransactionId conflict_xid;
     280             : 
     281        2398 :             memcpy(&conflict_xid, rec + SizeOfHeapPrune, sizeof(TransactionId));
     282             : 
     283        2398 :             appendStringInfo(buf, "snapshotConflictHorizon: %u",
     284             :                              conflict_xid);
     285             :         }
     286             : 
     287        3064 :         appendStringInfo(buf, ", isCatalogRel: %c",
     288        3064 :                          xlrec->flags & XLHP_IS_CATALOG_REL ? 'T' : 'F');
     289             : 
     290        3064 :         if (xlrec->flags & XLHP_VM_ALL_VISIBLE)
     291             :         {
     292         572 :             uint8       vmflags = VISIBILITYMAP_ALL_VISIBLE;
     293             : 
     294         572 :             if (xlrec->flags & XLHP_VM_ALL_FROZEN)
     295         534 :                 vmflags |= VISIBILITYMAP_ALL_FROZEN;
     296         572 :             appendStringInfo(buf, ", vm_flags: 0x%02X", vmflags);
     297             :         }
     298             : 
     299        3064 :         if (XLogRecHasBlockData(record, 0))
     300             :         {
     301             :             Size        datalen;
     302             :             OffsetNumber *redirected;
     303             :             OffsetNumber *nowdead;
     304             :             OffsetNumber *nowunused;
     305             :             int         nredirected;
     306             :             int         nunused;
     307             :             int         ndead;
     308             :             int         nplans;
     309             :             xlhp_freeze_plan *plans;
     310             :             OffsetNumber *frz_offsets;
     311             : 
     312        3064 :             char       *cursor = XLogRecGetBlockData(record, 0, &datalen);
     313             : 
     314        3064 :             heap_xlog_deserialize_prune_and_freeze(cursor, xlrec->flags,
     315             :                                                    &nplans, &plans, &frz_offsets,
     316             :                                                    &nredirected, &redirected,
     317             :                                                    &ndead, &nowdead,
     318             :                                                    &nunused, &nowunused);
     319             : 
     320        3064 :             appendStringInfo(buf, ", nplans: %u, nredirected: %u, ndead: %u, nunused: %u",
     321             :                              nplans, nredirected, ndead, nunused);
     322             : 
     323        3064 :             if (nplans > 0)
     324             :             {
     325        1028 :                 appendStringInfoString(buf, ", plans:");
     326        1028 :                 array_desc(buf, plans, sizeof(xlhp_freeze_plan), nplans,
     327             :                            &plan_elem_desc, &frz_offsets);
     328             :             }
     329             : 
     330        3064 :             if (nredirected > 0)
     331             :             {
     332         704 :                 appendStringInfoString(buf, ", redirected:");
     333         704 :                 array_desc(buf, redirected, sizeof(OffsetNumber) * 2,
     334             :                            nredirected, &redirect_elem_desc, NULL);
     335             :             }
     336             : 
     337        3064 :             if (ndead > 0)
     338             :             {
     339         816 :                 appendStringInfoString(buf, ", dead:");
     340         816 :                 array_desc(buf, nowdead, sizeof(OffsetNumber), ndead,
     341             :                            &offset_elem_desc, NULL);
     342             :             }
     343             : 
     344        3064 :             if (nunused > 0)
     345             :             {
     346         756 :                 appendStringInfoString(buf, ", unused:");
     347         756 :                 array_desc(buf, nowunused, sizeof(OffsetNumber), nunused,
     348             :                            &offset_elem_desc, NULL);
     349             :             }
     350             :         }
     351             :     }
     352       13496 :     else if (info == XLOG_HEAP2_VISIBLE)
     353             :     {
     354        2548 :         xl_heap_visible *xlrec = (xl_heap_visible *) rec;
     355             : 
     356        2548 :         appendStringInfo(buf, "snapshotConflictHorizon: %u, flags: 0x%02X",
     357        2548 :                          xlrec->snapshotConflictHorizon, xlrec->flags);
     358             :     }
     359       10948 :     else if (info == XLOG_HEAP2_MULTI_INSERT)
     360             :     {
     361        7718 :         xl_heap_multi_insert *xlrec = (xl_heap_multi_insert *) rec;
     362        7718 :         bool        isinit = (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE) != 0;
     363             : 
     364        7718 :         appendStringInfo(buf, "ntuples: %d, flags: 0x%02X", xlrec->ntuples,
     365        7718 :                          xlrec->flags);
     366             : 
     367        7718 :         if (xlrec->flags & XLH_INSERT_ALL_FROZEN_SET)
     368           0 :             appendStringInfo(buf, ", vm_flags: 0x%02X",
     369             :                              VISIBILITYMAP_ALL_VISIBLE |
     370             :                              VISIBILITYMAP_ALL_FROZEN);
     371             : 
     372        7718 :         if (XLogRecHasBlockData(record, 0) && !isinit)
     373             :         {
     374        7424 :             appendStringInfoString(buf, ", offsets:");
     375        7424 :             array_desc(buf, xlrec->offsets, sizeof(OffsetNumber),
     376        7424 :                        xlrec->ntuples, &offset_elem_desc, NULL);
     377             :         }
     378             :     }
     379        3230 :     else if (info == XLOG_HEAP2_LOCK_UPDATED)
     380             :     {
     381           0 :         xl_heap_lock_updated *xlrec = (xl_heap_lock_updated *) rec;
     382             : 
     383           0 :         appendStringInfo(buf, "xmax: %u, off: %u, ",
     384           0 :                          xlrec->xmax, xlrec->offnum);
     385           0 :         infobits_desc(buf, xlrec->infobits_set, "infobits");
     386           0 :         appendStringInfo(buf, ", flags: 0x%02X", xlrec->flags);
     387             :     }
     388        3230 :     else if (info == XLOG_HEAP2_NEW_CID)
     389             :     {
     390        3230 :         xl_heap_new_cid *xlrec = (xl_heap_new_cid *) rec;
     391             : 
     392        3230 :         appendStringInfo(buf, "rel: %u/%u/%u, tid: %u/%u",
     393             :                          xlrec->target_locator.spcOid,
     394             :                          xlrec->target_locator.dbOid,
     395             :                          xlrec->target_locator.relNumber,
     396        3230 :                          ItemPointerGetBlockNumber(&(xlrec->target_tid)),
     397        3230 :                          ItemPointerGetOffsetNumber(&(xlrec->target_tid)));
     398        3230 :         appendStringInfo(buf, ", cmin: %u, cmax: %u, combo: %u",
     399             :                          xlrec->cmin, xlrec->cmax, xlrec->combocid);
     400             :     }
     401       16560 : }
     402             : 
     403             : const char *
     404      270878 : heap_identify(uint8 info)
     405             : {
     406      270878 :     const char *id = NULL;
     407             : 
     408      270878 :     switch (info & ~XLR_INFO_MASK)
     409             :     {
     410      142686 :         case XLOG_HEAP_INSERT:
     411      142686 :             id = "INSERT";
     412      142686 :             break;
     413        1416 :         case XLOG_HEAP_INSERT | XLOG_HEAP_INIT_PAGE:
     414        1416 :             id = "INSERT+INIT";
     415        1416 :             break;
     416         432 :         case XLOG_HEAP_DELETE:
     417         432 :             id = "DELETE";
     418         432 :             break;
     419       60618 :         case XLOG_HEAP_UPDATE:
     420       60618 :             id = "UPDATE";
     421       60618 :             break;
     422         442 :         case XLOG_HEAP_UPDATE | XLOG_HEAP_INIT_PAGE:
     423         442 :             id = "UPDATE+INIT";
     424         442 :             break;
     425        1638 :         case XLOG_HEAP_HOT_UPDATE:
     426        1638 :             id = "HOT_UPDATE";
     427        1638 :             break;
     428           0 :         case XLOG_HEAP_HOT_UPDATE | XLOG_HEAP_INIT_PAGE:
     429           0 :             id = "HOT_UPDATE+INIT";
     430           0 :             break;
     431          14 :         case XLOG_HEAP_TRUNCATE:
     432          14 :             id = "TRUNCATE";
     433          14 :             break;
     434           0 :         case XLOG_HEAP_CONFIRM:
     435           0 :             id = "HEAP_CONFIRM";
     436           0 :             break;
     437       61002 :         case XLOG_HEAP_LOCK:
     438       61002 :             id = "LOCK";
     439       61002 :             break;
     440        2630 :         case XLOG_HEAP_INPLACE:
     441        2630 :             id = "INPLACE";
     442        2630 :             break;
     443             :     }
     444             : 
     445      270878 :     return id;
     446             : }
     447             : 
     448             : const char *
     449       16574 : heap2_identify(uint8 info)
     450             : {
     451       16574 :     const char *id = NULL;
     452             : 
     453       16574 :     switch (info & ~XLR_INFO_MASK)
     454             :     {
     455        1090 :         case XLOG_HEAP2_PRUNE_ON_ACCESS:
     456        1090 :             id = "PRUNE_ON_ACCESS";
     457        1090 :             break;
     458        1406 :         case XLOG_HEAP2_PRUNE_VACUUM_SCAN:
     459        1406 :             id = "PRUNE_VACUUM_SCAN";
     460        1406 :             break;
     461         574 :         case XLOG_HEAP2_PRUNE_VACUUM_CLEANUP:
     462         574 :             id = "PRUNE_VACUUM_CLEANUP";
     463         574 :             break;
     464        2550 :         case XLOG_HEAP2_VISIBLE:
     465        2550 :             id = "VISIBLE";
     466        2550 :             break;
     467        7476 :         case XLOG_HEAP2_MULTI_INSERT:
     468        7476 :             id = "MULTI_INSERT";
     469        7476 :             break;
     470         246 :         case XLOG_HEAP2_MULTI_INSERT | XLOG_HEAP_INIT_PAGE:
     471         246 :             id = "MULTI_INSERT+INIT";
     472         246 :             break;
     473           0 :         case XLOG_HEAP2_LOCK_UPDATED:
     474           0 :             id = "LOCK_UPDATED";
     475           0 :             break;
     476        3232 :         case XLOG_HEAP2_NEW_CID:
     477        3232 :             id = "NEW_CID";
     478        3232 :             break;
     479           0 :         case XLOG_HEAP2_REWRITE:
     480           0 :             id = "REWRITE";
     481           0 :             break;
     482             :     }
     483             : 
     484       16574 :     return id;
     485             : }

Generated by: LCOV version 1.16