MySQL client/server protocol framing.
Every MySQL wire packet is a 4-byte header — <<length::24-little, sequence_id::8>> —
followed by exactly length payload bytes.
Split packets (>= 16 MiB)
A logical payload of 0xFFFFFF bytes or more does not fit the 24-bit length field, so
the peer splits it: each 0xFFFFFF-byte packet means more follows, and the sequence
terminates with the first packet shorter than 0xFFFFFF — which is zero-length when
the logical payload is an exact multiple of 0xFFFFFF.
Reading a continuation packet as a fresh header does not fail loudly: the reader
desynchronises mid-payload and every subsequent frame is garbage. read_packet/2
therefore reassembles the whole logical payload before returning, and raises on a
short or truncated read rather than returning a partial payload.
Errors raised here never include payload bytes — only lengths, frame indices and the transport reason.
Summary
Types
MySQL packet sequence id — wraps at 255.
A socket tagged with its transport module, so the same framing serves both the plaintext and the TLS-upgraded connection.
Functions
Frames payload under sequence_id, splitting it when it reaches 16 MiB.
Reads a length-encoded integer, returning {value, rest}.
Reads a length-encoded string, returning {value, rest}.
Reads one logical packet, reassembling a split (>= 16 MiB) payload.
Frames payload under sequence_id and writes it to socket.
Types
@type sequence_id() :: 0..255
MySQL packet sequence id — wraps at 255.
@type socket() :: {:gen_tcp, :gen_tcp.socket()} | {:ssl, :ssl.sslsocket()}
A socket tagged with its transport module, so the same framing serves both the plaintext and the TLS-upgraded connection.
Functions
@spec encode(binary(), sequence_id()) :: iodata()
Frames payload under sequence_id, splitting it when it reaches 16 MiB.
A payload that is an exact multiple of 0xFFFFFF bytes is terminated with a
zero-length packet, mirroring what read_packet/2 expects to read. Sequence ids
increment per frame and wrap at 255.
@spec lenenc_int(binary()) :: {non_neg_integer(), binary()}
Reads a length-encoded integer, returning {value, rest}.
Reads a length-encoded string, returning {value, rest}.
@spec read_packet(socket(), timeout()) :: {sequence_id(), binary()}
Reads one logical packet, reassembling a split (>= 16 MiB) payload.
Returns {sequence_id, payload} where sequence_id is the id of the last frame of
the sequence. Raises if the stream ends or stalls mid-packet.
@spec send_packet(socket(), binary(), sequence_id()) :: :ok | {:error, term()}
Frames payload under sequence_id and writes it to socket.