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

Giorgi / DuckDB.NET / 22921736195

10 Mar 2026 07:24PM UTC coverage: 89.526% (+0.08%) from 89.45%
22921736195

push

github

Giorgi
Update global.json

1255 of 1463 branches covered (85.78%)

Branch coverage included in aggregate %.

2651 of 2900 relevant lines covered (91.41%)

447643.55 hits per line

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

86.14
/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, ScalarFunctionOptions? options = null)
12
    {
13
        RegisterScalarMethod(name, (_, w, index) => action(w, index), TypeExtensions.GetLogicalType<TResult>(), varargs: false, options);
12✔
14
    }
6✔
15

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

21
    public void RegisterScalarFunction<T1, T2, TResult>(string name, Action<IReadOnlyList<IDuckDBDataReader>, IDuckDBDataWriter, ulong> action, ScalarFunctionOptions? options = null)
22
    {
23
        RegisterScalarMethod(name, action, TypeExtensions.GetLogicalType<TResult>(), varargs: false, options,
24✔
24
            TypeExtensions.GetLogicalType<T1>(), TypeExtensions.GetLogicalType<T2>());
24✔
25
    }
24✔
26

27
    public void RegisterScalarFunction<T1, T2, T3, TResult>(string name, Action<IReadOnlyList<IDuckDBDataReader>, IDuckDBDataWriter, ulong> action, ScalarFunctionOptions? options = null)
28
    {
29
        RegisterScalarMethod(name, action, TypeExtensions.GetLogicalType<TResult>(), varargs: false, options,
6✔
30
            TypeExtensions.GetLogicalType<T1>(), TypeExtensions.GetLogicalType<T2>(), TypeExtensions.GetLogicalType<T3>());
6✔
31
    }
6✔
32

33
    public void RegisterScalarFunction<T1, T2, T3, T4, TResult>(string name, Action<IReadOnlyList<IDuckDBDataReader>, IDuckDBDataWriter, ulong> action, ScalarFunctionOptions? options = null)
34
    {
35
        RegisterScalarMethod(name, action, TypeExtensions.GetLogicalType<TResult>(), varargs: false, options,
×
36
            TypeExtensions.GetLogicalType<T1>(), TypeExtensions.GetLogicalType<T2>(),
×
37
            TypeExtensions.GetLogicalType<T3>(), TypeExtensions.GetLogicalType<T4>());
×
38
    }
×
39

40
    private unsafe void RegisterScalarMethod(string name, Action<IReadOnlyList<IDuckDBDataReader>, IDuckDBDataWriter, ulong> action, DuckDBLogicalType returnType,
41
                                             bool varargs, ScalarFunctionOptions? options, params DuckDBLogicalType[] parameterTypes)
42
    {
43
        var function = NativeMethods.ScalarFunction.DuckDBCreateScalarFunction();
108✔
44
        NativeMethods.ScalarFunction.DuckDBScalarFunctionSetName(function, name);
108✔
45

46
        if (varargs)
108✔
47
        {
48
            if (parameterTypes.Length != 1)
27!
49
            {
50
                throw new InvalidOperationException("Cannot use params with multiple parameters");
×
51
            }
52

53
            NativeMethods.ScalarFunction.DuckDBScalarFunctionSetVarargs(function, parameterTypes[0]);
27✔
54
            parameterTypes[0].Dispose();
27✔
55
        }
56
        else
57
        {
58
            foreach (var type in parameterTypes)
384✔
59
            {
60
                NativeMethods.ScalarFunction.DuckDBScalarFunctionAddParameter(function, type);
111✔
61
                type.Dispose();
111✔
62
            }
63
        }
64

65
        // Functions with parameters default to pure; parameterless functions (e.g. random()) default to volatile
66
        var defaultPure = parameterTypes.Length > 0;
108✔
67
        if (!(options?.IsPureFunction ?? defaultPure))
108✔
68
        {
69
            NativeMethods.ScalarFunction.DuckDBScalarFunctionSetVolatile(function);
15✔
70
        }
71

72
        if (options?.HandlesNulls == true)
108✔
73
        {
74
            NativeMethods.ScalarFunction.DuckDBScalarFunctionSetSpecialHandling(function);
27✔
75
        }
76

77
        NativeMethods.ScalarFunction.DuckDBScalarFunctionSetReturnType(function, returnType);
108✔
78
        NativeMethods.ScalarFunction.DuckDBScalarFunctionSetBind(function, &ScalarFunctionBind);
108✔
79
        NativeMethods.ScalarFunction.DuckDBScalarFunctionSetFunction(function, &ScalarFunctionCallback);
108✔
80

81
        var info = new ScalarFunctionInfo(returnType, action);
108✔
82

83
        NativeMethods.ScalarFunction.DuckDBScalarFunctionSetExtraInfo(function, info.ToHandle(), &DestroyExtraInfo);
108✔
84

85
        var state = NativeMethods.ScalarFunction.DuckDBRegisterScalarFunction(NativeConnection, function);
108✔
86

87
        NativeMethods.ScalarFunction.DuckDBDestroyScalarFunction(ref function);
108✔
88

89
        if (!state.IsSuccess())
108!
90
        {
91
            throw new InvalidOperationException($"Error registering user defined scalar function: {name}");
×
92
        }
93
    }
