You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by pt...@apache.org on 2022/09/29 09:11:09 UTC
[ignite-3] branch main updated: IGNITE-15431 .NET: Add support for all native data types (#1132)
This is an automated email from the ASF dual-hosted git repository.
ptupitsyn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new 2240ca187a IGNITE-15431 .NET: Add support for all native data types (#1132)
2240ca187a is described below
commit 2240ca187a93b774183f8b43378f1eb826b69781
Author: Pavel Tupitsyn <pt...@apache.org>
AuthorDate: Thu Sep 29 12:11:04 2022 +0300
IGNITE-15431 .NET: Add support for all native data types (#1132)
* Add support for `BitArray`, `decimal`, `BigInteger`, `LocalDate`, `LocalTime`, `LocalDateTime`, `Instant` data types.
* Use NodaTime library for temporal types (`LocalDate`, `LocalTime`, `LocalDateTime`, `Instant`), since built-in .NET types do not map correctly to the native types (see [IEP-54: Schema-first Approach - Type Mapping](https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=199540072#IEP54:SchemafirstApproach-Typemapping)).
---
.../internal/binarytuple/BinaryTupleBuilder.java | 5 +-
.../internal/binarytuple/BinaryTupleParser.java | 5 +
.../internal/client/proto/ClientDataType.java | 5 +-
.../internal/client/proto/ClientMessagePacker.java | 5 +-
.../client/proto/ClientMessageUnpacker.java | 4 -
.../Serialization/ObjectSerializerHandlerOld.cs | 142 -------------
.../SerializerHandlerBenchmarksBase.cs | 8 +-
.../SerializerHandlerWriteBenchmarks.cs | 12 --
.../Apache.Ignite.Tests/Compute/ComputeTests.cs | 2 +-
.../dotnet/Apache.Ignite.Tests/IgniteTestsBase.cs | 2 +
.../Proto/BinaryTuple/BinaryTupleTests.cs | 182 ++++++++++++++++
.../Apache.Ignite.Tests/Table/PocoAllColumns.cs | 49 +++++
.../Table/RecordViewBinaryTests.cs | 50 +++++
.../Table/RecordViewPocoTests.cs | 64 ++++++
.../Serialization/ObjectSerializerHandlerTests.cs | 4 +-
.../Apache.Ignite.Tests/Table/TablesTests.cs | 8 +-
.../dotnet/Apache.Ignite/Apache.Ignite.csproj | 4 +-
.../Proto/BinaryTuple/BinaryTupleBuilder.cs | 232 ++++++++++++++++++++-
.../Proto/BinaryTuple/BinaryTupleReader.cs | 180 +++++++++++++++-
.../Apache.Ignite/Internal/Proto/ClientDataType.cs | 59 +++---
.../Internal/Proto/ClientDataTypeExtensions.cs | 38 ++--
.../Internal/Proto/MessagePackWriterExtensions.cs | 72 +------
.../dotnet/Apache.Ignite/Internal/Sql/ResultSet.cs | 2 +-
.../dotnet/Apache.Ignite/Internal/Table/Column.cs | 2 +-
.../Table/Serialization/BinaryTupleMethods.cs | 47 ++++-
.../Table/Serialization/ObjectSerializerHandler.cs | 22 +-
.../Table/Serialization/TupleSerializerHandler.cs | 6 +-
.../dotnet/Apache.Ignite/Internal/Table/Table.cs | 9 +-
.../runner/app/PlatformTestNodeRunner.java | 55 ++++-
.../runner/app/client/ItThinClientComputeTest.java | 1 -
30 files changed, 933 insertions(+), 343 deletions(-)
diff --git a/modules/binary-tuple/src/main/java/org/apache/ignite/internal/binarytuple/BinaryTupleBuilder.java b/modules/binary-tuple/src/main/java/org/apache/ignite/internal/binarytuple/BinaryTupleBuilder.java
index 965cef3f55..ffecd0e8d3 100644
--- a/modules/binary-tuple/src/main/java/org/apache/ignite/internal/binarytuple/BinaryTupleBuilder.java
+++ b/modules/binary-tuple/src/main/java/org/apache/ignite/internal/binarytuple/BinaryTupleBuilder.java
@@ -296,7 +296,10 @@ public class BinaryTupleBuilder {
* @return {@code this} for chaining.
*/
public BinaryTupleBuilder appendNumberNotNull(BigInteger value) {
- putBytes(value.toByteArray());
+ if (!value.equals(BigInteger.ZERO)) {
+ putBytes(value.toByteArray());
+ }
+
return proceed();
}
diff --git a/modules/binary-tuple/src/main/java/org/apache/ignite/internal/binarytuple/BinaryTupleParser.java b/modules/binary-tuple/src/main/java/org/apache/ignite/internal/binarytuple/BinaryTupleParser.java
index 47e8e4ca87..7665977c59 100644
--- a/modules/binary-tuple/src/main/java/org/apache/ignite/internal/binarytuple/BinaryTupleParser.java
+++ b/modules/binary-tuple/src/main/java/org/apache/ignite/internal/binarytuple/BinaryTupleParser.java
@@ -318,6 +318,11 @@ public class BinaryTupleParser {
public final BigInteger numberValue(int begin, int end) {
byte[] bytes;
int len = end - begin;
+
+ if (len == 0) {
+ return BigInteger.ZERO;
+ }
+
if (buffer.hasArray()) {
bytes = buffer.array();
begin += buffer.arrayOffset();
diff --git a/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientDataType.java b/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientDataType.java
index f07be8d37e..2d151c3489 100644
--- a/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientDataType.java
+++ b/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientDataType.java
@@ -69,9 +69,6 @@ public class ClientDataType {
/** Number. */
public static final int NUMBER = 16;
- /** Boolean. */
- public static final int BOOLEAN = 17;
-
/** Big Integer. */
- public static final int BIGINTEGER = 18;
+ public static final int BIGINTEGER = 17;
}
diff --git a/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientMessagePacker.java b/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientMessagePacker.java
index 2178c3dcdc..fa96c779cf 100644
--- a/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientMessagePacker.java
+++ b/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientMessagePacker.java
@@ -814,10 +814,7 @@ public class ClientMessagePacker implements AutoCloseable {
Class<?> cls = obj.getClass();
- if (cls == Boolean.class) {
- packInt(ClientDataType.BOOLEAN);
- packBoolean((Boolean) obj);
- } else if (cls == Byte.class) {
+ if (cls == Byte.class) {
packInt(ClientDataType.INT8);
packByte((Byte) obj);
} else if (cls == Short.class) {
diff --git a/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientMessageUnpacker.java b/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientMessageUnpacker.java
index e983fcf54a..8944b7c31b 100644
--- a/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientMessageUnpacker.java
+++ b/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientMessageUnpacker.java
@@ -19,7 +19,6 @@ package org.apache.ignite.internal.client.proto;
import static org.apache.ignite.internal.client.proto.ClientDataType.BIGINTEGER;
import static org.apache.ignite.internal.client.proto.ClientDataType.BITMASK;
-import static org.apache.ignite.internal.client.proto.ClientDataType.BOOLEAN;
import static org.apache.ignite.internal.client.proto.ClientDataType.BYTES;
import static org.apache.ignite.internal.client.proto.ClientDataType.DATE;
import static org.apache.ignite.internal.client.proto.ClientDataType.DATETIME;
@@ -1025,9 +1024,6 @@ public class ClientMessageUnpacker implements AutoCloseable {
}
switch (dataType) {
- case BOOLEAN:
- return unpackBoolean();
-
case INT8:
return unpackByte();
diff --git a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/ObjectSerializerHandlerOld.cs b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/ObjectSerializerHandlerOld.cs
deleted file mode 100644
index 7d7059b766..0000000000
--- a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/ObjectSerializerHandlerOld.cs
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * 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.Ignite.Benchmarks.Table.Serialization
-{
- using System;
- using System.Reflection;
- using Internal.Proto;
- using Internal.Table;
- using Internal.Table.Serialization;
- using MessagePack;
-
- /// <summary>
- /// Old object serializer handler implementation as a baseline for benchmarks.
- /// </summary>
- /// <typeparam name="T">Object type.</typeparam>
- internal class ObjectSerializerHandlerOld<T> : IRecordSerializerHandler<T>
- where T : class
- {
- /// <inheritdoc/>
- public T Read(ref MessagePackReader reader, Schema schema, bool keyOnly = false)
- {
- var columns = schema.Columns;
- var count = keyOnly ? schema.KeyColumnCount : columns.Count;
- var res = Activator.CreateInstance<T>();
- var type = typeof(T);
-
- for (var index = 0; index < count; index++)
- {
- if (reader.TryReadNoValue())
- {
- continue;
- }
-
- var col = columns[index];
- var prop = GetPropertyIgnoreCase(type, col.Name);
-
- if (prop != null)
- {
- var value = reader.ReadObject(col.Type);
- prop.SetValue(res, value);
- }
- else
- {
- reader.Skip();
- }
- }
-
- return (T)(object)res;
- }
-
- /// <inheritdoc/>
- public T ReadValuePart(ref MessagePackReader reader, Schema schema, T key)
- {
- var columns = schema.Columns;
- var res = Activator.CreateInstance<T>();
- var type = typeof(T);
-
- for (var i = 0; i < columns.Count; i++)
- {
- var col = columns[i];
- var prop = GetPropertyIgnoreCase(type, col.Name);
-
- if (i < schema.KeyColumnCount)
- {
- if (prop != null)
- {
- prop.SetValue(res, prop.GetValue(key));
- }
- }
- else
- {
- if (reader.TryReadNoValue())
- {
- continue;
- }
-
- if (prop != null)
- {
- prop.SetValue(res, reader.ReadObject(col.Type));
- }
- else
- {
- reader.Skip();
- }
- }
- }
-
- return res;
- }
-
- /// <inheritdoc/>
- public void Write(ref MessagePackWriter writer, Schema schema, T record, bool keyOnly = false)
- {
- var columns = schema.Columns;
- var count = keyOnly ? schema.KeyColumnCount : columns.Count;
- var type = record.GetType();
-
- for (var index = 0; index < count; index++)
- {
- var col = columns[index];
- var prop = GetPropertyIgnoreCase(type, col.Name);
-
- if (prop == null)
- {
- writer.WriteNoValue();
- }
- else
- {
- writer.WriteObject(prop.GetValue(record));
- }
- }
- }
-
- private static PropertyInfo? GetPropertyIgnoreCase(Type type, string name)
- {
- foreach (var p in type.GetProperties())
- {
- if (p.Name.Equals(name, StringComparison.OrdinalIgnoreCase))
- {
- return p;
- }
- }
-
- return null;
- }
- }
-}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerBenchmarksBase.cs b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerBenchmarksBase.cs
index a675657f61..9333ddcc7d 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerBenchmarksBase.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerBenchmarksBase.cs
@@ -46,17 +46,15 @@ namespace Apache.Ignite.Benchmarks.Table.Serialization
internal static readonly Schema Schema = new(1, 1, new[]
{
- new Column(nameof(Car.Id), ClientDataType.Uuid, Nullable: false, IsKey: true, SchemaIndex: 0),
- new Column(nameof(Car.BodyType), ClientDataType.String, Nullable: false, IsKey: false, SchemaIndex: 1),
- new Column(nameof(Car.Seats), ClientDataType.Int32, Nullable: false, IsKey: false, SchemaIndex: 2)
+ new Column(nameof(Car.Id), ClientDataType.Uuid, Nullable: false, IsKey: true, SchemaIndex: 0, Scale: 0),
+ new Column(nameof(Car.BodyType), ClientDataType.String, Nullable: false, IsKey: false, SchemaIndex: 1, Scale: 0),
+ new Column(nameof(Car.Seats), ClientDataType.Int32, Nullable: false, IsKey: false, SchemaIndex: 2, Scale: 0)
});
internal static readonly byte[] SerializedData = GetSerializedData();
internal static readonly ObjectSerializerHandler<Car> ObjectSerializerHandler = new();
- internal static readonly ObjectSerializerHandlerOld<Car> ObjectSerializerHandlerOld = new();
-
protected Consumer Consumer { get; } = new();
internal static void VerifyWritten(PooledArrayBufferWriter pooledWriter)
diff --git a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerWriteBenchmarks.cs b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerWriteBenchmarks.cs
index 57f0698ec8..8b8c010fd2 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerWriteBenchmarks.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerWriteBenchmarks.cs
@@ -79,17 +79,5 @@ namespace Apache.Ignite.Benchmarks.Table.Serialization
writer.Flush();
VerifyWritten(pooledWriter);
}
-
- // [Benchmark]
- public void WriteObjectOld()
- {
- using var pooledWriter = new PooledArrayBufferWriter();
- var writer = pooledWriter.GetMessageWriter();
-
- ObjectSerializerHandlerOld.Write(ref writer, Schema, Object);
-
- writer.Flush();
- VerifyWritten(pooledWriter);
- }
}
}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Compute/ComputeTests.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Compute/ComputeTests.cs
index 2d6d728dc7..4c5a6567cc 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Compute/ComputeTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Compute/ComputeTests.cs
@@ -159,7 +159,7 @@ namespace Apache.Ignite.Tests.Compute
StringAssert.Contains("Specified node is not present in the cluster: y", ex!.Message);
}
- // TODO: Support all types (IGNITE-15431).
+ // TODO IGNITE-17777 Thin 3.0: use BinaryTuple for Compute and SQL results and arguments
[Test]
public async Task TestAllSupportedArgTypes()
{
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/IgniteTestsBase.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/IgniteTestsBase.cs
index 2ed3bad6e0..c1cbc16e03 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/IgniteTestsBase.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/IgniteTestsBase.cs
@@ -31,6 +31,8 @@ namespace Apache.Ignite.Tests
{
protected const string TableName = "PUB.TBL1";
+ protected const string TableAllColumnsName = "PUB.TBL_ALL_COLUMNS";
+
protected const string KeyCol = "key";
protected const string ValCol = "val";
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Proto/BinaryTuple/BinaryTupleTests.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Proto/BinaryTuple/BinaryTupleTests.cs
index ba96516c73..a6d1016cbc 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Proto/BinaryTuple/BinaryTupleTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Proto/BinaryTuple/BinaryTupleTests.cs
@@ -18,7 +18,11 @@
namespace Apache.Ignite.Tests.Proto.BinaryTuple
{
using System;
+ using System.Collections;
+ using System.Linq;
+ using System.Numerics;
using Internal.Proto.BinaryTuple;
+ using NodaTime;
using NUnit.Framework;
/// <summary>
@@ -344,6 +348,184 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
Assert.AreEqual(guid, reader.GetGuid(1));
}
+ [Test]
+ public void TestBytes([Values(0, 1, 123)] int count)
+ {
+ var bytes = Enumerable.Range(1, count).Select(x => (byte)x).ToArray();
+ var reader = BuildAndRead((ref BinaryTupleBuilder b) => b.AppendBytes(bytes));
+ var res = reader.GetBytes(0);
+
+ CollectionAssert.AreEqual(bytes, res);
+ }
+
+ [Test]
+ public void TestBitMask([Values(0, 1, 123)] int count)
+ {
+ var bitMask = new BitArray(count);
+
+ for (var i = 0; i < count; i++)
+ {
+ bitMask.Set(i, i % 2 == 0);
+ }
+
+ var reader = BuildAndRead((ref BinaryTupleBuilder b) => b.AppendBitmask(bitMask));
+ var res = reader.GetBitmask(0);
+
+ Assert.GreaterOrEqual(res.Length, bitMask.Length); // Resulting bitmask may be padded with false bits to the byte boundary.
+
+ for (var i = 0; i < count; i++)
+ {
+ Assert.AreEqual(i % 2 == 0, res.Get(i));
+ }
+ }
+
+ [Test]
+ public void TestBigInteger([Values(0, 15, 123)] long val, [Values(1, 33, 456, 9876)] int exp)
+ {
+ var bigInt = BigInteger.Pow(val, exp);
+
+ var reader = BuildAndRead((ref BinaryTupleBuilder b) => b.AppendNumber(bigInt));
+ var res = reader.GetNumber(0);
+
+ Assert.AreEqual(bigInt, res);
+ }
+
+ [Test]
+ public void TestDecimal()
+ {
+ Test(0, 3);
+ Test(0, 0);
+
+ Test(12345.6789m, 4);
+ Test(12345.678m, 4);
+ Test(12345.67m, 4);
+
+ Test(12345.6789m, 2, 12345.67m);
+ Test(12345.6789m, 0, 12345m);
+
+ static void Test(decimal val, int scale, decimal? expected = null)
+ {
+ var reader = BuildAndRead((ref BinaryTupleBuilder b) => b.AppendDecimal(val, scale));
+ var res = reader.GetDecimal(0, scale);
+
+ Assert.AreEqual(expected ?? val, res);
+ }
+ }
+
+ [Test]
+ public void TestDecimalScaleOverflow()
+ {
+ const int scale = 100;
+
+ var ex = Assert.Throws<OverflowException>(
+ () => BuildAndRead((ref BinaryTupleBuilder b) => b.AppendDecimal(12.34m, scale)).GetDecimal(0, scale));
+
+ Assert.AreEqual("Value was either too large or too small for a Decimal.", ex!.Message);
+ }
+
+ [Test]
+ public void TestDecimalMagnitudeOverflow()
+ {
+ var magnitude = Enumerable.Range(1, 100).Select(_ => (byte)250).ToArray();
+
+ var ex = Assert.Throws<OverflowException>(
+ () => BuildAndRead((ref BinaryTupleBuilder b) => b.AppendBytes(magnitude)).GetDecimal(0, 0));
+
+ Assert.AreEqual("Value was either too large or too small for a Decimal.", ex!.Message);
+ }
+
+ [Test]
+ public void TestDate()
+ {
+ var val = LocalDate.FromDateTime(DateTime.UtcNow);
+
+ var reader = BuildAndRead(
+ (ref BinaryTupleBuilder b) =>
+ {
+ b.AppendDate(default);
+ b.AppendDate(val);
+ b.AppendDate(LocalDate.MaxIsoValue);
+ b.AppendDate(LocalDate.MinIsoValue);
+ },
+ 4);
+
+ Assert.AreEqual(default(LocalDate), reader.GetDate(0));
+ Assert.AreEqual(val, reader.GetDate(1));
+ Assert.AreEqual(LocalDate.MaxIsoValue, reader.GetDate(2));
+ Assert.AreEqual(LocalDate.MinIsoValue, reader.GetDate(3));
+ }
+
+ [Test]
+ public void TestTime()
+ {
+ var val = LocalDateTime.FromDateTime(DateTime.UtcNow).TimeOfDay;
+
+ var reader = BuildAndRead(
+ (ref BinaryTupleBuilder b) =>
+ {
+ b.AppendTime(default);
+ b.AppendTime(val);
+ b.AppendTime(LocalTime.MinValue);
+ b.AppendTime(LocalTime.MaxValue);
+ b.AppendTime(LocalTime.Midnight);
+ b.AppendTime(LocalTime.Noon);
+ },
+ 6);
+
+ Assert.AreEqual(default(LocalTime), reader.GetTime(0));
+ Assert.AreEqual(val, reader.GetTime(1));
+ Assert.AreEqual(LocalTime.MinValue, reader.GetTime(2));
+ Assert.AreEqual(LocalTime.MaxValue, reader.GetTime(3));
+ Assert.AreEqual(LocalTime.Midnight, reader.GetTime(4));
+ Assert.AreEqual(LocalTime.Noon, reader.GetTime(5));
+ }
+
+ [Test]
+ public void TestDateTime()
+ {
+ var val = LocalDateTime.FromDateTime(DateTime.UtcNow);
+
+ var reader = BuildAndRead(
+ (ref BinaryTupleBuilder b) =>
+ {
+ b.AppendDateTime(default);
+ b.AppendDateTime(val);
+ b.AppendDateTime(LocalDateTime.MaxIsoValue);
+ b.AppendDateTime(LocalDateTime.MinIsoValue);
+ },
+ 4);
+
+ Assert.AreEqual(default(LocalDateTime), reader.GetDateTime(0));
+ Assert.AreEqual(val, reader.GetDateTime(1));
+ Assert.AreEqual(LocalDateTime.MaxIsoValue, reader.GetDateTime(2));
+ Assert.AreEqual(LocalDateTime.MinIsoValue, reader.GetDateTime(3));
+ }
+
+ [Test]
+ public void TestTimestamp()
+ {
+ var val = Instant.FromDateTimeUtc(DateTime.UtcNow);
+
+ var reader = BuildAndRead(
+ (ref BinaryTupleBuilder b) =>
+ {
+ b.AppendTimestamp(default);
+ b.AppendTimestamp(val);
+ b.AppendTimestamp(Instant.MaxValue);
+ b.AppendTimestamp(Instant.MinValue);
+ b.AppendTimestamp(NodaConstants.BclEpoch);
+ b.AppendTimestamp(NodaConstants.JulianEpoch);
+ },
+ 6);
+
+ Assert.AreEqual(NodaConstants.UnixEpoch, reader.GetTimestamp(0));
+ Assert.AreEqual(val, reader.GetTimestamp(1));
+ Assert.AreEqual(Instant.MaxValue, reader.GetTimestamp(2));
+ Assert.AreEqual(Instant.MinValue, reader.GetTimestamp(3));
+ Assert.AreEqual(NodaConstants.BclEpoch, reader.GetTimestamp(4));
+ Assert.AreEqual(NodaConstants.JulianEpoch, reader.GetTimestamp(5));
+ }
+
private static BinaryTupleReader BuildAndRead(BinaryTupleBuilderAction build, int numElements = 1)
{
var bytes = Build(build, numElements);
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/PocoAllColumns.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/PocoAllColumns.cs
new file mode 100644
index 0000000000..eb8456d19b
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/PocoAllColumns.cs
@@ -0,0 +1,49 @@
+/*
+ * 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.Ignite.Tests.Table
+{
+ using System;
+ using System.Collections;
+ using System.Diagnostics.CodeAnalysis;
+ using NodaTime;
+
+ /// <summary>
+ /// Test user object.
+ /// </summary>
+ [SuppressMessage("Microsoft.Naming", "CA1720:AvoidTypeNamesInParameters", Justification = "POCO mapping.")]
+ [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "POCO mapping.")]
+ [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "POCO mapping.")]
+
+ public record PocoAllColumns(
+ long Key,
+ string? Str,
+ sbyte Int8,
+ short Int16,
+ int Int32,
+ long Int64,
+ float Float,
+ double Double,
+ Guid Uuid,
+ LocalDate Date,
+ BitArray BitMask,
+ LocalTime Time,
+ LocalDateTime DateTime,
+ Instant Timestamp,
+ byte[] Blob,
+ decimal Decimal);
+}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewBinaryTests.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewBinaryTests.cs
index 1ff147bb5e..f65f4663b4 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewBinaryTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewBinaryTests.cs
@@ -18,11 +18,13 @@
namespace Apache.Ignite.Tests.Table
{
using System;
+ using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using Ignite.Table;
+ using NodaTime;
using NUnit.Framework;
/// <summary>
@@ -534,5 +536,53 @@ namespace Apache.Ignite.Tests.Table
Assert.AreEqual(key, resTuple["key"]);
Assert.AreEqual(val, resTuple["val"]);
}
+
+ [Test]
+ public async Task TestAllColumns()
+ {
+ var table = await Client.Tables.GetTableAsync(TableAllColumnsName);
+ var tupleView = table!.RecordBinaryView;
+
+ var dt = LocalDateTime.FromDateTime(DateTime.UtcNow);
+ var tuple = new IgniteTuple
+ {
+ ["Key"] = 123L,
+ ["Str"] = "str",
+ ["Int8"] = (sbyte)8,
+ ["Int16"] = (short)16,
+ ["Int32"] = 32,
+ ["Int64"] = 64L,
+ ["Float"] = 32.32f,
+ ["Double"] = 64.64,
+ ["Uuid"] = Guid.NewGuid(),
+ ["Date"] = dt.Date,
+ ["BitMask"] = new BitArray(new byte[] { 1 }),
+ ["Time"] = dt.TimeOfDay,
+ ["DateTime"] = dt,
+ ["Timestamp"] = Instant.FromDateTimeUtc(DateTime.UtcNow),
+ ["Blob"] = new byte[] { 1, 2, 3 },
+ ["Decimal"] = 123.456m
+ };
+
+ await tupleView.UpsertAsync(null, tuple);
+
+ var res = await tupleView.GetAsync(null, tuple);
+
+ Assert.AreEqual(tuple["Blob"], res!["Blob"]);
+ Assert.AreEqual(tuple["Date"], res["Date"]);
+ Assert.AreEqual(tuple["Decimal"], res["Decimal"]);
+ Assert.AreEqual(tuple["Double"], res["Double"]);
+ Assert.AreEqual(tuple["Float"], res["Float"]);
+ Assert.AreEqual(tuple["Int8"], res["Int8"]);
+ Assert.AreEqual(tuple["Int16"], res["Int16"]);
+ Assert.AreEqual(tuple["Int32"], res["Int32"]);
+ Assert.AreEqual(tuple["Int64"], res["Int64"]);
+ Assert.AreEqual(tuple["Str"], res["Str"]);
+ Assert.AreEqual(tuple["Uuid"], res["Uuid"]);
+ Assert.AreEqual(tuple["BitMask"], res["BitMask"]);
+ Assert.AreEqual(tuple["Timestamp"], res["Timestamp"]);
+ Assert.AreEqual(tuple["Time"], res["Time"]);
+ Assert.AreEqual(tuple["DateTime"], res["DateTime"]);
+ }
}
}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewPocoTests.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewPocoTests.cs
index f3281ac82d..2761bd9ba2 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewPocoTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewPocoTests.cs
@@ -18,10 +18,12 @@
namespace Apache.Ignite.Tests.Table
{
using System;
+ using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
+ using NodaTime;
using NUnit.Framework;
/// <summary>
@@ -569,5 +571,67 @@ namespace Apache.Ignite.Tests.Table
Assert.AreEqual(poco.Prop9, res.Prop9);
Assert.AreEqual(poco.Prop10, res.Prop10);
}
+
+ [Test]
+ public async Task TestAllColumnsPoco()
+ {
+ var table = await Client.Tables.GetTableAsync(TableAllColumnsName);
+ var pocoView = table!.GetRecordView<PocoAllColumns>();
+
+ var dt = LocalDateTime.FromDateTime(DateTime.UtcNow);
+ var poco = new PocoAllColumns(
+ Key: 123,
+ Str: "str",
+ Int8: 8,
+ Int16: 16,
+ Int32: 32,
+ Int64: 64,
+ Float: 32.32f,
+ Double: 64.64,
+ Uuid: Guid.NewGuid(),
+ Date: dt.Date,
+ BitMask: new BitArray(new byte[] { 1 }),
+ Time: dt.TimeOfDay,
+ DateTime: dt,
+ Timestamp: Instant.FromDateTimeUtc(DateTime.UtcNow),
+ Blob: new byte[] { 1, 2, 3 },
+ Decimal: 123.456m);
+
+ await pocoView.UpsertAsync(null, poco);
+
+ var res = await pocoView.GetAsync(null, poco);
+
+ Assert.AreEqual(poco.Blob, res!.Blob);
+ Assert.AreEqual(poco.Date, res.Date);
+ Assert.AreEqual(poco.Decimal, res.Decimal);
+ Assert.AreEqual(poco.Double, res.Double);
+ Assert.AreEqual(poco.Float, res.Float);
+ Assert.AreEqual(poco.Int8, res.Int8);
+ Assert.AreEqual(poco.Int16, res.Int16);
+ Assert.AreEqual(poco.Int32, res.Int32);
+ Assert.AreEqual(poco.Int64, res.Int64);
+ Assert.AreEqual(poco.Str, res.Str);
+ Assert.AreEqual(poco.Uuid, res.Uuid);
+ Assert.AreEqual(poco.BitMask, res.BitMask);
+ Assert.AreEqual(poco.Timestamp, res.Timestamp);
+ Assert.AreEqual(poco.Time, res.Time);
+ Assert.AreEqual(poco.DateTime, res.DateTime);
+ }
+
+ [Test]
+ public async Task TestUnsupportedColumnTypeThrowsException()
+ {
+ var table = await Client.Tables.GetTableAsync(TableAllColumnsName);
+ var pocoView = table!.GetRecordView<UnsupportedByteType>();
+
+ var ex = Assert.ThrowsAsync<IgniteClientException>(async () => await pocoView.UpsertAsync(null, new UnsupportedByteType(1)));
+ Assert.AreEqual(
+ "Can't map field 'UnsupportedByteType.<Int8>k__BackingField' of type 'System.Byte' " +
+ "to column 'INT8' of type 'System.SByte' - types do not match.",
+ ex!.Message);
+ }
+
+ // ReSharper disable once NotAccessedPositionalProperty.Local
+ private record UnsupportedByteType(byte Int8);
}
}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/Serialization/ObjectSerializerHandlerTests.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/Serialization/ObjectSerializerHandlerTests.cs
index 638d192be4..1d276584e1 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/Serialization/ObjectSerializerHandlerTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/Serialization/ObjectSerializerHandlerTests.cs
@@ -34,8 +34,8 @@ namespace Apache.Ignite.Tests.Table.Serialization
{
private static readonly Schema Schema = new(1, 1, new[]
{
- new Column("Key", ClientDataType.Int64, false, true, 0),
- new Column("Val", ClientDataType.String, false, false, 1)
+ new Column("Key", ClientDataType.Int64, false, true, 0, 0),
+ new Column("Val", ClientDataType.String, false, false, 1, 0)
});
[Test]
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/TablesTests.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/TablesTests.cs
index ab082298ff..aa80584cc4 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/TablesTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/TablesTests.cs
@@ -18,6 +18,7 @@
namespace Apache.Ignite.Tests.Table
{
using System;
+ using System.Linq;
using System.Threading.Tasks;
using Ignite.Table;
using NUnit.Framework;
@@ -30,10 +31,11 @@ namespace Apache.Ignite.Tests.Table
[Test]
public async Task TestGetTables()
{
- var tables = await Client.Tables.GetTablesAsync();
+ var tables = (await Client.Tables.GetTablesAsync()).OrderBy(x => x.Name).ToList();
- Assert.AreEqual(1, tables.Count);
- Assert.AreEqual(TableName, tables[0].Name);
+ Assert.AreEqual(2, tables.Count);
+ Assert.AreEqual(TableAllColumnsName, tables[0].Name);
+ Assert.AreEqual(TableName, tables[1].Name);
}
[Test]
diff --git a/modules/platforms/dotnet/Apache.Ignite/Apache.Ignite.csproj b/modules/platforms/dotnet/Apache.Ignite/Apache.Ignite.csproj
index f61653cec9..1d4a5647f3 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Apache.Ignite.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite/Apache.Ignite.csproj
@@ -30,6 +30,7 @@
<ItemGroup>
<PackageReference Include="MessagePack" Version="[2.1.80,)" />
+ <PackageReference Include="NodaTime" Version="[3.*,)" />
</ItemGroup>
<ItemGroup>
@@ -42,8 +43,7 @@
</ItemGroup>
<ItemGroup>
- <ProjectReference Include="..\Apache.Ignite.Internal.Generators\Apache.Ignite.Internal.Generators.csproj"
- OutputItemType="Analyzer" ReferenceOutputAssembly="false" SetTargetFramework="TargetFramework=netstandard2.0" />
+ <ProjectReference Include="..\Apache.Ignite.Internal.Generators\Apache.Ignite.Internal.Generators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" SetTargetFramework="TargetFramework=netstandard2.0" />
</ItemGroup>
</Project>
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleBuilder.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleBuilder.cs
index 73a91bf5bb..4860fc4b5a 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleBuilder.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleBuilder.cs
@@ -18,13 +18,18 @@
namespace Apache.Ignite.Internal.Proto.BinaryTuple
{
using System;
+ using System.Buffers.Binary;
+ using System.Collections;
using System.Diagnostics;
+ using System.Numerics;
+ using System.Runtime.InteropServices;
using Buffers;
+ using NodaTime;
/// <summary>
/// Binary tuple builder.
/// </summary>
- internal ref struct BinaryTupleBuilder // TODO: Support all types (IGNITE-15431).
+ internal ref struct BinaryTupleBuilder
{
/** Number of elements in the tuple. */
private readonly int _numElements;
@@ -266,6 +271,12 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
OnWrite();
}
+ /// <summary>
+ /// Appends bytes.
+ /// </summary>
+ /// <param name="value">Value.</param>
+ public void AppendBytes(byte[] value) => AppendBytes(value.AsSpan());
+
/// <summary>
/// Appends a guid.
/// </summary>
@@ -280,12 +291,143 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
OnWrite();
}
+ /// <summary>
+ /// Appends a bitmask.
+ /// </summary>
+ /// <param name="value">Value.</param>
+ public void AppendBitmask(BitArray value)
+ {
+ var size = (value.Length + 7) / 8; // Ceiling division.
+ var arr = ByteArrayPool.Rent(size);
+
+ try
+ {
+ value.CopyTo(arr, 0);
+ PutBytes(arr.AsSpan()[..size]);
+
+ OnWrite();
+ }
+ finally
+ {
+ ByteArrayPool.Return(arr);
+ }
+ }
+
+ /// <summary>
+ /// Appends a decimal.
+ /// </summary>
+ /// <param name="value">Value.</param>
+ /// <param name="scale">Decimal scale from schema.</param>
+ public void AppendDecimal(decimal value, int scale)
+ {
+ var bits = decimal.GetBits(value);
+ var valueScale = (bits[3] & 0x00FF0000) >> 16;
+
+ var bytes = MemoryMarshal.Cast<int, byte>(bits.AsSpan(0, 3));
+ var unscaledValue = new BigInteger(bytes);
+
+ if (scale > valueScale)
+ {
+ unscaledValue *= BigInteger.Pow(new BigInteger(10), scale - valueScale);
+ }
+ else if (scale < valueScale)
+ {
+ unscaledValue /= BigInteger.Pow(new BigInteger(10), valueScale - scale);
+ }
+
+ var size = unscaledValue.GetByteCount();
+ var destination = GetSpan(size);
+ var success = unscaledValue.TryWriteBytes(destination, out int written);
+
+ Debug.Assert(success, "success");
+ Debug.Assert(written == size, "written == size");
+
+ OnWrite();
+ }
+
+ /// <summary>
+ /// Appends a number.
+ /// </summary>
+ /// <param name="value">Value.</param>
+ public void AppendNumber(BigInteger value)
+ {
+ if (value != default)
+ {
+ var size = value.GetByteCount();
+ var destination = GetSpan(size);
+ var success = value.TryWriteBytes(destination, out int written);
+
+ Debug.Assert(success, "success");
+ Debug.Assert(written == size, "written == size");
+ }
+
+ OnWrite();
+ }
+
+ /// <summary>
+ /// Appends a date.
+ /// </summary>
+ /// <param name="value">Value.</param>
+ public void AppendDate(LocalDate value)
+ {
+ if (value != default)
+ {
+ PutDate(value);
+ }
+
+ OnWrite();
+ }
+
+ /// <summary>
+ /// Appends a time.
+ /// </summary>
+ /// <param name="value">Value.</param>
+ public void AppendTime(LocalTime value)
+ {
+ if (value != default)
+ {
+ PutTime(value);
+ }
+
+ OnWrite();
+ }
+
+ /// <summary>
+ /// Appends a date and time.
+ /// </summary>
+ /// <param name="value">Value.</param>
+ public void AppendDateTime(LocalDateTime value)
+ {
+ if (value != default)
+ {
+ PutDate(value.Date);
+ PutTime(value.TimeOfDay);
+ }
+
+ OnWrite();
+ }
+
+ /// <summary>
+ /// Appends a timestamp (instant).
+ /// </summary>
+ /// <param name="value">Value.</param>
+ public void AppendTimestamp(Instant value)
+ {
+ if (value != default)
+ {
+ PutTimestamp(value);
+ }
+
+ OnWrite();
+ }
+
/// <summary>
/// Appends an object.
/// </summary>
/// <param name="value">Value.</param>
/// <param name="colType">Column type.</param>
- public void AppendObject(object? value, ClientDataType colType)
+ /// <param name="scale">Decimal scale.</param>
+ public void AppendObject(object? value, ClientDataType colType, int scale)
{
if (value == null)
{
@@ -332,9 +474,34 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
break;
case ClientDataType.BitMask:
+ AppendBitmask((BitArray)value);
+ break;
+
case ClientDataType.Decimal:
+ AppendDecimal((decimal)value, scale);
+ break;
+
+ case ClientDataType.Number:
+ AppendNumber((BigInteger)value);
+ break;
+
+ case ClientDataType.Date:
+ AppendDate((LocalDate)value);
+ break;
+
+ case ClientDataType.Time:
+ AppendTime((LocalTime)value);
+ break;
+
+ case ClientDataType.DateTime:
+ AppendDateTime((LocalDateTime)value);
+ break;
+
+ case ClientDataType.Timestamp:
+ AppendTimestamp((Instant)value);
+ break;
+
default:
- // TODO: Support all types (IGNITE-15431).
throw new IgniteClientException(ErrorGroups.Client.Protocol, "Unsupported type: " + colType);
}
}
@@ -451,6 +618,65 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
_buffer.Advance(actualBytes);
}
+ private void PutTimestamp(Instant value)
+ {
+ // Logic taken from
+ // https://github.com/nodatime/nodatime.serialization/blob/main/src/NodaTime.Serialization.Protobuf/NodaExtensions.cs#L69
+ // (Apache License).
+ // See discussion: https://github.com/nodatime/nodatime/issues/1644#issuecomment-1260524451
+ long seconds = value.ToUnixTimeSeconds();
+ Duration remainder = value - Instant.FromUnixTimeSeconds(seconds);
+ int nanos = (int)remainder.NanosecondOfDay;
+
+ PutLong(seconds);
+
+ if (nanos != 0)
+ {
+ PutInt(nanos);
+ }
+ }
+
+ private void PutTime(LocalTime value)
+ {
+ long hour = value.Hour;
+ long minute = value.Minute;
+ long second = value.Second;
+ long nanos = value.NanosecondOfSecond;
+
+ if ((nanos % 1000) != 0)
+ {
+ long time = (hour << 42) | (minute << 36) | (second << 30) | nanos;
+ PutInt((int)time);
+ PutShort((short)(time >> 32));
+ }
+ else if ((nanos % 1000000) != 0)
+ {
+ long time = (hour << 32) | (minute << 26) | (second << 20) | (nanos / 1000);
+ PutInt((int)time);
+ PutByte((sbyte)(time >> 32));
+ }
+ else
+ {
+ long time = (hour << 22) | (minute << 16) | (second << 10) | (nanos / 1000000);
+ PutInt((int)time);
+ }
+ }
+
+ private void PutDate(LocalDate value)
+ {
+ int year = value.Year;
+ int month = value.Month;
+ int day = value.Day;
+
+ int date = (year << 9) | (month << 5) | day;
+
+ // Write int32 as 3 bytes, preserving sign.
+ Span<byte> buf = stackalloc byte[4];
+ BinaryPrimitives.WriteInt32LittleEndian(buf, date << 8);
+
+ buf[1..].CopyTo(GetSpan(3));
+ }
+
/// <summary>
/// Proceed to the next tuple element.
/// </summary>
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleReader.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleReader.cs
index 04adff9878..684f9cd2c3 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleReader.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleReader.cs
@@ -19,12 +19,15 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
{
using System;
using System.Buffers.Binary;
+ using System.Collections;
using System.Diagnostics;
+ using System.Numerics;
+ using NodaTime;
/// <summary>
/// Binary tuple reader.
/// </summary>
- internal readonly ref struct BinaryTupleReader // TODO: Support all types (IGNITE-15431).
+ internal readonly ref struct BinaryTupleReader
{
/** Buffer. */
private readonly ReadOnlyMemory<byte> _buffer;
@@ -190,13 +193,103 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
var s => BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(s))
};
+ /// <summary>
+ /// Gets a bit mask value.
+ /// </summary>
+ /// <param name="index">Index.</param>
+ /// <returns>Value.</returns>
+ public BitArray GetBitmask(int index) => Seek(index) switch
+ {
+ { IsEmpty: true } => new BitArray(0),
+ var s => new BitArray(s.ToArray())
+ };
+
+ /// <summary>
+ /// Gets a decimal value.
+ /// </summary>
+ /// <param name="index">Index.</param>
+ /// <param name="scale">Decimal scale.</param>
+ /// <returns>Value.</returns>
+ public decimal GetDecimal(int index, int scale) => Seek(index) switch
+ {
+ { IsEmpty: true } => default,
+ var s => ReadDecimal(s, scale)
+ };
+
+ /// <summary>
+ /// Gets a number (big integer) value.
+ /// </summary>
+ /// <param name="index">Index.</param>
+ /// <returns>Value.</returns>
+ public BigInteger GetNumber(int index) => Seek(index) switch
+ {
+ { IsEmpty: true } => default,
+ var s => new BigInteger(s)
+ };
+
+ /// <summary>
+ /// Gets a local date value.
+ /// </summary>
+ /// <param name="index">Index.</param>
+ /// <returns>Value.</returns>
+ public LocalDate GetDate(int index) => Seek(index) switch
+ {
+ { IsEmpty: true } => default,
+ var s => ReadDate(s)
+ };
+
+ /// <summary>
+ /// Gets a local time value.
+ /// </summary>
+ /// <param name="index">Index.</param>
+ /// <returns>Value.</returns>
+ public LocalTime GetTime(int index) => Seek(index) switch
+ {
+ { IsEmpty: true } => default,
+ var s => ReadTime(s)
+ };
+
+ /// <summary>
+ /// Gets a local date and time value.
+ /// </summary>
+ /// <param name="index">Index.</param>
+ /// <returns>Value.</returns>
+ public LocalDateTime GetDateTime(int index) => Seek(index) switch
+ {
+ { IsEmpty: true } => default,
+ var s => ReadDate(s) + ReadTime(s[3..])
+ };
+
+ /// <summary>
+ /// Gets a timestamp (instant) value.
+ /// </summary>
+ /// <param name="index">Index.</param>
+ /// <returns>Value.</returns>
+ public Instant GetTimestamp(int index) => Seek(index) switch
+ {
+ { IsEmpty: true } => default,
+ var s => ReadTimestamp(s)
+ };
+
+ /// <summary>
+ /// Gets bytes.
+ /// </summary>
+ /// <param name="index">Index.</param>
+ /// <returns>Value.</returns>
+ public byte[] GetBytes(int index) => Seek(index) switch
+ {
+ { IsEmpty: true } => Array.Empty<byte>(),
+ var s => s.ToArray()
+ };
+
/// <summary>
/// Gets an object value according to the specified type.
/// </summary>
/// <param name="index">Index.</param>
/// <param name="columnType">Column type.</param>
+ /// <param name="scale">Column decimal scale.</param>
/// <returns>Value.</returns>
- public object? GetObject(int index, ClientDataType columnType)
+ public object? GetObject(int index, ClientDataType columnType, int scale)
{
if (IsNull(index))
{
@@ -213,12 +306,91 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
ClientDataType.Double => GetDouble(index),
ClientDataType.Uuid => GetGuid(index),
ClientDataType.String => GetString(index),
-
- // TODO: Support all types (IGNITE-15431).
+ ClientDataType.Decimal => GetDecimal(index, scale),
+ ClientDataType.Bytes => GetBytes(index),
+ ClientDataType.BitMask => GetBitmask(index),
+ ClientDataType.Date => GetDate(index),
+ ClientDataType.Time => GetTime(index),
+ ClientDataType.DateTime => GetDateTime(index),
+ ClientDataType.Timestamp => GetTimestamp(index),
+ ClientDataType.Number => GetNumber(index),
_ => throw new IgniteClientException(ErrorGroups.Client.Protocol, "Unsupported type: " + columnType)
};
}
+ private static Instant ReadTimestamp(ReadOnlySpan<byte> span)
+ {
+ var len = span.Length;
+ if (len == 0)
+ {
+ return default;
+ }
+
+ long seconds = BinaryPrimitives.ReadInt64LittleEndian(span);
+ int nanos = len == 8 ? 0 : BinaryPrimitives.ReadInt32LittleEndian(span[8..]);
+
+ return Instant.FromUnixTimeSeconds(seconds).PlusNanoseconds(nanos);
+ }
+
+ private static LocalDate ReadDate(ReadOnlySpan<byte> span)
+ {
+ // Read int32 from 3 bytes, preserving sign.
+ Span<byte> buf = stackalloc byte[4];
+ span[..3].CopyTo(buf[1..]);
+
+ int date = BinaryPrimitives.ReadInt32LittleEndian(buf) >> 8;
+
+ int day = date & 31;
+ int month = (date >> 5) & 15;
+ int year = (date >> 9); // Sign matters.
+
+ return new LocalDate(year, month, day);
+ }
+
+ private static LocalTime ReadTime(ReadOnlySpan<byte> span)
+ {
+ long time = BinaryPrimitives.ReadUInt32LittleEndian(span);
+ var length = span.Length;
+
+ int nanos;
+ if (length == 4)
+ {
+ nanos = ((int) time & ((1 << 10) - 1)) * 1000 * 1000;
+ time >>= 10;
+ }
+ else if (length == 5)
+ {
+ time |= (long)span[4] << 32;
+ nanos = ((int) time & ((1 << 20) - 1)) * 1000;
+ time >>= 20;
+ }
+ else
+ {
+ time |= (long)BinaryPrimitives.ReadUInt16LittleEndian(span[4..]) << 32;
+ nanos = ((int)time & ((1 << 30) - 1));
+ time >>= 30;
+ }
+
+ int second = ((int) time) & 63;
+ int minute = ((int) time >> 6) & 63;
+ int hour = ((int) time >> 12) & 31;
+
+ return LocalTime.FromHourMinuteSecondNanosecond(hour, minute, second, nanos);
+ }
+
+ private static decimal ReadDecimal(ReadOnlySpan<byte> span, int scale)
+ {
+ var unscaled = new BigInteger(span);
+ var res = (decimal)unscaled;
+
+ if (scale > 0)
+ {
+ res /= (decimal)BigInteger.Pow(10, scale);
+ }
+
+ return res;
+ }
+
private int GetOffset(int position)
{
var span = _buffer.Span[position..];
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/ClientDataType.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/ClientDataType.cs
index 2346fb2367..7aa2c3d36b 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/ClientDataType.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/ClientDataType.cs
@@ -22,59 +22,52 @@ namespace Apache.Ignite.Internal.Proto
/// </summary>
internal enum ClientDataType
{
- /// <summary>
- /// Byte.
- /// </summary>
+ /// <summary> Byte. </summary>
Int8 = 1,
- /// <summary>
- /// Short.
- /// </summary>
+ /// <summary> Short. </summary>
Int16 = 2,
- /// <summary>
- /// Int.
- /// </summary>
+ /// <summary> Int. </summary>
Int32 = 3,
- /// <summary>
- /// Long.
- /// </summary>
+ /// <summary> Long. </summary>
Int64 = 4,
- /// <summary>
- /// Float.
- /// </summary>
+ /// <summary> Float. </summary>
Float = 5,
- /// <summary>
- /// Double.
- /// </summary>
+ /// <summary> Double. </summary>
Double = 6,
- /// <summary>
- /// Decimal.
- /// </summary>
+ /// <summary> Decimal. </summary>
Decimal = 7,
- /// <summary>
- /// UUID / Guid.
- /// </summary>
+ /// <summary> UUID / Guid. </summary>
Uuid = 8,
- /// <summary>
- /// String.
- /// </summary>
+ /// <summary> String. </summary>
String = 9,
- /// <summary>
- /// Byte array.
- /// </summary>
+ /// <summary> Byte array. </summary>
Bytes = 10,
- /// <summary>
- /// BitMask.
- /// </summary>
+ /// <summary> BitMask. </summary>
BitMask = 11,
+
+ /// <summary> Local date. </summary>
+ Date = 12,
+
+ /// <summary> Local time. </summary>
+ Time = 13,
+
+ /// <summary> Local date and time. </summary>
+ DateTime = 14,
+
+ /// <summary> Timestamp (instant). </summary>
+ Timestamp = 15,
+
+ /// <summary> Number (BigInt). </summary>
+ Number = 16,
}
}
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/ClientDataTypeExtensions.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/ClientDataTypeExtensions.cs
index 8bdb4fb41d..5c6a73673e 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/ClientDataTypeExtensions.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/ClientDataTypeExtensions.cs
@@ -19,6 +19,8 @@ namespace Apache.Ignite.Internal.Proto
{
using System;
using System.Collections;
+ using System.Numerics;
+ using NodaTime;
/// <summary>
/// Extension methods for <see cref="ClientDataType"/>.
@@ -30,23 +32,25 @@ namespace Apache.Ignite.Internal.Proto
/// </summary>
/// <param name="clientDataType">Client data type.</param>
/// <returns>Corresponding CLR type.</returns>
- public static (Type Primary, Type? Alternative) ToType(this ClientDataType clientDataType)
+ public static Type ToType(this ClientDataType clientDataType) => clientDataType switch
{
- return clientDataType switch
- {
- ClientDataType.Int8 => (typeof(byte), typeof(sbyte)),
- ClientDataType.Int16 => (typeof(short), typeof(ushort)),
- ClientDataType.Int32 => (typeof(int), typeof(uint)),
- ClientDataType.Int64 => (typeof(long), typeof(ulong)),
- ClientDataType.Float => (typeof(float), null),
- ClientDataType.Double => (typeof(double), null),
- ClientDataType.Decimal => (typeof(decimal), null),
- ClientDataType.Uuid => (typeof(Guid), null),
- ClientDataType.String => (typeof(string), null),
- ClientDataType.Bytes => (typeof(byte[]), null),
- ClientDataType.BitMask => (typeof(BitArray), null),
- _ => throw new ArgumentOutOfRangeException(nameof(clientDataType), clientDataType, null)
- };
- }
+ ClientDataType.Int8 => typeof(sbyte),
+ ClientDataType.Int16 => typeof(short),
+ ClientDataType.Int32 => typeof(int),
+ ClientDataType.Int64 => typeof(long),
+ ClientDataType.Float => typeof(float),
+ ClientDataType.Double => typeof(double),
+ ClientDataType.Decimal => typeof(decimal),
+ ClientDataType.Uuid => typeof(Guid),
+ ClientDataType.String => typeof(string),
+ ClientDataType.Bytes => typeof(byte[]),
+ ClientDataType.BitMask => typeof(BitArray),
+ ClientDataType.Date => typeof(LocalDate),
+ ClientDataType.Time => typeof(LocalTime),
+ ClientDataType.DateTime => typeof(LocalDateTime),
+ ClientDataType.Timestamp => typeof(Instant),
+ ClientDataType.Number => typeof(BigInteger),
+ _ => throw new ArgumentOutOfRangeException(nameof(clientDataType), clientDataType, null)
+ };
}
}
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/MessagePackWriterExtensions.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/MessagePackWriterExtensions.cs
index 71ebdce32c..88350bf314 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/MessagePackWriterExtensions.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/MessagePackWriterExtensions.cs
@@ -75,76 +75,6 @@ namespace Apache.Ignite.Internal.Proto
return span;
}
- /// <summary>
- /// Writes an object.
- /// </summary>
- /// <param name="writer">Writer.</param>
- /// <param name="obj">Object.</param>
- public static void WriteObject(this ref MessagePackWriter writer, object? obj)
- {
- // TODO: Support all types (IGNITE-15431).
- switch (obj)
- {
- case null:
- writer.WriteNil();
- return;
-
- case string str:
- writer.Write(str);
- return;
-
- case Guid g:
- writer.Write(g);
- return;
-
- case byte b:
- writer.Write(b);
- return;
-
- case sbyte sb:
- writer.Write(sb);
- return;
-
- case short s:
- writer.Write(s);
- return;
-
- case ushort us:
- writer.Write(us);
- return;
-
- case int i:
- writer.Write(i);
- return;
-
- case uint ui:
- writer.Write(ui);
- return;
-
- case long l:
- writer.Write(l);
- return;
-
- case ulong ul:
- writer.Write(ul);
- return;
-
- case char ch:
- writer.Write(ch);
- return;
-
- case float f:
- writer.Write(f);
- return;
-
- case double d:
- writer.Write(d);
- return;
- }
-
- throw new IgniteClientException(ErrorGroups.Client.Protocol, "Unsupported type: " + obj.GetType());
- }
-
/// <summary>
/// Writes an object with type code.
/// </summary>
@@ -152,7 +82,7 @@ namespace Apache.Ignite.Internal.Proto
/// <param name="obj">Object.</param>
public static void WriteObjectWithType(this ref MessagePackWriter writer, object? obj)
{
- // TODO: Support all types (IGNITE-15431).
+ // TODO IGNITE-17777 Thin 3.0: use BinaryTuple for Compute and SQL results and arguments
switch (obj)
{
case null:
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Sql/ResultSet.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Sql/ResultSet.cs
index 18caa865eb..e012473713 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Sql/ResultSet.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Sql/ResultSet.cs
@@ -261,7 +261,7 @@ namespace Apache.Ignite.Internal.Sql
case SqlColumnType.Decimal:
case SqlColumnType.Timestamp:
default:
- // TODO: Support all types (IGNITE-15431).
+ // TODO IGNITE-17777 Thin 3.0: use BinaryTuple for Compute and SQL results and arguments
throw new ArgumentOutOfRangeException(nameof(type), type, null);
}
}
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Column.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Column.cs
index aac0b6e0bc..90df8d276b 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Column.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Column.cs
@@ -22,5 +22,5 @@ namespace Apache.Ignite.Internal.Table
/// <summary>
/// Schema column.
/// </summary>
- internal record Column(string Name, ClientDataType Type, bool Nullable, bool IsKey, int SchemaIndex);
+ internal record Column(string Name, ClientDataType Type, bool Nullable, bool IsKey, int SchemaIndex, int Scale);
}
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/BinaryTupleMethods.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/BinaryTupleMethods.cs
index b107a5b6b5..66a01f5a00 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/BinaryTupleMethods.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/BinaryTupleMethods.cs
@@ -18,8 +18,10 @@
namespace Apache.Ignite.Internal.Table.Serialization
{
using System;
+ using System.Collections;
using System.Collections.Generic;
using System.Reflection;
+ using NodaTime;
using Proto.BinaryTuple;
/// <summary>
@@ -41,6 +43,14 @@ namespace Apache.Ignite.Internal.Table.Serialization
private static readonly MethodInfo AppendDouble = typeof(BinaryTupleBuilder).GetMethod(nameof(BinaryTupleBuilder.AppendDouble))!;
private static readonly MethodInfo AppendGuid = typeof(BinaryTupleBuilder).GetMethod(nameof(BinaryTupleBuilder.AppendGuid))!;
private static readonly MethodInfo AppendString = typeof(BinaryTupleBuilder).GetMethod(nameof(BinaryTupleBuilder.AppendStringNullable))!;
+ private static readonly MethodInfo AppendDate = typeof(BinaryTupleBuilder).GetMethod(nameof(BinaryTupleBuilder.AppendDate))!;
+ private static readonly MethodInfo AppendBitmask = typeof(BinaryTupleBuilder).GetMethod(nameof(BinaryTupleBuilder.AppendBitmask))!;
+ private static readonly MethodInfo AppendTime = typeof(BinaryTupleBuilder).GetMethod(nameof(BinaryTupleBuilder.AppendTime))!;
+ private static readonly MethodInfo AppendDateTime = typeof(BinaryTupleBuilder).GetMethod(nameof(BinaryTupleBuilder.AppendDateTime))!;
+ private static readonly MethodInfo AppendTimestamp = typeof(BinaryTupleBuilder).GetMethod(nameof(BinaryTupleBuilder.AppendTimestamp))!;
+ private static readonly MethodInfo AppendDecimal = typeof(BinaryTupleBuilder).GetMethod(nameof(BinaryTupleBuilder.AppendDecimal))!;
+ private static readonly MethodInfo AppendBytes =
+ typeof(BinaryTupleBuilder).GetMethod(nameof(BinaryTupleBuilder.AppendBytes), new[] { typeof(byte[]) })!;
private static readonly MethodInfo GetByte = typeof(BinaryTupleReader).GetMethod(nameof(BinaryTupleReader.GetByte))!;
private static readonly MethodInfo GetShort = typeof(BinaryTupleReader).GetMethod(nameof(BinaryTupleReader.GetShort))!;
@@ -50,8 +60,14 @@ namespace Apache.Ignite.Internal.Table.Serialization
private static readonly MethodInfo GetDouble = typeof(BinaryTupleReader).GetMethod(nameof(BinaryTupleReader.GetDouble))!;
private static readonly MethodInfo GetGuid = typeof(BinaryTupleReader).GetMethod(nameof(BinaryTupleReader.GetGuid))!;
private static readonly MethodInfo GetString = typeof(BinaryTupleReader).GetMethod(nameof(BinaryTupleReader.GetStringNullable))!;
+ private static readonly MethodInfo GetDate = typeof(BinaryTupleReader).GetMethod(nameof(BinaryTupleReader.GetDate))!;
+ private static readonly MethodInfo GetBitmask = typeof(BinaryTupleReader).GetMethod(nameof(BinaryTupleReader.GetBitmask))!;
+ private static readonly MethodInfo GetTime = typeof(BinaryTupleReader).GetMethod(nameof(BinaryTupleReader.GetTime))!;
+ private static readonly MethodInfo GetDateTime = typeof(BinaryTupleReader).GetMethod(nameof(BinaryTupleReader.GetDateTime))!;
+ private static readonly MethodInfo GetTimestamp = typeof(BinaryTupleReader).GetMethod(nameof(BinaryTupleReader.GetTimestamp))!;
+ private static readonly MethodInfo GetDecimal = typeof(BinaryTupleReader).GetMethod(nameof(BinaryTupleReader.GetDecimal))!;
+ private static readonly MethodInfo GetBytes = typeof(BinaryTupleReader).GetMethod(nameof(BinaryTupleReader.GetBytes))!;
- // TODO: Support all types (IGNITE-15431).
private static readonly IReadOnlyDictionary<Type, MethodInfo> WriteMethods = new Dictionary<Type, MethodInfo>
{
{ typeof(string), AppendString },
@@ -61,7 +77,14 @@ namespace Apache.Ignite.Internal.Table.Serialization
{ typeof(long), AppendLong },
{ typeof(float), AppendFloat },
{ typeof(double), AppendDouble },
- { typeof(Guid), AppendGuid }
+ { typeof(Guid), AppendGuid },
+ { typeof(LocalDate), AppendDate },
+ { typeof(BitArray), AppendBitmask },
+ { typeof(LocalTime), AppendTime },
+ { typeof(LocalDateTime), AppendDateTime },
+ { typeof(Instant), AppendTimestamp },
+ { typeof(byte[]), AppendBytes },
+ { typeof(decimal), AppendDecimal }
};
private static readonly IReadOnlyDictionary<Type, MethodInfo> ReadMethods = new Dictionary<Type, MethodInfo>
@@ -73,7 +96,14 @@ namespace Apache.Ignite.Internal.Table.Serialization
{ typeof(long), GetLong },
{ typeof(float), GetFloat },
{ typeof(double), GetDouble },
- { typeof(Guid), GetGuid }
+ { typeof(Guid), GetGuid },
+ { typeof(LocalDate), GetDate },
+ { typeof(BitArray), GetBitmask },
+ { typeof(LocalTime), GetTime },
+ { typeof(LocalDateTime), GetDateTime },
+ { typeof(Instant), GetTimestamp },
+ { typeof(decimal), GetDecimal },
+ { typeof(byte[]), GetBytes }
};
/// <summary>
@@ -82,9 +112,7 @@ namespace Apache.Ignite.Internal.Table.Serialization
/// <param name="valueType">Type of the value to write.</param>
/// <returns>Write method for the specified value type.</returns>
public static MethodInfo GetWriteMethod(Type valueType) =>
- WriteMethods.TryGetValue(valueType, out var method)
- ? method
- : throw new IgniteClientException(ErrorGroups.Client.Configuration, "Unsupported type: " + valueType);
+ WriteMethods.TryGetValue(valueType, out var method) ? method : throw GetUnsupportedTypeException(valueType);
/// <summary>
/// Gets the read method.
@@ -92,8 +120,9 @@ namespace Apache.Ignite.Internal.Table.Serialization
/// <param name="valueType">Type of the value to read.</param>
/// <returns>Read method for the specified value type.</returns>
public static MethodInfo GetReadMethod(Type valueType) =>
- ReadMethods.TryGetValue(valueType, out var method)
- ? method
- : throw new IgniteClientException(ErrorGroups.Client.Configuration, "Unsupported type: " + valueType);
+ ReadMethods.TryGetValue(valueType, out var method) ? method : throw GetUnsupportedTypeException(valueType);
+
+ private static IgniteClientException GetUnsupportedTypeException(Type valueType) =>
+ new(ErrorGroups.Client.Configuration, "Unsupported type: " + valueType);
}
}
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/ObjectSerializerHandler.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/ObjectSerializerHandler.cs
index b0e29bee1b..914bf87bfd 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/ObjectSerializerHandler.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/ObjectSerializerHandler.cs
@@ -132,6 +132,11 @@ namespace Apache.Ignite.Internal.Table.Serialization
il.Emit(OpCodes.Ldarg_2); // record
il.Emit(OpCodes.Ldfld, fieldInfo);
+ if (col.Type == ClientDataType.Decimal)
+ {
+ EmitLdcI4(il, col.Scale);
+ }
+
var writeMethod = BinaryTupleMethods.GetWriteMethod(fieldInfo.FieldType);
il.Emit(OpCodes.Call, writeMethod);
}
@@ -243,13 +248,18 @@ namespace Apache.Ignite.Internal.Table.Serialization
il.Emit(OpCodes.Ldarg_0); // reader
EmitLdcI4(il, elemIdx); // index
+ if (col.Type == ClientDataType.Decimal)
+ {
+ EmitLdcI4(il, col.Scale);
+ }
+
il.Emit(OpCodes.Call, readMethod);
il.Emit(OpCodes.Stfld, fieldInfo); // res.field = value
}
- private static void EmitLdcI4(ILGenerator il, int elemIdx)
+ private static void EmitLdcI4(ILGenerator il, int val)
{
- switch (elemIdx)
+ switch (val)
{
case 0:
il.Emit(OpCodes.Ldc_I4_0);
@@ -288,20 +298,20 @@ namespace Apache.Ignite.Internal.Table.Serialization
break;
default:
- il.Emit(OpCodes.Ldc_I4, elemIdx);
+ il.Emit(OpCodes.Ldc_I4, val);
break;
}
}
private static void ValidateFieldType(FieldInfo fieldInfo, Column column)
{
- var (columnTypePrimary, columnTypeAlternative) = column.Type.ToType();
+ var columnType = column.Type.ToType();
var fieldType = fieldInfo.FieldType;
- if (fieldType != columnTypePrimary && fieldType != columnTypeAlternative)
+ if (fieldType != columnType)
{
var message = $"Can't map field '{fieldInfo.DeclaringType?.Name}.{fieldInfo.Name}' of type '{fieldType}' " +
- $"to column '{column.Name}' of type '{columnTypePrimary}' - types do not match.";
+ $"to column '{column.Name}' of type '{columnType}' - types do not match.";
throw new IgniteClientException(ErrorGroups.Client.Configuration, message);
}
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/TupleSerializerHandler.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/TupleSerializerHandler.cs
index 747a5a53a1..8c274a4126 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/TupleSerializerHandler.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/TupleSerializerHandler.cs
@@ -51,7 +51,7 @@ namespace Apache.Ignite.Internal.Table.Serialization
for (var index = 0; index < count; index++)
{
var column = columns[index];
- tuple[column.Name] = tupleReader.GetObject(index, column.Type);
+ tuple[column.Name] = tupleReader.GetObject(index, column.Type, column.Scale);
}
return tuple;
@@ -74,7 +74,7 @@ namespace Apache.Ignite.Internal.Table.Serialization
}
else
{
- tuple[column.Name] = tupleReader.GetObject(i - schema.KeyColumnCount, column.Type);
+ tuple[column.Name] = tupleReader.GetObject(i - schema.KeyColumnCount, column.Type, column.Scale);
}
}
@@ -99,7 +99,7 @@ namespace Apache.Ignite.Internal.Table.Serialization
if (colIdx >= 0)
{
- tupleBuilder.AppendObject(record[colIdx], col.Type);
+ tupleBuilder.AppendObject(record[colIdx], col.Type, col.Scale);
}
else
{
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Table.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Table.cs
index dfa3f90f07..904023b2eb 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Table.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Table.cs
@@ -213,17 +213,20 @@ namespace Apache.Ignite.Internal.Table
for (var i = 0; i < columnCount; i++)
{
var propertyCount = r.ReadArrayHeader();
+ const int expectedCount = 6;
- Debug.Assert(propertyCount >= 4, "propertyCount >= 4");
+ Debug.Assert(propertyCount >= expectedCount, "propertyCount >= " + expectedCount);
var name = r.ReadString();
var type = r.ReadInt32();
var isKey = r.ReadBoolean();
var isNullable = r.ReadBoolean();
+ r.ReadBoolean(); // IsColocation.
+ var scale = r.ReadInt32();
- r.Skip(propertyCount - 4);
+ r.Skip(propertyCount - expectedCount);
- var column = new Column(name, (ClientDataType)type, isNullable, isKey, i);
+ var column = new Column(name, (ClientDataType)type, isNullable, isKey, i, scale);
columns[i] = column;
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/PlatformTestNodeRunner.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/PlatformTestNodeRunner.java
index f24b244ad9..0229a6259d 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/PlatformTestNodeRunner.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/PlatformTestNodeRunner.java
@@ -51,6 +51,8 @@ public class PlatformTestNodeRunner {
private static final String TABLE_NAME = "tbl1";
+ private static final String TABLE_NAME_ALL_COLUMNS = "tbl_all_columns";
+
/** Time to keep the node alive. */
private static final int RUN_TIME_MINUTES = 30;
@@ -119,29 +121,60 @@ public class PlatformTestNodeRunner {
System.out.println("Ignite nodes started");
+ createTables(startedNodes.get(0));
+
+ String ports = startedNodes.stream()
+ .map(n -> String.valueOf(getPort((IgniteImpl) n)))
+ .collect(Collectors.joining(","));
+
+ System.out.println("THIN_CLIENT_PORTS=" + ports);
+
+ Thread.sleep(RUN_TIME_MINUTES * 60_000);
+
+ System.out.println("Exiting after " + RUN_TIME_MINUTES + " minutes.");
+ }
+
+ private static void createTables(Ignite node) {
var keyCol = "key";
- var valCol = "val";
TableDefinition schTbl = SchemaBuilders.tableBuilder(SCHEMA_NAME, TABLE_NAME).columns(
SchemaBuilders.column(keyCol, ColumnType.INT64).build(),
- SchemaBuilders.column(valCol, ColumnType.string()).asNullable(true).build()
+ SchemaBuilders.column("val", ColumnType.string()).asNullable(true).build()
).withPrimaryKey(keyCol).build();
- startedNodes.get(0).tables().createTable(schTbl.canonicalName(), tblCh ->
+ node.tables().createTable(schTbl.canonicalName(), tblCh ->
SchemaConfigurationConverter.convert(schTbl, tblCh)
.changeReplicas(1)
.changePartitions(10)
);
- String ports = startedNodes.stream()
- .map(n -> String.valueOf(getPort((IgniteImpl) n)))
- .collect(Collectors.joining(","));
-
- System.out.println("THIN_CLIENT_PORTS=" + ports);
-
- Thread.sleep(RUN_TIME_MINUTES * 60_000);
+ TableDefinition schTbl2 = SchemaBuilders.tableBuilder(SCHEMA_NAME, TABLE_NAME_ALL_COLUMNS).columns(
+ SchemaBuilders.column(keyCol, ColumnType.INT64).build(),
+ SchemaBuilders.column("str", ColumnType.string()).asNullable(true).build(),
+ SchemaBuilders.column("int8", ColumnType.INT8).asNullable(true).build(),
+ SchemaBuilders.column("int16", ColumnType.INT16).asNullable(true).build(),
+ SchemaBuilders.column("int32", ColumnType.INT32).asNullable(true).build(),
+ SchemaBuilders.column("int64", ColumnType.INT64).asNullable(true).build(),
+ SchemaBuilders.column("float", ColumnType.FLOAT).asNullable(true).build(),
+ SchemaBuilders.column("double", ColumnType.DOUBLE).asNullable(true).build(),
+ SchemaBuilders.column("uuid", ColumnType.UUID).asNullable(true).build(),
+ SchemaBuilders.column("date", ColumnType.DATE).asNullable(true).build(),
+ SchemaBuilders.column("bitmask", ColumnType.bitmaskOf(64)).asNullable(true).build(),
+ SchemaBuilders.column("time", ColumnType.time(ColumnType.TemporalColumnType.MAX_TIME_PRECISION))
+ .asNullable(true).build(),
+ SchemaBuilders.column("datetime", ColumnType.datetime(ColumnType.TemporalColumnType.MAX_TIME_PRECISION))
+ .asNullable(true).build(),
+ SchemaBuilders.column("timestamp", ColumnType.timestamp(ColumnType.TemporalColumnType.MAX_TIME_PRECISION))
+ .asNullable(true).build(),
+ SchemaBuilders.column("blob", ColumnType.blob()).asNullable(true).build(),
+ SchemaBuilders.column("decimal", ColumnType.decimal()).asNullable(true).build()
+ ).withPrimaryKey(keyCol).build();
- System.out.println("Exiting after " + RUN_TIME_MINUTES + " minutes.");
+ node.tables().createTable(schTbl2.canonicalName(), tblCh ->
+ SchemaConfigurationConverter.convert(schTbl2, tblCh)
+ .changeReplicas(1)
+ .changePartitions(10)
+ );
}
/**
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientComputeTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientComputeTest.java
index 90998c0098..677cd39253 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientComputeTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientComputeTest.java
@@ -212,7 +212,6 @@ public class ItThinClientComputeTest extends ItAbstractThinClientTest {
testEchoArg(LocalTime.now());
testEchoArg(LocalDateTime.now());
testEchoArg(Instant.now());
- testEchoArg(true);
testEchoArg(BigInteger.TEN);
}