Skip to content
Open
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
42 changes: 39 additions & 3 deletions DuckDB.NET.Data/DataChunk/Reader/ListVectorDataReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ internal sealed class ListVectorDataReader : VectorDataReaderBase
{
private readonly ulong arraySize;
private readonly VectorDataReaderBase listDataReader;
private Type? cachedListType;
private IListFactory? cachedListFactory;

public bool IsList => DuckDBType == DuckDBType.List;

Expand Down Expand Up @@ -51,8 +53,7 @@ private object GetList(Type returnType, ulong listOffset, ulong length)

var allowNulls = listType.AllowsNullValue(out _, out var nullableType);

var list = Activator.CreateInstance(returnType) as IList
?? throw new ArgumentException($"The type '{returnType.Name}' specified in parameter {nameof(returnType)} cannot be instantiated as an IList.");
var list = CreateList(returnType, length);

//Special case for specific types to avoid boxing
return list switch
Expand Down Expand Up @@ -105,6 +106,31 @@ IList BuildListCommon(IList result, Type targetType)
}
}

private IList CreateList(Type returnType, ulong length)
{
if (returnType != cachedListType)
{
cachedListType = returnType;
cachedListFactory = returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(List<>)
? CreateListFactory(returnType)
: null;
}

if (cachedListFactory != null)
{
return cachedListFactory.Create(checked((int)length));
}
Comment thread
skuirrels marked this conversation as resolved.

return Activator.CreateInstance(returnType) as IList
?? throw new ArgumentException($"The type '{returnType.Name}' specified in parameter {nameof(returnType)} cannot be instantiated as an IList.");
}

private static IListFactory CreateListFactory(Type returnType)
{
var factoryType = typeof(ListFactory<>).MakeGenericType(returnType.GetGenericArguments()[0]);
return (IListFactory)Activator.CreateInstance(factoryType)!;
}

internal override void Reset(IntPtr vector)
{
base.Reset(vector);
Expand All @@ -119,4 +145,14 @@ public override void Dispose()
listDataReader.Dispose();
base.Dispose();
}
}

private interface IListFactory
{
IList Create(int capacity);
}

private sealed class ListFactory<T> : IListFactory
{
public IList Create(int capacity) => new List<T>(capacity);
}
}
15 changes: 14 additions & 1 deletion DuckDB.NET.Test/DuckDBDataReaderListTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@

public class DuckDBDataReaderListTests(DuckDBDatabaseFixture db) : DuckDBTestBase(db)
{
[Fact]
public void PreSizesListResults()
{
Command.CommandText = "SELECT range(17)::INTEGER[];";
using var reader = Command.ExecuteReader();

reader.Read();
var list = reader.GetFieldValue<List<int>>(0);

list.Should().HaveCount(17);
list.Capacity.Should().Be(17);
}

[Fact]
public void ReadListOfIntegers()
{
Expand Down Expand Up @@ -251,4 +264,4 @@ class Person
public List<int> Ids { get; set; }
public DuckDBDataReaderEnumTests.Mood Mood { get; set; }
}
}
}
Loading