1 /*-------------------------------------------------------------------------
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
11 * src/backend/access/rmgrdesc/xactdesc.c
13 *-------------------------------------------------------------------------
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
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.
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
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
);
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
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
);
217 ParsePrepareRecord(uint8 info
, xl_xact_prepare
*xlrec
, xl_xact_parsed_prepare
*parsed
)
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
));
252 xact_desc_relations(StringInfo buf
, char *label
, int nrels
,
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
);
271 xact_desc_subxacts(StringInfo buf
, int nsubxacts
, TransactionId
*subxacts
)
277 appendStringInfoString(buf
, "; subxacts:");
278 for (i
= 0; i
< nsubxacts
; i
++)
279 appendStringInfo(buf
, " %u", subxacts
[i
]);
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
,
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",
313 LSN_FORMAT_ARGS(parsed
.origin_lsn
),
314 timestamptz_to_str(parsed
.origin_timestamp
));
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",
338 LSN_FORMAT_ARGS(parsed
.origin_lsn
),
339 timestamptz_to_str(parsed
.origin_timestamp
));
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
,
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",
368 LSN_FORMAT_ARGS(parsed
.origin_lsn
),
369 timestamptz_to_str(parsed
.origin_timestamp
));
373 xact_desc_assignment(StringInfo buf
, xl_xact_assignment
*xlrec
)
377 appendStringInfoString(buf
, "subxacts:");
379 for (i
= 0; i
< xlrec
->nsubxacts
; i
++)
380 appendStringInfo(buf
, " %u", xlrec
->xsub
[i
]);
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
,
432 xact_identify(uint8 info
)
434 const char *id
= NULL
;
436 switch (info
& XLOG_XACT_OPMASK
)
438 case XLOG_XACT_COMMIT
:
441 case XLOG_XACT_PREPARE
:
444 case XLOG_XACT_ABORT
:
447 case XLOG_XACT_COMMIT_PREPARED
:
448 id
= "COMMIT_PREPARED";
450 case XLOG_XACT_ABORT_PREPARED
:
451 id
= "ABORT_PREPARED";
453 case XLOG_XACT_ASSIGNMENT
:
456 case XLOG_XACT_INVALIDATIONS
: