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 e7746cf275 IGNITE-21526 .NET: Clean up IEP-54 leftovers (#3349)
e7746cf275 is described below
commit e7746cf275a65a611b83ca6453aee2c3be08c000
Author: Pavel Tupitsyn <[email protected]>
AuthorDate: Tue Mar 5 14:06:23 2024 +0200
IGNITE-21526 .NET: Clean up IEP-54 leftovers (#3349)
Refactor the logic that relies on "key columns come first" design, support
any key column order (use `keyIndex` from the server schema).
---
.../SerializerHandlerBenchmarksBase.cs | 16 +--
.../SerializerHandlerReadBenchmarks.cs | 12 +-
.../SerializerHandlerWriteBenchmarks.cs | 24 +---
.../Proto/ColocationHashTests.cs | 14 +-
.../Table/BinaryTupleIgniteTupleAdapterTests.cs | 55 +++++++-
.../Serialization/ObjectSerializerHandlerTests.cs | 14 +-
.../SerializerHandlerConsistencyTests.cs | 152 +++++++++++++++++++++
.../BinaryTuple/BinaryTupleIgniteTupleAdapter.cs | 33 ++---
.../dotnet/Apache.Ignite/Internal/Table/Column.cs | 22 ++-
.../Apache.Ignite/Internal/Table/DataStreamer.cs | 6 +-
.../{Column.cs => HashedColumnIndexProvider.cs} | 35 +++--
.../dotnet/Apache.Ignite/Internal/Table/Schema.cs | 99 ++++++++++++--
.../Serialization/IRecordSerializerHandler.cs | 15 +-
.../Table/Serialization/ObjectSerializerHandler.cs | 75 +++++-----
.../Serialization/TuplePairSerializerHandler.cs | 45 +++---
.../Table/Serialization/TupleSerializerHandler.cs | 41 +++---
.../dotnet/Apache.Ignite/Internal/Table/Table.cs | 24 +---
17 files changed, 480 insertions(+), 202 deletions(-)
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 bc94378f00..ad3f1b5188 100644
---
a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerBenchmarksBase.cs
+++
b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerBenchmarksBase.cs
@@ -44,16 +44,14 @@ namespace Apache.Ignite.Benchmarks.Table.Serialization
[nameof(Car.Seats)] = Object.Seats
};
- internal static readonly Schema Schema = new(
- Version: 1,
- TableId: 1,
- KeyColumnCount: 1,
- ColocationColumnCount: 1,
- Columns: new[]
+ internal static readonly Schema Schema = Schema.CreateInstance(
+ version: 1,
+ tableId: 1,
+ columns: new[]
{
- new Column(nameof(Car.Id), ColumnType.Uuid, IsNullable: false,
ColocationIndex: 0, IsKey: true, SchemaIndex: 0, Scale: 0, Precision: 0),
- new Column(nameof(Car.BodyType), ColumnType.String,
IsNullable: false, ColocationIndex: -1, IsKey: false, SchemaIndex: 1, Scale: 0,
Precision: 0),
- new Column(nameof(Car.Seats), ColumnType.Int32, IsNullable:
false, ColocationIndex: -1, IsKey: false, SchemaIndex: 2, Scale: 0, Precision:
0)
+ new Column(nameof(Car.Id), ColumnType.Uuid, IsNullable: false,
ColocationIndex: 0, KeyIndex: 0, SchemaIndex: 0, Scale: 0, Precision: 0),
+ new Column(nameof(Car.BodyType), ColumnType.String,
IsNullable: false, ColocationIndex: -1, KeyIndex: -1, SchemaIndex: 1, Scale: 0,
Precision: 0),
+ new Column(nameof(Car.Seats), ColumnType.Int32, IsNullable:
false, ColocationIndex: -1, KeyIndex: -1, SchemaIndex: 2, Scale: 0, Precision:
0)
});
internal static readonly byte[] SerializedData = GetSerializedData();
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerReadBenchmarks.cs
b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerReadBenchmarks.cs
index 4ad9571b2a..07c2f8d410 100644
---
a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerReadBenchmarks.cs
+++
b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerReadBenchmarks.cs
@@ -24,16 +24,16 @@ namespace Apache.Ignite.Benchmarks.Table.Serialization
using Internal.Table.Serialization;
/// <summary>
- /// Benchmarks for <see cref="IRecordSerializerHandler{T}.Read"/>
implementations.
+ /// Benchmarks for <see cref="IRecordSerializerHandler{T}"/> read methods.
///
- /// Results on i9-12900H, .NET SDK 6.0.405, Ubuntu 22.04:
+ /// Results on i9-12900H, .NET SDK 6.0.419, Ubuntu 22.04:
///
/// | Method | Mean | Error | StdDev | Ratio |
RatioSD | Gen 0 | Allocated |
/// |-------------------
|----------:|---------:|---------:|------:|--------:|-------:|----------:|
- /// | ReadObjectManual | 53.23 ns | 0.320 ns | 0.268 ns | 1.00 |
0.00 | 0.0003 | 80 B |
- /// | ReadObject | 88.01 ns | 0.484 ns | 0.453 ns | 1.65 |
0.01 | 0.0002 | 80 B |
- /// | ReadTuple | 23.66 ns | 0.274 ns | 0.257 ns | 0.44 |
0.00 | 0.0004 | 120 B |
- /// | ReadTupleAndFields | 136.34 ns | 2.014 ns | 1.884 ns | 2.56 |
0.04 | 0.0007 | 208 B |.
+ /// | ReadObjectManual | 52.88 ns | 0.348 ns | 0.325 ns | 1.00 |
0.00 | 0.0003 | 80 B |
+ /// | ReadObject | 89.68 ns | 0.738 ns | 0.654 ns | 1.70 |
0.02 | 0.0002 | 80 B |
+ /// | ReadTuple | 20.02 ns | 0.147 ns | 0.131 ns | 0.38 |
0.00 | 0.0004 | 112 B |
+ /// | ReadTupleAndFields | 127.92 ns | 2.234 ns | 2.194 ns | 2.42 |
0.05 | 0.0007 | 200 B |.
/// </summary>
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic",
Justification = "Benchmarks.")]
[MemoryDiagnoser]
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 b91818af3c..f573ed8158 100644
---
a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerWriteBenchmarks.cs
+++
b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerWriteBenchmarks.cs
@@ -24,25 +24,15 @@ namespace Apache.Ignite.Benchmarks.Table.Serialization
using Internal.Table.Serialization;
/// <summary>
- /// Benchmarks for <see cref="IRecordSerializerHandler{T}.Write(ref
Apache.Ignite.Internal.Proto.MsgPack.MsgPackWriter,Apache.Ignite.Internal.Table.Schema,T,bool,bool)"/>
implementations.
+ /// Benchmarks for <see cref="IRecordSerializerHandler{T}"/> write methods.
///
- /// Comparison of MessagePack library and our own implementation,
i9-12900H, .NET SDK 6.0.405, Ubuntu 22.04:
+ /// Results on i9-12900H, .NET SDK 6.0.419, Ubuntu 22.04:
///
- /// MessagePack 2.1.90 (old):
- ///
- /// | Method | Mean | Error | StdDev | Ratio | Gen 0 |
Allocated |
- /// |------------------
|---------:|--------:|--------:|------:|-------:|----------:|
- /// | WriteObjectManual | 189.6 ns | 0.55 ns | 0.51 ns | 1.00 | 0.0002 |
80 B |
- /// | WriteObject | 221.7 ns | 0.78 ns | 0.73 ns | 1.17 | 0.0002 |
80 B |
- /// | WriteTuple | 310.3 ns | 1.59 ns | 1.41 ns | 1.64 | 0.0005 |
184 B |
- ///
- /// Custom MsgPack (new):
- ///
- /// | Method | Mean | Error | StdDev | Ratio | Gen 0 |
Allocated |
- /// |------------------
|---------:|--------:|--------:|------:|-------:|----------:|
- /// | WriteObjectManual | 135.7 ns | 1.13 ns | 1.06 ns | 1.00 | 0.0002 |
80 B |
- /// | WriteObject | 161.2 ns | 0.59 ns | 0.52 ns | 1.19 | 0.0002 |
80 B |
- /// | WriteTuple | 250.5 ns | 0.91 ns | 0.85 ns | 1.85 | 0.0005 |
184 B |.
+ /// | Method | Mean | Error | StdDev | Ratio | RatioSD |
Gen 0 | Allocated |
+ /// |------------------
|---------:|--------:|--------:|------:|--------:|-------:|----------:|
+ /// | WriteObjectManual | 116.4 ns | 1.33 ns | 1.11 ns | 1.00 | 0.00 |
0.0002 | 80 B |
+ /// | WriteObject | 137.5 ns | 1.26 ns | 1.18 ns | 1.18 | 0.01 |
0.0002 | 80 B |
+ /// | WriteTuple | 213.3 ns | 2.76 ns | 2.59 ns | 1.83 | 0.02 |
0.0007 | 184 B |.
/// </summary>
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic",
Justification = "Benchmarks.")]
[MemoryDiagnoser]
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Tests/Proto/ColocationHashTests.cs
b/modules/platforms/dotnet/Apache.Ignite.Tests/Proto/ColocationHashTests.cs
index 9d9b3e103b..6f19c79f25 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Proto/ColocationHashTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Proto/ColocationHashTests.cs
@@ -239,7 +239,7 @@ public class ColocationHashTests : IgniteTestsBase
var schema = GetSchema(arr, timePrecision, timestampPrecision);
var noValueSet = new byte[arr.Count].AsSpan();
- TupleSerializerHandler.Instance.Write(ref builder, igniteTuple,
schema, arr.Count, noValueSet);
+ TupleSerializerHandler.Instance.Write(ref builder, igniteTuple,
schema, keyOnly: false, noValueSet);
return builder.GetHash();
}
finally
@@ -265,7 +265,7 @@ public class ColocationHashTests : IgniteTestsBase
{
var columns = arr.Select((obj, ci) => GetColumn(obj, ci,
timePrecision, timestampPrecision)).ToArray();
- return new Schema(Version: 0, 0, arr.Count, arr.Count, columns);
+ return Schema.CreateInstance(version: 0, tableId: 0, columns);
}
private static Column GetColumn(object value, int schemaIndex, int
timePrecision, int timestampPrecision)
@@ -301,7 +301,15 @@ public class ColocationHashTests : IgniteTestsBase
var scale = value is decimal d ?
BitConverter.GetBytes(decimal.GetBits(d)[3])[2] : 0;
- return new Column("m_Item" + (schemaIndex + 1), colType, false, true,
schemaIndex, schemaIndex, Scale: scale, precision);
+ return new Column(
+ Name: "m_Item" + (schemaIndex + 1),
+ Type: colType,
+ IsNullable: false,
+ KeyIndex: schemaIndex,
+ ColocationIndex: schemaIndex,
+ SchemaIndex: schemaIndex,
+ Scale: scale,
+ Precision: precision);
}
private async Task AssertClientAndServerHashesAreEqual(int timePrecision =
9, int timestampPrecision = 6, params object[] keys)
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/BinaryTupleIgniteTupleAdapterTests.cs
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/BinaryTupleIgniteTupleAdapterTests.cs
index 3a1727b461..cac949c183 100644
---
a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/BinaryTupleIgniteTupleAdapterTests.cs
+++
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/BinaryTupleIgniteTupleAdapterTests.cs
@@ -65,6 +65,55 @@ public class BinaryTupleIgniteTupleAdapterTests :
IgniteTupleTests
Assert.IsNull(tuple.GetFieldValue<object>("_schema"));
}
+ [Test]
+ public void TestReverseKeyColumnOrder([Values(true, false)] bool keyOnly)
+ {
+ var cols = new[]
+ {
+ new Column("val1", ColumnType.String, false, KeyIndex: -1,
ColocationIndex: -1, SchemaIndex: 0, 0, 0),
+ new Column("key1", ColumnType.Int32, false, KeyIndex: 1,
ColocationIndex: 0, SchemaIndex: 1, 0, 0),
+ new Column("val2", ColumnType.Uuid, false, KeyIndex: -1,
ColocationIndex: -1, SchemaIndex: 2, 0, 0),
+ new Column("key2", ColumnType.Int64, false, KeyIndex: 0,
ColocationIndex: 1, SchemaIndex: 3, 0, 0)
+ };
+
+ var schema = Schema.CreateInstance(0, 0, cols);
+
+ using var builder = new
BinaryTupleBuilder(schema.GetColumnsFor(keyOnly).Length);
+
+ if (keyOnly)
+ {
+ builder.AppendLong(456L);
+ builder.AppendInt(123);
+ }
+ else
+ {
+ builder.AppendString("v1");
+ builder.AppendInt(123);
+ builder.AppendGuid(Guid.Empty);
+ builder.AppendLong(456L);
+ }
+
+ var buf = builder.Build().ToArray();
+ var keyTuple = new BinaryTupleIgniteTupleAdapter(buf, schema, keyOnly:
keyOnly);
+
+ Assert.AreEqual(keyOnly ? 2 : 4, keyTuple.FieldCount);
+ Assert.AreEqual(123, keyTuple["key1"]);
+ Assert.AreEqual(456L, keyTuple["key2"]);
+
+ if (keyOnly)
+ {
+ Assert.AreEqual(123, keyTuple[1]);
+ Assert.AreEqual(456L, keyTuple[0]);
+ }
+ else
+ {
+ Assert.AreEqual("v1", keyTuple[0]);
+ Assert.AreEqual(123, keyTuple[1]);
+ Assert.AreEqual(Guid.Empty, keyTuple[2]);
+ Assert.AreEqual(456L, keyTuple[3]);
+ }
+ }
+
protected override string GetShortClassName() =>
nameof(BinaryTupleIgniteTupleAdapter);
protected override IIgniteTuple CreateTuple(IIgniteTuple source)
@@ -77,16 +126,16 @@ public class BinaryTupleIgniteTupleAdapterTests :
IgniteTupleTests
var name = source.GetName(i);
var val = source[i]!;
var type = GetColumnType(val);
- var col = new Column(name, type, true, false, 0, i, 0, 0);
+ var col = new Column(Name: name, Type: type, IsNullable: true,
KeyIndex: i, ColocationIndex: i, SchemaIndex: i, Scale: 0, Precision: 0);
cols.Add(col);
builder.AppendObject(val, type);
}
var buf = builder.Build().ToArray();
- var schema = new Schema(0, 0, 0, 0, cols);
+ var schema = Schema.CreateInstance(0, 0, cols.ToArray());
- return new BinaryTupleIgniteTupleAdapter(buf, schema, cols.Count);
+ return new BinaryTupleIgniteTupleAdapter(buf, schema, keyOnly: false);
static ColumnType GetColumnType(object? obj)
{
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 58a08e61aa..17f52c8da6 100644
---
a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/Serialization/ObjectSerializerHandlerTests.cs
+++
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/Serialization/ObjectSerializerHandlerTests.cs
@@ -32,15 +32,13 @@ namespace Apache.Ignite.Tests.Table.Serialization
// ReSharper disable NotAccessedPositionalProperty.Local
public class ObjectSerializerHandlerTests
{
- private static readonly Schema Schema = new(
- Version: 1,
- TableId: 1,
- KeyColumnCount: 1,
- ColocationColumnCount: 1,
- Columns: new[]
+ private static readonly Schema Schema = Schema.CreateInstance(
+ version: 1,
+ tableId: 1,
+ columns: new[]
{
- new Column("Key", ColumnType.Int64, IsNullable: false,
ColocationIndex: 0, IsKey: true, SchemaIndex: 0, Scale: 0, Precision: 0),
- new Column("Val", ColumnType.String, IsNullable: false,
ColocationIndex: -1, IsKey: false, SchemaIndex: 1, Scale: 0, Precision: 0)
+ new Column("Key", ColumnType.Int64, IsNullable: false,
ColocationIndex: 0, KeyIndex: 0, SchemaIndex: 0, Scale: 0, Precision: 0),
+ new Column("Val", ColumnType.String, IsNullable: false,
ColocationIndex: -1, KeyIndex: -1, SchemaIndex: 1, Scale: 0, Precision: 0)
});
[Test]
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/Serialization/SerializerHandlerConsistencyTests.cs
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/Serialization/SerializerHandlerConsistencyTests.cs
new file mode 100644
index 0000000000..084416438d
--- /dev/null
+++
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/Serialization/SerializerHandlerConsistencyTests.cs
@@ -0,0 +1,152 @@
+/*
+ * 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.
+ */
+
+// ReSharper disable UnusedAutoPropertyAccessor.Local
+namespace Apache.Ignite.Tests.Table.Serialization;
+
+using System;
+using Ignite.Sql;
+using Ignite.Table;
+using Internal.Buffers;
+using Internal.Table;
+using Internal.Table.Serialization;
+using NUnit.Framework;
+
+/// <summary>
+/// Tests that different serializers produce consistent results.
+/// </summary>
+public class SerializerHandlerConsistencyTests
+{
+ private const int ExpectedColocationHash = 1326971215;
+
+ private static readonly Schema Schema = Schema.CreateInstance(
+ version: 0,
+ tableId: 0,
+ columns: new[]
+ {
+ new Column("val1", ColumnType.String, false, KeyIndex: -1,
ColocationIndex: -1, SchemaIndex: 0, 0, 0),
+ new Column("key1", ColumnType.Int32, false, KeyIndex: 1,
ColocationIndex: 0, SchemaIndex: 1, 0, 0),
+ new Column("val2", ColumnType.Uuid, false, KeyIndex: -1,
ColocationIndex: -1, SchemaIndex: 2, 0, 0),
+ new Column("key2", ColumnType.String, false, KeyIndex: 0,
ColocationIndex: 1, SchemaIndex: 3, 0, 0)
+ });
+
+ [Test]
+ public void TestSerializationAndHashing([Values(true, false)] bool keyOnly)
+ {
+ var tupleHandler = TupleSerializerHandler.Instance;
+ var tupleKvHandler = TuplePairSerializerHandler.Instance;
+ var objectHandler = new ObjectSerializerHandler<Poco>();
+ var objectKvHandler = new ObjectSerializerHandler<KvPair<PocoKey,
PocoVal>>();
+
+ var poco = new Poco
+ {
+ Val1 = "v1",
+ Key1 = 123,
+ Val2 = Guid.NewGuid(),
+ Key2 = "k2"
+ };
+
+ var pocoKv = new KvPair<PocoKey, PocoVal>
+ {
+ Key = new PocoKey
+ {
+ Key1 = poco.Key1,
+ Key2 = poco.Key2
+ },
+ Val = new PocoVal
+ {
+ Val1 = poco.Val1,
+ Val2 = poco.Val2
+ }
+ };
+
+ var tuple = new IgniteTuple
+ {
+ ["key1"] = poco.Key1,
+ ["key2"] = poco.Key2
+ };
+
+ if (!keyOnly)
+ {
+ tuple["val1"] = poco.Val1;
+ tuple["val2"] = poco.Val2;
+ }
+
+ var tupleKv = new KvPair<IIgniteTuple, IIgniteTuple>(
+ new IgniteTuple
+ {
+ ["key1"] = poco.Key1,
+ ["key2"] = poco.Key2
+ },
+ new IgniteTuple());
+
+ if (!keyOnly)
+ {
+ tupleKv.Val["val1"] = poco.Val1;
+ tupleKv.Val["val2"] = poco.Val2;
+ }
+
+ var (tupleBuf, tupleHash) = Serialize(tupleHandler, tuple, keyOnly);
+ var (tupleKvBuf, tupleKvHash) = Serialize(tupleKvHandler, tupleKv,
keyOnly);
+ var (pocoBuf, pocoHash) = Serialize(objectHandler, poco, keyOnly);
+ var (pocoKvBuf, pocoKvHash) = Serialize(objectKvHandler, pocoKv,
keyOnly);
+
+ Assert.AreEqual(ExpectedColocationHash, tupleHash);
+ Assert.AreEqual(ExpectedColocationHash, tupleKvHash);
+ Assert.AreEqual(ExpectedColocationHash, pocoHash);
+ Assert.AreEqual(ExpectedColocationHash, pocoKvHash);
+
+ CollectionAssert.AreEqual(tupleBuf, tupleKvBuf);
+ CollectionAssert.AreEqual(tupleBuf, pocoBuf);
+ CollectionAssert.AreEqual(tupleBuf, pocoKvBuf);
+ }
+
+ private static (byte[] Buf, int Hash)
Serialize<T>(IRecordSerializerHandler<T> handler, T obj, bool keyOnly = false)
+ {
+ using var buf = new PooledArrayBuffer();
+
+ var writer = buf.MessageWriter;
+ var hash = handler.Write(ref writer, Schema, obj, keyOnly,
computeHash: true);
+
+ return (buf.GetWrittenMemory().ToArray(), hash);
+ }
+
+ private class Poco
+ {
+ public string? Val1 { get; set; }
+
+ public int Key1 { get; set; }
+
+ public Guid Val2 { get; set; }
+
+ public string Key2 { get; set; } = string.Empty;
+ }
+
+ private class PocoKey
+ {
+ public int Key1 { get; set; }
+
+ public string Key2 { get; set; } = string.Empty;
+ }
+
+ private class PocoVal
+ {
+ public string? Val1 { get; set; }
+
+ public Guid Val2 { get; set; }
+ }
+}
diff --git
a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleIgniteTupleAdapter.cs
b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleIgniteTupleAdapter.cs
index 2e4230be28..289d8dd4a5 100644
---
a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleIgniteTupleAdapter.cs
+++
b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleIgniteTupleAdapter.cs
@@ -30,14 +30,12 @@ using Table.Serialization;
/// </summary>
internal sealed class BinaryTupleIgniteTupleAdapter : IIgniteTuple,
IEquatable<BinaryTupleIgniteTupleAdapter>, IEquatable<IIgniteTuple>
{
- private readonly int _schemaFieldCount; // Key-only tuples have less
fields than schema.
+ private readonly bool _keyOnly;
private Memory<byte> _data;
private Schema? _schema;
- private Dictionary<string, int>? _indexes;
-
private IgniteTuple? _tuple;
/// <summary>
@@ -45,25 +43,25 @@ internal sealed class BinaryTupleIgniteTupleAdapter :
IIgniteTuple, IEquatable<B
/// </summary>
/// <param name="data">Binary tuple data.</param>
/// <param name="schema">Schema.</param>
- /// <param name="fieldCount">Field count.</param>
- public BinaryTupleIgniteTupleAdapter(Memory<byte> data, Schema schema, int
fieldCount)
+ /// <param name="keyOnly">Whether only the key part should be
exposed.</param>
+ public BinaryTupleIgniteTupleAdapter(Memory<byte> data, Schema schema,
bool keyOnly)
{
- Debug.Assert(fieldCount <= schema.Columns.Count, "fieldCount <=
schema.Columns.Count");
-
_data = data;
_schema = schema;
- _schemaFieldCount = fieldCount;
+ _keyOnly = keyOnly;
}
/// <inheritdoc/>
- public int FieldCount => _tuple?.FieldCount ?? _schemaFieldCount;
+ public int FieldCount => _tuple?.FieldCount ?? Columns.Count;
+
+ private IReadOnlyCollection<Column> Columns =>
_schema!.GetColumnsFor(_keyOnly);
/// <inheritdoc/>
public object? this[int ordinal]
{
get => _tuple != null
? _tuple[ordinal]
- : TupleSerializerHandler.ReadObject(_data.Span, _schema!,
_schemaFieldCount, ordinal);
+ : TupleSerializerHandler.ReadObject(_data.Span, _schema!,
_keyOnly, ordinal);
set => InitTuple()[ordinal] = value;
}
@@ -93,17 +91,13 @@ internal sealed class BinaryTupleIgniteTupleAdapter :
IIgniteTuple, IEquatable<B
return _tuple.GetOrdinal(name);
}
- if (_indexes == null)
+ var column = _schema!.GetColumn(name);
+ if (column == null)
{
- _indexes = new Dictionary<string, int>(_schema!.Columns.Count);
-
- for (var i = 0; i < _schema.Columns.Count; i++)
- {
-
_indexes[IgniteTupleCommon.ParseColumnName(_schema.Columns[i].Name)] = i;
- }
+ return -1;
}
- return _indexes.TryGetValue(IgniteTupleCommon.ParseColumnName(name),
out var index) ? index : -1;
+ return _keyOnly ? column.KeyIndex : column.SchemaIndex;
}
/// <inheritdoc/>
@@ -131,11 +125,10 @@ internal sealed class BinaryTupleIgniteTupleAdapter :
IIgniteTuple, IEquatable<B
Debug.Assert(_schema != null, "_schema != null");
// Copy data to a mutable IgniteTuple.
- _tuple = TupleSerializerHandler.ReadTuple(_data.Span, _schema,
_schemaFieldCount);
+ _tuple = TupleSerializerHandler.ReadTuple(_data.Span, _schema,
_keyOnly);
// Release schema and data.
_schema = default;
- _indexes = default;
_data = default;
return _tuple;
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Column.cs
b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Column.cs
index 0f3729291e..1288eca621 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Column.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Column.cs
@@ -26,8 +26,26 @@ internal record Column(
string Name,
ColumnType Type,
bool IsNullable,
- bool IsKey,
+ int KeyIndex,
int ColocationIndex,
int SchemaIndex,
int Scale,
- int Precision);
+ int Precision)
+{
+ /// <summary>
+ /// Gets a value indicating whether this column is a part of the key.
+ /// </summary>
+ public bool IsKey => KeyIndex >= 0;
+
+ /// <summary>
+ /// Gets a value indicating whether this column is a part of the
colocation key.
+ /// </summary>
+ public bool IsColocation => ColocationIndex >= 0;
+
+ /// <summary>
+ /// Gets the column index within a binary tuple.
+ /// </summary>
+ /// <param name="keyOnly">Whether a key-only binary tuple is used.</param>
+ /// <returns>Index within a binary tuple.</returns>
+ public int GetBinaryTupleIndex(bool keyOnly) => keyOnly ? KeyIndex :
SchemaIndex;
+}
diff --git
a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/DataStreamer.cs
b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/DataStreamer.cs
index e6d388405d..63268259fb 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/DataStreamer.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/DataStreamer.cs
@@ -162,7 +162,7 @@ internal static class DataStreamer
(Batch<T> Batch, string Partition) Add(T item)
{
var schema0 = schema;
- var tupleBuilder = new BinaryTupleBuilder(schema0.Columns.Count,
hashedColumnsPredicate: schema0);
+ var tupleBuilder = new BinaryTupleBuilder(schema0.Columns.Length,
hashedColumnsPredicate: schema0.HashedColumnIndexProvider);
try
{
@@ -176,14 +176,14 @@ internal static class DataStreamer
(Batch<T> Batch, string Partition) Add0(T item, ref BinaryTupleBuilder
tupleBuilder, Schema schema0)
{
- var columnCount = schema0.Columns.Count;
+ var columnCount = schema0.Columns.Length;
// Use MemoryMarshal to work around [CS8352]: "Cannot use variable
'noValueSet' in this context
// because it may expose referenced variables outside of their
declaration scope".
Span<byte> noValueSet = stackalloc byte[columnCount / 8 + 1];
Span<byte> noValueSetRef = MemoryMarshal.CreateSpan(ref
MemoryMarshal.GetReference(noValueSet), columnCount);
- writer.Handler.Write(ref tupleBuilder, item, schema0, columnCount,
noValueSetRef);
+ writer.Handler.Write(ref tupleBuilder, item, schema0, keyOnly:
false, noValueSetRef);
// ReSharper disable once AccessToModifiedClosure (reviewed)
var partitionAssignment0 = partitionAssignment;
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Column.cs
b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/HashedColumnIndexProvider.cs
similarity index 51%
copy from modules/platforms/dotnet/Apache.Ignite/Internal/Table/Column.cs
copy to
modules/platforms/dotnet/Apache.Ignite/Internal/Table/HashedColumnIndexProvider.cs
index 0f3729291e..a914dc920a 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Column.cs
+++
b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/HashedColumnIndexProvider.cs
@@ -17,17 +17,30 @@
namespace Apache.Ignite.Internal.Table;
-using Ignite.Sql;
+using System.Collections.Generic;
+using Proto.BinaryTuple;
/// <summary>
-/// Schema column.
+/// Schema-based hashed column index provider.
/// </summary>
-internal record Column(
- string Name,
- ColumnType Type,
- bool IsNullable,
- bool IsKey,
- int ColocationIndex,
- int SchemaIndex,
- int Scale,
- int Precision);
+internal sealed class HashedColumnIndexProvider : IHashedColumnIndexProvider
+{
+ private readonly IReadOnlyList<Column> _columns;
+
+ /// <summary>
+ /// Initializes a new instance of the <see
cref="HashedColumnIndexProvider"/> class.
+ /// </summary>
+ /// <param name="columns">Columns.</param>
+ /// <param name="hashedColumnCount">Hashed column count.</param>
+ public HashedColumnIndexProvider(IReadOnlyList<Column> columns, int
hashedColumnCount)
+ {
+ _columns = columns;
+ HashedColumnCount = hashedColumnCount;
+ }
+
+ /// <inheritdoc/>
+ public int HashedColumnCount { get; init; }
+
+ /// <inheritdoc/>
+ public int HashedColumnOrder(int index) => _columns[index].ColocationIndex;
+}
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Schema.cs
b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Schema.cs
index 8fa9c84238..d4e260d998 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Schema.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Schema.cs
@@ -18,6 +18,9 @@
namespace Apache.Ignite.Internal.Table
{
using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Linq;
using Proto.BinaryTuple;
/// <summary>
@@ -25,27 +28,101 @@ namespace Apache.Ignite.Internal.Table
/// </summary>
/// <param name="Version">Version.</param>
/// <param name="TableId">Table id.</param>
- /// <param name="KeyColumnCount">Key column count.</param>
/// <param name="ColocationColumnCount">Colocation column count.</param>
/// <param name="Columns">Columns in schema order.</param>
+ /// <param name="KeyColumns">Key part columns.</param>
+ /// <param name="ValColumns">Val part columns.</param>
+ /// <param name="ColumnsByName">Column name map.</param>
+ /// <param name="HashedColumnIndexProvider">Hashed column index
provider.</param>
+ /// <param name="KeyOnlyHashedColumnIndexProvider">Hashed column index
provider for key-only mode.</param>
+ [SuppressMessage("Performance", "CA1819:Properties should not return
arrays", Justification = "Reviewed.")]
internal sealed record Schema(
int Version,
int TableId,
- int KeyColumnCount,
int ColocationColumnCount,
- IReadOnlyList<Column> Columns) : IHashedColumnIndexProvider
+ Column[] Columns,
+ Column[] KeyColumns,
+ Column[] ValColumns,
+ IReadOnlyDictionary<string, Column> ColumnsByName,
+ IHashedColumnIndexProvider HashedColumnIndexProvider,
+ IHashedColumnIndexProvider KeyOnlyHashedColumnIndexProvider)
{
/// <summary>
- /// Gets the value column count.
+ /// Gets column by name.
/// </summary>
- public int ValueColumnCount => Columns.Count - KeyColumnCount;
+ /// <param name="name">Column name.</param>
+ /// <returns>Column or null.</returns>
+ public Column? GetColumn(string name) =>
ColumnsByName!.GetValueOrDefault(IgniteTupleCommon.ParseColumnName(name), null);
- /// <inheritdoc/>
- public int HashedColumnCount => ColocationColumnCount;
+ /// <summary>
+ /// Create schema instance.
+ /// </summary>
+ /// <param name="version">Version.</param>
+ /// <param name="tableId">Table ID.</param>
+ /// <param name="columns">Columns.</param>
+ /// <returns>Schema.</returns>
+ public static Schema CreateInstance(int version, int tableId, Column[]
columns)
+ {
+ var keyColumnCount = columns.Count(static x => x.IsKey);
+ var keyColumns = new Column[keyColumnCount];
+ var valColumns = new Column[columns.Length - keyColumnCount];
+ int colocationColumnCount = 0;
+ int valIdx = 0;
+
+ foreach (var column in columns)
+ {
+ if (column.IsKey)
+ {
+ Debug.Assert(keyColumns[column.KeyIndex] == null,
"Duplicate key index: " + column);
+ keyColumns[column.KeyIndex] = column;
+ }
+ else
+ {
+ valColumns[valIdx++] = column;
+ }
+
+ if (column.IsColocation)
+ {
+ colocationColumnCount++;
+ }
+ }
+
+ // ReSharper disable once
ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
+ Debug.Assert(keyColumns.All(x => x != null), "Some key columns are
missing");
+ Debug.Assert(columns.Length == 0 || colocationColumnCount > 0, "No
hashed columns");
+
+ var columnMap = new Dictionary<string, Column>(columns.Length);
+ foreach (var column in columns)
+ {
+ columnMap[IgniteTupleCommon.ParseColumnName(column.Name)] =
column;
+ }
- /// <inheritdoc/>
- public int HashedColumnOrder(int index) => index < KeyColumnCount
- ? Columns[index].ColocationIndex
- : -1;
+ return new Schema(
+ version,
+ tableId,
+ colocationColumnCount,
+ columns,
+ keyColumns,
+ valColumns,
+ columnMap,
+ new HashedColumnIndexProvider(columns, colocationColumnCount),
+ new HashedColumnIndexProvider(keyColumns,
colocationColumnCount));
+ }
+
+ /// <summary>
+ /// Gets columns.
+ /// </summary>
+ /// <param name="keyOnly">Key only flag.</param>
+ /// <returns>Columns according to the key flag.</returns>
+ public Column[] GetColumnsFor(bool keyOnly) =>
+ keyOnly ? KeyColumns : Columns;
+
+ /// <summary>
+ /// Gets the hashed column index provider for the specified key-only
flag.
+ /// </summary>
+ /// <param name="keyOnly">Key only flag.</param>
+ /// <returns>Hashed column index provider.</returns>
+ public IHashedColumnIndexProvider GetHashedColumnIndexProviderFor(bool
keyOnly) =>
+ keyOnly ? KeyOnlyHashedColumnIndexProvider :
HashedColumnIndexProvider;
}
}
diff --git
a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/IRecordSerializerHandler.cs
b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/IRecordSerializerHandler.cs
index 4b8219bb59..3446f3bada 100644
---
a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/IRecordSerializerHandler.cs
+++
b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/IRecordSerializerHandler.cs
@@ -47,15 +47,18 @@ namespace Apache.Ignite.Internal.Table.Serialization
/// <returns>Key hash when <paramref name="computeHash"/> is
<c>true</c>; 0 otherwise.</returns>
int Write(ref MsgPackWriter writer, Schema schema, T record, bool
keyOnly = false, bool computeHash = false)
{
- var columns = schema.Columns;
- var count = keyOnly ? schema.KeyColumnCount : columns.Count;
+ var count = keyOnly ? schema.KeyColumns.Length :
schema.Columns.Length;
var noValueSet = writer.WriteBitSet(count);
- var tupleBuilder = new BinaryTupleBuilder(count,
hashedColumnsPredicate: computeHash ? schema : null);
+ var hashedColumnsPredicate = computeHash
+ ? schema.GetHashedColumnIndexProviderFor(keyOnly)
+ : null;
+
+ var tupleBuilder = new BinaryTupleBuilder(count,
hashedColumnsPredicate: hashedColumnsPredicate);
try
{
- Write(ref tupleBuilder, record, schema, count, noValueSet);
+ Write(ref tupleBuilder, record, schema, keyOnly, noValueSet);
var binaryTupleMemory = tupleBuilder.Build();
writer.Write(binaryTupleMemory.Span);
@@ -74,13 +77,13 @@ namespace Apache.Ignite.Internal.Table.Serialization
/// <param name="tupleBuilder">Tuple builder.</param>
/// <param name="record">Record.</param>
/// <param name="schema">Schema.</param>
- /// <param name="columnCount">Column count.</param>
+ /// <param name="keyOnly">Key only part.</param>
/// <param name="noValueSet">No-value set.</param>
void Write(
ref BinaryTupleBuilder tupleBuilder,
T record,
Schema schema,
- int columnCount,
+ bool keyOnly,
Span<byte> noValueSet);
}
}
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 d42651954b..35bcf52947 100644
---
a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/ObjectSerializerHandler.cs
+++
b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/ObjectSerializerHandler.cs
@@ -37,7 +37,7 @@ namespace Apache.Ignite.Internal.Table.Serialization
/// <typeparam name="T">Object type.</typeparam>
internal sealed class ObjectSerializerHandler<T> :
IRecordSerializerHandler<T>
{
- private readonly ConcurrentDictionary<(int, int), WriteDelegate<T>>
_writers = new();
+ private readonly ConcurrentDictionary<(int, bool), WriteDelegate<T>>
_writers = new();
private readonly ConcurrentDictionary<(int, bool), ReadDelegate<T>>
_readers = new();
@@ -56,7 +56,7 @@ namespace Apache.Ignite.Internal.Table.Serialization
? w
: _readers.GetOrAdd(cacheKey, EmitReader(schema, keyOnly));
- var columnCount = keyOnly ? schema.KeyColumnCount :
schema.Columns.Count;
+ var columnCount = keyOnly ? schema.KeyColumns.Length :
schema.Columns.Length;
var binaryTupleReader = new BinaryTupleReader(reader.ReadBinary(),
columnCount);
@@ -64,18 +64,18 @@ namespace Apache.Ignite.Internal.Table.Serialization
}
/// <inheritdoc/>
- public void Write(ref BinaryTupleBuilder tupleBuilder, T record,
Schema schema, int columnCount, Span<byte> noValueSet)
+ public void Write(ref BinaryTupleBuilder tupleBuilder, T record,
Schema schema, bool keyOnly, Span<byte> noValueSet)
{
- var cacheKey = (schema.Version, columnCount);
+ var cacheKey = (schema.Version, keyOnly);
var writeDelegate = _writers.TryGetValue(cacheKey, out var w)
? w
- : _writers.GetOrAdd(cacheKey, EmitWriter(schema, columnCount));
+ : _writers.GetOrAdd(cacheKey, EmitWriter(schema, keyOnly));
writeDelegate(ref tupleBuilder, noValueSet, record);
}
- private static WriteDelegate<T> EmitWriter(Schema schema, int count)
+ private static WriteDelegate<T> EmitWriter(Schema schema, bool keyOnly)
{
var type = typeof(T);
@@ -88,12 +88,12 @@ namespace Apache.Ignite.Internal.Table.Serialization
if (type.IsGenericType && type.GetGenericTypeDefinition() ==
typeof(KvPair<,>))
{
- return EmitKvWriter(schema, count, method);
+ return EmitKvWriter(schema, keyOnly, method);
}
var il = method.GetILGenerator();
- var columns = schema.Columns;
+ var columns = schema.GetColumnsFor(keyOnly);
var columnMap = type.GetFieldsByColumnName();
if (BinaryTupleMethods.GetWriteMethodOrNull(type) is { }
directWriteMethod)
@@ -116,7 +116,7 @@ namespace Apache.Ignite.Internal.Table.Serialization
il.Emit(OpCodes.Call, directWriteMethod);
- for (var index = 1; index < count; index++)
+ for (var index = 1; index < columns.Length; index++)
{
il.Emit(OpCodes.Ldarg_0); // writer
il.Emit(OpCodes.Ldarg_1); // noValueSet
@@ -130,9 +130,8 @@ namespace Apache.Ignite.Internal.Table.Serialization
int mappedCount = 0;
- for (var index = 0; index < count; index++)
+ foreach (var col in columns)
{
- var col = columns[index];
var fieldInfo = columnMap.TryGetValue(col.Name, out var
columnInfo) ? columnInfo.Field : null;
if (fieldInfo == null)
@@ -164,14 +163,14 @@ namespace Apache.Ignite.Internal.Table.Serialization
}
}
- ValidateMappedCount(mappedCount, type, schema, count);
+ ValidateMappedCount(mappedCount, type, schema, keyOnly);
il.Emit(OpCodes.Ret);
return
(WriteDelegate<T>)method.CreateDelegate(typeof(WriteDelegate<T>));
}
- private static WriteDelegate<T> EmitKvWriter(Schema schema, int count,
DynamicMethod method)
+ private static WriteDelegate<T> EmitKvWriter(Schema schema, bool
keyOnly, DynamicMethod method)
{
var (keyType, valType, keyField, valField, keyColumnMap,
valColumnMap) = GetKeyValTypes();
@@ -180,21 +179,19 @@ namespace Apache.Ignite.Internal.Table.Serialization
var il = method.GetILGenerator();
- var columns = schema.Columns;
+ var columns = schema.GetColumnsFor(keyOnly);
int mappedCount = 0;
- for (var index = 0; index < count; index++)
+ foreach (var col in columns)
{
- var col = columns[index];
-
FieldInfo? fieldInfo;
- if (keyWriteMethod != null && index == 0)
+ if (keyWriteMethod != null && col.IsKey)
{
fieldInfo = keyField;
}
- else if (valWriteMethod != null && index ==
schema.KeyColumnCount)
+ else if (valWriteMethod != null && !col.IsKey)
{
fieldInfo = valField;
}
@@ -219,7 +216,7 @@ namespace Apache.Ignite.Internal.Table.Serialization
{
ValidateFieldType(fieldInfo, col);
- var field = index < schema.KeyColumnCount ? keyField :
valField;
+ var field = col.IsKey ? keyField : valField;
if (field != fieldInfo)
{
@@ -262,7 +259,7 @@ namespace Apache.Ignite.Internal.Table.Serialization
keyWriteMethod != null ? null : keyType,
valWriteMethod != null ? null : valType,
schema,
- count);
+ keyOnly);
il.Emit(OpCodes.Ret);
@@ -306,16 +303,15 @@ namespace Apache.Ignite.Internal.Table.Serialization
var local = il.DeclareAndInitLocal(type);
- var columns = schema.Columns;
- var count = keyOnly ? schema.KeyColumnCount : columns.Count;
+ var columns = schema.GetColumnsFor(keyOnly);
var columnMap = type.GetFieldsByColumnName();
- for (var i = 0; i < count; i++)
+ foreach (var col in columns)
{
- var col = columns[i];
var fieldInfo = columnMap.TryGetValue(col.Name, out var
columnInfo) ? columnInfo.Field : null;
+ var idx = col.GetBinaryTupleIndex(keyOnly);
- EmitFieldRead(fieldInfo, il, col, i, local);
+ EmitFieldRead(fieldInfo, il, col, idx, local);
}
il.Emit(OpCodes.Ldloc_0); // res
@@ -338,22 +334,20 @@ namespace Apache.Ignite.Internal.Table.Serialization
var keyLocal = keyMethod == null ? il.DeclareAndInitLocal(keyType)
: null;
var valLocal = valMethod == null ? il.DeclareAndInitLocal(valType)
: null;
- var columns = schema.Columns;
- var count = keyOnly ? schema.KeyColumnCount : columns.Count;
+ var columns = schema.GetColumnsFor(keyOnly);
- for (var i = 0; i < count; i++)
+ foreach (var col in columns)
{
- var col = columns[i];
FieldInfo? fieldInfo;
LocalBuilder? local;
- if (i == 0 && keyMethod != null)
+ if (col.IsKey && keyMethod != null)
{
fieldInfo = keyField;
local = kvLocal;
ValidateSingleFieldMappingType(keyType, col);
}
- else if (i == schema.KeyColumnCount && valMethod != null)
+ else if (!col.IsKey && valMethod != null)
{
fieldInfo = valField;
local = kvLocal;
@@ -369,7 +363,8 @@ namespace Apache.Ignite.Internal.Table.Serialization
: null;
}
- EmitFieldRead(fieldInfo, il, col, i, local);
+ var idx = col.GetBinaryTupleIndex(keyOnly);
+ EmitFieldRead(fieldInfo, il, col, idx, local);
}
// Copy Key to KvPair.
@@ -475,7 +470,7 @@ namespace Apache.Ignite.Internal.Table.Serialization
}
}
- private static void ValidateMappedCount(int mappedCount, Type type,
Schema schema, int columnCount)
+ private static void ValidateMappedCount(int mappedCount, Type type,
Schema schema, bool keyOnly)
{
if (mappedCount == 0)
{
@@ -483,7 +478,7 @@ namespace Apache.Ignite.Internal.Table.Serialization
throw new ArgumentException($"Can't map '{type}' to columns
'{columnStr}'. Matching fields not found.");
}
- if (columnCount < schema.Columns.Count)
+ if (keyOnly)
{
// Key-only mode - skip "all fields are mapped" validation.
// It will be performed anyway when using the whole schema.
@@ -501,9 +496,8 @@ namespace Apache.Ignite.Internal.Table.Serialization
extraColumns.Add(field.Name);
}
- for (var index = 0; index < columnCount; index++)
+ foreach (var column in schema.GetColumnsFor(keyOnly))
{
- var column = schema.Columns[index];
extraColumns.Remove(column.Name);
}
@@ -511,7 +505,7 @@ namespace Apache.Ignite.Internal.Table.Serialization
}
}
- private static void ValidateKvMappedCount(int mappedCount, Type?
keyType, Type? valType, Schema schema, int columnCount)
+ private static void ValidateKvMappedCount(int mappedCount, Type?
keyType, Type? valType, Schema schema, bool keyOnly)
{
if (mappedCount == 0)
{
@@ -520,7 +514,7 @@ namespace Apache.Ignite.Internal.Table.Serialization
}
var keyFields = keyType?.GetColumns() ??
Array.Empty<ReflectionUtils.ColumnInfo>();
- var valFields = valType != null && columnCount >
schema.KeyColumnCount
+ var valFields = valType != null && !keyOnly
? valType.GetColumns()
: Array.Empty<ReflectionUtils.ColumnInfo>();
@@ -544,9 +538,8 @@ namespace Apache.Ignite.Internal.Table.Serialization
extraColumns.Add(field.Name);
}
- for (var index = 0; index < columnCount; index++)
+ foreach (var column in schema.GetColumnsFor(keyOnly))
{
- var column = schema.Columns[index];
extraColumns.Remove(column.Name);
}
diff --git
a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/TuplePairSerializerHandler.cs
b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/TuplePairSerializerHandler.cs
index 7aa0aa24f5..18474d6846 100644
---
a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/TuplePairSerializerHandler.cs
+++
b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/TuplePairSerializerHandler.cs
@@ -47,21 +47,32 @@ internal sealed class TuplePairSerializerHandler :
IRecordSerializerHandler<KvPa
/// <inheritdoc/>
public KvPair<IIgniteTuple, IIgniteTuple> Read(ref MsgPackReader reader,
Schema schema, bool keyOnly = false)
{
- var columns = schema.Columns;
- var count = keyOnly ? schema.KeyColumnCount : columns.Count;
- var keyTuple = new IgniteTuple(count);
- var valTuple = keyOnly ? null! : new
IgniteTuple(schema.ValueColumnCount);
- var tupleReader = new BinaryTupleReader(reader.ReadBinary(), count);
-
- for (var index = 0; index < count; index++)
+ if (keyOnly)
{
- var column = columns[index];
+ var keyTuple = new IgniteTuple(schema.KeyColumns.Length);
+ var tupleReader = new BinaryTupleReader(reader.ReadBinary(),
schema.KeyColumns.Length);
- var tuple = index < schema.KeyColumnCount ? keyTuple : valTuple;
- tuple[column.Name] = tupleReader.GetObject(index, column.Type,
column.Scale);
+ foreach (var column in schema.KeyColumns)
+ {
+ keyTuple[column.Name] = tupleReader.GetObject(column.KeyIndex,
column.Type, column.Scale);
+ }
+
+ return new(keyTuple);
}
+ else
+ {
+ var keyTuple = new IgniteTuple(schema.KeyColumns.Length);
+ var valTuple = new IgniteTuple(schema.ValColumns.Length);
+ var tupleReader = new BinaryTupleReader(reader.ReadBinary(),
schema.Columns.Length);
- return new(keyTuple, valTuple);
+ foreach (var column in schema.Columns)
+ {
+ var tuple = column.IsKey ? keyTuple : valTuple;
+ tuple[column.Name] = tupleReader.GetObject(column.SchemaIndex,
column.Type, column.Scale);
+ }
+
+ return new(keyTuple, valTuple);
+ }
}
/// <inheritdoc/>
@@ -69,7 +80,7 @@ internal sealed class TuplePairSerializerHandler :
IRecordSerializerHandler<KvPa
ref BinaryTupleBuilder tupleBuilder,
KvPair<IIgniteTuple, IIgniteTuple> record,
Schema schema,
- int columnCount,
+ bool keyOnly,
Span<byte> noValueSet)
{
var key = record.Key;
@@ -77,17 +88,17 @@ internal sealed class TuplePairSerializerHandler :
IRecordSerializerHandler<KvPa
IgniteArgumentCheck.NotNull(key);
- if (columnCount > schema.KeyColumnCount)
+ if (!keyOnly)
{
IgniteArgumentCheck.NotNull(val);
}
int written = 0;
+ var columns = schema.GetColumnsFor(keyOnly);
- for (var index = 0; index < columnCount; index++)
+ foreach (var col in columns)
{
- var col = schema.Columns[index];
- var rec = index < schema.KeyColumnCount ? key : val;
+ var rec = col.IsKey ? key : val;
var colIdx = rec.GetOrdinal(col.Name);
if (colIdx >= 0)
@@ -101,7 +112,7 @@ internal sealed class TuplePairSerializerHandler :
IRecordSerializerHandler<KvPa
}
}
- ValidateMappedCount(record, schema, columnCount, written);
+ ValidateMappedCount(record, schema, columns.Length, written);
}
private static void ValidateMappedCount(KvPair<IIgniteTuple, IIgniteTuple>
record, Schema schema, int columnCount, int written)
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 edeccb4c82..1e8dfdadeb 100644
---
a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/TupleSerializerHandler.cs
+++
b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/TupleSerializerHandler.cs
@@ -19,7 +19,6 @@ namespace Apache.Ignite.Internal.Table.Serialization
{
using System;
using System.Collections.Generic;
- using System.Diagnostics;
using System.Linq;
using Common;
using Ignite.Table;
@@ -49,20 +48,17 @@ namespace Apache.Ignite.Internal.Table.Serialization
/// </summary>
/// <param name="buf">Buffer.</param>
/// <param name="schema">Schema.</param>
- /// <param name="count">Column count to read.</param>
+ /// <param name="keyOnly">Whether to read only the key columns.</param>
/// <returns>Tuple.</returns>
- public static IgniteTuple ReadTuple(ReadOnlySpan<byte> buf, Schema
schema, int count)
+ public static IgniteTuple ReadTuple(ReadOnlySpan<byte> buf, Schema
schema, bool keyOnly)
{
- Debug.Assert(count <= schema.Columns.Count, "count <=
schema.Columns.Count");
+ var columns = schema.GetColumnsFor(keyOnly);
+ var tuple = new IgniteTuple(columns.Length);
+ var tupleReader = new BinaryTupleReader(buf, columns.Length);
- var tuple = new IgniteTuple(count);
- var tupleReader = new BinaryTupleReader(buf, count);
- var columns = schema.Columns;
-
- for (var index = 0; index < count; index++)
+ foreach (var column in columns)
{
- var column = columns[index];
- tuple[column.Name] = tupleReader.GetObject(index, column.Type,
column.Scale);
+ tuple[column.Name] =
tupleReader.GetObject(column.GetBinaryTupleIndex(keyOnly), column.Type,
column.Scale);
}
return tuple;
@@ -73,13 +69,14 @@ namespace Apache.Ignite.Internal.Table.Serialization
/// </summary>
/// <param name="buf">Binary tuple buffer.</param>
/// <param name="schema">Schema.</param>
- /// <param name="count">Column count.</param>
+ /// <param name="keyOnly">Whether <paramref name="buf"/> is a key-only
binary tuple.</param>
/// <param name="index">Column index.</param>
/// <returns>Column value.</returns>
- public static object? ReadObject(ReadOnlySpan<byte> buf, Schema
schema, int count, int index)
+ public static object? ReadObject(ReadOnlySpan<byte> buf, Schema
schema, bool keyOnly, int index)
{
- var tupleReader = new BinaryTupleReader(buf, count);
- var column = schema.Columns[index];
+ var columns = schema.GetColumnsFor(keyOnly);
+ var tupleReader = new BinaryTupleReader(buf, columns.Length);
+ var column = columns[index];
return tupleReader.GetObject(index, column.Type, column.Scale);
}
@@ -89,16 +86,16 @@ namespace Apache.Ignite.Internal.Table.Serialization
new BinaryTupleIgniteTupleAdapter(
data: reader.ReadBinary().ToArray(),
schema: schema,
- fieldCount: keyOnly ? schema.KeyColumnCount :
schema.Columns.Count);
+ keyOnly);
/// <inheritdoc/>
- public void Write(ref BinaryTupleBuilder tupleBuilder, IIgniteTuple
record, Schema schema, int columnCount, Span<byte> noValueSet)
+ public void Write(ref BinaryTupleBuilder tupleBuilder, IIgniteTuple
record, Schema schema, bool keyOnly, Span<byte> noValueSet)
{
int written = 0;
+ var columns = keyOnly ? schema.KeyColumns : schema.Columns;
- for (var index = 0; index < columnCount; index++)
+ foreach (var col in columns)
{
- var col = schema.Columns[index];
var colIdx = record.GetOrdinal(col.Name);
if (colIdx >= 0)
@@ -112,7 +109,7 @@ namespace Apache.Ignite.Internal.Table.Serialization
}
}
- ValidateMappedCount(record, schema, columnCount, written);
+ ValidateMappedCount(record, schema, columns.Length, written);
}
private static void ValidateMappedCount(IIgniteTuple record, Schema
schema, int columnCount, int written)
@@ -130,12 +127,10 @@ namespace Apache.Ignite.Internal.Table.Serialization
{
var name = record.GetName(i);
- if (extraColumns.Contains(name))
+ if (!extraColumns.Add(name))
{
throw new ArgumentException("Duplicate column in
Tuple: " + name, nameof(record));
}
-
- extraColumns.Add(name);
}
for (var i = 0; i < columnCount; i++)
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Table.cs
b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Table.cs
index 4f39ad1ea3..ffb772e985 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Table.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Table.cs
@@ -332,8 +332,6 @@ namespace Apache.Ignite.Internal.Table
{
var schemaVersion = r.ReadInt32();
var columnCount = r.ReadInt32();
- var keyColumnCount = 0;
- var colocationColumnCount = 0;
var columns = new Column[columnCount];
@@ -347,7 +345,6 @@ namespace Apache.Ignite.Internal.Table
var name = r.ReadString();
var type = r.ReadInt32();
var keyIndex = r.ReadInt32();
- var isKey = keyIndex >= 0;
var isNullable = r.ReadBoolean();
var colocationIndex = r.ReadInt32();
var scale = r.ReadInt32();
@@ -355,27 +352,10 @@ namespace Apache.Ignite.Internal.Table
r.Skip(propertyCount - expectedCount);
- var column = new Column(name, (ColumnType)type, isNullable,
isKey, colocationIndex, i, scale, precision);
-
- columns[i] = column;
-
- if (isKey)
- {
- keyColumnCount++;
- }
-
- if (colocationIndex >= 0)
- {
- colocationColumnCount++;
- }
+ columns[i] = new Column(name, (ColumnType)type, isNullable,
keyIndex, colocationIndex, i, scale, precision);
}
- var schema = new Schema(
- Version: schemaVersion,
- TableId: Id,
- KeyColumnCount: keyColumnCount,
- ColocationColumnCount: colocationColumnCount,
- Columns: columns);
+ var schema = Schema.CreateInstance(schemaVersion, Id, columns);
_schemas[schemaVersion] = Task.FromResult(schema);