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

Giorgi / DuckDB.NET / 22729722447

05 Mar 2026 04:48PM UTC coverage: 89.491% (-0.2%) from 89.682%
22729722447

push

github

Giorgi
Use field keyword

1260 of 1467 branches covered (85.89%)

Branch coverage included in aggregate %.

5 of 5 new or added lines in 2 files covered. (100.0%)

12 existing lines in 3 files now uncovered.

2606 of 2853 relevant lines covered (91.34%)

451516.24 hits per line

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

87.68
/DuckDB.NET.Data/DataChunk/Reader/VectorDataReaderBase.cs
1
using System.IO;
2
using System.Linq.Expressions;
3
using System.Reflection;
4
using System.Runtime.CompilerServices;
5

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

8
internal class VectorDataReaderBase : IDisposable, IDuckDBDataReader
9
{
10
    private unsafe ulong* validityMaskPointer;
11

12
    public Type ClrType => field ??= GetColumnType();
322,409✔
13

14
    public Type ProviderSpecificClrType => field ??= GetColumnProviderSpecificType();
189✔
15

16

17
    public string ColumnName { get; }
162,685✔
18
    public DuckDBType DuckDBType { get; }
21,810,121✔
19
    private protected unsafe void* DataPointer { get; private set; }
21,775,099✔
20

21
    internal unsafe VectorDataReaderBase(void* dataPointer, ulong* validityMaskPointer, DuckDBType columnType, string columnName)
57,127✔
22
    {
23
        DataPointer = dataPointer;
57,127✔
24
        this.validityMaskPointer = validityMaskPointer;
57,127✔
25

26
        DuckDBType = columnType;
57,127✔
27
        ColumnName = columnName;
57,127✔
28
    }
57,127✔
29

30
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
31
    public unsafe bool IsValid(ulong offset)
32
    {
33
        if (validityMaskPointer == default)
31,777,991✔
34
        {
35
            return true;
18,991,133✔
36
        }
37

38
        var validityMaskEntryIndex = offset / 64;
12,786,858✔
39
        var validityBitIndex = (int)(offset % 64);
12,786,858✔
40

41
        var validityMaskEntryPtr = validityMaskPointer + validityMaskEntryIndex;
12,786,858✔
42
        var validityBit = 1ul << validityBitIndex;
12,786,858✔
43

44
        var isValid = (*validityMaskEntryPtr & validityBit) != 0;
12,786,858✔
45
        return isValid;
12,786,858✔
46
    }
47

48
    public virtual T GetValue<T>(ulong offset)
49
    {
50
        // When T is Nullable<TUnderlying> (e.g. int?), we can't call GetValidValue<int>() directly
51
        // because we only have T=int? at compile time. NullableHandler uses a pre-compiled expression
52
        // tree that calls GetValidValue<int>() and converts to int?, avoiding boxing through the
53
        // non-generic GetValue(offset, Type) path.
54
        if (NullableHandler<T>.IsNullableValueType)
5,759,037✔
55
        {
56
            return NullableHandler<T>.Read(this, offset);
1,500,523✔
57
        }
58

59
        if (IsValid(offset))
4,258,514✔
60
        {
61
            return GetValidValue<T>(offset, typeof(T));
4,258,331✔
62
        }
63

64
        throw new InvalidCastException($"Column '{ColumnName}' value is null");
183✔
65
    }
66

67
    /// <summary>
68
    /// Called when the value at specified <param name="offset">offset</param> is valid (isn't null)
69
    /// </summary>
70
    /// <typeparam name="T">Type of the return value</typeparam>
71
    /// <param name="offset">Position to read the data from</param>
72
    /// <param name="targetType">Type of the return value</param>
73
    /// <returns>Data at the specified offset</returns>
74
    protected virtual T GetValidValue<T>(ulong offset, Type targetType)
75
    {
76
        return (T)GetValue(offset, targetType);
517,325✔
77
    }
78

79
    public object GetValue(ulong offset)
80
    {
81
        return GetValue(offset, ClrType);
302,064✔
82
    }
83

84
    internal virtual object GetValue(ulong offset, Type targetType)
85
    {
UNCOV
86
        return DuckDBType switch
×
UNCOV
87
        {
×
UNCOV
88
            DuckDBType.Invalid => throw new DuckDBException($"Invalid type for column {ColumnName}"),
×
89
            _ => throw new ArgumentException($"Unrecognised type {DuckDBType} ({(int)DuckDBType}) for column {ColumnName}")
×
90
        };
×
91
    }
92

93
    internal object GetProviderSpecificValue(ulong offset)
94
    {
95
        return GetValue(offset, ProviderSpecificClrType);
48✔
96
    }
97

98
    protected virtual Type GetColumnType()
99
    {
100
        return DuckDBType switch
39,514!
101
        {
39,514✔
UNCOV
102
            DuckDBType.Invalid => throw new DuckDBException($"Invalid type for column {ColumnName}"),
×
103
            DuckDBType.Boolean => typeof(bool),
72✔
104
            DuckDBType.TinyInt => typeof(sbyte),
39✔
105
            DuckDBType.SmallInt => typeof(short),
39✔
106
            DuckDBType.Integer => typeof(int),
288✔
107
            DuckDBType.BigInt => typeof(long),
210✔
108
            DuckDBType.UnsignedTinyInt => typeof(byte),
42✔
109
            DuckDBType.UnsignedSmallInt => typeof(ushort),
39✔
110
            DuckDBType.UnsignedInteger => typeof(uint),
39✔
111
            DuckDBType.UnsignedBigInt => typeof(ulong),
57✔
112
            DuckDBType.Float => typeof(float),
27✔
113
            DuckDBType.Double => typeof(double),
57✔
114
            DuckDBType.Timestamp => typeof(DateTime),
81✔
115
            DuckDBType.Interval => typeof(TimeSpan),
15✔
116
            DuckDBType.Date => typeof(DateOnly),
57✔
117
            DuckDBType.Time => typeof(TimeOnly),
96✔
118
            DuckDBType.TimeTz => typeof(DateTimeOffset),
21✔
119
            DuckDBType.HugeInt => typeof(BigInteger),
15✔
120
            DuckDBType.UnsignedHugeInt => typeof(BigInteger),
3✔
121
            DuckDBType.Varchar => typeof(string),
408✔
122
            DuckDBType.Decimal => typeof(decimal),
207✔
123
            DuckDBType.TimestampS => typeof(DateTime),
21✔
124
            DuckDBType.TimestampMs => typeof(DateTime),
21✔
125
            DuckDBType.TimestampNs => typeof(DateTime),
24✔
126
            DuckDBType.Blob => typeof(Stream),
9✔
127
            DuckDBType.Enum => typeof(string),
42✔
128
            DuckDBType.Uuid => typeof(Guid),
24✔
129
            DuckDBType.Struct => typeof(Dictionary<string, object>),
9✔
130
            DuckDBType.Bit => typeof(string),
6✔
131
            DuckDBType.TimestampTz => typeof(DateTime),
24✔
132
            DuckDBType.VarInt => typeof(BigInteger),
37,522✔
UNCOV
133
            _ => throw new ArgumentException($"Unrecognised type {DuckDBType} ({(int)DuckDBType}) for column {ColumnName}")
×
134
        };
39,514✔
135
    }
136

137
    protected virtual Type GetColumnProviderSpecificType()
138
    {
139
        return DuckDBType switch
123!
140
        {
123✔
UNCOV
141
            DuckDBType.Invalid => throw new DuckDBException($"Invalid type for column {ColumnName}"),
×
142
            DuckDBType.Boolean => typeof(bool),
3✔
143
            DuckDBType.TinyInt => typeof(sbyte),
3✔
144
            DuckDBType.SmallInt => typeof(short),
3✔
145
            DuckDBType.Integer => typeof(int),
9✔
146
            DuckDBType.BigInt => typeof(long),
3✔
147
            DuckDBType.UnsignedTinyInt => typeof(byte),
3✔
148
            DuckDBType.UnsignedSmallInt => typeof(ushort),
3✔
149
            DuckDBType.UnsignedInteger => typeof(uint),
3✔
150
            DuckDBType.UnsignedBigInt => typeof(ulong),
3✔
151
            DuckDBType.Float => typeof(float),
3✔
152
            DuckDBType.Double => typeof(double),
6✔
153
            DuckDBType.Timestamp => typeof(DuckDBTimestamp),
6✔
154
            DuckDBType.Interval => typeof(DuckDBInterval),
3✔
155
            DuckDBType.Date => typeof(DuckDBDateOnly),
6✔
156
            DuckDBType.Time => typeof(DuckDBTimeOnly),
3✔
157
            DuckDBType.TimeTz => typeof(DuckDBTimeTz),
3✔
158
            DuckDBType.HugeInt => typeof(DuckDBHugeInt),
3✔
159
            DuckDBType.UnsignedHugeInt => typeof(DuckDBUHugeInt),
3✔
160
            DuckDBType.Varchar => typeof(string),
3✔
161
            DuckDBType.Decimal => typeof(decimal),
12✔
162
            DuckDBType.TimestampS => typeof(DuckDBTimestamp),
3✔
163
            DuckDBType.TimestampMs => typeof(DuckDBTimestamp),
3✔
164
            DuckDBType.TimestampNs => typeof(DuckDBTimestamp),
3✔
165
            DuckDBType.Blob => typeof(Stream),
3✔
166
            DuckDBType.Enum => typeof(string),
9✔
167
            DuckDBType.Uuid => typeof(Guid),
3✔
168
            DuckDBType.Struct => typeof(Dictionary<string, object>),
3✔
169
            DuckDBType.Bit => typeof(string),
3✔
170
            DuckDBType.TimestampTz => typeof(DuckDBTimestamp),
6✔
171
            DuckDBType.VarInt => typeof(BigInteger),
3✔
UNCOV
172
            _ => throw new ArgumentException($"Unrecognised type {DuckDBType} ({(int)DuckDBType}) for column {ColumnName}")
×
173
        };
123✔
174
    }
175

176
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
177
    protected unsafe T GetFieldData<T>(ulong offset) where T : unmanaged => *((T*)DataPointer + offset);
16,968,767✔
178

179
    /// <summary>
180
    /// Updates the data and validity pointers for a new chunk without recreating the reader.
181
    /// Composite readers (Struct, List, Map, Decimal) override this to also reset nested readers.
182
    /// </summary>
183
    internal virtual unsafe void Reset(IntPtr vector)
184
    {
185
        DataPointer = NativeMethods.Vectors.DuckDBVectorGetData(vector);
1,839✔
186
        validityMaskPointer = NativeMethods.Vectors.DuckDBVectorGetValidity(vector);
1,839✔
187
    }
1,839✔
188

189
    public virtual void Dispose()
190
    {
191
    }
40,804✔
192

193
    private static class NullableHandler<T>
194
    {
195
        private static Type type;
196
        private static Type? underlyingType;
197

198
        static NullableHandler()
199
        {
200
            type = typeof(T);
432✔
201
            underlyingType = Nullable.GetUnderlyingType(type);
432✔
202
            IsNullableValueType = underlyingType != null;
432✔
203
            Read = IsNullableValueType ? Compile() : null!;
432✔
204
        }
432✔
205

206
        public static readonly bool IsNullableValueType;
207
        public static readonly Func<VectorDataReaderBase, ulong, T> Read;
208

209
        // For T = int?, builds a delegate equivalent to:
210
        //   (VectorDataReaderBase reader, ulong offset) =>
211
        //       reader.IsValid(offset)
212
        //           ? (int?)reader.GetValidValue<int>(offset, typeof(int))
213
        //           : default(int?)
214
        private static Func<VectorDataReaderBase, ulong, T> Compile()
215
        {
216
            if (underlyingType is null) return null!;
69!
217

218
            var reader = Expression.Parameter(typeof(VectorDataReaderBase));
69✔
219
            var offset = Expression.Parameter(typeof(ulong));
69✔
220

221
            var isValid = Expression.Call(reader, typeof(VectorDataReaderBase).GetMethod(nameof(IsValid))!, offset);
69✔
222

223
            var methodInfo = typeof(VectorDataReaderBase).GetMethod(nameof(GetValidValue), BindingFlags.Instance | BindingFlags.NonPublic)!;
69✔
224
            var genericGetValidValue = methodInfo.MakeGenericMethod(underlyingType);
69✔
225

226
            var getValidValue = Expression.Call(reader, genericGetValidValue, offset, Expression.Constant(underlyingType));
69✔
227

228
            var body = Expression.Condition(isValid, Expression.Convert(getValidValue, type), Expression.Default(type));
69✔
229

230
            return Expression.Lambda<Func<VectorDataReaderBase, ulong, T>>(body, reader, offset).Compile();
69✔
231
        }
232
    }
233
}
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