Skip to content

Commit 39e2797

Browse files
committed
Merge branch 'Table-Valued-Functions' into develop
2 parents e4f65ec + 9d86c6f commit 39e2797

13 files changed

+557
-23
lines changed

DuckDB.NET.Bindings/Bindings.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,8 @@ Updated to DuckDB v1.1.2
4747
<ItemGroup>
4848
<PackageReference Include="GitVersion.MsBuild" Version="5.11.1" PrivateAssets="all" />
4949
</ItemGroup>
50+
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
51+
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
52+
</ItemGroup>
5053
<!-- End native lib section -->
5154
</Project>

DuckDB.NET.Bindings/DuckDBWrapperObjects.cs

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
using System;
2-
using Microsoft.Win32.SafeHandles;
1+
using Microsoft.Win32.SafeHandles;
2+
using System;
3+
using System.Runtime.CompilerServices;
34

45
namespace DuckDB.NET.Native;
56

@@ -89,7 +90,7 @@ protected override bool ReleaseHandle()
8990
}
9091
}
9192

92-
public class DuckDBValue() : SafeHandleZeroOrMinusOneIsInvalid(true)
93+
public class DuckDBValue() : SafeHandleZeroOrMinusOneIsInvalid(true), IDuckDBValueReader
9394
{
9495
private DuckDBValue[] childValues = [];
9596

@@ -105,7 +106,61 @@ protected override bool ReleaseHandle()
105106
}
106107

107108
internal void SetChildValues(DuckDBValue[] values)
108-
{
109+
{
109110
childValues = values;
110111
}
111-
}
112+
113+
public T GetValue<T>()
114+
{
115+
var logicalType = NativeMethods.Value.DuckDBGetValueType(this);
116+
117+
//Logical type is part of the duckdb_value object and it shouldn't be released separately
118+
//It will get released when the duckdb_value object is destroyed below.
119+
var add = false;
120+
logicalType.DangerousAddRef(ref add);
121+
122+
var duckDBType = NativeMethods.LogicalType.DuckDBGetTypeId(logicalType);
123+
124+
return duckDBType switch
125+
{
126+
DuckDBType.Boolean => Cast(NativeMethods.Value.DuckDBGetBool(this)),
127+
128+
DuckDBType.TinyInt => Cast(NativeMethods.Value.DuckDBGetInt8(this)),
129+
DuckDBType.SmallInt => Cast(NativeMethods.Value.DuckDBGetInt16(this)),
130+
DuckDBType.Integer => Cast(NativeMethods.Value.DuckDBGetInt32(this)),
131+
DuckDBType.BigInt => Cast(NativeMethods.Value.DuckDBGetInt64(this)),
132+
133+
DuckDBType.UnsignedTinyInt => Cast(NativeMethods.Value.DuckDBGetUInt8(this)),
134+
DuckDBType.UnsignedSmallInt => Cast(NativeMethods.Value.DuckDBGetUInt16(this)),
135+
DuckDBType.UnsignedInteger => Cast(NativeMethods.Value.DuckDBGetUInt32(this)),
136+
DuckDBType.UnsignedBigInt => Cast(NativeMethods.Value.DuckDBGetUInt64(this)),
137+
138+
DuckDBType.Float => Cast(NativeMethods.Value.DuckDBGetFloat(this)),
139+
DuckDBType.Double => Cast(NativeMethods.Value.DuckDBGetDouble(this)),
140+
141+
DuckDBType.Decimal => Cast(decimal.Parse(NativeMethods.Value.DuckDBGetVarchar(this))),
142+
DuckDBType.Uuid => Cast(new Guid(NativeMethods.Value.DuckDBGetVarchar(this))),
143+
144+
//DuckDBType.HugeInt => expr,
145+
//DuckDBType.UnsignedHugeInt => expr,
146+
147+
DuckDBType.Varchar => Cast(NativeMethods.Value.DuckDBGetVarchar(this)),
148+
149+
//DuckDBType.Date => expr,
150+
//DuckDBType.Time => expr,
151+
//DuckDBType.TimeTz => expr,
152+
//DuckDBType.Interval => expr,
153+
DuckDBType.Timestamp => Cast(NativeMethods.DateTimeHelpers.DuckDBFromTimestamp(NativeMethods.Value.DuckDBGetTimestamp(this)).ToDateTime()),
154+
//DuckDBType.TimestampS => expr,
155+
//DuckDBType.TimestampMs => expr,
156+
//DuckDBType.TimestampNs => expr,
157+
//DuckDBType.TimestampTz => expr,
158+
_ => throw new NotImplementedException($"Cannot read value of type {typeof(T).FullName}")
159+
};
160+
161+
T Cast<TSource>(TSource value)
162+
{
163+
return Unsafe.As<TSource, T>(ref value);
164+
}
165+
}
166+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace DuckDB.NET.Native;
2+
3+
public interface IDuckDBValueReader
4+
{
5+
T GetValue<T>();
6+
}

