Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions DOCKERHUB.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,12 @@ Fourteen external engines share one interface, and three of them are read-only b

### Engines with no provider of their own

Twenty-one further engines speak the wire protocol of one of the fourteen drivers above, so they connect through it unchanged: pick that driver in the connection dialog. The table has eighteen rows rather than twenty-one because engines that behave identically share a row; all twenty-one are named in it. Every one of them was measured against a real instance rather than assumed, and how much of the product worked is recorded per engine.
Twenty-three further engines speak the wire protocol of one of the fourteen drivers above, so they connect through it unchanged: pick that driver in the connection dialog. The table has nineteen rows rather than twenty-three because engines that behave identically share a row; all twenty-three are named in it. Every one of them was measured against a real instance rather than assumed, and how much of the product worked is recorded per engine.

| Engine | Connect as | Support |
| :--- | :--- | :--- |
| MariaDB | `mysql` | Full |
| MariaDB · Percona Server for MySQL | `mysql` | Full — both are drop-in builds and both were measured rather than assumed: all fifteen surfaces answer and the numbers are correct. Nothing on screen says Percona, though: `version()` answers a bare 8.4.11-11 and the product name is only in `@@version_comment` |
| Percona Distribution for PostgreSQL | `postgres` | Full — behaves as PostgreSQL throughout, with correct row counts and sizes, and unlike the MySQL build it names itself in `version()` |
| TiDB | `mysql` | Full — but a freshly loaded table reads 0 rows and 0 B until TiDB's background statistics catch up, the slow-query panel stays empty, and only a standalone `--store=unistore` server was probed |
| Vitess | `mysql` | Full. Row counts and sizes are exact, but a running query cannot be cancelled: vtgate refuses `KILL QUERY` and the statement runs to completion. Per-index sizes read 0 bytes, and only an unsharded single-shard keyspace was probed |
| Citus | `postgres` | Full — but statistics describe the coordinator, so a distributed table's row count and size are wrong rather than missing |
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ Standalone application only: the embedded `@libredb/studio` package carries no a
| **Apache Cassandra** | `cassandra-driver` (pure JS, no native module) | CQL IDE over the native protocol (port 9042), keyspace browser marking partition and clustering keys, `system_views` overview, uptime and running statements. No EXPLAIN (the keyword is not in CQL), no cancellation (the protocol has none), no maintenance (every operation is a `nodetool` action), and **no row counts or sizes**: the only figures Cassandra publishes are partition estimates from flushed files and whole mebibytes, so neither is shown rather than shown wrong |
| **Redis** | `ioredis` | Command editor, key browser, INFO-based monitoring |

