1 /*-------------------------------------------------------------------------
2 *
3 * xactdesc.c
4 * rmgr descriptor routines for access/transam/xact.c
5 *
6 * Portions Copyright (c) 1996-2016, 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 "catalog/catalog.h"
20 #include "storage/sinval.h"
21 #include "storage/standbydefs.h"
22 #include "utils/timestamp.h"
23
24 /*
25 * Parse the WAL format of an xact commit and abort records into an easier to
26 * understand format.
27 *
28 * This routines are in xactdesc.c because they're accessed in backend (when
29 * replaying WAL) and frontend (pg_xlogdump) code. This file is the only xact
30 * specific one shared between both. They're complicated enough that
31 * duplication would be bothersome.
32 */
33
34 void
ParseCommitRecord(uint8 info,xl_xact_commit * xlrec,xl_xact_parsed_commit * parsed)35 ParseCommitRecord(uint8 info, xl_xact_commit *xlrec, xl_xact_parsed_commit *parsed)
36 {
37 char *data = ((char *) xlrec) + MinSizeOfXactCommit;
38
39 memset(parsed, 0, sizeof(*parsed));
40
41 parsed->xinfo = 0; /* default, if no XLOG_XACT_HAS_INFO is
42 * present */
43
44 parsed->xact_time = xlrec->xact_time;
45
46 if (info & XLOG_XACT_HAS_INFO)
47 {
48 xl_xact_xinfo *xl_xinfo = (xl_xact_xinfo *) data;
49
50 parsed->xinfo = xl_xinfo->xinfo;
51
52 data += sizeof(xl_xact_xinfo);
53 }
54
55 if (parsed->xinfo & XACT_XINFO_HAS_DBINFO)
56 {
57 xl_xact_dbinfo *xl_dbinfo = (xl_xact_dbinfo *) data;
58
59 parsed->dbId = xl_dbinfo->dbId;
60 parsed->tsId = xl_dbinfo->tsId;
61
62 data += sizeof(xl_xact_dbinfo);
63 }
64
65 if (parsed->xinfo & XACT_XINFO_HAS_SUBXACTS)
66 {
67 xl_xact_subxacts *xl_subxacts = (xl_xact_subxacts *) data;
68
69 parsed->nsubxacts = xl_subxacts->nsubxacts;
70 parsed->subxacts = xl_subxacts->subxacts;
71
72 data += MinSizeOfXactSubxacts;
73 data += parsed->nsubxacts * sizeof(TransactionId);
74 }
75
76 if (parsed->xinfo & XACT_XINFO_HAS_RELFILENODES)
77 {
78 xl_xact_relfilenodes *xl_relfilenodes = (xl_xact_relfilenodes *) data;
79
80 parsed->nrels = xl_relfilenodes->nrels;
81 parsed->xnodes = xl_relfilenodes->xnodes;
82
83 data += MinSizeOfXactRelfilenodes;
84 data += xl_relfilenodes->nrels * sizeof(RelFileNode);
85 }
86
87 if (parsed->xinfo & XACT_XINFO_HAS_INVALS)
88 {
89 xl_xact_invals *xl_invals = (xl_xact_invals *) data;
90
91 parsed->nmsgs = xl_invals->nmsgs;
92 parsed->msgs = xl_invals->msgs;
93
94 data += MinSizeOfXactInvals;
95 data += xl_invals->nmsgs * sizeof(SharedInvalidationMessage);
96 }
97
98 if (parsed->xinfo & XACT_XINFO_HAS_TWOPHASE)
99 {
100 xl_xact_twophase *xl_twophase = (xl_xact_twophase *) data;
101
102 parsed->twophase_xid = xl_twophase->xid;
103
104 data += sizeof(xl_xact_twophase);
105 }
106
107 if (parsed->xinfo & XACT_XINFO_HAS_ORIGIN)
108 {
109 xl_xact_origin xl_origin;
110
111 /* we're only guaranteed 4 byte alignment, so copy onto stack */
112 memcpy(&xl_origin, data, sizeof(xl_origin));
113
114 parsed->origin_lsn = xl_origin.origin_lsn;
115 parsed->origin_timestamp = xl_origin.origin_timestamp;
116
117 data += sizeof(xl_xact_origin);
118 }
119 }
120
121 void
ParseAbortRecord(uint8 info,xl_xact_abort * xlrec,xl_xact_parsed_abort * parsed)122 ParseAbortRecord(uint8 info, xl_xact_abort *xlrec, xl_xact_parsed_abort *parsed)
123 {
124 char *data = ((char *) xlrec) + MinSizeOfXactAbort;
125
126 memset(parsed, 0, sizeof(*parsed));
127
128 parsed->xinfo = 0; /* default, if no XLOG_XACT_HAS_INFO is
129 * present */
130
131 parsed->xact_time = xlrec->xact_time;
132
133 if (info & XLOG_XACT_HAS_INFO)
134 {
135 xl_xact_xinfo *xl_xinfo = (xl_xact_xinfo *) data;
136
137 parsed->xinfo = xl_xinfo->xinfo;
138
139 data += sizeof(xl_xact_xinfo);
140 }
141
142 if (parsed->xinfo & XACT_XINFO_HAS_SUBXACTS)
143 {
144 xl_xact_subxacts *xl_subxacts = (xl_xact_subxacts *) data;
145
146 parsed->nsubxacts = xl_subxacts->nsubxacts;
147 parsed->subxacts = xl_subxacts->subxacts;
148
149 data += MinSizeOfXactSubxacts;
150 data += parsed->nsubxacts * sizeof(TransactionId);
151 }
152
153 if (parsed->xinfo & XACT_XINFO_HAS_RELFILENODES)
154 {
155 xl_xact_relfilenodes *xl_relfilenodes = (xl_xact_relfilenodes *) data;
156
157 parsed->nrels = xl_relfilenodes->nrels;
158 parsed->xnodes = xl_relfilenodes->xnodes;
159
160 data += MinSizeOfXactRelfilenodes;
161 data += xl_relfilenodes->nrels * sizeof(RelFileNode);
162 }
163
164 if (parsed->xinfo & XACT_XINFO_HAS_TWOPHASE)
165 {
166 xl_xact_twophase *xl_twophase = (xl_xact_twophase *) data;
167
168 parsed->twophase_xid = xl_twophase->xid;
169
170 data += sizeof(xl_xact_twophase);
171 }
172 }
173
174 static void
xact_desc_commit(StringInfo buf,uint8 info,xl_xact_commit * xlrec,RepOriginId origin_id)175 xact_desc_commit(StringInfo buf, uint8 info, xl_xact_commit *xlrec, RepOriginId origin_id)
176 {
177 xl_xact_parsed_commit parsed;
178 int i;
179
180 ParseCommitRecord(info, xlrec, &parsed);
181
182 /* If this is a prepared xact, show the xid of the original xact */
183 if (TransactionIdIsValid(parsed.twophase_xid))
184 appendStringInfo(buf, "%u: ", parsed.twophase_xid);
185
186 appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time));
187
188 if (parsed.nrels > 0)
189 {
190 appendStringInfoString(buf, "; rels:");
191 for (i = 0; i < parsed.nrels; i++)
192 {
193 char *path = relpathperm(parsed.xnodes[i], MAIN_FORKNUM);
194
195 appendStringInfo(buf, " %s", path);
196 pfree(path);
197 }
198 }
199 if (parsed.nsubxacts > 0)
200 {
201 appendStringInfoString(buf, "; subxacts:");
202 for (i = 0; i < parsed.nsubxacts; i++)
203 appendStringInfo(buf, " %u", parsed.subxacts[i]);
204 }
205 if (parsed.nmsgs > 0)
206 {
207 standby_desc_invalidations(
208 buf, parsed.nmsgs, parsed.msgs, parsed.dbId, parsed.tsId,
209 XactCompletionRelcacheInitFileInval(parsed.xinfo));
210 }
211
212 if (XactCompletionForceSyncCommit(parsed.xinfo))
213 appendStringInfoString(buf, "; sync");
214
215 if (parsed.xinfo & XACT_XINFO_HAS_ORIGIN)
216 {
217 appendStringInfo(buf, "; origin: node %u, lsn %X/%X, at %s",
218 origin_id,
219 (uint32) (parsed.origin_lsn >> 32),
220 (uint32) parsed.origin_lsn,
221 timestamptz_to_str(parsed.origin_timestamp));
222 }
223 }
224
225 static void
xact_desc_abort(StringInfo buf,uint8 info,xl_xact_abort * xlrec)226 xact_desc_abort(StringInfo buf, uint8 info, xl_xact_abort *xlrec)
227 {
228 xl_xact_parsed_abort parsed;
229 int i;
230
231 ParseAbortRecord(info, xlrec, &parsed);
232
233 /* If this is a prepared xact, show the xid of the original xact */
234 if (TransactionIdIsValid(parsed.twophase_xid))
235 appendStringInfo(buf, "%u: ", parsed.twophase_xid);
236
237 appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time));
238 if (parsed.nrels > 0)
239 {
240 appendStringInfoString(buf, "; rels:");
241 for (i = 0; i < parsed.nrels; i++)
242 {
243 char *path = relpathperm(parsed.xnodes[i], MAIN_FORKNUM);
244
245 appendStringInfo(buf, " %s", path);
246 pfree(path);
247 }
248 }
249
250 if (parsed.nsubxacts > 0)
251 {
252 appendStringInfoString(buf, "; subxacts:");
253 for (i = 0; i < parsed.nsubxacts; i++)
254 appendStringInfo(buf, " %u", parsed.subxacts[i]);
255 }
256 }
257
258 static void
xact_desc_assignment(StringInfo buf,xl_xact_assignment * xlrec)259 xact_desc_assignment(StringInfo buf, xl_xact_assignment *xlrec)
260 {
261 int i;
262
263 appendStringInfoString(buf, "subxacts:");
264
265 for (i = 0; i < xlrec->nsubxacts; i++)
266 appendStringInfo(buf, " %u", xlrec->xsub[i]);
267 }
268
269 void
xact_desc(StringInfo buf,XLogReaderState * record)270 xact_desc(StringInfo buf, XLogReaderState *record)
271 {
272 char *rec = XLogRecGetData(record);
273 uint8 info = XLogRecGetInfo(record) & XLOG_XACT_OPMASK;
274
275 if (info == XLOG_XACT_COMMIT || info == XLOG_XACT_COMMIT_PREPARED)
276 {
277 xl_xact_commit *xlrec = (xl_xact_commit *) rec;
278
279 xact_desc_commit(buf, XLogRecGetInfo(record), xlrec,
280 XLogRecGetOrigin(record));
281 }
282 else if (info == XLOG_XACT_ABORT || info == XLOG_XACT_ABORT_PREPARED)
283 {
284 xl_xact_abort *xlrec = (xl_xact_abort *) rec;
285
286 xact_desc_abort(buf, XLogRecGetInfo(record), xlrec);
287 }
288 else if (info == XLOG_XACT_ASSIGNMENT)
289 {
290 xl_xact_assignment *xlrec = (xl_xact_assignment *) rec;
291
292 /*
293 * Note that we ignore the WAL record's xid, since we're more
294 * interested in the top-level xid that issued the record and which
295 * xids are being reported here.
296 */
297 appendStringInfo(buf, "xtop %u: ", xlrec->xtop);
298 xact_desc_assignment(buf, xlrec);
299 }
300 }
301
302 const char *
xact_identify(uint8 info)303 xact_identify(uint8 info)
304 {
305 const char *id = NULL;
306
307 switch (info & XLOG_XACT_OPMASK)
308 {
309 case XLOG_XACT_COMMIT:
310 id = "COMMIT";
311 break;
312 case XLOG_XACT_PREPARE:
313 id = "PREPARE";
314 break;
315 case XLOG_XACT_ABORT:
316 id = "ABORT";
317 break;
318 case XLOG_XACT_COMMIT_PREPARED:
319 id = "COMMIT_PREPARED";
320 break;
321 case XLOG_XACT_ABORT_PREPARED:
322 id = "ABORT_PREPARED";
323 break;
324 case XLOG_XACT_ASSIGNMENT:
325 id = "ASSIGNMENT";
326 break;
327 }
328
329 return id;
330 }
331