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

Giorgi / DuckDB.NET / 33165511888

28 Aug 2026 11:00AM UTC coverage: 88.316% (+0.1%) from 88.203%
33165511888

push

github

Giorgi
Make GetFieldValue<object> return the same value as GetValue

DbDataReader defines GetFieldValue<T> as (T)GetValue(ordinal), so
GetFieldValue<object> is the ADO.NET spelling of "the default value",
and providers such as Npgsql honour it. Here the request fell through to
the vector readers, which served object inconsistently: numeric and
hugeint columns threw InvalidCastException, enums threw
NullReferenceException, timestamps and dates returned native structs,
structs returned an empty object, lists and maps threw, and nulls threw
instead of returning DBNull.

Route object requests through GetValue at the reader level, which the
scalar function parameter path already did. Nested object targets such
as List<object> stay rejected, since only the top-level object has a
defined meaning.

1586 of 1883 branches covered (84.23%)

Branch coverage included in aggregate %.

2 of 2 new or added lines in 1 file covered. (100.0%)

12 existing lines in 2 files now uncovered.

3161 of 3492 relevant lines covered (90.52%)

250660.27 hits per line

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

85.71
/DuckDB.NET.Data/DataChunk/Reader/MapVectorDataReader.cs
1
namespace DuckDB.NET.Data.DataChunk.Reader;
2

3
internal sealed class MapVectorDataReader : VectorDataReaderBase
4
{
5
    private readonly VectorDataReaderBase keyReader;
6
    private readonly VectorDataReaderBase valueReader;
7

8
    internal unsafe MapVectorDataReader(IntPtr vector, void* dataPointer, ulong* validityMaskPointer, DuckDBType columnType, DuckDBLogicalType logicalColumnType, string columnName) 
9
                    : base(dataPointer, validityMaskPointer, columnType, columnName)
132✔
10
    {
11
        using var keyTypeLogical = NativeMethods.LogicalType.DuckDBMapTypeKeyType(logicalColumnType);
132✔
12
        using var valueTypeLogical = NativeMethods.LogicalType.DuckDBMapTypeValueType(logicalColumnType);
132✔
13

14
        var childVector = NativeMethods.Vectors.DuckDBListVectorGetChild(vector);
132✔
15

16
        var keyVector = NativeMethods.Vectors.DuckDBStructVectorGetChild(childVector, 0);
132✔
17
        var valueVector = NativeMethods.Vectors.DuckDBStructVectorGetChild(childVector, 1);
132✔
18

19
        keyReader = VectorDataReaderFactory.CreateReader(keyVector, keyTypeLogical, columnName);
132✔
20
        valueReader = VectorDataReaderFactory.CreateReader(valueVector, valueTypeLogical, columnName);
132✔
21
    }
264✔
22

23
    protected override Type GetColumnType()
24
    {
25
        return typeof(Dictionary<,>).MakeGenericType(keyReader.ClrType, valueReader.ClrType);
14✔
26
    }
27

28
    protected override Type GetColumnProviderSpecificType()
29
    {
30
        return typeof(Dictionary<,>).MakeGenericType(keyReader.ProviderSpecificClrType, valueReader.ProviderSpecificClrType);
4✔
31
    }
32

33
    internal override unsafe object GetValue(ulong offset, Type targetType)
34
    {
35
        if (DuckDBType != DuckDBType.Map)
40!
36
        {
37
            return base.GetValue(offset, targetType);
×
38
        }
39

40
        if (Activator.CreateInstance(targetType) is not IDictionary instance)
40✔
41
        {
42
            throw new InvalidOperationException($"Cannot read Map column {ColumnName} in a non-dictionary type");
2✔
43
        }
44

45
        var arguments = targetType.GetGenericArguments();
38✔
46

47
        var keyTargetType = keyReader.ClrType;
38✔
48
        var valueTargetType = valueReader.ClrType;
38✔
49
        var allowsNullValues = false;
38✔
50

51
        if (arguments.Length == 2)
38✔
52
        {
53
            if (arguments[0] == typeof(object) || arguments[1] == typeof(object))
38✔
54
            {
55
                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().");
4✔
56
            }
57

58
            arguments[0].AllowsNullValue(out _, out var underlyingKeyType);
34✔
59
            keyTargetType = underlyingKeyType ?? arguments[0];
34✔
60

61
            allowsNullValues = arguments[1].AllowsNullValue(out _, out var underlyingValueType);
34✔
62
            valueTargetType = underlyingValueType ?? arguments[1];
34✔
63
        }
64

65
        var listData = (DuckDBListEntry*)DataPointer + offset;
34✔
66

67
        for (ulong i = 0; i < listData->Length; i++)
192✔
68
        {
69
            var childOffset = i + listData->Offset;
64✔
70

71
            var key = keyReader.IsValid(childOffset) ? keyReader.GetValue(childOffset, keyTargetType) : null!;
64!
72
            var value = valueReader.IsValid(childOffset) ? valueReader.GetValue(childOffset, valueTargetType) : null;
64✔
73

74
            if (allowsNullValues || value != null)
64✔
75
            {
76
                instance.Add(key, value);
62✔
77
            }
78
            else
79
            {
80
                throw new InvalidCastException($"The Map in column {ColumnName} contains null value but dictionary does not allow null values");
2✔
81
            }
82
        }
83

84
        return instance;
32✔
85
    }
86

87
    internal override void Reset(IntPtr vector)
88
    {
UNCOV
89
        base.Reset(vector);
×
UNCOV
90
        var childVector = NativeMethods.Vectors.DuckDBListVectorGetChild(vector);
×
UNCOV
91
        var keyVector = NativeMethods.Vectors.DuckDBStructVectorGetChild(childVector, 0);
×
UNCOV
92
        var valueVector = NativeMethods.Vectors.DuckDBStructVectorGetChild(childVector, 1);
×
UNCOV
93
        keyReader.Reset(keyVector);
×
UNCOV
94
        valueReader.Reset(valueVector);
×
UNCOV
95
    }
×
96

97
    public override void Dispose()
98
    {
99
        keyReader.Dispose();
6✔
100
        valueReader.Dispose();
6✔
101
        base.Dispose();
6✔
102
    }
6✔
103
}
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