DuckDB.NET.Bindings/NativeMethods/NativeMethods.LogicalType.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ public static class LogicalType
1111
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_create_logical_type")]
1212
public static extern DuckDBLogicalType DuckDBCreateLogicalType(DuckDBType type);
1313

14+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_create_decimal_type")]
15+
public static extern DuckDBLogicalType DuckDBCreateDecimalType(byte width, byte scale);
16+
1417
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_get_type_id")]
1518
public static extern DuckDBType DuckDBGetTypeId(DuckDBLogicalType type);
1619

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
using System.Runtime.InteropServices;
2+
using System;
3+
4+
namespace DuckDB.NET.Native;
5+
6+
public partial class NativeMethods
7+
{
8+
public static class TableFunction
9+
{
10+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_create_table_function")]
11+
public static extern IntPtr DuckDBCreateTableFunction();
12+
13+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_destroy_table_function")]
14+
public static extern void DuckDBDestroyTableFunction(out IntPtr tableFunction);
15+
16+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_table_function_set_name")]
17+
public static extern void DuckDBTableFunctionSetName(IntPtr tableFunction, SafeUnmanagedMemoryHandle name);
18+
19+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_table_function_add_parameter")]
20+
public static extern void DuckDBTableFunctionAddParameter(IntPtr tableFunction, DuckDBLogicalType type);
21+
22+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_table_function_set_extra_info")]
23+
public static extern unsafe void DuckDBTableFunctionSetExtraInfo(IntPtr tableFunction, IntPtr extraInfo, delegate* unmanaged[Cdecl]<IntPtr, void> destroy);
24+
25+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_table_function_set_bind")]
26+
public static extern unsafe void DuckDBTableFunctionSetBind(IntPtr tableFunction, delegate* unmanaged[Cdecl]<IntPtr, void> bind);
27+
28+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_table_function_set_init")]
29+
public static extern unsafe void DuckDBTableFunctionSetInit(IntPtr tableFunction, delegate* unmanaged[Cdecl]<IntPtr, void> init);
30+
31+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_table_function_set_function")]
32+
public static extern unsafe void DuckDBTableFunctionSetFunction(IntPtr tableFunction, delegate* unmanaged[Cdecl]<IntPtr, IntPtr, void> callback);
33+
34+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_register_table_function")]
35+
public static extern DuckDBState DuckDBRegisterTableFunction(DuckDBNativeConnection con, IntPtr tableFunction);
36+
37+
#region TableFunctionBind
38+
39+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_get_extra_info")]
40+
public static extern unsafe IntPtr DuckDBBindGetExtraInfo(IntPtr info);
41+
42+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_add_result_column")]
43+
public static extern unsafe void DuckDBBindAddResultColumn(IntPtr info, SafeUnmanagedMemoryHandle name, DuckDBLogicalType type);
44+
45+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_get_parameter_count")]
46+
public static extern unsafe ulong DuckDBBindGetParameterCount(IntPtr info);
47+
48+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_get_parameter")]
49+
public static extern unsafe DuckDBValue DuckDBBindGetParameter(IntPtr info, ulong index);
50+
51+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_set_bind_data")]
52+
public static extern unsafe void DuckDBBindSetBindData(IntPtr info, IntPtr bindData, delegate* unmanaged[Cdecl]<IntPtr, void> destroy);
53+
54+
#endregion
55+
56+
#region TableFunction
57+
58+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_function_get_extra_info")]
59+
public static extern unsafe IntPtr DuckDBFunctionGetExtraInfo(IntPtr info);
60+
61+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_function_get_bind_data")]
62+
public static extern unsafe IntPtr DuckDBFunctionGetBindData(IntPtr info);
63+
64+
#endregion
65+
}
66+
}

