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

Giorgi / DuckDB.NET / 14602424605

22 Apr 2025 06:53PM UTC coverage: 89.849% (+0.02%) from 89.832%
14602424605

Pull #258

github

web-flow
Merge 44bb2141c into 35e92d966
Pull Request #258: Cache prepared statements

1114 of 1277 branches covered (87.24%)

Branch coverage included in aggregate %.

91 of 95 new or added lines in 3 files covered. (95.79%)

1 existing line in 1 file now uncovered.

2152 of 2358 relevant lines covered (91.26%)

749413.62 hits per line

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

96.45
/DuckDB.NET.Data/DuckDBDataReader.cs
1
using DuckDB.NET.Data.DataChunk.Reader;
2
using DuckDB.NET.Native;
3
using System;
4
using System.Collections;
5
using System.Collections.Generic;
6
using System.Data;
7
using System.Data.Common;
8
using System.IO;
9

10
namespace DuckDB.NET.Data;
11

12
public class DuckDBDataReader : DbDataReader
13
{
14
    private readonly DuckDBCommand command;
15
    private readonly bool closeConnection;
16

17
    private DuckDBResult? currentResult;
18
    private DuckDBDataChunk? currentChunk;
19

20
    private int fieldCount;
21

22
    private ulong currentChunkRowCount;
23
    private ulong rowsReadFromCurrentChunk;
24

25
    private bool closed;
26
    private bool hasRows;
27
    private bool streamingResult;
28
    private long currentChunkIndex;
29

30
    private readonly IEnumerator<PreparedStatement.PreparedStatement> statementEnumerator;
31
    private VectorDataReaderBase[] vectorReaders = [];
39,315✔
32
    private Dictionary<string, int> columnMapping = [];
39,315✔
33

34
    internal DuckDBDataReader(DuckDBCommand command, IEnumerable<PreparedStatement.PreparedStatement> statements, bool closeConnection)
39,315✔
35
    {
36
        this.command = command;
39,315✔
37
        this.closeConnection = closeConnection;
39,315✔
38
        statementEnumerator = statements.GetEnumerator();
39,315✔
39

40
        InitNextReader();
39,315✔
41

42
        // Do not modify the command's state if an exception was thrown in InitNextReader().
43
        command.DataReader = this;
39,291✔
44
    }
39,291✔
45

46
    private bool InitNextReader()
47
    {
48
        while (statementEnumerator.MoveNext())
39,522✔
49
        {
50
            currentResult?.Dispose();
39,321✔
51
            currentResult = null;  // Prevent double disposal.
39,321✔
52

53
            try
54
            {
55
                var current = statementEnumerator.Current.Execute();
39,321✔
56
                currentResult = current;
39,303✔
57

58
                if (NativeMethods.Query.DuckDBResultReturnType(current) == DuckDBResultType.QueryResult)
39,303✔
59
                {
60
                    currentChunkIndex = 0;
39,297✔
61

62
                    columnMapping = [];
39,297✔
63
                    fieldCount = (int)NativeMethods.Query.DuckDBColumnCount(ref current);
39,297✔
64
                    streamingResult = NativeMethods.Types.DuckDBResultIsStreaming(current) > 0;
39,297✔
65

66
                    hasRows = InitChunkData();
39,297✔
67

68
                    return true;
39,297✔
69
                }
70
            }
6✔
71
            catch
18✔
72
            {
73
                Dispose();
18✔
74
                throw;
18✔
75
            }
76
        }
77

78
        return false;
192✔
79
    }
39,297✔
80

81
    private bool InitChunkData()
82
    {
83
        foreach (var reader in vectorReaders)
82,024✔
84
        {
85
            reader.Dispose();
1,180✔
86
        }
87

88
        var current = currentResult!.Value;
39,832✔
89

90
        currentChunk?.Dispose();
39,832✔
91
        currentChunk = streamingResult ? NativeMethods.StreamingResult.DuckDBStreamFetchChunk(current) : NativeMethods.Types.DuckDBResultGetChunk(current, currentChunkIndex);
39,832✔
92

93
        rowsReadFromCurrentChunk = 0;
39,832✔
94

95
        currentChunkRowCount = NativeMethods.DataChunks.DuckDBDataChunkGetSize(currentChunk);
39,832✔
96

97
        if (vectorReaders.Length != fieldCount)
39,832✔
98
        {
99
            vectorReaders = new VectorDataReaderBase[fieldCount];
39,288✔
100
        }
101

102
        for (int index = 0; index < fieldCount; index++)
178,768✔
103
        {
104
            var vector = NativeMethods.DataChunks.DuckDBDataChunkGetVector(currentChunk, index);
49,552✔
105

106
            using var logicalType = NativeMethods.Query.DuckDBColumnLogicalType(ref current, index);
49,552✔
107

108
            var columnName = vectorReaders[index]?.ColumnName ?? NativeMethods.Query.DuckDBColumnName(ref current, index).ToManagedString(false);
49,552✔
109
            vectorReaders[index] = VectorDataReaderFactory.CreateReader(vector, logicalType, columnName);
49,552✔
110
        }
111

112
        if (columnMapping.Count == 0)
39,832✔
113
        {
114
            for (var i = 0; i < vectorReaders.Length; i++)
175,356✔
115
            {
116
                if (!columnMapping.ContainsKey(vectorReaders[i].ColumnName))
48,381✔
117
                {
118
                    columnMapping.Add(vectorReaders[i].ColumnName, i);
48,378✔
119
                }
120
            }
121
        }
122

123
        return currentChunkRowCount > 0;
39,832✔
124
    }
125

126
    public override bool GetBoolean(int ordinal)
127
    {
128
        return GetFieldValue<bool>(ordinal);
9✔
129
    }
130

131
    public override byte GetByte(int ordinal)
132
    {
133
        return GetFieldValue<byte>(ordinal);
18✔
134
    }
135

136
    public override long GetBytes(int ordinal, long dataOffset, byte[]? buffer, int bufferOffset, int length)
137
    {
138
        throw new NotImplementedException();
×
139
    }
140

141
    public override char GetChar(int ordinal)
142
    {
143
        throw new NotSupportedException();
×
144
    }
145

146
    public override long GetChars(int ordinal, long dataOffset, char[]? buffer, int bufferOffset, int length)
147
    {
148
        throw new NotSupportedException();
×
149
    }
150

151
    public override string GetDataTypeName(int ordinal)
152
    {
153
        return vectorReaders[ordinal].DuckDBType.ToString();
6✔
154
    }
155

156
    public override DateTime GetDateTime(int ordinal)
157
    {
158
        return GetFieldValue<DateTime>(ordinal);
99✔
159
    }
160

161
    public override decimal GetDecimal(int ordinal)
162
    {
163
        return GetFieldValue<decimal>(ordinal);
357✔
164
    }
165

166
    public override double GetDouble(int ordinal)
167
    {
168
        return GetFieldValue<double>(ordinal);
3✔
169
    }
170

171
    public override Type GetFieldType(int ordinal)
172
    {
173
        return vectorReaders[ordinal].ClrType;
19,839✔
174
    }
175

176
    public override Type GetProviderSpecificFieldType(int ordinal)
177
    {
178
        return vectorReaders[ordinal].ProviderSpecificClrType;
117✔
179
    }
180

181
    public override float GetFloat(int ordinal)
182
    {
183
        return GetFieldValue<float>(ordinal);
3✔
184
    }
185

186
    public override Guid GetGuid(int ordinal)
187
    {
188
        return GetFieldValue<Guid>(ordinal);
12✔
189
    }
190

191
    public override short GetInt16(int ordinal)
192
    {
193
        return GetFieldValue<short>(ordinal);
18✔
194
    }
195

196
    public override int GetInt32(int ordinal)
197
    {
198
        return GetFieldValue<int>(ordinal);
30,060✔
199
    }
200

201
    public override long GetInt64(int ordinal)
202
    {
203
        return GetFieldValue<long>(ordinal);
18✔
204
    }
205

206
    public override string GetName(int ordinal)
207
    {
208
        return vectorReaders[ordinal].ColumnName;
954✔
209
    }
210

211
    public override int GetOrdinal(string name)
212
    {
213
        if (columnMapping.TryGetValue(name, out var index))
180✔
214
        {
215
            return index;
174✔
216
        }
217

218
        throw new DuckDBException($"Column with name {name} was not found.");
6✔
219
    }
220

221
    public override string GetString(int ordinal)
222
    {
223
        return GetFieldValue<string>(ordinal);
30,042✔
224
    }
225

226
    public override T GetFieldValue<T>(int ordinal)
227
    {
228
        CheckRowRead();
748,677✔
229

230
        return vectorReaders[ordinal].GetValue<T>(rowsReadFromCurrentChunk - 1);
748,674✔
231
    }
232

233
    public override object GetValue(int ordinal)
234
    {
235
        CheckRowRead();
173,919✔
236

237
        return IsDBNull(ordinal) ? DBNull.Value : vectorReaders[ordinal].GetValue(rowsReadFromCurrentChunk - 1);
173,916✔
238
    }
239

240
    public override object GetProviderSpecificValue(int ordinal)
241
    {
242
        CheckRowRead();
132✔
243

244
        return IsDBNull(ordinal) ? DBNull.Value : vectorReaders[ordinal].GetProviderSpecificValue(rowsReadFromCurrentChunk - 1);
132✔
245
    }
246

247
    public override int GetValues(object[] values)
248
    {
249
        for (var i = 0; i < FieldCount; i++)
3,546✔
250
        {
251
            values[i] = GetValue(i);
1,242✔
252
        }
253

254
        return FieldCount;
531✔
255
    }
256

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

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

266
        return !vectorReaders[ordinal].IsValid(rowsReadFromCurrentChunk - 1);
234,270✔
267
    }
