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

Giorgi / DuckDB.NET / 22921736195

10 Mar 2026 07:24PM UTC coverage: 89.526% (+0.08%) from 89.45%
22921736195

push

github

Giorgi
Update global.json

1255 of 1463 branches covered (85.78%)

Branch coverage included in aggregate %.

2651 of 2900 relevant lines covered (91.41%)

447643.55 hits per line

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

97.94
/DuckDB.NET.Data/DataChunk/Reader/StructVectorDataReader.cs
1
using System.Collections.Concurrent;
2
using System.Linq.Expressions;
3
using DuckDB.NET.Data.Common;
4

5
namespace DuckDB.NET.Data.DataChunk.Reader;
6

7
internal sealed class StructVectorDataReader : VectorDataReaderBase
8
{
9
    private static readonly ConcurrentDictionary<Type, TypeDetails> TypeCache = new();
3✔
10
    // TODO: Replace with OrderedDictionary<string, VectorDataReaderBase> when .NET 8 support is dropped.
11
    // The parallel array exists only to guarantee field-ordinal iteration in Reset/Dispose.
12
    private readonly Dictionary<string, VectorDataReaderBase> structDataReaders;
13
    private readonly VectorDataReaderBase[] orderedReaders;
14

15
    internal unsafe StructVectorDataReader(IntPtr vector, void* dataPointer, ulong* validityMaskPointer, DuckDBType columnType, string columnName) : base(dataPointer, validityMaskPointer, columnType, columnName)
822✔
16
    {
17
        using var logicalType = NativeMethods.Vectors.DuckDBVectorGetColumnType(vector);
822✔
18
        var memberCount = NativeMethods.LogicalType.DuckDBStructTypeChildCount(logicalType);
822✔
19
        structDataReaders = new Dictionary<string, VectorDataReaderBase>((int)memberCount, StringComparer.OrdinalIgnoreCase);
822✔
20
        orderedReaders = new VectorDataReaderBase[memberCount];
822✔
21

22
        for (int index = 0; index < memberCount; index++)
5,022✔
23
        {
24
            var name = NativeMethods.LogicalType.DuckDBStructTypeChildName(logicalType, index);
1,689✔
25
            var childVector = NativeMethods.Vectors.DuckDBStructVectorGetChild(vector, index);
1,689✔
26

27
            using var childType = NativeMethods.LogicalType.DuckDBStructTypeChildType(logicalType, index);
1,689✔
28
            var reader = VectorDataReaderFactory.CreateReader(childVector, childType, columnName);
1,689✔
29
            structDataReaders[name] = reader;
1,689✔
30
            orderedReaders[index] = reader;
1,689✔
31
        }
32
    }
1,644✔
33

34
    internal override object GetValue(ulong offset, Type targetType)
35
    {
36
        if (DuckDBType == DuckDBType.Struct)
84!
37
        {
38
            return GetStruct(offset, targetType);
84✔
39
        }
40

41
        return base.GetValue(offset, targetType);
×
42
    }
43

44
    private object GetStruct(ulong offset, Type returnType)
45
    {
46
        var result = Activator.CreateInstance(returnType);
84✔
47

48
        if (result is Dictionary<string, object?> dictionary)
84✔
49
        {
50
            foreach (var reader in structDataReaders)
114✔
51
            {
52
                var value = reader.Value.IsValid(offset) ? reader.Value.GetValue(offset) : null;
42✔
53
                dictionary.Add(reader.Key, value);
42✔
54
            }
55

56
            return result;
15✔
57
        }
58

59
        var typeDetails = TypeCache.GetOrAdd(returnType, type =>
69✔
60
        {
69✔
61
            var propertyInfos = returnType.GetProperties();
18✔
62
            var details = new TypeDetails();
18✔
63

69✔
64
            foreach (var propertyInfo in propertyInfos)
144✔
65
            {
69✔
66
                if (propertyInfo.SetMethod == null)
54✔
67
                {
69✔
68
                    continue;
69✔
69
                }
69✔
70

69✔
71
                var isNullable = propertyInfo.PropertyType.AllowsNullValue(out var isNullableValueType, out var underlyingType);
51✔
72

69✔
73
                var instanceParam = Expression.Parameter(typeof(object));
51✔
74
                var argumentParam = Expression.Parameter(typeof(object));
51✔
75

69✔
76
                var setAction = Expression.Lambda<Action<object, object>>(
51✔
77
                    Expression.Call(Expression.Convert(instanceParam, type), propertyInfo.SetMethod, Expression.Convert(argumentParam, propertyInfo.PropertyType)),
51✔
78
                    instanceParam, argumentParam
51✔
79
                ).Compile();
51✔
80

69✔
81
                details.Properties.Add(propertyInfo.Name, new PropertyDetails(propertyInfo.PropertyType, isNullable, isNullableValueType, underlyingType, setAction));
51✔
82
            }
69✔
83

69✔
84
            return details;
18✔
85
        });
69✔
86

87
        foreach (var property in typeDetails.Properties)
507✔
88
        {
89
            structDataReaders.TryGetValue(property.Key, out var reader);
186✔
90
            var isNullable = property.Value.Nullable;
186✔
91

92
            if (reader == null)
186✔
93
            {
94
                //if (!isNullable)
95
                //{
96
                //    throw new NullReferenceException($"Property '{properties.Key}' not found in struct");
97
                //}
98

99
                continue;
100
            }
101

102
            if (reader.IsValid(offset))
186✔
103
            {
104
                var value = reader.GetValue(offset, property.Value.NullableType ?? property.Value.PropertyType);
138✔
105
                property.Value.Setter(result!, value);
138✔
106
            }
107
            else
108
            {
109
                if (!isNullable)
48✔
110
                {
111
                    throw new InvalidCastException($"Property '{property.Key}' is not nullable but struct contains null value");
3✔
112
                }
113
            }
114
        }
115

116
        return result!;
66✔
117
    }
118

119
    internal override void Reset(IntPtr vector)
120
    {
121
        base.Reset(vector);
3✔
122
        for (int index = 0; index < orderedReaders.Length; index++)
36✔
123
        {
124
            var childVector = NativeMethods.Vectors.DuckDBStructVectorGetChild(vector, index);
15✔
125
            orderedReaders[index].Reset(childVector);
15✔
126
        }
127
    }
3✔
128

129
    public override void Dispose()
130
    {
131
        foreach (var reader in orderedReaders)
252✔
132
        {
133
            reader.Dispose();
99✔
134
        }
135

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

© 2026 Coveralls, Inc