Notes · 2026-08-25

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

unixODBC 2.3.12 and 2.3.14 overwrite their own stack reading the first SQL_ERROR diagnostic from a driver whose SQLWCHAR is four bytes - Virtuoso and Arrow Flight SQL on macOS, Ingres on Linux.

What you see

Connect works. SELECT works. The first statement that fails on the server — a DROP of a missing table, a typo — kills the process. On macOS it is a bare SIGABRT with nothing on stderr and no crash report; unixODBC's own isql dies the same way. On Linux the same class of failure prints:

*** stack smashing detected ***: terminated

Successful statements are unaffected, which is what makes it look like a server problem. It is not. It is the driver manager.

What is actually happening

SQLWCHAR is not one width. unixODBC defines it as two bytes (UTF-16); iODBC defines it as the platform wchar_t, four bytes, one code point per unit. A driver built to the second convention and loaded through the first exchanges wide text in the wrong unit size.

The abort is in the diagnostic path. On the first call that returns SQL_ERRORSQLExecDirect, SQLPrepare, SQLExecDirectW alike — unixODBC reads the driver's wide diagnostic into a fixed 12-byte stack array, SQLWCHAR sqlstate[6] in extract_diag_error_w (DriverManager/__info.c). A four-byte driver writes 24 bytes into it, and the stack protector aborts:

frame #0: libsystem_c.dylib`__stack_chk_fail
frame #1: libodbc.2.dylib`extract_diag_error_w(...) at __info.c
frame #2: libodbc.2.dylib`function_return_ex(level=3, ..., ret_code=-1, ...) at __info.c:5215
frame #3: libodbc.2.dylib`SQLExecDirect(...) at SQLExecDirect.c:527

The same program linked against iODBC 3.52.16 gets the proper diagnostics from the same drivers ([42S02] … SQ074: Line 1: No table no_such_table_xyz) and survives.

How to tell a driver's SQLWCHAR width

Two ways, neither needing the driver's source.

Check what it links. MySQL Connector/ODBC 26.7.1 for macOS arm64 links @rpath/libiodbcinst.dylib; OpenSearch's macOS package links /usr/lib/libiodbc. A driver that links an iODBC library is an iODBC-width driver. Relinking it to unixODBC is not a valid recipe — the widths do not change with the link line.

Or ask it directly. dlopen the driver, run a statement that fails, and call its SQLGetDiagRecW with a buffer pre-filled with 0xAA. A four-byte driver writes UCS-4 — 5b 00 00 00 4f 00 00 00 … for "[Ope", 5b 00 00 00 41 00 00 00 … for "[Apa" — and fills BufferLength × 4 bytes.

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

Which drivers do this

Three in the compatibility matrix. Virtuoso's macOS driver virtodbcu_r.so (Homebrew 7.2.17, whose formula has no unixODBC dependency); the Arrow Flight SQL ODBC driver 0.9.7 armv8 build, which fronts sqlflite, InfluxDB 3 and Dremio; and Actian Ingres 10.1's libiiodbcdriver.1.so on Linux, whose wide entry points take the platform wchar_t and which joined the matrix with the seven entries added on 2026-09-03. Neither of the first two documents the width anywhere.

The Ingres case shows the other half of the same mismatch. unixODBC picks a driver's Unicode path purely by finding SQLConnectW in it, so with Ingres the connection string arrives as its first character and the connect fails before any diagnostic is reached:

08004  786744  E_GC0138_GCN_NO_SERVER  User provided a server class as part of the
       database name (dbname/class), but no servers of that class ... are running in
       the target installation

Virtuoso's Linux Unicode build virtodbcu.so is four-byte too: reached through unixODBC's ANSI entry points, the first failing statement aborts inside libodbc.so.2 with the stack smashing message above, in a 40-line probe with nothing else in the picture.

What to do

Build your client against iODBC and use those drivers through it. Through an iODBC-built bridge, Flight SQL, InfluxDB 3, Dremio and Virtuoso all pass the compatibility workload on macOS. One client build per driver manager: a unixODBC-built one for unixODBC drivers (psqlodbc, sqliteodbc, the MariaDB connector), an iODBC-built one for these. For Ingres, whose narrow entry points are correct and complete, the matrix loads the driver behind a generated ANSI-only forwarding library of 72 one-jump stubs, so unixODBC does the UCS-2 conversion itself and the driver only ever sees narrow calls.

Keeping unixODBC and never letting a statement fail is not a workaround anyone should ship.

Upstream

Found on 2026-08-25 and reported as lurcher/unixODBC#239, with a driver-independent reproduction: a 40-line fake driver compiled with SQL_WCHART_CONVERT triggers it on Linux, its two-byte twin does not, on 2.3.12 and on 2.3.14 built from the release tarball; plus lldb frames from two real macOS drivers and a fix suggestion. It is the same crash as the earlier #227 (Informix), which had been closed without a reproduction. The issue was closed as not planned on 2026-08-25; the maintainer committed a mismatch check the same day (a2acae7: doubled buffers, a sentinel test, a message before the abort), so the crash is now caught rather than silent. A follow-up with a test table and a patch — report the mismatch as a diagnostic instead of aborting, and prime the sentinel on the ODBC 2 path — was posted on the closed issue. The two drivers' undocumented widths were reported separately: openlink/virtuoso-opensource#1469 and dremio/warpdrive#16, both open.

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/#virtuoso, and the finding is on file with its reproduction in docs/UPSTREAM.md of the repository.