268

269
    public override int FieldCount => fieldCount;
3,000✔
270

271
    public override object this[int ordinal] => GetValue(ordinal);
46,992✔
272

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

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

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

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

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

286
    public override bool Read()
287
    {
288
        if (rowsReadFromCurrentChunk == currentChunkRowCount)
485,874✔
289
        {
290
            currentChunkIndex++;
535✔
291
            var hasData = InitChunkData();
535✔
292

293
            if (hasData)
535✔
294
            {
295
                rowsReadFromCurrentChunk++;
112✔
296
            }
297

298
            return hasData;
535✔
299
        }
300
        else
301
        {
302
            rowsReadFromCurrentChunk++;
485,339✔
303

304
            return true;
485,339✔
305
        }
306
    }
307

308
    public override int Depth { get; }
×
309

310
    public override IEnumerator GetEnumerator()
311
    {
312
        return new DbEnumerator(this, closeConnection);
3✔
313
    }
314

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

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

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

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

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

354
        return table;
45✔
355
    }
356

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

361
        command.DataReader = null;
39,309✔
362

363
        foreach (var reader in vectorReaders)
175,362✔
364
        {
365
            reader.Dispose();
48,372✔
366
        }
367

368
        currentResult?.Dispose();
39,309✔
369
        currentResult = null;  // Prevent double disposal.
39,309✔
370

371
        currentChunk?.Dispose();
39,309✔
372

373
        try
374
        {
375
            // Try to consume the enumerator to ensure that all statements are prepared.
376
            while (statementEnumerator.MoveNext())
39,309✔
377
            {
378
            }
379
        }
39,309✔
NEW
380
        catch
×
381
        {
382
            // Dispose() must not throw exceptions.
UNCOV
383
        }
×
384

385
        statementEnumerator.Dispose();
39,309✔
386

387
        closed = true;
39,309✔
388

389
        if (closeConnection)
39,309✔
390
        {
391
            command.CloseConnection();
9✔
392
        }
393
    }
39,309✔
394

395
    private void CheckRowRead()
396
    {
397
        if (rowsReadFromCurrentChunk <= 0)
1,157,001✔
398
        {
399
            throw new InvalidOperationException("No row has been read");
9✔
400
        }
401
    }
1,156,992✔
402
}
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

© 2025 Coveralls, Inc