Skip to content

Commit 1490436

Browse files
adwk67claude
andauthored
Rank the closest SQLGetTypeInfo row first; convert character data to SQL_C_INTERVAL_* (#11)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent e07450c commit 1490436

9 files changed

Lines changed: 1077 additions & 53 deletions

File tree

‎AGENTS.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,12 @@ a driver to work on Windows, because omitting any one can cause silent crashes,
758758
(-8) are returned, pyodbc cannot perform type conversions and `SQLGetData`
759759
fails for numeric types.
760760

761+
- **`get_type_info`, several rows per `DATA_TYPE`**: mark the closest match
762+
with `TypeInfoRow::with_preferred(true)`, and assert
763+
`conformance::type_info_preference_issues` is empty in the driver's tests.
764+
Core orders the preferred row first. Unmarked, the alphabetically first row
765+
leads, and Power Query uses that row's TYPE_NAME as a `CAST` target.
766+
761767
- **`SQL_GETDATA_EXTENSIONS`**: Report exactly what the shared
762768
`stackable-odbc-core` fetch/bind implementation supports; do not reflexively
763769
return `0x0F`. `SQL_GD_ANY_COLUMN | SQL_GD_ANY_ORDER | SQL_GD_BOUND` (`0x0B`)

‎CHANGELOG.md‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,42 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2222
dialects, `ConnectParams` round-tripping a whole connection string rather than
2323
one pair, the two parameter-conversion tables, and every `*_from_raw` swept
2424
across its entire input domain.
25+
- `TypeInfoRow::with_preferred` and `TypeInfoRow::preferred`, so a driver can
26+
mark which of several `SQLGetTypeInfo` rows sharing a `DATA_TYPE` maps most
27+
closely to it.
28+
- `conformance::type_info_preference_issues`, behind the `test-support`
29+
feature, which reports every shared `DATA_TYPE` without exactly one preferred
30+
row.
31+
32+
### Changed
33+
34+
- `SQLGetTypeInfo` orders the preferred row first among rows sharing a
35+
`DATA_TYPE`, before falling back to `TYPE_NAME`, as the spec's "how closely
36+
the data type maps" ordering requires. Previously the alphabetically first
37+
row led, so an application taking the first row for a `DATA_TYPE` (Power
38+
Query does, for `CAST` targets) could get an unrelated type. A driver that
39+
marks nothing is unaffected.
40+
41+
### Fixed
42+
43+
- Character data now converts to the `SQL_C_INTERVAL_*` C types, as the *SQL to
44+
C: Character* table requires, instead of failing with `07006`. Text of the form
45+
`[-]y-m` converts to the year-month C types and `[-]d hh:mm:ss[.f]` to the
46+
day-time ones, with months 0-11 and hours, minutes and seconds in their usual
47+
ranges; anything else is `22018`. Lost trailing fields (`01S07`) or an
48+
overflowing leading field (`22015`) behave as for an interval column, and a
49+
leading field too large to carry at all is `22015` too; a non-zero fraction
50+
digit past the ninth is `01S07`. The
51+
accepted shapes follow psqlODBC's `interval2istruct`, since the spec leaves
52+
"valid interval value" open for a column value.
53+
- Exact numeric data (`SQL_TINYINT` to `SQL_BIGINT`, `SQL_DECIMAL`,
54+
`SQL_NUMERIC`) now converts to the `SQL_C_INTERVAL_*` C types too, as the *SQL
55+
to C: Numeric* table requires, instead of failing with `07006`. The number
56+
counts the target's leading field (`5` read as `SQL_C_INTERVAL_DAY` is five
57+
days), a dropped fraction is `01S07` and a value too large is `22015`. That
58+
table defines only the failure case for multi-field targets, so the same rule
59+
applies there: `1.5` read as `SQL_C_INTERVAL_DAY_TO_SECOND` is 1 day 12 hours.
60+
Approximate numerics stay `07006`, as the table's footnote [c] requires.
2561

2662
## [0.1.0] — 2026-08-04
2763

‎src/backend.rs‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,13 @@ pub trait Backend: Sized + Send + Sync + 'static {
623623
/// type variants so that ODBC applications can match on `SQL_VARCHAR` as well as
624624
/// `SQL_WVARCHAR`.
625625
///
626+
/// Declaration order does not matter: core orders the result set by
627+
/// DATA_TYPE, then the preferred row, then TYPE_NAME. Where several rows
628+
/// share a DATA_TYPE, mark the one that maps most closely to it with
629+
/// [`TypeInfoRow::with_preferred`], since applications take the first row
630+
/// for a DATA_TYPE as the type to use; check the markers in the driver's
631+
/// tests with `conformance::type_info_preference_issues`.
632+
///
626633
/// Takes a connection because the type list can genuinely differ by data
627634
/// source (a server-version probe gating a type's availability, say), so
628635
/// the answer is computed rather than a `'static` borrow, hence

0 commit comments

Comments
 (0)