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