1 /*-------------------------------------------------------------------------
2  *
3  * xactdesc.c
4  *	  rmgr descriptor routines for access/transam/xact.c
5  *
6  * Portions Copyright (c) 1996-2020, 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 /*
213  * ParsePrepareRecord
214  */
215 void
216 ParsePrepareRecord(uint8 info, xl_xact_prepare *xlrec, xl_xact_parsed_prepare *parsed)
217 {
218 	char	   *bufptr;
219 
220 	bufptr = ((char *) xlrec) + MAXALIGN(sizeof(xl_xact_prepare));
221 
222 	memset(parsed, 0, sizeof(*parsed));
223 
224 	parsed->xact_time = xlrec->prepared_at;
225 	parsed->origin_lsn = xlrec->origin_lsn;
226 	parsed->origin_timestamp = xlrec->origin_timestamp;
227 	parsed->twophase_xid = xlrec->xid;
228 	parsed->dbId = xlrec->database;
229 	parsed->nsubxacts = xlrec->nsubxacts;
230 	parsed->nrels = xlrec->ncommitrels;
231 	parsed->nabortrels = xlrec->nabortrels;
232 	parsed->nmsgs = xlrec->ninvalmsgs;
233 
234 	strncpy(parsed->twophase_gid, bufptr, xlrec->gidlen);
235 	bufptr += MAXALIGN(xlrec->gidlen);
236 
237 	parsed->subxacts = (TransactionId *) bufptr;
238 	bufptr += MAXALIGN(xlrec->nsubxacts * sizeof(TransactionId));
239 
240 	parsed->xnodes = (RelFileNode *) bufptr;
241 	bufptr += MAXALIGN(xlrec->ncommitrels * sizeof(RelFileNode));
242 
243 	parsed->abortnodes = (RelFileNode *) bufptr;
244 	bufptr += MAXALIGN(xlrec->nabortrels * sizeof(RelFileNode));
245 
246 	parsed->msgs = (SharedInvalidationMessage *) bufptr;
247 	bufptr += MAXALIGN(xlrec->ninvalmsgs * sizeof(SharedInvalidationMessage));
248 }
249 
250 static void
251 xact_desc_relations(StringInfo buf, char *label, int nrels,
252 					RelFileNode *xnodes)
253 {
254 	int			i;
255 
256 	if (nrels > 0)
257 	{
258 		appendStringInfo(buf, "; %s:", label);
259 		for (i = 0; i < nrels; i++)
260 		{
261 			char	   *path = relpathperm(xnodes[i], MAIN_FORKNUM);
262 
263 			appendStringInfo(buf, " %s", path);
264 			pfree(path);
265 		}
266 	}
267 }
268 
269 static void
270 xact_desc_subxacts(StringInfo buf, int nsubxacts, TransactionId *subxacts)
271 {
272 	int			i;
273 
274 	if (nsubxacts > 0)
275 	{
276 		appendStringInfoString(buf, "; subxacts:");
277 		for (i = 0; i < nsubxacts; i++)
278 			appendStringInfo(buf, " %u", subxacts[i]);
279 	}
280 }
281 
282 static void
283 xact_desc_commit(StringInfo buf, uint8 info, xl_xact_commit *xlrec, RepOriginId origin_id)
284 {
285 	xl_xact_parsed_commit parsed;
286 
287 	ParseCommitRecord(info, xlrec, &parsed);
288 
289 	/* If this is a prepared xact, show the xid of the original xact */
290 	if (TransactionIdIsValid(parsed.twophase_xid))
291 		appendStringInfo(buf, "%u: ", parsed.twophase_xid);
292 
293 	appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time));
294 
295 	xact_desc_relations(buf, "rels", parsed.nrels, parsed.xnodes);
296 	xact_desc_subxacts(buf, parsed.nsubxacts, parsed.subxacts);
297 
298 	standby_desc_invalidations(buf, parsed.nmsgs, parsed.msgs, parsed.dbId,
299 							   parsed.tsId,
300 							   XactCompletionRelcacheInitFileInval(parsed.xinfo));
301 
302 	if (XactCompletionForceSyncCommit(parsed.xinfo))
303 		appendStringInfoString(buf, "; sync");
304 
305 	if (parsed.xinfo & XACT_XINFO_HAS_ORIGIN)
306 	{
307 		appendStringInfo(buf, "; origin: node %u, lsn %X/%X, at %s",
308 						 origin_id,
309 						 (uint32) (parsed.origin_lsn >> 32),
310 						 (uint32) parsed.origin_lsn,
311 						 timestamptz_to_str(parsed.origin_timestamp));
312 	}
313 }
314 
315 static void
316 xact_desc_abort(StringInfo buf, uint8 info, xl_xact_abort *xlrec)
317 {
318 	xl_xact_parsed_abort parsed;
319 
320 	ParseAbortRecord(info, xlrec, &parsed);
321 
322 	/* If this is a prepared xact, show the xid of the original xact */
323 	if (TransactionIdIsValid(parsed.twophase_xid))
324 		appendStringInfo(buf, "%u: ", parsed.twophase_xid);
325 
326 	appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time));
327 
328 	xact_desc_relations(buf, "rels", parsed.nrels, parsed.xnodes);
329 	xact_desc_subxacts(buf, parsed.nsubxacts, parsed.subxacts);
330 }
331 
332 static void
333 xact_desc_prepare(StringInfo buf, uint8 info, xl_xact_prepare *xlrec)
334 {
335 	xl_xact_parsed_prepare parsed;
336 
337 	ParsePrepareRecord(info, xlrec, &parsed);
338 
339 	appendStringInfo(buf, "gid %s: ", parsed.twophase_gid);
340 	appendStringInfoString(buf, timestamptz_to_str(parsed.xact_time));
341 
342 	xact_desc_relations(buf, "rels(commit)", parsed.nrels, parsed.xnodes);
343 	xact_desc_relations(buf, "rels(abort)", parsed.nabortrels,
344 						parsed.abortnodes);
345 	xact_desc_subxacts(buf, parsed.nsubxacts, parsed.subxacts);
346 
347 	standby_desc_invalidations(buf, parsed.nmsgs, parsed.msgs, parsed.dbId,
348 							   parsed.tsId, xlrec->initfileinval);
349 }
350 
351 static void
352 xact_desc_assignment(StringInfo buf, xl_xact_assignment *xlrec)
353 {
354 	int			i;
355 
356 	appendStringInfoString(buf, "subxacts:");
357 
358 	for (i = 0; i < xlrec->nsubxacts; i++)
359 		appendStringInfo(buf, " %u", xlrec->xsub[i]);
360 }
361 
362 void
363 xact_desc(StringInfo buf, XLogReaderState *record)
364 {
365 	char	   *rec = XLogRecGetData(record);
366 	uint8		info = XLogRecGetInfo(record) & XLOG_XACT_OPMASK;
367 
368 	if (info == XLOG_XACT_COMMIT || info == XLOG_XACT_COMMIT_PREPARED)
369 	{
370 		xl_xact_commit *xlrec = (xl_xact_commit *) rec;
371 
372 		xact_desc_commit(buf, XLogRecGetInfo(record), xlrec,
373 						 XLogRecGetOrigin(record));
374 	}
375 	else if (info == XLOG_XACT_ABORT || info == XLOG_XACT_ABORT_PREPARED)
376 	{
377 		xl_xact_abort *xlrec = (xl_xact_abort *) rec;
378 
379 		xact_desc_abort(buf, XLogRecGetInfo(record), xlrec);
380 	}
381 	else if (info == XLOG_XACT_PREPARE)
382 	{
383 		xl_xact_prepare *xlrec = (xl_xact_prepare *) rec;
384 
385 		xact_desc_prepare(buf, XLogRecGetInfo(record), xlrec);
386 	}
387 	else if (info == XLOG_XACT_ASSIGNMENT)
388 	{
389 		xl_xact_assignment *xlrec = (xl_xact_assignment *) rec;
390 
391 		/*
392 		 * Note that we ignore the WAL record's xid, since we're more
393 		 * interested in the top-level xid that issued the record and which
394 		 * xids are being reported here.
395 		 */
396 		appendStringInfo(buf, "xtop %u: ", xlrec->xtop);
397 		xact_desc_assignment(buf, xlrec);
398 	}
399 }
400 
401 const char *
402 xact_identify(uint8 info)
403 {
404 	const char *id = NULL;
405 
406 	switch (info & XLOG_XACT_OPMASK)
407 	{
408 		case XLOG_XACT_COMMIT:
409 			id = "COMMIT";
410 			break;
411 		case XLOG_XACT_PREPARE:
412 			id = "PREPARE";
413 			break;
414 		case XLOG_XACT_ABORT:
415 			id = "ABORT";
416 			break;
417 		case XLOG_XACT_COMMIT_PREPARED:
418 			id = "COMMIT_PREPARED";
419 			break;
420 		case XLOG_XACT_ABORT_PREPARED:
421 			id = "ABORT_PREPARED";
422 			break;
423 		case XLOG_XACT_ASSIGNMENT:
424 			id = "ASSIGNMENT";
425 			break;
426 	}
427 
428 	return id;
429 }
430