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

Giorgi / DuckDB.NET / 21644766496

03 Feb 2026 07:34PM UTC coverage: 89.45%. First build
21644766496

push

github

Giorgi
Merge branch 'develop'

1203 of 1395 branches covered (86.24%)

Branch coverage included in aggregate %.

125 of 154 new or added lines in 7 files covered. (81.17%)

2375 of 2605 relevant lines covered (91.17%)

377965.41 hits per line

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

93.5
/DuckDB.NET.Data/DuckDBConnection.cs
1
using DuckDB.NET.Data.Connection;
2
using DuckDB.NET.Native;
3
using System;
4
using System.ComponentModel;
5
using System.Data;
6
using System.Data.Common;
7
using System.Diagnostics.CodeAnalysis;
8
using System.Runtime.CompilerServices;
9

10
namespace DuckDB.NET.Data;
11

12
public partial class DuckDBConnection : DbConnection
13
{
14
    private readonly ConnectionManager connectionManager = ConnectionManager.Default;
43,266✔
15
    private ConnectionState connectionState = ConnectionState.Closed;
16
    private DuckDBConnectionString? parsedConnection;
17
    private ConnectionReference? connectionReference;
18
    private bool inMemoryDuplication = false;
19
    
20
    private static readonly StateChangeEventArgs FromClosedToOpenEventArgs = new(ConnectionState.Closed, ConnectionState.Open);
2✔
21
    private static readonly StateChangeEventArgs FromOpenToClosedEventArgs = new(ConnectionState.Open, ConnectionState.Closed);
2✔
22

23
    #region Protected Properties
24

25
    protected override DbProviderFactory? DbProviderFactory => DuckDBClientFactory.Instance;
×
26

27
    #endregion
28

29
    internal DuckDBTransaction? Transaction { get; set; }
53,878✔
30

31
    internal DuckDBConnectionString ParsedConnection => parsedConnection ??= DuckDBConnectionStringBuilder.Parse(ConnectionString);
43,316✔
32

33
    public DuckDBConnection()
2✔
34
    {
35
        ConnectionString = string.Empty;
2✔
36
    }
2✔
37

38
    public DuckDBConnection(string connectionString)
43,264✔
39
    {
40
        ConnectionString = connectionString;
43,264✔
41
    }
43,264✔
42

43
#if NET6_0_OR_GREATER
44
    [AllowNull]
45
#endif
46
    [DefaultValue("")]
47
    public override string ConnectionString { get; set; }
126,756✔
48

49
    public override string Database
50
    {
51
        get
52
        {
53
            if (!string.IsNullOrEmpty(ConnectionString))
4✔
54
            {
55
                return ParsedConnection.DataSource;
2✔
56
            }
57

58
            throw new InvalidOperationException("Connection string must be specified.");
2✔
59
        }
60
    }
61

62
    public override string DataSource
63
    {
64
        get
65
        {
66
            if (!string.IsNullOrEmpty(ConnectionString))
4✔
67
            {
68
                return ParsedConnection!.DataSource;
2✔
69
            }
70

71
            throw new InvalidOperationException("Connection string must be specified.");
2✔
72
        }
73
    }
74

75
    /// <summary>
76
    /// Returns the native connection object that can be used to call DuckDB C API functions.
77
    /// </summary>
78
    public DuckDBNativeConnection NativeConnection => connectionReference?.NativeConnection
105,352!
79
                                                      ?? throw new InvalidOperationException("The DuckDBConnection must be open to access the native connection.");
105,352✔
80

81
    public override string ServerVersion => NativeMethods.Startup.DuckDBLibraryVersion().ToManagedString(false);
×
82

83
    public override ConnectionState State => connectionState;
172,050✔
84

85
    public override void ChangeDatabase(string databaseName)
86
    {
87
        throw new NotSupportedException();
×
88
    }
89

90
    public override void Close()
91
    {
92
        if (connectionState == ConnectionState.Closed)
43,294✔
93
        {
94
            throw new InvalidOperationException("Connection is already closed.");
4✔
95
        }
96

97
        if (connectionReference is not null) //Should always be the case
43,290✔
98
        {
99
            connectionManager.ReturnConnectionReference(connectionReference);
43,290✔
100
        }
101

102
        connectionState = ConnectionState.Closed;
43,290✔
103
        OnStateChange(FromOpenToClosedEventArgs);
43,290✔
104
    }
43,290✔
105

106
    public override void Open()
107
    {
108
        if (connectionState == ConnectionState.Open)
43,308✔
109
        {
110
            throw new InvalidOperationException("Connection is already open.");
2✔
111
        }
112

113
        //In case of inMemoryDuplication, we can safely take the hypothesis that connectionReference is already assigned
114
        connectionReference = inMemoryDuplication ? connectionManager.DuplicateConnectionReference(connectionReference!)
43,306✔
115
                                                  : connectionManager.GetConnectionReference(ParsedConnection);
43,306✔
116

117
        connectionState = ConnectionState.Open;
43,294✔
118
        OnStateChange(FromClosedToOpenEventArgs);
43,294✔
119
    }
43,294✔
120

121
    protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel)
122
    {
123
        return BeginTransaction(isolationLevel);
4✔
124
    }
125

126
    public new DuckDBTransaction BeginTransaction()
127
    {
128
        return BeginTransaction(IsolationLevel.Unspecified);
22✔
129
    }
130

131
    private new DuckDBTransaction BeginTransaction(IsolationLevel isolationLevel)
132
    {
133
        EnsureConnectionOpen();
26✔
134
        if (Transaction != null)
24✔
135
        {
136
            throw new InvalidOperationException("Already in a transaction.");
2✔
137
        }
138

139
        return Transaction = new DuckDBTransaction(this, isolationLevel);
22✔
140
    }
141

142
    protected override DbCommand CreateDbCommand()
143
    {
144
        return CreateCommand();
40,332✔
145
    }
146

147
    public new virtual DuckDBCommand CreateCommand()
148
    {
149
        return new DuckDBCommand
53,816✔
150
        {
53,816✔
151
            Connection = this,
53,816✔
152
            Transaction = Transaction
53,816✔
153
        };
53,816✔
154
    }
155

156
    public DuckDBAppender CreateAppender(string table) => CreateAppender(null, null, table);
114✔
157

158
    public DuckDBAppender CreateAppender(string? schema, string table) => CreateAppender(null, schema, table);
6✔
159

160
    public DuckDBAppender CreateAppender(string? catalog, string? schema, string table)
161
    {
162
        EnsureConnectionOpen();
128✔
163
        using var unmanagedCatalog = catalog.ToUnmanagedString();
128✔
164
        using var unmanagedSchema = schema.ToUnmanagedString();
128✔
165
        using var unmanagedTable = table.ToUnmanagedString();
128✔
166

167
        var appenderState = NativeMethods.Appender.DuckDBAppenderCreateExt(NativeConnection, unmanagedCatalog, unmanagedSchema, unmanagedTable, out var nativeAppender);
128✔
168

169
        if (!appenderState.IsSuccess())
128✔
170
        {
171
            try
172
            {
173
                DuckDBAppender.ThrowLastError(nativeAppender);
2✔
174
            }
175
            finally
176
            {
177
                nativeAppender.Close();
2✔
178
            }
2✔
179
        }
180

181
        return new DuckDBAppender(nativeAppender, GetTableName());
126✔
182

183
        string GetTableName()
184
        {
185
            return string.IsNullOrEmpty(schema) ? table : $"{schema}.{table}";
126✔
186
        }
187
    }
126✔
188

189
    /// <summary>
190
    /// Creates a type-safe appender using an AppenderMap for property-to-column mappings.
191
    /// </summary>
192
    /// <typeparam name="T">The type to append</typeparam>
193
    /// <typeparam name="TMap">The AppenderMap type defining the mappings</typeparam>
194
    /// <param name="table">The table name</param>
195
    /// <returns>A type-safe mapped appender</returns>
196
    public DuckDBMappedAppender<T, TMap> CreateAppender<T, TMap>(string table) 
197
        where TMap : Mapping.DuckDBAppenderMap<T>, new()
198
    {
199
        return CreateAppender<T, TMap>(null, null, table);
6✔
200
    }
201

202
    /// <summary>
203
    /// Creates a type-safe appender using an AppenderMap for property-to-column mappings.
204
    /// </summary>
205
    /// <typeparam name="T">The type to append</typeparam>
206
    /// <typeparam name="TMap">The AppenderMap type defining the mappings</typeparam>
207
    /// <param name="schema">The schema name</param>
208
    /// <param name="table">The table name</param>
209
    /// <returns>A type-safe mapped appender</returns>
210
    public DuckDBMappedAppender<T, TMap> CreateAppender<T, TMap>(string? schema, string table)
211
        where TMap : Mapping.DuckDBAppenderMap<T>, new()
212
    {
NEW
213
        return CreateAppender<T, TMap>(null, schema, table);
×
214
    }
215

216
    /// <summary>
217
    /// Creates a type-safe appender using an AppenderMap for property-to-column mappings.
218
    /// </summary>
219
    /// <typeparam name="T">The type to append</typeparam>
220
    /// <typeparam name="TMap">The AppenderMap type defining the mappings</typeparam>
221
    /// <param name="catalog">The catalog name</param>
222
    /// <param name="schema">The schema name</param>
223
    /// <param name="table">The table name</param>
224
    /// <returns>A type-safe mapped appender</returns>
225
    public DuckDBMappedAppender<T, TMap> CreateAppender<T, TMap>(string? catalog, string? schema, string table)
226
        where TMap : Mapping.DuckDBAppenderMap<T>, new()
227
    {
228
        var appender = CreateAppender(catalog, schema, table);
6✔
229
        return new DuckDBMappedAppender<T, TMap>(appender);
6✔
230
    }
231

232
    protected override void Dispose(bool disposing)
233
    {
234
        if (disposing)
43,272✔
235
        {
236
            // this check is to ensure exact same behavior as previous version
237
            // where Close() was calling Dispose(true) instead of the other way around.
238
            if (connectionState == ConnectionState.Open)
43,270✔
239
            {
240
                Close();
43,238✔
241
            }
242
        }
243

244
        base.Dispose(disposing);
43,272✔
245
    }
43,272✔
246

247
    private void EnsureConnectionOpen([CallerMemberName] string operation = "")
248
    {
249
        if (State != ConnectionState.Open)
162✔
250
        {
251
            throw new InvalidOperationException($"{operation} requires an open connection");
4✔
252
        }
253
    }
158✔
254

255
    public DuckDBConnection Duplicate()
256
    {
257
        EnsureConnectionOpen();
8✔
258

259
        // We're sure that the connectionString is not null because we previously checked the connection was open
260
        if (!ParsedConnection!.InMemory)
6✔
261
        {
262
            throw new NotSupportedException("Duplication of the connection is only supported for in-memory connections.");
2✔
263
        }
264

265
        var duplicatedConnection = new DuckDBConnection(ConnectionString)
4✔
266
        {
4✔
267
            parsedConnection = ParsedConnection,
4✔
268
            inMemoryDuplication = true,
4✔
269
            connectionReference = connectionReference,
4✔
270
        };
4✔
271

272
        return duplicatedConnection;
4✔
273
    }
274

275
    public override DataTable GetSchema() =>
276
        GetSchema(DbMetaDataCollectionNames.MetaDataCollections);
2✔
277

278
    public override DataTable GetSchema(string collectionName) =>
279
        GetSchema(collectionName, null);
30✔
280

281
    public override DataTable GetSchema(string collectionName, string?[]? restrictionValues) =>
282
        DuckDBSchema.GetSchema(this, collectionName, restrictionValues);
48✔
283

284
    public DuckDBQueryProgress GetQueryProgress()
285
    {
286
        EnsureConnectionOpen();
×
287
        return NativeMethods.Startup.DuckDBQueryProgress(NativeConnection);
×
288
    }
289
}
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