Notes · 2026-08-29

A bound NULL becomes an empty string, and a parameter array stops at set 1

What clickhouse-odbc 1.5.5 does when you write through it - SQL_NULL_DATA ignored when a value buffer is bound, parameter arrays that land two rows of five, and no affected-row count.

Writing through clickhouse-odbc

Reading through clickhouse-odbc 1.5.5 is uneventful. Writing is where the surprises are, and each of the three below can pass unnoticed: the call returns SQL_SUCCESS and the wrong data, or no data, is what lands in the table. They were found running one workload through the driver against ClickHouse 26.7.5.10 and reported on 2026-08-29.

A bound NULL is sent as an empty string

SQL_NULL_DATA is ignored whenever a value buffer is bound to the parameter. The driver sends the parameter empty instead of null, and what happens next depends on the column type:

SQL_DEFAULT_PARAM behaves the same way. What works is binding the parameter with a NULL value pointer — then the null reaches the server as a null. That is the workaround, and it is the one the compatibility entry uses.

Two related facts make this harder to spot than it should be: SQL_DESCRIBE_PARAMETER is N, and SQLDescribeParam answers SQL_UNKNOWN_TYPE, so a client cannot ask the driver what it is about to do with a parameter.

A five-set parameter array lands two rows

Parameter arrays here are not ODBC's. The driver implements its own protocol, described in clickhouse-odbc#324: SQLExecute sends set 0, and each SQLMoreResults sends the next set. On 1.5.5 that sequence breaks after one step. The first SQLMoreResults does send set 1 — and then answers SQL_NO_DATA, after which nothing advances. A five-set array therefore lands two rows, under SQL_SUCCESS followed by SQL_NO_DATA, with no diagnostic saying anything was dropped. A client that follows the specification instead — one SQLExecute for the whole array — lands one row.

Both defects were filed together, each with a standalone C program, as ClickHouse/clickhouse-odbc#582, open. Until it is fixed, treat parameter arrays as unusable here: the compatibility matrix turns them off for this driver and batches rows into a multi-row INSERT instead.

No affected-row count, since 2021

SQLRowCount answers 0 for every write. Not −1, which ODBC defines as "not available" — 0, which a caller reads as "nothing was written". That has been open since 2021, and it still reproduces on 1.5.5 against 26.7.5.10 — ClickHouse/clickhouse-odbc#335 (comment), which also notes that the server already sends written_rows in the X-ClickHouse-Summary header on every HTTP response by default, so the number the driver would need is already arriving.

One HTTP request per execute, and what that costs

The driver talks to the server over HTTP, one request per execute. With one row per statement that is about 16 rows per second — the floor is the request, not ClickHouse. Batching rows into a single multi-row INSERT is what moves it: the same 300-row ingest runs at 911 rows per second, a 57x difference, and around 1,000 rows/s in the general case.

Two limits shape the batch. The server's HTTP form parsing caps a request at 1,000 fields, which bounds how many rows one INSERT can carry. And the driver will happily SQLPrepare a 500-row-group INSERT and then refuse to execute it, so a client has to discover the real ceiling by halving — 500 down to 125 for a four-column table — rather than by asking.

None of this needs a driver change to work around; it needs the client to stop sending one row per round trip.

What to take away

If you write to ClickHouse through ODBC on 1.5.5: bind nulls with a NULL value pointer, do not trust parameter arrays, do not trust SQLRowCount, and batch your inserts. All four are things a caller can do today; three of them are on file upstream, two with reproductions that need nothing but a C compiler and unixODBC.

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