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

Giorgi / DuckDB.NET / 8606966591

08 Apr 2024 09:38PM UTC coverage: 89.795% (-0.007%) from 89.802%
8606966591

push

github

Giorgi
Add support for appending blobs. Closes #181

589 of 686 branches covered (85.86%)

Branch coverage included in aggregate %.

9 of 10 new or added lines in 1 file covered. (90.0%)

1 existing line in 1 file now uncovered.

1382 of 1509 relevant lines covered (91.58%)

12616.4 hits per line

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

90.6
/DuckDB.NET.Data/DuckDBAppender.cs
1
using System;
2
using System.Diagnostics;
3
using System.Diagnostics.CodeAnalysis;
4
using System.Numerics;
5
using DuckDB.NET.Native;
6

7
namespace DuckDB.NET.Data;
8

9
public class DuckDBAppender : IDisposable
10
{
11
    private bool closed;
12
    private readonly Native.DuckDBAppender nativeAppender;
13

14
    internal DuckDBAppender(Native.DuckDBAppender appender)
33✔
15
    {
16
        nativeAppender = appender;
33✔
17
    }
33✔
18

19
    public DuckDBAppenderRow CreateRow()
20
    {
21
        // https://duckdb.org/docs/api/c/api#duckdb_appender_begin_row
22
        // Begin row is a no op. Do not need to call.
23
        // NativeMethods.Appender.DuckDBAppenderBeingRow(_connection.NativeConnection)
24

25
        if (closed)
30,114✔
26
        {
27
            throw new InvalidOperationException("Appender is already closed");
3✔
28
        }
29

30
        return new DuckDBAppenderRow(nativeAppender);
30,111✔
31
    }
32

33
    public void Close()
34
    {
35
        closed = true;
33✔
36

37
        try
38
        {
39
            var state = NativeMethods.Appender.DuckDBAppenderClose(nativeAppender);
33✔
40
            if (!state.IsSuccess())
33✔
41
            {
42
                ThrowLastError(nativeAppender);
3✔
43
            }
44
        }
30✔
45
        finally
46
        {
47
            nativeAppender.Close();
33✔
48
        }
33✔
49
    }
30✔
50

51
    public void Dispose()
52
    {
53
        if (!closed)
33✔
54
        {
55
            Close();
27✔
56
        }
57
    }
30✔
58

59
    [DoesNotReturn]
60
    [StackTraceHidden]
61
    internal static void ThrowLastError(Native.DuckDBAppender appender)
62
    {
63
        var errorMessage = NativeMethods.Appender.DuckDBAppenderError(appender).ToManagedString(false);
15✔
64

65
        throw new DuckDBException(errorMessage);
15✔
66
    }
67
}
68

