12.5. Logical Decoding Output Plugins: pgoutput
Alpha Version: Work in progress.
The primary role of an output plugin is to serialize the change data—which has been organized and reordered by the ReorderBuffer — into a format compatible with sending to a subscriber.
The specific tasks performed by the plugin include:
- Data Transformation and Filtering: The plugin performs a final validation of the binary data within the ReorderBuffer against the publication’s configuration. It determines whether the target table or specific actions (e.g., INSERT, TRUNCATE) are included in the subscription and evaluates row filters or column lists. Once irrelevant data is excluded, the plugin formats the remaining payload into the appropriate data types and structures (e.g., text or binary) dictated by the replication protocol.
- Message Generation: The plugin constructs a logical sequence of messages, such as BEGIN, INSERT/UPDATE/DELETE/TRUNCATE, and COMMIT. These messages are passed to the walsender process, which manages the network sending.
As the name implies, this mechanism is pluggable. Custom plugins can be implemented to support unique output formats or specialized internal processing logic. In standard PostgreSQL logical replication, the officially supported pgoutput is used as the default plugin.
The pgoutput message format consists of a series of tagged data packets. The following sections describe the structure of these messages.
While PostgreSQL supports multiple protocol versions (versions 1 through 4), this section primarily focuses on the core structures. For a comprehensive reference, please consult the official documentation: Logical Replication Message Formats.
12.5.1. TupleData (shared sub-message)
The TupleData structure is a common sub-message used within DML operations to represent tuple contents.
[num_cols: Int16]
For each column:
[kind: Byte1('n'|'u'|'t'|'b')]
if kind = 't' or 'b':
[length: Int32] [value: Byte*n*]| Item | Type | Description |
|---|---|---|
| num_cols | Int16 | Number of columns in the tuple. |
| For each column: | ||
| kind | Byte1 | ’n’ = NULL; ‘u’ = unchanged TOASTed value; ’t’ = text formatted; ‘b’ = binary formatted. |
| length | Int32 | Length of the column value in bytes. Present only when kind is ’t’ or ‘b’. |
| value | Byten | The actual column value. n matches the preceding length. Present only when kind is ’t’ or ‘b’. |
12.5.2. Transaction Control
12.5.2.1. Begin (‘B’)
Identifies the start of a transaction. It provides the transaction’s LSN and commit timestamp, allowing the subscriber to maintain chronological order.
[Byte1('B')] [final_lsn: Int64] [commit_timestamp: Int64] [txid: Int32]| Item | Type | Description |
|---|---|---|
| ‘B’ | Byte1 | Identifies the message as a BEGIN message. |
| final_lsn | Int64 (XLogRecPtr) | The final LSN of the transaction. (Matches the commit_lsn in ‘C’) |
| commit_timestamp | Int64 (TimestampTz) | The commit timestamp in microseconds since the PostgreSQL epoch (2000-01-01). |
| txid | Int32 (TransactionId) | The XID (Transaction ID) of the transaction. |
12.5.2.2. Commit (‘C’)
Identifies the end of a transaction. Upon receiving this, the subscriber applies the accumulated changes locally as a single atomic unit.
[Byte1('C')] [flags: Int8] [commit_lsn: Int64] [end_lsn: Int64] [commit_timestamp: Int64]| Item | Type | Description |
|---|---|---|
| ‘C’ | Byte1 | Identifies the message as a COMMIT message. |
| flags | Int8(0) | Reserved flags; currently unused. |
| commit_lsn | Int64 (XLogRecPtr) | The LSN of the commit. |
| end_lsn | Int64 (XLogRecPtr) | The end LSN of the transaction. |
| commit_timestamp | Int64 (TimestampTz) | The commit timestamp. |
12.5.3. DML
12.5.3.1. Origin (‘O’)
Used in setups involving multi-node replication. It informs the subscriber where the transaction originally occurred to prevent replication loops.
[Byte1('O')] [origin_lsn: Int64] [origin_name: String]| item | type | description |
|---|---|---|
| ‘O’ | Byte1 | Identifies the message as an origin message. |
| origin_lsn | Int64 (XLogRecPtr) | The LSN of the commit on the origin server. |
| origin_name | String | Name of the origin. Note that there can be multiple Origin messages inside a single transaction. |
12.5.3.2. Relation (‘R’)
Provides the metadata for a specific table. This message is typically sent before the first DML message for a table in a session, mapping a unique ID to the table’s schema and columns.
[Byte1('R')] [rel_id: Int32] [namespace: String] [relname: String]
[replica_identity: Int8] [num_columns: Int16]
For each column:
[flags: Int8] [name: String] [type_oid: Int32] [atttypmod: Int32]| item | type | description |
|---|---|---|
| ‘R’ | Byte1 | Identifies the message as a relation message. |
| rel_id | Int32 (Oid) | OID of the relation. |
| namespace | String | Namespace (empty string for pg_catalog). |
| relname | String | Relation name. |
| replica_identity | Int8 | Replica identity configuration for the relation (same as relreplident in pg_class). ’d’=default, ’n’=nothing, ‘f’=full, ‘i’=index. |
| num_columns | Int16 | Number of columns. |
| For each column: | ||
| flags | Int8 | Flags for the column. 0 = no flags; 1 = column is part of the replica identity key. |
| name | String | Name of the column. |
| type_oid | Int32 (Oid) | OID of the column’s data type. |
| atttypmod | Int32 | Type modifier of the column (atttypmod). |
12.5.3.3. Insert (‘I’)
Represents the insertion of a new tuple. It includes the target relation ID and the TupleData of the new tuple.
[Byte1('I')] [rel_id: Int32] [Byte1('N')] [new_tuple: TupleData]| item | type | description |
|---|---|---|
| ‘I’ | Byte1 | Identifies the message as an insert message. |
| rel_id | Int32 (Oid) | OID of the relation corresponding to the ID in the relation message. |
| ‘N’ | Byte1 | Identifies the following TupleData as a new tuple. |
| new_tuple | TupleData | TupleData representing the contents of the new tuple. |
12.5.3.4. Update (‘U’)
Represents an update to an existing tuple. Depending on the REPLICA IDENTITY configuration and whether the key columns changed, it may include the old tuple’s values to allow the subscriber to identify the correct tuple to modify.
-- REPLICA IDENTITY DEFAULT or INDEX (key columns changed):
[Byte1('U')] [rel_id: Int32] [Byte1('K')] [old_tuple: TupleData] [Byte1('N')] [new_tuple: TupleData]
-- REPLICA IDENTITY FULL:
[Byte1('U')] [rel_id: Int32] [Byte1('O')] [old_tuple: TupleData] [Byte1('N')] [new_tuple: TupleData]
-- REPLICA IDENTITY DEFAULT or INDEX (key columns NOT changed):
[Byte1('U')] [rel_id: Int32] [Byte1('N')] [new_tuple: TupleData]| item | type | description |
|---|---|---|
| ‘U’ | Byte1 | Identifies the message as an update message. |
| rel_id | Int32 (Oid) | OID of the relation corresponding to the ID in the relation message. |
| ‘K’ | Byte1 | (Optional) Identifies the following TupleData as a key. Present only if the update changed data in any column that is part of the REPLICA IDENTITY index. Mutually exclusive with ‘O’. |
| ‘O’ | Byte1 | (Optional) Identifies the following TupleData as an old tuple. Present only if the table has REPLICA IDENTITY set to FULL. Mutually exclusive with ‘K’. |
| old_tuple | TupleData | (Optional) TupleData for the old tuple or primary key. Present only if the preceding ‘K’ or ‘O’ marker is present. |
| ‘N’ | Byte1 | Identifies the following TupleData as a new tuple. |
| new_tuple | TupleData | TupleData representing the contents of the new tuple. |
12.5.3.5. Delete (‘D’)
Represents the deletion of a tuple. The subscriber uses the provided key or old tuple data to locate and remove the record.
-- REPLICA IDENTITY DEFAULT or INDEX:
[Byte1('D')] [rel_id: Int32] [Byte1('K')] [old_key_tuple: TupleData]
-- REPLICA IDENTITY FULL:
[Byte1('D')] [rel_id: Int32] [Byte1('O')] [old_tuple: TupleData]| item | type | description |
|---|---|---|
| ‘D’ | Byte1 | Identifies the message as a delete message. |
| rel_id | Int32 (Oid) | OID of the relation corresponding to the ID in the relation message. |
| ‘K’ | Byte1 | (Either ‘K’ or ‘O’, never both) Identifies the following TupleData as a key. Present if the table uses an index as REPLICA IDENTITY. |
| ‘O’ | Byte1 | (Either ‘K’ or ‘O’, never both) Identifies the following TupleData as an old tuple. Present if the table has REPLICA IDENTITY set to FULL. |
| old_key_tuple | TupleData | TupleData representing the contents of the old tuple or primary key, depending on the preceding marker. |
12.5.3.6. Truncate (‘T’)
Represents a bulk removal of all tuples in one or more tables.
[Byte1('T')] [num_relations: Int32] [options: Int8]
[rel_id: Int32] ... -- repeated num_relations times| item | type | description |
|---|---|---|
| ‘T’ | Byte1 | Identifies the message as a truncate message. |
| num_relations | Int32 | Number of relations to be truncated. |
| options | Int8 | Option bits for TRUNCATE: bit 0 (1) = CASCADE; bit 1 (2) = RESTART IDENTITY. |
| rel_id | Int32 (Oid) | OID of the relation corresponding to the ID in the relation message. This field is repeated num_relations times. |
12.5.4. Stream Control
The following messages are utilized when the publisher sends data in segments to support the Streaming of Large Transactions.
For further details, refer to Section 12.6.3.
12.5.4.1. Stream Start (‘S’)
Identifies the beginning of a stream segment. This is used when a large, ongoing transaction is split into multiple segments for sending.
[Byte1('S')] [txid: Int32] [first_segment: Int8]| Item | Type | Description |
|---|---|---|
| ‘S’ | Byte1 | Identifies the message as a Stream Start message. |
| txid | Int32 (TransactionId) | The XID of the transaction. |
| first_segment | Int8 | Set to 1 if this is the first segment for this XID; otherwise 0. |
12.5.4.2. Stream Stop (‘E’)
Identifies the end of a stream segment.
[Byte1('E')]| Item | Type | Description |
|---|---|---|
| ‘E’ | Byte1 | Identifies the message as a Stream Stop message. |
12.5.4.3. Stream Commit (‘c’)
Identifies the commit of a streamed transaction. It contains similar information to a standard Commit (‘C’) but operates within the streaming context.
[Byte1('c')] [txid: Int32] [flags: Int8] [commit_lsn: Int64] [end_lsn: Int64] [commit_timestamp: Int64]| Item | Type | Description |
|---|---|---|
| ‘c’ | Byte1 | Identifies the message as a Stream Commit message. |
| txid | Int32 (TransactionId) | The XID of the transaction. |
| flags | Int8(0) | Reserved flags; currently unused. |
| commit_lsn | Int64 (XLogRecPtr) | The LSN of the commit. |
| end_lsn | Int64 (XLogRecPtr) | The end LSN of the transaction. |
| commit_timestamp | Int64 (TimestampTz) | The commit timestamp. |
12.5.4.4. Stream Abort (‘A’)
Identifies the abort (rollback) of a streamed transaction. This is also used for the abort of subtransactions.
[Byte1('A')] [txid: Int32] [subxid: Int32] [abort_lsn: Int64]? [abort_timestamp: Int64]?| Item | Type | Description |
|---|---|---|
| ‘A’ | Byte1 | Identifies the message as a Stream Abort message. |
| txid | Int32 (TransactionId) | The XID of the transaction. |
| subxid | Int32 (TransactionId) | The XID of the subtransaction (same as txid for top-level transactions). |
| abort_lsn | Int64 (XLogRecPtr) | The LSN of the abort operation. Present only if parallel streaming is enabled (Protocol v4+). |
| abort_timestamp | Int64 (TimestampTz) | The abort timestamp. Present only if parallel streaming is enabled (Protocol v4+). |