108✔
94

95
    [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
96
    private static unsafe void ScalarFunctionBind(IntPtr info)
97
    {
98
        try
99
        {
100
            var connectionId = UdfExceptionStore.GetScalarFunctionBindConnectionId(info);
162✔
101
            NativeMethods.ScalarFunction.DuckDBScalarFunctionSetBindData(info, connectionId.ToHandle(), &DestroyExtraInfo);
162✔
102
        }
162✔
103
        catch
×
104
        {
105
            // If we can't get the connection ID, we still allow the function to proceed
106
        }
×
107
    }
162✔
108

109
    [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
110
    private static void ScalarFunctionCallback(IntPtr info, IntPtr chunk, IntPtr outputVector)
111
    {
112
        VectorDataReaderBase[] readers = [];
276✔
113
        VectorDataWriterBase? writer = null;
276✔
114

115
        try
116
        {
117
            var dataChunk = new DuckDBDataChunk(chunk);
276✔
118

119
            var chunkSize = NativeMethods.DataChunks.DuckDBDataChunkGetSize(dataChunk);
276✔
120
            var handle = GCHandle.FromIntPtr(NativeMethods.ScalarFunction.DuckDBScalarFunctionGetExtraInfo(info));
276✔
121

122
            if (handle.Target is not ScalarFunctionInfo functionInfo)
276!
123
            {
124
                throw new InvalidOperationException("User defined scalar function execution failed. Function extra info is null");
×
125
            }
126

127
            readers = new VectorDataReaderBase[NativeMethods.DataChunks.DuckDBDataChunkGetColumnCount(dataChunk)];
276✔
128

129
            for (var index = 0; index < readers.Length; index++)
1,362✔
130
            {
131
                var vector = NativeMethods.DataChunks.DuckDBDataChunkGetVector(dataChunk, index);
405✔
132
                using var logicalType = NativeMethods.Vectors.DuckDBVectorGetColumnType(vector);
405✔
133
                readers[index] = VectorDataReaderFactory.CreateReader(vector, logicalType);
405✔
134
            }
135

136
            writer = VectorDataWriterFactory.CreateWriter(outputVector, functionInfo.ReturnType);
276✔
137

138
            functionInfo.Action(readers, writer, chunkSize);
276✔
139
        }
240✔
140
        catch (Exception ex)
36✔
141
        {
142
            try
143
            {
144
                var bindDataHandle = GCHandle.FromIntPtr(NativeMethods.ScalarFunction.DuckDBScalarFunctionGetBindData(info));
36✔
145
                if (bindDataHandle.Target is ulong connectionId)
36✔
146
                {
147
                    UdfExceptionStore.Store(connectionId, ex);
36✔
148
                }
149
            }
36✔
150
            catch
×
151
            {
152
                // If we can't get the connection ID, we still report the error message
153
            }
×
154

155
            NativeMethods.ScalarFunction.DuckDBScalarFunctionSetError(info, ex.Message);
36✔
156
        }
36✔
157
        finally
158
        {
159
            foreach (var reader in readers)
1,362✔
160
            {
161
                reader.Dispose();
405✔
162
            }
163

164
            writer?.Dispose();
276✔
165
        }
276✔
166
    }
276✔
167

168
    [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
169
    private static void DestroyExtraInfo(IntPtr pointer) => pointer.FreeHandle();
474✔
170
}
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