• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Giorgi / DuckDB.NET / 40296324877

26 Oct 2026 09:00AM UTC coverage: 92.811% (+0.3%) from 92.531%
40296324877

push

github

Giorgi
Test the allocator grouping on whichever option the running DuckDB has

DuckDB's main branch replaced the block_allocator_memory setting with
enable_block_allocator (duckdb/duckdb#27520), so the nightly build failed to
find it. The grouping rule covers both, as both names have an allocator and a
block segment. The test now takes whichever option the running DuckDB has, so
it runs against DuckDB 2.0 and against the nightly builds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F9b3CGWkCMzmhoxge1QiZ8

2599 of 2865 branches covered (90.72%)

Branch coverage included in aggregate %.

4205 of 4466 relevant lines covered (94.16%)

1056578.13 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.89
/DuckDB.NET.Data/DataChunk/Reader/StructVectorDataReader.cs
1
using System.Runtime.CompilerServices;
2

3
namespace DuckDB.NET.Data.DataChunk.Reader;
4

5
// The setters of a target type's properties, which do not depend on the column, are in
6
// StructVectorDataReader.Setters.cs.
7
internal sealed partial class StructVectorDataReader : VectorDataReaderBase
8
{
9
    // TODO: Replace with OrderedDictionary<string, VectorDataReaderBase> when .NET 8 support is dropped.
10
    // The parallel array exists only to guarantee field-ordinal iteration in Reset/Dispose.
11
    private readonly Dictionary<string, VectorDataReaderBase> fieldReadersByName;
12
    private readonly VectorDataReaderBase[] fieldReaders;
13

14
    // What GetValueCore found out about the type it was asked for last. A type's setters are worked out once per
15
    // process (SetterCache); this reader pairs them with its fields once per type it is asked for, as a column is
16
    // read as the same type row after row.
17
    private Type? planType;
18
    private StructPlan plan;
19

20
    internal unsafe StructVectorDataReader(IntPtr vector, void* dataPointer, ulong* validityMaskPointer, DuckDBType columnType, string columnName) : base(dataPointer, validityMaskPointer, columnType, columnName)
2,037 ✔
21
    {
22
        using var logicalType = NativeMethods.Vectors.DuckDBVectorGetColumnType(vector);
2,037 ✔
23
        var memberCount = NativeMethods.LogicalType.DuckDBStructTypeChildCount(logicalType);
2,037 ✔
24
        fieldReadersByName = new Dictionary<string, VectorDataReaderBase>((int)memberCount, StringComparer.OrdinalIgnoreCase);
2,037 ✔
25
        fieldReaders = new VectorDataReaderBase[memberCount];
2,037 ✔
26

27
        for (int index = 0; index < memberCount; index++)
9,918 ✔
28
        {
29
            var name = NativeMethods.LogicalType.DuckDBStructTypeChildName(logicalType, index);
2,922 ✔
30
            var childVector = NativeMethods.Vectors.DuckDBStructVectorGetChild(vector, index);
2,922 ✔
31

32
            using var childType = NativeMethods.LogicalType.DuckDBStructTypeChildType(logicalType, index);
2,922 ✔
33
            var reader = VectorDataReaderFactory.CreateReader(childVector, childType, columnName);
2,922 ✔
34
            fieldReadersByName[name] = reader;
2,922 ✔
35
            fieldReaders[index] = reader;
2,922 ✔
36
        }
37
    }
4,074 ✔
38

39
    internal override object GetValueCore(ulong offset, Type targetType)
40
    {
41
        if (DuckDBType != DuckDBType.Struct)
152,175 !
42
        {
43
            return base.GetValueCore(offset, targetType);
×
44
        }
45

46
        if (targetType != planType)
152,175 ✔
47
        {
48
            plan = CreatePlan(targetType);
36,171 ✔
49
            planType = targetType;
36,171 ✔
50
        }
51

52
        return plan.IsDictionary ? GetDictionary(offset, targetType) : GetObject(offset, targetType);
152,175 ✔
53
    }
54

55
    // A Dictionary<string, object>, or a class derived from one: every field, read as the column's own type.
56
    private object GetDictionary(ulong offset, Type targetType)
57
    {
58
        var dictionary = (Dictionary<string, object?>)Activator.CreateInstance(targetType)!;
30,204 ✔
59

60
        foreach (var reader in fieldReadersByName)
181,002 ✔
61
        {
62
            var value = reader.Value.IsValid(offset) ? reader.Value.GetValue(offset) : null;
60,297 ✔
63
            dictionary.Add(reader.Key, value);
60,297 ✔
64
        }
65

66
        return dictionary;
30,204 ✔
67
    }
68

69
    // Any other type: each property that has a field of its name is set. A C# struct is set inside its box, which is
70
    // what is returned.
71
    private object GetObject(ulong offset, Type targetType)
72
    {
73
        var result = Activator.CreateInstance(targetType)!;
121,971 ✔
74

75
        foreach (var (setter, fieldReader) in plan.Properties)
1,034,364 ✔
76
        {
77
            setter.Set(result, fieldReader, offset);
399,804 ✔
78
        }
79

80
        return result;
112,785 ✔
81
    }
82

83
    // Not inlined, so that GetValueCore does not set up a native-call frame on every row (Reader/DESIGN.md).
84
    [MethodImpl(MethodImplOptions.NoInlining)]
85
    private StructPlan CreatePlan(Type targetType)
86
    {
87
        if (typeof(Dictionary<string, object?>).IsAssignableFrom(targetType))
36,171 ✔
88
        {
89
            return new StructPlan(IsDictionary: true, Properties: []);
159 ✔
90
        }
91

92
        // A static lambda: one that used targetType allocated a closure and a delegate for every call.
93
        // CreateTypedSetters returns null where no code can be made at run time.
94
        var setters = SetterCache.GetOrAdd(targetType, static type => CreateTypedSetters(type) ?? CreateBoxedSetters(type));
36,216 !
95

96
        // In the order of the type's properties, each settable property that has a field of the same name. The other
97
        // properties are left as they are.
98
        var properties = new List<BoundProperty>(setters.Length);
36,012 ✔
99

100
        foreach (var setter in setters)
193,080 ✔
101
        {
102
            if (fieldReadersByName.TryGetValue(setter.Name, out var fieldReader))
60,528 ✔
103
            {
104
                properties.Add(new BoundProperty(setter, fieldReader));
60,525 ✔
105
            }
106
        }
107

108
        return new StructPlan(IsDictionary: false, Properties: [.. properties]);
36,012 ✔
109
    }
110

111
    // IsDictionary: the target is a Dictionary<string, object> or derives from one. Properties: otherwise, the
112
    // properties that are set, each with the reader of its field.
113
    private readonly record struct StructPlan(bool IsDictionary, BoundProperty[] Properties);
274,146 ✔
114

115
    // A property of the target type, with the reader of the field of the same name.
116
    private readonly record struct BoundProperty(PropertySetter Setter, VectorDataReaderBase FieldReader);
799,608 ✔
117

118
    internal override void Reset(IntPtr vector)
119
    {
120
        base.Reset(vector);
219 ✔
121
        for (int index = 0; index < fieldReaders.Length; index++)
972 ✔
122
        {
123
            var childVector = NativeMethods.Vectors.DuckDBStructVectorGetChild(vector, index);
267 ✔
124
            fieldReaders[index].Reset(childVector);
267 ✔
125
        }
126
    }
219 ✔
127

128
    public override void Dispose()
129
    {
130
        foreach (var reader in fieldReaders)
3,876 ✔
131
        {
132
            reader.Dispose();
1,014 ✔
133
        }
134

135
        base.Dispose();
924 ✔
136
    }
924 ✔
137
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc