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