Coverage Report

Created: 2025-10-09 06:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/access/rmgrdesc/nbtdesc.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * nbtdesc.c
4
 *    rmgr descriptor routines for access/nbtree/nbtxlog.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/nbtdesc.c
12
 *
13
 *-------------------------------------------------------------------------
14
 */
15
#include "postgres.h"
16
17
#include "access/nbtxlog.h"
18
#include "access/rmgrdesc_utils.h"
19
20
static void delvacuum_desc(StringInfo buf, char *block_data,
21
               uint16 ndeleted, uint16 nupdated);
22
23
void
24
btree_desc(StringInfo buf, XLogReaderState *record)
25
0
{
26
0
  char     *rec = XLogRecGetData(record);
27
0
  uint8   info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
28
29
0
  switch (info)
30
0
  {
31
0
    case XLOG_BTREE_INSERT_LEAF:
32
0
    case XLOG_BTREE_INSERT_UPPER:
33
0
    case XLOG_BTREE_INSERT_META:
34
0
    case XLOG_BTREE_INSERT_POST:
35
0
      {
36
0
        xl_btree_insert *xlrec = (xl_btree_insert *) rec;
37
38
0
        appendStringInfo(buf, "off: %u", xlrec->offnum);
39
0
        break;
40
0
      }
41
0
    case XLOG_BTREE_SPLIT_L:
42
0
    case XLOG_BTREE_SPLIT_R:
43
0
      {
44
0
        xl_btree_split *xlrec = (xl_btree_split *) rec;
45
46
0
        appendStringInfo(buf, "level: %u, firstrightoff: %d, newitemoff: %d, postingoff: %d",
47
0
                 xlrec->level, xlrec->firstrightoff,
48
0
                 xlrec->newitemoff, xlrec->postingoff);
49
0
        break;
50
0
      }
51
0
    case XLOG_BTREE_DEDUP:
52
0
      {
53
0
        xl_btree_dedup *xlrec = (xl_btree_dedup *) rec;
54
55
0
        appendStringInfo(buf, "nintervals: %u", xlrec->nintervals);
56
0
        break;
57
0
      }
58
0
    case XLOG_BTREE_VACUUM:
59
0
      {
60
0
        xl_btree_vacuum *xlrec = (xl_btree_vacuum *) rec;
61
62
0
        appendStringInfo(buf, "ndeleted: %u, nupdated: %u",
63
0
                 xlrec->ndeleted, xlrec->nupdated);
64
65
0
        if (XLogRecHasBlockData(record, 0))
66
0
          delvacuum_desc(buf, XLogRecGetBlockData(record, 0, NULL),
67
0
                   xlrec->ndeleted, xlrec->nupdated);
68
0
        break;
69
0
      }
70
0
    case XLOG_BTREE_DELETE:
71
0
      {
72
0
        xl_btree_delete *xlrec = (xl_btree_delete *) rec;
73
74
0
        appendStringInfo(buf, "snapshotConflictHorizon: %u, ndeleted: %u, nupdated: %u, isCatalogRel: %c",
75
0
                 xlrec->snapshotConflictHorizon,
76
0
                 xlrec->ndeleted, xlrec->nupdated,
77
0
                 xlrec->isCatalogRel ? 'T' : 'F');
78
79
0
        if (XLogRecHasBlockData(record, 0))
80
0
          delvacuum_desc(buf, XLogRecGetBlockData(record, 0, NULL),
81
0
                   xlrec->ndeleted, xlrec->nupdated);
82
0
        break;
83
0
      }
84
0
    case XLOG_BTREE_MARK_PAGE_HALFDEAD:
85
0
      {
86
0
        xl_btree_mark_page_halfdead *xlrec = (xl_btree_mark_page_halfdead *) rec;
87
88
0
        appendStringInfo(buf, "topparent: %u, leaf: %u, left: %u, right: %u",
89
0
                 xlrec->topparent, xlrec->leafblk, xlrec->leftblk, xlrec->rightblk);
90
0
        break;
91
0
      }
92
0
    case XLOG_BTREE_UNLINK_PAGE_META:
93
0
    case XLOG_BTREE_UNLINK_PAGE:
94
0
      {
95
0
        xl_btree_unlink_page *xlrec = (xl_btree_unlink_page *) rec;
96
97
0
        appendStringInfo(buf, "left: %u, right: %u, level: %u, safexid: %u:%u, ",
98
0
                 xlrec->leftsib, xlrec->rightsib, xlrec->level,
99
0
                 EpochFromFullTransactionId(xlrec->safexid),
100
0
                 XidFromFullTransactionId(xlrec->safexid));
101
0
        appendStringInfo(buf, "leafleft: %u, leafright: %u, leaftopparent: %u",
102
0
                 xlrec->leafleftsib, xlrec->leafrightsib,
103
0
                 xlrec->leaftopparent);
104
0
        break;
105
0
      }
106
0
    case XLOG_BTREE_NEWROOT:
107
0
      {
108
0
        xl_btree_newroot *xlrec = (xl_btree_newroot *) rec;
109
110
0
        appendStringInfo(buf, "level: %u", xlrec->level);
111
0
        break;
112
0
      }
113
0
    case XLOG_BTREE_REUSE_PAGE:
114
0
      {
115
0
        xl_btree_reuse_page *xlrec = (xl_btree_reuse_page *) rec;
116
117
0
        appendStringInfo(buf, "rel: %u/%u/%u, snapshotConflictHorizon: %u:%u, isCatalogRel: %c",
118
0
                 xlrec->locator.spcOid, xlrec->locator.dbOid,
119
0
                 xlrec->locator.relNumber,
120
0
                 EpochFromFullTransactionId(xlrec->snapshotConflictHorizon),
121
0
                 XidFromFullTransactionId(xlrec->snapshotConflictHorizon),
122
0
                 xlrec->isCatalogRel ? 'T' : 'F');
123
0
        break;
124
0
      }
125
0
    case XLOG_BTREE_META_CLEANUP:
126
0
      {
127
0
        xl_btree_metadata *xlrec;
128
129
0
        xlrec = (xl_btree_metadata *) XLogRecGetBlockData(record, 0,
130
0
                                  NULL);
131
0
        appendStringInfo(buf, "last_cleanup_num_delpages: %u",
132
0
                 xlrec->last_cleanup_num_delpages);
133
0
        break;
134
0
      }
135
0
  }
136
0
}
137
138
const char *
139
btree_identify(uint8 info)
140
0
{
141
0
  const char *id = NULL;
142
143
0
  switch (info & ~XLR_INFO_MASK)
144
0
  {
145
0
    case XLOG_BTREE_INSERT_LEAF:
146
0
      id = "INSERT_LEAF";
147
0
      break;
148
0
    case XLOG_BTREE_INSERT_UPPER:
149
0
      id = "INSERT_UPPER";
150
0
      break;
151
0
    case XLOG_BTREE_INSERT_META:
152
0
      id = "INSERT_META";
153
0
      break;
154
0
    case XLOG_BTREE_SPLIT_L:
155
0
      id = "SPLIT_L";
156
0
      break;
157
0
    case XLOG_BTREE_SPLIT_R:
158
0
      id = "SPLIT_R";
159
0
      break;
160
0
    case XLOG_BTREE_INSERT_POST:
161
0
      id = "INSERT_POST";
162
0
      break;
163
0
    case XLOG_BTREE_DEDUP:
164
0
      id = "DEDUP";
165
0
      break;
166
0
    case XLOG_BTREE_VACUUM:
167
0
      id = "VACUUM";
168
0
      break;
169
0
    case XLOG_BTREE_DELETE:
170
0
      id = "DELETE";
171
0
      break;
172
0
    case XLOG_BTREE_MARK_PAGE_HALFDEAD:
173
0
      id = "MARK_PAGE_HALFDEAD";
174
0
      break;
175
0
    case XLOG_BTREE_UNLINK_PAGE:
176
0
      id = "UNLINK_PAGE";
177
0
      break;
178
0
    case XLOG_BTREE_UNLINK_PAGE_META:
179
0
      id = "UNLINK_PAGE_META";
180
0
      break;
181
0
    case XLOG_BTREE_NEWROOT:
182
0
      id = "NEWROOT";
183
0
      break;
184
0
    case XLOG_BTREE_REUSE_PAGE:
185
0
      id = "REUSE_PAGE";
186
0
      break;
187
0
    case XLOG_BTREE_META_CLEANUP:
188
0
      id = "META_CLEANUP";
189
0
      break;
190
0
  }
191
192
0
  return id;
193
0
}
194
195
static void
196
delvacuum_desc(StringInfo buf, char *block_data,
197
         uint16 ndeleted, uint16 nupdated)
