• 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

91.11
/DuckDB.NET.Data/DataChunk/Reader/MapVectorDataReader.cs
1
using System.Diagnostics.CodeAnalysis;
2
using System.Runtime.CompilerServices;
3

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

6
// How the keys and values of a Dictionary<TKey, TValue> are read without a box (CreateBuilder and the builders)
7
// is in MapVectorDataReader.Builders.cs.
8
internal sealed partial class MapVectorDataReader : VectorDataReaderBase
9
{
10
    private readonly VectorDataReaderBase keyReader;
11
    private readonly VectorDataReaderBase valueReader;
12

13
    // What GetValueCore found out about the type it was asked for last. It is worked out once per type it is asked
14
    // for, as a column is read as the same type row after row.
15
    private Type? planType;
16
    private MapPlan plan;
17

18
    internal unsafe MapVectorDataReader(IntPtr vector, void* dataPointer, ulong* validityMaskPointer, DuckDBType columnType, DuckDBLogicalType logicalColumnType, string columnName)
19
        : base(dataPointer, validityMaskPointer, columnType, columnName)
1,701 ✔
20
    {
21
        using var keyTypeLogical = NativeMethods.LogicalType.DuckDBMapTypeKeyType(logicalColumnType);
1,701 ✔
22
        using var valueTypeLogical = NativeMethods.LogicalType.DuckDBMapTypeValueType(logicalColumnType);
1,701 ✔
23

24
        var childVector = NativeMethods.Vectors.DuckDBListVectorGetChild(vector);
1,701 ✔
25

26
        var keyVector = NativeMethods.Vectors.DuckDBStructVectorGetChild(childVector, 0);
1,701 ✔
27
        var valueVector = NativeMethods.Vectors.DuckDBStructVectorGetChild(childVector, 1);
1,701 ✔
28

29
        keyReader = VectorDataReaderFactory.CreateReader(keyVector, keyTypeLogical, columnName);
1,701 ✔
30
        valueReader = VectorDataReaderFactory.CreateReader(valueVector, valueTypeLogical, columnName);
1,701 ✔
31
    }
3,402 ✔
32

33
    protected override Type GetColumnType()
34
    {
35
        return typeof(Dictionary<,>).MakeGenericType(keyReader.ClrType, valueReader.ClrType);
39 ✔
36
    }
37

38
    protected override Type GetColumnProviderSpecificType()
39
    {
40
        return typeof(Dictionary<,>).MakeGenericType(keyReader.ProviderSpecificClrType, valueReader.ProviderSpecificClrType);
6 ✔
41
    }
42

43
    internal override unsafe object GetValueCore(ulong offset, Type targetType)
44
    {
45
        if (DuckDBType != DuckDBType.Map)
299,169 !
46
        {
47
            return base.GetValueCore(offset, targetType);
×
48
        }
49

50
        if (targetType != planType)
299,169 ✔
51
        {
52
            plan = CreatePlan(targetType);
218,154 ✔
53
            planType = targetType;
208,032 ✔
54
        }
55

56
        var listData = (DuckDBListEntry*)DataPointer + offset;
289,047 ✔
57

58
        if (plan.Builder != null)
289,047 ✔
59
        {
60
            return plan.Builder.Build(keyReader, valueReader, listData->Offset, listData->Length);
271,035 ✔
61
        }
62

63
        return GetBoxedDictionary(targetType, listData->Offset, listData->Length);
18,012 ✔
64
    }
65

66
    // Any type without a builder (see CreateBuilder): created as itself and filled through IDictionary, with every key
67
    // and value read as an object.
68
    private IDictionary GetBoxedDictionary(Type targetType, ulong listOffset, ulong length)
69
    {
70
        var instance = Activator.CreateInstance(targetType) as IDictionary ?? throw NotADictionaryException();
18,012 ✔
71

72
        for (ulong i = 0; i < length; i++)
108,048 ✔
73
        {
74
            var childOffset = i + listOffset;
36,018 ✔
75

76
            if (!keyReader.IsValid(childOffset))
36,018 !
77
            {
78
                ThrowNullKey(ColumnName);
×
79
            }
80

81
            var key = keyReader.GetValue(childOffset, plan.KeyType);
36,018 ✔
82
            var value = valueReader.IsValid(childOffset) ? valueReader.GetValue(childOffset, plan.ValueType) : null;
36,018 ✔
83

84
            if (plan.AllowsNullValues || value != null)
36,018 !
85
            {
86
                instance.Add(key, value);
36,018 ✔
87
            }
88
            else
89
            {
90
                ThrowNullValue(ColumnName);
×
91
            }
92
        }
93

94
        return instance;
18,006 ✔
95
    }
96

97
    // Returns the exception, for '?? throw', as the list reader's NotAListException does.
98
    [MethodImpl(MethodImplOptions.NoInlining)]
99
    private InvalidOperationException NotADictionaryException() => new($"Cannot read Map column {ColumnName} in a non-dictionary type");
6 ✔
100

101
    // Used by the IDictionary path and by the builders. A child reader carries the name of the map column.
102
    [DoesNotReturn]
103
    [MethodImpl(MethodImplOptions.NoInlining)]
104
    private static void ThrowNullValue(string columnName) => throw new InvalidCastException($"The Map in column {columnName} contains null value but dictionary does not allow null values");
36,009 ✔
105

106
    // Used by the IDictionary path and by the builders. SQL cannot make a NULL map key, but a map can also arrive
107
    // from Parquet, Arrow or the C API.
108
    [DoesNotReturn]
109
    [MethodImpl(MethodImplOptions.NoInlining)]
110
    private static void ThrowNullKey(string columnName) => throw new InvalidCastException($"The Map in column {columnName} contains a null key");
×
111

112
    // Not inlined, so that GetValueCore does not set up a native-call frame on every row (Reader/DESIGN.md).
113
    [MethodImpl(MethodImplOptions.NoInlining)]
114
    private MapPlan CreatePlan(Type targetType)
115
    {
116
        // The key and value types are those of the Dictionary<TKey, TValue> the target is or derives from, so a class
117
        // that is not generic itself, or whose type arguments are not the key and value, is read by its base. Another
118
        // type (a SortedDictionary, a Hashtable) gives its own type arguments, or none.
119
        var dictionaryType = targetType.FindGenericBase(typeof(Dictionary<,>));
218,154 ✔
120
        var arguments = (dictionaryType ?? targetType).GetGenericArguments();
218,154 ✔
121

122
        // What the IDictionary path reads a key and a value as, and whether it adds a NULL value; a builder knows
123
        // its own types.
124
        var keyTargetType = keyReader.ClrType;
218,154 ✔
125
        var valueTargetType = valueReader.ClrType;
218,154 ✔
126
        var allowsNullValues = false;
218,154 ✔
127

128
        if (arguments.Length == 2)
218,154 ✔
129
        {
130
            if (arguments[0] == typeof(object) || arguments[1] == typeof(object))
218,148 ✔
131
            {
132
                throw new InvalidCastException($"Cannot read Map column {ColumnName} as a dictionary with object keys or values. Use a dictionary with concrete key and value types, or GetValue().");
10,122 ✔
133
            }
134

135
            arguments[0].AllowsNullValue(out _, out var underlyingKeyType);
208,026 ✔
136
            keyTargetType = underlyingKeyType ?? arguments[0];
208,026 ✔
137

138
            allowsNullValues = arguments[1].AllowsNullValue(out _, out var underlyingValueType);
208,026 ✔
139
            valueTargetType = underlyingValueType ?? arguments[1];
208,026 ✔
140
        }
141

142
        return new MapPlan(Builder: CreateBuilder(targetType, dictionaryType), KeyType: keyTargetType,
208,032 ✔
143
                           ValueType: valueTargetType, AllowsNullValues: allowsNullValues);
208,032 ✔
144
    }
145

146
    // Builder reads keys and values on the typed path, or is null. KeyType, ValueType and AllowsNullValues are for
147
    // the IDictionary path: the types a key and a value are read as, and whether a NULL value is added.
148
    private readonly record struct MapPlan(MapBuilder? Builder, Type KeyType, Type ValueType, bool AllowsNullValues);
650,130 ✔
149

150
    internal override void Reset(IntPtr vector)
151
    {
152
        base.Reset(vector);
381 ✔
153
        var childVector = NativeMethods.Vectors.DuckDBListVectorGetChild(vector);
381 ✔
154
        var keyVector = NativeMethods.Vectors.DuckDBStructVectorGetChild(childVector, 0);
381 ✔
155
        var valueVector = NativeMethods.Vectors.DuckDBStructVectorGetChild(childVector, 1);
381 ✔
156
        keyReader.Reset(keyVector);
381 ✔
157
        valueReader.Reset(valueVector);
381 ✔
158
    }
381 ✔
159

160
    public override void Dispose()
161
    {
162
        keyReader.Dispose();
1,512 ✔
163
        valueReader.Dispose();
1,512 ✔
164
        base.Dispose();
1,512 ✔
165
    }
1,512 ✔
166
}
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