Four conversions taos-odbc calls "not implemented yet", and one that aborts
What you see
Bind a TDengine timestamp column and the whole result set fails:
#1 Column converstion to 'SQL_C_TYPE_TIMESTAMP[0x5d/93]' not implemented yet
SQLBindParameter answers the same way for a timestamp parameter. It is not a missing
feature of the type — SQLGetData on the same column converts to TIMESTAMP_STRUCT
perfectly well. taos-odbc looks a conversion up by the exact (C type, SQL type, TDengine
type) triple and its table is sparse; the bound-column and bound-parameter routes are simply
not in it.
Four such gaps, each with a plain-ODBC program, were filed on 2026-08-28 as taosdata/taos-connector-odbc#63, against the connector built from source at 3.3.6.13; open.
SQLBindColtoSQL_C_TYPE_TIMESTAMP, above. Its bound conversions are the numeric C types,SQL_C_CHAR,SQL_C_WCHARandSQL_C_BINARY, so the way through is to take a timestamp column as text and parse it — which also keeps the column inside a block cursor instead of dropping the whole result set to one-rowSQLGetData.SQL_TYPE_TIMESTAMPparameters, the same error. Send the value as"YYYY-MM-DD HH:MM:SS.ffffff"SQL_VARCHARtext and the server parses the literal.BOOLparameters from any C type but one.SQL_C_BIT→SQL_BITis "not implemented yet"; the single route into a TDengineBOOLcolumn isSQL_C_SBIGINTdescribed asSQL_TINYINT. An integer describedSQL_INTEGERis not in the driver's table either, and the words"true"/"false"in a VARCHAR are parsed there withstrtolland refused.- Any
DECIMALcolumn fails the wholeSELECT:'UNKNOWN'[21] not supported yet— 21 isTSDB_DATA_TYPE_DECIMAL64, and the 128-bitTSDB_DATA_TYPE_DECIMALis missing from the type table too. TDengine 3.3.6 has the type; its driver does not. Read the column as its exact text instead.
One connection keyword matters before any of this: without TIMESTAMP_AS_IS=1, taos-odbc's
own setting, the driver describes every TIMESTAMP column as an SQL_WVARCHAR holding
formatted text, so timestamps never arrive as timestamps at all.
The assert that takes the process with it
src/core/env.c, _env_set_odbc_version: given SQL_OV_ODBC3_80 it builds the 01S02
"substituted" diagnostic record as written — and then calls OA_NIY(0), which aborts the
process. SQL_OV_ODBC3_80 is exactly what Rust's odbc-api sets, so a Rust program dies at
environment setup, before it has issued a statement. Removing that one call makes the
function return the SQL_SUCCESS_WITH_INFO its own code has already prepared; the other way
round it is not to let odbc-api touch this driver at all. Through everything else the
driver is fast — 933,052 rows/s from Rust on the 20,000-row fixture, against 403k rows/s
through the compatibility run's own reader.
Every table starts with a TIMESTAMP, and that rules out generated DDL
CREATE TABLE t (i INT, ...)
-> First column must be timestamp (0x80002641)
The key is range-checked as well as mandatory: non-NULL, distinct per row and inside the
database's retention window. So feeding it integers does not help either —
INSERT INTO t VALUES (1, ...) is Timestamp data out of range (0x8000060B), one microsecond
after the epoch being far outside the default KEEP of 3650 days. Any generated CREATE
TABLE that declares no timestamp column, and any positional INSERT that fills columns from
the first one, is therefore impossible against TDengine; a bulk load has to go into a table
whose first column is a timestamp, which the driver's real parameter binding handles.
Two more entry-level facts. Identifiers quote with a backtick, and "..." is a string
literal — FROM "adbc_big" is syntax error near '"adbc_big"'. And an int64 payload whose
values all fit in 32 bits gets bound as SQL_C_SLONG/SQL_INTEGER, which the driver's table
maps only to a TDengine INT; values past 2³¹ go as SQL_C_SBIGINT/SQL_BIGINT and reach a
BIGINT column.
On Windows it is the system code page
The Windows client package installs taos_odbc.dll as the TDengine driver. Two separate
things go wrong there. First, the 3.4.2.5 client cannot speak the native protocol to a 3.3.6
server at all: TCP connects and the server drops the connection with read invalid packet in
taosd's log. Connecting over websocket through taosadapter instead — a URL={ws://…}
connection string against a published 6041 — reaches the server and passes every column
but one.
That one is the NCHAR column, and it is the ANSI code page rather than anything
TDengine-specific: taos_odbc takes the client character set from GetACP() and ignores
CHARSET_FOR_COL_BIND, CHARSET_ENCODER_FOR_COL_BIND and a UCRT setlocale(".UTF-8")
alike, so with a 1252 system code page reading héllo 🚀 fails
[iconv] Character set conversion for UTF-32LE to CP1252 failed
through SQL_C_CHAR and SQL_C_WCHAR both, pyodbc identical. Only a UTF-8 system code
page changes that.
Related: Most Windows-only ODBC failures are one thing wearing different clothes
Finally, if you probe this driver with pyodbc, put it on the narrow path first
(conn.setencoding(encoding="utf-8", ctype=pyodbc.SQL_CHAR)). pyodbc sends statement text as
UTF-16 by default, and taos-odbc fails any statement carrying a non-ASCII character with
conversion for 'UTF-8' to 'UTF-8' not found.
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/#tdengine, and the finding is on file with its reproduction in docs/UPSTREAM.md of the repository.