vikrantpuppala opened a new issue, #50560:
URL: https://github.com/apache/arrow/issues/50560
# [C++][FlightRPC][ODBC] SQLDescribeCol writes only 2 of 8 bytes of
column_size for numeric/decimal columns
### 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`:
```cpp
SQLSMALLINT precision = 0; // ~line 58
...
SQLSMALLINT scale = 0; // ~line 60
```
`ODBCDescriptor::GetField(SQL_DESC_PRECISION, ...)` forwards to the
`GetAttribute`
template in `cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_descriptor.cc`
(~line 417):
```cpp
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:
```cpp
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)`:
```cpp
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`):
1. `SQL_DESC_PRECISION → column_size_ptr` (numeric/decimal) — **the real
bug**:
2-byte write into an 8-byte `SQLULEN`, upper 6 bytes garbage.
2. `SQL_DESC_SCALE → decimal_digits_ptr` (exact numeric) — wrong `sizeof`, no
corruption.
3. `SQL_DESC_PRECISION → decimal_digits_ptr` (datetime /
interval-with-seconds)
— wrong `sizeof`, no corruption.
### Introduced by
PR apache/arrow#48052 (GH-47724, "[C++][FlightRPC][ODBC] implement
SQLDescribeCol"). The bug is still present on `main`.
### Component(s)
C++, FlightRPC
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]