Skip to content

Commit

Permalink
Add Ordinal and Name Caching to SqliteDataRecord
Browse files Browse the repository at this point in the history
This speeds up repeated ordinal and Name access. Example existing usage could be from SqlDataReaderExtension.GetValueOrDefault or similar.

Related to #24140
  • Loading branch information
Strepto committed Feb 12, 2021
1 parent b708390 commit d2c4747
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 5 deletions.
20 changes: 16 additions & 4 deletions src/Microsoft.Data.Sqlite.Core/SqliteDataRecord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
Expand All @@ -17,6 +18,8 @@ internal class SqliteDataRecord : SqliteValueReader, IDisposable
private readonly SqliteConnection _connection;
private byte[][]? _blobCache;
private int?[]? _typeCache;
private Dictionary<string, int>? _columnNameOrdinalCache;
private string?[]? _columnNameCache;
private bool _stepped;
private int? _rowidOrdinal;

Expand Down Expand Up @@ -100,26 +103,35 @@ protected override T GetNull<T>(int ordinal)

public virtual string GetName(int ordinal)
{
var name = sqlite3_column_name(Handle, ordinal).utf8_to_string();
var name = _columnNameCache?[ordinal] ?? sqlite3_column_name(Handle, ordinal).utf8_to_string();
if (name == null
&& (ordinal < 0 || ordinal >= FieldCount))
{
// NB: Message is provided by the framework
throw new ArgumentOutOfRangeException(nameof(ordinal), ordinal, message: null);
}

_columnNameCache ??= new string[FieldCount];
_columnNameCache[ordinal] = name;

return name!;
}

public virtual int GetOrdinal(string name)
{
for (var i = 0; i < FieldCount; i++)
if (_columnNameOrdinalCache == null)
{
if (GetName(i) == name)
_columnNameOrdinalCache = new Dictionary<string, int>();
for (var i = 0; i < FieldCount; i++)
{
return i;
_columnNameOrdinalCache[GetName(i)] = i;
}
}

if (_columnNameOrdinalCache.TryGetValue(name, out var ordinal))
{
return ordinal;
}

// NB: Message is provided by framework
throw new ArgumentOutOfRangeException(nameof(name), name, message: null);
Expand Down
8 changes: 7 additions & 1 deletion test/Microsoft.Data.Sqlite.Tests/SqliteDataReaderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1227,6 +1227,8 @@ public void GetName_works()
using (var reader = connection.ExecuteReader("SELECT 1 AS Id;"))
{
Assert.Equal("Id", reader.GetName(0));
// Repeated access might use caching.
Assert.Equal("Id", reader.GetName(0));
}
}
}
Expand Down Expand Up @@ -1263,9 +1265,12 @@ public void GetOrdinal_works()
{
connection.Open();

using (var reader = connection.ExecuteReader("SELECT 1 AS Id;"))
using (var reader = connection.ExecuteReader("SELECT 1 as Id, 'ÆØÅ' AS Value"))
{
Assert.Equal(0, reader.GetOrdinal("Id"));
Assert.Equal(1, reader.GetOrdinal("Value"));
// Repeated access may use caching
Assert.Equal(1, reader.GetOrdinal("Value"));
}
}
}
Expand Down Expand Up @@ -1297,6 +1302,7 @@ public void GetOrdinal_throws_when_closed()
public void GetOrdinal_throws_when_non_query()
=> X_throws_when_non_query(r => r.GetOrdinal("dummy"));


[Fact]
public void GetString_works_utf8()
=> GetX_works(
Expand Down

0 comments on commit d2c4747

Please sign in to comment.