Skip to content

Commit

Permalink
Merge branch 'bind-value' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
Giorgi committed Sep 10, 2024
2 parents 4fd76ae + c124690 commit 1ccf91b
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 110 deletions.
9 changes: 9 additions & 0 deletions DuckDB.NET.Bindings/DuckDBWrapperObjects.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,13 @@ protected override bool ReleaseHandle()
NativeMethods.DataChunks.DuckDBDestroyDataChunk(out handle);
return true;
}
}

public class DuckDBValue() : SafeHandleZeroOrMinusOneIsInvalid(true)
{
protected override bool ReleaseHandle()
{
NativeMethods.Value.DuckDBDestroyValue(out handle);
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ public static class PreparedStatements
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_nparams")]
public static extern long DuckDBParams(DuckDBPreparedStatement preparedStatement);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_value")]
public static extern DuckDBState DuckDBBindValue(DuckDBPreparedStatement preparedStatement, long index, DuckDBValue val);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_parameter_index")]
public static extern DuckDBState DuckDBBindParameterIndex(DuckDBPreparedStatement preparedStatement, out int index, string name);

Expand Down Expand Up @@ -66,6 +69,15 @@ public static class PreparedStatements
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_double")]
public static extern DuckDBState DuckDBBindDouble(DuckDBPreparedStatement preparedStatement, long index, double val);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_date")]
public static extern DuckDBState DuckDBBindDate(DuckDBPreparedStatement preparedStatement, long index, DuckDBDate val);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_time")]
public static extern DuckDBState DuckDBBindTime(DuckDBPreparedStatement preparedStatement, long index, DuckDBTime val);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_timestamp")]
public static extern DuckDBState DuckDBBindTimestamp(DuckDBPreparedStatement preparedStatement, long index, DuckDBTimestampStruct val);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_varchar")]
public static extern DuckDBState DuckDBBindVarchar(DuckDBPreparedStatement preparedStatement, long index, string val);

Expand All @@ -78,15 +90,6 @@ public static class PreparedStatements
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_null")]
public static extern DuckDBState DuckDBBindNull(DuckDBPreparedStatement preparedStatement, long index);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_date")]
public static extern DuckDBState DuckDBBindDate(DuckDBPreparedStatement preparedStatement, long index, DuckDBDate val);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_time")]
public static extern DuckDBState DuckDBBindTime(DuckDBPreparedStatement preparedStatement, long index, DuckDBTime val);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_timestamp")]
public static extern DuckDBState DuckDBBindTimestamp(DuckDBPreparedStatement preparedStatement, long index, DuckDBTimestampStruct val);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_execute_prepared")]
public static extern DuckDBState DuckDBExecutePrepared(DuckDBPreparedStatement preparedStatement, out DuckDBResult result);

Expand Down
73 changes: 73 additions & 0 deletions DuckDB.NET.Bindings/NativeMethods/NativeMethods.Value.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using System;
using System.Runtime.InteropServices;

namespace DuckDB.NET.Native;

public partial class NativeMethods
{
public static class Value
{
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_create_varchar")]
public static extern DuckDBValue DuckDBCreateVarchar(SafeUnmanagedMemoryHandle value);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_create_bool")]
public static extern DuckDBValue DuckDBCreateBool(bool value);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_create_int8")]
public static extern DuckDBValue DuckDBCreateInt8(sbyte value);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_create_uint8")]
public static extern DuckDBValue DuckDBCreateUInt8(byte value);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_create_int16")]
public static extern DuckDBValue DuckDBCreateInt16(short value);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_create_uint16")]
public static extern DuckDBValue DuckDBCreateUInt16(ushort value);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_create_int32")]
public static extern DuckDBValue DuckDBCreateInt32(int value);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_create_uint32")]
public static extern DuckDBValue DuckDBCreateUInt32(uint value);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_create_int64")]
public static extern DuckDBValue DuckDBCreateInt64(long value);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_create_uint64")]
public static extern DuckDBValue DuckDBCreateUInt64(ulong value);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_create_hugeint")]
public static extern DuckDBValue DuckDBCreateHugeInt(DuckDBHugeInt value);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_create_uhugeint")]
public static extern DuckDBValue DuckDBCreateUHugeInt(DuckDBUHugeInt value);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_create_float")]
public static extern DuckDBValue DuckDBCreateFloat(float value);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_create_double")]
public static extern DuckDBValue DuckDBCreateDouble(double value);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_create_date")]
public static extern DuckDBValue DuckDBCreateDate(DuckDBDate value);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_create_time")]
public static extern DuckDBValue DuckDBCreateTime(DuckDBTime value);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_create_time_tz")]
public static extern DuckDBValue DuckDBCreateTimeTz(DuckDBTimeTz value);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_create_timestamp")]
public static extern DuckDBValue DuckDBCreateTimestamp(DuckDBTimestampStruct value);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_create_interval")]
public static extern DuckDBValue DuckDBCreateInterval(DuckDBInterval value);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_create_blob")]
public static extern DuckDBValue DuckDBCreateBlob([In] byte[] value, long length);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_destroy_value")]
public static extern void DuckDBDestroyValue(out IntPtr config);
}
}
168 changes: 67 additions & 101 deletions DuckDB.NET.Data/Internal/PreparedStatement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,71 @@ namespace DuckDB.NET.Data.Internal;

internal sealed class PreparedStatement : IDisposable
{
private static readonly Dictionary<DbType, Func<DuckDBPreparedStatement, long, object, DuckDBState>> Binders = new()
private static readonly Dictionary<DbType, Func<object, DuckDBValue>> ValueCreators = new()
{
{ DbType.Guid, BindObject },
{ DbType.Currency, BindObject },
{ DbType.Boolean, BindBoolean },
{ DbType.SByte, BindInt8 },
{ DbType.Int16, BindInt16 },
{ DbType.Int32, BindInt32 },
{ DbType.Int64, BindInt64 },
{ DbType.Byte, BindUInt8 },
{ DbType.UInt16, BindUInt16 },
{ DbType.UInt32, BindUInt32 },
{ DbType.UInt64, BindUInt64 },
{ DbType.Single, BindFloat },
{ DbType.Double, BindDouble },
{ DbType.String, BindString },
{ DbType.VarNumeric, BindHugeInt },
{ DbType.Binary, BindBlob },
{ DbType.Date, BindDateOnly },
{ DbType.Time, BindTimeOnly },
{ DbType.DateTime, BindTimestamp }
{ DbType.Guid, value =>
{
using var handle = value.ToString().ToUnmanagedString();
return NativeMethods.Value.DuckDBCreateVarchar(handle);
}
},
{ DbType.Currency, value =>
{
using var handle = ((decimal)value).ToString(CultureInfo.InvariantCulture).ToUnmanagedString();
return NativeMethods.Value.DuckDBCreateVarchar(handle);
}
},
{ DbType.Boolean, value => NativeMethods.Value.DuckDBCreateBool((bool)value) },
{ DbType.SByte, value => NativeMethods.Value.DuckDBCreateInt8((sbyte)value) },
{ DbType.Int16, value => NativeMethods.Value.DuckDBCreateInt16((short)value) },
{ DbType.Int32, value => NativeMethods.Value.DuckDBCreateInt32((int)value) },
{ DbType.Int64, value => NativeMethods.Value.DuckDBCreateInt64((long)value) },
{ DbType.Byte, value => NativeMethods.Value.DuckDBCreateUInt8((byte)value) },
{ DbType.UInt16, value => NativeMethods.Value.DuckDBCreateUInt16((ushort)value) },
{ DbType.UInt32, value => NativeMethods.Value.DuckDBCreateUInt32((uint)value) },
{ DbType.UInt64, value => NativeMethods.Value.DuckDBCreateUInt64((ulong)value) },
{ DbType.Single, value => NativeMethods.Value.DuckDBCreateFloat((float)value) },
{ DbType.Double, value => NativeMethods.Value.DuckDBCreateDouble((double)value) },
{ DbType.String, value =>
{
using var handle = ((string)value).ToUnmanagedString();
return NativeMethods.Value.DuckDBCreateVarchar(handle);
}
},
{ DbType.VarNumeric, value => NativeMethods.Value.DuckDBCreateHugeInt(new((BigInteger)value)) },
{ DbType.Binary, value =>
{
var bytes = (byte[])value;
return NativeMethods.Value.DuckDBCreateBlob(bytes, bytes.Length);
}
},
{ DbType.Date, value =>
{
#if NET6_0_OR_GREATER
var date = NativeMethods.DateTimeHelpers.DuckDBToDate(value is DateOnly dateOnly ? (DuckDBDateOnly)dateOnly : (DuckDBDateOnly)value);
#else
var date = NativeMethods.DateTimeHelpers.DuckDBToDate((DuckDBDateOnly)value);
#endif
return NativeMethods.Value.DuckDBCreateDate(date);
}
},
{ DbType.Time, value =>
{
#if NET6_0_OR_GREATER
var time = NativeMethods.DateTimeHelpers.DuckDBToTime(value is TimeOnly timeOnly ? (DuckDBTimeOnly)timeOnly : (DuckDBTimeOnly)value);
#else
var time = NativeMethods.DateTimeHelpers.DuckDBToTime((DuckDBTimeOnly)value);
#endif
return NativeMethods.Value.DuckDBCreateTime(time);
}
},
{ DbType.DateTime, value =>
{
var timestamp = DuckDBTimestamp.FromDateTime((DateTime)value);
var timestampStruct = NativeMethods.DateTimeHelpers.DuckDBToTimestamp(timestamp);
return NativeMethods.Value.DuckDBCreateTimestamp(timestampStruct);
}
},
};

private readonly DuckDBPreparedStatement statement;
Expand Down Expand Up @@ -142,12 +186,13 @@ private static void BindParameter(DuckDBPreparedStatement preparedStatement, lon
return;
}

if (!Binders.TryGetValue(parameter.DbType, out var binder))
if (!ValueCreators.TryGetValue(parameter.DbType, out var func))
{
throw new InvalidOperationException($"Unable to bind value of type {parameter.DbType}.");
}

var result = binder(preparedStatement, index, parameter.Value!);
using var duckDBValue = func(parameter.Value!);
var result = NativeMethods.PreparedStatements.DuckDBBindValue(preparedStatement, index, duckDBValue);

if (!result.IsSuccess())
{
Expand All @@ -156,85 +201,6 @@ private static void BindParameter(DuckDBPreparedStatement preparedStatement, lon
}
}

private static DuckDBState BindObject(DuckDBPreparedStatement preparedStatement, long index, object value)
=> BindString(preparedStatement, index, Convert.ToString(value, CultureInfo.InvariantCulture)!);

private static DuckDBState BindBoolean(DuckDBPreparedStatement preparedStatement, long index, object value)
=> NativeMethods.PreparedStatements.DuckDBBindBoolean(preparedStatement, index, (bool)value);

private static DuckDBState BindInt8(DuckDBPreparedStatement preparedStatement, long index, object value)
=> NativeMethods.PreparedStatements.DuckDBBindInt8(preparedStatement, index, (sbyte)value);

private static DuckDBState BindInt16(DuckDBPreparedStatement preparedStatement, long index, object value)
=> NativeMethods.PreparedStatements.DuckDBBindInt16(preparedStatement, index, (short)value);

private static DuckDBState BindInt32(DuckDBPreparedStatement preparedStatement, long index, object value)
=> NativeMethods.PreparedStatements.DuckDBBindInt32(preparedStatement, index, (int)value);

private static DuckDBState BindInt64(DuckDBPreparedStatement preparedStatement, long index, object value)
=> NativeMethods.PreparedStatements.DuckDBBindInt64(preparedStatement, index, (long)value);

private static DuckDBState BindUInt8(DuckDBPreparedStatement preparedStatement, long index, object value)
=> NativeMethods.PreparedStatements.DuckDBBindUInt8(preparedStatement, index, (byte)value);

private static DuckDBState BindUInt16(DuckDBPreparedStatement preparedStatement, long index, object value)
=> NativeMethods.PreparedStatements.DuckDBBindUInt16(preparedStatement, index, (ushort)value);

private static DuckDBState BindUInt32(DuckDBPreparedStatement preparedStatement, long index, object value)
=> NativeMethods.PreparedStatements.DuckDBBindUInt32(preparedStatement, index, (uint)value);

private static DuckDBState BindUInt64(DuckDBPreparedStatement preparedStatement, long index, object value)
=> NativeMethods.PreparedStatements.DuckDBBindUInt64(preparedStatement, index, (ulong)value);


private static DuckDBState BindFloat(DuckDBPreparedStatement preparedStatement, long index, object value)
=> NativeMethods.PreparedStatements.DuckDBBindFloat(preparedStatement, index, (float)value);

private static DuckDBState BindDouble(DuckDBPreparedStatement preparedStatement, long index, object value)
=> NativeMethods.PreparedStatements.DuckDBBindDouble(preparedStatement, index, (double)value);

private static DuckDBState BindString(DuckDBPreparedStatement preparedStatement, long index, object value)
{
using var unmanagedString = (value as string)?.ToUnmanagedString() ?? throw new ArgumentException(nameof(value));
return NativeMethods.PreparedStatements.DuckDBBindVarchar(preparedStatement, index, unmanagedString);
}

private static DuckDBState BindHugeInt(DuckDBPreparedStatement preparedStatement, long index, object value) =>
NativeMethods.PreparedStatements.DuckDBBindHugeInt(preparedStatement, index, new((BigInteger)value));

private static DuckDBState BindBlob(DuckDBPreparedStatement preparedStatement, long index, object value)
{
var bytes = (byte[])value;
return NativeMethods.PreparedStatements.DuckDBBindBlob(preparedStatement, index, bytes, bytes.LongLength);
}

private static DuckDBState BindDateOnly(DuckDBPreparedStatement preparedStatement, long index, object value)
{
#if NET6_0_OR_GREATER
var date = NativeMethods.DateTimeHelpers.DuckDBToDate(value is DateOnly dateOnly ? (DuckDBDateOnly)dateOnly : (DuckDBDateOnly)value);
#else
var date = NativeMethods.DateTimeHelpers.DuckDBToDate((DuckDBDateOnly)value);
#endif
return NativeMethods.PreparedStatements.DuckDBBindDate(preparedStatement, index, date);
}

private static DuckDBState BindTimeOnly(DuckDBPreparedStatement preparedStatement, long index, object value)
{
#if NET6_0_OR_GREATER
var time = NativeMethods.DateTimeHelpers.DuckDBToTime(value is TimeOnly dateOnly ? (DuckDBTimeOnly)dateOnly : (DuckDBTimeOnly)value);
#else
var time = NativeMethods.DateTimeHelpers.DuckDBToTime((DuckDBTimeOnly)value);
#endif
return NativeMethods.PreparedStatements.DuckDBBindTime(preparedStatement, index, time);
}

private static DuckDBState BindTimestamp(DuckDBPreparedStatement preparedStatement, long index, object value)
{
var timestamp = DuckDBTimestamp.FromDateTime((DateTime)value);
var timestampStruct = NativeMethods.DateTimeHelpers.DuckDBToTimestamp(timestamp);
return NativeMethods.PreparedStatements.DuckDBBindTimestamp(preparedStatement, index, timestampStruct);
}

public void Dispose()
{
statement.Dispose();
Expand Down

0 comments on commit 1ccf91b

Please sign in to comment.