1 /*-------------------------------------------------------------------------
2  *
3  * xactdesc.c
4  *	  rmgr descriptor routines for access/transam/xact.c
5  *
6  * Portions Copyright (c) 1996-2021, 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 /*
213  * ParsePrepareRecord
214  */
215 void
ParsePrepareRecord(uint8 info,xl_xact_prepare * xlrec,xl_xact_parsed_prepare * parsed)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
xact_desc_relations(StringInfo buf,char * label,int nrels,RelFileNode * xnodes)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
xact_desc_subxacts(StringInfo buf,int nsubxacts,TransactionId * subxacts)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
xact_desc_commit(StringInfo buf,uint8 info,xl_xact_commit * xlrec,RepOriginId origin_id)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 						 LSN_FORMAT_ARGS(parsed.origin_lsn),
310 						 timestamptz_to_str(parsed.origin_timestamp));
311 	}
312 }
313 
314 static void
xact_desc_abort(StringInfo buf,uint8 info,xl_xact_abort * xlrec)315 xact_desc_abort(StringInfo buf, uint8 info, xl_xact_abort *xlrec)
316 {
317 	xl_xact_parsed_abort parsed;
318 
319 	ParseAbortRecord(info, xlrec, &parsed);
320 
321 	/* If this is a prepared xact, show the xid of the original xact */
322 	if (TransactionIdIsValid(parsed.twophase_xid))
323 		appendStringInfo(buf, "%u: ", parsed.twophase_xid);
324 
325 	appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time));
326 
327 	xact_desc_relations(buf, "rels", parsed.nrels, parsed.xnodes);
328 	xact_desc_subxacts(buf, parsed.nsubxacts, parsed.subxacts);
329 }
330 
331 static void
xact_desc_prepare(StringInfo buf,uint8 info,xl_xact_prepare * xlrec)332 xact_desc_prepare(StringInfo buf, uint8 info, xl_xact_prepare *xlrec)
333 {
334 	xl_xact_parsed_prepare parsed;
335 
336 	ParsePrepareRecord(info, xlrec, &parsed);
337 
338 	appendStringInfo(buf, "gid %s: ", parsed.twophase_gid);
339 	appendStringInfoString(buf, timestamptz_to_str(parsed.xact_time));
340 
341 	xact_desc_relations(buf, "rels(commit)", parsed.nrels, parsed.xnodes);
342 	xact_desc_relations(buf, "rels(abort)", parsed.nabortrels,
343 						parsed.abortnodes);
344 	xact_desc_subxacts(buf, parsed.nsubxacts, parsed.subxacts);
345 
346 	standby_desc_invalidations(buf, parsed.nmsgs, parsed.msgs, parsed.dbId,
347 							   parsed.tsId, xlrec->initfileinval);
348 }
349 
350 static void
xact_desc_assignment(StringInfo buf,xl_xact_assignment * xlrec)351 xact_desc_assignment(StringInfo buf, xl_xact_assignment *xlrec)
352 {
353 	int			i;
354 
355 	appendStringInfoString(buf, "subxacts:");
356 
357 	for (i = 0; i < xlrec->nsubxacts; i++)
358 		appendStringInfo(buf, " %u", xlrec->xsub[i]);
359 }
360 
361 void
xact_desc(StringInfo buf,XLogReaderState * record)362 xact_desc(StringInfo buf, XLogReaderState *record)
363 {
364 	char	   *rec = XLogRecGetData(record);
365 	uint8		info = XLogRecGetInfo(record) & XLOG_XACT_OPMASK;
366 
367 	if (info == XLOG_XACT_COMMIT || info == XLOG_XACT_COMMIT_PREPARED)
368 	{
369 		xl_xact_commit *xlrec = (xl_xact_commit *) rec;
370 
371 		xact_desc_commit(buf, XLogRecGetInfo(record), xlrec,
372 						 XLogRecGetOrigin(record));
373 	}
374 	else if (info == XLOG_XACT_ABORT || info == XLOG_XACT_ABORT_PREPARED)
375 	{
376 		xl_xact_abort *xlrec = (xl_xact_abort *) rec;
377 
378 		xact_desc_abort(buf, XLogRecGetInfo(record), xlrec);
379 	}
380 	else if (info == XLOG_XACT_PREPARE)
381 	{
382 		xl_xact_prepare *xlrec = (xl_xact_prepare *) rec;
383 
384 		xact_desc_prepare(buf, XLogRecGetInfo(record), xlrec);
385 	}
386 	else if (info == XLOG_XACT_ASSIGNMENT)
387 	{
388 		xl_xact_assignment *xlrec = (xl_xact_assignment *) rec;
389 
390 		/*
391 		 * Note that we ignore the WAL record's xid, since we're more
392 		 * interested in the top-level xid that issued the record and which
393 		 * xids are being reported here.
394 		 */
395 		appendStringInfo(buf, "xtop %u: ", xlrec->xtop);
396 		xact_desc_assignment(buf, xlrec);
397 	}
398 	else if (info == XLOG_XACT_INVALIDATIONS)
399 	{
400 		xl_xact_invals *xlrec = (xl_xact_invals *) rec;
401 
402 		standby_desc_invalidations(buf, xlrec->nmsgs, xlrec->msgs, InvalidOid,
403 								   InvalidOid, false);
404 	}
405 }
406 
407 const char *
xact_identify(uint8 info)408 xact_identify(uint8 info)
409 {
410 	const char *id = NULL;
411 
412 	switch (info & XLOG_XACT_OPMASK)
413 	{
414 		case XLOG_XACT_COMMIT:
415 			id = "COMMIT";
416 			break;
417 		case XLOG_XACT_PREPARE:
418 			id = "PREPARE";
419 			break;
420 		case XLOG_XACT_ABORT:
421 			id = "ABORT";
422 			break;
423 		case XLOG_XACT_COMMIT_PREPARED:
424 			id = "COMMIT_PREPARED";
425 			break;
426 		case XLOG_XACT_ABORT_PREPARED:
427 			id = "ABORT_PREPARED";
428 			break;
429 		case XLOG_XACT_ASSIGNMENT:
430 			id = "ASSIGNMENT";
431 			break;
432 		case XLOG_XACT_INVALIDATIONS:
433 			id = "INVALIDATION";
434 			break;
435 	}
436 
437 	return id;
438 }
439