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 *
41 55 : get_wal_level_string(int wal_level)
42 : {
43 : const struct config_enum_entry *entry;
44 55 : const char *wal_level_str = "?";
45 :
46 209 : for (entry = wal_level_options; entry->name; entry++)
47 : {
48 209 : if (entry->val == wal_level)
49 : {
50 55 : wal_level_str = entry->name;
51 55 : break;
52 : }
53 : }
54 :
55 55 : return wal_level_str;
56 : }
57 :
58 : const char *
59 53 : get_checksum_state_string(uint32 state)
60 : {
61 53 : switch (state)
62 : {
63 53 : case PG_DATA_CHECKSUM_VERSION:
64 53 : return "on";
65 0 : case PG_DATA_CHECKSUM_INPROGRESS_OFF:
66 0 : return "inprogress-off";
67 0 : case PG_DATA_CHECKSUM_INPROGRESS_ON:
68 0 : return "inprogress-on";
69 0 : case PG_DATA_CHECKSUM_OFF:
70 0 : return "off";
71 : }
72 :
73 : Assert(false);
74 0 : 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
93 18385 : xlog_desc(StringInfo buf, XLogReaderState *record)
94 : {
95 18385 : char *rec = XLogRecGetData(record);
96 18385 : uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
97 :
98 18385 : if (info == XLOG_CHECKPOINT_SHUTDOWN ||
99 : info == XLOG_CHECKPOINT_ONLINE)
100 31 : {
101 31 : CheckPoint *checkpoint = (CheckPoint *) rec;
102 :
103 93 : 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",
109 31 : LSN_FORMAT_ARGS(checkpoint->redo),
110 : checkpoint->ThisTimeLineID,
111 : checkpoint->PrevTimeLineID,
112 31 : checkpoint->fullPageWrites ? "true" : "false",
113 : get_wal_level_string(checkpoint->wal_level),
114 31 : checkpoint->logicalDecodingEnabled ? "true" : "false",
115 31 : EpochFromFullTransactionId(checkpoint->nextXid),
116 31 : 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 : }
130 18354 : else if (info == XLOG_NEXTOID)
131 : {
132 : Oid nextOid;
133 :
134 20 : memcpy(&nextOid, rec, sizeof(Oid));
135 20 : appendStringInfo(buf, "%u", nextOid);
136 : }
137 18334 : else if (info == XLOG_RESTORE_POINT)
138 : {
139 0 : xl_restore_point *xlrec = (xl_restore_point *) rec;
140 :
141 0 : appendStringInfoString(buf, xlrec->rp_name);
142 : }
143 18334 : else if (info == XLOG_FPI || info == XLOG_FPI_FOR_HINT)
144 : {
145 : /* no further information to print */
146 : }
147 30 : else if (info == XLOG_BACKUP_END)
148 : {
149 : XLogRecPtr startpoint;
150 :
151 0 : memcpy(&startpoint, rec, sizeof(XLogRecPtr));
152 0 : appendStringInfo(buf, "%X/%08X", LSN_FORMAT_ARGS(startpoint));
153 : }
154 30 : else if (info == XLOG_PARAMETER_CHANGE)
155 : {
156 : xl_parameter_change xlrec;
157 : const char *wal_level_str;
158 :
159 2 : memcpy(&xlrec, rec, sizeof(xl_parameter_change));
160 2 : wal_level_str = get_wal_level_string(xlrec.wal_level);
161 :
162 4 : 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 2 : xlrec.wal_log_hints ? "on" : "off",
173 2 : xlrec.track_commit_timestamp ? "on" : "off");
174 : }
175 28 : else if (info == XLOG_FPW_CHANGE)
176 : {
177 : bool fpw;
178 :
179 0 : memcpy(&fpw, rec, sizeof(bool));
180 0 : appendStringInfoString(buf, fpw ? "true" : "false");
181 : }
182 28 : else if (info == XLOG_END_OF_RECOVERY)
183 : {
184 : xl_end_of_recovery xlrec;
185 :
186 0 : memcpy(&xlrec, rec, sizeof(xl_end_of_recovery));
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 : }
192 28 : else if (info == XLOG_OVERWRITE_CONTRECORD)
193 : {
194 : xl_overwrite_contrecord xlrec;
195 :
196 0 : memcpy(&xlrec, rec, sizeof(xl_overwrite_contrecord));
197 0 : appendStringInfo(buf, "lsn %X/%08X; time %s",
198 0 : LSN_FORMAT_ARGS(xlrec.overwritten_lsn),
199 : timestamptz_to_str(xlrec.overwrite_time));
200 : }
201 28 : else if (info == XLOG_CHECKPOINT_REDO)
202 : {
203 : xl_checkpoint_redo xlrec;
204 :
205 22 : memcpy(&xlrec, rec, sizeof(xl_checkpoint_redo));
206 22 : 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 : }
210 6 : else if (info == XLOG_LOGICAL_DECODING_STATUS_CHANGE)
211 : {
212 : bool enabled;
213 :
214 2 : memcpy(&enabled, rec, sizeof(bool));
215 2 : appendStringInfoString(buf, enabled ? "true" : "false");
216 : }
217 : else if (info == XLOG_ASSIGN_LSN)
218 : {
219 : /* no further information to print */
220 : }
221 18385 : }
222 :
223 : const char *
224 18400 : xlog_identify(uint8 info)
225 : {
226 18400 : const char *id = NULL;
227 :
228 18400 : switch (info & ~XLR_INFO_MASK)
229 : {
230 9 : case XLOG_CHECKPOINT_SHUTDOWN:
231 9 : id = "CHECKPOINT_SHUTDOWN";
232 9 : break;
233 25 : case XLOG_CHECKPOINT_ONLINE:
234 25 : id = "CHECKPOINT_ONLINE";
235 25 : break;
236 0 : case XLOG_NOOP:
237 0 : id = "NOOP";
238 0 : break;
239 23 : case XLOG_NEXTOID:
240 23 : id = "NEXTOID";
241 23 : break;
242 4 : case XLOG_SWITCH:
243 4 : id = "SWITCH";
244 4 : break;
245 0 : case XLOG_BACKUP_END:
246 0 : id = "BACKUP_END";
247 0 : break;
248 2 : case XLOG_PARAMETER_CHANGE:
249 2 : id = "PARAMETER_CHANGE";
250 2 : break;
251 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;
260 0 : case XLOG_OVERWRITE_CONTRECORD:
261 0 : id = "OVERWRITE_CONTRECORD";
262 0 : break;
263 17139 : case XLOG_FPI:
264 17139 : id = "FPI";
265 17139 : break;
266 1171 : case XLOG_FPI_FOR_HINT:
267 1171 : id = "FPI_FOR_HINT";
268 1171 : break;
269 25 : case XLOG_CHECKPOINT_REDO:
270 25 : id = "CHECKPOINT_REDO";
271 25 : break;
272 2 : case XLOG_LOGICAL_DECODING_STATUS_CHANGE:
273 2 : id = "LOGICAL_DECODING_STATUS_CHANGE";
274 2 : break;
275 0 : case XLOG_ASSIGN_LSN:
276 0 : id = "ASSIGN_LSN";
277 0 : break;
278 : }
279 :
280 18400 : return id;
281 : }
282 :
283 : const char *
284 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 :
295 0 : return id;
296 : }
297 :
298 : /*
299 : * Returns a string giving information about all the blocks in an
300 : * XLogRecord.
301 : */
302 : void
303 586893 : XLogRecGetBlockRefInfo(XLogReaderState *record, bool pretty,
304 : bool detailed_format, StringInfo buf,
305 : uint32 *fpi_len)
306 : {
307 : int block_id;
308 :
309 : Assert(record != NULL);
310 :
311 586893 : if (detailed_format && pretty)
312 0 : appendStringInfoChar(buf, '\n');
313 :
314 1252906 : for (block_id = 0; block_id <= XLogRecMaxBlockId(record); block_id++)
315 : {
316 : RelFileLocator rlocator;
317 : ForkNumber forknum;
318 : BlockNumber blk;
319 :
320 666013 : if (!XLogRecGetBlockTagExtended(record, block_id,
321 : &rlocator, &forknum, &blk, NULL))
322 151 : continue;
323 :
324 665862 : if (detailed_format)
325 : {
326 : /* Get block references in detailed format. */
327 :
328 0 : if (pretty)
329 0 : appendStringInfoChar(buf, '\t');
330 0 : else if (block_id > 0)
331 0 : appendStringInfoChar(buf, ' ');
332 :
333 0 : appendStringInfo(buf,
334 : "blkref #%d: rel %u/%u/%u fork %s blk %u",
335 : block_id,
336 : rlocator.spcOid, rlocator.dbOid, rlocator.relNumber,
337 0 : forkNames[forknum],
338 : blk);
339 :
340 0 : if (XLogRecHasBlockImage(record, block_id))
341 : {
342 0 : uint8 bimg_info = XLogRecGetBlock(record, block_id)->bimg_info;
343 :
344 : /* Calculate the amount of FPI data in the record. */
345 0 : if (fpi_len)
346 0 : *fpi_len += XLogRecGetBlock(record, block_id)->bimg_len;
347 :
348 0 : if (BKPIMAGE_COMPRESSED(bimg_info))
349 : {
350 : const char *method;
351 :
352 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 : {
375 0 : appendStringInfo(buf,
376 : " (FPW%s); hole: offset: %u, length: %u",
377 0 : XLogRecBlockImageApply(record, block_id) ?
378 : "" : " for WAL verification",
379 0 : XLogRecGetBlock(record, block_id)->hole_offset,
380 0 : XLogRecGetBlock(record, block_id)->hole_length);
381 : }
382 : }
383 :
384 0 : if (pretty)
385 0 : appendStringInfoChar(buf, '\n');
386 : }
387 : else
388 : {
389 : /* Get block references in short format. */
390 :
391 665862 : if (forknum != MAIN_FORKNUM)
392 : {
393 6223 : appendStringInfo(buf,
394 : ", blkref #%d: rel %u/%u/%u fork %s blk %u",
395 : block_id,
396 : rlocator.spcOid, rlocator.dbOid, rlocator.relNumber,
397 6223 : forkNames[forknum],
398 : blk);
399 : }
400 : else
401 : {
402 659639 : 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 665862 : if (XLogRecHasBlockImage(record, block_id))
410 : {
411 : /* Calculate the amount of FPI data in the record. */
412 20640 : if (fpi_len)
413 0 : *fpi_len += XLogRecGetBlock(record, block_id)->bimg_len;
414 :
415 20640 : if (XLogRecBlockImageApply(record, block_id))
416 20640 : appendStringInfoString(buf, " FPW");
417 : else
418 0 : appendStringInfoString(buf, " FPW for WAL verification");
419 : }
420 : }
421 : }
422 :
423 586893 : if (!detailed_format && pretty)
424 586893 : appendStringInfoChar(buf, '\n');
425 586893 : }
|