1 /*-------------------------------------------------------------------------
2 *
3 * xactdesc.c
4 * rmgr descriptor routines for access/transam/xact.c
5 *
6 * Portions Copyright (c) 1996-2020, 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 (uint32) (parsed.origin_lsn >> 32),
310 (uint32) parsed.origin_lsn,
311 timestamptz_to_str(parsed.origin_timestamp));
312 }
313 }
314
315 static void
xact_desc_abort(StringInfo buf,uint8 info,xl_xact_abort * xlrec)316 xact_desc_abort(StringInfo buf, uint8 info, xl_xact_abort *xlrec)
317 {
318 xl_xact_parsed_abort parsed;
319
320 ParseAbortRecord(info, xlrec, &parsed);
321
322 /* If this is a prepared xact, show the xid of the original xact */
323 if (TransactionIdIsValid(parsed.twophase_xid))
324 appendStringInfo(buf, "%u: ", parsed.twophase_xid);
325
326 appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time));
327
328 xact_desc_relations(buf, "rels", parsed.nrels, parsed.xnodes);
329 xact_desc_subxacts(buf, parsed.nsubxacts, parsed.subxacts);
330 }
331
332 static void
xact_desc_prepare(StringInfo buf,uint8 info,xl_xact_prepare * xlrec)333 xact_desc_prepare(StringInfo buf, uint8 info, xl_xact_prepare *xlrec)
334 {
335 xl_xact_parsed_prepare parsed;
336
337 ParsePrepareRecord(info, xlrec, &parsed);
338
339 appendStringInfo(buf, "gid %s: ", parsed.twophase_gid);
340 appendStringInfoString(buf, timestamptz_to_str(parsed.xact_time));
341
342 xact_desc_relations(buf, "rels(commit)", parsed.nrels, parsed.xnodes);
343 xact_desc_relations(buf, "rels(abort)", parsed.nabortrels,
344 parsed.abortnodes);
345 xact_desc_subxacts(buf, parsed.nsubxacts, parsed.subxacts);
346
347 standby_desc_invalidations(buf, parsed.nmsgs, parsed.msgs, parsed.dbId,
348 parsed.tsId, xlrec->initfileinval);
349 }
350
351 static void
xact_desc_assignment(StringInfo buf,xl_xact_assignment * xlrec)352 xact_desc_assignment(StringInfo buf, xl_xact_assignment *xlrec)
353 {
354 int i;
355
356 appendStringInfoString(buf, "subxacts:");
357
358 for (i = 0; i < xlrec->nsubxacts; i++)
359 appendStringInfo(buf, " %u", xlrec->xsub[i]);
360 }
361
362 void
xact_desc(StringInfo buf,XLogReaderState * record)363 xact_desc(StringInfo buf, XLogReaderState *record)
364 {
365 char *rec = XLogRecGetData(record);
366 uint8 info = XLogRecGetInfo(record) & XLOG_XACT_OPMASK;
367
368 if (info == XLOG_XACT_COMMIT || info == XLOG_XACT_COMMIT_PREPARED)
369 {
370 xl_xact_commit *xlrec = (xl_xact_commit *) rec;
371
372 xact_desc_commit(buf, XLogRecGetInfo(record), xlrec,
373 XLogRecGetOrigin(record));
374 }
375 else if (info == XLOG_XACT_ABORT || info == XLOG_XACT_ABORT_PREPARED)
376 {
377 xl_xact_abort *xlrec = (xl_xact_abort *) rec;
378
379 xact_desc_abort(buf, XLogRecGetInfo(record), xlrec);
380 }
381 else if (info == XLOG_XACT_PREPARE)
382 {
383 xl_xact_prepare *xlrec = (xl_xact_prepare *) rec;
384
385 xact_desc_prepare(buf, XLogRecGetInfo(record), xlrec);
386 }
387 else if (info == XLOG_XACT_ASSIGNMENT)
388 {
389 xl_xact_assignment *xlrec = (xl_xact_assignment *) rec;
390
391 /*
392 * Note that we ignore the WAL record's xid, since we're more
393 * interested in the top-level xid that issued the record and which
394 * xids are being reported here.
395 */
396 appendStringInfo(buf, "xtop %u: ", xlrec->xtop);
397 xact_desc_assignment(buf, xlrec);
398 }
399 }
400
401 const char *
xact_identify(uint8 info)402 xact_identify(uint8 info)
403 {
404 const char *id = NULL;
405
406 switch (info & XLOG_XACT_OPMASK)
407 {
408 case XLOG_XACT_COMMIT:
409 id = "COMMIT";
410 break;
411 case XLOG_XACT_PREPARE:
412 id = "PREPARE";
413 break;
414 case XLOG_XACT_ABORT:
415 id = "ABORT";
416 break;
417 case XLOG_XACT_COMMIT_PREPARED:
418 id = "COMMIT_PREPARED";
419 break;
420 case XLOG_XACT_ABORT_PREPARED:
421 id = "ABORT_PREPARED";
422 break;
423 case XLOG_XACT_ASSIGNMENT:
424 id = "ASSIGNMENT";
425 break;
426 }
427
428 return id;
429 }
430