Skip to content

Commit

Permalink
Parse decimals with data chunks
Browse files Browse the repository at this point in the history
  • Loading branch information
Giorgi committed Sep 7, 2023
1 parent 7932f5a commit 29b7222
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 14 deletions.
18 changes: 14 additions & 4 deletions DuckDB.NET.Bindings/DuckDBWrapperObjects.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
using System;

using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
using Microsoft.Win32.SafeHandles;

namespace DuckDB.NET
{
Expand Down Expand Up @@ -83,4 +80,17 @@ protected override bool ReleaseHandle()
return true;
}
}

public class DuckDBLogicalType : SafeHandleZeroOrMinusOneIsInvalid
{
public DuckDBLogicalType() : base(true)
{
}

protected override bool ReleaseHandle()
{
NativeMethods.LogicalType.DuckDBDestroyLogicalType(out handle);
return true;
}
}
}
21 changes: 21 additions & 0 deletions DuckDB.NET.Bindings/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ public static class Query
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_column_type")]
public static extern DuckDBType DuckDBColumnType([In, Out] DuckDBResult result, long col);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_column_logical_type")]
public static extern DuckDBLogicalType DuckDBColumnLogicalType(DuckDBResult result, long col);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_column_count")]
public static extern long DuckDBColumnCount([In, Out] DuckDBResult result);

Expand All @@ -84,6 +87,21 @@ public static class Query
public static extern IntPtr DuckDBResultError([In, Out] DuckDBResult result);
}

public static class LogicalType
{
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_decimal_width")]
public static extern byte DuckDBDecimalWidth(DuckDBLogicalType type);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_decimal_scale")]
public static extern byte DuckDBDecimalScale(DuckDBLogicalType type);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_decimal_internal_type")]
public static extern DuckDBType DuckDBDecimalInternalType(DuckDBLogicalType type);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_destroy_logical_type")]
public static extern void DuckDBDestroyLogicalType(out IntPtr type);
}

public static class DataChunks
{
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_data_chunk_get_column_count")]
Expand Down Expand Up @@ -403,6 +421,9 @@ public static class Helpers
{
[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_free")]
public static extern void DuckDBFree(IntPtr ptr);

[DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_decimal_to_double")]
public static extern double DuckDBDecimalToDouble(DuckDBDecimal val);
}

public static class DateTime
Expand Down
37 changes: 34 additions & 3 deletions DuckDB.NET.Data/DuckDBDataReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Globalization;
using System.IO;
using System.Numerics;
using System.Runtime.InteropServices;
Expand Down Expand Up @@ -122,8 +121,40 @@ private DuckDBTimeOnly GetTimeOnly(int ordinal)

public override decimal GetDecimal(int ordinal)
{
return 0;
return decimal.Parse(GetString(ordinal), CultureInfo.InvariantCulture);
using (var logicalType = NativeMethods.Query.DuckDBColumnLogicalType(currentResult, ordinal))
{
var scale = NativeMethods.LogicalType.DuckDBDecimalScale(logicalType);
var width = NativeMethods.LogicalType.DuckDBDecimalWidth(logicalType);
var internalType = NativeMethods.LogicalType.DuckDBDecimalInternalType(logicalType);

decimal result = 0;

var pow = (decimal)Math.Pow(10, scale);

switch (internalType)
{
case DuckDBType.DuckdbTypeSmallInt:
result = decimal.Divide(GetInt16(ordinal), pow);
break;
case DuckDBType.DuckdbTypeInteger:
result = decimal.Divide(GetInt32(ordinal), pow);
break;
case DuckDBType.DuckdbTypeBigInt:
result = decimal.Divide(GetInt64(ordinal), pow);
break;
case DuckDBType.DuckdbTypeHugeInt:
{
var hugeInt = GetBigInteger(ordinal);

result = (decimal)BigInteger.DivRem(hugeInt, (BigInteger)pow, out var remainder);

result += decimal.Divide((decimal)remainder, pow);
break;
}
}

return result;
}
}

public override double GetDouble(int ordinal)
Expand Down
30 changes: 23 additions & 7 deletions DuckDB.NET.Test/Parameters/DecimalParameterTest.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Globalization;
using System.Text.RegularExpressions;
using DuckDB.NET.Data;
using FluentAssertions;
using Xunit;
Expand All @@ -17,18 +16,35 @@ public void SimpleTest()

var values = new[]{0m, decimal.Zero, decimal.MinValue,
decimal.MaxValue, decimal.MaxValue / 3, decimal.One,
decimal.One / 2, decimal.One / 3, decimal.MinusOne,
//decimal.MinusOne / 2,
decimal.MinusOne / 3};
decimal.One / 2, decimal.MinusOne,
decimal.MinusOne / 2};

foreach (var value in values)
{
var command = connection.CreateCommand();
command.CommandText = $"SELECT {Convert.ToString(value, CultureInfo.InvariantCulture)};";
command.CommandText = $"SELECT {Convert.ToString(value, CultureInfo.InvariantCulture)}::DECIMAL(38,9);";
command.ExecuteNonQuery();

//var scalar = command.ExecuteScalar();
//scalar.Should().Be(value);
var scalar = command.ExecuteScalar();
scalar.Should().Be(value);

var reader = command.ExecuteReader();
reader.Read();
var receivedValue = reader.GetDecimal(0);
receivedValue.Should().Be(value);
}


values = new[] { decimal.One / 3, decimal.MinusOne / 3 };

foreach (var value in values)
{
var command = connection.CreateCommand();
command.CommandText = $"SELECT {Convert.ToString(value, CultureInfo.InvariantCulture)}::DECIMAL(38,28);";
command.ExecuteNonQuery();

var scalar = command.ExecuteScalar();
scalar.Should().Be(value);

var reader = command.ExecuteReader();
reader.Read();
Expand Down

0 comments on commit 29b7222

Please sign in to comment.