*** a/src/backend/access/rmgrdesc/xactdesc.c --- b/src/backend/access/rmgrdesc/xactdesc.c *************** *** 209,214 **** ParseAbortRecord(uint8 info, xl_xact_abort *xlrec, xl_xact_parsed_abort *parsed) --- 209,249 ---- } } + /* + * ParsePrepareRecord + */ + void + ParsePrepareRecord(uint8 info, xl_xact_prepare *xlrec, xl_xact_parsed_prepare *parsed) + { + char *bufptr; + + bufptr = (char *) xlrec + MAXALIGN(sizeof(xl_xact_prepare)); + + parsed->origin_lsn = xlrec->origin_lsn; + parsed->origin_timestamp = xlrec->origin_timestamp; + parsed->twophase_xid = xlrec->xid; + parsed->dbId = xlrec->database; + parsed->nsubxacts = xlrec->nsubxacts; + parsed->nrels = xlrec->ncommitrels; + parsed->nabortrels = xlrec->nabortrels; + parsed->nmsgs = xlrec->ninvalmsgs; + + strncpy(parsed->twophase_gid, bufptr, xlrec->gidlen); + bufptr += MAXALIGN(xlrec->gidlen); + + parsed->subxacts = (TransactionId *) bufptr; + bufptr += MAXALIGN(xlrec->nsubxacts * sizeof(TransactionId)); + + parsed->xnodes = (RelFileNode *) bufptr; + bufptr += MAXALIGN(xlrec->ncommitrels * sizeof(RelFileNode)); + + parsed->abortnodes = (RelFileNode *) bufptr; + bufptr += MAXALIGN(xlrec->nabortrels * sizeof(RelFileNode)); + + parsed->msgs = (SharedInvalidationMessage *) bufptr; + bufptr += MAXALIGN(xlrec->ninvalmsgs * sizeof(SharedInvalidationMessage)); + } + static void xact_desc_commit(StringInfo buf, uint8 info, xl_xact_commit *xlrec, RepOriginId origin_id) { *************** *** 293,298 **** xact_desc_abort(StringInfo buf, uint8 info, xl_xact_abort *xlrec) --- 328,344 ---- } } + static void + xact_desc_prepare(StringInfo buf, uint8 info, xl_xact_prepare *xlrec) + { + xl_xact_parsed_prepare parsed; + + ParsePrepareRecord(info, xlrec, &parsed); + + appendStringInfo(buf, "gid %s: ", parsed.twophase_gid); + appendStringInfoString(buf, timestamptz_to_str(parsed.xact_time)); + } + static void xact_desc_assignment(StringInfo buf, xl_xact_assignment *xlrec) { *************** *** 323,328 **** xact_desc(StringInfo buf, XLogReaderState *record) --- 369,380 ---- xact_desc_abort(buf, XLogRecGetInfo(record), xlrec); } + else if (info == XLOG_XACT_PREPARE) + { + xl_xact_prepare *xlrec = (xl_xact_prepare *) rec; + + xact_desc_prepare(buf, XLogRecGetInfo(record), xlrec); + } else if (info == XLOG_XACT_ASSIGNMENT) { xl_xact_assignment *xlrec = (xl_xact_assignment *) rec; *** a/src/backend/access/transam/twophase.c --- b/src/backend/access/transam/twophase.c *************** *** 911,933 **** TwoPhaseGetDummyProc(TransactionId xid, bool lock_held) */ #define TWOPHASE_MAGIC 0x57F94534 /* format identifier */ ! typedef struct TwoPhaseFileHeader ! { ! uint32 magic; /* format identifier */ ! uint32 total_len; /* actual file length */ ! TransactionId xid; /* original transaction XID */ ! Oid database; /* OID of database it was in */ ! TimestampTz prepared_at; /* time of preparation */ ! Oid owner; /* user running the transaction */ ! int32 nsubxacts; /* number of following subxact XIDs */ ! int32 ncommitrels; /* number of delete-on-commit rels */ ! int32 nabortrels; /* number of delete-on-abort rels */ ! int32 ninvalmsgs; /* number of cache invalidation messages */ ! bool initfileinval; /* does relcache init file need invalidation? */ ! uint16 gidlen; /* length of the GID - GID follows the header */ ! XLogRecPtr origin_lsn; /* lsn of this record at origin node */ ! TimestampTz origin_timestamp; /* time of prepare at origin node */ ! } TwoPhaseFileHeader; /* * Header for each record in a state file --- 911,917 ---- */ #define TWOPHASE_MAGIC 0x57F94534 /* format identifier */ ! typedef xl_xact_prepare TwoPhaseFileHeader; /* * Header for each record in a state file *************** *** 1332,1375 **** ReadTwoPhaseFile(TransactionId xid, bool missing_ok) return buf; } - /* - * ParsePrepareRecord - */ - void - ParsePrepareRecord(uint8 info, char *xlrec, xl_xact_parsed_prepare *parsed) - { - TwoPhaseFileHeader *hdr; - char *bufptr; - - hdr = (TwoPhaseFileHeader *) xlrec; - bufptr = xlrec + MAXALIGN(sizeof(TwoPhaseFileHeader)); - - parsed->origin_lsn = hdr->origin_lsn; - parsed->origin_timestamp = hdr->origin_timestamp; - parsed->twophase_xid = hdr->xid; - parsed->dbId = hdr->database; - parsed->nsubxacts = hdr->nsubxacts; - parsed->nrels = hdr->ncommitrels; - parsed->nabortrels = hdr->nabortrels; - parsed->nmsgs = hdr->ninvalmsgs; - - strncpy(parsed->twophase_gid, bufptr, hdr->gidlen); - bufptr += MAXALIGN(hdr->gidlen); - - parsed->subxacts = (TransactionId *) bufptr; - bufptr += MAXALIGN(hdr->nsubxacts * sizeof(TransactionId)); - - parsed->xnodes = (RelFileNode *) bufptr; - bufptr += MAXALIGN(hdr->ncommitrels * sizeof(RelFileNode)); - - parsed->abortnodes = (RelFileNode *) bufptr; - bufptr += MAXALIGN(hdr->nabortrels * sizeof(RelFileNode)); - - parsed->msgs = (SharedInvalidationMessage *) bufptr; - bufptr += MAXALIGN(hdr->ninvalmsgs * sizeof(SharedInvalidationMessage)); - } - - /* * Reads 2PC data from xlog. During checkpoint this data will be moved to --- 1316,1321 ---- *** a/src/include/access/twophase.h --- b/src/include/access/twophase.h *************** *** 47,54 **** extern bool StandbyTransactionIdIsPrepared(TransactionId xid); extern TransactionId PrescanPreparedTransactions(TransactionId **xids_p, int *nxids_p); - extern void ParsePrepareRecord(uint8 info, char *xlrec, - xl_xact_parsed_prepare *parsed); extern void StandbyRecoverPreparedTransactions(void); extern void RecoverPreparedTransactions(void); --- 47,52 ---- *** a/src/include/access/xact.h --- b/src/include/access/xact.h *************** *** 292,297 **** typedef struct xl_xact_abort --- 292,315 ---- } xl_xact_abort; #define MinSizeOfXactAbort sizeof(xl_xact_abort) + typedef struct xl_xact_prepare + { + uint32 magic; /* format identifier */ + uint32 total_len; /* actual file length */ + TransactionId xid; /* original transaction XID */ + Oid database; /* OID of database it was in */ + TimestampTz prepared_at; /* time of preparation */ + Oid owner; /* user running the transaction */ + int32 nsubxacts; /* number of following subxact XIDs */ + int32 ncommitrels; /* number of delete-on-commit rels */ + int32 nabortrels; /* number of delete-on-abort rels */ + int32 ninvalmsgs; /* number of cache invalidation messages */ + bool initfileinval; /* does relcache init file need invalidation? */ + uint16 gidlen; /* length of the GID - GID follows the header */ + XLogRecPtr origin_lsn; /* lsn of this record at origin node */ + TimestampTz origin_timestamp; /* time of prepare at origin node */ + } xl_xact_prepare; + /* * Commit/Abort records in the above form are a bit verbose to parse, so * there's a deconstructed versions generated by ParseCommit/AbortRecord() for *************** *** 435,440 **** extern const char *xact_identify(uint8 info); --- 453,459 ---- /* also in xactdesc.c, so they can be shared between front/backend code */ extern void ParseCommitRecord(uint8 info, xl_xact_commit *xlrec, xl_xact_parsed_commit *parsed); extern void ParseAbortRecord(uint8 info, xl_xact_abort *xlrec, xl_xact_parsed_abort *parsed); + extern void ParsePrepareRecord(uint8 info, xl_xact_prepare *xlrec, xl_xact_parsed_prepare *parsed); extern void EnterParallelMode(void); extern void ExitParallelMode(void);