/* * Handle rmgr STANDBY_ID records for DecodeRecordIntoReorderBuffer(). */ static void DecodeStandbyOp(LogicalDecodingContext *ctx, XLogRecordBuffer *buf) { SnapBuild *builder = ctx->snapshot_builder; XLogRecord *r = &buf->record; uint8 info = r->xl_info & ~XLR_INFO_MASK; ReorderBufferProcessXid(ctx->reorder, r->xl_xid, buf->origptr); switch (info) { case XLOG_RUNNING_XACTS: { xl_running_xacts *running = (xl_running_xacts *) buf->record_data; SnapBuildProcessRunningXacts(builder, buf->origptr, running); /* * Abort all transactions that we keep track of, that are * older than the record's oldestRunningXid. This is the most * convenient spot for doing so since, in contrast to shutdown * or end-of-recovery checkpoints, we have information about * all running transactions which includes prepared ones, * while shutdown checkpoints just know that no non-prepared * transactions are in progress. */ ReorderBufferAbortOld(ctx->reorder, running->oldestRunningXid); } break; case XLOG_STANDBY_LOCK: break; default: elog(ERROR, "unexpected RM_STANDBY_ID record type: %u", info); } }
/* * Handle rmgr HEAP2_ID records for DecodeRecordIntoReorderBuffer(). */ static void DecodeHeap2Op(LogicalDecodingContext *ctx, XLogRecordBuffer *buf) { uint8 info = buf->record.xl_info & XLOG_HEAP_OPMASK; TransactionId xid = buf->record.xl_xid; SnapBuild *builder = ctx->snapshot_builder; ReorderBufferProcessXid(ctx->reorder, xid, buf->origptr); /* no point in doing anything yet */ if (SnapBuildCurrentState(builder) < SNAPBUILD_FULL_SNAPSHOT) return; switch (info) { case XLOG_HEAP2_MULTI_INSERT: if (SnapBuildProcessChange(builder, xid, buf->origptr)) DecodeMultiInsert(ctx, buf); break; case XLOG_HEAP2_NEW_CID: { xl_heap_new_cid *xlrec; xlrec = (xl_heap_new_cid *) buf->record_data; SnapBuildProcessNewCid(builder, xid, buf->origptr, xlrec); break; } case XLOG_HEAP2_REWRITE: /* * Although these records only exist to serve the needs of logical * decoding, all the work happens as part of crash or archive * recovery, so we don't need to do anything here. */ break; /* * Everything else here is just low level physical stuff we're not * interested in. */ case XLOG_HEAP2_FREEZE_PAGE: case XLOG_HEAP2_CLEAN: case XLOG_HEAP2_CLEANUP_INFO: case XLOG_HEAP2_VISIBLE: case XLOG_HEAP2_LOCK_UPDATED: break; default: elog(ERROR, "unexpected RM_HEAP2_ID record type: %u", info); } }
/* * Handle rmgr LOGICALMSG_ID records for DecodeRecordIntoReorderBuffer(). */ static void DecodeLogicalMsgOp(LogicalDecodingContext *ctx, XLogRecordBuffer *buf) { SnapBuild *builder = ctx->snapshot_builder; XLogReaderState *r = buf->record; TransactionId xid = XLogRecGetXid(r); uint8 info = XLogRecGetInfo(r) & ~XLR_INFO_MASK; RepOriginId origin_id = XLogRecGetOrigin(r); Snapshot snapshot; xl_logical_message *message; if (info != XLOG_LOGICAL_MESSAGE) elog(ERROR, "unexpected RM_LOGICALMSG_ID record type: %u", info); ReorderBufferProcessXid(ctx->reorder, XLogRecGetXid(r), buf->origptr); /* No point in doing anything yet. */ if (SnapBuildCurrentState(builder) < SNAPBUILD_FULL_SNAPSHOT) return; message = (xl_logical_message *) XLogRecGetData(r); if (message->dbId != ctx->slot->data.database || FilterByOrigin(ctx, origin_id)) return; if (message->transactional && !SnapBuildProcessChange(builder, xid, buf->origptr)) return; else if (!message->transactional && (SnapBuildCurrentState(builder) != SNAPBUILD_CONSISTENT || SnapBuildXactNeedsSkip(builder, buf->origptr))) return; snapshot = SnapBuildGetOrBuildSnapshot(builder, xid); ReorderBufferQueueMessage(ctx->reorder, xid, snapshot, buf->endptr, message->transactional, message->message, /* first part of message is * prefix */ message->message_size, message->message + message->prefix_size); }
/* * Handle rmgr XLOG_ID records for DecodeRecordIntoReorderBuffer(). */ static void DecodeXLogOp(LogicalDecodingContext *ctx, XLogRecordBuffer *buf) { SnapBuild *builder = ctx->snapshot_builder; uint8 info = XLogRecGetInfo(buf->record) & ~XLR_INFO_MASK; ReorderBufferProcessXid(ctx->reorder, XLogRecGetXid(buf->record), buf->origptr); switch (info) { /* this is also used in END_OF_RECOVERY checkpoints */ case XLOG_CHECKPOINT_SHUTDOWN: case XLOG_END_OF_RECOVERY: SnapBuildSerializationPoint(builder, buf->origptr); break; case XLOG_CHECKPOINT_ONLINE: /* * a RUNNING_XACTS record will have been logged near to this, we * can restart from there. */ break; case XLOG_NOOP: case XLOG_NEXTOID: case XLOG_SWITCH: case XLOG_BACKUP_END: case XLOG_PARAMETER_CHANGE: case XLOG_RESTORE_POINT: case XLOG_FPW_CHANGE: case XLOG_FPI_FOR_HINT: case XLOG_FPI: break; default: elog(ERROR, "unexpected RM_XLOG_ID record type: %u", info); } }
/* * Take every XLogReadRecord()ed record and perform the actions required to * decode it using the output plugin already setup in the logical decoding * context. * * NB: Note that every record's xid needs to be processed by reorderbuffer * (xids contained in the content of records are not relevant for this rule). * That means that for records which'd otherwise not go through the * reorderbuffer ReorderBufferProcessXid() has to be called. We don't want to * call ReorderBufferProcessXid for each record type by default, because * e.g. empty xacts can be handled more efficiently if there's no previous * state for them. */ void LogicalDecodingProcessRecord(LogicalDecodingContext *ctx, XLogRecord *record) { XLogRecordBuffer buf; buf.origptr = ctx->reader->ReadRecPtr; buf.endptr = ctx->reader->EndRecPtr; buf.record = *record; buf.record_data = XLogRecGetData(record); /* cast so we get a warning when new rmgrs are added */ switch ((RmgrIds) buf.record.xl_rmid) { /* * Rmgrs we care about for logical decoding. Add new rmgrs in * rmgrlist.h's order. */ case RM_XLOG_ID: DecodeXLogOp(ctx, &buf); break; case RM_XACT_ID: DecodeXactOp(ctx, &buf); break; case RM_STANDBY_ID: DecodeStandbyOp(ctx, &buf); break; case RM_HEAP2_ID: DecodeHeap2Op(ctx, &buf); break; case RM_HEAP_ID: DecodeHeapOp(ctx, &buf); break; /* * Rmgrs irrelevant for logical decoding; they describe stuff not * represented in logical decoding. Add new rmgrs in rmgrlist.h's * order. */ case RM_SMGR_ID: case RM_CLOG_ID: case RM_DBASE_ID: case RM_TBLSPC_ID: case RM_MULTIXACT_ID: case RM_RELMAP_ID: case RM_BTREE_ID: case RM_HASH_ID: case RM_GIN_ID: case RM_GIST_ID: case RM_SEQ_ID: case RM_SPGIST_ID: case RM_BITMAP_ID: case RM_DISTRIBUTEDLOG_ID: /* just deal with xid, and done */ ReorderBufferProcessXid(ctx->reorder, record->xl_xid, buf.origptr); break; case RM_APPEND_ONLY_ID: /* * GPDB_94_MERGE_FIXME: logical decoding hasn't been implemented for * append-only tables yet. */ break; case RM_NEXT_ID: elog(ERROR, "unexpected RM_NEXT_ID rmgr_id: %u", (RmgrIds) buf.record.xl_rmid); } }
/* * Handle rmgr HEAP_ID records for DecodeRecordIntoReorderBuffer(). */ static void DecodeHeapOp(LogicalDecodingContext *ctx, XLogRecordBuffer *buf) { uint8 info = buf->record.xl_info & XLOG_HEAP_OPMASK; TransactionId xid = buf->record.xl_xid; SnapBuild *builder = ctx->snapshot_builder; ReorderBufferProcessXid(ctx->reorder, xid, buf->origptr); /* no point in doing anything yet */ if (SnapBuildCurrentState(builder) < SNAPBUILD_FULL_SNAPSHOT) return; switch (info) { case XLOG_HEAP_INSERT: if (SnapBuildProcessChange(builder, xid, buf->origptr)) DecodeInsert(ctx, buf); break; /* * Treat HOT update as normal updates. There is no useful * information in the fact that we could make it a HOT update * locally and the WAL layout is compatible. */ case XLOG_HEAP_HOT_UPDATE: case XLOG_HEAP_UPDATE: if (SnapBuildProcessChange(builder, xid, buf->origptr)) DecodeUpdate(ctx, buf); break; case XLOG_HEAP_DELETE: if (SnapBuildProcessChange(builder, xid, buf->origptr)) DecodeDelete(ctx, buf); break; case XLOG_HEAP_NEWPAGE: /* * This is only used in places like indexams and CLUSTER which * don't contain changes relevant for logical replication. */ break; case XLOG_HEAP_INPLACE: /* * Inplace updates are only ever performed on catalog tuples and * can, per definition, not change tuple visibility. Since we * don't decode catalog tuples, we're not interested in the * record's contents. * * In-place updates can be used either by XID-bearing transactions * (e.g. in CREATE INDEX CONCURRENTLY) or by XID-less * transactions (e.g. VACUUM). In the former case, the commit * record will include cache invalidations, so we mark the * transaction as catalog modifying here. Currently that's * redundant because the commit will do that as well, but once we * support decoding in-progress relations, this will be important. */ if (!TransactionIdIsValid(xid)) break; SnapBuildProcessChange(builder, xid, buf->origptr); ReorderBufferXidSetCatalogChanges(ctx->reorder, xid, buf->origptr); break; case XLOG_HEAP_LOCK: /* we don't care about row level locks for now */ break; default: elog(ERROR, "unexpected RM_HEAP_ID record type: %u", info); break; } }
/* * Handle rmgr XACT_ID records for DecodeRecordIntoReorderBuffer(). */ static void DecodeXactOp(LogicalDecodingContext *ctx, XLogRecordBuffer *buf) { SnapBuild *builder = ctx->snapshot_builder; ReorderBuffer *reorder = ctx->reorder; XLogRecord *r = &buf->record; uint8 info = r->xl_info & ~XLR_INFO_MASK; /* * No point in doing anything yet, data could not be decoded anyway. It's * ok not to call ReorderBufferProcessXid() in that case, except in the * assignment case there'll not be any later records with the same xid; * and in the assignment case we'll not decode those xacts. */ if (SnapBuildCurrentState(builder) < SNAPBUILD_FULL_SNAPSHOT) return; switch (info) { case XLOG_XACT_COMMIT: { xl_xact_commit *xlrec; TransactionId *subxacts = NULL; SharedInvalidationMessage *invals = NULL; xlrec = (xl_xact_commit *) buf->record_data; subxacts = (TransactionId *) &(xlrec->xnodes[xlrec->nrels]); invals = (SharedInvalidationMessage *) &(subxacts[xlrec->nsubxacts]); DecodeCommit(ctx, buf, r->xl_xid, xlrec->dbId, xlrec->xact_time, xlrec->nsubxacts, subxacts, xlrec->nmsgs, invals); break; } case XLOG_XACT_COMMIT_PREPARED: { xl_xact_commit_prepared *prec; xl_xact_commit *xlrec; TransactionId *subxacts; SharedInvalidationMessage *invals = NULL; /* Prepared commits contain a normal commit record... */ prec = (xl_xact_commit_prepared *) buf->record_data; xlrec = &prec->crec; subxacts = (TransactionId *) &(xlrec->xnodes[xlrec->nrels]); invals = (SharedInvalidationMessage *) &(subxacts[xlrec->nsubxacts]); DecodeCommit(ctx, buf, prec->xid, xlrec->dbId, xlrec->xact_time, xlrec->nsubxacts, subxacts, xlrec->nmsgs, invals); break; } case XLOG_XACT_COMMIT_COMPACT: { xl_xact_commit_compact *xlrec; xlrec = (xl_xact_commit_compact *) buf->record_data; DecodeCommit(ctx, buf, r->xl_xid, InvalidOid, xlrec->xact_time, xlrec->nsubxacts, xlrec->subxacts, 0, NULL); break; } case XLOG_XACT_ABORT: { xl_xact_abort *xlrec; TransactionId *sub_xids; xlrec = (xl_xact_abort *) buf->record_data; sub_xids = (TransactionId *) &(xlrec->xnodes[xlrec->nrels]); DecodeAbort(ctx, buf->origptr, r->xl_xid, sub_xids, xlrec->nsubxacts); break; } case XLOG_XACT_ABORT_PREPARED: { xl_xact_abort_prepared *prec; xl_xact_abort *xlrec; TransactionId *sub_xids; /* prepared abort contain a normal commit abort... */ prec = (xl_xact_abort_prepared *) buf->record_data; xlrec = &prec->arec; sub_xids = (TransactionId *) &(xlrec->xnodes[xlrec->nrels]); /* r->xl_xid is committed in a separate record */ DecodeAbort(ctx, buf->origptr, prec->xid, sub_xids, xlrec->nsubxacts); break; } case XLOG_XACT_ASSIGNMENT: { xl_xact_assignment *xlrec; int i; TransactionId *sub_xid; xlrec = (xl_xact_assignment *) buf->record_data; 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. */ ReorderBufferProcessXid(reorder, r->xl_xid, buf->origptr); break; default: elog(ERROR, "unexpected RM_XACT_ID record type: %u", info); } }
/* * Take every XLogReadRecord()ed record and perform the actions required to * decode it using the output plugin already setup in the logical decoding * context. * * NB: Note that every record's xid needs to be processed by reorderbuffer * (xids contained in the content of records are not relevant for this rule). * That means that for records which'd otherwise not go through the * reorderbuffer ReorderBufferProcessXid() has to be called. We don't want to * call ReorderBufferProcessXid for each record type by default, because * e.g. empty xacts can be handled more efficiently if there's no previous * state for them. */ void LogicalDecodingProcessRecord(LogicalDecodingContext *ctx, XLogReaderState *record) { XLogRecordBuffer buf; buf.origptr = ctx->reader->ReadRecPtr; buf.endptr = ctx->reader->EndRecPtr; buf.record = record; /* cast so we get a warning when new rmgrs are added */ switch ((RmgrIds) XLogRecGetRmid(record)) { /* * Rmgrs we care about for logical decoding. Add new rmgrs in * rmgrlist.h's order. */ case RM_XLOG_ID: DecodeXLogOp(ctx, &buf); break; case RM_XACT_ID: DecodeXactOp(ctx, &buf); break; case RM_STANDBY_ID: DecodeStandbyOp(ctx, &buf); break; case RM_HEAP2_ID: DecodeHeap2Op(ctx, &buf); break; case RM_HEAP_ID: DecodeHeapOp(ctx, &buf); break; case RM_LOGICALMSG_ID: DecodeLogicalMsgOp(ctx, &buf); break; /* * Rmgrs irrelevant for logical decoding; they describe stuff not * represented in logical decoding. Add new rmgrs in rmgrlist.h's * order. */ case RM_SMGR_ID: case RM_CLOG_ID: case RM_DBASE_ID: case RM_TBLSPC_ID: case RM_MULTIXACT_ID: case RM_RELMAP_ID: case RM_BTREE_ID: case RM_HASH_ID: case RM_GIN_ID: case RM_GIST_ID: case RM_SEQ_ID: case RM_SPGIST_ID: case RM_BRIN_ID: case RM_COMMIT_TS_ID: case RM_REPLORIGIN_ID: case RM_GENERIC_ID: /* just deal with xid, and done */ ReorderBufferProcessXid(ctx->reorder, XLogRecGetXid(record), buf.origptr); break; case RM_NEXT_ID: elog(ERROR, "unexpected RM_NEXT_ID rmgr_id: %u", (RmgrIds) XLogRecGetRmid(buf.record)); } }
/* * 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. It's * ok not to call ReorderBufferProcessXid() in that case, except in the * assignment case there'll not be any later records with the same xid; * and in the assignment case we'll not decode those xacts. */ 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. */ ReorderBufferProcessXid(reorder, XLogRecGetXid(r), buf->origptr); break; default: elog(ERROR, "unexpected RM_XACT_ID record type: %u", info); } }