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