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

Giorgi / DuckDB.NET / 23004948356

12 Mar 2026 01:42PM UTC coverage: 89.595% (+0.07%) from 89.526%
23004948356

push

github

Giorgi
Update GitVersion

1277 of 1485 branches covered (85.99%)

Branch coverage included in aggregate %.

2658 of 2907 relevant lines covered (91.43%)

451450.8 hits per line

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

96.81
/DuckDB.NET.Data/DuckDBDataReader.cs
1
using DuckDB.NET.Data.Common;
2
using DuckDB.NET.Data.DataChunk.Reader;
3
using System.IO;
4
using System.Runtime.CompilerServices;
5

6
namespace DuckDB.NET.Data;
7

8
public class DuckDBDataReader : DbDataReader
9
{
10
    private readonly DuckDBCommand command;
11
    private readonly CommandBehavior behavior;
12

13
    private DuckDBResult currentResult;
14
    private DuckDBDataChunk? currentChunk;
15

16
    private int fieldCount;
17

18
    private ulong currentChunkRowCount;
19
    private ulong rowsReadFromCurrentChunk;
20

21
    private bool closed;
22
    private bool hasRows;
23
    private bool streamingResult;
24
    private long currentChunkIndex;
25

26
    private readonly IEnumerator<DuckDBResult> resultEnumerator;
27
    private VectorDataReaderBase[] vectorReaders = [];
39,805✔
28
    private Dictionary<string, int> columnMapping = [];
39,805✔
29

30
    internal DuckDBDataReader(DuckDBCommand command, IEnumerable<DuckDBResult> queryResults, CommandBehavior behavior)
39,805✔
31
    {
32
        this.command = command;
39,805✔
33
        this.behavior = behavior;
39,805✔
34
        resultEnumerator = queryResults.GetEnumerator();
39,805✔
35

36
        InitNextReader();
39,805✔
37
    }
39,757✔
38

39
    private bool InitNextReader()
40
    {
41
        foreach (var reader in vectorReaders)
81,776✔
42
        {
43
            reader?.Dispose();
729!
44
        }
45

46
        vectorReaders = [];
40,159✔
47

48
        while (resultEnumerator.MoveNext())
40,165✔
49
        {
50
            var result = resultEnumerator.Current;
39,769✔
51
            if (NativeMethods.Query.DuckDBResultReturnType(result) == DuckDBResultType.QueryResult)
39,769✔
52
            {
53
                currentChunkIndex = 0;
39,763✔
54
                currentResult = result;
39,763✔
55

56
                columnMapping = [];
39,763✔
57
                fieldCount = (int)NativeMethods.Query.DuckDBColumnCount(ref currentResult);
39,763✔
58
                streamingResult = NativeMethods.Types.DuckDBResultIsStreaming(currentResult) > 0;
39,763✔
59

60
                hasRows = InitChunkData();
39,763✔
61

62
                return true;
39,763✔
63
            }
64

65
            result.Close();
6✔
66
        }
67

68
        return false;
345✔
69
    }
70

71
    private bool InitChunkData()
72
    {
73
        var canReuse = vectorReaders.Length > 0;
40,486✔
74

75
        currentChunk?.Dispose();
40,486✔
76
        currentChunk = streamingResult ? NativeMethods.StreamingResult.DuckDBStreamFetchChunk(currentResult) : NativeMethods.Types.DuckDBResultGetChunk(currentResult, currentChunkIndex);
40,486✔
77

78
        rowsReadFromCurrentChunk = 0;
40,486✔
79

80
        currentChunkRowCount = NativeMethods.DataChunks.DuckDBDataChunkGetSize(currentChunk);
40,486✔
81

82
        if (!canReuse)
40,486✔
83
        {
84
            vectorReaders = new VectorDataReaderBase[fieldCount];
39,763✔
85
        }
86

87
        for (int index = 0; index < fieldCount; index++)
182,842✔
88
        {
89
            var vector = NativeMethods.DataChunks.DuckDBDataChunkGetVector(currentChunk, index);
50,935✔
90

91
            if (canReuse)
50,935✔
92
            {
93
                vectorReaders[index].Reset(vector);
1,425✔
94
            }
95
            else
96
            {
97
                using var logicalType = NativeMethods.Query.DuckDBColumnLogicalType(ref currentResult, index);
49,510✔
98

99
                var columnName = NativeMethods.Query.DuckDBColumnName(ref currentResult, index);
49,510✔
100
                vectorReaders[index] = VectorDataReaderFactory.CreateReader(vector, logicalType, columnName);
49,510✔
101
            }
102
        }
103

104
        if (columnMapping.Count == 0)
40,486✔
105
        {
106
            for (var i = 0; i < vectorReaders.Length; i++)
178,546✔
107
            {
108
                columnMapping.TryAdd(vectorReaders[i].ColumnName, i);
49,510✔
109
            }
110
        }
111

112
        return currentChunkRowCount > 0;
40,486✔
113
    }
114

115
    public override bool GetBoolean(int ordinal)
116
    {
117
        return GetFieldValue<bool>(ordinal);
81✔
118
    }
119

120
    public override byte GetByte(int ordinal)
121
    {
122
        return GetFieldValue<byte>(ordinal);
18✔
123
    }
124

125
    public override long GetBytes(int ordinal, long dataOffset, byte[]? buffer, int bufferOffset, int length)
126
    {
127
        throw new NotImplementedException();
×
128
    }
129

130
    public override char GetChar(int ordinal)
131
    {
132
        throw new NotSupportedException();
×
133
    }
134

135
    public override long GetChars(int ordinal, long dataOffset, char[]? buffer, int bufferOffset, int length)
136
    {
137
        throw new NotSupportedException();
×
138
    }
139

140
    public override string GetDataTypeName(int ordinal)
141
    {
142
        return vectorReaders[ordinal].DuckDBType.ToString();
6✔
143
    }
144

145
    public override DateTime GetDateTime(int ordinal)
146
    {
147
        return GetFieldValue<DateTime>(ordinal);
177✔
148
    }
149

150
    public override decimal GetDecimal(int ordinal)
151
    {
152
        return GetFieldValue<decimal>(ordinal);
462✔
153
    }
154

155
    public override double GetDouble(int ordinal)
156
    {
157
        return GetFieldValue<double>(ordinal);
3✔
158
    }
159

160
    public override Type GetFieldType(int ordinal)
161
    {
162
        return vectorReaders[ordinal].ClrType;
20,285✔
163
    }
164

165
    public override Type GetProviderSpecificFieldType(int ordinal)
166
    {
167
        return vectorReaders[ordinal].ProviderSpecificClrType;
123✔
168
    }
169

170
    public override float GetFloat(int ordinal)
171
    {
172
        return GetFieldValue<float>(ordinal);
9✔
173
    }
174

175
    public override Guid GetGuid(int ordinal)
176
    {
177
        return GetFieldValue<Guid>(ordinal);
12✔
178
    }
179

180
    public override short GetInt16(int ordinal)
181
    {
182
        return GetFieldValue<short>(ordinal);
18✔
183
    }
184

185
    public override int GetInt32(int ordinal)
186
    {
187
        return GetFieldValue<int>(ordinal);
30,072✔
188
    }
189

190
    public override long GetInt64(int ordinal)
191
    {
192
        return GetFieldValue<long>(ordinal);
18✔
193
    }
194

195
    public override string GetName(int ordinal)
196
    {
197
        return vectorReaders[ordinal].ColumnName;
1,377✔
198
    }
199

200
    public override int GetOrdinal(string name)
201
    {
202
        if (columnMapping.TryGetValue(name, out var index))
186✔
203
        {
204
            return index;
180✔
205
        }
206

207
        throw new DuckDBException($"Column with name {name} was not found.");
6✔
208
    }
209

210
    public override string GetString(int ordinal)
211
    {
212
        return GetFieldValue<string>(ordinal);
30,051✔
213
    }
214

215
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
216
    public override T GetFieldValue<T>(int ordinal)
217
    {
218
        CheckRowRead();
749,213✔
219

220
        return vectorReaders[ordinal].GetValueStrict<T>(rowsReadFromCurrentChunk - 1);
749,210✔
221
    }
222

223
    public override object GetValue(int ordinal)
224
    {
225
        CheckRowRead();
308,339✔
226

227
        var offset = rowsReadFromCurrentChunk - 1;
308,336✔
228
        var reader = vectorReaders[ordinal];
308,336✔
229

230
        return reader.IsValid(offset) ? reader.GetValue(offset) : DBNull.Value;
308,336✔
231
    }
232

233
    public override object GetProviderSpecificValue(int ordinal)
234
    {
235
        CheckRowRead();
132✔
236

237
        var offset = rowsReadFromCurrentChunk - 1;
132✔
238
        var reader = vectorReaders[ordinal];
132✔
239

240
        return reader.IsValid(offset) ? reader.GetProviderSpecificValue(offset) : DBNull.Value;
132✔
241
    }
242

243
    public override int GetValues(object[] values)
244
    {
245
        CheckRowRead();
531✔
246

247
        var offset = rowsReadFromCurrentChunk - 1;
531✔
248

249
        for (var i = 0; i < FieldCount; i++)
3,546✔
250
        {
251
            var reader = vectorReaders[i];
1,242✔
252
            values[i] = reader.IsValid(offset) ? reader.GetValue(offset) : DBNull.Value;
1,242✔
253
        }
254

255
        return FieldCount;
531✔
256
    }
257

258
    public override Stream GetStream(int ordinal)
259
    {
260
        return GetFieldValue<Stream>(ordinal);
27✔
261
    }
262

263
    public override bool IsDBNull(int ordinal)
264
    {
265
        CheckRowRead();
60,234✔
266

267
        return !vectorReaders[ordinal].IsValid(rowsReadFromCurrentChunk - 1);
60,231✔
268
    }
269

270
    public override int FieldCount => fieldCount;
3,360✔
271

272
    public override object this[int ordinal] => GetValue(ordinal);
77,232✔
273

274
    public override object this[string name] => GetValue(GetOrdinal(name));
3✔
275

276
    public override int RecordsAffected => -1;
×
277

278
    public override bool HasRows => hasRows;
18✔
279

280
    public override bool IsClosed => closed;
81✔
281

282
    public override bool NextResult()
283
    {
284
        return InitNextReader();
354✔
285
    }
286

287
    public override bool Read()
288
    {
289
        if (rowsReadFromCurrentChunk == currentChunkRowCount)
606,742✔
290
        {
291
            currentChunkIndex++;
723✔
292
            var hasData = InitChunkData();
723✔
293

294
            if (hasData)
723✔
295
            {
296
                rowsReadFromCurrentChunk++;
147✔
297
            }
298

299
            return hasData;
723✔
300
        }
301
        else
302
        {
303
            rowsReadFromCurrentChunk++;
606,019✔
304

305
            return true;
606,019✔
306
        }
307
    }
308

309
    public override int Depth { get; }
×
310

311
    public override IEnumerator GetEnumerator()
312
    {
313
        return new DbEnumerator(this, behavior == CommandBehavior.CloseConnection);
3✔
314
    }
315

316
    public override DataTable GetSchemaTable()
317
    {
318
        var table = new DataTable
45✔
319
        {
45✔
320
            Columns =
45✔
321
            {
45✔
322
                { SchemaTableColumn.ColumnName, typeof(string) },
45✔
323
                { SchemaTableColumn.ColumnOrdinal, typeof(int) },
45✔
324
                { SchemaTableColumn.ColumnSize, typeof(int) },
45✔
325
                { SchemaTableColumn.NumericPrecision, typeof(byte)},
45✔
326
                { SchemaTableColumn.NumericScale, typeof(byte) },
45✔
327
                { SchemaTableColumn.DataType, typeof(Type) },
45✔
328
                { SchemaTableColumn.AllowDBNull, typeof(bool)  }
45✔
329
            }
45✔
330
        };
45✔
331

332
        var rowData = new object[7];
45✔
333

334
        for (var i = 0; i < FieldCount; i++)
636✔
335
        {
336
            rowData[0] = GetName(i);
273✔
337
            rowData[1] = i;
273✔
338
            rowData[2] = -1;
273✔
339
            rowData[5] = GetFieldType(i);
273✔
340
            rowData[6] = true;
273✔
341

342
            if (vectorReaders[i] is DecimalVectorDataReader decimalVectorDataReader)
273✔
343
            {
344
                rowData[4] = decimalVectorDataReader.Scale;
6✔
345
                rowData[3] = decimalVectorDataReader.Precision;
6✔
346
            }
347
            else
348
            {
349
                rowData[3] = rowData[4] = DBNull.Value;
267✔
350
            }
351

352
            table.Rows.Add(rowData);
273✔
353
        }
354

355
        return table;
45✔
356
    }
357

358
    public override void Close()
359
    {
360
        if (closed) return;
39,166✔
361

362
        foreach (var reader in vectorReaders)
156,436✔
363
        {
364
            reader.Dispose();
39,130✔
365
        }
366

367
        currentChunk?.Dispose();
39,088✔
368
        currentResult.Close();
39,088✔
369

370
        if (behavior == CommandBehavior.CloseConnection)
39,088✔
371
        {
372
            command.CloseConnection();
6✔
373
        }
374

375
        closed = true;
39,088✔
376
        resultEnumerator.Dispose();
39,088✔
377
    }
39,088✔
378

379
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
380
    private void CheckRowRead()
381
    {
382
        if (rowsReadFromCurrentChunk <= 0)
1,118,449✔
383
        {
384
            ThrowNoRowRead();
9✔
385
        }
386
    }
1,118,440✔
387

388
    [MethodImpl(MethodImplOptions.NoInlining)]
389
    private static void ThrowNoRowRead()
390
    {
391
        throw new InvalidOperationException("No row has been read");
9✔
392
    }
393
}
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