From 8dade5b7e916a0df75b6c5abaf5493bafc4488f1 Mon Sep 17 00:00:00 2001 From: Curt Hagenlocher Date: Tue, 18 Feb 2025 11:31:10 -0800 Subject: [PATCH] feat(csharp/src/Apache.Arrow.Adbc): improved performance of ValueAt helper and AdbcDataReader (#2534) I noticed that the ValueAt helper had compiled into some very inefficient IL, so I decided to improve its performance by replacing with a simpler switch statement. I then realized that in AdbcDataReader, we don't need to do the type check on every row because it's the same underlying Arrow type for the entire column -- so it's faster to get a delegate once and keep using the same delegate for the entire data set. Benchmarking came up with these results when testing through AdbcDataReader.GetValues: | Method | Mean | Error | StdDev | |------------ |--------:|---------:|---------:| | Original | 6.263 s | 0.0575 s | 0.0538 s | | New ValueAt | 5.400 s | 0.0424 s | 0.0397 s | | Getter | 4.650 s | 0.0281 s | 0.0249 s | --- csharp/Apache.Arrow.Adbc.sln | 7 + csharp/Benchmarks/Benchmarks.csproj | 28 ++ csharp/Benchmarks/Program.cs | 80 ++++++ .../Extensions/IArrowArrayExtensions.cs | 246 ++++++++++++++---- csharp/src/Client/AdbcDataReader.cs | 55 ++-- csharp/src/Client/SchemaConverter.cs | 10 + 6 files changed, 343 insertions(+), 83 deletions(-) create mode 100644 csharp/Benchmarks/Benchmarks.csproj create mode 100644 csharp/Benchmarks/Program.cs diff --git a/csharp/Apache.Arrow.Adbc.sln b/csharp/Apache.Arrow.Adbc.sln index cb68e885a8..baeed931c4 100644 --- a/csharp/Apache.Arrow.Adbc.sln +++ b/csharp/Apache.Arrow.Adbc.sln @@ -36,6 +36,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Apache.Arrow.Adbc.Drivers.F EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Apache.Arrow.Adbc.Tests.Drivers.FlightSql", "test\Drivers\FlightSql\Apache.Arrow.Adbc.Tests.Drivers.FlightSql.csproj", "{5B27FB02-D4AE-4ACB-AD88-5E64EEB61729}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmarks", "Benchmarks\Benchmarks.csproj", "{BAF2CF14-BA77-429E-AF54-A34B978E9F5C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -94,6 +96,10 @@ Global {5B27FB02-D4AE-4ACB-AD88-5E64EEB61729}.Debug|Any CPU.Build.0 = Debug|Any CPU {5B27FB02-D4AE-4ACB-AD88-5E64EEB61729}.Release|Any CPU.ActiveCfg = Release|Any CPU {5B27FB02-D4AE-4ACB-AD88-5E64EEB61729}.Release|Any CPU.Build.0 = Release|Any CPU + {BAF2CF14-BA77-429E-AF54-A34B978E9F5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAF2CF14-BA77-429E-AF54-A34B978E9F5C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAF2CF14-BA77-429E-AF54-A34B978E9F5C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAF2CF14-BA77-429E-AF54-A34B978E9F5C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -112,6 +118,7 @@ Global {C5503227-C5A7-406F-83AA-681F292EA61F} = {C7290227-E925-47E7-8B6B-A8B171645D58} {77D5A92F-4136-4DE7-81F4-43B981223280} = {FEB257A0-4FD3-495E-9A47-9E1649755445} {5B27FB02-D4AE-4ACB-AD88-5E64EEB61729} = {C7290227-E925-47E7-8B6B-A8B171645D58} + {BAF2CF14-BA77-429E-AF54-A34B978E9F5C} = {5BD04C26-CE52-4893-8C1A-479705195CEF} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {4795CF16-0FDB-4BE0-9768-5CF31564DC03} diff --git a/csharp/Benchmarks/Benchmarks.csproj b/csharp/Benchmarks/Benchmarks.csproj new file mode 100644 index 0000000000..9782bf7223 --- /dev/null +++ b/csharp/Benchmarks/Benchmarks.csproj @@ -0,0 +1,28 @@ + + + + Exe + net8.0 + enable + enable + $([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture.ToString().ToLowerInvariant()) + + + + + + + + + + + + + + + + + + + + diff --git a/csharp/Benchmarks/Program.cs b/csharp/Benchmarks/Program.cs new file mode 100644 index 0000000000..357285e0e9 --- /dev/null +++ b/csharp/Benchmarks/Program.cs @@ -0,0 +1,80 @@ +/* +* 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 Apache.Arrow.Adbc.Tests; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +namespace Apache.Arrow.Adbc.Benchmarks +{ + public class ClientBenchmark + { + public static DuckDbFixture? DuckDb; + public static Client.AdbcConnection? Connection; + + static ClientBenchmark() + { + DuckDb = new DuckDbFixture(); + + using (var database = DuckDb.OpenDatabase("test.db")) + { + using var connection = database.Connect(null); + using var statement = connection.CreateStatement(); + + statement.SqlQuery = "INSTALL tpch"; + statement.ExecuteUpdate(); + + statement.SqlQuery = "LOAD tpch"; + statement.ExecuteUpdate(); + + statement.SqlQuery = "CALL dbgen(sf = 1)"; + statement.ExecuteUpdate(); + } + + Connection = DuckDb.CreateConnection("test.db", null); + } + + [Benchmark] + public void Test() + { + using var command = Connection!.CreateCommand(); + command.CommandText = "SELECT * FROM lineitem"; + using var result = command.ExecuteReader(); + object[] row = new object[result.FieldCount]; + while (result.Read()) + { + result.GetValues(row); + } + } + } + + public class Program + { + static void Main(string[] args) + { + try + { + BenchmarkRunner.Run(typeof(Program).Assembly); + } + finally + { + ClientBenchmark.Connection?.Dispose(); + ClientBenchmark.DuckDb?.Dispose(); + } + } + } +} diff --git a/csharp/src/Apache.Arrow.Adbc/Extensions/IArrowArrayExtensions.cs b/csharp/src/Apache.Arrow.Adbc/Extensions/IArrowArrayExtensions.cs index bcde4045f7..1ce24e35da 100644 --- a/csharp/src/Apache.Arrow.Adbc/Extensions/IArrowArrayExtensions.cs +++ b/csharp/src/Apache.Arrow.Adbc/Extensions/IArrowArrayExtensions.cs @@ -41,43 +41,50 @@ public static class IArrowArrayExtensions if (arrowArray == null) throw new ArgumentNullException(nameof(arrowArray)); if (index < 0) throw new ArgumentOutOfRangeException(nameof(index)); - switch (arrowArray) + switch (arrowArray.Data.DataType.TypeId) { - case BooleanArray booleanArray: - return booleanArray.GetValue(index); - case Date32Array date32Array: - return date32Array.GetDateTime(index); - case Date64Array date64Array: - return date64Array.GetDateTime(index); - case Decimal128Array decimal128Array: - return decimal128Array.GetSqlDecimal(index); - case Decimal256Array decimal256Array: - return decimal256Array.GetString(index); - case DoubleArray doubleArray: - return doubleArray.GetValue(index); - case FloatArray floatArray: - return floatArray.GetValue(index); + case ArrowTypeId.Null: + return null; + case ArrowTypeId.Boolean: + return ((BooleanArray)arrowArray).GetValue(index); + case ArrowTypeId.Date32: + return ((Date32Array)arrowArray).GetDateTime(index); + case ArrowTypeId.Date64: + return ((Date64Array)arrowArray).GetDateTime(index); + case ArrowTypeId.Decimal32: + return ((Decimal32Array)arrowArray).GetDecimal(index); + case ArrowTypeId.Decimal64: + return ((Decimal64Array)arrowArray).GetDecimal(index); + case ArrowTypeId.Decimal128: + return ((Decimal128Array)arrowArray).GetSqlDecimal(index); + case ArrowTypeId.Decimal256: + return ((Decimal256Array)arrowArray).GetString(index); + case ArrowTypeId.Double: + return ((DoubleArray)arrowArray).GetValue(index); + case ArrowTypeId.Float: + return ((FloatArray)arrowArray).GetValue(index); #if NET5_0_OR_GREATER - case PrimitiveArray halfFloatArray: - return halfFloatArray.GetValue(index); + case ArrowTypeId.HalfFloat: + return ((HalfFloatArray)arrowArray).GetValue(index); #endif - case Int8Array int8Array: - return int8Array.GetValue(index); - case Int16Array int16Array: - return int16Array.GetValue(index); - case Int32Array int32Array: - return int32Array.GetValue(index); - case Int64Array int64Array: - return int64Array.GetValue(index); - case StringArray stringArray: - return stringArray.GetString(index); + case ArrowTypeId.Int8: + return ((Int8Array)arrowArray).GetValue(index); + case ArrowTypeId.Int16: + return ((Int16Array)arrowArray).GetValue(index); + case ArrowTypeId.Int32: + return ((Int32Array)arrowArray).GetValue(index); + case ArrowTypeId.Int64: + return ((Int64Array)arrowArray).GetValue(index); + case ArrowTypeId.String: + return ((StringArray)arrowArray).GetString(index); #if NET6_0_OR_GREATER - case Time32Array time32Array: - return time32Array.GetTime(index); - case Time64Array time64Array: - return time64Array.GetTime(index); + case ArrowTypeId.Time32: + return ((Time32Array)arrowArray).GetTime(index); + case ArrowTypeId.Time64: + return ((Time64Array)arrowArray).GetTime(index); #else - case Time32Array time32Array: + case ArrowTypeId.Time32: + Time32Array time32Array = (Time32Array)arrowArray; int? time32 = time32Array.GetValue(index); if (time32 == null) { return null; } return ((Time32Type)time32Array.Data.DataType).Unit switch @@ -86,7 +93,8 @@ public static class IArrowArrayExtensions TimeUnit.Millisecond => TimeSpan.FromMilliseconds(time32.Value), _ => throw new InvalidDataException("Unsupported time unit for Time32Type") }; - case Time64Array time64Array: + case ArrowTypeId.Time64: + Time64Array time64Array = (Time64Array)arrowArray; long? time64 = time64Array.GetValue(index); if (time64 == null) { return null; } return ((Time64Type)time64Array.Data.DataType).Unit switch @@ -96,35 +104,159 @@ public static class IArrowArrayExtensions _ => throw new InvalidDataException("Unsupported time unit for Time64Type") }; #endif - case TimestampArray timestampArray: - return timestampArray.GetTimestamp(index); - case UInt8Array uInt8Array: - return uInt8Array.GetValue(index); - case UInt16Array uInt16Array: - return uInt16Array.GetValue(index); - case UInt32Array uInt32Array: - return uInt32Array.GetValue(index); - case UInt64Array uInt64Array: - return uInt64Array.GetValue(index); - case DayTimeIntervalArray dayTimeIntervalArray: - return dayTimeIntervalArray.GetValue(index); - case MonthDayNanosecondIntervalArray monthDayNanosecondIntervalArray: - return monthDayNanosecondIntervalArray.GetValue(index); - case YearMonthIntervalArray yearMonthIntervalArray: - return yearMonthIntervalArray.GetValue(index); - case BinaryArray binaryArray: - if (!binaryArray.IsNull(index)) + case ArrowTypeId.Timestamp: + return ((TimestampArray)arrowArray).GetTimestamp(index); + case ArrowTypeId.UInt8: + return ((UInt8Array)arrowArray).GetValue(index); + case ArrowTypeId.UInt16: + return ((UInt16Array)arrowArray).GetValue(index); + case ArrowTypeId.UInt32: + return ((UInt32Array)arrowArray).GetValue(index); + case ArrowTypeId.UInt64: + return ((UInt64Array)arrowArray).GetValue(index); + case ArrowTypeId.Interval: + switch (((IntervalType)arrowArray.Data.DataType).Unit) { - return binaryArray.GetBytes(index).ToArray(); + case IntervalUnit.DayTime: + return ((DayTimeIntervalArray)arrowArray).GetValue(index); + case IntervalUnit.MonthDayNanosecond: + return ((MonthDayNanosecondIntervalArray)arrowArray).GetValue(index); + case IntervalUnit.YearMonth: + return ((YearMonthIntervalArray)arrowArray).GetValue(index); + default: + throw new NotSupportedException($"Unsupported interval unit: {((IntervalType)arrowArray.Data.DataType).Unit}"); + } + case ArrowTypeId.Binary: + if (!arrowArray.IsNull(index)) + { + return ((BinaryArray)arrowArray).GetBytes(index).ToArray(); } else { return null; } - case ListArray listArray: - return listArray.GetSlicedValues(index); - case StructArray structArray: - return SerializeToJson(structArray, index); + case ArrowTypeId.List: + return ((ListArray)arrowArray).GetSlicedValues(index); + case ArrowTypeId.Struct: + return SerializeToJson(((StructArray)arrowArray), index); + + // not covered: + // -- map array + // -- dictionary array + // -- fixed size binary + // -- union array + } + + return null; + } + + /// + /// Helper extension to get a value from the at the specified index. + /// + /// + /// The Arrow array. + /// + /// + /// The index in the array to get the value from. + /// + public static Func GetValueConverter(this IArrowType arrayType) + { + if (arrayType == null) throw new ArgumentNullException(nameof(arrayType)); + + switch (arrayType.TypeId) + { + case ArrowTypeId.Null: + return (array, index) => null; + case ArrowTypeId.Boolean: + return (array, index) => ((BooleanArray)array).GetValue(index); + case ArrowTypeId.Date32: + return (array, index) => ((Date32Array)array).GetDateTime(index); + case ArrowTypeId.Date64: + return (array, index) => ((Date64Array)array).GetDateTime(index); + case ArrowTypeId.Decimal32: + return (array, index) => ((Decimal32Array)array).GetDecimal(index); + case ArrowTypeId.Decimal64: + return (array, index) => ((Decimal64Array)array).GetDecimal(index); + case ArrowTypeId.Decimal128: + return (array, index) => ((Decimal128Array)array).GetSqlDecimal(index); + case ArrowTypeId.Decimal256: + return (array, index) => ((Decimal256Array)array).GetString(index); + case ArrowTypeId.Double: + return (array, index) => ((DoubleArray)array).GetValue(index); + case ArrowTypeId.Float: + return (array, index) => ((FloatArray)array).GetValue(index); +#if NET5_0_OR_GREATER + case ArrowTypeId.HalfFloat: + return (array, index) => ((HalfFloatArray)array).GetValue(index); +#endif + case ArrowTypeId.Int8: + return (array, index) => ((Int8Array)array).GetValue(index); + case ArrowTypeId.Int16: + return (array, index) => ((Int16Array)array).GetValue(index); + case ArrowTypeId.Int32: + return (array, index) => ((Int32Array)array).GetValue(index); + case ArrowTypeId.Int64: + return (array, index) => ((Int64Array)array).GetValue(index); + case ArrowTypeId.String: + return (array, index) => ((StringArray)array).GetString(index); +#if NET6_0_OR_GREATER + case ArrowTypeId.Time32: + return (array, index) => ((Time32Array)array).GetTime(index); + case ArrowTypeId.Time64: + return (array, index) => ((Time64Array)array).GetTime(index); +#else + case ArrowTypeId.Time32: + Time32Type time32Type = (Time32Type)arrayType; + switch (time32Type.Unit) + { + case TimeUnit.Second: + return (array, index) => array.IsNull(index) ? null : TimeSpan.FromSeconds(((Time32Array)array).GetValue(index)!.Value); + case TimeUnit.Millisecond: + return (array, index) => array.IsNull(index) ? null : TimeSpan.FromMilliseconds(((Time32Array)array).GetValue(index)!.Value); + default: + throw new InvalidDataException("Unsupported time unit for Time32Type"); + } + case ArrowTypeId.Time64: + Time64Type time64Type = (Time64Type)arrayType; + switch (time64Type.Unit) + { + case TimeUnit.Microsecond: + return (array, index) => array.IsNull(index) ? null : TimeSpan.FromTicks(((Time64Array)array).GetValue(index)!.Value * 10); + case TimeUnit.Nanosecond: + return (array, index) => array.IsNull(index) ? null : TimeSpan.FromTicks(((Time64Array)array).GetValue(index)!.Value / 100); + default: + throw new InvalidDataException("Unsupported time unit for Time64Type"); + } +#endif + case ArrowTypeId.Timestamp: + return (array, index) => ((TimestampArray)array).GetTimestamp(index); + case ArrowTypeId.UInt8: + return (array, index) => ((UInt8Array)array).GetValue(index); + case ArrowTypeId.UInt16: + return (array, index) => ((UInt16Array)array).GetValue(index); + case ArrowTypeId.UInt32: + return (array, index) => ((UInt32Array)array).GetValue(index); + case ArrowTypeId.UInt64: + return (array, index) => ((UInt64Array)array).GetValue(index); + case ArrowTypeId.Interval: + IntervalType intervalType = (IntervalType)arrayType; + switch (intervalType.Unit) + { + case IntervalUnit.DayTime: + return (array, index) => ((DayTimeIntervalArray)array).GetValue(index); + case IntervalUnit.MonthDayNanosecond: + return (array, index) => ((MonthDayNanosecondIntervalArray)array).GetValue(index); + case IntervalUnit.YearMonth: + return (array, index) => ((YearMonthIntervalArray)array).GetValue(index); + default: + throw new NotSupportedException($"Unsupported interval unit: {intervalType.Unit}"); + } + case ArrowTypeId.Binary: + return (array, index) => array.IsNull(index) ? null : ((BinaryArray)array).GetBytes(index).ToArray(); + case ArrowTypeId.List: + return (array, index) => ((ListArray)array).GetSlicedValues(index); + case ArrowTypeId.Struct: + return (array, index) => SerializeToJson((StructArray)array, index); // not covered: // -- map array @@ -133,7 +265,7 @@ public static class IArrowArrayExtensions // -- union array } - return null; + throw new NotSupportedException($"Unsupported ArrowTypeId: {arrayType.TypeId}"); } /// diff --git a/csharp/src/Client/AdbcDataReader.cs b/csharp/src/Client/AdbcDataReader.cs index 219e25e90a..17c6656692 100644 --- a/csharp/src/Client/AdbcDataReader.cs +++ b/csharp/src/Client/AdbcDataReader.cs @@ -50,8 +50,10 @@ public sealed class AdbcDataReader : DbDataReader, IDbColumnSchemaGenerator private RecordBatch? recordBatch; private int currentRowInRecordBatch; private readonly Schema schema; + private readonly Func[] converters; private bool isClosed; private int recordsAffected = -1; + private Dictionary? columnNameToIndex; /// /// An event that is raised when a value is read from an IArrowArray. @@ -83,11 +85,17 @@ internal AdbcDataReader(AdbcCommand adbcCommand, QueryResult adbcQueryResult, De this.isClosed = false; this.DecimalBehavior = decimalBehavior; this.StructBehavior = structBehavior; + + this.converters = new Func[this.schema.FieldsList.Count]; + for (int i = 0; i < this.converters.Length; i++) + { + this.converters[i] = this.schema.FieldsList[i].DataType.GetValueConverter(); + } } public override object this[int ordinal] => GetValue(ordinal); - public override object this[string name] => GetValue(this.RecordBatch.Column(name)) ?? DBNull.Value; + public override object this[string name] => GetValue(GetOrdinal(name)); public override int Depth => 0; @@ -165,7 +173,7 @@ public override string GetDataTypeName(int ordinal) public override DateTime GetDateTime(int ordinal) { - return (DateTime) GetValue(ordinal); + return (DateTime)GetValue(ordinal); } public override decimal GetDecimal(int ordinal) @@ -210,12 +218,12 @@ public IArrowType GetFieldArrowType(int ordinal) public override float GetFloat(int ordinal) { - return (float) GetValue(ordinal); + return (float)GetValue(ordinal); } public override Guid GetGuid(int ordinal) { - return (Guid) GetValue(ordinal); + return (Guid)GetValue(ordinal); } public override short GetInt16(int ordinal) @@ -235,12 +243,20 @@ public override long GetInt64(int ordinal) public override string GetName(int ordinal) { - return this.schema.GetFieldByIndex(ordinal)?.Name ?? string.Empty; + return this.schema.GetFieldByIndex(ordinal)?.Name ?? string.Empty; } public override int GetOrdinal(string name) { - return this.schema.GetFieldIndex(name); + if (this.columnNameToIndex == null) + { + this.columnNameToIndex = new Dictionary(this.schema.FieldsList.Count); + for (int i = 0; i < this.schema.FieldsList.Count; i++) + { + this.columnNameToIndex[this.schema.FieldsList[i].Name] = i; + } + } + return this.columnNameToIndex[name]; } public override string GetString(int ordinal) @@ -250,10 +266,13 @@ public override string GetString(int ordinal) public override object GetValue(int ordinal) { - object? value = GetValue(this.RecordBatch.Column(ordinal)); + IArrowArray arrowArray = this.RecordBatch.Column(ordinal); - if (value == null) - return DBNull.Value; + // if the OnGetValue event is set, call it + object? value = OnGetValue?.Invoke(arrowArray, this.currentRowInRecordBatch); + + // if the value is null, try to get the value from the ArrowArray + value = value ?? this.converters[ordinal](arrowArray, this.currentRowInRecordBatch) ?? DBNull.Value; if (value is SqlDecimal dValue && this.DecimalBehavior == DecimalBehavior.OverflowDecimalAsString) { @@ -261,7 +280,7 @@ public override object GetValue(int ordinal) { return dValue.Value; } - catch(OverflowException) + catch (OverflowException) { return dValue.ToString(); } @@ -360,22 +379,6 @@ public ReadOnlyCollection GetAdbcColumnSchema() return dbColumns.AsReadOnly(); } - /// - /// Gets the value from an IArrowArray at the current row index. - /// - /// - /// - private object? GetValue(IArrowArray arrowArray) - { - // if the OnGetValue event is set, call it - object? result = OnGetValue?.Invoke(arrowArray, this.currentRowInRecordBatch); - - // if the value is null, try to get the value from the ArrowArray - result = result ?? arrowArray.ValueAt(this.currentRowInRecordBatch); - - return result; - } - /// /// Retrieves the next record batch. /// diff --git a/csharp/src/Client/SchemaConverter.cs b/csharp/src/Client/SchemaConverter.cs index 14e1058c0a..dae2dbf3c8 100644 --- a/csharp/src/Client/SchemaConverter.cs +++ b/csharp/src/Client/SchemaConverter.cs @@ -83,6 +83,16 @@ public static DataTable ConvertArrowSchema(Schema schema, AdbcStatement adbcStat row[SchemaTableColumn.NumericScale] = Convert.ToInt32(scaleValue); } } + else if (f.DataType is Decimal32Type decimal32Type) + { + row[SchemaTableColumn.NumericPrecision] = decimal32Type.Precision; + row[SchemaTableColumn.NumericScale] = decimal32Type.Scale; + } + else if (f.DataType is Decimal128Type decimal64Type) + { + row[SchemaTableColumn.NumericPrecision] = decimal64Type.Precision; + row[SchemaTableColumn.NumericScale] = decimal64Type.Scale; + } else if (f.DataType is Decimal128Type decimal128Type) { row[SchemaTableColumn.NumericPrecision] = decimal128Type.Precision;