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 "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 *
605 rhaas@postgresql.org 40 :CBC 48 : get_wal_level_string(int wal_level)
41 : : {
42 : : const struct config_enum_entry *entry;
43 : 48 : const char *wal_level_str = "?";
44 : :
45 [ + - ]: 148 : for (entry = wal_level_options; entry->name; entry++)
46 : : {
47 [ + + ]: 148 : if (entry->val == wal_level)
48 : : {
49 : 48 : wal_level_str = entry->name;
50 : 48 : break;
51 : : }
52 : : }
53 : :
54 : 48 : return wal_level_str;
55 : : }
56 : :
57 : : void
4133 heikki.linnakangas@i 58 : 7935 : xlog_desc(StringInfo buf, XLogReaderState *record)
59 : : {
4292 60 : 7935 : char *rec = XLogRecGetData(record);
4133 61 : 7935 : uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
62 : :
4855 alvherre@alvh.no-ip. 63 [ + + + + ]: 7935 : if (info == XLOG_CHECKPOINT_SHUTDOWN ||
64 : : info == XLOG_CHECKPOINT_ONLINE)
65 : 28 : {
66 : 28 : CheckPoint *checkpoint = (CheckPoint *) rec;
67 : :
251 alvherre@kurilemu.de 68 [ + + ]:GNC 84 : 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",
1846 peter@eisentraut.org 73 :CBC 28 : LSN_FORMAT_ARGS(checkpoint->redo),
74 : : checkpoint->ThisTimeLineID,
75 : : checkpoint->PrevTimeLineID,
4855 alvherre@alvh.no-ip. 76 [ + + ]: 28 : checkpoint->fullPageWrites ? "true" : "false",
77 : : get_wal_level_string(checkpoint->wal_level),
82 msawada@postgresql.o 78 :GNC 28 : checkpoint->logicalDecodingEnabled ? "true" : "false",
2042 andres@anarazel.de 79 :CBC 28 : EpochFromFullTransactionId(checkpoint->nextXid),
80 [ + + ]: 28 : 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 : : }
4855 alvherre@alvh.no-ip. 93 [ + + ]: 7907 : else if (info == XLOG_NEXTOID)
94 : : {
95 : : Oid nextOid;
96 : :
97 : 13 : memcpy(&nextOid, rec, sizeof(Oid));
4195 andres@anarazel.de 98 : 13 : appendStringInfo(buf, "%u", nextOid);
99 : : }
4855 alvherre@alvh.no-ip. 100 [ - + ]: 7894 : else if (info == XLOG_RESTORE_POINT)
101 : : {
4855 alvherre@alvh.no-ip. 102 :UBC 0 : xl_restore_point *xlrec = (xl_restore_point *) rec;
103 : :
3961 peter_e@gmx.net 104 : 0 : appendStringInfoString(buf, xlrec->rp_name);
105 : : }
4129 heikki.linnakangas@i 106 [ + + + + ]:CBC 7894 : else if (info == XLOG_FPI || info == XLOG_FPI_FOR_HINT)
107 : : {
108 : : /* no further information to print */
109 : : }
4855 alvherre@alvh.no-ip. 110 [ + + ]: 70 : else if (info == XLOG_BACKUP_END)
111 : : {
112 : : XLogRecPtr startpoint;
113 : :
114 : 10 : memcpy(&startpoint, rec, sizeof(XLogRecPtr));
251 alvherre@kurilemu.de 115 :GNC 10 : appendStringInfo(buf, "%X/%08X", LSN_FORMAT_ARGS(startpoint));
116 : : }
4855 alvherre@alvh.no-ip. 117 [ + + ]:CBC 60 : else if (info == XLOG_PARAMETER_CHANGE)
118 : : {
119 : : xl_parameter_change xlrec;
120 : : const char *wal_level_str;
121 : :
122 : 5 : memcpy(&xlrec, rec, sizeof(xl_parameter_change));
605 rhaas@postgresql.org 123 : 5 : wal_level_str = get_wal_level_string(xlrec.wal_level);
124 : :
4118 heikki.linnakangas@i 125 : 10 : 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 [ + + ]: 5 : xlrec.wal_log_hints ? "on" : "off",
136 [ - + ]: 5 : xlrec.track_commit_timestamp ? "on" : "off");
137 : : }
4855 alvherre@alvh.no-ip. 138 [ - + ]: 55 : else if (info == XLOG_FPW_CHANGE)
139 : : {
140 : : bool fpw;
141 : :
4855 alvherre@alvh.no-ip. 142 :UBC 0 : memcpy(&fpw, rec, sizeof(bool));
3961 peter_e@gmx.net 143 [ # # ]: 0 : appendStringInfoString(buf, fpw ? "true" : "false");
144 : : }
4793 simon@2ndQuadrant.co 145 [ - + ]:CBC 55 : else if (info == XLOG_END_OF_RECOVERY)
146 : : {
147 : : xl_end_of_recovery xlrec;
148 : :
4793 simon@2ndQuadrant.co 149 :UBC 0 : memcpy(&xlrec, rec, sizeof(xl_end_of_recovery));
605 rhaas@postgresql.org 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 : : }
1628 alvherre@alvh.no-ip. 155 [ + + ]:CBC 55 : else if (info == XLOG_OVERWRITE_CONTRECORD)
156 : : {
157 : : xl_overwrite_contrecord xlrec;
158 : :
159 : 1 : memcpy(&xlrec, rec, sizeof(xl_overwrite_contrecord));
251 alvherre@kurilemu.de 160 :GNC 1 : appendStringInfo(buf, "lsn %X/%08X; time %s",
1628 alvherre@alvh.no-ip. 161 :CBC 1 : LSN_FORMAT_ARGS(xlrec.overwritten_lsn),
162 : : timestamptz_to_str(xlrec.overwrite_time));
163 : : }
878 rhaas@postgresql.org 164 [ + + ]: 54 : else if (info == XLOG_CHECKPOINT_REDO)
165 : : {
166 : : int wal_level;
167 : :
605 168 : 15 : memcpy(&wal_level, rec, sizeof(int));
169 : 15 : appendStringInfo(buf, "wal_level %s", get_wal_level_string(wal_level));
170 : : }
82 msawada@postgresql.o 171 [ + + ]:GNC 39 : else if (info == XLOG_LOGICAL_DECODING_STATUS_CHANGE)
172 : : {
173 : : bool enabled;
174 : :
175 : 34 : memcpy(&enabled, rec, sizeof(bool));
176 [ + + ]: 34 : appendStringInfoString(buf, enabled ? "true" : "false");
177 : : }
178 : : else if (info == XLOG_ASSIGN_LSN)
179 : : {
180 : : /* no further information to print */
181 : : }
4195 andres@anarazel.de 182 :CBC 7935 : }
183 : :
184 : : const char *
185 : 7940 : xlog_identify(uint8 info)
186 : : {
187 : 7940 : const char *id = NULL;
188 : :
4192 189 [ + + - + : 7940 : switch (info & ~XLR_INFO_MASK)
+ + + - -
- + + + +
+ - - ]
190 : : {
4195 191 : 14 : case XLOG_CHECKPOINT_SHUTDOWN:
192 : 14 : id = "CHECKPOINT_SHUTDOWN";
193 : 14 : break;
194 : 15 : case XLOG_CHECKPOINT_ONLINE:
195 : 15 : id = "CHECKPOINT_ONLINE";
196 : 15 : break;
4195 andres@anarazel.de 197 :UBC 0 : case XLOG_NOOP:
198 : 0 : id = "NOOP";
199 : 0 : break;
4195 andres@anarazel.de 200 :CBC 14 : case XLOG_NEXTOID:
201 : 14 : id = "NEXTOID";
202 : 14 : break;
203 : 5 : case XLOG_SWITCH:
204 : 5 : id = "SWITCH";
205 : 5 : break;
206 : 10 : case XLOG_BACKUP_END:
207 : 10 : id = "BACKUP_END";
208 : 10 : break;
209 : 5 : case XLOG_PARAMETER_CHANGE:
210 : 5 : id = "PARAMETER_CHANGE";
211 : 5 : break;
4195 andres@anarazel.de 212 :UBC 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;
1628 alvherre@alvh.no-ip. 221 :CBC 1 : case XLOG_OVERWRITE_CONTRECORD:
222 : 1 : id = "OVERWRITE_CONTRECORD";
223 : 1 : break;
4195 andres@anarazel.de 224 : 7174 : case XLOG_FPI:
225 : 7174 : id = "FPI";
226 : 7174 : break;
4129 heikki.linnakangas@i 227 : 652 : case XLOG_FPI_FOR_HINT:
228 : 652 : id = "FPI_FOR_HINT";
229 : 652 : break;
878 rhaas@postgresql.org 230 : 16 : case XLOG_CHECKPOINT_REDO:
231 : 16 : id = "CHECKPOINT_REDO";
232 : 16 : break;
82 msawada@postgresql.o 233 :GNC 34 : case XLOG_LOGICAL_DECODING_STATUS_CHANGE:
234 : 34 : id = "LOGICAL_DECODING_STATUS_CHANGE";
235 : 34 : break;
2 pg@bowt.ie 236 :UNC 0 : case XLOG_ASSIGN_LSN:
237 : 0 : id = "ASSIGN_LSN";
238 : 0 : break;
239 : : }
240 : :
4195 andres@anarazel.de 241 :CBC 7940 : return id;
242 : : }
243 : :
244 : : /*
245 : : * Returns a string giving information about all the blocks in an
246 : : * XLogRecord.
247 : : */
248 : : void
1437 jdavis@postgresql.or 249 : 296118 : XLogRecGetBlockRefInfo(XLogReaderState *record, bool pretty,
250 : : bool detailed_format, StringInfo buf,
251 : : uint32 *fpi_len)
252 : : {
253 : : int block_id;
254 : :
255 [ - + ]: 296118 : Assert(record != NULL);
256 : :
257 [ + + - + ]: 296118 : if (detailed_format && pretty)
1437 jdavis@postgresql.or 258 :UBC 0 : appendStringInfoChar(buf, '\n');
259 : :
1437 jdavis@postgresql.or 260 [ + + ]:CBC 622162 : for (block_id = 0; block_id <= XLogRecMaxBlockId(record); block_id++)
261 : : {
262 : : RelFileLocator rlocator;
263 : : ForkNumber forknum;
264 : : BlockNumber blk;
265 : :
1434 tgl@sss.pgh.pa.us 266 [ + + ]: 326044 : if (!XLogRecGetBlockTagExtended(record, block_id,
267 : : &rlocator, &forknum, &blk, NULL))
1437 jdavis@postgresql.or 268 : 84 : continue;
269 : :
270 [ + + ]: 325960 : if (detailed_format)
271 : : {
272 : : /* Get block references in detailed format. */
273 : :
274 [ - + ]: 36312 : if (pretty)
1437 jdavis@postgresql.or 275 :UBC 0 : appendStringInfoChar(buf, '\t');
1437 jdavis@postgresql.or 276 [ + + ]:CBC 36312 : else if (block_id > 0)
277 : 2008 : appendStringInfoChar(buf, ' ');
278 : :
279 : 36312 : appendStringInfo(buf,
280 : : "blkref #%d: rel %u/%u/%u fork %s blk %u",
281 : : block_id,
282 : : rlocator.spcOid, rlocator.dbOid, rlocator.relNumber,
283 : 36312 : forkNames[forknum],
284 : : blk);
285 : :
286 [ + + ]: 36312 : if (XLogRecHasBlockImage(record, block_id))
287 : : {
288 : 5259 : uint8 bimg_info = XLogRecGetBlock(record, block_id)->bimg_info;
289 : :
290 : : /* Calculate the amount of FPI data in the record. */
291 [ + - ]: 5259 : if (fpi_len)
292 : 5259 : *fpi_len += XLogRecGetBlock(record, block_id)->bimg_len;
293 : :
294 [ - + ]: 5259 : if (BKPIMAGE_COMPRESSED(bimg_info))
295 : : {
296 : : const char *method;
297 : :
1437 jdavis@postgresql.or 298 [ # # ]:UBC 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 : : {
1437 jdavis@postgresql.or 321 :CBC 5259 : appendStringInfo(buf,
322 : : " (FPW%s); hole: offset: %u, length: %u",
323 : 5259 : XLogRecBlockImageApply(record, block_id) ?
324 : : "" : " for WAL verification",
325 : 5259 : XLogRecGetBlock(record, block_id)->hole_offset,
326 [ + + ]: 5259 : XLogRecGetBlock(record, block_id)->hole_length);
327 : : }
328 : : }
329 : :
330 [ - + ]: 36312 : if (pretty)
1437 jdavis@postgresql.or 331 :UBC 0 : appendStringInfoChar(buf, '\n');
332 : : }
333 : : else
334 : : {
335 : : /* Get block references in short format. */
336 : :
1437 jdavis@postgresql.or 337 [ + + ]:CBC 289648 : if (forknum != MAIN_FORKNUM)
338 : : {
339 : 3234 : appendStringInfo(buf,
340 : : ", blkref #%d: rel %u/%u/%u fork %s blk %u",
341 : : block_id,
342 : : rlocator.spcOid, rlocator.dbOid, rlocator.relNumber,
343 : 3234 : forkNames[forknum],
344 : : blk);
345 : : }
346 : : else
347 : : {
348 : 286414 : 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 [ + + ]: 289648 : if (XLogRecHasBlockImage(record, block_id))
356 : : {
357 : : /* Calculate the amount of FPI data in the record. */
358 [ - + ]: 9298 : if (fpi_len)
1437 jdavis@postgresql.or 359 :UBC 0 : *fpi_len += XLogRecGetBlock(record, block_id)->bimg_len;
360 : :
1437 jdavis@postgresql.or 361 [ + - ]:CBC 9298 : if (XLogRecBlockImageApply(record, block_id))
1286 drowley@postgresql.o 362 : 9298 : appendStringInfoString(buf, " FPW");
363 : : else
1286 drowley@postgresql.o 364 :UBC 0 : appendStringInfoString(buf, " FPW for WAL verification");
365 : : }
366 : : }
367 : : }
368 : :
1437 jdavis@postgresql.or 369 [ + + + - ]:CBC 296118 : if (!detailed_format && pretty)
370 : 261814 : appendStringInfoChar(buf, '\n');
371 : 296118 : }
|