What MySQL Connector/ODBC does to a DATETIME(6), and six other findings
Twelve entries in a compatibility matrix that drives 53 databases through one ODBC workload speak the MySQL wire protocol, and all twelve go through MySQL Connector/ODBC. Running that workload on Linux (9.4.0), Windows (8.4.0, then 26.7.1) and macOS (26.7.1) turned up seven things worth writing down.
A DATETIME(6) arrives with one fractional digit
INSERT … VALUES (?, ?) with datetime(2024,2,29,13,45,10,123456)
SELECT CAST(ts AS CHAR) -- 2024-02-29 13:45:10.100000
Connector/ODBC 9.4 describes a DATETIME(6) result column as COLUMN_SIZE 19 /
DECIMAL_DIGITS 0, and answers SQLGetTypeInfo(SQL_TYPE_TIMESTAMP) with COLUMN_SIZE
21 / MAXIMUM_SCALE 0. Neither number carries the column's real fractional precision. A
client that sizes its bind from the type table — pyodbc does, 21 − 20 = one
fractional digit — sends 13:45:10.123456 as 13:45:10.1. The same value as a SQL
literal, or bound as a string, round-trips all six digits, and NO_SSPS=1 changes
nothing: it happens on the text-substitution path too. The server is not at fault —
MariaDB Connector/ODBC and SingleStore's own driver describe the column 26/6, and pyodbc
is exact through SingleStore's. Filed on 2026-08-29 as
mysql/mysql-connector-odbc#20;
a plain-ODBC probe on 2026-09-04 added the consequence and the root cause. Bind the
timestamp with an explicit scale rather than take the driver's type table at face value.
SIGFPE inside get_column_size()
SHOW VARIABLES and SHOW COLLATION against a MySQL-wire server that reports a zero
character-set width — MatrixOne 4.2.0 — kill the client with SIGFPE in
get_column_size() (utility.cc) during SQLExecDirect, before any fetch. The same
statements against MySQL 8.4.11 are fine. Filed on 2026-08-29 with a gdb backtrace as
mysql/mysql-connector-odbc#21.
The only workaround is not to issue them.
SQLColumns segfaults on a NULL NUMERIC_PRECISION
MongoDB's BI Connector (mongosqld) reports NULL for NUMERIC_PRECISION,
NUMERIC_SCALE and CHARACTER_OCTET_LENGTH on every column, and Connector/ODBC 9 builds
SQLColumns entirely from information_schema (NO_I_S=1 is accepted and changes
nothing). For a DECIMAL column it runs strtol() on that NULL pointer and the process
dies — per column, not per table, so nothing in a return code separates a safe table from a
fatal one. The same server's handshake segfaults too, in the bundled libmysqlclient
(authsm_begin_plugin_auth, strcmp() on a NULL plugin name), when the greeting names no
auth plugin and PLUGIN_DIR is unset. Both have standalone C reproductions. Point
PLUGIN_DIR at the connector's own lib/plugin, and describe a zero-row SELECT rather
than call SQLColumns.
Related: SQLColumns segfaults on a NULL NUMERIC_PRECISION from mongosqld
Windows: NO_SSPS=1 against every server that is not MySQL
[HY000] (2031) No data supplied for parameters in prepared statement
Connector/ODBC 8.4.0 fails at the first bound parameter against TiDB, MariaDB, Dolt,
MatrixOne and OceanBase, in pyodbc identically; against MySQL itself and Percona the
default works, and 26.7.1 still needs it. With NO_SSPS=1 the connector substitutes
bound parameters into the statement text.
Related: A Unicode string parameter cannot reach a MySQL-wire warehouse's prepare
Windows: result sets read as 3-byte utf8
héllo 🚀 stores byte-exact and reads back as héllo ??? on every read path including
pyodbc; SQL_C_CHAR and SQL_C_WCHAR both return 3f 3f 3f for the emoji, and
CHARSET= makes no difference. It is a class, not five incidents: Connector/ODBC 8.4.0
decodes result sets from any MySQL-wire server that does not implement the character-set
session variables as 3-byte utf8 — on Windows that is Databend, GreptimeDB, MatrixOne,
StarRocks and MongoDB BI. The same driver reads 🚀 from MySQL, Percona, MariaDB, TiDB and
Dolt, which expose the variables; 9.4 on Linux and 26.7.1 on Windows read them all.
Linux: the driver stops loading once something imports pyarrow
OSError: /lib/x86_64-linux-gnu/libstdc++.so.6: cannot allocate memory in static TLS block
Through unixODBC it arrives as the misleading Can't open lib '…/libmyodbc9w.so' : file
not found. It is not about surplus static TLS running out. libarrow reaches libstdc++'s
thread-locals
through the dynamic TLS model, and the first such access to a dlopened library with no
static TLS offset makes glibc record it as dynamic-TLS-only for the rest of the process'
life. Connector/ODBC reaches the same thread-locals (_ZSt11__once_call,
_ZSt15__once_callable) through initial-exec, which requires that offset, so after
pyarrow the driver cannot be loaded at all, deterministically. import pandas and import
adbc_driver_manager.dbapi both do it; import numpy does not. Load the ODBC driver
before anything imports pyarrow, or preload libstdc++ from outside the process:
LD_PRELOAD=/lib/x86_64-linux-gnu/libstdc++.so.6 python your_program.py.
GLIBC_TUNABLES=glibc.rtld.optional_static_tls=N does not help. The analysis
is in docs/TROUBLESHOOTING.md.
macOS: an iODBC driver, and its wide parameters
Connector/ODBC 26.7.1 for macOS arm64 links @rpath/libiodbcinst.dylib — it is an iODBC
driver, four-byte SQLWCHAR. Loaded through unixODBC (two-byte) every call fails with an
empty diagnostic, [H000] [ (0) (SQLDriverConnect), pyodbc the same; relinking is not a
valid recipe. Through an iODBC-built stack it writes UTF-16 code units into the four-byte
slots, surrogate pairs as two units, but does not read bound SQL_C_WCHAR parameters in
four-byte units consistently — plain ASCII comes back inlined as garbage. Bind text narrow
there, through UTF-8; it costs nothing measurable.
Related: Vendor macOS drivers are built for iODBC's four-byte SQLWCHAR
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.