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

Giorgi / DuckDB.NET / 21645923868

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

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%)

566599.28 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;
64,899✔
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);
3✔
21
    private static readonly StateChangeEventArgs FromOpenToClosedEventArgs = new(ConnectionState.Open, ConnectionState.Closed);
3✔
22

23
    #region Protected Properties
24

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

27
    #endregion
28

29
    internal DuckDBTransaction? Transaction { get; set; }
80,816✔
30

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

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

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

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

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

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

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

71
            throw new InvalidOperationException("Connection string must be specified.");
3✔
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
158,023!
79
                                                      ?? throw new InvalidOperationException("The DuckDBConnection must be open to access the native connection.");
158,023✔
80

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

83
    public override ConnectionState State => connectionState;
258,068✔
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)
64,941✔
93
        {
94
            throw new InvalidOperationException("Connection is already closed.");
6✔
95
        }
96

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

183
        string GetTableName()
184
        {
185
            return string.IsNullOrEmpty(schema) ? table : $"{schema}.{table}";
189✔
186
        }
187
    }
189✔
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);
9✔
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);
9✔
229
        return new DuckDBMappedAppender<T, TMap>(appender);
9✔
230
    }
231

232
    protected override void Dispose(bool disposing)
233
    {
234
        if (disposing)
64,909✔
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)
64,905✔
239
            {
240
                Close();
64,857✔
241
            }
242
        }
243

244
        base.Dispose(disposing);
64,909✔
245
    }
64,909✔
246

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

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

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

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

272
        return duplicatedConnection;
6✔
273
    }
274

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

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

281
    public override DataTable GetSchema(string collectionName, string?[]? restrictionValues) =>
282
        DuckDBSchema.GetSchema(this, collectionName, restrictionValues);
72✔
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