198
0
{
199
0
  OffsetNumber *deletedoffsets;
200
0
  OffsetNumber *updatedoffsets;
201
0
  xl_btree_update *updates;
202
203
  /* Output deleted page offset number array */
204
0
  appendStringInfoString(buf, ", deleted:");
205
0
  deletedoffsets = (OffsetNumber *) block_data;
206
0
  array_desc(buf, deletedoffsets, sizeof(OffsetNumber), ndeleted,
207
0
         &offset_elem_desc, NULL);
208
209
  /*
210
   * Output updates as an array of "update objects", where each element
211
   * contains a page offset number from updated array.  (This is not the
212
   * most literal representation of the underlying physical data structure
213
   * that we could use.  Readability seems more important here.)
214
   */
215
0
  appendStringInfoString(buf, ", updated: [");
216
0
  updatedoffsets = (OffsetNumber *) (block_data + ndeleted *
217
0
                     sizeof(OffsetNumber));
218
0
  updates = (xl_btree_update *) ((char *) updatedoffsets +
219
0
                   nupdated *
220
0
                   sizeof(OffsetNumber));
221
0
  for (int i = 0; i < nupdated; i++)
222
0
  {
223
0
    OffsetNumber off = updatedoffsets[i];
224
225
0
    Assert(OffsetNumberIsValid(off));
226
0
    Assert(updates->ndeletedtids > 0);
227
228
    /*
229
     * "ptid" is the symbol name used when building each xl_btree_update's
230
     * array of offsets into a posting list tuple's ItemPointerData array.
231
     * xl_btree_update describes a subset of the existing TIDs to delete.
232
     */
233
0
    appendStringInfo(buf, "{ off: %u, nptids: %u, ptids: [",
234
0
             off, updates->ndeletedtids);
235
0
    for (int p = 0; p < updates->ndeletedtids; p++)
236
0
    {
237
0
      uint16     *ptid;
238
239
0
      ptid = (uint16 *) ((char *) updates + SizeOfBtreeUpdate) + p;
240
0
      appendStringInfo(buf, "%u", *ptid);
241
242
0
      if (p < updates->ndeletedtids - 1)
243
0
        appendStringInfoString(buf, ", ");
244
0
    }
245
0
    appendStringInfoString(buf, "] }");
246
0
    if (i < nupdated - 1)
247
0
      appendStringInfoString(buf, ", ");
248
249
0
    updates = (xl_btree_update *)
250
      ((char *) updates + SizeOfBtreeUpdate +
251
0
       updates->ndeletedtids * sizeof(uint16));
252
0
  }
253
0
  appendStringInfoChar(buf, ']');
254
0
}