Repository navigation
[C++][FlightRPC][ODBC] SQLDescribeCol writes only 2 of 8 bytes of column_size for numeric/decimal columns #50560
Description
Describe the bug, including details regarding any error messages, version, and platform.
In the Arrow Flight SQL ODBC driver, SQLDescribeCol returns garbage in the
upper bytes of the column_size (a.k.a. ColumnSizePtr) output argument for
numeric / decimal columns. Only the low 2 bytes are written; the upper 6 bytes
are left uninitialized.
Root cause — a write-width mismatch.
DescriptorRecord::precision is declared SQLSMALLINT (2 bytes) in
cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_descriptor.h:
SQLSMALLINT precision = 0; // ~line 58
...
SQLSMALLINT scale = 0; // ~line 60ODBCDescriptor::GetField(SQL_DESC_PRECISION, ...) forwards to the GetAttribute
template in cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_descriptor.cc
(~line 417):
case SQL_DESC_PRECISION:
GetAttribute(record.precision, value, buffer_length, output_length);
break;and that template (cpp/src/arrow/flight/sql/odbc/odbc_impl/attribute_utils.h)
copies exactly sizeof(T) bytes — T being deduced from the field, i.e.
SQLSMALLINT (2 bytes). The buffer_length argument is ignored for the
write:
template <typename T, typename O>
inline void GetAttribute(T attribute_value, SQLPOINTER output, O output_size,
O* output_len_ptr) {
if (output) {
T* typed_output = reinterpret_cast<T*>(output); // reinterprets the caller's pointer
*typed_output = attribute_value; // writes only sizeof(T) = 2 bytes
}
...
}But in SQLDescribeCol
(cpp/src/arrow/flight/sql/odbc/odbc_api.cc, ~line 1584) the numeric path
passes the SQLULEN* column_size_ptr (8 bytes) directly and claims
sizeof(SQLULEN):
case SQL_DOUBLE: {
ird->GetField(column_number, SQL_DESC_PRECISION, column_size_ptr,
sizeof(SQLULEN), nullptr);
break;
}GetAttribute then reinterprets the SQLULEN* as a SQLSMALLINT* and writes
only 2 bytes. The upper 6 bytes of the caller's SQLULEN remain
uninitialized garbage. An application that reads the full 8-byte column_size
(the correct ODBC type for ColumnSizePtr) sees a wildly wrong value for every
DECIMAL / NUMERIC / integer / REAL / FLOAT / DOUBLE column.
Two related, lower-severity smells at the decimal_digits sites. The
SQL_DESC_SCALE path (~line 1606) and the datetime/interval SQL_DESC_PRECISION
path (~line 1624) pass sizeof(SQLULEN) (8) as the buffer size even though
decimal_digits_ptr is a SQLSMALLINT* (2 bytes) and the underlying field is a
2-byte SQLSMALLINT. These do not corrupt memory today (the write width is
2 bytes, matching the 2-byte target), but the claimed buffer size is wrong and
misleading. They should pass sizeof(SQLSMALLINT).
Trace of the affected call sites (all in odbc_api.cc::SQLDescribeCol):
SQL_DESC_PRECISION → column_size_ptr(numeric/decimal) — the real bug:
2-byte write into an 8-byteSQLULEN, upper 6 bytes garbage.SQL_DESC_SCALE → decimal_digits_ptr(exact numeric) — wrongsizeof, no
corruption.SQL_DESC_PRECISION → decimal_digits_ptr(datetime / interval-with-seconds)
— wrongsizeof, no corruption.
Introduced by
PR #48052 (GH-47724, "[C++][FlightRPC][ODBC] implement
SQLDescribeCol"). The bug is still present on main.
Component(s)
C++, FlightRPC
Activity
apacheGH-50560: [C++][FlightRPC][ODBC] Fix SQLDescribeCol column_size…
apacheGH-50560: [C++][FlightRPC][ODBC] Fix SQLDescribeCol column_size…
apacheGH-50560: [C++][FlightRPC][ODBC] Fix SQLDescribeCol column_size…
apacheGH-50560: [C++][FlightRPC][ODBC] Fix SQLDescribeCol column_size…
GH-50560: [C++][FlightRPC][ODBC] Fix SQLDescribeCol column_size/decim…
lidavidm commentedon Aug 5, 2026
Issue resolved by pull request 50562
#50562