DuckDB.NET.Bindings/NativeMethods/NativeMethods.Value.cs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,66 @@ public static class Value
7070

7171
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_create_blob")]
7272
public static extern DuckDBValue DuckDBCreateBlob([In] byte[] value, long length);
73+
74+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_get_bool")]
75+
public static extern bool DuckDBGetBool(DuckDBValue value);
76+
77+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_get_int8")]
78+
public static extern sbyte DuckDBGetInt8(DuckDBValue value);
79+
80+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_get_uint8")]
81+
public static extern byte DuckDBGetUInt8(DuckDBValue value);
82+
83+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_get_int16")]
84+
public static extern short DuckDBGetInt16(DuckDBValue value);
85+
86+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_get_uint16")]
87+
public static extern ushort DuckDBGetUInt16(DuckDBValue value);
88+
89+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_get_int32")]
90+
public static extern int DuckDBGetInt32(DuckDBValue value);
91+
92+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_get_uint32")]
93+
public static extern uint DuckDBGetUInt32(DuckDBValue value);
94+
95+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_get_int64")]
96+
public static extern long DuckDBGetInt64(DuckDBValue value);
97+
98+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_get_uint64")]
99+
public static extern ulong DuckDBGetUInt64(DuckDBValue value);
100+
101+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_get_hugeint")]
102+
public static extern DuckDBHugeInt DuckDBGetHugeInt(DuckDBValue value);
103+
104+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_get_uhugeint")]
105+
public static extern DuckDBUHugeInt DuckDBGetUHugeInt(DuckDBValue value);
106+
107+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_get_float")]
108+
public static extern float DuckDBGetFloat(DuckDBValue value);
109+
110+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_get_double")]
111+
public static extern double DuckDBGetDouble(DuckDBValue value);
112+
113+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_get_date")]
114+
public static extern unsafe DuckDBDate DuckDBGetDate(DuckDBValue value);
115+
116+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_get_time")]
117+
public static extern unsafe DuckDBTime DuckDBGetTime(DuckDBValue value);
118+
119+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_get_time_tz")]
120+
public static extern unsafe DuckDBTimeTzStruct DuckDBGetTimeTz(DuckDBValue value);
121+
122+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_get_timestamp")]
123+
public static extern unsafe DuckDBTimestampStruct DuckDBGetTimestamp(DuckDBValue value);
124+
125+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_get_interval")]
126+
public static extern unsafe DuckDBInterval DuckDBGetInterval(DuckDBValue value);
127+
128+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_get_value_type")]
129+
public static extern unsafe DuckDBLogicalType DuckDBGetValueType(DuckDBValue value);
130+
131+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_get_varchar")]
132+
public static extern string DuckDBGetVarchar(DuckDBValue value);
73133

74134
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_create_list_value")]
75135
public static extern DuckDBValue DuckDBCreateListValue(DuckDBLogicalType logicalType, IntPtr[] values, long count);

