Coverage Report

Created: 2025-06-15 06:31

/src/postgres/src/backend/access/rmgrdesc/xlogdesc.c
Line
Count
Source (jump to first uncovered line)
1
/*-------------------------------------------------------------------------
2
 *
3
 * xlogdesc.c
4
 *    rmgr descriptor routines for access/transam/xlog.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/xlogdesc.c
12
 *
13
 *-------------------------------------------------------------------------
14
 */
15
#include "postgres.h"
16
17
#include "access/transam.h"
18
#include "access/xlog.h"
19
#include "access/xlog_internal.h"
20
#include "catalog/pg_control.h"
21
#include "utils/guc.h"
22
#include "utils/timestamp.h"
23
24
/*
25
 * GUC support
26
 */
27
const struct config_enum_entry wal_level_options[] = {
28
  {"minimal", WAL_LEVEL_MINIMAL, false},
29
  {"replica", WAL_LEVEL_REPLICA, false},
30
  {"archive", WAL_LEVEL_REPLICA, true}, /* deprecated */
31
  {"hot_standby", WAL_LEVEL_REPLICA, true}, /* deprecated */
32
  {"logical", WAL_LEVEL_LOGICAL, false},
33
  {NULL, 0, false}
34
};
35
36
/*
37
 * Find a string representation for wal_level
38
 */
