Notes · 2026-08-23

SQLColumns segfaults on a NULL NUMERIC_PRECISION from mongosqld

MySQL Connector/ODBC 9.4 runs strtol() on the NULL NUMERIC_PRECISION the MongoDB BI Connector reports for a DECIMAL column, and the process dies inside SQLColumns.

#0  __GI_____strtol_l_internal (nptr=0x0, ...)
#1  get_buffer_length (..., sqltype=3 /* SQL_DECIMAL */, col_size=0) at driver/catalog.cc:626
#2  columns_i_s (..., table="adbc_t", ...) at driver/catalog.cc:962
#3  MySQLColumns (...) / SQLColumnsW (...)

MongoDB has no SQL wire protocol of its own. The BI Connector is MongoDB's own answer to that: mongosqld sits in front of a MongoDB instance and presents each collection as a SQL table over the MySQL wire, announcing itself as 5.7.12 mongosqld v2.14.22. So the ODBC route to it is MySQL Connector/ODBC — and asking that connector for a table's columns kills the process, if the table has a DECIMAL column in it.

Why it dies

mongosqld's information_schema.columns reports NULL for NUMERIC_PRECISION, NUMERIC_SCALE and CHARACTER_OCTET_LENGTH on every column. Connector/ODBC 9 builds SQLColumns entirely from information_schema — the older SHOW-based path is gone, and there is no NO_I_S option left to switch back to (NO_I_S=1 is accepted and changes nothing). For a DECIMAL column it calls strtol() on that NULL pointer.

It is a per-column crash, not a per-table one, which makes it worse than it sounds: a table with no decimal column comes back with a clean six-column result and rc 0, and nothing in the return code separates the two cases.

./probe_c "Driver=…libmyodbc9w.so;…;PLUGIN_DIR=…;" columns adbc_big   # SQLColumns rc=0, six columns
./probe_c "Driver=…libmyodbc9w.so;…;PLUGIN_DIR=…;" columns adbc_t     # Segmentation fault

Both of those are a standalone C program against libmyodbc9w.so with nothing else in the picture. The workaround is to stop calling SQLColumns here and describe a zero-row SELECT * FROM <table> WHERE 1=0 instead — which is where a table's columns already come from on the schema path. It is keyed on SQL_DBMS_VER, the 5.7.12 mongosqld v2.14.22 string out of the handshake, rather than on SELECT version(), which answers a bare 5.7.12 and names nothing.

A second segfault, in the handshake

mongosqld offers only mysql_native_password, whose client-side plugin Connector/ODBC 9 no longer links in — it ships as a loadable .so beside the driver, and the generic tarball's compiled-in search path is /usr/local/mysql/lib/plugin. Where Dolt reports that as a clean Authentication plugin ... cannot be loaded, here SQLDriverConnect segfaults. Pointing PLUGIN_DIR at the tarball's own plugin directory avoids it entirely, so this one costs a connection property and nothing else.

COM_STMT_PREPARE is a third thing to set: it comes back as error 1295, This command is not supported in the prepared statement protocol yet. With NO_SSPS=1 the connector substitutes bound parameters into the SQL text and the parameterised SELECT works.

What the workload could and could not do

With those three settings the whole read side runs: int64, double, string (including "héllo 🚀" — the emoji survives the round trip), booleans as int8, timestamps, the all-NULL row, the parameterised SELECT, a 100,000-row batched read, catalog and schema metadata, and the error text. Because mongosqld translates SQL into MongoDB aggregation pipelines, the entry also runs a filtered count, a GROUP BY on a boolean field and a COUNT(DISTINCT _id) over the ObjectId every document carries. Fetch is 128k rows/s over 100,000 documents.

The write side does not exist. CREATE TABLE, INSERT and DROP TABLE answer 1105 … requires --writeMode, UPDATE/DELETE/TRUNCATE are not in the grammar at all, and --writeMode is refused together with a --schema path — so the collections are loaded with mongosh and the entry is read-only. Three mapping facts follow from the BI Connector's type system rather than from the driver: there is no binary type (bson.Binary is rejected as a DRDL Mongo type and varbinary/binary/bindata as DRDL SQL types; a sampled binData field maps to varchar and then reads back NULL), so bytes are stored and read as text; every decimal is described DECIMAL(65,20) whatever is in it, past what a 128-bit decimal holds, so those columns arrive as their exact text; and _id is an ordinary column, so it appears in every SELECT *.

One platform caveat. Through MySQL Connector/ODBC 8.4.0 on Windows this entry failed at one check only: héllo 🚀 stored byte-exact and read back as héllo ??? on every read path, because 8.4.0 substitutes ? for a non-BMP character before either C type sees it. Connector/ODBC 26.7.1 reads it correctly, and the entry passes.

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