Repository navigation
Phase 1 complete: wire-type codecs and the response packets - #4
Merged
Merged
Conversation
The first packet body, and the one worth having first: without it every failure in the connection phase is an opaque binary. Checked against the example in the protocol documentation — bytes and their meaning both from the spec, so the test is not just confirming my own reading of the field table. Capability flags are an argument because the SQL state is only sent when CLIENT_PROTOCOL_41 was negotiated, and an ERR packet can arrive during the connection phase before that is settled. The flag is a literal here; a flags module can come when there is a second caller for it.
The branch that matters during a failed login: an ERR packet can arrive before capabilities are settled, and then there is no SQL state to read. Error 1045 is what that actually looks like in practice.
A packet whose capabilities claim 4.1 but which carries no '#' marker raised a FunctionClauseError, naming a private function and telling the caller nothing. This input comes off the network, so it should not be a crash. The marker carries no information — it exists only so that pre-4.1 clients could detect the field's presence — so its absence is read as the server having sent no SQL state.
Every packet body above the frame is built from the protocol's own wire types, and this is the one with branching: four size forms, with boundaries at 251, 2^16 and 2^24. Tested at both sides of each boundary, plus a property over the whole 0..2^64-1 range. The one-byte form stops at 250 rather than 251 because 0xFB means NULL in a text resultset row and 0xFC to 0xFE are the prefixes for the longer forms, so those byte values cannot stand for themselves. 0xFB deliberately matches no decode clause. Where it appears it means NULL, and recognising that belongs to the row parser rather than here.
string<lenenc> is a length-encoded integer followed by that many bytes, so it is built directly on the integer codec — including the multi-byte prefix once a string passes 250 bytes, which the tests exercise. string<NUL> reads to the first terminator. Its property generator excludes zero bytes, because a string that can contain its own terminator has no decodable encoding — a limitation of the wire type rather than of the codec. string<EOF> has no function here. It is whatever remains of the payload, so callers already hold it as `rest`, and wrapping that in a call would add a layer that computes nothing.
The success half of every command response, and the first packet built out of the lenenc codecs: affected_rows and last_insert_id are both length-encoded integers. Checked against the example in the protocol documentation, plus the cases that carry real values — an insert reporting its AUTO_INCREMENT id, and a packet with a trailing info string. Status flags come from one of three shapes depending on capabilities: flags and warnings under CLIENT_PROTOCOL_41, flags alone under CLIENT_TRANSACTIONS, neither otherwise. Expressed as three clauses with guards, since that is what the spec's nested conditionals describe. CLIENT_SESSION_TRACK is not handled. With that capability info becomes a length-encoded string followed by an optional session-state block; the driver will not advertise it, so the server will not send it, and the parsing belongs with the rest of session tracking later.
Last of the three generic response packets. Deprecated since 5.7.5 in favour of an OK packet standing in for it, but still sent to any connection that does not advertise CLIENT_DEPRECATE_EOF. It carries warnings before status flags — the reverse of the OK packet, which holds the same two values the other way round. There is a test whose only purpose is to catch that swap, with values chosen so a transposition cannot pass. Deciding whether a payload beginning 0xFE is this packet, an OK packet standing in for it, or a length-encoded integer needs the payload length, so it is left to whatever dispatches on the payload.
Phase 1 is finished, so its checklist is ticked and the items that were deliberately left out are named: sequence id tracking, the 0xFE dispatcher, and session tracking in the OK packet. Also records what the earlier sections had drifted past — the transport behaviour, the four test tiers and where fixtures live, the branch-and-pull-request rule, gating checks on exit codes rather than filtered output, and that replacing db_connection is an option rather than a prerequisite.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Finishes phase 1. Eight commits, one per green step. 31 tests, 4 of them properties.
What is here
Wire types —
Mysqlex.Protocol.Codecstring<EOF>has no function — it is whatever remains of the payload, which callers already hold asrest. Fixed-width fields stay inline bitstring patterns. So all seven wire types are covered, two of them without code.Response packets —
Protocol.Err,Protocol.Ok,Protocol.Eof, plus aMysqlex.Errorstruct.With these, real server traffic that can now be parsed: a failed login, a successful login, the response to any
INSERT/UPDATE/DELETE/DDL, and any query error with its code, SQL state and message.What to look at
The lenenc one-byte form stops at 250, not 251.
0xFBmeans NULL in a text resultset row and0xFC–0xFEare the prefixes for the longer forms, so five byte values cannot stand for themselves. Tested either side of all three boundaries plus a property across 0..2^64-1.0xFBdeliberately matches no decode clause — it raises. Recognising NULL belongs to the row parser, which has the column context; returningnilfrom a function whose job is integers would be worse.EOF carries warnings before status flags — the reverse of OK, which holds the same two values the other way round. There is a test whose only purpose is catching that transposition, with values chosen so a swap cannot pass.
Capability-gated fields are guard clauses, not
ifs. The OK packet's status section is three clauses matching the spec'sif / else if / else; the ERR packet's SQL state is two. The handshake is full of these, so this is the pattern to agree on now.Spec examples are used as oracles. Every packet here is checked against the byte sequence in MySQL's own documentation, not against fixtures I built from my reading of the field table. That distinction is what would have caught the 2018
server_versionbug: a hand-built fixture encodes the misreading too.Deliberately absent
0xFEis EOF, an OK standing in for EOF, or a length-encoded integer needs the payload length and the command context.CLIENT_SESSION_TRACKin the OK packet — with it,infobecomes a length-encoded string followed by a session-state block. The capability will not be advertised until phase 5.Also
--warnings-as-errorscaught a real one:binary-size(length)with the variable bound outside the match needs the pin operator in current Elixir. That is a semantics change, not a style warning.The roadmap is brought up to date in the last commit — phase 1 ticked, the omissions above named, and the sections that had drifted (transport behaviour, test tiers, the branch-and-PR rule) corrected.