39
static const char *
40
get_wal_level_string(int wal_level)
41
0
{
42
0
  const struct config_enum_entry *entry;
43
0
  const char *wal_level_str = "?";
44
45
0
  for (entry = wal_level_options; entry->name; entry++)
46
0
  {
47
0
    if (entry->val == wal_level)
48
0
    {
49
0
      wal_level_str = entry->name;
50
0
      break;
51
0
    }
52
0
  }
53
54
0
  return wal_level_str;
55
0
}
56
57
void
58
xlog_desc(StringInfo buf, XLogReaderState *record)
59
0
{
60
0
  char     *rec = XLogRecGetData(record);
61
0
  uint8   info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
62
63
0
  if (info == XLOG_CHECKPOINT_SHUTDOWN ||
64
0
    info == XLOG_CHECKPOINT_ONLINE)
65
0
  {
66
0
    CheckPoint *checkpoint = (CheckPoint *) rec;
67
68
0
    appendStringInfo(buf, "redo %X/%X; "
69
0
             "tli %u; prev tli %u; fpw %s; wal_level %s; xid %u:%u; oid %u; multi %u; offset %u; "
70
0
             "oldest xid %u in DB %u; oldest multi %u in DB %u; "
71
0
             "oldest/newest commit timestamp xid: %u/%u; "
72
0
             "oldest running xid %u; %s",
73
0
             LSN_FORMAT_ARGS(checkpoint->redo),
74
0
             checkpoint->ThisTimeLineID,
75
0
             checkpoint->PrevTimeLineID,
76
0
             checkpoint->fullPageWrites ? "true" : "false",
77
0
             get_wal_level_string(checkpoint->wal_level),
78
0
             EpochFromFullTransactionId(checkpoint->nextXid),
79
0
             XidFromFullTransactionId(checkpoint->nextXid),
80
0
             checkpoint->nextOid,
81
0
             checkpoint->nextMulti,
82
0
             checkpoint->nextMultiOffset,
83
0
             checkpoint->oldestXid,
84
0
             checkpoint->oldestXidDB,
85
0
             checkpoint->oldestMulti,
86
0
             checkpoint->oldestMultiDB,
87
0
             checkpoint->oldestCommitTsXid,
88
0
             checkpoint->newestCommitTsXid,
89
0
             checkpoint->oldestActiveXid,
90
0
             (info == XLOG_CHECKPOINT_SHUTDOWN) ? "shutdown" : "online");
91
0
  }
92
0
  else if (info == XLOG_NEXTOID)
93
0
  {
94
0
    Oid     nextOid;
95
96
0
    memcpy(&nextOid, rec, sizeof(Oid));
97
0
    appendStringInfo(buf, "%u", nextOid);
98
0
  }
99
0
  else if (info == XLOG_RESTORE_POINT)
100
0
  {
101
0
    xl_restore_point *xlrec = (xl_restore_point *) rec;
102
103
0
    appendStringInfoString(buf, xlrec->rp_name);
104
0
  }
105
0
  else if (info == XLOG_FPI || info == XLOG_FPI_FOR_HINT)
106
0
  {
107
    /* no further information to print */
108
0
  }
109
0
  else if (info == XLOG_BACKUP_END)
110
0
  {
111
0
    XLogRecPtr  startpoint;
112
113
0
    memcpy(&startpoint, rec, sizeof(XLogRecPtr));
114
0
    appendStringInfo(buf, "%X/%X", LSN_FORMAT_ARGS(startpoint));
115
0
  }
116
0
  else if (info == XLOG_PARAMETER_CHANGE)
117
0
  {
118
0
    xl_parameter_change xlrec;
119
0
    const char *wal_level_str;
120
121
0
    memcpy(&xlrec, rec, sizeof(xl_parameter_change));
122
0
    wal_level_str = get_wal_level_string(xlrec.wal_level);
123
124
0
    appendStringInfo(buf, "max_connections=%d max_worker_processes=%d "
125
0
             "max_wal_senders=%d max_prepared_xacts=%d "
126
0
             "max_locks_per_xact=%d wal_level=%s "
127
0
             "wal_log_hints=%s track_commit_timestamp=%s",
128
0
             xlrec.MaxConnections,
129
0
             xlrec.max_worker_processes,
130
0
             xlrec.max_wal_senders,
131
0
             xlrec.max_prepared_xacts,
132
0
             xlrec.max_locks_per_xact,
133
0
             wal_level_str,
134
0
             xlrec.wal_log_hints ? "on" : "off",
135
0
             xlrec.track_commit_timestamp ? "on" : "off");
136
0
  }
137
0
  else if (info == XLOG_FPW_CHANGE)
138
0
  {
139
0
    bool    fpw;
140
141
0
    memcpy(&fpw, rec, sizeof(bool));
142
0
    appendStringInfoString(buf, fpw ? "true" : "false");
143
0
  }
144
0
  else if (info == XLOG_END_OF_RECOVERY)
145
0
  {
146
0
    xl_end_of_recovery xlrec;
147
148
0
    memcpy(&xlrec, rec, sizeof(xl_end_of_recovery));
149
0
    appendStringInfo(buf, "tli %u; prev tli %u; time %s; wal_level %s",
150
0
             xlrec.ThisTimeLineID, xlrec.PrevTimeLineID,
151
0
             timestamptz_to_str(xlrec.end_time),
152
0
             get_wal_level_string(xlrec.wal_level));
153
0
  }
154
0
  else if (info == XLOG_OVERWRITE_CONTRECORD)
155
0
  {
156
0
    xl_overwrite_contrecord xlrec;
157
158
0
    memcpy(&xlrec, rec, sizeof(xl_overwrite_contrecord));
159
0
    appendStringInfo(buf, "lsn %X/%X; time %s",
160
0
             LSN_FORMAT_ARGS(xlrec.overwritten_lsn),
161
0
             timestamptz_to_str(xlrec.overwrite_time));
162
0
  }
163
0
  else if (info == XLOG_CHECKPOINT_REDO)
164
0
  {
165
0
    int     wal_level;
166
167
0
    memcpy(&wal_level, rec, sizeof(int));
168
0
    appendStringInfo(buf, "wal_level %s", get_wal_level_string(wal_level));
169
0
  }
170
0
}
171
172
const char *
173
xlog_identify(uint8 info)
174
0
{
175
0
  const char *id = NULL;
176
177
0
  switch (info & ~XLR_INFO_MASK)
178
0
  {
179
0
    case XLOG_CHECKPOINT_SHUTDOWN:
180
0
      id = "CHECKPOINT_SHUTDOWN";
181
0
      break;
182
0
    case XLOG_CHECKPOINT_ONLINE:
183
0
      id = "CHECKPOINT_ONLINE";
184
0
      break;
185
0
    case XLOG_NOOP:
186
0
      id = "NOOP";
187
0
      break;
188
0
    case XLOG_NEXTOID:
189
0
      id = "NEXTOID";
190
0
      break;
191
0
    case XLOG_SWITCH:
192
0
      id = "SWITCH";
193
0
      break;
194
0
    case XLOG_BACKUP_END:
195
0
      id = "BACKUP_END";
196
0
      break;
197
0
    case XLOG_PARAMETER_CHANGE:
198
0
      id = "PARAMETER_CHANGE";
199
0
      break;
200
0
    case XLOG_RESTORE_POINT:
201
0
      id = "RESTORE_POINT";
202
0
      break;
203
0
    case XLOG_FPW_CHANGE:
204
0
      id = "FPW_CHANGE";
205
0
      break;
206
0
    case XLOG_END_OF_RECOVERY:
207
0
      id = "END_OF_RECOVERY";
208
0
      break;
209
0
    case XLOG_OVERWRITE_CONTRECORD:
210
0
      id = "OVERWRITE_CONTRECORD";
211
0
      break;
212
0
    case XLOG_FPI:
213
0
      id = "FPI";
214
0
      break;
215
0
    case XLOG_FPI_FOR_HINT:
216
0
      id = "FPI_FOR_HINT";
217
0
      break;
218
0
    case XLOG_CHECKPOINT_REDO:
219
0
      id = "CHECKPOINT_REDO";
220
0
      break;
221
0
  }
222
223
0
  return id;
224
0
}
225
226
/*
227
 * Returns a string giving information about all the blocks in an
228
 * XLogRecord.
229
 */
230
void
231
XLogRecGetBlockRefInfo(XLogReaderState *record, bool pretty,
232
             bool detailed_format, StringInfo buf,
233
             uint32 *fpi_len)
