Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion DuckDB.NET.Data/DuckDBDataReader.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using DuckDB.NET.Data.DataChunk.Reader;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Runtime.CompilerServices;

Expand Down Expand Up @@ -249,7 +250,19 @@ public override int GetOrdinal(string name)
return index;
}

throw new DuckDBException($"Column with name {name} was not found.");
// IDataRecord.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."
var compareInfo = CultureInfo.InvariantCulture.CompareInfo;
for (var i = 0; i < vectorReaders.Length; i++)
{
if (compareInfo.Compare(vectorReaders[i].ColumnName, name, CompareOptions.IgnoreCase | CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth) == 0)
{
return i;
}
}

throw new IndexOutOfRangeException($"Column with name {name} was not found.");
}

public override string GetString(int ordinal)
Expand Down
29 changes: 27 additions & 2 deletions DuckDB.NET.Test/DuckDBDataReaderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public void GetOrdinalReturnsColumnIndex()
reader.GetOrdinal("key").Should().Be(0);
reader.GetOrdinal("value").Should().Be(1);

reader.Invoking(dataReader => dataReader.GetOrdinal("Random")).Should().Throw<DuckDBException>();
reader.Invoking(dataReader => dataReader.GetOrdinal("Random")).Should().Throw<IndexOutOfRangeException>();
}

[Fact]
Expand All @@ -69,7 +69,7 @@ public void GetOrdinalRepeatedColumnReturnsFirstIndex()
reader.GetOrdinal("key").Should().Be(1);
reader.GetOrdinal("value").Should().Be(0);

reader.Invoking(dataReader => dataReader.GetOrdinal("Random")).Should().Throw<DuckDBException>();
reader.Invoking(dataReader => dataReader.GetOrdinal("Random")).Should().Throw<IndexOutOfRangeException>();
}

[Fact]
Expand Down Expand Up @@ -819,4 +819,29 @@ public void ReadIntegerAsAnotherIntegerType()
reader.Invoking(r => r.GetFieldValue<ulong>(1)).Should().Throw<InvalidCastException>().WithMessage("Cannot cast from Int32 to UInt64 in column negative");
reader.Invoking(r => r.GetFieldValue<byte>(2)).Should().Throw<InvalidCastException>().WithMessage("Cannot cast from Int16 to Byte in column small");
}

// https://learn.microsoft.com/dotnet/api/system.data.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."
[Fact]
public void GetOrdinalFallsBackToACaseInsensitiveMatch()
{
Command.CommandText = "SELECT 1 AS Id, 2 AS id, 3 AS \"Name\"";
using var reader = Command.ExecuteReader();

reader.GetOrdinal("Id").Should().Be(0);
reader.GetOrdinal("id").Should().Be(1, "an exact match wins over a case-insensitive one");
reader.GetOrdinal("NAME").Should().Be(2);
reader.GetOrdinal("name").Should().Be(2, "the lookup is width insensitive");
}

[Fact]
public void GetOrdinalThrowsIndexOutOfRangeForAnUnknownName()
{
Command.CommandText = "SELECT 1 AS Id";
using var reader = Command.ExecuteReader();

reader.Invoking(r => r.GetOrdinal("missing")).Should().Throw<IndexOutOfRangeException>();
}
}

Loading