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