Where the native PostgreSQL and MySQL ADBC drivers stop on wire-compatible databases
Fourteen databases in the matrix speak the PostgreSQL wire protocol without being
PostgreSQL, and fourteen speak MySQL's without being MySQL. Each of those has a native
ADBC driver that can be pointed at it: the Apache Arrow project's PostgreSQL driver
(adbc-driver-postgresql 1.12.0) and the ADBC Driver Foundry's MySQL driver (0.6.0). On
2026-09-05 the same seven-step workload ran through both native drivers and through
adbcBridge over each database's own ODBC driver, with delegation turned off so the
comparison is native protocol against ODBC:
connect · SELECT 1 · the entry's eight-column CREATE TABLE plus two INSERTs and a
read · adbc_ingest of 1,000 rows (int32, float64, utf8, date32) with a count check ·
GetTableSchema · GetObjects · read the ingested table back.
Result
| Native driver | adbcBridge over ODBC | |
|---|---|---|
| Both pass | PostgreSQL, YugabyteDB, TimescaleDB, Citus, Cloudberry, MySQL, MariaDB, Percona, MariaDB ColumnStore, TiDB, Dolt, MatrixOne, SingleStore, OceanBase | same 14 |
| Native stops, ODBC passes | — | CockroachDB, Materialize, QuestDB, RisingWave, openGauss, CrateDB, Spanner, ArcadeDB, YDB, Databend, GreptimeDB, Doris, StarRocks, MongoDB BI Connector |
| ODBC stops, native passes | none | — |
Where the native driver stops, and the first error it gives:
| Database | Step | Error |
|---|---|---|
| CockroachDB | SELECT 1 |
could not begin COPY: ERROR: unimplemented: binary format for COPY TO not implemented |
| CrateDB | SELECT 1 |
could not begin COPY: ERROR: line 1:6: no viable alternative at input 'COPY (' |
| YDB | SELECT 1 |
could not begin COPY: … RawStmt: alternative is not implemented yet : 138 |
| openGauss | SELECT 1 |
ReadRecord failed at row 0: Unexpected end of input (expected 2 bytes but found 0) |
| Materialize, QuestDB, RisingWave | connect | the type-resolver bootstrap SELECT oid, typname, typreceive, … FROM pg_catalog.pg_type is rejected |
| ArcadeDB | connect | Expected 5 or 6 columns from type resolver pg_type query but got 0 |
| Spanner | ingest | Primary key must be defined for table "adbc_ing_np" (reads work) |
| Databend | ingest | SyntaxException on the driver's generated CREATE TABLE … INT NULL |
| StarRocks | ingest | Unexpected input 'NULL' on the same generated DDL |
| GreptimeDB | DDL read | invalid decimal precision/scale (1023, 0); ingest: Missing time index constraint |
| Doris | connect | Only support prepare SelectStmt or InsertStmt now |
| MongoDB BI Connector | connect | recv handshake response error: invalid connection attribute at index 0: EOF |
None of these is a defect in the native drivers as such. The PostgreSQL driver reads every
result through COPY … TO STDOUT (FORMAT binary) and learns types from pg_catalog, which
is the fastest possible path against a real PostgreSQL server and simply is not implemented
by the databases that only emulate its wire protocol. The MySQL driver generates MySQL DDL
and uses server-side prepared statements, which Doris, StarRocks and Databend do not
accept. The ODBC drivers those vendors ship know their own servers; adbcBridge inherits
that. The two probes agree with the vendors' own documentation and with the entry notes in
the matrix. The probe script and the raw results are kept with the compatibility sources.
A defect in adbcBridge this run found
adbcBridge can hand a connection to a native ADBC driver instead of opening ODBC at all
(adbc.odbc.delegate, default auto). With adbc-driver-postgresql importable in the same
environment, auto delegates every connection whose ODBC driver is psqlodbc, and it
decides that before knowing what the server is. On CockroachDB, CrateDB, openGauss and YDB
the native driver connects and the first SELECT then fails as in the table above; on
Spanner reads work but adbc_ingest fails because the native driver's CREATE TABLE has no
primary key (adbcBridge's own ingest adds one). Where the native driver cannot connect at
all (Materialize, QuestDB, RisingWave, ArcadeDB) auto falls back to ODBC as designed.
Found on this run, fixed the same day. 0.1.1, released 2026-09-05,
probes the native driver before handing a connection over — one SELECT version() through
it at AdbcDatabaseInit, read back through its own result path — and stays on ODBC when
that fails, with the reason in adbc.odbc.delegate.last_error. Re-run after the fix with
delegation left on, all fourteen PostgreSQL-wire databases pass all seven steps through
adbcBridge.
On a 0.1.0 install the workaround is one database option:
adbc.odbc.delegate=never
(adbcbridge.connect(..., delegate="never") in Python, or ADBC_ODBC_DELEGATE=never in
the environment). The matrix's PostgreSQL-wire rows describe the ODBC path either way,
which is what they always measured.