69
public class DuckDBAppenderRow
70
{
71
    private readonly Native.DuckDBAppender appender;
72

73
    internal DuckDBAppenderRow(Native.DuckDBAppender appender)
30,111✔
74
    {
75
        this.appender = appender;
30,111✔
76
    }
30,111✔
77

78
    public void EndRow()
79
    {
80
        if (!NativeMethods.Appender.DuckDBAppenderEndRow(appender).IsSuccess())
30,105✔
81
        {
82
            DuckDBAppender.ThrowLastError(appender);
3✔
83
        }
84
    }
30,102✔
85

86
    public DuckDBAppenderRow AppendValue(bool? value) => Append(value);
69✔
87

88
    public DuckDBAppenderRow AppendValue(byte[]? value) => Append(value);
3✔
89

90
#if NET6_0_OR_GREATER
91
    public DuckDBAppenderRow AppendValue(Span<byte> value) => AppendSpan(value);
3✔
92
#endif
93

94
    public DuckDBAppenderRow AppendValue(string? value)
95
    {
96
        if (value == null)
30,096✔
97
        {
98
            return AppendNullValue();
3✔
99
        }
100

101
        using var unmanagedString = value.ToUnmanagedString();
30,093✔
102
        return Append(unmanagedString);
30,093✔
103
    }
30,090✔
104

105
    public DuckDBAppenderRow AppendNullValue() => Append<object>(null);
69✔
106

107
    public DuckDBAppenderRow AppendValue(BigInteger? value, bool unsigned = false)
108
    {
109
        if (value == null)
60!
110
        {
111
            return AppendNullValue();
×
112
        }
113

114
        if (unsigned)
60✔
115
        {
116
            NativeMethods.Appender.DuckDBAppendUHugeInt(appender, new DuckDBUHugeInt(value.Value));
30✔
117
        }
118
        else
119
        {
120
            NativeMethods.Appender.DuckDBAppendHugeInt(appender, new DuckDBHugeInt(value.Value));
30✔
121
        }
122

123
        return this;
60✔
124
    }
125

126
    #region Append Signed Int
127

128
    public DuckDBAppenderRow AppendValue(sbyte? value) => Append(value);
66✔
129

130
    public DuckDBAppenderRow AppendValue(short? value) => Append(value);
60✔
131

132
    public DuckDBAppenderRow AppendValue(int? value) => Append(value);
30,075✔
133

134
    public DuckDBAppenderRow AppendValue(long? value) => Append(value);
60✔
135

136
    #endregion
137

138
    #region Append Unsigned Int
139

140
    public DuckDBAppenderRow AppendValue(byte? value) => Append(value);
69✔
141

142
    public DuckDBAppenderRow AppendValue(ushort? value) => Append(value);
60✔
143

144
    public DuckDBAppenderRow AppendValue(uint? value) => Append(value);
60✔
145

146
    public DuckDBAppenderRow AppendValue(ulong? value) => Append(value);
60✔
147

148
    #endregion
149

150
    #region Append Float
151

152
    public DuckDBAppenderRow AppendValue(float? value) => Append(value);
60✔
153

154
    public DuckDBAppenderRow AppendValue(double? value) => Append(value);
60✔
155

156
    #endregion
157

158
    #region Append Temporal
159
#if NET6_0_OR_GREATER
160
    public DuckDBAppenderRow AppendValue(DateOnly? value) => Append(value);
×
161

162
    public DuckDBAppenderRow AppendValue(TimeOnly? value) => Append(value);
×
163
#else
164
    public DuckDBAppenderRow AppendValue(DuckDBDateOnly? value) => Append(value);
165

166
    public DuckDBAppenderRow AppendValue(DuckDBTimeOnly? value) => Append(value);
167
#endif
168

169
    public DuckDBAppenderRow AppendValue(DateTime? value) => Append(value);
30✔
170

171
    #endregion
172

173
    private DuckDBAppenderRow Append<T>(T? value)
174
    {
175
        var state = value switch
60,894!
176
        {
60,894✔
177
            null => NativeMethods.Appender.DuckDBAppendNull(appender),
69✔
178
            bool val => NativeMethods.Appender.DuckDBAppendBool(appender, val),
69✔
179
            SafeUnmanagedMemoryHandle val => NativeMethods.Appender.DuckDBAppendVarchar(appender, val),
30,093✔
180

60,894✔
181
            sbyte val => NativeMethods.Appender.DuckDBAppendInt8(appender, val),
66✔
182
            short val => NativeMethods.Appender.DuckDBAppendInt16(appender, val),
60✔
183
            int val => NativeMethods.Appender.DuckDBAppendInt32(appender, val),
30,075✔
184
            long val => NativeMethods.Appender.DuckDBAppendInt64(appender, val),
60✔
185

60,894✔
186
            byte val => NativeMethods.Appender.DuckDBAppendUInt8(appender, val),
69✔
187
            ushort val => NativeMethods.Appender.DuckDBAppendUInt16(appender, val),
60✔
188
            uint val => NativeMethods.Appender.DuckDBAppendUInt32(appender, val),
60✔
189
            ulong val => NativeMethods.Appender.DuckDBAppendUInt64(appender, val),
60✔
190

60,894✔
191
            float val => NativeMethods.Appender.DuckDBAppendFloat(appender, val),
60✔
192
            double val => NativeMethods.Appender.DuckDBAppendDouble(appender, val),
60✔
193

60,894✔
194
            DateTime val => NativeMethods.Appender.DuckDBAppendTimestamp(appender, NativeMethods.DateTime.DuckDBToTimestamp(DuckDBTimestamp.FromDateTime(val))),
30✔
195
#if NET6_0_OR_GREATER
60,894✔
196
            DateOnly val => NativeMethods.Appender.DuckDBAppendDate(appender, NativeMethods.DateTime.DuckDBToDate(val)),
×
197
            TimeOnly val => NativeMethods.Appender.DuckDBAppendTime(appender, NativeMethods.DateTime.DuckDBToTime(val)),
×
198
#else
60,894✔
199
            DuckDBDateOnly val => NativeMethods.Appender.DuckDBAppendDate(appender, NativeMethods.DateTime.DuckDBToDate(val)),
60,894✔
200
            DuckDBTimeOnly val => NativeMethods.Appender.DuckDBAppendTime(appender, NativeMethods.DateTime.DuckDBToTime(val)),
60,894✔
201
#endif
60,894✔
202
            byte[] val => AppendByteArray(val),
3✔
UNCOV
203
            _ => throw new InvalidOperationException($"Unsupported type {typeof(T).Name}")
×
204
        };
60,894✔
205

206
        if (!state.IsSuccess())
60,894✔
207
        {
208
            DuckDBAppender.ThrowLastError(appender);
6✔
209
        }
210

211
        return this;
60,888✔
212
    }
213

214
    private unsafe DuckDBState AppendByteArray(byte[] val)
215
    {
216
        fixed (byte* pSource = val)
3!
217
        {
218
            return NativeMethods.Appender.DuckDBAppendBlob(appender, pSource, val.Length);
3✔
219
        }
220
    }
221

222
#if NET6_0_OR_GREATER
223
    private unsafe DuckDBAppenderRow AppendSpan(Span<byte> val)
224
    {
225
        fixed (byte* pSource = val)
3✔
226
        {
227
            var state = NativeMethods.Appender.DuckDBAppendBlob(appender, pSource, val.Length);
3✔
228

229
            if (!state.IsSuccess())
3!
230
            {
NEW
231
                DuckDBAppender.ThrowLastError(appender);
×
232
            }
233
        }
234

235
        return this;
3✔
236
    } 
237
#endif
238
}
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