ClickHouse.Client icon indicating copy to clipboard operation
ClickHouse.Client copied to clipboard

Big decimals support

Open DarkWanderer opened this issue 3 years ago • 6 comments

Big decimals support using custom type poached from public domain Planned features:

  • Feature toggle to use regular decimal vs new class
  • Type is convertible to normal numeric types and string

TODO

  • [x] Fix all inaccuracies in math operations
  • [ ] Do a round of optimizations
  • [ ] Add config flag for supporting the new decimal
  • [x] Implement parsing from text

Ref #137

DarkWanderer avatar Jul 09 '22 12:07 DarkWanderer

Hello! If I try to use ClickhouseDecimal as a Dapper parameter to Where. I get an exception "The member DecimalName of type ClickHouse.Client.Numerics.ClickHouseDecimal cannot be used as a parameter value".

    var connectionFactory = host.Services.GetService<IClickhouseConnectionFactory>()!;
    var text = @"
        SELECT *
        FROM xxxxxx.dapper_test2
        WHERE DecimalName = {DecimalName:decimal128(18)}
        ;
        ";

    var parameters = new Dictionary<string, object> { { "DecimalName", 
        new ClickHouseDecimal(BigInteger.Parse("123456789123123450000000000000"), 18)} };

    using var con = connectionFactory.GetConnection();

    var cmd = new CommandDefinition(text, parameters);
    try
    {
        var res = (await con.QueryAsync<DapperDto3>(cmd)).AsList();
    }
    catch (Exception e)
    {
        Console.WriteLine(e);
    }

DmitryLk avatar Jul 12 '22 08:07 DmitryLk

Hi! Scale parameter in new ClickhouseDecimal class can be positive only (ushort). Are you planning to do it also negative to suppot such cases as {mantissa:1, scale=-3} => 1000? Thank

DmitryLk avatar Jul 12 '22 08:07 DmitryLk

@DmitryLk you need to add a custom type handler to Dapper to be able to handle the new Decimal:

Class definition:


        private class ClickHouseDecimalHandler : SqlMapper.TypeHandler<ClickHouseDecimal>
        {
            public override void SetValue(IDbDataParameter parameter, ClickHouseDecimal value) => parameter.Value = value.ToString(CultureInfo.InvariantCulture);

            public override ClickHouseDecimal Parse(object value) => value switch
            {
                ClickHouseDecimal chd => chd,
                IConvertible ic => Convert.ToDecimal(ic),
                _ => throw new ArgumentException(nameof(value))
            };
        }

After that, ClickHouseDecimal works both as parameter and as SELECTable value

            var parameters = new Dictionary<string, object> { { "value", new ClickHouseDecimal(1m) } };
            var results = await connection.QueryAsync<ClickHouseDecimal>("SELECT {value:Decimal32(3)}", parameters);
            Assert.AreEqual(value, results.Single());

DarkWanderer avatar Jul 12 '22 10:07 DarkWanderer

Positive-only scale/exponent is by design right now as it matches ClickHouse's type. I'll think about switching back to possibly-negative exponent

DarkWanderer avatar Jul 12 '22 10:07 DarkWanderer

Thanks. ClickhouseDecimal parameter with custom handler to Dapper is working

DmitryLk avatar Jul 12 '22 11:07 DmitryLk

Code Coverage

Package Line Rate Branch Rate Complexity Health
ClickHouse.Client 86% 74% 1036
Summary 86% (1335 / 1561) 74% (471 / 640) 1036

github-actions[bot] avatar Jul 18 '22 21:07 github-actions[bot]

Code Coverage

Package Line Rate Branch Rate Complexity Health
ClickHouse.Client 86% 75% 1082
Summary 86% (1423 / 1651) 75% (495 / 662) 1082

github-actions[bot] avatar Oct 13 '22 20:10 github-actions[bot]