In set_param() in ls_odbc.c, the NULL indicator is declared static:
static SQLLEN cbNull = SQL_NULL_DATA;
The ODBC spec documents StrLen_or_IndPtr as both input and output — some drivers write back to it after execution. Because cbNull is static (shared across all calls in the process), a driver write-back corrupts the value for all subsequent NULL bindings.
The same static cbNull appears as dead code in raw_readparams_table() and raw_readparams_args() — declared but never used, likely a leftover from an earlier version.
Reproduction
demo_cbnull.c simulates set_param() with the static declaration and a conforming driver write-back:
static long cbNull = SQL_NULL_DATA; /* mirrors ls_odbc.c line 751 */
static long bind_null(void) {
long indicator = cbNull;
cbNull = SQL_NTS; /* driver write-back — ODBC spec permits this */
return indicator;
}
The second call to bind_null() should return SQL_NULL_DATA (-1) but gets SQL_NTS (-3) because the shared static was mutated by the first call.

In
set_param()inls_odbc.c, the NULL indicator is declared static:The ODBC spec documents
StrLen_or_IndPtras both input and output — some drivers write back to it after execution. BecausecbNullis static (shared across all calls in the process), a driver write-back corrupts the value for all subsequent NULL bindings.The same
static cbNullappears as dead code inraw_readparams_table()andraw_readparams_args()— declared but never used, likely a leftover from an earlier version.Reproduction
demo_cbnull.csimulatesset_param()with the static declaration and a conforming driver write-back:The second call to
bind_null()should returnSQL_NULL_DATA (-1)but getsSQL_NTS (-3)because the shared static was mutated by the first call.