Age Owner TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * xactdesc.c
4 : * rmgr descriptor routines for access/transam/xact.c
5 : *
6 : * Portions Copyright (c) 1996-2023, 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 "replication/origin.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_waldump) 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
2947 andres 35 CBC 21513 : ParseCommitRecord(uint8 info, xl_xact_commit *xlrec, xl_xact_parsed_commit *parsed)
36 : {
37 21513 : char *data = ((char *) xlrec) + MinSizeOfXactCommit;
38 :
39 21513 : memset(parsed, 0, sizeof(*parsed));
40 :
2878 bruce 41 21513 : parsed->xinfo = 0; /* default, if no XLOG_XACT_HAS_INFO is
42 : * present */
43 :
2947 andres 44 21513 : parsed->xact_time = xlrec->xact_time;
45 :
46 21513 : if (info & XLOG_XACT_HAS_INFO)
47 : {
48 15160 : xl_xact_xinfo *xl_xinfo = (xl_xact_xinfo *) data;
49 :
50 15160 : parsed->xinfo = xl_xinfo->xinfo;
51 :
52 15160 : data += sizeof(xl_xact_xinfo);
53 : }
54 :
55 21513 : if (parsed->xinfo & XACT_XINFO_HAS_DBINFO)
56 : {
57 15029 : xl_xact_dbinfo *xl_dbinfo = (xl_xact_dbinfo *) data;
58 :
59 15029 : parsed->dbId = xl_dbinfo->dbId;
60 15029 : parsed->tsId = xl_dbinfo->tsId;
61 :
62 15029 : data += sizeof(xl_xact_dbinfo);
63 : }
64 :
65 21513 : if (parsed->xinfo & XACT_XINFO_HAS_SUBXACTS)
66 : {
2878 bruce 67 245 : xl_xact_subxacts *xl_subxacts = (xl_xact_subxacts *) data;
68 :
2947 andres 69 245 : parsed->nsubxacts = xl_subxacts->nsubxacts;
70 245 : parsed->subxacts = xl_subxacts->subxacts;
71 :
72 245 : data += MinSizeOfXactSubxacts;
73 245 : data += parsed->nsubxacts * sizeof(TransactionId);
74 : }
75 :
277 rhaas 76 GNC 21513 : if (parsed->xinfo & XACT_XINFO_HAS_RELFILELOCATORS)
77 : {
78 1812 : xl_xact_relfilelocators *xl_rellocators = (xl_xact_relfilelocators *) data;
79 :
80 1812 : parsed->nrels = xl_rellocators->nrels;
81 1812 : parsed->xlocators = xl_rellocators->xlocators;
82 :
83 1812 : data += MinSizeOfXactRelfileLocators;
84 1812 : data += xl_rellocators->nrels * sizeof(RelFileLocator);
85 : }
86 :
368 andres 87 CBC 21513 : if (parsed->xinfo & XACT_XINFO_HAS_DROPPED_STATS)
88 : {
89 2349 : xl_xact_stats_items *xl_drops = (xl_xact_stats_items *) data;
90 :
91 2349 : parsed->nstats = xl_drops->nitems;
92 2349 : parsed->stats = xl_drops->items;
93 :
94 2349 : data += MinSizeOfXactStatsItems;
95 2349 : data += xl_drops->nitems * sizeof(xl_xact_stats_item);
96 : }
97 :
2947 98 21513 : if (parsed->xinfo & XACT_XINFO_HAS_INVALS)
99 : {
100 13417 : xl_xact_invals *xl_invals = (xl_xact_invals *) data;
101 :
102 13417 : parsed->nmsgs = xl_invals->nmsgs;
103 13417 : parsed->msgs = xl_invals->msgs;
104 :
105 13417 : data += MinSizeOfXactInvals;
106 13417 : data += xl_invals->nmsgs * sizeof(SharedInvalidationMessage);
107 : }
108 :
109 21513 : if (parsed->xinfo & XACT_XINFO_HAS_TWOPHASE)
110 : {
111 156 : xl_xact_twophase *xl_twophase = (xl_xact_twophase *) data;
112 :
113 156 : parsed->twophase_xid = xl_twophase->xid;
114 :
115 156 : data += sizeof(xl_xact_twophase);
116 :
1838 simon 117 156 : if (parsed->xinfo & XACT_XINFO_HAS_GID)
118 : {
1833 tgl 119 93 : strlcpy(parsed->twophase_gid, data, sizeof(parsed->twophase_gid));
1818 heikki.linnakangas 120 93 : data += strlen(data) + 1;
121 : }
122 : }
123 :
124 : /* Note: no alignment is guaranteed after this point */
125 :
2902 andres 126 21513 : if (parsed->xinfo & XACT_XINFO_HAS_ORIGIN)
127 : {
128 : xl_xact_origin xl_origin;
129 :
130 : /* no alignment is guaranteed, so copy onto stack */
2900 131 72 : memcpy(&xl_origin, data, sizeof(xl_origin));
132 :
133 72 : parsed->origin_lsn = xl_origin.origin_lsn;
134 72 : parsed->origin_timestamp = xl_origin.origin_timestamp;
135 :
2902 136 72 : data += sizeof(xl_xact_origin);
137 : }
2947 138 21513 : }
139 :
140 : void
141 1552 : ParseAbortRecord(uint8 info, xl_xact_abort *xlrec, xl_xact_parsed_abort *parsed)
142 : {
143 1552 : char *data = ((char *) xlrec) + MinSizeOfXactAbort;
144 :
145 1552 : memset(parsed, 0, sizeof(*parsed));
146 :
2878 bruce 147 1552 : parsed->xinfo = 0; /* default, if no XLOG_XACT_HAS_INFO is
148 : * present */
149 :
2947 andres 150 1552 : parsed->xact_time = xlrec->xact_time;
151 :
152 1552 : if (info & XLOG_XACT_HAS_INFO)
153 : {
154 996 : xl_xact_xinfo *xl_xinfo = (xl_xact_xinfo *) data;
155 :
156 996 : parsed->xinfo = xl_xinfo->xinfo;
157 :
158 996 : data += sizeof(xl_xact_xinfo);
159 : }
160 :
1838 simon 161 1552 : if (parsed->xinfo & XACT_XINFO_HAS_DBINFO)
162 : {
163 42 : xl_xact_dbinfo *xl_dbinfo = (xl_xact_dbinfo *) data;
164 :
165 42 : parsed->dbId = xl_dbinfo->dbId;
166 42 : parsed->tsId = xl_dbinfo->tsId;
167 :
168 42 : data += sizeof(xl_xact_dbinfo);
169 : }
170 :
2947 andres 171 1552 : if (parsed->xinfo & XACT_XINFO_HAS_SUBXACTS)
172 : {
2878 bruce 173 17 : xl_xact_subxacts *xl_subxacts = (xl_xact_subxacts *) data;
174 :
2947 andres 175 17 : parsed->nsubxacts = xl_subxacts->nsubxacts;
176 17 : parsed->subxacts = xl_subxacts->subxacts;
177 :
178 17 : data += MinSizeOfXactSubxacts;
179 17 : data += parsed->nsubxacts * sizeof(TransactionId);
180 : }
181 :
277 rhaas 182 GNC 1552 : if (parsed->xinfo & XACT_XINFO_HAS_RELFILELOCATORS)
183 : {
184 237 : xl_xact_relfilelocators *xl_rellocator = (xl_xact_relfilelocators *) data;
185 :
186 237 : parsed->nrels = xl_rellocator->nrels;
187 237 : parsed->xlocators = xl_rellocator->xlocators;
188 :
189 237 : data += MinSizeOfXactRelfileLocators;
190 237 : data += xl_rellocator->nrels * sizeof(RelFileLocator);
191 : }
192 :
368 andres 193 CBC 1552 : if (parsed->xinfo & XACT_XINFO_HAS_DROPPED_STATS)
194 : {
195 301 : xl_xact_stats_items *xl_drops = (xl_xact_stats_items *) data;
196 :
197 301 : parsed->nstats = xl_drops->nitems;
198 301 : parsed->stats = xl_drops->items;
199 :
200 301 : data += MinSizeOfXactStatsItems;
201 301 : data += xl_drops->nitems * sizeof(xl_xact_stats_item);
202 : }
203 :
2947 204 1552 : if (parsed->xinfo & XACT_XINFO_HAS_TWOPHASE)
205 : {
206 69 : xl_xact_twophase *xl_twophase = (xl_xact_twophase *) data;
207 :
208 69 : parsed->twophase_xid = xl_twophase->xid;
209 :
210 69 : data += sizeof(xl_xact_twophase);
211 :
1838 simon 212 69 : if (parsed->xinfo & XACT_XINFO_HAS_GID)
213 : {
1833 tgl 214 42 : strlcpy(parsed->twophase_gid, data, sizeof(parsed->twophase_gid));
1818 heikki.linnakangas 215 42 : data += strlen(data) + 1;
216 : }
217 : }
218 :
219 : /* Note: no alignment is guaranteed after this point */
220 :
1838 simon 221 1552 : if (parsed->xinfo & XACT_XINFO_HAS_ORIGIN)
222 : {
223 : xl_xact_origin xl_origin;
224 :
225 : /* no alignment is guaranteed, so copy onto stack */
226 10 : memcpy(&xl_origin, data, sizeof(xl_origin));
227 :
228 10 : parsed->origin_lsn = xl_origin.origin_lsn;
229 10 : parsed->origin_timestamp = xl_origin.origin_timestamp;
230 :
231 10 : data += sizeof(xl_xact_origin);
232 : }
2947 andres 233 1552 : }
234 :
235 : /*
236 : * ParsePrepareRecord
237 : */
238 : void
1243 fujii 239 125 : ParsePrepareRecord(uint8 info, xl_xact_prepare *xlrec, xl_xact_parsed_prepare *parsed)
240 : {
241 : char *bufptr;
242 :
243 125 : bufptr = ((char *) xlrec) + MAXALIGN(sizeof(xl_xact_prepare));
244 :
245 125 : memset(parsed, 0, sizeof(*parsed));
246 :
247 125 : parsed->xact_time = xlrec->prepared_at;
248 125 : parsed->origin_lsn = xlrec->origin_lsn;
249 125 : parsed->origin_timestamp = xlrec->origin_timestamp;
250 125 : parsed->twophase_xid = xlrec->xid;
251 125 : parsed->dbId = xlrec->database;
252 125 : parsed->nsubxacts = xlrec->nsubxacts;
253 125 : parsed->nrels = xlrec->ncommitrels;
254 125 : parsed->nabortrels = xlrec->nabortrels;
255 125 : parsed->nmsgs = xlrec->ninvalmsgs;
256 :
257 125 : strncpy(parsed->twophase_gid, bufptr, xlrec->gidlen);
258 125 : bufptr += MAXALIGN(xlrec->gidlen);
259 :
260 125 : parsed->subxacts = (TransactionId *) bufptr;
261 125 : bufptr += MAXALIGN(xlrec->nsubxacts * sizeof(TransactionId));
262 :
277 rhaas 263 GNC 125 : parsed->xlocators = (RelFileLocator *) bufptr;
264 125 : bufptr += MAXALIGN(xlrec->ncommitrels * sizeof(RelFileLocator));
265 :
266 125 : parsed->abortlocators = (RelFileLocator *) bufptr;
267 125 : bufptr += MAXALIGN(xlrec->nabortrels * sizeof(RelFileLocator));
268 :
368 andres 269 CBC 125 : parsed->stats = (xl_xact_stats_item *) bufptr;
270 125 : bufptr += MAXALIGN(xlrec->ncommitstats * sizeof(xl_xact_stats_item));
271 :
272 125 : parsed->abortstats = (xl_xact_stats_item *) bufptr;
273 125 : bufptr += MAXALIGN(xlrec->nabortstats * sizeof(xl_xact_stats_item));
274 :
1243 fujii 275 125 : parsed->msgs = (SharedInvalidationMessage *) bufptr;
276 125 : bufptr += MAXALIGN(xlrec->ninvalmsgs * sizeof(SharedInvalidationMessage));
277 125 : }
278 :
279 : static void
280 28 : xact_desc_relations(StringInfo buf, char *label, int nrels,
281 : RelFileLocator *xlocators)
282 : {
283 : int i;
284 :
285 28 : if (nrels > 0)
286 : {
287 5 : appendStringInfo(buf, "; %s:", label);
288 22 : for (i = 0; i < nrels; i++)
289 : {
277 rhaas 290 GNC 17 : char *path = relpathperm(xlocators[i], MAIN_FORKNUM);
291 :
3784 alvherre 292 CBC 17 : appendStringInfo(buf, " %s", path);
293 17 : pfree(path);
294 : }
295 : }
1243 fujii 296 28 : }
297 :
298 : static void
299 28 : xact_desc_subxacts(StringInfo buf, int nsubxacts, TransactionId *subxacts)
300 : {
301 : int i;
302 :
303 28 : if (nsubxacts > 0)
304 : {
3447 rhaas 305 UBC 0 : appendStringInfoString(buf, "; subxacts:");
1243 fujii 306 0 : for (i = 0; i < nsubxacts; i++)
307 0 : appendStringInfo(buf, " %u", subxacts[i]);
308 : }
1243 fujii 309 CBC 28 : }
310 :
311 : static void
368 andres 312 28 : xact_desc_stats(StringInfo buf, const char *label,
313 : int ndropped, xl_xact_stats_item *dropped_stats)
314 : {
315 : int i;
316 :
317 28 : if (ndropped > 0)
318 : {
319 9 : appendStringInfo(buf, "; %sdropped stats:", label);
320 25 : for (i = 0; i < ndropped; i++)
321 : {
340 peter 322 16 : appendStringInfo(buf, " %d/%u/%u",
368 andres 323 16 : dropped_stats[i].kind,
324 16 : dropped_stats[i].dboid,
325 16 : dropped_stats[i].objoid);
326 : }
327 : }
328 28 : }
329 :
330 : static void
1243 fujii 331 28 : xact_desc_commit(StringInfo buf, uint8 info, xl_xact_commit *xlrec, RepOriginId origin_id)
332 : {
333 : xl_xact_parsed_commit parsed;
334 :
335 28 : ParseCommitRecord(info, xlrec, &parsed);
336 :
337 : /* If this is a prepared xact, show the xid of the original xact */
338 28 : if (TransactionIdIsValid(parsed.twophase_xid))
1243 fujii 339 UBC 0 : appendStringInfo(buf, "%u: ", parsed.twophase_xid);
340 :
1243 fujii 341 CBC 28 : appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time));
342 :
277 rhaas 343 GNC 28 : xact_desc_relations(buf, "rels", parsed.nrels, parsed.xlocators);
1243 fujii 344 CBC 28 : xact_desc_subxacts(buf, parsed.nsubxacts, parsed.subxacts);
368 andres 345 28 : xact_desc_stats(buf, "", parsed.nstats, parsed.stats);
346 :
1165 alvherre 347 28 : standby_desc_invalidations(buf, parsed.nmsgs, parsed.msgs, parsed.dbId,
348 : parsed.tsId,
349 28 : XactCompletionRelcacheInitFileInval(parsed.xinfo));
350 :
482 michael 351 28 : if (XactCompletionApplyFeedback(parsed.xinfo))
482 michael 352 UBC 0 : appendStringInfoString(buf, "; apply_feedback");
353 :
2947 andres 354 CBC 28 : if (XactCompletionForceSyncCommit(parsed.xinfo))
2838 heikki.linnakangas 355 14 : appendStringInfoString(buf, "; sync");
356 :
2902 andres 357 28 : if (parsed.xinfo & XACT_XINFO_HAS_ORIGIN)
358 : {
2902 andres 359 UBC 0 : appendStringInfo(buf, "; origin: node %u, lsn %X/%X, at %s",
360 : origin_id,
775 peter 361 0 : LSN_FORMAT_ARGS(parsed.origin_lsn),
362 : timestamptz_to_str(parsed.origin_timestamp));
363 : }
3784 alvherre 364 CBC 28 : }
365 :
366 : static void
482 michael 367 UBC 0 : xact_desc_abort(StringInfo buf, uint8 info, xl_xact_abort *xlrec, RepOriginId origin_id)
368 : {
369 : xl_xact_parsed_abort parsed;
370 :
2947 andres 371 0 : ParseAbortRecord(info, xlrec, &parsed);
372 :
373 : /* If this is a prepared xact, show the xid of the original xact */
374 0 : if (TransactionIdIsValid(parsed.twophase_xid))
375 0 : appendStringInfo(buf, "%u: ", parsed.twophase_xid);
376 :
3784 alvherre 377 0 : appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time));
378 :
277 rhaas 379 UNC 0 : xact_desc_relations(buf, "rels", parsed.nrels, parsed.xlocators);
1243 fujii 380 UBC 0 : xact_desc_subxacts(buf, parsed.nsubxacts, parsed.subxacts);
381 :
482 michael 382 0 : if (parsed.xinfo & XACT_XINFO_HAS_ORIGIN)
383 : {
384 0 : appendStringInfo(buf, "; origin: node %u, lsn %X/%X, at %s",
385 : origin_id,
386 0 : LSN_FORMAT_ARGS(parsed.origin_lsn),
387 : timestamptz_to_str(parsed.origin_timestamp));
388 : }
389 :
368 andres 390 0 : xact_desc_stats(buf, "", parsed.nstats, parsed.stats);
1243 fujii 391 0 : }
392 :
393 : static void
482 michael 394 0 : xact_desc_prepare(StringInfo buf, uint8 info, xl_xact_prepare *xlrec, RepOriginId origin_id)
395 : {
396 : xl_xact_parsed_prepare parsed;
397 :
1243 fujii 398 0 : ParsePrepareRecord(info, xlrec, &parsed);
399 :
400 0 : appendStringInfo(buf, "gid %s: ", parsed.twophase_gid);
401 0 : appendStringInfoString(buf, timestamptz_to_str(parsed.xact_time));
402 :
277 rhaas 403 UNC 0 : xact_desc_relations(buf, "rels(commit)", parsed.nrels, parsed.xlocators);
1243 fujii 404 UBC 0 : xact_desc_relations(buf, "rels(abort)", parsed.nabortrels,
405 : parsed.abortlocators);
368 andres 406 0 : xact_desc_stats(buf, "commit ", parsed.nstats, parsed.stats);
407 0 : xact_desc_stats(buf, "abort ", parsed.nabortstats, parsed.abortstats);
1243 fujii 408 0 : xact_desc_subxacts(buf, parsed.nsubxacts, parsed.subxacts);
409 :
1165 alvherre 410 0 : standby_desc_invalidations(buf, parsed.nmsgs, parsed.msgs, parsed.dbId,
411 0 : parsed.tsId, xlrec->initfileinval);
412 :
413 : /*
414 : * Check if the replication origin has been set in this record in the same
415 : * way as PrepareRedoAdd().
416 : */
482 michael 417 0 : if (origin_id != InvalidRepOriginId)
418 0 : appendStringInfo(buf, "; origin: node %u, lsn %X/%X, at %s",
419 : origin_id,
420 0 : LSN_FORMAT_ARGS(parsed.origin_lsn),
421 : timestamptz_to_str(parsed.origin_timestamp));
3784 alvherre 422 0 : }
423 :
424 : static void
425 0 : xact_desc_assignment(StringInfo buf, xl_xact_assignment *xlrec)
426 : {
427 : int i;
428 :
3447 rhaas 429 0 : appendStringInfoString(buf, "subxacts:");
430 :
3784 alvherre 431 0 : for (i = 0; i < xlrec->nsubxacts; i++)
432 0 : appendStringInfo(buf, " %u", xlrec->xsub[i]);
433 0 : }
434 :
435 : void
3062 heikki.linnakangas 436 CBC 28 : xact_desc(StringInfo buf, XLogReaderState *record)
437 : {
3221 438 28 : char *rec = XLogRecGetData(record);
2947 andres 439 28 : uint8 info = XLogRecGetInfo(record) & XLOG_XACT_OPMASK;
440 :
441 28 : if (info == XLOG_XACT_COMMIT || info == XLOG_XACT_COMMIT_PREPARED)
3784 alvherre 442 28 : {
443 28 : xl_xact_commit *xlrec = (xl_xact_commit *) rec;
444 :
2902 andres 445 28 : xact_desc_commit(buf, XLogRecGetInfo(record), xlrec,
446 28 : XLogRecGetOrigin(record));
447 : }
2947 andres 448 UBC 0 : else if (info == XLOG_XACT_ABORT || info == XLOG_XACT_ABORT_PREPARED)
3784 alvherre 449 0 : {
450 0 : xl_xact_abort *xlrec = (xl_xact_abort *) rec;
451 :
482 michael 452 0 : xact_desc_abort(buf, XLogRecGetInfo(record), xlrec,
453 0 : XLogRecGetOrigin(record));
454 : }
1243 fujii 455 0 : else if (info == XLOG_XACT_PREPARE)
456 : {
457 0 : xl_xact_prepare *xlrec = (xl_xact_prepare *) rec;
458 :
482 michael 459 0 : xact_desc_prepare(buf, XLogRecGetInfo(record), xlrec,
460 0 : XLogRecGetOrigin(record));
461 : }
3784 alvherre 462 0 : else if (info == XLOG_XACT_ASSIGNMENT)
463 : {
464 0 : xl_xact_assignment *xlrec = (xl_xact_assignment *) rec;
465 :
466 : /*
467 : * Note that we ignore the WAL record's xid, since we're more
468 : * interested in the top-level xid that issued the record and which
469 : * xids are being reported here.
470 : */
3124 andres 471 0 : appendStringInfo(buf, "xtop %u: ", xlrec->xtop);
3784 alvherre 472 0 : xact_desc_assignment(buf, xlrec);
473 : }
990 akapila 474 0 : else if (info == XLOG_XACT_INVALIDATIONS)
475 : {
476 0 : xl_xact_invals *xlrec = (xl_xact_invals *) rec;
477 :
478 0 : standby_desc_invalidations(buf, xlrec->nmsgs, xlrec->msgs, InvalidOid,
479 : InvalidOid, false);
480 : }
3124 andres 481 CBC 28 : }
482 :
483 : const char *
484 28 : xact_identify(uint8 info)
485 : {
486 28 : const char *id = NULL;
487 :
2947 488 28 : switch (info & XLOG_XACT_OPMASK)
489 : {
3124 490 28 : case XLOG_XACT_COMMIT:
491 28 : id = "COMMIT";
492 28 : break;
3124 andres 493 UBC 0 : case XLOG_XACT_PREPARE:
494 0 : id = "PREPARE";
495 0 : break;
496 0 : case XLOG_XACT_ABORT:
497 0 : id = "ABORT";
498 0 : break;
499 0 : case XLOG_XACT_COMMIT_PREPARED:
500 0 : id = "COMMIT_PREPARED";
501 0 : break;
502 0 : case XLOG_XACT_ABORT_PREPARED:
503 0 : id = "ABORT_PREPARED";
504 0 : break;
505 0 : case XLOG_XACT_ASSIGNMENT:
506 0 : id = "ASSIGNMENT";
507 0 : break;
990 akapila 508 0 : case XLOG_XACT_INVALIDATIONS:
509 0 : id = "INVALIDATION";
510 0 : break;
511 : }
512 :
3124 andres 513 CBC 28 : return id;
514 : }
|