Skip to content

Commit d937f68

Browse files
committed
Bind decimals without table
1 parent a4ca0c6 commit d937f68

5 files changed

Lines changed: 59 additions & 26 deletions

File tree

DuckDB.NET.Bindings/DuckDBDecimal.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
namespace DuckDB.NET.Native;
44

55
[StructLayout(LayoutKind.Sequential)]
6-
public readonly struct DuckDBDecimal
6+
public readonly struct DuckDBDecimal(byte width, byte scale, DuckDBHugeInt value)
77
{
8-
public byte Width { get; }
9-
public byte Scale { get; }
8+
public byte Width { get; } = width;
9+
public byte Scale { get; } = scale;
1010

11-
public DuckDBHugeInt Value { get; }
11+
public DuckDBHugeInt Value { get; } = value;
1212
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ public static class Value
4747
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_create_uhugeint")]
4848
public static extern DuckDBValue DuckDBCreateUHugeInt(DuckDBUHugeInt value);
4949

50+
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_create_decimal")]
51+
public static extern DuckDBValue DuckDBCreateDecimal(DuckDBDecimal value);
52+
5053
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_create_float")]
5154
public static extern DuckDBValue DuckDBCreateFloat(float value);
5255

DuckDB.NET.Data/DataChunk/Writer/DecimalVectorDataWriter.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ internal override bool AppendDecimal(decimal value, ulong rowIndex)
1616
switch (decimalType)
1717
{
1818
case DuckDBType.SmallInt:
19-
AppendValueInternal<short>((short)decimal.Multiply(value, new decimal(power)), rowIndex);
19+
AppendValueInternal((short)decimal.Multiply(value, new decimal(power)), rowIndex);
2020
break;
2121
case DuckDBType.Integer:
22-
AppendValueInternal<int>((int)decimal.Multiply(value, new decimal(power)), rowIndex);
22+
AppendValueInternal((int)decimal.Multiply(value, new decimal(power)), rowIndex);
2323
break;
2424
case DuckDBType.BigInt:
25-
AppendValueInternal<long>((long)decimal.Multiply(value, new decimal(power)), rowIndex);
25+
AppendValueInternal((long)decimal.Multiply(value, new decimal(power)), rowIndex);
2626
break;
2727
case DuckDBType.HugeInt:
2828
var integralPart = decimal.Truncate(value);

DuckDB.NET.Data/PreparedStatement/ClrToDuckDBConverter.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,5 +159,22 @@ private static DuckDBValue StringToDuckDBValue(string? value)
159159
return NativeMethods.Value.DuckDBCreateVarchar(handle);
160160
}
161161

162-
private static DuckDBValue DecimalToDuckDBValue(decimal value) => StringToDuckDBValue(value.ToString(CultureInfo.InvariantCulture));
162+
private static DuckDBValue DecimalToDuckDBValue(decimal value)
163+
{
164+
var bits = decimal.GetBits(value);
165+
var scale = (byte)((bits[3] >> 16) & 0x7F);
166+
167+
var power = Math.Pow(10, scale);
168+
169+
var integralPart = decimal.Truncate(value);
170+
var fractionalPart = value - integralPart;
171+
172+
var result = BigInteger.Multiply(new BigInteger(integralPart), new BigInteger(power));
173+
174+
result += new BigInteger(decimal.Multiply(fractionalPart, (decimal)power));
175+
176+
int width = result.IsZero ? 1 : (int)Math.Floor(BigInteger.Log10(BigInteger.Abs(result))) + 1;
177+
178+
return NativeMethods.Value.DuckDBCreateDecimal(new DuckDBDecimal((byte)width, scale, new DuckDBHugeInt(result)));
179+
}
163180
}

DuckDB.NET.Test/Parameters/DecimalParameterTest.cs

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public void SimpleTest()
3131
}
3232

3333

34-
values = new[] { decimal.One / 3, decimal.MinusOne / 3 };
34+
values = [decimal.One / 3, decimal.MinusOne / 3];
3535

3636
foreach (var value in values)
3737
{
@@ -51,33 +51,28 @@ public void SimpleTest()
5151
[Fact]
5252
public void InsertSelectValueTest()
5353
{
54-
DecimalTests(new[]
55-
{
54+
DecimalTests([
5655
0m, decimal.Zero,
5756
decimal.One,
5857
decimal.One / 2, decimal.MinusOne,
5958
decimal.MinusOne / 2
60-
}, 38, 15);
59+
], 38, 15);
6160

62-
DecimalTests(new[]
63-
{
61+
DecimalTests([
6462
decimal.MinValue, decimal.MaxValue
65-
}, 38, 0);
63+
], 38, 0);
6664

67-
DecimalTests(new[]
68-
{
69-
decimal.One/3, decimal.MinusOne/3
70-
}, 38, 28);
65+
DecimalTests([
66+
decimal.One/3, decimal.MinusOne/3, -123456789.987654321m
67+
], 38, 28);
7168

72-
DecimalTests(new[]
73-
{
69+
DecimalTests([
7470
0.3333M, 56.1234M
75-
}, 8, 4);
71+
], 8, 4);
7672

77-
DecimalTests(new[]
78-
{
73+
DecimalTests([
7974
0.33M, 12.34M
80-
}, 4, 2);
75+
], 4, 2);
8176

8277
void DecimalTests(decimal[] values, int precision, int scale)
8378
{
@@ -118,7 +113,7 @@ public void InsertSelectValueTestWithCulture()
118113
{
119114
var defaultCulture = System.Threading.Thread.CurrentThread.CurrentCulture;
120115

121-
DecimalTests(new[] { "fr-fr", "en-us" }, decimal.One / 2, 38, 15);
116+
DecimalTests(["fr-fr", "en-us"], decimal.One / 2, 38, 15);
122117

123118
void DecimalTests(string[] cultures, decimal value, int precision, int scale)
124119
{
@@ -155,4 +150,22 @@ void DecimalTests(string[] cultures, decimal value, int precision, int scale)
155150
System.Threading.Thread.CurrentThread.CurrentCulture = defaultCulture;
156151
}
157152
}
153+
154+
[Fact]
155+
public void BindParameterWithoutTable()
156+
{
157+
decimal[] values = [decimal.Zero, 0.00m, 123456789.987654321m, -123456789.987654321m, 1.230m, -1.23m,
158+
0.000000001m, -0.000000001m, 1000000.000000001m, -1000000.000000001m, 1.123456789012345678901m];
159+
160+
foreach (var value in values)
161+
{
162+
Command.CommandText = "SELECT ?;";
163+
Command.Parameters.Clear();
164+
Command.Parameters.Add(new DuckDBParameter(value));
165+
166+
var result = Command.ExecuteScalar();
167+
168+
result.Should().BeOfType<decimal>().Subject.Should().Be(value);
169+
}
170+
}
158171
}

0 commit comments

Comments
 (0)