MySQL replication commands and text-resultset decoding.
Builds the request payloads capstan sends over an authenticated
Capstan.Protocol.Packet socket — COM_QUERY, COM_REGISTER_SLAVE, and the
resume-carrying COM_BINLOG_DUMP_GTID — and decodes the text resultset a
COM_QUERY returns. Framing, the >16 MiB split, and length-encoded primitives all
live in Capstan.Protocol.Packet; GTID-set algebra lives in Capstan.Gtid.
The resume encoding (F2 — silent-loss class)
COM_BINLOG_DUMP_GTID carries the checkpoint the client has already applied, and
the server resumes streaming from the first transaction not in it. The wire
interval end is EXCLUSIVE while Capstan.Gtid is INCLUSIVE: a checkpoint of
uuid:1-11 (GTIDs 1..11 applied) encodes as start = 1, end = 12. The high + 1
conversion lives here, in com_binlog_dump_gtid/2, and nowhere else. An off-by-one
skips or replays exactly one transaction per interval on every restart — silently,
with no server error — so it is pinned by round-trip byte tests and a live tripwire.
NULL in a text resultset
A NULL column value is the single byte 0xFB, which is not a length. Packet's
lenenc_int/1 has no 0xFB clause by design, so the row decoder must detect NULL
and yield nil before delegating to lenenc_str/1 — otherwise a NULL column would
raise FunctionClauseError.
Summary
Functions
Builds a COM_BINLOG_DUMP_GTID request that resumes from gtid_set.
Builds a COM_QUERY request: the command byte 0x03 followed by the SQL text.
Builds a COM_REGISTER_SLAVE request identifying this client to the source.
Sends sql as a COM_QUERY on socket and reads the response.
Types
@type row() :: [binary() | nil]
A row of a text resultset; a NULL column is nil.
Functions
@spec com_binlog_dump_gtid(non_neg_integer(), Capstan.Gtid.t()) :: binary()
Builds a COM_BINLOG_DUMP_GTID request that resumes from gtid_set.
The payload is the command byte, flags (BINLOG_THROUGH_GTID), server_id, an
empty binlog name (length 0), the start position (4), then the data_size-prefixed
GTID-set block: n_sids, and per source the 16 raw UUID bytes, its interval count,
and each {start, end} pair. Each INCLUSIVE high is encoded as the EXCLUSIVE
wire high + 1. An empty set encodes as n_sids = 0 and streams everything
the server still retains.
Builds a COM_QUERY request: the command byte 0x03 followed by the SQL text.
@spec com_register_slave(non_neg_integer()) :: binary()
Builds a COM_REGISTER_SLAVE request identifying this client to the source.
Hostname, user and password are advertised empty and the replication rank and
master-id are zero — capstan positions with COM_BINLOG_DUMP_GTID, so only the
server_id is meaningful.
@spec query(Capstan.Protocol.Packet.socket(), binary(), timeout()) :: :ok | {:ok, [row()]} | {:error, {:query_error, non_neg_integer()} | {:transport, term()}}
Sends sql as a COM_QUERY on socket and reads the response.
Returns :ok for a statement with no resultset (an OK packet — e.g. SET),
{:ok, rows} for a text resultset (each row a list of column values, nil for a
NULL column), and {:error, {:query_error, code}} for a server error. A transport
failure on send is {:error, {:transport, reason}}.