Decodes a TRANSACTION_PAYLOAD_EVENT (type 40) body into the inner event
stream of the transaction it wraps — the binlog_transaction_compression
consumer arm (ADR-0011).
Body layout (MySQL server source, libbinlogevents/src/codecs/binary.cpp,
read first-hand): a net_field_length TLV header — triples of
(type, value_length, value) closed by a bare 0 end mark — followed by the
payload bytes. Field types: 0 end mark, 1 payload size, 2 compression
type, 3 uncompressed size; unrecognized fields are skipped by their length.
The compression type value is 0 for ZSTD and 255 for NONE
(compression/base.h) — only ZSTD is consumed; anything else fails closed.
The payload is one or more zstd frames (Capstan.Zstd). The inflated bytes
are the transaction's events — QUERY(BEGIN), TABLE_MAP, row events,
terminator — each a plain 19-byte header + body with no per-event CRC
trailer (the outer type-40 event's CRC32 covers the compressed payload as a
whole; observed live, the inner headers' event_size values sum exactly to
the inflated length). Splitting is driven by each inner header's event_size.
Every divergence from the declared shape — a missing field, a size that does
not fit, an unknown compression type, a zstd corruption signal, an
uncompressed-size mismatch, a malformed inner header — is {:error, reason},
never a guess. All reasons are value-free (Rule 1): the payload and inner
bytes carry row values and DDL text and never enter an error term.
Summary
Functions
Decodes a type-40 body to its inner event list: parses the TLV header, inflates the ZSTD payload, and splits the inner event stream.
Functions
@spec decode(binary()) :: {:ok, [Capstan.Binlog.Event.t()]} | {:error, term()}
Decodes a type-40 body to its inner event list: parses the TLV header, inflates the ZSTD payload, and splits the inner event stream.
The uncompressed size, when the server declares it, is checked against the inflated length — a server-declared oracle over the decompressor's output.