This is an automated email from the ASF dual-hosted git repository.
curth pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow.git
The following commit(s) were added to refs/heads/main by this push:
new b0e13cc41d GH-44271: [C#] Add support for Decimal32 and Decimal64
(#44272)
b0e13cc41d is described below
commit b0e13cc41d27ffb734bc163b165fc0989adaa9c9
Author: Curt Hagenlocher <[email protected]>
AuthorDate: Mon Sep 30 20:34:37 2024 -0700
GH-44271: [C#] Add support for Decimal32 and Decimal64 (#44272)
### What changes are included in this PR?
Implementation of decimal32 and decimal64 for C#.
Tests for the implementation, including enablement of Archery tests.
### Are these changes tested?
Yes.
### Are there any user-facing changes?
New types Decimal32Array and Decimal64Array.
Closes #44271
* GitHub Issue: #44271
Authored-by: Curt Hagenlocher <[email protected]>
Signed-off-by: Curt Hagenlocher <[email protected]>
---
.../Arrays/ArrowArrayBuilderFactory.cs | 4 +
.../src/Apache.Arrow/Arrays/ArrowArrayFactory.cs | 4 +
csharp/src/Apache.Arrow/Arrays/Decimal32Array.cs | 182 +++++++++++
csharp/src/Apache.Arrow/Arrays/Decimal64Array.cs | 182 +++++++++++
csharp/src/Apache.Arrow/C/CArrowSchemaExporter.cs | 4 +
csharp/src/Apache.Arrow/C/CArrowSchemaImporter.cs | 18 +-
csharp/src/Apache.Arrow/Ipc/ArrowStreamWriter.cs | 6 +
.../Apache.Arrow/Ipc/ArrowTypeFlatbufferBuilder.cs | 16 +
csharp/src/Apache.Arrow/Ipc/MessageSerializer.cs | 4 +
csharp/src/Apache.Arrow/RecordBatch.Builder.cs | 6 +
csharp/src/Apache.Arrow/Types/Decimal32Type.cs | 35 +++
csharp/src/Apache.Arrow/Types/Decimal64Type.cs | 35 +++
csharp/src/Apache.Arrow/Types/IArrowType.cs | 2 +
.../test/Apache.Arrow.IntegrationTest/JsonFile.cs | 18 +-
.../ArrowArrayConcatenatorTests.cs | 9 +-
.../test/Apache.Arrow.Tests/ArrowReaderVerifier.cs | 4 +
.../CDataInterfacePythonTests.cs | 4 +-
.../test/Apache.Arrow.Tests/Decimal32ArrayTests.cs | 337 +++++++++++++++++++++
.../test/Apache.Arrow.Tests/Decimal64ArrayTests.cs | 337 +++++++++++++++++++++
csharp/test/Apache.Arrow.Tests/TableTests.cs | 4 +-
csharp/test/Apache.Arrow.Tests/TestData.cs | 29 ++
dev/archery/archery/integration/datagen.py | 2 -
docs/source/status.rst | 4 +-
23 files changed, 1225 insertions(+), 21 deletions(-)
diff --git a/csharp/src/Apache.Arrow/Arrays/ArrowArrayBuilderFactory.cs
b/csharp/src/Apache.Arrow/Arrays/ArrowArrayBuilderFactory.cs
index cb7164aa14..3fb77d2c3b 100644
--- a/csharp/src/Apache.Arrow/Arrays/ArrowArrayBuilderFactory.cs
+++ b/csharp/src/Apache.Arrow/Arrays/ArrowArrayBuilderFactory.cs
@@ -78,6 +78,10 @@ namespace Apache.Arrow
return new ListViewArray.Builder(dataType as ListViewType);
case ArrowTypeId.FixedSizeList:
return new FixedSizeListArray.Builder(dataType as
FixedSizeListType);
+ case ArrowTypeId.Decimal32:
+ return new Decimal32Array.Builder(dataType as
Decimal32Type);
+ case ArrowTypeId.Decimal64:
+ return new Decimal64Array.Builder(dataType as
Decimal64Type);
case ArrowTypeId.Decimal128:
return new Decimal128Array.Builder(dataType as
Decimal128Type);
case ArrowTypeId.Decimal256:
diff --git a/csharp/src/Apache.Arrow/Arrays/ArrowArrayFactory.cs
b/csharp/src/Apache.Arrow/Arrays/ArrowArrayFactory.cs
index bd06c3a1b8..38092a7a23 100644
--- a/csharp/src/Apache.Arrow/Arrays/ArrowArrayFactory.cs
+++ b/csharp/src/Apache.Arrow/Arrays/ArrowArrayFactory.cs
@@ -87,6 +87,10 @@ namespace Apache.Arrow
return new Time64Array(data);
case ArrowTypeId.Duration:
return new DurationArray(data);
+ case ArrowTypeId.Decimal32:
+ return new Decimal32Array(data);
+ case ArrowTypeId.Decimal64:
+ return new Decimal64Array(data);
case ArrowTypeId.Decimal128:
return new Decimal128Array(data);
case ArrowTypeId.Decimal256:
diff --git a/csharp/src/Apache.Arrow/Arrays/Decimal32Array.cs
b/csharp/src/Apache.Arrow/Arrays/Decimal32Array.cs
new file mode 100644
index 0000000000..21dee1108a
--- /dev/null
+++ b/csharp/src/Apache.Arrow/Arrays/Decimal32Array.cs
@@ -0,0 +1,182 @@
+// 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;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Apache.Arrow.Arrays;
+using Apache.Arrow.Types;
+
+namespace Apache.Arrow
+{
+ public class Decimal32Array : FixedSizeBinaryArray, IReadOnlyList<decimal?>
+ {
+ public class Builder : BuilderBase<Decimal32Array, Builder>
+ {
+ public Builder(Decimal32Type type) : base(type, 4)
+ {
+ DataType = type;
+ }
+
+ protected new Decimal32Type DataType { get; }
+
+ protected override Decimal32Array Build(ArrayData data)
+ {
+ return new Decimal32Array(data);
+ }
+
+ public Builder Append(decimal value)
+ {
+ Span<byte> bytes = stackalloc byte[DataType.ByteWidth];
+ DecimalUtility.GetBytes(value, DataType.Precision,
DataType.Scale, DataType.ByteWidth, bytes);
+
+ return Append(bytes);
+ }
+
+ public Builder AppendRange(IEnumerable<decimal> values)
+ {
+ if (values == null)
+ {
+ throw new ArgumentNullException(nameof(values));
+ }
+
+ foreach (decimal d in values)
+ {
+ Append(d);
+ }
+
+ return Instance;
+ }
+
+ public Builder Append(string value)
+ {
+ if (value == null)
+ {
+ AppendNull();
+ }
+ else
+ {
+ Span<byte> bytes = stackalloc byte[DataType.ByteWidth];
+ DecimalUtility.GetBytes(value, DataType.Precision,
DataType.Scale, ByteWidth, bytes);
+ Append(bytes);
+ }
+
+ return Instance;
+ }
+
+ public Builder AppendRange(IEnumerable<string> values)
+ {
+ if (values == null)
+ {
+ throw new ArgumentNullException(nameof(values));
+ }
+
+ foreach (string s in values)
+ {
+ Append(s);
+ }
+
+ return Instance;
+ }
+
+ public Builder Set(int index, decimal value)
+ {
+ Span<byte> bytes = stackalloc byte[DataType.ByteWidth];
+ DecimalUtility.GetBytes(value, DataType.Precision,
DataType.Scale, DataType.ByteWidth, bytes);
+
+ return Set(index, bytes);
+ }
+ }
+
+ public Decimal32Array(ArrayData data)
+ : base(ArrowTypeId.Decimal32, data)
+ {
+ data.EnsureDataType(ArrowTypeId.Decimal32);
+ data.EnsureBufferCount(2);
+ Debug.Assert(Data.DataType is Decimal32Type);
+ }
+ public override void Accept(IArrowArrayVisitor visitor) =>
Accept(this, visitor);
+
+ public int Scale => ((Decimal32Type)Data.DataType).Scale;
+ public int Precision => ((Decimal32Type)Data.DataType).Precision;
+ public int ByteWidth => ((Decimal32Type)Data.DataType).ByteWidth;
+
+ public decimal? GetValue(int index)
+ {
+ if (IsNull(index))
+ {
+ return null;
+ }
+ return DecimalUtility.GetDecimal(ValueBuffer, Offset + index,
Scale, ByteWidth);
+ }
+
+ public IList<decimal?> ToList(bool includeNulls = false)
+ {
+ var list = new List<decimal?>(Length);
+
+ for (int i = 0; i < Length; i++)
+ {
+ decimal? value = GetValue(i);
+
+ if (value.HasValue)
+ {
+ list.Add(value.Value);
+ }
+ else
+ {
+ if (includeNulls)
+ {
+ list.Add(null);
+ }
+ }
+ }
+
+ return list;
+ }
+
+ public string GetString(int index)
+ {
+ if (IsNull(index))
+ {
+ return null;
+ }
+ return DecimalUtility.GetString(ValueBuffer, Offset + index,
Precision, Scale, ByteWidth);
+ }
+
+ public decimal? GetDecimal(int index)
+ {
+ if (IsNull(index))
+ {
+ return null;
+ }
+
+ return DecimalUtility.GetDecimal(ValueBuffer, Offset + index,
Scale, ByteWidth);
+ }
+
+ int IReadOnlyCollection<decimal?>.Count => Length;
+ decimal? IReadOnlyList<decimal?>.this[int index] => GetDecimal(index);
+
+ IEnumerator<decimal?> IEnumerable<decimal?>.GetEnumerator()
+ {
+ for (int index = 0; index < Length; index++)
+ {
+ yield return GetDecimal(index);
+ }
+ }
+
+ IEnumerator IEnumerable.GetEnumerator() =>
((IEnumerable<decimal>)this).GetEnumerator();
+ }
+}
diff --git a/csharp/src/Apache.Arrow/Arrays/Decimal64Array.cs
b/csharp/src/Apache.Arrow/Arrays/Decimal64Array.cs
new file mode 100644
index 0000000000..b50baef7d9
--- /dev/null
+++ b/csharp/src/Apache.Arrow/Arrays/Decimal64Array.cs
@@ -0,0 +1,182 @@
+// 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;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Apache.Arrow.Arrays;
+using Apache.Arrow.Types;
+
+namespace Apache.Arrow
+{
+ public class Decimal64Array : FixedSizeBinaryArray, IReadOnlyList<decimal?>
+ {
+ public class Builder : BuilderBase<Decimal64Array, Builder>
+ {
+ public Builder(Decimal64Type type) : base(type, 8)
+ {
+ DataType = type;
+ }
+
+ protected new Decimal64Type DataType { get; }
+
+ protected override Decimal64Array Build(ArrayData data)
+ {
+ return new Decimal64Array(data);
+ }
+
+ public Builder Append(decimal value)
+ {
+ Span<byte> bytes = stackalloc byte[DataType.ByteWidth];
+ DecimalUtility.GetBytes(value, DataType.Precision,
DataType.Scale, DataType.ByteWidth, bytes);
+
+ return Append(bytes);
+ }
+
+ public Builder AppendRange(IEnumerable<decimal> values)
+ {
+ if (values == null)
+ {
+ throw new ArgumentNullException(nameof(values));
+ }
+
+ foreach (decimal d in values)
+ {
+ Append(d);
+ }
+
+ return Instance;
+ }
+
+ public Builder Append(string value)
+ {
+ if (value == null)
+ {
+ AppendNull();
+ }
+ else
+ {
+ Span<byte> bytes = stackalloc byte[DataType.ByteWidth];
+ DecimalUtility.GetBytes(value, DataType.Precision,
DataType.Scale, ByteWidth, bytes);
+ Append(bytes);
+ }
+
+ return Instance;
+ }
+
+ public Builder AppendRange(IEnumerable<string> values)
+ {
+ if (values == null)
+ {
+ throw new ArgumentNullException(nameof(values));
+ }
+
+ foreach (string s in values)
+ {
+ Append(s);
+ }
+
+ return Instance;
+ }
+
+ public Builder Set(int index, decimal value)
+ {
+ Span<byte> bytes = stackalloc byte[DataType.ByteWidth];
+ DecimalUtility.GetBytes(value, DataType.Precision,
DataType.Scale, DataType.ByteWidth, bytes);
+
+ return Set(index, bytes);
+ }
+ }
+
+ public Decimal64Array(ArrayData data)
+ : base(ArrowTypeId.Decimal64, data)
+ {
+ data.EnsureDataType(ArrowTypeId.Decimal64);
+ data.EnsureBufferCount(2);
+ Debug.Assert(Data.DataType is Decimal64Type);
+ }
+ public override void Accept(IArrowArrayVisitor visitor) =>
Accept(this, visitor);
+
+ public int Scale => ((Decimal64Type)Data.DataType).Scale;
+ public int Precision => ((Decimal64Type)Data.DataType).Precision;
+ public int ByteWidth => ((Decimal64Type)Data.DataType).ByteWidth;
+
+ public decimal? GetValue(int index)
+ {
+ if (IsNull(index))
+ {
+ return null;
+ }
+ return DecimalUtility.GetDecimal(ValueBuffer, Offset + index,
Scale, ByteWidth);
+ }
+
+ public IList<decimal?> ToList(bool includeNulls = false)
+ {
+ var list = new List<decimal?>(Length);
+
+ for (int i = 0; i < Length; i++)
+ {
+ decimal? value = GetValue(i);
+
+ if (value.HasValue)
+ {
+ list.Add(value.Value);
+ }
+ else
+ {
+ if (includeNulls)
+ {
+ list.Add(null);
+ }
+ }
+ }
+
+ return list;
+ }
+
+ public string GetString(int index)
+ {
+ if (IsNull(index))
+ {
+ return null;
+ }
+ return DecimalUtility.GetString(ValueBuffer, Offset + index,
Precision, Scale, ByteWidth);
+ }
+
+ public decimal? GetDecimal(int index)
+ {
+ if (IsNull(index))
+ {
+ return null;
+ }
+
+ return DecimalUtility.GetDecimal(ValueBuffer, Offset + index,
Scale, ByteWidth);
+ }
+
+ int IReadOnlyCollection<decimal?>.Count => Length;
+ decimal? IReadOnlyList<decimal?>.this[int index] => GetDecimal(index);
+
+ IEnumerator<decimal?> IEnumerable<decimal?>.GetEnumerator()
+ {
+ for (int index = 0; index < Length; index++)
+ {
+ yield return GetDecimal(index);
+ }
+ }
+
+ IEnumerator IEnumerable.GetEnumerator() =>
((IEnumerable<decimal>)this).GetEnumerator();
+ }
+}
diff --git a/csharp/src/Apache.Arrow/C/CArrowSchemaExporter.cs
b/csharp/src/Apache.Arrow/C/CArrowSchemaExporter.cs
index 92d48a2d70..1cf6dc78a7 100644
--- a/csharp/src/Apache.Arrow/C/CArrowSchemaExporter.cs
+++ b/csharp/src/Apache.Arrow/C/CArrowSchemaExporter.cs
@@ -161,6 +161,10 @@ namespace Apache.Arrow.C
case FloatType _: return "f";
case DoubleType _: return "g";
// Decimal
+ case Decimal32Type decimalType:
+ return $"d:{decimalType.Precision},{decimalType.Scale},32";
+ case Decimal64Type decimalType:
+ return $"d:{decimalType.Precision},{decimalType.Scale},64";
case Decimal128Type decimalType:
return $"d:{decimalType.Precision},{decimalType.Scale}";
case Decimal256Type decimalType:
diff --git a/csharp/src/Apache.Arrow/C/CArrowSchemaImporter.cs
b/csharp/src/Apache.Arrow/C/CArrowSchemaImporter.cs
index 94177184de..a772b8f4a5 100644
--- a/csharp/src/Apache.Arrow/C/CArrowSchemaImporter.cs
+++ b/csharp/src/Apache.Arrow/C/CArrowSchemaImporter.cs
@@ -224,19 +224,17 @@ namespace Apache.Arrow.C
// Decimals
if (format.StartsWith("d:"))
{
- bool is256 = format.EndsWith(",256");
- string parameters_part = format.Remove(0, 2);
- if (is256) parameters_part.Substring(0,
parameters_part.Length - 5);
- string[] parameters = parameters_part.Split(',');
+ string[] parameters = format.Substring(2).Split(',');
int precision = Int32.Parse(parameters[0]);
int scale = Int32.Parse(parameters[1]);
- if (is256)
+ int bitWidth = parameters.Length == 2 ? 128 :
Int32.Parse(parameters[2]);
+ switch (bitWidth)
{
- return new Decimal256Type(precision, scale);
- }
- else
- {
- return new Decimal128Type(precision, scale);
+ case 32: return new Decimal32Type(precision, scale);
+ case 64: return new Decimal64Type(precision, scale);
+ case 128: return new Decimal128Type(precision, scale);
+ case 256: return new Decimal256Type(precision, scale);
+ default: throw new InvalidDataException($"Unexpected
bit width {bitWidth}");
}
}
diff --git a/csharp/src/Apache.Arrow/Ipc/ArrowStreamWriter.cs
b/csharp/src/Apache.Arrow/Ipc/ArrowStreamWriter.cs
index eaa8471fa7..ff0b64c09e 100644
--- a/csharp/src/Apache.Arrow/Ipc/ArrowStreamWriter.cs
+++ b/csharp/src/Apache.Arrow/Ipc/ArrowStreamWriter.cs
@@ -68,6 +68,8 @@ namespace Apache.Arrow.Ipc
IArrowArrayVisitor<FixedSizeBinaryArray>,
IArrowArrayVisitor<StructArray>,
IArrowArrayVisitor<UnionArray>,
+ IArrowArrayVisitor<Decimal32Array>,
+ IArrowArrayVisitor<Decimal64Array>,
IArrowArrayVisitor<Decimal128Array>,
IArrowArrayVisitor<Decimal256Array>,
IArrowArrayVisitor<DictionaryArray>,
@@ -292,6 +294,10 @@ namespace Apache.Arrow.Ipc
_buffers.Add(CreateSlicedBuffer(array.ValueBuffer, itemSize,
array.Offset, array.Length));
}
+ public void Visit(Decimal32Array array) => Visit(array as
FixedSizeBinaryArray);
+
+ public void Visit(Decimal64Array array) => Visit(array as
FixedSizeBinaryArray);
+
public void Visit(Decimal128Array array) => Visit(array as
FixedSizeBinaryArray);
public void Visit(Decimal256Array array) => Visit(array as
FixedSizeBinaryArray);
diff --git a/csharp/src/Apache.Arrow/Ipc/ArrowTypeFlatbufferBuilder.cs
b/csharp/src/Apache.Arrow/Ipc/ArrowTypeFlatbufferBuilder.cs
index adc229a051..d1fb921686 100644
--- a/csharp/src/Apache.Arrow/Ipc/ArrowTypeFlatbufferBuilder.cs
+++ b/csharp/src/Apache.Arrow/Ipc/ArrowTypeFlatbufferBuilder.cs
@@ -74,6 +74,8 @@ namespace Apache.Arrow.Ipc
IArrowTypeVisitor<FixedSizeListType>,
IArrowTypeVisitor<UnionType>,
IArrowTypeVisitor<StructType>,
+ IArrowTypeVisitor<Decimal32Type>,
+ IArrowTypeVisitor<Decimal64Type>,
IArrowTypeVisitor<Decimal128Type>,
IArrowTypeVisitor<Decimal256Type>,
IArrowTypeVisitor<DictionaryType>,
@@ -276,6 +278,20 @@ namespace Apache.Arrow.Ipc
Result = FieldType.Build(Flatbuf.Type.Struct_,
Flatbuf.Struct_.EndStruct_(Builder));
}
+ public void Visit(Decimal32Type type)
+ {
+ Result = FieldType.Build(
+ Flatbuf.Type.Decimal,
+ Flatbuf.Decimal.CreateDecimal(Builder, type.Precision,
type.Scale, type.BitWidth));
+ }
+
+ public void Visit(Decimal64Type type)
+ {
+ Result = FieldType.Build(
+ Flatbuf.Type.Decimal,
+ Flatbuf.Decimal.CreateDecimal(Builder, type.Precision,
type.Scale, type.BitWidth));
+ }
+
public void Visit(Decimal128Type type)
{
Result = FieldType.Build(
diff --git a/csharp/src/Apache.Arrow/Ipc/MessageSerializer.cs
b/csharp/src/Apache.Arrow/Ipc/MessageSerializer.cs
index 8e15632c51..7c7f7a38da 100644
--- a/csharp/src/Apache.Arrow/Ipc/MessageSerializer.cs
+++ b/csharp/src/Apache.Arrow/Ipc/MessageSerializer.cs
@@ -142,6 +142,10 @@ namespace Apache.Arrow.Ipc
Flatbuf.Decimal decMeta =
field.Type<Flatbuf.Decimal>().Value;
switch (decMeta.BitWidth)
{
+ case 32:
+ return new Types.Decimal32Type(decMeta.Precision,
decMeta.Scale);
+ case 64:
+ return new Types.Decimal64Type(decMeta.Precision,
decMeta.Scale);
case 128:
return new Types.Decimal128Type(decMeta.Precision,
decMeta.Scale);
case 256:
diff --git a/csharp/src/Apache.Arrow/RecordBatch.Builder.cs
b/csharp/src/Apache.Arrow/RecordBatch.Builder.cs
index 8e0d17ae06..e9a84a48d8 100644
--- a/csharp/src/Apache.Arrow/RecordBatch.Builder.cs
+++ b/csharp/src/Apache.Arrow/RecordBatch.Builder.cs
@@ -47,6 +47,12 @@ namespace Apache.Arrow
#endif
public FloatArray Float(Action<FloatArray.Builder> action) =>
Build<FloatArray, FloatArray.Builder>(new FloatArray.Builder(), action);
public DoubleArray Double(Action<DoubleArray.Builder> action) =>
Build<DoubleArray, DoubleArray.Builder>(new DoubleArray.Builder(), action);
+ public Decimal32Array Decimal32(Decimal32Type type,
Action<Decimal32Array.Builder> action) =>
+ Build<Decimal32Array, Decimal32Array.Builder>(
+ new Decimal32Array.Builder(type), action);
+ public Decimal64Array Decimal64(Decimal64Type type,
Action<Decimal64Array.Builder> action) =>
+ Build<Decimal64Array, Decimal64Array.Builder>(
+ new Decimal64Array.Builder(type), action);
public Decimal128Array Decimal128(Decimal128Type type,
Action<Decimal128Array.Builder> action) =>
Build<Decimal128Array, Decimal128Array.Builder>(
new Decimal128Array.Builder(type), action);
diff --git a/csharp/src/Apache.Arrow/Types/Decimal32Type.cs
b/csharp/src/Apache.Arrow/Types/Decimal32Type.cs
new file mode 100644
index 0000000000..c494b3b013
--- /dev/null
+++ b/csharp/src/Apache.Arrow/Types/Decimal32Type.cs
@@ -0,0 +1,35 @@
+// 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.
+
+namespace Apache.Arrow.Types
+{
+ public sealed class Decimal32Type : FixedSizeBinaryType
+ {
+ public override ArrowTypeId TypeId => ArrowTypeId.Decimal32;
+ public override string Name => "decimal32";
+
+ public int Precision { get; }
+ public int Scale { get; }
+
+ public Decimal32Type(int precision, int scale)
+ : base(4)
+ {
+ Precision = precision;
+ Scale = scale;
+ }
+
+ public override void Accept(IArrowTypeVisitor visitor) => Accept(this,
visitor);
+ }
+}
diff --git a/csharp/src/Apache.Arrow/Types/Decimal64Type.cs
b/csharp/src/Apache.Arrow/Types/Decimal64Type.cs
new file mode 100644
index 0000000000..30d544b326
--- /dev/null
+++ b/csharp/src/Apache.Arrow/Types/Decimal64Type.cs
@@ -0,0 +1,35 @@
+// 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.
+
+namespace Apache.Arrow.Types
+{
+ public sealed class Decimal64Type : FixedSizeBinaryType
+ {
+ public override ArrowTypeId TypeId => ArrowTypeId.Decimal64;
+ public override string Name => "decimal64";
+
+ public int Precision { get; }
+ public int Scale { get; }
+
+ public Decimal64Type(int precision, int scale)
+ : base(8)
+ {
+ Precision = precision;
+ Scale = scale;
+ }
+
+ public override void Accept(IArrowTypeVisitor visitor) => Accept(this,
visitor);
+ }
+}
diff --git a/csharp/src/Apache.Arrow/Types/IArrowType.cs
b/csharp/src/Apache.Arrow/Types/IArrowType.cs
index 7a3159a1bb..6020fd41e3 100644
--- a/csharp/src/Apache.Arrow/Types/IArrowType.cs
+++ b/csharp/src/Apache.Arrow/Types/IArrowType.cs
@@ -56,6 +56,8 @@ namespace Apache.Arrow.Types
LargeList,
LargeBinary,
LargeString,
+ Decimal32,
+ Decimal64,
}
public interface IArrowType
diff --git a/csharp/test/Apache.Arrow.IntegrationTest/JsonFile.cs
b/csharp/test/Apache.Arrow.IntegrationTest/JsonFile.cs
index c9e44b8d2f..a75c4193da 100644
--- a/csharp/test/Apache.Arrow.IntegrationTest/JsonFile.cs
+++ b/csharp/test/Apache.Arrow.IntegrationTest/JsonFile.cs
@@ -231,7 +231,11 @@ namespace Apache.Arrow.IntegrationTest
return type.BitWidth switch
{
256 => new Decimal256Type(type.DecimalPrecision, type.Scale),
- _ => new Decimal128Type(type.DecimalPrecision, type.Scale),
+ 128 => new Decimal128Type(type.DecimalPrecision, type.Scale),
+ 64 => new Decimal64Type(type.DecimalPrecision, type.Scale),
+ 32 => new Decimal32Type(type.DecimalPrecision, type.Scale),
+ 0 => new Decimal128Type(type.DecimalPrecision, type.Scale),
+ _ => throw new NotSupportedException($"Decimal type not
supported. BitWidth: {type.BitWidth}"),
};
}
@@ -458,6 +462,8 @@ namespace Apache.Arrow.IntegrationTest
IArrowTypeVisitor<UInt64Type>,
IArrowTypeVisitor<FloatType>,
IArrowTypeVisitor<DoubleType>,
+ IArrowTypeVisitor<Decimal32Type>,
+ IArrowTypeVisitor<Decimal64Type>,
IArrowTypeVisitor<Decimal128Type>,
IArrowTypeVisitor<Decimal256Type>,
IArrowTypeVisitor<Date32Type>,
@@ -553,6 +559,16 @@ namespace Apache.Arrow.IntegrationTest
}
}
+ public void Visit(Decimal32Type type)
+ {
+ Array = new Decimal32Array(GetDecimalArrayData(type));
+ }
+
+ public void Visit(Decimal64Type type)
+ {
+ Array = new Decimal64Array(GetDecimalArrayData(type));
+ }
+
public void Visit(Decimal128Type type)
{
Array = new Decimal128Array(GetDecimalArrayData(type));
diff --git a/csharp/test/Apache.Arrow.Tests/ArrowArrayConcatenatorTests.cs
b/csharp/test/Apache.Arrow.Tests/ArrowArrayConcatenatorTests.cs
index 2437d3d94c..a45a50c4a2 100644
--- a/csharp/test/Apache.Arrow.Tests/ArrowArrayConcatenatorTests.cs
+++ b/csharp/test/Apache.Arrow.Tests/ArrowArrayConcatenatorTests.cs
@@ -80,8 +80,10 @@ namespace Apache.Arrow.Tests
Date32Type.Default,
Date64Type.Default,
TimestampType.Default,
+ new Decimal32Type(7, 3),
+ new Decimal64Type(14, 4),
new Decimal128Type(14, 10),
- new Decimal256Type(14,10),
+ new Decimal256Type(14, 10),
new ListType(Int64Type.Default),
new ListViewType(Int64Type.Default),
new StructType(new List<Field>{
@@ -138,6 +140,8 @@ namespace Apache.Arrow.Tests
IArrowTypeVisitor<BinaryViewType>,
IArrowTypeVisitor<StringType>,
IArrowTypeVisitor<StringViewType>,
+ IArrowTypeVisitor<Decimal32Type>,
+ IArrowTypeVisitor<Decimal64Type>,
IArrowTypeVisitor<Decimal128Type>,
IArrowTypeVisitor<Decimal256Type>,
IArrowTypeVisitor<Date32Type>,
@@ -207,8 +211,9 @@ namespace Apache.Arrow.Tests
public void Visit(Date32Type type) => GenerateTestData<DateTime,
Date32Array, Date32Array.Builder>(type, x => DateTime.MinValue.AddDays(x));
public void Visit(Date64Type type) => GenerateTestData<DateTime,
Date64Array, Date64Array.Builder>(type, x => DateTime.MinValue.AddDays(x));
+ public void Visit(Decimal32Type type) =>
GenerateTestData<Decimal32Array, Decimal32Array.Builder>(type, (builder, x) =>
builder.Append(x));
+ public void Visit(Decimal64Type type) =>
GenerateTestData<Decimal64Array, Decimal64Array.Builder>(type, (builder, x) =>
builder.Append(x));
public void Visit(Decimal128Type type) =>
GenerateTestData<Decimal128Array, Decimal128Array.Builder>(type, (builder, x)
=> builder.Append(x));
-
public void Visit(Decimal256Type type) =>
GenerateTestData<Decimal256Array, Decimal256Array.Builder>(type, (builder, x)
=> builder.Append(x));
public void Visit(TimestampType type)
diff --git a/csharp/test/Apache.Arrow.Tests/ArrowReaderVerifier.cs
b/csharp/test/Apache.Arrow.Tests/ArrowReaderVerifier.cs
index 35b2c4e7f2..5977c3288a 100644
--- a/csharp/test/Apache.Arrow.Tests/ArrowReaderVerifier.cs
+++ b/csharp/test/Apache.Arrow.Tests/ArrowReaderVerifier.cs
@@ -106,6 +106,8 @@ namespace Apache.Arrow.Tests
IArrowArrayVisitor<LargeBinaryArray>,
IArrowArrayVisitor<StructArray>,
IArrowArrayVisitor<UnionArray>,
+ IArrowArrayVisitor<Decimal32Array>,
+ IArrowArrayVisitor<Decimal64Array>,
IArrowArrayVisitor<Decimal128Array>,
IArrowArrayVisitor<Decimal256Array>,
IArrowArrayVisitor<DictionaryArray>,
@@ -150,6 +152,8 @@ namespace Apache.Arrow.Tests
public void Visit(LargeListArray array) => CompareArrays(array);
public void Visit(FixedSizeListArray array) =>
CompareArrays(array);
public void Visit(FixedSizeBinaryArray array) =>
CompareArrays(array);
+ public void Visit(Decimal32Array array) => CompareArrays(array);
+ public void Visit(Decimal64Array array) => CompareArrays(array);
public void Visit(Decimal128Array array) => CompareArrays(array);
public void Visit(Decimal256Array array) => CompareArrays(array);
public void Visit(StringArray array) =>
CompareBinaryArrays<StringArray>(array);
diff --git a/csharp/test/Apache.Arrow.Tests/CDataInterfacePythonTests.cs
b/csharp/test/Apache.Arrow.Tests/CDataInterfacePythonTests.cs
index 638cbfb272..0ef8eba261 100644
--- a/csharp/test/Apache.Arrow.Tests/CDataInterfacePythonTests.cs
+++ b/csharp/test/Apache.Arrow.Tests/CDataInterfacePythonTests.cs
@@ -754,7 +754,7 @@ namespace Apache.Arrow.Tests
public unsafe void RoundTripTestBatch()
{
// TODO: Enable these once this the version of pyarrow referenced
during testing supports them
- HashSet<ArrowTypeId> unsupported = new HashSet<ArrowTypeId> {
ArrowTypeId.ListView, ArrowTypeId.BinaryView, ArrowTypeId.StringView };
+ HashSet<ArrowTypeId> unsupported = new HashSet<ArrowTypeId> {
ArrowTypeId.ListView, ArrowTypeId.BinaryView, ArrowTypeId.StringView,
ArrowTypeId.Decimal32, ArrowTypeId.Decimal64 };
RecordBatch batch1 = TestData.CreateSampleRecordBatch(4,
excludedTypes: unsupported);
RecordBatch batch2 = batch1.Clone();
@@ -796,7 +796,7 @@ namespace Apache.Arrow.Tests
public unsafe void RoundTripTestSlicedBatch()
{
// TODO: Enable these once this the version of pyarrow referenced
during testing supports them
- HashSet<ArrowTypeId> unsupported = new HashSet<ArrowTypeId> {
ArrowTypeId.ListView, ArrowTypeId.BinaryView, ArrowTypeId.StringView };
+ HashSet<ArrowTypeId> unsupported = new HashSet<ArrowTypeId> {
ArrowTypeId.ListView, ArrowTypeId.BinaryView, ArrowTypeId.StringView,
ArrowTypeId.Decimal32, ArrowTypeId.Decimal64 };
RecordBatch batch1 = TestData.CreateSampleRecordBatch(4,
excludedTypes: unsupported);
RecordBatch batch1slice = batch1.Slice(1, 2);
RecordBatch batch2 = batch1slice.Clone();
diff --git a/csharp/test/Apache.Arrow.Tests/Decimal32ArrayTests.cs
b/csharp/test/Apache.Arrow.Tests/Decimal32ArrayTests.cs
new file mode 100644
index 0000000000..f7b7d409a0
--- /dev/null
+++ b/csharp/test/Apache.Arrow.Tests/Decimal32ArrayTests.cs
@@ -0,0 +1,337 @@
+// 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.Linq;
+using Apache.Arrow.Types;
+using Xunit;
+
+namespace Apache.Arrow.Tests
+{
+ public class Decimal32ArrayTests
+ {
+ public class Builder
+ {
+ public class AppendNull
+ {
+ [Fact]
+ public void AppendThenGetGivesNull()
+ {
+ // Arrange
+ var builder = new Decimal32Array.Builder(new
Decimal32Type(7, 3));
+
+ // 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));
+ }
+ }
+
+ public class Append
+ {
+ [Theory]
+ [InlineData(200)]
+ public void AppendDecimal(int count)
+ {
+ // Arrange
+ var builder = new Decimal32Array.Builder(new
Decimal32Type(7, 3));
+
+ // 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(), 2);
+ 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));
+ }
+ }
+
+ [Fact]
+ public void AppendLargeDecimal()
+ {
+ // Arrange
+ var builder = new Decimal32Array.Builder(new
Decimal32Type(7, 3));
+ decimal large = 9999.999M;
+ // Act
+ builder.Append(large);
+ builder.Append(-large);
+
+ // Assert
+ var array = builder.Build();
+ Assert.Equal(large, array.GetValue(0));
+ Assert.Equal(-large, array.GetValue(1));
+ }
+
+ [Fact]
+ public void AppendFractionalDecimal()
+ {
+ // Arrange
+ var builder = new Decimal32Array.Builder(new
Decimal32Type(9, 9));
+ decimal fraction = 0.999999999M;
+ // Act
+ builder.Append(fraction);
+ builder.Append(-fraction);
+
+ // Assert
+ var array = builder.Build();
+ Assert.Equal(fraction, array.GetValue(0));
+ Assert.Equal(-fraction, array.GetValue(1));
+ }
+
+ [Fact]
+ public void AppendRangeDecimal()
+ {
+ // Arrange
+ var builder = new Decimal32Array.Builder(new
Decimal32Type(7, 3));
+ var range = new decimal[] { 2.123M, 1.598M, -0.001M,
7987.123M };
+
+ // 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.Null(array.GetValue(range.Length));
+ }
+
+ [Fact]
+ public void AppendClearAppendDecimal()
+ {
+ // Arrange
+ var builder = new Decimal32Array.Builder(new
Decimal32Type(7, 3));
+
+ // 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 Decimal32Array.Builder(new
Decimal32Type(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 Decimal32Array.Builder(new
Decimal32Type(7, 3))
+ .Resize(1);
+
+ // Act
+ builder.Set(0, 50.123M);
+ 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 Decimal32Array.Builder(new
Decimal32Type(7, 3))
+ .Resize(1);
+
+ // Act
+ builder.Set(0, 50.123M);
+ builder.SetNull(0);
+
+ // Assert
+ var array = builder.Build();
+ Assert.Null(array.GetValue(0));
+ }
+ }
+
+ public class Swap
+ {
+ [Fact]
+ public void SetDecimal()
+ {
+ // Arrange
+ var builder = new Decimal32Array.Builder(new
Decimal32Type(7, 3));
+
+ // 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 Decimal32Array.Builder(new
Decimal32Type(7, 3));
+
+ // 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 Strings
+ {
+ [Theory]
+ [InlineData(200)]
+ public void AppendString(int count)
+ {
+ // Arrange
+ const int precision = 4;
+ var builder = new Decimal32Array.Builder(new
Decimal32Type(9, precision));
+
+ // Act
+ string[] testData = new string[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(), precision - 2);
+ builder.Append(rnd);
+ testData[i] = decimal.Round(rnd, precision -
1).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(array.GetDecimal(i));
+ }
+ else
+ {
+ Assert.Equal(NormalizeNumber(testData[i]),
NormalizeNumber(array.GetString(i)));
+ Assert.Equal(Decimal.Parse(testData[i]),
array.GetDecimal(i));
+ }
+ }
+ }
+
+ static string NormalizeNumber(string number)
+ {
+ if (number.IndexOf('.') > 0)
+ {
+ number = number.TrimEnd('0');
+ number = number.TrimEnd('.');
+ }
+ return number;
+ }
+ }
+ }
+
+ [Fact]
+ public void SliceDecimal32Array()
+ {
+ // Arrange
+ const int originalLength = 50;
+ const int offset = 3;
+ const int sliceLength = 32;
+
+ var builder = new Decimal32Array.Builder(new Decimal32Type(7, 3));
+ 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(), 2));
+ }
+ }
+
+ var array = builder.Build();
+
+ // Act
+ var slice = (Decimal32Array)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));
+ Assert.Equal(array.GetString(offset + i), slice.GetString(i));
+ }
+
+ Assert.Equal(
+ array.ToList(includeNulls:
true).Skip(offset).Take(sliceLength).ToList(),
+ slice.ToList(includeNulls: true));
+ }
+ }
+}
diff --git a/csharp/test/Apache.Arrow.Tests/Decimal64ArrayTests.cs
b/csharp/test/Apache.Arrow.Tests/Decimal64ArrayTests.cs
new file mode 100644
index 0000000000..b37a44a859
--- /dev/null
+++ b/csharp/test/Apache.Arrow.Tests/Decimal64ArrayTests.cs
@@ -0,0 +1,337 @@
+// 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.Linq;
+using Apache.Arrow.Types;
+using Xunit;
+
+namespace Apache.Arrow.Tests
+{
+ public class Decimal64ArrayTests
+ {
+ public class Builder
+ {
+ public class AppendNull
+ {
+ [Fact]
+ public void AppendThenGetGivesNull()
+ {
+ // Arrange
+ var builder = new Decimal64Array.Builder(new
Decimal64Type(7, 3));
+
+ // 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));
+ }
+ }
+
+ public class Append
+ {
+ [Theory]
+ [InlineData(200)]
+ public void AppendDecimal(int count)
+ {
+ // Arrange
+ var builder = new Decimal64Array.Builder(new
Decimal64Type(7, 3));
+
+ // 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(), 2);
+ 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));
+ }
+ }
+
+ [Fact]
+ public void AppendLargeDecimal()
+ {
+ // Arrange
+ var builder = new Decimal64Array.Builder(new
Decimal64Type(19, 6));
+ decimal large = 9199999999999.999999M;
+ // Act
+ builder.Append(large);
+ builder.Append(-large);
+
+ // Assert
+ var array = builder.Build();
+ Assert.Equal(large, array.GetValue(0));
+ Assert.Equal(-large, array.GetValue(1));
+ }
+
+ [Fact]
+ public void AppendFractionalDecimal()
+ {
+ // Arrange
+ var builder = new Decimal64Array.Builder(new
Decimal64Type(9, 9));
+ decimal fraction = 0.999999999M;
+ // Act
+ builder.Append(fraction);
+ builder.Append(-fraction);
+
+ // Assert
+ var array = builder.Build();
+ Assert.Equal(fraction, array.GetValue(0));
+ Assert.Equal(-fraction, array.GetValue(1));
+ }
+
+ [Fact]
+ public void AppendRangeDecimal()
+ {
+ // Arrange
+ var builder = new Decimal64Array.Builder(new
Decimal64Type(17, 4));
+ var range = new decimal[] { 2.123M, 12345678.598M,
-0.001M, 7987.1237M };
+
+ // 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.Null(array.GetValue(range.Length));
+ }
+
+ [Fact]
+ public void AppendClearAppendDecimal()
+ {
+ // Arrange
+ var builder = new Decimal64Array.Builder(new
Decimal64Type(7, 3));
+
+ // 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 Decimal64Array.Builder(new
Decimal64Type(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 Decimal64Array.Builder(new
Decimal64Type(7, 3))
+ .Resize(1);
+
+ // Act
+ builder.Set(0, 50.123M);
+ 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 Decimal64Array.Builder(new
Decimal64Type(7, 3))
+ .Resize(1);
+
+ // Act
+ builder.Set(0, 50.123M);
+ builder.SetNull(0);
+
+ // Assert
+ var array = builder.Build();
+ Assert.Null(array.GetValue(0));
+ }
+ }
+
+ public class Swap
+ {
+ [Fact]
+ public void SetDecimal()
+ {
+ // Arrange
+ var builder = new Decimal64Array.Builder(new
Decimal64Type(7, 3));
+
+ // 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 Decimal64Array.Builder(new
Decimal64Type(7, 3));
+
+ // 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 Strings
+ {
+ [Theory]
+ [InlineData(200)]
+ public void AppendString(int count)
+ {
+ // Arrange
+ const int precision = 4;
+ var builder = new Decimal64Array.Builder(new
Decimal64Type(9, precision));
+
+ // Act
+ string[] testData = new string[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(), precision - 2);
+ builder.Append(rnd);
+ testData[i] = decimal.Round(rnd, precision -
1).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(array.GetDecimal(i));
+ }
+ else
+ {
+ Assert.Equal(NormalizeNumber(testData[i]),
NormalizeNumber(array.GetString(i)));
+ Assert.Equal(Decimal.Parse(testData[i]),
array.GetDecimal(i));
+ }
+ }
+ }
+
+ static string NormalizeNumber(string number)
+ {
+ if (number.IndexOf('.') > 0)
+ {
+ number = number.TrimEnd('0');
+ number = number.TrimEnd('.');
+ }
+ return number;
+ }
+ }
+ }
+
+ [Fact]
+ public void SliceDecimal64Array()
+ {
+ // Arrange
+ const int originalLength = 50;
+ const int offset = 3;
+ const int sliceLength = 32;
+
+ var builder = new Decimal64Array.Builder(new Decimal64Type(7, 3));
+ 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(), 2));
+ }
+ }
+
+ var array = builder.Build();
+
+ // Act
+ var slice = (Decimal64Array)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));
+ Assert.Equal(array.GetString(offset + i), slice.GetString(i));
+ }
+
+ Assert.Equal(
+ array.ToList(includeNulls:
true).Skip(offset).Take(sliceLength).ToList(),
+ slice.ToList(includeNulls: true));
+ }
+ }
+}
diff --git a/csharp/test/Apache.Arrow.Tests/TableTests.cs
b/csharp/test/Apache.Arrow.Tests/TableTests.cs
index 35fbe7cba6..a12e7b9ccd 100644
--- a/csharp/test/Apache.Arrow.Tests/TableTests.cs
+++ b/csharp/test/Apache.Arrow.Tests/TableTests.cs
@@ -63,9 +63,9 @@ namespace Apache.Arrow.Tests
Table table1 = Table.TableFromRecordBatches(recordBatch1.Schema,
recordBatches);
Assert.Equal(20, table1.RowCount);
#if NET5_0_OR_GREATER
- Assert.Equal(38, table1.ColumnCount);
+ Assert.Equal(40, table1.ColumnCount);
#else
- Assert.Equal(37, table1.ColumnCount);
+ Assert.Equal(39, table1.ColumnCount);
#endif
Assert.Equal("ChunkedArray: Length=20, DataType=list",
table1.Column(0).Data.ToString());
diff --git a/csharp/test/Apache.Arrow.Tests/TestData.cs
b/csharp/test/Apache.Arrow.Tests/TestData.cs
index 36969766ae..08ada33d7e 100644
--- a/csharp/test/Apache.Arrow.Tests/TestData.cs
+++ b/csharp/test/Apache.Arrow.Tests/TestData.cs
@@ -72,6 +72,8 @@ namespace Apache.Arrow.Tests
AddField(CreateField(StringType.Default, i));
AddField(CreateField(StringViewType.Default, i));
AddField(CreateField(new StructType(new List<Field> {
CreateField(StringType.Default, i), CreateField(Int32Type.Default, i) }), i));
+ AddField(CreateField(new Decimal32Type(9, 2), i));
+ AddField(CreateField(new Decimal64Type(10, 4), i));
AddField(CreateField(new Decimal128Type(10, 6), i));
AddField(CreateField(new Decimal256Type(16, 8), i));
AddField(CreateField(new MapType(StringType.Default,
Int32Type.Default), i));
@@ -154,6 +156,8 @@ namespace Apache.Arrow.Tests
IArrowTypeVisitor<FixedSizeListType>,
IArrowTypeVisitor<StructType>,
IArrowTypeVisitor<UnionType>,
+ IArrowTypeVisitor<Decimal32Type>,
+ IArrowTypeVisitor<Decimal64Type>,
IArrowTypeVisitor<Decimal128Type>,
IArrowTypeVisitor<Decimal256Type>,
IArrowTypeVisitor<DictionaryType>,
@@ -190,6 +194,31 @@ namespace Apache.Arrow.Tests
public void Visit(HalfFloatType type) => GenerateArray(new
HalfFloatArray.Builder(), x => ((Half)x / (Half)Length));
#endif
public void Visit(DoubleType type) => GenerateArray(new
DoubleArray.Builder(), x => ((double)x / Length));
+
+ public void Visit(Decimal32Type type)
+ {
+ var builder = new Decimal32Array.Builder(type).Reserve(Length);
+
+ for (var i = 0; i < Length; i++)
+ {
+ builder.Append((decimal)i / Length);
+ }
+
+ Array = builder.Build();
+ }
+
+ public void Visit(Decimal64Type type)
+ {
+ var builder = new Decimal64Array.Builder(type).Reserve(Length);
+
+ for (var i = 0; i < Length; i++)
+ {
+ builder.Append((decimal)i / Length);
+ }
+
+ Array = builder.Build();
+ }
+
public void Visit(Decimal128Type type)
{
var builder = new
Decimal128Array.Builder(type).Reserve(Length);
diff --git a/dev/archery/archery/integration/datagen.py
b/dev/archery/archery/integration/datagen.py
index d7f88083f4..9f86d172dd 100644
--- a/dev/archery/archery/integration/datagen.py
+++ b/dev/archery/archery/integration/datagen.py
@@ -1906,7 +1906,6 @@ def get_generated_json_files(tempdir=None):
.skip_tester('JS'),
generate_decimal32_case()
- .skip_tester('C#')
.skip_tester('Java')
.skip_tester('JS')
.skip_tester('nanoarrow')
@@ -1914,7 +1913,6 @@ def get_generated_json_files(tempdir=None):
.skip_tester('Go'),
generate_decimal64_case()
- .skip_tester('C#')
.skip_tester('Java')
.skip_tester('JS')
.skip_tester('nanoarrow')
diff --git a/docs/source/status.rst b/docs/source/status.rst
index 7deb3f512c..c838604fca 100644
--- a/docs/source/status.rst
+++ b/docs/source/status.rst
@@ -44,9 +44,9 @@ Data Types
+-------------------+-------+-------+-------+----+-------+-------+-------+-------+-----------+
| Float32/64 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓
| ✓ |
+-------------------+-------+-------+-------+----+-------+-------+-------+-------+-----------+
-| Decimal32 | ✓ | | ✓ | | | | |
| |
+| Decimal32 | ✓ | | ✓ | | ✓ | | |
| |
+-------------------+-------+-------+-------+----+-------+-------+-------+-------+-----------+
-| Decimal64 | ✓ | | ✓ | | | | |
| |
+| Decimal64 | ✓ | | ✓ | | ✓ | | |
| |
+-------------------+-------+-------+-------+----+-------+-------+-------+-------+-----------+
| Decimal128 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| ✓ |
+-------------------+-------+-------+-------+----+-------+-------+-------+-------+-----------+