1 /*-------------------------------------------------------------------------
2 *
3 * xactdesc.c
4 * rmgr descriptor routines for access/transam/xact.c
5 *
6 * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 *
10 * IDENTIFICATION
11 * src/backend/access/rmgrdesc/xactdesc.c
12 *
13 *-------------------------------------------------------------------------
14 */
15 #include "postgres.h"
16
17 #include "access/transam.h"
18 #include "access/xact.h"
19 #include "storage/sinval.h"
20 #include "storage/standbydefs.h"
21 #include "utils/timestamp.h"
22
23 /*
24 * Parse the WAL format of an xact commit and abort records into an easier to
25 * understand format.
26 *
27 * This routines are in xactdesc.c because they're accessed in backend (when
28 * replaying WAL) and frontend (pg_waldump) code. This file is the only xact
29 * specific one shared between both. They're complicated enough that
30 * duplication would be bothersome.
31 */
32
33 void
ParseCommitRecord(uint8 info,xl_xact_commit * xlrec,xl_xact_parsed_commit * parsed)34 ParseCommitRecord(uint8 info, xl_xact_commit *xlrec, xl_xact_parsed_commit *parsed)
35 {
36 char *data = ((char *) xlrec) + MinSizeOfXactCommit;
37
38 memset(parsed, 0, sizeof(*parsed));
39
40 parsed->xinfo = 0; /* default, if no XLOG_XACT_HAS_INFO is
41 * present */
42
43 parsed->xact_time = xlrec->xact_time;
44
45 if (info & XLOG_XACT_HAS_INFO)
46 {
47 xl_xact_xinfo *xl_xinfo = (xl_xact_xinfo *) data;
48
49 parsed->xinfo = xl_xinfo->xinfo;
50
51 data += sizeof(xl_xact_xinfo);
52 }
53
54 if (parsed->xinfo & XACT_XINFO_HAS_DBINFO)
55 {
56 xl_xact_dbinfo *xl_dbinfo = (xl_xact_dbinfo *) data;
57
58 parsed->dbId = xl_dbinfo->dbId;
59 parsed->tsId = xl_dbinfo->tsId;
60
61 data += sizeof(xl_xact_dbinfo);
62 }
63
64 if (parsed->xinfo & XACT_XINFO_HAS_SUBXACTS)
65 {
66 xl_xact_subxacts *xl_subxacts = (xl_xact_subxacts *) data;
67
68 parsed->nsubxacts = xl_subxacts->nsubxacts;
69 parsed->subxacts = xl_subxacts->subxacts;
70
71 data += MinSizeOfXactSubxacts;
72 data += parsed->nsubxacts * sizeof(TransactionId);
73 }
74
75 if (parsed->xinfo & XACT_XINFO_HAS_RELFILENODES)
76 {
77 xl_xact_relfilenodes *xl_relfilenodes = (xl_xact_relfilenodes *) data;
78
79 parsed->nrels = xl_relfilenodes->nrels;
80 parsed->xnodes = xl_relfilenodes->xnodes;
81
82 data += MinSizeOfXactRelfilenodes;
83 data += xl_relfilenodes->nrels * sizeof(RelFileNode);
84 }
85
86 if (parsed->xinfo & XACT_XINFO_HAS_INVALS)
87 {
88 xl_xact_invals *xl_invals = (xl_xact_invals *) data;
89
90 parsed->nmsgs = xl_invals->nmsgs;
91 parsed->msgs = xl_invals->msgs;
92
93 data += MinSizeOfXactInvals;
94 data += xl_invals->nmsgs * sizeof(SharedInvalidationMessage);
95 }
96
97 if (parsed->xinfo & XACT_XINFO_HAS_TWOPHASE)
98 {
99 xl_xact_twophase *xl_twophase = (xl_xact_twophase *) data;
100
101 parsed->twophase_xid = xl_twophase->xid;
102
103 data += sizeof(xl_xact_twophase);
104
105 if (parsed->xinfo & XACT_XINFO_HAS_GID)
106 {
107 strlcpy(parsed->twophase_gid, data, sizeof(parsed->twophase_gid));
108 data += strlen(data) + 1;
109 }
110 }
111
112 /* Note: no alignment is guaranteed after this point */
113
114 if (parsed->xinfo & XACT_XINFO_HAS_ORIGIN)
115 {
116 xl_xact_origin xl_origin;
117
118 /* no alignment is guaranteed, so copy onto stack */
119 memcpy(&xl_origin, data, sizeof(xl_origin));
120
121 parsed->origin_lsn = xl_origin.origin_lsn;
122 parsed->origin_timestamp = xl_origin.origin_timestamp;
123
124 data += sizeof(xl_xact_origin);
125 }
126 }
127
128 void
ParseAbortRecord(uint8 info,xl_xact_abort * xlrec,xl_xact_parsed_abort * parsed)129 ParseAbortRecord(uint8 info, xl_xact_abort *xlrec, xl_xact_parsed_abort *parsed)
130 {
131 char *data = ((char *) xlrec) + MinSizeOfXactAbort;
132
133 memset(parsed, 0, sizeof(*parsed));
134
135 parsed->xinfo = 0; /* default, if no XLOG_XACT_HAS_INFO is
136 * present */
137
138 parsed->xact_time = xlrec->xact_time;
139
140 if (info & XLOG_XACT_HAS_INFO)
141 {
142 xl_xact_xinfo *xl_xinfo = (xl_xact_xinfo *) data;
143
144 parsed->xinfo = xl_xinfo->xinfo;
145
146 data += sizeof(xl_xact_xinfo);
147 }
148
149 if (parsed->xinfo & XACT_XINFO_HAS_DBINFO)
150 {
151 xl_xact_dbinfo *xl_dbinfo = (xl_xact_dbinfo *) data;
152
153 parsed->dbId = xl_dbinfo->dbId;
154 parsed->tsId = xl_dbinfo->tsId;
155
156 data += sizeof(xl_xact_dbinfo);
157 }
158
159 if (parsed->xinfo & XACT_XINFO_HAS_SUBXACTS)
160 {
161 xl_xact_subxacts *xl_subxacts = (xl_xact_subxacts *) data;
162
163 parsed->nsubxacts = xl_subxacts->nsubxacts;
164 parsed->subxacts = xl_subxacts->subxacts;
165
166 data += MinSizeOfXactSubxacts;
167 data += parsed->nsubxacts * sizeof(TransactionId);
168 }
169
170 if (parsed->xinfo & XACT_XINFO_HAS_RELFILENODES)
171 {
172 xl_xact_relfilenodes *xl_relfilenodes = (xl_xact_relfilenodes *) data;
173
174 parsed->nrels = xl_relfilenodes->nrels;
175 parsed->xnodes = xl_relfilenodes->xnodes;
176
177 data += MinSizeOfXactRelfilenodes;
178 data += xl_relfilenodes->nrels * sizeof(RelFileNode);
179 }
180
181 if (parsed->xinfo & XACT_XINFO_HAS_TWOPHASE)
182 {
183 xl_xact_twophase *xl_twophase = (xl_xact_twophase *) data;
184
185 parsed->twophase_xid = xl_twophase->xid;
186
187 data += sizeof(xl_xact_twophase);
188
189 if (parsed->xinfo & XACT_XINFO_HAS_GID)
190 {
191 strlcpy(parsed->twophase_gid, data, sizeof(parsed->twophase_gid));
192 data += strlen(data) + 1;
193 }
194 }
195
196 /* Note: no alignment is guaranteed after this point */
197
198 if (parsed->xinfo & XACT_XINFO_HAS_ORIGIN)
199 {
200 xl_xact_origin xl_origin;
201
202 /* no alignment is guaranteed, so copy onto stack */
203 memcpy(&xl_origin, data, sizeof(xl_origin));
204
205 parsed->origin_lsn = xl_origin.origin_lsn;
206 parsed->origin_timestamp = xl_origin.origin_timestamp;
207
208 data += sizeof(xl_xact_origin);
209 }
210 }
211
212 /*
213 * ParsePrepareRecord
214 */
215 void
ParsePrepareRecord(uint8 info,xl_xact_prepare * xlrec,xl_xact_parsed_prepare * parsed)216 ParsePrepareRecord(uint8 info, xl_xact_prepare *xlrec, xl_xact_parsed_prepare *parsed)
217 {
218 char *bufptr;
219
220 bufptr = ((char *) xlrec) + MAXALIGN(sizeof(xl_xact_prepare));
221
222 memset(parsed, 0, sizeof(*parsed));
223
224 parsed->xact_time = xlrec->prepared_at;
225 parsed->origin_lsn = xlrec->origin_lsn;
226 parsed->origin_timestamp = xlrec->origin_timestamp;
227 parsed->twophase_xid = xlrec->xid;
228 parsed->dbId = xlrec->database;
229 parsed->nsubxacts = xlrec->nsubxacts;
230 parsed->nrels = xlrec->ncommitrels;
231 parsed->nabortrels = xlrec->nabortrels;
232 parsed->nmsgs = xlrec->ninvalmsgs;
233
234 strncpy(parsed->twophase_gid, bufptr, xlrec->gidlen);
235 bufptr += MAXALIGN(xlrec->gidlen);
236
237 parsed->subxacts = (TransactionId *) bufptr;
238 bufptr += MAXALIGN(xlrec->nsubxacts * sizeof(TransactionId));
239
240 parsed->xnodes = (RelFileNode *) bufptr;
241 bufptr += MAXALIGN(xlrec->ncommitrels * sizeof(RelFileNode));
242
243 parsed->abortnodes = (RelFileNode *) bufptr;
244 bufptr += MAXALIGN(xlrec->nabortrels * sizeof(RelFileNode));
245
246 parsed->msgs = (SharedInvalidationMessage *) bufptr;
247 bufptr += MAXALIGN(xlrec->ninvalmsgs * sizeof(SharedInvalidationMessage));
248 }
249
250 static void
xact_desc_relations(StringInfo buf,char * label,int nrels,RelFileNode * xnodes)251 xact_desc_relations(StringInfo buf, char *label, int nrels,
252 RelFileNode *xnodes)
253 {
254 int i;
255
256 if (nrels > 0)
257 {
258 appendStringInfo(buf, "; %s:", label);
259 for (i = 0; i < nrels; i++)
260 {
261 char *path = relpathperm(xnodes[i], MAIN_FORKNUM);
262
263 appendStringInfo(buf, " %s", path);
264 pfree(path);
265 }
266 }
267 }
268
269 static void
xact_desc_subxacts(StringInfo buf,int nsubxacts,TransactionId * subxacts)270 xact_desc_subxacts(StringInfo buf, int nsubxacts, TransactionId *subxacts)
271 {
272 int i;
273
274 if (nsubxacts > 0)
275 {
276 appendStringInfoString(buf, "; subxacts:");
277 for (i = 0; i < nsubxacts; i++)
278 appendStringInfo(buf, " %u", subxacts[i]);
279 }
280 }
281
282 static void
xact_desc_commit(StringInfo buf,uint8 info,xl_xact_commit * xlrec,RepOriginId origin_id)283 xact_desc_commit(StringInfo buf, uint8 info, xl_xact_commit *xlrec, RepOriginId origin_id)
284 {
285 xl_xact_parsed_commit parsed;
286
287 ParseCommitRecord(info, xlrec, &parsed);
288
289 /* If this is a prepared xact, show the xid of the original xact */
290 if (TransactionIdIsValid(parsed.twophase_xid))
291 appendStringInfo(buf, "%u: ", parsed.twophase_xid);
292
293 appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time));
294
295 xact_desc_relations(buf, "rels", parsed.nrels, parsed.xnodes);
296 xact_desc_subxacts(buf, parsed.nsubxacts, parsed.subxacts);
297
298 standby_desc_invalidations(buf, parsed.nmsgs, parsed.msgs, parsed.dbId,
299 parsed.tsId,
300 XactCompletionRelcacheInitFileInval(parsed.xinfo));
301
302 if (XactCompletionForceSyncCommit(parsed.xinfo))
303 appendStringInfoString(buf, "; sync");
304
305 if (parsed.xinfo & XACT_XINFO_HAS_ORIGIN)
306 {
307 appendStringInfo(buf, "; origin: node %u, lsn %X/%X, at %s",
308 origin_id,
309 LSN_FORMAT_ARGS(parsed.origin_lsn),
310 timestamptz_to_str(parsed.origin_timestamp));
311 }
312 }
313
314 static void
xact_desc_abort(StringInfo buf,uint8 info,xl_xact_abort * xlrec)315 xact_desc_abort(StringInfo buf, uint8 info, xl_xact_abort *xlrec)
316 {
317 xl_xact_parsed_abort parsed;
318
319 ParseAbortRecord(info, xlrec, &parsed);
320
321 /* If this is a prepared xact, show the xid of the original xact */
322 if (TransactionIdIsValid(parsed.twophase_xid))
323 appendStringInfo(buf, "%u: ", parsed.twophase_xid);
324
325 appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time));
326
327 xact_desc_relations(buf, "rels", parsed.nrels, parsed.xnodes);
328 xact_desc_subxacts(buf, parsed.nsubxacts, parsed.subxacts);
329 }
330
331 static void
xact_desc_prepare(StringInfo buf,uint8 info,xl_xact_prepare * xlrec)332 xact_desc_prepare(StringInfo buf, uint8 info, xl_xact_prepare *xlrec)
333 {
334 xl_xact_parsed_prepare parsed;
335
336 ParsePrepareRecord(info, xlrec, &parsed);
337
338 appendStringInfo(buf, "gid %s: ", parsed.twophase_gid);
339 appendStringInfoString(buf, timestamptz_to_str(parsed.xact_time));
340
341 xact_desc_relations(buf, "rels(commit)", parsed.nrels, parsed.xnodes);
342 xact_desc_relations(buf, "rels(abort)", parsed.nabortrels,
343 parsed.abortnodes);
344 xact_desc_subxacts(buf, parsed.nsubxacts, parsed.subxacts);
345
346 standby_desc_invalidations(buf, parsed.nmsgs, parsed.msgs, parsed.dbId,
347 parsed.tsId, xlrec->initfileinval);
348 }
349
350 static void
xact_desc_assignment(StringInfo buf,xl_xact_assignment * xlrec)351 xact_desc_assignment(StringInfo buf, xl_xact_assignment *xlrec)
352 {
353 int i;
354
355 appendStringInfoString(buf, "subxacts:");
356
357 for (i = 0; i < xlrec->nsubxacts; i++)
358 appendStringInfo(buf, " %u", xlrec->xsub[i]);
359 }
360
361 void
xact_desc(StringInfo buf,XLogReaderState * record)362 xact_desc(StringInfo buf, XLogReaderState *record)
363 {
364 char *rec = XLogRecGetData(record);
365 uint8 info = XLogRecGetInfo(record) & XLOG_XACT_OPMASK;
366
367 if (info == XLOG_XACT_COMMIT || info == XLOG_XACT_COMMIT_PREPARED)
368 {
369 xl_xact_commit *xlrec = (xl_xact_commit *) rec;
370
371 xact_desc_commit(buf, XLogRecGetInfo(record), xlrec,
372 XLogRecGetOrigin(record));
373 }
374 else if (info == XLOG_XACT_ABORT || info == XLOG_XACT_ABORT_PREPARED)
375 {
376 xl_xact_abort *xlrec = (xl_xact_abort *) rec;
377
378 xact_desc_abort(buf, XLogRecGetInfo(record), xlrec);
379 }
380 else if (info == XLOG_XACT_PREPARE)
381 {
382 xl_xact_prepare *xlrec = (xl_xact_prepare *) rec;
383
384 xact_desc_prepare(buf, XLogRecGetInfo(record), xlrec);
385 }
386 else if (info == XLOG_XACT_ASSIGNMENT)
387 {
388 xl_xact_assignment *xlrec = (xl_xact_assignment *) rec;
389
390 /*
391 * Note that we ignore the WAL record's xid, since we're more
392 * interested in the top-level xid that issued the record and which
393 * xids are being reported here.
394 */
395 appendStringInfo(buf, "xtop %u: ", xlrec->xtop);
396 xact_desc_assignment(buf, xlrec);
397 }
398 else if (info == XLOG_XACT_INVALIDATIONS)
399 {
400 xl_xact_invals *xlrec = (xl_xact_invals *) rec;
401
402 standby_desc_invalidations(buf, xlrec->nmsgs, xlrec->msgs, InvalidOid,
403 InvalidOid, false);
404 }
405 }
406
407 const char *
xact_identify(uint8 info)408 xact_identify(uint8 info)
409 {
410 const char *id = NULL;
411
412 switch (info & XLOG_XACT_OPMASK)
413 {
414 case XLOG_XACT_COMMIT:
415 id = "COMMIT";
416 break;
417 case XLOG_XACT_PREPARE:
418 id = "PREPARE";
419 break;
420 case XLOG_XACT_ABORT:
421 id = "ABORT";
422 break;
423 case XLOG_XACT_COMMIT_PREPARED:
424 id = "COMMIT_PREPARED";
425 break;
426 case XLOG_XACT_ABORT_PREPARED:
427 id = "ABORT_PREPARED";
428 break;
429 case XLOG_XACT_ASSIGNMENT:
430 id = "ASSIGNMENT";
431 break;
432 case XLOG_XACT_INVALIDATIONS:
433 id = "INVALIDATION";
434 break;
435 }
436
437 return id;
438 }
439