Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * xlogdesc.c
4 : : * rmgr descriptor routines for access/transam/xlog.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/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 "storage/checksum.h"
22 : : #include "utils/guc.h"
23 : : #include "utils/timestamp.h"
24 : :
25 : : /*
26 : : * GUC support
27 : : */
28 : : const struct config_enum_entry wal_level_options[] = {
29 : : {"minimal", WAL_LEVEL_MINIMAL, false},
30 : : {"replica", WAL_LEVEL_REPLICA, false},
31 : : {"archive", WAL_LEVEL_REPLICA, true}, /* deprecated */
32 : : {"hot_standby", WAL_LEVEL_REPLICA, true}, /* deprecated */
33 : : {"logical", WAL_LEVEL_LOGICAL, false},
34 : : {NULL, 0, false}
35 : : };
36 : :
37 : : /*
38 : : * Find a string representation for wal_level
39 : : */
40 : : static const char *
656 rhaas@postgresql.org 41 :CBC 66 : get_wal_level_string(int wal_level)
42 : : {
43 : : const struct config_enum_entry *entry;
44 : 66 : const char *wal_level_str = "?";
45 : :
46 [ + - ]: 238 : for (entry = wal_level_options; entry->name; entry++)
47 : : {
48 [ + + ]: 238 : if (entry->val == wal_level)
49 : : {
50 : 66 : wal_level_str = entry->name;
51 : 66 : break;
52 : : }
53 : : }
54 : :
55 : 66 : return wal_level_str;
56 : : }
57 : :
58 : : const char *
32 dgustafsson@postgres 59 :GNC 1932 : get_checksum_state_string(uint32 state)
60 : : {
61 [ + - + + : 1932 : switch (state)
- ]
62 : : {
63 : 1886 : case PG_DATA_CHECKSUM_VERSION:
64 : 1886 : return "on";
32 dgustafsson@postgres 65 :UNC 0 : case PG_DATA_CHECKSUM_INPROGRESS_OFF:
66 : 0 : return "inprogress-off";
32 dgustafsson@postgres 67 :GNC 30 : case PG_DATA_CHECKSUM_INPROGRESS_ON:
68 : 30 : return "inprogress-on";
69 : 16 : case PG_DATA_CHECKSUM_OFF:
70 : 16 : return "off";
71 : : }
72 : :
32 dgustafsson@postgres 73 :UNC 0 : Assert(false);
74 : : return "?";
75 : : }
76 : :
77 : : void
78 : 0 : xlog2_desc(StringInfo buf, XLogReaderState *record)
79 : : {
80 : 0 : char *rec = XLogRecGetData(record);
81 : 0 : uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
82 : :
83 [ # # ]: 0 : if (info == XLOG2_CHECKSUMS)
84 : : {
85 : : xl_checksum_state xlrec;
86 : :
87 : 0 : memcpy(&xlrec, rec, sizeof(xl_checksum_state));
88 : 0 : appendStringInfoString(buf, get_checksum_state_string(xlrec.new_checksum_state));
89 : : }
90 : 0 : }
91 : :
92 : : void
4184 heikki.linnakangas@i 93 :CBC 18828 : xlog_desc(StringInfo buf, XLogReaderState *record)
94 : : {
4343 95 : 18828 : char *rec = XLogRecGetData(record);
4184 96 : 18828 : uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
97 : :
4906 alvherre@alvh.no-ip. 98 [ + + + + ]: 18828 : if (info == XLOG_CHECKPOINT_SHUTDOWN ||
99 : : info == XLOG_CHECKPOINT_ONLINE)
100 : 38 : {
101 : 38 : CheckPoint *checkpoint = (CheckPoint *) rec;
102 : :
302 alvherre@kurilemu.de 103 [ + + ]:GNC 114 : appendStringInfo(buf, "redo %X/%08X; "
104 : : "tli %u; prev tli %u; fpw %s; wal_level %s; logical decoding %s; xid %u:%u; oid %u; multi %u; offset %" PRIu64 "; "
105 : : "oldest xid %u in DB %u; oldest multi %u in DB %u; "
106 : : "oldest/newest commit timestamp xid: %u/%u; "
107 : : "oldest running xid %u; "
108 : : "checksums %s; %s",
1897 peter@eisentraut.org 109 :CBC 38 : LSN_FORMAT_ARGS(checkpoint->redo),
110 : : checkpoint->ThisTimeLineID,
111 : : checkpoint->PrevTimeLineID,
4906 alvherre@alvh.no-ip. 112 [ + + ]: 38 : checkpoint->fullPageWrites ? "true" : "false",
113 : : get_wal_level_string(checkpoint->wal_level),
133 msawada@postgresql.o 114 :GNC 38 : checkpoint->logicalDecodingEnabled ? "true" : "false",
2093 andres@anarazel.de 115 :CBC 38 : EpochFromFullTransactionId(checkpoint->nextXid),
116 [ + + ]: 38 : XidFromFullTransactionId(checkpoint->nextXid),
117 : : checkpoint->nextOid,
118 : : checkpoint->nextMulti,
119 : : checkpoint->nextMultiOffset,
120 : : checkpoint->oldestXid,
121 : : checkpoint->oldestXidDB,
122 : : checkpoint->oldestMulti,
123 : : checkpoint->oldestMultiDB,
124 : : checkpoint->oldestCommitTsXid,
125 : : checkpoint->newestCommitTsXid,
126 : : checkpoint->oldestActiveXid,
127 : : get_checksum_state_string(checkpoint->dataChecksumState),
128 : : (info == XLOG_CHECKPOINT_SHUTDOWN) ? "shutdown" : "online");
129 : : }
4906 alvherre@alvh.no-ip. 130 [ + + ]: 18790 : else if (info == XLOG_NEXTOID)
131 : : {
132 : : Oid nextOid;
133 : :
134 : 21 : memcpy(&nextOid, rec, sizeof(Oid));
4246 andres@anarazel.de 135 : 21 : appendStringInfo(buf, "%u", nextOid);
136 : : }
4906 alvherre@alvh.no-ip. 137 [ - + ]: 18769 : else if (info == XLOG_RESTORE_POINT)
138 : : {
4906 alvherre@alvh.no-ip. 139 :UBC 0 : xl_restore_point *xlrec = (xl_restore_point *) rec;
140 : :
4012 peter_e@gmx.net 141 : 0 : appendStringInfoString(buf, xlrec->rp_name);
142 : : }
4180 heikki.linnakangas@i 143 [ + + + + ]:CBC 18769 : else if (info == XLOG_FPI || info == XLOG_FPI_FOR_HINT)
144 : : {
145 : : /* no further information to print */
146 : : }
4906 alvherre@alvh.no-ip. 147 [ + + ]: 78 : else if (info == XLOG_BACKUP_END)
148 : : {
149 : : XLogRecPtr startpoint;
150 : :
151 : 10 : memcpy(&startpoint, rec, sizeof(XLogRecPtr));
302 alvherre@kurilemu.de 152 :GNC 10 : appendStringInfo(buf, "%X/%08X", LSN_FORMAT_ARGS(startpoint));
153 : : }
4906 alvherre@alvh.no-ip. 154 [ + + ]:CBC 68 : else if (info == XLOG_PARAMETER_CHANGE)
155 : : {
156 : : xl_parameter_change xlrec;
157 : : const char *wal_level_str;
158 : :
159 : 5 : memcpy(&xlrec, rec, sizeof(xl_parameter_change));
656 rhaas@postgresql.org 160 : 5 : wal_level_str = get_wal_level_string(xlrec.wal_level);
161 : :
4169 heikki.linnakangas@i 162 : 10 : appendStringInfo(buf, "max_connections=%d max_worker_processes=%d "
163 : : "max_wal_senders=%d max_prepared_xacts=%d "
164 : : "max_locks_per_xact=%d wal_level=%s "
165 : : "wal_log_hints=%s track_commit_timestamp=%s",
166 : : xlrec.MaxConnections,
167 : : xlrec.max_worker_processes,
168 : : xlrec.max_wal_senders,
169 : : xlrec.max_prepared_xacts,
170 : : xlrec.max_locks_per_xact,
171 : : wal_level_str,
172 [ + + ]: 5 : xlrec.wal_log_hints ? "on" : "off",
173 [ - + ]: 5 : xlrec.track_commit_timestamp ? "on" : "off");
174 : : }
4906 alvherre@alvh.no-ip. 175 [ - + ]: 63 : else if (info == XLOG_FPW_CHANGE)
176 : : {
177 : : bool fpw;
178 : :
4906 alvherre@alvh.no-ip. 179 :UBC 0 : memcpy(&fpw, rec, sizeof(bool));
4012 peter_e@gmx.net 180 [ # # ]: 0 : appendStringInfoString(buf, fpw ? "true" : "false");
181 : : }
4844 simon@2ndQuadrant.co 182 [ - + ]:CBC 63 : else if (info == XLOG_END_OF_RECOVERY)
183 : : {
184 : : xl_end_of_recovery xlrec;
185 : :
4844 simon@2ndQuadrant.co 186 :UBC 0 : memcpy(&xlrec, rec, sizeof(xl_end_of_recovery));
656 rhaas@postgresql.org 187 : 0 : appendStringInfo(buf, "tli %u; prev tli %u; time %s; wal_level %s",
188 : : xlrec.ThisTimeLineID, xlrec.PrevTimeLineID,
189 : : timestamptz_to_str(xlrec.end_time),
190 : : get_wal_level_string(xlrec.wal_level));
191 : : }
1679 alvherre@alvh.no-ip. 192 [ + + ]:CBC 63 : else if (info == XLOG_OVERWRITE_CONTRECORD)
193 : : {
194 : : xl_overwrite_contrecord xlrec;
195 : :
196 : 1 : memcpy(&xlrec, rec, sizeof(xl_overwrite_contrecord));
302 alvherre@kurilemu.de 197 :GNC 1 : appendStringInfo(buf, "lsn %X/%08X; time %s",
1679 alvherre@alvh.no-ip. 198 :CBC 1 : LSN_FORMAT_ARGS(xlrec.overwritten_lsn),
199 : : timestamptz_to_str(xlrec.overwrite_time));
200 : : }
929 rhaas@postgresql.org 201 [ + + ]: 62 : else if (info == XLOG_CHECKPOINT_REDO)
202 : : {
203 : : xl_checkpoint_redo xlrec;
204 : :
35 dgustafsson@postgres 205 :GNC 23 : memcpy(&xlrec, rec, sizeof(xl_checkpoint_redo));
32 206 : 23 : appendStringInfo(buf, "wal_level %s; checksums %s",
207 : : get_wal_level_string(xlrec.wal_level),
208 : : get_checksum_state_string(xlrec.data_checksum_version));
209 : : }
133 msawada@postgresql.o 210 [ + + ]: 39 : else if (info == XLOG_LOGICAL_DECODING_STATUS_CHANGE)
211 : : {
212 : : bool enabled;
213 : :
214 : 34 : memcpy(&enabled, rec, sizeof(bool));
215 [ + + ]: 34 : appendStringInfoString(buf, enabled ? "true" : "false");
216 : : }
217 : : else if (info == XLOG_ASSIGN_LSN)
218 : : {
219 : : /* no further information to print */
220 : : }
4246 andres@anarazel.de 221 :CBC 18828 : }
222 : :
223 : : const char *
224 : 18843 : xlog_identify(uint8 info)
225 : : {
226 : 18843 : const char *id = NULL;
227 : :
4243 228 [ + + - + : 18843 : switch (info & ~XLR_INFO_MASK)
+ + + - -
- + + + +
+ - - ]
229 : : {
4246 230 : 16 : case XLOG_CHECKPOINT_SHUTDOWN:
231 : 16 : id = "CHECKPOINT_SHUTDOWN";
232 : 16 : break;
233 : 25 : case XLOG_CHECKPOINT_ONLINE:
234 : 25 : id = "CHECKPOINT_ONLINE";
235 : 25 : break;
4246 andres@anarazel.de 236 :UBC 0 : case XLOG_NOOP:
237 : 0 : id = "NOOP";
238 : 0 : break;
4246 andres@anarazel.de 239 :CBC 24 : case XLOG_NEXTOID:
240 : 24 : id = "NEXTOID";
241 : 24 : break;
242 : 5 : case XLOG_SWITCH:
243 : 5 : id = "SWITCH";
244 : 5 : break;
245 : 10 : case XLOG_BACKUP_END:
246 : 10 : id = "BACKUP_END";
247 : 10 : break;
248 : 5 : case XLOG_PARAMETER_CHANGE:
249 : 5 : id = "PARAMETER_CHANGE";
250 : 5 : break;
4246 andres@anarazel.de 251 :UBC 0 : case XLOG_RESTORE_POINT:
252 : 0 : id = "RESTORE_POINT";
253 : 0 : break;
254 : 0 : case XLOG_FPW_CHANGE:
255 : 0 : id = "FPW_CHANGE";
256 : 0 : break;
257 : 0 : case XLOG_END_OF_RECOVERY:
258 : 0 : id = "END_OF_RECOVERY";
259 : 0 : break;
1679 alvherre@alvh.no-ip. 260 :CBC 1 : case XLOG_OVERWRITE_CONTRECORD:
261 : 1 : id = "OVERWRITE_CONTRECORD";
262 : 1 : break;
4246 andres@anarazel.de 263 : 17507 : case XLOG_FPI:
264 : 17507 : id = "FPI";
265 : 17507 : break;
4180 heikki.linnakangas@i 266 : 1190 : case XLOG_FPI_FOR_HINT:
267 : 1190 : id = "FPI_FOR_HINT";
268 : 1190 : break;
929 rhaas@postgresql.org 269 : 26 : case XLOG_CHECKPOINT_REDO:
270 : 26 : id = "CHECKPOINT_REDO";
271 : 26 : break;
133 msawada@postgresql.o 272 :GNC 34 : case XLOG_LOGICAL_DECODING_STATUS_CHANGE:
273 : 34 : id = "LOGICAL_DECODING_STATUS_CHANGE";
274 : 34 : break;
53 pg@bowt.ie 275 :UNC 0 : case XLOG_ASSIGN_LSN:
276 : 0 : id = "ASSIGN_LSN";
277 : 0 : break;
278 : : }
279 : :
4246 andres@anarazel.de 280 :GNC 18843 : return id;
281 : : }
282 : :
283 : : const char *
32 dgustafsson@postgres 284 :UNC 0 : xlog2_identify(uint8 info)
285 : : {
286 : 0 : const char *id = NULL;
287 : :
288 [ # # ]: 0 : switch (info & ~XLR_INFO_MASK)
289 : : {
290 : 0 : case XLOG2_CHECKSUMS:
291 : 0 : id = "CHECKSUMS";
292 : 0 : break;
293 : : }
294 : :
32 dgustafsson@postgres 295 :LBC (7944) : return id;
296 : : }
297 : :
298 : : /*
299 : : * Returns a string giving information about all the blocks in an
300 : : * XLogRecord.
301 : : */
302 : : void
1488 jdavis@postgresql.or 303 :CBC 603085 : XLogRecGetBlockRefInfo(XLogReaderState *record, bool pretty,
304 : : bool detailed_format, StringInfo buf,
305 : : uint32 *fpi_len)
306 : : {
307 : : int block_id;
308 : :
309 [ - + ]: 603085 : Assert(record != NULL);
310 : :
311 [ + + - + ]: 603085 : if (detailed_format && pretty)
1488 jdavis@postgresql.or 312 :UBC 0 : appendStringInfoChar(buf, '\n');
313 : :
1488 jdavis@postgresql.or 314 [ + + ]:CBC 1280500 : for (block_id = 0; block_id <= XLogRecMaxBlockId(record); block_id++)
315 : : {
316 : : RelFileLocator rlocator;
317 : : ForkNumber forknum;
318 : : BlockNumber blk;
319 : :
1485 tgl@sss.pgh.pa.us 320 [ + + ]: 677415 : if (!XLogRecGetBlockTagExtended(record, block_id,
321 : : &rlocator, &forknum, &blk, NULL))
1488 jdavis@postgresql.or 322 : 154 : continue;
323 : :
324 [ + + ]: 677261 : if (detailed_format)
325 : : {
326 : : /* Get block references in detailed format. */
327 : :
328 [ - + ]: 36320 : if (pretty)
1488 jdavis@postgresql.or 329 :UBC 0 : appendStringInfoChar(buf, '\t');
1488 jdavis@postgresql.or 330 [ + + ]:CBC 36320 : else if (block_id > 0)
331 : 2007 : appendStringInfoChar(buf, ' ');
332 : :
333 : 36320 : appendStringInfo(buf,
334 : : "blkref #%d: rel %u/%u/%u fork %s blk %u",
335 : : block_id,
336 : : rlocator.spcOid, rlocator.dbOid, rlocator.relNumber,
337 : 36320 : forkNames[forknum],
338 : : blk);
339 : :
340 [ + + ]: 36320 : if (XLogRecHasBlockImage(record, block_id))
341 : : {
342 : 5262 : uint8 bimg_info = XLogRecGetBlock(record, block_id)->bimg_info;
343 : :
344 : : /* Calculate the amount of FPI data in the record. */
345 [ + - ]: 5262 : if (fpi_len)
346 : 5262 : *fpi_len += XLogRecGetBlock(record, block_id)->bimg_len;
347 : :
348 [ - + ]: 5262 : if (BKPIMAGE_COMPRESSED(bimg_info))
349 : : {
350 : : const char *method;
351 : :
1488 jdavis@postgresql.or 352 [ # # ]:UBC 0 : if ((bimg_info & BKPIMAGE_COMPRESS_PGLZ) != 0)
353 : 0 : method = "pglz";
354 [ # # ]: 0 : else if ((bimg_info & BKPIMAGE_COMPRESS_LZ4) != 0)
355 : 0 : method = "lz4";
356 [ # # ]: 0 : else if ((bimg_info & BKPIMAGE_COMPRESS_ZSTD) != 0)
357 : 0 : method = "zstd";
358 : : else
359 : 0 : method = "unknown";
360 : :
361 : 0 : appendStringInfo(buf,
362 : : " (FPW%s); hole: offset: %u, length: %u, "
363 : : "compression saved: %u, method: %s",
364 : 0 : XLogRecBlockImageApply(record, block_id) ?
365 : : "" : " for WAL verification",
366 : 0 : XLogRecGetBlock(record, block_id)->hole_offset,
367 : 0 : XLogRecGetBlock(record, block_id)->hole_length,
368 : : BLCKSZ -
369 : 0 : XLogRecGetBlock(record, block_id)->hole_length -
370 [ # # ]: 0 : XLogRecGetBlock(record, block_id)->bimg_len,
371 : : method);
372 : : }
373 : : else
374 : : {
1488 jdavis@postgresql.or 375 :CBC 5262 : appendStringInfo(buf,
376 : : " (FPW%s); hole: offset: %u, length: %u",
377 : 5262 : XLogRecBlockImageApply(record, block_id) ?
378 : : "" : " for WAL verification",
379 : 5262 : XLogRecGetBlock(record, block_id)->hole_offset,
380 [ + + ]: 5262 : XLogRecGetBlock(record, block_id)->hole_length);
381 : : }
382 : : }
383 : :
384 [ - + ]: 36320 : if (pretty)
1488 jdavis@postgresql.or 385 :UBC 0 : appendStringInfoChar(buf, '\n');
386 : : }
387 : : else
388 : : {
389 : : /* Get block references in short format. */
390 : :
1488 jdavis@postgresql.or 391 [ + + ]:CBC 640941 : if (forknum != MAIN_FORKNUM)
392 : : {
393 : 6269 : appendStringInfo(buf,
394 : : ", blkref #%d: rel %u/%u/%u fork %s blk %u",
395 : : block_id,
396 : : rlocator.spcOid, rlocator.dbOid, rlocator.relNumber,
397 : 6269 : forkNames[forknum],
398 : : blk);
399 : : }
400 : : else
401 : : {
402 : 634672 : appendStringInfo(buf,
403 : : ", blkref #%d: rel %u/%u/%u blk %u",
404 : : block_id,
405 : : rlocator.spcOid, rlocator.dbOid, rlocator.relNumber,
406 : : blk);
407 : : }
408 : :
409 [ + + ]: 640941 : if (XLogRecHasBlockImage(record, block_id))
410 : : {
411 : : /* Calculate the amount of FPI data in the record. */
412 [ - + ]: 20980 : if (fpi_len)
1488 jdavis@postgresql.or 413 :UBC 0 : *fpi_len += XLogRecGetBlock(record, block_id)->bimg_len;
414 : :
1488 jdavis@postgresql.or 415 [ + - ]:CBC 20980 : if (XLogRecBlockImageApply(record, block_id))
1337 drowley@postgresql.o 416 : 20980 : appendStringInfoString(buf, " FPW");
417 : : else
1337 drowley@postgresql.o 418 :UBC 0 : appendStringInfoString(buf, " FPW for WAL verification");
419 : : }
420 : : }
421 : : }
422 : :
1488 jdavis@postgresql.or 423 [ + + + - ]:CBC 603085 : if (!detailed_format && pretty)
424 : 568772 : appendStringInfoChar(buf, '\n');
425 : 603085 : }
|