You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ak...@apache.org on 2016/08/26 09:16:35 UTC
[31/50] ignite git commit: IGNITE-3325 .NET: Improve CompiledQuery in
LINQ provider
IGNITE-3325 .NET: Improve CompiledQuery in LINQ provider
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/d6033712
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/d6033712
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/d6033712
Branch: refs/heads/ignite-3443
Commit: d6033712425cd0157fa640a7d46ec6579fd7e74b
Parents: f925873
Author: Pavel Tupitsyn <pt...@apache.org>
Authored: Tue Aug 23 14:10:55 2016 +0300
Committer: Pavel Tupitsyn <pt...@apache.org>
Committed: Tue Aug 23 14:10:55 2016 +0300
----------------------------------------------------------------------
.../Cache/Query/CacheLinqTest.cs | 196 +++++++++++++-
.../Apache.Ignite.Linq.csproj | 2 +-
.../dotnet/Apache.Ignite.Linq/CompiledQuery.cs | 1 +
.../dotnet/Apache.Ignite.Linq/CompiledQuery2.cs | 257 +++++++++++++++++++
.../Impl/CacheFieldsQueryExecutor.cs | 116 ++++++++-
.../Impl/CacheQueryExpressionVisitor.cs | 15 ++
.../Impl/CacheQueryModelVisitor.cs | 15 +-
.../Impl/CacheQueryableBase.cs | 22 ++
.../Apache.Ignite.Linq/Impl/ExpressionWalker.cs | 9 +
.../Apache.Ignite.Linq/Impl/ICacheQueryProxy.cs | 40 ---
.../Impl/ICacheQueryableInternal.cs | 14 +-
11 files changed, 622 insertions(+), 65 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ignite/blob/d6033712/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheLinqTest.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheLinqTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheLinqTest.cs
index a47f3f0..a8cc8fd 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheLinqTest.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheLinqTest.cs
@@ -28,6 +28,7 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
{
using System;
using System.Collections;
+ using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions;
using System.Text.RegularExpressions;
@@ -506,9 +507,11 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
public void TestInvalidJoin()
{
// Join on non-IQueryable
- Assert.Throws<NotSupportedException>(() =>
+ var ex = Assert.Throws<NotSupportedException>(() =>
// ReSharper disable once ReturnValueOfPureMethodIsNotUsed
GetPersonCache().AsCacheQueryable().Join(GetOrgCache(), p => p.Key, o => o.Key, (p, o) => p).ToList());
+
+ Assert.IsTrue(ex.Message.StartsWith("Unexpected query source"));
}
/// <summary>
@@ -836,11 +839,187 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
}
/// <summary>
- /// Tests the compiled query.
+ /// Tests the compiled query with various constructs.
/// </summary>
[Test]
public void TestCompiledQuery()
{
+ var persons = GetPersonCache().AsCacheQueryable();
+ var roles = GetRoleCache().AsCacheQueryable();
+
+ // Embedded args
+ var qry0 = CompiledQuery2.Compile(() => persons.Where(x => x.Key < 3 && x.Value.Name.Contains("son")));
+ Assert.AreEqual(3, qry0().Count());
+
+ // Lambda args
+ var qry1 = CompiledQuery2.Compile((int minKey, int take, int skip) => persons.Where(x => x.Key > minKey)
+ .Take(take).Skip(skip));
+ Assert.AreEqual(3, qry1(-1, 3, 1).GetAll().Count);
+
+ qry1 = CompiledQuery2.Compile((int skip, int take, int minKey) => persons.Where(x => x.Key > minKey)
+ .Take(take).Skip(skip));
+
+ Assert.AreEqual(5, qry1(2, 5, 20).GetAll().Count);
+
+ // Mixed args
+ var qry2 = CompiledQuery2.Compile((int maxKey, int minKey) =>
+ persons.Where(x => x.Key < maxKey
+ && x.Value.Name.Contains("er")
+ && x.Value.Age < maxKey
+ && x.Key > minKey).Skip(2));
+
+ Assert.AreEqual(6, qry2(10, 1).Count());
+
+ // Join
+ var qry3 = CompiledQuery2.Compile(() =>
+ roles.Join(persons, r => r.Key.Foo, p => p.Key, (r, p) => r.Value.Name));
+
+ Assert.AreEqual(RoleCount, qry3().Count());
+
+ // Join with subquery
+ var qry4 = CompiledQuery2.Compile(
+ (int a, int b, string sep) =>
+ roles
+ .Where(x => x.Key.Bar > a)
+ .Join(persons.Where(x => x.Key < b && x.Key > 0),
+ r => r.Key.Foo,
+ p => p.Value.Address.Zip,
+ (r, p) => p.Value.Name + sep + r.Value.Name + "|")
+ .Skip(a).Take(1000)
+ );
+
+ Assert.AreEqual(new[] { " Person_2 =Role_2|", " Person_3 =|"}, qry4(1, PersonCount, "=").ToArray());
+
+ // Union
+ var qry5 = CompiledQuery2.Compile(() => roles.Select(x => -x.Key.Foo).Union(persons.Select(x => x.Key)));
+
+ Assert.AreEqual(RoleCount + PersonCount, qry5().Count());
+
+ // Projection
+ var qry6 = CompiledQuery2.Compile((int minAge) => persons
+ .Select(x => x.Value)
+ .Where(x => x.Age >= minAge)
+ .Select(x => new {x.Name, x.Age})
+ .OrderBy(x => x.Name));
+
+ var res = qry6(PersonCount - 3).GetAll();
+
+ Assert.AreEqual(3, res.Count);
+ Assert.AreEqual(PersonCount - 3, res[0].Age);
+ }
+
+ /// <summary>
+ /// Tests the compiled query overloads.
+ /// </summary>
+ [Test]
+ public void TestCompiledQueryOverloads()
+ {
+ var cache = GetPersonCache().AsCacheQueryable();
+
+ // const args are allowed
+ Assert.AreEqual(5, CompiledQuery2.Compile(() => cache.Where(x => x.Key < 5))().GetAll().Count);
+
+ // 0 arg
+ var qry0 = CompiledQuery2.Compile(() => cache.Select(x => x.Value.Name));
+ Assert.AreEqual(PersonCount, qry0().ToArray().Length);
+
+ // 1 arg
+ var qry1 = CompiledQuery2.Compile((int k) => cache.Where(x => x.Key < k));
+ Assert.AreEqual(3, qry1(3).ToArray().Length);
+
+ // 1 arg twice
+ var qry1T = CompiledQuery2.Compile((int k) => cache.Where(x => x.Key < k && x.Value.Age < k));
+ Assert.AreEqual(3, qry1T(3).ToArray().Length);
+
+ // 2 arg
+ var qry2 =
+ CompiledQuery2.Compile((int i, string s) => cache.Where(x => x.Key < i && x.Value.Name.StartsWith(s)));
+ Assert.AreEqual(5, qry2(5, " Pe").ToArray().Length);
+
+ // Changed param order
+ var qry2R =
+ CompiledQuery2.Compile((string s, int i) => cache.Where(x => x.Key < i && x.Value.Name.StartsWith(s)));
+ Assert.AreEqual(5, qry2R(" Pe", 5).ToArray().Length);
+
+ // 3 arg
+ var qry3 = CompiledQuery2.Compile((int i, string s, double d) =>
+ cache.Where(x => x.Value.Address.Zip > d && x.Key < i && x.Value.Name.Contains(s)));
+ Assert.AreEqual(5, qry3(5, "son", -10).ToArray().Length);
+
+ // 4 arg
+ var qry4 = CompiledQuery2.Compile((int a, int b, int c, int d) =>
+ cache.Select(x => x.Key).Where(k => k > a && k > b && k > c && k < d));
+ Assert.AreEqual(new[] {3, 4}, qry4(0, 1, 2, 5).ToArray());
+
+ // 5 arg
+ var qry5 = CompiledQuery2.Compile((int a, int b, int c, int d, int e) =>
+ cache.Select(x => x.Key).Where(k => k > a && k > b && k > c && k < d && k < e));
+ Assert.AreEqual(new[] {3, 4}, qry5(0, 1, 2, 5, 6).ToArray());
+
+ // 6 arg
+ var qry6 = CompiledQuery2.Compile((int a, int b, int c, int d, int e, int f) =>
+ cache.Select(x => x.Key).Where(k => k > a && k > b && k > c && k < d && k < e && k < f));
+ Assert.AreEqual(new[] {3, 4}, qry6(0, 1, 2, 5, 6, 7).ToArray());
+
+ // 7 arg
+ var qry7 = CompiledQuery2.Compile((int a, int b, int c, int d, int e, int f, int g) =>
+ cache.Select(x => x.Key).Where(k => k > a && k > b && k > c && k < d && k < e && k < f && k < g));
+ Assert.AreEqual(new[] {3, 4}, qry7(0, 1, 2, 5, 6, 7, 8).ToArray());
+
+ // 8 arg
+ var qry8 = CompiledQuery2.Compile((int a, int b, int c, int d, int e, int f, int g, int h) =>
+ cache.Select(x => x.Key).Where(k => k > a && k > b && k > c && k < d && k < e && k < f && k < g && k < h));
+ Assert.AreEqual(new[] {3, 4}, qry8(0, 1, 2, 5, 6, 7, 8, 9).ToArray());
+ }
+
+ /// <summary>
+ /// Tests the free-form compiled query, where user provides an array of arguments.
+ /// </summary>
+ [Test]
+ public void TestCompiledQueryFreeform()
+ {
+ var cache = GetPersonCache().AsCacheQueryable();
+
+ var qry = cache.Where(x => x.Key < 5);
+
+ // Simple
+ var compiled = CompiledQuery2.Compile(qry);
+
+ Assert.AreEqual(5, compiled(5).Count());
+ Assert.AreEqual(6, compiled(6).Count());
+
+ // Select
+ var compiledSelect = CompiledQuery2.Compile(qry.Select(x => x.Value.Name).OrderBy(x => x));
+
+ Assert.AreEqual(3, compiledSelect(3).Count());
+ Assert.AreEqual(" Person_0 ", compiledSelect(1).Single());
+
+ // Join
+ var compiledJoin = CompiledQuery2.Compile(qry.Join(
+ GetOrgCache().AsCacheQueryable().Where(x => x.Value.Name.StartsWith("Org")),
+ p => p.Value.OrganizationId, o => o.Value.Id, (p, o) => o.Key));
+
+ Assert.AreEqual(1000, compiledJoin("Org", 1).Single());
+
+ // Many parameters
+ var qry2 = cache.Where(x => x.Key < 3)
+ .Where(x => x.Key > 0)
+ .Where(x => x.Value.Name.Contains(""))
+ .Where(x => x.Value.Address.Zip > 0)
+ .Where(x => x.Value.Age == 7);
+
+ var compiled2 = CompiledQuery2.Compile(qry2);
+
+ Assert.AreEqual(17, compiled2(18, 16, "ers", 13, 17).Single().Key);
+ }
+
+#pragma warning disable 618 // obsolete class
+ /// <summary>
+ /// Tests the old, deprecated compiled query.
+ /// </summary>
+ [Test]
+ public void TestCompiledQueryOld()
+ {
var cache = GetPersonCache().AsCacheQueryable();
// const args are not allowed
@@ -865,7 +1044,7 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
Assert.AreEqual(5, qry2R(" Pe", 5).ToArray().Length);
// 3 arg
- var qry3 = CompiledQuery.Compile((int i, string s, double d) =>
+ var qry3 = CompiledQuery.Compile((int i, string s, double d) =>
cache.Where(x => x.Value.Address.Zip > d && x.Key < i && x.Value.Name.Contains(s)));
Assert.AreEqual(5, qry3(5, "son", -10).ToArray().Length);
@@ -873,28 +1052,29 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
var keys = cache.Select(x => x.Key);
var qry4 = CompiledQuery.Compile((int a, int b, int c, int d) =>
keys.Where(k => k > a && k > b && k > c && k < d));
- Assert.AreEqual(new[] {3, 4}, qry4(0, 1, 2, 5).ToArray());
+ Assert.AreEqual(new[] { 3, 4 }, qry4(0, 1, 2, 5).ToArray());
// 5 arg
var qry5 = CompiledQuery.Compile((int a, int b, int c, int d, int e) =>
keys.Where(k => k > a && k > b && k > c && k < d && k < e));
- Assert.AreEqual(new[] {3, 4}, qry5(0, 1, 2, 5, 6).ToArray());
+ Assert.AreEqual(new[] { 3, 4 }, qry5(0, 1, 2, 5, 6).ToArray());
// 6 arg
var qry6 = CompiledQuery.Compile((int a, int b, int c, int d, int e, int f) =>
keys.Where(k => k > a && k > b && k > c && k < d && k < e && k < f));
- Assert.AreEqual(new[] {3, 4}, qry6(0, 1, 2, 5, 6, 7).ToArray());
+ Assert.AreEqual(new[] { 3, 4 }, qry6(0, 1, 2, 5, 6, 7).ToArray());
// 7 arg
var qry7 = CompiledQuery.Compile((int a, int b, int c, int d, int e, int f, int g) =>
keys.Where(k => k > a && k > b && k > c && k < d && k < e && k < f && k < g));
- Assert.AreEqual(new[] {3, 4}, qry7(0, 1, 2, 5, 6, 7, 8).ToArray());
+ Assert.AreEqual(new[] { 3, 4 }, qry7(0, 1, 2, 5, 6, 7, 8).ToArray());
// 8 arg
var qry8 = CompiledQuery.Compile((int a, int b, int c, int d, int e, int f, int g, int h) =>
keys.Where(k => k > a && k > b && k > c && k < d && k < e && k < f && k < g && k < h));
- Assert.AreEqual(new[] {3, 4}, qry8(0, 1, 2, 5, 6, 7, 8, 9).ToArray());
+ Assert.AreEqual(new[] { 3, 4 }, qry8(0, 1, 2, 5, 6, 7, 8, 9).ToArray());
}
+#pragma warning restore 618
/// <summary>
/// Tests the cache of primitive types.
http://git-wip-us.apache.org/repos/asf/ignite/blob/d6033712/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj
----------------------------------------------------------------------
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 d1dad16..d00ebea 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj
@@ -52,6 +52,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="CacheExtensions.cs" />
+ <Compile Include="CompiledQuery2.cs" />
<Compile Include="CompiledQuery.cs" />
<Compile Include="ICacheQueryable.cs" />
<Compile Include="Impl\AliasDictionary.cs" />
@@ -64,7 +65,6 @@
<Compile Include="Impl\CacheQueryModelVisitor.cs" />
<Compile Include="Impl\CacheQueryParser.cs" />
<Compile Include="Impl\ICacheQueryableInternal.cs" />
- <Compile Include="Impl\ICacheQueryProxy.cs" />
<Compile Include="Impl\MethodVisitor.cs" />
<Compile Include="Impl\QueryData.cs" />
<Compile Include="Impl\SqlTypes.cs" />
http://git-wip-us.apache.org/repos/asf/ignite/blob/d6033712/modules/platforms/dotnet/Apache.Ignite.Linq/CompiledQuery.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/CompiledQuery.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/CompiledQuery.cs
index 9e6c6c7..817c423 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Linq/CompiledQuery.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/CompiledQuery.cs
@@ -28,6 +28,7 @@ namespace Apache.Ignite.Linq
/// <summary>
/// Represents a compiled cache query.
/// </summary>
+ [Obsolete("Use CompiledQuery2 class.")]
public static class CompiledQuery
{
/// <summary>
http://git-wip-us.apache.org/repos/asf/ignite/blob/d6033712/modules/platforms/dotnet/Apache.Ignite.Linq/CompiledQuery2.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/CompiledQuery2.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/CompiledQuery2.cs
new file mode 100644
index 0000000..85aafd9
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/CompiledQuery2.cs
@@ -0,0 +1,257 @@
+\ufeff/*
+ * 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 System.Diagnostics;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Linq;
+ using System.Linq.Expressions;
+ using Apache.Ignite.Core.Cache.Query;
+ using Apache.Ignite.Core.Impl.Common;
+ using Apache.Ignite.Linq.Impl;
+
+ /// <summary>
+ /// Represents a compiled cache query.
+ /// </summary>
+ public static class CompiledQuery2
+ {
+ /// <summary>
+ /// Delegate for compiled query with arbitrary number of arguments.
+ /// </summary>
+ /// <typeparam name="T">Result type.</typeparam>
+ /// <param name="args">The arguments.</param>
+ /// <returns>Query cursor.</returns>
+ public delegate IQueryCursor<T> CompiledQueryDelegate<T>(params object[] args);
+
+ /// <summary>
+ /// Creates a new delegate that represents the compiled cache query.
+ /// </summary>
+ /// <param name="query">The query to compile.</param>
+ /// <returns>Delegate that represents the compiled cache query.</returns>
+ [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0",
+ Justification = "Invalid warning, validation is present.")]
+ public static Func<IQueryCursor<T>> Compile<T>(Expression<Func<IQueryable<T>>> query)
+ {
+ IgniteArgumentCheck.NotNull(query, "query");
+
+ var compiledQuery = GetCompiledQuery<T>(query, query.Compile());
+
+ return () => compiledQuery(new object[0]);
+ }
+
+ /// <summary>
+ /// Creates a new delegate that represents the compiled cache query with any number of arguments.
+ /// <para />
+ /// This method differs from other Compile methods in that it takes in <see cref="ICacheQueryable"/> directly,
+ /// and returns a delegate that takes an array of parameters.
+ /// It is up to the user to provide query arguments in correct order.
+ /// <para />
+ /// This method also imposes no restrictions on where the query comes from (in contrary to other methods).
+ /// </summary>
+ /// <param name="query">The query to compile.</param>
+ /// <returns>Delegate that represents the compiled cache query.</returns>
+ [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0",
+ Justification = "Invalid warning, validation is present.")]
+ public static CompiledQueryDelegate<T> Compile<T>(IQueryable<T> query)
+ {
+ IgniteArgumentCheck.NotNull(query, "query");
+
+ var cacheQueryable = query as ICacheQueryableInternal;
+
+ if (cacheQueryable == null)
+ throw GetInvalidQueryException(query);
+
+ var compileQuery = cacheQueryable.CompileQuery<T>();
+
+ // Special delegate is required to allow params[].
+ return args => compileQuery(args);
+ }
+
+ /// <summary>
+ /// Creates a new delegate that represents the compiled cache query.
+ /// </summary>
+ /// <param name="query">The query to compile.</param>
+ /// <returns>Delegate that represents the compiled cache query.</returns>
+ [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0",
+ Justification = "Invalid warning, validation is present.")]
+ public static Func<T1, IQueryCursor<T>> Compile<T, T1>(Expression<Func<T1, IQueryable<T>>> query)
+ {
+ IgniteArgumentCheck.NotNull(query, "query");
+
+ var compiledQuery = GetCompiledQuery<T>(query, query.Compile());
+
+ return x => compiledQuery(new object[] {x});
+ }
+
+ /// <summary>
+ /// Creates a new delegate that represents the compiled cache query.
+ /// </summary>
+ /// <param name="query">The query to compile.</param>
+ /// <returns>Delegate that represents the compiled cache query.</returns>
+ [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0",
+ Justification = "Invalid warning, validation is present.")]
+ public static Func<T1, T2, IQueryCursor<T>> Compile<T, T1, T2>(Expression<Func<T1, T2,
+ IQueryable<T>>> query)
+ {
+ IgniteArgumentCheck.NotNull(query, "query");
+
+ var compiledQuery = GetCompiledQuery<T>(query, query.Compile());
+
+ return (x, y) => compiledQuery(new object[] {x, y});
+ }
+
+ /// <summary>
+ /// Creates a new delegate that represents the compiled cache query.
+ /// </summary>
+ /// <param name="query">The query to compile.</param>
+ /// <returns>Delegate that represents the compiled cache query.</returns>
+ [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0",
+ Justification = "Invalid warning, validation is present.")]
+ public static Func<T1, T2, T3, IQueryCursor<T>> Compile<T, T1, T2, T3>(Expression<Func<T1, T2, T3,
+ IQueryable<T>>> query)
+ {
+ IgniteArgumentCheck.NotNull(query, "query");
+
+ var compiledQuery = GetCompiledQuery<T>(query, query.Compile());
+
+ return (x, y, z) => compiledQuery(new object[] {x, y, z});
+ }
+
+ /// <summary>
+ /// Creates a new delegate that represents the compiled cache query.
+ /// </summary>
+ /// <param name="query">The query to compile.</param>
+ /// <returns>Delegate that represents the compiled cache query.</returns>
+ [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0",
+ Justification = "Invalid warning, validation is present.")]
+ public static Func<T1, T2, T3, T4, IQueryCursor<T>> Compile<T, T1, T2, T3, T4>(Expression<Func<T1, T2, T3, T4,
+ IQueryable<T>>> query)
+ {
+ IgniteArgumentCheck.NotNull(query, "query");
+
+ var compiledQuery = GetCompiledQuery<T>(query, query.Compile());
+
+ return (x, y, z, a) => compiledQuery(new object[] {x, y, z, a});
+ }
+
+ /// <summary>
+ /// Creates a new delegate that represents the compiled cache query.
+ /// </summary>
+ /// <param name="query">The query to compile.</param>
+ /// <returns>Delegate that represents the compiled cache query.</returns>
+ [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0",
+ Justification = "Invalid warning, validation is present.")]
+ public static Func<T1, T2, T3, T4, T5, IQueryCursor<T>> Compile<T, T1, T2, T3, T4, T5>(
+ Expression<Func<T1, T2, T3, T4, T5, IQueryable<T>>> query)
+ {
+ IgniteArgumentCheck.NotNull(query, "query");
+
+ var compiledQuery = GetCompiledQuery<T>(query, query.Compile());
+
+ return (x, y, z, a, b) => compiledQuery(new object[] {x, y, z, a, b});
+ }
+
+ /// <summary>
+ /// Creates a new delegate that represents the compiled cache query.
+ /// </summary>
+ /// <param name="query">The query to compile.</param>
+ /// <returns>Delegate that represents the compiled cache query.</returns>
+ [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0",
+ Justification = "Invalid warning, validation is present.")]
+ public static Func<T1, T2, T3, T4, T5, T6, IQueryCursor<T>> Compile<T, T1, T2, T3, T4, T5, T6>(
+ Expression<Func<T1, T2, T3, T4, T5, T6, IQueryable<T>>> query)
+ {
+ IgniteArgumentCheck.NotNull(query, "query");
+
+ var compiledQuery = GetCompiledQuery<T>(query, query.Compile());
+
+ return (x, y, z, a, b, c) => compiledQuery(new object[] {x, y, z, a, b, c});
+ }
+
+ /// <summary>
+ /// Creates a new delegate that represents the compiled cache query.
+ /// </summary>
+ /// <param name="query">The query to compile.</param>
+ /// <returns>Delegate that represents the compiled cache query.</returns>
+ [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0",
+ Justification = "Invalid warning, validation is present.")]
+ public static Func<T1, T2, T3, T4, T5, T6, T7, IQueryCursor<T>> Compile<T, T1, T2, T3, T4, T5, T6, T7>(
+ Expression<Func<T1, T2, T3, T4, T5, T6, T7, IQueryable<T>>> query)
+ {
+ IgniteArgumentCheck.NotNull(query, "query");
+
+ var compiledQuery = GetCompiledQuery<T>(query, query.Compile());
+
+ return (x, y, z, a, b, c, d) => compiledQuery(new object[] {x, y, z, a, b, c, d});
+ }
+
+ /// <summary>
+ /// Creates a new delegate that represents the compiled cache query.
+ /// </summary>
+ /// <param name="query">The query to compile.</param>
+ /// <returns>Delegate that represents the compiled cache query.</returns>
+ [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0",
+ Justification = "Invalid warning, validation is present.")]
+ public static Func<T1, T2, T3, T4, T5, T6, T7, T8, IQueryCursor<T>> Compile<T, T1, T2, T3, T4, T5, T6, T7, T8>(
+ Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, IQueryable<T>>> query)
+ {
+ IgniteArgumentCheck.NotNull(query, "query");
+
+ var compiledQuery = GetCompiledQuery<T>(query, query.Compile());
+
+ return (x, y, z, a, b, c, d, e) => compiledQuery(new object[] {x, y, z, a, b, c, d, e});
+ }
+
+ /// <summary>
+ /// Gets the compiled query.
+ /// </summary>
+ private static Func<object[], IQueryCursor<T>> GetCompiledQuery<T>(LambdaExpression expression,
+ Delegate queryCaller)
+ {
+ Debug.Assert(expression != null);
+ Debug.Assert(queryCaller != null);
+
+ // Get default parameter values.
+ var paramValues = expression.Parameters
+ .Select(x => x.Type)
+ .Select(x => x.IsValueType ? Activator.CreateInstance(x) : null)
+ .ToArray();
+
+ // Invoke the delegate to obtain the cacheQueryable.
+ var queryable = queryCaller.DynamicInvoke(paramValues);
+
+ var cacheQueryable = queryable as ICacheQueryableInternal;
+
+ if (cacheQueryable == null)
+ throw GetInvalidQueryException(queryable);
+
+ return cacheQueryable.CompileQuery<T>(expression);
+ }
+
+ /// <summary>
+ /// Gets the invalid query exception.
+ /// </summary>
+ private static ArgumentException GetInvalidQueryException(object queryable)
+ {
+ return new ArgumentException(
+ string.Format("{0} can only compile cache queries produced by AsCacheQueryable method. " +
+ "Provided query is not valid: '{1}'", typeof(CompiledQuery2).FullName, queryable));
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/d6033712/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheFieldsQueryExecutor.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheFieldsQueryExecutor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheFieldsQueryExecutor.cs
index c715e4c..b4c3292 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheFieldsQueryExecutor.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheFieldsQueryExecutor.cs
@@ -92,7 +92,7 @@ namespace Apache.Ignite.Linq.Impl
Debug.WriteLine("\nFields Query: {0} | {1}", qryData.QueryText,
string.Join(", ", qryData.Parameters.Select(x => x == null ? "null" : x.ToString())));
- var qry = new SqlFieldsQuery(qryData.QueryText, _local, qryData.Parameters.ToArray());
+ var qry = GetFieldsQuery(qryData.QueryText, qryData.Parameters.ToArray());
var selector = GetResultSelector<T>(queryModel.SelectClause.Selector);
@@ -100,7 +100,7 @@ namespace Apache.Ignite.Linq.Impl
}
/// <summary>
- /// Compiles the query.
+ /// Compiles the query (old method, does not support some scenarios).
/// </summary>
public Func<object[], IQueryCursor<T>> CompileQuery<T>(QueryModel queryModel, Delegate queryCaller)
{
@@ -132,12 +132,120 @@ namespace Apache.Ignite.Linq.Impl
// Check if user param order is already correct
if (indices.SequenceEqual(Enumerable.Range(0, indices.Length)))
- return args => _cache.QueryFields(new SqlFieldsQuery(qryText, _local, args), selector);
+ return args => _cache.QueryFields(GetFieldsQuery(qryText, args), selector);
// Return delegate with reorder
- return args => _cache.QueryFields(new SqlFieldsQuery(qryText, _local,
+ return args => _cache.QueryFields(GetFieldsQuery(qryText,
args.Select((x, i) => args[indices[i]]).ToArray()), selector);
}
+ /// <summary>
+ /// Compiles the query without regard to number or order of arguments.
+ /// </summary>
+ public Func<object[], IQueryCursor<T>> CompileQuery<T>(QueryModel queryModel)
+ {
+ Debug.Assert(queryModel != null);
+
+ var qryText = GetQueryData(queryModel).QueryText;
+ var selector = GetResultSelector<T>(queryModel.SelectClause.Selector);
+
+ return args => _cache.QueryFields(GetFieldsQuery(qryText, args), selector);
+ }
+
+ /// <summary>
+ /// Compiles the query.
+ /// </summary>
+ /// <typeparam name="T">Result type.</typeparam>
+ /// <param name="queryModel">The query model.</param>
+ /// <param name="queryLambdaModel">The query model generated from lambda body.</param>
+ /// <param name="queryLambda">The query lambda.</param>
+ /// <returns>Compiled query func.</returns>
+ public Func<object[], IQueryCursor<T>> CompileQuery<T>(QueryModel queryModel, QueryModel queryLambdaModel,
+ LambdaExpression queryLambda)
+ {
+ Debug.Assert(queryModel != null);
+
+ // Get model from lambda to map arguments properly.
+ var qryData = GetQueryData(queryLambdaModel);
+
+ var qryText = GetQueryData(queryModel).QueryText;
+ var qryTextLambda = qryData.QueryText;
+
+ if (qryText != qryTextLambda)
+ {
+ Debug.WriteLine(qryText);
+ Debug.WriteLine(qryTextLambda);
+
+ throw new InvalidOperationException("Error compiling query: entire LINQ expression should be " +
+ "specified within lambda passed to Compile method. " +
+ "Part of the query can't be outside the Compile method call.");
+ }
+
+ var selector = GetResultSelector<T>(queryModel.SelectClause.Selector);
+
+ var qryParams = qryData.Parameters.ToArray();
+
+ // Compiled query is a delegate with query parameters
+ // Delegate parameters order and query parameters order may differ
+
+ // Simple case: lambda with no parameters. Only embedded parameters are used.
+ if (!queryLambda.Parameters.Any())
+ {
+ return argsUnused => _cache.QueryFields(GetFieldsQuery(qryText, qryParams), selector);
+ }
+
+ // These are in order of usage in query
+ var qryOrderArgs = qryParams.OfType<ParameterExpression>().Select(x => x.Name).ToArray();
+
+ // These are in order they come from user
+ var userOrderArgs = queryLambda.Parameters.Select(x => x.Name).ToList();
+
+ // Simple case: all query args directly map to the lambda args in the same order
+ if (qryOrderArgs.Length == qryParams.Length
+ && qryOrderArgs.SequenceEqual(userOrderArgs))
+ {
+ return args => _cache.QueryFields(GetFieldsQuery(qryText, args), selector);
+ }
+
+ // General case: embedded args and lambda args are mixed; same args can be used multiple times.
+ // Produce a mapping that defines where query arguments come from.
+ var mapping = qryParams.Select(x =>
+ {
+ var pe = x as ParameterExpression;
+
+ if (pe != null)
+ return userOrderArgs.IndexOf(pe.Name);
+
+ return -1;
+ }).ToArray();
+
+ return args => _cache.QueryFields(
+ GetFieldsQuery(qryText, MapQueryArgs(args, qryParams, mapping)), selector);
+ }
+
+ /// <summary>
+ /// Maps the query arguments.
+ /// </summary>
+ private static object[] MapQueryArgs(object[] userArgs, object[] embeddedArgs, int[] mapping)
+ {
+ var mappedArgs = new object[embeddedArgs.Length];
+
+ for (var i = 0; i < mappedArgs.Length; i++)
+ {
+ var map = mapping[i];
+
+ mappedArgs[i] = map < 0 ? embeddedArgs[i] : userArgs[map];
+ }
+
+ return mappedArgs;
+ }
+
+ /// <summary>
+ /// Gets the fields query.
+ /// </summary>
+ private SqlFieldsQuery GetFieldsQuery(string text, object[] args)
+ {
+ return new SqlFieldsQuery(text, _local, args);
+ }
/** <inheritdoc /> */
public static QueryData GetQueryData(QueryModel queryModel)
http://git-wip-us.apache.org/repos/asf/ignite/blob/d6033712/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs
index eaca07a..2c48554 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs
@@ -242,6 +242,21 @@ namespace Apache.Ignite.Linq.Impl
}
/** <inheritdoc /> */
+ public override Expression Visit(Expression expression)
+ {
+ var paramExpr = expression as ParameterExpression;
+
+ if (paramExpr != null)
+ {
+ // This happens only with compiled queries, where parameters come from enclosing lambda.
+ AppendParameter(paramExpr);
+ return expression;
+ }
+
+ return base.Visit(expression);
+ }
+
+ /** <inheritdoc /> */
protected override Expression VisitQuerySourceReference(QuerySourceReferenceExpression expression)
{
// Count, sum, max, min expect a single field or *
http://git-wip-us.apache.org/repos/asf/ignite/blob/d6033712/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs
index 1888414..51297ee 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs
@@ -273,6 +273,10 @@ namespace Apache.Ignite.Linq.Impl
// Workaround for unlimited offset (IGNITE-2602)
// H2 allows NULL & -1 for unlimited, but Ignite indexing does not
// Maximum limit that works is (int.MaxValue - offset)
+
+ if (offset.Count is ParameterExpression)
+ throw new NotSupportedException("Skip() without Take() is not supported in compiled queries.");
+
var offsetInt = (int) ((ConstantExpression) offset.Count).Value;
_builder.Append((int.MaxValue - offsetInt).ToString(CultureInfo.InvariantCulture));
}
@@ -424,17 +428,6 @@ namespace Apache.Ignite.Linq.Impl
}
else
{
- var innerExpr = joinClause.InnerSequence as ConstantExpression;
-
- if (innerExpr == null)
- throw new NotSupportedException("Unexpected JOIN inner sequence (subqueries are not supported): " +
- joinClause.InnerSequence);
-
- if (!(innerExpr.Value is ICacheQueryable))
- throw new NotSupportedException("Unexpected JOIN inner sequence " +
- "(only results of cache.ToQueryable() are supported): " +
- innerExpr.Value);
-
var queryable = ExpressionWalker.GetCacheQueryable(joinClause);
var tableName = ExpressionWalker.GetTableNameWithSchema(queryable);
_builder.AppendFormat("inner join {0} as {1} on (", tableName, _aliases.GetTableAlias(tableName));
http://git-wip-us.apache.org/repos/asf/ignite/blob/d6033712/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryableBase.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryableBase.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryableBase.cs
index d3115be..ee0239c 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryableBase.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryableBase.cs
@@ -89,6 +89,28 @@ namespace Apache.Ignite.Linq.Impl
return executor.CompileQuery<TQ>(GetQueryModel(), queryCaller);
}
+ /** <inheritdoc /> */
+ public Func<object[], IQueryCursor<TQ>> CompileQuery<TQ>(LambdaExpression queryExpression)
+ {
+ var executor = CacheQueryProvider.Executor;
+
+ // Generate two models: from current expression, and from provided lambda.
+ // Lambda expression provides a way to identify argument mapping.
+ // Comparing two models allows to check whether whole query is within lambda.
+ var model = GetQueryModel();
+ var lambdaModel = CacheQueryProvider.GenerateQueryModel(queryExpression.Body);
+
+ return executor.CompileQuery<TQ>(model, lambdaModel, queryExpression);
+ }
+
+ /** <inheritdoc /> */
+ public Func<object[], IQueryCursor<TQ>> CompileQuery<TQ>()
+ {
+ var executor = CacheQueryProvider.Executor;
+
+ return executor.CompileQuery<TQ>(GetQueryModel());
+ }
+
/// <summary>
/// Gets the cache query provider.
/// </summary>
http://git-wip-us.apache.org/repos/asf/ignite/blob/d6033712/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ExpressionWalker.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ExpressionWalker.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ExpressionWalker.cs
index 96371cc..f529da7 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ExpressionWalker.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ExpressionWalker.cs
@@ -118,6 +118,11 @@ namespace Apache.Ignite.Linq.Impl
/// </summary>
public static T EvaluateExpression<T>(Expression expr)
{
+ var constExpr = expr as ConstantExpression;
+
+ if (constExpr != null)
+ return (T)constExpr.Value;
+
var memberExpr = expr as MemberExpression;
if (memberExpr != null)
@@ -137,6 +142,10 @@ namespace Apache.Ignite.Linq.Impl
}
}
+ // Case for compiled queries: return unchanged.
+ if (expr is ParameterExpression)
+ return (T) (object) expr;
+
throw new NotSupportedException("Expression not supported: " + expr);
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/d6033712/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ICacheQueryProxy.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ICacheQueryProxy.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ICacheQueryProxy.cs
deleted file mode 100644
index b00937c..0000000
--- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ICacheQueryProxy.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-\ufeff/*
- * 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
-{
- using System;
- using Apache.Ignite.Core.Binary;
- using Apache.Ignite.Core.Cache.Query;
-
- /// <summary>
- /// Cache proxy interface that allows query execution without key/value generic arguments.
- /// </summary>
- internal interface ICacheQueryProxy
- {
- /// <summary>
- /// Queries separate entry fields.
- /// </summary>
- /// <typeparam name="T">Type of the result.</typeparam>
- /// <param name="qry">SQL fields query.</param>
- /// <param name="readerFunc">Reader function, takes raw reader and field count, returns typed result.</param>
- /// <returns>
- /// Cursor.
- /// </returns>
- IQueryCursor<T> QueryFields<T>(SqlFieldsQuery qry, Func<IBinaryRawReader, int, T> readerFunc);
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/d6033712/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ICacheQueryableInternal.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ICacheQueryableInternal.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ICacheQueryableInternal.cs
index 3e252c5..ffc81b4 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ICacheQueryableInternal.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ICacheQueryableInternal.cs
@@ -18,6 +18,7 @@
namespace Apache.Ignite.Linq.Impl
{
using System;
+ using System.Linq.Expressions;
using Apache.Ignite.Core.Cache.Configuration;
using Apache.Ignite.Core.Cache.Query;
using Remotion.Linq;
@@ -46,9 +47,20 @@ namespace Apache.Ignite.Linq.Impl
QueryModel GetQueryModel();
/// <summary>
- /// Compiles the query.
+ /// Compiles the query (the old way).
/// </summary>
/// <param name="queryCaller">Caller expression to examine argument order.</param>
Func<object[], IQueryCursor<T>> CompileQuery<T>(Delegate queryCaller);
+
+ /// <summary>
+ /// Compiles the query.
+ /// </summary>
+ /// <param name="queryExpression">The query expression.</param>
+ Func<object[], IQueryCursor<T>> CompileQuery<T>(LambdaExpression queryExpression);
+
+ /// <summary>
+ /// Compiles the query without regard to the order and number of arguments.
+ /// </summary>
+ Func<object[], IQueryCursor<T>> CompileQuery<T>();
}
}
\ No newline at end of file