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

Giorgi / DuckDB.NET / 13493114806

24 Feb 2025 07:45AM UTC coverage: 89.838% (-0.05%) from 89.888%
13493114806

push

github

Giorgi
Expose query progress on DuckDBConnection

1094 of 1255 branches covered (87.17%)

Branch coverage included in aggregate %.

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

2115 of 2317 relevant lines covered (91.28%)

757462.42 hits per line

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

94.12
/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,884✔
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,592✔
30

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

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

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

43
#if NET6_0_OR_GREATER
44
    [AllowNull]
45
#endif
46
    [DefaultValue("")]
47
    public override string ConnectionString { get; set; }
190,092✔
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
157,743!
79
                                                      ?? throw new InvalidOperationException("The DuckDBConnection must be open to access the native connection.");
157,743✔
80

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

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

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

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

106
    public override void Open()
107
    {
108
        if (connectionState == ConnectionState.Open)
64,947✔
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,944✔
115
                                                  : connectionManager.GetConnectionReference(ParsedConnection);
64,944✔
116

117
        connectionState = ConnectionState.Open;
64,926✔
118
        OnStateChange(FromClosedToOpenEventArgs);
64,926✔
119
    }
64,926✔
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);
24✔
129
    }
130

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

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

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

147
    public new virtual DuckDBCommand CreateCommand()
148
    {
149
        return new DuckDBCommand
80,523✔
150
        {
80,523✔
151
            Connection = this,
80,523✔
152
            Transaction = Transaction
80,523✔
153
        };
80,523✔
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();
183✔
163
        using var unmanagedCatalog = catalog.ToUnmanagedString();
183✔
164
        using var unmanagedSchema = schema.ToUnmanagedString();
183✔
165
        using var unmanagedTable = table.ToUnmanagedString();
183✔
166

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

169
        if (!appenderState.IsSuccess())
183✔
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());
180✔
182

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

189
    protected override void Dispose(bool disposing)
190
    {
191
        if (disposing)
64,896✔
192
        {
193
            // this check is to ensure exact same behavior as previous version
194
            // where Close() was calling Dispose(true) instead of the other way around.
195
            if (connectionState == ConnectionState.Open)
64,890✔
196
            {
197
                Close();
64,842✔
198
            }
199
        }
200

201
        base.Dispose(disposing);
64,896✔
202
    }
64,896✔
203

204
    private void EnsureConnectionOpen([CallerMemberName] string operation = "")
205
    {
206
        if (State != ConnectionState.Open)
222✔
207
        {
208
            throw new InvalidOperationException($"{operation} requires an open connection");
6✔
209
        }
210
    }
216✔
211

212
    public DuckDBConnection Duplicate()
213
    {
214
        EnsureConnectionOpen();
9✔
215

216
        // We're sure that the connectionString is not null because we previously checked the connection was open
217
        if (!ParsedConnection!.InMemory)
6✔
218
        {
219
            throw new NotSupportedException("Duplication of the connection is only supported for in-memory connections.");
3✔
220
        }
221

222
        var duplicatedConnection = new DuckDBConnection(ConnectionString)
3✔
223
        {
3✔
224
            parsedConnection = ParsedConnection,
3✔
225
            inMemoryDuplication = true,
3✔
226
            connectionReference = connectionReference,
3✔
227
        };
3✔
228

229
        return duplicatedConnection;
3✔
230
    }
231

232
    public override DataTable GetSchema() =>
233
        GetSchema(DbMetaDataCollectionNames.MetaDataCollections);
3✔
234

235
    public override DataTable GetSchema(string collectionName) =>
236
        GetSchema(collectionName, null);
45✔
237

238
    public override DataTable GetSchema(string collectionName, string?[]? restrictionValues) =>
239
        DuckDBSchema.GetSchema(this, collectionName, restrictionValues);
72✔
240

241
    public DuckDBQueryProgress GetQueryProgress()
242
    {
NEW
243
        EnsureConnectionOpen();
×
NEW
244
        return NativeMethods.Startup.DuckDBQueryProgress(NativeConnection);
×
245
    }
246
}
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