Capstan.Protocol.Command (Capstan v0.1.0)

Copy Markdown View Source

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

Types

A row of a text resultset; a NULL column is nil.

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

row()

@type row() :: [binary() | nil]

A row of a text resultset; a NULL column is nil.

Functions

com_binlog_dump_gtid(server_id, gtid_set)

@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.

com_query(sql)

@spec com_query(binary()) :: binary()

Builds a COM_QUERY request: the command byte 0x03 followed by the SQL text.

com_register_slave(server_id)

@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.

query(socket, sql, timeout \\ 20000)

@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}}.