From 35471479c5de161ab9aaf22ba55d4be807bdba0c Mon Sep 17 00:00:00 2001 From: gurustron Date: Mon, 31 Jul 2017 15:14:15 +0300 Subject: [PATCH 01/26] define API and interface --- .../Cache/Query/Linq/CacheLinqTest.Custom.cs | 22 +++++++++++++++++++ .../Apache.Ignite.Linq.csproj | 1 + .../Apache.Ignite.Linq/CacheExtensions.cs | 17 ++++++++++++++ .../Apache.Ignite.Linq/IUpdateDescriptor.cs | 10 +++++++++ 4 files changed, 50 insertions(+) create mode 100644 modules/platforms/dotnet/Apache.Ignite.Linq/IUpdateDescriptor.cs diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs index f797b4cc8da81..bb58baab0285d 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs @@ -103,5 +103,27 @@ public void TestRemoveAll() var ex = Assert.Throws(() => qry.RemoveAll()); Assert.AreEqual("Failed to parse query", ex.Message.Substring(0, 21)); } + + /// + /// Tests the UpdateAll extension. + /// + [Test] + public void TestUpdateAll() + { + // Use new cache to avoid touching static data. + var cache = Ignition.GetIgnite().CreateCache(new CacheConfiguration("deleteAllTest", + new QueryEntity(typeof(int), typeof(Person))) + { + SqlEscapeAll = GetSqlEscapeAll() + }); + + Enumerable.Range(1, 10).ToList().ForEach(x => cache.Put(x, new Person(x, x.ToString()))); + + var queryable = cache.AsCacheQueryable(); + + queryable.UpdateAll(d => d.Set(p => p.AliasTest, 1) + .Set(p => p.Age, p => p.Age + 1)); + + } } } \ No newline at end of file diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj b/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj index 735e4f2311b52..212e53555543d 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj @@ -72,6 +72,7 @@ + diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheExtensions.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheExtensions.cs index f759dbbab745c..9e3495ada6dad 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheExtensions.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheExtensions.cs @@ -191,5 +191,22 @@ public static ICacheQueryable ToCacheQueryable(this IQueryable query) return query.Provider.Execute(Expression.Call(null, method, query.Expression, Expression.Quote(predicate))); } + + /// + /// Updates all rows that are matched by the specified query. + /// + /// This method results in "UPDATE" distributed SQL query, performing bulk update + /// (as opposed to fetching all rows locally). + /// + /// Key type. + /// Value type. + /// The query. + /// The update description. + /// Affected row count. + public static int UpdateAll(this IQueryable> query, + Expression, IUpdateDescriptor>> updateDescription) + { + throw new NotImplementedException(); + } } } \ No newline at end of file diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/IUpdateDescriptor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/IUpdateDescriptor.cs new file mode 100644 index 0000000000000..7dbe8ad583c4d --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/IUpdateDescriptor.cs @@ -0,0 +1,10 @@ +namespace Apache.Ignite.Linq +{ + using System; + + public interface IUpdateDescriptor + { + IUpdateDescriptor Set(Func selector, TProp value); + IUpdateDescriptor Set(Func selector, Func valueBuilder); + } +} \ No newline at end of file From 303c89ff203b41b1beb85945363cf3aa49fa59f0 Mon Sep 17 00:00:00 2001 From: gurustron Date: Tue, 15 Aug 2017 18:33:55 +0300 Subject: [PATCH 02/26] implementation started --- .../Cache/Query/Linq/CacheLinqTest.Custom.cs | 4 +- .../Apache.Ignite.Linq.csproj | 2 + .../Apache.Ignite.Linq/CacheExtensions.cs | 31 ++++++- .../Impl/CacheQueryModelVisitor.cs | 12 ++- .../Impl/CacheQueryParser.cs | 3 + .../Impl/Dml/UpdateAllExpressionNode.cs | 88 +++++++++++++++++++ .../Impl/Dml/UpdateAllResultOperator.cs | 60 +++++++++++++ 7 files changed, 196 insertions(+), 4 deletions(-) create mode 100644 modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs create mode 100644 modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs index bb58baab0285d..6d3e835f46477 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs @@ -111,7 +111,7 @@ public void TestRemoveAll() public void TestUpdateAll() { // Use new cache to avoid touching static data. - var cache = Ignition.GetIgnite().CreateCache(new CacheConfiguration("deleteAllTest", + var cache = Ignition.GetIgnite().CreateCache(new CacheConfiguration("updateAllTest", new QueryEntity(typeof(int), typeof(Person))) { SqlEscapeAll = GetSqlEscapeAll() @@ -124,6 +124,8 @@ public void TestUpdateAll() queryable.UpdateAll(d => d.Set(p => p.AliasTest, 1) .Set(p => p.Age, p => p.Age + 1)); + //queryable.UpdateAll(entry => "asd"); + } } } \ No newline at end of file diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj b/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj index 212e53555543d..37f46b83145cb 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj @@ -63,7 +63,9 @@ + + diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheExtensions.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheExtensions.cs index 9e3495ada6dad..02c8be5ec901c 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheExtensions.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheExtensions.cs @@ -206,7 +206,36 @@ public static ICacheQueryable ToCacheQueryable(this IQueryable query) public static int UpdateAll(this IQueryable> query, Expression, IUpdateDescriptor>> updateDescription) { - throw new NotImplementedException(); + IgniteArgumentCheck.NotNull(query, "query"); + IgniteArgumentCheck.NotNull(updateDescription, "updateDescription"); + + var method = UpdateAllExpressionNode.UpdateAllDescriptorMethodInfo + .MakeGenericMethod(typeof(TKey), typeof(TValue)); // TODO: cache? + + return query.Provider.Execute(Expression.Call(null, method, new[] { query.Expression, + Expression.Quote(updateDescription)})); } + + //public static int UpdateAll(this IQueryable> query, Expression, string>> func) + //{ + // var method = UpdateAllExpressionNode.UpdateAllString + // .MakeGenericMethod(typeof(TKey), typeof(TValue)); // TODO: cache? + + // return query.Provider.Execute(Expression.Call(null, method, query.Expression, + // Expression.Quote(func))); + //} + + //public class UD:IUpdateDescriptor + //{ + // public IUpdateDescriptor Set(Func selector, TProp value) + // { + // return this; + // } + + // public IUpdateDescriptor Set(Func selector, Func valueBuilder) + // { + // return this; + // } + //} } } \ No newline at end of file diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs index 3a3e5fd983037..e6a1b97a3ed45 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs @@ -109,6 +109,11 @@ private void VisitQueryModel(QueryModel queryModel, bool includeAllFields) var hasDelete = VisitRemoveOperator(queryModel); + if (!hasDelete && queryModel.ResultOperators.LastOrDefault() is UpdateAllResultOperator) + { + _builder.Append("update "); + } + else if (!hasDelete) { // SELECT @@ -121,7 +126,7 @@ private void VisitQueryModel(QueryModel queryModel, bool includeAllFields) // FROM ... WHERE ... JOIN ... base.VisitQueryModel(queryModel); - if (!hasDelete) + if (!hasDelete && queryModel.ResultOperators.LastOrDefault() is UpdateAllResultOperator) { // UNION ... ProcessResultOperatorsEnd(queryModel); @@ -389,8 +394,11 @@ private bool ProcessGroupings(QueryModel queryModel) public override void VisitMainFromClause(MainFromClause fromClause, QueryModel queryModel) { base.VisitMainFromClause(fromClause, queryModel); + if (!queryModel.ResultOperators.Any(rOp => rOp is UpdateAllResultOperator)) + { + _builder.AppendFormat("from "); + } - _builder.AppendFormat("from "); ValidateFromClause(fromClause); _aliases.AppendAsClause(_builder, fromClause).Append(" "); diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryParser.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryParser.cs index 17ec0a3bd7cb7..3f69cce901ed5 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryParser.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryParser.cs @@ -66,6 +66,9 @@ private static INodeTypeProvider CreateNodeTypeProvider() methodInfoRegistry.Register(RemoveAllExpressionNode.GetSupportedMethods(), typeof(RemoveAllExpressionNode)); + methodInfoRegistry.Register(UpdateAllExpressionNode.GetSupportedMethods(), + typeof(UpdateAllExpressionNode)); + return new CompoundNodeTypeProvider(new INodeTypeProvider[] { methodInfoRegistry, diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs new file mode 100644 index 0000000000000..e15cacd8ad03b --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs @@ -0,0 +1,88 @@ +/* + * 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.Linq.Impl.Dml +{ + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.Linq; + using System.Linq.Expressions; + using System.Reflection; + using Apache.Ignite.Core.Cache; + using Remotion.Linq.Clauses; + using Remotion.Linq.Parsing.Structure.IntermediateModel; + + /// + /// Represents a for + /// . + /// When user calls UpdateAll, this node is generated. + /// + internal sealed class UpdateAllExpressionNode : ResultOperatorExpressionNodeBase + { + /** */ + private static readonly MethodInfo[] UpdateAllMethodInfos = typeof(CacheLinqExtensions) + .GetMethods() + .Where(x => x.Name == "UpdateAll") + .ToArray(); + + /// + /// The UpdateAll(pred) method. + /// + public static readonly MethodInfo UpdateAllDescriptorMethodInfo = + UpdateAllMethodInfos.Single(x => x.GetParameters().Length == 2 && x.GetParameters()[1].ParameterType.IsGenericType); + + + //public static readonly MethodInfo UpdateAllString = + // UpdateAllMethodInfos.Single(x => x.GetParameters().Length == 2); + + /// + /// Initializes a new instance of the class. + /// + /// The parse information. + /// The optional predicate. + /// The optional selector. + public UpdateAllExpressionNode(MethodCallExpressionParseInfo parseInfo, + LambdaExpression optionalPredicate, LambdaExpression optionalSelector) + : base(parseInfo, optionalPredicate, optionalSelector) + { + // No-op. + } + + /** */ + [ExcludeFromCodeCoverage] + public override Expression Resolve(ParameterExpression inputParameter, Expression expressionToBeResolved, + ClauseGenerationContext clauseGenerationContext) + { + throw CreateResolveNotSupportedException(); + } + + /** */ + protected override ResultOperatorBase CreateResultOperator(ClauseGenerationContext clauseGenerationContext) + { + return new UpdateAllResultOperator(); + } + + /// + /// Gets the supported methods. + /// + public static IEnumerable GetSupportedMethods() + { + yield return UpdateAllDescriptorMethodInfo; + //yield return UpdateAllString; + } + } +} diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs new file mode 100644 index 0000000000000..858736234d964 --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs @@ -0,0 +1,60 @@ +/* + * 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.Linq.Impl.Dml +{ + using System; + using System.Diagnostics.CodeAnalysis; + using System.Linq.Expressions; + using Apache.Ignite.Core.Cache; + using Remotion.Linq.Clauses; + using Remotion.Linq.Clauses.ResultOperators; + using Remotion.Linq.Clauses.StreamedData; + + /// + /// Represents an operator for . + /// + internal sealed class UpdateAllResultOperator : ValueFromSequenceResultOperatorBase + { + /** */ + public override IStreamedDataInfo GetOutputDataInfo(IStreamedDataInfo inputInfo) + { + return new StreamedScalarValueInfo(typeof(int)); + } + + /** */ + [ExcludeFromCodeCoverage] + public override ResultOperatorBase Clone(CloneContext cloneContext) + { + return new UpdateAllResultOperator(); + } + + /** */ + [ExcludeFromCodeCoverage] + public override void TransformExpressions(Func transformation) + { + // No-op. + } + + /** */ + [ExcludeFromCodeCoverage] + public override StreamedValue ExecuteInMemory(StreamedSequence sequence) + { + throw new NotSupportedException("RemoveAll is not supported for in-memory sequences."); + } + } +} \ No newline at end of file From 46a7f00648e19a5480c850a8d69fbb6b8c51d316 Mon Sep 17 00:00:00 2001 From: gurustron Date: Mon, 26 Feb 2018 23:58:19 +0300 Subject: [PATCH 03/26] remove impl for now --- .../Impl/CacheQueryModelVisitor.cs | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs index 736d1c050badf..00a6ccbaa85f9 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs @@ -109,11 +109,11 @@ private void VisitQueryModel(QueryModel queryModel, bool includeAllFields) var hasDelete = VisitRemoveOperator(queryModel); - if (!hasDelete && queryModel.ResultOperators.LastOrDefault() is UpdateAllResultOperator) - { - _builder.Append("update "); - } - else + //if (!hasDelete && queryModel.ResultOperators.LastOrDefault() is UpdateAllResultOperator) + //{ + // _builder.Append("update "); + //} + //else if (!hasDelete) { // SELECT @@ -126,7 +126,7 @@ private void VisitQueryModel(QueryModel queryModel, bool includeAllFields) // FROM ... WHERE ... JOIN ... base.VisitQueryModel(queryModel); - if (!hasDelete && queryModel.ResultOperators.LastOrDefault() is UpdateAllResultOperator) + if (!hasDelete /*&& queryModel.ResultOperators.LastOrDefault() is UpdateAllResultOperator*/) { // UNION ... ProcessResultOperatorsEnd(queryModel); @@ -394,11 +394,12 @@ private bool ProcessGroupings(QueryModel queryModel) public override void VisitMainFromClause(MainFromClause fromClause, QueryModel queryModel) { base.VisitMainFromClause(fromClause, queryModel); - if (!queryModel.ResultOperators.Any(rOp => rOp is UpdateAllResultOperator)) - { - _builder.AppendFormat("from "); - } + //if (!queryModel.ResultOperators.Any(rOp => rOp is UpdateAllResultOperator)) + //{ + // _builder.AppendFormat("from "); + //} + _builder.AppendFormat("from "); ValidateFromClause(fromClause); _aliases.AppendAsClause(_builder, fromClause).Append(" "); From c551c80db3a5074cbfc00209f3c6b3dfae98e121 Mon Sep 17 00:00:00 2001 From: gurustron Date: Tue, 27 Feb 2018 03:39:54 +0300 Subject: [PATCH 04/26] implementation experiments --- .../Cache/Query/Linq/CacheLinqTest.Custom.cs | 5 +- .../Apache.Ignite.Linq/CacheLinqExtensions.cs | 75 ++++++++++++++++++- .../Impl/Dml/UpdateAllExpressionNode.cs | 33 ++++++-- .../Impl/Dml/UpdateAllResultOperator.cs | 3 +- 4 files changed, 103 insertions(+), 13 deletions(-) diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs index 6d3e835f46477..8e26d895d47b4 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs @@ -121,7 +121,10 @@ public void TestUpdateAll() var queryable = cache.AsCacheQueryable(); - queryable.UpdateAll(d => d.Set(p => p.AliasTest, 1) + queryable + .Where(p => p.Value.Age > 0) + .Take(2) + .UpdateAll(d => d.Set(p => p.AliasTest, 1) .Set(p => p.Age, p => p.Age + 1)); //queryable.UpdateAll(entry => "asd"); diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs index 23ce78964253f..b9adbc612bf88 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs @@ -211,11 +211,80 @@ public static ICacheQueryable ToCacheQueryable(this IQueryable query) IgniteArgumentCheck.NotNull(query, "query"); IgniteArgumentCheck.NotNull(updateDescription, "updateDescription"); - var method = UpdateAllExpressionNode.UpdateAllDescriptorMethodInfo + if (!(updateDescription.Body is MethodCallExpression)) + { + throw new NotSupportedException(); + } + + var parameter = Expression.Parameter(typeof(TValue), "p"); + + var type = typeof(UpdateDesc<>).MakeGenericType(typeof(TValue)); + + var newDescriptor = Expression.New(type.GetConstructors().First()); + + var methodCall = (MethodCallExpression) updateDescription.Body; + while (true) + { + if (methodCall.Object is MethodCallExpression) + { + methodCall = (MethodCallExpression) methodCall.Object; + } + else + { + methodCall = methodCall.Update(newDescriptor, methodCall.Arguments); + break; + } + } + + + var updateDescriptionBody = updateDescription.Body; + var updateDescriptionNodeType = updateDescription.NodeType; + var expressionType = updateDescriptionBody.NodeType; + + var mc = updateDescriptionBody as MethodCallExpression; + var expression = Expression.Lambda>>(methodCall, parameter); + return UpdateAllImpl(query, expression); + } + + public class UpdateAllExpression:Expression + { + public int I { get; set; } + + public override ExpressionType NodeType + { + get { return ExpressionType.Lambda; } + } + } + + internal static int UpdateAllImpl(this IQueryable> query, + //UpdateAllExpression updateDescription + Expression>> updateDescription + ) + { + IgniteArgumentCheck.NotNull(query, "query"); + IgniteArgumentCheck.NotNull(updateDescription, "updateDescription"); + + var method = UpdateAllExpressionNode.UpdateAllImplDescriptorMethodInfo .MakeGenericMethod(typeof(TKey), typeof(TValue)); // TODO: cache? - return query.Provider.Execute(Expression.Call(null, method, new[] { query.Expression, - Expression.Quote(updateDescription)})); + return query.Provider.Execute(Expression.Call(null, method, new[] + { + query.Expression, + Expression.Quote(updateDescription) + })); + } + + class UpdateDesc:IUpdateDescriptor + { + public IUpdateDescriptor Set(Func selector, TProp value) + { + return this; + } + + public IUpdateDescriptor Set(Func selector, Func valueBuilder) + { + return this; + } } //public static int UpdateAll(this IQueryable> query, Expression, string>> func) diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs index e15cacd8ad03b..2a8dfb4764a6a 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs @@ -33,18 +33,33 @@ namespace Apache.Ignite.Linq.Impl.Dml /// internal sealed class UpdateAllExpressionNode : ResultOperatorExpressionNodeBase { + static UpdateAllExpressionNode() + { + var updateAllMethodInfos = typeof(CacheLinqExtensions) + .GetMethods() + .Where(x => x.Name == "UpdateAll") + .ToArray(); + var updateAllImplMethodInfos = typeof(CacheLinqExtensions) + .GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic) + .Where(x => x.Name == "UpdateAllImpl") + .ToArray(); + UpdateAllDescriptorMethodInfo = + updateAllMethodInfos.Single(x => x.GetParameters().Length == 2 && x.GetParameters()[1].ParameterType.IsGenericType); + UpdateAllImplDescriptorMethodInfo = + updateAllImplMethodInfos.Single(x => x.GetParameters().Length == 2); + } + + + /** */ + /** */ - private static readonly MethodInfo[] UpdateAllMethodInfos = typeof(CacheLinqExtensions) - .GetMethods() - .Where(x => x.Name == "UpdateAll") - .ToArray(); /// /// The UpdateAll(pred) method. /// - public static readonly MethodInfo UpdateAllDescriptorMethodInfo = - UpdateAllMethodInfos.Single(x => x.GetParameters().Length == 2 && x.GetParameters()[1].ParameterType.IsGenericType); + public static readonly MethodInfo UpdateAllDescriptorMethodInfo; + public static readonly MethodInfo UpdateAllImplDescriptorMethodInfo; //public static readonly MethodInfo UpdateAllString = // UpdateAllMethodInfos.Single(x => x.GetParameters().Length == 2); @@ -55,9 +70,10 @@ internal sealed class UpdateAllExpressionNode : ResultOperatorExpressionNodeBase /// The parse information. /// The optional predicate. /// The optional selector. + /// public UpdateAllExpressionNode(MethodCallExpressionParseInfo parseInfo, - LambdaExpression optionalPredicate, LambdaExpression optionalSelector) - : base(parseInfo, optionalPredicate, optionalSelector) + Expression test) + : base(parseInfo, null, null) { // No-op. } @@ -81,6 +97,7 @@ protected override ResultOperatorBase CreateResultOperator(ClauseGenerationConte /// public static IEnumerable GetSupportedMethods() { + yield return UpdateAllImplDescriptorMethodInfo; yield return UpdateAllDescriptorMethodInfo; //yield return UpdateAllString; } diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs index 858736234d964..881fbeb2fb25c 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs @@ -54,7 +54,8 @@ public override void TransformExpressions(Func transform [ExcludeFromCodeCoverage] public override StreamedValue ExecuteInMemory(StreamedSequence sequence) { - throw new NotSupportedException("RemoveAll is not supported for in-memory sequences."); + throw new NotSupportedException("UpdateAll is not supported for in-memory sequences."); } + } } \ No newline at end of file From fee3870c1e40edcd41deb06f780d26f98c204ac1 Mon Sep 17 00:00:00 2001 From: gurustron Date: Wed, 28 Feb 2018 01:13:38 +0300 Subject: [PATCH 05/26] UpdateDescription set up --- .../Apache.Ignite.Linq.csproj | 1 + .../Apache.Ignite.Linq/CacheLinqExtensions.cs | 80 +++++-------------- .../Impl/Dml/UpdateAllExpressionNode.cs | 12 +-- .../Impl/Dml/UpdateAllResultOperator.cs | 9 ++- .../Impl/Dml/UpdateDescription.cs | 28 +++++++ 5 files changed, 61 insertions(+), 69 deletions(-) create mode 100644 modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateDescription.cs diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj b/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj index 67a6664369da9..8e9eb3088ed93 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj @@ -79,6 +79,7 @@ + diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs index b9adbc612bf88..69658a598121b 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs @@ -18,6 +18,8 @@ namespace Apache.Ignite.Linq { using System; + using System.Collections.Generic; + using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; @@ -32,7 +34,7 @@ namespace Apache.Ignite.Linq /// /// Extensions methods for . /// - public static class CacheLinqExtensions + public static partial class CacheLinqExtensions { /// /// Gets an instance over this cache. @@ -213,14 +215,11 @@ public static ICacheQueryable ToCacheQueryable(this IQueryable query) if (!(updateDescription.Body is MethodCallExpression)) { - throw new NotSupportedException(); + throw new NotSupportedException("Expression is not supported for UpdateAll: " + updateDescription.Body); } var parameter = Expression.Parameter(typeof(TValue), "p"); - - var type = typeof(UpdateDesc<>).MakeGenericType(typeof(TValue)); - - var newDescriptor = Expression.New(type.GetConstructors().First()); + var updates = new List>(); var methodCall = (MethodCallExpression) updateDescription.Body; while (true) @@ -228,38 +227,30 @@ public static ICacheQueryable ToCacheQueryable(this IQueryable query) if (methodCall.Object is MethodCallExpression) { methodCall = (MethodCallExpression) methodCall.Object; + updates.Add(methodCall.Arguments); } else { - methodCall = methodCall.Update(newDescriptor, methodCall.Arguments); break; } } + var lambda = Expression.Lambda>( + Expression.Constant(new UpdateDescription {Updates = updates}, typeof(UpdateDescription)), parameter); - var updateDescriptionBody = updateDescription.Body; - var updateDescriptionNodeType = updateDescription.NodeType; - var expressionType = updateDescriptionBody.NodeType; - - var mc = updateDescriptionBody as MethodCallExpression; - var expression = Expression.Lambda>>(methodCall, parameter); - return UpdateAllImpl(query, expression); - } - - public class UpdateAllExpression:Expression - { - public int I { get; set; } - - public override ExpressionType NodeType - { - get { return ExpressionType.Lambda; } - } + return UpdateAllImpl(query, lambda); } + /// + /// Internal method rewriting user call + /// + /// Key type. + /// Value type. + /// The query. + /// The update description. + /// Affected row count. internal static int UpdateAllImpl(this IQueryable> query, - //UpdateAllExpression updateDescription - Expression>> updateDescription - ) + Expression> updateDescription) { IgniteArgumentCheck.NotNull(query, "query"); IgniteArgumentCheck.NotNull(updateDescription, "updateDescription"); @@ -273,40 +264,5 @@ public override ExpressionType NodeType Expression.Quote(updateDescription) })); } - - class UpdateDesc:IUpdateDescriptor - { - public IUpdateDescriptor Set(Func selector, TProp value) - { - return this; - } - - public IUpdateDescriptor Set(Func selector, Func valueBuilder) - { - return this; - } - } - - //public static int UpdateAll(this IQueryable> query, Expression, string>> func) - //{ - // var method = UpdateAllExpressionNode.UpdateAllString - // .MakeGenericMethod(typeof(TKey), typeof(TValue)); // TODO: cache? - - // return query.Provider.Execute(Expression.Call(null, method, query.Expression, - // Expression.Quote(func))); - //} - - //public class UD:IUpdateDescriptor - //{ - // public IUpdateDescriptor Set(Func selector, TProp value) - // { - // return this; - // } - - // public IUpdateDescriptor Set(Func selector, Func valueBuilder) - // { - // return this; - // } - //} } } \ No newline at end of file diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs index 2a8dfb4764a6a..c53d3874f0b3a 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs @@ -33,6 +33,8 @@ namespace Apache.Ignite.Linq.Impl.Dml /// internal sealed class UpdateAllExpressionNode : ResultOperatorExpressionNodeBase { + private readonly Expression _updateDescription; + static UpdateAllExpressionNode() { var updateAllMethodInfos = typeof(CacheLinqExtensions) @@ -68,14 +70,12 @@ static UpdateAllExpressionNode() /// Initializes a new instance of the class. /// /// The parse information. - /// The optional predicate. - /// The optional selector. - /// + /// Expression with update description info public UpdateAllExpressionNode(MethodCallExpressionParseInfo parseInfo, - Expression test) + Expression updateDescription) : base(parseInfo, null, null) { - // No-op. + _updateDescription = updateDescription; } /** */ @@ -89,7 +89,7 @@ static UpdateAllExpressionNode() /** */ protected override ResultOperatorBase CreateResultOperator(ClauseGenerationContext clauseGenerationContext) { - return new UpdateAllResultOperator(); + return new UpdateAllResultOperator(_updateDescription); } /// diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs index 881fbeb2fb25c..ef678f5f791ad 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs @@ -30,6 +30,13 @@ namespace Apache.Ignite.Linq.Impl.Dml /// internal sealed class UpdateAllResultOperator : ValueFromSequenceResultOperatorBase { + private readonly Expression _updateDescription; + + public UpdateAllResultOperator(Expression updateDescription) + { + _updateDescription = updateDescription; + } + /** */ public override IStreamedDataInfo GetOutputDataInfo(IStreamedDataInfo inputInfo) { @@ -40,7 +47,7 @@ public override IStreamedDataInfo GetOutputDataInfo(IStreamedDataInfo inputInfo) [ExcludeFromCodeCoverage] public override ResultOperatorBase Clone(CloneContext cloneContext) { - return new UpdateAllResultOperator(); + return new UpdateAllResultOperator(_updateDescription); } /** */ diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateDescription.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateDescription.cs new file mode 100644 index 0000000000000..4a8790678e935 --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateDescription.cs @@ -0,0 +1,28 @@ +/* + * 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.Linq +{ + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Linq.Expressions; + + public class UpdateDescription + { + public List> Updates { get; set; } + } +} \ No newline at end of file From 45eceb26d6c594e952169f2307702a89891f5b2e Mon Sep 17 00:00:00 2001 From: gurustron Date: Thu, 1 Mar 2018 00:39:48 +0300 Subject: [PATCH 06/26] continue implementation(fix rewrite, generate sql). small refactoring --- .../Apache.Ignite.Linq/CacheLinqExtensions.cs | 15 +-- .../Impl/CacheQueryModelVisitor.cs | 114 +++++++++++------- 2 files changed, 74 insertions(+), 55 deletions(-) diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs index 69658a598121b..926e2a2a59392 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs @@ -222,17 +222,10 @@ public static ICacheQueryable ToCacheQueryable(this IQueryable query) var updates = new List>(); var methodCall = (MethodCallExpression) updateDescription.Body; - while (true) + while (methodCall != null) { - if (methodCall.Object is MethodCallExpression) - { - methodCall = (MethodCallExpression) methodCall.Object; - updates.Add(methodCall.Arguments); - } - else - { - break; - } + updates.Add(methodCall.Arguments); + methodCall = methodCall.Object as MethodCallExpression; } var lambda = Expression.Lambda>( @@ -249,7 +242,7 @@ public static ICacheQueryable ToCacheQueryable(this IQueryable query) /// The query. /// The update description. /// Affected row count. - internal static int UpdateAllImpl(this IQueryable> query, + internal static int UpdateAllImpl(IQueryable> query, Expression> updateDescription) { IgniteArgumentCheck.NotNull(query, "query"); diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs index 00a6ccbaa85f9..29044ece40f50 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs @@ -107,27 +107,26 @@ private void VisitQueryModel(QueryModel queryModel, bool includeAllFields) { _aliases.Push(); - var hasDelete = VisitRemoveOperator(queryModel); - - //if (!hasDelete && queryModel.ResultOperators.LastOrDefault() is UpdateAllResultOperator) - //{ - // _builder.Append("update "); - //} - //else - if (!hasDelete) + var lastResultOp = queryModel.ResultOperators.LastOrDefault(); + if (lastResultOp is RemoveAllResultOperator) + { + VisitRemoveOperator(queryModel); + } + else if(lastResultOp is UpdateAllResultOperator) + { + VisitUpdateAllOperator(queryModel); + } + else { // SELECT _builder.Append("select "); // TOP 1 FLD1, FLD2 VisitSelectors(queryModel, includeAllFields); - } - // FROM ... WHERE ... JOIN ... - base.VisitQueryModel(queryModel); + // FROM ... WHERE ... JOIN ... + base.VisitQueryModel(queryModel); - if (!hasDelete /*&& queryModel.ResultOperators.LastOrDefault() is UpdateAllResultOperator*/) - { // UNION ... ProcessResultOperatorsEnd(queryModel); } @@ -135,43 +134,66 @@ private void VisitQueryModel(QueryModel queryModel, bool includeAllFields) _aliases.Pop(); } + private void VisitUpdateAllOperator(QueryModel queryModel) + { + var resultOps = queryModel.ResultOperators; + + _builder.Append("update "); + + // FROM ... WHERE ... JOIN ... + base.VisitQueryModel(queryModel); + + if (resultOps.Count == 2) + { + var resOp = resultOps[0] as TakeResultOperator; + + if (resOp == null) + throw new NotSupportedException( + "UpdateAll can not be combined with result operators (other than Take): " + + resultOps[0].GetType().Name); + + _builder.Append("limit "); + BuildSqlExpression(resOp.Count); + } + else if (resultOps.Count > 2) + { + throw new NotSupportedException( + "UpdateAll can not be combined with result operators (other than Take): " + + string.Join(", ", resultOps.Select(x => x.GetType().Name))); + } + } + /// /// Visits the remove operator. Returns true if it is present. /// - private bool VisitRemoveOperator(QueryModel queryModel) + private void VisitRemoveOperator(QueryModel queryModel) { var resultOps = queryModel.ResultOperators; - if (resultOps.LastOrDefault() is RemoveAllResultOperator) - { - _builder.Append("delete "); - - if (resultOps.Count == 2) - { - var resOp = resultOps[0] as TakeResultOperator; + _builder.Append("delete "); - if (resOp == null) - { - throw new NotSupportedException( - "RemoveAll can not be combined with result operators (other than Take): " + - resultOps[0].GetType().Name); - } + if (resultOps.Count == 2) + { + var resOp = resultOps[0] as TakeResultOperator; - _builder.Append("top "); - BuildSqlExpression(resOp.Count); - _builder.Append(" "); - } - else if (resultOps.Count > 2) - { + if (resOp == null) throw new NotSupportedException( "RemoveAll can not be combined with result operators (other than Take): " + - string.Join(", ", resultOps.Select(x => x.GetType().Name))); - } + resultOps[0].GetType().Name); - return true; + _builder.Append("top "); + BuildSqlExpression(resOp.Count); + _builder.Append(" "); } - - return false; + else if (resultOps.Count > 2) + { + throw new NotSupportedException( + "RemoveAll can not be combined with result operators (other than Take): " + + string.Join(", ", resultOps.Select(x => x.GetType().Name))); + } + + // FROM ... WHERE ... JOIN ... + base.VisitQueryModel(queryModel); } /// @@ -344,6 +366,10 @@ private void ProcessSkipTake(QueryModel queryModel) /** */ protected override void VisitBodyClauses(ObservableCollection bodyClauses, QueryModel queryModel) { + if (queryModel.ResultOperators.LastOrDefault() is UpdateAllResultOperator) + { + + } var i = 0; foreach (var join in bodyClauses.OfType()) VisitJoinClause(join, queryModel, i++); @@ -394,19 +420,19 @@ private bool ProcessGroupings(QueryModel queryModel) public override void VisitMainFromClause(MainFromClause fromClause, QueryModel queryModel) { base.VisitMainFromClause(fromClause, queryModel); - //if (!queryModel.ResultOperators.Any(rOp => rOp is UpdateAllResultOperator)) - //{ - // _builder.AppendFormat("from "); - //} - _builder.AppendFormat("from "); + if (!(queryModel.ResultOperators.LastOrDefault() is UpdateAllResultOperator)) + { + _builder.Append("from "); + } + ValidateFromClause(fromClause); _aliases.AppendAsClause(_builder, fromClause).Append(" "); var i = 0; foreach (var additionalFrom in queryModel.BodyClauses.OfType()) { - _builder.AppendFormat(", "); + _builder.Append(", "); ValidateFromClause(additionalFrom); VisitAdditionalFromClause(additionalFrom, queryModel, i++); From 8270c3ace564d20a097d951e821c2ed6df9dd8e6 Mon Sep 17 00:00:00 2001 From: gurustron Date: Thu, 1 Mar 2018 09:38:14 +0300 Subject: [PATCH 07/26] change to be implementable --- .../Cache/Query/Linq/CacheLinqTest.Custom.cs | 2 +- .../Apache.Ignite.Linq/CacheLinqExtensions.cs | 19 +++++++++++++++---- .../Apache.Ignite.Linq/IUpdateDescriptor.cs | 7 ++++--- .../Impl/CacheQueryModelVisitor.cs | 19 +++++++++++++++++-- .../Impl/Dml/UpdateAllResultOperator.cs | 6 +++--- 5 files changed, 40 insertions(+), 13 deletions(-) diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs index 8e26d895d47b4..c45dc27beaef1 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs @@ -125,7 +125,7 @@ public void TestUpdateAll() .Where(p => p.Value.Age > 0) .Take(2) .UpdateAll(d => d.Set(p => p.AliasTest, 1) - .Set(p => p.Age, p => p.Age + 1)); + .Set(p => p.Age, p => p.Value.Age + 1)); //queryable.UpdateAll(entry => "asd"); diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs index 926e2a2a59392..23133b64e293a 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs @@ -208,7 +208,7 @@ public static ICacheQueryable ToCacheQueryable(this IQueryable query) /// The update description. /// Affected row count. public static int UpdateAll(this IQueryable> query, - Expression, IUpdateDescriptor>> updateDescription) + Expression, IUpdateDescriptor>> updateDescription) { IgniteArgumentCheck.NotNull(query, "query"); IgniteArgumentCheck.NotNull(updateDescription, "updateDescription"); @@ -218,7 +218,7 @@ public static ICacheQueryable ToCacheQueryable(this IQueryable query) throw new NotSupportedException("Expression is not supported for UpdateAll: " + updateDescription.Body); } - var parameter = Expression.Parameter(typeof(TValue), "p"); + var parameter = Expression.Parameter(typeof(ICacheEntry), "p"); var updates = new List>(); var methodCall = (MethodCallExpression) updateDescription.Body; @@ -228,7 +228,7 @@ public static ICacheQueryable ToCacheQueryable(this IQueryable query) methodCall = methodCall.Object as MethodCallExpression; } - var lambda = Expression.Lambda>( + var lambda = Expression.Lambda, UpdateDescription>>( Expression.Constant(new UpdateDescription {Updates = updates}, typeof(UpdateDescription)), parameter); return UpdateAllImpl(query, lambda); @@ -243,7 +243,7 @@ public static ICacheQueryable ToCacheQueryable(this IQueryable query) /// The update description. /// Affected row count. internal static int UpdateAllImpl(IQueryable> query, - Expression> updateDescription) + Expression, UpdateDescription>> updateDescription) { IgniteArgumentCheck.NotNull(query, "query"); IgniteArgumentCheck.NotNull(updateDescription, "updateDescription"); @@ -257,5 +257,16 @@ public static ICacheQueryable ToCacheQueryable(this IQueryable query) Expression.Quote(updateDescription) })); } + + public struct MyStruct + { + public Expression Get { get; set; } + public Expression Set { get; set; } + } + + internal static class Set + { + //public static ICacheEntry Set(ICacheEntry e, ) + } } } \ No newline at end of file diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/IUpdateDescriptor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/IUpdateDescriptor.cs index 7dbe8ad583c4d..0d7f38e02e7f2 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/IUpdateDescriptor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/IUpdateDescriptor.cs @@ -1,10 +1,11 @@ namespace Apache.Ignite.Linq { using System; + using Apache.Ignite.Core.Cache; - public interface IUpdateDescriptor + public interface IUpdateDescriptor { - IUpdateDescriptor Set(Func selector, TProp value); - IUpdateDescriptor Set(Func selector, Func valueBuilder); + IUpdateDescriptor Set(Func selector, TProp value); + IUpdateDescriptor Set(Func selector, Func, TProp> valueBuilder); } } \ No newline at end of file diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs index 29044ece40f50..23dd605e7007e 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs @@ -366,10 +366,25 @@ private void ProcessSkipTake(QueryModel queryModel) /** */ protected override void VisitBodyClauses(ObservableCollection bodyClauses, QueryModel queryModel) { - if (queryModel.ResultOperators.LastOrDefault() is UpdateAllResultOperator) + var updateAllResultOperator = queryModel.ResultOperators.LastOrDefault() as UpdateAllResultOperator; + if (updateAllResultOperator != null) { - + var lambdaExpression = updateAllResultOperator.Description as LambdaExpression; + var constantExpression = lambdaExpression.Body as ConstantExpression; + var updateDescription = constantExpression.Value as UpdateDescription; + _builder.Append("set "); + var first = true; + foreach (var update in updateDescription.Updates) + { + if (!first) _builder.Append(", "); + first = false; + BuildSqlExpression(((LambdaExpression)update[0]).Body); + _builder.Append(" = ("); + BuildSqlExpression(((LambdaExpression)update[1]).Body); + _builder.Append(") "); + } } + var i = 0; foreach (var join in bodyClauses.OfType()) VisitJoinClause(join, queryModel, i++); diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs index ef678f5f791ad..fdad7135268aa 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs @@ -30,11 +30,11 @@ namespace Apache.Ignite.Linq.Impl.Dml /// internal sealed class UpdateAllResultOperator : ValueFromSequenceResultOperatorBase { - private readonly Expression _updateDescription; + public Expression Description { get; private set; } public UpdateAllResultOperator(Expression updateDescription) { - _updateDescription = updateDescription; + Description = updateDescription; } /** */ @@ -47,7 +47,7 @@ public override IStreamedDataInfo GetOutputDataInfo(IStreamedDataInfo inputInfo) [ExcludeFromCodeCoverage] public override ResultOperatorBase Clone(CloneContext cloneContext) { - return new UpdateAllResultOperator(_updateDescription); + return new UpdateAllResultOperator(Description); } /** */ From 42dc08e1fa6b39947b7f15b1afda3fc96899344c Mon Sep 17 00:00:00 2001 From: gurustron Date: Thu, 1 Mar 2018 23:08:55 +0300 Subject: [PATCH 08/26] play a little bit more, queryreference works --- .../Cache/Query/Linq/CacheLinqTest.Custom.cs | 5 +- .../Apache.Ignite.Linq/CacheLinqExtensions.cs | 108 ++++++++++++------ .../Apache.Ignite.Linq/IUpdateDescriptor.cs | 32 +++++- .../Impl/CacheQueryModelVisitor.cs | 31 ++++- .../Impl/Dml/UpdateAllExpressionNode.cs | 26 +++-- .../Impl/Dml/UpdateAllResultOperator.cs | 2 +- 6 files changed, 144 insertions(+), 60 deletions(-) diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs index c45dc27beaef1..ed8fb85c194f8 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs @@ -123,9 +123,10 @@ public void TestUpdateAll() queryable .Where(p => p.Value.Age > 0) + //.MyWhere(p => p.Value.Age > 0) .Take(2) - .UpdateAll(d => d.Set(p => p.AliasTest, 1) - .Set(p => p.Age, p => p.Value.Age + 1)); + .UpdateAll(d => UD.Create(d).Set(p => d.Value.AliasTest, 1) + .Set(p => d.Value.Age, p => d.Value.Age + 1)); //queryable.UpdateAll(entry => "asd"); diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs index 23133b64e293a..0d94b4d07c64d 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs @@ -23,6 +23,7 @@ namespace Apache.Ignite.Linq using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; + using System.Reflection; using Apache.Ignite.Core.Cache; using Apache.Ignite.Core.Cache.Configuration; using Apache.Ignite.Core.Cache.Query; @@ -208,47 +209,32 @@ public static ICacheQueryable ToCacheQueryable(this IQueryable query) /// The update description. /// Affected row count. public static int UpdateAll(this IQueryable> query, - Expression, IUpdateDescriptor>> updateDescription) + Expression, IUpdateDescriptor>> updateDescription) { IgniteArgumentCheck.NotNull(query, "query"); IgniteArgumentCheck.NotNull(updateDescription, "updateDescription"); - if (!(updateDescription.Body is MethodCallExpression)) - { - throw new NotSupportedException("Expression is not supported for UpdateAll: " + updateDescription.Body); - } + //if (!(updateDescription.Body is MethodCallExpression)) + //{ + // throw new NotSupportedException("Expression is not supported for UpdateAll: " + updateDescription.Body); + //} - var parameter = Expression.Parameter(typeof(ICacheEntry), "p"); - var updates = new List>(); + //var parameter = Expression.Parameter(typeof(ICacheEntry), "p"); + //var updates = new List>(); - var methodCall = (MethodCallExpression) updateDescription.Body; - while (methodCall != null) - { - updates.Add(methodCall.Arguments); - methodCall = methodCall.Object as MethodCallExpression; - } + //var methodCall = (MethodCallExpression) updateDescription.Body; + //while (methodCall != null) + //{ + // updates.Add(methodCall.Arguments); + // methodCall = methodCall.Object as MethodCallExpression; + //} - var lambda = Expression.Lambda, UpdateDescription>>( - Expression.Constant(new UpdateDescription {Updates = updates}, typeof(UpdateDescription)), parameter); + //var lambda = Expression.Lambda, UpdateDescription>>( + // Expression.Constant(new UpdateDescription {Updates = updates}, typeof(UpdateDescription)), parameter); - return UpdateAllImpl(query, lambda); - } + //return UpdateAllImpl(query, lambda); - /// - /// Internal method rewriting user call - /// - /// Key type. - /// Value type. - /// The query. - /// The update description. - /// Affected row count. - internal static int UpdateAllImpl(IQueryable> query, - Expression, UpdateDescription>> updateDescription) - { - IgniteArgumentCheck.NotNull(query, "query"); - IgniteArgumentCheck.NotNull(updateDescription, "updateDescription"); - - var method = UpdateAllExpressionNode.UpdateAllImplDescriptorMethodInfo + var method = UpdateAllExpressionNode.UpdateAllDescriptorMethodInfo .MakeGenericMethod(typeof(TKey), typeof(TValue)); // TODO: cache? return query.Provider.Execute(Expression.Call(null, method, new[] @@ -258,15 +244,63 @@ public static ICacheQueryable ToCacheQueryable(this IQueryable query) })); } - public struct MyStruct - { - public Expression Get { get; set; } - public Expression Set { get; set; } - } + ///// + ///// Internal method rewriting user call + ///// + ///// Key type. + ///// Value type. + ///// The query. + ///// The update description. + ///// Affected row count. + //internal static int UpdateAllImpl(IQueryable> query, + // Expression, UpdateDescription>> updateDescription) + //{ + // IgniteArgumentCheck.NotNull(query, "query"); + // IgniteArgumentCheck.NotNull(updateDescription, "updateDescription"); + + // var method = UpdateAllExpressionNode.UpdateAllImplDescriptorMethodInfo + // .MakeGenericMethod(typeof(TKey), typeof(TValue)); // TODO: cache? + + // return query.Provider.Execute(Expression.Call(null, method, new[] + // { + // query.Expression, + // Expression.Quote(updateDescription) + // })); + //} internal static class Set { //public static ICacheEntry Set(ICacheEntry e, ) } + + private static MethodInfo GetMethodInfo(Func f, T1 _) + { + return f.Method; + } + + private static MethodInfo GetMethodInfo(Func f, T1 _, T2 __) + { + return f.Method; + } + + private static MethodInfo GetMethodInfo(Func f, T1 _, T2 __, T3 ___) + { + return f.Method; + } + + //private static MethodInfo GetMethodInfo(Func f, T1 unused1, T2 unused2, T3 unused3, T4 unused4) + //{ + // return f.Method; + //} + + //private static MethodInfo GetMethodInfo(Func f, T1 unused1, T2 unused2, T3 unused3, T4 unused4, T5 unused5) + //{ + // return f.Method; + //} + + //private static MethodInfo GetMethodInfo(Func f, T1 unused1, T2 unused2, T3 unused3, T4 unused4, T5 unused5, T6 unused6) + //{ + // return f.Method; + //} } } \ No newline at end of file diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/IUpdateDescriptor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/IUpdateDescriptor.cs index 0d7f38e02e7f2..52df54058bdae 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/IUpdateDescriptor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/IUpdateDescriptor.cs @@ -3,9 +3,35 @@ namespace Apache.Ignite.Linq using System; using Apache.Ignite.Core.Cache; - public interface IUpdateDescriptor + public interface IUpdateDescriptor { - IUpdateDescriptor Set(Func selector, TProp value); - IUpdateDescriptor Set(Func selector, Func, TProp> valueBuilder); + IUpdateDescriptor Set(Func, TProp> selector, TProp value); + IUpdateDescriptor Set(Func, TProp> selector, Func, TProp> valueBuilder); + } + + public class UD : IUpdateDescriptor + { + public UD(ICacheEntry cacheEntry) + { + + } + + public IUpdateDescriptor Set(Func, TProp> selector, TProp value) + { + return this; + } + + public IUpdateDescriptor Set(Func, TProp> selector, Func, TProp> valueBuilder) + { + return this; + } + } + + public static class UD + { + public static IUpdateDescriptor Create(ICacheEntry entry) + { + return new UD(entry); + } } } \ No newline at end of file diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs index 23dd605e7007e..bee77cb3a07fd 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs @@ -369,20 +369,39 @@ protected override void VisitBodyClauses(ObservableCollection bodyC var updateAllResultOperator = queryModel.ResultOperators.LastOrDefault() as UpdateAllResultOperator; if (updateAllResultOperator != null) { - var lambdaExpression = updateAllResultOperator.Description as LambdaExpression; - var constantExpression = lambdaExpression.Body as ConstantExpression; - var updateDescription = constantExpression.Value as UpdateDescription; _builder.Append("set "); var first = true; - foreach (var update in updateDescription.Updates) + var methodCall = updateAllResultOperator.Description as MethodCallExpression; + while (methodCall != null) { + var update = methodCall.Arguments; + if (!first) _builder.Append(", "); first = false; - BuildSqlExpression(((LambdaExpression)update[0]).Body); + BuildSqlExpression(update[0]); _builder.Append(" = ("); - BuildSqlExpression(((LambdaExpression)update[1]).Body); + BuildSqlExpression(update[1]); _builder.Append(") "); + + methodCall = methodCall.Object as MethodCallExpression; } + + //var lambdaExpression = updateAllResultOperator.Description as LambdaExpression; + //var constantExpression = lambdaExpression.Body as ConstantExpression; + //var updateDescription = constantExpression.Value as UpdateDescription; + //_builder.Append("set "); + //var first = true; + //foreach (var update in updateDescription.Updates) + //{ + // if (!first) _builder.Append(", "); + // first = false; + // BuildSqlExpression(((LambdaExpression)update[0]).Body); + // _builder.Append(" = ("); + // BuildSqlExpression(((LambdaExpression)update[1]).Body); + // _builder.Append(") "); + //} + + } var i = 0; diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs index c53d3874f0b3a..81d5b59c9f4d3 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs @@ -33,7 +33,7 @@ namespace Apache.Ignite.Linq.Impl.Dml /// internal sealed class UpdateAllExpressionNode : ResultOperatorExpressionNodeBase { - private readonly Expression _updateDescription; + private readonly LambdaExpression _updateDescription; static UpdateAllExpressionNode() { @@ -41,14 +41,14 @@ static UpdateAllExpressionNode() .GetMethods() .Where(x => x.Name == "UpdateAll") .ToArray(); - var updateAllImplMethodInfos = typeof(CacheLinqExtensions) - .GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic) - .Where(x => x.Name == "UpdateAllImpl") - .ToArray(); + //var updateAllImplMethodInfos = typeof(CacheLinqExtensions) + // .GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic) + // .Where(x => x.Name == "UpdateAllImpl") + // .ToArray(); UpdateAllDescriptorMethodInfo = updateAllMethodInfos.Single(x => x.GetParameters().Length == 2 && x.GetParameters()[1].ParameterType.IsGenericType); - UpdateAllImplDescriptorMethodInfo = - updateAllImplMethodInfos.Single(x => x.GetParameters().Length == 2); + //UpdateAllImplDescriptorMethodInfo = + // updateAllImplMethodInfos.Single(x => x.GetParameters().Length == 2); } @@ -61,7 +61,7 @@ static UpdateAllExpressionNode() /// public static readonly MethodInfo UpdateAllDescriptorMethodInfo; - public static readonly MethodInfo UpdateAllImplDescriptorMethodInfo; + //public static readonly MethodInfo UpdateAllImplDescriptorMethodInfo; //public static readonly MethodInfo UpdateAllString = // UpdateAllMethodInfos.Single(x => x.GetParameters().Length == 2); @@ -72,7 +72,7 @@ static UpdateAllExpressionNode() /// The parse information. /// Expression with update description info public UpdateAllExpressionNode(MethodCallExpressionParseInfo parseInfo, - Expression updateDescription) + LambdaExpression updateDescription) : base(parseInfo, null, null) { _updateDescription = updateDescription; @@ -89,7 +89,11 @@ static UpdateAllExpressionNode() /** */ protected override ResultOperatorBase CreateResultOperator(ClauseGenerationContext clauseGenerationContext) { - return new UpdateAllResultOperator(_updateDescription); + var expression = Source.Resolve(_updateDescription.Parameters[0], + _updateDescription.Body, + clauseGenerationContext); + + return new UpdateAllResultOperator(expression); } /// @@ -97,7 +101,7 @@ protected override ResultOperatorBase CreateResultOperator(ClauseGenerationConte /// public static IEnumerable GetSupportedMethods() { - yield return UpdateAllImplDescriptorMethodInfo; + //yield return UpdateAllImplDescriptorMethodInfo; yield return UpdateAllDescriptorMethodInfo; //yield return UpdateAllString; } diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs index fdad7135268aa..fac36a8b05a4d 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs @@ -54,7 +54,7 @@ public override ResultOperatorBase Clone(CloneContext cloneContext) [ExcludeFromCodeCoverage] public override void TransformExpressions(Func transformation) { - // No-op. + Description = transformation(Description); } /** */ From 2bceb7602078b6facfef78f013aebd3c88911b3e Mon Sep 17 00:00:00 2001 From: gurustron Date: Fri, 2 Mar 2018 01:16:11 +0300 Subject: [PATCH 09/26] base implementation --- .../Cache/Query/Linq/CacheLinqTest.Custom.cs | 10 +-- .../Apache.Ignite.Linq.csproj | 2 +- .../Apache.Ignite.Linq/CacheLinqExtensions.cs | 55 +----------- .../Apache.Ignite.Linq/IUpdateDescriptor.cs | 59 +++++++------ .../Impl/CacheQueryModelVisitor.cs | 31 ++----- ...escription.cs => MemberUpdateContainer.cs} | 20 +++-- .../Impl/Dml/UpdateAllExpressionNode.cs | 88 +++++++++++++------ .../Impl/Dml/UpdateAllResultOperator.cs | 13 +-- 8 files changed, 127 insertions(+), 151 deletions(-) rename modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/{UpdateDescription.cs => MemberUpdateContainer.cs} (66%) diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs index ed8fb85c194f8..d708dd2fcf756 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs @@ -121,15 +121,15 @@ public void TestUpdateAll() var queryable = cache.AsCacheQueryable(); - queryable + var before = queryable.ToArray(); + var updated = queryable .Where(p => p.Value.Age > 0) - //.MyWhere(p => p.Value.Age > 0) .Take(2) - .UpdateAll(d => UD.Create(d).Set(p => d.Value.AliasTest, 1) - .Set(p => d.Value.Age, p => d.Value.Age + 1)); + .UpdateAll(d => d.Set(p => p.AliasTest, 1) + .Set(p => p.Age, p => p.Value.Age + 1)); - //queryable.UpdateAll(entry => "asd"); + var after = queryable.ToArray(); } } } \ No newline at end of file diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj b/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj index 8e9eb3088ed93..d5a2d83c3ef85 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj @@ -64,6 +64,7 @@ + @@ -79,7 +80,6 @@ - diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs index 0d94b4d07c64d..a1644da536fe8 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs @@ -18,15 +18,11 @@ namespace Apache.Ignite.Linq { using System; - using System.Collections.Generic; - using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; using System.Reflection; using Apache.Ignite.Core.Cache; - using Apache.Ignite.Core.Cache.Configuration; - using Apache.Ignite.Core.Cache.Query; using Apache.Ignite.Core.Impl.Cache; using Apache.Ignite.Core.Impl.Common; using Apache.Ignite.Linq.Impl; @@ -209,31 +205,11 @@ public static ICacheQueryable ToCacheQueryable(this IQueryable query) /// The update description. /// Affected row count. public static int UpdateAll(this IQueryable> query, - Expression, IUpdateDescriptor>> updateDescription) + Expression, IUpdateDescriptor>> updateDescription) { IgniteArgumentCheck.NotNull(query, "query"); IgniteArgumentCheck.NotNull(updateDescription, "updateDescription"); - //if (!(updateDescription.Body is MethodCallExpression)) - //{ - // throw new NotSupportedException("Expression is not supported for UpdateAll: " + updateDescription.Body); - //} - - //var parameter = Expression.Parameter(typeof(ICacheEntry), "p"); - //var updates = new List>(); - - //var methodCall = (MethodCallExpression) updateDescription.Body; - //while (methodCall != null) - //{ - // updates.Add(methodCall.Arguments); - // methodCall = methodCall.Object as MethodCallExpression; - //} - - //var lambda = Expression.Lambda, UpdateDescription>>( - // Expression.Constant(new UpdateDescription {Updates = updates}, typeof(UpdateDescription)), parameter); - - //return UpdateAllImpl(query, lambda); - var method = UpdateAllExpressionNode.UpdateAllDescriptorMethodInfo .MakeGenericMethod(typeof(TKey), typeof(TValue)); // TODO: cache? @@ -244,35 +220,6 @@ public static ICacheQueryable ToCacheQueryable(this IQueryable query) })); } - ///// - ///// Internal method rewriting user call - ///// - ///// Key type. - ///// Value type. - ///// The query. - ///// The update description. - ///// Affected row count. - //internal static int UpdateAllImpl(IQueryable> query, - // Expression, UpdateDescription>> updateDescription) - //{ - // IgniteArgumentCheck.NotNull(query, "query"); - // IgniteArgumentCheck.NotNull(updateDescription, "updateDescription"); - - // var method = UpdateAllExpressionNode.UpdateAllImplDescriptorMethodInfo - // .MakeGenericMethod(typeof(TKey), typeof(TValue)); // TODO: cache? - - // return query.Provider.Execute(Expression.Call(null, method, new[] - // { - // query.Expression, - // Expression.Quote(updateDescription) - // })); - //} - - internal static class Set - { - //public static ICacheEntry Set(ICacheEntry e, ) - } - private static MethodInfo GetMethodInfo(Func f, T1 _) { return f.Method; diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/IUpdateDescriptor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/IUpdateDescriptor.cs index 52df54058bdae..1eed65ec51c02 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/IUpdateDescriptor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/IUpdateDescriptor.cs @@ -1,37 +1,42 @@ +/* + * 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.Linq { using System; using Apache.Ignite.Core.Cache; + /// + /// Dummy interface to provide update description for + /// + /// Key type. + /// Value type. public interface IUpdateDescriptor { - IUpdateDescriptor Set(Func, TProp> selector, TProp value); - IUpdateDescriptor Set(Func, TProp> selector, Func, TProp> valueBuilder); - } - - public class UD : IUpdateDescriptor - { - public UD(ICacheEntry cacheEntry) - { - - } + /// + /// + /// + /// + /// + /// + /// + IUpdateDescriptor Set(Func selector, TProp value); - public IUpdateDescriptor Set(Func, TProp> selector, TProp value) - { - return this; - } - - public IUpdateDescriptor Set(Func, TProp> selector, Func, TProp> valueBuilder) - { - return this; - } - } - - public static class UD - { - public static IUpdateDescriptor Create(ICacheEntry entry) - { - return new UD(entry); - } + IUpdateDescriptor Set(Func selector, + Func, TProp> valueBuilder); } } \ No newline at end of file diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs index bee77cb3a07fd..5567d2e18647f 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs @@ -371,37 +371,16 @@ protected override void VisitBodyClauses(ObservableCollection bodyC { _builder.Append("set "); var first = true; - var methodCall = updateAllResultOperator.Description as MethodCallExpression; - while (methodCall != null) + foreach (var update in updateAllResultOperator.Updates) { - var update = methodCall.Arguments; - if (!first) _builder.Append(", "); first = false; - BuildSqlExpression(update[0]); - _builder.Append(" = ("); - BuildSqlExpression(update[1]); - _builder.Append(") "); - - methodCall = methodCall.Object as MethodCallExpression; + BuildSqlExpression(update.Selector); + _builder.Append(" = "); + BuildSqlExpression(update.Value); } - //var lambdaExpression = updateAllResultOperator.Description as LambdaExpression; - //var constantExpression = lambdaExpression.Body as ConstantExpression; - //var updateDescription = constantExpression.Value as UpdateDescription; - //_builder.Append("set "); - //var first = true; - //foreach (var update in updateDescription.Updates) - //{ - // if (!first) _builder.Append(", "); - // first = false; - // BuildSqlExpression(((LambdaExpression)update[0]).Body); - // _builder.Append(" = ("); - // BuildSqlExpression(((LambdaExpression)update[1]).Body); - // _builder.Append(") "); - //} - - + _builder.Append(" "); } var i = 0; diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateDescription.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/MemberUpdateContainer.cs similarity index 66% rename from modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateDescription.cs rename to modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/MemberUpdateContainer.cs index 4a8790678e935..057b4186f8de3 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateDescription.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/MemberUpdateContainer.cs @@ -15,14 +15,24 @@ * limitations under the License. */ -namespace Apache.Ignite.Linq + +namespace Apache.Ignite.Linq.Impl.Dml { - using System.Collections.Generic; - using System.Collections.ObjectModel; using System.Linq.Expressions; - public class UpdateDescription + /// + /// Contains information about member update + /// + public struct MemberUpdateContainer { - public List> Updates { get; set; } + /// + /// Gets or sets member selector + /// + public Expression Selector { get; set; } + + /// + /// Gets or sets member new value + /// + public Expression Value { get; set; } } } \ No newline at end of file diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs index 81d5b59c9f4d3..bef79d0954f37 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs @@ -17,6 +17,7 @@ namespace Apache.Ignite.Linq.Impl.Dml { + using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; @@ -24,11 +25,15 @@ namespace Apache.Ignite.Linq.Impl.Dml using System.Reflection; using Apache.Ignite.Core.Cache; using Remotion.Linq.Clauses; + using Remotion.Linq.Clauses.Expressions; + using Remotion.Linq.Parsing.ExpressionVisitors; using Remotion.Linq.Parsing.Structure.IntermediateModel; /// - /// Represents a for - /// . + /// Represents a for + /// + /// . /// When user calls UpdateAll, this node is generated. /// internal sealed class UpdateAllExpressionNode : ResultOperatorExpressionNodeBase @@ -41,33 +46,19 @@ static UpdateAllExpressionNode() .GetMethods() .Where(x => x.Name == "UpdateAll") .ToArray(); - //var updateAllImplMethodInfos = typeof(CacheLinqExtensions) - // .GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic) - // .Where(x => x.Name == "UpdateAllImpl") - // .ToArray(); + UpdateAllDescriptorMethodInfo = - updateAllMethodInfos.Single(x => x.GetParameters().Length == 2 && x.GetParameters()[1].ParameterType.IsGenericType); - //UpdateAllImplDescriptorMethodInfo = - // updateAllImplMethodInfos.Single(x => x.GetParameters().Length == 2); + updateAllMethodInfos.Single(x => + x.GetParameters().Length == 2 && x.GetParameters()[1].ParameterType.IsGenericType); } - - /** */ - - /** */ - /// /// The UpdateAll(pred) method. /// public static readonly MethodInfo UpdateAllDescriptorMethodInfo; - //public static readonly MethodInfo UpdateAllImplDescriptorMethodInfo; - - //public static readonly MethodInfo UpdateAllString = - // UpdateAllMethodInfos.Single(x => x.GetParameters().Length == 2); - /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The parse information. /// Expression with update description info @@ -89,11 +80,56 @@ static UpdateAllExpressionNode() /** */ protected override ResultOperatorBase CreateResultOperator(ClauseGenerationContext clauseGenerationContext) { - var expression = Source.Resolve(_updateDescription.Parameters[0], - _updateDescription.Body, + var querySourceRefExpression = (QuerySourceReferenceExpression) Source.Resolve( + _updateDescription.Parameters[0], + _updateDescription.Parameters[0], clauseGenerationContext); - - return new UpdateAllResultOperator(expression); + + var cacheEntryType = querySourceRefExpression.Type; + var querySourceAccessValue = + Expression.MakeMemberAccess(querySourceRefExpression, cacheEntryType.GetMember("Value").First()); + + + if (!(_updateDescription.Body is MethodCallExpression)) + throw new NotSupportedException("Expression is not supported for UpdateAll: " + + _updateDescription.Body); + + var updates = new List(); + + var methodCall = (MethodCallExpression) _updateDescription.Body; + while (methodCall != null) + { + if (methodCall.Arguments.Count != 2) + throw new NotSupportedException("Method is not supported for UpdateAll: " + methodCall); + + var selectorLambda = (LambdaExpression) methodCall.Arguments[0]; + var selector = ReplacingExpressionVisitor.Replace(selectorLambda.Parameters[0], querySourceAccessValue, + selectorLambda.Body); + + var newValue = methodCall.Arguments[1]; + switch (newValue.NodeType) + { + case ExpressionType.Constant: + break; + case ExpressionType.Lambda: + var newValueLambda = (LambdaExpression) newValue; + newValue = ReplacingExpressionVisitor.Replace(newValueLambda.Parameters[0], + querySourceRefExpression, newValueLambda.Body); + break; + default: + throw new NotSupportedException("Value expression is not supported for UpdateAll: " + newValue); + } + + updates.Add(new MemberUpdateContainer + { + Selector = selector, + Value = newValue + }); + + methodCall = methodCall.Object as MethodCallExpression; + } + + return new UpdateAllResultOperator(updates); } /// @@ -101,9 +137,7 @@ protected override ResultOperatorBase CreateResultOperator(ClauseGenerationConte /// public static IEnumerable GetSupportedMethods() { - //yield return UpdateAllImplDescriptorMethodInfo; yield return UpdateAllDescriptorMethodInfo; - //yield return UpdateAllString; } } -} +} \ No newline at end of file diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs index fac36a8b05a4d..ca93ea995df71 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs @@ -18,7 +18,9 @@ namespace Apache.Ignite.Linq.Impl.Dml { using System; + using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; + using System.Linq; using System.Linq.Expressions; using Apache.Ignite.Core.Cache; using Remotion.Linq.Clauses; @@ -30,11 +32,11 @@ namespace Apache.Ignite.Linq.Impl.Dml /// internal sealed class UpdateAllResultOperator : ValueFromSequenceResultOperatorBase { - public Expression Description { get; private set; } + public MemberUpdateContainer[] Updates { get; private set; } - public UpdateAllResultOperator(Expression updateDescription) + public UpdateAllResultOperator(IEnumerable updates) { - Description = updateDescription; + Updates = updates.ToArray(); } /** */ @@ -47,14 +49,14 @@ public override IStreamedDataInfo GetOutputDataInfo(IStreamedDataInfo inputInfo) [ExcludeFromCodeCoverage] public override ResultOperatorBase Clone(CloneContext cloneContext) { - return new UpdateAllResultOperator(Description); + return new UpdateAllResultOperator(Updates); } /** */ [ExcludeFromCodeCoverage] public override void TransformExpressions(Func transformation) { - Description = transformation(Description); + // No-op. } /** */ @@ -63,6 +65,5 @@ public override StreamedValue ExecuteInMemory(StreamedSequence sequence) { throw new NotSupportedException("UpdateAll is not supported for in-memory sequences."); } - } } \ No newline at end of file From 28a6c111b534131a00a55b5e9ff8153d1601ac9e Mon Sep 17 00:00:00 2001 From: gurustron Date: Fri, 2 Mar 2018 01:20:41 +0300 Subject: [PATCH 10/26] typo fix --- .../Impl/CacheQueryModelVisitor.cs | 22 +++++++++---------- .../Impl/Dml/UpdateAllExpressionNode.cs | 1 - 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs index 5567d2e18647f..a11c0b53272b0 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs @@ -201,13 +201,13 @@ private void VisitRemoveOperator(QueryModel queryModel) /// public void VisitSelectors(QueryModel queryModel, bool includeAllFields) { - var parenCount = ProcessResultOperatorsBegin(queryModel); + var parentCount = ProcessResultOperatorsBegin(queryModel); - if (parenCount >= 0) + if (parentCount >= 0) { // FIELD1, FIELD2 - BuildSqlExpression(queryModel.SelectClause.Selector, parenCount > 0, includeAllFields); - _builder.Append(')', parenCount).Append(" "); + BuildSqlExpression(queryModel.SelectClause.Selector, parentCount > 0, includeAllFields); + _builder.Append(')', parentCount).Append(" "); } } @@ -217,34 +217,34 @@ public void VisitSelectors(QueryModel queryModel, bool includeAllFields) [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] private int ProcessResultOperatorsBegin(QueryModel queryModel) { - int parenCount = 0; + int parentCount = 0; foreach (var op in queryModel.ResultOperators.Reverse()) { if (op is CountResultOperator || op is AnyResultOperator) { _builder.Append("count ("); - parenCount++; + parentCount++; } else if (op is SumResultOperator) { _builder.Append("sum ("); - parenCount++; + parentCount++; } else if (op is MinResultOperator) { _builder.Append("min ("); - parenCount++; + parentCount++; } else if (op is MaxResultOperator) { _builder.Append("max ("); - parenCount++; + parentCount++; } else if (op is AverageResultOperator) { _builder.Append("avg ("); - parenCount++; + parentCount++; } else if (op is DistinctResultOperator) _builder.Append("distinct "); @@ -260,7 +260,7 @@ private int ProcessResultOperatorsBegin(QueryModel queryModel) else throw new NotSupportedException("Operator is not supported: " + op); } - return parenCount; + return parentCount; } /// diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs index bef79d0954f37..1bf197207c54a 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs @@ -89,7 +89,6 @@ protected override ResultOperatorBase CreateResultOperator(ClauseGenerationConte var querySourceAccessValue = Expression.MakeMemberAccess(querySourceRefExpression, cacheEntryType.GetMember("Value").First()); - if (!(_updateDescription.Body is MethodCallExpression)) throw new NotSupportedException("Expression is not supported for UpdateAll: " + _updateDescription.Body); From a01870ecbfc2f91f281b5c89bf5ad5ee65eda703 Mon Sep 17 00:00:00 2001 From: gurustron Date: Fri, 2 Mar 2018 01:55:20 +0300 Subject: [PATCH 11/26] some clean up --- .../Cache/Query/Linq/CacheLinqTest.Custom.cs | 4 +- .../Apache.Ignite.Linq/CacheLinqExtensions.cs | 29 ------------- .../Apache.Ignite.Linq/IUpdateDescriptor.cs | 15 +++++-- .../Impl/CacheQueryModelVisitor.cs | 41 +++++++++---------- .../Impl/Dml/UpdateAllExpressionNode.cs | 4 +- 5 files changed, 33 insertions(+), 60 deletions(-) diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs index d708dd2fcf756..82ed45ea52a1a 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs @@ -121,15 +121,13 @@ public void TestUpdateAll() var queryable = cache.AsCacheQueryable(); - var before = queryable.ToArray(); var updated = queryable .Where(p => p.Value.Age > 0) .Take(2) .UpdateAll(d => d.Set(p => p.AliasTest, 1) .Set(p => p.Age, p => p.Value.Age + 1)); - - var after = queryable.ToArray(); + Assert.AreEqual(updated, 2); } } } \ No newline at end of file diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs index a1644da536fe8..5d5cea6523913 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs @@ -220,34 +220,5 @@ public static ICacheQueryable ToCacheQueryable(this IQueryable query) })); } - private static MethodInfo GetMethodInfo(Func f, T1 _) - { - return f.Method; - } - - private static MethodInfo GetMethodInfo(Func f, T1 _, T2 __) - { - return f.Method; - } - - private static MethodInfo GetMethodInfo(Func f, T1 _, T2 __, T3 ___) - { - return f.Method; - } - - //private static MethodInfo GetMethodInfo(Func f, T1 unused1, T2 unused2, T3 unused3, T4 unused4) - //{ - // return f.Method; - //} - - //private static MethodInfo GetMethodInfo(Func f, T1 unused1, T2 unused2, T3 unused3, T4 unused4, T5 unused5) - //{ - // return f.Method; - //} - - //private static MethodInfo GetMethodInfo(Func f, T1 unused1, T2 unused2, T3 unused3, T4 unused4, T5 unused5, T6 unused6) - //{ - // return f.Method; - //} } } \ No newline at end of file diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/IUpdateDescriptor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/IUpdateDescriptor.cs index 1eed65ec51c02..c43f007e3341c 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/IUpdateDescriptor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/IUpdateDescriptor.cs @@ -28,14 +28,21 @@ namespace Apache.Ignite.Linq public interface IUpdateDescriptor { /// - /// + /// Allows to specifify member update with constant /// - /// - /// - /// + /// Member type + /// Member selector + /// New value /// IUpdateDescriptor Set(Func selector, TProp value); + /// + /// Allows to specifify member update with expression + /// + /// Member type + /// Member selector + /// New value expression + /// IUpdateDescriptor Set(Func selector, Func, TProp> valueBuilder); } diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs index a11c0b53272b0..ff96e0c7804f6 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs @@ -134,14 +134,14 @@ private void VisitQueryModel(QueryModel queryModel, bool includeAllFields) _aliases.Pop(); } - private void VisitUpdateAllOperator(QueryModel queryModel) + /// + /// Visits the remove operator. Returns true if it is present. + /// + private void VisitRemoveOperator(QueryModel queryModel) { var resultOps = queryModel.ResultOperators; - _builder.Append("update "); - - // FROM ... WHERE ... JOIN ... - base.VisitQueryModel(queryModel); + _builder.Append("delete "); if (resultOps.Count == 2) { @@ -149,28 +149,32 @@ private void VisitUpdateAllOperator(QueryModel queryModel) if (resOp == null) throw new NotSupportedException( - "UpdateAll can not be combined with result operators (other than Take): " + + "RemoveAll can not be combined with result operators (other than Take): " + resultOps[0].GetType().Name); - _builder.Append("limit "); + _builder.Append("top "); BuildSqlExpression(resOp.Count); + _builder.Append(" "); } else if (resultOps.Count > 2) { throw new NotSupportedException( - "UpdateAll can not be combined with result operators (other than Take): " + + "RemoveAll can not be combined with result operators (other than Take): " + string.Join(", ", resultOps.Select(x => x.GetType().Name))); } + + // FROM ... WHERE ... JOIN ... + base.VisitQueryModel(queryModel); } - /// - /// Visits the remove operator. Returns true if it is present. - /// - private void VisitRemoveOperator(QueryModel queryModel) + private void VisitUpdateAllOperator(QueryModel queryModel) { var resultOps = queryModel.ResultOperators; - _builder.Append("delete "); + _builder.Append("update "); + + // FROM ... WHERE ... JOIN ... + base.VisitQueryModel(queryModel); if (resultOps.Count == 2) { @@ -178,22 +182,18 @@ private void VisitRemoveOperator(QueryModel queryModel) if (resOp == null) throw new NotSupportedException( - "RemoveAll can not be combined with result operators (other than Take): " + + "UpdateAll can not be combined with result operators (other than Take): " + resultOps[0].GetType().Name); - _builder.Append("top "); + _builder.Append("limit "); BuildSqlExpression(resOp.Count); - _builder.Append(" "); } else if (resultOps.Count > 2) { throw new NotSupportedException( - "RemoveAll can not be combined with result operators (other than Take): " + + "UpdateAll can not be combined with result operators (other than Take): " + string.Join(", ", resultOps.Select(x => x.GetType().Name))); } - - // FROM ... WHERE ... JOIN ... - base.VisitQueryModel(queryModel); } /// @@ -579,7 +579,6 @@ public override void VisitJoinClause(JoinClause joinClause, QueryModel queryMode } } - /// /// Visists Join clause in case of join with local collection /// diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs index 1bf197207c54a..738c82ac86367 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs @@ -47,9 +47,7 @@ static UpdateAllExpressionNode() .Where(x => x.Name == "UpdateAll") .ToArray(); - UpdateAllDescriptorMethodInfo = - updateAllMethodInfos.Single(x => - x.GetParameters().Length == 2 && x.GetParameters()[1].ParameterType.IsGenericType); + UpdateAllDescriptorMethodInfo = updateAllMethodInfos.Single(); } /// From b15c10932a1ae7fbfce7835f42faab130aab0956 Mon Sep 17 00:00:00 2001 From: gurustron Date: Fri, 2 Mar 2018 02:00:54 +0300 Subject: [PATCH 12/26] some unit test --- .../Cache/Query/Linq/CacheLinqTest.Custom.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs index 82ed45ea52a1a..e1fa06703be0d 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs @@ -128,6 +128,27 @@ public void TestUpdateAll() .Set(p => p.Age, p => p.Value.Age + 1)); Assert.AreEqual(updated, 2); + + // Skip is not supported with DELETE. + var nex = Assert.Throws( + () => queryable.Skip(1).UpdateAll(d => d.Set(p => p.Age, 15))); + Assert.AreEqual( + "UpdateAll can not be combined with result operators (other than Take): SkipResultOperator", + nex.Message); + + // Multiple result operators are not supported with DELETE. + nex = Assert.Throws(() => queryable.Skip(1).Take(1).UpdateAll(d => d.Set(p => p.Age, 15))); + Assert.AreEqual( + "UpdateAll can not be combined with result operators (other than Take): SkipResultOperator, " + + "TakeResultOperator, UpdateAllResultOperator", nex.Message); + + // Joins are not supported in H2. + var qry = queryable + .Where(x => x.Key == 7) + .Join(GetPersonCache().AsCacheQueryable(), p => p.Key, p => p.Key, (p1, p2) => p1); + + var ex = Assert.Throws(() => qry.UpdateAll(d => d.Set(p => p.Age, 15))); + Assert.AreEqual("Failed to parse query", ex.Message.Substring(0, 21)); } } } \ No newline at end of file From bee592d87c82e938f9693cd2d121850099375a44 Mon Sep 17 00:00:00 2001 From: gurustron Date: Fri, 2 Mar 2018 03:53:51 +0300 Subject: [PATCH 13/26] Some more unit tests --- .../Cache/Query/Linq/CacheLinqTest.Custom.cs | 74 ++++++++++++++++--- .../Impl/AliasDictionary.cs | 9 ++- .../Impl/CacheQueryExpressionVisitor.cs | 17 ++++- .../Impl/CacheQueryModelVisitor.cs | 10 +-- 4 files changed, 91 insertions(+), 19 deletions(-) diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs index e1fa06703be0d..d076b2967ce36 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs @@ -28,6 +28,7 @@ namespace Apache.Ignite.Core.Tests.Cache.Query.Linq { using System; using System.Linq; + using Apache.Ignite.Core.Cache; using Apache.Ignite.Core.Cache.Configuration; using Apache.Ignite.Core.Common; using Apache.Ignite.Linq; @@ -110,18 +111,73 @@ public void TestRemoveAll() [Test] public void TestUpdateAll() { + var orgQueryable = GetOrgCache().AsCacheQueryable(); + var allOrgs = orgQueryable.ToArray(); + // Use new cache to avoid touching static data. - var cache = Ignition.GetIgnite().CreateCache(new CacheConfiguration("updateAllTest", - new QueryEntity(typeof(int), typeof(Person))) + var personCount = 10; + + var cache = Ignition.GetIgnite().GetOrCreateCache(new CacheConfiguration("updateAllTest", + new QueryEntity(typeof(int), typeof(Person)), new QueryEntity(typeof(int), typeof(Organization))) { - SqlEscapeAll = GetSqlEscapeAll() + SqlEscapeAll = GetSqlEscapeAll(), + CacheMode = CacheMode.Replicated }); - Enumerable.Range(1, 10).ToList().ForEach(x => cache.Put(x, new Person(x, x.ToString()))); + Enumerable.Range(1, personCount).ToList().ForEach(x => + cache.Put(x, new Person(x, x.ToString()) {OrganizationId = 1000 + x % allOrgs.Length})); - var queryable = cache.AsCacheQueryable(); + var personQueryable = cache.AsCacheQueryable(); - var updated = queryable + // *** + // Unconditional + // *** + + Action, bool>> assertAll = (i, func) => + { + Assert.AreEqual(personCount, i); + Assert.IsTrue(personQueryable.ToArray().All(func)); + }; + + // Constant value + var updated = personQueryable + .UpdateAll(d => d.Set(p => p.AliasTest, 7)); + assertAll(updated, e => e.Value.AliasTest == 7); + + // Expression value - from self + updated = personQueryable + .UpdateAll(d => d.Set(p => p.AliasTest, e => e.Key)); + assertAll(updated, e => e.Value.AliasTest == e.Key); + + // Expression value - subquery with same cache + updated = personQueryable + .UpdateAll(d => d.Set(p => p.AliasTest, e => personQueryable.Where(ie => ie.Key == e.Key).Select(ie => ie.Key).First())); + assertAll(updated, e => e.Value.AliasTest == e.Key); + + // Expression value - subquery with other cache + updated = personQueryable + .UpdateAll(d => d.Set(p => p.AliasTest, e => orgQueryable.Count(o => o.Key > e.Key))); + + assertAll(updated, e => e.Value.AliasTest == allOrgs.Count(o => o.Key > e.Key)); + + updated = personQueryable + .UpdateAll(d => d.Set(p => p.Name, e => orgQueryable.Where(o => o.Key == e.Value.OrganizationId).Select(o => o.Value.Name).First())); + assertAll(updated, e => e.Value.Name == allOrgs.Where(o => o.Key == e.Value.OrganizationId).Select(o => o.Value.Name).First()); + + // Row number limit. + var count = 2; + var name = "rowLimit" + count; + updated = personQueryable + .Take(count) + .UpdateAll(d => d.Set(p => p.Name, name)); + Assert.AreEqual(count, updated); + Assert.AreEqual(count, personQueryable.Count(e => e.Value.Name == name)); + + // *** + // Conditional + // *** + + updated = personQueryable .Where(p => p.Value.Age > 0) .Take(2) .UpdateAll(d => d.Set(p => p.AliasTest, 1) @@ -131,19 +187,19 @@ public void TestUpdateAll() // Skip is not supported with DELETE. var nex = Assert.Throws( - () => queryable.Skip(1).UpdateAll(d => d.Set(p => p.Age, 15))); + () => personQueryable.Skip(1).UpdateAll(d => d.Set(p => p.Age, 15))); Assert.AreEqual( "UpdateAll can not be combined with result operators (other than Take): SkipResultOperator", nex.Message); // Multiple result operators are not supported with DELETE. - nex = Assert.Throws(() => queryable.Skip(1).Take(1).UpdateAll(d => d.Set(p => p.Age, 15))); + nex = Assert.Throws(() => personQueryable.Skip(1).Take(1).UpdateAll(d => d.Set(p => p.Age, 15))); Assert.AreEqual( "UpdateAll can not be combined with result operators (other than Take): SkipResultOperator, " + "TakeResultOperator, UpdateAllResultOperator", nex.Message); // Joins are not supported in H2. - var qry = queryable + var qry = personQueryable .Where(x => x.Key == 7) .Join(GetPersonCache().AsCacheQueryable(), p => p.Key, p => p.Key, (p1, p2) => p1); diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/AliasDictionary.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/AliasDictionary.cs index 8902e7c51263f..15f47a55dc251 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/AliasDictionary.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/AliasDictionary.cs @@ -20,6 +20,7 @@ namespace Apache.Ignite.Linq.Impl using System; using System.Collections.Generic; using System.Diagnostics; + using System.Linq; using System.Linq.Expressions; using System.Text; using Remotion.Linq.Clauses; @@ -49,11 +50,15 @@ internal class AliasDictionary /// /// Pushes current aliases to stack. /// - public void Push() + public void Push(bool copyAliases) { + var aliasesTemp = copyAliases + ? _tableAliases.ToDictionary(p => p.Key, p => p.Value) + : new Dictionary(); + _stack.Push(_tableAliases); - _tableAliases = new Dictionary(); + _tableAliases = aliasesTemp; } /// diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs index 4caefe1b11c49..634bbeae9541a 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs @@ -54,6 +54,9 @@ internal class CacheQueryExpressionVisitor : ThrowingExpressionVisitor /** */ private readonly bool _includeAllFields; + /** */ + private readonly bool _visitSubqueryModel; + /// /// Initializes a new instance of the class. /// @@ -62,13 +65,15 @@ internal class CacheQueryExpressionVisitor : ThrowingExpressionVisitor /// for the whole-table select instead of _key, _val. /// Flag indicating that star '*' qualifier should be used /// for the whole-table select as well as _key, _val. - public CacheQueryExpressionVisitor(CacheQueryModelVisitor modelVisitor, bool useStar, bool includeAllFields) + /// Flag, indicating that subquery should be visited as full query + public CacheQueryExpressionVisitor(CacheQueryModelVisitor modelVisitor, bool useStar, bool includeAllFields, bool visitSubqueryModel) { Debug.Assert(modelVisitor != null); _modelVisitor = modelVisitor; _useStar = useStar; _includeAllFields = includeAllFields; + _visitSubqueryModel = visitSubqueryModel; } /// @@ -548,9 +553,15 @@ protected override Expression VisitSubQuery(SubQueryExpression expression) var subQueryModel = expression.QueryModel; var contains = subQueryModel.ResultOperators.FirstOrDefault() as ContainsResultOperator; - + + if (_visitSubqueryModel) + { + ResultBuilder.Append("("); + _modelVisitor.VisitQueryModel(subQueryModel, false, true); + ResultBuilder.Append(")"); + } // Check if IEnumerable.Contains is used. - if (subQueryModel.ResultOperators.Count == 1 && contains != null) + else if (subQueryModel.ResultOperators.Count == 1 && contains != null) { VisitContains(subQueryModel, contains); } diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs index ff96e0c7804f6..2f2ddbe1728ff 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs @@ -103,9 +103,9 @@ public override void VisitQueryModel(QueryModel queryModel) /// /// Visits the query model. /// - private void VisitQueryModel(QueryModel queryModel, bool includeAllFields) + internal void VisitQueryModel(QueryModel queryModel, bool includeAllFields, bool copyAliases = false) { - _aliases.Push(); + _aliases.Push(copyAliases); var lastResultOp = queryModel.ResultOperators.LastOrDefault(); if (lastResultOp is RemoveAllResultOperator) @@ -377,7 +377,7 @@ protected override void VisitBodyClauses(ObservableCollection bodyC first = false; BuildSqlExpression(update.Selector); _builder.Append(" = "); - BuildSqlExpression(update.Value); + BuildSqlExpression(update.Value, visitSubqueryModel: true); } _builder.Append(" "); @@ -694,9 +694,9 @@ private void BuildJoinSubCondition(Expression innerKey, Expression outerKey) /// /// Builds the SQL expression. /// - private void BuildSqlExpression(Expression expression, bool useStar = false, bool includeAllFields = false) + private void BuildSqlExpression(Expression expression, bool useStar = false, bool includeAllFields = false, bool visitSubqueryModel = false) { - new CacheQueryExpressionVisitor(this, useStar, includeAllFields).Visit(expression); + new CacheQueryExpressionVisitor(this, useStar, includeAllFields, visitSubqueryModel).Visit(expression); } } } From 737b56ddb648a5fd0aab5f8cab685a6db6cf3604 Mon Sep 17 00:00:00 2001 From: gurustron Date: Fri, 2 Mar 2018 04:15:27 +0300 Subject: [PATCH 14/26] simple condition --- .../Cache/Query/Linq/CacheLinqTest.Custom.cs | 42 +++++++++++-------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs index d076b2967ce36..350a95bf837d7 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs @@ -129,40 +129,48 @@ public void TestUpdateAll() var personQueryable = cache.AsCacheQueryable(); - // *** - // Unconditional - // *** + Action, bool>> assertAll = func => + Assert.IsTrue(personQueryable.ToArray().All(func)); - Action, bool>> assertAll = (i, func) => + Action, bool>> assertAllCount = (i, func) => { Assert.AreEqual(personCount, i); - Assert.IsTrue(personQueryable.ToArray().All(func)); + assertAll(func); }; + // *** + // Unconditional + // *** + // Constant value var updated = personQueryable .UpdateAll(d => d.Set(p => p.AliasTest, 7)); - assertAll(updated, e => e.Value.AliasTest == 7); + assertAllCount(updated, e => e.Value.AliasTest == 7); // Expression value - from self updated = personQueryable .UpdateAll(d => d.Set(p => p.AliasTest, e => e.Key)); - assertAll(updated, e => e.Value.AliasTest == e.Key); + assertAllCount(updated, e => e.Value.AliasTest == e.Key); // Expression value - subquery with same cache updated = personQueryable .UpdateAll(d => d.Set(p => p.AliasTest, e => personQueryable.Where(ie => ie.Key == e.Key).Select(ie => ie.Key).First())); - assertAll(updated, e => e.Value.AliasTest == e.Key); + assertAllCount(updated, e => e.Value.AliasTest == e.Key); + + // Multiple sets + var aliasValue = 3; + updated = personQueryable + .UpdateAll(d => d.Set(p => p.AliasTest, aliasValue).Set(p => p.Name, aliasValue.ToString())); + assertAllCount(updated, e => e.Value.AliasTest == aliasValue && e.Value.Name == aliasValue.ToString()); // Expression value - subquery with other cache updated = personQueryable .UpdateAll(d => d.Set(p => p.AliasTest, e => orgQueryable.Count(o => o.Key > e.Key))); - - assertAll(updated, e => e.Value.AliasTest == allOrgs.Count(o => o.Key > e.Key)); + assertAllCount(updated, e => e.Value.AliasTest == allOrgs.Count(o => o.Key > e.Key)); updated = personQueryable .UpdateAll(d => d.Set(p => p.Name, e => orgQueryable.Where(o => o.Key == e.Value.OrganizationId).Select(o => o.Value.Name).First())); - assertAll(updated, e => e.Value.Name == allOrgs.Where(o => o.Key == e.Value.OrganizationId).Select(o => o.Value.Name).First()); + assertAllCount(updated, e => e.Value.Name == allOrgs.Where(o => o.Key == e.Value.OrganizationId).Select(o => o.Value.Name).First()); // Row number limit. var count = 2; @@ -177,13 +185,13 @@ public void TestUpdateAll() // Conditional // *** + // Simple conditiona + aliasValue = 777; updated = personQueryable - .Where(p => p.Value.Age > 0) - .Take(2) - .UpdateAll(d => d.Set(p => p.AliasTest, 1) - .Set(p => p.Age, p => p.Value.Age + 1)); - - Assert.AreEqual(updated, 2); + .Where(p => p.Key > 8) + .UpdateAll(d => d.Set(p => p.AliasTest, aliasValue)); + Assert.AreEqual(2, updated); + assertAll(e => (e.Key <=8 && e.Value.AliasTest != aliasValue) || e.Value.AliasTest == aliasValue); // Skip is not supported with DELETE. var nex = Assert.Throws( From a1ae8d71639527aa4c81f6c924f0bde6a0ee8781 Mon Sep 17 00:00:00 2001 From: gurustron Date: Fri, 2 Mar 2018 04:20:48 +0300 Subject: [PATCH 15/26] Test: conditional with limit --- .../Cache/Query/Linq/CacheLinqTest.Custom.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs index 350a95bf837d7..4bffee574613c 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs @@ -185,7 +185,7 @@ public void TestUpdateAll() // Conditional // *** - // Simple conditiona + // Simple conditional aliasValue = 777; updated = personQueryable .Where(p => p.Key > 8) @@ -193,6 +193,16 @@ public void TestUpdateAll() Assert.AreEqual(2, updated); assertAll(e => (e.Key <=8 && e.Value.AliasTest != aliasValue) || e.Value.AliasTest == aliasValue); + // Conditional with limit + aliasValue = 8888; + updated = personQueryable + .Where(p => p.Key > 1) + .Take(1) + .UpdateAll(d => d.Set(p => p.AliasTest, aliasValue)); + Assert.AreEqual(1, updated); + assertAll(e => (e.Key != 2 && e.Value.AliasTest != aliasValue) || e.Value.AliasTest == aliasValue); + + // Skip is not supported with DELETE. var nex = Assert.Throws( () => personQueryable.Skip(1).UpdateAll(d => d.Set(p => p.Age, 15))); From 5ffed8dc5b1543fda535ffb4d2c72328dc2d8197 Mon Sep 17 00:00:00 2001 From: gurustron Date: Fri, 2 Mar 2018 04:21:41 +0300 Subject: [PATCH 16/26] Comment --- .../Cache/Query/Linq/CacheLinqTest.Custom.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs index 4bffee574613c..213528874ba1e 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs @@ -202,6 +202,9 @@ public void TestUpdateAll() Assert.AreEqual(1, updated); assertAll(e => (e.Key != 2 && e.Value.AliasTest != aliasValue) || e.Value.AliasTest == aliasValue); + // *** + // Not supported + // *** // Skip is not supported with DELETE. var nex = Assert.Throws( From 617f5f24fe1802fd45976c8c0d541bc3386689fe Mon Sep 17 00:00:00 2001 From: gurustron Date: Fri, 2 Mar 2018 05:10:42 +0300 Subject: [PATCH 17/26] small cleanup --- .../Cache/Query/Linq/CacheLinqTest.Custom.cs | 31 ++++++++++--------- .../Apache.Ignite.Linq/CacheLinqExtensions.cs | 2 +- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs index 213528874ba1e..8db5edb7d5f82 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs @@ -152,25 +152,29 @@ public void TestUpdateAll() .UpdateAll(d => d.Set(p => p.AliasTest, e => e.Key)); assertAllCount(updated, e => e.Value.AliasTest == e.Key); - // Expression value - subquery with same cache - updated = personQueryable - .UpdateAll(d => d.Set(p => p.AliasTest, e => personQueryable.Where(ie => ie.Key == e.Key).Select(ie => ie.Key).First())); - assertAllCount(updated, e => e.Value.AliasTest == e.Key); - // Multiple sets var aliasValue = 3; updated = personQueryable .UpdateAll(d => d.Set(p => p.AliasTest, aliasValue).Set(p => p.Name, aliasValue.ToString())); assertAllCount(updated, e => e.Value.AliasTest == aliasValue && e.Value.Name == aliasValue.ToString()); + // Expression value - subquery with same cache + updated = personQueryable + .UpdateAll(d => d.Set(p => p.AliasTest, + e => personQueryable.Where(ie => ie.Key == e.Key).Select(ie => ie.Key).First())); + assertAllCount(updated, e => e.Value.AliasTest == e.Key); + // Expression value - subquery with other cache updated = personQueryable .UpdateAll(d => d.Set(p => p.AliasTest, e => orgQueryable.Count(o => o.Key > e.Key))); assertAllCount(updated, e => e.Value.AliasTest == allOrgs.Count(o => o.Key > e.Key)); updated = personQueryable - .UpdateAll(d => d.Set(p => p.Name, e => orgQueryable.Where(o => o.Key == e.Value.OrganizationId).Select(o => o.Value.Name).First())); - assertAllCount(updated, e => e.Value.Name == allOrgs.Where(o => o.Key == e.Value.OrganizationId).Select(o => o.Value.Name).First()); + .UpdateAll(d => d.Set(p => p.Name, + e => orgQueryable.Where(o => o.Key == e.Value.OrganizationId).Select(o => o.Value.Name).First())); + assertAllCount(updated, + e => e.Value.Name == allOrgs.Where(o => o.Key == e.Value.OrganizationId).Select(o => o.Value.Name) + .First()); // Row number limit. var count = 2; @@ -191,7 +195,7 @@ public void TestUpdateAll() .Where(p => p.Key > 8) .UpdateAll(d => d.Set(p => p.AliasTest, aliasValue)); Assert.AreEqual(2, updated); - assertAll(e => (e.Key <=8 && e.Value.AliasTest != aliasValue) || e.Value.AliasTest == aliasValue); + assertAll(e => e.Key <= 8 && e.Value.AliasTest != aliasValue || e.Value.AliasTest == aliasValue); // Conditional with limit aliasValue = 8888; @@ -200,7 +204,7 @@ public void TestUpdateAll() .Take(1) .UpdateAll(d => d.Set(p => p.AliasTest, aliasValue)); Assert.AreEqual(1, updated); - assertAll(e => (e.Key != 2 && e.Value.AliasTest != aliasValue) || e.Value.AliasTest == aliasValue); + assertAll(e => e.Key != 2 && e.Value.AliasTest != aliasValue || e.Value.AliasTest == aliasValue); // *** // Not supported @@ -209,14 +213,13 @@ public void TestUpdateAll() // Skip is not supported with DELETE. var nex = Assert.Throws( () => personQueryable.Skip(1).UpdateAll(d => d.Set(p => p.Age, 15))); - Assert.AreEqual( - "UpdateAll can not be combined with result operators (other than Take): SkipResultOperator", + Assert.AreEqual("UpdateAll can not be combined with result operators (other than Take): SkipResultOperator", nex.Message); // Multiple result operators are not supported with DELETE. - nex = Assert.Throws(() => personQueryable.Skip(1).Take(1).UpdateAll(d => d.Set(p => p.Age, 15))); - Assert.AreEqual( - "UpdateAll can not be combined with result operators (other than Take): SkipResultOperator, " + + nex = Assert.Throws(() => + personQueryable.Skip(1).Take(1).UpdateAll(d => d.Set(p => p.Age, 15))); + Assert.AreEqual("UpdateAll can not be combined with result operators (other than Take): SkipResultOperator, " + "TakeResultOperator, UpdateAllResultOperator", nex.Message); // Joins are not supported in H2. diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs index 5d5cea6523913..f14b77d52a01f 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs @@ -31,7 +31,7 @@ namespace Apache.Ignite.Linq /// /// Extensions methods for . /// - public static partial class CacheLinqExtensions + public static class CacheLinqExtensions { /// /// Gets an instance over this cache. From 7172ba8c2ce5e3791520a4ae159b617530f87859 Mon Sep 17 00:00:00 2001 From: gurustron Date: Sun, 4 Mar 2018 21:53:03 +0300 Subject: [PATCH 18/26] implement "Contains" in value --- .../Cache/Query/Linq/CacheLinqTest.Base.cs | 9 +++ .../Cache/Query/Linq/CacheLinqTest.Custom.cs | 77 +++++++++++++------ .../Impl/CacheQueryExpressionVisitor.cs | 22 ++++-- 3 files changed, 78 insertions(+), 30 deletions(-) diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Base.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Base.cs index d70a7a42885f7..ee95ea11892b2 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Base.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Base.cs @@ -346,6 +346,8 @@ public Person(int age, string name) [QuerySqlField] public int AliasTest { get; set; } + [QuerySqlField] public bool Bool { get; set; } + public void WriteBinary(IBinaryWriter writer) { writer.WriteInt("age1", Age); @@ -354,6 +356,7 @@ public void WriteBinary(IBinaryWriter writer) writer.WriteObject("Address", Address); writer.WriteTimestamp("Birthday", Birthday); writer.WriteInt("AliasTest", AliasTest); + writer.WriteBoolean("Bool", Bool); } public void ReadBinary(IBinaryReader reader) @@ -364,6 +367,12 @@ public void ReadBinary(IBinaryReader reader) Address = reader.ReadObject
("Address"); Birthday = reader.ReadTimestamp("Birthday"); AliasTest = reader.ReadInt("AliasTest"); + Bool = reader.ReadBoolean("Bool"); + } + + public override string ToString() + { + return String.Format("Age:{0} Name:{1} OrgId:{2} Alias:{3}", Age, Name, OrganizationId, AliasTest); } } diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs index 8db5edb7d5f82..8bc4f7083c459 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs @@ -30,6 +30,7 @@ namespace Apache.Ignite.Core.Tests.Cache.Query.Linq using System.Linq; using Apache.Ignite.Core.Cache; using Apache.Ignite.Core.Cache.Configuration; + using Apache.Ignite.Core.Cache.Query; using Apache.Ignite.Core.Common; using Apache.Ignite.Linq; using NUnit.Framework; @@ -111,24 +112,47 @@ public void TestRemoveAll() [Test] public void TestUpdateAll() { - var orgQueryable = GetOrgCache().AsCacheQueryable(); - var allOrgs = orgQueryable.ToArray(); // Use new cache to avoid touching static data. var personCount = 10; + var orgsCount = 3; var cache = Ignition.GetIgnite().GetOrCreateCache(new CacheConfiguration("updateAllTest", - new QueryEntity(typeof(int), typeof(Person)), new QueryEntity(typeof(int), typeof(Organization))) + new QueryEntity(typeof(int), typeof(Person))) { SqlEscapeAll = GetSqlEscapeAll(), CacheMode = CacheMode.Replicated }); - Enumerable.Range(1, personCount).ToList().ForEach(x => - cache.Put(x, new Person(x, x.ToString()) {OrganizationId = 1000 + x % allOrgs.Length})); + var orgsCache = Ignition.GetIgnite().GetOrCreateCache(new CacheConfiguration("updateAllTestOrgs", + new QueryEntity(typeof(int), typeof(Organization))) + { + SqlEscapeAll = GetSqlEscapeAll(), + CacheMode = CacheMode.Replicated + }); + + Enumerable.Range(1, personCount) + .ToList() + .ForEach(x => cache.Put(x, new Person(x, x.ToString()) + { + Birthday = DateTime.UtcNow.AddDays(personCount - x), + OrganizationId = x % orgsCount + })); + + var allOrgs = Enumerable.Range(1, orgsCount) + .Select(x => new Organization + { + Id = x, + Name = x.ToString() + }) + .ToList(); + + allOrgs.ForEach(x => orgsCache.Put(x.Id, x)); var personQueryable = cache.AsCacheQueryable(); + var orgsQueryable = orgsCache.AsCacheQueryable(); + Action, bool>> assertAll = func => Assert.IsTrue(personQueryable.ToArray().All(func)); @@ -145,45 +169,52 @@ public void TestUpdateAll() // Constant value var updated = personQueryable .UpdateAll(d => d.Set(p => p.AliasTest, 7)); - assertAllCount(updated, e => e.Value.AliasTest == 7); + assertAllCount(updated, p => p.Value.AliasTest == 7); // Expression value - from self updated = personQueryable .UpdateAll(d => d.Set(p => p.AliasTest, e => e.Key)); - assertAllCount(updated, e => e.Value.AliasTest == e.Key); + assertAllCount(updated, p => p.Value.AliasTest == p.Key); // Multiple sets var aliasValue = 3; updated = personQueryable .UpdateAll(d => d.Set(p => p.AliasTest, aliasValue).Set(p => p.Name, aliasValue.ToString())); - assertAllCount(updated, e => e.Value.AliasTest == aliasValue && e.Value.Name == aliasValue.ToString()); + assertAllCount(updated, p => p.Value.AliasTest == aliasValue && p.Value.Name == aliasValue.ToString()); // Expression value - subquery with same cache updated = personQueryable .UpdateAll(d => d.Set(p => p.AliasTest, e => personQueryable.Where(ie => ie.Key == e.Key).Select(ie => ie.Key).First())); - assertAllCount(updated, e => e.Value.AliasTest == e.Key); + assertAllCount(updated, p => p.Value.AliasTest == p.Key); // Expression value - subquery with other cache updated = personQueryable - .UpdateAll(d => d.Set(p => p.AliasTest, e => orgQueryable.Count(o => o.Key > e.Key))); - assertAllCount(updated, e => e.Value.AliasTest == allOrgs.Count(o => o.Key > e.Key)); + .UpdateAll(d => d.Set(p => p.AliasTest, p => orgsQueryable.Count(o => o.Value.Id > p.Key))); + + assertAllCount(updated, p => p.Value.AliasTest == allOrgs.Count(o => o.Id > p.Key)); updated = personQueryable .UpdateAll(d => d.Set(p => p.Name, - e => orgQueryable.Where(o => o.Key == e.Value.OrganizationId).Select(o => o.Value.Name).First())); + e => orgsQueryable.Where(o => o.Key == e.Value.OrganizationId).Select(o => o.Value.Name).First())); assertAllCount(updated, - e => e.Value.Name == allOrgs.Where(o => o.Key == e.Value.OrganizationId).Select(o => o.Value.Name) - .First()); + p => p.Value.Name == allOrgs.Where(o => o.Id == p.Value.OrganizationId).Select(o => o.Name) + .FirstOrDefault()); + + // Expression value - Contains subquery with other cache + updated = personQueryable + .UpdateAll(d => d.Set(p => p.Bool, + p => orgsQueryable.Select(o => o.Key).Contains(p.Value.OrganizationId))); + assertAllCount(updated, + p => p.Value.Bool == allOrgs.Select(o => o.Id).Contains(p.Value.OrganizationId)); // Row number limit. - var count = 2; - var name = "rowLimit" + count; + var name = "rowLimit" + 2; updated = personQueryable - .Take(count) + .Take(2) .UpdateAll(d => d.Set(p => p.Name, name)); - Assert.AreEqual(count, updated); - Assert.AreEqual(count, personQueryable.Count(e => e.Value.Name == name)); + Assert.AreEqual(2, updated); + Assert.AreEqual(2, personQueryable.Count(p => p.Value.Name == name)); // *** // Conditional @@ -195,16 +226,16 @@ public void TestUpdateAll() .Where(p => p.Key > 8) .UpdateAll(d => d.Set(p => p.AliasTest, aliasValue)); Assert.AreEqual(2, updated); - assertAll(e => e.Key <= 8 && e.Value.AliasTest != aliasValue || e.Value.AliasTest == aliasValue); + assertAll(p => p.Key <= 8 && p.Value.AliasTest != aliasValue || p.Value.AliasTest == aliasValue); // Conditional with limit aliasValue = 8888; updated = personQueryable .Where(p => p.Key > 1) - .Take(1) + .Take(3) .UpdateAll(d => d.Set(p => p.AliasTest, aliasValue)); - Assert.AreEqual(1, updated); - assertAll(e => e.Key != 2 && e.Value.AliasTest != aliasValue || e.Value.AliasTest == aliasValue); + Assert.AreEqual(3, updated); + Assert.AreEqual(3, personQueryable.ToArray().Count(p => p.Value.AliasTest == aliasValue)); // *** // Not supported diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs index 634bbeae9541a..075fbabeecd56 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs @@ -554,17 +554,17 @@ protected override Expression VisitSubQuery(SubQueryExpression expression) var contains = subQueryModel.ResultOperators.FirstOrDefault() as ContainsResultOperator; - if (_visitSubqueryModel) + // Check if IEnumerable.Contains is used. + if (subQueryModel.ResultOperators.Count == 1 && contains != null) + { + VisitContains(subQueryModel, contains); + } + else if (_visitSubqueryModel) { ResultBuilder.Append("("); _modelVisitor.VisitQueryModel(subQueryModel, false, true); ResultBuilder.Append(")"); } - // Check if IEnumerable.Contains is used. - else if (subQueryModel.ResultOperators.Count == 1 && contains != null) - { - VisitContains(subQueryModel, contains); - } else { // This happens when New expression uses a subquery, in a GroupBy. @@ -590,7 +590,15 @@ private void VisitContains(QueryModel subQueryModel, ContainsResultOperator cont Visit(contains.Item); ResultBuilder.Append(" IN ("); - _modelVisitor.VisitQueryModel(subQueryModel); + if (_visitSubqueryModel) + { + _modelVisitor.VisitQueryModel(subQueryModel, false, true); + } + else + { + _modelVisitor.VisitQueryModel(subQueryModel); + } + ResultBuilder.Append(")"); } else From ea9ee417daa6697a83c1b7fbaacca7b3ccec9681 Mon Sep 17 00:00:00 2001 From: gurustron Date: Sun, 4 Mar 2018 23:10:28 +0300 Subject: [PATCH 19/26] small clean up --- .../Cache/Query/Linq/CacheLinqTest.Custom.cs | 1 - .../Apache.Ignite.Linq/CacheLinqExtensions.cs | 2 +- .../Impl/AliasDictionary.cs | 8 ++--- .../Impl/CacheQueryExpressionVisitor.cs | 12 ++++---- .../Impl/Dml/UpdateAllExpressionNode.cs | 30 +++++++++---------- 5 files changed, 25 insertions(+), 28 deletions(-) diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs index 8bc4f7083c459..86ee78a4468ac 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs @@ -30,7 +30,6 @@ namespace Apache.Ignite.Core.Tests.Cache.Query.Linq using System.Linq; using Apache.Ignite.Core.Cache; using Apache.Ignite.Core.Cache.Configuration; - using Apache.Ignite.Core.Cache.Query; using Apache.Ignite.Core.Common; using Apache.Ignite.Linq; using NUnit.Framework; diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs index f14b77d52a01f..122d5d3bff257 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs @@ -210,7 +210,7 @@ public static ICacheQueryable ToCacheQueryable(this IQueryable query) IgniteArgumentCheck.NotNull(query, "query"); IgniteArgumentCheck.NotNull(updateDescription, "updateDescription"); - var method = UpdateAllExpressionNode.UpdateAllDescriptorMethodInfo + var method = UpdateAllExpressionNode.UpdateAllMethodInfo .MakeGenericMethod(typeof(TKey), typeof(TValue)); // TODO: cache? return query.Provider.Execute(Expression.Call(null, method, new[] diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/AliasDictionary.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/AliasDictionary.cs index 15f47a55dc251..85584918c07d8 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/AliasDictionary.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/AliasDictionary.cs @@ -52,13 +52,11 @@ internal class AliasDictionary ///
public void Push(bool copyAliases) { - var aliasesTemp = copyAliases - ? _tableAliases.ToDictionary(p => p.Key, p => p.Value) - : new Dictionary(); - _stack.Push(_tableAliases); - _tableAliases = aliasesTemp; + _tableAliases = copyAliases + ? _tableAliases.ToDictionary(p => p.Key, p => p.Value) + : new Dictionary(); } /// diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs index 075fbabeecd56..7e0efe29b0ad7 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs @@ -55,7 +55,7 @@ internal class CacheQueryExpressionVisitor : ThrowingExpressionVisitor private readonly bool _includeAllFields; /** */ - private readonly bool _visitSubqueryModel; + private readonly bool _visitEntireSubQueryModel; /// /// Initializes a new instance of the class. @@ -65,15 +65,15 @@ internal class CacheQueryExpressionVisitor : ThrowingExpressionVisitor /// for the whole-table select instead of _key, _val. /// Flag indicating that star '*' qualifier should be used /// for the whole-table select as well as _key, _val. - /// Flag, indicating that subquery should be visited as full query - public CacheQueryExpressionVisitor(CacheQueryModelVisitor modelVisitor, bool useStar, bool includeAllFields, bool visitSubqueryModel) + /// Flag, indicating that subquery should be visited as full query + public CacheQueryExpressionVisitor(CacheQueryModelVisitor modelVisitor, bool useStar, bool includeAllFields, bool visitEntireSubQueryModel) { Debug.Assert(modelVisitor != null); _modelVisitor = modelVisitor; _useStar = useStar; _includeAllFields = includeAllFields; - _visitSubqueryModel = visitSubqueryModel; + _visitEntireSubQueryModel = visitEntireSubQueryModel; } /// @@ -559,7 +559,7 @@ protected override Expression VisitSubQuery(SubQueryExpression expression) { VisitContains(subQueryModel, contains); } - else if (_visitSubqueryModel) + else if (_visitEntireSubQueryModel) { ResultBuilder.Append("("); _modelVisitor.VisitQueryModel(subQueryModel, false, true); @@ -590,7 +590,7 @@ private void VisitContains(QueryModel subQueryModel, ContainsResultOperator cont Visit(contains.Item); ResultBuilder.Append(" IN ("); - if (_visitSubqueryModel) + if (_visitEntireSubQueryModel) { _modelVisitor.VisitQueryModel(subQueryModel, false, true); } diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs index 738c82ac86367..63f1ff871a85a 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs @@ -38,22 +38,18 @@ namespace Apache.Ignite.Linq.Impl.Dml /// internal sealed class UpdateAllExpressionNode : ResultOperatorExpressionNodeBase { - private readonly LambdaExpression _updateDescription; - - static UpdateAllExpressionNode() - { - var updateAllMethodInfos = typeof(CacheLinqExtensions) - .GetMethods() - .Where(x => x.Name == "UpdateAll") - .ToArray(); - - UpdateAllDescriptorMethodInfo = updateAllMethodInfos.Single(); - } - /// - /// The UpdateAll(pred) method. + /// The UpdateAll method. /// - public static readonly MethodInfo UpdateAllDescriptorMethodInfo; + public static readonly MethodInfo UpdateAllMethodInfo = typeof(CacheLinqExtensions) + .GetMethods() + .Single(x => x.Name == "UpdateAll"); + + //** */ + private static readonly MethodInfo[] SupportedMethods = {UpdateAllMethodInfo}; + + //** */ + private readonly LambdaExpression _updateDescription; /// /// Initializes a new instance of the class. @@ -78,6 +74,10 @@ static UpdateAllExpressionNode() /** */ protected override ResultOperatorBase CreateResultOperator(ClauseGenerationContext clauseGenerationContext) { + if (_updateDescription.Parameters.Count != 1) + throw new NotSupportedException("Expression is not supported for UpdateAll: " + + _updateDescription); + var querySourceRefExpression = (QuerySourceReferenceExpression) Source.Resolve( _updateDescription.Parameters[0], _updateDescription.Parameters[0], @@ -134,7 +134,7 @@ protected override ResultOperatorBase CreateResultOperator(ClauseGenerationConte /// public static IEnumerable GetSupportedMethods() { - yield return UpdateAllDescriptorMethodInfo; + return SupportedMethods; } } } \ No newline at end of file From eaffab9b7237187cd22924fda59f9fdae2f4cac7 Mon Sep 17 00:00:00 2001 From: gurustron Date: Sun, 4 Mar 2018 23:30:17 +0300 Subject: [PATCH 20/26] small refactoring --- .../Impl/CacheQueryModelVisitor.cs | 50 ++++++++++++------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs index 2f2ddbe1728ff..e7933c345edf8 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs @@ -173,7 +173,7 @@ private void VisitUpdateAllOperator(QueryModel queryModel) _builder.Append("update "); - // FROM ... WHERE ... JOIN ... + // FROM ... WHERE ... base.VisitQueryModel(queryModel); if (resultOps.Count == 2) @@ -366,23 +366,6 @@ private void ProcessSkipTake(QueryModel queryModel) /** */ protected override void VisitBodyClauses(ObservableCollection bodyClauses, QueryModel queryModel) { - var updateAllResultOperator = queryModel.ResultOperators.LastOrDefault() as UpdateAllResultOperator; - if (updateAllResultOperator != null) - { - _builder.Append("set "); - var first = true; - foreach (var update in updateAllResultOperator.Updates) - { - if (!first) _builder.Append(", "); - first = false; - BuildSqlExpression(update.Selector); - _builder.Append(" = "); - BuildSqlExpression(update.Value, visitSubqueryModel: true); - } - - _builder.Append(" "); - } - var i = 0; foreach (var join in bodyClauses.OfType()) VisitJoinClause(join, queryModel, i++); @@ -434,7 +417,8 @@ public override void VisitMainFromClause(MainFromClause fromClause, QueryModel q { base.VisitMainFromClause(fromClause, queryModel); - if (!(queryModel.ResultOperators.LastOrDefault() is UpdateAllResultOperator)) + var isUpdateQuery = queryModel.ResultOperators.LastOrDefault() is UpdateAllResultOperator; + if (!isUpdateQuery) { _builder.Append("from "); } @@ -450,6 +434,11 @@ public override void VisitMainFromClause(MainFromClause fromClause, QueryModel q VisitAdditionalFromClause(additionalFrom, queryModel, i++); } + + if (isUpdateQuery) + { + BuildSetClauseForUpdateAll(queryModel); + } } /// @@ -698,5 +687,28 @@ private void BuildSqlExpression(Expression expression, bool useStar = false, boo { new CacheQueryExpressionVisitor(this, useStar, includeAllFields, visitSubqueryModel).Visit(expression); } + + /// + /// Builds SET clause of UPDATE statement + /// + private void BuildSetClauseForUpdateAll(QueryModel queryModel) + { + var updateAllResultOperator = queryModel.ResultOperators.LastOrDefault() as UpdateAllResultOperator; + if (updateAllResultOperator != null) + { + _builder.Append("set "); + var first = true; + foreach (var update in updateAllResultOperator.Updates) + { + if (!first) _builder.Append(", "); + first = false; + BuildSqlExpression(update.Selector); + _builder.Append(" = "); + BuildSqlExpression(update.Value, visitSubqueryModel: true); + } + + _builder.Append(" "); + } + } } } From a592ddf9e4c13c12e3a9d3f8b27f329e3e5c5b5a Mon Sep 17 00:00:00 2001 From: gurustron Date: Sun, 4 Mar 2018 23:55:31 +0300 Subject: [PATCH 21/26] remove tostring from test class --- .../Cache/Query/Linq/CacheLinqTest.Base.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Base.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Base.cs index ee95ea11892b2..5b56abd9c4185 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Base.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Base.cs @@ -369,11 +369,6 @@ public void ReadBinary(IBinaryReader reader) AliasTest = reader.ReadInt("AliasTest"); Bool = reader.ReadBoolean("Bool"); } - - public override string ToString() - { - return String.Format("Age:{0} Name:{1} OrgId:{2} Alias:{3}", Age, Name, OrganizationId, AliasTest); - } } public class Address From d801d73243b2b70cf8b293c56235f8e80b7bc354 Mon Sep 17 00:00:00 2001 From: gurustron Date: Mon, 5 Mar 2018 09:25:33 +0300 Subject: [PATCH 22/26] fix inspections --- .../platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs | 2 ++ .../dotnet/Apache.Ignite.Linq/Impl/Dml/MemberUpdateContainer.cs | 2 +- .../Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs | 1 - .../Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs | 1 - 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs index 122d5d3bff257..743a302861f8a 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs @@ -23,6 +23,8 @@ namespace Apache.Ignite.Linq using System.Linq.Expressions; using System.Reflection; using Apache.Ignite.Core.Cache; + using Apache.Ignite.Core.Cache.Configuration; + using Apache.Ignite.Core.Cache.Query; using Apache.Ignite.Core.Impl.Cache; using Apache.Ignite.Core.Impl.Common; using Apache.Ignite.Linq.Impl; diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/MemberUpdateContainer.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/MemberUpdateContainer.cs index 057b4186f8de3..bf8a5fd433333 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/MemberUpdateContainer.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/MemberUpdateContainer.cs @@ -23,7 +23,7 @@ namespace Apache.Ignite.Linq.Impl.Dml /// /// Contains information about member update /// - public struct MemberUpdateContainer + internal struct MemberUpdateContainer { /// /// Gets or sets member selector diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs index 63f1ff871a85a..8be55b465c471 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs @@ -23,7 +23,6 @@ namespace Apache.Ignite.Linq.Impl.Dml using System.Linq; using System.Linq.Expressions; using System.Reflection; - using Apache.Ignite.Core.Cache; using Remotion.Linq.Clauses; using Remotion.Linq.Clauses.Expressions; using Remotion.Linq.Parsing.ExpressionVisitors; diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs index ca93ea995df71..60e6a4d356101 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs @@ -22,7 +22,6 @@ namespace Apache.Ignite.Linq.Impl.Dml using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; - using Apache.Ignite.Core.Cache; using Remotion.Linq.Clauses; using Remotion.Linq.Clauses.ResultOperators; using Remotion.Linq.Clauses.StreamedData; From fb37fe578f9ca7d91ca893059bc9d211d43e3e57 Mon Sep 17 00:00:00 2001 From: gurustron Date: Mon, 5 Mar 2018 09:35:11 +0300 Subject: [PATCH 23/26] inspections --- .../platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs index 743a302861f8a..2b65d851f220f 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs @@ -21,7 +21,6 @@ namespace Apache.Ignite.Linq using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; - using System.Reflection; using Apache.Ignite.Core.Cache; using Apache.Ignite.Core.Cache.Configuration; using Apache.Ignite.Core.Cache.Query; From 3609babb248280aa19cc5c8e9874d60f0f549ce0 Mon Sep 17 00:00:00 2001 From: gurustron Date: Tue, 6 Mar 2018 23:02:19 +0300 Subject: [PATCH 24/26] fixes after review: xml comments, line length, split tests --- .../Cache/Query/Linq/CacheLinqTest.Custom.cs | 182 ++++++++++-------- .../Apache.Ignite.Linq/IUpdateDescriptor.cs | 10 +- .../Impl/AliasDictionary.cs | 1 + .../Impl/CacheQueryExpressionVisitor.cs | 15 +- .../Impl/CacheQueryModelVisitor.cs | 25 +-- .../Impl/Dml/UpdateAllExpressionNode.cs | 6 +- .../Impl/Dml/UpdateAllResultOperator.cs | 7 + 7 files changed, 145 insertions(+), 101 deletions(-) diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs index 86ee78a4468ac..58e514435ded0 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs @@ -106,106 +106,66 @@ public void TestRemoveAll() } /// - /// Tests the UpdateAll extension. + /// Tests the UpdateAll extension without condition. /// [Test] - public void TestUpdateAll() + public void TestUpdateAllUnconditional() { - // Use new cache to avoid touching static data. var personCount = 10; - var orgsCount = 3; - - var cache = Ignition.GetIgnite().GetOrCreateCache(new CacheConfiguration("updateAllTest", - new QueryEntity(typeof(int), typeof(Person))) - { - SqlEscapeAll = GetSqlEscapeAll(), - CacheMode = CacheMode.Replicated - }); - - var orgsCache = Ignition.GetIgnite().GetOrCreateCache(new CacheConfiguration("updateAllTestOrgs", - new QueryEntity(typeof(int), typeof(Organization))) - { - SqlEscapeAll = GetSqlEscapeAll(), - CacheMode = CacheMode.Replicated - }); + var orgCount = 3; + var personQueryable = GetPersonCacheQueryable("updateAllTest_Unconditional_Persons", personCount, orgCount); + var orgQueryable = GetOrgCacheQueryable("updateAllTest_Unconditional_Org", orgCount); - Enumerable.Range(1, personCount) - .ToList() - .ForEach(x => cache.Put(x, new Person(x, x.ToString()) - { - Birthday = DateTime.UtcNow.AddDays(personCount - x), - OrganizationId = x % orgsCount - })); - - var allOrgs = Enumerable.Range(1, orgsCount) - .Select(x => new Organization - { - Id = x, - Name = x.ToString() - }) - .ToList(); - - allOrgs.ForEach(x => orgsCache.Put(x.Id, x)); - - var personQueryable = cache.AsCacheQueryable(); - - var orgsQueryable = orgsCache.AsCacheQueryable(); - - Action, bool>> assertAll = func => - Assert.IsTrue(personQueryable.ToArray().All(func)); - - Action, bool>> assertAllCount = (i, func) => - { - Assert.AreEqual(personCount, i); - assertAll(func); - }; - - // *** - // Unconditional - // *** + var allOrg = orgQueryable.ToArray(); // Constant value var updated = personQueryable .UpdateAll(d => d.Set(p => p.AliasTest, 7)); - assertAllCount(updated, p => p.Value.AliasTest == 7); + Assert.AreEqual(personCount, updated); + AssertAll(personQueryable, p => p.Value.AliasTest == 7); // Expression value - from self updated = personQueryable .UpdateAll(d => d.Set(p => p.AliasTest, e => e.Key)); - assertAllCount(updated, p => p.Value.AliasTest == p.Key); + Assert.AreEqual(personCount, updated); + AssertAll(personQueryable, p => p.Value.AliasTest == p.Key); // Multiple sets var aliasValue = 3; updated = personQueryable .UpdateAll(d => d.Set(p => p.AliasTest, aliasValue).Set(p => p.Name, aliasValue.ToString())); - assertAllCount(updated, p => p.Value.AliasTest == aliasValue && p.Value.Name == aliasValue.ToString()); + Assert.AreEqual(personCount, updated); + AssertAll(personQueryable, p => p.Value.AliasTest == aliasValue && p.Value.Name == aliasValue.ToString()); // Expression value - subquery with same cache updated = personQueryable .UpdateAll(d => d.Set(p => p.AliasTest, e => personQueryable.Where(ie => ie.Key == e.Key).Select(ie => ie.Key).First())); - assertAllCount(updated, p => p.Value.AliasTest == p.Key); + Assert.AreEqual(personCount, updated); + AssertAll(personQueryable, p => p.Value.AliasTest == p.Key); // Expression value - subquery with other cache updated = personQueryable - .UpdateAll(d => d.Set(p => p.AliasTest, p => orgsQueryable.Count(o => o.Value.Id > p.Key))); - - assertAllCount(updated, p => p.Value.AliasTest == allOrgs.Count(o => o.Id > p.Key)); + .UpdateAll(d => d.Set(p => p.AliasTest, p => orgQueryable.Count(o => o.Value.Id > p.Key))); + Assert.AreEqual(personCount, updated); + AssertAll(personQueryable, p => p.Value.AliasTest == allOrg.Count(o => o.Value.Id > p.Key)); updated = personQueryable .UpdateAll(d => d.Set(p => p.Name, - e => orgsQueryable.Where(o => o.Key == e.Value.OrganizationId).Select(o => o.Value.Name).First())); - assertAllCount(updated, - p => p.Value.Name == allOrgs.Where(o => o.Id == p.Value.OrganizationId).Select(o => o.Name) + e => orgQueryable.Where(o => o.Key == e.Value.OrganizationId).Select(o => o.Value.Name).First())); + Assert.AreEqual(personCount, updated); + AssertAll(personQueryable, + p => p.Value.Name == allOrg.Where(o => o.Key == p.Value.OrganizationId).Select(o => o.Value.Name) .FirstOrDefault()); // Expression value - Contains subquery with other cache updated = personQueryable .UpdateAll(d => d.Set(p => p.Bool, - p => orgsQueryable.Select(o => o.Key).Contains(p.Value.OrganizationId))); - assertAllCount(updated, - p => p.Value.Bool == allOrgs.Select(o => o.Id).Contains(p.Value.OrganizationId)); + p => orgQueryable.Select(o => o.Key).Contains(p.Value.OrganizationId))); + Assert.AreEqual(personCount, updated); + AssertAll(personQueryable, + p => p.Value.Bool == allOrg.Select(o => o.Key).Contains(p.Value.OrganizationId)); // Row number limit. var name = "rowLimit" + 2; @@ -214,18 +174,25 @@ public void TestUpdateAll() .UpdateAll(d => d.Set(p => p.Name, name)); Assert.AreEqual(2, updated); Assert.AreEqual(2, personQueryable.Count(p => p.Value.Name == name)); + } - // *** - // Conditional - // *** + /// + /// Tests the UpdateAll extension with condition. + /// + [Test] + public void TestUpdateAllWithCondition() + { + // Use new cache to avoid touching static data. + var personQueryable = GetPersonCacheQueryable("updateAllTest_WithCondition_Persons", 10); // Simple conditional - aliasValue = 777; - updated = personQueryable + var aliasValue = 777; + var updated = personQueryable .Where(p => p.Key > 8) .UpdateAll(d => d.Set(p => p.AliasTest, aliasValue)); Assert.AreEqual(2, updated); - assertAll(p => p.Key <= 8 && p.Value.AliasTest != aliasValue || p.Value.AliasTest == aliasValue); + AssertAll(personQueryable, + p => p.Key <= 8 && p.Value.AliasTest != aliasValue || p.Value.AliasTest == aliasValue); // Conditional with limit aliasValue = 8888; @@ -235,11 +202,17 @@ public void TestUpdateAll() .UpdateAll(d => d.Set(p => p.AliasTest, aliasValue)); Assert.AreEqual(3, updated); Assert.AreEqual(3, personQueryable.ToArray().Count(p => p.Value.AliasTest == aliasValue)); + } - // *** - // Not supported - // *** - + /// + /// Tests not supported queries for the UpdateAll extension . + /// + [Test] + public void TestUpdateAllUnsupported() + { + // Use new cache to avoid touching static data. + var personQueryable = GetPersonCacheQueryable("updateAllTest_Unsupported_Persons", 10); + // Skip is not supported with DELETE. var nex = Assert.Throws( () => personQueryable.Skip(1).UpdateAll(d => d.Set(p => p.Age, 15))); @@ -249,7 +222,8 @@ public void TestUpdateAll() // Multiple result operators are not supported with DELETE. nex = Assert.Throws(() => personQueryable.Skip(1).Take(1).UpdateAll(d => d.Set(p => p.Age, 15))); - Assert.AreEqual("UpdateAll can not be combined with result operators (other than Take): SkipResultOperator, " + + Assert.AreEqual( + "UpdateAll can not be combined with result operators (other than Take): SkipResultOperator, " + "TakeResultOperator, UpdateAllResultOperator", nex.Message); // Joins are not supported in H2. @@ -260,5 +234,61 @@ public void TestUpdateAll() var ex = Assert.Throws(() => qry.UpdateAll(d => d.Set(p => p.Age, 15))); Assert.AreEqual("Failed to parse query", ex.Message.Substring(0, 21)); } + + /// + /// Gets filled persons cache queryable + /// + private IQueryable> GetPersonCacheQueryable(string cacheName, int personCount, int? orgCount = null) + { + var cache = GetCache(cacheName); + Enumerable.Range(1, personCount) + .ToList() + .ForEach(x => cache.Put(x, new Person(x, x.ToString()) + { + Birthday = DateTime.UtcNow.AddDays(personCount - x), + OrganizationId = x % orgCount ?? 0 + })); + + return cache.AsCacheQueryable(); + } + + private void AssertAll( + IQueryable> personQueryable, + Func, bool> predicate) + { + Assert.IsTrue(personQueryable.ToArray().All(predicate)); + } + + /// + /// Gets filled organization cache queryable + /// + private IQueryable> GetOrgCacheQueryable(string cacheName, int orgCount) + { + var cache = GetCache(cacheName); + var allOrg = Enumerable.Range(1, orgCount) + .Select(x => new Organization + { + Id = x, + Name = x.ToString() + }) + .ToList(); + + allOrg.ForEach(x => cache.Put(x.Id, x)); + + return cache.AsCacheQueryable(); + } + + /// + /// Gets cache of + /// + private ICache GetCache(string cacheName) + { + return Ignition.GetIgnite().GetOrCreateCache(new CacheConfiguration(cacheName, + new QueryEntity(typeof(int), typeof(T))) + { + SqlEscapeAll = GetSqlEscapeAll(), + CacheMode = CacheMode.Replicated + }); + } } } \ No newline at end of file diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/IUpdateDescriptor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/IUpdateDescriptor.cs index c43f007e3341c..2327b99202e81 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/IUpdateDescriptor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/IUpdateDescriptor.cs @@ -21,14 +21,16 @@ namespace Apache.Ignite.Linq using Apache.Ignite.Core.Cache; /// - /// Dummy interface to provide update description for + /// Dummy interface to provide update description for + /// /// /// Key type. /// Value type. public interface IUpdateDescriptor { /// - /// Allows to specifify member update with constant + /// Specifies member update with constant /// /// Member type /// Member selector @@ -37,11 +39,11 @@ public interface IUpdateDescriptor IUpdateDescriptor Set(Func selector, TProp value); /// - /// Allows to specifify member update with expression + /// Specifies member update with expression /// /// Member type /// Member selector - /// New value expression + /// New value generator /// IUpdateDescriptor Set(Func selector, Func, TProp> valueBuilder); diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/AliasDictionary.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/AliasDictionary.cs index 85584918c07d8..15710db300cd9 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/AliasDictionary.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/AliasDictionary.cs @@ -50,6 +50,7 @@ internal class AliasDictionary /// /// Pushes current aliases to stack. /// + /// Flag indicating that current aliases should be copied public void Push(bool copyAliases) { _stack.Push(_tableAliases); diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs index 7e0efe29b0ad7..d92c3bde26e17 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs @@ -61,12 +61,17 @@ internal class CacheQueryExpressionVisitor : ThrowingExpressionVisitor /// Initializes a new instance of the class. /// /// The _model visitor. - /// Flag indicating that star '*' qualifier should be used - /// for the whole-table select instead of _key, _val. - /// Flag indicating that star '*' qualifier should be used - /// for the whole-table select as well as _key, _val. + /// + /// Flag indicating that star '*' qualifier should be used + /// for the whole-table select instead of _key, _val. + /// + /// + /// Flag indicating that star '*' qualifier should be used + /// for the whole-table select as well as _key, _val. + /// /// Flag, indicating that subquery should be visited as full query - public CacheQueryExpressionVisitor(CacheQueryModelVisitor modelVisitor, bool useStar, bool includeAllFields, bool visitEntireSubQueryModel) + public CacheQueryExpressionVisitor(CacheQueryModelVisitor modelVisitor, bool useStar, bool includeAllFields, + bool visitEntireSubQueryModel) { Debug.Assert(modelVisitor != null); diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs index e7933c345edf8..a42c31afc535a 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs @@ -201,13 +201,13 @@ private void VisitUpdateAllOperator(QueryModel queryModel) /// public void VisitSelectors(QueryModel queryModel, bool includeAllFields) { - var parentCount = ProcessResultOperatorsBegin(queryModel); + var parenCount = ProcessResultOperatorsBegin(queryModel); - if (parentCount >= 0) + if (parenCount >= 0) { // FIELD1, FIELD2 - BuildSqlExpression(queryModel.SelectClause.Selector, parentCount > 0, includeAllFields); - _builder.Append(')', parentCount).Append(" "); + BuildSqlExpression(queryModel.SelectClause.Selector, parenCount > 0, includeAllFields); + _builder.Append(')', parenCount).Append(" "); } } @@ -217,34 +217,34 @@ public void VisitSelectors(QueryModel queryModel, bool includeAllFields) [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] private int ProcessResultOperatorsBegin(QueryModel queryModel) { - int parentCount = 0; + int parenCount = 0; foreach (var op in queryModel.ResultOperators.Reverse()) { if (op is CountResultOperator || op is AnyResultOperator) { _builder.Append("count ("); - parentCount++; + parenCount++; } else if (op is SumResultOperator) { _builder.Append("sum ("); - parentCount++; + parenCount++; } else if (op is MinResultOperator) { _builder.Append("min ("); - parentCount++; + parenCount++; } else if (op is MaxResultOperator) { _builder.Append("max ("); - parentCount++; + parenCount++; } else if (op is AverageResultOperator) { _builder.Append("avg ("); - parentCount++; + parenCount++; } else if (op is DistinctResultOperator) _builder.Append("distinct "); @@ -260,7 +260,7 @@ private int ProcessResultOperatorsBegin(QueryModel queryModel) else throw new NotSupportedException("Operator is not supported: " + op); } - return parentCount; + return parenCount; } /// @@ -683,7 +683,8 @@ private void BuildJoinSubCondition(Expression innerKey, Expression outerKey) /// /// Builds the SQL expression. /// - private void BuildSqlExpression(Expression expression, bool useStar = false, bool includeAllFields = false, bool visitSubqueryModel = false) + private void BuildSqlExpression(Expression expression, bool useStar = false, bool includeAllFields = false, + bool visitSubqueryModel = false) { new CacheQueryExpressionVisitor(this, useStar, includeAllFields, visitSubqueryModel).Visit(expression); } diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs index 8be55b465c471..ec89d23b98a3f 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs @@ -30,9 +30,7 @@ namespace Apache.Ignite.Linq.Impl.Dml /// /// Represents a for - /// - /// . + /// . /// When user calls UpdateAll, this node is generated. /// internal sealed class UpdateAllExpressionNode : ResultOperatorExpressionNodeBase @@ -75,7 +73,7 @@ protected override ResultOperatorBase CreateResultOperator(ClauseGenerationConte { if (_updateDescription.Parameters.Count != 1) throw new NotSupportedException("Expression is not supported for UpdateAll: " + - _updateDescription); + _updateDescription); var querySourceRefExpression = (QuerySourceReferenceExpression) Source.Resolve( _updateDescription.Parameters[0], diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs index 60e6a4d356101..be682b044e7cc 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllResultOperator.cs @@ -31,8 +31,15 @@ namespace Apache.Ignite.Linq.Impl.Dml /// internal sealed class UpdateAllResultOperator : ValueFromSequenceResultOperatorBase { + /// + /// Gets the members updates + /// public MemberUpdateContainer[] Updates { get; private set; } + /// + /// Initializes a new instance of + /// + /// members updates public UpdateAllResultOperator(IEnumerable updates) { Updates = updates.ToArray(); From 92ca8685d03728ca1ff7909321f48247a360d5f0 Mon Sep 17 00:00:00 2001 From: gurustron Date: Tue, 6 Mar 2018 23:16:37 +0300 Subject: [PATCH 25/26] some more changes --- .../Cache/Query/Linq/CacheLinqTest.Custom.cs | 3 +++ .../Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs index 58e514435ded0..dce10012a78d2 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs @@ -252,6 +252,9 @@ public void TestUpdateAllUnsupported() return cache.AsCacheQueryable(); } + /// + /// Asserts that all values in cache correspond to predicate + /// private void AssertAll( IQueryable> personQueryable, Func, bool> predicate) diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs index ec89d23b98a3f..8cfb552d10bd8 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/Dml/UpdateAllExpressionNode.cs @@ -111,7 +111,8 @@ protected override ResultOperatorBase CreateResultOperator(ClauseGenerationConte querySourceRefExpression, newValueLambda.Body); break; default: - throw new NotSupportedException("Value expression is not supported for UpdateAll: " + newValue); + throw new NotSupportedException("Value expression is not supported for UpdateAll: " + + newValue); } updates.Add(new MemberUpdateContainer From e35774d43f04403c42481ae30be3772b078810f4 Mon Sep 17 00:00:00 2001 From: gurustron Date: Tue, 6 Mar 2018 23:24:56 +0300 Subject: [PATCH 26/26] line lengths --- .../Cache/Query/Linq/CacheLinqTest.Custom.cs | 3 ++- .../Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs index dce10012a78d2..69bf4c2e35bde 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Custom.cs @@ -238,7 +238,8 @@ public void TestUpdateAllUnsupported() /// /// Gets filled persons cache queryable /// - private IQueryable> GetPersonCacheQueryable(string cacheName, int personCount, int? orgCount = null) + private IQueryable> GetPersonCacheQueryable(string cacheName, int personCount, + int? orgCount = null) { var cache = GetCache(cacheName); Enumerable.Range(1, personCount) diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs index d92c3bde26e17..c5c887f64275b 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs @@ -69,7 +69,10 @@ internal class CacheQueryExpressionVisitor : ThrowingExpressionVisitor /// Flag indicating that star '*' qualifier should be used /// for the whole-table select as well as _key, _val. /// - /// Flag, indicating that subquery should be visited as full query + /// + /// Flag indicating that subquery + /// should be visited as full query + /// public CacheQueryExpressionVisitor(CacheQueryModelVisitor modelVisitor, bool useStar, bool includeAllFields, bool visitEntireSubQueryModel) {