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 a12385a IGNITE-16093 Thin client: Handle absent and null values
differently (#513)
a12385a is described below
commit a12385a9c89186cc362fba6f3e29be45de477077
Author: Pavel Tupitsyn <[email protected]>
AuthorDate: Fri Dec 17 14:19:56 2021 +0300
IGNITE-16093 Thin client: Handle absent and null values differently (#513)
Ignite Table API handles "column set to null" (1) and "column not set" (2)
differently.
* Non-nullable column does not allow (1), but allows (2) as long as there
is a default value.
* Nullable column will be set to null in (1) and to default value in (2).
Add a new data type `NO_VALUE` to the protocol to reflect this distinction.
---
.../internal/client/proto/ClientMessageCommon.java | 3 +
.../internal/client/proto/ClientMessagePacker.java | 18 ++++++
.../client/proto/ClientMessageUnpacker.java | 23 ++++++++
.../internal/client/proto/ClientMsgPackType.java | 3 +
.../proto/ClientMessagePackerUnpackerTest.java | 25 ++++++++
.../handler/requests/table/ClientTableCommon.java | 11 +++-
.../ignite/internal/client/table/ClientTable.java | 68 +++++++---------------
.../ignite/client/AbstractClientTableTest.java | 7 +++
.../org/apache/ignite/client/ClientTableTest.java | 43 ++++++++++++++
.../ignite/client/fakes/FakeIgniteTables.java | 25 ++++++++
.../Proto/MessagePackExtensionsTest.cs | 27 +++++++++
.../Internal/Proto/ClientMessagePackType.cs | 7 ++-
.../Internal/Proto/MessagePackReaderExtensions.cs | 24 ++++++++
.../Internal/Proto/MessagePackWriterExtensions.cs | 12 ++++
.../Apache.Ignite/Internal/Proto/ProtoCommon.cs | 7 ---
.../dotnet/Apache.Ignite/Internal/Table/Table.cs | 12 +++-
16 files changed, 258 insertions(+), 57 deletions(-)
diff --git
a/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientMessageCommon.java
b/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientMessageCommon.java
index 73a840c..8435c86 100644
---
a/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientMessageCommon.java
+++
b/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientMessageCommon.java
@@ -26,4 +26,7 @@ public class ClientMessageCommon {
/** Magic bytes before handshake. */
public static final byte[] MAGIC_BYTES = new byte[]{0x49, 0x47, 0x4E,
0x49}; // IGNI
+
+ /** Special "no value" object. */
+ public static final Object NO_VALUE = new Object();
}
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 9ca5549..3902654 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
@@ -18,6 +18,7 @@
package org.apache.ignite.internal.client.proto;
import static
org.apache.ignite.internal.client.proto.ClientMessageCommon.HEADER_SIZE;
+import static
org.apache.ignite.internal.client.proto.ClientMessageCommon.NO_VALUE;
import static org.msgpack.core.MessagePack.Code;
import io.netty.buffer.ByteBuf;
@@ -82,6 +83,17 @@ public class ClientMessagePacker implements AutoCloseable {
}
/**
+ * Writes a "no value" value.
+ */
+ public void packNoValue() {
+ assert !closed : "Packer is closed";
+
+ buf.writeByte(Code.FIXEXT1);
+ buf.writeByte(ClientMsgPackType.NO_VALUE);
+ buf.writeByte(0);
+ }
+
+ /**
* Writes a boolean value.
*
* @param b the value to be written.
@@ -648,6 +660,12 @@ public class ClientMessagePacker implements AutoCloseable {
return;
}
+ if (val == NO_VALUE) {
+ packNoValue();
+
+ return;
+ }
+
if (val instanceof Byte) {
packByte((byte) val);
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 f31ec61..551c22c 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
@@ -548,6 +548,29 @@ public class ClientMessageUnpacker implements
AutoCloseable {
}
/**
+ * Tries to read a "no value" value.
+ *
+ * @return True when there was a "no value" value, false otherwise.
+ */
+ public boolean tryUnpackNoValue() {
+ assert refCnt > 0 : "Unpacker is closed";
+
+ int idx = buf.readerIndex();
+ byte code = buf.getByte(idx);
+
+ if (code == Code.FIXEXT1) {
+ byte extCode = buf.getByte(idx + 1);
+
+ if (extCode == ClientMsgPackType.NO_VALUE) {
+ buf.readerIndex(idx + 3);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
* Reads a payload.
*
* @param length Payload size.
diff --git
a/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientMsgPackType.java
b/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientMsgPackType.java
index 4c4fc99..80950d7 100644
---
a/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientMsgPackType.java
+++
b/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientMsgPackType.java
@@ -47,4 +47,7 @@ public class ClientMsgPackType {
/** Ignite UUID. */
public static final byte IGNITE_UUID = 9;
+
+ /** Absent value for a column. */
+ public static final byte NO_VALUE = 10;
}
diff --git
a/modules/client-common/src/test/java/org/apache/ignite/internal/client/proto/ClientMessagePackerUnpackerTest.java
b/modules/client-common/src/test/java/org/apache/ignite/internal/client/proto/ClientMessagePackerUnpackerTest.java
index 96a6be4..db97c9c 100644
---
a/modules/client-common/src/test/java/org/apache/ignite/internal/client/proto/ClientMessagePackerUnpackerTest.java
+++
b/modules/client-common/src/test/java/org/apache/ignite/internal/client/proto/ClientMessagePackerUnpackerTest.java
@@ -20,6 +20,8 @@ package org.apache.ignite.internal.client.proto;
import static
org.apache.ignite.internal.testframework.IgniteTestUtils.randomBytes;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.buffer.Unpooled;
@@ -321,4 +323,27 @@ public class ClientMessagePackerUnpackerTest {
}
}
}
+
+ @Test
+ public void testNoValue() {
+ try (var packer = new
ClientMessagePacker(PooledByteBufAllocator.DEFAULT.directBuffer())) {
+ packer.packInt(1);
+ packer.packNoValue();
+ packer.packString("s");
+
+ var buf = packer.getBuffer();
+
+ byte[] data = new byte[buf.readableBytes()];
+ buf.readBytes(data);
+
+ try (var unpacker = new
ClientMessageUnpacker(Unpooled.wrappedBuffer(data))) {
+ unpacker.skipValues(4);
+
+ assertFalse(unpacker.tryUnpackNoValue());
+ assertEquals(1, unpacker.unpackInt());
+ assertTrue(unpacker.tryUnpackNoValue());
+ assertEquals("s", unpacker.unpackString());
+ }
+ }
+ }
}
diff --git
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/table/ClientTableCommon.java
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/table/ClientTableCommon.java
index 4cd7d73..e462bac 100644
---
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/table/ClientTableCommon.java
+++
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/table/ClientTableCommon.java
@@ -17,6 +17,8 @@
package org.apache.ignite.client.handler.requests.table;
+import static
org.apache.ignite.internal.client.proto.ClientMessageCommon.NO_VALUE;
+
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.Instant;
@@ -311,7 +313,7 @@ class ClientTableCommon {
var tuple = Tuple.create(cnt);
for (int i = 0; i < cnt; i++) {
- if (unpacker.tryUnpackNil()) {
+ if (unpacker.tryUnpackNoValue()) {
continue;
}
@@ -437,13 +439,18 @@ class ClientTableCommon {
}
private static void writeColumnValue(ClientMessagePacker packer, Tuple
tuple, Column col) {
- var val = tuple.valueOrDefault(col.name(), null);
+ var val = tuple.valueOrDefault(col.name(), NO_VALUE);
if (val == null) {
packer.packNil();
return;
}
+ if (val == NO_VALUE) {
+ packer.packNoValue();
+ return;
+ }
+
switch (col.type().spec()) {
case INT8:
packer.packByte((byte) val);
diff --git
a/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientTable.java
b/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientTable.java
index 0f350c0..1c84100 100644
---
a/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientTable.java
+++
b/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientTable.java
@@ -17,6 +17,8 @@
package org.apache.ignite.internal.client.table;
+import static
org.apache.ignite.internal.client.proto.ClientMessageCommon.NO_VALUE;
+
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -249,29 +251,20 @@ public class ClientTable implements Table {
boolean keyOnly,
boolean skipHeader
) {
- // TODO: Special case for ClientTupleBuilder - it has columns in order
- // TODO: Optimize (IGNITE-16082).
- var vals = new Object[keyOnly ? schema.keyColumnCount() :
schema.columns().length];
- var tupleSize = tuple.columnCount();
-
- for (var i = 0; i < tupleSize; i++) {
- var colName = tuple.columnName(i);
- var col = schema.column(colName);
-
- if (keyOnly && !col.key()) {
- continue;
- }
-
- vals[col.schemaIndex()] = tuple.value(i);
- }
-
if (!skipHeader) {
out.packIgniteUuid(id);
out.packInt(schema.version());
}
- for (var val : vals) {
- out.packObject(val);
+ var columns = schema.columns();
+ var count = keyOnly ? schema.keyColumnCount() : columns.length;
+
+ for (var i = 0; i < count; i++) {
+ var col = columns[i];
+
+ Object v = tuple.valueOrDefault(col.name(), NO_VALUE);
+
+ out.packObject(v);
}
}
@@ -291,39 +284,22 @@ public class ClientTable implements Table {
ClientMessagePacker out,
boolean skipHeader
) {
- // TODO: Handle missing values and null values differently
(IGNITE-16093).
- var vals = new Object[schema.columns().length];
-
- for (var i = 0; i < key.columnCount(); i++) {
- var colName = key.columnName(i);
- var col = schema.column(colName);
-
- if (!col.key()) {
- continue;
- }
-
- vals[col.schemaIndex()] = key.value(i);
- }
-
- if (val != null) {
- for (var i = 0; i < val.columnCount(); i++) {
- var colName = val.columnName(i);
- var col = schema.column(colName);
-
- if (col.key()) {
- continue;
- }
-
- vals[col.schemaIndex()] = val.value(i);
- }
- }
-
if (!skipHeader) {
out.packIgniteUuid(id);
out.packInt(schema.version());
}
- for (var v : vals) {
+ var columns = schema.columns();
+
+ for (var i = 0; i < columns.length; i++) {
+ var col = columns[i];
+
+ Object v = col.key()
+ ? key.valueOrDefault(col.name(), NO_VALUE)
+ : val != null
+ ? val.valueOrDefault(col.name(), NO_VALUE)
+ : NO_VALUE;
+
out.packObject(v);
}
}
diff --git
a/modules/client/src/test/java/org/apache/ignite/client/AbstractClientTableTest.java
b/modules/client/src/test/java/org/apache/ignite/client/AbstractClientTableTest.java
index e42c71a..42e9d88 100644
---
a/modules/client/src/test/java/org/apache/ignite/client/AbstractClientTableTest.java
+++
b/modules/client/src/test/java/org/apache/ignite/client/AbstractClientTableTest.java
@@ -19,6 +19,7 @@ package org.apache.ignite.client;
import static
org.apache.ignite.client.fakes.FakeIgniteTables.TABLE_ALL_COLUMNS;
import static org.apache.ignite.client.fakes.FakeIgniteTables.TABLE_ONE_COLUMN;
+import static
org.apache.ignite.client.fakes.FakeIgniteTables.TABLE_WITH_DEFAULT_VALUES;
import java.math.BigDecimal;
import java.math.BigInteger;
@@ -93,6 +94,12 @@ public class AbstractClientTableTest extends
AbstractClientTest {
return client.tables().table(DEFAULT_TABLE);
}
+ protected Table tableWithDefaultValues() {
+ server.tables().createTableIfNotExists(TABLE_WITH_DEFAULT_VALUES, tbl
-> tbl.changeReplicas(1));
+
+ return client.tables().table(TABLE_WITH_DEFAULT_VALUES);
+ }
+
protected static Tuple allClumnsTableKey(long id) {
return Tuple.create().set("gid", id).set("id", String.valueOf(id));
}
diff --git
a/modules/client/src/test/java/org/apache/ignite/client/ClientTableTest.java
b/modules/client/src/test/java/org/apache/ignite/client/ClientTableTest.java
index 20b8819..a588e8b 100644
--- a/modules/client/src/test/java/org/apache/ignite/client/ClientTableTest.java
+++ b/modules/client/src/test/java/org/apache/ignite/client/ClientTableTest.java
@@ -329,4 +329,47 @@ public class ClientTableTest extends
AbstractClientTableTest {
assertEquals(3L, skippedTuples[1].longValue("id"));
assertEquals("z", skippedTuples[1].stringValue("name"));
}
+
+ @Test
+ public void testColumnWithDefaultValueNotSetReturnsDefault() {
+ RecordView<Tuple> table = tableWithDefaultValues().recordView();
+
+ var tuple = Tuple.create()
+ .set("id", 1);
+
+ table.upsert(tuple);
+
+ var res = table.get(tuple);
+
+ assertEquals("def_str", res.stringValue("str"));
+ assertEquals("def_str2", res.stringValue("str_non_null"));
+ }
+
+ @Test
+ public void testNullableColumnWithDefaultValueSetNullReturnsNull() {
+ RecordView<Tuple> table = tableWithDefaultValues().recordView();
+
+ var tuple = Tuple.create()
+ .set("id", 1)
+ .set("str", null);
+
+ table.upsert(tuple);
+
+ var res = table.get(tuple);
+
+ assertNull(res.stringValue("str"));
+ }
+
+ @Test
+ public void testNonNullableColumnWithDefaultValueSetNullThrowsException() {
+ RecordView<Tuple> table = tableWithDefaultValues().recordView();
+
+ var tuple = Tuple.create()
+ .set("id", 1)
+ .set("str_non_null", null);
+
+ var ex = assertThrows(CompletionException.class, () ->
table.upsert(tuple));
+
+ assertTrue(ex.getMessage().contains("null was passed, but column is
not nullable"), ex.getMessage());
+ }
}
diff --git
a/modules/client/src/test/java/org/apache/ignite/client/fakes/FakeIgniteTables.java
b/modules/client/src/test/java/org/apache/ignite/client/fakes/FakeIgniteTables.java
index aef3812..2f9a602 100644
---
a/modules/client/src/test/java/org/apache/ignite/client/fakes/FakeIgniteTables.java
+++
b/modules/client/src/test/java/org/apache/ignite/client/fakes/FakeIgniteTables.java
@@ -46,6 +46,8 @@ public class FakeIgniteTables implements IgniteTables,
IgniteTablesInternal {
public static final String TABLE_ONE_COLUMN = "one-column";
+ public static final String TABLE_WITH_DEFAULT_VALUES = "default-columns";
+
private final ConcurrentHashMap<String, TableImpl> tables = new
ConcurrentHashMap<>();
private final ConcurrentHashMap<IgniteUuid, TableImpl> tablesById = new
ConcurrentHashMap<>();
@@ -172,6 +174,10 @@ public class FakeIgniteTables implements IgniteTables,
IgniteTablesInternal {
history = this::getOneColumnSchema;
break;
+ case TABLE_WITH_DEFAULT_VALUES:
+ history = this::getDefaultColumnValuesSchema;
+ break;
+
default:
history = this::getSchema;
break;
@@ -249,6 +255,25 @@ public class FakeIgniteTables implements IgniteTables,
IgniteTablesInternal {
* @param v Version.
* @return Schema descriptor.
*/
+ private SchemaDescriptor getDefaultColumnValuesSchema(Integer v) {
+ return new SchemaDescriptor(
+ v,
+ new Column[]{
+ new Column("id", NativeTypes.INT32, false)
+ },
+ new Column[]{
+ new Column("num", NativeTypes.INT8, true, () -> (byte)
42),
+ new Column("str", NativeTypes.STRING, true, () ->
"def_str"),
+ new Column("str_non_null", NativeTypes.STRING, false,
() -> "def_str2"),
+ });
+ }
+
+ /**
+ * Gets the schema.
+ *
+ * @param v Version.
+ * @return Schema descriptor.
+ */
@SuppressWarnings("ZeroLengthArrayAllocation")
private SchemaDescriptor getOneColumnSchema(Integer v) {
return new SchemaDescriptor(
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Tests/Proto/MessagePackExtensionsTest.cs
b/modules/platforms/dotnet/Apache.Ignite.Tests/Proto/MessagePackExtensionsTest.cs
index 88a0952..0197e20 100644
---
a/modules/platforms/dotnet/Apache.Ignite.Tests/Proto/MessagePackExtensionsTest.cs
+++
b/modules/platforms/dotnet/Apache.Ignite.Tests/Proto/MessagePackExtensionsTest.cs
@@ -122,6 +122,33 @@ namespace Apache.Ignite.Tests.Proto
CollectionAssert.AreEqual(JavaUuidBytes, bytes);
}
+ [Test]
+ public void TestNoValue()
+ {
+ var res1 = WriteRead(
+ buf =>
+ {
+ var w = buf.GetMessageWriter();
+
+ w.Write(3);
+ w.WriteNoValue();
+ w.Write("abc");
+
+ w.Flush();
+ },
+ m =>
+ {
+ var r = new MessagePackReader(m);
+
+ return (r.TryReadNoValue(), r.ReadInt32(),
r.TryReadNoValue(), r.ReadString());
+ });
+
+ Assert.IsFalse(res1.Item1);
+ Assert.AreEqual(3, res1.Item2);
+ Assert.IsTrue(res1.Item3);
+ Assert.AreEqual("abc", res1.Item4);
+ }
+
private static T WriteRead<T>(Action<PooledArrayBufferWriter> write,
Func<ReadOnlyMemory<byte>, T> read)
{
var bufferWriter = new PooledArrayBufferWriter();
diff --git
a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/ClientMessagePackType.cs
b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/ClientMessagePackType.cs
index c629bbf..2484353 100644
---
a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/ClientMessagePackType.cs
+++
b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/ClientMessagePackType.cs
@@ -65,6 +65,11 @@ namespace Apache.Ignite.Internal.Proto
/// <summary>
/// Ignite UUID.
/// </summary>
- IgniteUuid = 9
+ IgniteUuid = 9,
+
+ /// <summary>
+ /// Absent value for a column.
+ /// </summary>
+ NoValue = 10
}
}
diff --git
a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/MessagePackReaderExtensions.cs
b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/MessagePackReaderExtensions.cs
index 0d4a706..4f773cd 100644
---
a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/MessagePackReaderExtensions.cs
+++
b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/MessagePackReaderExtensions.cs
@@ -143,6 +143,30 @@ namespace Apache.Ignite.Internal.Proto
return res;
}
+ /// <summary>
+ /// Reads <see cref="ClientMessagePackType.NoValue"/> if it is the
next token.
+ /// </summary>
+ /// <param name="reader">Reader.</param>
+ /// <returns><c>true</c> if the next token was NoValue; <c>false</c>
otherwise.</returns>
+ public static bool TryReadNoValue(this ref MessagePackReader reader)
+ {
+ if (reader.NextCode != MessagePackCode.FixExt1)
+ {
+ return false;
+ }
+
+ var header = reader.CreatePeekReader().ReadExtensionFormatHeader();
+
+ if (header.TypeCode != (sbyte)ClientMessagePackType.NoValue)
+ {
+ return false;
+ }
+
+ reader.ReadRaw(3);
+
+ return true;
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ValidateExtensionType(
ref MessagePackReader reader,
diff --git
a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/MessagePackWriterExtensions.cs
b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/MessagePackWriterExtensions.cs
index 23b1b81..ca87fb3 100644
---
a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/MessagePackWriterExtensions.cs
+++
b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/MessagePackWriterExtensions.cs
@@ -83,6 +83,18 @@ namespace Apache.Ignite.Internal.Proto
}
/// <summary>
+ /// Writes Ignite UUID.
+ /// </summary>
+ /// <param name="writer">Writer.</param>
+ public static void WriteNoValue(this ref MessagePackWriter writer)
+ {
+ writer.WriteExtensionFormatHeader(
+ new ExtensionHeader((sbyte)ClientMessagePackType.NoValue, 1));
+
+ writer.Advance(1);
+ }
+
+ /// <summary>
/// Writes an object.
/// </summary>
/// <param name="writer">Writer.</param>
diff --git
a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/ProtoCommon.cs
b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/ProtoCommon.cs
index fc8f35e..f290e53 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/ProtoCommon.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/ProtoCommon.cs
@@ -17,8 +17,6 @@
namespace Apache.Ignite.Internal.Proto
{
- using System.Text;
-
/// <summary>
/// Common protocol data.
/// </summary>
@@ -28,10 +26,5 @@ namespace Apache.Ignite.Internal.Proto
/// Magic bytes.
/// </summary>
public static readonly byte[] MagicBytes = { (byte)'I', (byte)'G',
(byte)'N', (byte)'I' };
-
- /// <summary>
- /// String encoding.
- /// </summary>
- public static readonly Encoding Encoding = new
UTF8Encoding(encoderShouldEmitUTF8Identifier: false);
}
}
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Table.cs
b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Table.cs
index 4e2eb18..407fba0 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Table.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Table.cs
@@ -352,6 +352,11 @@ namespace Apache.Ignite.Internal.Table
}
else
{
+ if (r.TryReadNoValue())
+ {
+ continue;
+ }
+
tuple[column.Name] = r.ReadObject(column.Type);
}
}
@@ -367,6 +372,11 @@ namespace Apache.Ignite.Internal.Table
for (var index = 0; index < count; index++)
{
+ if (r.TryReadNoValue())
+ {
+ continue;
+ }
+
var column = columns[index];
tuple[column.Name] = r.ReadObject(column.Type);
}
@@ -443,7 +453,7 @@ namespace Apache.Ignite.Internal.Table
if (colIdx < 0)
{
- w.WriteNil();
+ w.WriteNoValue();
}
else
{