Line data Source code
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 74 : get_wal_level_string(int wal_level)
41 : {
42 : const struct config_enum_entry *entry;
43 74 : const char *wal_level_str = "?";
44 :
45 238 : for (entry = wal_level_options; entry->name; entry++)
46 : {
47 238 : if (entry->val == wal_level)
48 : {
49 74 : wal_level_str = entry->name;
50 74 : break;
51 : }
52 : }
53 :
54 74 : return wal_level_str;
55 : }
56 :
57 : void
58 15504 : xlog_desc(StringInfo buf, XLogReaderState *record)
59 : {
60 15504 : char *rec = XLogRecGetData(record);
61 15504 : uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
62 :
63 15504 : if (info == XLOG_CHECKPOINT_SHUTDOWN ||
64 : info == XLOG_CHECKPOINT_ONLINE)
65 42 : {
66 42 : CheckPoint *checkpoint = (CheckPoint *) rec;
67 :
68 126 : 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 42 : LSN_FORMAT_ARGS(checkpoint->redo),
74 : checkpoint->ThisTimeLineID,
75 : checkpoint->PrevTimeLineID,
76 42 : checkpoint->fullPageWrites ? "true" : "false",
77 : get_wal_level_string(checkpoint->wal_level),
78 42 : checkpoint->logicalDecodingEnabled ? "true" : "false",
79 42 : EpochFromFullTransactionId(checkpoint->nextXid),
80 42 : 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 15462 : else if (info == XLOG_NEXTOID)
94 : {
95 : Oid nextOid;
96 :
97 24 : memcpy(&nextOid, rec, sizeof(Oid));
98 24 : appendStringInfo(buf, "%u", nextOid);
99 : }
100 15438 : 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 15438 : else if (info == XLOG_FPI || info == XLOG_FPI_FOR_HINT)
107 : {
108 : /* no further information to print */
109 : }
110 44 : 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 44 : else if (info == XLOG_PARAMETER_CHANGE)
118 : {
119 : xl_parameter_change xlrec;
120 : const char *wal_level_str;
121 :
122 4 : memcpy(&xlrec, rec, sizeof(xl_parameter_change));
123 4 : wal_level_str = get_wal_level_string(xlrec.wal_level);
124 :
125 8 : 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 4 : xlrec.wal_log_hints ? "on" : "off",
136 4 : xlrec.track_commit_timestamp ? "on" : "off");
137 : }
138 40 : 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 40 : 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 40 : 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 40 : else if (info == XLOG_CHECKPOINT_REDO)
165 : {
166 : int wal_level;
167 :
168 28 : memcpy(&wal_level, rec, sizeof(int));
169 28 : appendStringInfo(buf, "wal_level %s", get_wal_level_string(wal_level));
170 : }
171 12 : else if (info == XLOG_LOGICAL_DECODING_STATUS_CHANGE)
172 : {
173 : bool enabled;
174 :
175 4 : memcpy(&enabled, rec, sizeof(bool));
176 4 : appendStringInfoString(buf, enabled ? "true" : "false");
177 : }
178 15504 : }
179 :
180 : const char *
181 15514 : xlog_identify(uint8 info)
182 : {
183 15514 : const char *id = NULL;
184 :
185 15514 : switch (info & ~XLR_INFO_MASK)
186 : {
187 14 : case XLOG_CHECKPOINT_SHUTDOWN:
188 14 : id = "CHECKPOINT_SHUTDOWN";
189 14 : break;
190 30 : case XLOG_CHECKPOINT_ONLINE:
191 30 : id = "CHECKPOINT_ONLINE";
192 30 : break;
193 0 : case XLOG_NOOP:
194 0 : id = "NOOP";
195 0 : break;
196 26 : case XLOG_NEXTOID:
197 26 : id = "NEXTOID";
198 26 : break;
199 8 : case XLOG_SWITCH:
200 8 : id = "SWITCH";
201 8 : break;
202 0 : case XLOG_BACKUP_END:
203 0 : id = "BACKUP_END";
204 0 : break;
205 4 : case XLOG_PARAMETER_CHANGE:
206 4 : id = "PARAMETER_CHANGE";
207 4 : break;
208 0 : case XLOG_RESTORE_POINT:
209 0 : id = "RESTORE_POINT";
210 0 : break;
211 0 : case XLOG_FPW_CHANGE:
212 0 : id = "FPW_CHANGE";
213 0 : break;
214 0 : case XLOG_END_OF_RECOVERY:
215 0 : id = "END_OF_RECOVERY";
216 0 : break;
217 0 : case XLOG_OVERWRITE_CONTRECORD:
218 0 : id = "OVERWRITE_CONTRECORD";
219 0 : break;
220 14128 : case XLOG_FPI:
221 14128 : id = "FPI";
222 14128 : break;
223 1270 : case XLOG_FPI_FOR_HINT:
224 1270 : id = "FPI_FOR_HINT";
225 1270 : break;
226 30 : case XLOG_CHECKPOINT_REDO:
227 30 : id = "CHECKPOINT_REDO";
228 30 : break;
229 4 : case XLOG_LOGICAL_DECODING_STATUS_CHANGE:
230 4 : id = "LOGICAL_DECODING_STATUS_CHANGE";
231 4 : break;
232 : }
233 :
234 15514 : return id;
235 : }
236 :
237 : /*
238 : * Returns a string giving information about all the blocks in an
239 : * XLogRecord.
240 : */
241 : void
242 515736 : XLogRecGetBlockRefInfo(XLogReaderState *record, bool pretty,
243 : bool detailed_format, StringInfo buf,
244 : uint32 *fpi_len)
245 : {
246 : int block_id;
247 :
248 : Assert(record != NULL);
249 :
250 515736 : if (detailed_format && pretty)
251 0 : appendStringInfoChar(buf, '\n');
252 :
253 1086976 : for (block_id = 0; block_id <= XLogRecMaxBlockId(record); block_id++)
254 : {
255 : RelFileLocator rlocator;
256 : ForkNumber forknum;
257 : BlockNumber blk;
258 :
259 571240 : if (!XLogRecGetBlockTagExtended(record, block_id,
260 : &rlocator, &forknum, &blk, NULL))
261 178 : continue;
262 :
263 571062 : 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 : block_id,
275 : rlocator.spcOid, rlocator.dbOid, rlocator.relNumber,
276 0 : forkNames[forknum],
277 : 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 : 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 : BLCKSZ -
308 0 : XLogRecGetBlock(record, block_id)->hole_length -
309 0 : XLogRecGetBlock(record, block_id)->bimg_len,
310 : method);
311 : }
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 : }
322 :
323 0 : if (pretty)
324 0 : appendStringInfoChar(buf, '\n');
325 : }
326 : else
327 : {
328 : /* Get block references in short format. */
329 :
330 571062 : if (forknum != MAIN_FORKNUM)
331 : {
332 6452 : appendStringInfo(buf,
333 : ", blkref #%d: rel %u/%u/%u fork %s blk %u",
334 : block_id,
335 : rlocator.spcOid, rlocator.dbOid, rlocator.relNumber,
336 6452 : forkNames[forknum],
337 : blk);
338 : }
339 : else
340 : {
341 564610 : appendStringInfo(buf,
342 : ", blkref #%d: rel %u/%u/%u blk %u",
343 : block_id,
344 : rlocator.spcOid, rlocator.dbOid, rlocator.relNumber,
345 : blk);
346 : }
347 :
348 571062 : if (XLogRecHasBlockImage(record, block_id))
349 : {
350 : /* Calculate the amount of FPI data in the record. */
351 18408 : if (fpi_len)
352 0 : *fpi_len += XLogRecGetBlock(record, block_id)->bimg_len;
353 :
354 18408 : if (XLogRecBlockImageApply(record, block_id))
355 18408 : appendStringInfoString(buf, " FPW");
356 : else
357 0 : appendStringInfoString(buf, " FPW for WAL verification");
358 : }
359 : }
360 : }
361 :
362 515736 : if (!detailed_format && pretty)
363 515736 : appendStringInfoChar(buf, '\n');
364 515736 : }
|