1/*-------------------------------------------------------------------------
2 *
3 * xactdesc.c
4 * rmgr descriptor routines for access/transam/xact.c
5 *
6 * Portions Copyright (c) 1996-2019, 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
33void
34ParseCommitRecord(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
128void
129ParseAbortRecord(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
212static void
213xact_desc_commit(StringInfo buf, uint8 info, xl_xact_commit *xlrec, RepOriginId origin_id)
214{
215 xl_xact_parsed_commit parsed;
216 int i;
217
218 ParseCommitRecord(info, xlrec, &parsed);
219
220 /* If this is a prepared xact, show the xid of the original xact */
221 if (TransactionIdIsValid(parsed.twophase_xid))
222 appendStringInfo(buf, "%u: ", parsed.twophase_xid);
223
224 appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time));
225
226 if (parsed.nrels > 0)
227 {
228 appendStringInfoString(buf, "; rels:");
229 for (i = 0; i < parsed.nrels; i++)
230 {
231 char *path = relpathperm(parsed.xnodes[i], MAIN_FORKNUM);
232
233 appendStringInfo(buf, " %s", path);
234 pfree(path);
235 }
236 }
237 if (parsed.nsubxacts > 0)
238 {
239 appendStringInfoString(buf, "; subxacts:");
240 for (i = 0; i < parsed.nsubxacts; i++)
241 appendStringInfo(buf, " %u", parsed.subxacts[i]);
242 }
243 if (parsed.nmsgs > 0)
244 {
245 standby_desc_invalidations(
246 buf, parsed.nmsgs, parsed.msgs, parsed.dbId, parsed.tsId,
247 XactCompletionRelcacheInitFileInval(parsed.xinfo));
248 }
249
250 if (XactCompletionForceSyncCommit(parsed.xinfo))
251 appendStringInfoString(buf, "; sync");
252
253 if (parsed.xinfo & XACT_XINFO_HAS_ORIGIN)
254 {
255 appendStringInfo(buf, "; origin: node %u, lsn %X/%X, at %s",
256 origin_id,
257 (uint32) (parsed.origin_lsn >> 32),
258 (uint32) parsed.origin_lsn,
259 timestamptz_to_str(parsed.origin_timestamp));
260 }
261}
262
263static void
264xact_desc_abort(StringInfo buf, uint8 info, xl_xact_abort *xlrec)
265{
266 xl_xact_parsed_abort parsed;
267 int i;
268
269 ParseAbortRecord(info, xlrec, &parsed);
270
271 /* If this is a prepared xact, show the xid of the original xact */
272 if (TransactionIdIsValid(parsed.twophase_xid))
273 appendStringInfo(buf, "%u: ", parsed.twophase_xid);
274
275 appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time));
276 if (parsed.nrels > 0)
277 {
278 appendStringInfoString(buf, "; rels:");
279 for (i = 0; i < parsed.nrels; i++)
280 {
281 char *path = relpathperm(parsed.xnodes[i], MAIN_FORKNUM);
282
283 appendStringInfo(buf, " %s", path);
284 pfree(path);
285 }
286 }
287
288 if (parsed.nsubxacts > 0)
289 {
290 appendStringInfoString(buf, "; subxacts:");
291 for (i = 0; i < parsed.nsubxacts; i++)
292 appendStringInfo(buf, " %u", parsed.subxacts[i]);
293 }
294}
295
296static void
297xact_desc_assignment(StringInfo buf, xl_xact_assignment *xlrec)
298{
299 int i;
300
301 appendStringInfoString(buf, "subxacts:");
302
303 for (i = 0; i < xlrec->nsubxacts; i++)
304 appendStringInfo(buf, " %u", xlrec->xsub[i]);
305}
306
307void
308xact_desc(StringInfo buf, XLogReaderState *record)
309{
310 char *rec = XLogRecGetData(record);
311 uint8 info = XLogRecGetInfo(record) & XLOG_XACT_OPMASK;
312
313 if (info == XLOG_XACT_COMMIT || info == XLOG_XACT_COMMIT_PREPARED)
314 {
315 xl_xact_commit *xlrec = (xl_xact_commit *) rec;
316
317 xact_desc_commit(buf, XLogRecGetInfo(record), xlrec,
318 XLogRecGetOrigin(record));
319 }
320 else if (info == XLOG_XACT_ABORT || info == XLOG_XACT_ABORT_PREPARED)
321 {
322 xl_xact_abort *xlrec = (xl_xact_abort *) rec;
323
324 xact_desc_abort(buf, XLogRecGetInfo(record), xlrec);
325 }
326 else if (info == XLOG_XACT_ASSIGNMENT)
327 {
328 xl_xact_assignment *xlrec = (xl_xact_assignment *) rec;
329
330 /*
331 * Note that we ignore the WAL record's xid, since we're more
332 * interested in the top-level xid that issued the record and which
333 * xids are being reported here.
334 */
335 appendStringInfo(buf, "xtop %u: ", xlrec->xtop);
336 xact_desc_assignment(buf, xlrec);
337 }
338}
339
340const char *
341xact_identify(uint8 info)
342{
343 const char *id = NULL;
344
345 switch (info & XLOG_XACT_OPMASK)
346 {
347 case XLOG_XACT_COMMIT:
348 id = "COMMIT";
349 break;
350 case XLOG_XACT_PREPARE:
351 id = "PREPARE";
352 break;
353 case XLOG_XACT_ABORT:
354 id = "ABORT";
355 break;
356 case XLOG_XACT_COMMIT_PREPARED:
357 id = "COMMIT_PREPARED";
358 break;
359 case XLOG_XACT_ABORT_PREPARED:
360 id = "ABORT_PREPARED";
361 break;
362 case XLOG_XACT_ASSIGNMENT:
363 id = "ASSIGNMENT";
364 break;
365 }
366
367 return id;
368}
369