> **Twenty-one more engines have no driver of their own.** The fourteen above are the drivers this build ships. Twenty-one further engines speak one of those wire protocols and connect through an existing driver unchanged, so fourteen drivers reach thirty-five named engines in all. They are MariaDB, TiDB, Vitess, StarRocks, Apache Doris, OceanBase, SingleStore, Citus, TimescaleDB, YugabyteDB, AlloyDB Omni, Apache Cloudberry (incubating), CockroachDB, Materialize and RisingWave (as PostgreSQL or MySQL), Valkey, DragonflyDB, KeyDB and Garnet (as Redis), FerretDB (as MongoDB), and ScyllaDB (as Cassandra). Each was measured against a live instance, and how much of the product works differs per engine. MariaDB, TiDB, Vitess, AlloyDB Omni, Citus, TimescaleDB, YugabyteDB, Valkey, DragonflyDB, KeyDB and FerretDB behave as their driver's own engine, though three of them report statistics you should not trust: a Citus distributed table and a TimescaleDB hypertable report row counts and sizes that are wrong rather than missing, and YugabyteDB reports 0 until you run `ANALYZE`. Vitess is not one of those three, its row counts and sizes being exact to the byte, but a running query cannot be cancelled there: vtgate refuses `KILL QUERY` and the statement runs to completion. AlloyDB Omni is not one of them either, reporting 2000 rows for 2000 and 270336 bytes for 270336, but two things there surprise: `version()` names AlloyDB nowhere, so the version panel cannot be told apart from a stock PostgreSQL 17, and eight of AlloyDB's own `google_ml` tables list in the object browser, which any role that can connect at all may also read. StarRocks reports itself as MySQL 5.1 and loses its overview, health and session panels, its monitoring dashboard rendering six panels with the session one carrying the engine's own refusal; Apache Doris - the engine StarRocks is a fork of - loses only the overview and health panels, to one statement form its grammar rejects, and is the more trustworthy of the two where it counts: it reports 2000 rows and 10187 bytes for a table holding exactly that, where StarRocks reports zeros, though a freshly loaded table there reads 0 for about a minute before its background statistics land, no index is ever reported, and a foreign key is accepted, listed by `SHOW CONSTRAINTS`, invisible to the ER diagram and unenforced; Cloudberry loses the monitoring dashboard and its table and index statistics, all three to one MPP planner restriction, and reads a foreign key back as though it were enforced when it is not, though its row counts are correct; CockroachDB loses the object browser and the size panels; OceanBase answers fourteen of the fifteen surfaces but only twelve of them usefully, health failing outright because its tenant has no `performance_schema` database at all and every size reading 0 B, though its row counts are correct once `ANALYZE TABLE` has run; SingleStore lost five surfaces to a cause that was ours rather than its own - the provider sent every statement through the prepared-statement protocol, which SingleStore refuses for the `SHOW` and `EXPLAIN` statements four panels need - and four of those five are now recovered, its Explain panel being the one that is not, because there the grammar wants `EXPLAIN JSON` and the statement fails on either protocol; its numbers are still missing rather than wrong, a 2000-row table reading 0 rows and 0 B with no `ANALYZE` able to change it; ScyllaDB loses five surfaces and Test Connection with them, all six to one absent keyspace - the overview, health, performance-metrics, active-session and monitoring panels read Cassandra's `system_views` virtual tables and ScyllaDB has no `system_views` keyspace at all - those five now degrade to empty rather than throwing, so Test Connection passes and the dialog saves the connection, which it could not do at all until that change - while the editor and the object browser work in full, every one of 18 CQL types reading back byte-identically to the Cassandra 5.0.9 probed in the same pass; Materialize and RisingWave are query-editor-only. Garnet behaves as Redis and is the one relative here whose own version was available and went unread: the overview shows Redis 7.4.3 while `INFO` also carries `garnet_version:2.1.5`, and two of its readings are absences wearing a value, every size showing 0 B because it publishes no `used_memory` and the cache hit ratio showing 100% because it publishes no keyspace counters. The per-engine detail, with the exact version probed, is in [`docs/providers/README.md`](docs/providers/README.md#wire-compatible-engines) — we publish a name only after connecting to it, so a name absent there is untested rather than unsupported.
> **Twenty-three more engines have no driver of their own.** The fourteen above are the drivers this build ships. Twenty-three further engines speak one of those wire protocols and connect through an existing driver unchanged, so fourteen drivers reach thirty-seven named engines in all. They are MariaDB, Percona Server for MySQL, TiDB, Vitess, StarRocks, Apache Doris, OceanBase, SingleStore, Citus, Percona Distribution for PostgreSQL, TimescaleDB, YugabyteDB, AlloyDB Omni, Apache Cloudberry (incubating), CockroachDB, Materialize and RisingWave (as PostgreSQL or MySQL), Valkey, DragonflyDB, KeyDB and Garnet (as Redis), FerretDB (as MongoDB), and ScyllaDB (as Cassandra). Each was measured against a live instance, and how much of the product works differs per engine. MariaDB, both Percona distributions, TiDB, Vitess, AlloyDB Omni, Citus, TimescaleDB, YugabyteDB, Valkey, DragonflyDB, KeyDB and FerretDB behave as their driver's own engine, though three of them report statistics you should not trust: a Citus distributed table and a TimescaleDB hypertable report row counts and sizes that are wrong rather than missing, and YugabyteDB reports 0 until you run `ANALYZE`. Vitess is not one of those three, its row counts and sizes being exact to the byte, but a running query cannot be cancelled there: vtgate refuses `KILL QUERY` and the statement runs to completion. AlloyDB Omni is not one of them either, reporting 2000 rows for 2000 and 270336 bytes for 270336, but two things there surprise: `version()` names AlloyDB nowhere, so the version panel cannot be told apart from a stock PostgreSQL 17, and eight of AlloyDB's own `google_ml` tables list in the object browser, which any role that can connect at all may also read. StarRocks reports itself as MySQL 5.1 and loses its overview, health and session panels, its monitoring dashboard rendering six panels with the session one carrying the engine's own refusal; Apache Doris - the engine StarRocks is a fork of - loses only the overview and health panels, to one statement form its grammar rejects, and is the more trustworthy of the two where it counts: it reports 2000 rows and 10187 bytes for a table holding exactly that, where StarRocks reports zeros, though a freshly loaded table there reads 0 for about a minute before its background statistics land, no index is ever reported, and a foreign key is accepted, listed by `SHOW CONSTRAINTS`, invisible to the ER diagram and unenforced; Cloudberry loses the monitoring dashboard and its table and index statistics, all three to one MPP planner restriction, and reads a foreign key back as though it were enforced when it is not, though its row counts are correct; CockroachDB loses the object browser and the size panels; OceanBase answers fourteen of the fifteen surfaces but only twelve of them usefully, health failing outright because its tenant has no `performance_schema` database at all and every size reading 0 B, though its row counts are correct once `ANALYZE TABLE` has run; SingleStore lost five surfaces to a cause that was ours rather than its own - the provider sent every statement through the prepared-statement protocol, which SingleStore refuses for the `SHOW` and `EXPLAIN` statements four panels need - and four of those five are now recovered, its Explain panel being the one that is not, because there the grammar wants `EXPLAIN JSON` and the statement fails on either protocol; its numbers are still missing rather than wrong, a 2000-row table reading 0 rows and 0 B with no `ANALYZE` able to change it; ScyllaDB loses five surfaces and Test Connection with them, all six to one absent keyspace - the overview, health, performance-metrics, active-session and monitoring panels read Cassandra's `system_views` virtual tables and ScyllaDB has no `system_views` keyspace at all - those five now degrade to empty rather than throwing, so Test Connection passes and the dialog saves the connection, which it could not do at all until that change - while the editor and the object browser work in full, every one of 18 CQL types reading back byte-identically to the Cassandra 5.0.9 probed in the same pass; Materialize and RisingWave are query-editor-only. Garnet behaves as Redis and is the one relative here whose own version was available and went unread: the overview shows Redis 7.4.3 while `INFO` also carries `garnet_version:2.1.5`, and two of its readings are absences wearing a value, every size showing 0 B because it publishes no `used_memory` and the cache hit ratio showing 100% because it publishes no keyspace counters. The per-engine detail, with the exact version probed, is in [`docs/providers/README.md`](docs/providers/README.md#wire-compatible-engines) — we publish a name only after connecting to it, so a name absent there is untested rather than unsupported.

> **Transport security is cross-cutting, not per engine.** The SSH tunnel is opened before the provider connects and the connection is rewritten to the local endpoint, so it is provider-independent: it applies to any connection configured with a host and a port. A connection entered as a connection string instead (an option for MongoDB, Couchbase and ClickHouse) carries neither, so it is not tunnelled; SQLite has neither either. The SSL/TLS panel is honoured by every engine that shows it — which is every engine except the two file-based ones, SQLite and the embedded LibreDB, where no transport exists to secure and no panel is offered. On Trino it is load-bearing rather than optional, because the coordinator refuses a password over plain HTTP. Oracle is the one engine whose mapping carries a caveat worth stating up front: its Thin driver always verifies the certificate chain, so `require` needs the server's CA supplied when that certificate is self-signed, and a connect string pasted whole keeps whatever protocol it names.

Expand Down
28 changes: 28 additions & 0 deletions database-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,19 @@ services:
ports:
- "15432:5432"

# Percona Distribution for PostgreSQL. Same reasoning as `percona-mysql` above, and the
# one measured difference is the opposite of that one's: this build DOES put its own name
# in `version()`, so the product displays it.
percona-postgresql:
image: percona/percona-distribution-postgresql:18.6
container_name: libredb-percona-postgresql
profiles: ["compat"]
environment:
POSTGRES_PASSWORD: "Probe123pass!"
POSTGRES_DB: probe
ports:
- "15440:5432"

# `yugabyted` is the single-node launcher: it starts the master and the tserver in
# one container, and --background=false keeps it in the foreground so Docker owns
# the process and the container does not exit immediately. YSQL listens on 5433,
Expand Down Expand Up @@ -706,6 +719,21 @@ services:
timeout: 5s
retries: 20

# Percona Server for MySQL, a drop-in build of MySQL. Nothing here is Percona-specific:
# the point of the entry is that the probe ran against a real one rather than assuming a
# drop-in behaves like the thing it drops into.
#
# PINNED to 8.4: `version()` answers a bare 8.4.11-11 there and the product name is only in
# `@@version_comment`, so the tag is the only durable record of which build was measured.
percona-mysql:
image: percona/percona-server:8.4
container_name: libredb-percona-mysql
profiles: ["compat"]
environment:
MYSQL_ROOT_PASSWORD: "Probe123pass!"
ports:
- "13307:3306"

# TiDB needs no PD and no TiKV to run here: --store=unistore is its embedded
# single-node storage engine, so this is one container like every other entry, and
# an empty --path keeps that store in memory. TiDB speaks the MySQL protocol on
Expand Down
24 changes: 23 additions & 1 deletion docs/BACKLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ None of it is a GitHub issue.
**Sections**

- [SQL statement reading](#sql-statement-reading) — S2–S7 · 5
- [Drivers and connections](#drivers-and-connections) — D1–D27, D30, D31, U17 · 9
- [Drivers and connections](#drivers-and-connections) — D1–D27, D30–D32, U17 · 10
- [Value interpolation](#value-interpolation) — V1
- [Row editing](#row-editing) — R1
- [Studio UI and query execution](#studio-ui-and-query-execution) — X2–X14, U2–U21 · 10
Expand Down Expand Up @@ -368,6 +368,28 @@ across two connections where the second one's read fails, not through the hook i

---

### D32. The MySQL health panel reports `performance_schema` unavailable on a server that has it

Measured 2026-08-26 while probing Percona Server for MySQL, and confirmed on the baseline in the
same pass, which is what makes it ours: `getHealth()` returns
`slowQueries: [{ query: "Performance schema not available", calls: 0, avgTime: "N/A" }]` on both
`percona/percona-server:8.4` and a stock **MySQL 26.7.0**, while on each of them
`@@performance_schema` is `1` and `getSlowQueries()` on the same connection returns real rows.

So the health reading states an engine capability as absent when it is present, and the panel
beside it disproves the statement. It is a fabricated absence rather than a missing number, which
is the class the absence rule (#477) exists to prevent - the honest reading is either the rows
`getSlowQueries()` can already produce, or no slow-query line at all.

Not Percona's, not new, and not caused by the wire-compatible work - it just took a drop-in build
to notice, because a caveat had to be written about a reading that turned out to be the baseline's.

**Done when:** the health panel's slow-query line either carries the rows the slow-query read
returns or is absent, and the "not available" wording appears only on a server where
`@@performance_schema` is actually 0 - both arms measured, one server of each kind.

---

## Value interpolation

### V1. Query history records the placeholders, not the values that were bound
Expand Down
Loading
Loading