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

Generated by: LCOV version 1.14