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

Giorgi / DuckDB.NET / 22038483173

15 Feb 2026 03:45PM UTC coverage: 89.308% (+0.04%) from 89.265%
22038483173

push

github

Giorgi
Report error when scalar function throws exception, minor fixes

1199 of 1391 branches covered (86.2%)

Branch coverage included in aggregate %.

21 of 22 new or added lines in 1 file covered. (95.45%)

2326 of 2556 relevant lines covered (91.0%)

554752.46 hits per line

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

79.27
/DuckDB.NET.Data/DuckDBConnection.ScalarFunction.cs
1
using DuckDB.NET.Data.Connection;
2
using DuckDB.NET.Data.DataChunk.Reader;
3
using DuckDB.NET.Data.DataChunk.Writer;
4
using System.Runtime.CompilerServices;
5
using System.Runtime.InteropServices;
6

7
namespace DuckDB.NET.Data;
8

9
partial class DuckDBConnection
10
{
11
    public void RegisterScalarFunction<TResult>(string name, Action<IDuckDBDataWriter, ulong> action, bool isPureFunction = false)
12
    {
13
        RegisterScalarMethod(name, (_, w, index) => action(w, index), TypeExtensions.GetLogicalType<TResult>(), varargs: false, !isPureFunction);
6✔
14
    }
3✔
15

16
    public void RegisterScalarFunction<T, TResult>(string name, Action<IReadOnlyList<IDuckDBDataReader>, IDuckDBDataWriter, ulong> action, bool isPureFunction = true, bool @params = false)
17
    {
18
        RegisterScalarMethod(name, action, TypeExtensions.GetLogicalType<TResult>(), @params, !isPureFunction, TypeExtensions.GetLogicalType<T>());
12✔
19
    }
12✔
20

21
    public void RegisterScalarFunction<T1, T2, TResult>(string name, Action<IReadOnlyList<IDuckDBDataReader>, IDuckDBDataWriter, ulong> action, bool isPureFunction = true)
22
    {
23
        RegisterScalarMethod(name, action, TypeExtensions.GetLogicalType<TResult>(), varargs: false, !isPureFunction,
6✔
24
                              TypeExtensions.GetLogicalType<T1>(), 
6✔
25
                              TypeExtensions.GetLogicalType<T2>());
6✔
26
    }
6✔
27

28
    public void RegisterScalarFunction<T1, T2, T3, TResult>(string name, Action<IReadOnlyList<IDuckDBDataReader>, IDuckDBDataWriter, ulong> action, bool isPureFunction = true)
29
    {
30
        RegisterScalarMethod(name, action, TypeExtensions.GetLogicalType<TResult>(), varargs: false, !isPureFunction,
×
31
                              TypeExtensions.GetLogicalType<T1>(),
×
32
                              TypeExtensions.GetLogicalType<T2>(),
×
33
                              TypeExtensions.GetLogicalType<T3>());
×
34
    }
×
35

36
    public void RegisterScalarFunction<T1, T2, T3, T4, TResult>(string name, Action<IReadOnlyList<IDuckDBDataReader>, IDuckDBDataWriter, ulong> action, bool isPureFunction = true)
37
    {
38
        RegisterScalarMethod(name, action, TypeExtensions.GetLogicalType<TResult>(), varargs: false, !isPureFunction,
×
39
                              TypeExtensions.GetLogicalType<T1>(),
×
40
                              TypeExtensions.GetLogicalType<T2>(),
×
41
                              TypeExtensions.GetLogicalType<T3>(),
×
42
                              TypeExtensions.GetLogicalType<T4>());
×
43
    }
×
44

45
    private unsafe void RegisterScalarMethod(string name, Action<IReadOnlyList<IDuckDBDataReader>, IDuckDBDataWriter, ulong> action, DuckDBLogicalType returnType,
46
                                             bool varargs, bool @volatile, params DuckDBLogicalType[] parameterTypes)
47
    {
48
        var function = NativeMethods.ScalarFunction.DuckDBCreateScalarFunction();
21✔
49
        NativeMethods.ScalarFunction.DuckDBScalarFunctionSetName(function, name);
21✔
50

51
        if (varargs)
21✔
52
        {
53
            if (parameterTypes.Length != 1)
3!
54
            {
55
                throw new InvalidOperationException("Cannot use params with multiple parameters");
×
56
            }
57

58
            NativeMethods.ScalarFunction.DuckDBScalarFunctionSetVarargs(function, parameterTypes[0]);
3✔
59
            parameterTypes[0].Dispose();
3✔
60
        }
61
        else
62
        {
63
            foreach (var type in parameterTypes)
78✔
64
            {
65
                NativeMethods.ScalarFunction.DuckDBScalarFunctionAddParameter(function, type);
21✔
66
                type.Dispose();
21✔
67
            }
68
        }
69

70
        if (@volatile)
21✔
71
        {
72
            NativeMethods.ScalarFunction.DuckDBScalarFunctionSetVolatile(function);
9✔
73
        }
74

75
        NativeMethods.ScalarFunction.DuckDBScalarFunctionSetReturnType(function, returnType);
21✔
76
        NativeMethods.ScalarFunction.DuckDBScalarFunctionSetFunction(function, &ScalarFunctionCallback);
21✔
77

78
        var info = new ScalarFunctionInfo(returnType, action);
21✔
79

80
        NativeMethods.ScalarFunction.DuckDBScalarFunctionSetExtraInfo(function, info.ToHandle(), &DestroyExtraInfo);
21✔
81

82
        var state = NativeMethods.ScalarFunction.DuckDBRegisterScalarFunction(NativeConnection, function);
21✔
83

84
        NativeMethods.ScalarFunction.DuckDBDestroyScalarFunction(ref function);
21✔
85

86
        if (!state.IsSuccess())
21!
87
        {
88
            throw new InvalidOperationException($"Error registering user defined scalar function: {name}");
×
89
        }
90
    }
21✔
91

92
    [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
93
    private static void ScalarFunctionCallback(IntPtr info, IntPtr chunk, IntPtr outputVector)
94
    {
95
        VectorDataReaderBase[] readers = [];
87✔
96
        VectorDataWriterBase? writer = null;
87✔
97

98
        try
99
        {
100
            var dataChunk = new DuckDBDataChunk(chunk);
87✔
101

102
            var chunkSize = NativeMethods.DataChunks.DuckDBDataChunkGetSize(dataChunk);
87✔
103
            var handle = GCHandle.FromIntPtr(NativeMethods.ScalarFunction.DuckDBScalarFunctionGetExtraInfo(info));
87✔
104

105
            if (handle.Target is not ScalarFunctionInfo functionInfo)
87!
106
            {
NEW
107
                throw new InvalidOperationException("User defined scalar function execution failed. Function extra info is null");
×
108
            }
109

110
            readers = new VectorDataReaderBase[NativeMethods.DataChunks.DuckDBDataChunkGetColumnCount(dataChunk)];
87✔
111

112
            for (var index = 0; index < readers.Length; index++)
390✔
113
            {
114
                var vector = NativeMethods.DataChunks.DuckDBDataChunkGetVector(dataChunk, index);
108✔
115
                using var logicalType = NativeMethods.Vectors.DuckDBVectorGetColumnType(vector);
108✔
116
                readers[index] = VectorDataReaderFactory.CreateReader(vector, logicalType);
108✔
117
            }
118

119
            writer = VectorDataWriterFactory.CreateWriter(outputVector, functionInfo.ReturnType);
87✔
120

121
            functionInfo.Action(readers, writer, chunkSize);
87✔
122
        }
78✔
123
        catch (Exception ex)
9✔
124
        {
125
            NativeMethods.ScalarFunction.DuckDBScalarFunctionSetError(info, ex.Message);
9✔
126
        }
9✔
127
        finally
128
        {
129
            foreach (var reader in readers)
390✔
130
            {
131
                reader.Dispose();
108✔
132
            }
133

134
            writer?.Dispose();
87✔
135
        }
87✔
136
    }
87✔
137

138
    [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
139
    private static void DestroyExtraInfo(IntPtr pointer) => pointer.FreeHandle();
102✔
140
}
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