LCOV - code coverage report
Current view: top level - src/bin/pg_waldump - heapdesc.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 89.8 % 254 228
Test Date: 2026-02-17 17:20:33 Functions: 100.0 % 8 8
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-2026, 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        62061 : infobits_desc(StringInfo buf, uint8 infobits, const char *keyname)
      28              : {
      29        62061 :     appendStringInfo(buf, "%s: [", keyname);
      30              : 
      31              :     Assert(buf->data[buf->len - 1] != ' ');
      32              : 
      33        62061 :     if (infobits & XLHL_XMAX_IS_MULTI)
      34            0 :         appendStringInfoString(buf, "IS_MULTI, ");
      35        62061 :     if (infobits & XLHL_XMAX_LOCK_ONLY)
      36        30496 :         appendStringInfoString(buf, "LOCK_ONLY, ");
      37        62061 :     if (infobits & XLHL_XMAX_EXCL_LOCK)
      38        30496 :         appendStringInfoString(buf, "EXCL_LOCK, ");
      39        62061 :     if (infobits & XLHL_XMAX_KEYSHR_LOCK)
      40            0 :         appendStringInfoString(buf, "KEYSHR_LOCK, ");
      41        62061 :     if (infobits & XLHL_KEYS_UPDATED)
      42          219 :         appendStringInfoString(buf, "KEYS_UPDATED, ");
      43              : 
      44        62061 :     if (buf->data[buf->len - 1] == ' ')
      45              :     {
      46              :         /* Truncate-away final unneeded ", "  */
      47              :         Assert(buf->data[buf->len - 2] == ',');
      48        30715 :         buf->len -= 2;
      49        30715 :         buf->data[buf->len] = '\0';
      50              :     }
      51              : 
      52        62061 :     appendStringInfoChar(buf, ']');
      53        62061 : }
      54              : 
      55              : static void
      56            6 : truncate_flags_desc(StringInfo buf, uint8 flags)
      57              : {
      58            6 :     appendStringInfoString(buf, "flags: [");
      59              : 
      60            6 :     if (flags & XLH_TRUNCATE_CASCADE)
      61            0 :         appendStringInfoString(buf, "CASCADE, ");
      62            6 :     if (flags & XLH_TRUNCATE_RESTART_SEQS)
      63            0 :         appendStringInfoString(buf, "RESTART_SEQS, ");
      64              : 
      65            6 :     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            6 :     appendStringInfoChar(buf, ']');
      74            6 : }
      75              : 
      76              : static void
      77          622 : plan_elem_desc(StringInfo buf, void *plan, void *data)
      78              : {
      79          622 :     xlhp_freeze_plan *new_plan = (xlhp_freeze_plan *) plan;
      80          622 :     OffsetNumber **offsets = data;
      81              : 
      82          622 :     appendStringInfo(buf, "{ xmax: %u, infomask: %u, infomask2: %u, ntuples: %u",
      83              :                      new_plan->xmax,
      84          622 :                      new_plan->t_infomask, new_plan->t_infomask2,
      85          622 :                      new_plan->ntuples);
      86              : 
      87          622 :     appendStringInfoString(buf, ", offsets:");
      88          622 :     array_desc(buf, *offsets, sizeof(OffsetNumber), new_plan->ntuples,
      89              :                &offset_elem_desc, NULL);
      90              : 
      91          622 :     *offsets += new_plan->ntuples;
      92              : 
      93          622 :     appendStringInfoString(buf, " }");
      94          622 : }
      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         1532 : 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         1532 :     if (flags & XLHP_HAS_FREEZE_PLANS)
     114              :     {
     115          522 :         xlhp_freeze_plans *freeze_plans = (xlhp_freeze_plans *) cursor;
     116              : 
     117          522 :         *nplans = freeze_plans->nplans;
     118              :         Assert(*nplans > 0);
     119          522 :         *plans = freeze_plans->plans;
     120              : 
     121          522 :         cursor += offsetof(xlhp_freeze_plans, plans);
     122          522 :         cursor += sizeof(xlhp_freeze_plan) * *nplans;
     123              :     }
     124              :     else
     125              :     {
     126         1010 :         *nplans = 0;
     127         1010 :         *plans = NULL;
     128              :     }
     129              : 
     130         1532 :     if (flags & XLHP_HAS_REDIRECTIONS)
     131              :     {
     132          354 :         xlhp_prune_items *subrecord = (xlhp_prune_items *) cursor;
     133              : 
     134          354 :         *nredirected = subrecord->ntargets;
     135              :         Assert(*nredirected > 0);
     136          354 :         *redirected = &subrecord->data[0];
     137              : 
     138          354 :         cursor += offsetof(xlhp_prune_items, data);
     139          354 :         cursor += sizeof(OffsetNumber[2]) * *nredirected;
     140              :     }
     141              :     else
     142              :     {
     143         1178 :         *nredirected = 0;
     144         1178 :         *redirected = NULL;
     145              :     }
     146              : 
     147         1532 :     if (flags & XLHP_HAS_DEAD_ITEMS)
     148              :     {
     149          404 :         xlhp_prune_items *subrecord = (xlhp_prune_items *) cursor;
     150              : 
     151          404 :         *ndead = subrecord->ntargets;
     152              :         Assert(*ndead > 0);
     153          404 :         *nowdead = subrecord->data;
     154              : 
     155          404 :         cursor += offsetof(xlhp_prune_items, data);
     156          404 :         cursor += sizeof(OffsetNumber) * *ndead;
     157              :     }
     158              :     else
     159              :     {
     160         1128 :         *ndead = 0;
     161         1128 :         *nowdead = NULL;
     162              :     }
     163              : 
     164         1532 :     if (flags & XLHP_HAS_NOW_UNUSED_ITEMS)
     165              :     {
     166          372 :         xlhp_prune_items *subrecord = (xlhp_prune_items *) cursor;
     167              : 
     168          372 :         *nunused = subrecord->ntargets;
     169              :         Assert(*nunused > 0);
     170          372 :         *nowunused = subrecord->data;
     171              : 
     172          372 :         cursor += offsetof(xlhp_prune_items, data);
     173          372 :         cursor += sizeof(OffsetNumber) * *nunused;
     174              :     }
     175              :     else
     176              :     {
     177         1160 :         *nunused = 0;
     178         1160 :         *nowunused = NULL;
     179              :     }
     180              : 
     181         1532 :     *frz_offsets = (OffsetNumber *) cursor;
     182         1532 : }
     183              : 
     184              : void
     185       135706 : heap_desc(StringInfo buf, XLogReaderState *record)
     186              : {
     187       135706 :     char       *rec = XLogRecGetData(record);
     188       135706 :     uint8       info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
     189              : 
     190       135706 :     info &= XLOG_HEAP_OPMASK;
     191       135706 :     if (info == XLOG_HEAP_INSERT)
     192              :     {
     193        72337 :         xl_heap_insert *xlrec = (xl_heap_insert *) rec;
     194              : 
     195        72337 :         appendStringInfo(buf, "off: %u, flags: 0x%02X",
     196        72337 :                          xlrec->offnum,
     197        72337 :                          xlrec->flags);
     198              :     }
     199        63369 :     else if (info == XLOG_HEAP_DELETE)
     200              :     {
     201          219 :         xl_heap_delete *xlrec = (xl_heap_delete *) rec;
     202              : 
     203          219 :         appendStringInfo(buf, "xmax: %u, off: %u, ",
     204          219 :                          xlrec->xmax, xlrec->offnum);
     205          219 :         infobits_desc(buf, xlrec->infobits_set, "infobits");
     206          219 :         appendStringInfo(buf, ", flags: 0x%02X", xlrec->flags);
     207              :     }
     208        63150 :     else if (info == XLOG_HEAP_UPDATE)
     209              :     {
     210        30524 :         xl_heap_update *xlrec = (xl_heap_update *) rec;
     211              : 
     212        30524 :         appendStringInfo(buf, "old_xmax: %u, old_off: %u, ",
     213        30524 :                          xlrec->old_xmax, xlrec->old_offnum);
     214        30524 :         infobits_desc(buf, xlrec->old_infobits_set, "old_infobits");
     215        30524 :         appendStringInfo(buf, ", flags: 0x%02X, new_xmax: %u, new_off: %u",
     216        30524 :                          xlrec->flags, xlrec->new_xmax, xlrec->new_offnum);
     217              :     }
     218        32626 :     else if (info == XLOG_HEAP_HOT_UPDATE)
     219              :     {
     220          822 :         xl_heap_update *xlrec = (xl_heap_update *) rec;
     221              : 
     222          822 :         appendStringInfo(buf, "old_xmax: %u, old_off: %u, ",
     223          822 :                          xlrec->old_xmax, xlrec->old_offnum);
     224          822 :         infobits_desc(buf, xlrec->old_infobits_set, "old_infobits");
     225          822 :         appendStringInfo(buf, ", flags: 0x%02X, new_xmax: %u, new_off: %u",
     226          822 :                          xlrec->flags, xlrec->new_xmax, xlrec->new_offnum);
     227              :     }
     228        31804 :     else if (info == XLOG_HEAP_TRUNCATE)
     229              :     {
     230            6 :         xl_heap_truncate *xlrec = (xl_heap_truncate *) rec;
     231              : 
     232            6 :         truncate_flags_desc(buf, xlrec->flags);
     233            6 :         appendStringInfo(buf, ", nrelids: %u", xlrec->nrelids);
     234            6 :         appendStringInfoString(buf, ", relids:");
     235            6 :         array_desc(buf, xlrec->relids, sizeof(Oid), xlrec->nrelids,
     236              :                    &oid_elem_desc, NULL);
     237              :     }
     238        31798 :     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        31798 :     else if (info == XLOG_HEAP_LOCK)
     245              :     {
     246        30496 :         xl_heap_lock *xlrec = (xl_heap_lock *) rec;
     247              : 
     248        30496 :         appendStringInfo(buf, "xmax: %u, off: %u, ",
     249        30496 :                          xlrec->xmax, xlrec->offnum);
     250        30496 :         infobits_desc(buf, xlrec->infobits_set, "infobits");
     251        30496 :         appendStringInfo(buf, ", flags: 0x%02X", xlrec->flags);
     252              :     }
     253         1302 :     else if (info == XLOG_HEAP_INPLACE)
     254              :     {
     255         1302 :         xl_heap_inplace *xlrec = (xl_heap_inplace *) rec;
     256              : 
     257         1302 :         appendStringInfo(buf, "off: %u", xlrec->offnum);
     258         1302 :         standby_desc_invalidations(buf, xlrec->nmsgs, xlrec->msgs,
     259              :                                    xlrec->dbId, xlrec->tsId,
     260         1302 :                                    xlrec->relcacheInitFileInval);
     261              :     }
     262       135706 : }
     263              : 
     264              : void
     265         8323 : heap2_desc(StringInfo buf, XLogReaderState *record)
     266              : {
     267         8323 :     char       *rec = XLogRecGetData(record);
     268         8323 :     uint8       info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
     269              : 
     270         8323 :     info &= XLOG_HEAP_OPMASK;
     271         8323 :     if (info == XLOG_HEAP2_PRUNE_ON_ACCESS ||
     272         7073 :         info == XLOG_HEAP2_PRUNE_VACUUM_SCAN ||
     273              :         info == XLOG_HEAP2_PRUNE_VACUUM_CLEANUP)
     274         1532 :     {
     275         1532 :         xl_heap_prune *xlrec = (xl_heap_prune *) rec;
     276              : 
     277         1532 :         if (xlrec->flags & XLHP_HAS_CONFLICT_HORIZON)
     278              :         {
     279              :             TransactionId conflict_xid;
     280              : 
     281         1197 :             memcpy(&conflict_xid, rec + SizeOfHeapPrune, sizeof(TransactionId));
     282              : 
     283         1197 :             appendStringInfo(buf, "snapshotConflictHorizon: %u",
     284              :                              conflict_xid);
     285              :         }
     286              : 
     287         1532 :         appendStringInfo(buf, ", isCatalogRel: %c",
     288         1532 :                          xlrec->flags & XLHP_IS_CATALOG_REL ? 'T' : 'F');
     289              : 
     290         1532 :         if (xlrec->flags & XLHP_VM_ALL_VISIBLE)
     291              :         {
     292          282 :             uint8       vmflags = VISIBILITYMAP_ALL_VISIBLE;
     293              : 
     294          282 :             if (xlrec->flags & XLHP_VM_ALL_FROZEN)
     295          269 :                 vmflags |= VISIBILITYMAP_ALL_FROZEN;
     296          282 :             appendStringInfo(buf, ", vm_flags: 0x%02X", vmflags);
     297              :         }
     298              : 
     299         1532 :         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         1532 :             char       *cursor = XLogRecGetBlockData(record, 0, &datalen);
     313              : 
     314         1532 :             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         1532 :             appendStringInfo(buf, ", nplans: %u, nredirected: %u, ndead: %u, nunused: %u",
     321              :                              nplans, nredirected, ndead, nunused);
     322              : 
     323         1532 :             if (nplans > 0)
     324              :             {
     325          522 :                 appendStringInfoString(buf, ", plans:");
     326          522 :                 array_desc(buf, plans, sizeof(xlhp_freeze_plan), nplans,
     327              :                            &plan_elem_desc, &frz_offsets);
     328              :             }
     329              : 
     330         1532 :             if (nredirected > 0)
     331              :             {
     332          354 :                 appendStringInfoString(buf, ", redirected:");
     333          354 :                 array_desc(buf, redirected, sizeof(OffsetNumber) * 2,
     334              :                            nredirected, &redirect_elem_desc, NULL);
     335              :             }
     336              : 
     337         1532 :             if (ndead > 0)
     338              :             {
     339          404 :                 appendStringInfoString(buf, ", dead:");
     340          404 :                 array_desc(buf, nowdead, sizeof(OffsetNumber), ndead,
     341              :                            &offset_elem_desc, NULL);
     342              :             }
     343              : 
     344         1532 :             if (nunused > 0)
     345              :             {
     346          372 :                 appendStringInfoString(buf, ", unused:");
     347          372 :                 array_desc(buf, nowunused, sizeof(OffsetNumber), nunused,
     348              :                            &offset_elem_desc, NULL);
     349              :             }
     350              :         }
     351              :     }
     352         6791 :     else if (info == XLOG_HEAP2_VISIBLE)
     353              :     {
     354         1298 :         xl_heap_visible *xlrec = (xl_heap_visible *) rec;
     355              : 
     356         1298 :         appendStringInfo(buf, "snapshotConflictHorizon: %u, flags: 0x%02X",
     357         1298 :                          xlrec->snapshotConflictHorizon, xlrec->flags);
     358              :     }
     359         5493 :     else if (info == XLOG_HEAP2_MULTI_INSERT)
     360              :     {
     361         3878 :         xl_heap_multi_insert *xlrec = (xl_heap_multi_insert *) rec;
     362         3878 :         bool        isinit = (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE) != 0;
     363              : 
     364         3878 :         appendStringInfo(buf, "ntuples: %d, flags: 0x%02X", xlrec->ntuples,
     365         3878 :                          xlrec->flags);
     366              : 
     367         3878 :         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         3878 :         if (XLogRecHasBlockData(record, 0) && !isinit)
     373              :         {
     374         3728 :             appendStringInfoString(buf, ", offsets:");
     375         3728 :             array_desc(buf, xlrec->offsets, sizeof(OffsetNumber),
     376         3728 :                        xlrec->ntuples, &offset_elem_desc, NULL);
     377              :         }
     378              :     }
     379         1615 :     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         1615 :     else if (info == XLOG_HEAP2_NEW_CID)
     389              :     {
     390         1615 :         xl_heap_new_cid *xlrec = (xl_heap_new_cid *) rec;
     391              : 
     392         1615 :         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         1615 :                          ItemPointerGetBlockNumber(&(xlrec->target_tid)),
     397         1615 :                          ItemPointerGetOffsetNumber(&(xlrec->target_tid)));
     398         1615 :         appendStringInfo(buf, ", cmin: %u, cmax: %u, combo: %u",
     399              :                          xlrec->cmin, xlrec->cmax, xlrec->combocid);
     400              :     }
     401         8323 : }
     402              : 
     403              : const char *
     404       135715 : heap_identify(uint8 info)
     405              : {
     406       135715 :     const char *id = NULL;
     407              : 
     408       135715 :     switch (info & ~XLR_INFO_MASK)
     409              :     {
     410        71621 :         case XLOG_HEAP_INSERT:
     411        71621 :             id = "INSERT";
     412        71621 :             break;
     413          718 :         case XLOG_HEAP_INSERT | XLOG_HEAP_INIT_PAGE:
     414          718 :             id = "INSERT+INIT";
     415          718 :             break;
     416          220 :         case XLOG_HEAP_DELETE:
     417          220 :             id = "DELETE";
     418          220 :             break;
     419        30305 :         case XLOG_HEAP_UPDATE:
     420        30305 :             id = "UPDATE";
     421        30305 :             break;
     422          221 :         case XLOG_HEAP_UPDATE | XLOG_HEAP_INIT_PAGE:
     423          221 :             id = "UPDATE+INIT";
     424          221 :             break;
     425          823 :         case XLOG_HEAP_HOT_UPDATE:
     426          823 :             id = "HOT_UPDATE";
     427          823 :             break;
     428            0 :         case XLOG_HEAP_HOT_UPDATE | XLOG_HEAP_INIT_PAGE:
     429            0 :             id = "HOT_UPDATE+INIT";
     430            0 :             break;
     431            7 :         case XLOG_HEAP_TRUNCATE:
     432            7 :             id = "TRUNCATE";
     433            7 :             break;
     434            0 :         case XLOG_HEAP_CONFIRM:
     435            0 :             id = "HEAP_CONFIRM";
     436            0 :             break;
     437        30497 :         case XLOG_HEAP_LOCK:
     438        30497 :             id = "LOCK";
     439        30497 :             break;
     440         1303 :         case XLOG_HEAP_INPLACE:
     441         1303 :             id = "INPLACE";
     442         1303 :             break;
     443              :     }
     444              : 
     445       135715 :     return id;
     446              : }
     447              : 
     448              : const char *
     449         8330 : heap2_identify(uint8 info)
     450              : {
     451         8330 :     const char *id = NULL;
     452              : 
     453         8330 :     switch (info & ~XLR_INFO_MASK)
     454              :     {
     455          541 :         case XLOG_HEAP2_PRUNE_ON_ACCESS:
     456          541 :             id = "PRUNE_ON_ACCESS";
     457          541 :             break;
     458          711 :         case XLOG_HEAP2_PRUNE_VACUUM_SCAN:
     459          711 :             id = "PRUNE_VACUUM_SCAN";
     460          711 :             break;
     461          283 :         case XLOG_HEAP2_PRUNE_VACUUM_CLEANUP:
     462          283 :             id = "PRUNE_VACUUM_CLEANUP";
     463          283 :             break;
     464         1299 :         case XLOG_HEAP2_VISIBLE:
     465         1299 :             id = "VISIBLE";
     466         1299 :             break;
     467         3761 :         case XLOG_HEAP2_MULTI_INSERT:
     468         3761 :             id = "MULTI_INSERT";
     469         3761 :             break;
     470          119 :         case XLOG_HEAP2_MULTI_INSERT | XLOG_HEAP_INIT_PAGE:
     471          119 :             id = "MULTI_INSERT+INIT";
     472          119 :             break;
     473            0 :         case XLOG_HEAP2_LOCK_UPDATED:
     474            0 :             id = "LOCK_UPDATED";
     475            0 :             break;
     476         1616 :         case XLOG_HEAP2_NEW_CID:
     477         1616 :             id = "NEW_CID";
     478         1616 :             break;
     479            0 :         case XLOG_HEAP2_REWRITE:
     480            0 :             id = "REWRITE";
     481            0 :             break;
     482              :     }
     483              : 
     484         8330 :     return id;
     485              : }
        

Generated by: LCOV version 2.0-1