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 | |
33 | void |
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 |
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 | static void |
213 | xact_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 | |
263 | static void |
264 | xact_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 | |
296 | static void |
297 | xact_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 | |
307 | void |
308 | xact_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 | |
340 | const char * |
341 | xact_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 | |