From 9e171398c7dd923e5efbc9b65e5e7dd4c0e611f7 Mon Sep 17 00:00:00 2001 From: wdesgardin Date: Mon, 5 Apr 2021 18:54:20 +0200 Subject: [PATCH] Case insensitive SqliteDataReader Fixes dotnet/efcore#24011 --- .../SqliteDataReader.cs | 3 ++- .../SqliteDataRecord.cs | 7 ++++++ .../SqliteDataReaderTest.cs | 23 +++++++++++-------- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.Data.Sqlite.Core/SqliteDataReader.cs b/src/Microsoft.Data.Sqlite.Core/SqliteDataReader.cs index 729b8810755..14f5b60a4eb 100644 --- a/src/Microsoft.Data.Sqlite.Core/SqliteDataReader.cs +++ b/src/Microsoft.Data.Sqlite.Core/SqliteDataReader.cs @@ -90,8 +90,9 @@ public override int RecordsAffected /// /// Gets the value of the specified column. /// - /// The name of the column. The value is case-sensitive. + /// The name of the column. /// The value. + /// Performs a case-sensitive lookup first. If it fails, a second, case-insensitive search occurs. /// Data Types public override object this[string name] => _record == null diff --git a/src/Microsoft.Data.Sqlite.Core/SqliteDataRecord.cs b/src/Microsoft.Data.Sqlite.Core/SqliteDataRecord.cs index 20e54f3980d..d9d78baefee 100644 --- a/src/Microsoft.Data.Sqlite.Core/SqliteDataRecord.cs +++ b/src/Microsoft.Data.Sqlite.Core/SqliteDataRecord.cs @@ -133,6 +133,13 @@ public virtual int GetOrdinal(string name) return ordinal; } + var insensitiveMatchingColumns = _columnNameOrdinalCache.Where(kv => kv.Key.Equals(name, StringComparison.OrdinalIgnoreCase)); + if(insensitiveMatchingColumns.Count() == 1) { + ordinal = insensitiveMatchingColumns.First().Value; + _columnNameOrdinalCache.Add(name, ordinal); + return ordinal; + } + // NB: Message is provided by framework throw new ArgumentOutOfRangeException(nameof(name), name, message: null); } diff --git a/test/Microsoft.Data.Sqlite.Tests/SqliteDataReaderTest.cs b/test/Microsoft.Data.Sqlite.Tests/SqliteDataReaderTest.cs index 59534c302b4..8150c3ffd30 100644 --- a/test/Microsoft.Data.Sqlite.Tests/SqliteDataReaderTest.cs +++ b/test/Microsoft.Data.Sqlite.Tests/SqliteDataReaderTest.cs @@ -1276,19 +1276,21 @@ public void GetOrdinal_works() } } - [Fact] - public void GetOrdinal_throws_when_out_of_range() + [Theory] + [InlineData("SELECT 1;", "Name")] + [InlineData("SELECT 1 as Id, 2 as ID;", "id")] + public void GetOrdinal_throws_when_out_of_range(string query, string columnName) { using (var connection = new SqliteConnection("Data Source=:memory:")) { connection.Open(); - using (var reader = connection.ExecuteReader("SELECT 1;")) + using (var reader = connection.ExecuteReader(query)) { - var ex = Assert.Throws(() => reader.GetOrdinal("Name")); + var ex = Assert.Throws(() => reader.GetOrdinal(columnName)); Assert.NotNull(ex.Message); Assert.Equal("name", ex.ParamName); - Assert.Equal("Name", ex.ActualValue); + Assert.Equal(columnName, ex.ActualValue); } } } @@ -1583,19 +1585,22 @@ public void Item_by_ordinal_throws_when_non_query() } } - [Fact] - public void Item_by_name_works() + [Theory] + [InlineData("SELECT 1 as Id;", "Id", 1L)] + [InlineData("SELECT 1 as Id;", "id", 1L)] + [InlineData("SELECT 1 as Id, 2 as id;", "id", 2L)] + public void Item_by_name_works(string query, string column, long expected) { using (var connection = new SqliteConnection("Data Source=:memory:")) { connection.Open(); - using (var reader = connection.ExecuteReader("SELECT 1 AS Id;")) + using (var reader = connection.ExecuteReader(query)) { var hasData = reader.Read(); Assert.True(hasData); - Assert.Equal(1L, reader["Id"]); + Assert.Equal(expected, reader[column]); } } }