Update copyright for 2022
[pgsql.git] / src / backend / access / rmgrdesc / xactdesc.c
blob025d556f6ce9103269e376364db1eaafd7e6f6eb
1 /*-------------------------------------------------------------------------
3 * xactdesc.c
4 * rmgr descriptor routines for access/transam/xact.c
6 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
10 * IDENTIFICATION
11 * src/backend/access/rmgrdesc/xactdesc.c
13 *-------------------------------------------------------------------------
15 #include "postgres.h"
17 #include "access/transam.h"
18 #include "access/xact.h"
19 #include "replication/origin.h"
20 #include "storage/sinval.h"
21 #include "storage/standbydefs.h"
22 #include "utils/timestamp.h"
25 * Parse the WAL format of an xact commit and abort records into an easier to
26 * understand format.
28 * This routines are in xactdesc.c because they're accessed in backend (when
29 * replaying WAL) and frontend (pg_waldump) code. This file is the only xact
30 * specific one shared between both. They're complicated enough that
31 * duplication would be bothersome.
34 void
35 ParseCommitRecord(uint8 info, xl_xact_commit *xlrec, xl_xact_parsed_commit *parsed)
37 char *data = ((char *) xlrec) + MinSizeOfXactCommit;
39 memset(parsed, 0, sizeof(*parsed));
41 parsed->xinfo = 0; /* default, if no XLOG_XACT_HAS_INFO is
42 * present */
44 parsed->xact_time = xlrec->xact_time;
46 if (info & XLOG_XACT_HAS_INFO)
48 xl_xact_xinfo *xl_xinfo = (xl_xact_xinfo *) data;
50 parsed->xinfo = xl_xinfo->xinfo;
52 data += sizeof(xl_xact_xinfo);
55 if (parsed->xinfo & XACT_XINFO_HAS_DBINFO)
57 xl_xact_dbinfo *xl_dbinfo = (xl_xact_dbinfo *) data;
59 parsed->dbId = xl_dbinfo->dbId;
60 parsed->tsId = xl_dbinfo->tsId;
62 data += sizeof(xl_xact_dbinfo);
65 if (parsed->xinfo & XACT_XINFO_HAS_SUBXACTS)
67 xl_xact_subxacts *xl_subxacts = (xl_xact_subxacts *) data;
69 parsed->nsubxacts = xl_subxacts->nsubxacts;
70 parsed->subxacts = xl_subxacts->subxacts;
72 data += MinSizeOfXactSubxacts;
73 data += parsed->nsubxacts * sizeof(TransactionId);
76 if (parsed->xinfo & XACT_XINFO_HAS_RELFILENODES)
78 xl_xact_relfilenodes *xl_relfilenodes = (xl_xact_relfilenodes *) data;
80 parsed->nrels = xl_relfilenodes->nrels;
81 parsed->xnodes = xl_relfilenodes->xnodes;
83 data += MinSizeOfXactRelfilenodes;
84 data += xl_relfilenodes->nrels * sizeof(RelFileNode);
87 if (parsed->xinfo & XACT_XINFO_HAS_INVALS)
89 xl_xact_invals *xl_invals = (xl_xact_invals *) data;
91 parsed->nmsgs = xl_invals->nmsgs;
92 parsed->msgs = xl_invals->msgs;
94 data += MinSizeOfXactInvals;
95 data += xl_invals->nmsgs * sizeof(SharedInvalidationMessage);
98 if (parsed->xinfo & XACT_XINFO_HAS_TWOPHASE)
100 xl_xact_twophase *xl_twophase = (xl_xact_twophase *) data;
102 parsed->twophase_xid = xl_twophase->xid;
104 data += sizeof(xl_xact_twophase);
106 if (parsed->xinfo & XACT_XINFO_HAS_GID)
108 strlcpy(parsed->twophase_gid, data, sizeof(parsed->twophase_gid));
109 data += strlen(data) + 1;
113 /* Note: no alignment is guaranteed after this point */
115 if (parsed->xinfo & XACT_XINFO_HAS_ORIGIN)
117 xl_xact_origin xl_origin;
119 /* no alignment is guaranteed, so copy onto stack */
120 memcpy(&xl_origin, data, sizeof(xl_origin));
122 parsed->origin_lsn = xl_origin.origin_lsn;
123 parsed->origin_timestamp = xl_origin.origin_timestamp;
125 data += sizeof(xl_xact_origin);
129 void
130 ParseAbortRecord(uint8 info, xl_xact_abort *xlrec, xl_xact_parsed_abort *parsed)
132 char *data = ((char *) xlrec) + MinSizeOfXactAbort;
134 memset(parsed, 0, sizeof(*parsed));
136 parsed->xinfo = 0; /* default, if no XLOG_XACT_HAS_INFO is
137 * present */
139 parsed->xact_time = xlrec->xact_time;
141 if (info & XLOG_XACT_HAS_INFO)
143 xl_xact_xinfo *xl_xinfo = (xl_xact_xinfo *) data;
145 parsed->xinfo = xl_xinfo->xinfo;
147 data += sizeof(xl_xact_xinfo);
150 if (parsed->xinfo & XACT_XINFO_HAS_DBINFO)
152 xl_xact_dbinfo *xl_dbinfo = (xl_xact_dbinfo *) data;
154 parsed->dbId = xl_dbinfo->dbId;
155 parsed->tsId = xl_dbinfo->tsId;
157 data += sizeof(xl_xact_dbinfo);
160 if (parsed->xinfo & XACT_XINFO_HAS_SUBXACTS)
162 xl_xact_subxacts *xl_subxacts = (xl_xact_subxacts *) data;
164 parsed->nsubxacts = xl_subxacts->nsubxacts;
165 parsed->subxacts = xl_subxacts->subxacts;
167 data += MinSizeOfXactSubxacts;
168 data += parsed->nsubxacts * sizeof(TransactionId);
171 if (parsed->xinfo & XACT_XINFO_HAS_RELFILENODES)
173 xl_xact_relfilenodes *xl_relfilenodes = (xl_xact_relfilenodes *) data;
175 parsed->nrels = xl_relfilenodes->nrels;
176 parsed->xnodes = xl_relfilenodes->xnodes;
178 data += MinSizeOfXactRelfilenodes;
179 data += xl_relfilenodes->nrels * sizeof(RelFileNode);
182 if (parsed->xinfo & XACT_XINFO_HAS_TWOPHASE)
184 xl_xact_twophase *xl_twophase = (xl_xact_twophase *) data;
186 parsed->twophase_xid = xl_twophase->xid;
188 data += sizeof(xl_xact_twophase);
190 if (parsed->xinfo & XACT_XINFO_HAS_GID)
192 strlcpy(parsed->twophase_gid, data, sizeof(parsed->twophase_gid));
193 data += strlen(data) + 1;
197 /* Note: no alignment is guaranteed after this point */
199 if (parsed->xinfo & XACT_XINFO_HAS_ORIGIN)
201 xl_xact_origin xl_origin;
203 /* no alignment is guaranteed, so copy onto stack */
204 memcpy(&xl_origin, data, sizeof(xl_origin));
206 parsed->origin_lsn = xl_origin.origin_lsn;
207 parsed->origin_timestamp = xl_origin.origin_timestamp;
209 data += sizeof(xl_xact_origin);
214 * ParsePrepareRecord
216 void
217 ParsePrepareRecord(uint8 info, xl_xact_prepare *xlrec, xl_xact_parsed_prepare *parsed)
219 char *bufptr;
221 bufptr = ((char *) xlrec) + MAXALIGN(sizeof(xl_xact_prepare));
223 memset(parsed, 0, sizeof(*parsed));
225 parsed->xact_time = xlrec->prepared_at;
226 parsed->origin_lsn = xlrec->origin_lsn;
227 parsed->origin_timestamp = xlrec->origin_timestamp;
228 parsed->twophase_xid = xlrec->xid;
229 parsed->dbId = xlrec->database;
230 parsed->nsubxacts = xlrec->nsubxacts;
231 parsed->nrels = xlrec->ncommitrels;
232 parsed->nabortrels = xlrec->nabortrels;
233 parsed->nmsgs = xlrec->ninvalmsgs;
235 strncpy(parsed->twophase_gid, bufptr, xlrec->gidlen);
236 bufptr += MAXALIGN(xlrec->gidlen);
238 parsed->subxacts = (TransactionId *) bufptr;
239 bufptr += MAXALIGN(xlrec->nsubxacts * sizeof(TransactionId));
241 parsed->xnodes = (RelFileNode *) bufptr;
242 bufptr += MAXALIGN(xlrec->ncommitrels * sizeof(RelFileNode));
244 parsed->abortnodes = (RelFileNode *) bufptr;
245 bufptr += MAXALIGN(xlrec->nabortrels * sizeof(RelFileNode));
247 parsed->msgs = (SharedInvalidationMessage *) bufptr;
248 bufptr += MAXALIGN(xlrec->ninvalmsgs * sizeof(SharedInvalidationMessage));
251 static void
252 xact_desc_relations(StringInfo buf, char *label, int nrels,
253 RelFileNode *xnodes)
255 int i;
257 if (nrels > 0)
259 appendStringInfo(buf, "; %s:", label);
260 for (i = 0; i < nrels; i++)
262 char *path = relpathperm(xnodes[i], MAIN_FORKNUM);
264 appendStringInfo(buf, " %s", path);
265 pfree(path);
270 static void
271 xact_desc_subxacts(StringInfo buf, int nsubxacts, TransactionId *subxacts)
273 int i;
275 if (nsubxacts > 0)
277 appendStringInfoString(buf, "; subxacts:");
278 for (i = 0; i < nsubxacts; i++)
279 appendStringInfo(buf, " %u", subxacts[i]);
283 static void
284 xact_desc_commit(StringInfo buf, uint8 info, xl_xact_commit *xlrec, RepOriginId origin_id)
286 xl_xact_parsed_commit parsed;
288 ParseCommitRecord(info, xlrec, &parsed);
290 /* If this is a prepared xact, show the xid of the original xact */
291 if (TransactionIdIsValid(parsed.twophase_xid))
292 appendStringInfo(buf, "%u: ", parsed.twophase_xid);
294 appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time));
296 xact_desc_relations(buf, "rels", parsed.nrels, parsed.xnodes);
297 xact_desc_subxacts(buf, parsed.nsubxacts, parsed.subxacts);
299 standby_desc_invalidations(buf, parsed.nmsgs, parsed.msgs, parsed.dbId,
300 parsed.tsId,
301 XactCompletionRelcacheInitFileInval(parsed.xinfo));
303 if (XactCompletionApplyFeedback(parsed.xinfo))
304 appendStringInfoString(buf, "; apply_feedback");
306 if (XactCompletionForceSyncCommit(parsed.xinfo))
307 appendStringInfoString(buf, "; sync");
309 if (parsed.xinfo & XACT_XINFO_HAS_ORIGIN)
311 appendStringInfo(buf, "; origin: node %u, lsn %X/%X, at %s",
312 origin_id,
313 LSN_FORMAT_ARGS(parsed.origin_lsn),
314 timestamptz_to_str(parsed.origin_timestamp));
318 static void
319 xact_desc_abort(StringInfo buf, uint8 info, xl_xact_abort *xlrec, RepOriginId origin_id)
321 xl_xact_parsed_abort parsed;
323 ParseAbortRecord(info, xlrec, &parsed);
325 /* If this is a prepared xact, show the xid of the original xact */
326 if (TransactionIdIsValid(parsed.twophase_xid))
327 appendStringInfo(buf, "%u: ", parsed.twophase_xid);
329 appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time));
331 xact_desc_relations(buf, "rels", parsed.nrels, parsed.xnodes);
332 xact_desc_subxacts(buf, parsed.nsubxacts, parsed.subxacts);
334 if (parsed.xinfo & XACT_XINFO_HAS_ORIGIN)
336 appendStringInfo(buf, "; origin: node %u, lsn %X/%X, at %s",
337 origin_id,
338 LSN_FORMAT_ARGS(parsed.origin_lsn),
339 timestamptz_to_str(parsed.origin_timestamp));
343 static void
344 xact_desc_prepare(StringInfo buf, uint8 info, xl_xact_prepare *xlrec, RepOriginId origin_id)
346 xl_xact_parsed_prepare parsed;
348 ParsePrepareRecord(info, xlrec, &parsed);
350 appendStringInfo(buf, "gid %s: ", parsed.twophase_gid);
351 appendStringInfoString(buf, timestamptz_to_str(parsed.xact_time));
353 xact_desc_relations(buf, "rels(commit)", parsed.nrels, parsed.xnodes);
354 xact_desc_relations(buf, "rels(abort)", parsed.nabortrels,
355 parsed.abortnodes);
356 xact_desc_subxacts(buf, parsed.nsubxacts, parsed.subxacts);
358 standby_desc_invalidations(buf, parsed.nmsgs, parsed.msgs, parsed.dbId,
359 parsed.tsId, xlrec->initfileinval);
362 * Check if the replication origin has been set in this record in the
363 * same way as PrepareRedoAdd().
365 if (origin_id != InvalidRepOriginId)
366 appendStringInfo(buf, "; origin: node %u, lsn %X/%X, at %s",
367 origin_id,
368 LSN_FORMAT_ARGS(parsed.origin_lsn),
369 timestamptz_to_str(parsed.origin_timestamp));
372 static void
373 xact_desc_assignment(StringInfo buf, xl_xact_assignment *xlrec)
375 int i;
377 appendStringInfoString(buf, "subxacts:");
379 for (i = 0; i < xlrec->nsubxacts; i++)
380 appendStringInfo(buf, " %u", xlrec->xsub[i]);
383 void
384 xact_desc(StringInfo buf, XLogReaderState *record)
386 char *rec = XLogRecGetData(record);
387 uint8 info = XLogRecGetInfo(record) & XLOG_XACT_OPMASK;
389 if (info == XLOG_XACT_COMMIT || info == XLOG_XACT_COMMIT_PREPARED)
391 xl_xact_commit *xlrec = (xl_xact_commit *) rec;
393 xact_desc_commit(buf, XLogRecGetInfo(record), xlrec,
394 XLogRecGetOrigin(record));
396 else if (info == XLOG_XACT_ABORT || info == XLOG_XACT_ABORT_PREPARED)
398 xl_xact_abort *xlrec = (xl_xact_abort *) rec;
400 xact_desc_abort(buf, XLogRecGetInfo(record), xlrec,
401 XLogRecGetOrigin(record));
403 else if (info == XLOG_XACT_PREPARE)
405 xl_xact_prepare *xlrec = (xl_xact_prepare *) rec;
407 xact_desc_prepare(buf, XLogRecGetInfo(record), xlrec,
408 XLogRecGetOrigin(record));
410 else if (info == XLOG_XACT_ASSIGNMENT)
412 xl_xact_assignment *xlrec = (xl_xact_assignment *) rec;
415 * Note that we ignore the WAL record's xid, since we're more
416 * interested in the top-level xid that issued the record and which
417 * xids are being reported here.
419 appendStringInfo(buf, "xtop %u: ", xlrec->xtop);
420 xact_desc_assignment(buf, xlrec);
422 else if (info == XLOG_XACT_INVALIDATIONS)
424 xl_xact_invals *xlrec = (xl_xact_invals *) rec;
426 standby_desc_invalidations(buf, xlrec->nmsgs, xlrec->msgs, InvalidOid,
427 InvalidOid, false);
431 const char *
432 xact_identify(uint8 info)
434 const char *id = NULL;
436 switch (info & XLOG_XACT_OPMASK)
438 case XLOG_XACT_COMMIT:
439 id = "COMMIT";
440 break;
441 case XLOG_XACT_PREPARE:
442 id = "PREPARE";
443 break;
444 case XLOG_XACT_ABORT:
445 id = "ABORT";
446 break;
447 case XLOG_XACT_COMMIT_PREPARED:
448 id = "COMMIT_PREPARED";
449 break;
450 case XLOG_XACT_ABORT_PREPARED:
451 id = "ABORT_PREPARED";
452 break;
453 case XLOG_XACT_ASSIGNMENT:
454 id = "ASSIGNMENT";
455 break;
456 case XLOG_XACT_INVALIDATIONS:
457 id = "INVALIDATION";
458 break;
461 return id;