diff --git a/ClickHouse.Client.Tests/TestUtilities.cs b/ClickHouse.Client.Tests/TestUtilities.cs index 59b5e941..93974493 100644 --- a/ClickHouse.Client.Tests/TestUtilities.cs +++ b/ClickHouse.Client.Tests/TestUtilities.cs @@ -164,7 +164,11 @@ public static IEnumerable GetDataTypeSamples() if (SupportedFeatures.HasFlag(Feature.WideTypes)) { yield return new DataTypeSample("Int128", typeof(BigInteger), "toInt128(concat('-1', repeat('0', 30)))", -BigInteger.Pow(new BigInteger(10), 30)); - yield return new DataTypeSample("UInt128", typeof(BigInteger), "toInt128(concat('1', repeat('0', 30)))", BigInteger.Pow(new BigInteger(10), 30)); + yield return new DataTypeSample("Int128", typeof(BigInteger), "toInt128('170141183460469231731687303715884105727')", BigInteger.Parse("170141183460469231731687303715884105727")); + yield return new DataTypeSample("Int128", typeof(BigInteger), "toInt128('-170141183460469231731687303715884105728')", BigInteger.Parse("-170141183460469231731687303715884105728")); + + yield return new DataTypeSample("UInt128", typeof(BigInteger), "toInt128(concat('1', repeat('0', 30)))", BigInteger.Pow(new BigInteger(10), 30)); + yield return new DataTypeSample("UInt128", typeof(BigInteger), "toUInt128('340282366920938463463374607431768211455')", BigInteger.Parse("340282366920938463463374607431768211455")); yield return new DataTypeSample("Int256", typeof(BigInteger), "toInt256(concat('-1', repeat('0', 50)))", -BigInteger.Pow(new BigInteger(10), 50)); yield return new DataTypeSample("UInt256", typeof(BigInteger), "toInt256(concat('1', repeat('0', 50)))", BigInteger.Pow(new BigInteger(10), 50)); diff --git a/ClickHouse.Client/Types/AbstractBigIntegerType.cs b/ClickHouse.Client/Types/AbstractBigIntegerType.cs index e20457da..4b566f36 100644 --- a/ClickHouse.Client/Types/AbstractBigIntegerType.cs +++ b/ClickHouse.Client/Types/AbstractBigIntegerType.cs @@ -11,7 +11,17 @@ internal abstract class AbstractBigIntegerType : IntegerType public override Type FrameworkType => typeof(BigInteger); - public override object Read(ExtendedBinaryReader reader) => new BigInteger(reader.ReadBytes(Size)); + public override object Read(ExtendedBinaryReader reader) + { + if (Signed) + return new BigInteger(reader.ReadBytes(Size)); + + var data = new byte[Size + 1]; + for (int i = 0; i < Size; i++) + data[i] = reader.ReadByte(); + data[Size] = 0; + return new BigInteger(data); + } public abstract override string ToString(); @@ -30,13 +40,20 @@ public override void Write(ExtendedBinaryWriter writer, object value) _ => new BigInteger(Convert.ToInt64(value, CultureInfo.InvariantCulture)) }; + if (bigInt < 0 && !Signed) + throw new ArgumentException("Cannot convert negative BigInteger to UInt"); + byte[] bigIntBytes = bigInt.ToByteArray(); byte[] decimalBytes = new byte[Size]; - if (bigIntBytes.Length > Size) - throw new OverflowException(); + var lengthToCopy = bigIntBytes.Length; + if (!Signed && bigIntBytes[bigIntBytes.Length - 1] == 0) + lengthToCopy = bigIntBytes.Length - 1; + + if (lengthToCopy > Size) + throw new OverflowException($"Got {lengthToCopy} bytes, {Size} expected"); - bigIntBytes.CopyTo(decimalBytes, 0); + Array.Copy(bigIntBytes, decimalBytes, lengthToCopy); // If a negative BigInteger is not long enough to fill the whole buffer, // the remainder needs to be filled with 0xFF diff --git a/ClickHouse.Client/Types/IntegerType.cs b/ClickHouse.Client/Types/IntegerType.cs index fff054e8..419f3305 100644 --- a/ClickHouse.Client/Types/IntegerType.cs +++ b/ClickHouse.Client/Types/IntegerType.cs @@ -2,5 +2,6 @@ { internal abstract class IntegerType : ClickHouseType { + public virtual bool Signed => true; } } diff --git a/ClickHouse.Client/Types/UInt128Type.cs b/ClickHouse.Client/Types/UInt128Type.cs index 43c45dea..3da46013 100644 --- a/ClickHouse.Client/Types/UInt128Type.cs +++ b/ClickHouse.Client/Types/UInt128Type.cs @@ -1,9 +1,14 @@ -namespace ClickHouse.Client.Types +using System.Numerics; +using ClickHouse.Client.Formats; + +namespace ClickHouse.Client.Types { internal class UInt128Type : AbstractBigIntegerType { public override int Size => 16; public override string ToString() => "UInt128"; + + public override bool Signed => false; } } diff --git a/ClickHouse.Client/Types/UInt256Type.cs b/ClickHouse.Client/Types/UInt256Type.cs index 54609d9e..e2d7039a 100644 --- a/ClickHouse.Client/Types/UInt256Type.cs +++ b/ClickHouse.Client/Types/UInt256Type.cs @@ -1,9 +1,14 @@ -namespace ClickHouse.Client.Types +using System.Numerics; +using ClickHouse.Client.Formats; + +namespace ClickHouse.Client.Types { internal class UInt256Type : AbstractBigIntegerType { public override int Size => 32; public override string ToString() => "UInt256"; + + public override bool Signed => false; } }