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