Notes · 2026-08-25

Vendor macOS drivers are built for iODBC's four-byte SQLWCHAR

MySQL Connector/ODBC 26.7.1, Virtuoso 7.2.17 and Arrow Flight SQL ODBC 0.9.7 for macOS ship as iODBC-width drivers, and under unixODBC they return empty diagnostics or abort the process.

There are two symptoms, and they arrive in that order.

[H000] [ (0) (SQLDriverConnect)

Every call fails with an empty diagnostic — no SQLSTATE anyone recognises, no message. pyodbc fails the same way. Or, if the driver does connect, the first statement that errors on the server — a DROP of a table that is not there, a typo — kills the process with SIGABRT, nothing on stderr, no crash report. unixODBC's own isql dies the same way.

One type, two widths

macOS has two ODBC driver managers with different SQLWCHAR widths. unixODBC's is two bytes (UTF-16); iODBC's is wchar_t, four bytes, one code point per unit. A driver compiled against one cannot be loaded through the other, whatever install_name_tool says: the wide entry points exchange text in the wrong unit size, and the diagnostic strings come back unreadable — which is where the empty error comes from. Relinking such a driver is not a valid recipe.

Three vendor drivers reach macOS only in that form. MySQL Connector/ODBC 26.7.1 for arm64 links @rpath/libiodbcinst.dylib; it is an iODBC driver. OpenLink Virtuoso's Homebrew build 7.2.17 is one. So is Arrow Flight SQL ODBC 0.9.7 for Apple Silicon, which fronts Flight SQL, InfluxDB 3 and Dremio alike — one binary behind all three. (OpenSearch's macOS pkg links /usr/lib/libiodbc too.)

The abort has a precise cause, and it is not the driver's. unixODBC reads a driver's wide diagnostic on the first SQL_ERROR into a fixed 12-byte stack array — SQLWCHAR sqlstate[6] in DriverManager/__info.c, extract_diag_error_w. A four-byte-SQLWCHAR driver writes 24 bytes there, and the stack protector aborts. Through iODBC the same call returns a proper 42S02. It was reported on 2026-08-25 as lurcher/unixODBC#239; the maintainer closed it and then committed a mismatch check the same day, so the crash is now caught rather than silent. The undocumented driver widths were reported separately, to openlink/virtuoso-opensource#1469 and dremio/warpdrive#16.

Related: The process dies with SIGABRT on the first SQL error, and it is the manager

The supported configuration

Build a second copy of the bridge against iODBC 3.52.16 and use the iODBC drivers through it, keeping the unixODBC-built one for everything else. One build per driver manager. The connector as downloaded also needs its quarantine flag cleared, rpaths for libiodbcinst.dylib and its bundled libssl, and a fresh ad-hoc signature. With that in place, Virtuoso, Flight SQL, InfluxDB 3 and Dremio all pass the workload, and Databend, Doris, GreptimeDB and StarRocks — which fail inside MariaDB Connector/ODBC 3.2.9 at its connect-time DUAL probe or its prepared/binary-literal path — pass through MySQL's own connector in all five languages. The price is that pyodbc, odbc-api and arrow-odbc have no columns for those servers: they link unixODBC and cannot load an iODBC driver at all.

Three width bugs on the way there

The unixODBC overflow above is the first. The second was ours: the bridge's UTF-8 ↔ SQLWCHAR codecs assumed UTF-16 and wrote surrogate pairs into four-byte slots, which showed up as [08S01] (2013) Lost connection to MySQL server during query with the server logging Utf8Error { valid_up_to: 63 }. Every codec now honours sizeof(SQLWCHAR), and the test suite is built a second time with a four-byte SQLWCHAR to keep it that way. The third is MySQL Connector/ODBC 26.7.1's own: it writes UTF-16 code units into iODBC's four-byte slots (surrogate pairs as two units) but does not read bound SQL_C_WCHAR parameters in four-byte units consistently, so plain ASCII comes through inlined as garbage — text is bound narrow for that connector on a four-byte build, at no measurable cost (2.0M rows/s fetch on Databend either way). Virtuoso is the mirror image: on a four-byte build its wide path is correct end to end while its narrow charset is single-byte, so forcing narrow there was the wrong answer.

A postscript that is not about width at all

The C# comparison column on macOS was empty for a long time for an unrelated reason worth knowing. System.Data.Odbc loads libodbc.2.dylib by bare name, which against a from-source unixODBC prefix fails with Dependency unixODBC with minimum version 2.3.1 is required. DYLD_LIBRARY_PATH fixes it — but SIP strips DYLD_* from /bin/bash, so the variable has to be exported inside the runner script rather than around it. Nothing about the drivers or the bridge was involved.

Where this comes from: the adbcBridge compatibility matrix runs one workload through every database's ODBC driver on Linux, macOS and Windows and records each failure with its first error; the row for this database is at https://adbcbridge.org/matrix/#mysql, and the finding is on file with its reproduction in docs/UPSTREAM.md of the repository.