Skip to content

GetOrdinal: case-insensitive fallback and IndexOutOfRangeException - #375

Merged
Giorgi merged 1 commit into
Giorgi:developfrom
pengdows:fix/getordinal-contract
Oct 20, 2026
Merged

Giorgi merged 1 commit into
Giorgi:developfrom
pengdows:fix/getordinal-contract

Conversation

@alaricd

@alaricd alaricd commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

DuckDBDataReader.GetOrdinal only finds an exact, case-sensitive match. For a name that isn't found it throws DuckDBException.

The contract

IDataRecord.GetOrdinal, Remarks:

GetOrdinal performs a case-sensitive lookup first. If it fails, a second case-insensitive search is made. GetOrdinal is kana-width insensitive. If the index of the named field is not found, an IndexOutOfRangeException is thrown.

Why it matters

DuckDB folds unquoted identifiers, so SELECT id FROM t and reader.GetOrdinal("Id") refer to the same column, yet the second call throws. Mappers (Dapper, hand-written reader["Name"] code) that work against SqlClient, Npgsql, MySqlConnector and Microsoft.Data.Sqlite fail on DuckDB because of how a property happens to be capitalised. Code that catches IndexOutOfRangeException to test for an optional column, as the contract allows, gets a DuckDBException instead.

Change

  • The exact match from columnMapping is still tried first, so a column whose name matches exactly always wins (SELECT 1 AS Id, 2 AS id keeps returning 0 for "Id" and 1 for "id").
  • If that fails, the reader compares names with CompareOptions.IgnoreCase | IgnoreKanaType | IgnoreWidth, which is what "case-insensitive … kana-width insensitive" describes.
  • An unknown name throws IndexOutOfRangeException.

Behaviour change: callers that catch DuckDBException from GetOrdinal need to catch IndexOutOfRangeException. The two tests that pinned the old type (GetOrdinalReturnsColumnIndex, GetOrdinalRepeatedColumnReturnsFirstIndex) now expect the documented one.

New tests: GetOrdinalFallsBackToACaseInsensitiveMatch (exact match wins, case and width folding) and GetOrdinalThrowsIndexOutOfRangeForAnUnknownName. The full suite passes.

Conformance tests: GetOrdinal_works, GetOrdinal_throws_when_out_of_range.

Found by the pengdows ADO.NET conformance suite, which runs the documented System.Data.Common contract against every provider; each failure it reports cites the learn.microsoft.com sentence the provider departs from.

🤖 Generated with Claude Code

IDataRecord.GetOrdinal: "GetOrdinal performs a case-sensitive lookup
first. If it fails, a second case-insensitive search is made. GetOrdinal
is kana-width insensitive. If the index of the named field is not found,
an IndexOutOfRangeException is thrown."

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coveralls

Copy link
Copy Markdown

Coverage Report for CI Build 39416608021

Warning

Build has drifted: This PR's base is out of sync with its target branch, so coverage data may include unrelated changes.
Quick fix: rebase this PR. Learn more →

Coverage increased (+0.07%) to 92.561%

Details

  • Coverage increased (+0.07%) from the base build.
  • Patch coverage: 5 of 5 lines across 1 file are fully covered (100%).
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 4016
Covered Lines: 3780
Line Coverage: 94.12%
Relevant Branches: 2289
Covered Branches: 2056
Branch Coverage: 89.82%
Branches in Coverage %: Yes
Coverage Strength: 1003742.96 hits per line

💛 - Coveralls

@Giorgi
Giorgi merged commit 62a6a81 into Giorgi:develop Oct 20, 2026
11 of 12 checks passed

This branch is waiting to be deployed

1 waiting deployment
external — 9ed287c4 Waiting Oct 20, 2026 by alaricd via Authorize #679
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants