1 /*-------------------------------------------------------------------------
2  *
3  * xactdesc.c
4  *	  rmgr descriptor routines for access/transam/xact.c
5  *
6  * Portions Copyright (c) 1996-2018, 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 static void
xact_desc_commit(StringInfo buf,uint8 info,xl_xact_commit * xlrec,RepOriginId origin_id)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
xact_desc_abort(StringInfo buf,uint8 info,xl_xact_abort * xlrec)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
xact_desc_assignment(StringInfo buf,xl_xact_assignment * xlrec)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
xact_desc(StringInfo buf,XLogReaderState * record)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 *
xact_identify(uint8 info)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