Repository: ignite
Updated Branches:
  refs/heads/ignite-3560 3180190e4 -> 8d55120cb


IGNITE-3561 .NET: DistributedJoins property in SqlQuery & SqlFieldsQuery


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/856b536d
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/856b536d
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/856b536d

Branch: refs/heads/ignite-3560
Commit: 856b536db66edf9e7e6fc1b96da55a0e846c70c3
Parents: 37195a0
Author: Pavel Tupitsyn <ptupit...@apache.org>
Authored: Mon Jul 25 18:54:11 2016 +0300
Committer: Pavel Tupitsyn <ptupit...@apache.org>
Committed: Mon Jul 25 18:54:11 2016 +0300

----------------------------------------------------------------------
 .../org/apache/ignite/cache/query/SqlQuery.java |   3 +
 .../platform/cache/PlatformCache.java           |  10 +-
 .../core/include/ignite/cache/query/query_sql.h |   2 +
 .../ignite/cache/query/query_sql_fields.h       |   3 +
 .../Cache/Query/CacheLinqTest.cs                |  20 +-
 .../Cache/Query/CacheQueriesTest.cs             | 252 +++++--------------
 .../Cache/Query/SqlFieldsQuery.cs               |  27 +-
 .../Apache.Ignite.Core/Cache/Query/SqlQuery.cs  |  13 +
 .../Apache.Ignite.Core/Impl/Cache/CacheImpl.cs  |   3 +
 .../Apache.Ignite.Linq/CacheExtensions.cs       |  39 ++-
 .../Impl/CacheFieldsQueryExecutor.cs            |  63 ++++-
 .../Apache.Ignite.Linq/Impl/CacheQueryable.cs   |  11 +-
 .../Impl/CacheQueryableBase.cs                  |   7 +-
 13 files changed, 257 insertions(+), 196 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/856b536d/modules/core/src/main/java/org/apache/ignite/cache/query/SqlQuery.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/cache/query/SqlQuery.java 
