Notes · 2026-08-25

SQL_ERROR with no diagnostic, and a status array nobody writes

sqliteodbc 0.99991 fails SQLGetData(SQL_C_NUMERIC) with a bare SQL_ERROR and zero diagnostic records, and accepts SQL_ATTR_PARAM_STATUS_PTR without ever writing to it.

Two defects in sqliteodbc 0.99991 on Linux, both of the kind that make a caller doubt its own code rather than the driver, because in both cases the driver's answer is silence rather than an error you can quote. Found on 2026-08-25, running one workload through the 46 databases' ODBC drivers of that campaign.

SQLGetData(SQL_C_NUMERIC) returns SQL_ERROR with nothing behind it

Ask for a column as SQL_C_NUMERIC and you get SQL_ERROR. Then ask what went wrong and the driver has nothing to say: SQLGetDiagRec returns zero diagnostic records. No SQLSTATE, no message, no native code. There is nothing to search for, which is why this note exists.

The driver is not confused about what happened. Request the same conversion the other way, through SQLBindCol, and it answers properly:

HY003 invalid type 2

HY003 is the honest reply. It is only the SQLGetData path that loses it. So the same unsupported conversion is a documented refusal through one entry point and an unexplained failure through the other.

What to do: do not ask this driver for SQL_C_NUMERIC at all. Read decimal and numeric columns as text with SQL_C_CHAR and parse them yourself; a driver's textual rendering of a decimal is lossless, and it is exact into a 128-bit decimal for anything within precision 38. That is the route adbcBridge takes for every driver, which is why this defect costs it nothing.

SQL_ATTR_PARAM_STATUS_PTR is accepted and never written

Set a parameter status array before executing a parameter array, and the driver accepts the attribute — SQLSetStmtAttr succeeds — and then never writes into it. A caller that trusts the API reads back whatever was in that memory before the call: uninitialised status values, indistinguishable from real ones.

This is a shape worth recognising, because it is not unique to this driver. An out-parameter a driver accepts and never writes is invisible unless you prepare for it. The generic defence is to pre-fill the memory with a value that cannot be mistaken for an answer, so "not written" reads as unknown rather than as a plausible result. adbcBridge does exactly that for row counts, for a different driver that never wrote SQLRowCount: the variable is pre-filled with all-ones, which reads as -1 at either SQLLEN width, and -1 is what ODBC and ADBC already use for an unavailable row count. Every driver in the matrix that does write the out-parameter is unaffected — sqliteodbc among them, which writes 0 after DDL and 1 after a single-row INSERT, though it answers 0 for SQLRowCount on an open SELECT.

Related: Where PostgreSQL-wire servers part company with PostgreSQL

Three smaller ones in the same family

Recorded alongside those two, all from the same driver and all silent:

The last one is survivable only because of something the driver gets right: its result set is materialised in memory, and SQLFetchScroll(SQL_FETCH_ABSOLUTE) re-reads any row correctly with a plain SQLFetch resuming afterwards. That makes the 65,536-char width bindable — the truncated values can be re-read individually rather than forcing the whole result set to row-at-a-time reads, which is what an unbounded column costs on drivers with no such repair path.

The route around it

Where a native ADBC driver for the database exists, the best answer to an ODBC driver's quirks is not to use it. adbcBridge delegates SQLite to adbc_driver_sqlite when that driver is installed, so none of the above is on the path at all; the ODBC route is what runs when it is not installed, and it is the one the matrix measures.

No vendor issue is filed for any of these. They sit in the "documented here, not yet reported" table of docs/UPSTREAM.md with their conditions and first errors, so there is no fix version to name; a reproduction contributed by anyone is welcome.

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