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