| // 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.Collections.Generic; |
| using System.Data.SqlTypes; |
| using System.Linq; |
| using Apache.Arrow.Types; |
| using Xunit; |
| |
| namespace Apache.Arrow.Tests |
| { |
| public class Decimal256ArrayTests |
| { |
| static SqlDecimal? GetSqlDecimal(Decimal256Array array, int index) |
| { |
| SqlDecimal? result; |
| Assert.True(array.TryGetSqlDecimal(index, out result)); |
| return result; |
| } |
| |
| static SqlDecimal? Convert(decimal? value) |
| { |
| return value == null ? null : new SqlDecimal(value.Value); |
| } |
| |
| static decimal? Convert(SqlDecimal? value) |
| { |
| return value == null ? null : value.Value.Value; |
| } |
| |
| static decimal? Convert(string value) |
| { |
| return value == null ? null : decimal.Parse(value); |
| } |
| |
| public class Builder |
| { |
| public class AppendNull |
| { |
| [Fact] |
| public void AppendThenGetGivesNull() |
| { |
| // Arrange |
| var builder = new Decimal256Array.Builder(new Decimal256Type(8, 2)); |
| |
| // Act |
| |
| builder = builder.AppendNull(); |
| builder = builder.AppendNull(); |
| builder = builder.AppendNull(); |
| // Assert |
| var array = builder.Build(); |
| |
| Assert.Equal(3, array.Length); |
| Assert.Equal(array.Data.Buffers[1].Length, array.ByteWidth * 3); |
| Assert.Null(array.GetValue(0)); |
| Assert.Null(array.GetValue(1)); |
| Assert.Null(array.GetValue(2)); |
| |
| Assert.Null(GetSqlDecimal(array, 0)); |
| Assert.Null(GetSqlDecimal(array, 1)); |
| Assert.Null(GetSqlDecimal(array, 2)); |
| } |
| } |
| |
| public class Append |
| { |
| [Theory] |
| [InlineData(200)] |
| public void AppendDecimal(int count) |
| { |
| // Arrange |
| var builder = new Decimal256Array.Builder(new Decimal256Type(14, 10)); |
| |
| // Act |
| decimal?[] testData = new decimal?[count]; |
| for (int i = 0; i < count; i++) |
| { |
| if (i == count - 2) |
| { |
| builder.AppendNull(); |
| testData[i] = null; |
| continue; |
| } |
| decimal rnd = i * (decimal)Math.Round(new Random().NextDouble(), 10); |
| testData[i] = rnd; |
| builder.Append(rnd); |
| } |
| |
| // Assert |
| var array = builder.Build(); |
| Assert.Equal(count, array.Length); |
| for (int i = 0; i < count; i++) |
| { |
| Assert.Equal(testData[i], array.GetValue(i)); |
| Assert.Equal(Convert(testData[i]), GetSqlDecimal(array, i)); |
| } |
| } |
| |
| [Fact] |
| public void AppendLargeDecimal() |
| { |
| // Arrange |
| var builder = new Decimal256Array.Builder(new Decimal256Type(26, 2)); |
| decimal large = 999999999999909999999999.80M; |
| // Act |
| builder.Append(large); |
| builder.Append(-large); |
| |
| // Assert |
| var array = builder.Build(); |
| Assert.Equal(large, array.GetValue(0)); |
| Assert.Equal(-large, array.GetValue(1)); |
| |
| Assert.Equal(Convert(large), GetSqlDecimal(array, 0)); |
| Assert.Equal(Convert(-large), GetSqlDecimal(array, 1)); |
| } |
| |
| [Fact] |
| public void AppendMaxAndMinDecimal() |
| { |
| // Arrange |
| var builder = new Decimal256Array.Builder(new Decimal256Type(29, 0)); |
| |
| // Act |
| builder.Append(Decimal.MaxValue); |
| builder.Append(Decimal.MinValue); |
| builder.Append(Decimal.MaxValue - 10); |
| builder.Append(Decimal.MinValue + 10); |
| |
| // Assert |
| var array = builder.Build(); |
| Assert.Equal(Decimal.MaxValue, array.GetValue(0)); |
| Assert.Equal(Decimal.MinValue, array.GetValue(1)); |
| Assert.Equal(Decimal.MaxValue - 10, array.GetValue(2)); |
| Assert.Equal(Decimal.MinValue + 10, array.GetValue(3)); |
| |
| Assert.Equal(Convert(Decimal.MaxValue), GetSqlDecimal(array, 0)); |
| Assert.Equal(Convert(Decimal.MinValue), GetSqlDecimal(array, 1)); |
| Assert.Equal(Convert(Decimal.MaxValue) - 10, GetSqlDecimal(array, 2)); |
| Assert.Equal(Convert(Decimal.MinValue) + 10, GetSqlDecimal(array, 3)); |
| } |
| |
| [Fact] |
| public void AppendFractionalDecimal() |
| { |
| // Arrange |
| var builder = new Decimal256Array.Builder(new Decimal256Type(26, 20)); |
| decimal fraction = 0.99999999999990999992M; |
| // Act |
| builder.Append(fraction); |
| builder.Append(-fraction); |
| |
| // Assert |
| var array = builder.Build(); |
| Assert.Equal(fraction, array.GetValue(0)); |
| Assert.Equal(-fraction, array.GetValue(1)); |
| |
| Assert.Equal(Convert(fraction), GetSqlDecimal(array, 0)); |
| Assert.Equal(Convert(-fraction), GetSqlDecimal(array, 1)); |
| } |
| |
| [Fact] |
| public void AppendRangeDecimal() |
| { |
| // Arrange |
| var builder = new Decimal256Array.Builder(new Decimal256Type(24, 8)); |
| var range = new decimal[] { 2.123M, 1.5984M, -0.0000001M, 9878987987987987.1235407M }; |
| |
| // Act |
| builder.AppendRange(range); |
| builder.AppendNull(); |
| |
| // Assert |
| var array = builder.Build(); |
| for (int i = 0; i < range.Length; i++) |
| { |
| Assert.Equal(range[i], array.GetValue(i)); |
| Assert.Equal(Convert(range[i]), GetSqlDecimal(array, i)); |
| } |
| |
| Assert.Null(array.GetValue(range.Length)); |
| } |
| |
| [Fact] |
| public void AppendClearAppendDecimal() |
| { |
| // Arrange |
| var builder = new Decimal256Array.Builder(new Decimal256Type(24, 8)); |
| |
| // Act |
| builder.Append(1); |
| builder.Clear(); |
| builder.Append(10); |
| |
| // Assert |
| var array = builder.Build(); |
| Assert.Equal(10, array.GetValue(0)); |
| } |
| |
| [Fact] |
| public void AppendInvalidPrecisionAndScaleDecimal() |
| { |
| // Arrange |
| var builder = new Decimal256Array.Builder(new Decimal256Type(2, 1)); |
| |
| // Assert |
| Assert.Throws<OverflowException>(() => builder.Append(100)); |
| Assert.Throws<OverflowException>(() => builder.Append(0.01M)); |
| builder.Append(-9.9M); |
| builder.Append(0); |
| builder.Append(9.9M); |
| } |
| } |
| |
| public class Set |
| { |
| [Fact] |
| public void SetDecimal() |
| { |
| // Arrange |
| var builder = new Decimal256Array.Builder(new Decimal256Type(24, 8)) |
| .Resize(1); |
| |
| // Act |
| builder.Set(0, 50.123456M); |
| builder.Set(0, 1.01M); |
| |
| // Assert |
| var array = builder.Build(); |
| Assert.Equal(1.01M, array.GetValue(0)); |
| } |
| |
| [Fact] |
| public void SetNull() |
| { |
| // Arrange |
| var builder = new Decimal256Array.Builder(new Decimal256Type(24, 8)) |
| .Resize(1); |
| |
| // Act |
| builder.Set(0, 50.123456M); |
| builder.SetNull(0); |
| |
| // Assert |
| var array = builder.Build(); |
| Assert.Null(array.GetValue(0)); |
| } |
| } |
| |
| public class Swap |
| { |
| [Fact] |
| public void SetDecimal() |
| { |
| // Arrange |
| var builder = new Decimal256Array.Builder(new Decimal256Type(24, 8)); |
| |
| // Act |
| builder.Append(123.45M); |
| builder.Append(678.9M); |
| builder.Swap(0, 1); |
| |
| // Assert |
| var array = builder.Build(); |
| Assert.Equal(678.9M, array.GetValue(0)); |
| Assert.Equal(123.45M, array.GetValue(1)); |
| } |
| |
| [Fact] |
| public void SwapNull() |
| { |
| // Arrange |
| var builder = new Decimal256Array.Builder(new Decimal256Type(24, 8)); |
| |
| // Act |
| builder.Append(123.456M); |
| builder.AppendNull(); |
| builder.Swap(0, 1); |
| |
| // Assert |
| var array = builder.Build(); |
| Assert.Null(array.GetValue(0)); |
| Assert.Equal(123.456M, array.GetValue(1)); |
| } |
| } |
| |
| public class SqlDecimals |
| { |
| [Theory] |
| [InlineData(200)] |
| public void AppendSqlDecimal(int count) |
| { |
| // Arrange |
| const int precision = 10; |
| var builder = new Decimal256Array.Builder(new Decimal256Type(14, precision)); |
| |
| // Act |
| SqlDecimal?[] testData = new SqlDecimal?[count]; |
| for (int i = 0; i < count; i++) |
| { |
| if (i == count - 2) |
| { |
| builder.AppendNull(); |
| testData[i] = null; |
| continue; |
| } |
| SqlDecimal rnd = i * (SqlDecimal)Math.Round(new Random().NextDouble(), 10); |
| builder.Append(rnd); |
| testData[i] = SqlDecimal.Round(rnd, precision); |
| } |
| |
| // Assert |
| var array = builder.Build(); |
| Assert.Equal(count, array.Length); |
| for (int i = 0; i < count; i++) |
| { |
| Assert.Equal(testData[i], GetSqlDecimal(array, i)); |
| Assert.Equal(Convert(testData[i]), array.GetValue(i)); |
| } |
| |
| IReadOnlyList<SqlDecimal?> asDecimalList = array; |
| for (int i = 0; i < asDecimalList.Count; i++) |
| { |
| Assert.Equal(testData[i], asDecimalList[i]); |
| } |
| |
| IReadOnlyList<string> asStringList = array; |
| for (int i = 0; i < asStringList.Count; i++) |
| { |
| Assert.Equal(Convert(testData[i]?.ToString()), Convert(asStringList[i])); |
| } |
| } |
| |
| [Fact] |
| public void AppendMaxAndMinSqlDecimal() |
| { |
| // Arrange |
| var builder = new Decimal256Array.Builder(new Decimal256Type(38, 0)); |
| |
| // Act |
| builder.Append(SqlDecimal.MaxValue); |
| builder.Append(SqlDecimal.MinValue); |
| builder.Append(SqlDecimal.MaxValue - 10); |
| builder.Append(SqlDecimal.MinValue + 10); |
| |
| // Assert |
| var array = builder.Build(); |
| Assert.Equal(SqlDecimal.MaxValue, GetSqlDecimal(array, 0)); |
| Assert.Equal(SqlDecimal.MinValue, GetSqlDecimal(array, 1)); |
| Assert.Equal(SqlDecimal.MaxValue - 10, GetSqlDecimal(array, 2)); |
| Assert.Equal(SqlDecimal.MinValue + 10, GetSqlDecimal(array, 3)); |
| } |
| |
| [Fact] |
| public void AppendRangeSqlDecimal() |
| { |
| // Arrange |
| var builder = new Decimal256Array.Builder(new Decimal256Type(24, 8)); |
| var range = new SqlDecimal[] { 2.123M, 1.5984M, -0.0000001M, 9878987987987987.1235407M }; |
| |
| // Act |
| builder.AppendRange(range); |
| builder.AppendNull(); |
| |
| // Assert |
| var array = builder.Build(); |
| for (int i = 0; i < range.Length; i++) |
| { |
| Assert.Equal(range[i], GetSqlDecimal(array, i)); |
| Assert.Equal(Convert(range[i]), array.GetValue(i)); |
| } |
| |
| Assert.Null(array.GetValue(range.Length)); |
| } |
| } |
| |
| public class Strings |
| { |
| [Theory] |
| [InlineData(200)] |
| public void AppendString(int count) |
| { |
| // Arrange |
| const int precision = 10; |
| var builder = new Decimal256Array.Builder(new Decimal256Type(14, precision)); |
| |
| // Act |
| string[] testData = new string[count]; |
| for (int i = 0; i < count; i++) |
| { |
| if (i == count - 2) |
| { |
| builder.AppendNull(); |
| testData[i] = null; |
| continue; |
| } |
| SqlDecimal rnd = i * (SqlDecimal)Math.Round(new Random().NextDouble(), 10); |
| builder.Append(rnd); |
| testData[i] = SqlDecimal.Round(rnd, precision).ToString(); |
| } |
| |
| // Assert |
| var array = builder.Build(); |
| Assert.Equal(count, array.Length); |
| for (int i = 0; i < count; i++) |
| { |
| if (testData[i] == null) |
| { |
| Assert.Null(array.GetString(i)); |
| Assert.Null(GetSqlDecimal(array, i)); |
| } |
| else |
| { |
| Assert.Equal(testData[i].TrimEnd('0'), array.GetString(i).TrimEnd('0')); |
| Assert.Equal(SqlDecimal.Parse(testData[i]), GetSqlDecimal(array, i)); |
| } |
| } |
| } |
| |
| [Fact] |
| public void AppendMaxAndMinSqlDecimal() |
| { |
| // Arrange |
| var builder = new Decimal256Array.Builder(new Decimal256Type(38, 0)); |
| |
| // Act |
| builder.Append(SqlDecimal.MaxValue.ToString()); |
| builder.Append(SqlDecimal.MinValue.ToString()); |
| string maxMinusTen = (SqlDecimal.MaxValue - 10).ToString(); |
| string minPlusTen = (SqlDecimal.MinValue + 10).ToString(); |
| builder.Append(maxMinusTen); |
| builder.Append(minPlusTen); |
| |
| // Assert |
| var array = builder.Build(); |
| Assert.Equal(SqlDecimal.MaxValue.ToString(), array.GetString(0)); |
| Assert.Equal(SqlDecimal.MinValue.ToString(), array.GetString(1)); |
| Assert.Equal(maxMinusTen, array.GetString(2)); |
| Assert.Equal(minPlusTen, array.GetString(3)); |
| } |
| |
| [Fact] |
| public void AppendRangeSqlDecimal() |
| { |
| // Arrange |
| var builder = new Decimal256Array.Builder(new Decimal256Type(24, 8)); |
| var range = new SqlDecimal[] { 2.123M, 1.5984M, -0.0000001M, 9878987987987987.1235407M }; |
| |
| // Act |
| builder.AppendRange(range.Select(d => d.ToString())); |
| builder.AppendNull(); |
| |
| // Assert |
| var array = builder.Build(); |
| for (int i = 0; i < range.Length; i++) |
| { |
| Assert.Equal(range[i], GetSqlDecimal(array, i)); |
| Assert.Equal(range[i].ToString().TrimEnd('0'), array.GetString(i).TrimEnd('0')); |
| } |
| |
| Assert.Null(array.GetValue(range.Length)); |
| } |
| } |
| } |
| |
| [Fact] |
| public void SliceDecimal256Array() |
| { |
| // Arrange |
| const int originalLength = 50; |
| const int offset = 3; |
| const int sliceLength = 32; |
| |
| var builder = new Decimal256Array.Builder(new Decimal256Type(14, 10)); |
| var random = new Random(); |
| |
| for (int i = 0; i < originalLength; i++) |
| { |
| if (random.NextDouble() < 0.2) |
| { |
| builder.AppendNull(); |
| } |
| else |
| { |
| builder.Append(i * (decimal)Math.Round(random.NextDouble(), 10)); |
| } |
| } |
| |
| var array = builder.Build(); |
| |
| // Act |
| var slice = (Decimal256Array)array.Slice(offset, sliceLength); |
| |
| // Assert |
| Assert.NotNull(slice); |
| Assert.Equal(sliceLength, slice.Length); |
| for (int i = 0; i < sliceLength; ++i) |
| { |
| Assert.Equal(array.GetValue(offset + i), slice.GetValue(i)); |
| if (array.TryGetSqlDecimal(offset + i, out var expectedSqlDecimal)) |
| { |
| Assert.True(slice.TryGetSqlDecimal(i, out var actualSqlDecimal)); |
| Assert.Equal(expectedSqlDecimal, actualSqlDecimal); |
| } |
| else |
| { |
| Assert.False(slice.TryGetSqlDecimal(i, out _)); |
| } |
| Assert.Equal(array.GetString(offset + i), slice.GetString(i)); |
| } |
| |
| Assert.Equal( |
| array.ToList(includeNulls: true).Skip(offset).Take(sliceLength).ToList(), |
| slice.ToList(includeNulls: true)); |
| } |
| } |
| } |