| // Licensed to the Apache Software Foundation (ASF) under one or more |
| // contributor license agreements. See the NOTICE file distributed with |
| // this work for additional information regarding copyright ownership. |
| // The ASF licenses this file to You under the Apache License, Version 2.0 |
| // (the "License"); you may not use this file except in compliance with |
| // the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| using System; |
| using System.Data.SqlTypes; |
| using Apache.Arrow.Types; |
| using Xunit; |
| |
| namespace Apache.Arrow.Tests |
| { |
| public class DecimalUtilityTests |
| { |
| public class Overflow |
| { |
| [Theory] |
| [InlineData(100.123, 10, 4, false)] |
| [InlineData(100.123, 6, 4, false)] |
| [InlineData(100.123, 3, 3, true)] |
| [InlineData(100.123, 10, 2, true)] |
| [InlineData(100.123, 5, 2, true)] |
| [InlineData(100.123, 5, 3, true)] |
| [InlineData(100.123, 6, 3, false)] |
| public void HasExpectedResultOrThrows(decimal d, int precision, int scale, bool shouldThrow) |
| { |
| var builder = new Decimal128Array.Builder(new Decimal128Type(precision, scale)); |
| |
| if (shouldThrow) |
| { |
| Assert.Throws<OverflowException>(() => builder.Append(d)); |
| } |
| else |
| { |
| builder.Append(d); |
| var result = builder.Build(new TestMemoryAllocator()); |
| Assert.Equal(d, result.GetValue(0)); |
| } |
| } |
| |
| [Theory] |
| [InlineData(4.56, 38, 9, false)] |
| [InlineData(7.89, 76, 38, true)] |
| public void Decimal256HasExpectedResultOrThrows(decimal d, int precision, int scale, bool shouldThrow) |
| { |
| var builder = new Decimal256Array.Builder(new Decimal256Type(precision, scale)); |
| builder.Append(d); |
| Decimal256Array result = builder.Build(new TestMemoryAllocator()); ; |
| |
| if (shouldThrow) |
| { |
| Assert.Throws<OverflowException>(() => result.GetValue(0)); |
| } |
| else |
| { |
| Assert.Equal(d, result.GetValue(0)); |
| } |
| } |
| } |
| |
| public class SqlDecimals |
| { |
| [Fact] |
| public void NegativeSqlDecimal() |
| { |
| const int precision = 38; |
| const int scale = 0; |
| const int bitWidth = 16; |
| |
| var negative = new SqlDecimal(precision, scale, false, 0, 0, 1, 0); |
| var bytes = new byte[16]; |
| DecimalUtility.GetBytes(negative.Value, precision, scale, bitWidth, bytes); |
| var sqlNegative = DecimalUtility.GetSqlDecimal128(new ArrowBuffer(bytes), 0, precision, scale); |
| Assert.Equal(negative, sqlNegative); |
| |
| DecimalUtility.GetBytes(sqlNegative, precision, scale, bytes); |
| var decimalNegative = DecimalUtility.GetDecimal(new ArrowBuffer(bytes), 0, scale, bitWidth); |
| Assert.Equal(negative.Value, decimalNegative); |
| } |
| |
| [Fact] |
| public void LargeScale() |
| { |
| string digits = "1.2345678901234567890123456789012345678"; |
| |
| var positive = SqlDecimal.Parse(digits); |
| Assert.Equal(38, positive.Precision); |
| Assert.Equal(37, positive.Scale); |
| |
| var bytes = new byte[16]; |
| DecimalUtility.GetBytes(positive, positive.Precision, positive.Scale, bytes); |
| var sqlPositive = DecimalUtility.GetSqlDecimal128(new ArrowBuffer(bytes), 0, positive.Precision, positive.Scale); |
| |
| Assert.Equal(positive, sqlPositive); |
| Assert.Equal(digits, sqlPositive.ToString()); |
| |
| digits = "-" + digits; |
| var negative = SqlDecimal.Parse(digits); |
| Assert.Equal(38, positive.Precision); |
| Assert.Equal(37, positive.Scale); |
| |
| DecimalUtility.GetBytes(negative, negative.Precision, negative.Scale, bytes); |
| var sqlNegative = DecimalUtility.GetSqlDecimal128(new ArrowBuffer(bytes), 0, negative.Precision, negative.Scale); |
| |
| Assert.Equal(negative, sqlNegative); |
| Assert.Equal(digits, sqlNegative.ToString()); |
| } |
| } |
| |
| public class Strings |
| { |
| [Theory] |
| [InlineData(100.12, 10, 2, "100.12")] |
| [InlineData(100.12, 8, 3, "100.120")] |
| [InlineData(100.12, 7, 4, "100.1200")] |
| [InlineData(.12, 6, 3, "0.120")] |
| [InlineData(.0012, 5, 4, "0.0012")] |
| [InlineData(-100.12, 10, 2, "-100.12")] |
| [InlineData(-100.12, 8, 3, "-100.120")] |
| [InlineData(-100.12, 7, 4, "-100.1200")] |
| [InlineData(-.12, 6, 3, "-0.120")] |
| [InlineData(-.0012, 5, 4, "-0.0012")] |
| [InlineData(7.89, 76, 38, "7.89000000000000000000000000000000000000")] |
| public void FromDecimal(decimal d, int precision, int scale, string result) |
| { |
| if (precision <= 38) |
| { |
| TestFromDecimal(d, precision, scale, 16, result); |
| } |
| TestFromDecimal(d, precision, scale, 32, result); |
| } |
| |
| private void TestFromDecimal(decimal d, int precision, int scale, int byteWidth, string result) |
| { |
| var bytes = new byte[byteWidth]; |
| DecimalUtility.GetBytes(d, precision, scale, byteWidth, bytes); |
| Assert.Equal(result, DecimalUtility.GetString(new ArrowBuffer(bytes), 0, precision, scale, byteWidth)); |
| } |
| |
| [Theory] |
| [InlineData("100.12", 10, 2, "100.12")] |
| [InlineData("100.12", 8, 3, "100.120")] |
| [InlineData("100.12", 7, 4, "100.1200")] |
| [InlineData(".12", 6, 3, "0.120")] |
| [InlineData(".0012", 5, 4, "0.0012")] |
| [InlineData("-100.12", 10, 2, "-100.12")] |
| [InlineData("-100.12", 8, 3, "-100.120")] |
| [InlineData("-100.12", 7, 4, "-100.1200")] |
| [InlineData("-.12", 6, 3, "-0.120")] |
| [InlineData("-.0012", 5, 4, "-0.0012")] |
| [InlineData("+.0012", 5, 4, "0.0012")] |
| [InlineData("99999999999999999999999999999999999999", 38, 0, "99999999999999999999999999999999999999")] |
| [InlineData("-99999999999999999999999999999999999999", 38, 0, "-99999999999999999999999999999999999999")] |
| public void FromString(string s, int precision, int scale, string result) |
| { |
| TestFromString(s, precision, scale, 16, result); |
| TestFromString(s, precision, scale, 32, result); |
| } |
| |
| [Fact] |
| public void ThroughDecimal256() |
| { |
| var seventysix = new string('9', 76); |
| TestFromString(seventysix, 76, 0, 32, seventysix); |
| TestFromString("0000" + seventysix, 76, 0, 32, seventysix); |
| |
| seventysix = "-" + seventysix; |
| TestFromString(seventysix, 76, 0, 32, seventysix); |
| |
| var seventyseven = new string('9', 77); |
| Assert.Throws<OverflowException>(() => TestFromString(seventyseven, 76, 0, 32, seventyseven)); |
| } |
| |
| private void TestFromString(string s, int precision, int scale, int byteWidth, string result) |
| { |
| var bytes = new byte[byteWidth]; |
| DecimalUtility.GetBytes(s, precision, scale, byteWidth, bytes); |
| Assert.Equal(result, DecimalUtility.GetString(new ArrowBuffer(bytes), 0, precision, scale, byteWidth)); |
| } |
| |
| [Theory] |
| [InlineData("", 10, 2, 16, typeof(ArgumentException))] |
| [InlineData("", 10, 2, 32, typeof(ArgumentException))] |
| [InlineData(null, 10, 2, 32, typeof(ArgumentException))] |
| [InlineData("1.23", 10, 1, 16, typeof(OverflowException))] |
| [InlineData("12345678901234567890", 24, 1, 8, typeof(OverflowException))] |
| [InlineData("abc", 24, 1, 8, typeof(ArgumentException))] |
| public void ParseErrors(string s, int precision, int scale, int byteWidth, Type exceptionType) |
| { |
| byte[] bytes = new byte[byteWidth]; |
| Assert.Throws(exceptionType, () => DecimalUtility.GetBytes(s, precision, scale, byteWidth, bytes)); |
| } |
| } |
| } |
| } |