DuckDB.NET.Data/DuckDBConnection.ScalarFunction.cs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using DuckDB.NET.Data.Writer;
77
using DuckDB.NET.Native;
88
using System;
9+
using System.Collections.Generic;
910
using System.Diagnostics.CodeAnalysis;
1011
using System.Runtime.CompilerServices;
1112
using System.Runtime.InteropServices;
@@ -16,27 +17,27 @@ partial class DuckDBConnection
1617
{
1718
#if NET8_0_OR_GREATER
1819
[Experimental("DuckDBNET001")]
19-
public void RegisterScalarFunction<TResult>(string name, Action<IDuckDBDataReader[], IDuckDBDataWriter, ulong> action, bool isPureFunction = false)
20+
public void RegisterScalarFunction<TResult>(string name, Action<IReadOnlyList<IDuckDBDataReader>, IDuckDBDataWriter, ulong> action, bool isPureFunction = false)
2021
{
2122
RegisterScalarMethod(name, action, DuckDBTypeMap.GetLogicalType<TResult>(), varargs: false, !isPureFunction);
2223
}
2324

2425
[Experimental("DuckDBNET001")]
25-
public void RegisterScalarFunction<T, TResult>(string name, Action<IDuckDBDataReader[], IDuckDBDataWriter, ulong> action, bool isPureFunction = true, bool @params = false)
26+
public void RegisterScalarFunction<T, TResult>(string name, Action<IReadOnlyList<IDuckDBDataReader>, IDuckDBDataWriter, ulong> action, bool isPureFunction = true, bool @params = false)
2627
{
2728
RegisterScalarMethod(name, action, DuckDBTypeMap.GetLogicalType<TResult>(), @params, !isPureFunction, DuckDBTypeMap.GetLogicalType<T>());
2829
}
2930

3031
[Experimental("DuckDBNET001")]
31-
public void RegisterScalarFunction<T1, T2, TResult>(string name, Action<IDuckDBDataReader[], IDuckDBDataWriter, ulong> action, bool isPureFunction = true)
32+
public void RegisterScalarFunction<T1, T2, TResult>(string name, Action<IReadOnlyList<IDuckDBDataReader>, IDuckDBDataWriter, ulong> action, bool isPureFunction = true)
3233
{
3334
RegisterScalarMethod(name, action, DuckDBTypeMap.GetLogicalType<TResult>(), varargs: false, !isPureFunction,
3435
DuckDBTypeMap.GetLogicalType<T1>(),
3536
DuckDBTypeMap.GetLogicalType<T2>());
3637
}
3738

3839
[Experimental("DuckDBNET001")]
39-
public void RegisterScalarFunction<T1, T2, T3, TResult>(string name, Action<IDuckDBDataReader[], IDuckDBDataWriter, ulong> action, bool isPureFunction = true)
40+
public void RegisterScalarFunction<T1, T2, T3, TResult>(string name, Action<IReadOnlyList<IDuckDBDataReader>, IDuckDBDataWriter, ulong> action, bool isPureFunction = true)
4041
{
4142
RegisterScalarMethod(name, action, DuckDBTypeMap.GetLogicalType<TResult>(), varargs: false, !isPureFunction,
4243
DuckDBTypeMap.GetLogicalType<T1>(),
@@ -45,7 +46,7 @@ public void RegisterScalarFunction<T1, T2, T3, TResult>(string name, Action<IDuc
4546
}
4647

4748
[Experimental("DuckDBNET001")]
48-
public void RegisterScalarFunction<T1, T2, T3, T4, TResult>(string name, Action<IDuckDBDataReader[], IDuckDBDataWriter, ulong> action, bool isPureFunction = true)
49+
public void RegisterScalarFunction<T1, T2, T3, T4, TResult>(string name, Action<IReadOnlyList<IDuckDBDataReader>, IDuckDBDataWriter, ulong> action, bool isPureFunction = true)
4950
{
5051
RegisterScalarMethod(name, action, DuckDBTypeMap.GetLogicalType<TResult>(), varargs: false, !isPureFunction,
5152
DuckDBTypeMap.GetLogicalType<T1>(),
@@ -55,11 +56,14 @@ public void RegisterScalarFunction<T1, T2, T3, T4, TResult>(string name, Action<
5556
}
5657

5758
[Experimental("DuckDBNET001")]
58-
private unsafe void RegisterScalarMethod(string name, Action<IDuckDBDataReader[], IDuckDBDataWriter, ulong> action, DuckDBLogicalType returnType,
59+
private unsafe void RegisterScalarMethod(string name, Action<IReadOnlyList<IDuckDBDataReader>, IDuckDBDataWriter, ulong> action, DuckDBLogicalType returnType,
5960
bool varargs, bool @volatile, params DuckDBLogicalType[] parameterTypes)
6061
{
6162
var function = NativeMethods.ScalarFunction.DuckDBCreateScalarFunction();
62-
NativeMethods.ScalarFunction.DuckDBScalarFunctionSetName(function, name.ToUnmanagedString());
63+
using (var handle = name.ToUnmanagedString())
64+
{
65+
NativeMethods.ScalarFunction.DuckDBScalarFunctionSetName(function, handle);
66+
}
6367

6468
if (varargs)
6569
{
@@ -97,7 +101,7 @@ private unsafe void RegisterScalarMethod(string name, Action<IDuckDBDataReader[]
97101

98102
if (!state.IsSuccess())
99103
{
100-
throw new InvalidOperationException("Error registering user defined scalar function");
104+
throw new InvalidOperationException($"Error registering user defined scalar function: {name}");
101105
}
102106
}
103107

0 commit comments

Comments
 (0)