A MySQL-compatible warehouse can be mounted - #315
Merged
Merged
Conversation
WaylandYang
force-pushed
the
feat/mysql-wire-protocol
branch
from
September 4, 2026 18:05
a4f0f84 to
a77cd8c
Compare
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: WaylandYang <wayland0916@gmail.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: WaylandYang <wayland0916@gmail.com>
WaylandYang
force-pushed
the
feat/mysql-wire-protocol
branch
from
September 4, 2026 18:28
a77cd8c to
bc84b19
Compare
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.
Closes #303. Also settles the line 0018 left open — that record landed the HTTP family and skipped the MySQL wire family, so the order the
query_engineheader always described (Postgres, MySQL, HTTP) is now complete.One driver reaches MySQL, MariaDB, TiDB, OceanBase, Doris and StarRocks: they all speak this protocol. The seam did the work —
QueryEnginealready fixed the shape, so this is a new file, five lines in the factory and dispatch, and one widened CHECK (migration0025).The type table is the whole risk
Postgres hands
row_to_jsonthe row and gets JSON back with the column order intact. MySQL has no equivalent, so values are read column by column and the JSON assembled here — which means a table mapping column types to how they are read, and a wrong entry in that table does not raise anything. It silently turns a column into nulls.The table's authority is the driver's own
ColumnType::nameand thecompatiblepredicate of each type, not the MySQL manual. Reading those found two entries that would have been wrong:BIGINT UNSIGNED, notBIGINT, and itsi64decoder explicitly refuses unsigned columns. Matching on the bare names would have sent every unsigned column to the text fallback, which cannot read an integer either — the column would arrive as nulls.DECIMALhas no text or float path. The driver keeps it out off64("floating-point numbers have different semantics") and out ofString.BigDecimalis the only way in, which is whysqlx'sbigdecimalfeature is now on. Without it, money columns — nearly alwaysDECIMAL— would have come back empty.TINYINT(1)also reports asBOOLEANrather thanTINYINT, which the table follows.Decimals go out through the same
coercepath as the Databricks and Snowflake engines, so a value beyondf64precision stays a string instead of becoming a plausible-looking approximation.The other two differences from Postgres
Two spellings for the statement timeout. MySQL uses
max_execution_time(milliseconds), MariaDBmax_statement_time(seconds). Both are tried and a failure to set either is an error rather than a silent downgrade: this layer is what stops a full-table scan from taking the server down, and the outerLIMITdoes not stop it (the scan finishes, then the rows are truncated).information_schemameans the opposite thing. In MySQL a schema is a database, andinformation_schemais one of the system schemas to exclude rather than the catalog to read through. Empty column comments come back as''rather than NULL, so they are filtered — otherwise every table would carry a blank comment.mariadb://is rewritten tomysql://for the driver, since it is the same protocol under another name.Verified
Unit tests cover the type table entry by entry (every integer width signed and unsigned,
DECIMAL,BOOLEAN, the four time types, the binary fallback), the scheme rewrite, and the MySQL dialect passing the same SQL gate as the other engines — backticked identifiers parse where the Postgres dialect would reject them.End to end against a running server:
mysql://andmariadb://both register as themysqlengine, credentials come back ashost:port/dbonly, and an unsupported scheme is refused with a message that now listsmysql://. Migration0025applies and the CHECK holds the new value.cargo fmt --check,cargo clippy -D warnings,cargo test --workspacewithUTOPIA_TEST_REQUIRE_DB=1, andpnpm buildall pass.Verified against a running MariaDB 11.4. The registry is unreachable from this machine's Docker daemon (its internal proxy cannot get out), so the image was fetched over the host's own network and side-loaded. That turned the three untested statements into tested ones, and the live check is now a test in the module, guarded by
UTOPIA_TEST_MYSQL_URLso it skips where no server is running (#316 carries the setup).On the live server:
SELECT 1connects;fetch_schemareturns both tables with types at full precision (decimal(12,2),bigint(20) unsigned) and column comments attached, with empty comments filtered; and a real round trip types every value correctly —DECIMALcomes back as1234.56rather than null,BIGINT UNSIGNEDas the full18446744073709551615,TINYINT(1)astrue,DATEas2023-06-01, and an all-NULL row as nulls in every column. The first two of those are exactly the defects the driver's source predicted, now confirmed both ways.The MariaDB timeout branch ran for real: MariaDB rejects
max_execution_timewithERROR 1193 Unknown system variable, so the fallback tomax_statement_timeis what let the query through.What remains for #316 is narrower than it was: MySQL proper (its
max_execution_timebranch, and its nativeJSONtype — MariaDB aliasesJSONtoLONGTEXT, so that arm went untaken here), plus the protocol-compatible engines.🤖 Generated with Claude Code