Skip to content

Nine servers, one connection type, different answers

MariaDB, Percona, TiDB, Vitess and five more arrive through the same MySQL connection, and protocol compatibility is not capability compatibility.

There is no MariaDB button in the connection dialog. Anyone looking for how to make a MariaDB GUI client connect here gets a two-word answer - pick MySQL - because there is no mariadb type id. The same is true of eight other servers.

One connection type, nine servers

Nine engines reach the mysql provider, which is mysql2/promise over port 3306 with a pooled connection: MariaDB, Percona Server for MySQL, TiDB, Vitess, StarRocks, Apache Doris, OceanBase, SingleStore and Databend. None has a driver, a provider module or a reference document of its own. Each speaks the protocol the driver already speaks, so it connects through that driver unchanged.

The overview panel does not rename the server for you. VERSION() is the only thing that says which engine answered. MySQL returns a bare number, so the provider supplies the vendor name; MariaDB, TiDB, Vitess and OceanBase return a build string that already names themselves, and it is passed through untouched, because prefixing it would assert a vendor the server never claimed. StarRocks and SingleStore give nothing to key on: version() on StarRocks returns a fictitious 5.1.0, and the real build is only in current_version(), which the provider does not read.

How a MariaDB GUI client connects, and where the recipe bites

Discrete fields: host, port 3306, database, user, password. validate() requires host and database only when no connection string is given. One command gives you a fixture to point at - localhost:3306, database app, user root:

docker run --rm -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=app \
  -p 3306:3306 mysql:8

If Studio itself runs in a container, localhost is the wrong host - join the fixture network and use the service name.

The connection-string form is the same recipe in one line, and it carries a trap. When connectionString is supplied, buildPoolConfig() returns { ...baseConfig, uri } and skips the discrete-fields branch entirely, so timezone, connection.ssl and the cloud SSL auto-detection are ignored. Those settings have to be encoded in the URI itself:

mysql://root:secret@db.internal:3306/app?ssl-mode=VERIFY_IDENTITY

The paste box reads that query string, so VERIFY_IDENTITY arrives as verify-full. PREFERRED is deliberately not mapped: mapping it to disable would downgrade a connection that was in fact encrypted, and mapping it to require is the mirror-image guess, so the paste banner names the parameter it declined to act on instead.

Two more things the pool does not do. Only max is honoured, as mysql2’s connectionLimit, default 10. And there is no server-side query timeout, so a runaway query is not auto-killed; cancellation is explicit, through KILL QUERY against the recorded thread id.

Where the support levels diverge

These nine servers all reach the same provider, but their support levels differ sharply: four are Full, four are Partial, one is query editor only, and no managed MySQL service has been probed at all. A name enters the table below only after a live probe ran every introspection surface against a real instance through the real provider. The version column is what that server reported on that day, not a supported range.

Engine Support Probed version The part you would not guess
Percona Server for MySQL Full 8.4.11-11 Nothing on screen says Percona; version() answers a bare number
MariaDB Full 12.3.2-MariaDB performance_schema ships off; only 12.3 was probed
TiDB Full 8.0.11-TiDB-v8.5.1 Explain fails; a fresh table reads 0 rows until statistics catch up
Vitess Full 8.0.43-Vitess A running query cannot be cancelled; vtgate refuses KILL QUERY
StarRocks Partial 3.3.22 Row counts and sizes are hard zeros; no index is reported
Apache Doris Partial 4.1.3-rc02 A foreign key is invisible to the diagram and unenforced
OceanBase Partial 5.7.25-OceanBase_CE-v4.4.2.1 No performance_schema database at all, so health fails
SingleStore Partial 9.1.1 A 2000-row table reads 0 rows and 0 B in every panel
Databend Query editor only v1.2.925-patch-11 Parameterised reads are refused, so only the editor works

Full means every introspection surface answered; a caveat may still note data that is present but inaccurate. Partial means the editor works and parts of the object browser or the monitoring dashboard are blank. Query editor only means SQL runs and nothing else does: usable, but not as a way to manage a database.

Amazon RDS for MySQL, Aurora MySQL, Cloud SQL, Azure Database for MySQL and PlanetScale all speak this protocol, and none of them appears above, because none has been reached for a probe. Untested is not unsupported, and it is not supported either.

Statistics that ship switched off on some of them

MariaDB ships with performance_schema off. Measured on 12.3.2, @@performance_schema is 0. The tables still exist, so the metric queries do not fail - they return a row of NULLs, and every field of getPerformanceMetrics() with nothing behind it is left out of the object entirely. The cache-hit ratio, queries per second and buffer-pool usage are absent, not zero. The digest table is selectable and answers zero rows, so the slow-query list is empty rather than an error. Start the server with performance_schema=ON and the figures appear. information_schema, PROCESSLIST, schema introspection, sizes, row counts and EXPLAIN FORMAT=JSON are unaffected.

One metric runs the other direction. deadlocks reads Innodb_deadlocks, which MariaDB publishes and MySQL does not - measured as an empty SHOW STATUS result on both 8.0.46 and 26.7.0. It is the single performance figure a default MariaDB reports and a stock MySQL server does not.

OceanBase is the harder case: its tenants have no performance_schema database at all, so the queries raise rather than answering NULLs and the health read fails outright, setting the header badge to Slow. That badge is not latency. It is the health request failing, and health is exactly what that engine refuses.

The same absent-versus-zero rule governs index sizes. The per-index byte figure comes from mysql.innodb_index_stats, which needs SELECT on the mysql schema and has no row at all for a MyISAM table. In both cases the panel reads N/A rather than a 0 B the server never reported.

What not to generalise from MySQL to the rest

Four habits to break.

Do not assume the Explain panel works. EXPLAIN FORMAT=JSON is one statement built for every engine on this type id, and five of the nine reject it: TiDB, StarRocks, Doris and Databend fail to parse it, and SingleStore answers ER_PARSE_ERROR on both wire protocols because its grammar is EXPLAIN JSON. A plain EXPLAIN runs in the editor on Doris, SingleStore and Databend. The panel is what is missing, not the planner.

Do not read a zero as a measurement. StarRocks and SingleStore leave information_schema.TABLES zeroed and keep the real numbers elsewhere, so a populated table looks empty; on SingleStore, running ANALYZE does not change what the panels read. TiDB and Doris read zero immediately after a load and then correct themselves with no ANALYZE at all - on Doris the true numbers appeared about a minute later. Even on stock MySQL, TABLE_ROWS is an InnoDB estimate rather than a count and index scans is CARDINALITY rather than a usage counter.

Do not assume a foreign key is a foreign key. On Doris, ADD CONSTRAINT is accepted and SHOW CONSTRAINTS lists it, but KEY_COLUMN_USAGE is empty, so the ER diagram draws nothing - and an order row referencing a customer that does not exist inserts successfully, because the constraint is a planner hint there. SingleStore refuses them outright, and with ignore_foreign_keys on it accepts an inline one and strips it.

Do not expect agent AUTO mode anywhere on this type id. The read-only execution profile is database-native, and only PostgreSQL, SQLite and DuckDB implement it. An auto run against any of these nine ends engine-unsupported. PLAN mode does open on every one of them: it is toolless, executes nothing, and drafts a statement for a human to run. On Databend that difference is legible: the schema read fails, so a plan run succeeds and drafts nothing, saying it was given no inventory of this database and asking for the table and column names rather than inventing a schema.

One connection type is a statement about a wire protocol. It is not a statement about what the server on the other end will answer when a panel asks it a question.