234
0
{
235
0
  int     block_id;
236
237
0
  Assert(record != NULL);
238
239
0
  if (detailed_format && pretty)
240
0
    appendStringInfoChar(buf, '\n');
241
242
0
  for (block_id = 0; block_id <= XLogRecMaxBlockId(record); block_id++)
243
0
  {
244
0
    RelFileLocator rlocator;
245
0
    ForkNumber  forknum;
246
0
    BlockNumber blk;
247
248
0
    if (!XLogRecGetBlockTagExtended(record, block_id,
249
0
                    &rlocator, &forknum, &blk, NULL))
250
0
      continue;
251
252
0
    if (detailed_format)
253
0
    {
254
      /* Get block references in detailed format. */
255
256
0
      if (pretty)
257
0
        appendStringInfoChar(buf, '\t');
258
0
      else if (block_id > 0)
259
0
        appendStringInfoChar(buf, ' ');
260
261
0
      appendStringInfo(buf,
262
0
               "blkref #%d: rel %u/%u/%u fork %s blk %u",
263
0
               block_id,
264
0
               rlocator.spcOid, rlocator.dbOid, rlocator.relNumber,
265
0
               forkNames[forknum],
266
0
               blk);
267
268
0
      if (XLogRecHasBlockImage(record, block_id))
269
0
      {
270
0
        uint8   bimg_info = XLogRecGetBlock(record, block_id)->bimg_info;
271
272
        /* Calculate the amount of FPI data in the record. */
273
0
        if (fpi_len)
274
0
          *fpi_len += XLogRecGetBlock(record, block_id)->bimg_len;
275
276
0
        if (BKPIMAGE_COMPRESSED(bimg_info))
277
0
        {
278
0
          const char *method;
279
280
0
          if ((bimg_info & BKPIMAGE_COMPRESS_PGLZ) != 0)
281
0
            method = "pglz";
282
0
          else if ((bimg_info & BKPIMAGE_COMPRESS_LZ4) != 0)
283
0
            method = "lz4";
284
0
          else if ((bimg_info & BKPIMAGE_COMPRESS_ZSTD) != 0)
285
0
            method = "zstd";
286
0
          else
287
0
            method = "unknown";
288
289
0
          appendStringInfo(buf,
290
0
                   " (FPW%s); hole: offset: %u, length: %u, "
291
0
                   "compression saved: %u, method: %s",
292
0
                   XLogRecBlockImageApply(record, block_id) ?
293
0
                   "" : " for WAL verification",
294
0
                   XLogRecGetBlock(record, block_id)->hole_offset,
295
0
                   XLogRecGetBlock(record, block_id)->hole_length,
296
0
                   BLCKSZ -
297
0
                   XLogRecGetBlock(record, block_id)->hole_length -
298
0
                   XLogRecGetBlock(record, block_id)->bimg_len,
299
0
                   method);
300
0
        }
301
0
        else
302
0
        {
303
0
          appendStringInfo(buf,
304
0
                   " (FPW%s); hole: offset: %u, length: %u",
305
0
                   XLogRecBlockImageApply(record, block_id) ?
306
0
                   "" : " for WAL verification",
307
0
                   XLogRecGetBlock(record, block_id)->hole_offset,
308
0
                   XLogRecGetBlock(record, block_id)->hole_length);
309
0
        }
310
0
      }
311
312
0
      if (pretty)
313
0
        appendStringInfoChar(buf, '\n');
314
0
    }
315
0
    else
316
0
    {
317
      /* Get block references in short format. */
318
319
0
      if (forknum != MAIN_FORKNUM)
320
0
      {
321
0
        appendStringInfo(buf,
322
0
                 ", blkref #%d: rel %u/%u/%u fork %s blk %u",
323
0
                 block_id,
324
0
                 rlocator.spcOid, rlocator.dbOid, rlocator.relNumber,
325
0
                 forkNames[forknum],
326
0
                 blk);
327
0
      }
328
0
      else
329
0
      {
330
0
        appendStringInfo(buf,
331
0
                 ", blkref #%d: rel %u/%u/%u blk %u",
332
0
                 block_id,
333
0
                 rlocator.spcOid, rlocator.dbOid, rlocator.relNumber,
334
0
                 blk);
335
0
      }
336
337
0
      if (XLogRecHasBlockImage(record, block_id))
338
0
      {
339
        /* Calculate the amount of FPI data in the record. */
340
0
        if (fpi_len)
341
0
          *fpi_len += XLogRecGetBlock(record, block_id)->bimg_len;
342
343
0
        if (XLogRecBlockImageApply(record, block_id))
344
0
          appendStringInfoString(buf, " FPW");
345
0
        else
346
0
          appendStringInfoString(buf, " FPW for WAL verification");
347
0
      }
348
0
    }
349
0
  }
350
351
0
  if (!detailed_format && pretty)
352
0
    appendStringInfoChar(buf, '\n');
353
0
}