/* * Handle rmgr XACT_ID records for DecodeRecordIntoReorderBuffer(). */ static void DecodeXactOp(LogicalDecodingContext *ctx, XLogRecordBuffer *buf) { SnapBuild *builder = ctx->snapshot_builder; ReorderBuffer *reorder = ctx->reorder; XLogReaderState *r = buf->record; uint8 info = XLogRecGetInfo(r) & XLOG_XACT_OPMASK; /* no point in doing anything yet, data could not be decoded anyway */ if (SnapBuildCurrentState(builder) < SNAPBUILD_FULL_SNAPSHOT) return; switch (info) { case XLOG_XACT_COMMIT: case XLOG_XACT_COMMIT_PREPARED: { xl_xact_commit *xlrec; xl_xact_parsed_commit parsed; TransactionId xid; xlrec = (xl_xact_commit *) XLogRecGetData(r); ParseCommitRecord(XLogRecGetInfo(buf->record), xlrec, &parsed); if (!TransactionIdIsValid(parsed.twophase_xid)) xid = XLogRecGetXid(r); else xid = parsed.twophase_xid; DecodeCommit(ctx, buf, &parsed, xid); break; } case XLOG_XACT_ABORT: case XLOG_XACT_ABORT_PREPARED: { xl_xact_abort *xlrec; xl_xact_parsed_abort parsed; TransactionId xid; xlrec = (xl_xact_abort *) XLogRecGetData(r); ParseAbortRecord(XLogRecGetInfo(buf->record), xlrec, &parsed); if (!TransactionIdIsValid(parsed.twophase_xid)) xid = XLogRecGetXid(r); else xid = parsed.twophase_xid; DecodeAbort(ctx, buf, &parsed, xid); break; } case XLOG_XACT_ASSIGNMENT: { xl_xact_assignment *xlrec; int i; TransactionId *sub_xid; xlrec = (xl_xact_assignment *) XLogRecGetData(r); sub_xid = &xlrec->xsub[0]; for (i = 0; i < xlrec->nsubxacts; i++) { ReorderBufferAssignChild(reorder, xlrec->xtop, *(sub_xid++), buf->origptr); } break; } case XLOG_XACT_PREPARE: /* * Currently decoding ignores PREPARE TRANSACTION and will just * decode the transaction when the COMMIT PREPARED is sent or * throw away the transaction's contents when a ROLLBACK PREPARED * is received. In the future we could add code to expose prepared * transactions in the changestream allowing for a kind of * distributed 2PC. */ break; default: elog(ERROR, "unexpected RM_XACT_ID record type: %u", info); } }
static void xact_desc_commit(StringInfo buf, uint8 info, xl_xact_commit *xlrec, RepOriginId origin_id) { xl_xact_parsed_commit parsed; int i; ParseCommitRecord(info, xlrec, &parsed); /* If this is a prepared xact, show the xid of the original xact */ if (TransactionIdIsValid(parsed.twophase_xid)) appendStringInfo(buf, "%u: ", parsed.twophase_xid); appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time)); if (parsed.nrels > 0) { appendStringInfoString(buf, "; rels:"); for (i = 0; i < parsed.nrels; i++) { char *path = relpathperm(parsed.xnodes[i], MAIN_FORKNUM); appendStringInfo(buf, " %s", path); pfree(path); } } if (parsed.nsubxacts > 0) { appendStringInfoString(buf, "; subxacts:"); for (i = 0; i < parsed.nsubxacts; i++) appendStringInfo(buf, " %u", parsed.subxacts[i]); } if (parsed.nmsgs > 0) { if (XactCompletionRelcacheInitFileInval(parsed.xinfo)) appendStringInfo(buf, "; relcache init file inval dbid %u tsid %u", parsed.dbId, parsed.tsId); appendStringInfoString(buf, "; inval msgs:"); for (i = 0; i < parsed.nmsgs; i++) { SharedInvalidationMessage *msg = &parsed.msgs[i]; if (msg->id >= 0) appendStringInfo(buf, " catcache %d", msg->id); else if (msg->id == SHAREDINVALCATALOG_ID) appendStringInfo(buf, " catalog %u", msg->cat.catId); else if (msg->id == SHAREDINVALRELCACHE_ID) appendStringInfo(buf, " relcache %u", msg->rc.relId); /* not expected, but print something anyway */ else if (msg->id == SHAREDINVALSMGR_ID) appendStringInfoString(buf, " smgr"); /* not expected, but print something anyway */ else if (msg->id == SHAREDINVALRELMAP_ID) appendStringInfoString(buf, " relmap"); else if (msg->id == SHAREDINVALSNAPSHOT_ID) appendStringInfo(buf, " snapshot %u", msg->sn.relId); else appendStringInfo(buf, " unknown id %d", msg->id); } } if (XactCompletionForceSyncCommit(parsed.xinfo)) appendStringInfo(buf, "; sync"); if (parsed.xinfo & XACT_XINFO_HAS_ORIGIN) { appendStringInfo(buf, "; origin: node %u, lsn %X/%X, at %s", origin_id, (uint32)(parsed.origin_lsn >> 32), (uint32)parsed.origin_lsn, timestamptz_to_str(parsed.origin_timestamp)); }