b/modules/core/src/main/java/org/apache/ignite/cache/query/SqlQuery.java
index e05ff13..f809b8d 100644
--- a/modules/core/src/main/java/org/apache/ignite/cache/query/SqlQuery.java
+++ b/modules/core/src/main/java/org/apache/ignite/cache/query/SqlQuery.java
@@ -154,6 +154,9 @@ public final class SqlQuery<K, V> extends 
Query<Cache.Entry<K, V>> {
     /**
      * Specify if distributed joins are enabled for this query.
      *
+     * When disabled, join results will only contain colocated data (joins 
work locally).
+     * When enabled, joins work as expected, no matter how the data is 
distributed.
+     *
      * @param distributedJoins Distributed joins enabled.
      * @return {@code this} For chaining.
      */

http://git-wip-us.apache.org/repos/asf/ignite/blob/856b536d/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cache/PlatformCache.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cache/PlatformCache.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cache/PlatformCache.java
index 9bf330c..d572e8b 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cache/PlatformCache.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cache/PlatformCache.java
@@ -943,7 +943,9 @@ public class PlatformCache extends PlatformAbstractTarget {
 
         Object[] args = readQueryArgs(reader);
 
-        return new SqlQuery(typ, 
sql).setPageSize(pageSize).setArgs(args).setLocal(loc);
+        boolean distrJoins = reader.readBoolean();
+
+        return new SqlQuery(typ, 
sql).setPageSize(pageSize).setArgs(args).setLocal(loc).setDistributedJoins(distrJoins);
     }
 
     /**
@@ -956,7 +958,11 @@ public class PlatformCache extends PlatformAbstractTarget {
 
         Object[] args = readQueryArgs(reader);
 
-        return new 
SqlFieldsQuery(sql).setPageSize(pageSize).setArgs(args).setLocal(loc);
+        boolean distrJoins = reader.readBoolean();
+        boolean enforceJoinOrder = reader.readBoolean();
+
+        return new 
SqlFieldsQuery(sql).setPageSize(pageSize).setArgs(args).setLocal(loc)
+            
.setDistributedJoins(distrJoins).setEnforceJoinOrder(enforceJoinOrder);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/856b536d/modules/platforms/cpp/core/include/ignite/cache/query/query_sql.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/core/include/ignite/cache/query/query_sql.h 
b/modules/platforms/cpp/core/include/ignite/cache/query/query_sql.h
index f7a00fa..cb7a739 100644
--- a/modules/platforms/cpp/core/include/ignite/cache/query/query_sql.h
+++ b/modules/platforms/cpp/core/include/ignite/cache/query/query_sql.h
@@ -228,6 +228,8 @@ namespace ignite
 
                     for (std::vector<QueryArgumentBase*>::const_iterator it = 
args.begin(); it != args.end(); ++it)
                         (*it)->Write(writer);
+
+                    writer.WriteBool(false);  // distributed joins
                 }
 
             private:

http://git-wip-us.apache.org/repos/asf/ignite/blob/856b536d/modules/platforms/cpp/core/include/ignite/cache/query/query_sql_fields.h
----------------------------------------------------------------------
diff --git 
a/modules/platforms/cpp/core/include/ignite/cache/query/query_sql_fields.h 
b/modules/platforms/cpp/core/include/ignite/cache/query/query_sql_fields.h
index e21fc93..1c8570b 100644
--- a/modules/platforms/cpp/core/include/ignite/cache/query/query_sql_fields.h
+++ b/modules/platforms/cpp/core/include/ignite/cache/query/query_sql_fields.h
@@ -200,6 +200,9 @@ namespace ignite
 
                     for (std::vector<QueryArgumentBase*>::const_iterator it = 
args.begin(); it != args.end(); ++it)
                         (*it)->Write(writer);
+
+                    writer.WriteBool(false);  // distributed joins
+                    writer.WriteBool(false);  // enforce join order
                 }
 
             private:

http://git-wip-us.apache.org/repos/asf/ignite/blob/856b536d/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 08a4bdc..f76a74c 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
@@ -35,6 +35,8 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
     using Apache.Ignite.Core.Binary;
     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;
 
@@ -953,7 +955,7 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
             var cache = GetPersonCache();
 
             // Check regular query
-            var query = (ICacheQueryable) cache.AsCacheQueryable(true).Where(x 
=> x.Key > 10);
+            var query = (ICacheQueryable) cache.AsCacheQueryable(true, null, 
999, false, true).Where(x => x.Key > 10);
 
             Assert.AreEqual(cache.Name, query.CacheName);
             Assert.AreEqual(cache.Ignite, query.Ignite);
@@ -963,6 +965,9 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
             Assert.AreEqual(new[] {10}, fq.Arguments);
             Assert.IsTrue(fq.Local);
             Assert.AreEqual(PersonCount - 11, 
cache.QueryFields(fq).GetAll().Count);
+            Assert.AreEqual(999, fq.PageSize);
+            Assert.IsFalse(fq.EnableDistributedJoins);
+            Assert.IsTrue(fq.EnforceJoinOrder);
 
             // Check fields query
             var fieldsQuery = (ICacheQueryable) 
cache.AsCacheQueryable().Select(x => x.Value.Name);
@@ -973,6 +978,19 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
             fq = fieldsQuery.GetFieldsQuery();
             Assert.AreEqual("select _T0.Name from \"\".Person as _T0", fq.Sql);
             Assert.IsFalse(fq.Local);
+            Assert.AreEqual(SqlFieldsQuery.DfltPageSize, fq.PageSize);
+            Assert.IsFalse(fq.EnableDistributedJoins);
+            Assert.IsFalse(fq.EnforceJoinOrder);
+
+            // Check distributed joins flag propagation
+            var distrQuery = cache.AsCacheQueryable(true, null, 999, true, 
true).Where(x => x.Key > 10);
+            query = (ICacheQueryable) distrQuery;
+            Assert.IsTrue(query.GetFieldsQuery().EnableDistributedJoins);
+
+            // Easy check that EnableDistributedJoins is propagated to Java: 
it throws an error on replicated cache
+            var ex = Assert.Throws<IgniteException>(() => Assert.AreEqual(0, 
distrQuery.ToArray().Length));
+            Assert.AreEqual("Queries using distributed JOINs have to be run on 
partitioned cache, not on replicated.",
+                ex.Message);
         }
 
         /// <summary>

http://git-wip-us.apache.org/repos/asf/ignite/blob/856b536d/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheQueriesTest.cs
----------------------------------------------------------------------
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheQueriesTest.cs
 
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheQueriesTest.cs
index 6ed76678..7bfd202 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheQueriesTest.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheQueriesTest.cs
@@ -25,6 +25,7 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
     using System.Text;
     using Apache.Ignite.Core.Binary;
     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.Core.Impl.Binary;
@@ -33,7 +34,7 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
     /// <summary>
     /// Queries tests.
     /// </summary>
-    public class CacheQueriesTest
+    public sealed class CacheQueriesTest
     {
         /** Grid count. */
         private const int GridCnt = 2;
@@ -51,7 +52,7 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
         /// 
         /// </summary>
         [TestFixtureSetUp]
-        public virtual void StartGrids()
+        public void StartGrids()
         {
             TestUtils.JvmDebug = true;
             TestUtils.KillProcesses();
@@ -84,7 +85,7 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
         /// 
         /// </summary>
         [TestFixtureTearDown]
-        public virtual void StopGrids()
+        public void StopGrids()
         {
             for (int i = 0; i < GridCnt; i++)
                 Ignition.Stop("grid-" + i, true);
@@ -94,7 +95,7 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
         /// 
         /// </summary>
         [SetUp]
-        public virtual void BeforeTest()
+        public void BeforeTest()
         {
             Console.WriteLine("Test started: " + 
TestContext.CurrentContext.Test.Name);
         }
@@ -103,7 +104,7 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
         /// 
         /// </summary>
         [TearDown]
-        public virtual void AfterTest()
+        public void AfterTest()
         {
             var cache = Cache();
 
@@ -126,7 +127,7 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
         /// </summary>
         /// <param name="idx"></param>
         /// <returns></returns>
-        public IIgnite GetIgnite(int idx)
+        private IIgnite GetIgnite(int idx)
         {
             return Ignition.GetIgnite("grid-" + idx);
         }
@@ -136,7 +137,7 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
         /// </summary>
         /// <param name="idx"></param>
         /// <returns></returns>
-        public ICache<int, QueryPerson> Cache(int idx)
+        private ICache<int, QueryPerson> Cache(int idx)
         {
             return GetIgnite(idx).GetCache<int, QueryPerson>(CacheName);
         }
@@ -145,7 +146,7 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
         /// 
         /// </summary>
         /// <returns></returns>
-        public ICache<int, QueryPerson> Cache()
+        private ICache<int, QueryPerson> Cache()
         {
             return Cache(0);
         }
@@ -270,6 +271,7 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
         /// <summary>
         /// Test SQL query arguments passing.
         /// </summary>
+        [Test]
         public void TestSqlQueryArguments()
         {
             Cache().Put(1, new QueryPerson("Ivanov", 30));
@@ -289,6 +291,7 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
         /// <summary>
         /// Test SQL fields query arguments passing.
         /// </summary>
+        [Test]
         public void TestSqlFieldsQueryArguments()
         {
             Cache().Put(1, new QueryPerson("Ivanov", 30));
@@ -352,53 +355,19 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
         /// Check SQL query.
         /// </summary>
         [Test]
-        public void TestSqlQuery()
-        {
-            CheckSqlQuery(MaxItemCnt, false, false);
-        }
-
-        /// <summary>
-        /// Check SQL query in binary mode.
-        /// </summary>
-        [Test]
-        public void TestSqlQueryBinary()
-        {
-            CheckSqlQuery(MaxItemCnt, false, true);
-        }
-
-        /// <summary>
-        /// Check local SQL query.
-        /// </summary>
-        [Test]
-        public void TestSqlQueryLocal()
-        {
-            CheckSqlQuery(MaxItemCnt, true, false);
-        }
-
-        /// <summary>
-        /// Check local SQL query in binary mode.
-        /// </summary>
-        [Test]
-        public void TestSqlQueryLocalBinary()
-        {
-            CheckSqlQuery(MaxItemCnt, true, true);
-        }
-
-        /// <summary>
-        /// Check SQL query.
-        /// </summary>
-        /// <param name="cnt">Amount of cache entries to create.</param>
-        /// <param name="loc">Local query flag.</param>
-        /// <param name="keepBinary">Keep binary flag.</param>
-        private void CheckSqlQuery(int cnt, bool loc, bool keepBinary)
+        public void TestSqlQuery([Values(true, false)] bool loc, [Values(true, 
false)] bool keepBinary, 
+            [Values(true, false)] bool distrJoin)
         {
             var cache = Cache();
 
             // 1. Populate cache with data, calculating expected count in 
parallel.
-            var exp = PopulateCache(cache, loc, cnt, x => x < 50);
+            var exp = PopulateCache(cache, loc, MaxItemCnt, x => x < 50);
 
             // 2. Validate results.
-            var qry = new SqlQuery(typeof(QueryPerson), "age < 50", loc);
+            var qry = new SqlQuery(typeof(QueryPerson), "age < 50", loc)
+            {
+                EnableDistributedJoins = distrJoin
+            };
 
             ValidateQueryResults(cache, qry, exp, keepBinary);
         }
@@ -407,35 +376,22 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
         /// Check SQL fields query.
         /// </summary>
         [Test]
-        public void TestSqlFieldsQuery()
+        public void TestSqlFieldsQuery([Values(true, false)] bool loc, 
[Values(true, false)] bool distrJoin, 
+            [Values(true, false)] bool enforceJoinOrder)
         {
-            CheckSqlFieldsQuery(MaxItemCnt, false);
-        }
+            int cnt = MaxItemCnt;
 
-        /// <summary>
-        /// Check local SQL fields query.
-        /// </summary>
-        [Test]
-        public void TestSqlFieldsQueryLocal()
-        {
-            CheckSqlFieldsQuery(MaxItemCnt, true);
-        }
-
-        /// <summary>
-        /// Check SQL fields query.
-        /// </summary>
-        /// <param name="cnt">Amount of cache entries to create.</param>
-        /// <param name="loc">Local query flag.</param>
-        private void CheckSqlFieldsQuery(int cnt, bool loc)
-        {
             var cache = Cache();
 
             // 1. Populate cache with data, calculating expected count in 
parallel.
             var exp = PopulateCache(cache, loc, cnt, x => x < 50);
 
-            // 2. Vlaidate results.
-            SqlFieldsQuery qry = loc ? new SqlFieldsQuery("SELECT name, age 
FROM QueryPerson WHERE age < 50", true) :
-                new SqlFieldsQuery("SELECT name, age FROM QueryPerson WHERE 
age < 50");
+            // 2. Validate results.
+            var qry = new SqlFieldsQuery("SELECT name, age FROM QueryPerson 
WHERE age < 50", loc)
+            {
+                EnableDistributedJoins = distrJoin,
+                EnforceJoinOrder = enforceJoinOrder
+            };
 
             using (IQueryCursor<IList> cursor = cache.QueryFields(qry))
             {
@@ -471,113 +427,20 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
         /// Check text query.
         /// </summary>
         [Test]
-        public void TestTextQuery()
-        {
-            CheckTextQuery(MaxItemCnt, false, false);
-        }
-
-        /// <summary>
-        /// Check SQL query in binary mode.
-        /// </summary>
-        [Test]
-        public void TestTextQueryBinary()
-        {
-            CheckTextQuery(MaxItemCnt, false, true);
-        }
-
-        /// <summary>
-        /// Check local SQL query.
-        /// </summary>
-        [Test]
-        public void TestTextQueryLocal()
-        {
-            CheckTextQuery(MaxItemCnt, true, false);
-        }
-
-        /// <summary>
-        /// Check local SQL query in binary mode.
-        /// </summary>
-        [Test]
-        public void TestTextQueryLocalBinary()
-        {
-            CheckTextQuery(MaxItemCnt, true, true);
-        }
-
-        /// <summary>
-        /// Check text query.
-        /// </summary>
-        /// <param name="cnt">Amount of cache entries to create.</param>
-        /// <param name="loc">Local query flag.</param>
-        /// <param name="keepBinary">Keep binary flag.</param>
-        private void CheckTextQuery(int cnt, bool loc, bool keepBinary)
+        public void TestTextQuery([Values(true, false)] bool loc, 
[Values(true, false)] bool keepBinary)
         {
             var cache = Cache();
 
             // 1. Populate cache with data, calculating expected count in 
parallel.
-            var exp = PopulateCache(cache, loc, cnt, x => 
x.ToString().StartsWith("1"));
+            var exp = PopulateCache(cache, loc, MaxItemCnt, x => 
x.ToString().StartsWith("1"));
 
             // 2. Validate results.
-            TextQuery qry = loc ? new TextQuery(typeof(QueryPerson), "1*", 
true) :
-                new TextQuery(typeof(QueryPerson), "1*");
+            var qry = new TextQuery(typeof(QueryPerson), "1*", loc);
 
             ValidateQueryResults(cache, qry, exp, keepBinary);
         }
 
         /// <summary>
-        /// Check scan query.
-        /// </summary>
-        [Test]
-        public void TestScanQuery()
-        {
-            CheckScanQuery<QueryPerson>(MaxItemCnt, false, false);
-        }
-
-        /// <summary>
-        /// Check scan query in binary mode.
-        /// </summary>
-        [Test]
-        public void TestScanQueryBinary()
-        {
-            CheckScanQuery<BinaryObject>(MaxItemCnt, false, true);
-        }
-
-        /// <summary>
-        /// Check local scan query.
-        /// </summary>
-        [Test]
-        public void TestScanQueryLocal()
-        {
-            CheckScanQuery<QueryPerson>(MaxItemCnt, true, false);
-        }
-
-        /// <summary>
-        /// Check local scan query in binary mode.
-        /// </summary>
-        [Test]
-        public void TestScanQueryLocalBinary()
-        {
-            CheckScanQuery<BinaryObject>(MaxItemCnt, true, true);
-        }
-
-        /// <summary>
-        /// Check scan query with partitions.
-        /// </summary>
-        [Test]
-        public void TestScanQueryPartitions([Values(true, false)]  bool loc)
-        {
-            CheckScanQueryPartitions<QueryPerson>(MaxItemCnt, loc, false);
-        }
-
-        /// <summary>
-        /// Check scan query with partitions in binary mode.
-        /// </summary>
-        [Test]
-        public void TestScanQueryPartitionsBinary([Values(true, false)]  bool 
loc)
-        {
-            CheckScanQueryPartitions<BinaryObject>(MaxItemCnt, loc, true);
-        }
-
-        /// <summary>
         /// Tests that query attempt on non-indexed cache causes an exception.
         /// </summary>
         [Test]
@@ -603,12 +466,11 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
         /// <summary>
         /// Check scan query.
         /// </summary>
-        /// <param name="cnt">Amount of cache entries to create.</param>
-        /// <param name="loc">Local query flag.</param>
-        /// <param name="keepBinary">Keep binary flag.</param>
-        private void CheckScanQuery<TV>(int cnt, bool loc, bool keepBinary)
+        [Test]
+        public void TestScanQuery<TV>([Values(true, false)] bool loc, 
[Values(true, false)] bool keepBinary)
         {
             var cache = Cache();
+            var cnt = MaxItemCnt;
 
             // No predicate
             var exp = PopulateCache(cache, loc, cnt, x => true);
@@ -641,15 +503,14 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
         /// <summary>
         /// Checks scan query with partitions.
         /// </summary>
-        /// <param name="cnt">Amount of cache entries to create.</param>
-        /// <param name="loc">Local query flag.</param>
-        /// <param name="keepBinary">Keep binary flag.</param>
-        private void CheckScanQueryPartitions<TV>(int cnt, bool loc, bool 
keepBinary)
+        [Test]
+        public void TestScanQueryPartitions<TV>([Values(true, false)] bool 
loc, [Values(true, false)] bool keepBinary)
         {
             StopGrids();
             StartGrids();
 
             var cache = Cache();
+            var cnt = MaxItemCnt;
 
             var aff = cache.Ignite.GetAffinity(CacheName);
             var exp = PopulateCache(cache, loc, cnt, x => true);  // populate 
outside the loop (slow)
@@ -686,6 +547,39 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
         }
 
         /// <summary>
+        /// Tests the distributed joins flag.
+        /// </summary>
+        [Test]
+        public void TestDistributedJoins()
+        {
+            // Easy check that EnableDistributedJoins is propagated to Java: 
it throws an error on replicated cache
+            var cache = GetIgnite(0).GetOrCreateCache<int, QueryPerson>(
+                new CacheConfiguration("replicatedCache")
+                {
+                    CacheMode = CacheMode.Replicated,
+                    QueryEntities = new[]
+                    {
+                        new QueryEntity(typeof(QueryPerson))
+                        {
+                            Fields = new[] {new QueryField("age", typeof(int))}
+                        }
+                    }
+                });
+
+            cache[1] = new QueryPerson("Test", 150);
+
+            // Distributed joins disabled: query works
+            var qry = new SqlQuery(typeof(QueryPerson), "age < 50");
+            Assert.AreEqual(0, cache.Query(qry).GetAll().Count);
+
+            // Distributed joins enabled: query fails
+            qry.EnableDistributedJoins = true;
+            var ex = Assert.Throws<IgniteException>(() => Assert.AreEqual(0, 
cache.Query(qry).GetAll().Count));
+            Assert.AreEqual("Queries using distributed JOINs have to be run on 
partitioned cache, not on replicated.",
+                ex.Message);
+        }
+
+        /// <summary>
         /// Validates the query results.
         /// </summary>
         /// <param name="cache">Cache.</param>
@@ -853,14 +747,6 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
         /// <summary>
         /// Constructor.
         /// </summary>
-        public QueryPerson()
-        {
-            // No-op.
-        }
-
-        /// <summary>
-        /// Constructor.
-        /// </summary>
         /// <param name="name">Name.</param>
         /// <param name="age">Age.</param>
         public QueryPerson(string name, int age)

http://git-wip-us.apache.org/repos/asf/ignite/blob/856b536d/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Query/SqlFieldsQuery.cs
----------------------------------------------------------------------
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Query/SqlFieldsQuery.cs 
b/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Query/SqlFieldsQuery.cs
index 1753a8b..ed9d0eb 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Query/SqlFieldsQuery.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Query/SqlFieldsQuery.cs
@@ -74,8 +74,33 @@ namespace Apache.Ignite.Core.Cache.Query
         /// <summary>
         /// Optional page size.
         /// <para />
-        /// Defautls to <see cref="DfltPageSize"/>.
+        /// Defaults to <see cref="DfltPageSize"/>.
         /// </summary>
         public int PageSize { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether distributed joins should 
be enabled for this query.
+        /// <para />
+        /// When disabled, join results will only contain colocated data 
(joins work locally).
+        /// When enabled, joins work as expected, no matter how the data is 
distributed.
+        /// </summary>
+        /// <value>
+        /// <c>true</c> if enable distributed joins should be enabled; 
otherwise, <c>false</c>.
+        /// </value>
+        public bool EnableDistributedJoins { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether join order of tables 
should be enforced.
+        /// <para />
+        /// When true, query optimizer will not reorder tables in join.
+        /// <para />
+        /// It is not recommended to enable this property until you are sure 
that your indexes
+        /// and the query itself are correct and tuned as much as possible but
+        /// query optimizer still produces wrong join order.
+        /// </summary>
+        /// <value>
+        ///   <c>true</c> if join order should be enforced; otherwise, 
<c>false</c>.
+        /// </value>
+        public bool EnforceJoinOrder { get; set; }
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/856b536d/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Query/SqlQuery.cs
----------------------------------------------------------------------
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Query/SqlQuery.cs 
b/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Query/SqlQuery.cs
index 0e3c887..70e08b2 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Query/SqlQuery.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Query/SqlQuery.cs
@@ -97,6 +97,17 @@ namespace Apache.Ignite.Core.Cache.Query
         [SuppressMessage("Microsoft.Performance", 
"CA1819:PropertiesShouldNotReturnArrays")]
         public object[] Arguments { get; set; }
 
+        /// <summary>
+        /// Gets or sets a value indicating whether distributed joins should 
be enabled for this query.
+        /// <para />
+        /// When disabled, join results will only contain colocated data 
(joins work locally).
+        /// When enabled, joins work as expected, no matter how the data is 
distributed.
+        /// </summary>
+        /// <value>
+        /// <c>true</c> if enable distributed joins should be enabled; 
otherwise, <c>false</c>.
+        /// </value>
+        public bool EnableDistributedJoins { get; set; }
+
         /** <inheritDoc /> */
         internal override void Write(BinaryWriter writer, bool keepBinary)
         {
@@ -113,6 +124,8 @@ namespace Apache.Ignite.Core.Cache.Query
             writer.WriteInt(PageSize);
 
             WriteQueryArgs(writer, Arguments);
+
+            writer.WriteBoolean(EnableDistributedJoins);
         }
 
         /** <inheritDoc /> */

http://git-wip-us.apache.org/repos/asf/ignite/blob/856b536d/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheImpl.cs
----------------------------------------------------------------------
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheImpl.cs 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheImpl.cs
index 6afbc67..32c59de 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheImpl.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheImpl.cs
@@ -977,6 +977,9 @@ namespace Apache.Ignite.Core.Impl.Cache
 
                 WriteQueryArgs(writer, qry.Arguments);
 
+                writer.WriteBoolean(qry.EnableDistributedJoins);
+                writer.WriteBoolean(qry.EnforceJoinOrder);
+
                 FinishMarshal(writer);
 
                 cursor = UU.CacheOutOpQueryCursor(Target, (int) 
CacheOp.QrySqlFields, stream.SynchronizeOutput());

http://git-wip-us.apache.org/repos/asf/ignite/blob/856b536d/modules/platforms/dotnet/Apache.Ignite.Linq/CacheExtensions.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheExtensions.cs 
b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheExtensions.cs
index ecea4ed..e6d585c 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheExtensions.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheExtensions.cs
@@ -20,6 +20,7 @@ namespace Apache.Ignite.Linq
     using System.Linq;
     using Apache.Ignite.Core.Cache;
     using Apache.Ignite.Core.Cache.Configuration;
+    using Apache.Ignite.Core.Cache.Query;
     using Apache.Ignite.Linq.Impl;
 
     /// <summary>
@@ -92,7 +93,43 @@ namespace Apache.Ignite.Linq
         public static IQueryable<ICacheEntry<TKey, TValue>> 
AsCacheQueryable<TKey, TValue>(
             this ICache<TKey, TValue> cache, bool local, string tableName)
         {
-            return new CacheQueryable<TKey, TValue>(cache, local, tableName);
+            return cache.AsCacheQueryable(local, tableName, 
SqlFieldsQuery.DfltPageSize, false, false);
+        }
+
+        /// <summary>
+        /// Gets an <see cref="IQueryable{T}" /> instance over this cache.
+        /// <para />
+        /// Resulting query will be translated to cache SQL query and executed 
over the cache instance
+        /// via either <see cref="ICache{TK,TV}.Query" /> or <see 
cref="ICache{TK,TV}.QueryFields" />,
+        /// depending on requested result.
+        /// <para />
+        /// Result of this method (and subsequent query) can be cast to <see 
cref="ICacheQueryable" /> for introspection.
+        /// </summary>
+        /// <typeparam name="TKey">The type of the key.</typeparam>
+        /// <typeparam name="TValue">The type of the value.</typeparam>
+        /// <param name="cache">The cache.</param>
+        /// <param name="local">Local flag. When set query will be executed 
only on local node, so only local
+        /// entries will be returned as query result.</param>
+        /// <param name="tableName">Name of the table.
+        /// <para />
+        /// Table name is equal to short class name of a cache value.
+        /// When a cache has only one type of values, or only one <see 
cref="QueryEntity" /> defined,
+        /// table name will be inferred and can be omitted.</param>
+        /// <param name="pageSize">Query cursor page size. 
+        /// Defaults to <see cref="SqlFieldsQuery.DfltPageSize"/>.</param>
+        /// <param name="enableDistributedJoins">Distributed joins option, see
+        /// <see cref="SqlFieldsQuery.EnableDistributedJoins" />.</param>
+        /// <param name="enforceJoinOrder">Enforce join order flag,
+        /// see <see cref="SqlFieldsQuery.EnforceJoinOrder" />.</param>
+        /// <returns>
+        ///   <see cref="IQueryable{T}" /> instance over this cache.
+        /// </returns>
+        public static IQueryable<ICacheEntry<TKey, TValue>> 
AsCacheQueryable<TKey, TValue>(
+            this ICache<TKey, TValue> cache, bool local, string tableName, int 
pageSize,
+            bool enableDistributedJoins, bool enforceJoinOrder)
+        {
+            return new CacheQueryable<TKey, TValue>(cache, local, tableName, 
pageSize, enableDistributedJoins,
+                enforceJoinOrder);
         }
     }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/856b536d/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..7464a03 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheFieldsQueryExecutor.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheFieldsQueryExecutor.cs
@@ -46,17 +46,33 @@ namespace Apache.Ignite.Linq.Impl
         /** */
         private readonly bool _local;
 
+        /** */
+        private readonly int _pageSize;
+
+        /** */
+        private readonly bool _enableDistributedJoins;
+
+        /** */
+        private readonly bool _enforceJoinOrder;
+
         /// <summary>
         /// Initializes a new instance of the <see 
cref="CacheFieldsQueryExecutor" /> class.
         /// </summary>
         /// <param name="cache">The executor function.</param>
         /// <param name="local">Local flag.</param>
-        public CacheFieldsQueryExecutor(ICacheInternal cache, bool local)
+        /// <param name="pageSize">Size of the page.</param>
+        /// <param name="enableDistributedJoins">Distributed joins 
flag.</param>
+        /// <param name="enforceJoinOrder">Enforce join order flag.</param>
+        public CacheFieldsQueryExecutor(ICacheInternal cache, bool local, int 
pageSize, bool enableDistributedJoins, 
+            bool enforceJoinOrder)
         {
             Debug.Assert(cache != null);
 
             _cache = cache;
             _local = local;
+            _pageSize = pageSize;
+            _enableDistributedJoins = enableDistributedJoins;
+            _enforceJoinOrder = enforceJoinOrder;
         }
 
         /// <summary>
@@ -67,6 +83,30 @@ namespace Apache.Ignite.Linq.Impl
             get { return _local; }
         }
 
+        /// <summary>
+        /// Gets the size of the page.
+        /// </summary>
+        public int PageSize
+        {
+            get { return _pageSize; }
+        }
+
+        /// <summary>
+        /// Gets a value indicating whether distributed joins are enabled.
+        /// </summary>
+        public bool EnableDistributedJoins
+        {
+            get { return _enableDistributedJoins; }
+        }
+
+        /// <summary>
+        /// Gets a value indicating whether join order should be enforced.
+        /// </summary>
+        public bool EnforceJoinOrder
+        {
+            get { return _enforceJoinOrder; }
+        }
+
         /** <inheritdoc /> */
         public T ExecuteScalar<T>(QueryModel queryModel)
         {
@@ -92,7 +132,12 @@ 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 = new SqlFieldsQuery(qryData.QueryText, _local, 
qryData.Parameters.ToArray())
+            {
+                EnableDistributedJoins = _enableDistributedJoins,
+                PageSize = _pageSize,
+                EnforceJoinOrder = _enforceJoinOrder
+            };
 
             var selector = 
GetResultSelector<T>(queryModel.SelectClause.Selector);
 
@@ -132,11 +177,21 @@ 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(new SqlFieldsQuery(qryText, 
_local, args)
+                {
+                    EnableDistributedJoins = _enableDistributedJoins,
+                    PageSize = _pageSize,
+                    EnforceJoinOrder = _enforceJoinOrder
+                }, selector);
 
             // Return delegate with reorder
             return args => _cache.QueryFields(new SqlFieldsQuery(qryText, 
_local,
-                args.Select((x, i) => args[indices[i]]).ToArray()), selector);
+                args.Select((x, i) => args[indices[i]]).ToArray())
+            {
+                EnableDistributedJoins = _enableDistributedJoins,
+                PageSize = _pageSize,
+                EnforceJoinOrder = _enforceJoinOrder
+            }, selector);
         }
 
         /** <inheritdoc /> */

http://git-wip-us.apache.org/repos/asf/ignite/blob/856b536d/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryable.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryable.cs 
b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryable.cs
index 959cc4b..7ade159 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryable.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryable.cs
@@ -32,10 +32,15 @@ namespace Apache.Ignite.Linq.Impl
         /// <param name="cache">The cache.</param>
         /// <param name="local">Local flag.</param>
         /// <param name="tableName">Name of the table.</param>
-        public CacheQueryable(ICache<TKey, TValue> cache, bool local, string 
tableName)
+        /// <param name="pageSize">Size of the page.</param>
+        /// <param name="enableDistributedJoins">Distributed joins 
flag.</param>
+        /// <param name="enforceJoinOrder">Enforce join order flag.</param>
+        public CacheQueryable(ICache<TKey, TValue> cache, bool local, string 
tableName, int pageSize,
+            bool enableDistributedJoins,
+            bool enforceJoinOrder)
             : base(new CacheFieldsQueryProvider(CacheQueryParser.Instance,
-                new CacheFieldsQueryExecutor((ICacheInternal) cache, local),
-                cache.Ignite, cache.GetConfiguration(), tableName, 
typeof(TValue)))
+                new CacheFieldsQueryExecutor((ICacheInternal) cache, local, 
pageSize, enableDistributedJoins,
+                    enforceJoinOrder), cache.Ignite, cache.GetConfiguration(), 
tableName, typeof(TValue)))
         {
             // No-op.
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/856b536d/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..4b461bf 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryableBase.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryableBase.cs
@@ -66,7 +66,12 @@ namespace Apache.Ignite.Linq.Impl
             var data = GetQueryData();
             var executor = CacheQueryProvider.Executor;
 
-            return new SqlFieldsQuery(data.QueryText, executor.Local, 
data.Parameters.ToArray());
+            return new SqlFieldsQuery(data.QueryText, executor.Local, 
data.Parameters.ToArray())
+            {
+                EnableDistributedJoins = executor.EnableDistributedJoins,
+                EnforceJoinOrder = executor.EnforceJoinOrder,
+                PageSize = executor.PageSize
+            };
         }
 
         /** <inheritdoc /> */

Reply via email to