IGNITE-4045 .NET: Support DML API This closes #1309
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/b7908d7a Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/b7908d7a Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/b7908d7a Branch: refs/heads/ignite-comm-balance-master Commit: b7908d7a65f07615f2ff183e107c5002658bd511 Parents: d6d42c2 Author: Pavel Tupitsyn <ptupit...@apache.org> Authored: Mon Jan 16 17:48:08 2017 +0300 Committer: Pavel Tupitsyn <ptupit...@apache.org> Committed: Mon Jan 16 17:48:08 2017 +0300 ---------------------------------------------------------------------- .../utils/PlatformConfigurationUtils.java | 128 +++++++- .../Apache.Ignite.Core.Tests.csproj | 3 + .../Binary/BinaryBuilderSelfTest.cs | 159 ++++++---- .../BinaryBuilderSelfTestArrayIdentity.cs | 34 +++ .../Binary/BinaryEqualityComparerTest.cs | 279 +++++++++++++++++ .../Binary/IO/BinaryStreamsTest.cs | 19 ++ .../Cache/CacheConfigurationTest.cs | 5 +- .../Cache/Query/CacheDmlQueriesTest.cs | 296 +++++++++++++++++++ .../IgniteConfigurationSerializerTest.cs | 46 ++- .../IgniteConfigurationTest.cs | 28 ++ .../Apache.Ignite.Core.csproj | 4 + .../Binary/BinaryArrayEqualityComparer.cs | 149 ++++++++++ .../Binary/BinaryConfiguration.cs | 24 ++ .../Binary/BinaryTypeConfiguration.cs | 14 + .../Cache/Configuration/QueryEntity.cs | 33 ++- .../Cache/Configuration/QueryField.cs | 6 + .../Apache.Ignite.Core/IgniteConfiguration.cs | 85 ++++-- .../IgniteConfigurationSection.xsd | 19 ++ .../Apache.Ignite.Core/Impl/Binary/Binary.cs | 28 +- .../Binary/BinaryEqualityComparerSerializer.cs | 99 +++++++ .../Impl/Binary/BinaryFieldEqualityComparer.cs | 138 +++++++++ .../Impl/Binary/BinaryFullTypeDescriptor.cs | 21 +- .../Impl/Binary/BinaryObject.cs | 31 +- .../Impl/Binary/BinaryObjectBuilder.cs | 62 +++- .../Impl/Binary/BinaryObjectHeader.cs | 21 +- .../Impl/Binary/BinaryObjectSchemaHolder.cs | 22 ++ .../Binary/BinarySurrogateTypeDescriptor.cs | 6 + .../Impl/Binary/BinarySystemHandlers.cs | 6 +- .../Impl/Binary/BinaryWriter.cs | 11 +- .../Impl/Binary/DateTimeHolder.cs | 15 +- .../Impl/Binary/IBinaryEqualityComparer.cs | 53 ++++ .../Impl/Binary/IBinaryTypeDescriptor.cs | 5 + .../Impl/Binary/Io/BinaryHeapStream.cs | 9 + .../Impl/Binary/Io/BinaryStreamBase.cs | 13 + .../Impl/Binary/Io/IBinaryStream.cs | 25 ++ .../Impl/Binary/Marshaller.cs | 22 +- .../Impl/Binary/SerializableObjectHolder.cs | 16 + .../Common/IgniteConfigurationXmlSerializer.cs | 5 +- .../Impl/Memory/PlatformMemoryStream.cs | 16 + 39 files changed, 1803 insertions(+), 152 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformConfigurationUtils.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformConfigurationUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformConfigurationUtils.java index f845675..c0fde97 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformConfigurationUtils.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformConfigurationUtils.java @@ -17,8 +17,12 @@ package org.apache.ignite.internal.processors.platform.utils; +import org.apache.ignite.binary.BinaryArrayIdentityResolver; +import org.apache.ignite.binary.BinaryFieldIdentityResolver; +import org.apache.ignite.binary.BinaryIdentityResolver; import org.apache.ignite.binary.BinaryRawReader; import org.apache.ignite.binary.BinaryRawWriter; +import org.apache.ignite.binary.BinaryTypeConfiguration; import org.apache.ignite.cache.CacheAtomicWriteOrderMode; import org.apache.ignite.cache.CacheAtomicityMode; import org.apache.ignite.cache.CacheMemoryMode; @@ -68,6 +72,8 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.HashSet; /** * Configuration utils. @@ -428,14 +434,25 @@ public class PlatformConfigurationUtils { // Fields int cnt = in.readInt(); + Set<String> keyFields = new HashSet<>(cnt); if (cnt > 0) { LinkedHashMap<String, String> fields = new LinkedHashMap<>(cnt); - for (int i = 0; i < cnt; i++) - fields.put(in.readString(), in.readString()); + for (int i = 0; i < cnt; i++) { + String fieldName = in.readString(); + String fieldType = in.readString(); + + fields.put(fieldName, fieldType); + + if (in.readBoolean()) + keyFields.add(fieldName); + } res.setFields(fields); + + if (!keyFields.isEmpty()) + res.setKeyFields(keyFields); } // Aliases @@ -539,11 +556,29 @@ public class PlatformConfigurationUtils { cfg.setCommunicationSpi(comm); } - if (in.readBoolean()) { + if (in.readBoolean()) { // binary config is present if (cfg.getBinaryConfiguration() == null) cfg.setBinaryConfiguration(new BinaryConfiguration()); - cfg.getBinaryConfiguration().setCompactFooter(in.readBoolean()); + if (in.readBoolean()) // compact footer is set + cfg.getBinaryConfiguration().setCompactFooter(in.readBoolean()); + + int typeCnt = in.readInt(); + + if (typeCnt > 0) { + Collection<BinaryTypeConfiguration> types = new ArrayList<>(typeCnt); + + for (int i = 0; i < typeCnt; i++) { + BinaryTypeConfiguration type = new BinaryTypeConfiguration(in.readString()); + + type.setEnum(in.readBoolean()); + type.setIdentityResolver(readBinaryIdentityResolver(in)); + + types.add(type); + } + + cfg.getBinaryConfiguration().setTypeConfigurations(types); + } } int attrCnt = in.readInt(); @@ -812,11 +847,14 @@ public class PlatformConfigurationUtils { LinkedHashMap<String, String> fields = queryEntity.getFields(); if (fields != null) { + Set<String> keyFields = queryEntity.getKeyFields(); + writer.writeInt(fields.size()); for (Map.Entry<String, String> field : fields.entrySet()) { writer.writeString(field.getKey()); writer.writeString(field.getValue()); + writer.writeBoolean(keyFields != null && keyFields.contains(field.getKey())); } } else @@ -941,11 +979,29 @@ public class PlatformConfigurationUtils { w.writeBoolean(false); BinaryConfiguration bc = cfg.getBinaryConfiguration(); - w.writeBoolean(bc != null); - if (bc != null) + if (bc != null) { + w.writeBoolean(true); // binary config exists + w.writeBoolean(true); // compact footer is set w.writeBoolean(bc.isCompactFooter()); + Collection<BinaryTypeConfiguration> types = bc.getTypeConfigurations(); + + if (types != null) { + w.writeInt(types.size()); + + for (BinaryTypeConfiguration type : types) { + w.writeString(type.getTypeName()); + w.writeBoolean(type.isEnum()); + writeBinaryIdentityResolver(w, type.getIdentityResolver()); + } + } + else + w.writeInt(0); + } + else + w.writeBoolean(false); + Map<String, ?> attrs = cfg.getUserAttributes(); if (attrs != null) { @@ -1117,6 +1173,66 @@ public class PlatformConfigurationUtils { } /** + * Reads resolver + * + * @param r Reader. + * @return Resolver. + */ + private static BinaryIdentityResolver readBinaryIdentityResolver(BinaryRawReader r) { + int type = r.readByte(); + + switch (type) { + case 0: + return null; + + case 1: + return new BinaryArrayIdentityResolver(); + + case 2: + int cnt = r.readInt(); + + String[] fields = new String[cnt]; + + for (int i = 0; i < cnt; i++) + fields[i] = r.readString(); + + return new BinaryFieldIdentityResolver().setFieldNames(fields); + + default: + assert false; + return null; + } + } + + /** + * Writes the resolver. + * + * @param w Writer. + * @param resolver Resolver. + */ + private static void writeBinaryIdentityResolver(BinaryRawWriter w, BinaryIdentityResolver resolver) { + if (resolver instanceof BinaryArrayIdentityResolver) + w.writeByte((byte)1); + else if (resolver instanceof BinaryFieldIdentityResolver) { + w.writeByte((byte)2); + + String[] fields = ((BinaryFieldIdentityResolver)resolver).getFieldNames(); + + if (fields != null) { + w.writeInt(fields.length); + + for (String field : fields) + w.writeString(field); + } + else + w.writeInt(0); + } + else { + w.writeByte((byte)0); + } + } + + /** * Private constructor. */ private PlatformConfigurationUtils() { http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj index 78a08d2..e09c682 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj @@ -67,6 +67,8 @@ <Reference Include="System.Xml.Linq" /> </ItemGroup> <ItemGroup> + <Compile Include="Binary\BinaryBuilderSelfTestArrayIdentity.cs" /> + <Compile Include="Binary\BinaryEqualityComparerTest.cs" /> <Compile Include="Binary\BinaryReaderWriterTest.cs" /> <Compile Include="Binary\IO\BinaryStreamsTest.cs" /> <Compile Include="Binary\JavaTypeMappingTest.cs" /> @@ -76,6 +78,7 @@ <Compile Include="Cache\CacheMetricsTest.cs" /> <Compile Include="Cache\CacheResultTest.cs" /> <Compile Include="Cache\CacheSwapSpaceTest.cs" /> + <Compile Include="Cache\Query\CacheDmlQueriesTest.cs" /> <Compile Include="Cache\CacheAbstractTransactionalTest.cs" /> <Compile Include="Cache\Store\CacheStoreAdapterTest.cs" /> <Compile Include="Collections\MultiValueDictionaryTest.cs" /> http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryBuilderSelfTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryBuilderSelfTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryBuilderSelfTest.cs index c280255..d6551b5 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryBuilderSelfTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryBuilderSelfTest.cs @@ -58,33 +58,57 @@ namespace Apache.Ignite.Core.Tests.Binary { TypeConfigurations = new List<BinaryTypeConfiguration> { - new BinaryTypeConfiguration(typeof (Empty)), - new BinaryTypeConfiguration(typeof (Primitives)), - new BinaryTypeConfiguration(typeof (PrimitiveArrays)), - new BinaryTypeConfiguration(typeof (StringDateGuidEnum)), - new BinaryTypeConfiguration(typeof (WithRaw)), - new BinaryTypeConfiguration(typeof (MetaOverwrite)), - new BinaryTypeConfiguration(typeof (NestedOuter)), - new BinaryTypeConfiguration(typeof (NestedInner)), - new BinaryTypeConfiguration(typeof (MigrationOuter)), - new BinaryTypeConfiguration(typeof (MigrationInner)), - new BinaryTypeConfiguration(typeof (InversionOuter)), - new BinaryTypeConfiguration(typeof (InversionInner)), - new BinaryTypeConfiguration(typeof (CompositeOuter)), - new BinaryTypeConfiguration(typeof (CompositeInner)), - new BinaryTypeConfiguration(typeof (CompositeArray)), - new BinaryTypeConfiguration(typeof (CompositeContainer)), - new BinaryTypeConfiguration(typeof (ToBinary)), - new BinaryTypeConfiguration(typeof (Remove)), - new BinaryTypeConfiguration(typeof (RemoveInner)), - new BinaryTypeConfiguration(typeof (BuilderInBuilderOuter)), - new BinaryTypeConfiguration(typeof (BuilderInBuilderInner)), - new BinaryTypeConfiguration(typeof (BuilderCollection)), - new BinaryTypeConfiguration(typeof (BuilderCollectionItem)), - new BinaryTypeConfiguration(typeof (DecimalHolder)), - new BinaryTypeConfiguration(TypeEmpty), - new BinaryTypeConfiguration(typeof(TestEnumRegistered)), + new BinaryTypeConfiguration(typeof(Empty)) {EqualityComparer = GetIdentityResolver()}, + new BinaryTypeConfiguration(typeof(Primitives)) {EqualityComparer = GetIdentityResolver()}, + new BinaryTypeConfiguration(typeof(PrimitiveArrays)) {EqualityComparer = GetIdentityResolver()}, + new BinaryTypeConfiguration(typeof(StringDateGuidEnum)) + { + EqualityComparer = GetIdentityResolver() + }, + new BinaryTypeConfiguration(typeof(WithRaw)) {EqualityComparer = GetIdentityResolver()}, + new BinaryTypeConfiguration(typeof(MetaOverwrite)) {EqualityComparer = GetIdentityResolver()}, + new BinaryTypeConfiguration(typeof(NestedOuter)) {EqualityComparer = GetIdentityResolver()}, + new BinaryTypeConfiguration(typeof(NestedInner)) {EqualityComparer = GetIdentityResolver()}, + new BinaryTypeConfiguration(typeof(MigrationOuter)) {EqualityComparer = GetIdentityResolver()}, + new BinaryTypeConfiguration(typeof(MigrationInner)) {EqualityComparer = GetIdentityResolver()}, + new BinaryTypeConfiguration(typeof(InversionOuter)) {EqualityComparer = GetIdentityResolver()}, + new BinaryTypeConfiguration(typeof(InversionInner)) {EqualityComparer = GetIdentityResolver()}, + new BinaryTypeConfiguration(typeof(CompositeOuter)) {EqualityComparer = GetIdentityResolver()}, + new BinaryTypeConfiguration(typeof(CompositeInner)) {EqualityComparer = GetIdentityResolver()}, + new BinaryTypeConfiguration(typeof(CompositeArray)) {EqualityComparer = GetIdentityResolver()}, + new BinaryTypeConfiguration(typeof(CompositeContainer)) + { + EqualityComparer = GetIdentityResolver() + }, + new BinaryTypeConfiguration(typeof(ToBinary)) {EqualityComparer = GetIdentityResolver()}, + new BinaryTypeConfiguration(typeof(Remove)) {EqualityComparer = GetIdentityResolver()}, + new BinaryTypeConfiguration(typeof(RemoveInner)) {EqualityComparer = GetIdentityResolver()}, + new BinaryTypeConfiguration(typeof(BuilderInBuilderOuter)) + { + EqualityComparer = GetIdentityResolver() + }, + new BinaryTypeConfiguration(typeof(BuilderInBuilderInner)) + { + EqualityComparer = GetIdentityResolver() + }, + new BinaryTypeConfiguration(typeof(BuilderCollection)) + { + EqualityComparer = GetIdentityResolver() + }, + new BinaryTypeConfiguration(typeof(BuilderCollectionItem)) + { + EqualityComparer = GetIdentityResolver() + }, + new BinaryTypeConfiguration(typeof(DecimalHolder)) {EqualityComparer = GetIdentityResolver()}, + new BinaryTypeConfiguration(TypeEmpty) {EqualityComparer = GetIdentityResolver()}, + new BinaryTypeConfiguration(typeof(TestEnumRegistered)) + { + EqualityComparer = GetIdentityResolver() + }, new BinaryTypeConfiguration(typeof(NameMapperTestType)) + { + EqualityComparer = GetIdentityResolver() + } }, DefaultIdMapper = new IdMapper(), DefaultNameMapper = new NameMapper(), @@ -106,6 +130,14 @@ namespace Apache.Ignite.Core.Tests.Binary } /// <summary> + /// Gets the identity resolver. + /// </summary> + protected virtual IEqualityComparer<IBinaryObject> GetIdentityResolver() + { + return null; + } + + /// <summary> /// Tear down routine. /// </summary> [TestFixtureTearDown] @@ -535,7 +567,7 @@ namespace Apache.Ignite.Core.Tests.Binary IBinaryObject binObj = _grid.GetBinary().GetBuilder(typeof(Empty)).Build(); Assert.IsNotNull(binObj); - Assert.AreEqual(0, binObj.GetHashCode()); + Assert.AreEqual(GetIdentityResolver() == null ? 0 : 1, binObj.GetHashCode()); IBinaryType meta = binObj.GetBinaryType(); @@ -557,7 +589,7 @@ namespace Apache.Ignite.Core.Tests.Binary IBinaryObject binObj = _grid.GetBinary().GetBuilder(TypeEmpty).Build(); Assert.IsNotNull(binObj); - Assert.AreEqual(0, binObj.GetHashCode()); + Assert.AreEqual(GetIdentityResolver() == null ? 0 : 1, binObj.GetHashCode()); IBinaryType meta = binObj.GetBinaryType(); @@ -602,7 +634,9 @@ namespace Apache.Ignite.Core.Tests.Binary var obj2 = bin.GetBuilder("myType").SetStringField("str", "foo").SetIntField("int", 1).Build(); Assert.AreEqual(obj1, obj2); - Assert.AreEqual(obj1.GetHashCode(), obj2.GetHashCode()); + + Assert.AreEqual(0, obj1.GetHashCode()); + Assert.AreEqual(0, obj2.GetHashCode()); Assert.IsTrue(Regex.IsMatch(obj1.ToString(), @"myType \[idHash=[0-9]+, str=foo, int=1\]")); } @@ -630,7 +664,7 @@ namespace Apache.Ignite.Core.Tests.Binary CheckPrimitiveFields1(binObj); // Specific setter methods. - binObj = _grid.GetBinary().GetBuilder(typeof(Primitives)) + var binObj2 = _grid.GetBinary().GetBuilder(typeof(Primitives)) .SetByteField("fByte", 1) .SetBooleanField("fBool", true) .SetShortField("fShort", 2) @@ -643,7 +677,11 @@ namespace Apache.Ignite.Core.Tests.Binary .SetHashCode(100) .Build(); - CheckPrimitiveFields1(binObj); + CheckPrimitiveFields1(binObj2); + + // Check equality. + Assert.AreEqual(binObj, binObj2); + Assert.AreEqual(binObj.GetHashCode(), binObj2.GetHashCode()); // Overwrite with generic methods. binObj = binObj.ToBuilder() @@ -656,13 +694,12 @@ namespace Apache.Ignite.Core.Tests.Binary .SetField<float>("fFloat", 11) .SetField<double>("fDouble", 12) .SetField("fDecimal", 13.13m) - .SetHashCode(200) .Build(); CheckPrimitiveFields2(binObj); // Overwrite with specific methods. - binObj = binObj.ToBuilder() + binObj2 = binObj.ToBuilder() .SetByteField("fByte", 7) .SetBooleanField("fBool", false) .SetShortField("fShort", 8) @@ -672,10 +709,13 @@ namespace Apache.Ignite.Core.Tests.Binary .SetFloatField("fFloat", 11) .SetDoubleField("fDouble", 12) .SetDecimalField("fDecimal", 13.13m) - .SetHashCode(200) .Build(); CheckPrimitiveFields2(binObj); + + // Check equality. + Assert.AreEqual(binObj, binObj2); + Assert.AreEqual(binObj.GetHashCode(), binObj2.GetHashCode()); } /// <summary> @@ -729,8 +769,6 @@ namespace Apache.Ignite.Core.Tests.Binary /// </summary> private static void CheckPrimitiveFields2(IBinaryObject binObj) { - Assert.AreEqual(200, binObj.GetHashCode()); - Assert.AreEqual(7, binObj.GetField<byte>("fByte")); Assert.AreEqual(false, binObj.GetField<bool>("fBool")); Assert.AreEqual(8, binObj.GetField<short>("fShort")); @@ -777,7 +815,7 @@ namespace Apache.Ignite.Core.Tests.Binary CheckPrimitiveArrayFields1(binObj); // Specific setters. - binObj = _grid.GetBinary().GetBuilder(typeof(PrimitiveArrays)) + var binObj2 = _grid.GetBinary().GetBuilder(typeof(PrimitiveArrays)) .SetByteArrayField("fByte", new byte[] {1}) .SetBooleanArrayField("fBool", new[] {true}) .SetShortArrayField("fShort", new short[] {2}) @@ -789,7 +827,13 @@ namespace Apache.Ignite.Core.Tests.Binary .SetDecimalArrayField("fDecimal", new decimal?[] {7.7m}) .SetHashCode(100) .Build(); - + + CheckPrimitiveArrayFields1(binObj2); + + // Check equality. + Assert.AreEqual(binObj, binObj2); + Assert.AreEqual(binObj.GetHashCode(), binObj2.GetHashCode()); + // Overwrite with generic setter. binObj = _grid.GetBinary().GetBuilder(binObj) .SetField("fByte", new byte[] { 7 }) @@ -801,13 +845,12 @@ namespace Apache.Ignite.Core.Tests.Binary .SetField("fFloat", new float[] { 11 }) .SetField("fDouble", new double[] { 12 }) .SetField("fDecimal", new decimal?[] { 13.13m }) - .SetHashCode(200) .Build(); CheckPrimitiveArrayFields2(binObj); // Overwrite with specific setters. - binObj = _grid.GetBinary().GetBuilder(binObj) + binObj2 = _grid.GetBinary().GetBuilder(binObj) .SetByteArrayField("fByte", new byte[] { 7 }) .SetBooleanArrayField("fBool", new[] { false }) .SetShortArrayField("fShort", new short[] { 8 }) @@ -817,10 +860,13 @@ namespace Apache.Ignite.Core.Tests.Binary .SetFloatArrayField("fFloat", new float[] { 11 }) .SetDoubleArrayField("fDouble", new double[] { 12 }) .SetDecimalArrayField("fDecimal", new decimal?[] { 13.13m }) - .SetHashCode(200) .Build(); CheckPrimitiveArrayFields2(binObj); + + // Check equality. + Assert.AreEqual(binObj, binObj2); + Assert.AreEqual(binObj.GetHashCode(), binObj2.GetHashCode()); } /// <summary> @@ -874,8 +920,6 @@ namespace Apache.Ignite.Core.Tests.Binary /// </summary> private static void CheckPrimitiveArrayFields2(IBinaryObject binObj) { - Assert.AreEqual(200, binObj.GetHashCode()); - Assert.AreEqual(new byte[] { 7 }, binObj.GetField<byte[]>("fByte")); Assert.AreEqual(new[] { false }, binObj.GetField<bool[]>("fBool")); Assert.AreEqual(new short[] { 8 }, binObj.GetField<short[]>("fShort")); @@ -910,7 +954,7 @@ namespace Apache.Ignite.Core.Tests.Binary Guid? nGuid = Guid.NewGuid(); // Generic setters. - IBinaryObject binObj = _grid.GetBinary().GetBuilder(typeof(StringDateGuidEnum)) + var binObj = _grid.GetBinary().GetBuilder(typeof(StringDateGuidEnum)) .SetField("fStr", "str") .SetField("fNDate", nDate) .SetTimestampField("fNTimestamp", nDate) @@ -927,7 +971,7 @@ namespace Apache.Ignite.Core.Tests.Binary CheckStringDateGuidEnum1(binObj, nDate, nGuid); // Specific setters. - binObj = _grid.GetBinary().GetBuilder(typeof(StringDateGuidEnum)) + var binObj2 = _grid.GetBinary().GetBuilder(typeof(StringDateGuidEnum)) .SetStringField("fStr", "str") .SetField("fNDate", nDate) .SetTimestampField("fNTimestamp", nDate) @@ -941,7 +985,11 @@ namespace Apache.Ignite.Core.Tests.Binary .SetHashCode(100) .Build(); - CheckStringDateGuidEnum1(binObj, nDate, nGuid); + CheckStringDateGuidEnum1(binObj2, nDate, nGuid); + + // Check equality. + Assert.AreEqual(binObj, binObj2); + Assert.AreEqual(binObj.GetHashCode(), binObj2.GetHashCode()); // Overwrite. nDate = DateTime.Now.ToUniversalTime(); @@ -958,13 +1006,12 @@ namespace Apache.Ignite.Core.Tests.Binary .SetTimestampArrayField("fTimestampArr", new[] { nDate }) .SetField("fGuidArr", new[] { nGuid }) .SetField("fEnumArr", new[] { TestEnum.Two }) - .SetHashCode(200) .Build(); CheckStringDateGuidEnum2(binObj, nDate, nGuid); // Overwrite with specific setters - binObj = _grid.GetBinary().GetBuilder(typeof(StringDateGuidEnum)) + binObj2 = _grid.GetBinary().GetBuilder(typeof(StringDateGuidEnum)) .SetStringField("fStr", "str2") .SetField("fNDate", nDate) .SetTimestampField("fNTimestamp", nDate) @@ -975,10 +1022,13 @@ namespace Apache.Ignite.Core.Tests.Binary .SetTimestampArrayField("fTimestampArr", new[] { nDate }) .SetGuidArrayField("fGuidArr", new[] { nGuid }) .SetEnumArrayField("fEnumArr", new[] { TestEnum.Two }) - .SetHashCode(200) .Build(); - CheckStringDateGuidEnum2(binObj, nDate, nGuid); + CheckStringDateGuidEnum2(binObj2, nDate, nGuid); + + // Check equality. + Assert.AreEqual(binObj, binObj2); + Assert.AreEqual(binObj.GetHashCode(), binObj2.GetHashCode()); } /// <summary> @@ -1074,8 +1124,6 @@ namespace Apache.Ignite.Core.Tests.Binary /// </summary> private static void CheckStringDateGuidEnum2(IBinaryObject binObj, DateTime? nDate, Guid? nGuid) { - Assert.AreEqual(200, binObj.GetHashCode()); - Assert.AreEqual("str2", binObj.GetField<string>("fStr")); Assert.AreEqual(nDate, binObj.GetField<DateTime?>("fNDate")); Assert.AreEqual(nDate, binObj.GetField<DateTime?>("fNTimestamp")); @@ -1432,7 +1480,8 @@ namespace Apache.Ignite.Core.Tests.Binary IBinaryObjectBuilder builder = _grid.GetBinary().GetBuilder(typeof(MigrationOuter)); - builder.SetHashCode(outer.GetHashCode()); + if (GetIdentityResolver() == null) + builder.SetHashCode(outer.GetHashCode()); builder.SetField<object>("inner1", inner); builder.SetField<object>("inner2", inner); @@ -1642,6 +1691,7 @@ namespace Apache.Ignite.Core.Tests.Binary } Assert.AreEqual(binEnums[0], binEnums[1]); + Assert.AreEqual(binEnums[0].GetHashCode(), binEnums[1].GetHashCode()); } /// <summary> @@ -1659,6 +1709,9 @@ namespace Apache.Ignite.Core.Tests.Binary [Test] public void TestRemoteBinaryMode() { + if (GetIdentityResolver() != null) + return; // When identity resolver is set, it is required to have the same config on all nodes. + var cfg = new IgniteConfiguration(TestUtils.GetTestConfiguration()) { GridName = "grid2", http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryBuilderSelfTestArrayIdentity.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryBuilderSelfTestArrayIdentity.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryBuilderSelfTestArrayIdentity.cs new file mode 100644 index 0000000..b5e767c --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryBuilderSelfTestArrayIdentity.cs @@ -0,0 +1,34 @@ +/* + * 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.Core.Tests.Binary +{ + using System.Collections.Generic; + using Apache.Ignite.Core.Binary; + + /// <summary> + /// Tests with array equality comparer (identity resolver). + /// </summary> + public class BinaryBuilderSelfTestArrayIdentity : BinaryBuilderSelfTest + { + /** <inheritdoc /> */ + protected override IEqualityComparer<IBinaryObject> GetIdentityResolver() + { + return new BinaryArrayEqualityComparer(); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryEqualityComparerTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryEqualityComparerTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryEqualityComparerTest.cs new file mode 100644 index 0000000..f0550a8 --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryEqualityComparerTest.cs @@ -0,0 +1,279 @@ +/* + * 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.Core.Tests.Binary +{ + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using Apache.Ignite.Core.Binary; + using Apache.Ignite.Core.Common; + using Apache.Ignite.Core.Impl.Binary; + using Apache.Ignite.Core.Impl.Binary.IO; + using NUnit.Framework; + + /// <summary> + /// Equality comparers test. + /// </summary> + public class BinaryEqualityComparerTest + { + /// <summary> + /// Tests common public methods logic. + /// </summary> + [Test] + [SuppressMessage("ReSharper", "ReturnValueOfPureMethodIsNotUsed")] + public void TestPublicMethods() + { + var cmps = new IEqualityComparer<IBinaryObject>[] + { + new BinaryArrayEqualityComparer() + //new BinaryFieldEqualityComparer() + }; + + var obj = GetBinaryObject(1, "x", 0); + + foreach (var cmp in cmps) + { + Assert.IsTrue(cmp.Equals(null, null)); + Assert.IsTrue(cmp.Equals(obj, obj)); + + Assert.IsFalse(cmp.Equals(obj, null)); + Assert.IsFalse(cmp.Equals(null, obj)); + + Assert.AreEqual(0, cmp.GetHashCode(null)); + Assert.AreNotEqual(0, cmp.GetHashCode(obj)); + } + } + + /// <summary> + /// Tests the custom comparer. + /// </summary> + [Test] + public void TestCustomComparer() + { + var ex = Assert.Throws<IgniteException>(() => Ignition.Start( + new IgniteConfiguration(TestUtils.GetTestConfiguration()) + { + BinaryConfiguration = new BinaryConfiguration + { + TypeConfigurations = new[] + { + new BinaryTypeConfiguration(typeof(Foo)) + { + EqualityComparer = new MyComparer() + } + } + } + })); + + Assert.AreEqual("Unsupported IEqualityComparer<IBinaryObject> implementation: " + + "Apache.Ignite.Core.Tests.Binary.BinaryEqualityComparerTest+MyComparer. " + + "Only predefined implementations are supported.", ex.Message); + } + + /// <summary> + /// Tests the array comparer. + /// </summary> + [Test] + public void TestArrayComparer() + { + var cmp = (IBinaryEqualityComparer) new BinaryArrayEqualityComparer(); + + var ms = new BinaryHeapStream(10); + + Assert.AreEqual(1, cmp.GetHashCode(ms, 0, 0, null, 0, null, null)); + + ms.WriteByte(1); + Assert.AreEqual(31 + 1, cmp.GetHashCode(ms, 0, 1, null, 0, null, null)); + + ms.WriteByte(3); + Assert.AreEqual((31 + 1) * 31 + 3, cmp.GetHashCode(ms, 0, 2, null, 0, null, null)); + } + + /// <summary> + /// Tests public methods of array comparer. + /// </summary> + [Test] + public void TestArrayComparerPublic() + { + var cmp = new BinaryArrayEqualityComparer(); + + var obj1 = GetBinaryObject(1, "foo", 11); + var obj2 = GetBinaryObject(1, "bar", 11); + var obj3 = GetBinaryObject(2, "foo", 11); + var obj4 = GetBinaryObject(2, "bar", 11); + var obj5 = GetBinaryObject(1, "foo", 11); + var obj6 = GetBinaryObject(1, "foo", 12); + + // Equals. + Assert.IsTrue(cmp.Equals(obj1, obj1)); + Assert.IsTrue(cmp.Equals(obj1, obj5)); + Assert.IsFalse(cmp.Equals(obj1, obj2)); + Assert.IsFalse(cmp.Equals(obj1, obj3)); + Assert.IsFalse(cmp.Equals(obj1, obj4)); + Assert.IsFalse(cmp.Equals(obj1, obj6)); + + Assert.IsTrue(cmp.Equals(obj2, obj2)); + Assert.IsFalse(cmp.Equals(obj2, obj5)); + Assert.IsFalse(cmp.Equals(obj2, obj3)); + Assert.IsFalse(cmp.Equals(obj2, obj4)); + Assert.IsFalse(cmp.Equals(obj2, obj6)); + + Assert.IsTrue(cmp.Equals(obj3, obj3)); + Assert.IsFalse(cmp.Equals(obj3, obj5)); + Assert.IsFalse(cmp.Equals(obj3, obj4)); + Assert.IsFalse(cmp.Equals(obj3, obj6)); + + Assert.IsTrue(cmp.Equals(obj4, obj4)); + Assert.IsFalse(cmp.Equals(obj4, obj5)); + Assert.IsFalse(cmp.Equals(obj4, obj6)); + + Assert.IsTrue(cmp.Equals(obj5, obj5)); + Assert.IsFalse(cmp.Equals(obj5, obj6)); + + // BinaryObject.GetHashCode. + Assert.AreEqual(1934949494, obj1.GetHashCode()); + Assert.AreEqual(-2013102781, obj2.GetHashCode()); + Assert.AreEqual(1424415317, obj3.GetHashCode()); + Assert.AreEqual(1771330338, obj4.GetHashCode()); + Assert.AreEqual(obj1.GetHashCode(), obj5.GetHashCode()); + Assert.AreEqual(1934979285, cmp.GetHashCode(obj6)); + + // Comparer.GetHashCode. + Assert.AreEqual(2001751043, cmp.GetHashCode(GetBinaryObject(0, null, 0))); + Assert.AreEqual(194296580, cmp.GetHashCode(GetBinaryObject(1, null, 0))); + Assert.AreEqual(1934949494, cmp.GetHashCode(obj1)); + Assert.AreEqual(-2013102781, cmp.GetHashCode(obj2)); + Assert.AreEqual(1424415317, cmp.GetHashCode(obj3)); + Assert.AreEqual(1771330338, cmp.GetHashCode(obj4)); + Assert.AreEqual(cmp.GetHashCode(obj1), cmp.GetHashCode(obj5)); + Assert.AreEqual(1934979285, cmp.GetHashCode(obj6)); + + // GetHashCode consistency. + foreach (var obj in new[] {obj1, obj2, obj3, obj4, obj5, obj6}) + Assert.AreEqual(obj.GetHashCode(), cmp.GetHashCode(obj)); + } + + /// <summary> + /// Tests the field comparer. + /// </summary> + [Test] + public void TestFieldComparer() + { + var marsh = new Marshaller(new BinaryConfiguration + { + TypeConfigurations = new[] + { + new BinaryTypeConfiguration(typeof(Foo)) + { + EqualityComparer = new BinaryFieldEqualityComparer("Name", "Id") + } + } + }); + + var val = new Foo {Id = 58, Name = "John"}; + var binObj = marsh.Unmarshal<IBinaryObject>(marsh.Marshal(val), BinaryMode.ForceBinary); + var expHash = val.Name.GetHashCode() * 31 + val.Id.GetHashCode(); + Assert.AreEqual(expHash, binObj.GetHashCode()); + + val = new Foo {Id = 95}; + binObj = marsh.Unmarshal<IBinaryObject>(marsh.Marshal(val), BinaryMode.ForceBinary); + expHash = val.Id.GetHashCode(); + Assert.AreEqual(expHash, binObj.GetHashCode()); + } + + /// <summary> + /// Tests the field comparer validation. + /// </summary> + [Test] + public void TestFieldComparerValidation() + { + var ex = Assert.Throws<IgniteException>(() => Ignition.Start( + new IgniteConfiguration(TestUtils.GetTestConfiguration()) + { + BinaryConfiguration = new BinaryConfiguration + { + TypeConfigurations = new[] + { + new BinaryTypeConfiguration(typeof(Foo)) + { + EqualityComparer = new BinaryFieldEqualityComparer() + } + } + } + })); + + Assert.AreEqual("BinaryFieldEqualityComparer.FieldNames can not be null or empty.", ex.Message); + } + + /// <summary> + /// Gets the binary object. + /// </summary> + private static IBinaryObject GetBinaryObject(int id, string name, int raw) + { + var marsh = new Marshaller(new BinaryConfiguration + { + TypeConfigurations = new[] + { + new BinaryTypeConfiguration(typeof(Foo)) + { + EqualityComparer = new BinaryArrayEqualityComparer() + } + } + }); + + var bytes = marsh.Marshal(new Foo {Id = id, Name = name, Raw = raw}); + + return marsh.Unmarshal<IBinaryObject>(bytes, BinaryMode.ForceBinary); + } + + private class Foo : IBinarizable + { + public int Id { get; set; } + public string Name { get; set; } + public int Raw { get; set; } + + public void WriteBinary(IBinaryWriter writer) + { + writer.WriteInt("id", Id); + writer.WriteString("name", Name); + + writer.GetRawWriter().WriteInt(Raw); + } + + public void ReadBinary(IBinaryReader reader) + { + Id = reader.ReadInt("id"); + Name = reader.ReadString("name"); + + Raw = reader.GetRawReader().ReadInt(); + } + } + + private class MyComparer : IEqualityComparer<IBinaryObject> + { + public bool Equals(IBinaryObject x, IBinaryObject y) + { + return true; + } + + public int GetHashCode(IBinaryObject obj) + { + return 0; + } + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/IO/BinaryStreamsTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/IO/BinaryStreamsTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/IO/BinaryStreamsTest.cs index ad5358d..1ebe906 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/IO/BinaryStreamsTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/IO/BinaryStreamsTest.cs @@ -94,6 +94,12 @@ namespace Apache.Ignite.Core.Tests.Binary.IO stream.Write(bytes, 2); Assert.AreEqual(2, stream.Position); + + var proc = new SumStreamProcessor(); + Assert.AreEqual(0, stream.Apply(proc, 0)); + Assert.AreEqual(1, stream.Apply(proc, 1)); + Assert.AreEqual(3, stream.Apply(proc, 2)); + flush(); seek(); @@ -147,5 +153,18 @@ namespace Apache.Ignite.Core.Tests.Binary.IO check(() => stream.WriteShort(4), () => stream.ReadShort(), (short)4); check(() => stream.WriteShortArray(new short[] {4}), () => stream.ReadShortArray(1), new short[] {4}); } + + private class SumStreamProcessor : IBinaryStreamProcessor<int, int> + { + public unsafe int Invoke(byte* data, int arg) + { + int res = 0; + + for (var i = 0; i < arg; i++) + res += *(data + i); + + return res; + } + } } } http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/CacheConfigurationTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/CacheConfigurationTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/CacheConfigurationTest.cs index 9d55160..fb8725c 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/CacheConfigurationTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/CacheConfigurationTest.cs @@ -447,6 +447,7 @@ namespace Apache.Ignite.Core.Tests.Cache Assert.AreEqual(x.Name, y.Name); Assert.AreEqual(x.FieldTypeName, y.FieldTypeName); + Assert.AreEqual(x.IsKeyField, y.IsKeyField); } /// <summary> @@ -528,7 +529,7 @@ namespace Apache.Ignite.Core.Tests.Cache Fields = new[] { new QueryField("length", typeof(int)), - new QueryField("name", typeof(string)), + new QueryField("name", typeof(string)) {IsKeyField = true}, new QueryField("location", typeof(string)), }, Aliases = new [] {new QueryAlias("length", "len") }, @@ -624,7 +625,7 @@ namespace Apache.Ignite.Core.Tests.Cache { new QueryField("length", typeof(int)), new QueryField("name", typeof(string)), - new QueryField("location", typeof(string)), + new QueryField("location", typeof(string)) {IsKeyField = true} }, Aliases = new [] {new QueryAlias("length", "len") }, Indexes = new[] http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheDmlQueriesTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheDmlQueriesTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheDmlQueriesTest.cs new file mode 100644 index 0000000..c460252 --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheDmlQueriesTest.cs @@ -0,0 +1,296 @@ +/* + * 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.Core.Tests.Cache.Query +{ + using System.Linq; + using Apache.Ignite.Core.Binary; + using Apache.Ignite.Core.Cache.Configuration; + using Apache.Ignite.Core.Cache.Query; + using Apache.Ignite.Core.Common; + using Apache.Ignite.Core.Impl.Binary; + using NUnit.Framework; + + /// <summary> + /// Tests Data Manipulation Language queries. + /// </summary> + public class CacheDmlQueriesTest + { + /// <summary> + /// Sets up test fixture. + /// </summary> + [TestFixtureSetUp] + public void FixtureSetUp() + { + var cfg = new IgniteConfiguration(TestUtils.GetTestConfiguration()) + { + BinaryConfiguration = new BinaryConfiguration(typeof(Foo)) + { + TypeConfigurations = + { + new BinaryTypeConfiguration(typeof(Key)) + { + EqualityComparer = new BinaryArrayEqualityComparer() + }, + new BinaryTypeConfiguration(typeof(Key2)) + { + EqualityComparer = new BinaryFieldEqualityComparer("Hi", "Lo") + } + } + } + }; + + Ignition.Start(cfg); + } + + /// <summary> + /// Tears down test fixture. + /// </summary> + [TestFixtureTearDown] + public void FixtureTearDown() + { + Ignition.StopAll(true); + } + + /// <summary> + /// Tests primitive key. + /// </summary> + [Test] + public void TestPrimitiveKey() + { + var cfg = new CacheConfiguration("primitive_key", new QueryEntity(typeof(int), typeof(Foo))); + var cache = Ignition.GetIgnite().CreateCache<int, Foo>(cfg); + + // Test insert. + var res = cache.QueryFields(new SqlFieldsQuery("insert into foo(_key, id, name) " + + "values (?, ?, ?), (?, ?, ?)", + 1, 2, "John", 3, 4, "Mary")).GetAll(); + + Assert.AreEqual(1, res.Count); + Assert.AreEqual(1, res[0].Count); + Assert.AreEqual(2, res[0][0]); // 2 affected rows + + var foos = cache.OrderBy(x => x.Key).ToArray(); + + Assert.AreEqual(2, foos.Length); + + Assert.AreEqual(1, foos[0].Key); + Assert.AreEqual(2, foos[0].Value.Id); + Assert.AreEqual("John", foos[0].Value.Name); + + Assert.AreEqual(3, foos[1].Key); + Assert.AreEqual(4, foos[1].Value.Id); + Assert.AreEqual("Mary", foos[1].Value.Name); + + // Test key existence. + Assert.IsTrue(cache.ContainsKey(1)); + Assert.IsTrue(cache.ContainsKey(3)); + } + + /// <summary> + /// Tests composite key (which requires QueryField.IsKeyField). + /// </summary> + [Test] + public void TestCompositeKeyArrayEquality() + { + var cfg = new CacheConfiguration("composite_key_arr", new QueryEntity(typeof(Key), typeof(Foo))); + var cache = Ignition.GetIgnite().CreateCache<Key, Foo>(cfg); + + // Test insert. + var res = cache.QueryFields(new SqlFieldsQuery("insert into foo(hi, lo, id, name) " + + "values (1, 2, 3, 'John'), (4, 5, 6, 'Mary')")).GetAll(); + + Assert.AreEqual(1, res.Count); + Assert.AreEqual(1, res[0].Count); + Assert.AreEqual(2, res[0][0]); // 2 affected rows + + var foos = cache.OrderBy(x => x.Key.Lo).ToArray(); + + Assert.AreEqual(2, foos.Length); + + Assert.AreEqual(1, foos[0].Key.Hi); + Assert.AreEqual(2, foos[0].Key.Lo); + Assert.AreEqual(3, foos[0].Value.Id); + Assert.AreEqual("John", foos[0].Value.Name); + + Assert.AreEqual(4, foos[1].Key.Hi); + Assert.AreEqual(5, foos[1].Key.Lo); + Assert.AreEqual(6, foos[1].Value.Id); + Assert.AreEqual("Mary", foos[1].Value.Name); + + // Existence tests check that hash codes are consistent. + var binary = cache.Ignite.GetBinary(); + var binCache = cache.WithKeepBinary<IBinaryObject, IBinaryObject>(); + + Assert.IsTrue(cache.ContainsKey(new Key(2, 1))); + Assert.IsTrue(cache.ContainsKey(foos[0].Key)); + Assert.IsTrue(binCache.ContainsKey( + binary.GetBuilder(typeof(Key)).SetField("hi", 1).SetField("lo", 2).Build())); + + Assert.IsTrue(cache.ContainsKey(new Key(5, 4))); + Assert.IsTrue(cache.ContainsKey(foos[1].Key)); + Assert.IsTrue(binCache.ContainsKey( + binary.GetBuilder(typeof(Key)).SetField("hi", 4).SetField("lo", 5).Build())); + } + + /// <summary> + /// Tests composite key (which requires QueryField.IsKeyField). + /// </summary> + [Test] + public void TestCompositeKeyFieldEquality() + { + var cfg = new CacheConfiguration("composite_key_fld", new QueryEntity(typeof(Key2), typeof(Foo))); + var cache = Ignition.GetIgnite().CreateCache<Key2, Foo>(cfg); + + // Test insert. + var res = cache.QueryFields(new SqlFieldsQuery("insert into foo(hi, lo, str, id, name) " + + "values (1, 2, 'Foo', 3, 'John'), (4, 5, 'Bar', 6, 'Mary')")).GetAll(); + + Assert.AreEqual(1, res.Count); + Assert.AreEqual(1, res[0].Count); + Assert.AreEqual(2, res[0][0]); // 2 affected rows + + var foos = cache.OrderBy(x => x.Key.Lo).ToArray(); + + Assert.AreEqual(2, foos.Length); + + Assert.AreEqual(1, foos[0].Key.Hi); + Assert.AreEqual(2, foos[0].Key.Lo); + Assert.AreEqual("Foo", foos[0].Key.Str); + Assert.AreEqual(3, foos[0].Value.Id); + Assert.AreEqual("John", foos[0].Value.Name); + + Assert.AreEqual(4, foos[1].Key.Hi); + Assert.AreEqual(5, foos[1].Key.Lo); + Assert.AreEqual("Bar", foos[1].Key.Str); + Assert.AreEqual(6, foos[1].Value.Id); + Assert.AreEqual("Mary", foos[1].Value.Name); + + // Existence tests check that hash codes are consistent. + Assert.IsTrue(cache.ContainsKey(new Key2(2, 1, "Foo"))); + Assert.IsTrue(cache.ContainsKey(foos[0].Key)); + + Assert.IsTrue(cache.ContainsKey(new Key2(5, 4, "Bar"))); + Assert.IsTrue(cache.ContainsKey(foos[1].Key)); + } + + /// <summary> + /// Tests the composite key without IsKeyField configuration. + /// </summary> + [Test] + public void TestInvalidCompositeKey() + { + var cfg = new CacheConfiguration("invalid_composite_key", new QueryEntity + { + KeyTypeName = "Key", + ValueTypeName = "Foo", + Fields = new[] + { + new QueryField("Lo", typeof(int)), + new QueryField("Hi", typeof(int)), + new QueryField("Id", typeof(int)), + new QueryField("Name", typeof(string)) + } + }); + + var cache = Ignition.GetIgnite().CreateCache<Key, Foo>(cfg); + + var ex = Assert.Throws<IgniteException>( + () => cache.QueryFields(new SqlFieldsQuery("insert into foo(lo, hi, id, name) " + + "values (1, 2, 3, 'John'), (4, 5, 6, 'Mary')"))); + + Assert.AreEqual("Ownership flag not set for binary property. Have you set 'keyFields' " + + "property of QueryEntity in programmatic or XML configuration?", ex.Message); + } + + /// <summary> + /// Tests DML with pure binary cache mode, without classes. + /// </summary> + [Test] + public void TestBinaryMode() + { + var cfg = new CacheConfiguration("binary_only", new QueryEntity + { + KeyTypeName = "CarKey", + ValueTypeName = "Car", + Fields = new[] + { + new QueryField("VIN", typeof(string)) {IsKeyField = true}, + new QueryField("Id", typeof(int)) {IsKeyField = true}, + new QueryField("Make", typeof(string)), + new QueryField("Year", typeof(int)) + } + }); + + var cache = Ignition.GetIgnite().CreateCache<object, object>(cfg) + .WithKeepBinary<IBinaryObject, IBinaryObject>(); + + var res = cache.QueryFields(new SqlFieldsQuery("insert into car(VIN, Id, Make, Year) " + + "values ('DLRDMC', 88, 'DeLorean', 1982)")).GetAll(); + + Assert.AreEqual(1, res.Count); + Assert.AreEqual(1, res[0].Count); + Assert.AreEqual(1, res[0][0]); + + var car = cache.Single(); + Assert.AreEqual("CarKey", car.Key.GetBinaryType().TypeName); + Assert.AreEqual("Car", car.Value.GetBinaryType().TypeName); + } + + /// <summary> + /// Key. + /// </summary> + private struct Key + { + public Key(int lo, int hi) : this() + { + Lo = lo; + Hi = hi; + } + + [QuerySqlField] public int Lo { get; private set; } + [QuerySqlField] public int Hi { get; private set; } + } + + /// <summary> + /// Key. + /// </summary> + private struct Key2 + { + public Key2(int lo, int hi, string str) : this() + { + Lo = lo; + Hi = hi; + Str = str; + } + + [QuerySqlField] public int Lo { get; private set; } + [QuerySqlField] public int Hi { get; private set; } + [QuerySqlField] public string Str { get; private set; } + } + + /// <summary> + /// Value. + /// </summary> + private class Foo + { + [QuerySqlField] public int Id { get; set; } + [QuerySqlField] public string Name { get; set; } + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationSerializerTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationSerializerTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationSerializerTest.cs index 55b8dcf..26e04a9 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationSerializerTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationSerializerTest.cs @@ -45,6 +45,7 @@ namespace Apache.Ignite.Core.Tests using Apache.Ignite.Core.Discovery.Tcp; using Apache.Ignite.Core.Discovery.Tcp.Multicast; using Apache.Ignite.Core.Events; + using Apache.Ignite.Core.Impl.Binary; using Apache.Ignite.Core.Impl.Common; using Apache.Ignite.Core.Lifecycle; using Apache.Ignite.Core.Log; @@ -72,6 +73,14 @@ namespace Apache.Ignite.Core.Tests <types> <string>Apache.Ignite.Core.Tests.IgniteConfigurationSerializerTest+FooClass, Apache.Ignite.Core.Tests</string> </types> + <typeConfigurations> + <binaryTypeConfiguration affinityKeyFieldName='affKeyFieldName' isEnum='true' keepDeserialized='True' typeName='typeName'> + <equalityComparer type='BinaryArrayEqualityComparer' /> + <idMapper type='Apache.Ignite.Core.Tests.Binary.IdMapper, Apache.Ignite.Core.Tests' /> + <nameMapper type='Apache.Ignite.Core.Tests.IgniteConfigurationSerializerTest+NameMapper, Apache.Ignite.Core.Tests' /> + <serializer type='Apache.Ignite.Core.Tests.IgniteConfigurationSerializerTest+TestSerializer, Apache.Ignite.Core.Tests' /> + </binaryTypeConfiguration> + </typeConfigurations> </binaryConfiguration> <discoverySpi type='TcpDiscoverySpi' joinTimeout='0:1:0' localAddress='192.168.1.1' localPort='6655'> <ipFinder type='TcpDiscoveryMulticastIpFinder' addressRequestAttempts='7' /> @@ -86,7 +95,7 @@ namespace Apache.Ignite.Core.Tests <queryEntities> <queryEntity keyType='System.Int32' valueType='System.String'> <fields> - <queryField name='length' fieldType='System.Int32' /> + <queryField name='length' fieldType='System.Int32' isKeyField='true' /> </fields> <aliases> <queryAlias fullName='somefield.field' alias='shortField' /> @@ -159,6 +168,7 @@ namespace Apache.Ignite.Core.Tests Assert.AreEqual(typeof(string), queryEntity.ValueType); Assert.AreEqual("length", queryEntity.Fields.Single().Name); Assert.AreEqual(typeof(int), queryEntity.Fields.Single().FieldType); + Assert.IsTrue(queryEntity.Fields.Single().IsKeyField); Assert.AreEqual("somefield.field", queryEntity.Aliases.Single().FullName); Assert.AreEqual("shortField", queryEntity.Aliases.Single().Alias); Assert.AreEqual(QueryIndexType.Geospatial, queryEntity.Indexes.Single().IndexType); @@ -214,6 +224,16 @@ namespace Apache.Ignite.Core.Tests Assert.AreEqual(25, swap.MaximumWriteQueueSize); Assert.AreEqual(36, swap.ReadStripesNumber); Assert.AreEqual(47, swap.WriteBufferSize); + + var binType = cfg.BinaryConfiguration.TypeConfigurations.Single(); + Assert.AreEqual("typeName", binType.TypeName); + Assert.AreEqual("affKeyFieldName", binType.AffinityKeyFieldName); + Assert.IsTrue(binType.IsEnum); + Assert.AreEqual(true, binType.KeepDeserialized); + Assert.IsInstanceOf<BinaryArrayEqualityComparer>(binType.EqualityComparer); + Assert.IsInstanceOf<IdMapper>(binType.IdMapper); + Assert.IsInstanceOf<NameMapper>(binType.NameMapper); + Assert.IsInstanceOf<TestSerializer>(binType.Serializer); } /// <summary> @@ -334,12 +354,12 @@ namespace Apache.Ignite.Core.Tests }; Assert.AreEqual(FixLineEndings(@"<?xml version=""1.0"" encoding=""utf-16""?> -<igniteConfiguration gridName=""myGrid"" clientMode=""true"" xmlns=""http://ignite.apache.org/schema/dotnet/IgniteConfigurationSection""> +<igniteConfiguration clientMode=""true"" gridName=""myGrid"" xmlns=""http://ignite.apache.org/schema/dotnet/IgniteConfigurationSection""> <cacheConfiguration> - <cacheConfiguration name=""myCache"" cacheMode=""Replicated""> + <cacheConfiguration cacheMode=""Replicated"" name=""myCache""> <queryEntities> - <queryEntity valueTypeName=""java.lang.Integer"" valueType=""System.Int32"" /> - <queryEntity keyTypeName=""java.lang.Integer"" keyType=""System.Int32"" valueTypeName=""java.lang.String"" valueType=""System.String"" /> + <queryEntity valueType=""System.Int32"" valueTypeName=""java.lang.Integer"" /> + <queryEntity keyType=""System.Int32"" keyTypeName=""java.lang.Integer"" valueType=""System.String"" valueTypeName=""java.lang.String"" /> </queryEntities> </cacheConfiguration> </cacheConfiguration> @@ -364,12 +384,12 @@ namespace Apache.Ignite.Core.Tests } Assert.AreEqual(FixLineEndings(@"<?xml version=""1.0"" encoding=""utf-16""?> -<igCfg gridName=""myGrid"" clientMode=""true"" xmlns=""http://ignite.apache.org/schema/dotnet/IgniteConfigurationSection""> +<igCfg clientMode=""true"" gridName=""myGrid"" xmlns=""http://ignite.apache.org/schema/dotnet/IgniteConfigurationSection""> <cacheConfiguration> - <cacheConfiguration name=""myCache"" cacheMode=""Replicated""> + <cacheConfiguration cacheMode=""Replicated"" name=""myCache""> <queryEntities> - <queryEntity valueTypeName=""java.lang.Integer"" valueType=""System.Int32"" /> - <queryEntity keyTypeName=""java.lang.Integer"" keyType=""System.Int32"" valueTypeName=""java.lang.String"" valueType=""System.String"" /> + <queryEntity valueType=""System.Int32"" valueTypeName=""java.lang.Integer"" /> + <queryEntity keyType=""System.Int32"" keyTypeName=""java.lang.Integer"" valueType=""System.String"" valueTypeName=""java.lang.String"" /> </queryEntities> </cacheConfiguration> </cacheConfiguration> @@ -547,7 +567,8 @@ namespace Apache.Ignite.Core.Tests TypeName = "typeName", IdMapper = new IdMapper(), NameMapper = new NameMapper(), - Serializer = new TestSerializer() + Serializer = new TestSerializer(), + EqualityComparer = new BinaryArrayEqualityComparer() }, new BinaryTypeConfiguration { @@ -555,7 +576,8 @@ namespace Apache.Ignite.Core.Tests KeepDeserialized = false, AffinityKeyFieldName = "affKeyFieldName", TypeName = "typeName2", - Serializer = new BinaryReflectiveSerializer() + Serializer = new BinaryReflectiveSerializer(), + EqualityComparer = new BinaryFieldEqualityComparer() } }, Types = new[] {typeof (string).FullName}, @@ -595,7 +617,7 @@ namespace Apache.Ignite.Core.Tests { Fields = new[] { - new QueryField("field", typeof (int)) + new QueryField("field", typeof (int)) { IsKeyField = true } }, Indexes = new[] { http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationTest.cs index 2e39b9b..86ece98 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationTest.cs @@ -34,6 +34,7 @@ namespace Apache.Ignite.Core.Tests using Apache.Ignite.Core.Discovery.Tcp.Static; using Apache.Ignite.Core.Events; using Apache.Ignite.Core.Impl; + using Apache.Ignite.Core.Impl.Binary; using Apache.Ignite.Core.SwapSpace.File; using Apache.Ignite.Core.Transactions; using NUnit.Framework; @@ -181,6 +182,18 @@ namespace Apache.Ignite.Core.Tests Assert.AreEqual(swap.MaximumWriteQueueSize, resSwap.MaximumWriteQueueSize); Assert.AreEqual(swap.ReadStripesNumber, resSwap.ReadStripesNumber); Assert.AreEqual(swap.WriteBufferSize, resSwap.WriteBufferSize); + + var binCfg = cfg.BinaryConfiguration; + Assert.IsFalse(binCfg.CompactFooter); + + var typ = binCfg.TypeConfigurations.Single(); + Assert.AreEqual("myType", typ.TypeName); + Assert.IsTrue(typ.IsEnum); + Assert.AreEqual("affKey", typ.AffinityKeyFieldName); + Assert.AreEqual(false, typ.KeepDeserialized); + + CollectionAssert.AreEqual(new[] {"fld1", "fld2"}, + ((BinaryFieldEqualityComparer)typ.EqualityComparer).FieldNames); } } @@ -513,6 +526,21 @@ namespace Apache.Ignite.Core.Tests WriteBufferSize = 9, BaseDirectory = Path.GetTempPath(), MaximumSparsity = 11.22f + }, + BinaryConfiguration = new BinaryConfiguration + { + CompactFooter = false, + TypeConfigurations = new[] + { + new BinaryTypeConfiguration + { + TypeName = "myType", + IsEnum = true, + AffinityKeyFieldName = "affKey", + KeepDeserialized = false, + EqualityComparer = new BinaryFieldEqualityComparer("fld1", "fld2") + } + } } }; } http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj index a80dfc0..42ccdd4 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj @@ -90,6 +90,8 @@ <Reference Include="System.Xml" /> </ItemGroup> <ItemGroup> + <Compile Include="Binary\BinaryArrayEqualityComparer.cs" /> + <Compile Include="Impl\Binary\BinaryFieldEqualityComparer.cs" /> <Compile Include="Binary\BinaryReflectiveSerializer.cs" /> <Compile Include="Common\JavaException.cs" /> <Compile Include="DataStructures\Configuration\Package-Info.cs" /> @@ -97,8 +99,10 @@ <Compile Include="Discovery\Tcp\Multicast\Package-Info.cs" /> <Compile Include="Discovery\Tcp\Package-Info.cs" /> <Compile Include="Discovery\Tcp\Static\Package-Info.cs" /> + <Compile Include="Impl\Binary\BinaryEqualityComparerSerializer.cs" /> <Compile Include="Impl\Binary\BinaryProcessor.cs" /> <Compile Include="Impl\Binary\BinaryReflectiveSerializerInternal.cs" /> + <Compile Include="Impl\Binary\IBinaryEqualityComparer.cs" /> <Compile Include="Impl\Binary\IBinarySerializerInternal.cs" /> <Compile Include="Binary\Package-Info.cs" /> <Compile Include="Cache\Affinity\AffinityKey.cs" /> http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryArrayEqualityComparer.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryArrayEqualityComparer.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryArrayEqualityComparer.cs new file mode 100644 index 0000000..09f7f0f --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryArrayEqualityComparer.cs @@ -0,0 +1,149 @@ +/* + * 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.Core.Binary +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using Apache.Ignite.Core.Impl.Binary; + using Apache.Ignite.Core.Impl.Binary.IO; + + /// <summary> + /// Compares binary object equality using underlying byte array. + /// </summary> + public class BinaryArrayEqualityComparer : IEqualityComparer<IBinaryObject>, IBinaryEqualityComparer, + IBinaryStreamProcessor<KeyValuePair<int,int>, int> + { + /// <summary> + /// Determines whether the specified objects are equal. + /// </summary> + /// <param name="x">The first object to compare.</param> + /// <param name="y">The second object to compare.</param> + /// <returns> + /// true if the specified objects are equal; otherwise, false. + /// </returns> + public bool Equals(IBinaryObject x, IBinaryObject y) + { + if (x == null) + return y == null; + + if (y == null) + return false; + + if (ReferenceEquals(x, y)) + return true; + + var binx = GetBinaryObject(x); + var biny = GetBinaryObject(y); + + var lenx = GetDataLength(binx); + var leny = GetDataLength(biny); + + if (lenx != leny) + return false; + + var startx = GetDataStart(binx); + var starty = GetDataStart(biny); + + var arrx = binx.Data; + var arry = biny.Data; + + for (var i = 0; i < lenx; i++) + { + if (arrx[i + startx] != arry[i + starty]) + return false; + } + + return true; + } + + /// <summary> + /// Returns a hash code for this instance. + /// </summary> + /// <param name="obj">The object.</param> + /// <returns> + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// </returns> + public int GetHashCode(IBinaryObject obj) + { + if (obj == null) + return 0; + + var binObj = GetBinaryObject(obj); + + var arg = new KeyValuePair<int, int>(GetDataStart(binObj), GetDataLength(binObj)); + + return new BinaryHeapStream(binObj.Data).Apply(this, arg); + } + + /** <inheritdoc /> */ + int IBinaryEqualityComparer.GetHashCode(IBinaryStream stream, int startPos, int length, + BinaryObjectSchemaHolder schema, int schemaId, Marshaller marshaller, IBinaryTypeDescriptor desc) + { + Debug.Assert(stream != null); + Debug.Assert(startPos >= 0); + Debug.Assert(length >= 0); + + var arg = new KeyValuePair<int, int>(startPos, length); + + return stream.Apply(this, arg); + } + + /** <inheritdoc /> */ + unsafe int IBinaryStreamProcessor<KeyValuePair<int, int>, int>.Invoke(byte* data, KeyValuePair<int, int> arg) + { + var hash = 1; + var ptr = data + arg.Key; + + for (var i = 0; i < arg.Value; i++) + hash = 31 * hash + *(ptr + i); + + return hash; + } + + /// <summary> + /// Casts to <see cref="BinaryObject"/> or throws an error. + /// </summary> + private static BinaryObject GetBinaryObject(IBinaryObject obj) + { + var binObj = obj as BinaryObject; + + if (binObj != null) + return binObj; + + throw new NotSupportedException(string.Format("{0} of type {1} is not supported.", + typeof(IBinaryObject), obj.GetType())); + } + + /// <summary> + /// Gets the non-raw data length. + /// </summary> + private static int GetDataLength(BinaryObject binObj) + { + return binObj.Header.FooterStartOffset - BinaryObjectHeader.Size; + } + + /// <summary> + /// Gets the data starting position. + /// </summary> + private static int GetDataStart(BinaryObject binObj) + { + return binObj.Offset + BinaryObjectHeader.Size; + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryConfiguration.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryConfiguration.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryConfiguration.cs index 51df907..c738f28 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryConfiguration.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryConfiguration.cs @@ -141,5 +141,29 @@ namespace Apache.Ignite.Core.Binary { get { return _compactFooter; } } + + /// <summary> + /// Merges other config into this. + /// </summary> + internal void MergeTypes(BinaryConfiguration localConfig) + { + if (TypeConfigurations == null) + { + TypeConfigurations = localConfig.TypeConfigurations; + } + else if (localConfig.TypeConfigurations != null) + { + // Both configs are present. + // Local configuration is more complete and takes preference when it exists for a given type. + var localTypeNames = new HashSet<string>(localConfig.TypeConfigurations.Select(x => x.TypeName), + StringComparer.OrdinalIgnoreCase); + + var configs = new List<BinaryTypeConfiguration>(localConfig.TypeConfigurations); + + configs.AddRange(TypeConfigurations.Where(x=>!localTypeNames.Contains(x.TypeName))); + + TypeConfigurations = configs; + } + } } } http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryTypeConfiguration.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryTypeConfiguration.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryTypeConfiguration.cs index c36b9fd..722197c 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryTypeConfiguration.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryTypeConfiguration.cs @@ -18,6 +18,7 @@ namespace Apache.Ignite.Core.Binary { using System; + using System.Collections.Generic; using Apache.Ignite.Core.Impl.Common; /// <summary> @@ -69,6 +70,7 @@ namespace Apache.Ignite.Core.Binary TypeName = cfg.TypeName; KeepDeserialized = cfg.KeepDeserialized; IsEnum = cfg.IsEnum; + EqualityComparer = cfg.EqualityComparer; } /// <summary> @@ -113,6 +115,18 @@ namespace Apache.Ignite.Core.Binary public bool IsEnum { get; set; } /// <summary> + /// Gets or sets the equality comparer to compute hash codes and compare objects + /// in <see cref="IBinaryObject"/> form. + /// This comparer is important only for types that are used as cache keys. + /// <para /> + /// Null means legacy behavior: hash code is computed by calling <see cref="object.GetHashCode"/>, equality is + /// computed by comparing bytes in serialized (binary) form. + /// <para /> + /// Only predefined implementations are supported: <see cref="BinaryArrayEqualityComparer"/>. + /// </summary> + public IEqualityComparer<IBinaryObject> EqualityComparer { get; set; } + + /// <summary> /// Returns a string that represents the current object. /// </summary> /// <returns> http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Configuration/QueryEntity.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Configuration/QueryEntity.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Configuration/QueryEntity.cs index adfe9e1..ff9af37 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Configuration/QueryEntity.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Configuration/QueryEntity.cs @@ -178,8 +178,11 @@ namespace Apache.Ignite.Core.Cache.Configuration ValueTypeName = reader.ReadString(); var count = reader.ReadInt(); - Fields = count == 0 ? null : Enumerable.Range(0, count).Select(x => - new QueryField(reader.ReadString(), reader.ReadString())).ToList(); + Fields = count == 0 + ? null + : Enumerable.Range(0, count).Select(x => + new QueryField(reader.ReadString(), reader.ReadString()) {IsKeyField = reader.ReadBoolean()}) + .ToList(); count = reader.ReadInt(); Aliases = count == 0 ? null : Enumerable.Range(0, count) @@ -205,6 +208,7 @@ namespace Apache.Ignite.Core.Cache.Configuration { writer.WriteString(field.Name); writer.WriteString(field.FieldTypeName); + writer.WriteBoolean(field.IsKeyField); } } else @@ -264,16 +268,19 @@ namespace Apache.Ignite.Core.Cache.Configuration /// <summary> /// Rescans the attributes in <see cref="KeyType"/> and <see cref="ValueType"/>. /// </summary> - private void RescanAttributes(params Type[] types) + private void RescanAttributes(Type keyType, Type valType) { - if (types.Length == 0 || types.All(t => t == null)) + if (keyType == null && valType == null) return; var fields = new List<QueryField>(); var indexes = new List<QueryIndexEx>(); - foreach (var type in types.Where(t => t != null)) - ScanAttributes(type, fields, indexes, null, new HashSet<Type>()); + if (keyType != null) + ScanAttributes(keyType, fields, indexes, null, new HashSet<Type>(), true); + + if (valType != null) + ScanAttributes(valType, fields, indexes, null, new HashSet<Type>(), false); if (fields.Any()) Fields = fields; @@ -308,15 +315,17 @@ namespace Apache.Ignite.Core.Cache.Configuration } /// <summary> - /// Scans specified type for occurences of <see cref="QuerySqlFieldAttribute"/>. + /// Scans specified type for occurences of <see cref="QuerySqlFieldAttribute" />. /// </summary> /// <param name="type">The type.</param> /// <param name="fields">The fields.</param> /// <param name="indexes">The indexes.</param> /// <param name="parentPropName">Name of the parent property.</param> /// <param name="visitedTypes">The visited types.</param> + /// <param name="isKey">Whether this is a key type.</param> + /// <exception cref="System.InvalidOperationException">Recursive Query Field definition detected: + type</exception> private static void ScanAttributes(Type type, List<QueryField> fields, List<QueryIndexEx> indexes, - string parentPropName, ISet<Type> visitedTypes) + string parentPropName, ISet<Type> visitedTypes, bool isKey) { Debug.Assert(type != null); Debug.Assert(fields != null); @@ -344,9 +353,9 @@ namespace Apache.Ignite.Core.Cache.Configuration if (parentPropName != null) columnName = parentPropName + "." + columnName; - fields.Add(new QueryField(columnName, memberInfo.Value)); + fields.Add(new QueryField(columnName, memberInfo.Value) {IsKeyField = isKey}); - ScanAttributes(memberInfo.Value, fields, indexes, columnName, visitedTypes); + ScanAttributes(memberInfo.Value, fields, indexes, columnName, visitedTypes, isKey); } foreach (var attr in customAttributes.OfType<QueryTextFieldAttribute>()) @@ -359,9 +368,9 @@ namespace Apache.Ignite.Core.Cache.Configuration if (parentPropName != null) columnName = parentPropName + "." + columnName; - fields.Add(new QueryField(columnName, memberInfo.Value)); + fields.Add(new QueryField(columnName, memberInfo.Value) {IsKeyField = isKey}); - ScanAttributes(memberInfo.Value, fields, indexes, columnName, visitedTypes); + ScanAttributes(memberInfo.Value, fields, indexes, columnName, visitedTypes, isKey); } } http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Configuration/QueryField.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Configuration/QueryField.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Configuration/QueryField.cs index 12028e2..c33aa57 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Configuration/QueryField.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Configuration/QueryField.cs @@ -109,6 +109,12 @@ namespace Apache.Ignite.Core.Cache.Configuration } /// <summary> + /// Gets or sets a value indicating whether this field belongs to the cache key. + /// Proper value here is required for SQL DML queries which create/modify cache keys. + /// </summary> + public bool IsKeyField { get; set; } + + /// <summary> /// Validates this instance and outputs information to the log, if necessary. /// </summary> internal void Validate(ILogger log, string logInfo)