You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by vo...@apache.org on 2016/07/28 11:06:26 UTC

[01/50] [abbrv] ignite git commit: IGNITE-466: IGFS: Added "IgfsMode mode(IgfsPath)" method.

Repository: ignite
Updated Branches:
  refs/heads/ignite-3553 b77bee0fe -> 7d05b6500


IGNITE-466: IGFS: Added "IgfsMode mode(IgfsPath)" method.


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

Branch: refs/heads/ignite-3553
Commit: f7e8a6d66466fb561b5e2aa49fc45db7958f98fb
Parents: 80b3c4c
Author: vozerov-gridgain <vo...@gridgain.com>
Authored: Thu Jul 21 16:00:45 2016 +0300
Committer: vozerov-gridgain <vo...@gridgain.com>
Committed: Thu Jul 21 16:00:45 2016 +0300

----------------------------------------------------------------------
 .../src/main/java/org/apache/ignite/IgniteFileSystem.java   | 9 +++++++++
 .../ignite/internal/processors/igfs/IgfsAsyncImpl.java      | 6 ++++++
 .../apache/ignite/internal/processors/igfs/IgfsImpl.java    | 7 +++++++
 .../apache/ignite/internal/processors/igfs/IgfsMock.java    | 8 ++++++++
 4 files changed, 30 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/f7e8a6d6/modules/core/src/main/java/org/apache/ignite/IgniteFileSystem.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/IgniteFileSystem.java b/modules/core/src/main/java/org/apache/ignite/IgniteFileSystem.java
index def2965..bbc5bea 100644
--- a/modules/core/src/main/java/org/apache/ignite/IgniteFileSystem.java
+++ b/modules/core/src/main/java/org/apache/ignite/IgniteFileSystem.java
@@ -24,6 +24,7 @@ import org.apache.ignite.igfs.IgfsBlockLocation;
 import org.apache.ignite.igfs.IgfsFile;
 import org.apache.ignite.igfs.IgfsInputStream;
 import org.apache.ignite.igfs.IgfsMetrics;
+import org.apache.ignite.igfs.IgfsMode;
 import org.apache.ignite.igfs.IgfsOutputStream;
 import org.apache.ignite.igfs.IgfsPath;
 import org.apache.ignite.igfs.IgfsPathSummary;
@@ -456,6 +457,14 @@ public interface IgniteFileSystem extends IgniteAsyncSupport {
     @Nullable public IgfsFile info(IgfsPath path) throws IgniteException;
 
     /**
+     * Get mode for the given path.
+     *
+     * @param path Path.
+     * @return Mode used for this path.
+     */
+    public IgfsMode mode(IgfsPath path);
+
+    /**
      * Gets used space in bytes.
      *
      * @return Used space in bytes.

http://git-wip-us.apache.org/repos/asf/ignite/blob/f7e8a6d6/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsAsyncImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsAsyncImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsAsyncImpl.java
index 7530557..bf3d22b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsAsyncImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsAsyncImpl.java
@@ -26,6 +26,7 @@ import org.apache.ignite.configuration.FileSystemConfiguration;
 import org.apache.ignite.igfs.IgfsBlockLocation;
 import org.apache.ignite.igfs.IgfsFile;
 import org.apache.ignite.igfs.IgfsMetrics;
+import org.apache.ignite.igfs.IgfsMode;
 import org.apache.ignite.igfs.IgfsOutputStream;
 import org.apache.ignite.igfs.IgfsPath;
 import org.apache.ignite.igfs.IgfsPathSummary;
@@ -308,6 +309,11 @@ public class IgfsAsyncImpl extends AsyncSupportAdapter<IgniteFileSystem> impleme
     }
 
     /** {@inheritDoc} */
+    @Override public IgfsMode mode(IgfsPath path) {
+        return igfs.mode(path);
+    }
+
+    /** {@inheritDoc} */
     @Override public long usedSpaceSize() {
         return igfs.usedSpaceSize();
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/f7e8a6d6/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsImpl.java
index efb347f..35e387c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsImpl.java
@@ -578,6 +578,13 @@ public final class IgfsImpl implements IgfsEx {
     }
 
     /** {@inheritDoc} */
+    @Override public IgfsMode mode(IgfsPath path) {
+        A.notNull(path, "path");
+
+        return modeRslvr.resolveMode(path);
+    }
+
+    /** {@inheritDoc} */
     @Override public IgfsPathSummary summary(final IgfsPath path) {
         A.notNull(path, "path");
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/f7e8a6d6/modules/core/src/test/java/org/apache/ignite/internal/processors/igfs/IgfsMock.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/igfs/IgfsMock.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/igfs/IgfsMock.java
index dccab4a..a2bd9ca 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/igfs/IgfsMock.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/igfs/IgfsMock.java
@@ -24,6 +24,7 @@ import org.apache.ignite.configuration.FileSystemConfiguration;
 import org.apache.ignite.igfs.IgfsBlockLocation;
 import org.apache.ignite.igfs.IgfsFile;
 import org.apache.ignite.igfs.IgfsMetrics;
+import org.apache.ignite.igfs.IgfsMode;
 import org.apache.ignite.igfs.IgfsOutputStream;
 import org.apache.ignite.igfs.IgfsPath;
 import org.apache.ignite.igfs.IgfsPathSummary;
@@ -361,6 +362,13 @@ public class IgfsMock implements IgfsEx {
     }
 
     /** {@inheritDoc} */
+    @Override public IgfsMode mode(IgfsPath path) {
+        throwUnsupported();
+
+        return null;
+    }
+
+    /** {@inheritDoc} */
     @Override public long usedSpaceSize() throws IgniteException {
         throwUnsupported();
 


[44/50] [abbrv] ignite git commit: ignite-1232 Added possibility to test distributedJoins in IgniteCacheRandomOperationBenchmark

Posted by vo...@apache.org.
ignite-1232 Added possibility to test distributedJoins in IgniteCacheRandomOperationBenchmark


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

Branch: refs/heads/ignite-3553
Commit: 2f36ae559a83dc9d294de100e11fb4bbf3bae248
Parents: 77781e9
Author: sboikov <sb...@gridgain.com>
Authored: Wed Jul 27 14:56:31 2016 +0300
Committer: sboikov <sb...@gridgain.com>
Committed: Wed Jul 27 14:56:31 2016 +0300

----------------------------------------------------------------------
 modules/yardstick/config/queries.sql            |  1 +
 .../IgniteCacheRandomOperationBenchmark.java    | 71 ++++++++++++++++----
 2 files changed, 60 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/2f36ae55/modules/yardstick/config/queries.sql
----------------------------------------------------------------------
diff --git a/modules/yardstick/config/queries.sql b/modules/yardstick/config/queries.sql
index 8271100..7326b31 100644
--- a/modules/yardstick/config/queries.sql
+++ b/modules/yardstick/config/queries.sql
@@ -1,2 +1,3 @@
 SELECT Person.firstName  FROM "query".Person, "orgCache".Organization WHERE Person.orgId = Organization.id AND lower(Organization.name) = lower('Organization 55')
 SELECT Organization.name  FROM "orgCache".Organization WHERE lower(Organization.name) LIKE lower('%55%')
+SELECT Person.firstName  FROM "query".Person, "orgCache".Organization WHERE Person.orgId = Organization.id AND lower(Organization.name) = lower('Organization 55') #distributedJoins

http://git-wip-us.apache.org/repos/asf/ignite/blob/2f36ae55/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/load/IgniteCacheRandomOperationBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/load/IgniteCacheRandomOperationBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/load/IgniteCacheRandomOperationBenchmark.java
index e62df45..d37cdca 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/load/IgniteCacheRandomOperationBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/load/IgniteCacheRandomOperationBenchmark.java
@@ -61,6 +61,7 @@ import org.apache.ignite.cache.query.SqlQuery;
 import org.apache.ignite.cluster.ClusterGroup;
 import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.lang.IgniteBiPredicate;
 import org.apache.ignite.lang.IgniteRunnable;
 import org.apache.ignite.resources.IgniteInstanceResource;
@@ -110,7 +111,7 @@ public class IgniteCacheRandomOperationBenchmark extends IgniteAbstractBenchmark
     private Map<String, List<SqlCacheDescriptor>> cacheSqlDescriptors;
 
     /** List of SQL queries. */
-    private List<String> queries;
+    private List<TestQuery> queries;
 
     /** List of allowed cache operations which will be executed. */
     private List<Operation> allowedLoadTestOps;
@@ -237,7 +238,7 @@ public class IgniteCacheRandomOperationBenchmark extends IgniteAbstractBenchmark
                                     throw new IgniteException("Class is unknown for the load test. Make sure you " +
                                         "specified its full name [clsName=" + queryEntity.getKeyType() + ']');
 
-                                cofigureCacheSqlDescriptor(cacheName, queryEntity, valCls);
+                                configureCacheSqlDescriptor(cacheName, queryEntity, valCls);
                             }
                         } catch (ClassNotFoundException e) {
                             BenchmarkUtils.println(e.getMessage());
@@ -307,11 +308,13 @@ public class IgniteCacheRandomOperationBenchmark extends IgniteAbstractBenchmark
      */
     private void loadAllowedOperations() {
         allowedLoadTestOps = new ArrayList<>();
+
         if (args.allowedLoadTestOps().isEmpty())
             Collections.addAll(allowedLoadTestOps, Operation.values());
-        else
+        else {
             for (String opName : args.allowedLoadTestOps())
                 allowedLoadTestOps.add(Operation.valueOf(opName.toUpperCase()));
+        }
     }
 
     /**
@@ -330,7 +333,24 @@ public class IgniteCacheRandomOperationBenchmark extends IgniteAbstractBenchmark
                         if (line.trim().isEmpty())
                             continue;
 
-                        queries.add(line.trim());
+                        boolean distributedJoins = false;
+
+                        int commentIdx = line.lastIndexOf('#');
+
+                        if (commentIdx >= 0) {
+                            if (line.toUpperCase().indexOf("DISTRIBUTEDJOINS", commentIdx) > 0)
+                                distributedJoins = true;
+
+                            line = line.substring(0, commentIdx);
+                        }
+
+                        line = line.trim();
+
+                        TestQuery qry = new TestQuery(line, distributedJoins);
+
+                        queries.add(qry);
+
+                        BenchmarkUtils.println("Loaded query: " + qry);
                     }
                 }
             }
@@ -343,7 +363,7 @@ public class IgniteCacheRandomOperationBenchmark extends IgniteAbstractBenchmark
      * @param valCls Class of value.
      * @throws ClassNotFoundException If fail.
      */
-    private void cofigureCacheSqlDescriptor(String cacheName, QueryEntity qryEntity, Class valCls)
+    private void configureCacheSqlDescriptor(String cacheName, QueryEntity qryEntity, Class valCls)
         throws ClassNotFoundException {
         List<SqlCacheDescriptor> descs = cacheSqlDescriptors.get(cacheName);
 
@@ -883,9 +903,13 @@ public class IgniteCacheRandomOperationBenchmark extends IgniteAbstractBenchmark
                 }
             }
             else {
-                String sql = rendomizeSql();
+                TestQuery qry = queries.get(nextRandom(queries.size()));
+
+                String sql = randomizeSql(qry.sql);
 
                 sq = new SqlFieldsQuery(sql);
+
+                ((SqlFieldsQuery)sq).setDistributedJoins(qry.distributedJoin);
             }
 
             if (sq != null)
@@ -900,14 +924,12 @@ public class IgniteCacheRandomOperationBenchmark extends IgniteAbstractBenchmark
     /**
      * @return SQL string.
      */
-    private String rendomizeSql() {
-        String sql = queries.get(nextRandom(queries.size()));
-
-        int count = StringUtils.countOccurrencesOf(sql, "%s");
+    private String randomizeSql(String sql) {
+        int cnt = StringUtils.countOccurrencesOf(sql, "%s");
 
-        Integer[] sub = new Integer[count];
+        Integer[] sub = new Integer[cnt];
 
-        for (int i=0; i<count; i++)
+        for (int i = 0; i< cnt; i++)
             sub[i] = nextRandom(args.range());
 
         sql = String.format(sql, sub);
@@ -1196,4 +1218,29 @@ public class IgniteCacheRandomOperationBenchmark extends IgniteAbstractBenchmark
             return values()[num];
         }
     }
+
+    /**
+     *
+     */
+    private static class TestQuery {
+        /** */
+        private final String sql;
+
+        /** */
+        private final boolean distributedJoin;
+
+        /**
+          * @param sql SQL.
+         * @param distributedJoin Distributed join flag.
+         */
+        public TestQuery(String sql, boolean distributedJoin) {
+            this.sql = sql;
+            this.distributedJoin = distributedJoin;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return S.toString(TestQuery.class, this);
+        }
+    }
 }


[31/50] [abbrv] ignite git commit: ignite-1232 Stop idx pool on node stop

Posted by vo...@apache.org.
ignite-1232 Stop idx pool on node stop


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

Branch: refs/heads/ignite-3553
Commit: fb884ec37cc362764a84c3d4d8feb69648346964
Parents: e70adde
Author: sboikov <sb...@gridgain.com>
Authored: Tue Jul 26 11:13:52 2016 +0300
Committer: sboikov <sb...@gridgain.com>
Committed: Tue Jul 26 11:13:52 2016 +0300

----------------------------------------------------------------------
 .../ignite/internal/managers/communication/GridIoManager.java      | 2 ++
 1 file changed, 2 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/fb884ec3/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoManager.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoManager.java
index 99cb7f4..3fdda30 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoManager.java
@@ -559,6 +559,8 @@ public class GridIoManager extends GridManagerAdapter<CommunicationSpi<Serializa
 
             U.shutdownNow(getClass(), affPool, log);
 
+            U.shutdownNow(getClass(), idxPool, log);
+
             GridEventStorageManager evtMgr = ctx.event();
 
             if (evtMgr != null && discoLsnr != null)


[40/50] [abbrv] ignite git commit: Merge remote-tracking branch 'origin/ignite-1.7' into ignite-1.7

Posted by vo...@apache.org.
Merge remote-tracking branch 'origin/ignite-1.7' into ignite-1.7


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

Branch: refs/heads/ignite-3553
Commit: 34d553adaa5716faa67b84917e2aab24274c7327
Parents: 9a80838 a32eb87
Author: vozerov-gridgain <vo...@gridgain.com>
Authored: Wed Jul 27 13:42:44 2016 +0300
Committer: vozerov-gridgain <vo...@gridgain.com>
Committed: Wed Jul 27 13:42:44 2016 +0300

----------------------------------------------------------------------
 parent/pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------



[10/50] [abbrv] ignite git commit: ignite-1232 Distributed SQL joins implementation

Posted by vo...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheDistributedJoinPartitionedAndReplicatedTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheDistributedJoinPartitionedAndReplicatedTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheDistributedJoinPartitionedAndReplicatedTest.java
new file mode 100644
index 0000000..d7610dc
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheDistributedJoinPartitionedAndReplicatedTest.java
@@ -0,0 +1,487 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.processors.cache;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.QueryEntity;
+import org.apache.ignite.cache.QueryIndex;
+import org.apache.ignite.cache.affinity.Affinity;
+import org.apache.ignite.cache.query.QueryCursor;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+import static org.apache.ignite.cache.CacheAtomicWriteOrderMode.PRIMARY;
+import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC;
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+import static org.apache.ignite.cache.CacheMode.REPLICATED;
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+
+/**
+ *
+ */
+public class IgniteCacheDistributedJoinPartitionedAndReplicatedTest extends GridCommonAbstractTest {
+    /** */
+    private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
+
+    /** */
+    private static final String PERSON_CACHE = "person";
+
+    /** */
+    private static final String ORG_CACHE = "org";
+
+    /** */
+    private static final String ACCOUNT_CACHE = "acc";
+
+    /** */
+    private boolean client;
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(gridName);
+
+        TcpDiscoverySpi spi = ((TcpDiscoverySpi)cfg.getDiscoverySpi());
+
+        spi.setIpFinder(IP_FINDER);
+
+        cfg.setClientMode(client);
+
+        return cfg;
+    }
+
+    /**
+     * @param name Cache name.
+     * @param cacheMode Cache mode.
+     * @return Cache configuration.
+     */
+    private CacheConfiguration configuration(String name, CacheMode cacheMode) {
+        CacheConfiguration ccfg = new CacheConfiguration();
+
+        ccfg.setName(name);
+        ccfg.setWriteSynchronizationMode(FULL_SYNC);
+        ccfg.setAtomicWriteOrderMode(PRIMARY);
+        ccfg.setAtomicityMode(ATOMIC);
+        ccfg.setCacheMode(cacheMode);
+
+        if (cacheMode == PARTITIONED)
+            ccfg.setBackups(1);
+
+        return ccfg;
+    }
+
+    /**
+     * @param idx Use index flag.
+     * @param persCacheMode Person cache mode.
+     * @param orgCacheMode Organization cache mode.
+     * @param accCacheMode Account cache mode.
+     * @return Configurations.
+     */
+    private List<CacheConfiguration> caches(boolean idx,
+        CacheMode persCacheMode,
+        CacheMode orgCacheMode,
+        CacheMode accCacheMode) {
+        List<CacheConfiguration> ccfgs = new ArrayList<>();
+
+        {
+            CacheConfiguration ccfg = configuration(PERSON_CACHE, persCacheMode);
+
+            QueryEntity entity = new QueryEntity();
+            entity.setKeyType(Integer.class.getName());
+            entity.setValueType(Person.class.getName());
+            entity.addQueryField("orgId", Integer.class.getName(), null);
+            entity.addQueryField("name", String.class.getName(), null);
+
+            if (idx)
+                entity.setIndexes(F.asList(new QueryIndex("orgId"), new QueryIndex("name")));
+
+            ccfg.setQueryEntities(F.asList(entity));
+
+            ccfgs.add(ccfg);
+        }
+
+        {
+            CacheConfiguration ccfg = configuration(ORG_CACHE, orgCacheMode);
+
+            QueryEntity entity = new QueryEntity();
+            entity.setKeyType(Integer.class.getName());
+            entity.setValueType(Organization.class.getName());
+            entity.addQueryField("name", String.class.getName(), null);
+
+            if (idx)
+                entity.setIndexes(F.asList(new QueryIndex("name")));
+
+            ccfg.setQueryEntities(F.asList(entity));
+
+            ccfgs.add(ccfg);
+        }
+
+        {
+            CacheConfiguration ccfg = configuration(ACCOUNT_CACHE, accCacheMode);
+
+            QueryEntity entity = new QueryEntity();
+            entity.setKeyType(Integer.class.getName());
+            entity.setValueType(Account.class.getName());
+            entity.addQueryField("orgId", Integer.class.getName(), null);
+            entity.addQueryField("personId", Integer.class.getName(), null);
+            entity.addQueryField("name", String.class.getName(), null);
+
+            if (idx) {
+                entity.setIndexes(F.asList(new QueryIndex("orgId"),
+                    new QueryIndex("personId"),
+                    new QueryIndex("name")));
+            }
+
+            ccfg.setQueryEntities(F.asList(entity));
+
+            ccfgs.add(ccfg);
+        }
+
+        return ccfgs;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        super.beforeTestsStarted();
+
+        startGridsMultiThreaded(2);
+
+        client = true;
+
+        startGrid(2);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTestsStopped() throws Exception {
+        stopAllGrids();
+
+        super.afterTestsStopped();
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoin1() throws Exception {
+        join(true, REPLICATED, PARTITIONED, PARTITIONED);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoin2() throws Exception {
+        join(true, PARTITIONED, REPLICATED, PARTITIONED);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoin3() throws Exception {
+        join(true, PARTITIONED, PARTITIONED, REPLICATED);
+    }
+
+    /**
+     * @param idx Use index flag.
+     * @param persCacheMode Person cache mode.
+     * @param orgCacheMode Organization cache mode.
+     * @param accCacheMode Account cache mode.
+     * @throws Exception If failed.
+     */
+    private void join(boolean idx, CacheMode persCacheMode, CacheMode orgCacheMode, CacheMode accCacheMode)
+        throws Exception {
+        Ignite client = grid(2);
+
+        for (CacheConfiguration ccfg : caches(idx, persCacheMode, orgCacheMode, accCacheMode))
+            client.createCache(ccfg);
+
+        try {
+            IgniteCache<Object, Object> personCache = client.cache(PERSON_CACHE);
+            IgniteCache<Object, Object> orgCache = client.cache(ORG_CACHE);
+            IgniteCache<Object, Object> accCache = client.cache(ACCOUNT_CACHE);
+
+            Affinity<Object> aff = client.affinity(PERSON_CACHE);
+
+            AtomicInteger pKey = new AtomicInteger(100_000);
+            AtomicInteger orgKey = new AtomicInteger();
+            AtomicInteger accKey = new AtomicInteger();
+
+            ClusterNode node0 = ignite(0).cluster().localNode();
+            ClusterNode node1 = ignite(1).cluster().localNode();
+
+            /**
+             * One organization, one person, two accounts.
+             */
+
+            {
+                int orgId1 = keyForNode(aff, orgKey, node0);
+
+                orgCache.put(orgId1, new Organization("obj-" + orgId1));
+
+                int pid1 = keyForNode(aff, pKey, node0);
+                personCache.put(pid1, new Person(orgId1, "o1-p1"));
+
+                accCache.put(keyForNode(aff, accKey, node0), new Account(pid1, orgId1, "a0"));
+                accCache.put(keyForNode(aff, accKey, node1), new Account(pid1, orgId1, "a1"));
+            }
+
+            IgniteCache<Object, Object> qryCache = replicated(orgCache) ? personCache : orgCache;
+
+            checkQuery("select p._key, p.name, a.name " +
+                "from \"person\".Person p, \"acc\".Account a " +
+                "where p._key = a.personId", qryCache, false, 2);
+
+            checkQuery("select o.name, p._key, p.name, a.name " +
+                "from \"org\".Organization o, \"person\".Person p, \"acc\".Account a " +
+                "where p.orgId = o._key and p._key = a.personId and a.orgId=o._key", qryCache, false, 2);
+
+            checkQuery("select o.name, p._key, p.name, a.name " +
+                "from \"org\".Organization o, \"acc\".Account a, \"person\".Person p " +
+                "where p.orgId = o._key and p._key = a.personId and a.orgId=o._key", qryCache, false, 2);
+
+            checkQuery("select o.name, p._key, p.name, a.name " +
+                "from \"person\".Person p, \"org\".Organization o, \"acc\".Account a " +
+                "where p.orgId = o._key and p._key = a.personId and a.orgId=o._key", qryCache, false, 2);
+
+            checkQuery("select * from (select o.name n1, p._key, p.name n2, a.name n3 " +
+                "from \"acc\".Account a, \"person\".Person p, \"org\".Organization o " +
+                "where p.orgId = o._key and p._key = a.personId and a.orgId=o._key)", qryCache, false, 2);
+
+            checkQuery("select * from (select o.name n1, p._key, p.name n2, a.name n3 " +
+                "from \"person\".Person p, \"acc\".Account a, \"org\".Organization o " +
+                "where p.orgId = o._key and p._key = a.personId and a.orgId=o._key)", qryCache, false, 2);
+
+            List<List<?>> res = checkQuery("select count(*) " +
+                "from \"org\".Organization o, \"person\".Person p, \"acc\".Account a " +
+                "where p.orgId = o._key and p._key = a.personId and a.orgId=o._key", qryCache, false, 1);
+
+            assertEquals(2L, res.get(0).get(0));
+
+            checkQueries(qryCache, 2);
+
+            {
+                int orgId2 = keyForNode(aff, orgKey, node1);
+
+                orgCache.put(orgId2, new Organization("obj-" + orgId2));
+
+                int pid2 = keyForNode(aff, pKey, node0);
+                personCache.put(pid2, new Person(orgId2, "o2-p1"));
+
+                accCache.put(keyForNode(aff, accKey, node0), new Account(pid2, orgId2, "a3"));
+                accCache.put(keyForNode(aff, accKey, node1), new Account(pid2, orgId2, "a4"));
+            }
+
+            checkQuery("select o.name, p._key, p.name, a.name " +
+                "from \"org\".Organization o, \"person\".Person p, \"acc\".Account a " +
+                "where p.orgId = o._key and p._key = a.personId and a.orgId=o._key", qryCache, false, 4);
+
+            checkQuery("select o.name, p._key, p.name, a.name " +
+                "from \"org\".Organization o inner join \"person\".Person p on p.orgId = o._key " +
+                "inner join \"acc\".Account a on p._key = a.personId and a.orgId=o._key", qryCache, false, 4);
+
+            res = checkQuery("select count(*) " +
+                "from \"org\".Organization o, \"person\".Person p, \"acc\".Account a " +
+                "where p.orgId = o._key and p._key = a.personId and a.orgId=o._key", qryCache, false, 1);
+
+            assertEquals(4L, res.get(0).get(0));
+
+            checkQuery("select o.name, p._key, p.name, a.name " +
+                "from \"org\".Organization o, \"person\".Person p, \"acc\".Account a " +
+                "where p.orgId = o._key and a.orgId = o._key and a.orgId=o._key", qryCache, false, 4);
+
+            res = checkQuery("select count(*) " +
+                "from \"org\".Organization o, \"person\".Person p, \"acc\".Account a " +
+                "where p.orgId = o._key and a.orgId = o._key and a.orgId=o._key", qryCache, false, 1);
+
+            assertEquals(4L, res.get(0).get(0));
+
+            checkQueries(qryCache, 4);
+        }
+        finally {
+            client.destroyCache(PERSON_CACHE);
+            client.destroyCache(ORG_CACHE);
+            client.destroyCache(ACCOUNT_CACHE);
+        }
+    }
+
+    /**
+     * @param qryCache Query cache.
+     * @param expSize Expected results size.
+     */
+    private void checkQueries(IgniteCache<Object, Object> qryCache, int expSize) {
+        String[] cacheNames = {"\"org\".Organization o", "\"person\".Person p", "\"acc\".Account a"};
+
+        for (int c1 = 0; c1 < cacheNames.length; c1++) {
+            for (int c2 = 0; c2 < cacheNames.length; c2++) {
+                if (c2 == c1)
+                    continue;
+
+                for (int c3 = 0; c3 < cacheNames.length; c3++) {
+                    if (c3 == c1 || c3 == c2)
+                        continue;
+
+                    String cache1 = cacheNames[c1];
+                    String cache2 = cacheNames[c2];
+                    String cache3 = cacheNames[c3];
+
+                    String qry = "select o.name, p._key, p.name, a.name from " +
+                        cache1 + ", " +
+                        cache2 + ", " +
+                        cache3 + " " +
+                        "where p.orgId = o._key and p._key = a.personId and a.orgId=o._key";
+
+                    checkQuery(qry, qryCache, false, expSize);
+
+                    qry = "select o.name, p._key, p.name, a.name from " +
+                        cache1 + ", " +
+                        cache2 + ", " +
+                        cache3 + " " +
+                        "where p.orgId = o._key and a.orgId = o._key and a.orgId=o._key";
+
+                    checkQuery(qry, qryCache, false, expSize);
+                }
+            }
+        }
+    }
+
+    /**
+     * @param sql SQL.
+     * @param cache Cache.
+     * @param enforceJoinOrder Enforce join order flag.
+     * @param expSize Expected results size.
+     * @param args Arguments.
+     * @return Results.
+     */
+    private List<List<?>> checkQuery(String sql,
+        IgniteCache<Object, Object> cache,
+        boolean enforceJoinOrder,
+        int expSize,
+        Object... args) {
+        SqlFieldsQuery qry = new SqlFieldsQuery(sql);
+
+        qry.setDistributedJoins(true);
+        qry.setEnforceJoinOrder(enforceJoinOrder);
+        qry.setArgs(args);
+
+        log.info("Plan: " + queryPlan(cache, qry));
+
+        QueryCursor<List<?>> cur = cache.query(qry);
+
+        List<List<?>> res = cur.getAll();
+
+        if (expSize != res.size())
+            log.info("Results: " + res);
+
+        assertEquals(expSize, res.size());
+
+        return res;
+    }
+
+    /**
+     * @param cache Cache.
+     * @return {@code True} if cache is replicated.
+     */
+    private boolean replicated(IgniteCache<?, ?> cache) {
+        return cache.getConfiguration(CacheConfiguration.class).getCacheMode() == REPLICATED;
+    }
+
+    /**
+     *
+     */
+    private static class Account implements Serializable {
+        /** */
+        int personId;
+
+        /** */
+        int orgId;
+
+        /** */
+        String name;
+
+        /**
+         * @param personId Person ID.
+         * @param orgId Organization ID.
+         * @param name Name.
+         */
+        public Account(int personId, int orgId, String name) {
+            this.personId = personId;
+            this.orgId = orgId;
+            this.name = name;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return S.toString(Account.class, this);
+        }
+    }
+
+    /**
+     *
+     */
+    private static class Person implements Serializable {
+        /** */
+        int orgId;
+
+        /** */
+        String name;
+
+        /**
+         * @param orgId Organization ID.
+         * @param name Name.
+         */
+        public Person(int orgId, String name) {
+            this.orgId = orgId;
+            this.name = name;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return S.toString(Person.class, this);
+        }
+    }
+
+    /**
+     *
+     */
+    private static class Organization implements Serializable {
+        /** */
+        String name;
+
+        /**
+         * @param name Name.
+         */
+        public Organization(String name) {
+            this.name = name;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return S.toString(Organization.class, this);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheDistributedJoinQueryConditionsTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheDistributedJoinQueryConditionsTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheDistributedJoinQueryConditionsTest.java
new file mode 100644
index 0000000..af56c91
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheDistributedJoinQueryConditionsTest.java
@@ -0,0 +1,624 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.processors.cache;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.CacheMemoryMode;
+import org.apache.ignite.cache.QueryEntity;
+import org.apache.ignite.cache.QueryIndex;
+import org.apache.ignite.cache.affinity.Affinity;
+import org.apache.ignite.cache.query.QueryCursor;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+import static org.apache.ignite.cache.CacheAtomicWriteOrderMode.PRIMARY;
+import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC;
+import static org.apache.ignite.cache.CacheMemoryMode.OFFHEAP_TIERED;
+import static org.apache.ignite.cache.CacheMemoryMode.ONHEAP_TIERED;
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+
+/**
+ *
+ */
+public class IgniteCacheDistributedJoinQueryConditionsTest extends GridCommonAbstractTest {
+    /** */
+    private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
+
+    /** */
+    private static final String PERSON_CACHE = "person";
+
+    /** */
+    private static final String ORG_CACHE = "org";
+
+    /** */
+    private boolean client;
+
+    /** */
+    private int total;
+
+    /** */
+    private CacheMemoryMode memMode = ONHEAP_TIERED;
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(gridName);
+
+        TcpDiscoverySpi spi = ((TcpDiscoverySpi) cfg.getDiscoverySpi());
+
+        spi.setIpFinder(IP_FINDER);
+
+        cfg.setClientMode(client);
+
+        return cfg;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        super.beforeTestsStarted();
+
+        startGridsMultiThreaded(2);
+
+        client = true;
+
+        startGrid(2);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTestsStopped() throws Exception {
+        stopAllGrids();
+
+        super.afterTestsStopped();
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoinQuery1() throws Exception {
+        joinQuery1(true);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoinQuery1Offheap() throws Exception {
+        memMode = OFFHEAP_TIERED;
+
+        testJoinQuery1();
+    }
+
+    /**
+     * @param idx Use index flag.
+     * @throws Exception If failed.
+     */
+    private void joinQuery1(boolean idx) throws Exception {
+        Ignite client = grid(2);
+
+        try {
+            CacheConfiguration ccfg1 =
+                cacheConfiguration(PERSON_CACHE).setQueryEntities(F.asList(personEntity(idx, idx)));
+            CacheConfiguration ccfg2 =
+                cacheConfiguration(ORG_CACHE).setQueryEntities(F.asList(organizationEntity(idx)));
+
+            IgniteCache<Object, Object> pCache = client.createCache(ccfg1);
+            client.createCache(ccfg2);
+
+            List<Integer> orgIds = putData1();
+
+            checkQuery("select o._key, o.name, p._key, p.name " +
+                "from \"org\".Organization o, Person p " +
+                "where p.orgId = o._key", pCache, total);
+
+            checkQuery("select * from (select o._key, o.name, p._key pKey, p.name pName " +
+                "from \"org\".Organization o, Person p " +
+                "where p.orgId = o._key)", pCache, total);
+
+            checkQuery("select o._key, o.name, p._key, p.name " +
+                "from \"org\".Organization o inner join Person p " +
+                "on p.orgId = o._key", pCache, total);
+
+            checkQuery("select * from (select o._key o_key, o.name o_name, p._key p_key, p.name p_name " +
+                "from \"org\".Organization o inner join Person p " +
+                "on p.orgId = o._key)", pCache, total);
+
+            checkQuery("select o._key, o.name, p._key, p.name " +
+                "from \"org\".Organization o, Person p " +
+                "where p.orgId = o._key and o._key=" + orgIds.get(3), pCache, 3);
+
+            checkQuery("select o._key, o.name, p._key, p.name " +
+                "from \"org\".Organization o, Person p " +
+                "where p.orgId = o._key and o._key IN (" + orgIds.get(2) + "," + orgIds.get(3) + ")", pCache, 5);
+
+            checkQuery("select o._key, o.name, p._key, p.name " +
+                "from \"org\".Organization o, Person p " +
+                "where p.orgId = o._key and o._key IN (" + orgIds.get(2) + "," + orgIds.get(3) + ")", pCache, 5);
+
+            checkQuery("select o._key, o.name, p._key, p.name " +
+                "from \"org\".Organization o, Person p " +
+                "where p.orgId = o._key and o._key > " + orgIds.get(2), pCache, total - 3);
+
+            checkQuery("select o._key, o.name, p._key, p.name " +
+                "from \"org\".Organization o, Person p " +
+                "where p.orgId = o._key and o._key > " + orgIds.get(1) + " and o._key < " + orgIds.get(4), pCache, 5);
+
+            checkQuery("select o._key, o.name, p._key, p.name " +
+                "from \"org\".Organization o, Person p " +
+                "where p.name = o.name", pCache, total);
+
+            checkQuery("select o._key, o.name, p._key, p.name " +
+                "from \"org\".Organization o, Person p " +
+                "where p.name = o.name and o._key=" + orgIds.get(0), pCache, 0);
+
+            checkQuery("select o._key, o.name, p._key, p.name " +
+                "from \"org\".Organization o, Person p " +
+                "where p.name = o.name and o._key=" + orgIds.get(3), pCache, 3);
+
+            checkQuery("select o._key, o.name, p._key, p.name " +
+                "from \"org\".Organization o, Person p " +
+                "where p.name = o.name and o._key IN (" + orgIds.get(2) + "," + orgIds.get(3) + ")", pCache, 5);
+
+            checkQuery("select o._key, o.name, p._key, p.name " +
+                "from \"org\".Organization o, Person p " +
+                "where p.name = o.name and o.name='obj-" + orgIds.get(3) + "'", pCache, 3);
+        }
+        finally {
+            client.destroyCache(PERSON_CACHE);
+            client.destroyCache(ORG_CACHE);
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoinQuery2() throws Exception {
+        Ignite client = grid(2);
+
+        try {
+            CacheConfiguration ccfg1 = cacheConfiguration(PERSON_CACHE).setQueryEntities(F.asList(personEntity(false, true)));
+            CacheConfiguration ccfg2 = cacheConfiguration(ORG_CACHE).setQueryEntities(F.asList(organizationEntity(false)));
+
+            IgniteCache<Object, Object> pCache = client.createCache(ccfg1);
+            IgniteCache<Object, Object> orgCache = client.createCache(ccfg2);
+
+            ClusterNode node0 = ignite(0).cluster().localNode();
+            ClusterNode node1 = ignite(1).cluster().localNode();
+
+            Affinity<Object> aff = client.affinity(PERSON_CACHE);
+
+            AtomicInteger orgKey = new AtomicInteger();
+            AtomicInteger pKey = new AtomicInteger();
+
+            List<Integer> pIds = new ArrayList<>();
+
+            for (int i = 0; i < 3; i++) {
+                Integer orgId = keyForNode(aff, orgKey, node0);
+
+                orgCache.put(orgId, new Organization("org-" + orgId));
+
+                Integer pId = keyForNode(aff, pKey, node1);
+
+                pCache.put(pId, new Person(orgId, "p-" + orgId));
+
+                pIds.add(pId);
+            }
+
+            checkQuery("select o._key, o.name, p._key, p.name " +
+                "from \"org\".Organization o, Person p " +
+                "where p.orgId = o._key and p._key >= 0", pCache, 3);
+
+            checkQuery("select o._key, o.name, p._key, p.name " +
+                "from \"org\".Organization o, Person p " +
+                "where p.orgId = o._key and p._key=" + pIds.get(0), pCache, 1);
+
+            checkQuery("select o._key, o.name, p._key, p.name " +
+                "from \"org\".Organization o, Person p " +
+                "where p.orgId = o._key and p._key in (" + pIds.get(0) + ", " + pIds.get(1) + ")", pCache, 2);
+        }
+        finally {
+            client.destroyCache(PERSON_CACHE);
+            client.destroyCache(ORG_CACHE);
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void _testJoinQuery3() throws Exception {
+        Ignite client = grid(2);
+
+        try {
+            CacheConfiguration ccfg1 = cacheConfiguration(PERSON_CACHE).setQueryEntities(F.asList(personEntity(false, true)));
+            CacheConfiguration ccfg2 = cacheConfiguration(ORG_CACHE).setQueryEntities(F.asList(organizationEntity(false)));
+
+            IgniteCache<Object, Object> pCache = client.createCache(ccfg1);
+            IgniteCache<Object, Object> orgCache = client.createCache(ccfg2);
+
+            ClusterNode node0 = ignite(0).cluster().localNode();
+            ClusterNode node1 = ignite(1).cluster().localNode();
+
+            Affinity<Object> aff = client.affinity(PERSON_CACHE);
+
+            AtomicInteger orgKey = new AtomicInteger();
+            AtomicInteger pKey = new AtomicInteger();
+
+            List<Integer> pIds = new ArrayList<>();
+
+            for (int i = 0; i < 3; i++) {
+                Integer orgId = keyForNode(aff, orgKey, node0);
+
+                orgCache.put(orgId, new Organization("org-" + orgId));
+
+                Integer pId = keyForNode(aff, pKey, node1);
+
+                pCache.put(pId, new Person(orgId + 100_000, "p-" + orgId));
+
+                pIds.add(pId);
+            }
+
+            checkQuery("select o._key, o.name, p._key, p.name " +
+                "from \"org\".Organization o, Person p " +
+                "where p.orgId != o._key", pCache, 9);
+
+            checkQuery("select o._key, o.name, p._key, p.name " +
+                "from \"org\".Organization o, Person p " +
+                "where p.orgId != o._key and p._key=" + pIds.get(0), pCache, 3);
+
+            checkQuery("select o._key, o.name, p._key, p.name " +
+                "from \"org\".Organization o, Person p " +
+                "where p.orgId != o._key and p._key in (" + pIds.get(0) + ", " + pIds.get(1) + ")", pCache, 6);
+
+            checkQuery("select o._key, o.name, p._key, p.name " +
+                "from \"org\".Organization o, Person p " +
+                "where p.orgId != o._key and p._key >=" + pIds.get(0) + "and p._key <= " + pIds.get(2), pCache, 9);
+        }
+        finally {
+            client.destroyCache(PERSON_CACHE);
+            client.destroyCache(ORG_CACHE);
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoinQuery4() throws Exception {
+        Ignite client = grid(2);
+
+        try {
+            CacheConfiguration ccfg1 =
+                cacheConfiguration(PERSON_CACHE).setQueryEntities(F.asList(personEntity(true, false)));
+
+            IgniteCache<Object, Object> pCache = client.createCache(ccfg1);
+
+            ClusterNode node0 = ignite(0).cluster().localNode();
+            ClusterNode node1 = ignite(1).cluster().localNode();
+
+            Affinity<Object> aff = client.affinity(PERSON_CACHE);
+
+            AtomicInteger pKey = new AtomicInteger();
+
+            Integer pId0 = keyForNode(aff, pKey, node0);
+
+            pCache.put(pId0, new Person(0, "p0"));
+
+            for (int i = 0; i < 3; i++) {
+                Integer pId = keyForNode(aff, pKey, node1);
+
+                pCache.put(pId, new Person(0, "p"));
+            }
+
+            checkQuery("select p1._key, p1.name, p2._key, p2.name " +
+                "from Person p1, Person p2 " +
+                "where p2._key > p1._key", pCache, 6);
+
+            checkQuery("select p1._key, p1.name, p2._key, p2.name " +
+                "from Person p1, Person p2 " +
+                "where p2._key > p1._key and p1._key=" + pId0, pCache, 3);
+
+            checkQuery("select p1._key, p1.name, p2._key, p2.name " +
+                "from Person p1, Person p2 " +
+                "where p2._key > p1._key and p1.name='p0'", pCache, 3);
+
+            checkQuery("select p1._key, p1.name, p2._key, p2.name " +
+                "from Person p1, Person p2 " +
+                "where p1.name > p2.name", pCache, 3);
+        }
+        finally {
+            client.destroyCache(PERSON_CACHE);
+            client.destroyCache(ORG_CACHE);
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoinQuery5() throws Exception {
+        Ignite client = grid(2);
+
+        try {
+            CacheConfiguration ccfg1 = cacheConfiguration(PERSON_CACHE).setQueryEntities(F.asList(personEntity(false, true)));
+            CacheConfiguration ccfg2 = cacheConfiguration(ORG_CACHE).setQueryEntities(F.asList(organizationEntity(false)));
+
+            IgniteCache<Object, Object> pCache = client.createCache(ccfg1);
+            IgniteCache<Object, Object> orgCache = client.createCache(ccfg2);
+
+            ClusterNode node0 = ignite(0).cluster().localNode();
+            ClusterNode node1 = ignite(1).cluster().localNode();
+
+            Affinity<Object> aff = client.affinity(PERSON_CACHE);
+
+            AtomicInteger orgKey = new AtomicInteger();
+            AtomicInteger pKey = new AtomicInteger();
+
+            Integer orgId = keyForNode(aff, orgKey, node0);
+
+            orgCache.put(orgId, new Organization("org-" + orgId));
+
+            Integer pId = keyForNode(aff, pKey, node1);
+
+            pCache.put(pId, new Person(orgId, "p-" + orgId));
+
+            checkQuery("select o._key from \"org\".Organization o, Person p where p.orgId = o._key", pCache, 1);
+
+            // Distributed join is not enabled for expressions, just check query does not fail.
+            checkQuery("select o.name from \"org\".Organization o where o._key in " +
+                "(select o._key from \"org\".Organization o, Person p where p.orgId = o._key)", pCache, 0);
+        }
+        finally {
+            client.destroyCache(PERSON_CACHE);
+            client.destroyCache(ORG_CACHE);
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoinQuery6() throws Exception {
+        Ignite client = grid(2);
+
+        try {
+            CacheConfiguration ccfg1 =
+                cacheConfiguration(PERSON_CACHE).setQueryEntities(F.asList(personEntity(true, true)));
+            CacheConfiguration ccfg2 =
+                cacheConfiguration(ORG_CACHE).setQueryEntities(F.asList(organizationEntity(true)));
+
+            IgniteCache<Object, Object> pCache = client.createCache(ccfg1);
+
+            client.createCache(ccfg2);
+
+            putData1();
+
+            checkQuery("select _key, name from \"org\".Organization o " +
+                "inner join (select orgId from Person) p on p.orgId = o._key", pCache, total);
+
+            checkQuery("select o._key, o.name from (select _key, name from \"org\".Organization) o " +
+                "inner join Person p on p.orgId = o._key", pCache, total);
+
+            checkQuery("select o._key, o.name from (select _key, name from \"org\".Organization) o " +
+                "inner join (select orgId from Person) p on p.orgId = o._key", pCache, total);
+
+            checkQuery("select * from " +
+                "(select _key, name from \"org\".Organization) o " +
+                "inner join " +
+                "(select orgId from Person) p " +
+                "on p.orgId = o._key", pCache, total);
+        }
+        finally {
+            client.destroyCache(PERSON_CACHE);
+            client.destroyCache(ORG_CACHE);
+        }
+    }
+
+    /**
+     * @param sql SQL.
+     * @param cache Cache.
+     * @param expSize Expected results size.
+     * @param args Arguments.
+     */
+    private void checkQuery(String sql, IgniteCache<Object, Object> cache, int expSize, Object... args) {
+        log.info("Execute query: " + sql);
+
+        checkQuery(sql, cache, false, expSize, args);
+
+        checkQuery(sql, cache, true, expSize, args);
+    }
+
+    /**
+     * @param sql SQL.
+     * @param cache Cache.
+     * @param enforceJoinOrder Enforce join order flag.
+     * @param expSize Expected results size.
+     * @param args Arguments.
+     */
+    private void checkQuery(String sql,
+        IgniteCache<Object, Object> cache,
+        boolean enforceJoinOrder,
+        int expSize,
+        Object... args) {
+        SqlFieldsQuery qry = new SqlFieldsQuery(sql);
+
+        qry.setDistributedJoins(true);
+        qry.setEnforceJoinOrder(enforceJoinOrder);
+        qry.setArgs(args);
+
+        log.info("Plan: " + queryPlan(cache, qry));
+
+        QueryCursor<List<?>> cur = cache.query(qry);
+
+        List<List<?>> res = cur.getAll();
+
+        if (expSize != res.size())
+            log.info("Results: " + res);
+
+        assertEquals(expSize, res.size());
+    }
+
+    /**
+     * @param idxName Name index flag.
+     * @param idxOrgId Org ID index flag.
+     * @return Entity.
+     */
+    private QueryEntity personEntity(boolean idxName, boolean idxOrgId) {
+        QueryEntity entity = new QueryEntity();
+
+        entity.setKeyType(Integer.class.getName());
+        entity.setValueType(Person.class.getName());
+
+        entity.addQueryField("orgId", Integer.class.getName(), null);
+        entity.addQueryField("name", String.class.getName(), null);
+
+        List<QueryIndex> idxs = new ArrayList<>();
+
+        if (idxName) {
+            QueryIndex idx = new QueryIndex("name");
+
+            idxs.add(idx);
+        }
+
+        if (idxOrgId) {
+            QueryIndex idx = new QueryIndex("orgId");
+
+            idxs.add(idx);
+        }
+
+        entity.setIndexes(idxs);
+
+        return entity;
+    }
+
+    /**
+     * @param idxName Name index flag.
+     * @return Entity.
+     */
+    private QueryEntity organizationEntity(boolean idxName) {
+        QueryEntity entity = new QueryEntity();
+
+        entity.setKeyType(Integer.class.getName());
+        entity.setValueType(Organization.class.getName());
+
+        entity.addQueryField("name", String.class.getName(), null);
+
+        if (idxName) {
+            QueryIndex idx = new QueryIndex("name");
+
+            entity.setIndexes(F.asList(idx));
+        }
+
+        return entity;
+    }
+
+    /**
+     * @return Organization ids.
+     */
+    private List<Integer> putData1() {
+        total = 0;
+
+        Ignite client = grid(2);
+
+        Affinity<Object> aff = client.affinity(PERSON_CACHE);
+
+        IgniteCache<Object, Object> personCache = client.cache(PERSON_CACHE);
+        IgniteCache<Object, Object> orgCache = client.cache(ORG_CACHE);
+
+        AtomicInteger pKey = new AtomicInteger();
+        AtomicInteger orgKey = new AtomicInteger();
+
+        ClusterNode node0 = ignite(0).cluster().localNode();
+        ClusterNode node1 = ignite(1).cluster().localNode();
+
+        List<Integer> data = new ArrayList<>();
+
+        for (int i = 0; i < 5; i++) {
+            int orgId = keyForNode(aff, orgKey, node0);
+
+            orgCache.put(orgId, new Organization("obj-" + orgId));
+
+            for (int j = 0; j < i; j++) {
+                personCache.put(keyForNode(aff, pKey, node1), new Person(orgId, "obj-" + orgId));
+
+                total++;
+            }
+
+            data.add(orgId);
+        }
+
+        return data;
+    }
+
+    /**
+     * @param name Cache name.
+     * @return Configuration.
+     */
+    private CacheConfiguration cacheConfiguration(String name) {
+        CacheConfiguration ccfg = new CacheConfiguration();
+
+        ccfg.setName(name);
+        ccfg.setWriteSynchronizationMode(FULL_SYNC);
+        ccfg.setAtomicWriteOrderMode(PRIMARY);
+        ccfg.setAtomicityMode(ATOMIC);
+        ccfg.setBackups(0);
+        ccfg.setMemoryMode(memMode);
+
+        return ccfg;
+    }
+
+    /**
+     *
+     */
+    private static class Person implements Serializable {
+        /** */
+        int orgId;
+
+        /** */
+        String name;
+
+        /**
+         * @param orgId Organization ID.
+         * @param name Name.
+         */
+        public Person(int orgId, String name) {
+            this.orgId = orgId;
+            this.name = name;
+        }
+    }
+
+    /**
+     *
+     */
+    private static class Organization implements Serializable {
+        /** */
+        String name;
+
+        /**
+         * @param name Name.
+         */
+        public Organization(String name) {
+            this.name = name;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheDistributedJoinTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheDistributedJoinTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheDistributedJoinTest.java
new file mode 100644
index 0000000..ea3b141
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheDistributedJoinTest.java
@@ -0,0 +1,316 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.processors.cache;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Iterator;
+import java.util.List;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.query.QueryCursor;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.cache.query.annotations.QuerySqlField;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.util.GridRandom;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+/**
+ */
+public class IgniteCacheDistributedJoinTest extends GridCommonAbstractTest {
+    /** */
+    private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
+
+    /** */
+    private static Connection conn;
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(gridName);
+
+        TcpDiscoverySpi spi = ((TcpDiscoverySpi)cfg.getDiscoverySpi());
+
+        spi.setIpFinder(IP_FINDER);
+
+        CacheConfiguration<Integer, A> ccfga = new CacheConfiguration<>();
+
+        ccfga.setName("a");
+        ccfga.setSqlSchema("A");
+        ccfga.setAtomicityMode(CacheAtomicityMode.ATOMIC);
+        ccfga.setBackups(1);
+        ccfga.setCacheMode(CacheMode.PARTITIONED);
+        ccfga.setIndexedTypes(Integer.class, A.class);
+
+        CacheConfiguration<Integer, B> ccfgb = new CacheConfiguration<>();
+
+        ccfgb.setName("b");
+        ccfgb.setSqlSchema("B");
+        ccfgb.setAtomicityMode(CacheAtomicityMode.ATOMIC);
+        ccfgb.setBackups(1);
+        ccfgb.setCacheMode(CacheMode.PARTITIONED);
+        ccfgb.setIndexedTypes(Integer.class, B.class);
+
+        CacheConfiguration<Integer, C> ccfgc = new CacheConfiguration<>();
+
+        ccfgc.setName("c");
+        ccfgc.setSqlSchema("C");
+        ccfgc.setAtomicityMode(CacheAtomicityMode.ATOMIC);
+        ccfgc.setBackups(1);
+        ccfgc.setCacheMode(CacheMode.PARTITIONED);
+        ccfgc.setIndexedTypes(Integer.class, C.class);
+
+        cfg.setCacheConfiguration(ccfga, ccfgb, ccfgc);
+
+        return cfg;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        startGridsMultiThreaded(4);
+
+        awaitPartitionMapExchange();
+
+        conn = DriverManager.getConnection("jdbc:h2:mem:");
+
+        Statement s = conn.createStatement();
+
+        s.execute("create schema a");
+        s.execute("create schema b");
+        s.execute("create schema c");
+
+        s.execute("create table a.a(a bigint, b bigint, c bigint)");
+        s.execute("create table b.b(a bigint, b bigint, c bigint)");
+        s.execute("create table c.c(a bigint, b bigint, c bigint)");
+
+        s.execute("create index on a.a(a)");
+        s.execute("create index on a.a(b)");
+        s.execute("create index on a.a(c)");
+
+        s.execute("create index on b.b(a)");
+        s.execute("create index on b.b(b)");
+        s.execute("create index on b.b(c)");
+
+        s.execute("create index on c.c(a)");
+        s.execute("create index on c.c(b)");
+        s.execute("create index on c.c(c)");
+
+        GridRandom rnd = new GridRandom();
+        Ignite ignite = ignite(0);
+
+        IgniteCache<Integer,A> a = ignite.cache("a");
+        IgniteCache<Integer,B> b = ignite.cache("b");
+        IgniteCache<Integer,C> c = ignite.cache("c");
+
+        for (int i = 0; i < 100; i++) {
+            a.put(i, insert(s, new A(rnd.nextInt(50), rnd.nextInt(100), rnd.nextInt(150))));
+            b.put(i, insert(s, new B(rnd.nextInt(100), rnd.nextInt(50), rnd.nextInt(150))));
+            c.put(i, insert(s, new C(rnd.nextInt(150), rnd.nextInt(100), rnd.nextInt(50))));
+        }
+
+        checkSameResult(s, a, "select a, count(*) from a group by a order by a");
+        checkSameResult(s, a, "select b, count(*) from a group by b order by b");
+        checkSameResult(s, a, "select c, count(*) from a group by c order by c");
+
+        checkSameResult(s, b, "select a, count(*) from b group by a order by a");
+        checkSameResult(s, b, "select b, count(*) from b group by b order by b");
+        checkSameResult(s, b, "select c, count(*) from b group by c order by c");
+
+        checkSameResult(s, c, "select a, count(*) from c group by a order by a");
+        checkSameResult(s, c, "select b, count(*) from c group by b order by b");
+        checkSameResult(s, c, "select c, count(*) from c group by c order by c");
+
+        s.close();
+    }
+
+    /**
+     * @param s Statement.
+     * @param c Cache.
+     * @param qry Query.
+     * @throws SQLException If failed.
+     */
+    private <Z extends X> void checkSameResult(Statement s, IgniteCache<Integer, Z> c, String qry) throws SQLException {
+        s.executeUpdate("SET SCHEMA " + c.getName());
+
+        try (
+            ResultSet rs1 = s.executeQuery(qry);
+            QueryCursor<List<?>> rs2 = c.query(new SqlFieldsQuery(qry).setDistributedJoins(true))
+        ) {
+            Iterator<List<?>> iter = rs2.iterator();
+
+            for (;;) {
+                if (!rs1.next()) {
+                    assertFalse(iter.hasNext());
+
+                    return;
+                }
+
+                assertTrue(iter.hasNext());
+
+                List<?> row = iter.next();
+
+                for (int i = 0; i < row.size(); i++)
+                    assertEquals(rs1.getLong(i + 1), row.get(i));
+            }
+        }
+    }
+
+    /**
+     * @param s Statement.
+     * @param z Value.
+     * @return Value.
+     * @throws SQLException If failed.
+     */
+    private static <Z extends X> Z insert(Statement s, Z z) throws SQLException {
+        String tbl = z.getClass().getSimpleName();
+
+        tbl = tbl + "." + tbl;
+
+        String insert = "insert into " + tbl + " values(" + z.a + ", " + z.b + ", " + z.c + ")";
+
+        s.executeUpdate(insert);
+
+        return z;
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoins() throws Exception {
+        Ignite ignite = ignite(0);
+
+        IgniteCache<Integer,A> a = ignite.cache("a");
+        IgniteCache<Integer,B> b = ignite.cache("b");
+        IgniteCache<Integer,C> c = ignite.cache("c");
+
+        Statement s = conn.createStatement();
+
+        checkSameResult(s, a, "select a.c, b.b, c.a from a.a, b.b, c.c where a.a = b.a and b.c = c.c order by a.c, b.b, c.a");
+        checkSameResult(s, b, "select a.a, b.c, c.b from a.a, b.b, c.c where a.b = b.b and b.a = c.a order by a.a, b.c, c.b");
+        checkSameResult(s, c, "select a.b, b.a, c.c from a.a, b.b, c.c where a.c = b.c and b.b = c.b order by a.b, b.a, c.c");
+
+        for (int i = 0; i < 150; i++) {
+            checkSameResult(s, a, "select a.c, b.b, c.a from a.a, b.b, c.c where " +
+                i + " = a.c and a.a = b.a and b.c = c.c order by a.c, b.b, c.a");
+            checkSameResult(s, b, "select a.a, b.c, c.b from a.a, b.b, c.c where " +
+                i + " = c.b and a.b = b.b and b.a = c.a order by a.a, b.c, c.b");
+            checkSameResult(s, c, "select a.b, b.a, c.c from a.a, b.b, c.c where " +
+                i + " = b.c and a.c = b.c and b.b = c.b order by a.b, b.a, c.c");
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTestsStopped() throws Exception {
+        U.closeQuiet(conn);
+        stopAllGrids();
+    }
+
+    /**
+     *
+     */
+    public static class X {
+        /** */
+        public long a;
+
+        /** */
+        public long b;
+
+        /** */
+        public long c;
+
+        /**
+         * @param a A.
+         * @param b B.
+         * @param c C.
+         */
+        public X(int a, int b, int c) {
+            this.a = a;
+            this.b = b;
+            this.c = c;
+        }
+
+        /** */
+        @QuerySqlField(index = true)
+        public long getA() {
+            return a;
+        }
+
+        /** */
+        @QuerySqlField(index = true)
+        public long getB() {
+            return b;
+        }
+
+        /** */
+        @QuerySqlField(index = true)
+        public long getC() {
+            return c;
+        }
+    }
+
+    /**
+     *
+     */
+    public static class A extends X {
+        /**
+         * @param a A.
+         * @param b B.
+         * @param c C.
+         */
+        public A(int a, int b, int c) {
+            super(a, b, c);
+        }
+    }
+
+    /**
+     *
+     */
+    public static class B extends X {
+        /**
+         * @param a A.
+         * @param b B.
+         * @param c C.
+         */
+        public B(int a, int b, int c) {
+            super(a, b, c);
+        }
+    }
+
+    /**
+     *
+     */
+    public static class C extends X {
+        /**
+         * @param a A.
+         * @param b B.
+         * @param c C.
+         */
+        public C(int a, int b, int c) {
+            super(a, b, c);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheJoinPartitionedAndReplicatedCollocationTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheJoinPartitionedAndReplicatedCollocationTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheJoinPartitionedAndReplicatedCollocationTest.java
new file mode 100644
index 0000000..fa9032d
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheJoinPartitionedAndReplicatedCollocationTest.java
@@ -0,0 +1,399 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.processors.cache;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.QueryEntity;
+import org.apache.ignite.cache.QueryIndex;
+import org.apache.ignite.cache.affinity.Affinity;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.processors.query.h2.sql.AbstractH2CompareQueryTest;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+
+import static org.apache.ignite.cache.CacheAtomicWriteOrderMode.PRIMARY;
+import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC;
+import static org.apache.ignite.cache.CacheMode.REPLICATED;
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+
+/**
+ *
+ */
+public class IgniteCacheJoinPartitionedAndReplicatedCollocationTest extends AbstractH2CompareQueryTest {
+    /** */
+    private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
+
+    /** */
+    private static final String PERSON_CACHE = "person";
+
+    /** */
+    private static final String ACCOUNT_CACHE = "acc";
+
+    /** */
+    private boolean client;
+
+    /** */
+    private boolean h2DataInserted;
+
+    /** {@inheritDoc} */
+    @Override protected void setIndexedTypes(CacheConfiguration<?, ?> cc, CacheMode mode) {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void initCacheAndDbData() throws Exception {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void checkAllDataEquals() throws Exception {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(gridName);
+
+        TcpDiscoverySpi spi = ((TcpDiscoverySpi)cfg.getDiscoverySpi());
+
+        spi.setIpFinder(IP_FINDER);
+
+        cfg.setClientMode(client);
+
+        return cfg;
+    }
+
+    /**
+     * @return Cache configuration.
+     */
+    private CacheConfiguration personCache() {
+        CacheConfiguration ccfg = configuration(PERSON_CACHE, 0);
+
+        // Person cache is replicated.
+        ccfg.setCacheMode(REPLICATED);
+
+        QueryEntity entity = new QueryEntity();
+        entity.setKeyType(Integer.class.getName());
+        entity.setValueType(Person.class.getName());
+        entity.addQueryField("name", String.class.getName(), null);
+
+        ccfg.setQueryEntities(F.asList(entity));
+
+        return ccfg;
+    }
+
+    /**
+     * @param backups Number of backups.
+     * @return Cache configuration.
+     */
+    private CacheConfiguration accountCache(int backups) {
+        CacheConfiguration ccfg = configuration(ACCOUNT_CACHE, backups);
+
+        QueryEntity entity = new QueryEntity();
+        entity.setKeyType(Integer.class.getName());
+        entity.setValueType(Account.class.getName());
+        entity.addQueryField("personId", Integer.class.getName(), null);
+        entity.addQueryField("name", String.class.getName(), null);
+        entity.setIndexes(F.asList(new QueryIndex("personId")));
+
+        ccfg.setQueryEntities(F.asList(entity));
+
+        return ccfg;
+    }
+
+    /**
+     * @param name Cache name.
+     * @param backups Number of backups.
+     * @return Cache configuration.
+     */
+    private CacheConfiguration configuration(String name, int backups) {
+        CacheConfiguration ccfg = new CacheConfiguration();
+
+        ccfg.setName(name);
+        ccfg.setWriteSynchronizationMode(FULL_SYNC);
+        ccfg.setAtomicWriteOrderMode(PRIMARY);
+        ccfg.setAtomicityMode(ATOMIC);
+        ccfg.setBackups(backups);
+
+        return ccfg;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        super.beforeTestsStarted();
+
+        client = true;
+
+        startGrid(SRVS);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTestsStopped() throws Exception {
+        stopAllGrids();
+
+        super.afterTestsStopped();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected Statement initializeH2Schema() throws SQLException {
+        Statement st = super.initializeH2Schema();
+
+        st.execute("CREATE SCHEMA \"person\"");
+        st.execute("CREATE SCHEMA \"acc\"");
+
+        st.execute("create table \"person\".PERSON" +
+            "  (_key int not null," +
+            "  _val other not null," +
+            "  name varchar(255))");
+
+        st.execute("create table \"acc\".ACCOUNT" +
+            "  (_key int not null," +
+            "  _val other not null," +
+            "  personId int," +
+            "  name varchar(255))");
+
+        return st;
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoin() throws Exception {
+        Ignite client = grid(SRVS);
+
+        client.createCache(personCache());
+
+        checkJoin(0);
+
+        h2DataInserted = true;
+
+        checkJoin(1);
+
+        checkJoin(2);
+    }
+
+    /**
+     * @param accBackups Account cache backups.
+     * @throws Exception If failed.
+     */
+    private void checkJoin(int accBackups) throws Exception {
+        Ignite client = grid(SRVS);
+
+        IgniteCache<Object, Object> personCache = client.cache(PERSON_CACHE);
+
+        Affinity<Object> aff = client.affinity(PERSON_CACHE);
+
+        AtomicInteger pKey = new AtomicInteger(100_000);
+        AtomicInteger accKey = new AtomicInteger();
+
+        ClusterNode node0 = ignite(0).cluster().localNode();
+        ClusterNode node1 = ignite(1).cluster().localNode();
+
+        try {
+            IgniteCache<Object, Object> accCache = client.createCache(accountCache(accBackups));
+
+            Integer pKey1 = keyForNode(aff, pKey, node0); // No accounts.
+            insert(personCache, pKey1, new Person("p1"));
+
+            Integer pKey2 = keyForNode(aff, pKey, node0); // 1 collocated account.
+            insert(personCache, pKey2, new Person("p2"));
+            insert(accCache, keyForNode(aff, accKey, node0), new Account(pKey2, "a-p2"));
+
+            Integer pKey3 = keyForNode(aff, pKey, node0); // 1 non-collocated account.
+            insert(personCache, pKey3, new Person("p3"));
+            insert(accCache, keyForNode(aff, accKey, node1), new Account(pKey3, "a-p3"));
+
+            Integer pKey4 = keyForNode(aff, pKey, node0); // 1 collocated, 1 non-collocated account.
+            insert(personCache, pKey4, new Person("p4"));
+            insert(accCache, keyForNode(aff, accKey, node0), new Account(pKey4, "a-p4-1"));
+            insert(accCache, keyForNode(aff, accKey, node1), new Account(pKey4, "a-p4-2"));
+
+            Integer pKey5 = keyForNode(aff, pKey, node0); // 2 collocated accounts.
+            insert(personCache, pKey5, new Person("p5"));
+            insert(accCache, keyForNode(aff, accKey, node0), new Account(pKey5, "a-p5-1"));
+            insert(accCache, keyForNode(aff, accKey, node0), new Account(pKey5, "a-p5-1"));
+
+            Integer pKey6 = keyForNode(aff, pKey, node0); // 2 non-collocated accounts.
+            insert(personCache, pKey6, new Person("p6"));
+            insert(accCache, keyForNode(aff, accKey, node1), new Account(pKey6, "a-p5-1"));
+            insert(accCache, keyForNode(aff, accKey, node1), new Account(pKey6, "a-p5-1"));
+
+            Integer[] keys = {pKey1, pKey2, pKey3, pKey4, pKey5, pKey6};
+
+            for (int i = 0; i < keys.length; i++) {
+                log.info("Test key: " + i);
+
+                Integer key = keys[i];
+
+                checkQuery("select p._key, p.name, a.name " +
+                    "from \"person\".Person p, \"acc\".Account a " +
+                    "where p._key = a.personId and p._key=?", accCache, true, key);
+
+                checkQuery("select p._key, p.name, a.name " +
+                    "from \"acc\".Account a, \"person\".Person p " +
+                    "where p._key = a.personId and p._key=?", accCache, true, key);
+
+                checkQuery("select p._key, p.name, a.name " +
+                    "from \"person\".Person p right outer join \"acc\".Account a " +
+                    "on (p._key = a.personId) and p._key=?", accCache, true, key);
+
+                checkQuery("select p._key, p.name, a.name " +
+                    "from \"acc\".Account a left outer join \"person\".Person p " +
+                    "on (p._key = a.personId) and p._key=?", accCache, true, key);
+
+//                checkQuery("select p._key, p.name, a.name " +
+//                    "from \"acc\".Account a right outer join \"person\".Person p " +
+//                    "on (p._key = a.personId) and p._key=?", accCache, true, key);
+//
+//                checkQuery("select p._key, p.name, a.name " +
+//                    "from \"person\".Person p left outer join \"acc\".Account a " +
+//                    "on (p._key = a.personId) and p._key=?", accCache, true, key);
+            }
+        }
+        finally {
+            client.destroyCache(ACCOUNT_CACHE);
+
+            personCache.removeAll();
+        }
+    }
+
+    /**
+     * @param cache Cache.
+     * @param key Key.
+     * @param p Person.
+     * @throws Exception If failed.
+     */
+    private void insert(IgniteCache<Object, Object> cache, int key, Person p) throws Exception {
+        cache.put(key, p);
+
+        if (h2DataInserted)
+            return;
+
+        try(PreparedStatement st = conn.prepareStatement("insert into \"person\".PERSON " +
+            "(_key, _val, name) values(?, ?, ?)")) {
+            st.setObject(1, key);
+            st.setObject(2, p);
+            st.setObject(3, p.name);
+
+            st.executeUpdate();
+        }
+    }
+
+    /**
+     * @param cache Cache.
+     * @param key Key.
+     * @param  a Account.
+     * @throws Exception If failed.
+     */
+    private void insert(IgniteCache<Object, Object> cache, int key, Account a) throws Exception {
+        cache.put(key, a);
+
+        if (h2DataInserted)
+            return;
+
+        try(PreparedStatement st = conn.prepareStatement("insert into \"acc\".ACCOUNT " +
+            "(_key, _val, personId, name) values(?, ?, ?, ?)")) {
+            st.setObject(1, key);
+            st.setObject(2, a);
+            st.setObject(3, a.personId);
+            st.setObject(4, a.name);
+
+            st.executeUpdate();
+        }
+    }
+
+    /**
+     * @param sql SQL.
+     * @param cache Cache.
+     * @param enforceJoinOrder Enforce join order flag.
+     * @param args Arguments.
+     * @throws Exception If failed.
+     */
+    private void checkQuery(String sql,
+        IgniteCache<Object, Object> cache,
+        boolean enforceJoinOrder,
+        Object... args) throws Exception {
+        String plan = (String)cache.query(new SqlFieldsQuery("explain " + sql)
+            .setArgs(args)
+            .setDistributedJoins(true)
+            .setEnforceJoinOrder(enforceJoinOrder))
+            .getAll().get(0).get(0);
+
+        log.info("Plan: " + plan);
+
+        compareQueryRes0(cache, sql, true, enforceJoinOrder, args, Ordering.RANDOM);
+    }
+
+    /**
+     *
+     */
+    private static class Account implements Serializable {
+        /** */
+        int personId;
+
+        /** */
+        String name;
+
+        /**
+         * @param personId Person ID.
+         * @param name Name.
+         */
+        public Account(int personId, String name) {
+            this.personId = personId;
+            this.name = name;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return S.toString(Account.class, this);
+        }
+    }
+
+    /**
+     *
+     */
+    private static class Person implements Serializable {
+        /** */
+        String name;
+
+        /**
+         * @param name Name.
+         */
+        public Person(String name) {
+            this.name = name;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return S.toString(Person.class, this);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheJoinPartitionedAndReplicatedTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheJoinPartitionedAndReplicatedTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheJoinPartitionedAndReplicatedTest.java
index 39c0cb5..1d528c9 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheJoinPartitionedAndReplicatedTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheJoinPartitionedAndReplicatedTest.java
@@ -17,9 +17,6 @@
 
 package org.apache.ignite.internal.processors.cache;
 
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.List;
 import org.apache.ignite.Ignite;
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.cache.QueryEntity;
@@ -32,8 +29,15 @@ import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
 import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
 import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.GridTestUtils;
 import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
 
+import javax.cache.CacheException;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+
 import static org.apache.ignite.cache.CacheAtomicWriteOrderMode.PRIMARY;
 import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC;
 import static org.apache.ignite.cache.CacheMode.REPLICATED;
@@ -53,6 +57,9 @@ public class IgniteCacheJoinPartitionedAndReplicatedTest extends GridCommonAbstr
     private static final String ORG_CACHE = "org";
 
     /** */
+    private static final String ORG_CACHE_REPLICATED = "orgRepl";
+
+    /** */
     private boolean client;
 
     /** {@inheritDoc} */
@@ -95,6 +102,22 @@ public class IgniteCacheJoinPartitionedAndReplicatedTest extends GridCommonAbstr
             ccfgs.add(ccfg);
         }
 
+        {
+            CacheConfiguration ccfg = configuration(ORG_CACHE_REPLICATED);
+
+            ccfg.setCacheMode(REPLICATED);
+
+            QueryEntity entity = new QueryEntity();
+            entity.setKeyType(Integer.class.getName());
+            entity.setValueType(Organization.class.getName());
+            entity.addQueryField("id", Integer.class.getName(), null);
+            entity.addQueryField("name", String.class.getName(), null);
+
+            ccfg.setQueryEntities(F.asList(entity));
+
+            ccfgs.add(ccfg);
+        }
+
         cfg.setCacheConfiguration(ccfgs.toArray(new CacheConfiguration[ccfgs.size()]));
 
         cfg.setClientMode(client);
@@ -144,10 +167,12 @@ public class IgniteCacheJoinPartitionedAndReplicatedTest extends GridCommonAbstr
 
         IgniteCache<Object, Object> personCache = client.cache(PERSON_CACHE);
         IgniteCache<Object, Object> orgCache = client.cache(ORG_CACHE);
+        IgniteCache<Object, Object> orgCacheRepl = client.cache(ORG_CACHE_REPLICATED);
 
         List<Integer> keys = primaryKeys(ignite(0).cache(PERSON_CACHE), 3, 200_000);
 
         orgCache.put(keys.get(0), new Organization(0, "org1"));
+        orgCacheRepl.put(keys.get(0), new Organization(0, "org1"));
         personCache.put(keys.get(1), new Person(0, "p1"));
         personCache.put(keys.get(2), new Person(0, "p2"));
 
@@ -160,12 +185,54 @@ public class IgniteCacheJoinPartitionedAndReplicatedTest extends GridCommonAbstr
             "on (p.orgId = o.id)", orgCache, 2);
 
         checkQuery("select o.name, p._key, p.name " +
-            "from \"person\".Person p left join \"org\".Organization o " +
-            "on (p.orgId = o.id)", orgCache, 2);
+            "from \"person\".Person p join \"orgRepl\".Organization o " +
+            "on (p.orgId = o.id)", orgCacheRepl, 2);
+
+        checkQuery("select o.name, p._key, p.name " +
+            "from \"orgRepl\".Organization o join \"person\".Person p " +
+            "on (p.orgId = o.id)", orgCacheRepl, 2);
+
+        checkQuery("select p.name from \"person\".Person p", ignite(0).cache(PERSON_CACHE), 2);
+        checkQuery("select p.name from \"person\".Person p", ignite(1).cache(PERSON_CACHE), 2);
+
+        for (int i = 0; i < 10; i++)
+            checkQuery("select p.name from \"person\".Person p", personCache, 2);
 
         checkQuery("select o.name, p._key, p.name " +
             "from \"org\".Organization o left join \"person\".Person p " +
             "on (p.orgId = o.id)", orgCache, 2);
+
+        checkQuery("select o.name, p._key, p.name " +
+            "from \"person\".Person p left join \"orgRepl\".Organization o " +
+            "on (p.orgId = o.id)", orgCacheRepl, 2);
+
+        checkQuery("select o.name, p._key, p.name " +
+            "from \"orgRepl\".Organization o left join \"person\".Person p " +
+            "on (p.orgId = o.id)", orgCacheRepl, 2);
+
+        checkQueryFails("select o.name, p._key, p.name " +
+                "from \"person\".Person p left join \"org\".Organization o " +
+                "on (p.orgId = o.id)", personCache);
+
+        checkQueryFails("select o.name, p._key, p.name " +
+                "from \"org\".Organization o right join \"person\".Person p " +
+                "on (p.orgId = o.id)", personCache);
+    }
+
+    /**
+     * @param sql SQL.
+     * @param cache Cache.
+     */
+    private void checkQueryFails(final String sql, final IgniteCache<Object, Object> cache) {
+        GridTestUtils.assertThrows(log, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                SqlFieldsQuery qry = new SqlFieldsQuery(sql);
+
+                cache.query(qry).getAll();
+
+                return null;
+            }
+        }, CacheException.class, null);
     }
 
     /**
@@ -233,6 +300,7 @@ public class IgniteCacheJoinPartitionedAndReplicatedTest extends GridCommonAbstr
         int id;
 
         /**
+         * @param id ID.
          * @param name Name.
          */
         public Organization(int id, String name) {


[35/50] [abbrv] ignite git commit: ignite-3300 Fixed issue with partition value changing stored in KeyCacheObject.

Posted by vo...@apache.org.
ignite-3300 Fixed issue with partition value changing stored in KeyCacheObject.


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

Branch: refs/heads/ignite-3553
Commit: a441bb9cc4ef07f28fbee3a78480c1ffbc68058f
Parents: 422c9a8
Author: sboikov <sb...@gridgain.com>
Authored: Wed Jul 27 09:19:12 2016 +0300
Committer: sboikov <sb...@gridgain.com>
Committed: Wed Jul 27 09:19:12 2016 +0300

----------------------------------------------------------------------
 .../internal/binary/BinaryObjectImpl.java       |  11 +
 .../affinity/GridAffinityProcessor.java         | 117 +++++---
 .../processors/cache/CacheObjectContext.java    |  12 +
 .../processors/cache/GridCacheAdapter.java      |   8 +-
 .../cache/GridCacheAffinityManager.java         |  22 +-
 .../processors/cache/GridCacheContext.java      |  26 +-
 .../processors/cache/GridCacheEntryInfo.java    |   2 +-
 .../processors/cache/KeyCacheObject.java        |   7 +
 .../processors/cache/KeyCacheObjectImpl.java    |   8 +
 .../cache/binary/CacheObjectBinaryContext.java  |  10 +-
 .../binary/CacheObjectBinaryProcessorImpl.java  |  38 ++-
 .../dht/atomic/GridDhtAtomicCache.java          |   6 +-
 .../dht/atomic/GridNearAtomicUpdateFuture.java  |   4 +-
 .../cacheobject/IgniteCacheObjectProcessor.java |  15 +-
 .../IgniteCacheObjectProcessorImpl.java         |  69 ++++-
 .../datastreamer/DataStreamerImpl.java          |   6 +-
 .../affinity/AffinityClientNodeSelfTest.java    |  73 ++++-
 .../binary/BinaryMarshallerSelfTest.java        |   7 +-
 .../BinaryObjectBuilderAdditionalSelfTest.java  |  46 ++-
 .../cache/CacheGetEntryAbstractTest.java        |   2 +-
 .../expiry/IgniteCacheTtlCleanupSelfTest.java   |   2 +-
 .../CacheBinaryKeyConcurrentQueryTest.java      | 298 +++++++++++++++++++
 .../IgniteCacheWithIndexingTestSuite.java       |   2 +
 23 files changed, 621 insertions(+), 170 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/a441bb9c/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryObjectImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryObjectImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryObjectImpl.java
index ae110f1..7b42c03 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryObjectImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryObjectImpl.java
@@ -98,6 +98,17 @@ public final class BinaryObjectImpl extends BinaryObjectExImpl implements Extern
     }
 
     /** {@inheritDoc} */
+    @Override public KeyCacheObject copy(int part) {
+        if (this.part == part)
+            return this;
+
+        BinaryObjectImpl cp = new BinaryObjectImpl(ctx, arr, start);
+        cp.part = part;
+
+        return cp;
+    }
+
+    /** {@inheritDoc} */
     @Override public int partition() {
         return part;
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/a441bb9c/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityProcessor.java
index 0d3d36d..19e0842 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityProcessor.java
@@ -47,6 +47,7 @@ import org.apache.ignite.internal.processors.cache.CacheObject;
 import org.apache.ignite.internal.processors.cache.CacheObjectContext;
 import org.apache.ignite.internal.processors.cache.GridCacheAdapter;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.KeyCacheObject;
 import org.apache.ignite.internal.processors.timeout.GridTimeoutObjectAdapter;
 import org.apache.ignite.internal.util.GridLeanMap;
 import org.apache.ignite.internal.util.future.GridFinishedFuture;
@@ -144,6 +145,56 @@ public class GridAffinityProcessor extends GridProcessorAdapter {
     }
 
     /**
+     * @param cacheName Cache name.
+     * @param key Key.
+     * @return Key partition.
+     * @throws IgniteCheckedException If failed.
+     */
+    public int partition(@Nullable String cacheName, Object key) throws IgniteCheckedException {
+        return partition(cacheName, key, null);
+    }
+
+    /**
+     * @param cacheName Cache name (needed only if {@code aff} is not provided.
+     * @param key Key.
+     * @param aff Affinity information.
+     * @return Key partition.
+     * @throws IgniteCheckedException If failed.
+     */
+    public int partition(@Nullable String cacheName,
+        Object key,
+        @Nullable AffinityInfo aff) throws IgniteCheckedException {
+        if (key instanceof KeyCacheObject) {
+            int part = ((KeyCacheObject)key).partition();
+
+            if (part >= 0)
+                return part;
+        }
+
+        return partition0(cacheName, key, aff);
+    }
+
+    /**
+     * @param cacheName Cache name (needed only if {@code aff} is not provided.
+     * @param key Key.
+     * @param aff Affinity.
+     * @return Key partition.
+     * @throws IgniteCheckedException If failed.
+     */
+    public int partition0(@Nullable String cacheName,
+        Object key,
+        @Nullable AffinityInfo aff) throws IgniteCheckedException {
+        if (aff == null) {
+            aff = affinityCache(cacheName, ctx.discovery().topologyVersionEx());
+
+            if (aff == null)
+                throw new IgniteCheckedException("Failed to get cache affinity.");
+        }
+
+        return aff.affFunc.partition(aff.affinityKey(key));
+    }
+
+    /**
      * Maps keys to nodes for given cache.
      *
      * @param cacheName Cache name.
@@ -206,7 +257,9 @@ public class GridAffinityProcessor extends GridProcessorAdapter {
         if (affInfo == null)
             return Collections.emptyList();
 
-        return primaryAndBackups(affInfo, key);
+        int part = partition(cacheName, key, affInfo);
+
+        return affInfo.assignment.get(part);
     }
 
     /**
@@ -224,13 +277,10 @@ public class GridAffinityProcessor extends GridProcessorAdapter {
 
         AffinityInfo affInfo = affinityCache(cacheName, ctx.discovery().topologyVersionEx());
 
-        if (affInfo == null || affInfo.mapper == null)
+        if (affInfo == null)
             return null;
 
-        if (key instanceof CacheObject)
-            key = ((CacheObject)key).value(affInfo.cacheObjCtx, false);
-
-        return affInfo.mapper.affinityKey(key);
+        return affInfo.affinityKey(key);
     }
 
     /**
@@ -484,10 +534,7 @@ public class GridAffinityProcessor extends GridProcessorAdapter {
      * @throws IgniteCheckedException In case of error.
      */
     private <K> ClusterNode primary(AffinityInfo aff, K key) throws IgniteCheckedException {
-        if (key instanceof CacheObject && !(key instanceof BinaryObject))
-            key = ((CacheObject)key).value(aff.cacheObjCtx, false);
-
-        int part = aff.affFunc.partition(aff.mapper.affinityKey(key));
+        int part = partition(null, key, aff);
 
         Collection<ClusterNode> nodes = aff.assignment.get(part);
 
@@ -497,20 +544,6 @@ public class GridAffinityProcessor extends GridProcessorAdapter {
         return nodes.iterator().next();
     }
 
-    /**
-     * @param aff Affinity function.
-     * @param key Key to check.
-     * @return Primary and backup nodes.
-     */
-    private <K> List<ClusterNode> primaryAndBackups(AffinityInfo aff, K key) {
-        if (key instanceof CacheObject && !(key instanceof BinaryObject))
-            key = ((CacheObject)key).value(aff.cacheObjCtx, false);
-
-        int part = aff.affFunc.partition(aff.mapper.affinityKey(key));
-
-        return aff.assignment.get(part);
-    }
-
     /** {@inheritDoc} */
     @Override public void printMemoryStats() {
         X.println(">>>");
@@ -551,6 +584,17 @@ public class GridAffinityProcessor extends GridProcessorAdapter {
         }
 
         /**
+         * @param key Key.
+         * @return Affinity key.
+         */
+        private Object affinityKey(Object key) {
+            if (key instanceof CacheObject && !(key instanceof BinaryObject))
+                key = ((CacheObject)key).value(cacheObjCtx, false);
+
+            return mapper.affinityKey(key);
+        }
+
+        /**
          * @return Cache affinity function.
          */
         private AffinityFunction affinityFunction() {
@@ -564,13 +608,6 @@ public class GridAffinityProcessor extends GridProcessorAdapter {
             return assignment;
         }
 
-        /**
-         * @return Key mapper.
-         */
-        private AffinityKeyMapper keyMapper() {
-            return mapper;
-        }
-
         /** {@inheritDoc} */
         @Override public String toString() {
             return S.toString(AffinityInfo.class, this);
@@ -658,7 +695,7 @@ public class GridAffinityProcessor extends GridProcessorAdapter {
             ctx.gateway().readLock();
 
             try {
-                return cache().affinityFunction().partition(key);
+                return partition0(cacheName, key, cache());
             }
             catch (IgniteCheckedException e) {
                 throw new IgniteException(e);
@@ -769,10 +806,7 @@ public class GridAffinityProcessor extends GridProcessorAdapter {
             ctx.gateway().readLock();
 
             try {
-                if (key instanceof CacheObject)
-                    key = ((CacheObject)key).value(cache().cacheObjCtx, false);
-
-                return cache().keyMapper().affinityKey(key);
+                return cache().affinityKey(key);
             }
             catch (IgniteCheckedException e) {
                 throw new IgniteException(e);
@@ -828,7 +862,9 @@ public class GridAffinityProcessor extends GridProcessorAdapter {
             ctx.gateway().readLock();
 
             try {
-                return cache().assignment().get(partition(key));
+                AffinityInfo aff = cache();
+
+                return aff.assignment().get(GridAffinityProcessor.this.partition(cacheName, key, aff));
             }
             catch (IgniteCheckedException e) {
                 throw new IgniteException(e);
@@ -861,12 +897,17 @@ public class GridAffinityProcessor extends GridProcessorAdapter {
                 Map<Integer, ClusterNode> map = new HashMap<>();
 
                 if (!F.isEmpty(parts)) {
+                    AffinityInfo aff = cache();
+
                     for (int p : parts)
-                        map.put(p, mapPartitionToNode(p));
+                        map.put(p, F.first(aff.assignment().get(p)));
                 }
 
                 return map;
             }
+            catch (IgniteCheckedException e) {
+                throw new IgniteException(e);
+            }
             finally {
                 ctx.gateway().readUnlock();
             }

http://git-wip-us.apache.org/repos/asf/ignite/blob/a441bb9c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheObjectContext.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheObjectContext.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheObjectContext.java
index d7fdb83..c4203ef 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheObjectContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheObjectContext.java
@@ -41,6 +41,9 @@ import org.apache.ignite.internal.util.typedef.F;
     private IgniteCacheObjectProcessor proc;
 
     /** */
+    private String cacheName;
+
+    /** */
     private AffinityKeyMapper dfltAffMapper;
 
     /** */
@@ -63,11 +66,13 @@ import org.apache.ignite.internal.util.typedef.F;
      * @param addDepInfo {@code true} if deployment info should be associated with the objects of this cache.
      */
     public CacheObjectContext(GridKernalContext kernalCtx,
+        String cacheName,
         AffinityKeyMapper dfltAffMapper,
         boolean cpyOnGet,
         boolean storeVal,
         boolean addDepInfo) {
         this.kernalCtx = kernalCtx;
+        this.cacheName = cacheName;
         this.dfltAffMapper = dfltAffMapper;
         this.cpyOnGet = cpyOnGet;
         this.storeVal = storeVal;
@@ -78,6 +83,13 @@ import org.apache.ignite.internal.util.typedef.F;
     }
 
     /**
+     * @return Cache name.
+     */
+    public String cacheName() {
+        return cacheName;
+    }
+
+    /**
      * @return {@code True} if peer class loading is enabled.
      */
     public boolean p2pEnabled() {

http://git-wip-us.apache.org/repos/asf/ignite/blob/a441bb9c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java
index 90669e0..1ac94a4 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java
@@ -1387,15 +1387,15 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
 
         long start = statsEnabled ? System.nanoTime() : 0L;
 
-        boolean keeyBinary = ctx.keepBinary();
+        boolean keepBinary = ctx.keepBinary();
 
-        if (keeyBinary)
+        if (keepBinary)
             key = (K)ctx.toCacheKeyObject(key);
 
-        V val = get(key, !keeyBinary, false);
+        V val = get(key, !keepBinary, false);
 
         if (ctx.config().getInterceptor() != null) {
-            key = keeyBinary ? (K) ctx.unwrapBinaryIfNeeded(key, true, false) : key;
+            key = keepBinary ? (K) ctx.unwrapBinaryIfNeeded(key, true, false) : key;
 
             val = (V)ctx.config().getInterceptor().onGet(key, val);
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/a441bb9c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAffinityManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAffinityManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAffinityManager.java
index e0e8031..98c7682 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAffinityManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAffinityManager.java
@@ -187,34 +187,36 @@ public class GridCacheAffinityManager extends GridCacheManagerAdapter {
     }
 
     /**
+     * @param key Key.
+     * @return Partition.
+     */
+    public int partition(Object key) {
+        return partition(key, true);
+    }
+
+    /**
      * NOTE: Use this method always when you need to calculate partition id for
      * a key provided by user. It's required since we should apply affinity mapper
      * logic in order to find a key that will eventually be passed to affinity function.
      *
      * @param key Key.
+     * @param useKeyPart If {@code true} can use pre-calculated partition stored in KeyCacheObject.
      * @return Partition.
      */
-    public int partition(Object key) {
+    public int partition(Object key, boolean useKeyPart) {
         GridAffinityAssignmentCache aff0 = aff;
 
         if (aff0 == null)
             throw new IgniteException(FAILED_TO_FIND_CACHE_ERR_MSG + cctx.name());
 
-        boolean keyObj = key instanceof KeyCacheObject;
-
-        if (keyObj) {
+        if (useKeyPart && (key instanceof KeyCacheObject)) {
             int part = ((KeyCacheObject)key).partition();
 
             if (part != -1)
                 return part;
         }
 
-        int p = affFunction.partition(affinityKey(key));
-
-        if (keyObj)
-            ((KeyCacheObject)key).partition(p);
-
-        return p;
+        return affFunction.partition(affinityKey(key));
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/a441bb9c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheContext.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheContext.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheContext.java
index fec43d0..e0ecf1c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheContext.java
@@ -1807,20 +1807,9 @@ public class GridCacheContext<K, V> implements Externalizable {
      * @return Cache key object.
      */
     public KeyCacheObject toCacheKeyObject(Object obj) {
-        return toCacheKeyObject(obj, false);
-    }
-
-    /**
-     * @param obj Object.
-     * @return Cache key object.
-     */
-    public KeyCacheObject toCacheKeyObject(Object obj, boolean includePartition) {
         assert validObjectForCache(obj) : obj;
 
-        if (includePartition)
-            return cacheObjects().toCacheKeyObject(cacheObjCtx, obj, true, affinity().partition(obj));
-        else
-            return cacheObjects().toCacheKeyObject(cacheObjCtx, obj, true);
+        return cacheObjects().toCacheKeyObject(cacheObjCtx, this, obj, true);
     }
 
     /**
@@ -1841,7 +1830,7 @@ public class GridCacheContext<K, V> implements Externalizable {
     public KeyCacheObject toCacheKeyObject(byte[] bytes) throws IgniteCheckedException {
         Object obj = ctx.cacheObjects().unmarshal(cacheObjCtx, bytes, deploy().localLoader());
 
-        return cacheObjects().toCacheKeyObject(cacheObjCtx, obj, false);
+        return cacheObjects().toCacheKeyObject(cacheObjCtx, this, obj, false);
     }
 
     /**
@@ -1989,21 +1978,12 @@ public class GridCacheContext<K, V> implements Externalizable {
      * @return Read-only collection of KeyCacheObject instances.
      */
     public Collection<KeyCacheObject> cacheKeysView(Collection<?> keys) {
-        return cacheKeysView(keys, false);
-    }
-
-    /**
-     * @param keys Keys.
-     * @param includePartition Include partition.
-     * @return Read-only collection of KeyCacheObject instances.
-     */
-    public Collection<KeyCacheObject> cacheKeysView(Collection<?> keys, final boolean includePartition) {
         return F.viewReadOnly(keys, new C1<Object, KeyCacheObject>() {
             @Override public KeyCacheObject apply(Object key) {
                 if (key == null)
                     throw new NullPointerException("Null key.");
 
-                return toCacheKeyObject(key, includePartition);
+                return toCacheKeyObject(key);
             }
         });
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/a441bb9c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryInfo.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryInfo.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryInfo.java
index c42e788..f281227 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryInfo.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryInfo.java
@@ -407,7 +407,7 @@ public class GridCacheEntryInfo implements Message {
 
             Object key0 = ctx.cacheObjects().unmarshal(cacheObjCtx, keyBytes, clsLdr);
 
-            key = ctx.cacheObjects().toCacheKeyObject(cacheObjCtx, key0, false);
+            key = ctx.cacheObjects().toCacheKeyObject(cacheObjCtx, ctx, key0, false);
         }
         else
             key.finishUnmarshal(ctx.cacheObjectContext(), clsLdr);

http://git-wip-us.apache.org/repos/asf/ignite/blob/a441bb9c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/KeyCacheObject.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/KeyCacheObject.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/KeyCacheObject.java
index ffb846c..21b1f89 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/KeyCacheObject.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/KeyCacheObject.java
@@ -38,7 +38,14 @@ public interface KeyCacheObject extends CacheObject {
 
     /**
      * Sets partition ID for this key.
+     *
      * @param part Partition ID.
      */
     public void partition(int part);
+
+    /**
+     * @param part Partition ID.
+     * @return Copy of this object with given partition set.
+     */
+    public KeyCacheObject copy(int part);
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/a441bb9c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/KeyCacheObjectImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/KeyCacheObjectImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/KeyCacheObjectImpl.java
index 35e681c..146e554 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/KeyCacheObjectImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/KeyCacheObjectImpl.java
@@ -61,6 +61,14 @@ public class KeyCacheObjectImpl extends CacheObjectAdapter implements KeyCacheOb
     }
 
     /** {@inheritDoc} */
+    @Override public KeyCacheObject copy(int part) {
+        if (this.part == part)
+            return this;
+
+        return new KeyCacheObjectImpl(val, valBytes, part);
+    }
+
+    /** {@inheritDoc} */
     @Override public int partition() {
         return part;
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/a441bb9c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/binary/CacheObjectBinaryContext.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/binary/CacheObjectBinaryContext.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/binary/CacheObjectBinaryContext.java
index ec01f48..26c713c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/binary/CacheObjectBinaryContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/binary/CacheObjectBinaryContext.java
@@ -31,18 +31,24 @@ public class CacheObjectBinaryContext extends CacheObjectContext {
 
     /**
      * @param kernalCtx Kernal context.
+     * @param cacheName Cache name.
      * @param binaryEnabled Binary enabled flag.
      * @param cpyOnGet Copy on get flag.
      * @param storeVal {@code True} if should store unmarshalled value in cache.
      * @param depEnabled {@code true} if deployment is enabled for the given cache.
      */
     public CacheObjectBinaryContext(GridKernalContext kernalCtx,
+        String cacheName,
         boolean cpyOnGet,
         boolean storeVal,
         boolean binaryEnabled,
         boolean depEnabled) {
-        super(kernalCtx, binaryEnabled ? new CacheDefaultBinaryAffinityKeyMapper() :
-            new GridCacheDefaultAffinityKeyMapper(), cpyOnGet, storeVal, depEnabled);
+        super(kernalCtx,
+            cacheName,
+            binaryEnabled ? new CacheDefaultBinaryAffinityKeyMapper() : new GridCacheDefaultAffinityKeyMapper(),
+            cpyOnGet,
+            storeVal,
+            depEnabled);
 
         this.binaryEnabled = binaryEnabled;
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/a441bb9c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/binary/CacheObjectBinaryProcessorImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/binary/CacheObjectBinaryProcessorImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/binary/CacheObjectBinaryProcessorImpl.java
index f4d1d6e..3ab01e7 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/binary/CacheObjectBinaryProcessorImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/binary/CacheObjectBinaryProcessorImpl.java
@@ -736,6 +736,7 @@ public class CacheObjectBinaryProcessorImpl extends IgniteCacheObjectProcessorIm
         CacheObjectContext ctx0 = super.contextForCache(cfg);
 
         CacheObjectContext res = new CacheObjectBinaryContext(ctx,
+            cfg.getName(),
             ctx0.copyOnGet(),
             ctx0.storeValue(),
             binaryEnabled,
@@ -768,43 +769,38 @@ public class CacheObjectBinaryProcessorImpl extends IgniteCacheObjectProcessorIm
     }
 
     /** {@inheritDoc} */
-    @Override public KeyCacheObject toCacheKeyObject(CacheObjectContext ctx, Object obj, boolean userObj) {
-        return toCacheKeyObject(ctx, obj, userObj, -1);
-    }
-
-    /** {@inheritDoc} */
     @Override public KeyCacheObject toCacheKeyObject(
         CacheObjectContext ctx,
+        @Nullable GridCacheContext cctx,
         Object obj,
-        boolean userObj,
-        int partition
+        boolean userObj
     ) {
         if (!((CacheObjectBinaryContext)ctx).binaryEnabled())
-            return super.toCacheKeyObject(ctx, obj, userObj, partition);
+            return super.toCacheKeyObject(ctx, cctx, obj, userObj);
 
         if (obj instanceof KeyCacheObject) {
-            if (obj instanceof BinaryObjectImpl) {
+            KeyCacheObject key = (KeyCacheObject)obj;
+
+            if (key instanceof BinaryObjectImpl) {
                 // Need to create a copy because the key can be reused at the application layer after that (IGNITE-3505).
-                BinaryObjectImpl bObj = (BinaryObjectImpl)obj;
-                obj = new BinaryObjectImpl(bObj.context(), bObj.array(), bObj.start());
+                key = key.copy(partition(ctx, cctx, key));
             }
+            else if (key.partition() == -1)
+                // Assume others KeyCacheObjects can not be reused for another cache.
+                key.partition(partition(ctx, cctx, key));
 
-            ((KeyCacheObject)obj).partition(partition);
-
-            return (KeyCacheObject)obj;
+            return key;
         }
 
-        if (((CacheObjectBinaryContext)ctx).binaryEnabled()) {
-            obj = toBinary(obj);
+        obj = toBinary(obj);
 
-            if (obj instanceof KeyCacheObject) {
-                ((KeyCacheObject)obj).partition(partition);
+        if (obj instanceof BinaryObjectImpl) {
+            ((BinaryObjectImpl)obj).partition(partition(ctx, cctx, obj));
 
-                return (KeyCacheObject)obj;
-            }
+            return (KeyCacheObject)obj;
         }
 
-        return toCacheKeyObject0(obj, userObj, partition);
+        return toCacheKeyObject0(ctx, cctx, obj, userObj);
     }
 
     /** {@inheritDoc} */

http://git-wip-us.apache.org/repos/asf/ignite/blob/a441bb9c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java
index 561c6c6..3616082 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java
@@ -344,7 +344,7 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
         final boolean skipStore = opCtx != null && opCtx.skipStore();
 
         try {
-            return getAsync0(ctx.toCacheKeyObject(key, true),
+            return getAsync0(ctx.toCacheKeyObject(key),
                 !ctx.config().isReadFromBackup(),
                 subjId,
                 taskName,
@@ -390,7 +390,7 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
 
         return asyncOp(new CO<IgniteInternalFuture<V>>() {
             @Override public IgniteInternalFuture<V> apply() {
-                return getAsync0(ctx.toCacheKeyObject(key, true),
+                return getAsync0(ctx.toCacheKeyObject(key),
                     forcePrimary,
                     subjId0,
                     taskName,
@@ -436,7 +436,7 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
 
         return asyncOp(new CO<IgniteInternalFuture<Map<K, V>>>() {
             @Override public IgniteInternalFuture<Map<K, V>> apply() {
-                return getAllAsync0(ctx.cacheKeysView(keys, true),
+                return getAllAsync0(ctx.cacheKeysView(keys),
                     forcePrimary,
                     subjId0,
                     taskName,

http://git-wip-us.apache.org/repos/asf/ignite/blob/a441bb9c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicUpdateFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicUpdateFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicUpdateFuture.java
index b2f2704..0d88ef8 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicUpdateFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicUpdateFuture.java
@@ -883,7 +883,7 @@ public class GridNearAtomicUpdateFuture extends GridNearAtomicAbstractUpdateFutu
             if (val == null && op != GridCacheOperation.DELETE)
                 continue;
 
-            KeyCacheObject cacheKey = cctx.toCacheKeyObject(key, true);
+            KeyCacheObject cacheKey = cctx.toCacheKeyObject(key);
 
             if (remapKeys != null && !remapKeys.contains(cacheKey))
                 continue;
@@ -1000,7 +1000,7 @@ public class GridNearAtomicUpdateFuture extends GridNearAtomicAbstractUpdateFutu
         if (val == null && op != GridCacheOperation.DELETE)
             throw new NullPointerException("Null value.");
 
-        KeyCacheObject cacheKey = cctx.toCacheKeyObject(key, true);
+        KeyCacheObject cacheKey = cctx.toCacheKeyObject(key);
 
         if (op != TRANSFORM)
             val = cctx.toCacheObject(val);

http://git-wip-us.apache.org/repos/asf/ignite/blob/a441bb9c/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessor.java
index c488b3f..5ca3da8 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessor.java
@@ -133,23 +133,14 @@ public interface IgniteCacheObjectProcessor extends GridProcessor {
     public CacheObjectContext contextForCache(CacheConfiguration ccfg) throws IgniteCheckedException;
 
     /**
-     * @param ctx Cache context.
-     * @param obj Key value.
-     * @param userObj If {@code true} then given object is object provided by user and should be copied
-     *        before stored in cache.
-     * @return Cache key object.
-     */
-    public KeyCacheObject toCacheKeyObject(CacheObjectContext ctx, Object obj, boolean userObj);
-
-    /**
-     * @param ctx Cache context.
+     * @param ctx Cache objects context.
+     * @param cctx Cache context if cache is available.
      * @param obj Key value.
      * @param userObj If {@code true} then given object is object provided by user and should be copied
      *        before stored in cache.
-     * @param partition ID of partition this key belongs to.
      * @return Cache key object.
      */
-    public KeyCacheObject toCacheKeyObject(CacheObjectContext ctx, Object obj, boolean userObj, int partition);
+    public KeyCacheObject toCacheKeyObject(CacheObjectContext ctx, @Nullable GridCacheContext cctx, Object obj, boolean userObj);
 
     /**
      * @param ctx Cache context.

http://git-wip-us.apache.org/repos/asf/ignite/blob/a441bb9c/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessorImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessorImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessorImpl.java
index f62ce36..78a1838 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessorImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessorImpl.java
@@ -113,18 +113,21 @@ public class IgniteCacheObjectProcessorImpl extends GridProcessorAdapter impleme
     }
 
     /** {@inheritDoc} */
-    @Override @Nullable public KeyCacheObject toCacheKeyObject(CacheObjectContext ctx, Object obj, boolean userObj) {
-        return toCacheKeyObject(ctx, obj, userObj, -1);
-    }
-
-    /** {@inheritDoc} */
-    @Override public KeyCacheObject toCacheKeyObject(CacheObjectContext ctx, Object obj, boolean userObj, int partition) {
+    @Override public KeyCacheObject toCacheKeyObject(CacheObjectContext ctx,
+        @Nullable GridCacheContext cctx,
+        Object obj,
+        boolean userObj) {
         if (obj instanceof KeyCacheObject) {
-            ((KeyCacheObject)obj).partition(partition);
+            KeyCacheObject key = (KeyCacheObject)obj;
+
+            if (key.partition() == -1)
+                // Assume all KeyCacheObjects except BinaryObject can not be reused for another cache.
+                key.partition(partition(ctx, cctx, key));
+
             return (KeyCacheObject)obj;
         }
 
-        return toCacheKeyObject0(obj, userObj, partition);
+        return toCacheKeyObject0(ctx, cctx, obj, userObj);
     }
 
     /**
@@ -134,11 +137,16 @@ public class IgniteCacheObjectProcessorImpl extends GridProcessorAdapter impleme
      * @return Key cache object.
      */
     @SuppressWarnings("ExternalizableWithoutPublicNoArgConstructor")
-    protected KeyCacheObject toCacheKeyObject0(Object obj, boolean userObj, int partititon) {
+    protected KeyCacheObject toCacheKeyObject0(CacheObjectContext ctx,
+        @Nullable GridCacheContext cctx,
+        Object obj,
+        boolean userObj) {
+        int part = partition(ctx, cctx, obj);
+
         if (!userObj)
-            return new KeyCacheObjectImpl(obj, null, partititon);
+            return new KeyCacheObjectImpl(obj, null, part);
 
-        return new UserKeyCacheObjectImpl(obj, partititon);
+        return new UserKeyCacheObjectImpl(obj, part);
     }
 
     /** {@inheritDoc} */
@@ -212,6 +220,25 @@ public class IgniteCacheObjectProcessorImpl extends GridProcessorAdapter impleme
         return new UserCacheObjectImpl(obj, null);
     }
 
+    /**
+     * @param ctx Cache objects context.
+     * @param cctx Cache context.
+     * @param obj Object.
+     * @return Object partition.
+     */
+    protected final int partition(CacheObjectContext ctx, @Nullable GridCacheContext cctx, Object obj) {
+        try {
+            return cctx != null ?
+                cctx.affinity().partition(obj, false) :
+                ctx.kernalContext().affinity().partition0(ctx.cacheName(), obj, null);
+        }
+        catch (IgniteCheckedException e) {
+            U.error(log, "Failed to get partition");
+
+            return  -1;
+        }
+    }
+
     /** {@inheritDoc} */
     @Override public CacheObjectContext contextForCache(CacheConfiguration ccfg) throws IgniteCheckedException {
         assert ccfg != null;
@@ -223,6 +250,7 @@ public class IgniteCacheObjectProcessorImpl extends GridProcessorAdapter impleme
             !ccfg.isCopyOnRead();
 
         CacheObjectContext res = new CacheObjectContext(ctx,
+            ccfg.getName(),
             ccfg.getAffinityMapper() != null ? ccfg.getAffinityMapper() : new GridCacheDefaultAffinityKeyMapper(),
             ccfg.isCopyOnRead() && memMode != OFFHEAP_VALUES,
             storeVal,
@@ -309,8 +337,23 @@ public class IgniteCacheObjectProcessorImpl extends GridProcessorAdapter impleme
         /**
          * @param key Key.
          */
-        UserKeyCacheObjectImpl(Object key, int partition) {
-            super(key, null, partition);
+        UserKeyCacheObjectImpl(Object key, int part) {
+            super(key, null, part);
+        }
+
+        /**
+         * @param key Key.
+         */
+        UserKeyCacheObjectImpl(Object key, byte[] valBytes, int part) {
+            super(key, valBytes, part);
+        }
+
+        /** {@inheritDoc} */
+        @Override public KeyCacheObject copy(int part) {
+            if (this.partition() == part)
+                return this;
+
+            return new UserKeyCacheObjectImpl(val, valBytes, part);
         }
 
         /** {@inheritDoc} */

http://git-wip-us.apache.org/repos/asf/ignite/blob/a441bb9c/modules/core/src/main/java/org/apache/ignite/internal/processors/datastreamer/DataStreamerImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastreamer/DataStreamerImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastreamer/DataStreamerImpl.java
index 21df559..e565cba 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastreamer/DataStreamerImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastreamer/DataStreamerImpl.java
@@ -519,12 +519,12 @@ public class DataStreamerImpl<K, V> implements IgniteDataStreamer<K, V>, Delayed
                 keys = new GridConcurrentHashSet<>(entries.size(), U.capacity(entries.size()), 1);
 
                 for (Map.Entry<K, V> entry : entries)
-                    keys.add(cacheObjProc.toCacheKeyObject(cacheObjCtx, entry.getKey(), true));
+                    keys.add(cacheObjProc.toCacheKeyObject(cacheObjCtx, null, entry.getKey(), true));
             }
 
             Collection<? extends DataStreamerEntry> entries0 = F.viewReadOnly(entries, new C1<Entry<K, V>, DataStreamerEntry>() {
                 @Override public DataStreamerEntry apply(Entry<K, V> e) {
-                    KeyCacheObject key = cacheObjProc.toCacheKeyObject(cacheObjCtx, e.getKey(), true);
+                    KeyCacheObject key = cacheObjProc.toCacheKeyObject(cacheObjCtx, null, e.getKey(), true);
                     CacheObject val = cacheObjProc.toCacheObject(cacheObjCtx, e.getValue(), true);
 
                     return new DataStreamerEntry(key, val);
@@ -619,7 +619,7 @@ public class DataStreamerImpl<K, V> implements IgniteDataStreamer<K, V>, Delayed
         else
             checkSecurityPermission(SecurityPermission.CACHE_PUT);
 
-        KeyCacheObject key0 = cacheObjProc.toCacheKeyObject(cacheObjCtx, key, true);
+        KeyCacheObject key0 = cacheObjProc.toCacheKeyObject(cacheObjCtx, null, key, true);
         CacheObject val0 = cacheObjProc.toCacheObject(cacheObjCtx, val, true);
 
         return addDataInternal(Collections.singleton(new DataStreamerEntry(key0, val0)));

http://git-wip-us.apache.org/repos/asf/ignite/blob/a441bb9c/modules/core/src/test/java/org/apache/ignite/cache/affinity/AffinityClientNodeSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/cache/affinity/AffinityClientNodeSelfTest.java b/modules/core/src/test/java/org/apache/ignite/cache/affinity/AffinityClientNodeSelfTest.java
index 47b01f4..fc50541 100644
--- a/modules/core/src/test/java/org/apache/ignite/cache/affinity/AffinityClientNodeSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/cache/affinity/AffinityClientNodeSelfTest.java
@@ -57,15 +57,15 @@ public class AffinityClientNodeSelfTest extends GridCommonAbstractTest {
     /** */
     private static final String CACHE4 = "cache4";
 
+    /** */
+    private static final String CACHE5 = "cache5";
+
     /** {@inheritDoc} */
     @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
         IgniteConfiguration cfg = super.getConfiguration(gridName);
 
         ((TcpDiscoverySpi)cfg.getDiscoverySpi()).setIpFinder(ipFinder);
 
-        if (gridName.equals(getTestGridName(NODE_CNT - 1)))
-            cfg.setClientMode(true);
-
         CacheConfiguration ccfg1 = new CacheConfiguration();
 
         ccfg1.setBackups(1);
@@ -92,7 +92,18 @@ public class AffinityClientNodeSelfTest extends GridCommonAbstractTest {
         ccfg4.setName(CACHE4);
         ccfg4.setNodeFilter(new TestNodesFilter());
 
-        cfg.setCacheConfiguration(ccfg1, ccfg2, ccfg3, ccfg4);
+        CacheConfiguration ccfg5 = new CacheConfiguration();
+
+        ccfg5.setBackups(1);
+        ccfg5.setName(CACHE5);
+
+        if (gridName.equals(getTestGridName(NODE_CNT - 1))) {
+            cfg.setClientMode(true);
+
+            cfg.setCacheConfiguration(ccfg5);
+        }
+        else
+            cfg.setCacheConfiguration(ccfg1, ccfg2, ccfg3, ccfg4);
 
         return cfg;
     }
@@ -123,6 +134,8 @@ public class AffinityClientNodeSelfTest extends GridCommonAbstractTest {
 
         checkCache(CACHE4, 3);
 
+        checkCache(CACHE5, 2);
+
         Ignite client = ignite(NODE_CNT - 1);
 
         CacheConfiguration ccfg = new CacheConfiguration();
@@ -157,6 +170,8 @@ public class AffinityClientNodeSelfTest extends GridCommonAbstractTest {
     private void checkCache(String cacheName, int expNodes) {
         log.info("Test cache: " + cacheName);
 
+        Affinity<Object> aff0 = ignite(0).affinity(cacheName);
+
         Ignite client = ignite(NODE_CNT - 1);
 
         assertTrue(client.configuration().isClientMode());
@@ -166,14 +181,22 @@ public class AffinityClientNodeSelfTest extends GridCommonAbstractTest {
         for (int i = 0; i < NODE_CNT; i++) {
             Ignite ignite = ignite(i);
 
-            Affinity<Integer> aff = ignite.affinity(cacheName);
+            Affinity<Object> aff = ignite.affinity(cacheName);
 
             for (int part = 0; part < aff.partitions(); part++) {
                 Collection<ClusterNode> nodes = aff.mapPartitionToPrimaryAndBackups(part);
 
                 assertEquals(expNodes, nodes.size());
+                assertEquals(aff0.mapPartitionToPrimaryAndBackups(part), nodes);
 
                 assertFalse(nodes.contains(clientNode));
+
+                assertEquals(aff0.partition(part), aff.partition(part));
+
+                TestKey key = new TestKey(part, part + 1);
+
+                assertEquals(aff0.partition(key), aff.partition(key));
+                assertEquals(aff0.mapKeyToPrimaryAndBackups(key), aff.mapKeyToPrimaryAndBackups(key));
             }
         }
     }
@@ -181,6 +204,46 @@ public class AffinityClientNodeSelfTest extends GridCommonAbstractTest {
     /**
      *
      */
+    static class TestKey {
+        /** */
+        private int id;
+
+        /** */
+        @AffinityKeyMapped
+        private int affId;
+
+        /**
+         * @param id ID.
+         * @param affId Affinity ID.
+         */
+        public TestKey(int id, int affId) {
+            this.id = id;
+            this.affId = affId;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean equals(Object o) {
+            if (this == o)
+                return true;
+
+            if (o == null || getClass() != o.getClass())
+                return false;
+
+            TestKey testKey = (TestKey)o;
+
+            return id == testKey.id;
+
+        }
+
+        /** {@inheritDoc} */
+        @Override public int hashCode() {
+            return id;
+        }
+    }
+
+    /**
+     *
+     */
     private static class TestNodesFilter implements IgnitePredicate<ClusterNode> {
         /** {@inheritDoc} */
         @Override public boolean apply(ClusterNode clusterNode) {

http://git-wip-us.apache.org/repos/asf/ignite/blob/a441bb9c/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryMarshallerSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryMarshallerSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryMarshallerSelfTest.java
index 1285db7..f4c1bf7 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryMarshallerSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryMarshallerSelfTest.java
@@ -26,7 +26,6 @@ import java.lang.reflect.Field;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
 import java.lang.reflect.Proxy;
 import java.math.BigDecimal;
 import java.math.BigInteger;
@@ -90,13 +89,11 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.jsr166.ConcurrentHashMap8;
 
-import static org.apache.ignite.IgniteSystemProperties.IGNITE_BINARY_MARSHALLER_USE_STRING_SERIALIZATION_VER_2;
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.apache.ignite.internal.binary.streams.BinaryMemoryAllocator.INSTANCE;
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertNotEquals;
 
-import static java.nio.charset.StandardCharsets.UTF_8;
-
 /**
  * Binary marshaller tests.
  */
@@ -2401,7 +2398,7 @@ public class BinaryMarshallerSelfTest extends GridCommonAbstractTest {
 
         BinaryObjectImpl po = marshal(simpleObject(), marsh);
 
-        CacheObjectContext coCtx = new CacheObjectContext(newContext(), null, false, true, false);
+        CacheObjectContext coCtx = new CacheObjectContext(newContext(), null, null, false, true, false);
 
         assert po.value(coCtx, false) == po.value(coCtx, false);
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/a441bb9c/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryObjectBuilderAdditionalSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryObjectBuilderAdditionalSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryObjectBuilderAdditionalSelfTest.java
index be2ce9b..f999ad3 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryObjectBuilderAdditionalSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryObjectBuilderAdditionalSelfTest.java
@@ -21,6 +21,26 @@ import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
+import java.lang.reflect.Field;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.sql.Timestamp;
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
 import junit.framework.TestCase;
 import org.apache.ignite.IgniteBinary;
 import org.apache.ignite.IgniteCache;
@@ -29,9 +49,6 @@ import org.apache.ignite.binary.BinaryObject;
 import org.apache.ignite.binary.BinaryObjectBuilder;
 import org.apache.ignite.binary.BinaryObjectException;
 import org.apache.ignite.binary.BinaryType;
-import org.apache.ignite.cache.CacheAtomicityMode;
-import org.apache.ignite.cache.CacheMode;
-import org.apache.ignite.cache.CacheWriteSynchronizationMode;
 import org.apache.ignite.configuration.BinaryConfiguration;
 import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
@@ -46,27 +63,6 @@ import org.apache.ignite.testframework.GridTestUtils;
 import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
 import org.junit.Assert;
 
-import java.lang.reflect.Field;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.sql.Timestamp;
-import java.util.AbstractMap;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.UUID;
-
 import static org.apache.ignite.cache.CacheMode.PARTITIONED;
 import static org.apache.ignite.cache.CacheMode.REPLICATED;
 
@@ -1497,7 +1493,5 @@ public class BinaryObjectBuilderAdditionalSelfTest extends GridCommonAbstractTes
         assert OBJ.equals(binaryObj.type().fieldTypeName("asListHint"));
         assert OBJ.equals(binaryObj.type().fieldTypeName("asSetHint"));
         assert OBJ.equals(binaryObj.type().fieldTypeName("asMapHint"));
-
     }
-
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/a441bb9c/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheGetEntryAbstractTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheGetEntryAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheGetEntryAbstractTest.java
index c0ba42c..34480a2 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheGetEntryAbstractTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheGetEntryAbstractTest.java
@@ -463,7 +463,7 @@ public abstract class CacheGetEntryAbstractTest extends GridCacheAbstractSelfTes
             CacheObjectContext cacheObjCtx = cacheAdapter.context().cacheObjectContext();
 
             GridCacheMapEntry mapEntry = cacheAdapter.map().getEntry(cacheObjects.toCacheKeyObject(
-                cacheObjCtx, e.getKey(), true));
+                cacheObjCtx, null, e.getKey(), true));
 
             assertNotNull("No entry for key: " + e.getKey(), mapEntry);
             assertEquals(mapEntry.version(), e.version());

http://git-wip-us.apache.org/repos/asf/ignite/blob/a441bb9c/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/expiry/IgniteCacheTtlCleanupSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/expiry/IgniteCacheTtlCleanupSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/expiry/IgniteCacheTtlCleanupSelfTest.java
index e166acb..42027e4 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/expiry/IgniteCacheTtlCleanupSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/expiry/IgniteCacheTtlCleanupSelfTest.java
@@ -82,6 +82,6 @@ public class IgniteCacheTtlCleanupSelfTest extends GridCacheAbstractSelfTest {
         CacheObjectContext cacheObjCtx = cacheAdapter.context().cacheObjectContext();
 
         for (int i = 0; i < 100; i++)
-            assertNull(cacheAdapter.map().getEntry(cacheObjects.toCacheKeyObject(cacheObjCtx, i, true)));
+            assertNull(cacheAdapter.map().getEntry(cacheObjects.toCacheKeyObject(cacheObjCtx, null, i, true)));
     }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/a441bb9c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/CacheBinaryKeyConcurrentQueryTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/CacheBinaryKeyConcurrentQueryTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/CacheBinaryKeyConcurrentQueryTest.java
new file mode 100644
index 0000000..e2de281
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/CacheBinaryKeyConcurrentQueryTest.java
@@ -0,0 +1,298 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.processors.cache;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.cache.Cache;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteDataStreamer;
+import org.apache.ignite.binary.BinaryObject;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheEntry;
+import org.apache.ignite.cache.QueryEntity;
+import org.apache.ignite.cache.QueryIndex;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.cache.query.SqlQuery;
+import org.apache.ignite.cache.query.annotations.QuerySqlField;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC;
+import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+
+/**
+ *
+ */
+@SuppressWarnings("unchecked")
+public class CacheBinaryKeyConcurrentQueryTest extends GridCommonAbstractTest {
+    /** */
+    private static final TcpDiscoveryIpFinder ipFinder = new TcpDiscoveryVmIpFinder(true);
+
+    /** */
+    private static final int NODES = 3;
+
+    /** */
+    private static final int KEYS = 1000;
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(gridName);
+
+        ((TcpDiscoverySpi)cfg.getDiscoverySpi()).setIpFinder(ipFinder);
+
+        cfg.setMarshaller(null);
+
+        return cfg;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        super.beforeTestsStarted();
+
+        startGridsMultiThreaded(NODES);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTestsStopped() throws Exception {
+        stopAllGrids();
+
+        super.afterTestsStopped();
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testPutAndQueries() throws Exception {
+        Ignite ignite = ignite(0);
+
+        IgniteCache cache1 = ignite.createCache(cacheConfiguration("cache1", ATOMIC));
+        IgniteCache cache2 = ignite.createCache(cacheConfiguration("cache2", TRANSACTIONAL));
+
+        insertData(ignite, cache1.getName());
+        insertData(ignite, cache2.getName());
+
+        IgniteInternalFuture<?> fut1 = startUpdate(cache1.getName());
+        IgniteInternalFuture<?> fut2 = startUpdate(cache2.getName());
+
+        fut1.get();
+        fut2.get();
+    }
+
+    /**
+     * @param cacheName Cache name.
+     * @return Future.
+     */
+    private IgniteInternalFuture<?> startUpdate(final String cacheName) {
+        final long stopTime = System.currentTimeMillis() + 30_000;
+
+        final AtomicInteger idx = new AtomicInteger();
+
+        return GridTestUtils.runMultiThreadedAsync(new Callable<Object>() {
+            @Override public Void call() {
+                ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+                IgniteCache cache = ignite(idx.getAndIncrement() % NODES).cache(cacheName).withKeepBinary();
+
+                while (System.currentTimeMillis() < stopTime) {
+                    switch (rnd.nextInt(5)) {
+                        case 0: {
+                            TestKey key = new TestKey(rnd.nextInt(KEYS));
+
+                            CacheEntry e = cache.getEntry(key);
+
+                            assertNotNull(e);
+                            assertTrue(e.getKey() instanceof BinaryObject);
+
+                            cache.put(e.getKey(), new TestValue(rnd.nextInt(KEYS)));
+
+                            break;
+                        }
+
+                        case 1: {
+                            Iterator<Cache.Entry> it = cache.iterator();
+
+                            for (int i = 0; i < 100 && it.hasNext(); i++) {
+                                Cache.Entry e = it.next();
+
+                                assertTrue(e.getKey() instanceof BinaryObject);
+
+                                cache.put(e.getKey(), new TestValue(rnd.nextInt(KEYS)));
+                            }
+
+                            break;
+                        }
+
+                        case 2: {
+                            SqlFieldsQuery qry = new SqlFieldsQuery("select _key " +
+                                "from \"" + cache.getName() + "\".TestValue where id=?");
+
+                            qry.setArgs(rnd.nextInt(KEYS));
+
+                            List<List> res = cache.query(qry).getAll();
+
+                            assertEquals(1, res.size());
+
+                            BinaryObject key = (BinaryObject)res.get(0).get(0);
+
+                            cache.put(key, new TestValue(rnd.nextInt(KEYS)));
+
+                            break;
+                        }
+
+                        case 3: {
+                            SqlQuery qry = new SqlQuery("TestValue", "id=?");
+
+                            qry.setArgs(rnd.nextInt(KEYS));
+
+                            List<Cache.Entry> res = cache.query(qry).getAll();
+
+                            assertEquals(1, res.size());
+
+                            break;
+                        }
+
+                        case 4: {
+                            SqlQuery qry = new SqlQuery("TestValue", "order by id");
+
+                            int cnt = 0;
+
+                            for (Cache.Entry e : (Iterable<Cache.Entry>)cache.query(qry)) {
+                                assertNotNull(cache.get(e.getKey()));
+
+                                cnt++;
+                            }
+
+                            assertTrue(cnt > 0);
+
+                            break;
+                        }
+
+                        default:
+                            fail();
+                    }
+                }
+
+                return null;
+            }
+        }, NODES * 2, "test-thread");
+    }
+
+    /**
+     * @param ignite Node.
+     * @param cacheName Cache name.
+     */
+    private void insertData(Ignite ignite, String cacheName) {
+        try (IgniteDataStreamer streamer = ignite.dataStreamer(cacheName)) {
+            for (int i = 0; i < KEYS; i++)
+                streamer.addData(new TestKey(i), new TestValue(i));
+        }
+    }
+
+    /**
+     * @param name Cache name.
+     * @param atomicityMode Cache atomicity mode.
+     * @return Cache configuration.
+     */
+    private CacheConfiguration cacheConfiguration(String name, CacheAtomicityMode atomicityMode) {
+        CacheConfiguration ccfg = new CacheConfiguration();
+
+        ccfg.setName(name);
+        ccfg.setCacheMode(PARTITIONED);
+        ccfg.setWriteSynchronizationMode(FULL_SYNC);
+        ccfg.setAtomicityMode(atomicityMode);
+        ccfg.setBackups(1);
+
+        QueryEntity qryEntity = new QueryEntity();
+
+        qryEntity.setKeyType(TestKey.class.getName());
+        qryEntity.setValueType(TestValue.class.getName());
+
+        qryEntity.addQueryField("id", Integer.class.getName(), null);
+        qryEntity.addQueryField("val", Integer.class.getName(), null);
+
+        qryEntity.setIndexes(F.asList(new QueryIndex("id"), new QueryIndex("val")));
+
+        ccfg.setQueryEntities(F.asList(qryEntity));
+
+        return ccfg;
+    }
+
+    /**
+     *
+     */
+    static class TestKey {
+        /** */
+        @QuerySqlField(index = true)
+        private int id;
+
+        /**
+         * @param id ID.
+         */
+        public TestKey(int id) {
+            this.id = id;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean equals(Object o) {
+            if (this == o)
+                return true;
+
+            if (o == null || getClass() != o.getClass())
+                return false;
+
+            TestKey testKey = (TestKey)o;
+
+            return id == testKey.id;
+        }
+
+        /** {@inheritDoc} */
+        @Override public int hashCode() {
+            return id;
+        }
+    }
+
+    /**
+     *
+     */
+    static class TestValue {
+        /** */
+        @QuerySqlField(index = true)
+        private int val;
+
+        /**
+         * @param val Value.
+         */
+        public TestValue(int val) {
+            this.val = val;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/a441bb9c/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheWithIndexingTestSuite.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheWithIndexingTestSuite.java b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheWithIndexingTestSuite.java
index 5cdd744..a264a2f 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheWithIndexingTestSuite.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheWithIndexingTestSuite.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.testsuites;
 
 import junit.framework.TestSuite;
+import org.apache.ignite.internal.processors.cache.CacheBinaryKeyConcurrentQueryTest;
 import org.apache.ignite.internal.processors.cache.CacheConfigurationP2PTest;
 import org.apache.ignite.internal.processors.cache.CacheIndexStreamerTest;
 import org.apache.ignite.internal.processors.cache.CacheIndexingOffheapCleanupTest;
@@ -77,6 +78,7 @@ public class IgniteCacheWithIndexingTestSuite extends TestSuite {
         suite.addTestSuite(CacheRandomOperationsMultithreadedTest.class);
         suite.addTestSuite(IgniteCacheStarvationOnRebalanceTest.class);
         suite.addTestSuite(CacheOperationsWithExpirationTest.class);
+        suite.addTestSuite(CacheBinaryKeyConcurrentQueryTest.class);
         suite.addTestSuite(CacheIndexingOffheapCleanupTest.class);
 
         return suite;


[18/50] [abbrv] ignite git commit: IGNITE-3389 metadata result set throws NPE when closed - fixed

Posted by vo...@apache.org.
 IGNITE-3389 metadata result set throws NPE when closed - fixed


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

Branch: refs/heads/ignite-3553
Commit: ca5c8d8051e5bfb9c17e34738e2a4555e2b8c3f8
Parents: 68891e8
Author: Alexandre Boudnik <Al...@epam.com>
Authored: Fri Jul 22 18:40:47 2016 +0300
Committer: agura <ag...@gridgain.com>
Committed: Fri Jul 22 18:42:24 2016 +0300

----------------------------------------------------------------------
 .../internal/jdbc2/JdbcMetadataSelfTest.java    | 63 ++++++++++++++------
 .../ignite/jdbc/JdbcMetadataSelfTest.java       | 18 ++++++
 .../ignite/internal/jdbc2/JdbcResultSet.java    |  2 +-
 3 files changed, 64 insertions(+), 19 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/ca5c8d80/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcMetadataSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcMetadataSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcMetadataSelfTest.java
old mode 100644
new mode 100755
index f601dbc..7184b8d
--- a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcMetadataSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcMetadataSelfTest.java
@@ -17,24 +17,33 @@
 
 package org.apache.ignite.internal.jdbc2;
 
-import org.apache.ignite.*;
-import org.apache.ignite.cache.affinity.*;
-import org.apache.ignite.cache.query.annotations.*;
-import org.apache.ignite.configuration.*;
-import org.apache.ignite.internal.util.typedef.*;
-import org.apache.ignite.spi.discovery.tcp.*;
-import org.apache.ignite.spi.discovery.tcp.ipfinder.*;
-import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.*;
-import org.apache.ignite.testframework.junits.common.*;
-
-import java.io.*;
-import java.sql.*;
-import java.util.*;
-
-import static java.sql.Types.*;
-import static org.apache.ignite.IgniteJdbcDriver.*;
-import static org.apache.ignite.cache.CacheMode.*;
-import static org.apache.ignite.cache.CacheWriteSynchronizationMode.*;
+import java.io.Serializable;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Collection;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.affinity.AffinityKey;
+import org.apache.ignite.cache.query.annotations.QuerySqlField;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.ConnectorConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+import static java.sql.Types.INTEGER;
+import static java.sql.Types.OTHER;
+import static java.sql.Types.VARCHAR;
+import static org.apache.ignite.IgniteJdbcDriver.CFG_URL_PREFIX;
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
 
 /**
  * Metadata tests.
@@ -277,6 +286,24 @@ public class JdbcMetadataSelfTest extends GridCommonAbstractTest {
     }
 
     /**
+     * @throws Exception If failed.
+     */
+    public void testMetadataResultSetClose() throws Exception {
+        try (Connection conn = DriverManager.getConnection(BASE_URL);
+             ResultSet tbls = conn.getMetaData().getTables(null, null, "%", null)) {
+            int colCnt = tbls.getMetaData().getColumnCount();
+
+            while (tbls.next()) {
+                for (int i = 0; i < colCnt; i++)
+                    tbls.getObject(i + 1);
+            }
+        }
+        catch (Exception e) {
+            fail();
+        }
+    }
+
+    /**
      * Person.
      */
     @SuppressWarnings("UnusedDeclaration")

http://git-wip-us.apache.org/repos/asf/ignite/blob/ca5c8d80/modules/clients/src/test/java/org/apache/ignite/jdbc/JdbcMetadataSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/JdbcMetadataSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/JdbcMetadataSelfTest.java
index 0deb45d..72d93c9 100644
--- a/modules/clients/src/test/java/org/apache/ignite/jdbc/JdbcMetadataSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/JdbcMetadataSelfTest.java
@@ -289,6 +289,24 @@ public class JdbcMetadataSelfTest extends GridCommonAbstractTest {
     }
 
     /**
+     * @throws Exception If failed.
+     */
+    public void testMetadataResultSetClose() throws Exception {
+        try (Connection conn = DriverManager.getConnection(URL);
+             ResultSet tbls = conn.getMetaData().getTables(null, null, "%", null)) {
+            int colCnt = tbls.getMetaData().getColumnCount();
+
+            while (tbls.next()) {
+                for (int i = 0; i < colCnt; i++)
+                    tbls.getObject(i + 1);
+            }
+        }
+        catch (Exception e) {
+            fail();
+        }
+    }
+
+    /**
      * Person.
      */
     @SuppressWarnings("UnusedDeclaration")

http://git-wip-us.apache.org/repos/asf/ignite/blob/ca5c8d80/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java
old mode 100644
new mode 100755
index 5092b42..69dddad
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java
@@ -182,7 +182,7 @@ public class JdbcResultSet implements ResultSet {
      * If this result set is associated with locally executed query then query cursor will also closed.
      */
     void closeInternal() throws SQLException  {
-        if (((JdbcConnection)stmt.getConnection()).nodeId() == null)
+        if (((JdbcConnection)stmt.getConnection()).nodeId() == null && uuid != null)
             JdbcQueryTask.remove(uuid);
 
         closed = true;


[11/50] [abbrv] ignite git commit: ignite-1232 Distributed SQL joins implementation

Posted by vo...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2RowRange.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2RowRange.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2RowRange.java
new file mode 100644
index 0000000..bc8aa75
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2RowRange.java
@@ -0,0 +1,181 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.processors.query.h2.twostep.msg;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+import org.apache.ignite.internal.GridDirectCollection;
+import org.apache.ignite.internal.util.tostring.GridToStringInclude;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.plugin.extensions.communication.Message;
+import org.apache.ignite.plugin.extensions.communication.MessageCollectionItemType;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+
+/**
+ * Range of rows.
+ */
+public class GridH2RowRange implements Message {
+    /** */
+    private static int FLAG_PARTIAL = 1;
+
+    /** */
+    private int rangeId;
+
+    /** */
+    @GridDirectCollection(Message.class)
+    @GridToStringInclude
+    private List<GridH2RowMessage> rows;
+
+    /** */
+    private byte flags;
+
+    /**
+     * @param rangeId Range ID.
+     */
+    public void rangeId(int rangeId) {
+        this.rangeId = rangeId;
+    }
+
+    /**
+     * @return Range ID.
+     */
+    public int rangeId() {
+        return rangeId;
+    }
+
+    /**
+     * @param rows Rows.
+     */
+    public void rows(List<GridH2RowMessage> rows) {
+        this.rows = rows;
+    }
+
+    /**
+     * @return Rows.
+     */
+    public List<GridH2RowMessage> rows() {
+        return rows;
+    }
+
+    /**
+     * Sets that this is a partial range.
+     */
+    public void setPartial() {
+        flags |= FLAG_PARTIAL;
+    }
+
+    /**
+     * @return {@code true} If this is a partial range.
+     */
+    public boolean isPartial() {
+        return (flags & FLAG_PARTIAL) == FLAG_PARTIAL;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+        writer.setBuffer(buf);
+
+        if (!writer.isHeaderWritten()) {
+            if (!writer.writeHeader(directType(), fieldsCount()))
+                return false;
+
+            writer.onHeaderWritten();
+        }
+
+        switch (writer.state()) {
+            case 0:
+                if (!writer.writeByte("flags", flags))
+                    return false;
+
+                writer.incrementState();
+
+            case 1:
+                if (!writer.writeInt("rangeId", rangeId))
+                    return false;
+
+                writer.incrementState();
+
+            case 2:
+                if (!writer.writeCollection("rows", rows, MessageCollectionItemType.MSG))
+                    return false;
+
+                writer.incrementState();
+
+        }
+
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+        reader.setBuffer(buf);
+
+        if (!reader.beforeMessageRead())
+            return false;
+
+        switch (reader.state()) {
+            case 0:
+                flags = reader.readByte("flags");
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
+            case 1:
+                rangeId = reader.readInt("rangeId");
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
+            case 2:
+                rows = reader.readCollection("rows", MessageCollectionItemType.MSG);
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
+        }
+
+        return reader.afterMessageRead(GridH2RowRange.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte directType() {
+        return -34;
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte fieldsCount() {
+        return 3;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void onAckReceived() {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(GridH2RowRange.class, this, "rowsSize", rows != null ? rows.size() : 0);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2RowRangeBounds.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2RowRangeBounds.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2RowRangeBounds.java
new file mode 100644
index 0000000..e32e449
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2RowRangeBounds.java
@@ -0,0 +1,188 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.processors.query.h2.twostep.msg;
+
+import java.nio.ByteBuffer;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.plugin.extensions.communication.Message;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+
+/**
+ * Bounds of row range.
+ */
+public class GridH2RowRangeBounds implements Message {
+    /** */
+    private int rangeId;
+
+    /** */
+    private GridH2RowMessage first;
+
+    /** */
+    private GridH2RowMessage last;
+
+    /**
+     * @param rangeId Range ID.
+     * @param first First.
+     * @param last Last.
+     * @return Range bounds.
+     */
+    public static GridH2RowRangeBounds rangeBounds(int rangeId, GridH2RowMessage first, GridH2RowMessage last) {
+        GridH2RowRangeBounds res = new GridH2RowRangeBounds();
+
+        res.rangeId(rangeId);
+        res.first(first);
+        res.last(last);
+
+        return res;
+    }
+
+    /**
+     * @param rangeId Range ID.
+     */
+    public void rangeId(int rangeId) {
+        this.rangeId = rangeId;
+    }
+
+    /**
+     * @return Range ID.
+     */
+    public int rangeId() {
+        return rangeId;
+    }
+
+    /**
+     * @param first First.
+     */
+    public void first(GridH2RowMessage first) {
+        this.first = first;
+    }
+
+    /**
+     * @return First.
+     */
+    public GridH2RowMessage first() {
+        return first;
+    }
+
+    /**
+     * @param last Last.
+     */
+    public void last(GridH2RowMessage last) {
+        this.last = last;
+    }
+
+    /**
+     * @return Last.
+     */
+    public GridH2RowMessage last() {
+        return last;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+        writer.setBuffer(buf);
+
+        if (!writer.isHeaderWritten()) {
+            if (!writer.writeHeader(directType(), fieldsCount()))
+                return false;
+
+            writer.onHeaderWritten();
+        }
+
+        switch (writer.state()) {
+            case 0:
+                if (!writer.writeMessage("first", first))
+                    return false;
+
+                writer.incrementState();
+
+            case 1:
+                if (!writer.writeMessage("last", last))
+                    return false;
+
+                writer.incrementState();
+
+            case 2:
+                if (!writer.writeInt("rangeId", rangeId))
+                    return false;
+
+                writer.incrementState();
+
+        }
+
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+        reader.setBuffer(buf);
+
+        if (!reader.beforeMessageRead())
+            return false;
+
+        switch (reader.state()) {
+            case 0:
+                first = reader.readMessage("first");
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
+            case 1:
+                last = reader.readMessage("last");
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
+            case 2:
+                rangeId = reader.readInt("rangeId");
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
+        }
+
+        return reader.afterMessageRead(GridH2RowRangeBounds.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte directType() {
+        return -35;
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte fieldsCount() {
+        return 3;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void onAckReceived() {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(GridH2RowRangeBounds.class, this);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Short.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Short.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Short.java
index 841e01e..ebeca9d 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Short.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Short.java
@@ -99,7 +99,7 @@ public class GridH2Short extends GridH2ValueMessage {
 
         }
 
-        return true;
+        return reader.afterMessageRead(GridH2Short.class);
     }
 
     /** {@inheritDoc} */
@@ -111,4 +111,9 @@ public class GridH2Short extends GridH2ValueMessage {
     @Override public byte fieldsCount() {
         return 1;
     }
-}
\ No newline at end of file
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return String.valueOf(x);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2String.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2String.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2String.java
index 50b4d58..f2f9fdc 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2String.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2String.java
@@ -101,7 +101,7 @@ public class GridH2String extends GridH2ValueMessage {
 
         }
 
-        return true;
+        return reader.afterMessageRead(GridH2String.class);
     }
 
     /** {@inheritDoc} */
@@ -113,4 +113,9 @@ public class GridH2String extends GridH2ValueMessage {
     @Override public byte fieldsCount() {
         return 1;
     }
-}
\ No newline at end of file
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return x;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Time.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Time.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Time.java
index 1c6c7ae..172d695 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Time.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Time.java
@@ -102,7 +102,7 @@ public class GridH2Time extends GridH2ValueMessage {
 
         }
 
-        return true;
+        return reader.afterMessageRead(GridH2Time.class);
     }
 
     /** {@inheritDoc} */
@@ -114,4 +114,9 @@ public class GridH2Time extends GridH2ValueMessage {
     @Override public byte fieldsCount() {
         return 1;
     }
-}
\ No newline at end of file
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return String.valueOf(nanos);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Timestamp.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Timestamp.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Timestamp.java
index ccdba92..b020799 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Timestamp.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Timestamp.java
@@ -50,7 +50,7 @@ public class GridH2Timestamp extends GridH2ValueMessage {
         ValueTimestamp t = (ValueTimestamp)val;
 
         date = t.getDateValue();
-        nanos = t.getNanos();
+        nanos = t.getTimeNanos();
     }
 
     /** {@inheritDoc} */
@@ -119,7 +119,7 @@ public class GridH2Timestamp extends GridH2ValueMessage {
 
         }
 
-        return true;
+        return reader.afterMessageRead(GridH2Timestamp.class);
     }
 
     /** {@inheritDoc} */
@@ -131,4 +131,9 @@ public class GridH2Timestamp extends GridH2ValueMessage {
     @Override public byte fieldsCount() {
         return 2;
     }
-}
\ No newline at end of file
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return date + "_" + nanos;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Uuid.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Uuid.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Uuid.java
index fd14d90..fa9360b 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Uuid.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Uuid.java
@@ -119,7 +119,7 @@ public class GridH2Uuid extends GridH2ValueMessage {
 
         }
 
-        return true;
+        return reader.afterMessageRead(GridH2Uuid.class);
     }
 
     /** {@inheritDoc} */
@@ -131,4 +131,9 @@ public class GridH2Uuid extends GridH2ValueMessage {
     @Override public byte fieldsCount() {
         return 2;
     }
-}
\ No newline at end of file
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return ValueUuid.get(high, low).getString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2ValueMessage.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2ValueMessage.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2ValueMessage.java
index d528c47..18f8880 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2ValueMessage.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2ValueMessage.java
@@ -52,4 +52,4 @@ public abstract class GridH2ValueMessage implements Message {
     @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
         return true;
     }
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2ValueMessageFactory.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2ValueMessageFactory.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2ValueMessageFactory.java
index d414e5a..aa84e4b 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2ValueMessageFactory.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2ValueMessageFactory.java
@@ -90,6 +90,24 @@ public class GridH2ValueMessageFactory implements MessageFactory {
 
             case -22:
                 return new GridH2CacheObject();
+
+            case -30:
+                return new GridH2IndexRangeRequest();
+
+            case -31:
+                return new GridH2IndexRangeResponse();
+
+            case -32:
+                return new GridH2RowMessage();
+
+            case -33:
+                return new GridH2QueryRequest();
+
+            case -34:
+                return new GridH2RowRange();
+
+            case -35:
+                return new GridH2RowRangeBounds();
         }
 
         return null;
@@ -118,7 +136,7 @@ public class GridH2ValueMessageFactory implements MessageFactory {
      * @return Filled array.
      * @throws IgniteCheckedException If failed.
      */
-    public static Value[] fillArray(Iterator<Message> src, Value[] dst, GridKernalContext ctx)
+    public static Value[] fillArray(Iterator<? extends Message> src, Value[] dst, GridKernalContext ctx)
         throws IgniteCheckedException {
         for (int i = 0; i < dst.length; i++) {
             Message msg = src.next();
@@ -134,7 +152,7 @@ public class GridH2ValueMessageFactory implements MessageFactory {
      * @return Message.
      * @throws IgniteCheckedException If failed.
      */
-    public static Message toMessage(Value v) throws IgniteCheckedException {
+    public static GridH2ValueMessage toMessage(Value v) throws IgniteCheckedException {
         switch (v.getType()) {
             case Value.NULL:
                 return GridH2Null.INSTANCE;

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheReduceQueryMultithreadedSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheReduceQueryMultithreadedSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheReduceQueryMultithreadedSelfTest.java
deleted file mode 100644
index 170ab65..0000000
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheReduceQueryMultithreadedSelfTest.java
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.ignite.internal.processors.cache;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-import org.apache.ignite.configuration.CacheConfiguration;
-import org.apache.ignite.configuration.IgniteConfiguration;
-import org.apache.ignite.internal.IgniteInternalFuture;
-import org.apache.ignite.internal.processors.cache.query.CacheQuery;
-import org.apache.ignite.internal.util.typedef.F;
-import org.apache.ignite.internal.util.typedef.R1;
-import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
-import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
-import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
-
-import static org.apache.ignite.cache.CacheMode.PARTITIONED;
-import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
-
-/**
- * Multithreaded reduce query tests with lots of data.
- */
-public class GridCacheReduceQueryMultithreadedSelfTest extends GridCacheAbstractSelfTest {
-    /** */
-    private static final int GRID_CNT = 5;
-
-    /** */
-    private static final int TEST_TIMEOUT = 2 * 60 * 1000;
-
-    /** */
-    private static final TcpDiscoveryIpFinder ipFinder = new TcpDiscoveryVmIpFinder(true);
-
-    /** {@inheritDoc} */
-    @Override protected int gridCount() {
-        return GRID_CNT;
-    }
-
-    /** {@inheritDoc} */
-    @Override protected long getTestTimeout() {
-        return TEST_TIMEOUT;
-    }
-
-    /** {@inheritDoc} */
-    @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
-        IgniteConfiguration cfg = super.getConfiguration(gridName);
-
-        TcpDiscoverySpi disco = new TcpDiscoverySpi();
-
-        disco.setIpFinder(ipFinder);
-
-        cfg.setDiscoverySpi(disco);
-
-        return cfg;
-    }
-
-    /** {@inheritDoc} */
-    @Override protected CacheConfiguration cacheConfiguration(String gridName) throws Exception {
-        CacheConfiguration<?,?> cfg = super.cacheConfiguration(gridName);
-
-        cfg.setCacheMode(PARTITIONED);
-        cfg.setBackups(1);
-        cfg.setWriteSynchronizationMode(FULL_SYNC);
-
-        cfg.setIndexedTypes(
-            String.class, Integer.class
-        );
-
-        return cfg;
-    }
-
-    /**
-     * @throws Exception In case of error.
-     */
-    public void testReduceQuery() throws Exception {
-        final int keyCnt = 5000;
-        final int logFreq = 500;
-
-        final GridCacheAdapter<String, Integer> c = internalCache(jcache());
-
-        final CountDownLatch startLatch = new CountDownLatch(1);
-
-        IgniteInternalFuture<?> fut1 = multithreadedAsync(new Callable() {
-            @Override public Object call() throws Exception {
-                for (int i = 1; i < keyCnt; i++) {
-                    c.getAndPut(String.valueOf(i), i);
-
-                    startLatch.countDown();
-
-                    if (i % logFreq == 0)
-                        info("Stored entries: " + i);
-                }
-
-                return null;
-            }
-        }, 1);
-
-        // Create query.
-        final CacheQuery<List<?>> sumQry = c.context().queries().
-            createSqlFieldsQuery("select _val from Integer", false).timeout(TEST_TIMEOUT);
-
-        final R1<List<?>, Integer> rmtRdc = new R1<List<?>, Integer>() {
-            /** */
-            private AtomicInteger sum = new AtomicInteger();
-
-            @Override public boolean collect(List<?> e) {
-                sum.addAndGet((Integer)e.get(0));
-
-                return true;
-            }
-
-            @Override public Integer reduce() {
-                return sum.get();
-            }
-        };
-
-        final AtomicBoolean stop = new AtomicBoolean();
-
-        startLatch.await();
-
-        IgniteInternalFuture<?> fut2 = multithreadedAsync(new Callable() {
-            @Override public Object call() throws Exception {
-                int cnt = 0;
-
-                while (!stop.get()) {
-                    Collection<Integer> res = sumQry.execute(rmtRdc).get();
-
-                    int sum = F.sumInt(res);
-
-                    cnt++;
-
-                    assertTrue(sum > 0);
-
-                    if (cnt % logFreq == 0) {
-                        info("Reduced value: " + sum);
-                        info("Executed queries: " + cnt);
-                    }
-                }
-
-                return null;
-            }
-        }, 1);
-
-        fut1.get();
-
-        stop.set(true);
-
-        fut2.get();
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractQuerySelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractQuerySelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractQuerySelfTest.java
index 65d479d..7c5b472 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractQuerySelfTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractQuerySelfTest.java
@@ -69,7 +69,6 @@ import org.apache.ignite.events.Event;
 import org.apache.ignite.internal.IgniteKernal;
 import org.apache.ignite.internal.binary.BinaryMarshaller;
 import org.apache.ignite.internal.processors.cache.distributed.replicated.IgniteCacheReplicatedQuerySelfTest;
-import org.apache.ignite.internal.processors.cache.query.GridCacheQueryManager;
 import org.apache.ignite.internal.processors.cache.query.QueryCursorEx;
 import org.apache.ignite.internal.processors.query.GridQueryFieldMetadata;
 import org.apache.ignite.internal.util.tostring.GridToStringExclude;
@@ -969,22 +968,6 @@ public abstract class IgniteCacheAbstractQuerySelfTest extends GridCommonAbstrac
     /**
      * @throws Exception If failed.
      */
-    public void testEmptyObject() throws Exception {
-        IgniteCache<EmptyObject, EmptyObject> cache = ignite().cache(null);
-
-        cache.put(new EmptyObject(1), new EmptyObject(2));
-
-        for (int i = 0; i < gridCount(); i++) {
-            GridCacheQueryManager<Object, Object> qryMgr =
-                ((IgniteKernal)grid(i)).internalCache().context().queries();
-
-            assert !hasIndexTable(EmptyObject.class, qryMgr);
-        }
-    }
-
-    /**
-     * @throws Exception If failed.
-     */
     public void testPrimitiveType() throws Exception {
         IgniteCache<Integer, Integer> cache = ignite().cache(null);
 
@@ -1536,7 +1519,7 @@ public abstract class IgniteCacheAbstractQuerySelfTest extends GridCommonAbstrac
         }
 
         try {
-            IgniteCache<UUID, Person> cache = ignite().cache(null);
+            IgniteCache<UUID,Person> cache = ignite().cache(null);
 
             for (int i = 1; i <= 20; i++)
                 cache.put(UUID.randomUUID(), new Person("Person " + i, i));
@@ -1555,17 +1538,6 @@ public abstract class IgniteCacheAbstractQuerySelfTest extends GridCommonAbstrac
     }
 
     /**
-     * @param cls Class to check index table for.
-     * @param qryMgr Query manager.
-     * @return {@code true} if index has a table for given class.
-     * @throws IgniteCheckedException If failed.
-     */
-    private boolean hasIndexTable(Class<?> cls, GridCacheQueryManager<Object, Object> qryMgr)
-        throws IgniteCheckedException {
-        return qryMgr.size(cls) != -1;
-    }
-
-    /**
      *
      */
     private static class ArrayObject implements Serializable {

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheCrossCacheJoinRandomTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheCrossCacheJoinRandomTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheCrossCacheJoinRandomTest.java
new file mode 100644
index 0000000..4d5a44e
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheCrossCacheJoinRandomTest.java
@@ -0,0 +1,442 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.processors.cache;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Stack;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.QueryEntity;
+import org.apache.ignite.cache.QueryIndex;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.processors.query.h2.sql.AbstractH2CompareQueryTest;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.T2;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.jetbrains.annotations.Nullable;
+
+import static org.apache.ignite.cache.CacheAtomicWriteOrderMode.PRIMARY;
+import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC;
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+import static org.apache.ignite.cache.CacheMode.REPLICATED;
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+
+/**
+ *
+ */
+public class IgniteCacheCrossCacheJoinRandomTest extends AbstractH2CompareQueryTest {
+    /** */
+    private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
+
+    /** */
+    private boolean client;
+
+    /** */
+    private static final int OBJECTS = 200;
+
+    /** */
+    private static final int MAX_CACHES = 5;
+
+    /** */
+    private static Random rnd;
+
+    /** */
+    private static List<Map<Integer, Integer>> cachesData;
+
+    /** */
+    private static final List<T2<CacheMode, Integer>> MODES_1 = F.asList(
+        //new T2<>(REPLICATED, 0),
+        new T2<>(PARTITIONED, 0),
+        new T2<>(PARTITIONED, 1),
+        new T2<>(PARTITIONED, 2));
+
+    /** */
+    private static final List<T2<CacheMode, Integer>> MODES_2 = F.asList(
+        //new T2<>(REPLICATED, 0),
+        new T2<>(PARTITIONED, 0),
+        new T2<>(PARTITIONED, 1));
+
+    /** {@inheritDoc} */
+    @Override protected void setIndexedTypes(CacheConfiguration<?, ?> cc, CacheMode mode) {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void initCacheAndDbData() throws Exception {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void checkAllDataEquals() throws Exception {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override protected long getTestTimeout() {
+        return 10 * 60_000;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(gridName);
+
+        TcpDiscoverySpi spi = ((TcpDiscoverySpi)cfg.getDiscoverySpi());
+
+        spi.setIpFinder(IP_FINDER);
+
+        cfg.setClientMode(client);
+
+        return cfg;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        super.beforeTestsStarted();
+
+        client = true;
+
+        startGrid(SRVS);
+
+        long seed = System.currentTimeMillis();
+
+        rnd = new Random(seed);
+
+        log.info("Random seed: " + seed);
+
+        cachesData = new ArrayList<>(MAX_CACHES);
+
+        for (int i = 0; i < MAX_CACHES; i++) {
+            Map<Integer, Integer> data = createData(OBJECTS * 2);
+
+            insertH2(data, i);
+
+            cachesData.add(data);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTestsStopped() throws Exception {
+        stopAllGrids();
+
+        cachesData = null;
+
+        super.afterTestsStopped();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected Statement initializeH2Schema() throws SQLException {
+        Statement st = super.initializeH2Schema();
+
+        for (int i = 0; i < MAX_CACHES; i++) {
+            st.execute("CREATE SCHEMA \"cache" + i + "\"");
+
+            st.execute("create table \"cache" + i + "\".TESTOBJECT" +
+                "  (_key int not null," +
+                "  _val other not null," +
+                "  parentId int)");
+        }
+
+        return st;
+    }
+
+    /**
+     * @param name Cache name.
+     * @param cacheMode Cache mode.
+     * @param backups Number of backups.
+     * @return Cache configuration.
+     */
+    private CacheConfiguration configuration(String name, CacheMode cacheMode, int backups) {
+        CacheConfiguration ccfg = new CacheConfiguration();
+
+        ccfg.setName(name);
+        ccfg.setWriteSynchronizationMode(FULL_SYNC);
+        ccfg.setAtomicWriteOrderMode(PRIMARY);
+        ccfg.setAtomicityMode(ATOMIC);
+        ccfg.setCacheMode(cacheMode);
+
+        if (cacheMode == PARTITIONED)
+            ccfg.setBackups(backups);
+
+        QueryEntity entity = new QueryEntity();
+        entity.setKeyType(Integer.class.getName());
+        entity.setValueType(TestObject.class.getName());
+        entity.addQueryField("parentId", Integer.class.getName(), null);
+        entity.setIndexes(F.asList(new QueryIndex("parentId")));
+
+        ccfg.setQueryEntities(F.asList(entity));
+
+        return ccfg;
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoin2Caches() throws Exception {
+        testJoin(2, MODES_1);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoin3Caches() throws Exception {
+        testJoin(3, MODES_1);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoin4Caches() throws Exception {
+        testJoin(4, MODES_2);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoin5Caches() throws Exception {
+        testJoin(5, MODES_2);
+    }
+
+    /**
+     * @param caches Number of caches.
+     * @param allModes Cache modes.
+     * @throws Exception If failed.
+     */
+    private void testJoin(int caches, List<T2<CacheMode, Integer>> allModes) throws Exception {
+        checkJoin(cachesData, allModes, new Stack<T2<CacheMode, Integer>>(), caches);
+    }
+
+    /**
+     * @param cachesData Caches data.
+     * @param allModes Modes to test.
+     * @param modes Select modes.
+     * @param caches Caches number.
+     * @throws Exception If failed.
+     */
+    private void checkJoin(List<Map<Integer, Integer>> cachesData,
+        List<T2<CacheMode, Integer>> allModes,
+        Stack<T2<CacheMode, Integer>> modes,
+        int caches) throws Exception {
+        if (modes.size() == caches) {
+            List<CacheConfiguration> ccfgs = new ArrayList<>();
+
+            for (int i = 0; i < modes.size(); i++) {
+                T2<CacheMode, Integer> mode = modes.get(i);
+
+                CacheConfiguration ccfg = configuration("cache" + i, mode.get1(), mode.get2());
+
+                ccfgs.add(ccfg);
+            }
+
+            log.info("Check configurations: " + modes);
+
+            checkJoinQueries(ccfgs, cachesData);
+        }
+        else {
+            for (T2<CacheMode, Integer> mode : allModes) {
+                modes.push(mode);
+
+                checkJoin(cachesData, allModes, modes, caches);
+
+                modes.pop();
+            }
+        }
+    }
+
+    /**
+     * @param ccfgs Configurations.
+     * @param cachesData Caches data.
+     * @throws Exception If failed.
+     */
+    private void checkJoinQueries(List<CacheConfiguration> ccfgs, List<Map<Integer, Integer>> cachesData) throws Exception {
+        Ignite client = ignite(SRVS);
+
+        final int CACHES = ccfgs.size();
+
+        try {
+            IgniteCache cache = null;
+
+            boolean hasReplicated = false;
+
+            for (int i = 0; i < CACHES; i++) {
+                CacheConfiguration ccfg = ccfgs.get(i);
+
+                IgniteCache cache0 = client.createCache(ccfg);
+
+                if (ccfg.getCacheMode() == REPLICATED)
+                    hasReplicated = true;
+
+                if (cache == null && ccfg.getCacheMode() == PARTITIONED)
+                    cache = cache0;
+
+                insertCache(cachesData.get(i), cache0);
+            }
+
+            boolean distributedJoin = true;
+
+            // Do not use distributed join if all caches are REPLICATED.
+            if (cache == null) {
+                cache = client.cache(ccfgs.get(0).getName());
+
+                distributedJoin = false;
+            }
+
+            Object[] args = {};
+
+            compareQueryRes0(cache, createQuery(CACHES, false, null), distributedJoin, false, args, Ordering.RANDOM);
+
+            if (!hasReplicated) {
+                compareQueryRes0(cache, createQuery(CACHES, false, null), distributedJoin, true, args, Ordering.RANDOM);
+
+                compareQueryRes0(cache, createQuery(CACHES, true, null), distributedJoin, true, args, Ordering.RANDOM);
+            }
+
+            Map<Integer, Integer> data = cachesData.get(CACHES - 1);
+
+            final int QRY_CNT = CACHES > 4 ? 2 : 50;
+
+            int cnt = 0;
+
+            for (Integer objId : data.keySet()) {
+                compareQueryRes0(cache, createQuery(CACHES, false, objId), distributedJoin, false, args, Ordering.RANDOM);
+
+                if (!hasReplicated) {
+                    compareQueryRes0(cache, createQuery(CACHES, false, objId), distributedJoin, true, args, Ordering.RANDOM);
+
+                    compareQueryRes0(cache, createQuery(CACHES, true, objId), distributedJoin, true, args, Ordering.RANDOM);
+                }
+
+                if (cnt++ == QRY_CNT)
+                    break;
+            }
+        }
+        finally {
+            for (CacheConfiguration ccfg : ccfgs)
+                client.destroyCache(ccfg.getName());
+        }
+    }
+
+    /**
+     * @param caches Number of caches to join.
+     * @param outer If {@code true} creates outer join query, otherwise inner join.
+     * @param objId Object ID.
+     * @return SQL.
+     */
+    @SuppressWarnings("StringConcatenationInsideStringBufferAppend")
+    private String createQuery(int caches, boolean outer, @Nullable Integer objId) {
+        StringBuilder qry = new StringBuilder("select ");
+
+        for (int i = 0; i < caches; i++) {
+            if (i != 0)
+                qry.append(", ");
+
+            qry.append("o" + i + "._key");
+        }
+
+        qry.append(" from \"cache0\".TestObject o0 ");
+
+        for (int i = 1; i < caches; i++) {
+            String cacheName = "cache" + i;
+
+            String cur = "o" + i;
+            String prev = "o" + (i - 1);
+
+            qry.append(outer ? "left outer join " : "inner join ");
+            qry.append("\"" + cacheName + "\".TestObject " + cur);
+
+            if (i == caches - 1 && objId != null)
+                qry.append(" on (" + prev + ".parentId=" + cur + "._key and " + cur + "._key=" + objId + ") ");
+            else
+                qry.append(" on (" + prev + ".parentId=" + cur + "._key) ");
+        }
+
+        return qry.toString();
+    }
+
+    /**
+     * @param data Data.
+     * @param cache Cache.
+     */
+    private void insertCache(Map<Integer, Integer> data, IgniteCache<Object, Object> cache) {
+        for (Map.Entry<Integer, Integer> e : data.entrySet())
+            cache.put(e.getKey(), new TestObject(e.getValue()));
+    }
+
+    /**
+     * @param data Data.
+     * @param cache Cache index.
+     * @throws Exception If failed.
+     */
+    private void insertH2(Map<Integer, Integer> data, int cache) throws Exception {
+        for (Map.Entry<Integer, Integer> e : data.entrySet()) {
+            try (PreparedStatement st = conn.prepareStatement("insert into \"cache" + cache + "\".TESTOBJECT " +
+                "(_key, _val, parentId) values(?, ?, ?)")) {
+                st.setObject(1, e.getKey());
+                st.setObject(2, new TestObject(e.getValue()));
+                st.setObject(3, e.getValue());
+
+                st.executeUpdate();
+            }
+        }
+    }
+
+    /**
+     * @param cnt Objects count.
+     * @return Generated data.
+     */
+    private Map<Integer, Integer> createData(int cnt) {
+        Map<Integer, Integer> res = new LinkedHashMap<>();
+
+        while (res.size() < cnt)
+            res.put(rnd.nextInt(cnt), rnd.nextInt(OBJECTS + 1));
+
+        return res;
+    }
+
+    /**
+     *
+     */
+    static class TestObject implements Serializable {
+        /** */
+        int parentId;
+
+        /**
+         * @param parentId Parent object ID.
+         */
+        public TestObject(int parentId) {
+            this.parentId = parentId;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return S.toString(TestObject.class, this);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheDistributedJoinCollocatedAndNotTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheDistributedJoinCollocatedAndNotTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheDistributedJoinCollocatedAndNotTest.java
new file mode 100644
index 0000000..7881c44
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheDistributedJoinCollocatedAndNotTest.java
@@ -0,0 +1,365 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.processors.cache;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.CacheKeyConfiguration;
+import org.apache.ignite.cache.QueryEntity;
+import org.apache.ignite.cache.QueryIndex;
+import org.apache.ignite.cache.affinity.Affinity;
+import org.apache.ignite.cache.affinity.AffinityKeyMapped;
+import org.apache.ignite.cache.query.QueryCursor;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+import static org.apache.ignite.cache.CacheAtomicWriteOrderMode.PRIMARY;
+import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC;
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+
+/**
+ *
+ */
+public class IgniteCacheDistributedJoinCollocatedAndNotTest extends GridCommonAbstractTest {
+    /** */
+    private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
+
+    /** */
+    private static final String PERSON_CACHE = "person";
+
+    /** */
+    private static final String ORG_CACHE = "org";
+
+    /** */
+    private static final String ACCOUNT_CACHE = "acc";
+
+    /** */
+    private boolean client;
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(gridName);
+
+        CacheKeyConfiguration keyCfg = new CacheKeyConfiguration(PersonKey.class.getName(), "affKey");
+
+        cfg.setCacheKeyConfiguration(keyCfg);
+
+        TcpDiscoverySpi spi = ((TcpDiscoverySpi)cfg.getDiscoverySpi());
+
+        spi.setIpFinder(IP_FINDER);
+
+        List<CacheConfiguration> ccfgs = new ArrayList<>();
+
+        {
+            CacheConfiguration ccfg = configuration(PERSON_CACHE);
+
+            QueryEntity entity = new QueryEntity();
+            entity.setKeyType(PersonKey.class.getName());
+            entity.setValueType(Person.class.getName());
+            entity.addQueryField("id", Integer.class.getName(), null);
+            entity.addQueryField("affKey", Integer.class.getName(), null);
+            entity.addQueryField("name", String.class.getName(), null);
+
+            ccfg.setQueryEntities(F.asList(entity));
+
+            ccfgs.add(ccfg);
+        }
+
+        {
+            CacheConfiguration ccfg = configuration(ORG_CACHE);
+
+            QueryEntity entity = new QueryEntity();
+            entity.setKeyType(Integer.class.getName());
+            entity.setValueType(Organization.class.getName());
+            entity.addQueryField("name", String.class.getName(), null);
+            entity.setIndexes(F.asList(new QueryIndex("name")));
+
+            ccfg.setQueryEntities(F.asList(entity));
+
+            ccfgs.add(ccfg);
+        }
+
+        {
+            CacheConfiguration ccfg = configuration(ACCOUNT_CACHE);
+
+            QueryEntity entity = new QueryEntity();
+            entity.setKeyType(Integer.class.getName());
+            entity.setValueType(Account.class.getName());
+            entity.addQueryField("personId", Integer.class.getName(), null);
+            entity.addQueryField("name", String.class.getName(), null);
+            entity.setIndexes(F.asList(new QueryIndex("personId"), new QueryIndex("name")));
+
+            ccfg.setQueryEntities(F.asList(entity));
+
+            ccfgs.add(ccfg);
+        }
+
+        cfg.setCacheConfiguration(ccfgs.toArray(new CacheConfiguration[ccfgs.size()]));
+
+        cfg.setClientMode(client);
+
+        return cfg;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        super.beforeTestsStarted();
+
+        startGridsMultiThreaded(2);
+
+        client = true;
+
+        startGrid(2);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTestsStopped() throws Exception {
+        stopAllGrids();
+
+        super.afterTestsStopped();
+    }
+
+    /**
+     * @param name Cache name.
+     * @return Cache configuration.
+     */
+    private CacheConfiguration configuration(String name) {
+        CacheConfiguration ccfg = new CacheConfiguration();
+
+        ccfg.setName(name);
+        ccfg.setWriteSynchronizationMode(FULL_SYNC);
+        ccfg.setAtomicWriteOrderMode(PRIMARY);
+        ccfg.setAtomicityMode(ATOMIC);
+        ccfg.setBackups(1);
+
+        return ccfg;
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoin() throws Exception {
+        Ignite client = grid(2);
+
+        IgniteCache<Object, Object> personCache = client.cache(PERSON_CACHE);
+        IgniteCache<Object, Object> orgCache = client.cache(ORG_CACHE);
+        IgniteCache<Object, Object> accCache = client.cache(ACCOUNT_CACHE);
+
+        Affinity<Object> aff = client.affinity(PERSON_CACHE);
+
+        AtomicInteger orgKey = new AtomicInteger();
+        AtomicInteger accKey = new AtomicInteger();
+
+        ClusterNode node0 = ignite(0).cluster().localNode();
+        ClusterNode node1 = ignite(1).cluster().localNode();
+
+        /**
+         * One organization, one person, two accounts.
+         */
+
+        int orgId1 = keyForNode(aff, orgKey, node0);
+
+        orgCache.put(orgId1, new Organization("obj-" + orgId1));
+
+        personCache.put(new PersonKey(1, orgId1), new Person(1, "o1-p1"));
+        personCache.put(new PersonKey(2, orgId1), new Person(2, "o1-p2"));
+
+        accCache.put(keyForNode(aff, accKey, node0), new Account(1, "a0"));
+        accCache.put(keyForNode(aff, accKey, node1), new Account(1, "a1"));
+
+        // Join on affinity keys equals condition should not be distributed.
+        String qry = "select o.name, p._key, p.name " +
+            "from \"org\".Organization o, \"person\".Person p " +
+            "where p.affKey = o._key";
+
+        assertFalse(plan(qry, orgCache, false).contains("batched"));
+
+        checkQuery(qry, orgCache, false, 2);
+
+        checkQuery("select o.name, p._key, p.name, a.name " +
+            "from \"org\".Organization o, \"person\".Person p, \"acc\".Account a " +
+            "where p.affKey = o._key and p.id = a.personId", orgCache, true, 2);
+    }
+
+    /**
+     * @param sql SQL.
+     * @param cache Cache.
+     * @param enforceJoinOrder Enforce join order flag.
+     * @return Query plan.
+     */
+    private String plan(String sql,
+        IgniteCache<?, ?> cache,
+        boolean enforceJoinOrder) {
+        return (String)cache.query(new SqlFieldsQuery("explain " + sql)
+            .setDistributedJoins(true)
+            .setEnforceJoinOrder(enforceJoinOrder))
+            .getAll().get(0).get(0);
+    }
+
+    /**
+     * @param sql SQL.
+     * @param cache Cache.
+     * @param enforceJoinOrder Enforce join order flag.
+     * @param expSize Expected results size.
+     */
+    private void checkQuery(String sql,
+        IgniteCache<Object, Object> cache,
+        boolean enforceJoinOrder,
+        int expSize) {
+        String plan = (String)cache.query(new SqlFieldsQuery("explain " + sql)
+            .setDistributedJoins(true)
+            .setEnforceJoinOrder(enforceJoinOrder))
+            .getAll().get(0).get(0);
+
+        log.info("Plan: " + plan);
+
+        SqlFieldsQuery qry = new SqlFieldsQuery(sql);
+
+        qry.setDistributedJoins(true);
+        qry.setEnforceJoinOrder(enforceJoinOrder);
+
+        QueryCursor<List<?>> cur = cache.query(qry);
+
+        List<List<?>> res = cur.getAll();
+
+        if (expSize != res.size())
+            log.info("Results: " + res);
+
+        assertEquals(expSize, res.size());
+    }
+    /**
+     *
+     */
+    public static class PersonKey {
+        /** */
+        private int id;
+
+        /** */
+        @AffinityKeyMapped
+        private int affKey;
+
+        /**
+         * @param id Key.
+         * @param affKey Affinity key.
+         */
+        public PersonKey(int id, int affKey) {
+            this.id = id;
+            this.affKey = affKey;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean equals(Object o) {
+            if (this == o)
+                return true;
+
+            if (o == null || getClass() != o.getClass())
+                return false;
+
+            PersonKey other = (PersonKey)o;
+
+            return id == other.id;
+        }
+
+        /** {@inheritDoc} */
+        @Override public int hashCode() {
+            return id;
+        }
+    }
+
+    /**
+     *
+     */
+    private static class Account implements Serializable {
+        /** */
+        int personId;
+
+        /** */
+        String name;
+
+        /**
+         * @param personId Person ID.
+         * @param name Name.
+         */
+        public Account(int personId, String name) {
+            this.personId = personId;
+            this.name = name;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return S.toString(Account.class, this);
+        }
+    }
+
+    /**
+     *
+     */
+    private static class Person implements Serializable {
+        /** */
+        int id;
+
+        /** */
+        String name;
+
+        /**
+         * @param id Person ID.
+         * @param name Name.
+         */
+        public Person(int id, String name) {
+            this.id = id;
+            this.name = name;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return S.toString(Person.class, this);
+        }
+    }
+
+    /**
+     *
+     */
+    private static class Organization implements Serializable {
+        /** */
+        String name;
+
+        /**
+         * @param name Name.
+         */
+        public Organization(String name) {
+            this.name = name;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return S.toString(Organization.class, this);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheDistributedJoinCustomAffinityMapper.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheDistributedJoinCustomAffinityMapper.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheDistributedJoinCustomAffinityMapper.java
new file mode 100644
index 0000000..1d4f7b2
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheDistributedJoinCustomAffinityMapper.java
@@ -0,0 +1,262 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.processors.cache;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import javax.cache.CacheException;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.QueryEntity;
+import org.apache.ignite.cache.QueryIndex;
+import org.apache.ignite.cache.affinity.AffinityKeyMapper;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+import static org.apache.ignite.cache.CacheAtomicWriteOrderMode.PRIMARY;
+import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC;
+import static org.apache.ignite.cache.CacheMode.REPLICATED;
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+
+/**
+ *
+ */
+public class IgniteCacheDistributedJoinCustomAffinityMapper extends GridCommonAbstractTest {
+    /** */
+    private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
+
+    /** */
+    private static final String PERSON_CACHE = "person";
+
+    /** */
+    private static final String PERSON_CACHE_CUSTOM_AFF = "personCustomAff";
+
+    /** */
+    private static final String ORG_CACHE = "org";
+
+    /** */
+    private static final String ORG_CACHE_REPL_CUSTOM = "orgReplCustomAff";
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(gridName);
+
+        ((TcpDiscoverySpi)cfg.getDiscoverySpi()).setIpFinder(IP_FINDER);
+
+        List<CacheConfiguration> ccfgs = new ArrayList<>();
+
+        {
+            CacheConfiguration ccfg = configuration(PERSON_CACHE);
+
+            QueryEntity entity = new QueryEntity();
+            entity.setKeyType(Integer.class.getName());
+            entity.setValueType(Person.class.getName());
+            entity.addQueryField("orgId", Integer.class.getName(), null);
+            entity.setIndexes(F.asList(new QueryIndex("orgId")));
+
+            ccfg.setQueryEntities(F.asList(entity));
+
+            ccfgs.add(ccfg);
+        }
+
+        {
+            CacheConfiguration ccfg = configuration(PERSON_CACHE_CUSTOM_AFF);
+
+            ccfg.setAffinityMapper(new TestMapper());
+
+            QueryEntity entity = new QueryEntity();
+            entity.setKeyType(Integer.class.getName());
+            entity.setValueType(Person.class.getName());
+            entity.addQueryField("orgId", Integer.class.getName(), null);
+            entity.setIndexes(F.asList(new QueryIndex("orgId")));
+
+            ccfg.setQueryEntities(F.asList(entity));
+
+            ccfgs.add(ccfg);
+        }
+
+        {
+            CacheConfiguration ccfg = configuration(ORG_CACHE);
+
+            QueryEntity entity = new QueryEntity();
+            entity.setKeyType(Integer.class.getName());
+            entity.setValueType(Organization.class.getName());
+
+            ccfg.setQueryEntities(F.asList(entity));
+
+            ccfgs.add(ccfg);
+        }
+
+        {
+            CacheConfiguration ccfg = configuration(ORG_CACHE_REPL_CUSTOM);
+
+            ccfg.setCacheMode(REPLICATED);
+            ccfg.setAffinityMapper(new TestMapper());
+
+            QueryEntity entity = new QueryEntity();
+            entity.setKeyType(Integer.class.getName());
+            entity.setValueType(Organization.class.getName());
+
+            ccfg.setQueryEntities(F.asList(entity));
+
+            ccfgs.add(ccfg);
+        }
+
+        cfg.setCacheConfiguration(ccfgs.toArray(new CacheConfiguration[ccfgs.size()]));
+
+        return cfg;
+    }
+
+    /**
+     * @param name Cache name.
+     * @return Cache configuration.
+     */
+    private CacheConfiguration configuration(String name) {
+        CacheConfiguration ccfg = new CacheConfiguration();
+
+        ccfg.setName(name);
+        ccfg.setWriteSynchronizationMode(FULL_SYNC);
+        ccfg.setAtomicWriteOrderMode(PRIMARY);
+        ccfg.setAtomicityMode(ATOMIC);
+        ccfg.setBackups(0);
+
+        return ccfg;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        super.beforeTestsStarted();
+
+        startGridsMultiThreaded(3);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTestsStopped() throws Exception {
+        stopAllGrids();
+
+        super.afterTestsStopped();
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoinCustomAffinityMapper() throws Exception {
+        Ignite ignite = ignite(0);
+
+        IgniteCache<Object, Object> cache = ignite.cache(PERSON_CACHE);
+
+        checkQueryFails(cache, "select o._key k1, p._key k2 " +
+            "from \"org\".Organization o, \"personCustomAff\".Person p where o._key=p.orgId", false);
+
+        checkQueryFails(cache, "select o._key k1, p._key k2 " +
+            "from \"personCustomAff\".Person p, \"org\".Organization o where o._key=p.orgId", false);
+
+        {
+            // Check regular query does not fail.
+            SqlFieldsQuery qry = new SqlFieldsQuery("select o._key k1, p._key k2 " +
+                "from \"org\".Organization o, \"personCustomAff\".Person p where o._key=p.orgId");
+
+            cache.query(qry).getAll();
+        }
+
+        {
+            // Should not check affinity for replicated cache.
+            SqlFieldsQuery qry = new SqlFieldsQuery("select o1._key k1, p._key k2, o2._key k3 " +
+                "from \"org\".Organization o1, \"person\".Person p, \"orgReplCustomAff\".Organization o2 where " +
+                "o1._key=p.orgId and o2._key=p.orgId");
+
+            cache.query(qry).getAll();
+        }
+    }
+
+    /**
+     * @param cache Cache.
+     * @param sql SQL.
+     * @param enforceJoinOrder Enforce join order flag.
+     */
+    private void checkQueryFails(final IgniteCache<Object, Object> cache,
+        String sql,
+        boolean enforceJoinOrder) {
+        final SqlFieldsQuery qry = new SqlFieldsQuery(sql);
+
+        qry.setDistributedJoins(true);
+        qry.setEnforceJoinOrder(enforceJoinOrder);
+
+        Throwable err = GridTestUtils.assertThrows(log, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                cache.query(qry);
+
+                return null;
+            }
+        }, CacheException.class, null);
+
+        assertTrue("Unexpected error message: " + err.getMessage(),
+            err.getMessage().contains("can not use distributed joins for cache with custom AffinityKeyMapper configured."));
+    }
+
+    /**
+     *
+     */
+    static class TestMapper implements AffinityKeyMapper {
+        /** {@inheritDoc} */
+        @Override public Object affinityKey(Object key) {
+            return key;
+        }
+
+        /** {@inheritDoc} */
+        @Override public void reset() {
+            // No-op.
+        }
+    }
+    /**
+     *
+     */
+    private static class Person implements Serializable {
+        /** */
+        int orgId;
+
+        /**
+         * @param orgId Organization ID.
+         */
+        public Person(int orgId) {
+            this.orgId = orgId;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return S.toString(Person.class, this);
+        }
+    }
+
+    /**
+     *
+     */
+    private static class Organization implements Serializable {
+        // No-op.
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheDistributedJoinNoIndexTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheDistributedJoinNoIndexTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheDistributedJoinNoIndexTest.java
new file mode 100644
index 0000000..95c56fa
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheDistributedJoinNoIndexTest.java
@@ -0,0 +1,299 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.processors.cache;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.cache.CacheException;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.QueryEntity;
+import org.apache.ignite.cache.affinity.Affinity;
+import org.apache.ignite.cache.query.QueryCursor;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+import static org.apache.ignite.cache.CacheAtomicWriteOrderMode.PRIMARY;
+import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC;
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+
+/**
+ *
+ */
+public class IgniteCacheDistributedJoinNoIndexTest extends GridCommonAbstractTest {
+    /** */
+    private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
+
+    /** */
+    private static final String PERSON_CACHE = "person";
+
+    /** */
+    private static final String ORG_CACHE = "org";
+
+    /** */
+    private boolean client;
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(gridName);
+
+        TcpDiscoverySpi spi = ((TcpDiscoverySpi)cfg.getDiscoverySpi());
+
+        spi.setIpFinder(IP_FINDER);
+
+        List<CacheConfiguration> ccfgs = new ArrayList<>();
+
+        {
+            CacheConfiguration ccfg = configuration(PERSON_CACHE);
+
+            QueryEntity entity = new QueryEntity();
+            entity.setKeyType(Integer.class.getName());
+            entity.setValueType(Person.class.getName());
+            entity.addQueryField("orgId", Integer.class.getName(), null);
+            entity.addQueryField("orgName", String.class.getName(), null);
+
+            ccfg.setQueryEntities(F.asList(entity));
+
+            ccfgs.add(ccfg);
+        }
+
+        {
+            CacheConfiguration ccfg = configuration(ORG_CACHE);
+
+            QueryEntity entity = new QueryEntity();
+            entity.setKeyType(Integer.class.getName());
+            entity.setValueType(Organization.class.getName());
+            entity.addQueryField("name", String.class.getName(), null);
+
+            ccfg.setQueryEntities(F.asList(entity));
+
+            ccfgs.add(ccfg);
+        }
+
+        cfg.setCacheConfiguration(ccfgs.toArray(new CacheConfiguration[ccfgs.size()]));
+
+        cfg.setClientMode(client);
+
+        return cfg;
+    }
+
+    /**
+     * @param name Cache name.
+     * @return Cache configuration.
+     */
+    private CacheConfiguration configuration(String name) {
+        CacheConfiguration ccfg = new CacheConfiguration();
+
+        ccfg.setName(name);
+        ccfg.setWriteSynchronizationMode(FULL_SYNC);
+        ccfg.setAtomicWriteOrderMode(PRIMARY);
+        ccfg.setAtomicityMode(ATOMIC);
+        ccfg.setBackups(0);
+
+        return ccfg;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        super.beforeTestsStarted();
+
+        startGridsMultiThreaded(2);
+
+        client = true;
+
+        startGrid(2);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTestsStopped() throws Exception {
+        stopAllGrids();
+
+        super.afterTestsStopped();
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoin() throws Exception {
+        Ignite client = grid(2);
+
+        Affinity<Object> aff = client.affinity(PERSON_CACHE);
+
+        final IgniteCache<Object, Object> personCache = client.cache(PERSON_CACHE);
+        IgniteCache<Object, Object> orgCache = client.cache(ORG_CACHE);
+
+        AtomicInteger pKey = new AtomicInteger(100_000);
+        AtomicInteger orgKey = new AtomicInteger();
+
+        ClusterNode node0 = ignite(0).cluster().localNode();
+        ClusterNode node1 = ignite(1).cluster().localNode();
+
+        for (int i = 0; i < 3; i++) {
+            int orgId = keyForNode(aff, orgKey, node0);
+
+            orgCache.put(orgId, new Organization("org-" + i));
+
+            for (int j = 0; j < i; j++)
+                personCache.put(keyForNode(aff, pKey, node1), new Person(orgId, "org-" + i));
+        }
+
+        checkNoIndexError(personCache, "select o.name, p._key, p.orgName " +
+            "from \"org\".Organization o, \"person\".Person p " +
+            "where p.orgName = o.name");
+
+        checkNoIndexError(personCache, "select o.name, p._key, p.orgName " +
+            "from \"org\".Organization o inner join \"person\".Person p " +
+            "on p.orgName = o.name");
+
+        checkNoIndexError(personCache, "select o.name, p._key, p.orgName " +
+            "from \"org\".Organization o, \"person\".Person p " +
+            "where p.orgName > o.name");
+
+        checkNoIndexError(personCache, "select o.name, p._key, p.orgName " +
+            "from (select * from \"org\".Organization) o, \"person\".Person p " +
+            "where p.orgName = o.name");
+
+        checkNoIndexError(personCache, "select o.name, p._key, p.orgName " +
+            "from \"org\".Organization o, (select * from \"person\".Person) p " +
+            "where p.orgName = o.name");
+
+        checkNoIndexError(personCache, "select o.name, p._key, p.orgName " +
+            "from (select * from \"org\".Organization) o, (select * from \"person\".Person) p " +
+            "where p.orgName = o.name");
+
+        checkNoIndexError(personCache, "select o.name, p._key, p.orgName " +
+            "from \"org\".Organization o, \"person\".Person p");
+
+        checkNoIndexError(personCache, "select o.name, p._key, p.orgName " +
+            "from \"org\".Organization o, \"person\".Person p where o._key != p._key");
+
+        checkQuery("select o.name, p._key, p.orgName " +
+            "from \"org\".Organization o, \"person\".Person p " +
+            "where p._key = o._key and o.name=?", personCache, 0, "aaa");
+    }
+
+    /**
+     * @param cache Cache.
+     * @param sql SQL.
+     */
+    private void checkNoIndexError(final IgniteCache<Object, Object> cache, final String sql) {
+        Throwable err = GridTestUtils.assertThrows(log, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                SqlFieldsQuery qry = new SqlFieldsQuery(sql);
+
+                qry.setDistributedJoins(true);
+
+                cache.query(qry).getAll();
+
+                return null;
+            }
+        }, CacheException.class, null);
+
+        log.info("Error: " + err.getMessage());
+
+        assertTrue("Unexpected error message: " + err.getMessage(),
+            err.getMessage().contains("join condition does not use index"));
+    }
+
+    /**
+     * @param sql SQL.
+     * @param cache Cache.
+     * @param expSize Expected results size.
+     * @param args Arguments.
+     * @return Results.
+     */
+    private List<List<?>> checkQuery(String sql,
+        IgniteCache<Object, Object> cache,
+        int expSize,
+        Object... args) {
+        SqlFieldsQuery qry = new SqlFieldsQuery(sql);
+
+        qry.setDistributedJoins(true);
+        qry.setArgs(args);
+
+        log.info("Plan: " + queryPlan(cache, qry));
+
+        QueryCursor<List<?>> cur = cache.query(qry);
+
+        List<List<?>> res = cur.getAll();
+
+        if (expSize != res.size())
+            log.info("Results: " + res);
+
+        assertEquals(expSize, res.size());
+
+        return res;
+    }
+
+    /**
+     *
+     */
+    private static class Person implements Serializable {
+        /** */
+        int orgId;
+
+        /** */
+        String orgName;
+
+        /**
+         * @param orgId Organization ID.
+         * @param orgName Organization name.
+         */
+        public Person(int orgId, String orgName) {
+            this.orgId = orgId;
+            this.orgName = orgName;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return S.toString(Person.class, this);
+        }
+    }
+
+    /**
+     *
+     */
+    private static class Organization implements Serializable {
+        /** */
+        String name;
+
+        /**
+         * @param name Name.
+         */
+        public Organization(String name) {
+            this.name = name;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return S.toString(Organization.class, this);
+        }
+    }
+}


[48/50] [abbrv] ignite git commit: IGNITE-3578: CPP: Added properties to support distributed joins in CPP. This closes #894.

Posted by vo...@apache.org.
IGNITE-3578: CPP: Added properties to support distributed joins in CPP. This closes #894.


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

Branch: refs/heads/ignite-3553
Commit: 33d35b3ed87f986918f87838d4c9f12d23ec8dda
Parents: a7cf124
Author: isapego <is...@gridgain.com>
Authored: Thu Jul 28 08:59:26 2016 +0300
Committer: vozerov-gridgain <vo...@gridgain.com>
Committed: Thu Jul 28 08:59:26 2016 +0300

----------------------------------------------------------------------
 .../cpp/core-test/config/cache-query.xml        |  39 +-
 .../cpp/core-test/src/cache_query_test.cpp      | 443 ++++++++++++++++---
 .../core/include/ignite/cache/query/query_sql.h |  61 ++-
 .../ignite/cache/query/query_sql_fields.h       | 112 ++++-
 4 files changed, 569 insertions(+), 86 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/33d35b3e/modules/platforms/cpp/core-test/config/cache-query.xml
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/core-test/config/cache-query.xml b/modules/platforms/cpp/core-test/config/cache-query.xml
index 232b6da..06bc7f5 100644
--- a/modules/platforms/cpp/core-test/config/cache-query.xml
+++ b/modules/platforms/cpp/core-test/config/cache-query.xml
@@ -35,7 +35,7 @@
         <property name="cacheConfiguration">
             <list>
                 <bean class="org.apache.ignite.configuration.CacheConfiguration">
-                    <property name="name" value="cache"/>
+                    <property name="name" value="QueryPerson"/>
                     <property name="cacheMode" value="PARTITIONED"/>
                     <property name="atomicityMode" value="TRANSACTIONAL"/>
                     <property name="writeSynchronizationMode" value="FULL_SYNC"/>
@@ -69,6 +69,43 @@
                                     </list>
                                 </property>
                             </bean>
+							
+                            <bean class="org.apache.ignite.cache.CacheTypeMetadata">
+                                <property name="valueType" value="QueryRelation"/>
+                                <property name="queryFields">
+                                    <map>
+                                        <entry key="personId" value="java.lang.Integer"/>
+                                        <entry key="someVal" value="java.lang.Integer"/>
+                                    </map>
+                                </property>
+                            </bean>
+                        </list>
+                    </property>
+                </bean>
+				
+                <bean class="org.apache.ignite.configuration.CacheConfiguration">
+                    <property name="name" value="QueryRelation"/>
+                    <property name="cacheMode" value="PARTITIONED"/>
+                    <property name="atomicityMode" value="TRANSACTIONAL"/>
+                    <property name="writeSynchronizationMode" value="FULL_SYNC"/>
+
+                    <property name="affinity">
+                        <bean class="org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction">
+                            <property name="partitions" value="256"/>
+                        </bean>
+                    </property>
+                    
+                    <property name="typeMetadata">
+                        <list>
+                            <bean class="org.apache.ignite.cache.CacheTypeMetadata">
+                                <property name="valueType" value="QueryRelation"/>
+                                <property name="queryFields">
+                                    <map>
+                                        <entry key="personId" value="java.lang.Integer"/>
+                                        <entry key="someVal" value="java.lang.Integer"/>
+                                    </map>
+                                </property>
+                            </bean>
                         </list>
                     </property>
                 </bean>

http://git-wip-us.apache.org/repos/asf/ignite/blob/33d35b3e/modules/platforms/cpp/core-test/src/cache_query_test.cpp
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/core-test/src/cache_query_test.cpp b/modules/platforms/cpp/core-test/src/cache_query_test.cpp
index 168f3f9..e3fba02 100644
--- a/modules/platforms/cpp/core-test/src/cache_query_test.cpp
+++ b/modules/platforms/cpp/core-test/src/cache_query_test.cpp
@@ -175,12 +175,71 @@ private:
     Timestamp recordCreated;
 };
 
+/**
+ * Relation class for query tests.
+ */
+class IGNITE_IMPORT_EXPORT QueryRelation
+{
+public:
+    /**
+     * Constructor.
+     */
+    QueryRelation() :
+        personId(),
+        someVal()
+    {
+        // No-op.
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param name Name.
+     * @param age Age.
+     */
+    QueryRelation(int32_t personId, int32_t someVal) :
+        personId(personId),
+        someVal(someVal)
+    {
+        // No-op.
+    }
+
+    /**
+     * Get person ID.
+     * 
+     * @return Person ID.
+     */
+    int32_t GetPersonId() const
+    {
+        return personId;
+    }
+
+    /**
+     * Get hobby ID.
+     * 
+     * @return Some test value.
+     */
+    int32_t GetHobbyId() const
+    {
+        return someVal;
+    }
+
+private:
+    /** Person ID. */
+    int32_t personId;
+
+    /** Some test value. */
+    int32_t someVal;
+};
+
+
+
 namespace ignite
 {
     namespace binary
     {
         /**
-         * Binary type definition.
+         * Binary type definition for QueryPerson.
          */
         IGNITE_BINARY_TYPE_START(QueryPerson)
             IGNITE_BINARY_GET_TYPE_ID_AS_HASH(QueryPerson)
@@ -207,31 +266,45 @@ namespace ignite
             
                 return QueryPerson(name, age, birthday, recordCreated);
             }
-
         IGNITE_BINARY_TYPE_END
-    }
-}
 
-/** Node started during the test. */
-Ignite grid = Ignite();
+        /**
+         * Binary type definition for QueryRelation.
+         */
+        IGNITE_BINARY_TYPE_START(QueryRelation)
+            IGNITE_BINARY_GET_TYPE_ID_AS_HASH(QueryRelation)
+            IGNITE_BINARY_GET_TYPE_NAME_AS_IS(QueryRelation)
+            IGNITE_BINARY_GET_FIELD_ID_AS_HASH
+            IGNITE_BINARY_GET_HASH_CODE_ZERO(QueryRelation)
+            IGNITE_BINARY_IS_NULL_FALSE(QueryRelation)
+            IGNITE_BINARY_GET_NULL_DEFAULT_CTOR(QueryRelation)
 
-/** Cache accessor. */
-Cache<int, QueryPerson> GetCache()
-{
-    return grid.GetCache<int, QueryPerson>("cache");
+            void Write(BinaryWriter& writer, QueryRelation obj)
+            {
+                writer.WriteInt32("personId", obj.GetPersonId());
+                writer.WriteInt32("someVal", obj.GetHobbyId());
+            }
+
+            QueryRelation Read(BinaryReader& reader)
+            {
+                int32_t personId = reader.ReadInt32("personId");
+                int32_t someVal = reader.ReadInt32("someVal");
+
+                return QueryRelation(personId, someVal);
+            }
+        IGNITE_BINARY_TYPE_END
+    }
 }
 
 /**
  * Test setup fixture.
  */
-struct CacheQueryTestSuiteFixture {
-    /**
-     * Constructor.
-     */
-    CacheQueryTestSuiteFixture()
+struct CacheQueryTestSuiteFixture
+{
+    Ignite StartNode(const char* name)
     {
         IgniteConfiguration cfg;
-        
+
         cfg.jvmOpts.push_back("-Xdebug");
         cfg.jvmOpts.push_back("-Xnoagent");
         cfg.jvmOpts.push_back("-Djava.compiler=NONE");
@@ -246,18 +319,26 @@ struct CacheQueryTestSuiteFixture {
         cfg.jvmMaxMem = 4096;
 #endif
 
-        char* cfgPath = getenv("IGNITE_NATIVE_TEST_CPP_CONFIG_PATH");
-
-        cfg.springCfgPath = std::string(cfgPath).append("/").append("cache-query.xml");
+        cfg.springCfgPath.assign(getenv("IGNITE_NATIVE_TEST_CPP_CONFIG_PATH")).append("/cache-query.xml");
 
         IgniteError err;
 
-        Ignite grid0 = Ignition::Start(cfg, &err);
+        Ignite grid0 = Ignition::Start(cfg, name, &err);
 
         if (err.GetCode() != IgniteError::IGNITE_SUCCESS)
             BOOST_ERROR(err.GetText());
 
-        grid = grid0;
+        return grid0;
+    }
+
+
+    /**
+     * Constructor.
+     */
+    CacheQueryTestSuiteFixture() : 
+        grid(StartNode("Node1"))
+    {
+        // No-op.
     }
 
     /**
@@ -265,11 +346,44 @@ struct CacheQueryTestSuiteFixture {
      */
     ~CacheQueryTestSuiteFixture()
     {
-        Ignition::Stop(grid.GetName(), true);
+        Ignition::StopAll(true);
     }
+
+    /** Person cache accessor. */
+    Cache<int, QueryPerson> GetPersonCache()
+    {
+        return grid.GetCache<int, QueryPerson>("QueryPerson");
+    }
+
+    /** Relation cache accessor. */
+    Cache<int, QueryRelation> GetRelationCache()
+    {
+        return grid.GetCache<int, QueryRelation>("QueryRelation");
+    }
+
+    /** Node started during the test. */
+    Ignite grid;
 };
 
 /**
+ * Count number of records returned by cursor.
+ *
+ * @param cur Cursor.
+ */
+template<typename Cursor>
+int CountRecords(Cursor& cur)
+{
+    int number = 0;
+    while (cur.HasNext())
+    {
+        ++number;
+        cur.GetNext();
+    }
+
+    return number;
+}
+
+/**
  * Ensure that HasNext() fails.
  *
  * @param cur Cursor.
@@ -314,8 +428,7 @@ void CheckGetNextFail(Cursor& cur)
  *
  * @param cur Cursor.
  */
-template<typename Cursor>
-void CheckGetAllFail(Cursor& cur)
+void CheckGetAllFail(QueryCursor<int, QueryPerson>& cur)
 {
     try 
     {
@@ -361,8 +474,7 @@ void CheckEmpty(QueryFieldsCursor& cur)
  *
  * @param cur Cursor.
  */
-template<typename Cursor>
-void CheckEmptyGetAll(Cursor& cur)
+void CheckEmptyGetAll(QueryCursor<int, QueryPerson>& cur)
 {
     std::vector<CacheEntry<int, QueryPerson>> res;
 
@@ -382,8 +494,7 @@ void CheckEmptyGetAll(Cursor& cur)
  * @param name1 Name.
  * @param age1 Age.
  */
-template<typename Cursor>
-void CheckSingle(Cursor& cur, int key, const std::string& name, int age)
+void CheckSingle(QueryCursor<int, QueryPerson>& cur, int key, const std::string& name, int age)
 {
     BOOST_REQUIRE(cur.HasNext());
 
@@ -404,6 +515,30 @@ void CheckSingle(Cursor& cur, int key, const std::string& name, int age)
 }
 
 /**
+ * Check single result through iteration.
+ *
+ * @param cur Cursor.
+ * @param key Key.
+ * @param name Name.
+ * @param age Age.
+ */
+void CheckSingle(QueryFieldsCursor& cur, int key, const std::string& name, int age)
+{
+    BOOST_REQUIRE(cur.HasNext());
+
+    QueryFieldsRow row = cur.GetNext();
+
+    BOOST_REQUIRE_EQUAL(row.GetNext<int32_t>(), key);
+    BOOST_REQUIRE_EQUAL(row.GetNext<std::string>(), name);
+    BOOST_REQUIRE_EQUAL(row.GetNext<int32_t>(), age);
+
+    BOOST_REQUIRE(!row.HasNext());
+    BOOST_REQUIRE(!cur.HasNext());
+
+    CheckGetNextFail(cur);
+}
+
+/**
  * Check single result through GetAll().
  *
  * @param cur Cursor.
@@ -411,8 +546,7 @@ void CheckSingle(Cursor& cur, int key, const std::string& name, int age)
  * @param name1 Name.
  * @param age1 Age.
  */
-template<typename Cursor>
-void CheckSingleGetAll(Cursor& cur, int key, const std::string& name, int age)
+void CheckSingleGetAll(QueryCursor<int, QueryPerson>& cur, int key, const std::string& name, int age)
 {
     std::vector<CacheEntry<int, QueryPerson>> res;
 
@@ -444,8 +578,7 @@ void CheckSingleGetAll(Cursor& cur, int key, const std::string& name, int age)
  * @param name2 Name 2.
  * @param age2 Age 2.
  */
-template<typename Cursor>
-void CheckMultiple(Cursor& cur, int key1, const std::string& name1, 
+void CheckMultiple(QueryCursor<int, QueryPerson>& cur, int key1, const std::string& name1,
     int age1, int key2, const std::string& name2, int age2)
 {
     for (int i = 0; i < 2; i++)
@@ -489,8 +622,7 @@ void CheckMultiple(Cursor& cur, int key1, const std::string& name1,
  * @param name2 Name 2.
  * @param age2 Age 2.
  */
-template<typename Cursor>
-void CheckMultipleGetAll(Cursor& cur, int key1, const std::string& name1,
+void CheckMultipleGetAll(QueryCursor<int, QueryPerson>& cur, int key1, const std::string& name1,
     int age1, int key2, const std::string& name2, int age2)
 {
     std::vector<CacheEntry<int, QueryPerson>> res;
@@ -529,7 +661,7 @@ BOOST_FIXTURE_TEST_SUITE(CacheQueryTestSuite, CacheQueryTestSuiteFixture)
  */
 BOOST_AUTO_TEST_CASE(TestSqlQuery)
 {    
-    Cache<int, QueryPerson> cache = GetCache();
+    Cache<int, QueryPerson> cache = GetPersonCache();
 
     // Test query with no results.
     SqlQuery qry("QueryPerson", "age < 20");
@@ -541,8 +673,11 @@ BOOST_AUTO_TEST_CASE(TestSqlQuery)
     CheckEmptyGetAll(cursor);
 
     // Test simple query.
-    cache.Put(1, QueryPerson("A1", 10, BinaryUtils::MakeDateLocal(1990, 03, 18), BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 34, 579304685)));
-    cache.Put(2, QueryPerson("A2", 20, BinaryUtils::MakeDateLocal(1989, 10, 26), BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 35, 678403201)));
+    cache.Put(1, QueryPerson("A1", 10, BinaryUtils::MakeDateLocal(1990, 03, 18),
+        BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 34, 579304685)));
+
+    cache.Put(2, QueryPerson("A2", 20, BinaryUtils::MakeDateLocal(1989, 10, 26),
+        BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 35, 678403201)));
     
     cursor = cache.Query(qry);
     CheckSingle(cursor, 1, "A1", 10);
@@ -550,8 +685,23 @@ BOOST_AUTO_TEST_CASE(TestSqlQuery)
     cursor = cache.Query(qry);
     CheckSingleGetAll(cursor, 1, "A1", 10);
 
+    // Test simple distributed joins query.
+    BOOST_CHECK(!qry.IsDistributedJoins());
+    qry.SetDistributedJoins(true);
+    BOOST_CHECK(qry.IsDistributedJoins());
+
+    cursor = cache.Query(qry);
+    CheckSingle(cursor, 1, "A1", 10);
+
+    cursor = cache.Query(qry);
+    CheckSingleGetAll(cursor, 1, "A1", 10);
+
+    qry.SetDistributedJoins(false);
+
     // Test simple local query.
+    BOOST_CHECK(!qry.IsLocal());
     qry.SetLocal(true);
+    BOOST_CHECK(qry.IsLocal());
 
     cursor = cache.Query(qry);
     CheckSingle(cursor, 1, "A1", 10);
@@ -581,11 +731,60 @@ BOOST_AUTO_TEST_CASE(TestSqlQuery)
 }
 
 /**
+ * Test SQL query distributed joins.
+ */
+BOOST_AUTO_TEST_CASE(TestSqlQueryDistributedJoins)
+{    
+    Cache<int, QueryPerson> cache1 = GetPersonCache();
+    Cache<int, QueryRelation> cache2 = GetRelationCache();
+
+    // Starting second node.
+    Ignite node2 = StartNode("Node2");
+
+    int entryCnt = 1000;
+
+    // Filling caches
+    for (int i = 0; i < entryCnt; i++)
+    {
+        std::stringstream stream;
+
+        stream << "A" << i;
+
+        cache1.Put(i, QueryPerson(stream.str(), i * 10, BinaryUtils::MakeDateLocal(1970 + i),
+            BinaryUtils::MakeTimestampLocal(2016, 1, 1, i / 60, i % 60)));
+
+        cache2.Put(i + 1, QueryRelation(i, i * 10));
+    }
+
+    // Test query with no results.
+    SqlQuery qry("QueryPerson",
+        "from \"QueryPerson\".QueryPerson, \"QueryRelation\".QueryRelation "
+        "where \"QueryPerson\".QueryPerson.age = \"QueryRelation\".QueryRelation.someVal");
+
+    QueryCursor<int, QueryPerson> cursor = cache1.Query(qry);
+
+    // Ensure that data is not collocated, so not full result set is returned.
+    int recordsNum = CountRecords(cursor);
+
+    BOOST_CHECK_GT(recordsNum, 0);
+    BOOST_CHECK_LT(recordsNum, entryCnt);
+
+    qry.SetDistributedJoins(true);
+
+    cursor = cache1.Query(qry);
+
+    // Check that full result set is returned.
+    recordsNum = CountRecords(cursor);
+
+    BOOST_CHECK_EQUAL(recordsNum, entryCnt);
+}
+
+/**
  * Test text query.
  */
 BOOST_AUTO_TEST_CASE(TestTextQuery)
 {
-    Cache<int, QueryPerson> cache = GetCache();
+    Cache<int, QueryPerson> cache = GetPersonCache();
 
     // Test query with no results.
     TextQuery qry("QueryPerson", "A1");
@@ -597,8 +796,11 @@ BOOST_AUTO_TEST_CASE(TestTextQuery)
     CheckEmptyGetAll(cursor);
 
     // Test simple query.
-    cache.Put(1, QueryPerson("A1", 10, BinaryUtils::MakeDateLocal(1990, 03, 18), BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 34, 579304685)));
-    cache.Put(2, QueryPerson("A2", 20, BinaryUtils::MakeDateLocal(1989, 10, 26), BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 35, 678403201)));
+    cache.Put(1, QueryPerson("A1", 10, BinaryUtils::MakeDateLocal(1990, 03, 18),
+        BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 34, 579304685)));
+
+    cache.Put(2, QueryPerson("A2", 20, BinaryUtils::MakeDateLocal(1989, 10, 26),
+        BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 35, 678403201)));
 
     cursor = cache.Query(qry);
     CheckSingle(cursor, 1, "A1", 10);
@@ -631,7 +833,7 @@ BOOST_AUTO_TEST_CASE(TestTextQuery)
 BOOST_AUTO_TEST_CASE(TestScanQuery)
 {
     // Test simple query.
-    Cache<int, QueryPerson> cache = GetCache();
+    Cache<int, QueryPerson> cache = GetPersonCache();
 
     // Test query with no results.
     ScanQuery qry;
@@ -643,7 +845,8 @@ BOOST_AUTO_TEST_CASE(TestScanQuery)
     CheckEmptyGetAll(cursor);
 
     // Test simple query.
-    cache.Put(1, QueryPerson("A1", 10, BinaryUtils::MakeDateLocal(1990, 03, 18), BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 34, 579304685)));
+    cache.Put(1, QueryPerson("A1", 10, BinaryUtils::MakeDateLocal(1990, 03, 18),
+        BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 34, 579304685)));
 
     cursor = cache.Query(qry);
     CheckSingle(cursor, 1, "A1", 10);
@@ -652,7 +855,8 @@ BOOST_AUTO_TEST_CASE(TestScanQuery)
     CheckSingleGetAll(cursor, 1, "A1", 10);
 
     // Test query returning multiple entries.
-    cache.Put(2, QueryPerson("A2", 20, BinaryUtils::MakeDateLocal(1989, 10, 26), BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 35, 678403201)));
+    cache.Put(2, QueryPerson("A2", 20, BinaryUtils::MakeDateLocal(1989, 10, 26),
+        BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 35, 678403201)));
 
     cursor = cache.Query(qry);
     CheckMultiple(cursor, 1, "A1", 10, 2, "A2", 20);
@@ -667,9 +871,9 @@ BOOST_AUTO_TEST_CASE(TestScanQuery)
 BOOST_AUTO_TEST_CASE(TestScanQueryPartitioned)
 {
     // Populate cache with data.
-    Cache<int, QueryPerson> cache = GetCache();
+    Cache<int, QueryPerson> cache = GetPersonCache();
 
-    int32_t partCnt = 256;   // Defined in configuration explicitly.   
+    int32_t partCnt = 256;   // Defined in configuration explicitly.
     int32_t entryCnt = 1000; // Should be greater than partCnt.
     
     for (int i = 0; i < entryCnt; i++) 
@@ -678,7 +882,8 @@ BOOST_AUTO_TEST_CASE(TestScanQueryPartitioned)
 
         stream << "A" << i;
 
-        cache.Put(i, QueryPerson(stream.str(), i * 10, BinaryUtils::MakeDateLocal(1970 + i), BinaryUtils::MakeTimestampLocal(2016, 1, 1, i / 60, i % 60)));
+        cache.Put(i, QueryPerson(stream.str(), i * 10, BinaryUtils::MakeDateLocal(1970 + i),
+            BinaryUtils::MakeTimestampLocal(2016, 1, 1, i / 60, i % 60)));
     }
 
     // Iterate over all partitions and collect data.
@@ -711,12 +916,125 @@ BOOST_AUTO_TEST_CASE(TestScanQueryPartitioned)
 }
 
 /**
+ * Basic test for SQL fields query.
+ */
+BOOST_AUTO_TEST_CASE(TestSqlFieldsQueryBasic)
+{
+    Cache<int, QueryPerson> cache = GetPersonCache();
+
+    // Test query with no results.
+    SqlFieldsQuery qry("select _key, name, age from QueryPerson where age < 20");
+
+    QueryFieldsCursor cursor = cache.Query(qry);
+    CheckEmpty(cursor);
+
+    // Test simple query.
+    cache.Put(1, QueryPerson("A1", 10, BinaryUtils::MakeDateLocal(1990, 03, 18),
+        BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 34, 579304685)));
+
+    cache.Put(2, QueryPerson("A2", 20, BinaryUtils::MakeDateLocal(1989, 10, 26),
+        BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 35, 678403201)));
+
+    cursor = cache.Query(qry);
+    CheckSingle(cursor, 1, "A1", 10);
+
+    // Test simple distributed joins query.
+    BOOST_CHECK(!qry.IsDistributedJoins());
+    BOOST_CHECK(!qry.IsEnforceJoinOrder());
+
+    qry.SetDistributedJoins(true);
+
+    BOOST_CHECK(qry.IsDistributedJoins());
+    BOOST_CHECK(!qry.IsEnforceJoinOrder());
+
+    qry.SetEnforceJoinOrder(true);
+
+    BOOST_CHECK(qry.IsDistributedJoins());
+    BOOST_CHECK(qry.IsEnforceJoinOrder());
+
+    cursor = cache.Query(qry);
+    CheckSingle(cursor, 1, "A1", 10);
+    
+    qry.SetDistributedJoins(false);
+    qry.SetEnforceJoinOrder(false);
+
+    // Test simple local query.
+    BOOST_CHECK(!qry.IsLocal());
+
+    qry.SetLocal(true);
+
+    BOOST_CHECK(qry.IsLocal());
+
+    cursor = cache.Query(qry);
+    CheckSingle(cursor, 1, "A1", 10);
+
+    // Test query with arguments.
+    qry.SetSql("select _key, name, age from QueryPerson where age < ? AND name = ?");
+    qry.AddArgument<int>(20);
+    qry.AddArgument<std::string>("A1");
+
+    cursor = cache.Query(qry);
+    CheckSingle(cursor, 1, "A1", 10);
+}
+
+/**
+ * Test SQL fields query distributed joins.
+ */
+BOOST_AUTO_TEST_CASE(TestSqlFieldsQueryDistributedJoins)
+{
+    Cache<int, QueryPerson> cache1 = GetPersonCache();
+    Cache<int, QueryRelation> cache2 = GetRelationCache();
+
+    // Starting second node.
+    Ignite node2 = StartNode("Node2");
+
+    int entryCnt = 1000;
+
+    // Filling caches
+    for (int i = 0; i < entryCnt; i++)
+    {
+        std::stringstream stream;
+
+        stream << "A" << i;
+
+        cache1.Put(i, QueryPerson(stream.str(), i * 10, BinaryUtils::MakeDateLocal(1970 + i),
+            BinaryUtils::MakeTimestampLocal(2016, 1, 1, i / 60, i % 60)));
+
+        cache2.Put(i + 1, QueryRelation(i, i * 10));
+    }
+
+    // Test query with no results.
+    SqlFieldsQuery qry(
+        "select age, name "
+        "from \"QueryPerson\".QueryPerson "
+        "inner join \"QueryRelation\".QueryRelation "
+        "on \"QueryPerson\".QueryPerson.age = \"QueryRelation\".QueryRelation.someVal");
+
+    QueryFieldsCursor cursor = cache1.Query(qry);
+
+    // Ensure that data is not collocated, so not full result set is returned.
+    int recordsNum = CountRecords(cursor);
+
+    BOOST_CHECK_GT(recordsNum, 0);
+    BOOST_CHECK_LT(recordsNum, entryCnt);
+
+    qry.SetDistributedJoins(true);
+
+    cursor = cache1.Query(qry);
+
+    // Check that full result set is returned.
+    recordsNum = CountRecords(cursor);
+
+    BOOST_CHECK_EQUAL(recordsNum, entryCnt);
+}
+
+/**
  * Test fields query with single entry.
  */
 BOOST_AUTO_TEST_CASE(TestFieldsQuerySingle)
 {
     // Test simple query.
-    Cache<int, QueryPerson> cache = GetCache();
+    Cache<int, QueryPerson> cache = GetPersonCache();
 
     // Test query with two fields of different type.
     SqlFieldsQuery qry("select age, name from QueryPerson");
@@ -725,7 +1043,8 @@ BOOST_AUTO_TEST_CASE(TestFieldsQuerySingle)
     CheckEmpty(cursor);
     
     // Test simple query.
-    cache.Put(1, QueryPerson("A1", 10, BinaryUtils::MakeDateLocal(1990, 03, 18), BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 34, 579304685)));
+    cache.Put(1, QueryPerson("A1", 10, BinaryUtils::MakeDateLocal(1990, 03, 18),
+        BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 34, 579304685)));
 
     cursor = cache.Query(qry);
 
@@ -761,7 +1080,7 @@ BOOST_AUTO_TEST_CASE(TestFieldsQuerySingle)
 BOOST_AUTO_TEST_CASE(TestFieldsQueryExceptions)
 {
     // Test simple query.
-    Cache<int, QueryPerson> cache = GetCache();
+    Cache<int, QueryPerson> cache = GetPersonCache();
 
     // Test query with two fields of different type.
     SqlFieldsQuery qry("select age, name from QueryPerson");
@@ -770,7 +1089,8 @@ BOOST_AUTO_TEST_CASE(TestFieldsQueryExceptions)
     CheckEmpty(cursor);
 
     // Test simple query.
-    cache.Put(1, QueryPerson("A1", 10, BinaryUtils::MakeDateLocal(1990, 03, 18), BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 34, 579304685)));
+    cache.Put(1, QueryPerson("A1", 10, BinaryUtils::MakeDateLocal(1990, 03, 18),
+        BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 34, 579304685)));
 
     cursor = cache.Query(qry);
 
@@ -806,7 +1126,7 @@ BOOST_AUTO_TEST_CASE(TestFieldsQueryExceptions)
 BOOST_AUTO_TEST_CASE(TestFieldsQueryTwo)
 {
     // Test simple query.
-    Cache<int, QueryPerson> cache = GetCache();
+    Cache<int, QueryPerson> cache = GetPersonCache();
 
     // Test query with two fields of different type.
     SqlFieldsQuery qry("select age, name from QueryPerson");
@@ -815,8 +1135,11 @@ BOOST_AUTO_TEST_CASE(TestFieldsQueryTwo)
     CheckEmpty(cursor);
 
     // Test simple query.
-    cache.Put(1, QueryPerson("A1", 10, BinaryUtils::MakeDateLocal(1990, 03, 18), BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 34, 579304685)));
-    cache.Put(2, QueryPerson("A2", 20, BinaryUtils::MakeDateLocal(1989, 10, 26), BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 35, 678403201)));
+    cache.Put(1, QueryPerson("A1", 10, BinaryUtils::MakeDateLocal(1990, 03, 18),
+        BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 34, 579304685)));
+
+    cache.Put(2, QueryPerson("A2", 20, BinaryUtils::MakeDateLocal(1989, 10, 26),
+        BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 35, 678403201)));
 
     cursor = cache.Query(qry);
 
@@ -869,7 +1192,7 @@ BOOST_AUTO_TEST_CASE(TestFieldsQueryTwo)
 BOOST_AUTO_TEST_CASE(TestFieldsQuerySeveral)
 {
     // Test simple query.
-    Cache<int, QueryPerson> cache = GetCache();
+    Cache<int, QueryPerson> cache = GetPersonCache();
 
     // Test query with two fields of different type.
     SqlFieldsQuery qry("select name, age from QueryPerson");
@@ -935,7 +1258,7 @@ BOOST_AUTO_TEST_CASE(TestFieldsQuerySeveral)
 BOOST_AUTO_TEST_CASE(TestFieldsQueryDateLess)
 {
     // Test simple query.
-    Cache<int, QueryPerson> cache = GetCache();
+    Cache<int, QueryPerson> cache = GetPersonCache();
 
     // Test query with field of type 'Date'.
     SqlFieldsQuery qry("select birthday from QueryPerson where birthday<'1990-01-01'");
@@ -996,7 +1319,7 @@ BOOST_AUTO_TEST_CASE(TestFieldsQueryDateLess)
 BOOST_AUTO_TEST_CASE(TestFieldsQueryDateMore)
 {
     // Test simple query.
-    Cache<int, QueryPerson> cache = GetCache();
+    Cache<int, QueryPerson> cache = GetPersonCache();
 
     // Test query with field of type 'Date'.
     SqlFieldsQuery qry("select birthday from QueryPerson where birthday>'2070-01-01'");
@@ -1057,7 +1380,7 @@ BOOST_AUTO_TEST_CASE(TestFieldsQueryDateMore)
 BOOST_AUTO_TEST_CASE(TestFieldsQueryDateEqual)
 {
     // Test simple query.
-    Cache<int, QueryPerson> cache = GetCache();
+    Cache<int, QueryPerson> cache = GetPersonCache();
 
     // Test query with field of type 'Date'.
     SqlFieldsQuery qry("select birthday from QueryPerson where birthday='2032-01-01'");
@@ -1109,7 +1432,7 @@ BOOST_AUTO_TEST_CASE(TestFieldsQueryDateEqual)
 BOOST_AUTO_TEST_CASE(TestFieldsQueryTimestampLess)
 {
     // Test simple query.
-    Cache<int, QueryPerson> cache = GetCache();
+    Cache<int, QueryPerson> cache = GetPersonCache();
 
     // Test query with field of type 'Timestamp'.
     SqlFieldsQuery qry("select recordCreated from QueryPerson where recordCreated<'2016-01-01 01:00:00'");
@@ -1170,7 +1493,7 @@ BOOST_AUTO_TEST_CASE(TestFieldsQueryTimestampLess)
 BOOST_AUTO_TEST_CASE(TestFieldsQueryTimestampMore)
 {
     // Test simple query.
-    Cache<int, QueryPerson> cache = GetCache();
+    Cache<int, QueryPerson> cache = GetPersonCache();
 
     // Test query with field of type 'Timestamp'.
     SqlFieldsQuery qry("select recordCreated from QueryPerson where recordCreated>'2016-01-01 15:30:00'");
@@ -1233,7 +1556,7 @@ BOOST_AUTO_TEST_CASE(TestFieldsQueryTimestampMore)
 BOOST_AUTO_TEST_CASE(TestFieldsQueryTimestampEqual)
 {
     // Test simple query.
-    Cache<int, QueryPerson> cache = GetCache();
+    Cache<int, QueryPerson> cache = GetPersonCache();
 
     // Test query with field of type 'Timestamp'.
     SqlFieldsQuery qry("select recordCreated from QueryPerson where recordCreated='2016-01-01 09:18:00'");

http://git-wip-us.apache.org/repos/asf/ignite/blob/33d35b3e/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 cb7a739..0ec5d5f 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
@@ -48,8 +48,13 @@ namespace ignite
                  * @param type Type name.
                  * @param sql SQL string.
                  */
-                SqlQuery(const std::string& type, const std::string& sql)
-                    : type(type), sql(sql), pageSize(1024),  loc(false), args()
+                SqlQuery(const std::string& type, const std::string& sql) :
+                    type(type),
+                    sql(sql),
+                    pageSize(1024),
+                    loc(false),
+                    distributedJoins(false),
+                    args()
                 {
                     // No-op.
                 }
@@ -60,13 +65,18 @@ namespace ignite
                  * @param other Other instance.
                  */
                 SqlQuery(const SqlQuery& other) :
-                    type(other.type), sql(other.sql), pageSize(other.pageSize),
-                    loc(other.loc), args()
+                    type(other.type),
+                    sql(other.sql),
+                    pageSize(other.pageSize),
+                    loc(other.loc),
+                    distributedJoins(other.distributedJoins),
+                    args()
                 {
                     args.reserve(other.args.size());
 
-                    for (std::vector<QueryArgumentBase*>::const_iterator i = other.args.begin(); 
-                        i != other.args.end(); ++i)
+                    typedef std::vector<QueryArgumentBase*>::const_iterator Iter;
+
+                    for (Iter i = other.args.begin(); i != other.args.end(); ++i)
                         args.push_back((*i)->Copy());
                 }
 
@@ -81,11 +91,7 @@ namespace ignite
                     {
                         SqlQuery tmp(other);
 
-                        std::swap(type, tmp.type);
-                        std::swap(sql, tmp.sql);
-                        std::swap(pageSize, tmp.pageSize);
-                        std::swap(loc, tmp.loc);
-                        std::swap(args, tmp.args);
+                        Swap(tmp);
                     }
 
                     return *this;
@@ -96,7 +102,9 @@ namespace ignite
                  */
                 ~SqlQuery()
                 {
-                    for (std::vector<QueryArgumentBase*>::iterator it = args.begin(); it != args.end(); ++it)
+                    typedef std::vector<QueryArgumentBase*>::const_iterator Iter;
+
+                    for (Iter it = args.begin(); it != args.end(); ++it)
                         delete *it;
                 }
 
@@ -113,6 +121,7 @@ namespace ignite
                         std::swap(sql, other.sql);
                         std::swap(pageSize, other.pageSize);
                         std::swap(loc, other.loc);
+                        std::swap(distributedJoins, other.distributedJoins);
                         std::swap(args, other.args);
                     }
                 }
@@ -198,6 +207,29 @@ namespace ignite
                 }
 
                 /**
+                 * Check if distributed joins are enabled for this query.
+                 *
+                 * @return True If distributed joind enabled.
+                 */
+                bool IsDistributedJoins() const
+                {
+                    return distributedJoins;
+                }
+
+                /**
+                 * 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 enabled Distributed joins enabled.
+                 */
+                void SetDistributedJoins(bool enabled)
+                {
+                    distributedJoins = enabled;
+                }
+
+                /**
                  * Add argument.
                  *
                  * Template argument type should be copy-constructable and
@@ -229,7 +261,7 @@ 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(distributedJoins);
                 }
 
             private:
@@ -245,6 +277,9 @@ namespace ignite
                 /** Local flag. */
                 bool loc;
 
+                /** Distributed joins flag. */
+                bool distributedJoins;
+
                 /** Arguments. */
                 std::vector<QueryArgumentBase*> args;
             };

http://git-wip-us.apache.org/repos/asf/ignite/blob/33d35b3e/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 1c8570b..10dd6ab 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
@@ -48,7 +48,12 @@ namespace ignite
                  * @param sql SQL string.
                  */
                 SqlFieldsQuery(const std::string& sql) :
-                    sql(sql), pageSize(1024), loc(false), args()
+                    sql(sql),
+                    pageSize(1024),
+                    loc(false),
+                    distributedJoins(false),
+                    enforceJoinOrder(false),
+                    args()
                 {
                     // No-op.
                 }
@@ -60,7 +65,12 @@ namespace ignite
                  * @param loc Whether query should be executed locally.
                  */
                 SqlFieldsQuery(const std::string& sql, bool loc) :
-                    sql(sql), pageSize(1024), loc(false), args()
+                    sql(sql),
+                    pageSize(1024),
+                    loc(false),
+                    distributedJoins(false),
+                    enforceJoinOrder(false),
+                    args()
                 {
                     // No-op.
                 }
@@ -71,13 +81,18 @@ namespace ignite
                  * @param other Other instance.
                  */
                 SqlFieldsQuery(const SqlFieldsQuery& other) :
-                    sql(other.sql), pageSize(other.pageSize), loc(other.loc),
+                    sql(other.sql),
+                    pageSize(other.pageSize),
+                    loc(other.loc),
+                    distributedJoins(other.distributedJoins),
+                    enforceJoinOrder(other.enforceJoinOrder),
                     args()
                 {
                     args.reserve(other.args.size());
 
-                    for (std::vector<QueryArgumentBase*>::const_iterator i = other.args.begin(); 
-                        i != other.args.end(); ++i)
+                    typedef std::vector<QueryArgumentBase*>::const_iterator Iter;
+
+                    for (Iter i = other.args.begin(); i != other.args.end(); ++i)
                         args.push_back((*i)->Copy());
                 }
 
@@ -92,10 +107,7 @@ namespace ignite
                     {
                         SqlFieldsQuery tmp(other);
 
-                        std::swap(sql, tmp.sql);
-                        std::swap(pageSize, tmp.pageSize);
-                        std::swap(loc, tmp.loc);
-                        std::swap(args, tmp.args);
+                        Swap(tmp);
                     }
 
                     return *this;
@@ -106,11 +118,31 @@ namespace ignite
                  */
                 ~SqlFieldsQuery()
                 {
-                    for (std::vector<QueryArgumentBase*>::iterator it = args.begin(); it != args.end(); ++it)
+                    typedef std::vector<QueryArgumentBase*>::const_iterator Iter;
+
+                    for (Iter it = args.begin(); it != args.end(); ++it)
                         delete *it;
                 }
 
                 /**
+                 * Efficiently swaps contents with another SqlQuery instance.
+                 *
+                 * @param other Other instance.
+                 */
+                void Swap(SqlFieldsQuery& other)
+                {
+                    if (this != &other)
+                    {
+                        std::swap(sql, other.sql);
+                        std::swap(pageSize, other.pageSize);
+                        std::swap(loc, other.loc);
+                        std::swap(distributedJoins, other.distributedJoins);
+                        std::swap(enforceJoinOrder, other.enforceJoinOrder);
+                        std::swap(args, other.args);
+                    }
+                }
+
+                /**
                  * Get SQL string.
                  *
                  * @return SQL string.
@@ -171,6 +203,56 @@ namespace ignite
                 }
 
                 /**
+                 * Checks if join order of tables if enforced.
+                 *
+                 * @return Flag value.
+                 */
+                bool IsEnforceJoinOrder() const
+                {
+                    return enforceJoinOrder;
+                }
+
+                /**
+                 * Sets flag to enforce join order of tables in the query.
+                 * If set to true query optimizer will not reorder tables in
+                 * join. By default is false.
+                 *
+                 * It is not recommended to enable this property unless 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.
+                 *
+                 * @param enforce Flag value.
+                 */
+                void SetEnforceJoinOrder(bool enforce)
+                {
+                    enforceJoinOrder = enforce;
+                }
+
+                /**
+                 * Check if distributed joins are enabled for this query.
+                 *
+                 * @return True If distributed joind enabled.
+                 */
+                bool IsDistributedJoins() const
+                {
+                    return distributedJoins;
+                }
+
+                /**
+                 * 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 enabled Distributed joins enabled.
+                 */
+                void SetDistributedJoins(bool enabled)
+                {
+                    distributedJoins = enabled;
+                }
+
+                /**
                  * Add argument.
                  *
                  * Template argument type should be copy-constructable and
@@ -201,8 +283,8 @@ 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
+                    writer.WriteBool(distributedJoins);
+                    writer.WriteBool(enforceJoinOrder);
                 }
 
             private:
@@ -215,6 +297,12 @@ namespace ignite
                 /** Local flag. */
                 bool loc;
 
+                /** Distributed joins flag. */
+                bool distributedJoins;
+
+                /** Enforce join order flag. */
+                bool enforceJoinOrder;
+
                 /** Arguments. */
                 std::vector<QueryArgumentBase*> args;
             };


[28/50] [abbrv] ignite git commit: IGNITE-3561 .NET: DistributedJoins property in SqlQuery & SqlFieldsQuery

Posted by vo...@apache.org.
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-3553
Commit: 856b536db66edf9e7e6fc1b96da55a0e846c70c3
Parents: 37195a0
Author: Pavel Tupitsyn <pt...@apache.org>
Authored: Mon Jul 25 18:54:11 2016 +0300
Committer: Pavel Tupitsyn <pt...@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 /> */


[12/50] [abbrv] ignite git commit: ignite-1232 Distributed SQL joins implementation

Posted by vo...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridReduceQueryExecutor.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridReduceQueryExecutor.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridReduceQueryExecutor.java
index 6a079f0..04449ac 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridReduceQueryExecutor.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridReduceQueryExecutor.java
@@ -31,6 +31,7 @@ import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.NavigableMap;
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentMap;
@@ -59,11 +60,13 @@ import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionFullMap;
 import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionMap2;
+import org.apache.ignite.internal.processors.cache.query.GridCacheQueryMarshallable;
 import org.apache.ignite.internal.processors.cache.query.GridCacheSqlQuery;
 import org.apache.ignite.internal.processors.cache.query.GridCacheTwoStepQuery;
 import org.apache.ignite.internal.processors.query.GridQueryCacheObjectsIterator;
 import org.apache.ignite.internal.processors.query.h2.GridH2ResultSetIterator;
 import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2QueryContext;
 import org.apache.ignite.internal.processors.query.h2.sql.GridSqlQuerySplitter;
 import org.apache.ignite.internal.processors.query.h2.sql.GridSqlType;
 import org.apache.ignite.internal.processors.query.h2.twostep.messages.GridQueryCancelRequest;
@@ -71,11 +74,14 @@ import org.apache.ignite.internal.processors.query.h2.twostep.messages.GridQuery
 import org.apache.ignite.internal.processors.query.h2.twostep.messages.GridQueryNextPageRequest;
 import org.apache.ignite.internal.processors.query.h2.twostep.messages.GridQueryNextPageResponse;
 import org.apache.ignite.internal.processors.query.h2.twostep.messages.GridQueryRequest;
+import org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2QueryRequest;
 import org.apache.ignite.internal.util.GridSpinBusyLock;
+import org.apache.ignite.internal.util.typedef.CIX2;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteBiClosure;
 import org.apache.ignite.lang.IgniteFuture;
-import org.apache.ignite.marshaller.Marshaller;
+import org.apache.ignite.lang.IgniteProductVersion;
 import org.apache.ignite.plugin.extensions.communication.Message;
 import org.h2.command.ddl.CreateTableData;
 import org.h2.engine.Session;
@@ -92,6 +98,7 @@ import org.jetbrains.annotations.Nullable;
 import org.jsr166.ConcurrentHashMap8;
 
 import static org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion.NONE;
+import static org.apache.ignite.internal.processors.query.h2.opt.GridH2QueryType.REDUCE;
 
 /**
  * Reduce query executor.
@@ -101,6 +108,9 @@ public class GridReduceQueryExecutor {
     public static final byte QUERY_POOL = GridIoPolicy.SYSTEM_POOL;
 
     /** */
+    private static final IgniteProductVersion DISTRIBUTED_JOIN_SINCE = IgniteProductVersion.fromString("1.7.0");
+
+    /** */
     private GridKernalContext ctx;
 
     /** */
@@ -149,6 +159,13 @@ public class GridReduceQueryExecutor {
     /** */
     private final GridSpinBusyLock busyLock;
 
+    /** */
+    private final CIX2<ClusterNode,Message> locNodeHnd = new CIX2<ClusterNode,Message>() {
+        @Override public void applyx(ClusterNode locNode, Message msg) {
+            h2.mapQueryExecutor().onMessage(locNode.id(), msg);
+        }
+    };
+
     /**
      * @param busyLock Busy lock.
      */
@@ -173,6 +190,9 @@ public class GridReduceQueryExecutor {
                     return;
 
                 try {
+                    if (msg instanceof GridCacheQueryMarshallable)
+                        ((GridCacheQueryMarshallable)msg).unmarshall(ctx.config().getMarshaller(), ctx);
+
                     GridReduceQueryExecutor.this.onMessage(nodeId, msg);
                 }
                 finally {
@@ -339,13 +359,13 @@ public class GridReduceQueryExecutor {
      * @param extraSpaces Extra spaces.
      * @return {@code true} If preloading is active.
      */
-    private boolean isPreloadingActive(final GridCacheContext<?,?> cctx, List<String> extraSpaces) {
+    private boolean isPreloadingActive(final GridCacheContext<?, ?> cctx, List<Integer> extraSpaces) {
         if (hasMovingPartitions(cctx))
             return true;
 
         if (extraSpaces != null) {
-            for (String extraSpace : extraSpaces) {
-                if (hasMovingPartitions(cacheContext(extraSpace)))
+            for (int i = 0; i < extraSpaces.size(); i++) {
+                if (hasMovingPartitions(cacheContext(extraSpaces.get(i))))
                     return true;
             }
         }
@@ -357,7 +377,7 @@ public class GridReduceQueryExecutor {
      * @param cctx Cache context.
      * @return {@code true} If cache context
      */
-    private boolean hasMovingPartitions(GridCacheContext<?,?> cctx) {
+    private boolean hasMovingPartitions(GridCacheContext<?, ?> cctx) {
         GridDhtPartitionFullMap fullMap = cctx.topology().partitionMap(false);
 
         for (GridDhtPartitionMap2 map : fullMap.values()) {
@@ -369,34 +389,34 @@ public class GridReduceQueryExecutor {
     }
 
     /**
-     * @param name Cache name.
+     * @param cacheId Cache ID.
      * @return Cache context.
      */
-    private GridCacheContext<?,?> cacheContext(String name) {
-        return ctx.cache().internalCache(name).context();
+    private GridCacheContext<?,?> cacheContext(Integer cacheId) {
+        return ctx.cache().context().cacheContext(cacheId);
     }
 
     /**
      * @param topVer Topology version.
      * @param cctx Cache context for main space.
      * @param extraSpaces Extra spaces.
-     * @return Data nodes or {@code null} if repartitioning started and we need to retry..
+     * @return Data nodes or {@code null} if repartitioning started and we need to retry.
      */
     private Collection<ClusterNode> stableDataNodes(
         AffinityTopologyVersion topVer,
-        final GridCacheContext<?,?> cctx,
-        List<String> extraSpaces
+        final GridCacheContext<?, ?> cctx,
+        List<Integer> extraSpaces
     ) {
-        String space = cctx.name();
-
         Set<ClusterNode> nodes = new HashSet<>(cctx.affinity().assignment(topVer).primaryPartitionNodes());
 
         if (F.isEmpty(nodes))
-            throw new CacheException("Failed to find data nodes for cache: " + space);
+            throw new CacheException("Failed to find data nodes for cache: " + cctx.name());
 
         if (!F.isEmpty(extraSpaces)) {
-            for (String extraSpace : extraSpaces) {
-                GridCacheContext<?,?> extraCctx = cacheContext(extraSpace);
+            for (int i = 0; i < extraSpaces.size(); i++) {
+                GridCacheContext<?,?> extraCctx = cacheContext(extraSpaces.get(i));
+
+                String extraSpace = extraCctx.name();
 
                 if (extraCctx.isLocal())
                     continue; // No consistency guaranties for local caches.
@@ -448,10 +468,16 @@ public class GridReduceQueryExecutor {
     /**
      * @param cctx Cache context.
      * @param qry Query.
-     * @param keepBinary Keep binary.
+     * @param keepPortable Keep portable.
+     * @param enforceJoinOrder Enforce join order of tables.
      * @return Cursor.
      */
-    public Iterator<List<?>> query(GridCacheContext<?,?> cctx, GridCacheTwoStepQuery qry, boolean keepBinary) {
+    public Iterator<List<?>> query(
+        GridCacheContext<?, ?> cctx,
+        GridCacheTwoStepQuery qry,
+        boolean keepPortable,
+        boolean enforceJoinOrder
+    ) {
         for (int attempt = 0;; attempt++) {
             if (attempt != 0) {
                 try {
@@ -464,21 +490,15 @@ public class GridReduceQueryExecutor {
                 }
             }
 
-            long qryReqId = reqIdGen.incrementAndGet();
+            final long qryReqId = reqIdGen.incrementAndGet();
 
-            QueryRun r = new QueryRun();
+            final String space = cctx.name();
 
-            r.pageSize = qry.pageSize() <= 0 ? GridCacheTwoStepQuery.DFLT_PAGE_SIZE : qry.pageSize();
-
-            r.idxs = new ArrayList<>(qry.mapQueries().size());
-
-            String space = cctx.name();
-
-            r.conn = (JdbcConnection)h2.connectionForSpace(space);
+            final QueryRun r = new QueryRun(h2.connectionForSpace(space), qry.mapQueries().size(), qry.pageSize());
 
             AffinityTopologyVersion topVer = h2.readyTopologyVersion();
 
-            List<String> extraSpaces = extraSpaces(space, qry.spaces());
+            List<Integer> extraSpaces = qry.extraCaches();
 
             Collection<ClusterNode> nodes;
 
@@ -529,13 +549,12 @@ public class GridReduceQueryExecutor {
 
                     idx = tbl.getScanIndex(null);
 
-                    fakeTable(r.conn, tblIdx++).setInnerTable(tbl);
+                    fakeTable(r.conn, tblIdx++).innerTable(tbl);
                 }
                 else
                     idx = GridMergeIndexUnsorted.createDummy(ctx);
 
-                for (ClusterNode node : nodes)
-                    idx.addSource(node.id());
+                idx.setSources(nodes);
 
                 r.idxs.add(idx);
             }
@@ -548,10 +567,10 @@ public class GridReduceQueryExecutor {
                 if (ctx.clientDisconnected()) {
                     throw new CacheException("Query was cancelled, client node disconnected.",
                         new IgniteClientDisconnectedException(ctx.cluster().clientReconnectFuture(),
-                        "Client node disconnected."));
+                            "Client node disconnected."));
                 }
 
-                Collection<GridCacheSqlQuery> mapQrys = qry.mapQueries();
+                List<GridCacheSqlQuery> mapQrys = qry.mapQueries();
 
                 if (qry.explain()) {
                     mapQrys = new ArrayList<>(qry.mapQueries().size());
@@ -560,17 +579,37 @@ public class GridReduceQueryExecutor {
                         mapQrys.add(new GridCacheSqlQuery("EXPLAIN " + mapQry.query(), mapQry.parameters()));
                 }
 
-                if (nodes.size() != 1 || !F.first(nodes).isLocal()) { // Marshall params for remotes.
-                    Marshaller m = ctx.config().getMarshaller();
+                boolean retry = false;
 
-                    for (GridCacheSqlQuery mapQry : mapQrys)
-                        mapQry.marshallParams(m);
-                }
+                IgniteProductVersion minNodeVer = cctx.shared().exchange().minimumNodeVersion(topVer);
 
-                boolean retry = false;
+                final boolean oldStyle = minNodeVer.compareToIgnoreTimestamp(DISTRIBUTED_JOIN_SINCE) < 0;
+                final boolean distributedJoins = qry.distributedJoins();
+
+                if (oldStyle && distributedJoins)
+                    throw new CacheException("Failed to enable distributed joins. Topology contains older data nodes.");
 
                 if (send(nodes,
-                    new GridQueryRequest(qryReqId, r.pageSize, space, mapQrys, topVer, extraSpaces, null), partsMap)) {
+                    oldStyle ?
+                        new GridQueryRequest(qryReqId,
+                            r.pageSize,
+                            space,
+                            mapQrys,
+                            topVer,
+                            extraSpaces(space, qry.spaces()),
+                            null) :
+                        new GridH2QueryRequest()
+                            .requestId(qryReqId)
+                            .topologyVersion(topVer)
+                            .pageSize(r.pageSize)
+                            .caches(qry.caches())
+                            .tables(distributedJoins ? qry.tables() : null)
+                            .partitions(convert(partsMap))
+                            .queries(mapQrys)
+                            .flags(distributedJoins ? GridH2QueryRequest.FLAG_DISTRIBUTED_JOINS : 0),
+                    oldStyle && partsMap != null ? new ExplicitPartitionsSpecializer(partsMap) : null,
+                    distributedJoins)
+                ) {
                     awaitAllReplies(r, nodes);
 
                     Object state = r.state.get();
@@ -599,9 +638,6 @@ public class GridReduceQueryExecutor {
                 Iterator<List<?>> resIter = null;
 
                 if (!retry) {
-                    if (qry.explain())
-                        return explainPlan(r.conn, space, qry);
-
                     if (skipMergeTbl) {
                         List<List<?>> res = new ArrayList<>();
 
@@ -616,7 +652,7 @@ public class GridReduceQueryExecutor {
 
                             int cols = row.getColumnCount();
 
-                            List<Object> resRow  = new ArrayList<>(cols);
+                            List<Object> resRow = new ArrayList<>(cols);
 
                             for (int c = 0; c < cols; c++)
                                 resRow.add(row.getValue(c).getObject());
@@ -627,32 +663,52 @@ public class GridReduceQueryExecutor {
                         resIter = res.iterator();
                     }
                     else {
-                        GridCacheSqlQuery rdc = qry.reduceQuery();
+                        UUID locNodeId = ctx.localNodeId();
 
-                        // Statement caching is prohibited here because we can't guarantee correct merge index reuse.
-                        ResultSet res = h2.executeSqlQueryWithTimer(space,
-                            r.conn,
-                            rdc.query(),
-                            F.asList(rdc.parameters()),
-                            false);
+                        h2.setupConnection(r.conn, false, enforceJoinOrder);
 
-                        resIter = new Iter(res);
-                    }
-                }
+                        GridH2QueryContext.set(new GridH2QueryContext(locNodeId, locNodeId, qryReqId, REDUCE)
+                            .pageSize(r.pageSize).distributedJoins(false));
 
-                for (GridMergeIndex idx : r.idxs) {
-                    if (!idx.fetchedAll()) // We have to explicitly cancel queries on remote nodes.
-                        send(nodes, new GridQueryCancelRequest(qryReqId), null);
+                        try {
+                            if (qry.explain())
+                                return explainPlan(r.conn, space, qry);
+
+                            GridCacheSqlQuery rdc = qry.reduceQuery();
+
+                            ResultSet res = h2.executeSqlQueryWithTimer(space,
+                                r.conn,
+                                rdc.query(),
+                                F.asList(rdc.parameters()),
+                                false);
+
+                            resIter = new Iter(res);
+                        }
+                        finally {
+                            GridH2QueryContext.clearThreadLocal();
+                        }
+                    }
                 }
 
                 if (retry) {
+                    send(nodes, new GridQueryCancelRequest(qryReqId), null, false);
+
                     if (Thread.currentThread().isInterrupted())
                         throw new IgniteInterruptedCheckedException("Query was interrupted.");
 
                     continue;
                 }
 
-                return new GridQueryCacheObjectsIterator(resIter, cctx, keepBinary);
+                final Collection<ClusterNode> finalNodes = nodes;
+
+                return new GridQueryCacheObjectsIterator(resIter, cctx, keepPortable) {
+                    @Override public void close() throws Exception {
+                        super.close();
+
+                        if (distributedJoins || !allIndexesFetched(r.idxs))
+                            send(finalNodes, new GridQueryCancelRequest(qryReqId), null, false);
+                    }
+                };
             }
             catch (IgniteCheckedException | RuntimeException e) {
                 U.closeQuiet(r.conn);
@@ -678,13 +734,26 @@ public class GridReduceQueryExecutor {
 
                 if (!skipMergeTbl) {
                     for (int i = 0, mapQrys = qry.mapQueries().size(); i < mapQrys; i++)
-                        fakeTable(null, i).setInnerTable(null); // Drop all merge tables.
+                        fakeTable(null, i).innerTable(null); // Drop all merge tables.
                 }
             }
         }
     }
 
     /**
+     * @param idxs Merge indexes.
+     * @return {@code true} If all remote data was fetched.
+     */
+    private static boolean allIndexesFetched(List<GridMergeIndex> idxs) {
+        for (int i = 0; i <  idxs.size(); i++) {
+            if (!idxs.get(i).fetchedAll())
+                return false;
+        }
+
+        return true;
+    }
+
+    /**
      * @param r Query run.
      * @param nodes Nodes to check periodically if they alive.
      * @throws IgniteInterruptedCheckedException If interrupted.
@@ -715,6 +784,7 @@ public class GridReduceQueryExecutor {
     /**
      * Gets or creates new fake table for index.
      *
+     * @param c Connection.
      * @param idx Index of table.
      * @return Table.
      */
@@ -759,8 +829,8 @@ public class GridReduceQueryExecutor {
      * @param extraSpaces Extra spaces.
      * @return Collection of all data nodes owning all the caches or {@code null} for retry.
      */
-    private Collection<ClusterNode> replicatedUnstableDataNodes(final GridCacheContext<?,?> cctx,
-        List<String> extraSpaces) {
+    private Collection<ClusterNode> replicatedUnstableDataNodes(final GridCacheContext<?, ?> cctx,
+        List<Integer> extraSpaces) {
         assert cctx.isReplicated() : cctx.name() + " must be replicated";
 
         Set<ClusterNode> nodes = replicatedUnstableDataNodes(cctx);
@@ -769,15 +839,15 @@ public class GridReduceQueryExecutor {
             return null; // Retry.
 
         if (!F.isEmpty(extraSpaces)) {
-            for (String extraSpace : extraSpaces) {
-                GridCacheContext<?,?> extraCctx = cacheContext(extraSpace);
+            for (int i = 0; i < extraSpaces.size(); i++) {
+                GridCacheContext<?, ?> extraCctx = cacheContext(extraSpaces.get(i));
 
                 if (extraCctx.isLocal())
                     continue;
 
                 if (!extraCctx.isReplicated())
                     throw new CacheException("Queries running on replicated cache should not contain JOINs " +
-                        "with tables in partitioned caches [rCache=" + cctx.name() + ", pCache=" + extraSpace + "]");
+                        "with tables in partitioned caches [rCache=" + cctx.name() + ", pCache=" + extraCctx.name() + "]");
 
                 Set<ClusterNode> extraOwners = replicatedUnstableDataNodes(extraCctx);
 
@@ -846,14 +916,14 @@ public class GridReduceQueryExecutor {
      */
     @SuppressWarnings("unchecked")
     private Map<ClusterNode, IntArray> partitionedUnstableDataNodes(final GridCacheContext<?,?> cctx,
-        List<String> extraSpaces) {
+        List<Integer> extraSpaces) {
         assert !cctx.isReplicated() && !cctx.isLocal() : cctx.name() + " must be partitioned";
 
         final int partsCnt = cctx.affinity().partitions();
 
         if (extraSpaces != null) { // Check correct number of partitions for partitioned caches.
-            for (String extraSpace : extraSpaces) {
-                GridCacheContext<?,?> extraCctx = cacheContext(extraSpace);
+            for (int i = 0; i < extraSpaces.size(); i++) {
+                GridCacheContext<?, ?> extraCctx = cacheContext(extraSpaces.get(i));
 
                 if (extraCctx.isReplicated() || extraCctx.isLocal())
                     continue;
@@ -862,7 +932,7 @@ public class GridReduceQueryExecutor {
 
                 if (parts != partsCnt)
                     throw new CacheException("Number of partitions must be the same for correct collocation [cache1=" +
-                        cctx.name() + ", parts1=" + partsCnt + ", cache2=" + extraSpace + ", parts2=" + parts + "]");
+                        cctx.name() + ", parts1=" + partsCnt + ", cache2=" + extraCctx.name() + ", parts2=" + parts + "]");
             }
         }
 
@@ -885,8 +955,8 @@ public class GridReduceQueryExecutor {
         if (extraSpaces != null) {
             // Find owner intersections for each participating partitioned cache partition.
             // We need this for logical collocation between different partitioned caches with the same affinity.
-            for (String extraSpace : extraSpaces) {
-                GridCacheContext<?,?> extraCctx = cacheContext(extraSpace);
+            for (int i = 0; i < extraSpaces.size(); i++) {
+                GridCacheContext<?, ?> extraCctx = cacheContext(extraSpaces.get(i));
 
                 if (extraCctx.isReplicated() || extraCctx.isLocal())
                     continue;
@@ -895,10 +965,10 @@ public class GridReduceQueryExecutor {
                     List<ClusterNode> owners = extraCctx.topology().owners(p);
 
                     if (F.isEmpty(owners)) {
-                        if (!F.isEmpty(dataNodes(extraSpace, NONE)))
+                        if (!F.isEmpty(dataNodes(extraCctx.name(), NONE)))
                             return null; // Retry.
 
-                        throw new CacheException("Failed to find data nodes [cache=" + extraSpace + ", part=" + p + "]");
+                        throw new CacheException("Failed to find data nodes [cache=" + extraCctx.name() + ", part=" + p + "]");
                     }
 
                     if (partLocs[p] == null)
@@ -913,8 +983,8 @@ public class GridReduceQueryExecutor {
             }
 
             // Filter nodes where not all the replicated caches loaded.
-            for (String extraSpace : extraSpaces) {
-                GridCacheContext<?,?> extraCctx = cacheContext(extraSpace);
+            for (int i = 0; i < extraSpaces.size(); i++) {
+                GridCacheContext<?,?> extraCctx = cacheContext(extraSpaces.get(i));
 
                 if (!extraCctx.isReplicated())
                     continue;
@@ -960,7 +1030,7 @@ public class GridReduceQueryExecutor {
      * @param allSpaces All spaces.
      * @return List of all extra spaces or {@code null} if none.
      */
-    private List<String> extraSpaces(String mainSpace, Set<String> allSpaces) {
+    private List<String> extraSpaces(String mainSpace, Collection<String> allSpaces) {
         if (F.isEmpty(allSpaces) || (allSpaces.size() == 1 && allSpaces.contains(mainSpace)))
             return null;
 
@@ -996,7 +1066,7 @@ public class GridReduceQueryExecutor {
         for (GridCacheSqlQuery mapQry : qry.mapQueries()) {
             GridMergeTable tbl = createMergeTable(c, mapQry, false);
 
-            fakeTable(c, tblIdx++).setInnerTable(tbl);
+            fakeTable(c, tblIdx++).innerTable(tbl);
         }
 
         GridCacheSqlQuery rdc = qry.reduceQuery();
@@ -1032,39 +1102,27 @@ public class GridReduceQueryExecutor {
     /**
      * @param nodes Nodes.
      * @param msg Message.
-     * @param partsMap Partitions.
+     * @param specialize Optional closure to specialize message for each node.
+     * @param runLocParallel Run local handler in parallel thread.
      * @return {@code true} If all messages sent successfully.
      */
     private boolean send(
         Collection<ClusterNode> nodes,
         Message msg,
-        Map<ClusterNode,IntArray> partsMap
+        @Nullable IgniteBiClosure<ClusterNode, Message, Message> specialize,
+        boolean runLocParallel
     ) {
-        boolean locNodeFound = false;
-
-        boolean ok = true;
-
-        for (ClusterNode node : nodes) {
-            if (node.isLocal()) {
-                locNodeFound = true;
-
-                continue;
-            }
-
-            try {
-                ctx.io().send(node, GridTopic.TOPIC_QUERY, copy(msg, node, partsMap), QUERY_POOL);
-            }
-            catch (IgniteCheckedException e) {
-                ok = false;
-
-                U.warn(log, e.getMessage());
-            }
-        }
-
-        if (locNodeFound) // Local node goes the last to allow parallel execution.
-            h2.mapQueryExecutor().onMessage(ctx.localNodeId(), copy(msg, ctx.discovery().localNode(), partsMap));
-
-        return ok;
+        if (log.isDebugEnabled())
+            log.debug("Sending: [msg=" + msg + ", nodes=" + nodes + ", specialize=" + specialize + "]");
+
+        return h2.send(GridTopic.TOPIC_QUERY,
+            GridTopic.TOPIC_QUERY.ordinal(),
+            nodes,
+            msg,
+            specialize,
+            locNodeHnd,
+            QUERY_POOL,
+            runLocParallel);
     }
 
     /**
@@ -1074,8 +1132,7 @@ public class GridReduceQueryExecutor {
      * @return Copy of message with partitions set.
      */
     private Message copy(Message msg, ClusterNode node, Map<ClusterNode,IntArray> partsMap) {
-        if (partsMap == null)
-            return msg;
+        assert partsMap != null;
 
         GridQueryRequest res = new GridQueryRequest((GridQueryRequest)msg);
 
@@ -1083,11 +1140,35 @@ public class GridReduceQueryExecutor {
 
         assert parts != null : node;
 
-        int[] partsArr = new int[parts.size()];
+        res.partitions(toArray(parts));
 
-        parts.toArray(partsArr);
+        return res;
+    }
 
-        res.partitions(partsArr);
+    /**
+     * @param ints Ints.
+     * @return Array.
+     */
+    public static int[] toArray(IntArray ints) {
+        int[] res = new int[ints.size()];
+
+        ints.toArray(res);
+
+        return res;
+    }
+
+    /**
+     * @param m Map.
+     * @return Converted map.
+     */
+    private static Map<UUID, int[]> convert(Map<ClusterNode, IntArray> m) {
+        if (m == null)
+            return null;
+
+        Map<UUID, int[]> res = U.newHashMap(m.size());
+
+        for (Map.Entry<ClusterNode,IntArray> entry : m.entrySet())
+            res.put(entry.getKey().id(), toArray(entry.getValue()));
 
         return res;
     }
@@ -1097,7 +1178,7 @@ public class GridReduceQueryExecutor {
      * @param qry Query.
      * @param explain Explain.
      * @return Table.
-     * @throws IgniteCheckedException
+     * @throws IgniteCheckedException If failed.
      */
     private GridMergeTable createMergeTable(JdbcConnection conn, GridCacheSqlQuery qry, boolean explain)
         throws IgniteCheckedException {
@@ -1165,25 +1246,36 @@ public class GridReduceQueryExecutor {
     }
 
     /**
-     *
+     * Query run.
      */
     private static class QueryRun {
         /** */
-        private List<GridMergeIndex> idxs;
+        private final List<GridMergeIndex> idxs;
 
         /** */
         private CountDownLatch latch;
 
         /** */
-        private JdbcConnection conn;
+        private final JdbcConnection conn;
 
         /** */
-        private int pageSize;
+        private final int pageSize;
 
         /** Can be either CacheException in case of error or AffinityTopologyVersion to retry if needed. */
         private final AtomicReference<Object> state = new AtomicReference<>();
 
         /**
+         * @param conn Connection.
+         * @param idxsCnt Number of indexes.
+         * @param pageSize Page size.
+         */
+        private QueryRun(Connection conn, int idxsCnt, int pageSize) {
+            this.conn = (JdbcConnection)conn;
+            this.idxs = new ArrayList<>(idxsCnt);
+            this.pageSize = pageSize > 0 ? pageSize : GridCacheTwoStepQuery.DFLT_PAGE_SIZE;
+        }
+
+        /**
          * @param o Fail state object.
          * @param nodeId Node ID.
          */
@@ -1240,4 +1332,24 @@ public class GridReduceQueryExecutor {
             return res;
         }
     }
+
+    /**
+     *
+     */
+    private class ExplicitPartitionsSpecializer implements IgniteBiClosure<ClusterNode,Message,Message> {
+        /** */
+        private final Map<ClusterNode,IntArray> partsMap;
+
+        /**
+         * @param partsMap Partitions map.
+         */
+        private ExplicitPartitionsSpecializer(Map<ClusterNode,IntArray> partsMap) {
+            this.partsMap = partsMap;
+        }
+
+        /** {@inheritDoc} */
+        @Override public Message apply(ClusterNode n, Message msg) {
+            return copy(msg, n, partsMap);
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridThreadLocalTable.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridThreadLocalTable.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridThreadLocalTable.java
index a38d137..d46fb2f 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridThreadLocalTable.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridThreadLocalTable.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.processors.query.h2.twostep;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
+import javax.cache.CacheException;
 import org.h2.api.TableEngine;
 import org.h2.command.ddl.CreateTableData;
 import org.h2.engine.DbObject;
@@ -59,61 +60,74 @@ public class GridThreadLocalTable extends Table {
     /**
      * @param t Table or {@code null} to reset existing.
      */
-    public void setInnerTable(Table t) {
+    public void innerTable(Table t) {
         if (t == null)
             tbl.remove();
         else
             tbl.set(t);
     }
 
+    /**
+     * @return Inner table.
+     */
+    private Table innerTable() {
+        Table t = tbl.get();
+
+        if (t == null)
+            throw new CacheException("Table `" + getName() + "` can be accessed only within Ignite query context.");
+
+        return t;
+    }
+
     /** {@inheritDoc} */
     @Override public Index getPrimaryKey() {
-        return tbl.get().getPrimaryKey();
+        return innerTable().getPrimaryKey();
     }
 
     /** {@inheritDoc} */
     @Override public Column getRowIdColumn() {
-        return tbl.get().getRowIdColumn();
+        return innerTable().getRowIdColumn();
     }
 
     /** {@inheritDoc} */
-    @Override public PlanItem getBestPlanItem(Session session, int[] masks, TableFilter filter, SortOrder sortOrder) {
-        return tbl.get().getBestPlanItem(session, masks, filter, sortOrder);
+    @Override public PlanItem getBestPlanItem(Session session, int[] masks, TableFilter[] filters, int filter,
+        SortOrder sortOrder) {
+        return innerTable().getBestPlanItem(session, masks, filters, filter, sortOrder);
     }
 
     /** {@inheritDoc} */
     @Override public Value getDefaultValue(Session session, Column column) {
-        return tbl.get().getDefaultValue(session, column);
+        return innerTable().getDefaultValue(session, column);
     }
 
     /** {@inheritDoc} */
     @Override public SearchRow getTemplateSimpleRow(boolean singleColumn) {
-        return tbl.get().getTemplateSimpleRow(singleColumn);
+        return innerTable().getTemplateSimpleRow(singleColumn);
     }
 
     /** {@inheritDoc} */
     @Override public Row getTemplateRow() {
-        return tbl.get().getTemplateRow();
+        return innerTable().getTemplateRow();
     }
 
     /** {@inheritDoc} */
     @Override public Column getColumn(String columnName) {
-        return tbl.get().getColumn(columnName);
+        return innerTable().getColumn(columnName);
     }
 
     /** {@inheritDoc} */
     @Override public Column getColumn(int index) {
-        return tbl.get().getColumn(index);
+        return innerTable().getColumn(index);
     }
 
     /** {@inheritDoc} */
     @Override public Index getIndexForColumn(Column column) {
-        return tbl.get().getIndexForColumn(column);
+        return innerTable().getIndexForColumn(column);
     }
 
     /** {@inheritDoc} */
     @Override public Column[] getColumns() {
-        return tbl.get().getColumns();
+        return innerTable().getColumns();
     }
 
     /** {@inheritDoc} */
@@ -122,8 +136,8 @@ public class GridThreadLocalTable extends Table {
     }
 
     /** {@inheritDoc} */
-    @Override public void lock(Session session, boolean exclusive, boolean force) {
-        tbl.get().lock(session, exclusive, force);
+    @Override public boolean lock(Session session, boolean exclusive, boolean force) {
+        return innerTable().lock(session, exclusive, force);
     }
 
     /** {@inheritDoc} */
@@ -133,33 +147,33 @@ public class GridThreadLocalTable extends Table {
 
     /** {@inheritDoc} */
     @Override public void unlock(Session s) {
-        tbl.get().unlock(s);
+        innerTable().unlock(s);
     }
 
     /** {@inheritDoc} */
     @Override public Index addIndex(Session session, String indexName, int indexId, IndexColumn[] cols,
         IndexType indexType, boolean create, String indexComment) {
-        return tbl.get().addIndex(session, indexName, indexId, cols, indexType, create, indexComment);
+        return innerTable().addIndex(session, indexName, indexId, cols, indexType, create, indexComment);
     }
 
     /** {@inheritDoc} */
     @Override public void removeRow(Session session, Row row) {
-        tbl.get().removeRow(session, row);
+        innerTable().removeRow(session, row);
     }
 
     /** {@inheritDoc} */
     @Override public void truncate(Session session) {
-        tbl.get().truncate(session);
+        innerTable().truncate(session);
     }
 
     /** {@inheritDoc} */
     @Override public void addRow(Session session, Row row) {
-        tbl.get().addRow(session, row);
+        innerTable().addRow(session, row);
     }
 
     /** {@inheritDoc} */
     @Override public void checkSupportAlter() {
-        tbl.get().checkSupportAlter();
+        innerTable().checkSupportAlter();
     }
 
     /** {@inheritDoc} */
@@ -169,22 +183,22 @@ public class GridThreadLocalTable extends Table {
 
     /** {@inheritDoc} */
     @Override public Index getUniqueIndex() {
-        return tbl.get().getUniqueIndex();
+        return innerTable().getUniqueIndex();
     }
 
     /** {@inheritDoc} */
     @Override public Index getScanIndex(Session session) {
-        return tbl.get().getScanIndex(session);
+        return innerTable().getScanIndex(session);
     }
 
     /** {@inheritDoc} */
     @Override public ArrayList<Index> getIndexes() {
-        return tbl.get().getIndexes();
+        return innerTable().getIndexes();
     }
 
     /** {@inheritDoc} */
     @Override public boolean isLockedExclusively() {
-        return tbl.get().isLockedExclusively();
+        return innerTable().isLockedExclusively();
     }
 
     /** {@inheritDoc} */
@@ -194,12 +208,12 @@ public class GridThreadLocalTable extends Table {
 
     /** {@inheritDoc} */
     @Override public boolean isDeterministic() {
-        return tbl.get().isDeterministic();
+        return innerTable().isDeterministic();
     }
 
     /** {@inheritDoc} */
     @Override public boolean canGetRowCount() {
-        return tbl.get().canGetRowCount();
+        return innerTable().canGetRowCount();
     }
 
     /** {@inheritDoc} */
@@ -209,7 +223,7 @@ public class GridThreadLocalTable extends Table {
 
     /** {@inheritDoc} */
     @Override public long getRowCount(Session session) {
-        return tbl.get().getRowCount(session);
+        return innerTable().getRowCount(session);
     }
 
     /** {@inheritDoc} */

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Array.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Array.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Array.java
index 362a760..571f9ac 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Array.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Array.java
@@ -115,7 +115,7 @@ public class GridH2Array extends GridH2ValueMessage {
 
         }
 
-        return true;
+        return reader.afterMessageRead(GridH2Array.class);
     }
 
     /** {@inheritDoc} */
@@ -127,4 +127,9 @@ public class GridH2Array extends GridH2ValueMessage {
     @Override public byte fieldsCount() {
         return 1;
     }
-}
\ No newline at end of file
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return String.valueOf(x);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Boolean.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Boolean.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Boolean.java
index 0628764..edd404e 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Boolean.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Boolean.java
@@ -99,7 +99,7 @@ public class GridH2Boolean extends GridH2ValueMessage {
 
         }
 
-        return true;
+        return reader.afterMessageRead(GridH2Boolean.class);
     }
 
     /** {@inheritDoc} */
@@ -107,7 +107,13 @@ public class GridH2Boolean extends GridH2ValueMessage {
         return -5;
     }
 
+    /** {@inheritDoc} */
     @Override public byte fieldsCount() {
         return 1;
     }
-}
\ No newline at end of file
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return String.valueOf(x);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Byte.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Byte.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Byte.java
index 9f608b9..894794e 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Byte.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Byte.java
@@ -99,7 +99,7 @@ public class GridH2Byte extends GridH2ValueMessage {
 
         }
 
-        return true;
+        return reader.afterMessageRead(GridH2Byte.class);
     }
 
     /** {@inheritDoc} */
@@ -111,4 +111,9 @@ public class GridH2Byte extends GridH2ValueMessage {
     @Override public byte fieldsCount() {
         return 1;
     }
-}
\ No newline at end of file
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return String.valueOf(x);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Bytes.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Bytes.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Bytes.java
index 0fb9e5d..29a52be 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Bytes.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Bytes.java
@@ -24,6 +24,8 @@ import org.apache.ignite.plugin.extensions.communication.MessageWriter;
 import org.h2.value.Value;
 import org.h2.value.ValueBytes;
 
+import static org.h2.util.StringUtils.convertBytesToHex;
+
 /**
  * H2 Bytes.
  */
@@ -99,7 +101,7 @@ public class GridH2Bytes extends GridH2ValueMessage {
 
         }
 
-        return true;
+        return reader.afterMessageRead(GridH2Bytes.class);
     }
 
     /** {@inheritDoc} */
@@ -111,4 +113,9 @@ public class GridH2Bytes extends GridH2ValueMessage {
     @Override public byte fieldsCount() {
         return 1;
     }
-}
\ No newline at end of file
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return "b_" + convertBytesToHex(b);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2CacheObject.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2CacheObject.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2CacheObject.java
index 1a81234..942ab7c 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2CacheObject.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2CacheObject.java
@@ -102,7 +102,7 @@ public class GridH2CacheObject extends GridH2ValueMessage {
 
         }
 
-        return true;
+        return reader.afterMessageRead(GridH2CacheObject.class);
     }
 
     /** {@inheritDoc} */
@@ -146,4 +146,9 @@ public class GridH2CacheObject extends GridH2ValueMessage {
     @Override public byte fieldsCount() {
         return 2;
     }
-}
\ No newline at end of file
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return String.valueOf(obj);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Date.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Date.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Date.java
index 6bd5237..8025257 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Date.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Date.java
@@ -101,7 +101,7 @@ public class GridH2Date extends GridH2ValueMessage {
 
         }
 
-        return true;
+        return reader.afterMessageRead(GridH2Date.class);
     }
 
     /** {@inheritDoc} */
@@ -113,4 +113,9 @@ public class GridH2Date extends GridH2ValueMessage {
     @Override public byte fieldsCount() {
         return 1;
     }
-}
\ No newline at end of file
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return String.valueOf(date);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Decimal.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Decimal.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Decimal.java
index f5d4865..a3ad444 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Decimal.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Decimal.java
@@ -26,6 +26,8 @@ import org.apache.ignite.plugin.extensions.communication.MessageWriter;
 import org.h2.value.Value;
 import org.h2.value.ValueDecimal;
 
+import static org.h2.util.StringUtils.convertBytesToHex;
+
 /**
  * H2 Decimal.
  */
@@ -121,7 +123,7 @@ public class GridH2Decimal extends GridH2ValueMessage {
 
         }
 
-        return true;
+        return reader.afterMessageRead(GridH2Decimal.class);
     }
 
     /** {@inheritDoc} */
@@ -133,4 +135,9 @@ public class GridH2Decimal extends GridH2ValueMessage {
     @Override public byte fieldsCount() {
         return 2;
     }
-}
\ No newline at end of file
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return scale + "_" + convertBytesToHex(b);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Double.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Double.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Double.java
index 481db30..2ceea8d 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Double.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Double.java
@@ -99,7 +99,7 @@ public class GridH2Double extends GridH2ValueMessage {
 
         }
 
-        return true;
+        return reader.afterMessageRead(GridH2Double.class);
     }
 
     /** {@inheritDoc} */
@@ -111,4 +111,9 @@ public class GridH2Double extends GridH2ValueMessage {
     @Override public byte fieldsCount() {
         return 1;
     }
-}
\ No newline at end of file
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return String.valueOf(x);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Float.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Float.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Float.java
index 55f9380..6923470 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Float.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Float.java
@@ -99,7 +99,7 @@ public class GridH2Float extends GridH2ValueMessage {
 
         }
 
-        return true;
+        return reader.afterMessageRead(GridH2Float.class);
     }
 
     /** {@inheritDoc} */
@@ -111,4 +111,9 @@ public class GridH2Float extends GridH2ValueMessage {
     @Override public byte fieldsCount() {
         return 1;
     }
-}
\ No newline at end of file
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return String.valueOf(x);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Geometry.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Geometry.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Geometry.java
index 21070d1..0d118b4 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Geometry.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Geometry.java
@@ -25,6 +25,8 @@ import org.apache.ignite.plugin.extensions.communication.MessageReader;
 import org.apache.ignite.plugin.extensions.communication.MessageWriter;
 import org.h2.value.Value;
 
+import static org.h2.util.StringUtils.convertBytesToHex;
+
 /**
  * H2 Geometry.
  */
@@ -120,7 +122,7 @@ public class GridH2Geometry extends GridH2ValueMessage {
 
         }
 
-        return true;
+        return reader.afterMessageRead(GridH2Geometry.class);
     }
 
     /** {@inheritDoc} */
@@ -132,4 +134,9 @@ public class GridH2Geometry extends GridH2ValueMessage {
     @Override public byte fieldsCount() {
         return 1;
     }
-}
\ No newline at end of file
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return "g_" + convertBytesToHex(b);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2IndexRangeRequest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2IndexRangeRequest.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2IndexRangeRequest.java
new file mode 100644
index 0000000..e49c48f
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2IndexRangeRequest.java
@@ -0,0 +1,208 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.processors.query.h2.twostep.msg;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.UUID;
+import org.apache.ignite.internal.GridDirectCollection;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.plugin.extensions.communication.Message;
+import org.apache.ignite.plugin.extensions.communication.MessageCollectionItemType;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+
+/**
+ * Range request.
+ */
+public class GridH2IndexRangeRequest implements Message {
+    /** */
+    private UUID originNodeId;
+
+    /** */
+    private long qryId;
+
+    /** */
+    private int batchLookupId;
+
+    /** */
+    @GridDirectCollection(Message.class)
+    private List<GridH2RowRangeBounds> bounds;
+
+    /**
+     * @param bounds Range bounds list.
+     */
+    public void bounds(List<GridH2RowRangeBounds> bounds) {
+        this.bounds = bounds;
+    }
+
+    /**
+     * @return Range bounds list.
+     */
+    public List<GridH2RowRangeBounds> bounds() {
+        return bounds;
+    }
+
+    /**
+     * @return Origin node ID.
+     */
+    public UUID originNodeId() {
+        return originNodeId;
+    }
+
+    /**
+     * @param originNodeId Origin node ID.
+     */
+    public void originNodeId(UUID originNodeId) {
+        this.originNodeId = originNodeId;
+    }
+
+    /**
+     * @return Query ID.
+     */
+    public long queryId() {
+        return qryId;
+    }
+
+    /**
+     * @param qryId Query ID.
+     */
+    public void queryId(long qryId) {
+        this.qryId = qryId;
+    }
+
+    /**
+     * @param batchLookupId Batch lookup ID.
+     */
+    public void batchLookupId(int batchLookupId) {
+        this.batchLookupId = batchLookupId;
+    }
+
+    /**
+     * @return Batch lookup ID.
+     */
+    public int batchLookupId() {
+        return batchLookupId;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+        writer.setBuffer(buf);
+
+        if (!writer.isHeaderWritten()) {
+            if (!writer.writeHeader(directType(), fieldsCount()))
+                return false;
+
+            writer.onHeaderWritten();
+        }
+
+        switch (writer.state()) {
+            case 0:
+                if (!writer.writeInt("batchLookupId", batchLookupId))
+                    return false;
+
+                writer.incrementState();
+
+            case 1:
+                if (!writer.writeCollection("bounds", bounds, MessageCollectionItemType.MSG))
+                    return false;
+
+                writer.incrementState();
+
+            case 2:
+                if (!writer.writeUuid("originNodeId", originNodeId))
+                    return false;
+
+                writer.incrementState();
+
+            case 3:
+                if (!writer.writeLong("qryId", qryId))
+                    return false;
+
+                writer.incrementState();
+
+        }
+
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+        reader.setBuffer(buf);
+
+        if (!reader.beforeMessageRead())
+            return false;
+
+        switch (reader.state()) {
+            case 0:
+                batchLookupId = reader.readInt("batchLookupId");
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
+            case 1:
+                bounds = reader.readCollection("bounds", MessageCollectionItemType.MSG);
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
+            case 2:
+                originNodeId = reader.readUuid("originNodeId");
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
+            case 3:
+                qryId = reader.readLong("qryId");
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
+        }
+
+        return reader.afterMessageRead(GridH2IndexRangeRequest.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte directType() {
+        return -30;
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte fieldsCount() {
+        return 4;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void onAckReceived() {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(GridH2IndexRangeRequest.class, this, "boundsSize", bounds == null ? null : bounds.size());
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2IndexRangeResponse.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2IndexRangeResponse.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2IndexRangeResponse.java
new file mode 100644
index 0000000..c6414bd
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2IndexRangeResponse.java
@@ -0,0 +1,279 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.h2.twostep.msg;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.UUID;
+import org.apache.ignite.internal.GridDirectCollection;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.plugin.extensions.communication.Message;
+import org.apache.ignite.plugin.extensions.communication.MessageCollectionItemType;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+
+/**
+ * Range response message.
+ */
+public class GridH2IndexRangeResponse implements Message {
+    /** */
+    public static final byte STATUS_OK = 0;
+
+    /** */
+    public static final byte STATUS_ERROR = 1;
+
+    /** */
+    public static final byte STATUS_NOT_FOUND = 2;
+
+    /** */
+    private UUID originNodeId;
+
+    /** */
+    private long qryId;
+
+    /** */
+    private int batchLookupId;
+
+    /** */
+    @GridDirectCollection(Message.class)
+    private List<GridH2RowRange> ranges;
+
+    /** */
+    private byte status;
+
+    /** */
+    private String err;
+
+    /**
+     * @param ranges Ranges.
+     */
+    public void ranges(List<GridH2RowRange> ranges) {
+        this.ranges = ranges;
+    }
+
+    /**
+     * @return Ranges.
+     */
+    public List<GridH2RowRange> ranges() {
+        return ranges;
+    }
+
+    /**
+     * @return Origin node ID.
+     */
+    public UUID originNodeId() {
+        return originNodeId;
+    }
+
+    /**
+     * @param originNodeId Origin node ID.
+     */
+    public void originNodeId(UUID originNodeId) {
+        this.originNodeId = originNodeId;
+    }
+
+    /**
+     * @return Query ID.
+     */
+    public long queryId() {
+        return qryId;
+    }
+
+    /**
+     * @param qryId Query ID.
+     */
+    public void queryId(long qryId) {
+        this.qryId = qryId;
+    }
+
+    /**
+     * @param err Error message.
+     */
+    public void error(String err) {
+        this.err = err;
+    }
+
+    /**
+     * @return Error message or {@code null} if everything is ok.
+     */
+    public String error() {
+        return err;
+    }
+
+    /**
+     * @param status Status.
+     */
+    public void status(byte status) {
+        this.status = status;
+    }
+
+    /**
+     * @return Status.
+     */
+    public byte status() {
+        return status;
+    }
+
+    /**
+     * @param batchLookupId Batch lookup ID.
+     */
+    public void batchLookupId(int batchLookupId) {
+        this.batchLookupId = batchLookupId;
+    }
+
+    /**
+     * @return Batch lookup ID.
+     */
+    public int batchLookupId() {
+        return batchLookupId;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+        writer.setBuffer(buf);
+
+        if (!writer.isHeaderWritten()) {
+            if (!writer.writeHeader(directType(), fieldsCount()))
+                return false;
+
+            writer.onHeaderWritten();
+        }
+
+        switch (writer.state()) {
+            case 0:
+                if (!writer.writeInt("batchLookupId", batchLookupId))
+                    return false;
+
+                writer.incrementState();
+
+            case 1:
+                if (!writer.writeString("err", err))
+                    return false;
+
+                writer.incrementState();
+
+            case 2:
+                if (!writer.writeUuid("originNodeId", originNodeId))
+                    return false;
+
+                writer.incrementState();
+
+            case 3:
+                if (!writer.writeLong("qryId", qryId))
+                    return false;
+
+                writer.incrementState();
+
+            case 4:
+                if (!writer.writeCollection("ranges", ranges, MessageCollectionItemType.MSG))
+                    return false;
+
+                writer.incrementState();
+
+            case 5:
+                if (!writer.writeByte("status", status))
+                    return false;
+
+                writer.incrementState();
+
+        }
+
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+        reader.setBuffer(buf);
+
+        if (!reader.beforeMessageRead())
+            return false;
+
+        switch (reader.state()) {
+            case 0:
+                batchLookupId = reader.readInt("batchLookupId");
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
+            case 1:
+                err = reader.readString("err");
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
+            case 2:
+                originNodeId = reader.readUuid("originNodeId");
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
+            case 3:
+                qryId = reader.readLong("qryId");
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
+            case 4:
+                ranges = reader.readCollection("ranges", MessageCollectionItemType.MSG);
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
+            case 5:
+                status = reader.readByte("status");
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
+        }
+
+        return reader.afterMessageRead(GridH2IndexRangeResponse.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte directType() {
+        return -31;
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte fieldsCount() {
+        return 6;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void onAckReceived() {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(GridH2IndexRangeResponse.class, this, "rangesSize", ranges == null ? null : ranges.size());
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Integer.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Integer.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Integer.java
index 30c7623..7c6046c 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Integer.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Integer.java
@@ -99,7 +99,7 @@ public class GridH2Integer extends GridH2ValueMessage {
 
         }
 
-        return true;
+        return reader.afterMessageRead(GridH2Integer.class);
     }
 
     /** {@inheritDoc} */
@@ -111,4 +111,20 @@ public class GridH2Integer extends GridH2ValueMessage {
     @Override public byte fieldsCount() {
         return 1;
     }
-}
\ No newline at end of file
+
+    /** {@inheritDoc} */
+    @Override public boolean equals(Object obj) {
+        return obj == this || (obj != null && obj.getClass() == GridH2Integer.class && x == ((GridH2Integer)obj).x);
+
+    }
+
+    /** {@inheritDoc} */
+    @Override public int hashCode() {
+        return x;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return String.valueOf(x);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2JavaObject.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2JavaObject.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2JavaObject.java
index edfde33..b989171 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2JavaObject.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2JavaObject.java
@@ -24,6 +24,8 @@ import org.apache.ignite.plugin.extensions.communication.MessageWriter;
 import org.h2.value.Value;
 import org.h2.value.ValueJavaObject;
 
+import static org.h2.util.StringUtils.convertBytesToHex;
+
 /**
  * H2 Java Object.
  */
@@ -99,7 +101,7 @@ public class GridH2JavaObject extends GridH2ValueMessage {
 
         }
 
-        return true;
+        return reader.afterMessageRead(GridH2JavaObject.class);
     }
 
     /** {@inheritDoc} */
@@ -111,4 +113,9 @@ public class GridH2JavaObject extends GridH2ValueMessage {
     @Override public byte fieldsCount() {
         return 1;
     }
-}
\ No newline at end of file
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return "j_" + convertBytesToHex(b);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Long.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Long.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Long.java
index e6af9e1..3d360f0 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Long.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Long.java
@@ -99,7 +99,7 @@ public class GridH2Long extends GridH2ValueMessage {
 
         }
 
-        return true;
+        return reader.afterMessageRead(GridH2Long.class);
     }
 
     /** {@inheritDoc} */
@@ -111,4 +111,9 @@ public class GridH2Long extends GridH2ValueMessage {
     @Override public byte fieldsCount() {
         return 1;
     }
-}
\ No newline at end of file
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return String.valueOf(x);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Null.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Null.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Null.java
index 2394b78..50a49ba 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Null.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2Null.java
@@ -64,7 +64,13 @@ public class GridH2Null extends GridH2ValueMessage {
     @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
         reader.setBuffer(buf);
 
-        return reader.beforeMessageRead() && super.readFrom(buf, reader);
+        if (!reader.beforeMessageRead())
+            return false;
+
+        if (!super.readFrom(buf, reader))
+            return false;
+
+        return reader.afterMessageRead(GridH2Null.class);
     }
 
     /** {@inheritDoc} */
@@ -76,4 +82,9 @@ public class GridH2Null extends GridH2ValueMessage {
     @Override public byte fieldsCount() {
         return 0;
     }
-}
\ No newline at end of file
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return "NULL";
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2QueryRequest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2QueryRequest.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2QueryRequest.java
new file mode 100644
index 0000000..dc82b2c
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2QueryRequest.java
@@ -0,0 +1,401 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.processors.query.h2.twostep.msg;
+
+import java.nio.ByteBuffer;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import org.apache.ignite.internal.GridDirectCollection;
+import org.apache.ignite.internal.GridDirectMap;
+import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.query.GridCacheQueryMarshallable;
+import org.apache.ignite.internal.processors.cache.query.GridCacheSqlQuery;
+import org.apache.ignite.internal.util.tostring.GridToStringInclude;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.marshaller.Marshaller;
+import org.apache.ignite.plugin.extensions.communication.Message;
+import org.apache.ignite.plugin.extensions.communication.MessageCollectionItemType;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+
+/**
+ * Query request.
+ */
+public class GridH2QueryRequest implements Message, GridCacheQueryMarshallable {
+    /** */
+    private static final long serialVersionUID = 0L;
+
+    /**
+     * Map query will not destroy context until explicit query cancel request
+     * will be received because distributed join requests can be received.
+     */
+    public static int FLAG_DISTRIBUTED_JOINS = 1;
+
+    /** */
+    private long reqId;
+
+    /** */
+    @GridToStringInclude
+    @GridDirectCollection(Integer.class)
+    private List<Integer> caches;
+
+    /** Topology version. */
+    private AffinityTopologyVersion topVer;
+
+    /** Explicit partitions mappings for nodes. */
+    @GridToStringInclude
+    @GridDirectMap(keyType = UUID.class, valueType = int[].class)
+    private Map<UUID, int[]> parts;
+
+    /** */
+    private int pageSize;
+
+    /** */
+    @GridToStringInclude
+    @GridDirectCollection(Message.class)
+    private List<GridCacheSqlQuery> qrys;
+
+    /** */
+    private byte flags;
+
+    /** */
+    @GridToStringInclude
+    @GridDirectCollection(String.class)
+    private Collection<String> tbls;
+
+    /**
+     * @param tbls Tables.
+     * @return {@code this}.
+     */
+    public GridH2QueryRequest tables(Collection<String> tbls) {
+        this.tbls = tbls;
+
+        return this;
+    }
+
+    /**
+     * @return Tables.
+     */
+    public Collection<String> tables() {
+        return tbls;
+    }
+
+    /**
+     * @param reqId Request ID.
+     * @return {@code this}.
+     */
+    public GridH2QueryRequest requestId(long reqId) {
+        this.reqId = reqId;
+
+        return this;
+    }
+
+    /**
+     * @return Request ID.
+     */
+    public long requestId() {
+        return reqId;
+    }
+
+    /**
+     * @param caches Caches.
+     * @return {@code this}.
+     */
+    public GridH2QueryRequest caches(List<Integer> caches) {
+        this.caches = caches;
+
+        return this;
+    }
+
+    /**
+     * @return Caches.
+     */
+    public List<Integer> caches() {
+        return caches;
+    }
+
+    /**
+     * @param topVer Topology version.
+     * @return {@code this}.
+     */
+    public GridH2QueryRequest topologyVersion(AffinityTopologyVersion topVer) {
+        this.topVer = topVer;
+
+        return this;
+    }
+
+    /**
+     * @return Topology version.
+     */
+    public AffinityTopologyVersion topologyVersion() {
+        return topVer;
+    }
+
+    /**
+     * @return Explicit partitions mapping.
+     */
+    public Map<UUID,int[]> partitions() {
+        return parts;
+    }
+
+    /**
+     * @param parts Explicit partitions mapping.
+     * @return {@code this}.
+     */
+    public GridH2QueryRequest partitions(Map<UUID,int[]> parts) {
+        this.parts = parts;
+
+        return this;
+    }
+
+    /**
+     * @param pageSize Page size.
+     * @return {@code this}.
+     */
+    public GridH2QueryRequest pageSize(int pageSize) {
+        this.pageSize = pageSize;
+
+        return this;
+    }
+
+    /**
+     * @return Page size.
+     */
+    public int pageSize() {
+        return pageSize;
+    }
+
+    /**
+     * @param qrys SQL Queries.
+     * @return {@code this}.
+     */
+    public GridH2QueryRequest queries(List<GridCacheSqlQuery> qrys) {
+        this.qrys = qrys;
+
+        return this;
+    }
+
+    /**
+     * @return SQL Queries.
+     */
+    public List<GridCacheSqlQuery> queries() {
+        return qrys;
+    }
+
+    /**
+     * @param flags Flags.
+     * @return {@code this}.
+     */
+    public GridH2QueryRequest flags(int flags) {
+        this.flags = (byte)flags;
+
+        return this;
+    }
+
+    /**
+     * @param flags Flags to check.
+     * @return {@code true} If all the requested flags are set to {@code true}.
+     */
+    public boolean isFlagSet(int flags) {
+        return (this.flags & flags) == flags;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void marshall(Marshaller m) {
+        if (F.isEmpty(qrys))
+            return;
+
+        for (GridCacheSqlQuery qry : qrys)
+            qry.marshall(m);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void unmarshall(Marshaller m, GridKernalContext ctx) {
+        if (F.isEmpty(qrys))
+            return;
+
+        for (GridCacheSqlQuery qry : qrys)
+            qry.unmarshall(m, ctx);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+        writer.setBuffer(buf);
+
+        if (!writer.isHeaderWritten()) {
+            if (!writer.writeHeader(directType(), fieldsCount()))
+                return false;
+
+            writer.onHeaderWritten();
+        }
+
+        switch (writer.state()) {
+            case 0:
+                if (!writer.writeCollection("caches", caches, MessageCollectionItemType.INT))
+                    return false;
+
+                writer.incrementState();
+
+            case 1:
+                if (!writer.writeByte("flags", flags))
+                    return false;
+
+                writer.incrementState();
+
+            case 2:
+                if (!writer.writeInt("pageSize", pageSize))
+                    return false;
+
+                writer.incrementState();
+
+            case 3:
+                if (!writer.writeMap("parts", parts, MessageCollectionItemType.UUID, MessageCollectionItemType.INT_ARR))
+                    return false;
+
+                writer.incrementState();
+
+            case 4:
+                if (!writer.writeCollection("qrys", qrys, MessageCollectionItemType.MSG))
+                    return false;
+
+                writer.incrementState();
+
+            case 5:
+                if (!writer.writeLong("reqId", reqId))
+                    return false;
+
+                writer.incrementState();
+
+            case 6:
+                if (!writer.writeCollection("tbls", tbls, MessageCollectionItemType.STRING))
+                    return false;
+
+                writer.incrementState();
+
+            case 7:
+                if (!writer.writeMessage("topVer", topVer))
+                    return false;
+
+                writer.incrementState();
+
+        }
+
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+        reader.setBuffer(buf);
+
+        if (!reader.beforeMessageRead())
+            return false;
+
+        switch (reader.state()) {
+            case 0:
+                caches = reader.readCollection("caches", MessageCollectionItemType.INT);
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
+            case 1:
+                flags = reader.readByte("flags");
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
+            case 2:
+                pageSize = reader.readInt("pageSize");
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
+            case 3:
+                parts = reader.readMap("parts", MessageCollectionItemType.UUID, MessageCollectionItemType.INT_ARR, false);
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
+            case 4:
+                qrys = reader.readCollection("qrys", MessageCollectionItemType.MSG);
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
+            case 5:
+                reqId = reader.readLong("reqId");
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
+            case 6:
+                tbls = reader.readCollection("tbls", MessageCollectionItemType.STRING);
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
+            case 7:
+                topVer = reader.readMessage("topVer");
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
+        }
+
+        return reader.afterMessageRead(GridH2QueryRequest.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte directType() {
+        return -33;
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte fieldsCount() {
+        return 8;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void onAckReceived() {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(GridH2QueryRequest.class, this);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2RowMessage.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2RowMessage.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2RowMessage.java
new file mode 100644
index 0000000..59c548d
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2RowMessage.java
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.processors.query.h2.twostep.msg;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+import org.apache.ignite.internal.GridDirectCollection;
+import org.apache.ignite.internal.util.tostring.GridToStringInclude;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.plugin.extensions.communication.Message;
+import org.apache.ignite.plugin.extensions.communication.MessageCollectionItemType;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+
+/**
+ * SQL Row message.
+ */
+public class GridH2RowMessage implements Message {
+    /** */
+    @GridDirectCollection(Message.class)
+    @GridToStringInclude
+    private List<GridH2ValueMessage> vals;
+
+    /**
+     * @return Values of row.
+     */
+    public List<GridH2ValueMessage> values() {
+        return vals;
+    }
+
+    /**
+     * @param vals Values of row.
+     */
+    public void values(List<GridH2ValueMessage> vals) {
+        this.vals = vals;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+        writer.setBuffer(buf);
+
+        if (!writer.isHeaderWritten()) {
+            if (!writer.writeHeader(directType(), fieldsCount()))
+                return false;
+
+            writer.onHeaderWritten();
+        }
+
+        switch (writer.state()) {
+            case 0:
+                if (!writer.writeCollection("vals", vals, MessageCollectionItemType.MSG))
+                    return false;
+
+                writer.incrementState();
+
+        }
+
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+        reader.setBuffer(buf);
+
+        if (!reader.beforeMessageRead())
+            return false;
+
+        switch (reader.state()) {
+            case 0:
+                vals = reader.readCollection("vals", MessageCollectionItemType.MSG);
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
+        }
+
+        return reader.afterMessageRead(GridH2RowMessage.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte directType() {
+        return -32;
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte fieldsCount() {
+        return 1;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void onAckReceived() {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(GridH2RowMessage.class, this);
+    }
+}


[08/50] [abbrv] ignite git commit: ignite-1232 Distributed SQL joins implementation

Posted by vo...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/IgniteCacheQueryNodeRestartDistributedJoinSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/IgniteCacheQueryNodeRestartDistributedJoinSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/IgniteCacheQueryNodeRestartDistributedJoinSelfTest.java
new file mode 100644
index 0000000..0e6806f
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/IgniteCacheQueryNodeRestartDistributedJoinSelfTest.java
@@ -0,0 +1,476 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.processors.cache.distributed.near;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicIntegerArray;
+import javax.cache.CacheException;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.cache.query.annotations.QuerySqlField;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.util.GridRandom;
+import org.apache.ignite.internal.util.typedef.CAX;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.X;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+import static org.apache.ignite.cache.CacheRebalanceMode.SYNC;
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+
+/**
+ * Test for distributed queries with node restarts.
+ */
+public class IgniteCacheQueryNodeRestartDistributedJoinSelfTest extends GridCommonAbstractTest {
+    /** */
+    private static final String QRY_0 = "select co._key, count(*) cnt\n" +
+        "from \"pe\".Person pe, \"pr\".Product pr, \"co\".Company co, \"pu\".Purchase pu\n" +
+        "where pe._key = pu.personId and pu.productId = pr._key and pr.companyId = co._key \n" +
+        "group by co._key order by cnt desc, co._key";
+
+    /** */
+    private static final String QRY_0_BROADCAST = "select co._key, count(*) cnt\n" +
+        "from \"co\".Company co, \"pr\".Product pr, \"pu\".Purchase pu, \"pe\".Person pe \n" +
+        "where pe._key = pu.personId and pu.productId = pr._key and pr.companyId = co._key \n" +
+        "group by co._key order by cnt desc, co._key";
+
+    /** */
+    private static final String QRY_1 = "select pr._key, co._key\n" +
+        "from \"pr\".Product pr, \"co\".Company co\n" +
+        "where pr.companyId = co._key\n" +
+        "order by co._key, pr._key ";
+
+    /** */
+    private static final String QRY_1_BROADCAST = "select pr._key, co._key\n" +
+        "from \"co\".Company co, \"pr\".Product pr \n" +
+        "where pr.companyId = co._key\n" +
+        "order by co._key, pr._key ";
+
+    /** */
+    private static final int GRID_CNT = 6;
+
+    /** */
+    private static final int PERS_CNT = 600;
+
+    /** */
+    private static final int PURCHASE_CNT = 6000;
+
+    /** */
+    private static final int COMPANY_CNT = 25;
+
+    /** */
+    private static final int PRODUCT_CNT = 100;
+
+    /** */
+    private static TcpDiscoveryIpFinder ipFinder = new TcpDiscoveryVmIpFinder(true);
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+        IgniteConfiguration c = super.getConfiguration(gridName);
+
+        TcpDiscoverySpi disco = new TcpDiscoverySpi();
+
+        disco.setIpFinder(ipFinder);
+
+        c.setDiscoverySpi(disco);
+
+        int i = 0;
+
+        CacheConfiguration<?, ?>[] ccs = new CacheConfiguration[4];
+
+        for (String name : F.asList("pe", "pu", "co", "pr")) {
+            CacheConfiguration<?, ?> cc = defaultCacheConfiguration();
+
+            cc.setName(name);
+            cc.setCacheMode(PARTITIONED);
+            cc.setBackups(2);
+            cc.setWriteSynchronizationMode(FULL_SYNC);
+            cc.setAtomicityMode(TRANSACTIONAL);
+            cc.setRebalanceMode(SYNC);
+            cc.setLongQueryWarningTimeout(15_000);
+            cc.setAffinity(new RendezvousAffinityFunction(false, 60));
+
+            switch (name) {
+                case "pe":
+                    cc.setIndexedTypes(
+                        Integer.class, Person.class
+                    );
+
+                    break;
+
+                case "pu":
+                    cc.setIndexedTypes(
+                        Integer.class, Purchase.class
+                    );
+
+                    break;
+
+                case "co":
+                    cc.setIndexedTypes(
+                        Integer.class, Company.class
+                    );
+
+                    break;
+
+                case "pr":
+                    cc.setIndexedTypes(
+                        Integer.class, Product.class
+                    );
+
+                    break;
+            }
+
+            ccs[i++] = cc;
+        }
+
+        c.setCacheConfiguration(ccs);
+
+        return c;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        super.beforeTestsStarted();
+
+        startGridsMultiThreaded(GRID_CNT);
+
+        fillCaches();
+    }
+
+    /**
+     *
+     */
+    private void fillCaches() {
+        IgniteCache<Integer, Company> co = grid(0).cache("co");
+
+        for (int i = 0; i < COMPANY_CNT; i++)
+            co.put(i, new Company(i));
+
+        IgniteCache<Integer, Product> pr = grid(0).cache("pr");
+
+        Random rnd = new GridRandom();
+
+        for (int i = 0; i < PRODUCT_CNT; i++)
+            pr.put(i, new Product(i, rnd.nextInt(COMPANY_CNT)));
+
+        IgniteCache<Integer, Person> pe = grid(0).cache("pe");
+
+        for (int i = 0; i < PERS_CNT; i++)
+            pe.put(i, new Person(i));
+
+        IgniteCache<Integer, Purchase> pu = grid(0).cache("pu");
+
+        for (int i = 0; i < PURCHASE_CNT; i++) {
+            int persId = rnd.nextInt(PERS_CNT);
+            int prodId = rnd.nextInt(PRODUCT_CNT);
+
+            pu.put(i, new Purchase(persId, prodId));
+        }
+    }
+    /**
+     * @throws Exception If failed.
+     */
+    public void testRestarts() throws Exception {
+        restarts(false);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testRestartsBroadcast() throws Exception {
+        restarts(true);
+    }
+
+    /**
+     * @param broadcastQry If {@code true} tests broadcast query.
+     * @throws Exception If failed.
+     */
+    private void restarts(final boolean broadcastQry) throws Exception {
+        int duration = 90 * 1000;
+        int qryThreadNum = 4;
+        int restartThreadsNum = 2; // 4 + 2 = 6 nodes
+        final int nodeLifeTime = 4000;
+        final int logFreq = 100;
+
+        final AtomicIntegerArray locks = new AtomicIntegerArray(GRID_CNT);
+
+        SqlFieldsQuery qry0 ;
+
+        if (broadcastQry)
+            qry0 = new SqlFieldsQuery(QRY_0_BROADCAST).setDistributedJoins(true).setEnforceJoinOrder(true);
+        else
+            qry0 = new SqlFieldsQuery(QRY_0).setDistributedJoins(true);
+
+        String plan = queryPlan(grid(0).cache("pu"), qry0);
+
+        X.println("Plan1: " + plan);
+
+        assertEquals(broadcastQry, plan.contains("batched:broadcast"));
+
+        final List<List<?>> pRes = grid(0).cache("pu").query(qry0).getAll();
+
+        Thread.sleep(3000);
+
+        assertEquals(pRes, grid(0).cache("pu").query(qry0).getAll());
+
+        final SqlFieldsQuery qry1;
+
+        if (broadcastQry)
+            qry1 = new SqlFieldsQuery(QRY_1_BROADCAST).setDistributedJoins(true).setEnforceJoinOrder(true);
+        else
+            qry1 = new SqlFieldsQuery(QRY_1).setDistributedJoins(true);
+
+        plan = queryPlan(grid(0).cache("co"), qry1);
+
+        X.println("Plan2: " + plan);
+
+        assertEquals(broadcastQry, plan.contains("batched:broadcast"));
+
+        final List<List<?>> rRes = grid(0).cache("co").query(qry1).getAll();
+
+        assertFalse(pRes.isEmpty());
+        assertFalse(rRes.isEmpty());
+
+        final AtomicInteger qryCnt = new AtomicInteger();
+        final AtomicBoolean qrysDone = new AtomicBoolean();
+
+        IgniteInternalFuture<?> fut1 = multithreadedAsync(new CAX() {
+            @Override public void applyx() throws IgniteCheckedException {
+                GridRandom rnd = new GridRandom();
+
+                while (!qrysDone.get()) {
+                    int g;
+
+                    do {
+                        g = rnd.nextInt(locks.length());
+                    }
+                    while (!locks.compareAndSet(g, 0, 1));
+
+                    if (rnd.nextBoolean()) {
+                        IgniteCache<?, ?> cache = grid(g).cache("pu");
+
+                        SqlFieldsQuery qry;
+
+                        if (broadcastQry)
+                            qry = new SqlFieldsQuery(QRY_0_BROADCAST).setDistributedJoins(true).setEnforceJoinOrder(true);
+                        else
+                            qry = new SqlFieldsQuery(QRY_0).setDistributedJoins(true);
+
+                        boolean smallPageSize = rnd.nextBoolean();
+
+                        qry.setPageSize(smallPageSize ? 30 : 1000);
+
+                        try {
+                            assertEquals(pRes, cache.query(qry).getAll());
+                        }
+                        catch (CacheException e) {
+                            assertTrue("On large page size must retry.", smallPageSize);
+
+                            boolean failedOnRemoteFetch = false;
+
+                            for (Throwable th = e; th != null; th = th.getCause()) {
+                                if (!(th instanceof CacheException))
+                                    continue;
+
+                                if (th.getMessage() != null &&
+                                    th.getMessage().startsWith("Failed to fetch data from node:")) {
+                                    failedOnRemoteFetch = true;
+
+                                    break;
+                                }
+                            }
+
+                            if (!failedOnRemoteFetch) {
+                                e.printStackTrace();
+
+                                fail("Must fail inside of GridResultPage.fetchNextPage or subclass.");
+                            }
+                        }
+                    }
+                    else {
+                        IgniteCache<?, ?> cache = grid(g).cache("co");
+
+                        SqlFieldsQuery qry;
+
+                        if (broadcastQry)
+                            qry = new SqlFieldsQuery(QRY_1_BROADCAST).setDistributedJoins(true).setEnforceJoinOrder(true);
+                        else
+                            qry = new SqlFieldsQuery(QRY_1).setDistributedJoins(true);
+
+                        assertEquals(rRes, cache.query(qry1).getAll());
+                    }
+
+                    locks.set(g, 0);
+
+                    int c = qryCnt.incrementAndGet();
+
+                    if (c % logFreq == 0)
+                        info("Executed queries: " + c);
+                }
+            }
+        }, qryThreadNum, "query-thread");
+
+        final AtomicInteger restartCnt = new AtomicInteger();
+
+        final AtomicBoolean restartsDone = new AtomicBoolean();
+
+        IgniteInternalFuture<?> fut2 = multithreadedAsync(new Callable<Object>() {
+            @SuppressWarnings({"BusyWait"})
+            @Override public Object call() throws Exception {
+                GridRandom rnd = new GridRandom();
+
+                while (!restartsDone.get()) {
+                    int g;
+
+                    do {
+                        g = rnd.nextInt(locks.length());
+                    }
+                    while (!locks.compareAndSet(g, 0, -1));
+
+                    log.info("Stop node: " + g);
+
+                    stopGrid(g);
+
+                    Thread.sleep(rnd.nextInt(nodeLifeTime));
+
+                    log.info("Start node: " + g);
+
+                    startGrid(g);
+
+                    Thread.sleep(rnd.nextInt(nodeLifeTime));
+
+                    locks.set(g, 0);
+
+                    int c = restartCnt.incrementAndGet();
+
+                    if (c % logFreq == 0)
+                        info("Node restarts: " + c);
+                }
+
+                return true;
+            }
+        }, restartThreadsNum, "restart-thread");
+
+        Thread.sleep(duration);
+
+        info("Stopping..");
+
+        restartsDone.set(true);
+        qrysDone.set(true);
+
+        fut2.get();
+        fut1.get();
+
+        info("Stopped.");
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTestsStopped() throws Exception {
+        stopAllGrids();
+    }
+
+    /**
+     *
+     */
+    private static class Person implements Serializable {
+        /** */
+        @QuerySqlField(index = true)
+        int id;
+
+        /**
+         * @param id ID.
+         */
+        Person(int id) {
+            this.id = id;
+        }
+    }
+
+    /**
+     *
+     */
+    private static class Purchase implements Serializable {
+        /** */
+        @QuerySqlField(index = true)
+        int personId;
+
+        /** */
+        @QuerySqlField(index = true)
+        int productId;
+
+        /**
+         * @param personId Person ID.
+         * @param productId Product ID.
+         */
+        Purchase(int personId, int productId) {
+            this.personId = personId;
+            this.productId = productId;
+        }
+    }
+
+    /**
+     *
+     */
+    private static class Company implements Serializable {
+        /** */
+        @QuerySqlField(index = true)
+        int id;
+
+        /**
+         * @param id ID.
+         */
+        Company(int id) {
+            this.id = id;
+        }
+    }
+
+    /**
+     *
+     */
+    private static class Product implements Serializable {
+        /** */
+        @QuerySqlField(index = true)
+        int id;
+
+        /** */
+        @QuerySqlField(index = true)
+        int companyId;
+
+        /**
+         * @param id ID.
+         * @param companyId Company ID.
+         */
+        Product(int id, int companyId) {
+            this.id = id;
+            this.companyId = companyId;
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/reducefields/GridCacheAbstractReduceFieldsQuerySelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/reducefields/GridCacheAbstractReduceFieldsQuerySelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/reducefields/GridCacheAbstractReduceFieldsQuerySelfTest.java
deleted file mode 100644
index 82456fb..0000000
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/reducefields/GridCacheAbstractReduceFieldsQuerySelfTest.java
+++ /dev/null
@@ -1,420 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.ignite.internal.processors.cache.reducefields;
-
-import java.io.Serializable;
-import java.util.Collection;
-import java.util.List;
-import org.apache.ignite.IgniteCache;
-import org.apache.ignite.cache.CacheAtomicityMode;
-import org.apache.ignite.cache.CacheMode;
-import org.apache.ignite.cache.affinity.AffinityKey;
-import org.apache.ignite.cache.query.annotations.QuerySqlField;
-import org.apache.ignite.configuration.CacheConfiguration;
-import org.apache.ignite.configuration.IgniteConfiguration;
-import org.apache.ignite.configuration.NearCacheConfiguration;
-import org.apache.ignite.internal.IgniteKernal;
-import org.apache.ignite.internal.processors.cache.query.CacheQuery;
-import org.apache.ignite.internal.util.typedef.F;
-import org.apache.ignite.lang.IgniteBiTuple;
-import org.apache.ignite.lang.IgniteReducer;
-import org.apache.ignite.spi.discovery.DiscoverySpi;
-import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
-import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
-import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
-import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
-import org.jetbrains.annotations.Nullable;
-
-import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
-import static org.apache.ignite.cache.CacheMode.PARTITIONED;
-import static org.apache.ignite.cache.CacheMode.REPLICATED;
-import static org.apache.ignite.cache.CacheRebalanceMode.SYNC;
-import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
-
-/**
- * Tests for reduce fields queries.
- */
-public abstract class GridCacheAbstractReduceFieldsQuerySelfTest extends GridCommonAbstractTest {
-    /** IP finder. */
-    private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
-
-    /** Flag indicating if starting node should have cache. */
-    protected boolean hasCache;
-
-    /** {@inheritDoc} */
-    @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
-        IgniteConfiguration cfg = super.getConfiguration(gridName);
-
-        if (hasCache)
-            cfg.setCacheConfiguration(cache(null));
-        else
-            cfg.setCacheConfiguration();
-
-        cfg.setDiscoverySpi(discovery());
-
-        return cfg;
-    }
-
-    /**
-     * @return Distribution.
-     */
-    protected NearCacheConfiguration nearConfiguration() {
-        return new NearCacheConfiguration();
-    }
-
-    /**
-     * @param name Cache name.
-     * @return Cache.
-     */
-    private CacheConfiguration cache(@Nullable String name) {
-        CacheConfiguration<?,?> cache = defaultCacheConfiguration();
-
-        cache.setName(name);
-        cache.setCacheMode(cacheMode());
-        cache.setAtomicityMode(atomicityMode());
-        cache.setNearConfiguration(nearConfiguration());
-        cache.setWriteSynchronizationMode(FULL_SYNC);
-        cache.setRebalanceMode(SYNC);
-        cache.setIndexedTypes(
-            String.class, Organization.class,
-            AffinityKey.class, Person.class
-        );
-
-        if (cacheMode() == PARTITIONED)
-            cache.setBackups(1);
-
-        return cache;
-    }
-
-    /**
-     * @return Discovery SPI.
-     */
-    private static DiscoverySpi discovery() {
-        TcpDiscoverySpi spi = new TcpDiscoverySpi();
-
-        spi.setIpFinder(IP_FINDER);
-
-        return spi;
-    }
-
-    /** {@inheritDoc} */
-    @Override protected void beforeTestsStarted() throws Exception {
-        hasCache = true;
-
-        startGridsMultiThreaded(gridCount());
-
-        hasCache = false;
-
-        startGrid(gridCount());
-
-        IgniteCache<String, Organization> orgCache = grid(0).cache(null);
-
-        assert orgCache != null;
-
-        orgCache.put("o1", new Organization(1, "A"));
-        orgCache.put("o2", new Organization(2, "B"));
-
-        IgniteCache<AffinityKey<String>, Person> personCache = grid(0).cache(null);
-
-        assert personCache != null;
-
-        personCache.put(new AffinityKey<>("p1", "o1"), new Person("John White", 25, 1));
-        personCache.put(new AffinityKey<>("p2", "o1"), new Person("Joe Black", 35, 1));
-        personCache.put(new AffinityKey<>("p3", "o2"), new Person("Mike Green", 40, 2));
-    }
-
-    /** {@inheritDoc} */
-    @Override protected void afterTestsStopped() throws Exception {
-        stopAllGrids();
-    }
-
-    /**
-     * @return cache mode.
-     */
-    protected abstract CacheMode cacheMode();
-
-    /**
-     * @return Number of grids to start.
-     */
-    protected abstract int gridCount();
-
-    /**
-     * @return Cache atomicity mode.
-     */
-    protected CacheAtomicityMode atomicityMode() {
-        return TRANSACTIONAL;
-    }
-
-    /**
-     * @throws Exception If failed.
-     */
-    public void testNoDataInCache() throws Exception {
-        CacheQuery<List<?>> qry = ((IgniteKernal)grid(0))
-            .getCache(null).context().queries().createSqlFieldsQuery("select age from Person where orgId = 999", false);
-
-        Collection<IgniteBiTuple<Integer, Integer>> res = qry.execute(new AverageRemoteReducer()).get();
-
-        assertEquals("Result", 0, F.reduce(res, new AverageLocalReducer()).intValue());
-    }
-
-    /**
-     * @throws Exception If failed.
-     */
-    public void testAverageQuery() throws Exception {
-        CacheQuery<List<?>> qry = ((IgniteKernal)grid(0)).getCache(null).context().queries().
-            createSqlFieldsQuery("select age from Person", false);
-
-        Collection<IgniteBiTuple<Integer, Integer>> res = qry.execute(new AverageRemoteReducer()).get();
-
-        assertEquals("Average", 33, F.reduce(res, new AverageLocalReducer()).intValue());
-    }
-
-    /**
-     * @throws Exception If failed.
-     */
-    public void testAverageQueryWithArguments() throws Exception {
-        CacheQuery<List<?>> qry = ((IgniteKernal)grid(0)).getCache(null).context().queries().createSqlFieldsQuery(
-            "select age from Person where orgId = ?", false);
-
-        Collection<IgniteBiTuple<Integer, Integer>> res = qry.execute(new AverageRemoteReducer(), 1).get();
-
-        assertEquals("Average", 30, F.reduce(res, new AverageLocalReducer()).intValue());
-    }
-
-//    /**
-//     * @throws Exception If failed.
-//     */
-//    public void testFilters() throws Exception {
-//        GridCacheReduceFieldsQuery<Object, Object, GridBiTuple<Integer, Integer>, Integer> qry = ((IgniteKernal)grid(0)).cache(null)
-//            .queries().createReduceFieldsQuery("select age from Person");
-//
-//        qry = qry.remoteKeyFilter(
-//            new GridPredicate<Object>() {
-//                @Override public boolean apply(Object e) {
-//                    return !"p2".equals(((AffinityKey)e).key());
-//                }
-//            }
-//        ).remoteValueFilter(
-//            new P1<Object>() {
-//                @Override public boolean apply(Object e) {
-//                    return !"Mike Green".equals(((Person)e).name);
-//                }
-//            }
-//        );
-//
-//        qry = qry.remoteReducer(new AverageRemoteReducer()).localReducer(new AverageLocalReducer());
-//
-//        Integer avg = qry.reduce().get();
-//
-//        assertNotNull("Average", avg);
-//        assertEquals("Average", 25, avg.intValue());
-//    }
-
-//    /**
-//     * @throws Exception If failed.
-//     */
-//    public void testOnProjectionWithFilter() throws Exception {
-//        P2<AffinityKey<String>, Person> p = new P2<AffinityKey<String>, Person>() {
-//            @Override public boolean apply(AffinityKey<String> key, Person val) {
-//                return val.orgId == 1;
-//            }
-//        };
-//
-//        InternalCache<AffinityKey<String>, Person> cachePrj =
-//            grid(0).<AffinityKey<String>, Person>cache(null).projection(p);
-//
-//        GridCacheReduceFieldsQuery<AffinityKey<String>, Person, GridBiTuple<Integer, Integer>, Integer> qry =
-//            cachePrj.queries().createReduceFieldsQuery("select age from Person");
-//
-//        qry = qry.remoteValueFilter(
-//            new P1<Person>() {
-//                @Override public boolean apply(Person e) {
-//                    return !"Joe Black".equals(e.name);
-//                }
-//            });
-//
-//        qry = qry.remoteReducer(new AverageRemoteReducer()).localReducer(new AverageLocalReducer());
-//
-//        Integer avg = qry.reduce().get();
-//
-//        assertNotNull("Average", avg);
-//        assertEquals("Average", 25, avg.intValue());
-//    }
-
-    /**
-     * @return true if cache mode is replicated, false otherwise.
-     */
-    private boolean isReplicatedMode() {
-        return cacheMode() == REPLICATED;
-    }
-
-    /**
-     * Person.
-     */
-    @SuppressWarnings("UnusedDeclaration")
-    private static class Person implements Serializable {
-        /** Name. */
-        @QuerySqlField(index = false)
-        private final String name;
-
-        /** Age. */
-        @QuerySqlField(index = true)
-        private final int age;
-
-        /** Organization ID. */
-        @QuerySqlField(index = true)
-        private final int orgId;
-
-        /**
-         * @param name Name.
-         * @param age Age.
-         * @param orgId Organization ID.
-         */
-        private Person(String name, int age, int orgId) {
-            assert !F.isEmpty(name);
-            assert age > 0;
-            assert orgId > 0;
-
-            this.name = name;
-            this.age = age;
-            this.orgId = orgId;
-        }
-
-        /** {@inheritDoc} */
-        @Override public boolean equals(Object o) {
-            if (this == o)
-                return true;
-
-            if (o == null || getClass() != o.getClass())
-                return false;
-
-            Person person = (Person)o;
-
-            return age == person.age && orgId == person.orgId && name.equals(person.name);
-
-        }
-
-        /** {@inheritDoc} */
-        @Override public int hashCode() {
-            int res = name.hashCode();
-
-            res = 31 * res + age;
-            res = 31 * res + orgId;
-
-            return res;
-        }
-    }
-
-    /**
-     * Organization.
-     */
-    @SuppressWarnings("UnusedDeclaration")
-    private static class Organization implements Serializable {
-        /** ID. */
-        @QuerySqlField
-        private final int id;
-
-        /** Name. */
-        @QuerySqlField(index = false)
-        private final String name;
-
-        /**
-         * @param id ID.
-         * @param name Name.
-         */
-        private Organization(int id, String name) {
-            assert id > 0;
-            assert !F.isEmpty(name);
-
-            this.id = id;
-            this.name = name;
-        }
-
-        /** {@inheritDoc} */
-        @Override public boolean equals(Object o) {
-            if (this == o)
-                return true;
-
-            if (o == null || getClass() != o.getClass())
-                return false;
-
-            Organization that = (Organization)o;
-
-            return id == that.id && name.equals(that.name);
-
-        }
-
-        /** {@inheritDoc} */
-        @Override public int hashCode() {
-            int res = id;
-
-            res = 31 * res + name.hashCode();
-
-            return res;
-        }
-    }
-
-    /**
-     * Average remote reducer factory.
-     */
-    protected static class AverageRemoteReducer implements IgniteReducer<List<?>, IgniteBiTuple<Integer, Integer>> {
-        /** */
-        private int sum;
-
-        /** */
-        private int cnt;
-
-        /** {@inheritDoc} */
-        @Override public boolean collect(List<?> e) {
-            sum += (Integer)e.get(0);
-
-            cnt++;
-
-            return true;
-        }
-
-        /** {@inheritDoc} */
-        @Override public IgniteBiTuple<Integer, Integer> reduce() {
-            return F.t(sum, cnt);
-        }
-    }
-
-    /**
-     * Average local reducer factory.
-     */
-    protected static class AverageLocalReducer implements IgniteReducer<IgniteBiTuple<Integer, Integer>, Integer> {
-        /** */
-        private int sum;
-
-        /** */
-        private int cnt;
-
-        /** {@inheritDoc} */
-        @Override public boolean collect(IgniteBiTuple<Integer, Integer> t) {
-            sum += t.get1();
-            cnt += t.get2();
-
-            return true;
-        }
-
-        /** {@inheritDoc} */
-        @Override public Integer reduce() {
-            return cnt == 0 ? 0 : sum / cnt;
-        }
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/reducefields/GridCacheReduceFieldsQueryAtomicSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/reducefields/GridCacheReduceFieldsQueryAtomicSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/reducefields/GridCacheReduceFieldsQueryAtomicSelfTest.java
deleted file mode 100644
index 6aa467b..0000000
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/reducefields/GridCacheReduceFieldsQueryAtomicSelfTest.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.ignite.internal.processors.cache.reducefields;
-
-import org.apache.ignite.cache.CacheAtomicityMode;
-import org.apache.ignite.configuration.NearCacheConfiguration;
-
-import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC;
-
-/**
- * Reduce fields queries tests for partitioned cache.
- */
-public class GridCacheReduceFieldsQueryAtomicSelfTest extends GridCacheReduceFieldsQueryPartitionedSelfTest {
-    /** {@inheritDoc} */
-    @Override protected CacheAtomicityMode atomicityMode() {
-        return ATOMIC;
-    }
-
-    /** {@inheritDoc} */
-    @Override protected NearCacheConfiguration nearConfiguration() {
-        return null;
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/reducefields/GridCacheReduceFieldsQueryLocalSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/reducefields/GridCacheReduceFieldsQueryLocalSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/reducefields/GridCacheReduceFieldsQueryLocalSelfTest.java
deleted file mode 100644
index 17f024e..0000000
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/reducefields/GridCacheReduceFieldsQueryLocalSelfTest.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.ignite.internal.processors.cache.reducefields;
-
-import org.apache.ignite.cache.CacheMode;
-
-import static org.apache.ignite.cache.CacheMode.LOCAL;
-
-/**
- * Reduce fields queries tests for local cache.
- */
-public class GridCacheReduceFieldsQueryLocalSelfTest extends GridCacheAbstractReduceFieldsQuerySelfTest {
-    /** {@inheritDoc} */
-    @Override protected CacheMode cacheMode() {
-        return LOCAL;
-    }
-
-    /** {@inheritDoc} */
-    @Override protected int gridCount() {
-        return 1;
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/reducefields/GridCacheReduceFieldsQueryPartitionedSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/reducefields/GridCacheReduceFieldsQueryPartitionedSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/reducefields/GridCacheReduceFieldsQueryPartitionedSelfTest.java
deleted file mode 100644
index f5b16f5..0000000
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/reducefields/GridCacheReduceFieldsQueryPartitionedSelfTest.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.ignite.internal.processors.cache.reducefields;
-
-import java.util.List;
-import org.apache.ignite.cache.CacheMode;
-import org.apache.ignite.internal.IgniteKernal;
-import org.apache.ignite.internal.processors.cache.query.CacheQuery;
-import org.apache.ignite.lang.IgniteBiTuple;
-
-import static org.apache.ignite.cache.CacheMode.PARTITIONED;
-
-/**
- * Reduce fields queries tests for partitioned cache.
- */
-public class GridCacheReduceFieldsQueryPartitionedSelfTest extends GridCacheAbstractReduceFieldsQuerySelfTest {
-    /** {@inheritDoc} */
-    @Override protected CacheMode cacheMode() {
-        return PARTITIONED;
-    }
-
-    /** {@inheritDoc} */
-    @Override protected int gridCount() {
-        return 3;
-    }
-
-    /**
-     * @throws Exception If failed.
-     */
-    public void testIncludeBackups() throws Exception {
-        CacheQuery<List<?>> qry = ((IgniteKernal)grid(0)).getCache(null).context().queries().
-            createSqlFieldsQuery("select age from Person", false);
-
-        qry.includeBackups(true);
-
-        int sum = 0;
-
-        for (IgniteBiTuple<Integer, Integer> tuple : qry.execute(new AverageRemoteReducer()).get())
-            sum += tuple.get1();
-
-        // One backup, so sum is two times greater
-        assertEquals("Sum", 200, sum);
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/reducefields/GridCacheReduceFieldsQueryReplicatedSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/reducefields/GridCacheReduceFieldsQueryReplicatedSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/reducefields/GridCacheReduceFieldsQueryReplicatedSelfTest.java
deleted file mode 100644
index 3e08d6d..0000000
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/reducefields/GridCacheReduceFieldsQueryReplicatedSelfTest.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.ignite.internal.processors.cache.reducefields;
-
-import org.apache.ignite.cache.CacheMode;
-
-import static org.apache.ignite.cache.CacheMode.REPLICATED;
-
-/**
- * Reduce fields queries tests for replicated cache.
- */
-public class GridCacheReduceFieldsQueryReplicatedSelfTest extends GridCacheAbstractReduceFieldsQuerySelfTest {
-    /** {@inheritDoc} */
-    @Override protected CacheMode cacheMode() {
-        return REPLICATED;
-    }
-
-    /** {@inheritDoc} */
-    @Override protected int gridCount() {
-        return 3;
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteSqlSchemaIndexingTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteSqlSchemaIndexingTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteSqlSchemaIndexingTest.java
index 5c9acb5..88f1f1e 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteSqlSchemaIndexingTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteSqlSchemaIndexingTest.java
@@ -19,6 +19,7 @@ package org.apache.ignite.internal.processors.query;
 
 import java.util.List;
 import java.util.concurrent.Callable;
+import javax.cache.CacheException;
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.IgniteException;
 import org.apache.ignite.IgniteLogger;
@@ -102,7 +103,7 @@ public class IgniteSqlSchemaIndexingTest extends GridCommonAbstractTest {
 
                 return null;
             }
-        }, IgniteException.class, "Schema for cache already registered");
+        }, IgniteException.class, "Cache already registered: ");
     }
 
     /**
@@ -185,7 +186,7 @@ public class IgniteSqlSchemaIndexingTest extends GridCommonAbstractTest {
                 cache.query(qryWrong);
                 return null;
             }
-        }, IgniteException.class, "Failed to parse query");
+        }, CacheException.class, "Failed to parse query");
 
         SqlFieldsQuery qryCorrect = new SqlFieldsQuery("select f.\"id\", f.\"name\" " +
             "from \""+schemaName+"\".\"Fact\" f");

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteSqlSplitterSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteSqlSplitterSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteSqlSplitterSelfTest.java
index fd52469..8ab70ba 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteSqlSplitterSelfTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteSqlSplitterSelfTest.java
@@ -24,10 +24,15 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Random;
+import java.util.concurrent.Callable;
 import java.util.concurrent.atomic.AtomicLong;
+import javax.cache.CacheException;
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheKeyConfiguration;
 import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.CachePeekMode;
+import org.apache.ignite.cache.affinity.AffinityKeyMapped;
 import org.apache.ignite.cache.query.SqlFieldsQuery;
 import org.apache.ignite.cache.query.annotations.QuerySqlField;
 import org.apache.ignite.configuration.CacheConfiguration;
@@ -38,7 +43,9 @@ import org.apache.ignite.internal.util.typedef.X;
 import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
 import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
 import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.GridTestUtils;
 import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.springframework.util.StringUtils;
 
 /**
  * Tests for correct distributed partitioned queries.
@@ -52,6 +59,10 @@ public class IgniteSqlSplitterSelfTest extends GridCommonAbstractTest {
     @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
         IgniteConfiguration cfg = super.getConfiguration(gridName);
 
+        CacheKeyConfiguration keyCfg = new CacheKeyConfiguration(TestKey.class.getName(), "affKey");
+
+        cfg.setCacheKeyConfiguration(keyCfg);
+
         cfg.setPeerClassLoadingEnabled(false);
 
         TcpDiscoverySpi disco = new TcpDiscoverySpi();
@@ -97,6 +108,8 @@ public class IgniteSqlSplitterSelfTest extends GridCommonAbstractTest {
             Integer.class, Integer.class));
 
         try {
+            awaitPartitionMapExchange();
+
             List<Integer> res = new ArrayList<>();
 
             Random rnd = new GridRandom();
@@ -136,6 +149,8 @@ public class IgniteSqlSplitterSelfTest extends GridCommonAbstractTest {
             Integer.class, GroupIndexTestValue.class));
 
         try {
+            awaitPartitionMapExchange();
+
             // Check group index usage.
             String qry = "select 1 from GroupIndexTestValue ";
 
@@ -207,10 +222,659 @@ public class IgniteSqlSplitterSelfTest extends GridCommonAbstractTest {
     }
 
     /**
+     * @throws Exception If failed.
+     */
+    public void testDistributedJoins() throws Exception {
+        CacheConfiguration ccfg = cacheConfig("persOrg", true,
+            Integer.class, Person2.class, Integer.class, Organization.class);
+
+        IgniteCache<Integer, Object> c = ignite(0).getOrCreateCache(ccfg);
+
+        try {
+            awaitPartitionMapExchange();
+
+            doTestDistributedJoins(c, 30, 100, 1000, false);
+            doTestDistributedJoins(c, 30, 100, 1000, true);
+
+            doTestDistributedJoins(c, 3, 10, 3, false);
+            doTestDistributedJoins(c, 3, 10, 3, true);
+
+            doTestDistributedJoins(c, 300, 2000, 5, false);
+            doTestDistributedJoins(c, 300, 2000, 5, true);
+        }
+        finally {
+            c.destroy();
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testDistributedJoinsUnion() throws Exception {
+        CacheConfiguration ccfg = cacheConfig("persOrg", true,
+            Integer.class, Person2.class, Integer.class, Organization.class);
+
+        IgniteCache<Integer, Object> c = ignite(0).getOrCreateCache(ccfg);
+
+        try {
+            c.put(1, new Organization("o1"));
+            c.put(2, new Organization("o2"));
+            c.put(3, new Person2(1, "p1"));
+            c.put(4, new Person2(2, "p2"));
+            c.put(5, new Person2(3, "p3"));
+
+            String select = "select o.name n1, p.name n2 from Person2 p, Organization o where p.orgId = o._key and o._key=1" +
+                " union select o.name n1, p.name n2 from Person2 p, Organization o where p.orgId = o._key and o._key=2";
+
+            String plan = (String)c.query(new SqlFieldsQuery("explain " + select)
+                .setDistributedJoins(true).setEnforceJoinOrder(true))
+                .getAll().get(0).get(0);
+
+            X.println("Plan : " + plan);
+
+            assertEquals(2, StringUtils.countOccurrencesOf(plan, "batched"));
+            assertEquals(2, StringUtils.countOccurrencesOf(plan, "batched:unicast"));
+
+            assertEquals(2, c.query(new SqlFieldsQuery(select).setDistributedJoins(true)
+                .setEnforceJoinOrder(false)).getAll().size());
+
+            select = "select * from (" + select + ")";
+
+            plan = (String)c.query(new SqlFieldsQuery("explain " + select)
+                .setDistributedJoins(true).setEnforceJoinOrder(true))
+                .getAll().get(0).get(0);
+
+            X.println("Plan : " + plan);
+
+            assertEquals(2, StringUtils.countOccurrencesOf(plan, "batched"));
+            assertEquals(2, StringUtils.countOccurrencesOf(plan, "batched:unicast"));
+
+            assertEquals(2, c.query(new SqlFieldsQuery(select).setDistributedJoins(true)
+                .setEnforceJoinOrder(false)).getAll().size());
+        }
+        finally {
+            c.destroy();
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testDistributedJoinsUnionPartitionedReplicated() throws Exception {
+        CacheConfiguration ccfg1 = cacheConfig("pers", true,
+            Integer.class, Person2.class);
+        CacheConfiguration ccfg2 = cacheConfig("org", false,
+            Integer.class, Organization.class);
+
+        IgniteCache<Integer, Object> c1 = ignite(0).getOrCreateCache(ccfg1);
+        IgniteCache<Integer, Object> c2 = ignite(0).getOrCreateCache(ccfg2);
+
+        try {
+            c2.put(1, new Organization("o1"));
+            c2.put(2, new Organization("o2"));
+            c1.put(3, new Person2(1, "p1"));
+            c1.put(4, new Person2(2, "p2"));
+            c1.put(5, new Person2(3, "p3"));
+
+            String select0 = "select o.name n1, p.name n2 from \"pers\".Person2 p, \"org\".Organization o where p.orgId = o._key and o._key=1" +
+                " union select o.name n1, p.name n2 from \"org\".Organization o, \"pers\".Person2 p where p.orgId = o._key and o._key=2";
+
+            String plan = (String)c1.query(new SqlFieldsQuery("explain " + select0)
+                .setDistributedJoins(true))
+                .getAll().get(0).get(0);
+
+            X.println("Plan: " + plan);
+
+            assertEquals(0, StringUtils.countOccurrencesOf(plan, "batched"));
+            assertEquals(2, c1.query(new SqlFieldsQuery(select0).setDistributedJoins(true)).getAll().size());
+
+            String select = "select * from (" + select0 + ")";
+
+            plan = (String)c1.query(new SqlFieldsQuery("explain " + select)
+                .setDistributedJoins(true))
+                .getAll().get(0).get(0);
+
+            X.println("Plan : " + plan);
+
+            assertEquals(0, StringUtils.countOccurrencesOf(plan, "batched"));
+            assertEquals(2, c1.query(new SqlFieldsQuery(select).setDistributedJoins(true)).getAll().size());
+
+            String select1 = "select o.name n1, p.name n2 from \"pers\".Person2 p, \"org\".Organization o where p.orgId = o._key and o._key=1" +
+                " union select * from (select o.name n1, p.name n2 from \"org\".Organization o, \"pers\".Person2 p where p.orgId = o._key and o._key=2)";
+
+            plan = (String)c1.query(new SqlFieldsQuery("explain " + select1)
+                .setDistributedJoins(true)).getAll().get(0).get(0);
+
+            X.println("Plan: " + plan);
+
+            assertEquals(0, StringUtils.countOccurrencesOf(plan, "batched"));
+            assertEquals(2, c1.query(new SqlFieldsQuery(select).setDistributedJoins(true)).getAll().size());
+
+            select = "select * from (" + select1 + ")";
+
+            plan = (String)c1.query(new SqlFieldsQuery("explain " + select)
+                .setDistributedJoins(true)).getAll().get(0).get(0);
+
+            X.println("Plan : " + plan);
+
+            assertEquals(0, StringUtils.countOccurrencesOf(plan, "batched"));
+            assertEquals(2, c1.query(new SqlFieldsQuery(select).setDistributedJoins(true)).getAll().size());
+        }
+        finally {
+            c1.destroy();
+            c2.destroy();
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testDistributedJoinsPlan() throws Exception {
+        List<IgniteCache<Object, Object>> caches = new ArrayList<>();
+
+        IgniteCache<Object, Object> persPart =
+            ignite(0).createCache(cacheConfig("persPart", true, Integer.class, Person2.class));
+        caches.add(persPart);
+
+        IgniteCache<Object, Object> persPartAff =
+            ignite(0).createCache(cacheConfig("persPartAff", true, TestKey.class, Person2.class));
+        caches.add(persPartAff);
+
+        IgniteCache<Object, Object> orgPart =
+            ignite(0).createCache(cacheConfig("orgPart", true, Integer.class, Organization.class));
+        caches.add(orgPart);
+
+        IgniteCache<Object, Object> orgPartAff =
+            ignite(0).createCache(cacheConfig("orgPartAff", true, TestKey.class, Organization.class));
+        caches.add(orgPartAff);
+
+        IgniteCache<Object, Object> orgRepl =
+            ignite(0).createCache(cacheConfig("orgRepl", false, Integer.class, Organization.class));
+        caches.add(orgRepl);
+
+        IgniteCache<Object, Object> orgRepl2 =
+            ignite(0).createCache(cacheConfig("orgRepl2", false, Integer.class, Organization.class));
+        caches.add(orgRepl2);
+
+        try {
+            // Join two partitioned.
+
+            checkQueryPlan(persPart,
+                true,
+                1,
+                "select p._key k1, o._key k2 " +
+                    "from \"persPart\".Person2 p, \"orgPart\".Organization o " +
+                    "where p.orgId = o._key",
+                "batched:unicast");
+
+            checkQueryPlan(persPart,
+                false,
+                1,
+                "select p._key k1, o._key k2 " +
+                    "from \"persPart\".Person2 p, \"orgPartAff\".Organization o " +
+                    "where p.orgId = o.affKey",
+                "batched:unicast");
+
+            checkQueryPlan(persPart,
+                false,
+                1,
+                "select p._key k1, o._key k2 " +
+                    "from \"persPart\".Person2 p, \"orgPart\".Organization o " +
+                    "where p.orgId = o._key",
+                "batched:unicast");
+
+            checkQueryPlan(persPart,
+                false,
+                1,
+                "select p._key k1, o._key k2 " +
+                    "from \"persPart\".Person2 p inner join \"orgPart\".Organization o " +
+                    "on p.orgId = o._key",
+                "batched:unicast");
+
+            checkQueryPlan(persPart,
+                false,
+                1,
+                "select p._key k1, o._key k2 " +
+                    "from \"persPart\".Person2 p left outer join \"orgPart\".Organization o " +
+                    "on p.orgId = o._key",
+                "batched:unicast");
+
+            checkQueryPlan(persPart,
+                true,
+                1,
+                "select p._key k1, o._key k2 " +
+                    "from \"orgPart\".Organization o, \"persPart\".Person2 p " +
+                    "where p.orgId = o._key",
+                "batched:broadcast");
+
+            checkQueryPlan(persPart,
+                true,
+                1,
+                "select p._key k1, o._key k2 " +
+                    "from \"orgPartAff\".Organization o, \"persPart\".Person2 p " +
+                    "where p.orgId = o.affKey",
+                "batched:broadcast");
+
+            // Join partitioned and replicated.
+
+            checkQueryPlan(persPart,
+                true,
+                0,
+                "select p._key k1, o._key k2 " +
+                    "from \"persPart\".Person2 p, \"orgRepl\".Organization o " +
+                    "where p.orgId = o._key");
+
+            checkQueryPlan(persPart,
+                false,
+                0,
+                "select p._key k1, o._key k2 " +
+                    "from \"persPart\".Person2 p, \"orgRepl\".Organization o " +
+                    "where p.orgId = o._key");
+
+            checkQueryPlan(persPart,
+                false,
+                0,
+                "select p._key k1, o._key k2 " +
+                    "from \"persPart\".Person2 p, (select * from \"orgRepl\".Organization) o " +
+                    "where p.orgId = o._key");
+
+            checkQueryPlan(persPart,
+                false,
+                0,
+                "select p._key k1, o._key k2 " +
+                    "from (select * from \"orgRepl\".Organization) o, \"persPart\".Person2 p " +
+                    "where p.orgId = o._key");
+
+            checkQueryPlan(persPart,
+                false,
+                0,
+                "select p._key k1, o._key k2 " +
+                    "from \"persPart\".Person2 p inner join \"orgRepl\".Organization o " +
+                    "on p.orgId = o._key");
+
+            checkQueryPlan(persPart,
+                false,
+                0,
+                "select p._key k1, o._key k2 " +
+                    "from \"persPart\".Person2 p left outer join \"orgRepl\".Organization o " +
+                    "on p.orgId = o._key");
+
+            checkQueryPlan(persPart,
+                false,
+                0,
+                "select p._key k1, o._key k2 " +
+                    "from \"orgRepl\".Organization o, \"persPart\".Person2 p " +
+                    "where p.orgId = o._key");
+
+            checkQueryPlan(persPart,
+                false,
+                0,
+                "select p._key k1, o._key k2 " +
+                    "from \"orgRepl\".Organization o inner join \"persPart\".Person2 p " +
+                    "on p.orgId = o._key");
+
+//            checkQueryPlan(persPart,
+//                true,
+//                1,
+//                "select p._key k1, o._key k2 " +
+//                    "from \"orgRepl\".Organization o left outer join \"persPart\".Person2 p " +
+//                    "on p.orgId = o._key",
+//                "batched:broadcast");
+
+            // Join on affinity keys.
+
+            checkNoBatchedJoin(persPart, "select p._key k1, o._key k2 ",
+                "\"persPart\".Person2 p",
+                "\"orgPart\".Organization o",
+                "where p._key = o._key", true);
+
+            checkNoBatchedJoin(persPart, "select p._key k1, o._key k2 ",
+                "\"persPart\".Person2 p",
+                "\"orgRepl\".Organization o",
+                "where p._key = o._key", true);
+
+            checkNoBatchedJoin(persPartAff, "select p._key k1, o._key k2 ",
+                "\"persPartAff\".Person2 p",
+                "\"orgPart\".Organization o",
+                "where p.affKey = o._key", true);
+
+            checkNoBatchedJoin(persPartAff, "select p._key k1, o._key k2 ",
+                "\"persPartAff\".Person2 p",
+                "\"orgRepl\".Organization o",
+                "where p.affKey = o._key", true);
+
+            checkNoBatchedJoin(persPart, "select p._key k1, o._key k2 ",
+                "(select * from \"persPart\".Person2) p",
+                "\"orgPart\".Organization o",
+                "where p._key = o._key", false);
+
+            checkNoBatchedJoin(persPart, "select p._key k1, o._key k2 ",
+                "\"persPart\".Person2 p",
+                "(select * from \"orgPart\".Organization) o",
+                "where p._key = o._key", false);
+
+            // Join multiple.
+
+            {
+                String sql = "select * from " +
+                    "(select o1._key k1, o2._key k2 from \"orgRepl\".Organization o1, \"orgRepl2\".Organization o2 where o1._key > o2._key) o, " +
+                    "\"persPart\".Person2 p where p.orgId = o.k1";
+
+                checkQueryPlan(persPart,
+                    false,
+                    0,
+                    sql);
+
+                checkQueryPlan(persPart,
+                    true,
+                    0,
+                    sql);
+
+                sql = "select o.k1, p1._key k2, p2._key k3 from " +
+                    "(select o1._key k1, o2._key k2 from \"orgRepl\".Organization o1, \"orgRepl2\".Organization o2 where o1._key > o2._key) o, " +
+                    "\"persPartAff\".Person2 p1, \"persPart\".Person2 p2 where p1._key=p2._key and p2.orgId = o.k1";
+
+                checkQueryPlan(persPart,
+                    false,
+                    1,
+                    sql,
+                    "persPartAff", "persPart", "batched:unicast", "orgRepl");
+
+                checkQueryFails(persPart, sql, true);
+
+                sql = "select o.k1, p._key k2 from " +
+                    "(select o1._key k1, p1._key k2 from \"orgRepl\".Organization o1, \"persPart\".Person2 p1 where o1._key = p1.orgId) o, " +
+                    "\"persPartAff\".Person2 p where p._key=o.k1";
+
+                checkQueryPlan(persPart,
+                    false,
+                    1,
+                    sql,
+                    "FROM \"persPart\"", "INNER JOIN \"orgRepl\"", "INNER JOIN \"persPartAff\"", "batched:broadcast");
+
+                checkQueryFails(persPart, sql, true);
+            }
+
+            {
+                String sql = "select p1._key k1, p2._key k2, o._key k3 " +
+                    "from \"persPartAff\".Person2 p1, \"persPart\".Person2 p2, \"orgPart\".Organization o " +
+                    "where p1.affKey=p2._key and p2.orgId = o._key";
+
+                checkQueryPlan(persPart,
+                    true,
+                    2,
+                    sql,
+                    "batched:unicast", "batched:unicast");
+
+                checkQueryPlan(persPart,
+                    false,
+                    2,
+                    sql,
+                    "batched:unicast", "batched:unicast");
+            }
+
+            {
+                String sql = "select p1._key k1, p2._key k2, o._key k3 " +
+                    "from \"persPartAff\".Person2 p1, \"persPart\".Person2 p2, \"orgPart\".Organization o " +
+                    "where p1.affKey > p2._key and p2.orgId = o._key";
+
+                checkQueryPlan(persPart,
+                    true,
+                    2,
+                    sql,
+                    "batched:broadcast", "batched:unicast");
+
+                checkQueryPlan(persPart,
+                    false,
+                    2,
+                    sql,
+                    "batched:broadcast", "batched:unicast");
+            }
+
+            {
+                // First join is collocated, second is replicated.
+
+                String sql = "select p1._key k1, p2._key k2, o._key k3 " +
+                    "from \"persPartAff\".Person2 p1, \"persPart\".Person2 p2, \"orgRepl\".Organization o " +
+                    "where p1.affKey=p2._key and p2.orgId = o._key";
+
+                checkQueryPlan(persPart,
+                    true,
+                    0,
+                    sql);
+
+                checkQueryPlan(persPart,
+                    false,
+                    0,
+                    sql);
+            }
+
+            {
+                String sql = "select p1._key k1, p2._key k2, o._key k3 " +
+                    "from \"persPartAff\".Person2 p1, \"persPart\".Person2 p2, \"orgRepl\".Organization o " +
+                    "where p1._key=p2._key and p2.orgId = o._key";
+
+                checkQueryPlan(persPart,
+                    false,
+                    1,
+                    sql,
+                    "batched:unicast");
+
+                sql = "select p1._key k1, p2._key k2, o._key k3 " +
+                    "from \"orgRepl\".Organization o, \"persPartAff\".Person2 p1, \"persPart\".Person2 p2 " +
+                    "where p1._key=p2._key and p2.orgId = o._key";
+
+                checkQueryPlan(persPart,
+                    false,
+                    1,
+                    sql,
+                    "batched:unicast");
+
+                sql = "select p1._key k1, p2._key k2, o._key k3 " +
+                    "from \"persPartAff\".Person2 p1, \"orgRepl\".Organization o, \"persPart\".Person2 p2 " +
+                    "where p1._key=p2._key and p2.orgId = o._key";
+
+                checkQueryPlan(persPart,
+                    false,
+                    1,
+                    sql,
+                    "batched:unicast");
+
+                sql = "select p1._key k1, p2._key k2, o._key k3 " +
+                    "from (select * from \"orgRepl\".Organization) o, \"persPartAff\".Person2 p1, \"persPart\".Person2 p2 " +
+                    "where p1._key=p2._key and p2.orgId = o._key";
+
+                checkQueryPlan(persPart,
+                    false,
+                    1,
+                    sql,
+                    "batched:unicast");
+            }
+        }
+        finally {
+            for (IgniteCache<Object, Object> cache : caches)
+                ignite(0).destroyCache(cache.getName());
+        }
+    }
+    /**
+     * @throws Exception If failed.
+     */
+    public void testDistributedJoinsEnforceReplicatedNotLast() throws Exception {
+        List<IgniteCache<Object, Object>> caches = new ArrayList<>();
+
+        IgniteCache<Object, Object> persPart =
+            ignite(0).createCache(cacheConfig("persPart", true, Integer.class, Person2.class));
+        caches.add(persPart);
+
+        IgniteCache<Object, Object> persPartAff =
+            ignite(0).createCache(cacheConfig("persPartAff", true, TestKey.class, Person2.class));
+        caches.add(persPartAff);
+
+        IgniteCache<Object, Object> orgRepl =
+            ignite(0).createCache(cacheConfig("orgRepl", false, Integer.class, Organization.class));
+        caches.add(orgRepl);
+
+        try {
+            checkQueryFails(persPart, "select p1._key k1, p2._key k2, o._key k3 " +
+                "from \"orgRepl\".Organization o, \"persPartAff\".Person2 p1, \"persPart\".Person2 p2 " +
+                "where p1._key=p2._key and p2.orgId = o._key", true);
+
+            checkQueryFails(persPart, "select p1._key k1, p2._key k2, o._key k3 " +
+                "from \"persPartAff\".Person2 p1, \"orgRepl\".Organization o, \"persPart\".Person2 p2 " +
+                "where p1._key=p2._key and p2.orgId = o._key", true);
+
+            checkQueryFails(persPart, "select p1._key k1, p2._key k2, o._key k3 " +
+                "from \"persPartAff\".Person2 p1, (select * from \"orgRepl\".Organization) o, \"persPart\".Person2 p2 " +
+                "where p1._key=p2._key and p2.orgId = o._key", true);
+
+            checkQueryPlan(persPart,
+                true,
+                0,
+                "select p._key k1, o._key k2 from \"orgRepl\".Organization o, \"persPart\".Person2 p");
+
+            checkQueryPlan(persPart,
+                true,
+                0,
+                "select p._key k1, o._key k2 from \"orgRepl\".Organization o, \"persPart\".Person2 p union " +
+                    "select p._key k1, o._key k2 from \"persPart\".Person2 p, \"orgRepl\".Organization o");
+        }
+        finally {
+            for (IgniteCache<Object, Object> cache : caches)
+                ignite(0).destroyCache(cache.getName());
+        }
+    }
+
+    /**
+     * @param cache Cache.
+     * @param sql SQL.
+     * @param enforceJoinOrder Enforce join order flag.
+     */
+    private void checkQueryFails(final IgniteCache<Object, Object> cache,
+        String sql,
+        boolean enforceJoinOrder) {
+        final SqlFieldsQuery qry = new SqlFieldsQuery(sql);
+
+        qry.setDistributedJoins(true);
+        qry.setEnforceJoinOrder(enforceJoinOrder);
+
+        GridTestUtils.assertThrows(log, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                cache.query(qry);
+
+                return null;
+            }
+        }, CacheException.class, null);
+    }
+
+    /**
+     * @param cache Query cache.
+     * @param select Select clause.
+     * @param cache1 Cache name1.
+     * @param cache2 Cache name2.
+     * @param where Where clause.
+     * @param testEnforceJoinOrder If {@code true} tests query with enforced join order.
+     */
+    private void checkNoBatchedJoin(IgniteCache<Object, Object> cache,
+        String select,
+        String cache1,
+        String cache2,
+        String where,
+        boolean testEnforceJoinOrder) {
+        checkQueryPlan(cache,
+            false,
+            0,
+            select +
+                "from " + cache1 + ","  + cache2 + " "+ where);
+
+        checkQueryPlan(cache,
+            false,
+            0,
+            select +
+                "from " + cache2 + ","  + cache1 + " "+ where);
+
+        if (testEnforceJoinOrder) {
+            checkQueryPlan(cache,
+                true,
+                0,
+                select +
+                    "from " + cache1 + ","  + cache2 + " "+ where);
+
+            checkQueryPlan(cache,
+                true,
+                0,
+                select +
+                    "from " + cache2 + ","  + cache1 + " "+ where);
+        }
+    }
+
+    /**
+     * @param cache Cache.
+     * @param enforceJoinOrder Enforce join order flag.
+     * @param expBatchedJoins Expected batched joins count.
+     * @param sql Query.
+     * @param expText Expected text to find in plan.
+     */
+    private void checkQueryPlan(IgniteCache<Object, Object> cache,
+        boolean enforceJoinOrder,
+        int expBatchedJoins,
+        String sql,
+        String...expText) {
+        checkQueryPlan(cache,
+            enforceJoinOrder,
+            expBatchedJoins,
+            new SqlFieldsQuery(sql),
+            expText);
+
+        checkQueryPlan(cache,
+            enforceJoinOrder,
+            expBatchedJoins,
+            new SqlFieldsQuery("select * from (" + sql + ")"),
+            expText);
+    }
+
+    /**
+     * @param cache Cache.
+     * @param enforceJoinOrder Enforce join order flag.
+     * @param expBatchedJoins Expected batched joins count.
+     * @param qry Query.
+     * @param expText Expected text to find in plan.
+     */
+    private void checkQueryPlan(IgniteCache<Object, Object> cache,
+        boolean enforceJoinOrder,
+        int expBatchedJoins,
+        SqlFieldsQuery qry,
+        String...expText) {
+        qry.setEnforceJoinOrder(enforceJoinOrder);
+        qry.setDistributedJoins(true);
+
+        String plan = queryPlan(cache, qry);
+
+        log.info("Plan: " + plan);
+
+        assertEquals("Unexpected number of batched joins in plan [plan=" + plan + ", qry=" + qry + ']',
+            expBatchedJoins,
+            StringUtils.countOccurrencesOf(plan, "batched"));
+
+        int startIdx = 0;
+
+        for (String exp : expText) {
+            int idx = plan.indexOf(exp, startIdx);
+
+            if (idx == -1) {
+                fail("Plan does not contain expected string [startIdx=" + startIdx +
+                    ", plan=" + plan +
+                    ", exp=" + exp + ']');
+            }
+
+            startIdx = idx + 1;
+        }
+    }
+
+    /**
      * Test HAVING clause.
      */
     public void testHaving() {
-        IgniteCache<Integer, Integer> c = ignite(0).getOrCreateCache(cacheConfig("ints", true,
+        IgniteCache<Integer, Integer> c = ignite(0).getOrCreateCache(cacheConfig("having", true,
             Integer.class, Integer.class));
 
         try {
@@ -256,6 +920,61 @@ public class IgniteSqlSplitterSelfTest extends GridCommonAbstractTest {
 
     /**
      * @param c Cache.
+     * @param orgs Number of organizations.
+     * @param persons Number of persons.
+     * @param pageSize Page size.
+     * @param enforceJoinOrder Enforce join order.
+     */
+    private void doTestDistributedJoins(IgniteCache<Integer, Object> c, int orgs, int persons, int pageSize,
+        boolean enforceJoinOrder) {
+        assertEquals(0, c.size(CachePeekMode.ALL));
+
+        int key = 0;
+
+        for (int i = 0; i < orgs; i++) {
+            Organization o = new Organization();
+
+            o.name = "Org" + i;
+
+            c.put(key++, o);
+        }
+
+        Random rnd = new GridRandom();
+
+        for (int i = 0; i < persons; i++) {
+            Person2 p = new Person2();
+
+            p.name = "Person" + i;
+            p.orgId = rnd.nextInt(orgs);
+
+            c.put(key++, p);
+        }
+
+        String select = "select count(*) from Organization o, Person2 p where p.orgId = o._key";
+
+        String plan = (String)c.query(new SqlFieldsQuery("explain " + select)
+            .setDistributedJoins(true).setEnforceJoinOrder(enforceJoinOrder).setPageSize(pageSize))
+            .getAll().get(0).get(0);
+
+        X.println("Plan : " + plan);
+
+        if (enforceJoinOrder)
+            assertTrue(plan, plan.contains("batched:broadcast"));
+        else
+            assertTrue(plan, plan.contains("batched:unicast"));
+
+        assertEquals(Long.valueOf(persons), c.query(new SqlFieldsQuery(select).setDistributedJoins(true)
+            .setEnforceJoinOrder(enforceJoinOrder).setPageSize(pageSize)).getAll().get(0).get(0));
+
+        c.clear();
+
+        assertEquals(0, c.size(CachePeekMode.ALL));
+        assertEquals(0L, c.query(new SqlFieldsQuery(select).setDistributedJoins(true)
+            .setEnforceJoinOrder(enforceJoinOrder).setPageSize(pageSize)).getAll().get(0).get(0));
+    }
+
+    /**
+     * @param c Cache.
      * @param qry Query.
      * @param args Arguments.
      * @return Column as list.
@@ -282,8 +1001,6 @@ public class IgniteSqlSplitterSelfTest extends GridCommonAbstractTest {
      *
      */
     public void testFunctionNpe() {
-        assert false : "https://issues.apache.org/jira/browse/IGNITE-1886";
-
         IgniteCache<Integer, User> userCache = ignite(0).createCache(
             cacheConfig("UserCache", true, Integer.class, User.class));
         IgniteCache<Integer, UserOrder> userOrderCache = ignite(0).createCache(
@@ -391,9 +1108,11 @@ public class IgniteSqlSplitterSelfTest extends GridCommonAbstractTest {
      * Test value.
      */
     private static class GroupIndexTestValue implements Serializable {
+        /** */
         @QuerySqlField(orderedGroups = @QuerySqlField.Group(name = "grpIdx", order = 0))
         private int a;
 
+        /** */
         @QuerySqlField(orderedGroups = @QuerySqlField.Group(name = "grpIdx", order = 1))
         private int b;
 
@@ -407,23 +1126,131 @@ public class IgniteSqlSplitterSelfTest extends GridCommonAbstractTest {
         }
     }
 
+    /**
+     *
+     */
+    private static class Person2 implements Serializable {
+        /** */
+        @QuerySqlField(index = true)
+        int orgId;
+
+        /** */
+        @QuerySqlField
+        String name;
+
+        /**
+         *
+         */
+        public Person2() {
+            // No-op.
+        }
+
+        /**
+         * @param orgId Organization ID.
+         * @param name Name.
+         */
+        public Person2(int orgId, String name) {
+            this.orgId = orgId;
+            this.name = name;
+        }
+    }
+
+    /**
+     *
+     */
+    private static class TestKey implements Serializable {
+        /** */
+        @QuerySqlField(index = true)
+        @AffinityKeyMapped
+        int affKey;
+
+        /** */
+        @QuerySqlField()
+        int id;
+
+        /**
+         * @param affKey Affinity key.
+         * @param id ID.
+         */
+        public TestKey(int affKey, int id) {
+            this.affKey = affKey;
+            this.id = id;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean equals(Object o) {
+            if (this == o)
+                return true;
+
+            if (o == null || getClass() != o.getClass())
+                return false;
+
+            TestKey personKey = (TestKey)o;
+
+            return id == personKey.id;
+
+        }
+
+        /** {@inheritDoc} */
+        @Override public int hashCode() {
+            return id;
+        }
+    }
+
+    /**
+     *
+     */
+    private static class Organization implements Serializable {
+        /** */
+        @QuerySqlField
+        String name;
+
+        /**
+         *
+         */
+        public Organization() {
+            // No-op.
+        }
+
+        /**
+         * @param name Organization name.
+         */
+        public Organization(String name) {
+            this.name = name;
+        }
+    }
+
+    /**
+     *
+     */
     private static class User implements Serializable {
+        /** */
         @QuerySqlField
         private int id;
     }
 
+    /**
+     *
+     */
     private static class UserOrder implements Serializable {
+        /** */
         @QuerySqlField
         private int id;
 
+        /** */
         @QuerySqlField
         private int userId;
     }
 
+    /**
+     *
+     */
     private static class OrderGood implements Serializable {
+        /** */
         @QuerySqlField
         private int orderId;
 
+        /** */
         @QuerySqlField
         private int goodId;
     }


[25/50] [abbrv] ignite git commit: ignite-3573 Support of Distributed joins queries in REST API

Posted by vo...@apache.org.
ignite-3573 Support of Distributed joins queries in REST API


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

Branch: refs/heads/ignite-3553
Commit: 855281ed33563f861921c5f93b39625db0206f72
Parents: 763f067
Author: agura <ag...@gridgain.com>
Authored: Mon Jul 25 14:31:10 2016 +0300
Committer: agura <ag...@gridgain.com>
Committed: Mon Jul 25 15:24:41 2016 +0300

----------------------------------------------------------------------
 .../JettyRestProcessorAbstractSelfTest.java     | 121 ++++++++++++++++++-
 .../handlers/query/QueryCommandHandler.java     |   4 +
 .../rest/request/RestQueryRequest.java          |  17 +++
 .../http/jetty/GridJettyRestHandler.java        |   5 +
 4 files changed, 142 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/855281ed/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAbstractSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAbstractSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAbstractSelfTest.java
index 81bffcf..637099b 100644
--- a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAbstractSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAbstractSelfTest.java
@@ -1679,6 +1679,32 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro
     /**
      * @throws Exception If failed.
      */
+    public void testDistributedJoinsQuery() throws Exception {
+        String qry = "select * from Person, \"organization\".Organization " +
+            "where \"organization\".Organization.id = Person.orgId " +
+            "and \"organization\".Organization.name = ?";
+
+        Map<String, String> params = new HashMap<>();
+        params.put("cmd", GridRestCommand.EXECUTE_SQL_QUERY.key());
+        params.put("type", "Person");
+        params.put("distributedJoins", "true");
+        params.put("pageSize", "10");
+        params.put("cacheName", "person");
+        params.put("qry", URLEncoder.encode(qry, CHARSET));
+        params.put("arg1", "o1");
+
+        String ret = content(params);
+
+        JsonNode items = jsonResponse(ret).get("items");
+
+        assertEquals(2, items.size());
+
+        assertFalse(queryCursorFound());
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
     public void testSqlFieldsQuery() throws Exception {
         String qry = "select concat(firstName, ' ', lastName) from Person";
 
@@ -1700,6 +1726,28 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro
     /**
      * @throws Exception If failed.
      */
+    public void testDistributedJoinsSqlFieldsQuery() throws Exception {
+        String qry = "select * from \"person\".Person p, \"organization\".Organization o where o.id = p.orgId";
+
+        Map<String, String> params = new HashMap<>();
+        params.put("cmd", GridRestCommand.EXECUTE_SQL_FIELDS_QUERY.key());
+        params.put("distributedJoins", "true");
+        params.put("pageSize", "10");
+        params.put("cacheName", "person");
+        params.put("qry", URLEncoder.encode(qry, CHARSET));
+
+        String ret = content(params);
+
+        JsonNode items = jsonResponse(ret).get("items");
+
+        assertEquals(4, items.size());
+
+        assertFalse(queryCursorFound());
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
     public void testSqlFieldsMetadataQuery() throws Exception {
         String qry = "select firstName, lastName from Person";
 
@@ -1823,6 +1871,20 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro
      * Init cache.
      */
     private void initCache() {
+        CacheConfiguration<Integer, Organization> orgCacheCfg = new CacheConfiguration<>("organization");
+
+        orgCacheCfg.setIndexedTypes(Integer.class, Organization.class);
+
+        IgniteCache<Integer, Organization> orgCache = ignite(0).getOrCreateCache(orgCacheCfg);
+
+        orgCache.clear();
+
+        Organization o1 = new Organization(1, "o1");
+        Organization o2 = new Organization(2, "o2");
+
+        orgCache.put(1, o1);
+        orgCache.put(2, o2);
+
         CacheConfiguration<Integer, Person> personCacheCfg = new CacheConfiguration<>("person");
 
         personCacheCfg.setIndexedTypes(Integer.class, Person.class);
@@ -1831,10 +1893,10 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro
 
         personCache.clear();
 
-        Person p1 = new Person("John", "Doe", 2000);
-        Person p2 = new Person("Jane", "Doe", 1000);
-        Person p3 = new Person("John", "Smith", 1000);
-        Person p4 = new Person("Jane", "Smith", 2000);
+        Person p1 = new Person(1, "John", "Doe", 2000);
+        Person p2 = new Person(1, "Jane", "Doe", 1000);
+        Person p3 = new Person(2, "John", "Smith", 1000);
+        Person p4 = new Person(2, "Jane", "Smith", 2000);
 
         personCache.put(p1.getId(), p1);
         personCache.put(p2.getId(), p2);
@@ -1848,6 +1910,43 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro
         assertEquals(2, personCache.query(qry).getAll().size());
     }
 
+
+    /**
+     * Organization class.
+     */
+    public static class Organization implements Serializable {
+        /** Organization ID (indexed). */
+        @QuerySqlField(index = true)
+        private Integer id;
+
+        /** First name (not-indexed). */
+        @QuerySqlField(index = true)
+        private String name;
+
+        /**
+         * @param id Id.
+         * @param name Name.
+         */
+        Organization(Integer id, String name) {
+            this.id = id;
+            this.name = name;
+        }
+
+        /**
+         * @return Id.
+         */
+        public Integer getId() {
+            return id;
+        }
+
+        /**
+         * @return Name.
+         */
+        public String getName() {
+            return name;
+        }
+    }
+
     /**
      * Person class.
      */
@@ -1859,6 +1958,10 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro
         @QuerySqlField(index = true)
         private Integer id;
 
+        /** Organization id. */
+        @QuerySqlField(index = true)
+        private Integer orgId;
+
         /** First name (not-indexed). */
         @QuerySqlField
         private String firstName;
@@ -1876,15 +1979,23 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro
          * @param lastName Last name.
          * @param salary Salary.
          */
-        Person(String firstName, String lastName, double salary) {
+        Person(Integer orgId, String firstName, String lastName, double salary) {
             id = PERSON_ID++;
 
+            this.orgId = orgId;
             this.firstName = firstName;
             this.lastName = lastName;
             this.salary = salary;
         }
 
         /**
+         * @return Organization ID.
+         */
+        public Integer getOrganizationId() {
+            return orgId;
+        }
+
+        /**
          * @return First name.
          */
         public String getFirstName() {

http://git-wip-us.apache.org/repos/asf/ignite/blob/855281ed/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/query/QueryCommandHandler.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/query/QueryCommandHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/query/QueryCommandHandler.java
index 67e146b..4317dd9 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/query/QueryCommandHandler.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/query/QueryCommandHandler.java
@@ -278,6 +278,8 @@ public class QueryCommandHandler extends GridRestCommandHandlerAdapter {
 
                         ((SqlQuery)qry).setArgs(req.arguments());
 
+                        ((SqlQuery)qry).setDistributedJoins(req.distributedJoins());
+
                         break;
 
                     case SQL_FIELDS:
@@ -285,6 +287,8 @@ public class QueryCommandHandler extends GridRestCommandHandlerAdapter {
 
                         ((SqlFieldsQuery)qry).setArgs(req.arguments());
 
+                        ((SqlFieldsQuery)qry).setDistributedJoins(req.distributedJoins());
+
                         break;
 
                     case SCAN:

http://git-wip-us.apache.org/repos/asf/ignite/blob/855281ed/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/request/RestQueryRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/request/RestQueryRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/request/RestQueryRequest.java
index a719776..7159c83 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/request/RestQueryRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/request/RestQueryRequest.java
@@ -32,6 +32,9 @@ public class RestQueryRequest extends GridRestRequest {
     /** Page size. */
     private Integer pageSize;
 
+    /** Distributed joins. */
+    private boolean distributedJoins;
+
     /** Cache name. */
     private String cacheName;
 
@@ -90,6 +93,20 @@ public class RestQueryRequest extends GridRestRequest {
     }
 
     /**
+     * @param distributedJoins New distributed joins.
+     */
+    public void distributedJoins(boolean distributedJoins) {
+        this.distributedJoins = distributedJoins;
+    }
+
+    /**
+     * @return Distributed joins.
+     */
+    public boolean distributedJoins() {
+        return distributedJoins;
+    }
+
+    /**
      * @param cacheName Cache name.
      */
     public void cacheName(String cacheName) {

http://git-wip-us.apache.org/repos/asf/ignite/blob/855281ed/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java
----------------------------------------------------------------------
diff --git a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java
index c2679df..c864a10 100644
--- a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java
+++ b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java
@@ -566,6 +566,11 @@ public class GridJettyRestHandler extends AbstractHandler {
                 if (pageSize != null)
                     restReq0.pageSize(Integer.parseInt(pageSize));
 
+                String distributedJoins = (String)params.get("distributedJoins");
+
+                if (distributedJoins != null)
+                    restReq0.distributedJoins(Boolean.parseBoolean(distributedJoins));
+
                 restReq0.cacheName((String)params.get("cacheName"));
 
                 if (cmd == EXECUTE_SQL_QUERY)


[22/50] [abbrv] ignite git commit: ignite-3563 Support distributedJoins flag in JDBC driver

Posted by vo...@apache.org.
ignite-3563 Support distributedJoins flag in JDBC driver


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

Branch: refs/heads/ignite-3553
Commit: 0e15cf1df8b161dbc44f8d3810feca3192f05f73
Parents: 58d0544
Author: agura <ag...@gridgain.com>
Authored: Mon Jul 25 12:31:24 2016 +0300
Committer: agura <ag...@gridgain.com>
Committed: Mon Jul 25 15:11:12 2016 +0300

----------------------------------------------------------------------
 .../jdbc2/JdbcDistributedJoinsQueryTest.java    | 319 +++++++++++++++++++
 .../jdbc/suite/IgniteJdbcDriverTestSuite.java   |   2 +
 .../org/apache/ignite/IgniteJdbcDriver.java     |  14 +-
 .../ignite/internal/jdbc2/JdbcConnection.java   |  12 +
 .../ignite/internal/jdbc2/JdbcQueryTask.java    |   9 +-
 .../ignite/internal/jdbc2/JdbcResultSet.java    |   4 +-
 .../ignite/internal/jdbc2/JdbcStatement.java    |   2 +-
 7 files changed, 357 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/0e15cf1d/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDistributedJoinsQueryTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDistributedJoinsQueryTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDistributedJoinsQueryTest.java
new file mode 100644
index 0000000..53bfa73
--- /dev/null
+++ b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDistributedJoinsQueryTest.java
@@ -0,0 +1,319 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.jdbc2;
+
+import java.io.Serializable;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.query.annotations.QuerySqlField;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.ConnectorConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+import static org.apache.ignite.IgniteJdbcDriver.CFG_URL_PREFIX;
+import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+
+/**
+ * Tests for complex queries with distributed joins enabled (joins, etc.).
+ */
+public class JdbcDistributedJoinsQueryTest extends GridCommonAbstractTest {
+    /** IP finder. */
+    private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
+
+    /** JDBC URL. */
+    private static final String BASE_URL = CFG_URL_PREFIX + "distributedJoins=true@modules/clients/src/test/config/jdbc-config.xml";
+
+    /** Statement. */
+    private Statement stmt;
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(gridName);
+
+        CacheConfiguration<?,?> cache = defaultCacheConfiguration();
+
+        cache.setCacheMode(PARTITIONED);
+        cache.setBackups(1);
+        cache.setWriteSynchronizationMode(FULL_SYNC);
+        cache.setAtomicityMode(TRANSACTIONAL);
+        cache.setIndexedTypes(String.class, Organization.class, String.class, Person.class);
+
+        cfg.setCacheConfiguration(cache);
+
+        TcpDiscoverySpi disco = new TcpDiscoverySpi();
+
+        disco.setIpFinder(IP_FINDER);
+
+        cfg.setDiscoverySpi(disco);
+
+        cfg.setConnectorConfiguration(new ConnectorConfiguration());
+
+        return cfg;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        startGrids(3);
+
+        IgniteCache<String, Organization> orgCache = grid(0).cache(null);
+
+        assert orgCache != null;
+
+        orgCache.put("o1", new Organization(1, "A"));
+        orgCache.put("o2", new Organization(2, "B"));
+
+        IgniteCache<String, Person> personCache = grid(0).cache(null);
+
+        assert personCache != null;
+
+        personCache.put("p1", new Person(1, "John White", 25, 1));
+        personCache.put("p2", new Person(2, "Joe Black", 35, 1));
+        personCache.put("p3", new Person(3, "Mike Green", 40, 2));
+
+        Class.forName("org.apache.ignite.IgniteJdbcDriver");
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTestsStopped() throws Exception {
+        stopAllGrids();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() throws Exception {
+        stmt = DriverManager.getConnection(BASE_URL).createStatement();
+
+        assert stmt != null;
+        assert !stmt.isClosed();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        if (stmt != null) {
+            stmt.getConnection().close();
+            stmt.close();
+
+            assert stmt.isClosed();
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoin() throws Exception {
+        ResultSet rs = stmt.executeQuery(
+            "select p.id, p.name, o.name as orgName from Person p, Organization o where p.orgId = o.id");
+
+        assert rs != null;
+
+        int cnt = 0;
+
+        while (rs.next()) {
+            int id = rs.getInt("id");
+
+            if (id == 1) {
+                assert "John White".equals(rs.getString("name"));
+                assert "A".equals(rs.getString("orgName"));
+            }
+            else if (id == 2) {
+                assert "Joe Black".equals(rs.getString("name"));
+                assert "A".equals(rs.getString("orgName"));
+            }
+            else if (id == 3) {
+                assert "Mike Green".equals(rs.getString("name"));
+                assert "B".equals(rs.getString("orgName"));
+            }
+            else
+                assert false : "Wrong ID: " + id;
+
+            cnt++;
+        }
+
+        assertEquals(3, cnt);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoinWithoutAlias() throws Exception {
+        ResultSet rs = stmt.executeQuery(
+            "select p.id, p.name, o.name from Person p, Organization o where p.orgId = o.id");
+
+        assert rs != null;
+
+        int cnt = 0;
+
+        while (rs.next()) {
+            int id = rs.getInt(1);
+
+            if (id == 1) {
+                assert "John White".equals(rs.getString("name"));
+                assert "John White".equals(rs.getString(2));
+                assert "A".equals(rs.getString(3));
+            }
+            else if (id == 2) {
+                assert "Joe Black".equals(rs.getString("name"));
+                assert "Joe Black".equals(rs.getString(2));
+                assert "A".equals(rs.getString(3));
+            }
+            else if (id == 3) {
+                assert "Mike Green".equals(rs.getString("name"));
+                assert "Mike Green".equals(rs.getString(2));
+                assert "B".equals(rs.getString(3));
+            }
+            else
+                assert false : "Wrong ID: " + id;
+
+            cnt++;
+        }
+
+        assert cnt == 3;
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testIn() throws Exception {
+        ResultSet rs = stmt.executeQuery("select name from Person where age in (25, 35)");
+
+        assert rs != null;
+
+        int cnt = 0;
+
+        while (rs.next()) {
+            assert "John White".equals(rs.getString("name")) ||
+                "Joe Black".equals(rs.getString("name"));
+
+            cnt++;
+        }
+
+        assert cnt == 2;
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testBetween() throws Exception {
+        ResultSet rs = stmt.executeQuery("select name from Person where age between 24 and 36");
+
+        assert rs != null;
+
+        int cnt = 0;
+
+        while (rs.next()) {
+            assert "John White".equals(rs.getString("name")) ||
+                "Joe Black".equals(rs.getString("name"));
+
+            cnt++;
+        }
+
+        assert cnt == 2;
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCalculatedValue() throws Exception {
+        ResultSet rs = stmt.executeQuery("select age * 2 from Person");
+
+        assert rs != null;
+
+        int cnt = 0;
+
+        while (rs.next()) {
+            assert rs.getInt(1) == 50 ||
+                rs.getInt(1) == 70 ||
+                rs.getInt(1) == 80;
+
+            cnt++;
+        }
+
+        assert cnt == 3;
+    }
+
+    /**
+     * Person.
+     */
+    @SuppressWarnings("UnusedDeclaration")
+    private static class Person implements Serializable {
+        /** ID. */
+        @QuerySqlField
+        private final int id;
+
+        /** Name. */
+        @QuerySqlField(index = false)
+        private final String name;
+
+        /** Age. */
+        @QuerySqlField
+        private final int age;
+
+        /** Organization ID. */
+        @QuerySqlField(index = true)
+        private final int orgId;
+
+        /**
+         * @param id ID.
+         * @param name Name.
+         * @param age Age.
+         * @param orgId Organization ID.
+         */
+        private Person(int id, String name, int age, int orgId) {
+            assert !F.isEmpty(name);
+            assert age > 0;
+            assert orgId > 0;
+
+            this.id = id;
+            this.name = name;
+            this.age = age;
+            this.orgId = orgId;
+        }
+    }
+
+    /**
+     * Organization.
+     */
+    @SuppressWarnings("UnusedDeclaration")
+    private static class Organization implements Serializable {
+        /** ID. */
+        @QuerySqlField
+        private final int id;
+
+        /** Name. */
+        @QuerySqlField(index = false)
+        private final String name;
+
+        /**
+         * @param id ID.
+         * @param name Name.
+         */
+        private Organization(int id, String name) {
+            this.id = id;
+            this.name = name;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/0e15cf1d/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
index 603ee81..b1053b0 100644
--- a/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.jdbc.suite;
 
 import junit.framework.TestSuite;
+import org.apache.ignite.internal.jdbc2.JdbcDistributedJoinsQueryTest;
 import org.apache.ignite.jdbc.JdbcComplexQuerySelfTest;
 import org.apache.ignite.jdbc.JdbcConnectionSelfTest;
 import org.apache.ignite.jdbc.JdbcEmptyCacheSelfTest;
@@ -56,6 +57,7 @@ public class IgniteJdbcDriverTestSuite extends TestSuite {
         suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcPreparedStatementSelfTest.class));
         suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcResultSetSelfTest.class));
         suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcComplexQuerySelfTest.class));
+        suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcDistributedJoinsQueryTest.class));
         suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcMetadataSelfTest.class));
         suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcEmptyCacheSelfTest.class));
         suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcLocalCachesSelfTest.class));

http://git-wip-us.apache.org/repos/asf/ignite/blob/0e15cf1d/modules/core/src/main/java/org/apache/ignite/IgniteJdbcDriver.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/IgniteJdbcDriver.java b/modules/core/src/main/java/org/apache/ignite/IgniteJdbcDriver.java
index 567ff9f..d432c1e4 100644
--- a/modules/core/src/main/java/org/apache/ignite/IgniteJdbcDriver.java
+++ b/modules/core/src/main/java/org/apache/ignite/IgniteJdbcDriver.java
@@ -104,6 +104,11 @@ import org.apache.ignite.logger.java.JavaLogger;
  *          can make significant performance and network optimizations.
  *          Default value is {@code false}.
  *     </li>
+ *     <li>
+ *         {@code distributedJoins} - enables support of distributed joins feature. This flag does not make sense in
+ *         combination with {@code local} and/or {@code collocated} flags with {@code true} value or in case of querying
+ *         of local cache. Default value is {@code false}.
+ *     </li>
  * </ul>
  *
  * <h2 class="header">Configuration of Ignite Java client based connection</h2>
@@ -284,6 +289,9 @@ public class IgniteJdbcDriver implements Driver {
     /** Collocated parameter name. */
     private static final String PARAM_COLLOCATED = "collocated";
 
+    /** Distributed joins parameter name. */
+    private static final String PARAM_DISTRIBUTED_JOINS = "distributedJoins";
+
     /** Hostname property name. */
     public static final String PROP_HOST = PROP_PREFIX + "host";
 
@@ -302,6 +310,9 @@ public class IgniteJdbcDriver implements Driver {
     /** Collocated property name. */
     public static final String PROP_COLLOCATED = PROP_PREFIX + PARAM_COLLOCATED;
 
+    /** Distributed joins property name. */
+    public static final String PROP_DISTRIBUTED_JOINS = PROP_PREFIX + PARAM_DISTRIBUTED_JOINS;
+
     /** Cache name property name. */
     public static final String PROP_CFG = PROP_PREFIX + "cfg";
 
@@ -366,7 +377,8 @@ public class IgniteJdbcDriver implements Driver {
             new PropertyInfo("Cache name", info.getProperty(PROP_CACHE), ""),
             new PropertyInfo("Node ID", info.getProperty(PROP_NODE_ID), ""),
             new PropertyInfo("Local", info.getProperty(PROP_LOCAL), ""),
-            new PropertyInfo("Collocated", info.getProperty(PROP_COLLOCATED), "")
+            new PropertyInfo("Collocated", info.getProperty(PROP_COLLOCATED), ""),
+            new PropertyInfo("Distributed Joins", info.getProperty(PROP_DISTRIBUTED_JOINS), "")
         );
 
         if (info.getProperty(PROP_CFG) != null)

http://git-wip-us.apache.org/repos/asf/ignite/blob/0e15cf1d/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcConnection.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcConnection.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcConnection.java
index 2d2ce5d..6f0d9c4 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcConnection.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcConnection.java
@@ -69,6 +69,7 @@ import static java.util.concurrent.TimeUnit.SECONDS;
 import static org.apache.ignite.IgniteJdbcDriver.PROP_CACHE;
 import static org.apache.ignite.IgniteJdbcDriver.PROP_CFG;
 import static org.apache.ignite.IgniteJdbcDriver.PROP_COLLOCATED;
+import static org.apache.ignite.IgniteJdbcDriver.PROP_DISTRIBUTED_JOINS;
 import static org.apache.ignite.IgniteJdbcDriver.PROP_LOCAL;
 import static org.apache.ignite.IgniteJdbcDriver.PROP_NODE_ID;
 
@@ -113,6 +114,9 @@ public class JdbcConnection implements Connection {
     /** Collocated query flag. */
     private boolean collocatedQry;
 
+    /** Distributed joins flag. */
+    private boolean distributedJoins;
+
     /** Statements. */
     final Set<JdbcStatement> statements = new HashSet<>();
 
@@ -132,6 +136,7 @@ public class JdbcConnection implements Connection {
         this.cacheName = props.getProperty(PROP_CACHE);
         this.locQry = Boolean.parseBoolean(props.getProperty(PROP_LOCAL));
         this.collocatedQry = Boolean.parseBoolean(props.getProperty(PROP_COLLOCATED));
+        this.distributedJoins = Boolean.parseBoolean(props.getProperty(PROP_DISTRIBUTED_JOINS));
 
         String nodeIdProp = props.getProperty(PROP_NODE_ID);
 
@@ -706,6 +711,13 @@ public class JdbcConnection implements Connection {
     }
 
     /**
+     * @return Distributed joins flag.
+     */
+    boolean isDistributedJoins() {
+        return distributedJoins;
+    }
+
+    /**
      * Ensures that connection is not closed.
      *
      * @throws SQLException If connection is closed.

http://git-wip-us.apache.org/repos/asf/ignite/blob/0e15cf1d/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcQueryTask.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcQueryTask.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcQueryTask.java
index 1a5793a..c4911cb 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcQueryTask.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcQueryTask.java
@@ -92,6 +92,9 @@ class JdbcQueryTask implements IgniteCallable<JdbcQueryTask.QueryResult> {
     /** Collocated query flag. */
     private final boolean collocatedQry;
 
+    /** Distributed joins flag. */
+    private final boolean distributedJoins;
+
     /**
      * @param ignite Ignite.
      * @param cacheName Cache name.
@@ -102,9 +105,11 @@ class JdbcQueryTask implements IgniteCallable<JdbcQueryTask.QueryResult> {
      * @param uuid UUID.
      * @param locQry Local query flag.
      * @param collocatedQry Collocated query flag.
+     * @param distributedJoins Distributed joins flag.
      */
     public JdbcQueryTask(Ignite ignite, String cacheName, String sql,
-        boolean loc, Object[] args, int fetchSize, UUID uuid, boolean locQry, boolean collocatedQry) {
+        boolean loc, Object[] args, int fetchSize, UUID uuid,
+        boolean locQry, boolean collocatedQry, boolean distributedJoins) {
         this.ignite = ignite;
         this.args = args;
         this.uuid = uuid;
@@ -114,6 +119,7 @@ class JdbcQueryTask implements IgniteCallable<JdbcQueryTask.QueryResult> {
         this.loc = loc;
         this.locQry = locQry;
         this.collocatedQry = collocatedQry;
+        this.distributedJoins = distributedJoins;
     }
 
     /** {@inheritDoc} */
@@ -147,6 +153,7 @@ class JdbcQueryTask implements IgniteCallable<JdbcQueryTask.QueryResult> {
             qry.setPageSize(fetchSize);
             qry.setLocal(locQry);
             qry.setCollocated(collocatedQry);
+            qry.setDistributedJoins(distributedJoins);
 
             QueryCursor<List<?>> qryCursor = cache.query(qry);
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/0e15cf1d/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java
index 69dddad..8e0e9d0 100755
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java
@@ -146,8 +146,8 @@ public class JdbcResultSet implements ResultSet {
 
             boolean loc = nodeId == null;
 
-            JdbcQueryTask qryTask = new JdbcQueryTask(loc ? ignite : null, conn.cacheName(),
-                null, loc, null, fetchSize, uuid, conn.isLocalQuery(), conn.isCollocatedQuery());
+            JdbcQueryTask qryTask = new JdbcQueryTask(loc ? ignite : null, conn.cacheName(), null, loc, null,
+                fetchSize, uuid, conn.isLocalQuery(), conn.isCollocatedQuery(), conn.isDistributedJoins());
 
             try {
                 JdbcQueryTask.QueryResult res =

http://git-wip-us.apache.org/repos/asf/ignite/blob/0e15cf1d/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcStatement.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcStatement.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcStatement.java
index 6b806f3..e187dc0 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcStatement.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcStatement.java
@@ -96,7 +96,7 @@ public class JdbcStatement implements Statement {
         boolean loc = nodeId == null;
 
         JdbcQueryTask qryTask = new JdbcQueryTask(loc ? ignite : null, conn.cacheName(),
-            sql, loc, args, fetchSize, uuid, conn.isLocalQuery(), conn.isCollocatedQuery());
+            sql, loc, args, fetchSize, uuid, conn.isLocalQuery(), conn.isCollocatedQuery(), conn.isDistributedJoins());
 
         try {
             JdbcQueryTask.QueryResult res =


[42/50] [abbrv] ignite git commit: Merge branch 'ignite-1.6.3' into ignite-1.7

Posted by vo...@apache.org.
Merge branch 'ignite-1.6.3' into ignite-1.7


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

Branch: refs/heads/ignite-3553
Commit: db99becc2071674c90720cfe0035443d85f4df95
Parents: 34d553a 4979c87
Author: vozerov-gridgain <vo...@gridgain.com>
Authored: Wed Jul 27 13:51:55 2016 +0300
Committer: vozerov-gridgain <vo...@gridgain.com>
Committed: Wed Jul 27 13:51:55 2016 +0300

----------------------------------------------------------------------
 .../visor/node/VisorNodeDataCollectorJob.java   | 25 +++++++++++++++++---
 1 file changed, 22 insertions(+), 3 deletions(-)
----------------------------------------------------------------------



[49/50] [abbrv] ignite git commit: IGNITE-3577: ODBC: Added simplified "Address" porperty. This closes #898.

Posted by vo...@apache.org.
IGNITE-3577: ODBC: Added simplified "Address" porperty. This closes #898.


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

Branch: refs/heads/ignite-3553
Commit: 8386dd867a82e9814b43d54cca588a5d7a82ef4b
Parents: 33d35b3
Author: isapego <is...@gridgain.com>
Authored: Thu Jul 28 13:42:32 2016 +0300
Committer: vozerov-gridgain <vo...@gridgain.com>
Committed: Thu Jul 28 13:42:32 2016 +0300

----------------------------------------------------------------------
 .../cpp/common/include/ignite/common/utils.h    |  18 ++
 .../cpp/core-test/config/cache-query.xml        |   3 +-
 .../cpp/odbc-test/config/queries-test.xml       |   9 +-
 .../cpp/odbc-test/src/configuration_test.cpp    | 110 ++++++---
 .../cpp/odbc-test/src/queries_test.cpp          |  91 +++++---
 .../include/ignite/odbc/config/configuration.h  | 121 ++++++++--
 .../cpp/odbc/include/ignite/odbc/connection.h   |  38 ++--
 .../cpp/odbc/src/config/configuration.cpp       | 227 +++++++++----------
 modules/platforms/cpp/odbc/src/connection.cpp   |  47 ++--
 modules/platforms/cpp/odbc/src/odbc.cpp         |  27 ++-
 10 files changed, 436 insertions(+), 255 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/8386dd86/modules/platforms/cpp/common/include/ignite/common/utils.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/common/include/ignite/common/utils.h b/modules/platforms/cpp/common/include/ignite/common/utils.h
index c1046e2..331fcb2 100644
--- a/modules/platforms/cpp/common/include/ignite/common/utils.h
+++ b/modules/platforms/cpp/common/include/ignite/common/utils.h
@@ -179,6 +179,24 @@ namespace ignite
 
             return res;
         }
+
+        /**
+         * Check if the predicate returns true for all the elements of the
+         * sequence.
+         *
+         * @return True if the predicate returns true for all the elements
+         *     of the sequence and false otherwise.
+         */
+        template<typename Iter, typename Pred>
+        bool AllOf(Iter begin, Iter end, Pred pred)
+        {
+            Iter i = begin;
+
+            while (i != end && pred(*i))
+                ++i;
+
+            return i == end;
+        }
     }
 }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/8386dd86/modules/platforms/cpp/core-test/config/cache-query.xml
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/core-test/config/cache-query.xml b/modules/platforms/cpp/core-test/config/cache-query.xml
index 06bc7f5..c3b5389 100644
--- a/modules/platforms/cpp/core-test/config/cache-query.xml
+++ b/modules/platforms/cpp/core-test/config/cache-query.xml
@@ -119,11 +119,12 @@
                         <property name="addresses">
                             <list>
                                 <!-- In distributed environment, replace with actual host IP address. -->
-                                <value>127.0.0.1:47500..47501</value>
+                                <value>127.0.0.1:47500</value>
                             </list>
                         </property>
                     </bean>
                 </property>
+                <property name="socketTimeout" value="300" />
             </bean>
         </property>
     </bean>

http://git-wip-us.apache.org/repos/asf/ignite/blob/8386dd86/modules/platforms/cpp/odbc-test/config/queries-test.xml
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc-test/config/queries-test.xml b/modules/platforms/cpp/odbc-test/config/queries-test.xml
index f08f86d..054da42 100644
--- a/modules/platforms/cpp/odbc-test/config/queries-test.xml
+++ b/modules/platforms/cpp/odbc-test/config/queries-test.xml
@@ -26,12 +26,14 @@
         http://www.springframework.org/schema/util
         http://www.springframework.org/schema/util/spring-util.xsd">
     <bean id="grid.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
-        <!-- Set to true to enable distributed class loading for examples, default is false. -->
-        <property name="peerClassLoadingEnabled" value="true"/>
+        <property name="localHost" value="127.0.0.1"/>
+        <property name="connectorConfiguration"><null/></property>
 
         <!-- Enabling ODBC. -->
         <property name="odbcConfiguration">
-            <bean class="org.apache.ignite.configuration.OdbcConfiguration"></bean>
+            <bean class="org.apache.ignite.configuration.OdbcConfiguration">
+				<property name="endpointAddress" value="127.0.0.1:11110"/>
+			</bean>
         </property>
 
         <property name="cacheConfiguration">
@@ -89,6 +91,7 @@
                         </property>
                     </bean>
                 </property>
+                <property name="socketTimeout" value="300" />
             </bean>
         </property>
     </bean>

http://git-wip-us.apache.org/repos/asf/ignite/blob/8386dd86/modules/platforms/cpp/odbc-test/src/configuration_test.cpp
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc-test/src/configuration_test.cpp b/modules/platforms/cpp/odbc-test/src/configuration_test.cpp
index 85aa3ff..10fd137 100644
--- a/modules/platforms/cpp/odbc-test/src/configuration_test.cpp
+++ b/modules/platforms/cpp/odbc-test/src/configuration_test.cpp
@@ -24,47 +24,70 @@
 #include <boost/test/unit_test.hpp>
 
 #include <ignite/odbc/config/configuration.h>
+#include <ignite/ignite_error.h>
+#include <ignite/common/utils.h>
 
 using namespace ignite::odbc::config;
 
 namespace
 {
-    const char* testDriverName = "Ignite";
-    const char* testServerHost = "testhost.com";
+    const std::string testDriverName = "Ignite Driver";
+    const std::string testServerHost = "testhost.com";
     const uint16_t testServerPort = 4242;
-    const char* testCacheName = "TestCache";
-    const char* testDsn = "Ignite DSN";
+    const std::string testCacheName = "TestCache";
+    const std::string testDsn = "Ignite DSN";
+
+    const std::string testAddress = testServerHost + ':' + ignite::common::LexicalCast<std::string>(testServerPort);
 }
 
-BOOST_AUTO_TEST_SUITE(ConfigurationTestSuite)
+void CheckValidAddress(const char* connectStr, uint16_t port)
+{
+    Configuration cfg;
+
+    BOOST_CHECK_NO_THROW(cfg.FillFromConnectString(connectStr));
+
+    BOOST_CHECK_EQUAL(cfg.GetPort(), port);
+}
 
 void CheckConnectionConfig(const Configuration& cfg)
 {
-    BOOST_REQUIRE(cfg.GetDriver() == testDriverName);
-    BOOST_REQUIRE(cfg.GetHost() == testServerHost);
-    BOOST_REQUIRE(cfg.GetPort() == testServerPort);
-    BOOST_REQUIRE(cfg.GetCache() == testCacheName);
-    BOOST_REQUIRE(cfg.GetDsn().empty());
+    BOOST_CHECK_EQUAL(cfg.GetDriver(), testDriverName);
+    BOOST_CHECK_EQUAL(cfg.GetHost(), testServerHost);
+    BOOST_CHECK_EQUAL(cfg.GetPort(), testServerPort);
+    BOOST_CHECK_EQUAL(cfg.GetAddress(), testAddress);
+    BOOST_CHECK_EQUAL(cfg.GetCache(), testCacheName);
+    BOOST_CHECK_EQUAL(cfg.GetDsn(), std::string());
 
     std::stringstream constructor;
 
-    constructor << "driver={" << testDriverName << "};"
-                << "server=" << testServerHost << ";"
-                << "port=" << testServerPort << ";"
-                << "cache=" << testCacheName << ";";
+    constructor << "address=" << testAddress << ';'
+                << "cache=" << testCacheName << ';'
+                << "driver={" << testDriverName << "};";
 
     const std::string& expectedStr = constructor.str();
 
-    BOOST_REQUIRE(cfg.ToConnectString() == expectedStr);
+    BOOST_CHECK_EQUAL(cfg.ToConnectString(), expectedStr);
 }
 
 void CheckDsnConfig(const Configuration& cfg)
 {
-    BOOST_REQUIRE(cfg.GetDriver() == testDriverName);
-    BOOST_REQUIRE(cfg.GetDsn() == testDsn);
-    BOOST_REQUIRE(cfg.GetHost().empty());
-    BOOST_REQUIRE(cfg.GetCache().empty());
-    BOOST_REQUIRE(cfg.GetPort() == 0);
+    BOOST_CHECK_EQUAL(cfg.GetDriver(), testDriverName);
+    BOOST_CHECK_EQUAL(cfg.GetDsn(), testDsn);
+    BOOST_CHECK_EQUAL(cfg.GetCache(), Configuration::DefaultValue::cache);
+    BOOST_CHECK_EQUAL(cfg.GetAddress(), Configuration::DefaultValue::address);
+    BOOST_CHECK_EQUAL(cfg.GetHost(), std::string());
+    BOOST_CHECK_EQUAL(cfg.GetPort(), Configuration::DefaultValue::uintPort);
+}
+
+BOOST_AUTO_TEST_SUITE(ConfigurationTestSuite)
+
+BOOST_AUTO_TEST_CASE(CheckTestValuesNotEquealDefault)
+{
+    BOOST_CHECK_NE(testDriverName, Configuration::DefaultValue::driver);
+    BOOST_CHECK_NE(testAddress, Configuration::DefaultValue::address);
+    BOOST_CHECK_NE(testServerPort, Configuration::DefaultValue::uintPort);
+    BOOST_CHECK_NE(testCacheName, Configuration::DefaultValue::cache);
+    BOOST_CHECK_NE(testDsn, Configuration::DefaultValue::dsn);
 }
 
 BOOST_AUTO_TEST_CASE(TestConnectStringUppercase)
@@ -74,13 +97,12 @@ BOOST_AUTO_TEST_CASE(TestConnectStringUppercase)
     std::stringstream constructor;
 
     constructor << "DRIVER={" << testDriverName << "};"
-                << "SERVER=" << testServerHost <<";"
-                << "PORT=" << testServerPort << ";"
+                << "ADDRESS=" << testAddress << ';'
                 << "CACHE=" << testCacheName;
 
     const std::string& connectStr = constructor.str();
 
-    cfg.FillFromConnectString(connectStr.c_str(), connectStr.size());
+    cfg.FillFromConnectString(connectStr);
 
     CheckConnectionConfig(cfg);
 }
@@ -92,13 +114,12 @@ BOOST_AUTO_TEST_CASE(TestConnectStringLowercase)
     std::stringstream constructor;
 
     constructor << "driver={" << testDriverName << "};"
-                << "server=" << testServerHost << ";"
-                << "port=" << testServerPort << ";"
+                << "address=" << testAddress << ';'
                 << "cache=" << testCacheName;
 
     const std::string& connectStr = constructor.str();
 
-    cfg.FillFromConnectString(connectStr.c_str(), connectStr.size());
+    cfg.FillFromConnectString(connectStr);
 
     CheckConnectionConfig(cfg);
 }
@@ -110,8 +131,7 @@ BOOST_AUTO_TEST_CASE(TestConnectStringZeroTerminated)
     std::stringstream constructor;
 
     constructor << "driver={" << testDriverName << "};"
-                << "server=" << testServerHost << ";"
-                << "port=" << testServerPort << ";"
+                << "address=" << testAddress << ';'
                 << "cache=" << testCacheName;
 
     const std::string& connectStr = constructor.str();
@@ -128,13 +148,12 @@ BOOST_AUTO_TEST_CASE(TestConnectStringMixed)
     std::stringstream constructor;
 
     constructor << "Driver={" << testDriverName << "};"
-                << "Server=" << testServerHost << ";"
-                << "Port=" << testServerPort << ";"
+                << "Address=" << testAddress << ';'
                 << "Cache=" << testCacheName;
 
     const std::string& connectStr = constructor.str();
 
-    cfg.FillFromConnectString(connectStr.c_str(), connectStr.size());
+    cfg.FillFromConnectString(connectStr);
 
     CheckConnectionConfig(cfg);
 }
@@ -146,17 +165,40 @@ BOOST_AUTO_TEST_CASE(TestConnectStringWhitepaces)
     std::stringstream constructor;
 
     constructor << "DRIVER = {" << testDriverName << "} ;\n"
-                << " SERVER =" << testServerHost << " ; \n"
-                << "PORT= " << testServerPort << "; "
+                << " ADDRESS =" << testAddress << "; "
                 << "CACHE = \n\r" << testCacheName;
 
     const std::string& connectStr = constructor.str();
 
-    cfg.FillFromConnectString(connectStr.c_str(), connectStr.size());
+    cfg.FillFromConnectString(connectStr);
 
     CheckConnectionConfig(cfg);
 }
 
+BOOST_AUTO_TEST_CASE(TestConnectStringInvalidAddress)
+{
+    Configuration cfg;
+
+    BOOST_CHECK_THROW(cfg.FillFromConnectString("Address=example.com:0;"), ignite::IgniteError);
+    BOOST_CHECK_THROW(cfg.FillFromConnectString("Address=example.com:00000;"), ignite::IgniteError);
+    BOOST_CHECK_THROW(cfg.FillFromConnectString("Address=example.com:fdsf;"), ignite::IgniteError);
+    BOOST_CHECK_THROW(cfg.FillFromConnectString("Address=example.com:123:1;"), ignite::IgniteError);
+    BOOST_CHECK_THROW(cfg.FillFromConnectString("Address=example.com:12322221;"), ignite::IgniteError);
+    BOOST_CHECK_THROW(cfg.FillFromConnectString("Address=example.com:12322a;"), ignite::IgniteError);
+    BOOST_CHECK_THROW(cfg.FillFromConnectString("Address=example.com:;"), ignite::IgniteError);
+}
+
+BOOST_AUTO_TEST_CASE(TestConnectStringValidAddress)
+{
+    Configuration cfg;
+
+    CheckValidAddress("Address=example.com:1;", 1);
+    CheckValidAddress("Address=example.com:31242;", 31242);
+    CheckValidAddress("Address=example.com:55555;", 55555);
+    CheckValidAddress("Address=example.com:110;", 110);
+    CheckValidAddress("Address=example.com;", Configuration::DefaultValue::uintPort);
+}
+
 BOOST_AUTO_TEST_CASE(TestDsnStringUppercase)
 {
     Configuration cfg;

http://git-wip-us.apache.org/repos/asf/ignite/blob/8386dd86/modules/platforms/cpp/odbc-test/src/queries_test.cpp
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc-test/src/queries_test.cpp b/modules/platforms/cpp/odbc-test/src/queries_test.cpp
index cc3fa8e..ccb3a4d 100644
--- a/modules/platforms/cpp/odbc-test/src/queries_test.cpp
+++ b/modules/platforms/cpp/odbc-test/src/queries_test.cpp
@@ -24,6 +24,7 @@
 
 #include <vector>
 #include <string>
+#include <algorithm>
 
 #ifndef _MSC_VER
 #   define BOOST_TEST_DYN_LINK
@@ -76,39 +77,12 @@ std::string GetOdbcErrorMessage(SQLSMALLINT handleType, SQLHANDLE handle)
 struct QueriesTestSuiteFixture 
 {
     /**
-     * Constructor.
+     * Establish connection to node.
+     *
+     * @param connectStr Connection string.
      */
-    QueriesTestSuiteFixture() : testCache(0), env(NULL), dbc(NULL), stmt(NULL)
+    void Connect(const std::string& connectStr)
     {
-        IgniteConfiguration cfg;
-
-        cfg.jvmOpts.push_back("-Xdebug");
-        cfg.jvmOpts.push_back("-Xnoagent");
-        cfg.jvmOpts.push_back("-Djava.compiler=NONE");
-        cfg.jvmOpts.push_back("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005");
-        cfg.jvmOpts.push_back("-XX:+HeapDumpOnOutOfMemoryError");
-
-#ifdef IGNITE_TESTS_32
-        cfg.jvmInitMem = 256;
-        cfg.jvmMaxMem = 768;
-#else
-        cfg.jvmInitMem = 1024;
-        cfg.jvmMaxMem = 4096;
-#endif
-
-        char* cfgPath = getenv("IGNITE_NATIVE_TEST_ODBC_CONFIG_PATH");
-
-        cfg.springCfgPath = std::string(cfgPath).append("/").append("queries-test.xml");
-
-        IgniteError err;
-
-        grid = Ignition::Start(cfg, &err);
-
-        if (err.GetCode() != IgniteError::IGNITE_SUCCESS)
-            BOOST_FAIL(err.GetText());
-
-        testCache = grid.GetCache<int64_t, TestType>("cache");
-
         // Allocate an environment handle
         SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
 
@@ -123,13 +97,16 @@ struct QueriesTestSuiteFixture
         BOOST_REQUIRE(dbc != NULL);
 
         // Connect string
-        SQLCHAR connectStr[] = "DRIVER={Apache Ignite};SERVER=localhost;PORT=10800;CACHE=cache";
+        std::vector<SQLCHAR> connectStr0;
+
+        connectStr0.reserve(connectStr.size() + 1);
+        std::copy(connectStr.begin(), connectStr.end(), std::back_inserter(connectStr0));
 
         SQLCHAR outstr[ODBC_BUFFER_SIZE];
         SQLSMALLINT outstrlen;
 
         // Connecting to ODBC server.
-        SQLRETURN ret = SQLDriverConnect(dbc, NULL, connectStr, static_cast<SQLSMALLINT>(sizeof(connectStr)),
+        SQLRETURN ret = SQLDriverConnect(dbc, NULL, &connectStr0[0], static_cast<SQLSMALLINT>(connectStr0.size()),
             outstr, sizeof(outstr), &outstrlen, SQL_DRIVER_COMPLETE);
 
         if (!SQL_SUCCEEDED(ret))
@@ -146,6 +123,41 @@ struct QueriesTestSuiteFixture
     }
 
     /**
+     * Constructor.
+     */
+    QueriesTestSuiteFixture() : testCache(0), env(NULL), dbc(NULL), stmt(NULL)
+    {
+        IgniteConfiguration cfg;
+
+        cfg.jvmOpts.push_back("-Xdebug");
+        cfg.jvmOpts.push_back("-Xnoagent");
+        cfg.jvmOpts.push_back("-Djava.compiler=NONE");
+        cfg.jvmOpts.push_back("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005");
+        cfg.jvmOpts.push_back("-XX:+HeapDumpOnOutOfMemoryError");
+
+#ifdef IGNITE_TESTS_32
+        cfg.jvmInitMem = 256;
+        cfg.jvmMaxMem = 768;
+#else
+        cfg.jvmInitMem = 1024;
+        cfg.jvmMaxMem = 4096;
+#endif
+
+        char* cfgPath = getenv("IGNITE_NATIVE_TEST_ODBC_CONFIG_PATH");
+
+        cfg.springCfgPath = std::string(cfgPath).append("/").append("queries-test.xml");
+
+        IgniteError err;
+
+        grid = Ignition::Start(cfg, &err);
+
+        if (err.GetCode() != IgniteError::IGNITE_SUCCESS)
+            BOOST_FAIL(err.GetText());
+
+        testCache = grid.GetCache<int64_t, TestType>("cache");
+    }
+
+    /**
      * Destructor.
      */
     ~QueriesTestSuiteFixture()
@@ -166,6 +178,8 @@ struct QueriesTestSuiteFixture
     template<typename T>
     void CheckTwoRowsInt(SQLSMALLINT type)
     {
+        Connect("DRIVER={Apache Ignite};ADDRESS=127.0.0.1:11110;CACHE=cache");
+
         SQLRETURN ret;
 
         TestType in1(1, 2, 3, 4, "5", 6.0f, 7.0, true, Guid(8, 9), BinaryUtils::MakeDateGmt(1987, 6, 5), BinaryUtils::MakeTimestampGmt(1998, 12, 27, 1, 2, 3, 456));
@@ -273,6 +287,11 @@ struct QueriesTestSuiteFixture
 
 BOOST_FIXTURE_TEST_SUITE(QueriesTestSuite, QueriesTestSuiteFixture)
 
+BOOST_AUTO_TEST_CASE(TestLegacyConnection)
+{
+    Connect("DRIVER={Apache Ignite};SERVER=127.0.0.1;PORT=11110;CACHE=cache");
+}
+
 BOOST_AUTO_TEST_CASE(TestTwoRowsInt8)
 {
     CheckTwoRowsInt<int8_t>(SQL_C_STINYINT);
@@ -315,6 +334,8 @@ BOOST_AUTO_TEST_CASE(TestTwoRowsUint64)
 
 BOOST_AUTO_TEST_CASE(TestTwoRowsString)
 {
+    Connect("DRIVER={Apache Ignite};ADDRESS=127.0.0.1:11110;CACHE=cache");
+
     SQLRETURN ret;
 
     TestType in1(1, 2, 3, 4, "5", 6.0f, 7.0, true, Guid(8, 9), BinaryUtils::MakeDateGmt(1987, 6, 5), BinaryUtils::MakeTimestampGmt(1998, 12, 27, 1, 2, 3, 456));
@@ -408,6 +429,8 @@ BOOST_AUTO_TEST_CASE(TestTwoRowsString)
 
 BOOST_AUTO_TEST_CASE(TestOneRowString)
 {
+    Connect("DRIVER={Apache Ignite};ADDRESS=127.0.0.1:11110;CACHE=cache");
+
     SQLRETURN ret;
 
     TestType in(1, 2, 3, 4, "5", 6.0f, 7.0, true, Guid(8, 9), BinaryUtils::MakeDateGmt(1987, 6, 5), BinaryUtils::MakeTimestampGmt(1998, 12, 27, 1, 2, 3, 456));
@@ -469,6 +492,8 @@ BOOST_AUTO_TEST_CASE(TestOneRowString)
 
 BOOST_AUTO_TEST_CASE(TestOneRowStringLen)
 {
+    Connect("DRIVER={Apache Ignite};ADDRESS=127.0.0.1:11110;CACHE=cache");
+
     SQLRETURN ret;
 
     TestType in(1, 2, 3, 4, "5", 6.0f, 7.0, true, Guid(8, 9), BinaryUtils::MakeDateGmt(1987, 6, 5), BinaryUtils::MakeTimestampGmt(1998, 12, 27, 1, 2, 3, 456));

http://git-wip-us.apache.org/repos/asf/ignite/blob/8386dd86/modules/platforms/cpp/odbc/include/ignite/odbc/config/configuration.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc/include/ignite/odbc/config/configuration.h b/modules/platforms/cpp/odbc/include/ignite/odbc/config/configuration.h
index d6d7944..05fe8bf 100644
--- a/modules/platforms/cpp/odbc/include/ignite/odbc/config/configuration.h
+++ b/modules/platforms/cpp/odbc/include/ignite/odbc/config/configuration.h
@@ -23,6 +23,7 @@
 #include <map>
 
 #include <ignite/common/common.h>
+#include <ignite/common/utils.h>
 
 namespace ignite
 {
@@ -36,6 +37,68 @@ namespace ignite
             class Configuration
             {
             public:
+                /** Map containing connect arguments. */
+                typedef std::map<std::string, std::string> ArgumentMap;
+
+                /** Connection attribute keywords. */
+                struct Key
+                {
+                    /** Connection attribute keyword for DSN attribute. */
+                    static const std::string dsn;
+
+                    /** Connection attribute keyword for Driver attribute. */
+                    static const std::string driver;
+
+                    /** Connection attribute keyword for cache attribute. */
+                    static const std::string cache;
+
+                    /** Connection attribute keyword for address attribute. */
+                    static const std::string address;
+
+                    /** Connection attribute keyword for server attribute. */
+                    static const std::string server;
+
+                    /** Connection attribute keyword for port attribute. */
+                    static const std::string port;
+                };
+
+                /** Default values for configuration. */
+                struct DefaultValue
+                {
+                    /** Default value for DSN attribute. */
+                    static const std::string dsn;
+
+                    /** Default value for Driver attribute. */
+                    static const std::string driver;
+
+                    /** Default value for cache attribute. */
+                    static const std::string cache;
+
+                    /** Default value for address attribute. */
+                    static const std::string address;
+
+                    /** Default value for server attribute. */
+                    static const std::string server;
+
+                    /** Default value for port attribute. */
+                    static const std::string port;
+
+                    /** Default value for port attribute. Uint16 value. */
+                    static const uint16_t uintPort;
+                };
+
+                /**
+                 * Connection end point structure.
+                 */
+                struct EndPoint
+                {
+                    /** Remote host. */
+                    std::string host;
+
+                    /** TCP port. */
+                    uint16_t port;
+                };
+
                 /**
                  * Default constructor.
                  */
@@ -83,7 +146,7 @@ namespace ignite
                  */
                 uint16_t GetPort() const
                 {
-                    return port;
+                    return endPoint.port;
                 }
 
                 /**
@@ -93,7 +156,7 @@ namespace ignite
                  */
                 const std::string& GetDsn() const
                 {
-                    return dsn;
+                    return GetStringValue(Key::dsn, DefaultValue::dsn);
                 }
 
                 /**
@@ -103,7 +166,7 @@ namespace ignite
                  */
                 const std::string& GetDriver() const
                 {
-                    return driver;
+                    return GetStringValue(Key::driver, DefaultValue::driver);
                 }
 
                 /**
@@ -113,7 +176,7 @@ namespace ignite
                  */
                 const std::string& GetHost() const
                 {
-                    return host;
+                    return endPoint.host;
                 }
 
                 /**
@@ -123,15 +186,29 @@ namespace ignite
                  */
                 const std::string& GetCache() const
                 {
-                    return cache;
+                    return GetStringValue(Key::cache, DefaultValue::cache);
                 }
 
-            private:
-                IGNITE_NO_COPY_ASSIGNMENT(Configuration);
+                /**
+                 * Get address.
+                 *
+                 * @return Address.
+                 */
+                const std::string& GetAddress() const
+                {
+                    return GetStringValue(Key::address, DefaultValue::address);
+                }
 
-                /** Map containing connect arguments. */
-                typedef std::map<std::string, std::string> ArgumentMap;
+                /**
+                 * Get string value from the config.
+                 *
+                 * @param key Configuration key.
+                 * @param dflt Default value to be returned if there is no value stored.
+                 * @return Found or default value.
+                 */
+                const std::string& GetStringValue(const std::string& key, const std::string& dflt) const;
 
+            private:
                 /**
                  * Parse connect string into key-value storage.
                  *
@@ -139,22 +216,22 @@ namespace ignite
                  * @param len String length.
                  * @param params Parsing result.
                  */
-                void ParseAttributeList(const char* str, size_t len, char delimeter, ArgumentMap& args) const;
+                static void ParseAttributeList(const char* str, size_t len, char delimeter, ArgumentMap& args);
 
-                /** Data Source Name. */
-                std::string dsn;
-
-                /** Driver name. */
-                std::string driver;
-
-                /** Server hostname. */
-                std::string host;
+                /**
+                 * Parse address and extract connection end-point.
+                 *
+                 * @throw IgniteException if address can not be parsed.
+                 * @param address Address string to parse.
+                 * @param res Result is placed here.
+                 */
+                static void ParseAddress(const std::string& address, EndPoint& res);
 
-                /** Port of the server. */
-                uint16_t port;
+                /** Arguments. */
+                ArgumentMap arguments;
 
-                /** Cache name. */
-                std::string cache;
+                /** Connection end-point. */
+                EndPoint endPoint;
             };
         }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/8386dd86/modules/platforms/cpp/odbc/include/ignite/odbc/connection.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc/include/ignite/odbc/connection.h b/modules/platforms/cpp/odbc/include/ignite/odbc/connection.h
index 10ceb19..00bdfc8 100644
--- a/modules/platforms/cpp/odbc/include/ignite/odbc/connection.h
+++ b/modules/platforms/cpp/odbc/include/ignite/odbc/connection.h
@@ -25,6 +25,7 @@
 #include "ignite/odbc/parser.h"
 #include "ignite/odbc/system/socket_client.h"
 #include "ignite/odbc/config/connection_info.h"
+#include "ignite/odbc/config/configuration.h"
 #include "ignite/odbc/diagnostic/diagnosable_adapter.h"
 
 namespace ignite
@@ -74,18 +75,16 @@ namespace ignite
             /**
              * Establish connection to ODBC server.
              *
-             * @param server Server (DSN).
+             * @param connectStr Connection string.
              */
-            void Establish(const std::string& server);
+            void Establish(const std::string& connectStr);
 
             /**
              * Establish connection to ODBC server.
              *
-             * @param host Host.
-             * @param port Port.
-             * @param cache Cache name to connect to.
+             * @param cfg Configuration.
              */
-            void Establish(const std::string& host, uint16_t port, const std::string& cache);
+            void Establish(const config::Configuration cfg);
 
             /**
              * Release established connection.
@@ -124,6 +123,13 @@ namespace ignite
             const std::string& GetCache() const;
 
             /**
+             * Get configuration.
+             *
+             * @return Connection configuration.
+             */
+            const config::Configuration& GetConfiguration() const;
+
+            /**
              * Create diagnostic record associated with the Connection instance.
              *
              * @param sqlState SQL state.
@@ -132,8 +138,8 @@ namespace ignite
              * @param columnNum Associated column number.
              * @return DiagnosticRecord associated with the instance.
              */
-            diagnostic::DiagnosticRecord CreateStatusRecord(SqlState sqlState,
-                const std::string& message, int32_t rowNum = 0, int32_t columnNum = 0) const;
+            static diagnostic::DiagnosticRecord CreateStatusRecord(SqlState sqlState,
+                const std::string& message, int32_t rowNum = 0, int32_t columnNum = 0);
 
             /**
              * Synchronously send request message and receive response.
@@ -172,21 +178,19 @@ namespace ignite
              * Establish connection to ODBC server.
              * Internal call.
              *
-             * @param server Server (DNS).
+             * @param connectStr Connection string.
              * @return Operation result.
              */
-            SqlResult InternalEstablish(const std::string& server);
+            SqlResult InternalEstablish(const std::string& connectStr);
 
             /**
              * Establish connection to ODBC server.
              * Internal call.
              *
-             * @param host Host.
-             * @param port Port.
-             * @param cache Cache name to connect to.
+             * @param cfg Configuration.
              * @return Operation result.
              */
-            SqlResult InternalEstablish(const std::string& host, uint16_t port, const std::string& cache);
+            SqlResult InternalEstablish(const config::Configuration cfg);
 
             /**
              * Release established connection.
@@ -269,11 +273,11 @@ namespace ignite
             /** State flag. */
             bool connected;
 
-            /** Cache name. */
-            std::string cache;
-
             /** Message parser. */
             Parser parser;
+
+            /** Configuration. */
+            config::Configuration config;
         };
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/8386dd86/modules/platforms/cpp/odbc/src/config/configuration.cpp
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc/src/config/configuration.cpp b/modules/platforms/cpp/odbc/src/config/configuration.cpp
index 8d57dee..45b0507 100644
--- a/modules/platforms/cpp/odbc/src/config/configuration.cpp
+++ b/modules/platforms/cpp/odbc/src/config/configuration.cpp
@@ -15,8 +15,6 @@
  * limitations under the License.
  */
 
-#include <cstring>
-
 #include <string>
 #include <sstream>
 #include <algorithm>
@@ -31,50 +29,25 @@ namespace ignite
     {
         namespace config
         {
-            /** Default values for configuration. */
-            namespace dflt
-            {
-                /** Default value for DSN attribute. */
-                const std::string dsn = "Default Apache Ignite DSN";
-
-                /** Default value for Driver attribute. */
-                const std::string driver = "Apache Ignite";
-
-                /** Default value for host attribute. */
-                const std::string host = "localhost";
-
-                /** Default value for port attribute. */
-                const uint16_t port = 10800;
-
-                /** Default value for cache attribute. */
-                const std::string cache = "";
-            }
-
-            /** Connection attribute keywords. */
-            namespace attrkey
-            {
-                /** Connection attribute keyword for DSN attribute. */
-                const std::string dsn = "dsn";
-            
-                /** Connection attribute keyword for Driver attribute. */
-                const std::string driver = "driver";
-
-                /** Connection attribute keyword for server host attribute. */
-                const std::string host = "server";
-
-                /** Connection attribute keyword for server port attribute. */
-                const std::string port = "port";
-
-                /** Connection attribute keyword for cache attribute. */
-                const std::string cache = "cache";
-            }
+            const std::string Configuration::Key::dsn     = "dsn";
+            const std::string Configuration::Key::driver  = "driver";
+            const std::string Configuration::Key::cache   = "cache";
+            const std::string Configuration::Key::address = "address";
+            const std::string Configuration::Key::server  = "server";
+            const std::string Configuration::Key::port    = "port";
+
+            const std::string Configuration::DefaultValue::dsn     = "Apache Ignite DSN";
+            const std::string Configuration::DefaultValue::driver  = "Apache Ignite";
+            const std::string Configuration::DefaultValue::cache   = "";
+            const std::string Configuration::DefaultValue::address = "";
+            const std::string Configuration::DefaultValue::server  = "";
+            const std::string Configuration::DefaultValue::port    = "10800";
+            const uint16_t Configuration::DefaultValue::uintPort = common::LexicalCast<uint16_t>(port);
 
             Configuration::Configuration() :
-                dsn(dflt::dsn), driver(dflt::driver),
-                host(dflt::host), port(dflt::port),
-                cache(dflt::cache)
+                arguments()
             {
-                // No-op.
+                ParseAddress(DefaultValue::address, endPoint);
             }
 
             Configuration::~Configuration()
@@ -84,7 +57,11 @@ namespace ignite
 
             void Configuration::FillFromConnectString(const char* str, size_t len)
             {
-                ArgumentMap connect_attributes;
+                // Initializing map.
+                arguments.clear();
+
+                // Initializing DSN to empty string.
+                arguments[Key::dsn].clear();
 
                 // Ignoring terminating zero byte if present.
                 // Some Driver Managers pass zero-terminated connection string
@@ -92,39 +69,19 @@ namespace ignite
                 if (len && !str[len - 1])
                     --len;
 
-                ParseAttributeList(str, len, ';', connect_attributes);
-
-                ArgumentMap::const_iterator it;
+                ParseAttributeList(str, len, ';', arguments);
 
-                it = connect_attributes.find(attrkey::dsn);
-                if (it != connect_attributes.end())
-                    dsn = it->second;
-                else
-                    dsn.clear();
-
-                it = connect_attributes.find(attrkey::driver);
-                if (it != connect_attributes.end())
-                    driver = it->second;
-                else
-                    driver = dflt::driver;
-
-                it = connect_attributes.find(attrkey::host);
-                if (it != connect_attributes.end())
-                    host = it->second;
-                else
-                    host = dflt::host;
-
-                it = connect_attributes.find(attrkey::port);
-                if (it != connect_attributes.end())
-                    port = atoi(it->second.c_str());
-                else
-                    port = dflt::port;
-
-                it = connect_attributes.find(attrkey::cache);
-                if (it != connect_attributes.end())
-                    cache = it->second;
+                ArgumentMap::const_iterator it = arguments.find(Key::address);
+                if (it != arguments.end())
+                {
+                    // Parsing address.
+                    ParseAddress(it->second, endPoint);
+                }
                 else
-                    cache = dflt::cache;
+                {
+                    endPoint.host = GetStringValue(Key::server, DefaultValue::server);
+                    endPoint.port = common::LexicalCast<uint16_t>(GetStringValue(Key::port, DefaultValue::port));
+                }
             }
 
             void Configuration::FillFromConnectString(const std::string& str)
@@ -136,27 +93,27 @@ namespace ignite
             {
                 std::stringstream connect_string_buffer;
 
-                if (!driver.empty())
-                    connect_string_buffer << attrkey::driver << "={" << driver << "};";
-
-                if (!host.empty())
-                    connect_string_buffer << attrkey::host << '=' << host << ';';
-
-                if (port)
-                    connect_string_buffer << attrkey::port << '=' << port << ';';
+                for (ArgumentMap::const_iterator it = arguments.begin(); it != arguments.end(); ++it)
+                {
+                    const std::string& key = it->first;
+                    const std::string& value = it->second;
 
-                if (!dsn.empty())
-                    connect_string_buffer << attrkey::dsn << '=' << dsn << ';';
+                    if (value.empty())
+                        continue;
 
-                if (!cache.empty())
-                    connect_string_buffer << attrkey::cache << '=' << cache << ';';
+                    if (value.find(' ') == std::string::npos)
+                        connect_string_buffer << key << '=' << value << ';';
+                    else
+                        connect_string_buffer << key << "={" << value << "};";
+                }
 
                 return connect_string_buffer.str();
             }
 
             void Configuration::FillFromConfigAttributes(const char * attributes)
             {
-                ArgumentMap config_attributes;
+                // Initializing map.
+                arguments.clear();
 
                 size_t len = 0;
 
@@ -166,45 +123,34 @@ namespace ignite
 
                 ++len;
 
-                ParseAttributeList(attributes, len, '\0', config_attributes);
-
-                ArgumentMap::const_iterator it;
-
-                it = config_attributes.find(attrkey::dsn);
-                if (it != config_attributes.end())
-                    dsn = it->second;
-                else
-                    dsn = dflt::dsn;
+                ParseAttributeList(attributes, len, '\0', arguments);
 
-                it = config_attributes.find(attrkey::driver);
-                if (it != config_attributes.end())
-                    driver = it->second;
+                ArgumentMap::const_iterator it = arguments.find(Key::address);
+                if (it != arguments.end())
+                {
+                    // Parsing address.
+                    ParseAddress(it->second, endPoint);
+                }
                 else
-                    driver.clear();
+                {
+                    endPoint.host = GetStringValue(Key::server, DefaultValue::server);
+                    endPoint.port = common::LexicalCast<uint16_t>(GetStringValue(Key::port, DefaultValue::port));
+                }
+            }
 
-                it = config_attributes.find(attrkey::host);
-                if (it != config_attributes.end())
-                    host = it->second;
-                else
-                    host.clear();
+            const std::string& Configuration::GetStringValue(const std::string& key, const std::string& dflt) const
+            {
+                ArgumentMap::const_iterator it = arguments.find(common::ToLower(key));
 
-                it = config_attributes.find(attrkey::port);
-                if (it != config_attributes.end())
-                    port = atoi(it->second.c_str());
-                else
-                    port = 0;
+                if (it != arguments.end())
+                    return it->second;
 
-                it = config_attributes.find(attrkey::cache);
-                if (it != config_attributes.end())
-                    cache = it->second;
-                else
-                    cache.clear();
+                return dflt;
             }
 
-            void Configuration::ParseAttributeList(const char * str, size_t len, char delimeter, ArgumentMap & args) const
+            void Configuration::ParseAttributeList(const char * str, size_t len, char delimeter, ArgumentMap & args)
             {
                 std::string connect_str(str, len);
-                args.clear();
 
                 while (!connect_str.empty())
                 {
@@ -245,6 +191,51 @@ namespace ignite
                     connect_str.erase(attr_begin - 1);
                 }
             }
+
+            void Configuration::ParseAddress(const std::string& address, EndPoint& res)
+            {
+                int64_t colonNum = std::count(address.begin(), address.end(), ':');
+
+                if (colonNum == 0)
+                {
+                    res.host = address;
+                    res.port = DefaultValue::uintPort;
+                }
+                else if (colonNum == 1)
+                {
+                    size_t pos = address.find(':');
+
+                    if (pos == address.size() - 1)
+                        throw IgniteError(IgniteError::IGNITE_ERR_GENERIC,
+                            "Invalid address format: no port after colon");
+
+                    res.host = address.substr(0, pos);
+
+                    std::string port = address.substr(pos + 1);
+
+                    if (!common::AllOf(port.begin(), port.end(), isdigit))
+                        throw IgniteError(IgniteError::IGNITE_ERR_GENERIC,
+                            "Invalid address format: port can only contain digits");
+
+                    int32_t intPort = common::LexicalCast<int32_t>(port);
+
+                    if (port.size() > sizeof("65535") - 1 || intPort > UINT16_MAX)
+                    {
+                        throw IgniteError(IgniteError::IGNITE_ERR_GENERIC,
+                            "Invalid address format: Port value is too large,"
+                            " valid value should be in range from 1 to 65535");
+                    }
+
+                    if (intPort == 0)
+                        throw IgniteError(IgniteError::IGNITE_ERR_GENERIC,
+                            "Invalid address format: Port value can not be zero");
+
+                    res.port = static_cast<uint16_t>(intPort);
+                }
+                else
+                    throw IgniteError(IgniteError::IGNITE_ERR_GENERIC, 
+                        "Invalid address format: too many colons");
+            }
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/8386dd86/modules/platforms/cpp/odbc/src/connection.cpp
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc/src/connection.cpp b/modules/platforms/cpp/odbc/src/connection.cpp
index 2441759..844ad70 100644
--- a/modules/platforms/cpp/odbc/src/connection.cpp
+++ b/modules/platforms/cpp/odbc/src/connection.cpp
@@ -41,7 +41,11 @@ namespace ignite
     {
         const std::string Connection::PROTOCOL_VERSION_SINCE = "1.6.0";
 
-        Connection::Connection() : socket(), connected(false), cache(), parser()
+        Connection::Connection() :
+            socket(),
+            connected(false),
+            parser(),
+            config()
         {
             // No-op.
         }
@@ -53,8 +57,8 @@ namespace ignite
         
         const config::ConnectionInfo& Connection::GetInfo() const
         {
-            // Connection info is the same for all connections now.
-            static config::ConnectionInfo info;
+            // Connection info is constant and the same for all connections now.
+            const static config::ConnectionInfo info;
 
             return info;
         }
@@ -76,32 +80,38 @@ namespace ignite
             return res;
         }
 
-        void Connection::Establish(const std::string& server)
+        void Connection::Establish(const std::string& connectStr)
         {
-            IGNITE_ODBC_API_CALL(InternalEstablish(server));
+            IGNITE_ODBC_API_CALL(InternalEstablish(connectStr));
         }
 
-        SqlResult Connection::InternalEstablish(const std::string& server)
+        SqlResult Connection::InternalEstablish(const std::string& connectStr)
         {
             config::Configuration config;
 
-            if (server != config.GetDsn())
+            try
+            {
+                config.FillFromConnectString(connectStr);
+            }
+            catch (IgniteError& e)
             {
-                AddStatusRecord(SQL_STATE_HY000_GENERAL_ERROR, "Unknown server.");
+                AddStatusRecord(SQL_STATE_HY000_GENERAL_ERROR, e.GetText());
 
                 return SQL_RESULT_ERROR;
             }
 
-            return InternalEstablish(config.GetHost(), config.GetPort(), config.GetCache());
+            return InternalEstablish(config);
         }
 
-        void Connection::Establish(const std::string& host, uint16_t port, const std::string& cache)
+        void Connection::Establish(const config::Configuration cfg)
         {
-            IGNITE_ODBC_API_CALL(InternalEstablish(host, port, cache));
+            IGNITE_ODBC_API_CALL(InternalEstablish(cfg));
         }
 
-        SqlResult Connection::InternalEstablish(const std::string & host, uint16_t port, const std::string & cache)
+        SqlResult Connection::InternalEstablish(const config::Configuration cfg)
         {
+            config = cfg;
+
             if (connected)
             {
                 AddStatusRecord(SQL_STATE_08002_ALREADY_CONNECTED, "Already connected.");
@@ -109,9 +119,7 @@ namespace ignite
                 return SQL_RESULT_ERROR;
             }
 
-            this->cache = cache;
-
-            connected = socket.Connect(host.c_str(), port);
+            connected = socket.Connect(cfg.GetHost().c_str(), cfg.GetPort());
 
             if (!connected)
             {
@@ -262,11 +270,16 @@ namespace ignite
 
         const std::string& Connection::GetCache() const
         {
-            return cache;
+            return config.GetCache();
+        }
+
+        const config::Configuration& Connection::GetConfiguration() const
+        {
+            return config;
         }
 
         diagnostic::DiagnosticRecord Connection::CreateStatusRecord(SqlState sqlState,
-            const std::string& message, int32_t rowNum, int32_t columnNum) const
+            const std::string& message, int32_t rowNum, int32_t columnNum)
         {
             return diagnostic::DiagnosticRecord(sqlState, message, "", "", rowNum, columnNum);
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/8386dd86/modules/platforms/cpp/odbc/src/odbc.cpp
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc/src/odbc.cpp b/modules/platforms/cpp/odbc/src/odbc.cpp
index 3b31f1d..9b4179e 100644
--- a/modules/platforms/cpp/odbc/src/odbc.cpp
+++ b/modules/platforms/cpp/odbc/src/odbc.cpp
@@ -42,7 +42,16 @@ namespace ignite
 
         ignite::odbc::config::Configuration config;
 
-        config.FillFromConfigAttributes(attributes);
+        try
+        {
+            config.FillFromConfigAttributes(attributes);
+        }
+        catch (IgniteError& e)
+        {
+            SQLPostInstallerError(e.GetCode(), e.GetText());
+
+            return SQL_FALSE;
+        }
 
         if (!SQLValidDSN(config.GetDsn().c_str()))
             return SQL_FALSE;
@@ -323,18 +332,14 @@ namespace ignite
 
         std::string connectStr = SqlStringToString(inConnectionString, inConnectionStringLen);
 
-        ignite::odbc::config::Configuration config;
-
-        config.FillFromConnectString(connectStr);
-
-        connection->Establish(config.GetHost(), config.GetPort(), config.GetCache());
+        connection->Establish(connectStr);
 
         const DiagnosticRecordStorage& diag = connection->GetDiagnosticRecords();
 
         if (!diag.IsSuccessful())
             return diag.GetReturnCode();
 
-        std::string outConnectStr = config.ToConnectString();
+        std::string outConnectStr = connection->GetConfiguration().ToConnectString();
 
         size_t reslen = CopyStringToBuffer(outConnectStr,
             reinterpret_cast<char*>(outConnectionString),
@@ -357,7 +362,7 @@ namespace ignite
                          SQLSMALLINT    authLen)
     {
         using ignite::odbc::Connection;
-        using ignite::odbc::diagnostic::DiagnosticRecordStorage;
+        using ignite::odbc::config::Configuration;
         using ignite::utility::SqlStringToString;
 
         LOG_MSG("SQLConnect called\n");
@@ -367,9 +372,11 @@ namespace ignite
         if (!connection)
             return SQL_INVALID_HANDLE;
 
-        std::string server = SqlStringToString(serverName, serverNameLen);
+        //std::string server = SqlStringToString(serverName, serverNameLen);
+
+        Configuration config;
 
-        connection->Establish(server);
+        connection->Establish(config);
 
         return connection->GetDiagnosticRecords().GetReturnCode();
     }


[14/50] [abbrv] ignite git commit: ignite-1232 Distributed SQL joins implementation

Posted by vo...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2MetaTable.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2MetaTable.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2MetaTable.java
new file mode 100644
index 0000000..d8f2e08
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2MetaTable.java
@@ -0,0 +1,383 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.processors.query.h2.opt;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicLong;
+import org.h2.command.ddl.CreateTableData;
+import org.h2.engine.Session;
+import org.h2.index.BaseIndex;
+import org.h2.index.Cursor;
+import org.h2.index.Index;
+import org.h2.index.IndexCondition;
+import org.h2.index.IndexType;
+import org.h2.index.SingleRowCursor;
+import org.h2.message.DbException;
+import org.h2.result.Row;
+import org.h2.result.SearchRow;
+import org.h2.result.SortOrder;
+import org.h2.table.Column;
+import org.h2.table.IndexColumn;
+import org.h2.table.TableBase;
+import org.h2.table.TableFilter;
+import org.h2.value.Value;
+import org.h2.value.ValueInt;
+import org.jsr166.ConcurrentHashMap8;
+
+/**
+ * Meta table.
+ */
+public class GridH2MetaTable extends TableBase {
+    /** */
+    private static final int ID = 0;
+
+    /** */
+    private final MetaIndex index;
+
+    /** */
+    private final AtomicLong dataModificationId = new AtomicLong();
+
+    /** */
+    private final Set<Session> fakeExclusiveSet = Collections.newSetFromMap(
+        new ConcurrentHashMap8<Session,Boolean>());
+
+    /**
+     * @param data Data.
+     */
+    public GridH2MetaTable(CreateTableData data) {
+        super(data);
+
+        ArrayList<Column> cols = data.columns;
+        assert cols.size() == 4 : cols;
+
+        Column id = cols.get(ID);
+        assert "ID".equals(id.getName()) && id.getType() == Value.INT : cols;
+        assert id.getColumnId() == ID;
+
+        index = new MetaIndex();
+    }
+
+    /** {@inheritDoc} */
+    @Override public Row getTemplateRow() {
+        return new MetaRow();
+    }
+
+    /** {@inheritDoc} */
+    @Override public SearchRow getTemplateSimpleRow(boolean singleColumn) {
+        if (singleColumn)
+            return GridH2RowFactory.create((Value)null);
+
+        return new MetaRow();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean lock(Session session, boolean exclusive, boolean forceLockEvenInMvcc) {
+        if (fakeExclusiveSet.contains(session))
+            return true;
+
+        if (exclusive)
+            fakeExclusiveSet.add(session);
+
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void unlock(Session s) {
+        fakeExclusiveSet.remove(s);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void close(Session session) {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override public Index addIndex(Session session, String indexName, int indexId,
+        IndexColumn[] cols, IndexType indexType, boolean create, String indexComment) {
+        assert cols.length == 1 : "len: " + cols.length;
+
+        int colId = cols[0].column.getColumnId();
+        assert colId == ID : "colId: " + colId;
+
+        return index;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void removeRow(Session session, Row row) {
+        dataModificationId.incrementAndGet();
+        index.remove(session, row);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void truncate(Session session) {
+        dataModificationId.incrementAndGet();
+        index.truncate(session);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void addRow(Session session, Row row) {
+        dataModificationId.incrementAndGet();
+        index.add(session, row);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void checkSupportAlter() {
+        throw DbException.getUnsupportedException("alter");
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getTableType() {
+        return SYSTEM_TABLE;
+    }
+
+    /** {@inheritDoc} */
+    @Override public Index getScanIndex(Session session) {
+        return index;
+    }
+
+    /** {@inheritDoc} */
+    @Override public Index getUniqueIndex() {
+        return index;
+    }
+
+    /** {@inheritDoc} */
+    @Override public ArrayList<Index> getIndexes() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isLockedExclusively() {
+        return !fakeExclusiveSet.isEmpty();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isLockedExclusivelyBy(Session s) {
+        return fakeExclusiveSet.contains(s);
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getMaxDataModificationId() {
+        return dataModificationId.get();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isDeterministic() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean canGetRowCount() {
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean canDrop() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getRowCount(Session session) {
+        return index.getRowCount(session);
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getRowCountApproximation() {
+        return index.getRowCountApproximation();
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getDiskSpaceUsed() {
+        return 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void checkRename() {
+        throw DbException.getUnsupportedException("rename");
+    }
+
+    /**
+     * Get value row.
+     */
+    private static class MetaRow extends GridH2Row {
+        /** */
+        private Value v0;
+
+        /** */
+        private Value v1;
+
+        /** */
+        private Value v2;
+
+        /** */
+        private Value v3;
+
+        /** {@inheritDoc} */
+        @Override public int getColumnCount() {
+            return 4;
+        }
+
+        /** {@inheritDoc} */
+        @Override public Value getValue(int idx) {
+            switch (idx) {
+                case 0:
+                    return v0;
+
+                case 1:
+                    return v1;
+
+                case 2:
+                    return v2;
+
+                case 3:
+                    return v3;
+
+                default:
+                    throw new IllegalStateException("Index: " + idx);
+            }
+        }
+
+        /** {@inheritDoc} */
+        @Override public void setValue(int idx, Value v) {
+            switch (idx) {
+                case 0:
+                    v0 = v;
+
+                    break;
+
+                case 1:
+                    v1 = v;
+
+                    break;
+
+                case 2:
+                    v2 = v;
+
+                    break;
+
+                case 3:
+                    v3 = v;
+
+                    break;
+
+                default:
+                    throw new IllegalStateException("Index: " + idx);
+            }
+        }
+    }
+
+    /**
+     * Met index.
+     */
+    private static class MetaIndex extends BaseIndex {
+        /** */
+        private final ConcurrentMap<ValueInt, GridH2Row> rows = new ConcurrentHashMap8<>();
+
+        /** {@inheritDoc} */
+        @Override public void checkRename() {
+            throw DbException.getUnsupportedException("rename");
+        }
+
+        /** {@inheritDoc} */
+        @Override public void close(Session session) {
+            // No-op.
+        }
+
+        /**
+         * @param row Row.
+         * @return ID.
+         */
+        private static ValueInt id(SearchRow row) {
+            Value id = row.getValue(ID);
+
+            assert id != null;
+
+            return (ValueInt)id;
+        }
+
+        /** {@inheritDoc} */
+        @Override public void add(Session session, Row row) {
+            rows.put(id(row), (GridH2Row)row);
+        }
+
+        /** {@inheritDoc} */
+        @Override public void remove(Session session, Row row) {
+            rows.remove(id(row), row);
+        }
+
+        /** {@inheritDoc} */
+        @Override public Cursor find(Session session, SearchRow first, SearchRow last) {
+            if (first == null || last == null || !Objects.equals(id(first), id(last)))
+                return new GridH2Cursor(rows.values().iterator());
+
+            return new SingleRowCursor(rows.get(id(first)));
+        }
+
+        /** {@inheritDoc} */
+        @Override public double getCost(Session session, int[] masks, TableFilter[] filters,
+            int filter, SortOrder sortOrder) {
+            if ((masks[ID] & IndexCondition.EQUALITY) == IndexCondition.EQUALITY)
+                return 1;
+
+            return 1000 + rows.size();
+        }
+
+        /** {@inheritDoc} */
+        @Override public void remove(Session session) {
+            // No-op.
+        }
+
+        /** {@inheritDoc} */
+        @Override public void truncate(Session session) {
+            rows.clear();
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean canGetFirstOrLast() {
+            return false;
+        }
+
+        /** {@inheritDoc} */
+        @Override public Cursor findFirstOrLast(Session session, boolean first) {
+            throw new UnsupportedOperationException();
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean needRebuild() {
+            return false;
+        }
+
+        /** {@inheritDoc} */
+        @Override public long getRowCount(Session session) {
+            return rows.size();
+        }
+
+        /** {@inheritDoc} */
+        @Override public long getRowCountApproximation() {
+            return getRowCount(null);
+        }
+
+        /** {@inheritDoc} */
+        @Override public long getDiskSpaceUsed() {
+            return 0;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2QueryContext.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2QueryContext.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2QueryContext.java
new file mode 100644
index 0000000..19ea2b2
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2QueryContext.java
@@ -0,0 +1,612 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.processors.query.h2.opt;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentMap;
+import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridReservable;
+import org.apache.ignite.internal.util.tostring.GridToStringInclude;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.spi.indexing.IndexingQueryFilter;
+import org.jetbrains.annotations.Nullable;
+import org.jsr166.ConcurrentHashMap8;
+
+import static org.apache.ignite.internal.processors.query.h2.opt.GridH2QueryType.MAP;
+
+/**
+ * Thread local SQL query context which is intended to be accessible from everywhere.
+ */
+public class GridH2QueryContext {
+    /** */
+    private static final ThreadLocal<GridH2QueryContext> qctx = new ThreadLocal<>();
+
+    /** */
+    private static final ConcurrentMap<Key, GridH2QueryContext> qctxs = new ConcurrentHashMap8<>();
+
+    /** */
+    private final Key key;
+
+    /** */
+    private volatile boolean cleared;
+
+    /** Index snapshots. */
+    @GridToStringInclude
+    private Map<Long, Object> snapshots;
+
+    /** */
+    private List<GridReservable> reservations;
+
+    /** Range streams for indexes. */
+    private Map<Integer, Object> streams;
+
+    /** Range sources for indexes. */
+    private Map<SourceKey, Object> sources;
+
+    /** */
+    private int batchLookupIdGen;
+
+    /** */
+    private IndexingQueryFilter filter;
+
+    /** */
+    private AffinityTopologyVersion topVer;
+
+    /** */
+    private Map<UUID, int[]> partsMap;
+
+    /** */
+    private UUID[] partsNodes;
+
+    /** */
+    private boolean distributedJoins;
+
+    /** */
+    private int pageSize;
+
+    /** */
+    private GridH2CollocationModel qryCollocationMdl;
+
+    /**
+     * @param locNodeId Local node ID.
+     * @param nodeId The node who initiated the query.
+     * @param qryId The query ID.
+     * @param type Query type.
+     */
+    public GridH2QueryContext(UUID locNodeId, UUID nodeId, long qryId, GridH2QueryType type) {
+        key = new Key(locNodeId, nodeId, qryId, type);
+    }
+
+    /**
+     * @return Type.
+     */
+    public GridH2QueryType type() {
+        return key.type;
+    }
+
+    /**
+     * @return Origin node ID.
+     */
+    public UUID originNodeId() {
+        return key.nodeId;
+    }
+
+    /**
+     * @return Query request ID.
+     */
+    public long queryId() {
+        return key.qryId;
+    }
+
+    /**
+     * @return Query collocation model.
+     */
+    public GridH2CollocationModel queryCollocationModel() {
+        return qryCollocationMdl;
+    }
+
+    /**
+     * @param qryCollocationMdl Query collocation model.
+     */
+    public void queryCollocationModel(GridH2CollocationModel qryCollocationMdl) {
+        this.qryCollocationMdl = qryCollocationMdl;
+    }
+
+    /**
+     * @param distributedJoins Distributed joins can be run in this query.
+     * @return {@code this}.
+     */
+    public GridH2QueryContext distributedJoins(boolean distributedJoins) {
+        this.distributedJoins = distributedJoins;
+
+        return this;
+    }
+
+    /**
+     * @return {@code true} If distributed joins can be run in this query.
+     */
+    public boolean distributedJoins() {
+        return distributedJoins;
+    }
+
+    /**
+     * @param reservations Reserved partitions or group reservations.
+     * @return {@code this}.
+     */
+    public GridH2QueryContext reservations(List<GridReservable> reservations) {
+        this.reservations = reservations;
+
+        return this;
+    }
+
+    /**
+     * @param topVer Topology version.
+     * @return {@code this}.
+     */
+    public GridH2QueryContext topologyVersion(AffinityTopologyVersion topVer) {
+        this.topVer = topVer;
+
+        return this;
+    }
+
+    /**
+     * @return Topology version.
+     */
+    public AffinityTopologyVersion topologyVersion() {
+        return topVer;
+    }
+
+    /**
+     * @param partsMap Partitions map.
+     * @return {@code this}.
+     */
+    public GridH2QueryContext partitionsMap(Map<UUID,int[]> partsMap) {
+        this.partsMap = partsMap;
+
+        return this;
+    }
+
+    /**
+     * @return Partitions map.
+     */
+    public Map<UUID,int[]> partitionsMap() {
+        return partsMap;
+    }
+
+    /**
+     * @param p Partition.
+     * @param cctx Cache context.
+     * @return Owning node ID.
+     */
+    public UUID nodeForPartition(int p, GridCacheContext<?, ?> cctx) {
+        UUID[] nodeIds = partsNodes;
+
+        if (nodeIds == null) {
+            assert partsMap != null;
+
+            nodeIds = new UUID[cctx.affinity().partitions()];
+
+            for (Map.Entry<UUID, int[]> e : partsMap.entrySet()) {
+                UUID nodeId = e.getKey();
+                int[] nodeParts = e.getValue();
+
+                assert nodeId != null;
+                assert !F.isEmpty(nodeParts);
+
+                for (int part : nodeParts) {
+                    assert nodeIds[part] == null;
+
+                    nodeIds[part] = nodeId;
+                }
+            }
+
+            partsNodes = nodeIds;
+        }
+
+        return nodeIds[p];
+    }
+
+    /**
+     * @param idxId Index ID.
+     * @param snapshot Index snapshot.
+     */
+    public void putSnapshot(long idxId, Object snapshot) {
+        assert snapshot != null;
+        assert get() == null : "need to snapshot indexes before setting query context for correct visibility";
+
+        if (snapshot instanceof GridReservable && !((GridReservable)snapshot).reserve())
+            throw new IllegalStateException("Must be already reserved before.");
+
+        if (snapshots == null)
+            snapshots = new HashMap<>();
+
+        if (snapshots.put(idxId, snapshot) != null)
+            throw new IllegalStateException("Index already snapshoted.");
+    }
+
+    /**
+     * Clear taken snapshots.
+     */
+    public void clearSnapshots() {
+        if (F.isEmpty(snapshots))
+            return;
+
+        for (Object snapshot : snapshots.values()) {
+            if (snapshot instanceof GridReservable)
+                ((GridReservable)snapshot).release();
+        }
+
+        snapshots = null;
+    }
+
+    /**
+     * @param idxId Index ID.
+     * @return Index snapshot or {@code null} if none.
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T getSnapshot(long idxId) {
+        if (snapshots == null)
+            return null;
+
+        return (T)snapshots.get(idxId);
+    }
+
+    /**
+     * @param batchLookupId Batch lookup ID.
+     * @param streams Range streams.
+     */
+    public synchronized void putStreams(int batchLookupId, Object streams) {
+        if (this.streams == null) {
+            if (streams == null)
+                return;
+
+            this.streams = new HashMap<>();
+        }
+
+        if (streams == null)
+            this.streams.remove(batchLookupId);
+        else
+            this.streams.put(batchLookupId, streams);
+    }
+
+    /**
+     * @param batchLookupId Batch lookup ID.
+     * @return Range streams.
+     */
+    @SuppressWarnings("unchecked")
+    public synchronized <T> T getStreams(int batchLookupId) {
+        if (streams == null)
+            return null;
+
+        return (T)streams.get(batchLookupId);
+    }
+
+    /**
+     * @param ownerId Owner node ID.
+     * @param batchLookupId Batch lookup ID.
+     * @param src Range source.
+     */
+    public synchronized void putSource(UUID ownerId, int batchLookupId, Object src) {
+        SourceKey srcKey = new SourceKey(ownerId, batchLookupId);
+
+        if (src != null) {
+            if (sources == null)
+                sources = new HashMap<>();
+
+            sources.put(srcKey, src);
+        }
+        else if (sources != null)
+            sources.remove(srcKey);
+    }
+
+    /**
+     * @param ownerId Owner node ID.
+     * @param batchLookupId Batch lookup ID.
+     * @return Range source.
+     */
+    @SuppressWarnings("unchecked")
+    public synchronized <T> T getSource(UUID ownerId, int batchLookupId) {
+        if (sources == null)
+            return null;
+
+        return (T)sources.get(new SourceKey(ownerId, batchLookupId));
+    }
+
+    /**
+     * @return Next batch ID.
+     */
+    public int nextBatchLookupId() {
+        return ++batchLookupIdGen;
+    }
+
+    /**
+     * @return If indexes were snapshotted before query execution.
+     */
+    public boolean hasIndexSnapshots() {
+        return snapshots != null;
+    }
+
+    /**
+     * Sets current thread local context. This method must be called when all the non-volatile properties are
+     * already set to ensure visibility for other threads.
+     *
+     * @param x Query context.
+     */
+     public static void set(GridH2QueryContext x) {
+         assert qctx.get() == null;
+
+         // We need MAP query context to be available to other threads to run distributed joins.
+         if (x.key.type == MAP && x.distributedJoins() && qctxs.putIfAbsent(x.key, x) != null)
+             throw new IllegalStateException("Query context is already set.");
+
+         qctx.set(x);
+    }
+
+    /**
+     * Drops current thread local context.
+     */
+    public static void clearThreadLocal() {
+        GridH2QueryContext x = qctx.get();
+
+        assert x != null;
+
+        qctx.remove();
+    }
+
+    /**
+     * @param locNodeId Local node ID.
+     * @param nodeId The node who initiated the query.
+     * @param qryId The query ID.
+     * @param type Query type.
+     * @return {@code True} if context was found.
+     */
+    public static boolean clear(UUID locNodeId, UUID nodeId, long qryId, GridH2QueryType type) {
+        return doClear(new Key(locNodeId, nodeId, qryId, type), false);
+    }
+
+    /**
+     * @param key Context key.
+     * @param nodeStop Node is stopping.
+     * @return {@code True} if context was found.
+     */
+    private static boolean doClear(Key key, boolean nodeStop) {
+        assert key.type == MAP : key.type;
+
+        GridH2QueryContext x = qctxs.remove(key);
+
+        if (x == null)
+            return false;
+
+        assert x.key.equals(key);
+
+        x.clearContext(nodeStop);
+
+        return true;
+    }
+
+    /**
+     * @param nodeStop Node is stopping.
+     */
+    public void clearContext(boolean nodeStop) {
+        cleared = true;
+
+        clearSnapshots();
+
+        List<GridReservable> r = reservations;
+
+        if (!nodeStop && !F.isEmpty(r)) {
+            for (int i = 0; i < r.size(); i++)
+                r.get(i).release();
+        }
+    }
+
+    /**
+     * @return {@code true} If the context is cleared.
+     */
+    public boolean isCleared() {
+        return cleared;
+    }
+
+    /**
+     * @param locNodeId Local node ID.
+     * @param nodeId Dead node ID.
+     */
+    public static void clearAfterDeadNode(UUID locNodeId, UUID nodeId) {
+        for (Key key : qctxs.keySet()) {
+            if (key.locNodeId.equals(locNodeId) && key.nodeId.equals(nodeId))
+                doClear(key, false);
+        }
+    }
+
+    /**
+     * @param locNodeId Local node ID.
+     */
+    public static void clearLocalNodeStop(UUID locNodeId) {
+        for (Key key : qctxs.keySet()) {
+            if (key.locNodeId.equals(locNodeId))
+                doClear(key, true);
+        }
+    }
+
+    /**
+     * Access current thread local query context (if it was set).
+     *
+     * @return Current thread local query context or {@code null} if the query runs outside of Ignite context.
+     */
+    @Nullable public static GridH2QueryContext get() {
+        return qctx.get();
+    }
+
+    /**
+     * Access query context from another thread.
+     *
+     * @param locNodeId Local node ID.
+     * @param nodeId The node who initiated the query.
+     * @param qryId The query ID.
+     * @param type Query type.
+     * @return Query context.
+     */
+    @Nullable public static GridH2QueryContext get(
+        UUID locNodeId,
+        UUID nodeId,
+        long qryId,
+        GridH2QueryType type
+    ) {
+        return qctxs.get(new Key(locNodeId, nodeId, qryId, type));
+    }
+
+    /**
+     * @return Filter.
+     */
+    public IndexingQueryFilter filter() {
+        return filter;
+    }
+
+    /**
+     * @param filter Filter.
+     * @return {@code this}.
+     */
+    public GridH2QueryContext filter(IndexingQueryFilter filter) {
+        this.filter = filter;
+
+        return this;
+    }
+
+    /**
+     * @return Page size.
+     */
+    public int pageSize() {
+        return pageSize;
+    }
+
+    /**
+     * @param pageSize Page size.
+     * @return {@code this}.
+     */
+    public GridH2QueryContext pageSize(int pageSize) {
+        this.pageSize = pageSize;
+
+        return this;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(GridH2QueryContext.class, this);
+    }
+
+    /**
+     * Unique key for the query context.
+     */
+    private static class Key {
+        /** */
+        private final UUID locNodeId;
+
+        /** */
+        private final UUID nodeId;
+
+        /** */
+        private final long qryId;
+
+        /** */
+        private final GridH2QueryType type;
+
+        /**
+         * @param locNodeId Local node ID.
+         * @param nodeId The node who initiated the query.
+         * @param qryId The query ID.
+         * @param type Query type.
+         */
+        private Key(UUID locNodeId, UUID nodeId, long qryId, GridH2QueryType type) {
+            assert locNodeId != null;
+            assert nodeId != null;
+            assert type != null;
+
+            this.locNodeId = locNodeId;
+            this.nodeId = nodeId;
+            this.qryId = qryId;
+            this.type = type;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean equals(Object o) {
+            if (this == o)
+                return true;
+
+            if (o == null || getClass() != o.getClass())
+                return false;
+
+            Key key = (Key)o;
+
+            return qryId == key.qryId && nodeId.equals(key.nodeId) && type == key.type &&
+               locNodeId.equals(key.locNodeId) ;
+        }
+
+        /** {@inheritDoc} */
+        @Override public int hashCode() {
+            int res = locNodeId.hashCode();
+
+            res = 31 * res + nodeId.hashCode();
+            res = 31 * res + (int)(qryId ^ (qryId >>> 32));
+            res = 31 * res + type.hashCode();
+
+            return res;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return S.toString(Key.class, this);
+        }
+    }
+
+    /**
+     * Key for source.
+     */
+    private static final class SourceKey {
+        /** */
+        UUID ownerId;
+
+        /** */
+        int batchLookupId;
+
+        /**
+         * @param ownerId Owner node ID.
+         * @param batchLookupId Batch lookup ID.
+         */
+        SourceKey(UUID ownerId, int batchLookupId) {
+            this.ownerId = ownerId;
+            this.batchLookupId = batchLookupId;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean equals(Object o) {
+            SourceKey srcKey = (SourceKey)o;
+
+            return batchLookupId == srcKey.batchLookupId && ownerId.equals(srcKey.ownerId);
+        }
+
+        /** {@inheritDoc} */
+        @Override public int hashCode() {
+            return 31 * ownerId.hashCode() + batchLookupId;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2QueryType.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2QueryType.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2QueryType.java
new file mode 100644
index 0000000..f6d0408
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2QueryType.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.processors.query.h2.opt;
+
+/**
+ * Query type.
+ */
+public enum GridH2QueryType {
+    /**
+     * Map query. Runs over local partitions, possibly with distributed joins.
+     */
+    MAP,
+
+    /**
+     * Reduce query. Local query on a node which initiated the original query.
+     */
+    REDUCE,
+
+    /**
+     * Local query. It may be also a query over replicated cache but all the data is available locally.
+     */
+    LOCAL,
+
+    /**
+     * Replicated query over a network. Such a query can be sent from a client node or node which
+     * did not load all the partitions yet.
+     */
+    REPLICATED,
+
+    /**
+     * Parsing and optimization stage.
+     */
+    PREPARE,
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2RetryException.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2RetryException.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2RetryException.java
new file mode 100644
index 0000000..e333d71
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2RetryException.java
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.processors.query.h2.opt;
+
+import org.apache.ignite.IgniteException;
+
+/**
+ * Internal exception.
+ */
+public class GridH2RetryException extends IgniteException {
+    /**
+     * @param msg Message.
+     */
+    public GridH2RetryException(String msg) {
+        super(msg);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Row.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Row.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Row.java
index 3b6b56e..9486a2e 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Row.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Row.java
@@ -18,19 +18,14 @@
 package org.apache.ignite.internal.processors.query.h2.opt;
 
 import org.h2.result.Row;
+import org.h2.result.SearchRow;
+import org.h2.store.Data;
 import org.h2.value.Value;
 
 /**
  * Row with locking support needed for unique key conflicts resolution.
  */
-public class GridH2Row extends Row implements GridSearchRowPointer {
-    /**
-     * @param data Column values.
-     */
-    public GridH2Row(Value... data) {
-        super(data, MEMORY_CALCULATE);
-    }
-
+public abstract class GridH2Row extends Row implements GridSearchRowPointer {
     /** {@inheritDoc} */
     @Override public long pointer() {
         throw new IllegalStateException();
@@ -45,4 +40,79 @@ public class GridH2Row extends Row implements GridSearchRowPointer {
     @Override public void decrementRefCount() {
         throw new IllegalStateException();
     }
+
+    /** {@inheritDoc} */
+    @Override public Row getCopy() {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setVersion(int version) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override public int getByteCount(Data dummy) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isEmpty() {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setDeleted(boolean deleted) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setSessionId(int sessionId) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override public int getSessionId() {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void commit() {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isDeleted() {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setKeyAndVersion(SearchRow old) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override public int getVersion() {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setKey(long key) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getKey() {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override public int getMemory() {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override public Value[] getValueList() {
+        throw new UnsupportedOperationException();
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2RowDescriptor.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2RowDescriptor.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2RowDescriptor.java
index 80dcfcb..f519c30 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2RowDescriptor.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2RowDescriptor.java
@@ -18,7 +18,10 @@
 package org.apache.ignite.internal.processors.query.h2.opt;
 
 import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.internal.processors.cache.CacheObject;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor;
 import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing;
 import org.apache.ignite.internal.util.offheap.unsafe.GridOffHeapSmartPointerFactory;
 import org.apache.ignite.internal.util.offheap.unsafe.GridUnsafeGuard;
@@ -31,9 +34,30 @@ import org.jetbrains.annotations.Nullable;
  */
 public interface GridH2RowDescriptor extends GridOffHeapSmartPointerFactory<GridH2KeyValueRowOffheap> {
     /**
-     * @return Owner.
+     * Gets indexing.
+     *
+     * @return indexing.
+     */
+    public IgniteH2Indexing indexing();
+
+    /**
+     * Gets type descriptor.
+     *
+     * @return Type descriptor.
+     */
+    public GridQueryTypeDescriptor type();
+
+    /**
+     * Gets cache context for this row descriptor.
+     *
+     * @return Cache context.
+     */
+    public GridCacheContext<?, ?> context();
+
+    /**
+     * @return Cache configuration.
      */
-    public IgniteH2Indexing owner();
+    public CacheConfiguration configuration();
 
     /**
      * Creates new row.

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2RowFactory.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2RowFactory.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2RowFactory.java
new file mode 100644
index 0000000..00ff3f2
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2RowFactory.java
@@ -0,0 +1,179 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.processors.query.h2.opt;
+
+import org.apache.ignite.internal.util.tostring.GridToStringInclude;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.h2.result.RowFactory;
+import org.h2.value.Value;
+
+/**
+ * Row factory.
+ */
+public class GridH2RowFactory extends RowFactory {
+    /**
+     * @param v Value.
+     * @return Row.
+     */
+    public static GridH2Row create(Value v) {
+        return new RowKey(v);
+    }
+
+    /**
+     * @param v1 Value 1.
+     * @param v2 Value 2.
+     * @return Row.
+     */
+    public static GridH2Row create(Value v1, Value v2) {
+        return new RowPair(v1, v2);
+    }
+
+    /**
+     * @param data Values.
+     * @return Row.
+     */
+    public static GridH2Row create(Value... data) {
+        switch (data.length) {
+            case 0:
+                throw new IllegalStateException("Zero columns row.");
+
+            case 1:
+                return new RowKey(data[0]);
+
+            case 2:
+                return new RowPair(data[0], data[1]);
+
+            default:
+                return new RowSimple(data);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public GridH2Row createRow(Value[] data, int memory) {
+        return create(data);
+    }
+
+    /**
+     * Single value row.
+     */
+    private static final class RowKey extends GridH2Row {
+        /** */
+        private Value key;
+
+        /**
+         * @param key Key.
+         */
+        public RowKey(Value key) {
+            this.key = key;
+        }
+
+        /** {@inheritDoc} */
+        @Override public int getColumnCount() {
+            return 1;
+        }
+
+        /** {@inheritDoc} */
+        @Override public Value getValue(int idx) {
+            assert idx == 0 : idx;
+            return key;
+        }
+
+        /** {@inheritDoc} */
+        @Override public void setValue(int idx, Value v) {
+            assert idx == 0 : idx;
+            key = v;
+        }
+    }
+
+    /**
+     * Row of two values.
+     */
+    private static final class RowPair extends GridH2Row  {
+        /** */
+        private Value v1;
+
+        /** */
+        private Value v2;
+
+        /**
+         * @param v1 First value.
+         * @param v2 Second value.
+         */
+        private RowPair(Value v1, Value v2) {
+            this.v1 = v1;
+            this.v2 = v2;
+        }
+
+        /** {@inheritDoc} */
+        @Override public int getColumnCount() {
+            return 2;
+        }
+
+        /** {@inheritDoc} */
+        @Override public Value getValue(int idx) {
+            return idx == 0 ? v1 : v2;
+        }
+
+        /** {@inheritDoc} */
+        @Override public void setValue(int idx, Value v) {
+            if (idx == 0)
+                v1 = v;
+            else {
+                assert idx == 1 : idx;
+
+                v2 = v;
+            }
+        }
+    }
+
+    /**
+     * Simple array based row.
+     */
+    private static final class RowSimple extends GridH2Row {
+        /** */
+        @GridToStringInclude
+        private Value[] vals;
+
+        /**
+         * @param vals Values.
+         */
+        private RowSimple(Value[] vals) {
+            this.vals = vals;
+        }
+
+        /** {@inheritDoc} */
+        @Override public int getColumnCount() {
+            return vals.length;
+        }
+
+        /** {@inheritDoc} */
+        @Override public Value getValue(int idx) {
+            return vals[idx];
+        }
+
+        /** {@inheritDoc} */
+        @Override public void setValue(int idx, Value v) {
+            vals[idx] = v;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return S.toString(RowSimple.class, this);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java
index bea4dd8..8d080ae 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java
@@ -23,22 +23,24 @@ import java.sql.Statement;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Set;
-import java.util.concurrent.ConcurrentNavigableMap;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 import org.apache.ignite.IgniteCheckedException;
-import org.apache.ignite.IgniteException;
+import org.apache.ignite.IgniteInterruptedException;
 import org.apache.ignite.internal.processors.cache.CacheObject;
 import org.apache.ignite.internal.util.offheap.unsafe.GridUnsafeMemory;
 import org.h2.api.TableEngine;
 import org.h2.command.ddl.CreateTableData;
-import org.h2.engine.Constants;
 import org.h2.engine.Database;
 import org.h2.engine.DbObject;
 import org.h2.engine.Session;
+import org.h2.index.BaseIndex;
 import org.h2.index.Cursor;
 import org.h2.index.Index;
+import org.h2.index.IndexLookupBatch;
 import org.h2.index.IndexType;
 import org.h2.message.DbException;
 import org.h2.result.Row;
@@ -55,7 +57,10 @@ import org.jetbrains.annotations.Nullable;
 import org.jsr166.ConcurrentHashMap8;
 import org.jsr166.LongAdder8;
 
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+import static org.apache.ignite.internal.processors.query.h2.opt.GridH2AbstractKeyValueRow.KEY_COL;
 import static org.apache.ignite.internal.processors.query.h2.opt.GridH2AbstractKeyValueRow.VAL_COL;
+import static org.apache.ignite.internal.processors.query.h2.opt.GridH2QueryType.MAP;
 
 /**
  * H2 Table implementation.
@@ -74,10 +79,16 @@ public class GridH2Table extends TableBase {
     private final ReadWriteLock lock;
 
     /** */
-    private final Set<Session> sessions = Collections.newSetFromMap(new ConcurrentHashMap8<Session, Boolean>());
+    private boolean destroyed;
 
     /** */
-    private volatile Object[] actualSnapshot;
+    private final Set<Session> sessions = Collections.newSetFromMap(new ConcurrentHashMap8<Session,Boolean>());
+
+    /** */
+    private final AtomicReference<Object[]> actualSnapshot = new AtomicReference<>();
+
+    /** */
+    private IndexColumn affKeyCol;
 
     /** */
     private final LongAdder8 size = new LongAdder8();
@@ -102,17 +113,57 @@ public class GridH2Table extends TableBase {
         this.desc = desc;
         this.spaceName = spaceName;
 
+        if (desc != null && desc.context() != null && !desc.context().customAffinityMapper()) {
+            boolean affinityColExists = true;
+
+            String affKey = desc.type().affinityKey();
+
+            int affKeyColId = -1;
+
+            if (affKey != null) {
+                String colName = desc.context().config().isSqlEscapeAll() ? affKey : affKey.toUpperCase();
+
+                if (doesColumnExist(colName))
+                    affKeyColId = getColumn(colName).getColumnId();
+                else
+                    affinityColExists = false;
+            }
+            else
+                affKeyColId = KEY_COL;
+
+            if (affinityColExists) {
+                affKeyCol = indexColumn(affKeyColId, SortOrder.ASCENDING);
+
+                assert affKeyCol != null;
+            }
+        }
+
+        // Indexes must be created in the end when everything is ready.
         idxs = idxsFactory.createIndexes(this);
 
         assert idxs != null;
         assert idxs.size() >= 1;
 
-        lock = new ReentrantReadWriteLock();
-
         // Add scan index at 0 which is required by H2.
         idxs.add(0, new ScanIndex(index(0)));
 
         snapshotEnabled = desc == null || desc.snapshotableIndex();
+
+        lock = snapshotEnabled ? new ReentrantReadWriteLock() : null;
+    }
+
+    /**
+     * @return {@code true} If this is a partitioned table.
+     */
+    public boolean isPartitioned() {
+        return desc != null && desc.configuration().getCacheMode() == PARTITIONED;
+    }
+
+    /**
+     * @return Affinity key column or {@code null} if not available.
+     */
+    @Nullable public IndexColumn getAffinityKeyColumn() {
+        return affKeyCol;
     }
 
     /** {@inheritDoc} */
@@ -172,7 +223,7 @@ public class GridH2Table extends TableBase {
 
         GridUnsafeMemory mem = desc.memory();
 
-        readLock();
+        Lock l = lock(false, Long.MAX_VALUE);
 
         if (mem != null)
             desc.guard().begin();
@@ -191,7 +242,7 @@ public class GridH2Table extends TableBase {
             return true;
         }
         finally {
-            readUnlock();
+            unlock(l);
 
             if (mem != null)
                 desc.guard().end();
@@ -207,118 +258,219 @@ public class GridH2Table extends TableBase {
 
     /** {@inheritDoc} */
     @SuppressWarnings({"LockAcquiredButNotSafelyReleased", "SynchronizationOnLocalVariableOrMethodParameter", "unchecked"})
-    @Override public void lock(@Nullable final Session ses, boolean exclusive, boolean force) {
+    @Override public boolean lock(@Nullable final Session ses, boolean exclusive, boolean force) {
         if (ses != null) {
             if (!sessions.add(ses))
-                return;
+                return false;
 
-            synchronized (ses) {
-                ses.addLock(this);
-            }
+            ses.addLock(this);
         }
 
+        if (snapshotInLock())
+            snapshotIndexes(null);
+
+        return false;
+    }
+
+    /**
+     * @return {@code True} If we must snapshot and release index snapshots in {@link #lock(Session, boolean, boolean)}
+     * and {@link #unlock(Session)} methods.
+     */
+    private boolean snapshotInLock() {
+        if (!snapshotEnabled)
+            return false;
+
+        GridH2QueryContext qctx = GridH2QueryContext.get();
+
+        // On MAP queries with distributed joins we lock tables before the queries.
+        return qctx == null || qctx.type() != MAP || !qctx.hasIndexSnapshots();
+    }
+
+    /**
+     * @param qctx Query context.
+     */
+    public void snapshotIndexes(GridH2QueryContext qctx) {
         if (!snapshotEnabled)
             return;
 
-        Object[] snapshot;
+        Object[] snapshots;
 
-        for (long waitTime = 100;; waitTime *= 2) { // Increase wait time to avoid starvation.
-            snapshot = actualSnapshot;
+        Lock l;
 
-            if (snapshot != null) {
-                // Reuse existing snapshot without locking.
-                for (int i = 1, len = idxs.size(); i < len; i++)
-                    index(i).takeSnapshot(snapshot[i - 1]);
+        // Try to reuse existing snapshots outside of the lock.
+        for (long waitTime = 200;; waitTime *= 2) { // Increase wait time to avoid starvation.
+            snapshots = actualSnapshot.get();
 
-                return;
-            }
+            if (snapshots != null) { // Reuse existing snapshot without locking.
+                snapshots = doSnapshotIndexes(snapshots, qctx);
 
-            try {
-                if (lock.writeLock().tryLock(waitTime, TimeUnit.MILLISECONDS))
-                    break;
-            }
-            catch (InterruptedException e) {
-                throw new IgniteException("Thread got interrupted while trying to acquire index lock.", e);
+                if (snapshots != null)
+                    return; // Reused successfully.
             }
-        }
 
-        boolean snapshoted = false;
+            l = lock(true, waitTime);
+
+            if (l != null)
+                break;
+        }
 
         try {
-            snapshot = actualSnapshot; // Try again inside of the lock.
+            // Try again inside of the lock.
+            snapshots = actualSnapshot.get();
+
+            if (snapshots != null) // Try reusing.
+                snapshots = doSnapshotIndexes(snapshots, qctx);
 
-            if (snapshot == null) {
-                snapshot = takeIndexesSnapshot();
+            if (snapshots == null) { // Reuse failed, produce new snapshots.
+                snapshots = doSnapshotIndexes(null, qctx);
 
-                if (desc == null || desc.memory() == null) // This optimization is disabled for off-heap index.
-                    actualSnapshot = snapshot;
+                assert snapshots != null;
 
-                snapshoted = true;
+                actualSnapshot.set(snapshots);
             }
         }
         finally {
-            lock.writeLock().unlock();
+            unlock(l);
         }
+    }
 
-        if (!snapshoted) {
-            for (int i = 1, len = idxs.size(); i < len; i++)
-                index(i).takeSnapshot(snapshot[i - 1]);
+    /**
+     * @return Table identifier.
+     */
+    public String identifier() {
+        return getSchema().getName() + '.' + getName();
+    }
+
+    /**
+     * @param l Lock.
+     */
+    private static void unlock(Lock l) {
+        if (l != null)
+            l.unlock();
+    }
+
+    /**
+     * @param exclusive Exclusive lock.
+     * @param waitMillis Milliseconds to wait for the lock.
+     * @return The acquired lock or {@code null} if the lock time out occurred.
+     */
+    public Lock lock(boolean exclusive, long waitMillis) {
+        if (!snapshotEnabled)
+            return null;
+
+        Lock l = exclusive ? lock.writeLock() : lock.readLock();
+
+        try {
+            if (!l.tryLock(waitMillis, TimeUnit.MILLISECONDS))
+                return null;
+        }
+        catch (InterruptedException e) {
+            throw new IgniteInterruptedException("Thread got interrupted while trying to acquire table lock.", e);
         }
+
+        if (destroyed) {
+            unlock(l);
+
+            throw new IllegalStateException("Table " + identifier() + " already destroyed.");
+        }
+
+        return l;
     }
 
     /**
      * Must be called inside of write lock because when using multiple indexes we have to ensure that all of them have
      * the same contents at snapshot taking time.
      *
+     * @param qctx Query context.
      * @return New indexes data snapshot.
      */
     @SuppressWarnings("unchecked")
-    private Object[] takeIndexesSnapshot() {
-        int len = idxs.size();
+    private Object[] doSnapshotIndexes(Object[] snapshots, GridH2QueryContext qctx) {
+        assert snapshotEnabled;
 
-        Object[] snapshot = new ConcurrentNavigableMap[len - 1];
+        if (snapshots == null) // Nothing to reuse, create new snapshots.
+            snapshots = new Object[idxs.size() - 1];
 
-        for (int i = 1; i < len; i++) { // Take snapshots on all except first which is scan.
-            Object s = index(i).takeSnapshot(null);
+        // Take snapshots on all except first which is scan.
+        for (int i = 1, len = idxs.size(); i < len; i++) {
+            Object s = snapshots[i - 1];
 
-            snapshot[i - 1] = s;
-        }
+            boolean reuseExisting = s != null;
 
-        return snapshot;
-    }
+            s = index(i).takeSnapshot(s, qctx);
 
-    /** {@inheritDoc} */
-    @Override public void close(Session ses) {
-        assert !sessions.contains(ses);
-    }
+            if (reuseExisting && s == null) { // Existing snapshot was invalidated before we were able to reserve it.
+                // Release already taken snapshots.
+                if (qctx != null)
+                    qctx.clearSnapshots();
 
-    /** {@inheritDoc} */
-    @Override public void unlock(@Nullable Session ses) {
-        if (ses != null) {
-            boolean res = sessions.remove(ses);
+                for (int j = 1; j < i; j++)
+                    index(j).releaseSnapshot();
+
+                // Drop invalidated snapshot.
+                actualSnapshot.compareAndSet(snapshots, null);
+
+                return null;
+            }
 
-            assert res;
+            snapshots[i - 1] = s;
         }
 
-        for (int i = 1, len = idxs.size(); i < len; i++)  // Release snapshots on all except first which is scan.
-            index(i).releaseSnapshot();
+        return snapshots;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void close(Session ses) {
+        // No-op.
     }
 
     /**
-     * Closes table and releases resources.
+     * Destroy the table.
      */
-    public void close() {
-        writeLock();
+    public void destroy() {
+        Lock l = lock(true, Long.MAX_VALUE);
 
         try {
+            assert sessions.isEmpty() : sessions;
+
+            destroyed = true;
+
             for (int i = 1, len = idxs.size(); i < len; i++)
-                index(i).close(null);
+                index(i).destroy();
         }
         finally {
-            writeUnlock();
+            unlock(l);
         }
     }
 
+    /** {@inheritDoc} */
+    @Override public void unlock(@Nullable Session ses) {
+        if (ses != null && !sessions.remove(ses))
+            return;
+
+        if (snapshotInLock())
+            releaseSnapshots();
+    }
+
+    /**
+     * Releases snapshots.
+     */
+    public void releaseSnapshots() {
+        if (!snapshotEnabled)
+            return;
+
+        releaseSnapshots0(idxs);
+    }
+
+    /**
+     * @param idxs Indexes.
+     */
+    private void releaseSnapshots0(ArrayList<Index> idxs) {
+        // Release snapshots on all except first which is scan.
+        for (int i = 1, len = idxs.size(); i < len; i++)
+            ((GridH2IndexBase)idxs.get(i)).releaseSnapshot();
+    }
+
     /**
      * Updates table for given key. If value is null then row with given key will be removed from table,
      * otherwise value and expiration time will be updated or new row will be added.
@@ -336,7 +488,16 @@ public class GridH2Table extends TableBase {
 
         GridH2Row row = desc.createRow(key, val, expirationTime);
 
-        return doUpdate(row, rmv);
+        if (!rmv)
+            ((GridH2AbstractKeyValueRow)row).valuesCache(new Value[getColumns().length]);
+
+        try {
+            return doUpdate(row, rmv);
+        }
+        finally {
+            if (!rmv)
+                ((GridH2AbstractKeyValueRow)row).valuesCache(null);
+        }
     }
 
     /**
@@ -372,7 +533,7 @@ public class GridH2Table extends TableBase {
         // getting updated from different threads with different rows with the same key is impossible.
         GridUnsafeMemory mem = desc == null ? null : desc.memory();
 
-        readLock();
+        Lock l = lock(false, Long.MAX_VALUE);
 
         if (mem != null)
             desc.guard().begin();
@@ -400,7 +561,7 @@ public class GridH2Table extends TableBase {
                 while (++i < len) {
                     GridH2IndexBase idx = index(i);
 
-                    assert !idx.getIndexType().isUnique() : "Unique indexes are not supported.";
+                    assert !idx.getIndexType().isUnique() : "Unique indexes are not supported: " + idx;
 
                     GridH2Row old2 = idx.put(row);
 
@@ -417,7 +578,7 @@ public class GridH2Table extends TableBase {
                 //  index(1) is PK, get full row from there (search row here contains only key but no other columns).
                 GridH2Row old = pk.remove(row);
 
-                if (old instanceof GridH2AbstractKeyValueRow) { // Unswap value.
+                if (row.getColumnCount() != 1 && old instanceof GridH2AbstractKeyValueRow) { // Unswap value.
                     Value v = row.getValue(VAL_COL);
 
                     if (v != null)
@@ -440,12 +601,12 @@ public class GridH2Table extends TableBase {
             }
 
             // The snapshot is not actual after update.
-            actualSnapshot = null;
+            actualSnapshot.set(null);
 
             return true;
         }
         finally {
-            readUnlock();
+            unlock(l);
 
             if (mem != null)
                 desc.guard().end();
@@ -485,13 +646,12 @@ public class GridH2Table extends TableBase {
         if (!snapshotEnabled)
             return;
 
-        GridUnsafeMemory memory = desc == null ? null : desc.memory();
+        Lock l = lock(true, Long.MAX_VALUE);
 
-        lock.writeLock().lock();
+        ArrayList<Index> idxs0 = new ArrayList<>(idxs);
 
         try {
-            if (memory == null && actualSnapshot == null)
-                actualSnapshot = takeIndexesSnapshot(); // Allow read access while we are rebuilding indexes.
+            snapshotIndexes(null); // Allow read access while we are rebuilding indexes.
 
             for (int i = 1, len = idxs.size(); i < len; i++) {
                 GridH2IndexBase newIdx = index(i).rebuild();
@@ -502,13 +662,13 @@ public class GridH2Table extends TableBase {
                     idxs.set(0, new ScanIndex(newIdx));
             }
         }
-        catch (InterruptedException ignored) {
-            // No-op.
+        catch (InterruptedException e) {
+            throw new IgniteInterruptedException(e);
         }
         finally {
-            lock.writeLock().unlock();
+            releaseSnapshots0(idxs0);
 
-            actualSnapshot = null;
+            unlock(l);
         }
     }
 
@@ -621,38 +781,6 @@ public class GridH2Table extends TableBase {
     }
 
     /**
-     *
-     */
-    private void readLock() {
-        if (snapshotEnabled)
-            lock.readLock().lock();
-    }
-
-    /**
-     *
-     */
-    private void readUnlock() {
-        if (snapshotEnabled)
-            lock.readLock().unlock();
-    }
-
-    /**
-     *
-     */
-    private void writeLock() {
-        if (snapshotEnabled)
-            lock.writeLock().lock();
-    }
-
-    /**
-     *
-     */
-    private void writeUnlock() {
-        if (snapshotEnabled)
-            lock.writeLock().unlock();
-    }
-
-    /**
      * H2 Table engine.
      */
     @SuppressWarnings({"PublicInnerClass", "FieldAccessedSynchronizedAndUnsynchronized"})
@@ -729,7 +857,7 @@ public class GridH2Table extends TableBase {
      * Wrapper type for primary key.
      */
     @SuppressWarnings("PackageVisibleInnerClass")
-    static class ScanIndex implements Index {
+    static class ScanIndex extends BaseIndex {
         /** */
         static final String SCAN_INDEX_NAME_SUFFIX = "__SCAN_";
 
@@ -774,8 +902,8 @@ public class GridH2Table extends TableBase {
         }
 
         /** {@inheritDoc} */
-        @Override public void close(Session ses) {
-            delegate.close(ses);
+        @Override public final void close(Session ses) {
+            // No-op.
         }
 
         /** {@inheritDoc} */
@@ -819,8 +947,13 @@ public class GridH2Table extends TableBase {
         }
 
         /** {@inheritDoc} */
-        @Override public double getCost(Session ses, int[] masks, TableFilter tblFilter, SortOrder sortOrder) {
-            return getRowCountApproximation() + Constants.COST_ROW_OFFSET;
+        @Override public double getCost(Session ses, int[] masks, TableFilter[] filters, int filter,
+            SortOrder sortOrder) {
+            long rows = getRowCountApproximation();
+            double baseCost = getCostRangeIndex(masks, rows, filters, filter, sortOrder, true);
+            int mul = delegate.getDistributedMultiplier(ses, filters, filter);
+
+            return  mul * baseCost;
         }
 
         /** {@inheritDoc} */
@@ -884,6 +1017,11 @@ public class GridH2Table extends TableBase {
         }
 
         /** {@inheritDoc} */
+        @Override public IndexLookupBatch createLookupBatch(TableFilter filter) {
+            return delegate.createLookupBatch(filter);
+        }
+
+        /** {@inheritDoc} */
         @Override public void truncate(Session ses) {
             // No-op.
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TreeIndex.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TreeIndex.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TreeIndex.java
index 2c95b66..33aaf7b 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TreeIndex.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TreeIndex.java
@@ -17,10 +17,9 @@
 
 package org.apache.ignite.internal.processors.query.h2.opt;
 
-import java.io.Closeable;
-import java.util.Arrays;
 import java.util.Comparator;
 import java.util.Iterator;
+import java.util.List;
 import java.util.NavigableMap;
 import java.util.concurrent.ConcurrentNavigableMap;
 import java.util.concurrent.ConcurrentSkipListMap;
@@ -28,14 +27,14 @@ import org.apache.ignite.internal.util.GridEmptyIterator;
 import org.apache.ignite.internal.util.offheap.unsafe.GridOffHeapSnapTreeMap;
 import org.apache.ignite.internal.util.offheap.unsafe.GridUnsafeGuard;
 import org.apache.ignite.internal.util.snaptree.SnapTreeMap;
+import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.internal.SB;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.spi.indexing.IndexingQueryFilter;
 import org.h2.engine.Session;
 import org.h2.index.Cursor;
 import org.h2.index.IndexType;
-import org.h2.index.SingleRowCursor;
-import org.h2.result.Row;
+import org.h2.message.DbException;
 import org.h2.result.SearchRow;
 import org.h2.result.SortOrder;
 import org.h2.table.IndexColumn;
@@ -49,10 +48,7 @@ import org.jetbrains.annotations.Nullable;
 @SuppressWarnings("ComparatorNotSerializable")
 public class GridH2TreeIndex extends GridH2IndexBase implements Comparator<GridSearchRowPointer> {
     /** */
-    protected final ConcurrentNavigableMap<GridSearchRowPointer, GridH2Row> tree;
-
-    /** */
-    private final ThreadLocal<ConcurrentNavigableMap<GridSearchRowPointer, GridH2Row>> snapshot = new ThreadLocal<>();
+    private final ConcurrentNavigableMap<GridSearchRowPointer, GridH2Row> tree;
 
     /** */
     private final boolean snapshotEnabled;
@@ -63,19 +59,11 @@ public class GridH2TreeIndex extends GridH2IndexBase implements Comparator<GridS
      * @param name Index name.
      * @param tbl Table.
      * @param pk If this index is primary key.
-     * @param keyCol Primary key column index.
-     * @param valCol Value column index.
-     * @param cols Index columns list.
+     * @param colsList Index columns list.
      */
     @SuppressWarnings("unchecked")
-    public GridH2TreeIndex(String name, GridH2Table tbl, boolean pk, int keyCol, int valCol, IndexColumn... cols) {
-        super(keyCol, valCol);
-        if (!pk) {
-            // For other indexes we add primary key at the end to avoid conflicts.
-            cols = Arrays.copyOf(cols, cols.length + 1);
-
-            cols[cols.length - 1] = tbl.indexColumn(keyCol, SortOrder.ASCENDING);
-        }
+    public GridH2TreeIndex(String name, GridH2Table tbl, boolean pk, List<IndexColumn> colsList) {
+        IndexColumn[] cols = colsList.toArray(new IndexColumn[colsList.size()]);
 
         IndexColumn.mapColumns(cols, tbl);
 
@@ -146,61 +134,25 @@ public class GridH2TreeIndex extends GridH2IndexBase implements Comparator<GridS
                 }
             };
         }
-    }
-
-    /**
-     * Closes index and releases resources.
-     */
-    public void close() {
-        if (tree instanceof Closeable)
-            U.closeQuiet((Closeable)tree);
-    }
-
-    /**
-     * Takes snapshot to be used in current thread. If argument is null it will be taken from current trees.
-     *
-     * @param s Map to be used as snapshot if not null.
-     * @return Taken snapshot or given argument back.
-     */
-    @SuppressWarnings("unchecked")
-    @Override public Object takeSnapshot(@Nullable Object s) {
-        if (!snapshotEnabled)
-            return null;
-
-        assert snapshot.get() == null;
-
-        if (s == null)
-            s = tree instanceof SnapTreeMap ? ((SnapTreeMap)tree).clone() :
-                ((GridOffHeapSnapTreeMap)tree).clone();
-
-        snapshot.set((ConcurrentNavigableMap<GridSearchRowPointer, GridH2Row>)s);
 
-        return s;
+        initDistributedJoinMessaging(tbl);
     }
 
-    /**
-     * Releases snapshot for current thread.
-     */
-    @Override public void releaseSnapshot() {
-        if (!snapshotEnabled)
-            return;
-
-        ConcurrentNavigableMap<GridSearchRowPointer, GridH2Row> s = snapshot.get();
-
-        snapshot.set(null);
+    /** {@inheritDoc} */
+    @Override protected Object doTakeSnapshot() {
+        assert snapshotEnabled;
 
-        if (s instanceof Closeable)
-            U.closeQuiet((Closeable)s);
+        return tree instanceof SnapTreeMap ?
+            ((SnapTreeMap)tree).clone() :
+            ((GridOffHeapSnapTreeMap)tree).clone();
     }
 
-    /**
-     * @return Snapshot for current thread if there is one.
-     */
-    private ConcurrentNavigableMap<GridSearchRowPointer, GridH2Row> treeForRead() {
+    /** {@inheritDoc} */
+    protected final ConcurrentNavigableMap<GridSearchRowPointer, GridH2Row> treeForRead() {
         if (!snapshotEnabled)
             return tree;
 
-        ConcurrentNavigableMap<GridSearchRowPointer, GridH2Row> res = snapshot.get();
+        ConcurrentNavigableMap<GridSearchRowPointer, GridH2Row> res = threadLocalSnapshot();
 
         if (res == null)
             res = tree;
@@ -209,19 +161,21 @@ public class GridH2TreeIndex extends GridH2IndexBase implements Comparator<GridS
     }
 
     /** {@inheritDoc} */
-    @Override public void close(Session ses) {
-        assert snapshot.get() == null;
+    @Override public void destroy() {
+        assert threadLocalSnapshot() == null;
+
+        if (tree instanceof AutoCloseable)
+            U.closeQuiet((AutoCloseable)tree);
 
-        if (tree instanceof Closeable)
-            U.closeQuiet((Closeable)tree);
+        super.destroy();
     }
 
     /** {@inheritDoc} */
     @Override public long getRowCount(@Nullable Session ses) {
-        IndexingQueryFilter f = filters.get();
+        IndexingQueryFilter f = threadLocalFilter();
 
         // Fast path if we don't need to perform any filtering.
-        if (f == null || f.forSpace(((GridH2Table)getTable()).spaceName()) == null)
+        if (f == null || f.forSpace((getTable()).spaceName()) == null)
             return treeForRead().size();
 
         Iterator<GridH2Row> iter = doFind(null, false, null);
@@ -269,13 +223,17 @@ public class GridH2TreeIndex extends GridH2IndexBase implements Comparator<GridS
     }
 
     /** {@inheritDoc} */
-    @Override public double getCost(Session ses, int[] masks, TableFilter filter, SortOrder sortOrder) {
-        return getCostRangeIndex(masks, getRowCountApproximation(), filter, sortOrder);
+    @Override public double getCost(Session ses, int[] masks, TableFilter[] filters, int filter, SortOrder sortOrder) {
+        long rowCnt = getRowCountApproximation();
+        double baseCost = getCostRangeIndex(masks, rowCnt, filters, filter, sortOrder, false);
+        int mul = getDistributedMultiplier(ses, filters, filter);
+
+        return mul * baseCost;
     }
 
     /** {@inheritDoc} */
     @Override public boolean canFindNext() {
-        return true;
+        return false;
     }
 
     /** {@inheritDoc} */
@@ -309,10 +267,20 @@ public class GridH2TreeIndex extends GridH2IndexBase implements Comparator<GridS
      * @return Iterator over rows in given range.
      */
     @SuppressWarnings("unchecked")
-    private Iterator<GridH2Row> doFind(@Nullable SearchRow first, boolean includeFirst,
-        @Nullable SearchRow last) {
+    private Iterator<GridH2Row> doFind(@Nullable SearchRow first, boolean includeFirst, @Nullable SearchRow last) {
         ConcurrentNavigableMap<GridSearchRowPointer, GridH2Row> t = treeForRead();
 
+        return doFind0(t, first, includeFirst, last, threadLocalFilter());
+    }
+
+    /** {@inheritDoc} */
+    @Override protected final Iterator<GridH2Row> doFind0(
+        ConcurrentNavigableMap<GridSearchRowPointer, GridH2Row> t,
+        @Nullable SearchRow first,
+        boolean includeFirst,
+        @Nullable SearchRow last,
+        IndexingQueryFilter filter
+    ) {
         includeFirst &= first != null;
 
         NavigableMap<GridSearchRowPointer, GridH2Row> range = subTree(t, comparable(first, includeFirst ? -1 : 1),
@@ -321,7 +289,7 @@ public class GridH2TreeIndex extends GridH2IndexBase implements Comparator<GridS
         if (range == null)
             return new GridEmptyIterator<>();
 
-        return filter(range.values().iterator());
+        return filter(range.values().iterator(), filter);
     }
 
     /**
@@ -381,21 +349,12 @@ public class GridH2TreeIndex extends GridH2IndexBase implements Comparator<GridS
 
     /** {@inheritDoc} */
     @Override public boolean canGetFirstOrLast() {
-        return true;
+        return false;
     }
 
     /** {@inheritDoc} */
     @Override public Cursor findFirstOrLast(Session ses, boolean first) {
-        ConcurrentNavigableMap<GridSearchRowPointer, GridH2Row> tree = treeForRead();
-
-        Iterator<GridH2Row> iter = filter(first ? tree.values().iterator() : tree.descendingMap().values().iterator());
-
-        GridSearchRowPointer res = null;
-
-        if (iter.hasNext())
-            res = iter.next();
-
-        return new SingleRowCursor((Row)res);
+        throw DbException.throwInternalError();
     }
 
     /** {@inheritDoc} */
@@ -503,11 +462,8 @@ public class GridH2TreeIndex extends GridH2IndexBase implements Comparator<GridS
     @Override public GridH2TreeIndex rebuild() throws InterruptedException {
         IndexColumn[] cols = getIndexColumns();
 
-        if (!getIndexType().isUnique())
-            cols = Arrays.copyOf(cols, cols.length - 1);
-
-        GridH2TreeIndex idx = new GridH2TreeIndex(getName(), (GridH2Table)getTable(), getIndexType().isUnique(),
-            keyCol, valCol, cols);
+        GridH2TreeIndex idx = new GridH2TreeIndex(getName(), getTable(),
+            getIndexType().isUnique(), F.asList(cols));
 
         Thread thread = Thread.currentThread();
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Utils.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Utils.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Utils.java
deleted file mode 100644
index aafcfcc..0000000
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Utils.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.ignite.internal.processors.query.h2.opt;
-
-import java.sql.Timestamp;
-import java.util.Calendar;
-import java.util.GregorianCalendar;
-import org.h2.value.ValueTimestamp;
-
-/**
- *
- */
-@SuppressWarnings({"JavaAbbreviationUsage", "GridBracket"})
-public class GridH2Utils {
-    /** Copy/pasted from org.h2.util.DateTimeUtils */
-    private static final int SHIFT_YEAR = 9;
-
-    /** Copy/pasted from org.h2.util.DateTimeUtils */
-    private static final int SHIFT_MONTH = 5;
-
-    /** Static calendar. */
-    private static final Calendar staticCalendar = Calendar.getInstance();
-
-    /** */
-    private static final ThreadLocal<Calendar> localCalendar = new ThreadLocal<>();
-
-    /**
-     * @return The instance of calendar for local thread.
-     */
-    public static Calendar getLocalCalendar() {
-        Calendar res = localCalendar.get();
-
-        if (res == null) {
-            res = (Calendar)staticCalendar.clone();
-
-            localCalendar.set(res);
-        }
-
-        return res;
-    }
-
-    /**
-     * Get or create a timestamp value for the given timestamp.
-     *
-     * Copy/pasted from org.h2.value.ValueTimestamp#get(java.sql.Timestamp)
-     *
-     * @param timestamp The timestamp.
-     * @return The value.
-     */
-    public static ValueTimestamp toValueTimestamp(Timestamp timestamp) {
-        long ms = timestamp.getTime();
-        long nanos = timestamp.getNanos() % 1000000;
-
-        Calendar calendar = getLocalCalendar();
-
-        calendar.clear();
-        calendar.setTimeInMillis(ms);
-
-        long dateValue = dateValueFromCalendar(calendar);
-
-        nanos += nanosFromCalendar(calendar);
-
-        return ValueTimestamp.fromDateValueAndNanos(dateValue, nanos);
-    }
-
-    /**
-     * Calculate the nanoseconds since midnight from a given calendar.
-     *
-     * Copy/pasted from org.h2.util.DateTimeUtils#nanosFromCalendar(java.util.Calendar).
-     *
-     * @param cal The calendar.
-     * @return Nanoseconds.
-     */
-    private static long nanosFromCalendar(Calendar cal) {
-        int h = cal.get(Calendar.HOUR_OF_DAY);
-        int m = cal.get(Calendar.MINUTE);
-        int s = cal.get(Calendar.SECOND);
-        int millis = cal.get(Calendar.MILLISECOND);
-
-        return ((((((h * 60L) + m) * 60) + s) * 1000) + millis) * 1000000;
-    }
-
-    /**
-     * Calculate the date value from a given calendar.
-     *
-     * Copy/pasted from org.h2.util.DateTimeUtils#dateValueFromCalendar(java.util.Calendar)
-     *
-     * @param cal The calendar.
-     * @return The date value.
-     */
-    private static long dateValueFromCalendar(Calendar cal) {
-        int year, month, day;
-
-        year = getYear(cal);
-        month = cal.get(Calendar.MONTH) + 1;
-        day = cal.get(Calendar.DAY_OF_MONTH);
-
-        return ((long) year << SHIFT_YEAR) | (month << SHIFT_MONTH) | day;
-    }
-
-    /**
-     * Get the year (positive or negative) from a calendar.
-     *
-     * Copy/pasted from org.h2.util.DateTimeUtils#getYear(java.util.Calendar)
-     *
-     * @param calendar The calendar.
-     * @return The year.
-     */
-    private static int getYear(Calendar calendar) {
-        int year = calendar.get(Calendar.YEAR);
-
-        if (calendar.get(Calendar.ERA) == GregorianCalendar.BC) {
-            year = 1 - year;
-        }
-
-        return year;
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2ValueCacheObject.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2ValueCacheObject.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2ValueCacheObject.java
index 29f9675..80e8504 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2ValueCacheObject.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2ValueCacheObject.java
@@ -27,6 +27,7 @@ import org.apache.ignite.internal.processors.cache.CacheObject;
 import org.apache.ignite.internal.processors.cache.CacheObjectContext;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.h2.message.DbException;
+import org.h2.util.JdbcUtils;
 import org.h2.util.Utils;
 import org.h2.value.CompareMode;
 import org.h2.value.Value;
@@ -117,7 +118,7 @@ public class GridH2ValueCacheObject extends Value {
         }
 
         // For user-provided and array types.
-        return Utils.serialize(obj, null);
+        return JdbcUtils.serialize(obj, null);
     }
 
     /** {@inheritDoc} */

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneIndex.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneIndex.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneIndex.java
index 957e5f6..716c9cb 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneIndex.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneIndex.java
@@ -17,7 +17,6 @@
 
 package org.apache.ignite.internal.processors.query.h2.opt;
 
-import java.io.Closeable;
 import java.io.IOException;
 import java.util.Collection;
 import java.util.concurrent.atomic.AtomicLong;
@@ -51,7 +50,7 @@ import org.apache.lucene.search.ScoreDoc;
 import org.apache.lucene.search.TermRangeFilter;
 import org.apache.lucene.search.TopDocs;
 import org.apache.lucene.util.Version;
-import org.h2.util.Utils;
+import org.h2.util.JdbcUtils;
 import org.jetbrains.annotations.Nullable;
 
 import static org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing.KEY_FIELD_NAME;
@@ -60,7 +59,7 @@ import static org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing.VA
 /**
  * Lucene fulltext index.
  */
-public class GridLuceneIndex implements Closeable {
+public class GridLuceneIndex implements AutoCloseable {
     /** Field name for string representation of value. */
     public static final String VAL_STR_FIELD_NAME = "_gg_val_str__";
 
@@ -363,7 +362,7 @@ public class GridLuceneIndex implements Closeable {
         @SuppressWarnings("unchecked")
         private <Z> Z unmarshall(byte[] bytes, ClassLoader ldr) throws IgniteCheckedException {
             if (coctx == null) // For tests.
-                return (Z)Utils.deserialize(bytes, null);
+                return (Z)JdbcUtils.deserialize(bytes, null);
 
             return (Z)coctx.processor().unmarshal(coctx, bytes, ldr);
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlAlias.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlAlias.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlAlias.java
index 245b88e..3fb6f3c 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlAlias.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlAlias.java
@@ -52,6 +52,18 @@ public class GridSqlAlias extends GridSqlElement {
         this.alias = alias;
     }
 
+    /**
+     * @param el Element.
+     * @return Unwrapped from alias element.
+     */
+    public static GridSqlElement unwrap(GridSqlElement el) {
+        el = el instanceof GridSqlAlias ? el.child() : el;
+
+        assert el != null;
+
+        return el;
+    }
+
     /** {@inheritDoc} */
     @Override public String getSQL() {
         return child().getSQL() + (useAs ? " AS " : " ") + Parser.quoteIdentifier(alias);

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlColumn.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlColumn.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlColumn.java
index aca1398..6ef4446 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlColumn.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlColumn.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.internal.processors.query.h2.sql;
 
 import java.util.Collections;
+import org.h2.table.Column;
 
 /**
  * Column.
@@ -32,12 +33,16 @@ public class GridSqlColumn extends GridSqlElement implements GridSqlValue {
     /** SQL from original query. May be qualified or unqualified column name. */
     private final String sqlText;
 
+    /** */
+    private Column col;
+
     /**
+     * @param col Column.
      * @param from From.
      * @param name Name.
      * @param sqlText Text.
      */
-    public GridSqlColumn(GridSqlElement from, String name, String sqlText) {
+    public GridSqlColumn(Column col, GridSqlElement from, String name, String sqlText) {
         super(Collections.<GridSqlElement>emptyList());
 
         assert sqlText != null;
@@ -45,13 +50,7 @@ public class GridSqlColumn extends GridSqlElement implements GridSqlValue {
         expressionInFrom = from;
         colName = name;
         this.sqlText = sqlText;
-    }
-
-    /**
-     * @return Simple unqualified column with only name.
-     */
-    public GridSqlColumn simplify() {
-        return new GridSqlColumn(null, colName, colName);
+        this.col = col;
     }
 
     /**
@@ -72,4 +71,11 @@ public class GridSqlColumn extends GridSqlElement implements GridSqlValue {
     public GridSqlElement expressionInFrom() {
         return expressionInFrom;
     }
+
+    /**
+     * @return H2 Column.
+     */
+    public Column column() {
+        return col;
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlElement.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlElement.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlElement.java
index 57d3c57..d95c14a 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlElement.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlElement.java
@@ -119,4 +119,15 @@ public abstract class GridSqlElement implements Iterable<GridSqlElement> {
     @Override public String toString() {
         return getSQL();
     }
+
+    /** {@inheritDoc} */
+    @Override public int hashCode() {
+        throw new IllegalStateException();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean equals(Object o) {
+        return this == o || (!(o == null || getClass() != o.getClass()) &&
+            children.equals(((GridSqlElement)o).children));
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlOperation.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlOperation.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlOperation.java
index 18d8bdf..737c5b1 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlOperation.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlOperation.java
@@ -63,7 +63,7 @@ public class GridSqlOperation extends GridSqlElement {
     /**
      * @return Operation type.
      */
-    public GridSqlOperationType opType() {
+    public GridSqlOperationType operationType() {
         return opType;
     }
 


[03/50] [abbrv] ignite git commit: IGNITE-3515: NullPointerException when stopping IgniteSemaphore and no method has been called previously to initialize semaphore with initializeSemaphore(). Reviewed by Denis Magda.

Posted by vo...@apache.org.
IGNITE-3515: NullPointerException when stopping IgniteSemaphore and no method has been called previously to initialize semaphore with initializeSemaphore().
Reviewed by Denis Magda.


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

Branch: refs/heads/ignite-3553
Commit: ab4963a686ea7ca560604079c52b8939b605cfcf
Parents: a989f04
Author: Krome Plasma <re...@some.invalid.address>
Authored: Fri Jul 22 14:53:39 2016 +0300
Committer: Denis Magda <dm...@gridgain.com>
Committed: Fri Jul 22 14:53:39 2016 +0300

----------------------------------------------------------------------
 .../datastructures/GridCacheSemaphoreImpl.java  | 108 ++++++++++++-------
 .../IgniteSemaphoreAbstractSelfTest.java        |  25 ++++-
 2 files changed, 94 insertions(+), 39 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/ab4963a6/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheSemaphoreImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheSemaphoreImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheSemaphoreImpl.java
index 8f196be..a11c79d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheSemaphoreImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheSemaphoreImpl.java
@@ -36,6 +36,7 @@ import org.apache.ignite.IgniteInterruptedException;
 import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.IgniteSemaphore;
 import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.IgniteInterruptedCheckedException;
 import org.apache.ignite.internal.IgnitionEx;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.IgniteInternalCache;
@@ -51,11 +52,11 @@ import static org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC;
 import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_READ;
 
 /**
- * Cache semaphore implementation based on AbstractQueuedSynchronizer.
- * Current implementation supports only unfair semaphores.
- * If any node fails after acquiring permissions on cache semaphore, there are two different behaviors controlled with the
- * parameter failoverSafe. If this parameter is true, other nodes can reacquire permits that were acquired by the failing node.
- * In case this parameter is false, IgniteInterruptedException is called on every node waiting on this semaphore.
+ * Cache semaphore implementation based on AbstractQueuedSynchronizer. Current implementation supports only unfair
+ * semaphores. If any node fails after acquiring permissions on cache semaphore, there are two different behaviors
+ * controlled with the parameter failoverSafe. If this parameter is true, other nodes can reacquire permits that were
+ * acquired by the failing node. In case this parameter is false, IgniteInterruptedException is called on every node
+ * waiting on this semaphore.
  */
 public final class GridCacheSemaphoreImpl implements GridCacheSemaphoreEx, Externalizable {
     /** */
@@ -104,8 +105,7 @@ public final class GridCacheSemaphoreImpl implements GridCacheSemaphoreEx, Exter
     }
 
     /**
-     * Synchronization implementation for semaphore.
-     * Uses AQS state to represent permits.
+     * Synchronization implementation for semaphore. Uses AQS state to represent permits.
      */
     final class Sync extends AbstractQueuedSynchronizer {
         private static final long serialVersionUID = 1192457210091910933L;
@@ -155,10 +155,10 @@ public final class GridCacheSemaphoreImpl implements GridCacheSemaphoreEx, Exter
          * Get number of permits for node.
          *
          * @param nodeID Node ID.
-         * @return Number of permits node has acquired at this semaphore. Can be less than 0 if
-         * more permits were released than acquired on node.
+         * @return Number of permits node has acquired at this semaphore. Can be less than 0 if more permits were
+         * released than acquired on node.
          */
-        public int getPermitsForNode(UUID nodeID){
+        public int getPermitsForNode(UUID nodeID) {
             return nodeMap.containsKey(nodeID) ? nodeMap.get(nodeID) : 0;
         }
 
@@ -182,12 +182,10 @@ public final class GridCacheSemaphoreImpl implements GridCacheSemaphoreEx, Exter
         }
 
         /**
-         * Set a flag indicating that it is not safe to continue using this semaphore.
-         * This is the case only if one of two things happened:
-         * 1. A node that previously acquired on this semaphore failed and
-         * semaphore is created in non-failoversafe mode;
-         * 2. Local node failed (is closed), so any any threads on this node
-         * waiting to acquire are notified, and semaphore is not safe to be used anymore.
+         * Set a flag indicating that it is not safe to continue using this semaphore. This is the case only if one of
+         * two things happened: 1. A node that previously acquired on this semaphore failed and semaphore is created in
+         * non-failoversafe mode; 2. Local node failed (is closed), so any any threads on this node waiting to acquire
+         * are notified, and semaphore is not safe to be used anymore.
          *
          * @return True is semaphore is not safe to be used anymore.
          */
@@ -195,11 +193,12 @@ public final class GridCacheSemaphoreImpl implements GridCacheSemaphoreEx, Exter
             return broken;
         }
 
-        /** Flag indicating that a node failed and it is not safe to continue using this semaphore.
-         * Any attempt to acquire on broken semaphore will result in {@linkplain IgniteInterruptedException}.
+        /**
+         * Flag indicating that a node failed and it is not safe to continue using this semaphore. Any attempt to
+         * acquire on broken semaphore will result in {@linkplain IgniteInterruptedException}.
          *
          * @param broken True if semaphore should not be used anymore.
-         * */
+         */
         protected void setBroken(boolean broken) {
             this.broken = broken;
         }
@@ -211,9 +210,9 @@ public final class GridCacheSemaphoreImpl implements GridCacheSemaphoreEx, Exter
          * @return Negative number if thread should block, positive if thread successfully acquires permits.
          */
         final int nonfairTryAcquireShared(int acquires) {
-            for (;;) {
+            for (; ; ) {
                 // If broken, return immediately, exception will be thrown anyway.
-                if(broken)
+                if (broken)
                     return 1;
 
                 int available = getState();
@@ -238,9 +237,9 @@ public final class GridCacheSemaphoreImpl implements GridCacheSemaphoreEx, Exter
             if (releases == 0)
                 return true;
 
-            for (;;) {
+            for (; ; ) {
                 // If broken, return immediately, exception will be thrown anyway.
-                if(broken)
+                if (broken)
                     return true;
 
                 int cur = getState();
@@ -261,9 +260,9 @@ public final class GridCacheSemaphoreImpl implements GridCacheSemaphoreEx, Exter
          * @return Number of permits to drain.
          */
         final int drainPermits() {
-            for (;;) {
+            for (; ; ) {
                 // If broken, return immediately, exception will be thrown anyway.
-                if(broken)
+                if (broken)
                     return 1;
 
                 int current = getState();
@@ -288,7 +287,7 @@ public final class GridCacheSemaphoreImpl implements GridCacheSemaphoreEx, Exter
                         @Override public Boolean call() throws Exception {
                             try (IgniteInternalTx tx = CU.txStartInternal(ctx,
                                 semView,
-                                    PESSIMISTIC, REPEATABLE_READ)
+                                PESSIMISTIC, REPEATABLE_READ)
                             ) {
                                 GridCacheSemaphoreState val = semView.get(key);
 
@@ -304,11 +303,11 @@ public final class GridCacheSemaphoreImpl implements GridCacheSemaphoreEx, Exter
                                     if (!draining) {
                                         UUID nodeID = ctx.localNodeId();
 
-                                        Map<UUID,Integer> map = val.getWaiters();
+                                        Map<UUID, Integer> map = val.getWaiters();
 
                                         int waitingCnt = expVal - newVal;
 
-                                        if(map.containsKey(nodeID))
+                                        if (map.containsKey(nodeID))
                                             waitingCnt += map.get(nodeID);
 
                                         map.put(nodeID, waitingCnt);
@@ -370,9 +369,9 @@ public final class GridCacheSemaphoreImpl implements GridCacheSemaphoreEx, Exter
                                     throw new IgniteCheckedException("Failed to find semaphore with given name: " +
                                         name);
 
-                                Map<UUID,Integer> map = val.getWaiters();
+                                Map<UUID, Integer> map = val.getWaiters();
 
-                                if(!map.containsKey(nodeId)){
+                                if (!map.containsKey(nodeId)) {
                                     tx.rollback();
 
                                     return false;
@@ -380,7 +379,7 @@ public final class GridCacheSemaphoreImpl implements GridCacheSemaphoreEx, Exter
 
                                 int numPermits = map.get(nodeId);
 
-                                if(numPermits > 0)
+                                if (numPermits > 0)
                                     val.setCount(val.getCount() + numPermits);
 
                                 map.remove(nodeId);
@@ -552,7 +551,42 @@ public final class GridCacheSemaphoreImpl implements GridCacheSemaphoreEx, Exter
         }
     }
 
+    /** {@inheritDoc} */
     @Override public void stop() {
+        if (initGuard.get()) {
+            try {
+                // Wait while initialization is in progress.
+                U.await(initLatch);
+            }
+            catch (IgniteInterruptedCheckedException e) {
+                if (log.isDebugEnabled())
+                    log.error("Failed waiting while initialization is completed.", e);
+            }
+        }
+        else {
+            // Preventing concurrent initialization.
+            if (initGuard.compareAndSet(false, true)) {
+                initLatch.countDown();
+
+                if (log.isDebugEnabled())
+                    log.debug("Semaphore wasn't initialized. Prevented further initialization.");
+
+                return;
+            }
+            else {
+                try {
+                    // Wait while initialization is in progress.
+                    U.await(initLatch);
+                }
+                catch (IgniteInterruptedCheckedException e) {
+                    if (log.isDebugEnabled())
+                        log.error("Failed waiting while initialization is completed.", e);
+                }
+            }
+        }
+
+        assert sync != null;
+
         sync.setBroken(true);
 
         // Try to notify any waiting threads.
@@ -580,7 +614,7 @@ public final class GridCacheSemaphoreImpl implements GridCacheSemaphoreEx, Exter
 
             sync.acquireSharedInterruptibly(permits);
 
-            if(isBroken())
+            if (isBroken())
                 throw new InterruptedException();
         }
         catch (IgniteCheckedException e) {
@@ -697,7 +731,7 @@ public final class GridCacheSemaphoreImpl implements GridCacheSemaphoreEx, Exter
 
             boolean result = sync.nonfairTryAcquireShared(1) >= 0;
 
-            if(isBroken())
+            if (isBroken())
                 throw new InterruptedException();
 
             return result;
@@ -722,7 +756,7 @@ public final class GridCacheSemaphoreImpl implements GridCacheSemaphoreEx, Exter
 
             boolean result = sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
 
-            if(isBroken())
+            if (isBroken())
                 throw new InterruptedException();
 
             return result;
@@ -789,9 +823,9 @@ public final class GridCacheSemaphoreImpl implements GridCacheSemaphoreEx, Exter
         try {
             initializeSemaphore();
 
-            boolean result =  sync.tryAcquireSharedNanos(permits, unit.toNanos(timeout));
+            boolean result = sync.tryAcquireSharedNanos(permits, unit.toNanos(timeout));
 
-            if(isBroken())
+            if (isBroken())
                 throw new InterruptedException();
 
             return result;
@@ -859,7 +893,7 @@ public final class GridCacheSemaphoreImpl implements GridCacheSemaphoreEx, Exter
     }
 
     /** {@inheritDoc} */
-    @Override public boolean isBroken(){
+    @Override public boolean isBroken() {
         ctx.kernalContext().gateway().readLock();
 
         try {

http://git-wip-us.apache.org/repos/asf/ignite/blob/ab4963a6/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/IgniteSemaphoreAbstractSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/IgniteSemaphoreAbstractSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/IgniteSemaphoreAbstractSelfTest.java
index 200e276..5241dd1 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/IgniteSemaphoreAbstractSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/IgniteSemaphoreAbstractSelfTest.java
@@ -30,6 +30,7 @@ import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteCompute;
 import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.IgniteSemaphore;
+import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.IgniteInternalFuture;
 import org.apache.ignite.internal.IgniteKernal;
@@ -39,6 +40,7 @@ import org.apache.ignite.lang.IgniteCallable;
 import org.apache.ignite.lang.IgniteFuture;
 import org.apache.ignite.resources.IgniteInstanceResource;
 import org.apache.ignite.resources.LoggerResource;
+import org.apache.ignite.testframework.GridStringLogger;
 import org.apache.ignite.testframework.GridTestUtils;
 import org.jetbrains.annotations.Nullable;
 import org.junit.Rule;
@@ -234,6 +236,25 @@ public abstract class IgniteSemaphoreAbstractSelfTest extends IgniteAtomicsAbstr
     /**
      * @throws Exception If failed.
      */
+    public void testSemaphoreClosing() throws Exception {
+        IgniteConfiguration cfg;
+        GridStringLogger stringLogger;
+
+        stringLogger = new GridStringLogger();
+
+        cfg = optimize(getConfiguration("npeGrid"));
+        cfg.setGridLogger(stringLogger);
+
+        try (Ignite ignite = startGrid(cfg.getGridName(), cfg)) {
+            ignite.semaphore("semaphore", 1, true, true);
+        }
+
+        assertFalse(stringLogger.toString().contains(NullPointerException.class.getName()));
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
     private void checkSemaphoreSerialization() throws Exception {
         final IgniteSemaphore sem = grid(0).semaphore("semaphore", -gridCount() + 1, true, true);
 
@@ -276,8 +297,8 @@ public abstract class IgniteSemaphoreAbstractSelfTest extends IgniteAtomicsAbstr
     }
 
     /**
-     * This method only checks if parameter of new semaphore is initialized properly.
-     * For tests considering failure recovery see @GridCachePartitionedNodeFailureSelfTest.
+     * This method only checks if parameter of new semaphore is initialized properly. For tests considering failure
+     * recovery see
      *
      * @throws Exception Exception.
      */


[47/50] [abbrv] ignite git commit: Release Notes 1.7

Posted by vo...@apache.org.
Release Notes 1.7


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

Branch: refs/heads/ignite-3553
Commit: a7cf12491203c4aa3106a1b27e2d23a7e014d154
Parents: 5f4563c
Author: Anton Vinogradov <av...@apache.org>
Authored: Wed Jul 27 18:06:23 2016 +0300
Committer: Anton Vinogradov <av...@apache.org>
Committed: Wed Jul 27 18:06:23 2016 +0300

----------------------------------------------------------------------
 RELEASE_NOTES.txt | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/a7cf1249/RELEASE_NOTES.txt
----------------------------------------------------------------------
diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt
index cef51bd..60bb248 100644
--- a/RELEASE_NOTES.txt
+++ b/RELEASE_NOTES.txt
@@ -1,6 +1,23 @@
 Apache Ignite Release Notes
 ===========================
 
+Apache Ignite In-Memory Data Fabric 1.7
+---------------------------------------
+Ignite .NET:
+* Added NuGet distribution.
+* Fixed a bug in IBinaryObjectBuilder causing type name to be lost.
+
+Ignite C++:
+* Fixed string deserialization performance issue.
+
+Ignite:
+* DR: Fixed a bug causing retention of unused file store data.
+* Fixed CountDownLatch recreation.
+* Node can be assigned as primary only after preloading is finished.
+* ODBC: Added decimal type support.
+* Supported distributed SQL JOIN.
+* Visor: Fixed data nodes calculation when node filter is set for cache.
+
 Apache Ignite In-Memory Data Fabric 1.6
 ---------------------------------------
 Ignite .NET:


[39/50] [abbrv] ignite git commit: IGNITE-3561: .NET: Improved distributed joins API for LINQ, added more tests. This closes #893. This closes #900.

Posted by vo...@apache.org.
IGNITE-3561: .NET: Improved distributed joins API for LINQ, added more tests. This closes #893. This closes #900.


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

Branch: refs/heads/ignite-3553
Commit: 9a8083860ed61dde981cb6da65de869dc4205ae0
Parents: 42f42ea
Author: Pavel Tupitsyn <pt...@apache.org>
Authored: Wed Jul 27 13:42:30 2016 +0300
Committer: vozerov-gridgain <vo...@gridgain.com>
Committed: Wed Jul 27 13:42:30 2016 +0300

----------------------------------------------------------------------
 .../Cache/Query/CacheLinqTest.cs                | 55 ++++++++++--
 .../Cache/Query/CacheQueriesTest.cs             | 29 ++++---
 .../Apache.Ignite.Linq.csproj                   |  1 +
 .../Apache.Ignite.Linq/CacheExtensions.cs       | 23 +----
 .../Apache.Ignite.Linq/Impl/CacheQueryable.cs   | 15 ++--
 .../dotnet/Apache.Ignite.Linq/QueryOptions.cs   | 91 ++++++++++++++++++++
 6 files changed, 165 insertions(+), 49 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/9a808386/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 f76a74c..679f03a 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
@@ -36,7 +36,6 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
     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;
 
@@ -955,7 +954,12 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
             var cache = GetPersonCache();
 
             // Check regular query
-            var query = (ICacheQueryable) cache.AsCacheQueryable(true, null, 999, false, true).Where(x => x.Key > 10);
+            var query = (ICacheQueryable) cache.AsCacheQueryable(new QueryOptions
+            {
+                Local = true,
+                PageSize = 999,
+                EnforceJoinOrder = true
+            }).Where(x => x.Key > 10);
 
             Assert.AreEqual(cache.Name, query.CacheName);
             Assert.AreEqual(cache.Ignite, query.Ignite);
@@ -983,14 +987,10 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
             Assert.IsFalse(fq.EnforceJoinOrder);
 
             // Check distributed joins flag propagation
-            var distrQuery = cache.AsCacheQueryable(true, null, 999, true, true).Where(x => x.Key > 10);
+            var distrQuery = cache.AsCacheQueryable(new QueryOptions {EnableDistributedJoins = 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>
@@ -1022,6 +1022,45 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
         }
 
         /// <summary>
+        /// Tests the distributed joins.
+        /// </summary>
+        [Test]
+        public void TestDistributedJoins()
+        {
+            var ignite = Ignition.GetIgnite();
+
+            // Create and populate partitioned caches
+            var personCache = ignite.CreateCache<int, Person>(new CacheConfiguration("partitioned_persons",
+                new QueryEntity(typeof(int), typeof(Person))));
+
+            personCache.PutAll(GetSecondPersonCache().ToDictionary(x => x.Key, x => x.Value));
+
+            var roleCache = ignite.CreateCache<int, Role>(new CacheConfiguration("partitioned_roles",
+                    new QueryEntity(typeof(int), typeof(Role))));
+
+            roleCache.PutAll(GetRoleCache().ToDictionary(x => x.Key.Foo, x => x.Value));
+
+            // Test non-distributed join: returns partial results
+            var persons = personCache.AsCacheQueryable();
+            var roles = roleCache.AsCacheQueryable();
+
+            var res = persons.Join(roles, person => person.Key - PersonCount, role => role.Key, (person, role) => role)
+                .ToArray();
+
+            Assert.Greater(res.Length, 0);
+            Assert.Less(res.Length, RoleCount);
+
+            // Test distributed join: returns complete results
+            persons = personCache.AsCacheQueryable(new QueryOptions {EnableDistributedJoins = true});
+            roles = roleCache.AsCacheQueryable(new QueryOptions {EnableDistributedJoins = true});
+
+            res = persons.Join(roles, person => person.Key - PersonCount, role => role.Key, (person, role) => role)
+                .ToArray();
+
+            Assert.AreEqual(RoleCount, res.Length);
+        }
+
+        /// <summary>
         /// Gets the person cache.
         /// </summary>
         /// <returns></returns>

http://git-wip-us.apache.org/repos/asf/ignite/blob/9a808386/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 7bfd202..a8ffe13 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
@@ -552,31 +552,36 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
         [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))
+                        new QueryEntity(typeof(int), typeof(QueryPerson))
                         {
                             Fields = new[] {new QueryField("age", typeof(int))}
                         }
                     }
                 });
 
-            cache[1] = new QueryPerson("Test", 150);
+            const int count = 100;
 
-            // Distributed joins disabled: query works
-            var qry = new SqlQuery(typeof(QueryPerson), "age < 50");
-            Assert.AreEqual(0, cache.Query(qry).GetAll().Count);
+            cache.PutAll(Enumerable.Range(0, count).ToDictionary(x => x, x => new QueryPerson("Name" + x, x)));
 
-            // 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);
+            // Test non-distributed join: returns partial results
+            var sql = "select T0.Age from QueryPerson as T0 " +
+                      "inner join QueryPerson as T1 on ((? - T1.Age - 1) = T0._key)";
+
+            var res = cache.QueryFields(new SqlFieldsQuery(sql, count)).GetAll().Distinct().Count();
+
+            Assert.Greater(res, 0);
+            Assert.Less(res, count);
+
+            // Test distributed join: returns complete results
+            res = cache.QueryFields(new SqlFieldsQuery(sql, count) {EnableDistributedJoins = true})
+                .GetAll().Distinct().Count();
+
+            Assert.AreEqual(count, res);
         }
 
         /// <summary>

http://git-wip-us.apache.org/repos/asf/ignite/blob/9a808386/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 3abfa56..72e5f68 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj
@@ -71,6 +71,7 @@
     <Compile Include="Impl\ExpressionWalker.cs" />
     <Compile Include="Package-Info.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="QueryOptions.cs" />
   </ItemGroup>
   <ItemGroup>
     <None Include="Apache.Ignite.Linq.nuspec" />

http://git-wip-us.apache.org/repos/asf/ignite/blob/9a808386/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 e6d585c..4b536f4 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheExtensions.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheExtensions.cs
@@ -20,7 +20,6 @@ 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>
@@ -93,7 +92,7 @@ namespace Apache.Ignite.Linq
         public static IQueryable<ICacheEntry<TKey, TValue>> AsCacheQueryable<TKey, TValue>(
             this ICache<TKey, TValue> cache, bool local, string tableName)
         {
-            return cache.AsCacheQueryable(local, tableName, SqlFieldsQuery.DfltPageSize, false, false);
+            return cache.AsCacheQueryable(new QueryOptions {Local = local, TableName = tableName});
         }
 
         /// <summary>
@@ -108,28 +107,14 @@ namespace Apache.Ignite.Linq
         /// <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>
+        /// <param name="queryOptions">The query options.</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)
+            this ICache<TKey, TValue> cache, QueryOptions queryOptions)
         {
-            return new CacheQueryable<TKey, TValue>(cache, local, tableName, pageSize, enableDistributedJoins,
-                enforceJoinOrder);
+            return new CacheQueryable<TKey, TValue>(cache, queryOptions);
         }
     }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/9a808386/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 7ade159..7372776 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryable.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryable.cs
@@ -30,17 +30,12 @@ namespace Apache.Ignite.Linq.Impl
         /// Initializes a new instance of the <see cref="CacheQueryable{TKey, TValue}" /> class.
         /// </summary>
         /// <param name="cache">The cache.</param>
-        /// <param name="local">Local flag.</param>
-        /// <param name="tableName">Name of the table.</param>
-        /// <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)
+        /// <param name="queryOptions">The query options.</param>
+        public CacheQueryable(ICache<TKey, TValue> cache, QueryOptions queryOptions)
             : base(new CacheFieldsQueryProvider(CacheQueryParser.Instance,
-                new CacheFieldsQueryExecutor((ICacheInternal) cache, local, pageSize, enableDistributedJoins,
-                    enforceJoinOrder), cache.Ignite, cache.GetConfiguration(), tableName, typeof(TValue)))
+                new CacheFieldsQueryExecutor((ICacheInternal) cache, queryOptions.Local, queryOptions.PageSize,
+                    queryOptions.EnableDistributedJoins, queryOptions.EnforceJoinOrder), 
+                cache.Ignite, cache.GetConfiguration(), queryOptions.TableName, typeof(TValue)))
         {
             // No-op.
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/9a808386/modules/platforms/dotnet/Apache.Ignite.Linq/QueryOptions.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/QueryOptions.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/QueryOptions.cs
new file mode 100644
index 0000000..4fe3ee5
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/QueryOptions.cs
@@ -0,0 +1,91 @@
+\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.ComponentModel;
+    using Apache.Ignite.Core.Cache.Configuration;
+    using Apache.Ignite.Core.Cache.Query;
+
+    /// <summary>
+    /// Cache query options.
+    /// </summary>
+    public class QueryOptions
+    {
+        /// <summary> Default page size. </summary>
+        public const int DefaultPageSize = SqlFieldsQuery.DfltPageSize;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="QueryOptions"/> class.
+        /// </summary>
+        public QueryOptions()
+        {
+            PageSize = DefaultPageSize;
+        }
+
+        /// <summary>
+        /// Local flag. When set query will be executed only on local node, so only local 
+        /// entries will be returned as query result.
+        /// <para />
+        /// Defaults to <c>false</c>.
+        /// </summary>
+        public bool Local { get; set; }
+
+        /// <summary>
+        /// Page size, defaults to <see cref="DefaultPageSize"/>.
+        /// </summary>
+        [DefaultValue(DefaultPageSize)]
+        public int PageSize { get; set; }
+
+        /// <summary>
+        /// Gets or sets the 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 (null).
+        /// </summary>
+        /// <value>
+        /// The name of the table.
+        /// </value>
+        public string TableName { 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; }
+    }
+}


[23/50] [abbrv] ignite git commit: ignite-3563 Support distributedJoins flag in JDBC driver

Posted by vo...@apache.org.
ignite-3563 Support distributedJoins flag in JDBC driver


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

Branch: refs/heads/ignite-3553
Commit: 763f0676b4cef169f41f648b6e41b8bcb16203d9
Parents: fd50502
Author: agura <ag...@gridgain.com>
Authored: Mon Jul 25 12:31:24 2016 +0300
Committer: agura <ag...@gridgain.com>
Committed: Mon Jul 25 15:16:44 2016 +0300

----------------------------------------------------------------------
 .../jdbc2/JdbcDistributedJoinsQueryTest.java    | 319 +++++++++++++++++++
 .../jdbc/suite/IgniteJdbcDriverTestSuite.java   |   2 +
 .../org/apache/ignite/IgniteJdbcDriver.java     |  14 +-
 .../ignite/internal/jdbc2/JdbcConnection.java   |  12 +
 .../ignite/internal/jdbc2/JdbcQueryTask.java    |   9 +-
 .../ignite/internal/jdbc2/JdbcResultSet.java    |   4 +-
 .../ignite/internal/jdbc2/JdbcStatement.java    |   2 +-
 7 files changed, 357 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/763f0676/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDistributedJoinsQueryTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDistributedJoinsQueryTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDistributedJoinsQueryTest.java
new file mode 100644
index 0000000..53bfa73
--- /dev/null
+++ b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDistributedJoinsQueryTest.java
@@ -0,0 +1,319 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.jdbc2;
+
+import java.io.Serializable;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.query.annotations.QuerySqlField;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.ConnectorConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+import static org.apache.ignite.IgniteJdbcDriver.CFG_URL_PREFIX;
+import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+
+/**
+ * Tests for complex queries with distributed joins enabled (joins, etc.).
+ */
+public class JdbcDistributedJoinsQueryTest extends GridCommonAbstractTest {
+    /** IP finder. */
+    private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
+
+    /** JDBC URL. */
+    private static final String BASE_URL = CFG_URL_PREFIX + "distributedJoins=true@modules/clients/src/test/config/jdbc-config.xml";
+
+    /** Statement. */
+    private Statement stmt;
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(gridName);
+
+        CacheConfiguration<?,?> cache = defaultCacheConfiguration();
+
+        cache.setCacheMode(PARTITIONED);
+        cache.setBackups(1);
+        cache.setWriteSynchronizationMode(FULL_SYNC);
+        cache.setAtomicityMode(TRANSACTIONAL);
+        cache.setIndexedTypes(String.class, Organization.class, String.class, Person.class);
+
+        cfg.setCacheConfiguration(cache);
+
+        TcpDiscoverySpi disco = new TcpDiscoverySpi();
+
+        disco.setIpFinder(IP_FINDER);
+
+        cfg.setDiscoverySpi(disco);
+
+        cfg.setConnectorConfiguration(new ConnectorConfiguration());
+
+        return cfg;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        startGrids(3);
+
+        IgniteCache<String, Organization> orgCache = grid(0).cache(null);
+
+        assert orgCache != null;
+
+        orgCache.put("o1", new Organization(1, "A"));
+        orgCache.put("o2", new Organization(2, "B"));
+
+        IgniteCache<String, Person> personCache = grid(0).cache(null);
+
+        assert personCache != null;
+
+        personCache.put("p1", new Person(1, "John White", 25, 1));
+        personCache.put("p2", new Person(2, "Joe Black", 35, 1));
+        personCache.put("p3", new Person(3, "Mike Green", 40, 2));
+
+        Class.forName("org.apache.ignite.IgniteJdbcDriver");
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTestsStopped() throws Exception {
+        stopAllGrids();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() throws Exception {
+        stmt = DriverManager.getConnection(BASE_URL).createStatement();
+
+        assert stmt != null;
+        assert !stmt.isClosed();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        if (stmt != null) {
+            stmt.getConnection().close();
+            stmt.close();
+
+            assert stmt.isClosed();
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoin() throws Exception {
+        ResultSet rs = stmt.executeQuery(
+            "select p.id, p.name, o.name as orgName from Person p, Organization o where p.orgId = o.id");
+
+        assert rs != null;
+
+        int cnt = 0;
+
+        while (rs.next()) {
+            int id = rs.getInt("id");
+
+            if (id == 1) {
+                assert "John White".equals(rs.getString("name"));
+                assert "A".equals(rs.getString("orgName"));
+            }
+            else if (id == 2) {
+                assert "Joe Black".equals(rs.getString("name"));
+                assert "A".equals(rs.getString("orgName"));
+            }
+            else if (id == 3) {
+                assert "Mike Green".equals(rs.getString("name"));
+                assert "B".equals(rs.getString("orgName"));
+            }
+            else
+                assert false : "Wrong ID: " + id;
+
+            cnt++;
+        }
+
+        assertEquals(3, cnt);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoinWithoutAlias() throws Exception {
+        ResultSet rs = stmt.executeQuery(
+            "select p.id, p.name, o.name from Person p, Organization o where p.orgId = o.id");
+
+        assert rs != null;
+
+        int cnt = 0;
+
+        while (rs.next()) {
+            int id = rs.getInt(1);
+
+            if (id == 1) {
+                assert "John White".equals(rs.getString("name"));
+                assert "John White".equals(rs.getString(2));
+                assert "A".equals(rs.getString(3));
+            }
+            else if (id == 2) {
+                assert "Joe Black".equals(rs.getString("name"));
+                assert "Joe Black".equals(rs.getString(2));
+                assert "A".equals(rs.getString(3));
+            }
+            else if (id == 3) {
+                assert "Mike Green".equals(rs.getString("name"));
+                assert "Mike Green".equals(rs.getString(2));
+                assert "B".equals(rs.getString(3));
+            }
+            else
+                assert false : "Wrong ID: " + id;
+
+            cnt++;
+        }
+
+        assert cnt == 3;
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testIn() throws Exception {
+        ResultSet rs = stmt.executeQuery("select name from Person where age in (25, 35)");
+
+        assert rs != null;
+
+        int cnt = 0;
+
+        while (rs.next()) {
+            assert "John White".equals(rs.getString("name")) ||
+                "Joe Black".equals(rs.getString("name"));
+
+            cnt++;
+        }
+
+        assert cnt == 2;
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testBetween() throws Exception {
+        ResultSet rs = stmt.executeQuery("select name from Person where age between 24 and 36");
+
+        assert rs != null;
+
+        int cnt = 0;
+
+        while (rs.next()) {
+            assert "John White".equals(rs.getString("name")) ||
+                "Joe Black".equals(rs.getString("name"));
+
+            cnt++;
+        }
+
+        assert cnt == 2;
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCalculatedValue() throws Exception {
+        ResultSet rs = stmt.executeQuery("select age * 2 from Person");
+
+        assert rs != null;
+
+        int cnt = 0;
+
+        while (rs.next()) {
+            assert rs.getInt(1) == 50 ||
+                rs.getInt(1) == 70 ||
+                rs.getInt(1) == 80;
+
+            cnt++;
+        }
+
+        assert cnt == 3;
+    }
+
+    /**
+     * Person.
+     */
+    @SuppressWarnings("UnusedDeclaration")
+    private static class Person implements Serializable {
+        /** ID. */
+        @QuerySqlField
+        private final int id;
+
+        /** Name. */
+        @QuerySqlField(index = false)
+        private final String name;
+
+        /** Age. */
+        @QuerySqlField
+        private final int age;
+
+        /** Organization ID. */
+        @QuerySqlField(index = true)
+        private final int orgId;
+
+        /**
+         * @param id ID.
+         * @param name Name.
+         * @param age Age.
+         * @param orgId Organization ID.
+         */
+        private Person(int id, String name, int age, int orgId) {
+            assert !F.isEmpty(name);
+            assert age > 0;
+            assert orgId > 0;
+
+            this.id = id;
+            this.name = name;
+            this.age = age;
+            this.orgId = orgId;
+        }
+    }
+
+    /**
+     * Organization.
+     */
+    @SuppressWarnings("UnusedDeclaration")
+    private static class Organization implements Serializable {
+        /** ID. */
+        @QuerySqlField
+        private final int id;
+
+        /** Name. */
+        @QuerySqlField(index = false)
+        private final String name;
+
+        /**
+         * @param id ID.
+         * @param name Name.
+         */
+        private Organization(int id, String name) {
+            this.id = id;
+            this.name = name;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/763f0676/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
index 603ee81..b1053b0 100644
--- a/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.jdbc.suite;
 
 import junit.framework.TestSuite;
+import org.apache.ignite.internal.jdbc2.JdbcDistributedJoinsQueryTest;
 import org.apache.ignite.jdbc.JdbcComplexQuerySelfTest;
 import org.apache.ignite.jdbc.JdbcConnectionSelfTest;
 import org.apache.ignite.jdbc.JdbcEmptyCacheSelfTest;
@@ -56,6 +57,7 @@ public class IgniteJdbcDriverTestSuite extends TestSuite {
         suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcPreparedStatementSelfTest.class));
         suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcResultSetSelfTest.class));
         suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcComplexQuerySelfTest.class));
+        suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcDistributedJoinsQueryTest.class));
         suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcMetadataSelfTest.class));
         suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcEmptyCacheSelfTest.class));
         suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcLocalCachesSelfTest.class));

http://git-wip-us.apache.org/repos/asf/ignite/blob/763f0676/modules/core/src/main/java/org/apache/ignite/IgniteJdbcDriver.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/IgniteJdbcDriver.java b/modules/core/src/main/java/org/apache/ignite/IgniteJdbcDriver.java
index 567ff9f..d432c1e4 100644
--- a/modules/core/src/main/java/org/apache/ignite/IgniteJdbcDriver.java
+++ b/modules/core/src/main/java/org/apache/ignite/IgniteJdbcDriver.java
@@ -104,6 +104,11 @@ import org.apache.ignite.logger.java.JavaLogger;
  *          can make significant performance and network optimizations.
  *          Default value is {@code false}.
  *     </li>
+ *     <li>
+ *         {@code distributedJoins} - enables support of distributed joins feature. This flag does not make sense in
+ *         combination with {@code local} and/or {@code collocated} flags with {@code true} value or in case of querying
+ *         of local cache. Default value is {@code false}.
+ *     </li>
  * </ul>
  *
  * <h2 class="header">Configuration of Ignite Java client based connection</h2>
@@ -284,6 +289,9 @@ public class IgniteJdbcDriver implements Driver {
     /** Collocated parameter name. */
     private static final String PARAM_COLLOCATED = "collocated";
 
+    /** Distributed joins parameter name. */
+    private static final String PARAM_DISTRIBUTED_JOINS = "distributedJoins";
+
     /** Hostname property name. */
     public static final String PROP_HOST = PROP_PREFIX + "host";
 
@@ -302,6 +310,9 @@ public class IgniteJdbcDriver implements Driver {
     /** Collocated property name. */
     public static final String PROP_COLLOCATED = PROP_PREFIX + PARAM_COLLOCATED;
 
+    /** Distributed joins property name. */
+    public static final String PROP_DISTRIBUTED_JOINS = PROP_PREFIX + PARAM_DISTRIBUTED_JOINS;
+
     /** Cache name property name. */
     public static final String PROP_CFG = PROP_PREFIX + "cfg";
 
@@ -366,7 +377,8 @@ public class IgniteJdbcDriver implements Driver {
             new PropertyInfo("Cache name", info.getProperty(PROP_CACHE), ""),
             new PropertyInfo("Node ID", info.getProperty(PROP_NODE_ID), ""),
             new PropertyInfo("Local", info.getProperty(PROP_LOCAL), ""),
-            new PropertyInfo("Collocated", info.getProperty(PROP_COLLOCATED), "")
+            new PropertyInfo("Collocated", info.getProperty(PROP_COLLOCATED), ""),
+            new PropertyInfo("Distributed Joins", info.getProperty(PROP_DISTRIBUTED_JOINS), "")
         );
 
         if (info.getProperty(PROP_CFG) != null)

http://git-wip-us.apache.org/repos/asf/ignite/blob/763f0676/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcConnection.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcConnection.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcConnection.java
index 2d2ce5d..6f0d9c4 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcConnection.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcConnection.java
@@ -69,6 +69,7 @@ import static java.util.concurrent.TimeUnit.SECONDS;
 import static org.apache.ignite.IgniteJdbcDriver.PROP_CACHE;
 import static org.apache.ignite.IgniteJdbcDriver.PROP_CFG;
 import static org.apache.ignite.IgniteJdbcDriver.PROP_COLLOCATED;
+import static org.apache.ignite.IgniteJdbcDriver.PROP_DISTRIBUTED_JOINS;
 import static org.apache.ignite.IgniteJdbcDriver.PROP_LOCAL;
 import static org.apache.ignite.IgniteJdbcDriver.PROP_NODE_ID;
 
@@ -113,6 +114,9 @@ public class JdbcConnection implements Connection {
     /** Collocated query flag. */
     private boolean collocatedQry;
 
+    /** Distributed joins flag. */
+    private boolean distributedJoins;
+
     /** Statements. */
     final Set<JdbcStatement> statements = new HashSet<>();
 
@@ -132,6 +136,7 @@ public class JdbcConnection implements Connection {
         this.cacheName = props.getProperty(PROP_CACHE);
         this.locQry = Boolean.parseBoolean(props.getProperty(PROP_LOCAL));
         this.collocatedQry = Boolean.parseBoolean(props.getProperty(PROP_COLLOCATED));
+        this.distributedJoins = Boolean.parseBoolean(props.getProperty(PROP_DISTRIBUTED_JOINS));
 
         String nodeIdProp = props.getProperty(PROP_NODE_ID);
 
@@ -706,6 +711,13 @@ public class JdbcConnection implements Connection {
     }
 
     /**
+     * @return Distributed joins flag.
+     */
+    boolean isDistributedJoins() {
+        return distributedJoins;
+    }
+
+    /**
      * Ensures that connection is not closed.
      *
      * @throws SQLException If connection is closed.

http://git-wip-us.apache.org/repos/asf/ignite/blob/763f0676/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcQueryTask.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcQueryTask.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcQueryTask.java
index 1a5793a..c4911cb 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcQueryTask.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcQueryTask.java
@@ -92,6 +92,9 @@ class JdbcQueryTask implements IgniteCallable<JdbcQueryTask.QueryResult> {
     /** Collocated query flag. */
     private final boolean collocatedQry;
 
+    /** Distributed joins flag. */
+    private final boolean distributedJoins;
+
     /**
      * @param ignite Ignite.
      * @param cacheName Cache name.
@@ -102,9 +105,11 @@ class JdbcQueryTask implements IgniteCallable<JdbcQueryTask.QueryResult> {
      * @param uuid UUID.
      * @param locQry Local query flag.
      * @param collocatedQry Collocated query flag.
+     * @param distributedJoins Distributed joins flag.
      */
     public JdbcQueryTask(Ignite ignite, String cacheName, String sql,
-        boolean loc, Object[] args, int fetchSize, UUID uuid, boolean locQry, boolean collocatedQry) {
+        boolean loc, Object[] args, int fetchSize, UUID uuid,
+        boolean locQry, boolean collocatedQry, boolean distributedJoins) {
         this.ignite = ignite;
         this.args = args;
         this.uuid = uuid;
@@ -114,6 +119,7 @@ class JdbcQueryTask implements IgniteCallable<JdbcQueryTask.QueryResult> {
         this.loc = loc;
         this.locQry = locQry;
         this.collocatedQry = collocatedQry;
+        this.distributedJoins = distributedJoins;
     }
 
     /** {@inheritDoc} */
@@ -147,6 +153,7 @@ class JdbcQueryTask implements IgniteCallable<JdbcQueryTask.QueryResult> {
             qry.setPageSize(fetchSize);
             qry.setLocal(locQry);
             qry.setCollocated(collocatedQry);
+            qry.setDistributedJoins(distributedJoins);
 
             QueryCursor<List<?>> qryCursor = cache.query(qry);
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/763f0676/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java
index 69dddad..8e0e9d0 100755
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java
@@ -146,8 +146,8 @@ public class JdbcResultSet implements ResultSet {
 
             boolean loc = nodeId == null;
 
-            JdbcQueryTask qryTask = new JdbcQueryTask(loc ? ignite : null, conn.cacheName(),
-                null, loc, null, fetchSize, uuid, conn.isLocalQuery(), conn.isCollocatedQuery());
+            JdbcQueryTask qryTask = new JdbcQueryTask(loc ? ignite : null, conn.cacheName(), null, loc, null,
+                fetchSize, uuid, conn.isLocalQuery(), conn.isCollocatedQuery(), conn.isDistributedJoins());
 
             try {
                 JdbcQueryTask.QueryResult res =

http://git-wip-us.apache.org/repos/asf/ignite/blob/763f0676/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcStatement.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcStatement.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcStatement.java
index 6b806f3..e187dc0 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcStatement.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcStatement.java
@@ -96,7 +96,7 @@ public class JdbcStatement implements Statement {
         boolean loc = nodeId == null;
 
         JdbcQueryTask qryTask = new JdbcQueryTask(loc ? ignite : null, conn.cacheName(),
-            sql, loc, args, fetchSize, uuid, conn.isLocalQuery(), conn.isCollocatedQuery());
+            sql, loc, args, fetchSize, uuid, conn.isLocalQuery(), conn.isCollocatedQuery(), conn.isDistributedJoins());
 
         try {
             JdbcQueryTask.QueryResult res =


[36/50] [abbrv] ignite git commit: Merge remote-tracking branch 'remotes/community/ignite-1.6.3'

Posted by vo...@apache.org.
Merge remote-tracking branch 'remotes/community/ignite-1.6.3'

# Conflicts:
#	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionTopologyImpl.java
#	modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheWithIndexingTestSuite.java


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

Branch: refs/heads/ignite-3553
Commit: da563701caec058db4a264a4b08f0a35c4cd9b17
Parents: a441bb9 9d07e3e
Author: sboikov <sb...@gridgain.com>
Authored: Wed Jul 27 10:17:16 2016 +0300
Committer: sboikov <sb...@gridgain.com>
Committed: Wed Jul 27 10:17:16 2016 +0300

----------------------------------------------------------------------
 .../org/apache/ignite/internal/IgnitionEx.java  |   2 +
 .../distributed/dht/GridDhtLocalPartition.java  |   2 +-
 .../dht/GridDhtPartitionTopologyImpl.java       |  52 +++++++-
 .../ServicePredicateAccessCacheTest.java        |   4 +-
 .../ignite/cache/spring/SpringCacheManager.java |  17 ++-
 .../spring/SpringTransactionManager.java        |  59 +++++----
 .../test/java/config/spring-transactions.xml    |   4 +-
 .../ignite/TestInjectionLifecycleBean.java      |  42 +++++++
 .../org/apache/ignite/spring-injection-test.xml |  43 +++++++
 .../SpringCacheManagerContextInjectionTest.java | 126 +++++++++++++++++++
 .../testsuites/IgniteSpringTestSuite.java       |   5 +
 ...gTransactionManagerContextInjectionTest.java | 125 ++++++++++++++++++
 12 files changed, 448 insertions(+), 33 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/da563701/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionTopologyImpl.java
----------------------------------------------------------------------
diff --cc modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionTopologyImpl.java
index 3692022,50f7f0f..1299d20
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionTopologyImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionTopologyImpl.java
@@@ -23,16 -23,17 +23,18 @@@ import java.util.Collections
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Iterator;
 -import java.util.LinkedList;
  import java.util.List;
  import java.util.Map;
 +import java.util.NoSuchElementException;
  import java.util.Set;
  import java.util.UUID;
 +import java.util.concurrent.atomic.AtomicReferenceArray;
  import org.apache.ignite.IgniteCheckedException;
  import org.apache.ignite.IgniteLogger;
+ import org.apache.ignite.IgniteSystemProperties;
  import org.apache.ignite.cluster.ClusterNode;
  import org.apache.ignite.events.DiscoveryEvent;
+ import org.apache.ignite.internal.IgniteFutureTimeoutCheckedException;
  import org.apache.ignite.internal.IgniteInterruptedCheckedException;
  import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
  import org.apache.ignite.internal.processors.affinity.GridAffinityAssignment;
@@@ -178,10 -179,27 +181,27 @@@ class GridDhtPartitionTopologyImpl impl
      private boolean waitForRent() throws IgniteCheckedException {
          boolean changed = false;
  
+         GridDhtLocalPartition[] locPartsCopy = new GridDhtLocalPartition[locParts.length];
+ 
+         lock.readLock().lock();
+ 
+         try {
+             for (int i = 0; i < locParts.length; i++)
+                 locPartsCopy[i] = locParts[i];
+         }
+         finally {
+             lock.readLock().unlock();
+         }
+ 
+         final long longOpDumpTimeout =
+             IgniteSystemProperties.getLong(IgniteSystemProperties.IGNITE_LONG_OPERATIONS_DUMP_TIMEOUT, 60_000);
+ 
+         int dumpCnt = 0;
+ 
          GridDhtLocalPartition part;
  
-         for (int i = 0; i < locParts.length(); i++) {
-             part = locParts.get(i);
 -        for (int i = 0; i < locPartsCopy.length; i++) {
 -            part = locPartsCopy[i];
++        for (int i = 0; i < locPartsCopy.length(); i++) {
++            part = locPartsCopy.get(i);
  
              if (part == null)
                  continue;

http://git-wip-us.apache.org/repos/asf/ignite/blob/da563701/modules/spring/src/test/java/org/apache/ignite/testsuites/IgniteSpringTestSuite.java
----------------------------------------------------------------------


[19/50] [abbrv] ignite git commit: IGNITE-3399 Add support for primitive type names in QueryEntity - Fixes #878.

Posted by vo...@apache.org.
IGNITE-3399 Add support for primitive type names in QueryEntity - Fixes #878.

Signed-off-by: Sergi Vladykin <se...@gmail.com>


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

Branch: refs/heads/ignite-3553
Commit: df5a842f2ad3a676fcd0fd29b00a0eb361709488
Parents: ca5c8d8
Author: Jens Hoffmann <jc...@gmail.com>
Authored: Mon Jul 25 08:47:35 2016 +0300
Committer: Sergi Vladykin <se...@gmail.com>
Committed: Mon Jul 25 08:47:35 2016 +0300

----------------------------------------------------------------------
 .../processors/query/GridQueryProcessor.java    |  2 +-
 .../ignite/internal/util/IgniteUtils.java       | 32 ++++++++++++++++----
 .../IgniteCacheQuerySelfTestSuite.java          |  2 ++
 3 files changed, 29 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/df5a842f/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
index 04c6cb0..0dd542e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
@@ -1386,7 +1386,7 @@ public class GridQueryProcessor extends GridProcessorAdapter {
             aliases = Collections.emptyMap();
 
         for (Map.Entry<String, String> entry : qryEntity.getFields().entrySet()) {
-            BinaryProperty prop = buildBinaryProperty(entry.getKey(), U.classForName(entry.getValue(), Object.class), aliases);
+            BinaryProperty prop = buildBinaryProperty(entry.getKey(), U.classForName(entry.getValue(), Object.class, true), aliases);
 
             d.addProperty(prop, false);
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/df5a842f/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
index 7a81cf8..31128ca 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
@@ -1361,13 +1361,33 @@ public abstract class IgniteUtils {
      * @param dflt Default class to return.
      * @return Class or default given class if it can't be found.
      */
-    @Nullable public static Class<?> classForName(String cls, @Nullable Class<?> dflt) {
-        try {
-            return cls == null ? dflt : Class.forName(cls);
-        }
-        catch (ClassNotFoundException ignore) {
-            return dflt;
+    @Nullable public static Class<?> classForName(@Nullable String cls, @Nullable Class<?> dflt) {
+        return classForName(cls, dflt, false);
+    }
+
+    /**
+     * Gets class for the given name if it can be loaded or default given class.
+     *
+     * @param cls Class.
+     * @param dflt Default class to return.
+     * @param includePrimitiveTypes Whether class resolution should include primitive types (i.e. "int" will resolve to int.class if flag is set)
+     * @return Class or default given class if it can't be found.
+     */
+    @Nullable
+    public static Class<?> classForName(@Nullable String cls, @Nullable Class<?> dflt, boolean includePrimitiveTypes) {
+        Class<?> clazz;
+        if (cls == null)
+            clazz = dflt;
+        else if (includePrimitiveTypes && primitiveMap.containsKey(cls))
+            clazz = primitiveMap.get(cls);
+        else {
+            try {
+                clazz = Class.forName(cls);
+            } catch (ClassNotFoundException ignore) {
+                clazz = dflt;
+            }
         }
+        return clazz;
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/df5a842f/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java
index 65a94dd..0203354 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java
@@ -40,6 +40,7 @@ import org.apache.ignite.internal.processors.cache.IgniteCacheOffheapEvictQueryT
 import org.apache.ignite.internal.processors.cache.IgniteCacheOffheapIndexScanTest;
 import org.apache.ignite.internal.processors.cache.IgniteCacheOffheapTieredMultithreadedSelfTest;
 import org.apache.ignite.internal.processors.cache.IgniteCachePartitionedQueryMultiThreadedSelfTest;
+import org.apache.ignite.internal.processors.cache.IgniteCachePrimitiveFieldsQuerySelfTest;
 import org.apache.ignite.internal.processors.cache.IgniteCacheQueryEvictsMultiThreadedSelfTest;
 import org.apache.ignite.internal.processors.cache.IgniteCacheQueryH2IndexingLeakTest;
 import org.apache.ignite.internal.processors.cache.IgniteCacheQueryIndexSelfTest;
@@ -125,6 +126,7 @@ public class IgniteCacheQuerySelfTestSuite extends TestSuite {
         suite.addTestSuite(IgniteBinaryWrappedObjectFieldsQuerySelfTest.class);
         suite.addTestSuite(IgniteCacheQueryH2IndexingLeakTest.class);
         suite.addTestSuite(IgniteCacheQueryNoRebalanceSelfTest.class);
+        suite.addTestSuite(IgniteCachePrimitiveFieldsQuerySelfTest.class);
 
         suite.addTestSuite(IgniteCacheJoinQueryWithAffinityKeyTest.class);
         suite.addTestSuite(IgniteCacheDistributedJoinCollocatedAndNotTest.class);


[37/50] [abbrv] ignite git commit: Merge remote-tracking branch 'remotes/community/ignite-1.6.3'

Posted by vo...@apache.org.
Merge remote-tracking branch 'remotes/community/ignite-1.6.3'

# Conflicts:
#	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionTopologyImpl.java
#	modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheWithIndexingTestSuite.java


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

Branch: refs/heads/ignite-3553
Commit: 42f42eac58c718f7debdd5f6e1ed989bae48de69
Parents: da56370
Author: sboikov <sb...@gridgain.com>
Authored: Wed Jul 27 10:17:36 2016 +0300
Committer: sboikov <sb...@gridgain.com>
Committed: Wed Jul 27 10:17:36 2016 +0300

----------------------------------------------------------------------
 .../dht/GridDhtPartitionTopologyImpl.java           | 16 ++--------------
 .../org/apache/ignite/spring-injection-test.xml     |  2 +-
 2 files changed, 3 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/42f42eac/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionTopologyImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionTopologyImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionTopologyImpl.java
index 1299d20..558a654 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionTopologyImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionTopologyImpl.java
@@ -181,18 +181,6 @@ class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology {
     private boolean waitForRent() throws IgniteCheckedException {
         boolean changed = false;
 
-        GridDhtLocalPartition[] locPartsCopy = new GridDhtLocalPartition[locParts.length];
-
-        lock.readLock().lock();
-
-        try {
-            for (int i = 0; i < locParts.length; i++)
-                locPartsCopy[i] = locParts[i];
-        }
-        finally {
-            lock.readLock().unlock();
-        }
-
         final long longOpDumpTimeout =
             IgniteSystemProperties.getLong(IgniteSystemProperties.IGNITE_LONG_OPERATIONS_DUMP_TIMEOUT, 60_000);
 
@@ -200,8 +188,8 @@ class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology {
 
         GridDhtLocalPartition part;
 
-        for (int i = 0; i < locPartsCopy.length(); i++) {
-            part = locPartsCopy.get(i);
+        for (int i = 0; i < locParts.length(); i++) {
+            part = locParts.get(i);
 
             if (part == null)
                 continue;

http://git-wip-us.apache.org/repos/asf/ignite/blob/42f42eac/modules/spring/src/test/java/org/apache/ignite/spring-injection-test.xml
----------------------------------------------------------------------
diff --git a/modules/spring/src/test/java/org/apache/ignite/spring-injection-test.xml b/modules/spring/src/test/java/org/apache/ignite/spring-injection-test.xml
index b8368a3..7401986 100644
--- a/modules/spring/src/test/java/org/apache/ignite/spring-injection-test.xml
+++ b/modules/spring/src/test/java/org/apache/ignite/spring-injection-test.xml
@@ -18,7 +18,7 @@
 -->
 
 <!--
-    GridGain Spring configuration file to startup grid cache.
+    Ignite Spring configuration file to startup grid cache.
 -->
 <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"


[46/50] [abbrv] ignite git commit: Added support for distributed join flag in Visor.

Posted by vo...@apache.org.
Added support for distributed join flag in Visor.


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

Branch: refs/heads/ignite-3553
Commit: 5f4563cea930a8320593918d879554e6aba1abf7
Parents: f96b568
Author: AKuznetsov <ak...@gridgain.com>
Authored: Wed Jul 27 21:52:59 2016 +0700
Committer: AKuznetsov <ak...@gridgain.com>
Committed: Wed Jul 27 21:52:59 2016 +0700

----------------------------------------------------------------------
 .../internal/visor/query/VisorQueryArgV2.java   | 49 ++++++++++++++++++++
 .../internal/visor/query/VisorQueryJob.java     |  1 +
 2 files changed, 50 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/5f4563ce/modules/core/src/main/java/org/apache/ignite/internal/visor/query/VisorQueryArgV2.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/query/VisorQueryArgV2.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/query/VisorQueryArgV2.java
new file mode 100644
index 0000000..8ed3976
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/query/VisorQueryArgV2.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.visor.query;
+
+/**
+ * Arguments for {@link VisorQueryTask}.
+ */
+public class VisorQueryArgV2 extends VisorQueryArg {
+    /** */
+    private static final long serialVersionUID = 0L;
+
+    /** Distributed joins enabled flag. */
+    private final boolean distributedJoins;
+
+    /**
+     * @param cacheName Cache name for query.
+     * @param qryTxt Query text.
+     * @param distributedJoins If {@code true} then distributed joins enabled.
+     * @param loc Flag whether to execute query locally.
+     * @param pageSize Result batch size.
+     */
+    public VisorQueryArgV2(String cacheName, String qryTxt, boolean distributedJoins, boolean loc, int pageSize) {
+        super(cacheName, qryTxt, loc, pageSize);
+
+        this.distributedJoins = distributedJoins;
+    }
+
+    /**
+     * @return Distributed joins enabled flag.
+     */
+    public boolean distributedJoins() {
+        return distributedJoins;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/5f4563ce/modules/core/src/main/java/org/apache/ignite/internal/visor/query/VisorQueryJob.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/query/VisorQueryJob.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/query/VisorQueryJob.java
index 0f2f82e..5d0a82e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/visor/query/VisorQueryJob.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/query/VisorQueryJob.java
@@ -139,6 +139,7 @@ public class VisorQueryJob extends VisorJob<VisorQueryArg, IgniteBiTuple<? exten
                 SqlFieldsQuery qry = new SqlFieldsQuery(arg.queryTxt());
                 qry.setPageSize(arg.pageSize());
                 qry.setLocal(arg.local());
+                qry.setDistributedJoins(arg instanceof VisorQueryArgV2 && ((VisorQueryArgV2)arg).distributedJoins());
 
                 long start = U.currentTimeMillis();
 


[16/50] [abbrv] ignite git commit: ignite-1232 Distributed SQL joins implementation

Posted by vo...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/core/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/messages/GridQueryNextPageResponse.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/messages/GridQueryNextPageResponse.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/messages/GridQueryNextPageResponse.java
index b220291..4889069 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/messages/GridQueryNextPageResponse.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/messages/GridQueryNextPageResponse.java
@@ -144,11 +144,6 @@ public class GridQueryNextPageResponse implements Message {
     }
 
     /** {@inheritDoc} */
-    @Override public String toString() {
-        return S.toString(GridQueryNextPageResponse.class, this);
-    }
-
-    /** {@inheritDoc} */
     @Override public void onAckReceived() {
         // No-op.
     }
@@ -304,4 +299,11 @@ public class GridQueryNextPageResponse implements Message {
     public void retry(AffinityTopologyVersion retry) {
         this.retry = retry;
     }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(GridQueryNextPageResponse.class, this,
+            "valsSize", vals != null ? vals.size() : 0,
+            "rowsSize", plainRows != null ? plainRows.size() : 0);
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/core/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/messages/GridQueryRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/messages/GridQueryRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/messages/GridQueryRequest.java
index 60d348b..f7de86c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/messages/GridQueryRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/messages/GridQueryRequest.java
@@ -20,13 +20,16 @@ package org.apache.ignite.internal.processors.query.h2.twostep.messages;
 import java.nio.ByteBuffer;
 import java.util.Collection;
 import java.util.List;
-import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.internal.GridDirectCollection;
+import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.IgniteCodeGeneratingFail;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.query.GridCacheQueryMarshallable;
 import org.apache.ignite.internal.processors.cache.query.GridCacheSqlQuery;
 import org.apache.ignite.internal.util.tostring.GridToStringInclude;
+import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.marshaller.Marshaller;
 import org.apache.ignite.plugin.extensions.communication.Message;
 import org.apache.ignite.plugin.extensions.communication.MessageCollectionItemType;
 import org.apache.ignite.plugin.extensions.communication.MessageReader;
@@ -35,8 +38,9 @@ import org.apache.ignite.plugin.extensions.communication.MessageWriter;
 /**
  * Query request.
  */
+@Deprecated
 @IgniteCodeGeneratingFail
-public class GridQueryRequest implements Message {
+public class GridQueryRequest implements Message, GridCacheQueryMarshallable {
     /** */
     private static final long serialVersionUID = 0L;
 
@@ -165,11 +169,29 @@ public class GridQueryRequest implements Message {
     /**
      * @return Queries.
      */
-    public Collection<GridCacheSqlQuery> queries() throws IgniteCheckedException {
+    public Collection<GridCacheSqlQuery> queries() {
         return qrys;
     }
 
     /** {@inheritDoc} */
+    @Override public void marshall(Marshaller m) {
+        if (F.isEmpty(qrys))
+            return;
+
+        for (GridCacheSqlQuery qry : qrys)
+            qry.marshall(m);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void unmarshall(Marshaller m, GridKernalContext ctx) {
+        if (F.isEmpty(qrys))
+            return;
+
+        for (GridCacheSqlQuery qry : qrys)
+            qry.unmarshall(m, ctx);
+    }
+
+    /** {@inheritDoc} */
     @Override public String toString() {
         return S.toString(GridQueryRequest.class, this);
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
index 269795b..7a81cf8 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
@@ -6703,6 +6703,19 @@ public abstract class IgniteUtils {
     }
 
     /**
+     * Swaps two objects in array.
+     *
+     * @param arr Array.
+     * @param a Index of the first object.
+     * @param b Index of the second object.
+     */
+    public static void swap(Object[] arr, int a, int b) {
+        Object tmp = arr[a];
+        arr[a] = arr[b];
+        arr[b] = tmp;
+    }
+
+    /**
      * Returns array which is the union of two arrays
      * (array of elements contained in any of provided arrays).
      * <p/>

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/core/src/main/java/org/apache/ignite/internal/util/lang/GridFunc.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/lang/GridFunc.java b/modules/core/src/main/java/org/apache/ignite/internal/util/lang/GridFunc.java
index ab31625..5eb27d3 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/lang/GridFunc.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/lang/GridFunc.java
@@ -2313,6 +2313,26 @@ public class GridFunc {
     }
 
     /**
+     * Converts collection of numbers to primitive {@code int[]} array.
+     *
+     * @param col Collection of numbers.
+     * @return Array of integers.
+     */
+    public static int[] toIntArray(Collection<? extends Number> col) {
+        if (col == null)
+            return null;
+
+        int[] res = new int[col.size()];
+
+        Iterator<? extends Number> iter = col.iterator();
+
+        for (int i = 0; i < res.length; i++)
+            res[i] = iter.next().intValue();
+
+        return res;
+    }
+
+    /**
      * Utility map getter. This method analogous to {@link #addIfAbsent(Map, Object, Callable)}
      * method but this one doesn't put the default value into the map when key is not found.
      *

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/core/src/main/java/org/apache/ignite/internal/util/offheap/unsafe/GridOffHeapSnapTreeMap.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/offheap/unsafe/GridOffHeapSnapTreeMap.java b/modules/core/src/main/java/org/apache/ignite/internal/util/offheap/unsafe/GridOffHeapSnapTreeMap.java
index 04ea56c..7bde651 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/offheap/unsafe/GridOffHeapSnapTreeMap.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/offheap/unsafe/GridOffHeapSnapTreeMap.java
@@ -35,7 +35,6 @@
 
 package org.apache.ignite.internal.util.offheap.unsafe;
 
-import java.io.Closeable;
 import java.util.AbstractMap;
 import java.util.AbstractSet;
 import java.util.ArrayList;
@@ -51,10 +50,11 @@ import java.util.SortedSet;
 import java.util.concurrent.ConcurrentNavigableMap;
 import java.util.concurrent.ConcurrentSkipListMap;
 import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridReservable;
 import org.apache.ignite.internal.util.typedef.internal.SB;
 import org.jetbrains.annotations.Nullable;
 import org.jsr166.ConcurrentHashMap8;
@@ -107,7 +107,7 @@ import org.jsr166.ConcurrentHashMap8;
  */
 @SuppressWarnings("ALL")
 public class GridOffHeapSnapTreeMap<K extends GridOffHeapSmartPointer,V extends GridOffHeapSmartPointer>
-    extends AbstractMap<K,V> implements ConcurrentNavigableMap<K, V>, Cloneable, Closeable {
+    extends AbstractMap<K,V> implements ConcurrentNavigableMap<K, V>, Cloneable, AutoCloseable, GridReservable {
     /** This is a special value that indicates that an optimistic read failed. */
     private static final GridOffHeapSmartPointer SpecialRetry = new GridOffHeapSmartPointer() {
         @Override public long pointer() {
@@ -937,17 +937,17 @@ public class GridOffHeapSnapTreeMap<K extends GridOffHeapSmartPointer,V extends
         protected final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
 
         /** */
-        private final AtomicBoolean stopped = new AtomicBoolean(false);
+        private boolean stopped;
 
         /** {@inheritDoc} */
         @Override public boolean add(long node) {
-            ReentrantReadWriteLock.ReadLock l =  lock.readLock();
+            Lock l =  lock.readLock();
 
             if (!l.tryLock())
                 return false;
 
             try {
-                return super.add(node);
+                return !stopped && super.add(node);
             }
             finally {
                 l.unlock();
@@ -956,13 +956,13 @@ public class GridOffHeapSnapTreeMap<K extends GridOffHeapSmartPointer,V extends
 
         /** {@inheritDoc} */
         @Override public boolean add(RecycleQueue que) {
-            ReentrantReadWriteLock.ReadLock l =  lock.readLock();
+            Lock l =  lock.readLock();
 
             if (!l.tryLock())
                 return false;
 
             try {
-                return super.add(que);
+                return !stopped && super.add(que);
             }
             finally {
                 l.unlock();
@@ -980,20 +980,21 @@ public class GridOffHeapSnapTreeMap<K extends GridOffHeapSmartPointer,V extends
          * @return {@code true} If we stopped this queue.
          */
         public boolean stop() {
-            if (stopped.compareAndSet(false, true)) {
-                lock.writeLock().lock();
+            Lock l = lock.writeLock();
 
-                return true;
-            }
+            l.lock();
 
-            return false;
-        }
+            try {
+                if (stopped)
+                    return false;
 
-        /**
-         * @return {@code true} If this queue is stopped..
-         */
-        public boolean isStopped() {
-            return stopped.get();
+                stopped = true;
+
+                return true;
+            }
+            finally {
+                l.unlock();
+            }
         }
     }
 
@@ -1046,6 +1047,12 @@ public class GridOffHeapSnapTreeMap<K extends GridOffHeapSmartPointer,V extends
     /** Recycle queue for this snapshot. */
     private volatile StoppableRecycleQueue recycleBin;
 
+    /** */
+    private AtomicInteger reservations;
+
+    /** */
+    private volatile boolean closing;
+
     /**
      * @param keyFactory Key factory.
      * @param valFactory Value factory.
@@ -1079,10 +1086,53 @@ public class GridOffHeapSnapTreeMap<K extends GridOffHeapSmartPointer,V extends
         this.holderRef = rootHolder();
     }
 
+    /** {@inheritDoc} */
+    @Override public boolean reserve() {
+        for (;;) {
+            int r = reservations.get();
+
+            if (r == -1)
+                return false;
+
+            assert r >= 0 : r;
+
+            if (reservations.compareAndSet(r, r + 1))
+                return true;
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void release() {
+        for (;;) {
+            int r = reservations.get();
+
+            assert r > 0 : r;
+
+            int z = r == 1 && closing ? -1 : r - 1;
+
+            if (reservations.compareAndSet(r, z)) {
+                if (z == -1)
+                    doClose();
+
+                return;
+            }
+        }
+    }
+
     /**
      * Closes tree map and reclaims memory.
      */
-    public void close() {
+    @Override public void close() {
+        closing = true;
+
+        if (reservations == null || reservations.compareAndSet(0, -1))
+            doClose();
+    }
+
+    /**
+     *
+     */
+    private void doClose() {
         RecycleQueue q;
 
         if (snapshotId.longValue() == 0) {
@@ -1142,6 +1192,7 @@ public class GridOffHeapSnapTreeMap<K extends GridOffHeapSmartPointer,V extends
         copy.holderRef = rootHolder(holderRef);
         markShared(root());
 
+        copy.reservations = new AtomicInteger();
         copy.size = new AtomicInteger(size());
         copy.recycleBin = new StoppableRecycleQueue();
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/jdbc/TcpDiscoveryJdbcIpFinderSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/jdbc/TcpDiscoveryJdbcIpFinderSelfTest.java b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/jdbc/TcpDiscoveryJdbcIpFinderSelfTest.java
index 9392cc1..37352e1 100644
--- a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/jdbc/TcpDiscoveryJdbcIpFinderSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/jdbc/TcpDiscoveryJdbcIpFinderSelfTest.java
@@ -51,7 +51,7 @@ public class TcpDiscoveryJdbcIpFinderSelfTest extends
         dataSrc.setDriverClass("org.h2.Driver");
 
         if (initSchema)
-            dataSrc.setJdbcUrl("jdbc:h2:mem");
+            dataSrc.setJdbcUrl("jdbc:h2:mem:./test");
         else {
             dataSrc.setJdbcUrl("jdbc:h2:mem:jdbc_ipfinder_not_initialized_schema");
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java
index 87509a4..e475754 100644
--- a/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java
@@ -27,6 +27,7 @@ import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.Callable;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 import javax.cache.Cache;
 import javax.cache.CacheException;
@@ -47,6 +48,7 @@ import org.apache.ignite.cache.CacheAtomicityMode;
 import org.apache.ignite.cache.CachePeekMode;
 import org.apache.ignite.cache.affinity.Affinity;
 import org.apache.ignite.cache.affinity.AffinityFunction;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
 import org.apache.ignite.cluster.ClusterGroup;
 import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.cluster.ClusterTopologyException;
@@ -529,7 +531,7 @@ public abstract class GridCommonAbstractTest extends GridAbstractTest {
                                         ", locNode=" + g.cluster().localNode() + ']');
                                 }
 
-                                Thread.sleep(200); // Busy wait.
+                                Thread.sleep(20); // Busy wait.
 
                                 continue;
                             }
@@ -1177,4 +1179,39 @@ public abstract class GridCommonAbstractTest extends GridAbstractTest {
             }
         }
     }
+
+    /**
+     * @param aff Affinity.
+     * @param key Counter.
+     * @param node Target node.
+     * @return Key.
+     */
+    protected final Integer keyForNode(Affinity<Object> aff, AtomicInteger key, ClusterNode node) {
+        for (int i = 0; i < 100_000; i++) {
+            Integer next = key.getAndIncrement();
+
+            if (aff.mapKeyToNode(next).equals(node))
+                return next;
+        }
+
+        fail("Failed to find key for node: " + node);
+
+        return null;
+    }
+
+    /**
+     * @param cache Cache.
+     * @param qry Query.
+     * @return Query plan.
+     */
+    protected final String queryPlan(IgniteCache<?, ?> cache, SqlFieldsQuery qry) {
+        return (String)cache.query(new SqlFieldsQuery("explain " + qry.getSql())
+            .setArgs(qry.getArgs())
+            .setLocal(qry.isLocal())
+            .setCollocated(qry.isCollocated())
+            .setPageSize(qry.getPageSize())
+            .setDistributedJoins(qry.isDistributedJoins())
+            .setEnforceJoinOrder(qry.isEnforceJoinOrder()))
+            .getAll().get(0).get(0);
+    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/geospatial/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SpatialIndex.java
----------------------------------------------------------------------
diff --git a/modules/geospatial/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SpatialIndex.java b/modules/geospatial/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SpatialIndex.java
index 29b7dd4..3062d13 100644
--- a/modules/geospatial/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SpatialIndex.java
+++ b/modules/geospatial/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SpatialIndex.java
@@ -28,29 +28,31 @@ import java.util.Map;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
-import org.h2.engine.Constants;
 import org.h2.engine.Session;
 import org.h2.index.Cursor;
-import org.h2.index.IndexCondition;
 import org.h2.index.IndexType;
 import org.h2.index.SingleRowCursor;
 import org.h2.index.SpatialIndex;
+import org.h2.index.SpatialTreeIndex;
 import org.h2.message.DbException;
 import org.h2.mvstore.MVStore;
 import org.h2.mvstore.rtree.MVRTreeMap;
 import org.h2.mvstore.rtree.SpatialKey;
 import org.h2.result.SearchRow;
 import org.h2.result.SortOrder;
-import org.h2.table.Column;
 import org.h2.table.IndexColumn;
 import org.h2.table.Table;
 import org.h2.table.TableFilter;
 import org.h2.value.Value;
 import org.h2.value.ValueGeometry;
+import org.jetbrains.annotations.Nullable;
+
+import static org.apache.ignite.internal.processors.query.h2.opt.GridH2AbstractKeyValueRow.KEY_COL;
 
 /**
  * Spatial index.
  */
+@SuppressWarnings("unused"/*reflection*/)
 public class GridH2SpatialIndex extends GridH2IndexBase implements SpatialIndex {
     /** */
     private final ReadWriteLock lock = new ReentrantReadWriteLock();
@@ -80,12 +82,8 @@ public class GridH2SpatialIndex extends GridH2IndexBase implements SpatialIndex
      * @param tbl Table.
      * @param idxName Index name.
      * @param cols Columns.
-     * @param keyCol Key column.
-     * @param valCol Value column.
      */
-    public GridH2SpatialIndex(Table tbl, String idxName, IndexColumn[] cols, int keyCol, int valCol) {
-        super(keyCol, valCol);
-
+    public GridH2SpatialIndex(Table tbl, String idxName, IndexColumn... cols) {
         if (cols.length > 1)
             throw DbException.getUnsupportedException("can only do one column");
 
@@ -121,7 +119,14 @@ public class GridH2SpatialIndex extends GridH2IndexBase implements SpatialIndex
     }
 
     /** {@inheritDoc} */
+    @Nullable @Override protected Object doTakeSnapshot() {
+        return null; // TODO We do not support snapshots, but probably this is possible.
+    }
+
+    /** {@inheritDoc} */
     @Override public GridH2Row put(GridH2Row row) {
+        assert row instanceof GridH2AbstractKeyValueRow : "requires key to be at 0";
+
         Lock l = lock.writeLock();
 
         l.lock();
@@ -129,7 +134,7 @@ public class GridH2SpatialIndex extends GridH2IndexBase implements SpatialIndex
         try {
             checkClosed();
 
-            Value key = row.getValue(keyCol);
+            Value key = row.getValue(KEY_COL);
 
             assert key != null;
 
@@ -183,7 +188,7 @@ public class GridH2SpatialIndex extends GridH2IndexBase implements SpatialIndex
         try {
             checkClosed();
 
-            Value key = row.getValue(keyCol);
+            Value key = row.getValue(KEY_COL);
 
             assert key != null;
 
@@ -208,7 +213,7 @@ public class GridH2SpatialIndex extends GridH2IndexBase implements SpatialIndex
     }
 
     /** {@inheritDoc} */
-    @Override public void close(Session ses) {
+    @Override public void destroy() {
         Lock l = lock.writeLock();
 
         l.lock();
@@ -221,37 +226,31 @@ public class GridH2SpatialIndex extends GridH2IndexBase implements SpatialIndex
         finally {
             l.unlock();
         }
+
+        super.destroy();
     }
 
     /** {@inheritDoc} */
-    @Override protected long getCostRangeIndex(int[] masks, long rowCnt, TableFilter filter, SortOrder sortOrder) {
-        rowCnt += Constants.COST_ROW_OFFSET;
-        long cost = rowCnt;
-        long rows = rowCnt;
-
-        if (masks == null)
-            return cost;
-
-        for (Column column : columns) {
-            int idx = column.getColumnId();
-            int mask = masks[idx];
-            if ((mask & IndexCondition.SPATIAL_INTERSECTS) != 0) {
-                cost = 3 + rows / 4;
-
-                break;
-            }
-        }
-
-        return cost;
+    @Override public double getCost(Session ses, int[] masks, TableFilter[] filters, int filter, SortOrder sortOrder) {
+        return SpatialTreeIndex.getCostRangeIndex(masks,
+            table.getRowCountApproximation(), columns) / 10;
     }
 
     /** {@inheritDoc} */
-    @Override public double getCost(Session ses, int[] masks, TableFilter filter, SortOrder sortOrder) {
-        return getCostRangeIndex(masks, rowCnt, filter, sortOrder);
+    @Override public Cursor find(TableFilter filter, SearchRow first, SearchRow last) {
+        return find0(filter);
     }
 
     /** {@inheritDoc} */
     @Override public Cursor find(Session ses, SearchRow first, SearchRow last) {
+        return find0(null);
+    }
+
+    /**
+     * @param filter Table filter.
+     * @return Cursor.
+     */
+    private Cursor find0(TableFilter filter) {
         Lock l = lock.readLock();
 
         l.lock();
@@ -259,7 +258,7 @@ public class GridH2SpatialIndex extends GridH2IndexBase implements SpatialIndex
         try {
             checkClosed();
 
-            return new GridH2Cursor(rowIterator(treeMap.keySet().iterator()));
+            return new GridH2Cursor(rowIterator(treeMap.keySet().iterator(), filter));
         }
         finally {
             l.unlock();
@@ -273,9 +272,10 @@ public class GridH2SpatialIndex extends GridH2IndexBase implements SpatialIndex
 
     /**
      * @param i Spatial key iterator.
+     * @param filter Table filter.
      * @return Iterator over rows.
      */
-    private Iterator<GridH2Row> rowIterator(Iterator<SpatialKey> i) {
+    private Iterator<GridH2Row> rowIterator(Iterator<SpatialKey> i, TableFilter filter) {
         if (!i.hasNext())
             return Collections.emptyIterator();
 
@@ -290,7 +290,7 @@ public class GridH2SpatialIndex extends GridH2IndexBase implements SpatialIndex
         }
         while (i.hasNext());
 
-        return filter(rows.iterator());
+        return filter(rows.iterator(), threadLocalFilter());
     }
 
     /** {@inheritDoc} */
@@ -305,7 +305,7 @@ public class GridH2SpatialIndex extends GridH2IndexBase implements SpatialIndex
             if (!first)
                 throw DbException.throwInternalError("Spatial Index can only be fetch by ascending order");
 
-            Iterator<GridH2Row> iter = rowIterator(treeMap.keySet().iterator());
+            Iterator<GridH2Row> iter = rowIterator(treeMap.keySet().iterator(), null);
 
             return new SingleRowCursor(iter.hasNext() ? iter.next() : null);
         }
@@ -334,7 +334,7 @@ public class GridH2SpatialIndex extends GridH2IndexBase implements SpatialIndex
             if (intersection == null)
                 return find(filter.getSession(), null, null);
 
-            return new GridH2Cursor(rowIterator(treeMap.findIntersectingKeys(getEnvelope(intersection, 0))));
+            return new GridH2Cursor(rowIterator(treeMap.findIntersectingKeys(getEnvelope(intersection, 0)), filter));
         }
         finally {
             l.unlock();

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
index 0b0fe85..535881e 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
@@ -64,6 +64,7 @@ import org.apache.ignite.cache.query.annotations.QuerySqlFunction;
 import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.GridTopic;
 import org.apache.ignite.internal.IgniteInternalFuture;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cache.CacheEntryImpl;
@@ -74,6 +75,7 @@ import org.apache.ignite.internal.processors.cache.GridCacheAffinityManager;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.IgniteInternalCache;
 import org.apache.ignite.internal.processors.cache.QueryCursorImpl;
+import org.apache.ignite.internal.processors.cache.query.GridCacheQueryMarshallable;
 import org.apache.ignite.internal.processors.cache.query.GridCacheTwoStepQuery;
 import org.apache.ignite.internal.processors.query.GridQueryFieldMetadata;
 import org.apache.ignite.internal.processors.query.GridQueryFieldsResult;
@@ -82,14 +84,15 @@ import org.apache.ignite.internal.processors.query.GridQueryIndexDescriptor;
 import org.apache.ignite.internal.processors.query.GridQueryIndexing;
 import org.apache.ignite.internal.processors.query.GridQueryProperty;
 import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2IndexBase;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2DefaultTableEngine;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2KeyValueRowOffheap;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2KeyValueRowOnheap;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2QueryContext;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2RowDescriptor;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2RowFactory;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2TreeIndex;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2Utils;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2ValueCacheObject;
 import org.apache.ignite.internal.processors.query.h2.opt.GridLuceneIndex;
 import org.apache.ignite.internal.processors.query.h2.sql.GridSqlQuerySplitter;
@@ -100,37 +103,44 @@ import org.apache.ignite.internal.util.GridBoundedConcurrentLinkedHashMap;
 import org.apache.ignite.internal.util.GridEmptyCloseableIterator;
 import org.apache.ignite.internal.util.GridSpinBusyLock;
 import org.apache.ignite.internal.util.lang.GridCloseableIterator;
+import org.apache.ignite.internal.util.lang.GridPlainRunnable;
+import org.apache.ignite.internal.util.lang.IgniteInClosure2X;
 import org.apache.ignite.internal.util.offheap.unsafe.GridUnsafeGuard;
 import org.apache.ignite.internal.util.offheap.unsafe.GridUnsafeMemory;
 import org.apache.ignite.internal.util.typedef.F;
-import org.apache.ignite.internal.util.typedef.T3;
+import org.apache.ignite.internal.util.typedef.internal.CU;
 import org.apache.ignite.internal.util.typedef.internal.LT;
 import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.internal.util.typedef.internal.SB;
 import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteBiClosure;
 import org.apache.ignite.lang.IgniteBiPredicate;
 import org.apache.ignite.lang.IgniteBiTuple;
 import org.apache.ignite.lang.IgniteFuture;
+import org.apache.ignite.lang.IgniteInClosure;
 import org.apache.ignite.marshaller.Marshaller;
 import org.apache.ignite.marshaller.jdk.JdkMarshaller;
+import org.apache.ignite.plugin.extensions.communication.Message;
 import org.apache.ignite.resources.LoggerResource;
-import org.apache.ignite.spi.IgniteSpiCloseableIterator;
 import org.apache.ignite.spi.indexing.IndexingQueryFilter;
+import org.h2.api.ErrorCode;
 import org.h2.api.JavaObjectSerializer;
 import org.h2.command.CommandInterface;
-import org.h2.constant.ErrorCode;
-import org.h2.constant.SysProperties;
+import org.h2.engine.Session;
+import org.h2.engine.SysProperties;
 import org.h2.index.Index;
 import org.h2.index.SpatialIndex;
+import org.h2.jdbc.JdbcConnection;
 import org.h2.jdbc.JdbcPreparedStatement;
 import org.h2.message.DbException;
 import org.h2.mvstore.cache.CacheLongKeyLIRS;
+import org.h2.result.SortOrder;
 import org.h2.server.web.WebServer;
 import org.h2.table.Column;
 import org.h2.table.IndexColumn;
 import org.h2.table.Table;
 import org.h2.tools.Server;
-import org.h2.util.Utils;
+import org.h2.util.JdbcUtils;
 import org.h2.value.DataType;
 import org.h2.value.Value;
 import org.h2.value.ValueArray;
@@ -149,6 +159,7 @@ import org.h2.value.ValueNull;
 import org.h2.value.ValueShort;
 import org.h2.value.ValueString;
 import org.h2.value.ValueTime;
+import org.h2.value.ValueTimestamp;
 import org.h2.value.ValueUuid;
 import org.jetbrains.annotations.Nullable;
 import org.jsr166.ConcurrentHashMap8;
@@ -161,9 +172,8 @@ import static org.apache.ignite.internal.processors.query.GridQueryIndexType.FUL
 import static org.apache.ignite.internal.processors.query.GridQueryIndexType.GEO_SPATIAL;
 import static org.apache.ignite.internal.processors.query.GridQueryIndexType.SORTED;
 import static org.apache.ignite.internal.processors.query.h2.opt.GridH2AbstractKeyValueRow.KEY_COL;
-import static org.apache.ignite.internal.processors.query.h2.opt.GridH2AbstractKeyValueRow.VAL_COL;
-import static org.h2.result.SortOrder.ASCENDING;
-import static org.h2.result.SortOrder.DESCENDING;
+import static org.apache.ignite.internal.processors.query.h2.opt.GridH2QueryType.LOCAL;
+import static org.apache.ignite.internal.processors.query.h2.opt.GridH2QueryType.PREPARE;
 
 /**
  * Indexing implementation based on H2 database engine. In this implementation main query language is SQL,
@@ -181,8 +191,10 @@ import static org.h2.result.SortOrder.DESCENDING;
 public class IgniteH2Indexing implements GridQueryIndexing {
     /** Default DB options. */
     private static final String DB_OPTIONS = ";LOCK_MODE=3;MULTI_THREADED=1;DB_CLOSE_ON_EXIT=FALSE" +
-        ";DEFAULT_LOCK_TIMEOUT=10000;FUNCTIONS_IN_SCHEMA=true;OPTIMIZE_REUSE_RESULTS=0;QUERY_CACHE_SIZE=0;" +
-        "RECOMPILE_ALWAYS=1;MAX_OPERATION_MEMORY=0";
+        ";DEFAULT_LOCK_TIMEOUT=10000;FUNCTIONS_IN_SCHEMA=true;OPTIMIZE_REUSE_RESULTS=0;QUERY_CACHE_SIZE=0" +
+        ";RECOMPILE_ALWAYS=1;MAX_OPERATION_MEMORY=0;NESTED_JOINS=0;BATCH_JOINS=1" +
+        ";ROW_FACTORY=\"" + GridH2RowFactory.class.getName() + "\"" +
+        ";DEFAULT_TABLE_ENGINE=" + GridH2DefaultTableEngine.class.getName();
 
     /** */
     private static final int PREPARED_STMT_CACHE_SIZE = 256;
@@ -219,9 +231,12 @@ public class IgniteH2Indexing implements GridQueryIndexing {
      * Command in H2 prepared statement.
      */
     static {
-        try {
-            System.setProperty("h2.objectCache", "false");
+        // Initialize system properties for H2.
+        System.setProperty("h2.objectCache", "false");
+        System.setProperty("h2.serializeJavaObject", "false");
+        System.setProperty("h2.objectCacheMaxPerElementSize", "0"); // Avoid ValueJavaObject caching.
 
+        try {
             COMMAND_FIELD = JdbcPreparedStatement.class.getDeclaredField("command");
 
             COMMAND_FIELD.setAccessible(true);
@@ -260,6 +275,9 @@ public class IgniteH2Indexing implements GridQueryIndexing {
     private final Map<String, String> space2schema = new ConcurrentHashMap8<>();
 
     /** */
+    private GridSpinBusyLock busyLock;
+
+    /** */
     private final ThreadLocal<ConnectionWrapper> connCache = new ThreadLocal<ConnectionWrapper>() {
         @Nullable @Override public ConnectionWrapper get() {
             ConnectionWrapper c = super.get();
@@ -304,13 +322,35 @@ public class IgniteH2Indexing implements GridQueryIndexing {
     /** */
     private volatile GridKernalContext ctx;
 
+    /** */
+    private final ConcurrentMap<String, GridH2Table> dataTables = new ConcurrentHashMap8<>();
+
     /** Statement cache. */
     private final ConcurrentHashMap<Thread, StatementCache> stmtCache = new ConcurrentHashMap<>();
 
     /** */
-    private final GridBoundedConcurrentLinkedHashMap<T3<String, String, Boolean>, TwoStepCachedQuery> twoStepCache =
+    private final GridBoundedConcurrentLinkedHashMap<TwoStepCachedQueryKey, TwoStepCachedQuery> twoStepCache =
         new GridBoundedConcurrentLinkedHashMap<>(TWO_STEP_QRY_CACHE_SIZE);
 
+    /** */
+    private final IgniteInClosure<? super IgniteInternalFuture<?>> logger = new IgniteInClosure<IgniteInternalFuture<?>>() {
+        @Override public void apply(IgniteInternalFuture<?> fut) {
+            try {
+                fut.get();
+            }
+            catch (IgniteCheckedException e) {
+                U.error(log, e.getMessage(), e);
+            }
+        }
+    };
+
+    /**
+     * @return Kernal context.
+     */
+    public GridKernalContext kernalContext() {
+        return ctx;
+    }
+
     /**
      * @param space Space.
      * @return Connection.
@@ -695,8 +735,6 @@ public class IgniteH2Indexing implements GridQueryIndexing {
             U.close(stmt, log);
         }
 
-        tbl.tbl.close();
-
         if (tbl.luceneIdx != null)
             U.closeQuiet(tbl.luceneIdx);
 
@@ -705,7 +743,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
 
     /** {@inheritDoc} */
     @SuppressWarnings("unchecked")
-    @Override public <K, V> GridCloseableIterator<IgniteBiTuple<K, V>> queryText(
+    @Override public <K, V> GridCloseableIterator<IgniteBiTuple<K, V>> queryLocalText(
         @Nullable String spaceName, String qry, GridQueryTypeDescriptor type,
         IndexingQueryFilter filters) throws IgniteCheckedException {
         TableDescriptor tbl = tableDescriptor(spaceName, type);
@@ -727,14 +765,14 @@ public class IgniteH2Indexing implements GridQueryIndexing {
 
     /** {@inheritDoc} */
     @SuppressWarnings("unchecked")
-    @Override public GridQueryFieldsResult queryFields(@Nullable final String spaceName, final String qry,
-        @Nullable final Collection<Object> params, final IndexingQueryFilter filters)
+    @Override public GridQueryFieldsResult queryLocalSqlFields(@Nullable final String spaceName, final String qry,
+        @Nullable final Collection<Object> params, final IndexingQueryFilter filters, boolean enforceJoinOrder)
         throws IgniteCheckedException {
-        setFilters(filters);
+        Connection conn = connectionForSpace(spaceName);
 
-        try {
-            Connection conn = connectionForThread(schema(spaceName));
+        initLocalQueryContext(conn, enforceJoinOrder, filters);
 
+        try {
             ResultSet rs = executeSqlQueryWithTimer(spaceName, conn, qry, params, true);
 
             List<GridQueryFieldMetadata> meta = null;
@@ -751,7 +789,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
             return new GridQueryFieldsResultAdapter(meta, new FieldsIterator(rs));
         }
         finally {
-            setFilters(null);
+            GridH2QueryContext.clearThreadLocal();
         }
     }
 
@@ -897,25 +935,6 @@ public class IgniteH2Indexing implements GridQueryIndexing {
     }
 
     /**
-     * Executes query.
-     *
-     * @param space Space.
-     * @param qry Query.
-     * @param params Query parameters.
-     * @param tbl Target table of query to generate select.
-     * @return Result set.
-     * @throws IgniteCheckedException If failed.
-     */
-    private ResultSet executeQuery(String space, String qry, @Nullable Collection<Object> params, TableDescriptor tbl)
-            throws IgniteCheckedException {
-        Connection conn = connectionForThread(tbl.schemaName());
-
-        String sql = generateQuery(qry, tbl);
-
-        return executeSqlQueryWithTimer(space, conn, sql, params, true);
-    }
-
-    /**
      * Binds parameters to prepared statement.
      *
      * @param stmt Prepared statement.
@@ -932,43 +951,66 @@ public class IgniteH2Indexing implements GridQueryIndexing {
     }
 
     /**
-     * Executes regular query.
-     *
-     * @param spaceName Space name.
-     * @param qry Query.
-     * @param params Query parameters.
-     * @param type Query return type.
-     * @param filters Space name and key filters.
-     * @return Queried rows.
-     * @throws IgniteCheckedException If failed.
+     * @param conn Connection.
+     * @param enforceJoinOrder Enforce join order of tables.
+     * @param filter Filter.
      */
+    private void initLocalQueryContext(Connection conn, boolean enforceJoinOrder, IndexingQueryFilter filter) {
+        setupConnection(conn, false, enforceJoinOrder);
+
+        GridH2QueryContext.set(new GridH2QueryContext(nodeId, nodeId, 0, LOCAL).filter(filter).distributedJoins(false));
+    }
+
+    /**
+     * @param conn Connection to use.
+     * @param distributedJoins If distributed joins are enabled.
+     * @param enforceJoinOrder Enforce join order of tables.
+     */
+    public void setupConnection(Connection conn, boolean distributedJoins, boolean enforceJoinOrder) {
+        Session s = session(conn);
+
+        s.setForceJoinOrder(enforceJoinOrder);
+        s.setJoinBatchEnabled(distributedJoins);
+    }
+
+    /** {@inheritDoc} */
     @SuppressWarnings("unchecked")
-    @Override public <K, V> GridCloseableIterator<IgniteBiTuple<K, V>> query(@Nullable String spaceName,
+    @Override public <K, V> GridCloseableIterator<IgniteBiTuple<K, V>> queryLocalSql(@Nullable String spaceName,
         final String qry, @Nullable final Collection<Object> params, GridQueryTypeDescriptor type,
-        final IndexingQueryFilter filters) throws IgniteCheckedException {
+        final IndexingQueryFilter filter) throws IgniteCheckedException {
         final TableDescriptor tbl = tableDescriptor(spaceName, type);
 
         if (tbl == null)
             throw new CacheException("Failed to find SQL table for type: " + type.name());
 
-        setFilters(filters);
+        String sql = generateQuery(qry, tbl);
+
+        Connection conn = connectionForThread(tbl.schemaName());
+
+        initLocalQueryContext(conn, false, filter);
 
         try {
-            ResultSet rs = executeQuery(spaceName, qry, params, tbl);
+            ResultSet rs = executeSqlQueryWithTimer(spaceName, conn, sql, params, true);
 
             return new KeyValIterator(rs);
         }
         finally {
-            setFilters(null);
+            GridH2QueryContext.clearThreadLocal();
         }
     }
 
-    /** {@inheritDoc} */
-    @Override public Iterable<List<?>> queryTwoStep(final GridCacheContext<?,?> cctx, final GridCacheTwoStepQuery qry,
-        final boolean keepCacheObj) {
+    /**
+     * @param cctx Cache context.
+     * @param qry Query.
+     * @param keepCacheObj Flag to keep cache object.
+     * @param enforceJoinOrder Enforce join order of tables.
+     * @return Iterable result.
+     */
+    private Iterable<List<?>> runQueryTwoStep(final GridCacheContext<?,?> cctx, final GridCacheTwoStepQuery qry,
+        final boolean keepCacheObj, final boolean enforceJoinOrder) {
         return new Iterable<List<?>>() {
             @Override public Iterator<List<?>> iterator() {
-                return rdcQryExec.query(cctx, qry, keepCacheObj);
+                return rdcQryExec.query(cctx, qry, keepCacheObj, enforceJoinOrder);
             }
         };
     }
@@ -997,6 +1039,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
 
         fqry.setArgs(qry.getArgs());
         fqry.setPageSize(qry.getPageSize());
+        fqry.setDistributedJoins(qry.isDistributedJoins());
 
         final QueryCursor<List<?>> res = queryTwoStep(cctx, fqry);
 
@@ -1030,6 +1073,14 @@ public class IgniteH2Indexing implements GridQueryIndexing {
         };
     }
 
+    /**
+     * @param c Connection.
+     * @return Session.
+     */
+    public static Session session(Connection c) {
+        return (Session)((JdbcConnection)c).getSession();
+    }
+
     /** {@inheritDoc} */
     @Override public QueryCursor<List<?>> queryTwoStep(GridCacheContext<?,?> cctx, SqlFieldsQuery qry) {
         final String space = cctx.name();
@@ -1037,10 +1088,15 @@ public class IgniteH2Indexing implements GridQueryIndexing {
 
         Connection c = connectionForSpace(space);
 
+        final boolean enforceJoinOrder = qry.isEnforceJoinOrder();
+        final boolean distributedJoins = qry.isDistributedJoins() && cctx.isPartitioned();
+        final boolean grpByCollocated = qry.isCollocated();
+
         GridCacheTwoStepQuery twoStepQry;
         List<GridQueryFieldMetadata> meta;
 
-        final T3<String, String, Boolean> cachedQryKey = new T3<>(space, sqlQry, qry.isCollocated());
+        final TwoStepCachedQueryKey cachedQryKey = new TwoStepCachedQueryKey(space, sqlQry, grpByCollocated,
+            distributedJoins, enforceJoinOrder);
         TwoStepCachedQuery cachedQry = twoStepCache.get(cachedQryKey);
 
         if (cachedQry != null) {
@@ -1048,51 +1104,95 @@ public class IgniteH2Indexing implements GridQueryIndexing {
             meta = cachedQry.meta;
         }
         else {
+            final UUID locNodeId = ctx.localNodeId();
+
+            setupConnection(c, distributedJoins, enforceJoinOrder);
+
+            GridH2QueryContext.set(new GridH2QueryContext(locNodeId, locNodeId, 0, PREPARE)
+                .distributedJoins(distributedJoins));
+
             PreparedStatement stmt;
 
             boolean cachesCreated = false;
 
-            while (true) {
-                try {
-                    // Do not cache this statement because the whole two step query object will be cached later on.
-                    stmt = prepareStatement(c, sqlQry, false);
+            try {
+                while (true) {
+                    try {
+                        // Do not cache this statement because the whole two step query object will be cached later on.
+                        stmt = prepareStatement(c, sqlQry, false);
 
-                    break;
-                }
-                catch (SQLException e) {
-                    if (!cachesCreated && e.getErrorCode() == ErrorCode.SCHEMA_NOT_FOUND_1) {
-                        try {
-                            ctx.cache().createMissingCaches();
-                        }
-                        catch (IgniteCheckedException e1) {
-                            throw new CacheException("Failed to create missing caches.", e);
-                        }
+                        break;
+                    }
+                    catch (SQLException e) {
+                        if (!cachesCreated && e.getErrorCode() == ErrorCode.SCHEMA_NOT_FOUND_1) {
+                            try {
+                                ctx.cache().createMissingCaches();
+                            }
+                            catch (IgniteCheckedException e1) {
+                                throw new CacheException("Failed to create missing caches.", e);
+                            }
 
-                        cachesCreated = true;
+                            cachesCreated = true;
+                        }
+                        else
+                            throw new CacheException("Failed to parse query: " + sqlQry, e);
                     }
-                    else
-                        throw new CacheException("Failed to parse query: " + sqlQry, e);
                 }
             }
+            finally {
+                GridH2QueryContext.clearThreadLocal();
+            }
 
             try {
-                try {
-                    bindParameters(stmt, F.asList(qry.getArgs()));
-                }
-                catch (IgniteCheckedException e) {
-                    throw new CacheException("Failed to bind parameters: [qry=" + sqlQry + ", params=" +
-                        Arrays.deepToString(qry.getArgs()) + "]", e);
-                }
+                bindParameters(stmt, F.asList(qry.getArgs()));
 
-                try {
-                    twoStepQry = GridSqlQuerySplitter.split(
-                        (JdbcPreparedStatement)stmt, qry.getArgs(), qry.isCollocated(), this);
+                twoStepQry = GridSqlQuerySplitter.split((JdbcPreparedStatement)stmt, qry.getArgs(), grpByCollocated,
+                    distributedJoins);
 
-                    meta = meta(stmt.getMetaData());
+                List<Integer> caches;
+                List<Integer> extraCaches = null;
+
+                // Setup spaces from schemas.
+                if (!twoStepQry.schemas().isEmpty()) {
+                    Collection<String> spaces = new ArrayList<>(twoStepQry.schemas().size());
+                    caches = new ArrayList<>(twoStepQry.schemas().size() + 1);
+                    caches.add(cctx.cacheId());
+
+                    for (String schema : twoStepQry.schemas()) {
+                        String space0 = space(schema);
+
+                        spaces.add(space0);
+
+                        if (!F.eq(space0, space)) {
+                            int cacheId = CU.cacheId(space0);
+
+                            caches.add(cacheId);
+
+                            if (extraCaches == null)
+                                extraCaches = new ArrayList<>();
+
+                            extraCaches.add(cacheId);
+                        }
+                    }
+
+                    twoStepQry.spaces(spaces);
                 }
-                catch (SQLException e) {
-                    throw new CacheException(e);
+                else {
+                    caches = Collections.singletonList(cctx.cacheId());
+                    extraCaches = null;
                 }
+
+                twoStepQry.caches(caches);
+                twoStepQry.extraCaches(extraCaches);
+
+                meta = meta(stmt.getMetaData());
+            }
+            catch (IgniteCheckedException e) {
+                throw new CacheException("Failed to bind parameters: [qry=" + sqlQry + ", params=" +
+                    Arrays.deepToString(qry.getArgs()) + "]", e);
+            }
+            catch (SQLException e) {
+                throw new CacheException(e);
             }
             finally {
                 U.close(stmt, log);
@@ -1104,7 +1204,8 @@ public class IgniteH2Indexing implements GridQueryIndexing {
 
         twoStepQry.pageSize(qry.getPageSize());
 
-        QueryCursorImpl<List<?>> cursor = new QueryCursorImpl<>(queryTwoStep(cctx, twoStepQry, cctx.keepBinary()));
+        QueryCursorImpl<List<?>> cursor = new QueryCursorImpl<>(
+            runQueryTwoStep(cctx, twoStepQry, cctx.keepBinary(), enforceJoinOrder));
 
         cursor.fieldsMeta(meta);
 
@@ -1117,17 +1218,6 @@ public class IgniteH2Indexing implements GridQueryIndexing {
     }
 
     /**
-     * Sets filters for current thread. Must be set to not null value
-     * before executeQuery and reset to null after in finally block since it signals
-     * to table that it should return content without expired values.
-     *
-     * @param filters Filters.
-     */
-    public void setFilters(@Nullable IndexingQueryFilter filters) {
-        GridH2IndexBase.setFiltersForThread(filters);
-    }
-
-    /**
      * Prepares statement for query.
      *
      * @param qry Query string.
@@ -1331,7 +1421,18 @@ public class IgniteH2Indexing implements GridQueryIndexing {
 
         GridH2RowDescriptor desc = new RowDescriptor(tbl.type(), schema);
 
-        GridH2Table.Engine.createTable(conn, sql.toString(), desc, tbl, tbl.schema.spaceName);
+        GridH2Table res = GridH2Table.Engine.createTable(conn, sql.toString(), desc, tbl, tbl.schema.spaceName);
+
+        if (dataTables.putIfAbsent(res.identifier(), res) != null)
+            throw new IllegalStateException("Table already exists: " + res.identifier());
+    }
+
+    /**
+     * @param identifier Table identifier.
+     * @return Data table.
+     */
+    public GridH2Table dataTable(String identifier) {
+        return dataTables.get(identifier);
     }
 
     /**
@@ -1447,18 +1548,43 @@ public class IgniteH2Indexing implements GridQueryIndexing {
         tbl.tbl.rebuildIndexes();
     }
 
-    /** {@inheritDoc} */
-    @Override public long size(@Nullable String spaceName, GridQueryTypeDescriptor type,
-        IndexingQueryFilter filters) throws IgniteCheckedException {
+    /**
+     * Gets size (for tests only).
+     *
+     * @param spaceName Space name.
+     * @param type Type descriptor.
+     * @return Size.
+     * @throws IgniteCheckedException If failed or {@code -1} if the type is unknown.
+     */
+    long size(@Nullable String spaceName, GridQueryTypeDescriptor type) throws IgniteCheckedException {
         TableDescriptor tbl = tableDescriptor(spaceName, type);
 
         if (tbl == null)
             return -1;
 
-        IgniteSpiCloseableIterator<List<?>> iter = queryFields(spaceName,
-            "SELECT COUNT(*) FROM " + tbl.fullTableName(), null, null).iterator();
+        Connection conn = connectionForSpace(spaceName);
+
+        setupConnection(conn, false, false);
+
+        ResultSet rs = executeSqlQuery(conn,
+            "SELECT COUNT(*) FROM " + tbl.fullTableName(), null, false);
+
+        try {
+            if (!rs.next())
+                throw new IllegalStateException();
 
-        return ((Number)iter.next().get(0)).longValue();
+            return rs.getLong(1);
+        }
+        catch (SQLException e) {
+            throw new IgniteCheckedException(e);
+        }
+    }
+
+    /**
+     * @return Busy lock.
+     */
+    public GridSpinBusyLock busyLock() {
+        return busyLock;
     }
 
     /**
@@ -1481,8 +1607,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
         if (log.isDebugEnabled())
             log.debug("Starting cache query index...");
 
-        System.setProperty("h2.serializeJavaObject", "false");
-        System.setProperty("h2.objectCacheMaxPerElementSize", "0"); // Avoid ValueJavaObject caching.
+        this.busyLock = busyLock;
 
         if (SysProperties.serializeJavaObject) {
             U.warn(log, "Serialization of Java objects in H2 was enabled.");
@@ -1490,10 +1615,10 @@ public class IgniteH2Indexing implements GridQueryIndexing {
             SysProperties.serializeJavaObject = false;
         }
 
-        if (Utils.serializer != null)
+        if (JdbcUtils.serializer != null)
             U.warn(log, "Custom H2 serialization is already configured, will override.");
 
-        Utils.serializer = h2Serializer();
+        JdbcUtils.serializer = h2Serializer();
 
         String dbName = (ctx != null ? ctx.localNodeId() : UUID.randomUUID()).toString();
 
@@ -1522,8 +1647,11 @@ public class IgniteH2Indexing implements GridQueryIndexing {
             throw new IgniteCheckedException(e);
         }
 
-        if (ctx == null) // This is allowed in some tests.
+        if (ctx == null) {
+            // This is allowed in some tests.
+            nodeId = UUID.randomUUID();
             marshaller = new JdkMarshaller();
+        }
         else {
             this.ctx = ctx;
 
@@ -1548,9 +1676,92 @@ public class IgniteH2Indexing implements GridQueryIndexing {
     }
 
     /**
+     * @param topic Topic.
+     * @param topicOrd Topic ordinal for {@link GridTopic}.
+     * @param nodes Nodes.
+     * @param msg Message.
+     * @param specialize Optional closure to specialize message for each node.
+     * @param locNodeHnd Handler for local node.
+     * @param plc Policy identifying the executor service which will process message.
+     * @param runLocParallel Run local handler in parallel thread.
+     * @return {@code true} If all messages sent successfully.
+     */
+    public boolean send(
+        Object topic,
+        int topicOrd,
+        Collection<ClusterNode> nodes,
+        Message msg,
+        @Nullable IgniteBiClosure<ClusterNode, Message, Message> specialize,
+        @Nullable final IgniteInClosure2X<ClusterNode, Message> locNodeHnd,
+        byte plc,
+        boolean runLocParallel
+    ) {
+        boolean ok = true;
+
+        if (specialize == null && msg instanceof GridCacheQueryMarshallable)
+            ((GridCacheQueryMarshallable)msg).marshall(marshaller);
+
+        ClusterNode locNode = null;
+
+        for (ClusterNode node : nodes) {
+            if (node.isLocal()) {
+                locNode = node;
+
+                continue;
+            }
+
+            try {
+                if (specialize != null) {
+                    msg = specialize.apply(node, msg);
+
+                    if (msg instanceof GridCacheQueryMarshallable)
+                        ((GridCacheQueryMarshallable)msg).marshall(marshaller);
+                }
+
+                ctx.io().send(node, topic, topicOrd, msg, plc);
+            }
+            catch (IgniteCheckedException e) {
+                ok = false;
+
+                U.warn(log, "Failed to send message [node=" + node + ", msg=" + msg +
+                    ", errMsg=" + e.getMessage() + "]");
+            }
+        }
+
+        // Local node goes the last to allow parallel execution.
+        if (locNode != null) {
+            if (specialize != null)
+                msg = specialize.apply(locNode, msg);
+
+            if (runLocParallel) {
+                final ClusterNode finalLocNode = locNode;
+                final Message finalMsg = msg;
+
+                try {
+                    // We prefer runLocal to runLocalSafe, because the latter can produce deadlock here.
+                    ctx.closure().runLocal(new GridPlainRunnable() {
+                        @Override public void run() {
+                            locNodeHnd.apply(finalLocNode, finalMsg);
+                        }
+                    }, plc).listen(logger);
+                }
+                catch (IgniteCheckedException e) {
+                    ok = false;
+
+                    U.error(log, "Failed to execute query locally.", e);
+                }
+            }
+            else
+                locNodeHnd.apply(locNode, msg);
+        }
+
+        return ok;
+    }
+
+    /**
      * @return Serializer.
      */
-    protected JavaObjectSerializer h2Serializer() {
+    private JavaObjectSerializer h2Serializer() {
         return new JavaObjectSerializer() {
                 @Override public byte[] serialize(Object obj) throws Exception {
                     return marshaller.marshal(obj);
@@ -1607,8 +1818,6 @@ public class IgniteH2Indexing implements GridQueryIndexing {
 
         for (Schema schema : schemas.values()) {
             for (TableDescriptor desc : schema.tbls.values()) {
-                desc.tbl.close();
-
                 if (desc.luceneIdx != null)
                     U.closeQuiet(desc.luceneIdx);
             }
@@ -1632,18 +1841,19 @@ public class IgniteH2Indexing implements GridQueryIndexing {
         if (stmtCacheCleanupTask != null)
             stmtCacheCleanupTask.close();
 
+        GridH2QueryContext.clearLocalNodeStop(nodeId);
+
         if (log.isDebugEnabled())
             log.debug("Cache query index stopped.");
     }
 
     /** {@inheritDoc} */
-    @Override public void registerCache(CacheConfiguration<?,?> ccfg) throws IgniteCheckedException {
+    @Override public void registerCache(GridCacheContext<?, ?> cctx, CacheConfiguration<?,?> ccfg)
+        throws IgniteCheckedException {
         String schema = schemaNameFromCacheConf(ccfg);
 
-        if (schemas.putIfAbsent(schema, new Schema(ccfg.getName(), schema,
-            ccfg.getOffHeapMaxMemory() >= 0 || ccfg.getMemoryMode() == CacheMemoryMode.OFFHEAP_TIERED ?
-            new GridUnsafeMemory(0) : null, ccfg)) != null)
-            throw new IgniteCheckedException("Schema for cache already registered: " + U.maskName(ccfg.getName()));
+        if (schemas.putIfAbsent(schema, new Schema(ccfg.getName(), schema, cctx, ccfg)) != null)
+            throw new IgniteCheckedException("Cache already registered: " + U.maskName(ccfg.getName()));
 
         space2schema.put(emptyIfNull(ccfg.getName()), schema);
 
@@ -1661,6 +1871,8 @@ public class IgniteH2Indexing implements GridQueryIndexing {
             space2schema.remove(emptyIfNull(rmv.spaceName));
             mapQryExec.onCacheStop(ccfg.getName());
 
+            rmv.onDrop();
+
             try {
                 dropSchema(schema);
             }
@@ -1668,11 +1880,11 @@ public class IgniteH2Indexing implements GridQueryIndexing {
                 U.error(log, "Failed to drop schema on cache stop (will ignore): " + U.maskName(ccfg.getName()), e);
             }
 
-            for (Iterator<Map.Entry<T3<String, String, Boolean>, TwoStepCachedQuery>> it = twoStepCache.entrySet().iterator();
+            for (Iterator<Map.Entry<TwoStepCachedQueryKey, TwoStepCachedQuery>> it = twoStepCache.entrySet().iterator();
                  it.hasNext();) {
-                Map.Entry<T3<String, String, Boolean>, TwoStepCachedQuery> e = it.next();
+                Map.Entry<TwoStepCachedQueryKey, TwoStepCachedQuery> e = it.next();
 
-                if (F.eq(e.getKey().get1(), ccfg.getName()))
+                if (F.eq(e.getKey().space, ccfg.getName()))
                     it.remove();
             }
         }
@@ -1680,7 +1892,6 @@ public class IgniteH2Indexing implements GridQueryIndexing {
 
     /** {@inheritDoc} */
     @Override public IndexingQueryFilter backupFilter(
-        @Nullable final List<String> caches,
         @Nullable final AffinityTopologyVersion topVer,
         @Nullable final int[] parts
     ) {
@@ -1735,6 +1946,10 @@ public class IgniteH2Indexing implements GridQueryIndexing {
             @Override public boolean isValueRequired() {
                 return false;
             }
+
+            @Override public String toString() {
+                return "IndexingQueryFilter [ver=" + topVer + ']';
+            }
         };
     }
 
@@ -1762,6 +1977,81 @@ public class IgniteH2Indexing implements GridQueryIndexing {
     }
 
     /**
+     * Key for cached two-step query.
+     */
+    private static final class TwoStepCachedQueryKey {
+        /** */
+        private final String space;
+
+        /** */
+        private final String sql;
+
+        /** */
+        private final boolean grpByCollocated;
+
+        /** */
+        private final boolean distributedJoins;
+
+        /** */
+        private final boolean enforceJoinOrder;
+
+        /**
+         * @param space Space.
+         * @param sql Sql.
+         * @param grpByCollocated Collocated GROUP BY.
+         * @param distributedJoins Distributed joins enabled.
+         * @param enforceJoinOrder Enforce join order of tables.
+         */
+        private TwoStepCachedQueryKey(String space,
+            String sql,
+            boolean grpByCollocated,
+            boolean distributedJoins,
+            boolean enforceJoinOrder) {
+            this.space = space;
+            this.sql = sql;
+            this.grpByCollocated = grpByCollocated;
+            this.distributedJoins = distributedJoins;
+            this.enforceJoinOrder = enforceJoinOrder;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean equals(Object o) {
+            if (this == o)
+                return true;
+
+            if (o == null || getClass() != o.getClass())
+                return false;
+
+            TwoStepCachedQueryKey that = (TwoStepCachedQueryKey)o;
+
+            if (grpByCollocated != that.grpByCollocated)
+                return false;
+
+            if (distributedJoins != that.distributedJoins)
+                return false;
+
+            if (enforceJoinOrder != that.enforceJoinOrder)
+                return false;
+
+            if (space != null ? !space.equals(that.space) : that.space != null)
+                return false;
+
+            return sql.equals(that.sql);
+        }
+
+        /** {@inheritDoc} */
+        @Override public int hashCode() {
+            int res = space != null ? space.hashCode() : 0;
+            res = 31 * res + sql.hashCode();
+            res = 31 * res + (grpByCollocated ? 1 : 0);
+            res = 31 * res + (distributedJoins ? 1 : 0);
+            res = 31 * res + (enforceJoinOrder ? 1 : 0);
+
+            return res;
+        }
+    }
+
+    /**
      * Cached two-step query.
      */
     private static final class TwoStepCachedQuery {
@@ -1787,6 +2077,47 @@ public class IgniteH2Indexing implements GridQueryIndexing {
     }
 
     /**
+     * @param c1 First column.
+     * @param c2 Second column.
+     * @return {@code true} If they are the same.
+     */
+    private static boolean equal(IndexColumn c1, IndexColumn c2) {
+        return c1.column.getColumnId() == c2.column.getColumnId();
+    }
+
+    /**
+     * @param cols Columns list.
+     * @param col Column to find.
+     * @return {@code true} If found.
+     */
+    private static boolean containsColumn(List<IndexColumn> cols, IndexColumn col) {
+        for (int i = cols.size() - 1; i >= 0; i--) {
+            if (equal(cols.get(i), col))
+                return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * @param cols Columns list.
+     * @param keyCol Primary key column.
+     * @param affCol Affinity key column.
+     * @return The same list back.
+     */
+    private static List<IndexColumn> treeIndexColumns(List<IndexColumn> cols, IndexColumn keyCol, IndexColumn affCol) {
+        assert keyCol != null;
+
+        if (!containsColumn(cols, keyCol))
+            cols.add(keyCol);
+
+        if (affCol != null && !containsColumn(cols, affCol))
+            cols.add(affCol);
+
+        return cols;
+    }
+
+    /**
      * Wrapper to store connection and flag is schema set or not.
      */
     private static class ConnectionWrapper {
@@ -2033,7 +2364,15 @@ public class IgniteH2Indexing implements GridQueryIndexing {
 
             ArrayList<Index> idxs = new ArrayList<>();
 
-            idxs.add(new GridH2TreeIndex("_key_PK", tbl, true, KEY_COL, VAL_COL, tbl.indexColumn(0, ASCENDING)));
+            IndexColumn keyCol = tbl.indexColumn(KEY_COL, SortOrder.ASCENDING);
+            IndexColumn affCol = tbl.getAffinityKeyColumn();
+
+            if (affCol != null && equal(affCol, keyCol))
+                affCol = null;
+
+            // Add primary key index.
+            idxs.add(new GridH2TreeIndex("_key_PK", tbl, true,
+                treeIndexColumns(new ArrayList<IndexColumn>(2), keyCol, affCol)));
 
             if (type().valueClass() == String.class) {
                 try {
@@ -2044,6 +2383,8 @@ public class IgniteH2Indexing implements GridQueryIndexing {
                 }
             }
 
+            boolean affIdxFound = false;
+
             for (Map.Entry<String, GridQueryIndexDescriptor> e : type.indexes().entrySet()) {
                 String name = e.getKey();
                 GridQueryIndexDescriptor idx = e.getValue();
@@ -2057,30 +2398,40 @@ public class IgniteH2Indexing implements GridQueryIndexing {
                     }
                 }
                 else {
-                    IndexColumn[] cols = new IndexColumn[idx.fields().size()];
-
-                    int i = 0;
+                    List<IndexColumn> cols = new ArrayList<>(idx.fields().size() + 2);
 
                     boolean escapeAll = schema.escapeAll();
 
                     for (String field : idx.fields()) {
-                        // H2 reserved keywords used as column name is case sensitive.
                         String fieldName = escapeAll ? field : escapeName(field, false).toUpperCase();
 
                         Column col = tbl.getColumn(fieldName);
 
-                        cols[i++] = tbl.indexColumn(col.getColumnId(), idx.descending(field) ? DESCENDING : ASCENDING);
+                        cols.add(tbl.indexColumn(col.getColumnId(),
+                            idx.descending(field) ? SortOrder.DESCENDING : SortOrder.ASCENDING));
                     }
 
-                    if (idx.type() == SORTED)
-                        idxs.add(new GridH2TreeIndex(name, tbl, false, KEY_COL, VAL_COL, cols));
+                    if (idx.type() == SORTED) {
+                        // We don't care about number of fields in affinity index, just affinity key must be the first.
+                        affIdxFound |= affCol != null && equal(cols.get(0), affCol);
+
+                        cols = treeIndexColumns(cols, keyCol, affCol);
+
+                        idxs.add(new GridH2TreeIndex(name, tbl, false, cols));
+                    }
                     else if (idx.type() == GEO_SPATIAL)
-                        idxs.add(createH2SpatialIndex(tbl, name, cols, KEY_COL, VAL_COL));
+                        idxs.add(createH2SpatialIndex(tbl, name, cols.toArray(new IndexColumn[cols.size()])));
                     else
-                        throw new IllegalStateException();
+                        throw new IllegalStateException("Index type: " + idx.type());
                 }
             }
 
+            // Add explicit affinity key index if nothing alike was found.
+            if (affCol != null && !affIdxFound) {
+                idxs.add(new GridH2TreeIndex("AFFINITY_KEY", tbl, false,
+                    treeIndexColumns(new ArrayList<IndexColumn>(2), affCol, keyCol)));
+            }
+
             return idxs;
         }
 
@@ -2088,15 +2439,11 @@ public class IgniteH2Indexing implements GridQueryIndexing {
          * @param tbl Table.
          * @param idxName Index name.
          * @param cols Columns.
-         * @param keyCol Key column.
-         * @param valCol Value column.
          */
         private SpatialIndex createH2SpatialIndex(
             Table tbl,
             String idxName,
-            IndexColumn[] cols,
-            int keyCol,
-            int valCol
+            IndexColumn[] cols
         ) {
             String className = "org.apache.ignite.internal.processors.query.h2.opt.GridH2SpatialIndex";
 
@@ -2106,14 +2453,12 @@ public class IgniteH2Indexing implements GridQueryIndexing {
                 Constructor<?> ctor = cls.getConstructor(
                     Table.class,
                     String.class,
-                    IndexColumn[].class,
-                    int.class,
-                    int.class);
+                    IndexColumn[].class);
 
                 if (!ctor.isAccessible())
                     ctor.setAccessible(true);
 
-                return (SpatialIndex)ctor.newInstance(tbl, idxName, cols, keyCol, valCol);
+                return (SpatialIndex)ctor.newInstance(tbl, idxName, cols);
             }
             catch (Exception e) {
                 throw new IgniteException("Failed to instantiate: " + className, e);
@@ -2162,6 +2507,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
         }
 
         /** {@inheritDoc} */
+        @SuppressWarnings("unchecked")
         @Override protected IgniteBiTuple<K, V> createRow() {
             K key = (K)row[0];
             V val = (V)row[1];
@@ -2256,10 +2602,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
     /**
      * Database schema object.
      */
-    private static class Schema {
-        /** */
-        private static final long serialVersionUID = 0L;
-
+    private class Schema {
         /** */
         private final String spaceName;
 
@@ -2276,23 +2619,33 @@ public class IgniteH2Indexing implements GridQueryIndexing {
         private final CacheLongKeyLIRS<GridH2KeyValueRowOffheap> rowCache;
 
         /** */
+        private final GridCacheContext<?,?> cctx;
+
+        /** */
         private final CacheConfiguration<?,?> ccfg;
 
         /**
          * @param spaceName Space name.
          * @param schemaName Schema name.
-         * @param offheap Offheap memory.
+         * @param cctx Cache context.
          * @param ccfg Cache configuration.
          */
-        private Schema(@Nullable String spaceName, String schemaName, GridUnsafeMemory offheap, CacheConfiguration<?,?> ccfg) {
+        private Schema(String spaceName, String schemaName, GridCacheContext<?,?> cctx, CacheConfiguration<?,?> ccfg) {
             this.spaceName = spaceName;
+            this.cctx = cctx;
             this.schemaName = schemaName;
-            this.offheap = offheap;
             this.ccfg = ccfg;
 
-            if (offheap != null)
-                rowCache = new CacheLongKeyLIRS<>(ccfg.getSqlOnheapRowCacheSize(), 1, 128, 256);
-            else
+            offheap = ccfg.getOffHeapMaxMemory() >= 0 || ccfg.getMemoryMode() == CacheMemoryMode.OFFHEAP_TIERED ?
+                new GridUnsafeMemory(0) : null;
+
+            if (offheap != null) {
+                CacheLongKeyLIRS.Config lirsCfg = new CacheLongKeyLIRS.Config();
+
+                lirsCfg.maxMemory = ccfg.getSqlOnheapRowCacheSize();
+
+                rowCache = new CacheLongKeyLIRS<>(lirsCfg);
+            } else
                 rowCache = null;
         }
 
@@ -2310,6 +2663,19 @@ public class IgniteH2Indexing implements GridQueryIndexing {
         public boolean escapeAll() {
             return ccfg.isSqlEscapeAll();
         }
+
+        /**
+         * Called after the schema was dropped.
+         */
+        public void onDrop() {
+            for (TableDescriptor tblDesc : tbls.values()) {
+                GridH2Table tbl = tblDesc.tbl;
+
+                dataTables.remove(tbl.identifier(), tbl);
+
+                tbl.destroy();
+            }
+        }
     }
 
     /**
@@ -2390,6 +2756,26 @@ public class IgniteH2Indexing implements GridQueryIndexing {
         }
 
         /** {@inheritDoc} */
+        @Override public IgniteH2Indexing indexing() {
+            return IgniteH2Indexing.this;
+        }
+
+        /** {@inheritDoc} */
+        @Override public GridQueryTypeDescriptor type() {
+            return type;
+        }
+
+        /** {@inheritDoc} */
+        @Override public GridCacheContext<?,?> context() {
+            return schema.cctx;
+        }
+
+        /** {@inheritDoc} */
+        @Override public CacheConfiguration configuration() {
+            return schema.ccfg;
+        }
+
+        /** {@inheritDoc} */
         @Override public GridUnsafeGuard guard() {
             return guard;
         }
@@ -2414,11 +2800,6 @@ public class IgniteH2Indexing implements GridQueryIndexing {
         }
 
         /** {@inheritDoc} */
-        @Override public IgniteH2Indexing owner() {
-            return IgniteH2Indexing.this;
-        }
-
-        /** {@inheritDoc} */
         @Override public Value wrap(Object obj, int type) throws IgniteCheckedException {
             assert obj != null;
 
@@ -2457,7 +2838,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
                     if (obj instanceof java.util.Date && !(obj instanceof Timestamp))
                         obj = new Timestamp(((java.util.Date) obj).getTime());
 
-                    return GridH2Utils.toValueTimestamp((Timestamp)obj);
+                    return ValueTimestamp.get((Timestamp)obj);
                 case Value.DECIMAL:
                     return ValueDecimal.get((BigDecimal)obj);
                 case Value.STRING:
@@ -2491,7 +2872,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
             throws IgniteCheckedException {
             try {
                 if (val == null) // Only can happen for remove operation, can create simple search row.
-                    return new GridH2Row(wrap(key, keyType), null);
+                    return GridH2RowFactory.create(wrap(key, keyType));
 
                 return schema.offheap == null ?
                     new GridH2KeyValueRowOnheap(this, key, keyType, val, valType, expirationTime) :

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2AbstractKeyValueRow.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2AbstractKeyValueRow.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2AbstractKeyValueRow.java
index fe6851d..284f8c5 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2AbstractKeyValueRow.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2AbstractKeyValueRow.java
@@ -55,6 +55,15 @@ public abstract class GridH2AbstractKeyValueRow extends GridH2Row {
     @SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized")
     protected long expirationTime;
 
+    /** */
+    private Value key;
+
+    /** */
+    private volatile Value val;
+
+    /** */
+    private Value[] valCache;
+
     /**
      * Constructor.
      *
@@ -68,8 +77,6 @@ public abstract class GridH2AbstractKeyValueRow extends GridH2Row {
      */
     protected GridH2AbstractKeyValueRow(GridH2RowDescriptor desc, Object key, int keyType, @Nullable Object val,
         int valType, long expirationTime) throws IgniteCheckedException {
-        super(null, null);
-
         setValue(KEY_COL, desc.wrap(key, keyType));
 
         if (val != null) // We remove by key only, so value can be null here.
@@ -79,14 +86,17 @@ public abstract class GridH2AbstractKeyValueRow extends GridH2Row {
         this.expirationTime = expirationTime;
     }
 
+    /** {@inheritDoc} */
+    @Override public Value[] getValueList() {
+        throw new UnsupportedOperationException();
+    }
+
     /**
      * Protected constructor for {@link GridH2KeyValueRowOffheap}
      *
      * @param desc Row descriptor.
      */
     protected GridH2AbstractKeyValueRow(GridH2RowDescriptor desc) {
-        super(new Value[DEFAULT_COLUMNS_COUNT]);
-
         this.desc = desc;
     }
 
@@ -184,11 +194,20 @@ public abstract class GridH2AbstractKeyValueRow extends GridH2Row {
      * @return Value if exists.
      */
     protected final Value peekValue(int col) {
-        return getValueList()[col];
+        return col == KEY_COL ? key : val;
     }
 
     /** {@inheritDoc} */
     @Override public Value getValue(int col) {
+        Value[] vCache = valCache;
+
+        if (vCache != null) {
+            Value v = vCache[col];
+
+            if (v != null)
+                return v;
+        }
+
         if (col < DEFAULT_COLUMNS_COUNT) {
             Value v;
 
@@ -288,15 +307,35 @@ public abstract class GridH2AbstractKeyValueRow extends GridH2Row {
 
         Object res = desc.columnValue(key.getObject(), val.getObject(), col);
 
-        if (res == null)
-            return ValueNull.INSTANCE;
+        Value v;
 
-        try {
-            return desc.wrap(res, desc.fieldType(col));
+        if (res == null)
+            v = ValueNull.INSTANCE;
+        else {
+            try {
+                v = desc.wrap(res, desc.fieldType(col));
+            }
+            catch (IgniteCheckedException e) {
+                throw DbException.convert(e);
+            }
         }
-        catch (IgniteCheckedException e) {
-            throw DbException.convert(e);
+
+        if (vCache != null)
+            vCache[col + DEFAULT_COLUMNS_COUNT] = v;
+
+        return v;
+    }
+
+    /**
+     * @param valCache Value cache.
+     */
+    public void valuesCache(Value[] valCache) {
+        if (valCache != null) {
+            valCache[KEY_COL] = key;
+            valCache[VAL_COL] = val;
         }
+
+        this.valCache = valCache;
     }
 
     /**
@@ -467,6 +506,17 @@ public abstract class GridH2AbstractKeyValueRow extends GridH2Row {
     }
 
     /** {@inheritDoc} */
+    @Override public void setValue(int idx, Value v) {
+        if (idx == VAL_COL)
+            val = v;
+        else {
+            assert idx == KEY_COL : idx + " " + v;
+
+            key = v;
+        }
+    }
+
+    /** {@inheritDoc} */
     @Override public final int hashCode() {
         throw new IllegalStateException();
     }


[13/50] [abbrv] ignite git commit: ignite-1232 Distributed SQL joins implementation

Posted by vo...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlOperationType.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlOperationType.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlOperationType.java
index 38e3839..8d31651 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlOperationType.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlOperationType.java
@@ -119,7 +119,7 @@ public enum GridSqlOperationType {
 
         /** {@inheritDoc} */
         @Override public String getSql(GridSqlOperation operation) {
-            assert operation.opType().childrenCnt == 2;
+            assert operation.operationType().childrenCnt == 2;
 
             return '(' + operation.child(0).getSQL() + " " + delim + " " + operation.child(1).getSQL() + ')';
         }
@@ -132,7 +132,7 @@ public enum GridSqlOperationType {
 
         /** {@inheritDoc} */
         @Override public String getSql(GridSqlOperation operation) {
-            assert operation.opType().childrenCnt == 2;
+            assert operation.operationType().childrenCnt == 2;
 
             return "(INTERSECTS(" + operation.child(0).getSQL() + ", " + operation.child(1).getSQL() + "))";
         }
@@ -154,7 +154,7 @@ public enum GridSqlOperationType {
 
         /** {@inheritDoc} */
         @Override public String getSql(GridSqlOperation operation) {
-            assert operation.opType().childrenCnt == 1;
+            assert operation.operationType().childrenCnt == 1;
 
             return '(' + text + ' ' + operation.child().getSQL() + ')';
         }
@@ -176,7 +176,7 @@ public enum GridSqlOperationType {
 
         /** {@inheritDoc} */
         @Override public String getSql(GridSqlOperation operation) {
-            assert operation.opType().childrenCnt == 1;
+            assert operation.operationType().childrenCnt == 1;
 
             return '(' + operation.child().getSQL() + ' ' + text + ')';
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java
index 7001717..a7451c1 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java
@@ -21,8 +21,10 @@ import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.IdentityHashMap;
 import java.util.List;
+import javax.cache.CacheException;
 import org.apache.ignite.IgniteException;
 import org.h2.command.Command;
+import org.h2.command.CommandContainer;
 import org.h2.command.Prepared;
 import org.h2.command.dml.Explain;
 import org.h2.command.dml.Query;
@@ -48,6 +50,8 @@ import org.h2.expression.Parameter;
 import org.h2.expression.Subquery;
 import org.h2.expression.TableFunction;
 import org.h2.expression.ValueExpression;
+import org.h2.index.Index;
+import org.h2.index.ViewIndex;
 import org.h2.jdbc.JdbcPreparedStatement;
 import org.h2.result.SortOrder;
 import org.h2.table.Column;
@@ -91,14 +95,16 @@ import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlType.fro
 @SuppressWarnings("TypeMayBeWeakened")
 public class GridSqlQueryParser {
     /** */
-    private static final GridSqlOperationType[] OPERATION_OP_TYPES = new GridSqlOperationType[] {CONCAT, PLUS, MINUS, MULTIPLY, DIVIDE, null, MODULUS};
+    private static final GridSqlOperationType[] OPERATION_OP_TYPES =
+        {CONCAT, PLUS, MINUS, MULTIPLY, DIVIDE, null, MODULUS};
 
     /** */
-    private static final GridSqlOperationType[] COMPARISON_TYPES = new GridSqlOperationType[] {
-        EQUAL, BIGGER_EQUAL, BIGGER, SMALLER_EQUAL,
+    private static final GridSqlOperationType[] COMPARISON_TYPES =
+        {EQUAL, BIGGER_EQUAL, BIGGER, SMALLER_EQUAL,
         SMALLER, NOT_EQUAL, IS_NULL, IS_NOT_NULL,
-        null, null, null, SPATIAL_INTERSECTS /* 11 */, null, null, null, null, EQUAL_NULL_SAFE /* 16 */, null, null, null, null,
-        NOT_EQUAL_NULL_SAFE /* 21 */};
+        null, null, null, SPATIAL_INTERSECTS /* 11 */,
+        null, null, null, null, EQUAL_NULL_SAFE /* 16 */,
+        null, null, null, null, NOT_EQUAL_NULL_SAFE /* 21 */};
 
     /** */
     private static final Getter<Select, Expression> CONDITION = getter(Select.class, "condition");
@@ -134,7 +140,7 @@ public class GridSqlQueryParser {
     private static final Getter<ConditionAndOr, Expression> ANDOR_RIGHT = getter(ConditionAndOr.class, "right");
 
     /** */
-    private static final Getter<TableView, Query> VIEW_QUERY = getter(TableView.class, "viewQuery");
+    public static final Getter<TableView, Query> VIEW_QUERY = getter(TableView.class, "viewQuery");
 
     /** */
     private static final Getter<TableFilter, String> ALIAS = getter(TableFilter.class, "alias");
@@ -218,7 +224,8 @@ public class GridSqlQueryParser {
     private static final Getter<Explain, Prepared> EXPLAIN_COMMAND = getter(Explain.class, "command");
 
     /** */
-    private static volatile Getter<Command, Prepared> prepared;
+    private static final Getter<Command, Prepared> PREPARED =
+        GridSqlQueryParser.<Command, Prepared>getter(CommandContainer.class, "prepared");
 
     /** */
     private final IdentityHashMap<Object, Object> h2ObjToGridObj = new IdentityHashMap<>();
@@ -227,22 +234,12 @@ public class GridSqlQueryParser {
      * @param stmt Prepared statement.
      * @return Parsed select.
      */
-    public static GridSqlQuery parse(JdbcPreparedStatement stmt) {
+    public static Prepared prepared(JdbcPreparedStatement stmt) {
         Command cmd = COMMAND.get(stmt);
 
-        Getter<Command, Prepared> p = prepared;
+        assert cmd instanceof CommandContainer;
 
-        if (p == null) {
-            Class<? extends Command> cls = cmd.getClass();
-
-            assert "CommandContainer".equals(cls.getSimpleName());
-
-            prepared = p = getter(cls, "prepared");
-        }
-
-        Prepared statement = p.get(cmd);
-
-        return new GridSqlQueryParser().parse(statement);
+        return PREPARED.get(cmd);
     }
 
     /**
@@ -255,11 +252,15 @@ public class GridSqlQueryParser {
             Table tbl = filter.getTable();
 
             if (tbl instanceof TableBase)
-                res = new GridSqlTable(tbl.getSchema().getName(), tbl.getName());
+                res = new GridSqlTable(tbl);
             else if (tbl instanceof TableView) {
                 Query qry = VIEW_QUERY.get((TableView)tbl);
 
-                res = new GridSqlSubquery(parse(qry));
+                Index idx = filter.getIndex();
+
+                Query idxQry = idx instanceof ViewIndex ? ((ViewIndex)idx).getQuery() : null;
+
+                res = new GridSqlSubquery(parse(qry, idxQry));
             }
             else if (tbl instanceof FunctionTable)
                 res = parseExpression(FUNC_EXPR.get((FunctionTable)tbl), false);
@@ -286,7 +287,7 @@ public class GridSqlQueryParser {
     /**
      * @param select Select.
      */
-    public GridSqlSelect parse(Select select) {
+    public GridSqlSelect parse(Select select, @Nullable Query idxQry) {
         GridSqlSelect res = (GridSqlSelect)h2ObjToGridObj.get(select);
 
         if (res != null)
@@ -305,6 +306,9 @@ public class GridSqlQueryParser {
 
         TableFilter filter = select.getTopTableFilter();
 
+        if (idxQry instanceof Select)
+            filter = ((Select)idxQry).getTopTableFilter();
+
         do {
             assert0(filter != null, select);
             assert0(filter.getNestedJoin() == null, select);
@@ -366,11 +370,35 @@ public class GridSqlQueryParser {
     }
 
     /**
-     * @param qry Select.
+     * @param qry Prepared.
+     * @return Query.
+     */
+    public static Query query(Prepared qry) {
+        if (qry instanceof Query)
+            return (Query)qry;
+
+        if (qry instanceof Explain)
+            return query(EXPLAIN_COMMAND.get((Explain)qry));
+
+        throw new CacheException("Unsupported query: " + qry);
+    }
+
+    /**
+     * @param qry Prepared.
+     * @return Query.
      */
     public GridSqlQuery parse(Prepared qry) {
+        return parse(qry, null);
+    }
+
+    /**
+     * @param qry Select.
+     */
+    public GridSqlQuery parse(Prepared qry, @Nullable Query idxQry) {
+        assert qry != null;
+
         if (qry instanceof Select)
-            return parse((Select)qry);
+            return parse((Select)qry, idxQry);
 
         if (qry instanceof SelectUnion)
             return parse((SelectUnion)qry);
@@ -378,7 +406,7 @@ public class GridSqlQueryParser {
         if (qry instanceof Explain)
             return parse(EXPLAIN_COMMAND.get((Explain)qry)).explain(true);
 
-        throw new UnsupportedOperationException("Unknown query type: " + qry);
+        throw new CacheException("Unsupported query: " + qry);
     }
 
     /**
@@ -437,11 +465,12 @@ public class GridSqlQueryParser {
      */
     private GridSqlElement parseExpression0(Expression expression, boolean calcTypes) {
         if (expression instanceof ExpressionColumn) {
-            TableFilter tblFilter = ((ExpressionColumn)expression).getTableFilter();
-
-            GridSqlElement gridTblFilter = parseTable(tblFilter);
+            ExpressionColumn expCol = (ExpressionColumn)expression;
 
-            return new GridSqlColumn(gridTblFilter, expression.getColumnName(), expression.getSQL());
+            return new GridSqlColumn(expCol.getColumn(),
+                parseTable(expCol.getTableFilter()),
+                expression.getColumnName(),
+                expression.getSQL());
         }
 
         if (expression instanceof Alias)
@@ -475,12 +504,14 @@ public class GridSqlQueryParser {
 
             assert opType != null : COMPARISON_TYPE.get(cmp);
 
-            GridSqlElement left = parseExpression(COMPARISON_LEFT.get(cmp), calcTypes);
+            Expression leftExp = COMPARISON_LEFT.get(cmp);
+            GridSqlElement left = parseExpression(leftExp, calcTypes);
 
             if (opType.childrenCount() == 1)
                 return new GridSqlOperation(opType, left);
 
-            GridSqlElement right = parseExpression(COMPARISON_RIGHT.get(cmp), calcTypes);
+            Expression rightExp = COMPARISON_RIGHT.get(cmp);
+            GridSqlElement right = parseExpression(rightExp, calcTypes);
 
             return new GridSqlOperation(opType, left, right);
         }
@@ -685,7 +716,7 @@ public class GridSqlQueryParser {
      * Field getter.
      */
     @SuppressWarnings("unchecked")
-    private static class Getter<T, R> {
+    public static class Getter<T, R> {
         /** */
         private final Field fld;
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java
index 4d34ce8..7205a18 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java
@@ -23,18 +23,27 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Set;
 import org.apache.ignite.IgniteException;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.query.GridCacheSqlQuery;
 import org.apache.ignite.internal.processors.cache.query.GridCacheTwoStepQuery;
-import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
+import org.apache.ignite.lang.IgnitePredicate;
+import org.h2.command.Prepared;
 import org.h2.jdbc.JdbcPreparedStatement;
+import org.h2.table.Column;
+import org.h2.table.IndexColumn;
 import org.h2.util.IntArray;
 import org.jetbrains.annotations.Nullable;
 
+import static org.apache.ignite.internal.processors.query.h2.opt.GridH2CollocationModel.isCollocated;
 import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlFunctionType.AVG;
 import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlFunctionType.CAST;
 import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlFunctionType.COUNT;
 import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlFunctionType.SUM;
 import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlPlaceholder.EMPTY;
+import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlQueryParser.prepared;
+import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlQueryParser.query;
+import static org.apache.ignite.internal.processors.query.h2.twostep.GridReduceQueryExecutor.toArray;
 
 /**
  * Splits a single SQL query into two step map-reduce query.
@@ -136,26 +145,146 @@ public class GridSqlQuerySplitter {
     /**
      * @param stmt Prepared statement.
      * @param params Parameters.
-     * @param collocated Collocated query.
-     * @param igniteH2Indexing Indexing implementation.
+     * @param collocatedGrpBy Whether the query has collocated GROUP BY keys.
+     * @param distributedJoins If distributed joins enabled.
      * @return Two step query.
      */
-    public static GridCacheTwoStepQuery split(JdbcPreparedStatement stmt, Object[] params, boolean collocated, IgniteH2Indexing igniteH2Indexing) {
+    public static GridCacheTwoStepQuery split(
+        JdbcPreparedStatement stmt,
+        Object[] params,
+        final boolean collocatedGrpBy,
+        final boolean distributedJoins
+    ) {
         if (params == null)
             params = GridCacheSqlQuery.EMPTY_PARAMS;
 
+        Set<String> tbls = new HashSet<>();
         Set<String> schemas = new HashSet<>();
 
+        final Prepared prepared = prepared(stmt);
+
+        GridSqlQuery qry = new GridSqlQueryParser().parse(prepared);
+
+        qry = collectAllTables(qry, schemas, tbls);
+
+        // Build resulting two step query.
+        GridCacheTwoStepQuery res = new GridCacheTwoStepQuery(schemas, tbls);
+
         // Map query will be direct reference to the original query AST.
         // Thus all the modifications will be performed on the original AST, so we should be careful when
         // nullifying or updating things, have to make sure that we will not need them in the original form later.
-        final GridSqlSelect mapQry = wrapUnion(collectAllSpaces(GridSqlQueryParser.parse(stmt), schemas));
+        final GridSqlSelect mapQry = wrapUnion(qry);
+
+        GridCacheSqlQuery rdc = split(res, 0, mapQry, params, collocatedGrpBy);
+
+        res.reduceQuery(rdc);
+
+        // We do not have to look at each map query separately here, because if
+        // the whole initial query is collocated, then all the map sub-queries
+        // will be collocated as well.
+        res.distributedJoins(distributedJoins && !isCollocated(query(prepared)));
+
+        return res;
+    }
+
+    /**
+     * @param el Either {@link GridSqlSelect#from()} or {@link GridSqlSelect#where()} elements.
+     */
+    private static void findAffinityColumnConditions(GridSqlElement el) {
+        if (el == null)
+            return;
+
+        el = GridSqlAlias.unwrap(el);
+
+        if (el instanceof GridSqlJoin) {
+            GridSqlJoin join = (GridSqlJoin)el;
+
+            findAffinityColumnConditions(join.leftTable());
+            findAffinityColumnConditions(join.rightTable());
+            findAffinityColumnConditions(join.on());
+        }
+        else if (el instanceof GridSqlOperation) {
+            GridSqlOperationType type = ((GridSqlOperation)el).operationType();
+
+            switch(type) {
+                case AND:
+                    findAffinityColumnConditions(el.child(0));
+                    findAffinityColumnConditions(el.child(1));
+
+                    break;
+
+                case EQUAL:
+                    findAffinityColumn(el.child(0));
+                    findAffinityColumn(el.child(1));
+            }
+        }
+    }
+
+    /**
+     * @param exp Possible affinity column expression.
+     */
+    private static void findAffinityColumn(GridSqlElement exp) {
+        if (exp instanceof GridSqlColumn) {
+            GridSqlColumn col = (GridSqlColumn)exp;
+
+            GridSqlElement from = col.expressionInFrom();
+
+            if (from instanceof GridSqlTable) {
+                GridSqlTable fromTbl = (GridSqlTable)from;
+
+                GridH2Table tbl = fromTbl.dataTable();
+
+                if (tbl != null) {
+                    IndexColumn affKeyCol = tbl.getAffinityKeyColumn();
+                    Column expCol = col.column();
+
+                    if (affKeyCol != null && expCol != null &&
+                        affKeyCol.column.getColumnId() == expCol.getColumnId()) {
+                        // Mark that table lookup will use affinity key.
+                        fromTbl.affinityKeyCondition(true);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * @param qry Select.
+     * @return {@code true} If there is at least one partitioned table in FROM clause.
+     */
+    private static boolean hasPartitionedTableInFrom(GridSqlSelect qry) {
+        return findTablesInFrom(qry.from(), new IgnitePredicate<GridSqlElement>() {
+            @Override public boolean apply(GridSqlElement el) {
+                if (el instanceof GridSqlTable) {
+                    GridH2Table tbl = ((GridSqlTable)el).dataTable();
+
+                    assert tbl != null : el;
+
+                    GridCacheContext<?,?> cctx = tbl.rowDescriptor().context();
 
+                    return !cctx.isLocal() && !cctx.isReplicated();
+                }
+
+                return false;
+            }
+        });
+    }
+
+    /**
+     * @param res Resulting two step query.
+     * @param splitIdx Split index.
+     * @param mapQry Map query to be split.
+     * @param params Query parameters.
+     * @param collocatedGroupBy Whether the query has collocated GROUP BY keys.
+     * @return Reduce query for the given map query.
+     */
+    private static GridCacheSqlQuery split(GridCacheTwoStepQuery res, int splitIdx, final GridSqlSelect mapQry,
+        Object[] params, boolean collocatedGroupBy) {
         final boolean explain = mapQry.explain();
 
         mapQry.explain(false);
 
-        GridSqlSelect rdcQry = new GridSqlSelect().from(table(0));
+        GridSqlSelect rdcQry = new GridSqlSelect().from(table(splitIdx));
 
         // Split all select expressions into map-reduce parts.
         List<GridSqlElement> mapExps = new ArrayList<>(mapQry.allColumns());
@@ -172,9 +301,9 @@ public class GridSqlQuerySplitter {
         boolean aggregateFound = false;
 
         for (int i = 0, len = mapExps.size(); i < len; i++) // Remember len because mapExps list can grow.
-            aggregateFound |= splitSelectExpression(mapExps, rdcExps, colNames, i, collocated, i == havingCol);
+            aggregateFound |= splitSelectExpression(mapExps, rdcExps, colNames, i, collocatedGroupBy, i == havingCol);
 
-        // Fill select expressions.
+        // -- SELECT
         mapQry.clearColumns();
 
         for (GridSqlElement exp : mapExps) // Add all map expressions as visible.
@@ -189,12 +318,18 @@ public class GridSqlQuerySplitter {
         for (int i = rdcExps.size(); i < mapExps.size(); i++)  // Add all extra map columns as invisible reduce columns.
             rdcQry.addColumn(column(((GridSqlAlias)mapExps.get(i)).alias()), false);
 
+        // -- FROM
+        findAffinityColumnConditions(mapQry.from());
+
+        // -- WHERE
+        findAffinityColumnConditions(mapQry.where());
+
         // -- GROUP BY
-        if (mapQry.groupColumns() != null && !collocated)
+        if (mapQry.groupColumns() != null && !collocatedGroupBy)
             rdcQry.groupColumns(mapQry.groupColumns());
 
         // -- HAVING
-        if (havingCol >= 0 && !collocated) {
+        if (havingCol >= 0 && !collocatedGroupBy) {
             // TODO IGNITE-1140 - Find aggregate functions in HAVING clause or rewrite query to put all aggregates to SELECT clause.
             // We need to find HAVING column in reduce query.
             for (int i = visibleCols; i < rdcQry.allColumns(); i++) {
@@ -246,40 +381,25 @@ public class GridSqlQuerySplitter {
 
         IntArray paramIdxs = new IntArray(params.length);
 
-        GridCacheSqlQuery rdc = new GridCacheSqlQuery(rdcQry.getSQL(),
-            findParams(rdcQry, params, new ArrayList<>(), paramIdxs).toArray());
-
-        rdc.parameterIndexes(toIntArray(paramIdxs));
-
-        paramIdxs = new IntArray(params.length);
-
         GridCacheSqlQuery map = new GridCacheSqlQuery(mapQry.getSQL(),
-            findParams(mapQry, params, new ArrayList<>(params.length), paramIdxs).toArray())
-            .columns(collectColumns(mapExps));
+            findParams(mapQry, params, new ArrayList<>(params.length), paramIdxs).toArray());
 
-        map.parameterIndexes(toIntArray(paramIdxs));
+        map.columns(collectColumns(mapExps));
+        map.parameterIndexes(toArray(paramIdxs));
 
-        Set<String> spaces = new HashSet<>(schemas.size());
+        res.addMapQuery(map);
 
-        for (String schema : schemas)
-            spaces.add(igniteH2Indexing.space(schema));
+        res.explain(explain);
 
-        // Build resulting two step query.
-        GridCacheTwoStepQuery res = new GridCacheTwoStepQuery(spaces, rdc, rdcQry.simpleQuery()).addMapQuery(map);
+        paramIdxs = new IntArray(params.length);
 
-        res.explain(explain);
+        GridCacheSqlQuery rdc = new GridCacheSqlQuery(rdcQry.getSQL(),
+            findParams(rdcQry, params, new ArrayList<>(), paramIdxs).toArray());
 
-        return res;
-    }
+        rdc.parameterIndexes(toArray(paramIdxs));
+        res.skipMergeTable(rdcQry.simpleQuery());
 
-    /**
-     * @param arr Integer array.
-     * @return Primitive int array.
-     */
-    private static int[] toIntArray(IntArray arr) {
-        int[] res = new int[arr.size()];
-        arr.toArray(res);
-        return res;
+        return rdc;
     }
 
     /**
@@ -315,25 +435,26 @@ public class GridSqlQuerySplitter {
 
     /**
      * @param qry Query.
-     * @param schemas Shemas' names.
+     * @param schemas Schema names.
+     * @param tbls Tables.
      * @return Query.
      */
-    private static GridSqlQuery collectAllSpaces(GridSqlQuery qry, Set<String> schemas) {
+    private static GridSqlQuery collectAllTables(GridSqlQuery qry, Set<String> schemas, Set<String> tbls) {
         if (qry instanceof GridSqlUnion) {
             GridSqlUnion union = (GridSqlUnion)qry;
 
-            collectAllSpaces(union.left(), schemas);
-            collectAllSpaces(union.right(), schemas);
+            collectAllTables(union.left(), schemas, tbls);
+            collectAllTables(union.right(), schemas, tbls);
         }
         else {
             GridSqlSelect select = (GridSqlSelect)qry;
 
-            collectAllSpacesInFrom(select.from(), schemas);
+            collectAllTablesInFrom(select.from(), schemas, tbls);
 
             for (GridSqlElement el : select.columns(false))
-                collectAllSpacesInSubqueries(el, schemas);
+                collectAllTablesInSubqueries(el, schemas, tbls);
 
-            collectAllSpacesInSubqueries(select.where(), schemas);
+            collectAllTablesInSubqueries(select.where(), schemas, tbls);
         }
 
         return qry;
@@ -341,45 +462,85 @@ public class GridSqlQuerySplitter {
 
     /**
      * @param from From element.
-     * @param schemas Shemas' names.
+     * @param schemas Schema names.
+     * @param tbls Tables.
      */
-    private static void collectAllSpacesInFrom(GridSqlElement from, Set<String> schemas) {
-        assert from != null;
+    private static void collectAllTablesInFrom(GridSqlElement from, final Set<String> schemas, final Set<String> tbls) {
+        findTablesInFrom(from, new IgnitePredicate<GridSqlElement>() {
+            @Override public boolean apply(GridSqlElement el) {
+                if (el instanceof GridSqlTable) {
+                    GridSqlTable tbl = (GridSqlTable)el;
+
+                    String schema = tbl.schema();
+
+                    boolean addSchema = tbls == null;
+
+                    if (tbls != null)
+                        addSchema = tbls.add(tbl.dataTable().identifier());
+
+                    if (addSchema && schema != null && schemas != null)
+                        schemas.add(schema);
+                }
+                else if (el instanceof GridSqlSubquery)
+                    collectAllTables(((GridSqlSubquery)el).select(), schemas, tbls);
+
+                return false;
+            }
+        });
+    }
+
+    /**
+     * Processes all the tables and subqueries using the given closure.
+     *
+     * @param from FROM element.
+     * @param c Closure each found table and subquery will be passed to. If returns {@code true} the we need to stop.
+     * @return {@code true} If we have found.
+     */
+    private static boolean findTablesInFrom(GridSqlElement from, IgnitePredicate<GridSqlElement> c) {
+        if (from == null)
+            return false;
+
+        if (from instanceof GridSqlTable || from instanceof GridSqlSubquery)
+            return c.apply(from);
 
         if (from instanceof GridSqlJoin) {
             // Left and right.
-            collectAllSpacesInFrom(from.child(0), schemas);
-            collectAllSpacesInFrom(from.child(1), schemas);
-        }
-        else if (from instanceof GridSqlTable) {
-            String schema = ((GridSqlTable)from).schema();
+            if (findTablesInFrom(from.child(0), c))
+                return true;
 
-            if (schema != null)
-                schemas.add(schema);
+            if (findTablesInFrom(from.child(1), c))
+                return true;
+
+            // We don't process ON condition because it is not a joining part of from here.
+            return false;
         }
-        else if (from instanceof GridSqlSubquery)
-            collectAllSpaces(((GridSqlSubquery)from).select(), schemas);
         else if (from instanceof GridSqlAlias)
-            collectAllSpacesInFrom(from.child(), schemas);
-        else if (!(from instanceof GridSqlFunction))
-            throw new IllegalStateException(from.getClass().getName() + " : " + from.getSQL());
+            return findTablesInFrom(from.child(), c);
+        else if (from instanceof GridSqlFunction)
+            return false;
+
+        throw new IllegalStateException(from.getClass().getName() + " : " + from.getSQL());
     }
 
     /**
-     * Searches spaces in subqueries in SELECT and WHERE clauses.
+     * Searches schema names and tables in subqueries in SELECT and WHERE clauses.
+     *
      * @param el Element.
-     * @param schemas Schemas' names.
+     * @param schemas Schema names.
+     * @param tbls Tables.
      */
-    private static void collectAllSpacesInSubqueries(GridSqlElement el, Set<String> schemas) {
-        if (el instanceof GridSqlAlias)
-            el = el.child();
+    private static void collectAllTablesInSubqueries(GridSqlElement el, Set<String> schemas, Set<String> tbls) {
+        if (el == null)
+            return;
+
+        el = GridSqlAlias.unwrap(el);
 
         if (el instanceof GridSqlOperation || el instanceof GridSqlFunction) {
             for (GridSqlElement child : el)
-                collectAllSpacesInSubqueries(child, schemas);
+                collectAllTablesInSubqueries(child, schemas, tbls);
         }
         else if (el instanceof GridSqlSubquery)
-            collectAllSpaces(((GridSqlSubquery)el).select(), schemas);
+            collectAllTables(((GridSqlSubquery)el).select(), schemas, tbls);
     }
 
     /**
@@ -691,7 +852,7 @@ public class GridSqlQuerySplitter {
      * @return Column.
      */
     private static GridSqlColumn column(String name) {
-        return new GridSqlColumn(null, name, name);
+        return new GridSqlColumn(null, null, name, name);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlSelect.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlSelect.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlSelect.java
index 07ef1fa..a38ae68 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlSelect.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlSelect.java
@@ -131,13 +131,12 @@ public class GridSqlSelect extends GridSqlQuery {
 
     /**
      * @param buff Statement builder.
-     * @param expression Alias expression.
+     * @param exp Alias expression.
      */
-    private static void addAlias(StatementBuilder buff, GridSqlElement expression) {
-        if (expression instanceof GridSqlAlias)
-            expression = expression.child();
+    private static void addAlias(StatementBuilder buff, GridSqlElement exp) {
+        exp = GridSqlAlias.unwrap(exp);
 
-        buff.append(StringUtils.unEnclose(expression.getSQL()));
+        buff.append(StringUtils.unEnclose(exp.getSQL()));
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlTable.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlTable.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlTable.java
index 0bcdf1c..49c679d 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlTable.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlTable.java
@@ -18,7 +18,9 @@
 package org.apache.ignite.internal.processors.query.h2.sql;
 
 import java.util.Collections;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
 import org.h2.command.Parser;
+import org.h2.table.Table;
 import org.jetbrains.annotations.Nullable;
 
 /**
@@ -31,15 +33,56 @@ public class GridSqlTable extends GridSqlElement {
     /** */
     private final String tblName;
 
+    /** */
+    private final GridH2Table tbl;
+
+    /** */
+    private boolean affKeyCond;
+
     /**
      * @param schema Schema.
      * @param tblName Table name.
      */
     public GridSqlTable(@Nullable String schema, String tblName) {
+        this(schema, tblName, null);
+    }
+
+    /**
+     * @param tbl Table.
+     */
+    public GridSqlTable(Table tbl) {
+        this(tbl.getSchema().getName(), tbl.getName(), tbl);
+    }
+
+    /**
+     * @param schema Schema.
+     * @param tblName Table name.
+     * @param tbl H2 Table.
+     */
+    private GridSqlTable(@Nullable String schema, String tblName, @Nullable Table tbl) {
         super(Collections.<GridSqlElement>emptyList());
 
+        assert schema != null : "schema";
+        assert tblName != null : "tblName";
+
         this.schema = schema;
         this.tblName = tblName;
+
+        this.tbl = tbl instanceof GridH2Table ? (GridH2Table)tbl : null;
+    }
+
+    /**
+     * @param affKeyCond If affinity key condition is found.
+     */
+    public void affinityKeyCondition(boolean affKeyCond) {
+        this.affKeyCond = affKeyCond;
+    }
+
+    /**
+     * @return {@code true} If affinity key condition is found.
+     */
+    public boolean affinityKeyCondition() {
+        return affKeyCond;
     }
 
     /** {@inheritDoc} */
@@ -63,4 +106,31 @@ public class GridSqlTable extends GridSqlElement {
     public String tableName() {
         return tblName;
     }
+
+    /**
+     * @return Referenced data table.
+     */
+    public GridH2Table dataTable() {
+        return tbl;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean equals(Object o) {
+        if (!super.equals(o))
+            return false;
+
+        GridSqlTable that = (GridSqlTable)o;
+
+        return schema.equals(that.schema) && tblName.equals(that.tblName);
+    }
+
+    /** {@inheritDoc} */
+    @Override public int hashCode() {
+        int result = 1;
+
+        result = 31 * result + schema.hashCode();
+        result = 31 * result + tblName.hashCode();
+
+        return result;
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMapQueryExecutor.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMapQueryExecutor.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMapQueryExecutor.java
index 580058c..bb5e419 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMapQueryExecutor.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMapQueryExecutor.java
@@ -18,15 +18,16 @@
 package org.apache.ignite.internal.processors.query.h2.twostep;
 
 import java.lang.reflect.Field;
+import java.sql.Connection;
 import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
 import java.util.AbstractCollection;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
+import java.util.Objects;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.atomic.AtomicReferenceArray;
@@ -45,38 +46,46 @@ import org.apache.ignite.internal.GridTopic;
 import org.apache.ignite.internal.managers.communication.GridMessageListener;
 import org.apache.ignite.internal.managers.eventstorage.GridLocalEventListener;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
-import org.apache.ignite.internal.processors.cache.GridCacheAdapter;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtLocalPartition;
 import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionsReservation;
 import org.apache.ignite.internal.processors.cache.distributed.dht.GridReservable;
 import org.apache.ignite.internal.processors.cache.query.CacheQueryType;
+import org.apache.ignite.internal.processors.cache.query.GridCacheQueryMarshallable;
 import org.apache.ignite.internal.processors.cache.query.GridCacheSqlQuery;
 import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2QueryContext;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2RetryException;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
 import org.apache.ignite.internal.processors.query.h2.twostep.messages.GridQueryCancelRequest;
 import org.apache.ignite.internal.processors.query.h2.twostep.messages.GridQueryFailResponse;
 import org.apache.ignite.internal.processors.query.h2.twostep.messages.GridQueryNextPageRequest;
 import org.apache.ignite.internal.processors.query.h2.twostep.messages.GridQueryNextPageResponse;
 import org.apache.ignite.internal.processors.query.h2.twostep.messages.GridQueryRequest;
+import org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2QueryRequest;
+import org.apache.ignite.internal.util.GridBoundedConcurrentLinkedHashMap;
 import org.apache.ignite.internal.util.GridSpinBusyLock;
 import org.apache.ignite.internal.util.typedef.CI1;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.T2;
+import org.apache.ignite.internal.util.typedef.X;
+import org.apache.ignite.internal.util.typedef.internal.CU;
 import org.apache.ignite.internal.util.typedef.internal.U;
-import org.apache.ignite.marshaller.Marshaller;
 import org.apache.ignite.plugin.extensions.communication.Message;
 import org.h2.jdbc.JdbcResultSet;
 import org.h2.result.ResultInterface;
 import org.h2.value.Value;
-import org.jetbrains.annotations.Nullable;
 import org.jsr166.ConcurrentHashMap8;
 
 import static org.apache.ignite.events.EventType.EVT_CACHE_QUERY_EXECUTED;
 import static org.apache.ignite.events.EventType.EVT_CACHE_QUERY_OBJECT_READ;
 import static org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion.NONE;
 import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionState.OWNING;
+import static org.apache.ignite.internal.processors.query.h2.opt.GridH2QueryType.MAP;
+import static org.apache.ignite.internal.processors.query.h2.opt.GridH2QueryType.REPLICATED;
 import static org.apache.ignite.internal.processors.query.h2.twostep.GridReduceQueryExecutor.QUERY_POOL;
 import static org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2ValueMessageFactory.toMessages;
+import static org.jsr166.ConcurrentLinkedHashMap.QueuePolicy.PER_SEGMENT_Q;
 
 /**
  * Map query executor.
@@ -109,7 +118,7 @@ public class GridMapQueryExecutor {
     private IgniteH2Indexing h2;
 
     /** */
-    private ConcurrentMap<UUID, ConcurrentMap<Long, QueryResults>> qryRess = new ConcurrentHashMap8<>();
+    private ConcurrentMap<UUID, NodeResults> qryRess = new ConcurrentHashMap8<>();
 
     /** */
     private final GridSpinBusyLock busyLock;
@@ -136,16 +145,20 @@ public class GridMapQueryExecutor {
 
         log = ctx.log(GridMapQueryExecutor.class);
 
+        final UUID locNodeId = ctx.localNodeId();
+
         ctx.event().addLocalEventListener(new GridLocalEventListener() {
             @Override public void onEvent(final Event evt) {
                 UUID nodeId = ((DiscoveryEvent)evt).eventNode().id();
 
-                ConcurrentMap<Long,QueryResults> nodeRess = qryRess.remove(nodeId);
+                GridH2QueryContext.clearAfterDeadNode(locNodeId, nodeId);
+
+                NodeResults nodeRess = qryRess.remove(nodeId);
 
                 if (nodeRess == null)
                     return;
 
-                for (QueryResults ress : nodeRess.values())
+                for (QueryResults ress : nodeRess.results().values())
                     ress.cancel();
             }
         }, EventType.EVT_NODE_FAILED, EventType.EVT_NODE_LEFT);
@@ -156,6 +169,9 @@ public class GridMapQueryExecutor {
                     return;
 
                 try {
+                    if (msg instanceof GridCacheQueryMarshallable)
+                        ((GridCacheQueryMarshallable)msg).unmarshall(ctx.config().getMarshaller(), ctx);
+
                     GridMapQueryExecutor.this.onMessage(nodeId, msg);
                 }
                 finally {
@@ -180,12 +196,14 @@ public class GridMapQueryExecutor {
 
             boolean processed = true;
 
-            if (msg instanceof GridQueryRequest)
-                onQueryRequest(node, (GridQueryRequest)msg);
+            if (msg instanceof GridH2QueryRequest)
+                onQueryRequest(node, (GridH2QueryRequest)msg);
             else if (msg instanceof GridQueryNextPageRequest)
                 onNextPageRequest(node, (GridQueryNextPageRequest)msg);
             else if (msg instanceof GridQueryCancelRequest)
                 onCancel(node, (GridQueryCancelRequest)msg);
+            else if (msg instanceof GridQueryRequest)
+                onQueryRequest(node, (GridQueryRequest)msg);
             else
                 processed = false;
 
@@ -202,9 +220,19 @@ public class GridMapQueryExecutor {
      * @param msg Message.
      */
     private void onCancel(ClusterNode node, GridQueryCancelRequest msg) {
-        ConcurrentMap<Long,QueryResults> nodeRess = resultsForNode(node.id());
+        long qryReqId = msg.queryRequestId();
+
+        NodeResults nodeRess = resultsForNode(node.id());
+
+        boolean clear = GridH2QueryContext.clear(ctx.localNodeId(), node.id(), qryReqId, MAP);
 
-        QueryResults results = nodeRess.remove(msg.queryRequestId());
+        if (!clear) {
+            nodeRess.onCancel(qryReqId);
+
+            GridH2QueryContext.clear(ctx.localNodeId(), node.id(), qryReqId, MAP);
+        }
+
+        QueryResults results = nodeRess.results().remove(qryReqId);
 
         if (results == null)
             return;
@@ -216,13 +244,13 @@ public class GridMapQueryExecutor {
      * @param nodeId Node ID.
      * @return Results for node.
      */
-    private ConcurrentMap<Long, QueryResults> resultsForNode(UUID nodeId) {
-        ConcurrentMap<Long, QueryResults> nodeRess = qryRess.get(nodeId);
+    private NodeResults resultsForNode(UUID nodeId) {
+        NodeResults nodeRess = qryRess.get(nodeId);
 
         if (nodeRess == null) {
-            nodeRess = new ConcurrentHashMap8<>();
+            nodeRess = new NodeResults();
 
-            ConcurrentMap<Long, QueryResults> old = qryRess.putIfAbsent(nodeId, nodeRess);
+            NodeResults old = qryRess.putIfAbsent(nodeId, nodeRess);
 
             if (old != null)
                 nodeRess = old;
@@ -232,19 +260,6 @@ public class GridMapQueryExecutor {
     }
 
     /**
-     * @param cacheName Cache name.
-     * @return Cache context or {@code null} if none.
-     */
-    @Nullable private GridCacheContext<?,?> cacheContext(String cacheName) {
-        GridCacheAdapter<?,?> cache = ctx.cache().internalCache(cacheName);
-
-        if (cache == null)
-            return null;
-
-        return cache.context();
-    }
-
-    /**
      * @param cctx Cache context.
      * @param p Partition ID.
      * @return Partition.
@@ -254,7 +269,7 @@ public class GridMapQueryExecutor {
     }
 
     /**
-     * @param cacheNames Cache names.
+     * @param cacheIds Cache IDs.
      * @param topVer Topology version.
      * @param explicitParts Explicit partitions list.
      * @param reserved Reserved list.
@@ -262,7 +277,7 @@ public class GridMapQueryExecutor {
      * @throws IgniteCheckedException If failed.
      */
     private boolean reservePartitions(
-        Collection<String> cacheNames,
+        List<Integer> cacheIds,
         AffinityTopologyVersion topVer,
         final int[] explicitParts,
         List<GridReservable> reserved
@@ -271,8 +286,8 @@ public class GridMapQueryExecutor {
 
         Collection<Integer> partIds = wrap(explicitParts);
 
-        for (String cacheName : cacheNames) {
-            GridCacheContext<?, ?> cctx = cacheContext(cacheName);
+        for (int i = 0; i < cacheIds.size(); i++) {
+            GridCacheContext<?, ?> cctx = ctx.cache().context().cacheContext(cacheIds.get(i));
 
             if (cctx == null) // Cache was not found, probably was not deployed yet.
                 return false;
@@ -394,121 +409,226 @@ public class GridMapQueryExecutor {
      * @param req Query request.
      */
     private void onQueryRequest(ClusterNode node, GridQueryRequest req) {
-        ConcurrentMap<Long,QueryResults> nodeRess = resultsForNode(node.id());
+        List<Integer> cacheIds;
 
-        QueryResults qr = null;
+        if (req.extraSpaces() != null) {
+            cacheIds = new ArrayList<>(req.extraSpaces().size() + 1);
 
-        List<GridReservable> reserved = new ArrayList<>();
+            cacheIds.add(CU.cacheId(req.space()));
 
-        try {
-            // Unmarshall query params.
-            Collection<GridCacheSqlQuery> qrys;
+            for (String extraSpace : req.extraSpaces())
+                cacheIds.add(CU.cacheId(extraSpace));
+        }
+        else
+            cacheIds = Collections.singletonList(CU.cacheId(req.space()));
+
+        onQueryRequest0(node,
+            req.requestId(),
+            req.queries(),
+            cacheIds,
+            req.topologyVersion(),
+            null,
+            req.partitions(),
+            null,
+            req.pageSize(),
+            false);
+    }
 
-            try {
-                qrys = req.queries();
+    /**
+     * @param node Node.
+     * @param req Query request.
+     */
+    private void onQueryRequest(ClusterNode node, GridH2QueryRequest req) {
+        Map<UUID,int[]> partsMap = req.partitions();
+        int[] parts = partsMap == null ? null : partsMap.get(ctx.localNodeId());
+
+        onQueryRequest0(node,
+            req.requestId(),
+            req.queries(),
+            req.caches(),
+            req.topologyVersion(),
+            partsMap,
+            parts,
+            req.tables(),
+            req.pageSize(),
+            req.isFlagSet(GridH2QueryRequest.FLAG_DISTRIBUTED_JOINS));
+    }
+
+    /**
+     * @param node Node authored request.
+     * @param reqId Request ID.
+     * @param qrys Queries to execute.
+     * @param cacheIds Caches which will be affected by these queries.
+     * @param topVer Topology version.
+     * @param partsMap Partitions map for unstable topology.
+     * @param parts Explicit partitions for current node.
+     * @param tbls Tables.
+     * @param pageSize Page size.
+     * @param distributedJoins Can we expect distributed joins to be ran.
+     */
+    private void onQueryRequest0(
+        ClusterNode node,
+        long reqId,
+        Collection<GridCacheSqlQuery> qrys,
+        List<Integer> cacheIds,
+        AffinityTopologyVersion topVer,
+        Map<UUID, int[]> partsMap,
+        int[] parts,
+        Collection<String> tbls,
+        int pageSize,
+        boolean distributedJoins
+    ) {
+        // Prepare to run queries.
+        GridCacheContext<?, ?> mainCctx = ctx.cache().context().cacheContext(cacheIds.get(0));
 
-                if (!node.isLocal()) {
-                    Marshaller m = ctx.config().getMarshaller();
+        if (mainCctx == null)
+            throw new CacheException("Failed to find cache.");
 
-                    for (GridCacheSqlQuery qry : qrys)
-                        qry.unmarshallParams(m, ctx);
-                }
-            }
-            catch (IgniteCheckedException e) {
-                throw new CacheException("Failed to unmarshall parameters.", e);
-            }
+        NodeResults nodeRess = resultsForNode(node.id());
 
-            List<String> caches = (List<String>)F.concat(true, req.space(), req.extraSpaces());
+        QueryResults qr = null;
 
-            // Topology version can be null in rolling restart with previous version!
-            final AffinityTopologyVersion topVer = req.topologyVersion();
+        List<GridReservable> reserved = new ArrayList<>();
 
+        try {
             if (topVer != null) {
                 // Reserve primary for topology version or explicit partitions.
-                if (!reservePartitions(caches, topVer, req.partitions(), reserved)) {
-                    sendRetry(node, req.requestId());
+                if (!reservePartitions(cacheIds, topVer, parts, reserved)) {
+                    sendRetry(node, reqId);
 
                     return;
                 }
             }
 
-            // Prepare to run queries.
-            GridCacheContext<?,?> mainCctx = cacheContext(req.space());
+            qr = new QueryResults(reqId, qrys.size(), mainCctx);
 
-            if (mainCctx == null)
-                throw new CacheException("Failed to find cache: " + req.space());
+            if (nodeRess.results().put(reqId, qr) != null)
+                throw new IllegalStateException();
 
-            qr = new QueryResults(req.requestId(), qrys.size(), mainCctx);
+            // Prepare query context.
+            GridH2QueryContext qctx = new GridH2QueryContext(ctx.localNodeId(),
+                node.id(),
+                reqId,
+                mainCctx.isReplicated() ? REPLICATED : MAP)
+                .filter(h2.backupFilter(topVer, parts))
+                .partitionsMap(partsMap)
+                .distributedJoins(distributedJoins)
+                .pageSize(pageSize)
+                .topologyVersion(topVer)
+                .reservations(reserved);
 
-            if (nodeRess.put(req.requestId(), qr) != null)
-                throw new IllegalStateException();
+            List<GridH2Table> snapshotedTbls = null;
 
-            h2.setFilters(h2.backupFilter(caches, topVer, req.partitions()));
+            if (!F.isEmpty(tbls)) {
+                snapshotedTbls = new ArrayList<>(tbls.size());
 
-            // TODO Prepare snapshots for all the needed tables before the run.
+                for (String identifier : tbls) {
+                    GridH2Table tbl = h2.dataTable(identifier);
 
-            // Run queries.
-            int i = 0;
+                    Objects.requireNonNull(tbl, identifier);
 
-            for (GridCacheSqlQuery qry : qrys) {
-                ResultSet rs = h2.executeSqlQueryWithTimer(req.space(),
-                    h2.connectionForSpace(req.space()),
-                    qry.query(),
-                    F.asList(qry.parameters()),
-                    true);
+                    tbl.snapshotIndexes(qctx);
 
-                if (ctx.event().isRecordable(EVT_CACHE_QUERY_EXECUTED)) {
-                    ctx.event().record(new CacheQueryExecutedEvent<>(
-                        node,
-                        "SQL query executed.",
-                        EVT_CACHE_QUERY_EXECUTED,
-                        CacheQueryType.SQL.name(),
-                        mainCctx.namex(),
-                        null,
-                        qry.query(),
-                        null,
-                        null,
-                        qry.parameters(),
-                        node.id(),
-                        null));
+                    snapshotedTbls.add(tbl);
                 }
+            }
+
+            Connection conn = h2.connectionForSpace(mainCctx.name());
+
+            // Here we enforce join order to have the same behavior on all the nodes.
+            h2.setupConnection(conn, distributedJoins, true);
+
+            GridH2QueryContext.set(qctx);
 
-                assert rs instanceof JdbcResultSet : rs.getClass();
+            // qctx is set, we have to release reservations inside of it.
+            reserved = null;
 
-                qr.addResult(i, qry, node.id(), rs);
+            try {
+                if (nodeRess.cancelled(reqId)) {
+                    GridH2QueryContext.clear(ctx.localNodeId(), node.id(), reqId, qctx.type());
 
-                if (qr.canceled) {
-                    qr.result(i).close();
+                    nodeRess.results().remove(reqId);
 
                     return;
                 }
 
-                // Send the first page.
-                sendNextPage(nodeRess, node, qr, i, req.pageSize());
+                // Run queries.
+                int i = 0;
+
+                boolean evt = ctx.event().isRecordable(EVT_CACHE_QUERY_EXECUTED);
+
+                for (GridCacheSqlQuery qry : qrys) {
+                    ResultSet rs = h2.executeSqlQueryWithTimer(mainCctx.name(), conn, qry.query(),
+                        F.asList(qry.parameters()), true);
+
+                    if (evt) {
+                        ctx.event().record(new CacheQueryExecutedEvent<>(
+                            node,
+                            "SQL query executed.",
+                            EVT_CACHE_QUERY_EXECUTED,
+                            CacheQueryType.SQL.name(),
+                            mainCctx.namex(),
+                            null,
+                            qry.query(),
+                            null,
+                            null,
+                            qry.parameters(),
+                            node.id(),
+                            null));
+                    }
+
+                    assert rs instanceof JdbcResultSet : rs.getClass();
+
+                    qr.addResult(i, qry, node.id(), rs);
+
+                    if (qr.canceled) {
+                        qr.result(i).close();
+
+                        return;
+                    }
+
+                    // Send the first page.
+                    sendNextPage(nodeRess, node, qr, i, pageSize);
+
+                    i++;
+                }
+            }
+            finally {
+                GridH2QueryContext.clearThreadLocal();
+
+                if (!distributedJoins)
+                    qctx.clearContext(false);
 
-                i++;
+                if (!F.isEmpty(snapshotedTbls)) {
+                    for (GridH2Table dataTbl : snapshotedTbls)
+                        dataTbl.releaseSnapshots();
+                }
             }
         }
         catch (Throwable e) {
             if (qr != null) {
-                nodeRess.remove(req.requestId(), qr);
+                nodeRess.results().remove(reqId, qr);
 
                 qr.cancel();
             }
 
-            U.error(log, "Failed to execute local query: " + req, e);
+            if (X.hasCause(e, GridH2RetryException.class))
+                sendRetry(node, reqId);
+            else {
+                U.error(log, "Failed to execute local query.", e);
 
-            sendError(node, req.requestId(), e);
+                sendError(node, reqId, e);
 
-            if (e instanceof Error)
-                throw (Error)e;
+                if (e instanceof Error)
+                    throw (Error)e;
+            }
         }
         finally {
-            h2.setFilters(null);
-
-            // Release reserved partitions.
-            for (GridReservable r : reserved)
-                r.release();
+            if (reserved != null) {
+                // Release reserved partitions.
+                for (int i = 0; i < reserved.size(); i++)
+                    reserved.get(i).release();
+            }
         }
     }
 
@@ -521,8 +641,11 @@ public class GridMapQueryExecutor {
         try {
             GridQueryFailResponse msg = new GridQueryFailResponse(qryReqId, err);
 
-            if (node.isLocal())
+            if (node.isLocal()) {
+                U.error(log, "Failed to run map query on local node.", err);
+
                 h2.reduceQueryExecutor().onMessage(ctx.localNodeId(), msg);
+            }
             else
                 ctx.io().send(node, GridTopic.TOPIC_QUERY, msg, QUERY_POOL);
         }
@@ -538,9 +661,9 @@ public class GridMapQueryExecutor {
      * @param req Request.
      */
     private void onNextPageRequest(ClusterNode node, GridQueryNextPageRequest req) {
-        ConcurrentMap<Long, QueryResults> nodeRess = qryRess.get(node.id());
+        NodeResults nodeRess = qryRess.get(node.id());
 
-        QueryResults qr = nodeRess == null ? null : nodeRess.get(req.queryRequestId());
+        QueryResults qr = nodeRess == null ? null : nodeRess.results().get(req.queryRequestId());
 
         if (qr == null || qr.canceled)
             sendError(node, req.queryRequestId(), new CacheException("No query result found for request: " + req));
@@ -549,12 +672,13 @@ public class GridMapQueryExecutor {
     }
 
     /**
+     * @param nodeRess Results.
      * @param node Node.
      * @param qr Query results.
      * @param qry Query.
      * @param pageSize Page size.
      */
-    private void sendNextPage(ConcurrentMap<Long, QueryResults> nodeRess, ClusterNode node, QueryResults qr, int qry,
+    private void sendNextPage(NodeResults nodeRess, ClusterNode node, QueryResults qr, int qry,
         int pageSize) {
         QueryResult res = qr.result(qry);
 
@@ -570,14 +694,14 @@ public class GridMapQueryExecutor {
             res.close();
 
             if (qr.isAllClosed())
-                nodeRess.remove(qr.qryReqId, qr);
+                nodeRess.results().remove(qr.qryReqId, qr);
         }
 
         try {
             boolean loc = node.isLocal();
 
             GridQueryNextPageResponse msg = new GridQueryNextPageResponse(qr.qryReqId, qry, page,
-                page == 0 ? res.rowCount : -1 ,
+                page == 0 ? res.rowCnt : -1 ,
                 res.cols,
                 loc ? null : toMessages(rows, new ArrayList<Message>(res.cols)),
                 loc ? rows : null);
@@ -597,22 +721,26 @@ public class GridMapQueryExecutor {
     /**
      * @param node Node.
      * @param reqId Request ID.
-     * @throws IgniteCheckedException If failed.
      */
-    private void sendRetry(ClusterNode node, long reqId) throws IgniteCheckedException {
-        boolean loc = node.isLocal();
+    private void sendRetry(ClusterNode node, long reqId) {
+        try {
+            boolean loc = node.isLocal();
 
-        GridQueryNextPageResponse msg = new GridQueryNextPageResponse(reqId,
+            GridQueryNextPageResponse msg = new GridQueryNextPageResponse(reqId,
             /*qry*/0, /*page*/0, /*allRows*/0, /*cols*/1,
-            loc ? null : Collections.<Message>emptyList(),
-            loc ? Collections.<Value[]>emptyList() : null);
+                loc ? null : Collections.<Message>emptyList(),
+                loc ? Collections.<Value[]>emptyList() : null);
 
-        msg.retry(h2.readyTopologyVersion());
+            msg.retry(h2.readyTopologyVersion());
 
-        if (loc)
-            h2.reduceQueryExecutor().onMessage(ctx.localNodeId(), msg);
-        else
-            ctx.io().send(node, GridTopic.TOPIC_QUERY, msg, QUERY_POOL);
+            if (loc)
+                h2.reduceQueryExecutor().onMessage(ctx.localNodeId(), msg);
+            else
+                ctx.io().send(node, GridTopic.TOPIC_QUERY, msg, QUERY_POOL);
+        }
+        catch (Exception e) {
+            U.warn(log, "Failed to send retry message: " + e.getMessage());
+        }
     }
 
     /**
@@ -626,6 +754,44 @@ public class GridMapQueryExecutor {
         }
     }
 
+
+    /**
+     *
+     */
+    private static class NodeResults {
+        /** */
+        private final ConcurrentMap<Long, QueryResults> res = new ConcurrentHashMap8<>();
+
+        /** */
+        private final GridBoundedConcurrentLinkedHashMap<Long, Boolean> qryHist =
+            new GridBoundedConcurrentLinkedHashMap<>(1024, 1024, 0.75f, 64, PER_SEGMENT_Q);
+
+        /**
+         * @return All results.
+         */
+        ConcurrentMap<Long, QueryResults> results() {
+            return res;
+        }
+
+        /**
+         * @param qryId Query ID.
+         * @return {@code False} if query was already cancelled.
+         */
+        boolean cancelled(long qryId) {
+            return qryHist.get(qryId) != null;
+        }
+
+        /**
+         * @param qryId Query ID.
+         * @return {@code True} if cancelled.
+         */
+        boolean onCancel(long qryId) {
+            Boolean old = qryHist.putIfAbsent(qryId, Boolean.FALSE);
+
+            return old == null;
+        }
+    }
+
     /**
      *
      */
@@ -637,7 +803,7 @@ public class GridMapQueryExecutor {
         private final AtomicReferenceArray<QueryResult> results;
 
         /** */
-        private final GridCacheContext<?,?> cctx;
+        private final GridCacheContext<?, ?> cctx;
 
         /** */
         private volatile boolean canceled;
@@ -687,6 +853,9 @@ public class GridMapQueryExecutor {
             return true;
         }
 
+        /**
+         *
+         */
         void cancel() {
             if (canceled)
                 return;
@@ -728,7 +897,7 @@ public class GridMapQueryExecutor {
         private int page;
 
         /** */
-        private final int rowCount;
+        private final int rowCnt;
 
         /** */
         private volatile boolean closed;
@@ -752,7 +921,7 @@ public class GridMapQueryExecutor {
                 throw new IllegalStateException(e); // Must not happen.
             }
 
-            rowCount = res.getRowCount();
+            rowCnt = res.getRowCount();
             cols = res.getVisibleColumnCount();
         }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndex.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndex.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndex.java
index 3914bd7..796ea66 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndex.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndex.java
@@ -18,18 +18,22 @@
 package org.apache.ignite.internal.processors.query.h2.twostep;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.ConcurrentModificationException;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
-import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 import javax.cache.CacheException;
 import org.apache.ignite.IgniteException;
+import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.internal.GridKernalContext;
-import org.h2.engine.Constants;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2Cursor;
+import org.apache.ignite.internal.util.typedef.internal.U;
 import org.h2.engine.Session;
 import org.h2.index.BaseIndex;
 import org.h2.index.Cursor;
@@ -41,7 +45,6 @@ import org.h2.result.SortOrder;
 import org.h2.table.IndexColumn;
 import org.h2.table.TableFilter;
 import org.jetbrains.annotations.Nullable;
-import org.jsr166.ConcurrentHashMap8;
 
 import static org.apache.ignite.IgniteSystemProperties.IGNITE_SQL_MERGE_TABLE_MAX_SIZE;
 import static org.apache.ignite.IgniteSystemProperties.getInteger;
@@ -57,7 +60,7 @@ public abstract class GridMergeIndex extends BaseIndex {
     private final AtomicInteger expRowsCnt = new AtomicInteger(0);
 
     /** Remaining rows per source node ID. */
-    private final ConcurrentMap<UUID, Counter> remainingRows = new ConcurrentHashMap8<>();
+    private Map<UUID, Counter> remainingRows;
 
     /** */
     private final AtomicBoolean lastSubmitted = new AtomicBoolean();
@@ -136,11 +139,19 @@ public abstract class GridMergeIndex extends BaseIndex {
     }
 
     /**
-     * @param nodeId Node ID.
+     * Set source nodes.
+     *
+     * @param nodes Nodes.
      */
-    public void addSource(UUID nodeId) {
-        if (remainingRows.put(nodeId, new Counter()) != null)
-            throw new IllegalStateException();
+    public void setSources(Collection<ClusterNode> nodes) {
+        assert remainingRows == null;
+
+        remainingRows = U.newHashMap(nodes.size());
+
+        for (ClusterNode node : nodes) {
+            if (remainingRows.put(node.id(), new Counter()) != null)
+                throw new IllegalStateException("Duplicate node id: " + node.id());
+        }
     }
 
     /**
@@ -283,8 +294,8 @@ public abstract class GridMergeIndex extends BaseIndex {
     }
 
     /** {@inheritDoc} */
-    @Override public double getCost(Session ses, int[] masks, TableFilter filter, SortOrder sortOrder) {
-        return getRowCountApproximation() + Constants.COST_ROW_OFFSET;
+    @Override public double getCost(Session ses, int[] masks, TableFilter[] filters, int filter, SortOrder sortOrder) {
+        return getCostRangeIndex(masks, getRowCountApproximation(), filters, filter, sortOrder, true);
     }
 
     /** {@inheritDoc} */
@@ -318,51 +329,9 @@ public abstract class GridMergeIndex extends BaseIndex {
     }
 
     /**
-     * Cursor over iterator.
-     */
-    protected class IteratorCursor implements Cursor {
-        /** */
-        protected Iterator<Row> iter;
-
-        /** */
-        protected Row cur;
-
-        /**
-         * @param iter Iterator.
-         */
-        public IteratorCursor(Iterator<Row> iter) {
-            assert iter != null;
-
-            this.iter = iter;
-        }
-
-        /** {@inheritDoc} */
-        @Override public Row get() {
-            return cur;
-        }
-
-        /** {@inheritDoc} */
-        @Override public SearchRow getSearchRow() {
-            return get();
-        }
-
-        /** {@inheritDoc} */
-        @Override public boolean next() {
-            cur = iter.hasNext() ? iter.next() : null;
-
-            return cur != null;
-        }
-
-        /** {@inheritDoc} */
-        @Override public boolean previous() {
-            throw DbException.getUnsupportedException("previous");
-        }
-    }
-
-    /**
      * Fetching cursor.
      */
-    protected class FetchingCursor extends IteratorCursor {
+    protected class FetchingCursor extends GridH2Cursor {
         /** */
         private Iterator<Row> stream;
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndexUnsorted.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndexUnsorted.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndexUnsorted.java
index 5639340..8a8577f 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndexUnsorted.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndexUnsorted.java
@@ -25,6 +25,8 @@ import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 import javax.cache.CacheException;
 import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2Cursor;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2RowFactory;
 import org.h2.index.Cursor;
 import org.h2.index.IndexType;
 import org.h2.result.Row;
@@ -73,7 +75,7 @@ public class GridMergeIndexUnsorted extends GridMergeIndex {
 
     /** {@inheritDoc} */
     @Override protected Cursor findAllFetched(List<Row> fetched, @Nullable SearchRow first, @Nullable SearchRow last) {
-        return new IteratorCursor(fetched.iterator());
+        return new GridH2Cursor(fetched.iterator());
     }
 
     /** {@inheritDoc} */
@@ -112,7 +114,7 @@ public class GridMergeIndexUnsorted extends GridMergeIndex {
             }
 
             @Override public Row next() {
-                return new Row(iter.next(), 0);
+                return GridH2RowFactory.create(iter.next());
             }
 
             @Override public void remove() {

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeTable.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeTable.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeTable.java
index a86cbcd..1489021 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeTable.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeTable.java
@@ -50,8 +50,8 @@ public class GridMergeTable extends TableBase {
     }
 
     /** {@inheritDoc} */
-    @Override public void lock(Session session, boolean exclusive, boolean force) {
-        // No-op.
+    @Override public boolean lock(Session session, boolean exclusive, boolean force) {
+        return false;
     }
 
     /** {@inheritDoc} */


[05/50] [abbrv] ignite git commit: ignite-3300 Fixed potential race.

Posted by vo...@apache.org.
ignite-3300 Fixed potential race.


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

Branch: refs/heads/ignite-3553
Commit: d88f7787dfcd7cc6207a3a8f7416130f8c2fe757
Parents: 2007cf8
Author: sboikov <sb...@gridgain.com>
Authored: Fri Jul 22 16:32:26 2016 +0300
Committer: sboikov <sb...@gridgain.com>
Committed: Fri Jul 22 16:32:53 2016 +0300

----------------------------------------------------------------------
 .../processors/cache/GridCacheAffinityManager.java      | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/d88f7787/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAffinityManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAffinityManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAffinityManager.java
index 5e843dc..d182bb4 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAffinityManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAffinityManager.java
@@ -197,15 +197,21 @@ public class GridCacheAffinityManager extends GridCacheManagerAdapter {
     public int partition(Object key) {
         GridAffinityAssignmentCache aff0 = aff;
 
-        if (key instanceof KeyCacheObject && ((KeyCacheObject)key).partition() != -1)
-            return ((KeyCacheObject)key).partition();
+        boolean keyObj = key instanceof KeyCacheObject;
+
+        if (key instanceof KeyCacheObject) {
+            int part = ((KeyCacheObject)key).partition();
+
+            if (part != -1)
+                return part;
+        }
 
         if (aff0 == null)
             throw new IgniteException(FAILED_TO_FIND_CACHE_ERR_MSG + cctx.name());
 
         int p = affFunction.partition(affinityKey(key));
 
-        if (key instanceof KeyCacheObject)
+        if (keyObj)
             ((KeyCacheObject)key).partition(p);
 
         return p;


[33/50] [abbrv] ignite git commit: IGNITE-3349 .NET: Ignition.Start overload with a custom config file

Posted by vo...@apache.org.
IGNITE-3349 .NET: Ignition.Start overload with a custom config file


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

Branch: refs/heads/ignite-3553
Commit: e7547248b077275ea2d7eb4bbd8382b054e95f2b
Parents: 3af756d
Author: Pavel Tupitsyn <pt...@apache.org>
Authored: Tue Jul 26 15:33:39 2016 +0300
Committer: Pavel Tupitsyn <pt...@apache.org>
Committed: Tue Jul 26 15:33:39 2016 +0300

----------------------------------------------------------------------
 .../Apache.Ignite.Core.Tests.csproj             |  3 ++
 .../IgniteConfigurationSectionTest.cs           | 32 ++++++++++++++++
 .../IgniteStartStopTest.cs                      |  3 +-
 .../Apache.Ignite.Core.Tests/custom_app.config  | 34 +++++++++++++++++
 .../dotnet/Apache.Ignite.Core/Ignition.cs       | 40 +++++++++++++++++++-
 .../dotnet/Apache.Ignite/Config/Configurator.cs | 11 +++---
 6 files changed, 116 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/e7547248/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
index 5c10612..4e33a91 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
@@ -291,6 +291,9 @@
   <ItemGroup>
     <None Include="Apache.Ignite.Core.Tests.nunit" />
     <None Include="Apache.Ignite.Core.Tests.snk" />
+    <None Include="custom_app.config">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
     <None Include="app.config" />
     <Content Include="Config\ignite-dotnet-cfg.xml">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

http://git-wip-us.apache.org/repos/asf/ignite/blob/e7547248/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationSectionTest.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationSectionTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationSectionTest.cs
index 29aea90..4e10a2b 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationSectionTest.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationSectionTest.cs
@@ -64,6 +64,38 @@ namespace Apache.Ignite.Core.Tests
             {
                 Assert.IsTrue(ignite.Name.StartsWith("myGrid"));
             }
+
+            using (var ignite = Ignition.StartFromApplicationConfiguration(
+                "igniteConfiguration3", "custom_app.config"))
+            {
+                Assert.AreEqual("myGrid3", ignite.Name);
+            }
+        }
+
+        /// <summary>
+        /// Tests the ignite start error.
+        /// </summary>
+        [Test]
+        public void TestIgniteStartError()
+        {
+            var ex = Assert.Throws<ConfigurationErrorsException>(() =>
+                Ignition.StartFromApplicationConfiguration("igniteConfiguration111"));
+
+            Assert.AreEqual("Could not find IgniteConfigurationSection with name 'igniteConfiguration111'", 
+                ex.Message);
+
+
+            ex = Assert.Throws<ConfigurationErrorsException>(() =>
+                Ignition.StartFromApplicationConfiguration("igniteConfiguration", "somefile"));
+
+            Assert.AreEqual("Specified config file does not exist: somefile", ex.Message);
+
+
+            ex = Assert.Throws<ConfigurationErrorsException>(() =>
+                Ignition.StartFromApplicationConfiguration("igniteConfiguration", "custom_app.config"));
+
+            Assert.AreEqual("Could not find IgniteConfigurationSection with name 'igniteConfiguration' " +
+                            "in file 'custom_app.config'", ex.Message);
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/e7547248/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteStartStopTest.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteStartStopTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteStartStopTest.cs
index 47212fc..b863308 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteStartStopTest.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteStartStopTest.cs
@@ -125,6 +125,7 @@ namespace Apache.Ignite.Core.Tests
 
             Assert.AreSame(grid3, Ignition.GetIgnite(null));
             Assert.AreSame(grid3, Ignition.TryGetIgnite(null));
+            Assert.AreSame(grid3, Ignition.TryGetIgnite());
 
             Assert.Throws<IgniteException>(() => Ignition.GetIgnite("invalid_name"));
             Assert.IsNull(Ignition.TryGetIgnite("invalid_name"));
@@ -365,7 +366,7 @@ namespace Apache.Ignite.Core.Tests
             });
 
             // Wait for remote node to join
-            Assert.IsTrue(grid.WaitTopology(2, 30000));
+            Assert.IsTrue(grid.WaitTopology(2));
 
             // Wait some more for initialization
             Thread.Sleep(1000);

http://git-wip-us.apache.org/repos/asf/ignite/blob/e7547248/modules/platforms/dotnet/Apache.Ignite.Core.Tests/custom_app.config
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/custom_app.config b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/custom_app.config
new file mode 100644
index 0000000..41ea39e
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/custom_app.config
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  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.
+-->
+
+<configuration>
+    <configSections>
+        <section name="igniteConfiguration3" type="Apache.Ignite.Core.IgniteConfigurationSection, Apache.Ignite.Core" />
+    </configSections>
+
+    <igniteConfiguration3 gridName="myGrid3" localhost="127.0.0.1">
+        <discoverySpi type="TcpDiscoverySpi">
+            <ipFinder type="TcpDiscoveryStaticIpFinder">
+                <endpoints>
+                    <string>127.0.0.1:47500</string>
+                </endpoints>
+            </ipFinder>
+        </discoverySpi>
+    </igniteConfiguration3>
+</configuration>

http://git-wip-us.apache.org/repos/asf/ignite/blob/e7547248/modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs
index 7fbd100..c9ff3ff 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs
@@ -157,10 +157,48 @@ namespace Apache.Ignite.Core
         }
 
         /// <summary>
+        /// Reads <see cref="IgniteConfiguration" /> from application configuration
+        /// <see cref="IgniteConfigurationSection" /> with specified name and starts Ignite.
+        /// </summary>
+        /// <param name="sectionName">Name of the section.</param>
+        /// <param name="configPath">Path to the configuration file.</param>
+        /// <returns>Started Ignite.</returns>
+        public static IIgnite StartFromApplicationConfiguration(string sectionName, string configPath)
+        {
+            IgniteArgumentCheck.NotNullOrEmpty(sectionName, "sectionName");
+            IgniteArgumentCheck.NotNullOrEmpty(configPath, "configPath");
+
+            var fileMap = GetConfigMap(configPath);
+            var config = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
+
+            var section = config.GetSection(sectionName) as IgniteConfigurationSection;
+
+            if (section == null)
+                throw new ConfigurationErrorsException(
+                    string.Format("Could not find {0} with name '{1}' in file '{2}'",
+                        typeof(IgniteConfigurationSection).Name, sectionName, configPath));
+
+            return Start(section.IgniteConfiguration);
+        }
+
+        /// <summary>
+        /// Gets the configuration file map.
+        /// </summary>
+        private static ExeConfigurationFileMap GetConfigMap(string fileName)
+        {
+            var fullFileName = Path.GetFullPath(fileName);
+
+            if (!File.Exists(fullFileName))
+                throw new ConfigurationErrorsException("Specified config file does not exist: " + fileName);
+
+            return new ExeConfigurationFileMap { ExeConfigFilename = fullFileName };
+        }
+
+        /// <summary>
         /// Starts Ignite with given configuration.
         /// </summary>
         /// <returns>Started Ignite.</returns>
-        public unsafe static IIgnite Start(IgniteConfiguration cfg)
+        public static unsafe IIgnite Start(IgniteConfiguration cfg)
         {
             IgniteArgumentCheck.NotNull(cfg, "cfg");
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/e7547248/modules/platforms/dotnet/Apache.Ignite/Config/Configurator.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite/Config/Configurator.cs b/modules/platforms/dotnet/Apache.Ignite/Config/Configurator.cs
index c595de1..5f73a6d 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Config/Configurator.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Config/Configurator.cs
@@ -75,7 +75,8 @@ namespace Apache.Ignite.Config
 
             foreach (var arg in args)
             {
-                Func<string, bool> argIs = x => arg.Item1.Equals(x, StringComparison.OrdinalIgnoreCase);
+                var arg0 = arg;  // copy captured variable
+                Func<string, bool> argIs = x => arg0.Item1.Equals(x, StringComparison.OrdinalIgnoreCase);
 
                 if (argIs(CmdIgniteHome))
                     cfg.IgniteHome = arg.Item2;
@@ -152,12 +153,12 @@ namespace Apache.Ignite.Config
         /// </summary>
         private static ExeConfigurationFileMap GetConfigMap(string fileName)
         {
-            fileName = Path.GetFullPath(fileName);
+            var fullFileName = Path.GetFullPath(fileName);
 
-            if (!File.Exists(fileName))
-                throw new InvalidOperationException("Specified config file does not exist: " + fileName);
+            if (!File.Exists(fullFileName))
+                throw new ConfigurationErrorsException("Specified config file does not exist: " + fileName);
 
-            return new ExeConfigurationFileMap {ExeConfigFilename = fileName};
+            return new ExeConfigurationFileMap {ExeConfigFilename = fullFileName};
         }
 
         /// <summary>


[43/50] [abbrv] ignite git commit: Merge remote-tracking branch 'apache/ignite-1.7' into ignite-1.7

Posted by vo...@apache.org.
Merge remote-tracking branch 'apache/ignite-1.7' into ignite-1.7


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

Branch: refs/heads/ignite-3553
Commit: 77781e97b2736d74217f08daa8e82c419456a26a
Parents: 87a7c79 db99bec
Author: Andrey Novikov <an...@apache.org>
Authored: Wed Jul 27 17:57:13 2016 +0700
Committer: Andrey Novikov <an...@apache.org>
Committed: Wed Jul 27 17:57:13 2016 +0700

----------------------------------------------------------------------

----------------------------------------------------------------------



[27/50] [abbrv] ignite git commit: Merge remote-tracking branch 'remotes/community/ignite-1.6.3' into UPSTREAM_master

Posted by vo...@apache.org.
Merge remote-tracking branch 'remotes/community/ignite-1.6.3' into UPSTREAM_master


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

Branch: refs/heads/ignite-3553
Commit: 37195a024abf16de152dc06914cb88dfbffb1397
Parents: f245c38 332c8c2
Author: Pavel Tupitsyn <pt...@apache.org>
Authored: Mon Jul 25 16:13:39 2016 +0300
Committer: Pavel Tupitsyn <pt...@apache.org>
Committed: Mon Jul 25 16:13:39 2016 +0300

----------------------------------------------------------------------
 .../internal/processors/igfs/IgfsImpl.java      |  12 +-
 .../processors/igfs/IgfsMetaManager.java        | 115 ++++---
 .../igfs/IgfsSecondaryFileSystemImpl.java       |  15 +-
 .../igfs/IgfsSecondaryFileSystemV2.java         |  40 +++
 .../processors/igfs/IgfsAbstractSelfTest.java   | 111 ++++++-
 .../igfs/IgfsDualAbstractSelfTest.java          |  34 ++
 .../igfs/IgfsExUniversalFileSystemAdapter.java  |  15 +
 .../igfs/UniversalFileSystemAdapter.java        |  11 +
 .../fs/IgniteHadoopIgfsSecondaryFileSystem.java |  16 +-
 ...oopFileSystemUniversalFileSystemAdapter.java |   8 +
 .../Binary/BinaryBuilderSelfTest.cs             | 325 +++++++++++++++----
 .../Binary/IBinaryObjectBuilder.cs              |   2 +-
 .../Impl/Binary/BinaryObjectBuilder.cs          |  16 +-
 .../Impl/Binary/BinaryWriter.cs                 | 133 +++++---
 .../commands/cache/VisorCacheStopCommand.scala  |   5 +-
 15 files changed, 664 insertions(+), 194 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/37195a02/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsImpl.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/ignite/blob/37195a02/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs
----------------------------------------------------------------------


[45/50] [abbrv] ignite git commit: IGNITE-3591 .NET: Fix self-joins in LINQ

Posted by vo...@apache.org.
IGNITE-3591 .NET: Fix self-joins in LINQ


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

Branch: refs/heads/ignite-3553
Commit: f96b568e662758a915309fe631c5bec4fd0ccdfe
Parents: 2f36ae5
Author: Pavel Tupitsyn <pt...@apache.org>
Authored: Wed Jul 27 15:25:59 2016 +0300
Committer: Pavel Tupitsyn <pt...@apache.org>
Committed: Wed Jul 27 15:25:59 2016 +0300

----------------------------------------------------------------------
 .../Cache/Query/CacheLinqTest.cs                | 18 +++++
 .../Apache.Ignite.Linq/Impl/AliasDictionary.cs  | 74 ++++++++++++++++----
 .../Impl/CacheQueryExpressionVisitor.cs         |  4 +-
 .../Impl/CacheQueryModelVisitor.cs              |  6 +-
 .../Apache.Ignite.Linq/Impl/ExpressionWalker.cs | 34 ++++-----
 5 files changed, 96 insertions(+), 40 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/f96b568e/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 679f03a..70c5fb5 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
@@ -537,6 +537,24 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
         }
 
         /// <summary>
+        /// Tests the join of a table to itself.
+        /// </summary>
+        [Test]
+        public void TestSelfJoin()
+        {
+            // Different queryables
+            var p1 = GetPersonCache().AsCacheQueryable();
+            var p2 = GetPersonCache().AsCacheQueryable();
+
+            var qry = p1.Join(p2, x => x.Value.Age, x => x.Key, (x, y) => x.Key);
+            Assert.AreEqual(PersonCount, qry.ToArray().Distinct().Count());
+
+            // Same queryables
+            var qry2 = p1.Join(p1, x => x.Value.Age, x => x.Key, (x, y) => x.Key);
+            Assert.AreEqual(PersonCount, qry2.ToArray().Distinct().Count());
+        }
+
+        /// <summary>
         /// Tests the group by.
         /// </summary>
         [Test]

http://git-wip-us.apache.org/repos/asf/ignite/blob/f96b568e/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/AliasDictionary.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/AliasDictionary.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/AliasDictionary.cs
index 10a414c..5fd890d 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/AliasDictionary.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/AliasDictionary.cs
@@ -17,10 +17,13 @@
 
 namespace Apache.Ignite.Linq.Impl
 {
+    using System;
     using System.Collections.Generic;
     using System.Diagnostics;
+    using System.Linq.Expressions;
     using System.Text;
     using Remotion.Linq.Clauses;
+    using Remotion.Linq.Clauses.Expressions;
 
     /// <summary>
     /// Alias dictionary.
@@ -31,10 +34,10 @@ namespace Apache.Ignite.Linq.Impl
         private int _aliasIndex;
 
         /** */
-        private Dictionary<string, string> _aliases = new Dictionary<string, string>();
+        private Dictionary<IQuerySource, string> _aliases = new Dictionary<IQuerySource, string>();
 
         /** */
-        private readonly Stack<Dictionary<string, string>> _stack = new Stack<Dictionary<string, string>>();
+        private readonly Stack<Dictionary<IQuerySource, string>> _stack = new Stack<Dictionary<IQuerySource, string>>();
 
         /// <summary>
         /// Pushes current aliases to stack.
@@ -43,7 +46,7 @@ namespace Apache.Ignite.Linq.Impl
         {
             _stack.Push(_aliases);
 
-            _aliases = new Dictionary<string, string>();
+            _aliases = new Dictionary<IQuerySource, string>();
         }
 
         /// <summary>
@@ -57,27 +60,34 @@ namespace Apache.Ignite.Linq.Impl
         /// <summary>
         /// Gets the table alias.
         /// </summary>
-        public string GetTableAlias(ICacheQueryableInternal queryable)
+        public string GetTableAlias(Expression expression)
         {
-            Debug.Assert(queryable != null);
+            Debug.Assert(expression != null);
 
-            return GetTableAlias(ExpressionWalker.GetTableNameWithSchema(queryable));
+            return GetTableAlias(GetQuerySource(expression));
         }
 
-        /// <summary>
-        /// Gets the table alias.
-        /// </summary>
-        public string GetTableAlias(string fullName)
+        public string GetTableAlias(IFromClause fromClause)
         {
-            Debug.Assert(!string.IsNullOrEmpty(fullName));
+            return GetTableAlias(GetQuerySource(fromClause.FromExpression) ?? fromClause);
+        }
+
+        public string GetTableAlias(JoinClause joinClause)
+        {
+            return GetTableAlias(GetQuerySource(joinClause.InnerSequence) ?? joinClause);
+        }
+
+        private string GetTableAlias(IQuerySource querySource)
+        {
+            Debug.Assert(querySource != null);
 
             string alias;
 
-            if (!_aliases.TryGetValue(fullName, out alias))
+            if (!_aliases.TryGetValue(querySource, out alias))
             {
                 alias = "_T" + _aliasIndex++;
 
-                _aliases[fullName] = alias;
+                _aliases[querySource] = alias;
             }
 
             return alias;
@@ -94,9 +104,45 @@ namespace Apache.Ignite.Linq.Impl
             var queryable = ExpressionWalker.GetCacheQueryable(clause);
             var tableName = ExpressionWalker.GetTableNameWithSchema(queryable);
 
-            builder.AppendFormat("{0} as {1}", tableName, GetTableAlias(tableName));
+            builder.AppendFormat("{0} as {1}", tableName, GetTableAlias(clause));
 
             return builder;
         }
+
+        /// <summary>
+        /// Gets the query source.
+        /// </summary>
+        private static IQuerySource GetQuerySource(Expression expression)
+        {
+            var subQueryExp = expression as SubQueryExpression;
+
+            if (subQueryExp != null)
+                return GetQuerySource(subQueryExp.QueryModel.MainFromClause.FromExpression)
+                    ?? subQueryExp.QueryModel.MainFromClause;
+
+            var srcRefExp = expression as QuerySourceReferenceExpression;
+
+            if (srcRefExp != null)
+            {
+                var fromSource = srcRefExp.ReferencedQuerySource as IFromClause;
+
+                if (fromSource != null)
+                    return GetQuerySource(fromSource.FromExpression) ?? fromSource;
+
+                var joinSource = srcRefExp.ReferencedQuerySource as JoinClause;
+
+                if (joinSource != null)
+                    return GetQuerySource(joinSource.InnerSequence) ?? joinSource;
+
+                throw new NotSupportedException("Unexpected query source: " + srcRefExp.ReferencedQuerySource);
+            }
+
+            var memberExpr = expression as MemberExpression;
+
+            if (memberExpr != null)
+                return GetQuerySource(memberExpr.Expression);
+
+            return null;
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/f96b568e/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..2362137 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs
@@ -248,7 +248,7 @@ namespace Apache.Ignite.Linq.Impl
             // In other cases we need both parts of cache entry
             var format = _useStar ? "{0}.*" : "{0}._key, {0}._val";
 
-            var tableName = Aliases.GetTableAlias(ExpressionWalker.GetCacheQueryable(expression));
+            var tableName = Aliases.GetTableAlias(expression);
 
             ResultBuilder.AppendFormat(format, tableName);
 
@@ -283,7 +283,7 @@ namespace Apache.Ignite.Linq.Impl
             {
                 var fieldName = GetFieldName(expression, queryable);
 
-                ResultBuilder.AppendFormat("{0}.{1}", Aliases.GetTableAlias(queryable), fieldName);
+                ResultBuilder.AppendFormat("{0}.{1}", Aliases.GetTableAlias(expression), fieldName);
             }
             else
                 AppendParameter(RegisterEvaluatedParameter(expression));

http://git-wip-us.apache.org/repos/asf/ignite/blob/f96b568e/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..a8c4c67 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs
@@ -418,8 +418,7 @@ namespace Apache.Ignite.Linq.Impl
 
                 VisitQueryModel(subQuery.QueryModel, true);
 
-                var queryable = ExpressionWalker.GetCacheQueryable(subQuery.QueryModel.MainFromClause);
-                var alias = _aliases.GetTableAlias(queryable);
+                var alias = _aliases.GetTableAlias(subQuery.QueryModel.MainFromClause);
                 _builder.AppendFormat(") as {0} on (", alias);
             }
             else
@@ -437,7 +436,8 @@ namespace Apache.Ignite.Linq.Impl
 
                 var queryable = ExpressionWalker.GetCacheQueryable(joinClause);
                 var tableName = ExpressionWalker.GetTableNameWithSchema(queryable);
-                _builder.AppendFormat("inner join {0} as {1} on (", tableName, _aliases.GetTableAlias(tableName));
+                var alias = _aliases.GetTableAlias(joinClause);
+                _builder.AppendFormat("inner join {0} as {1} on (", tableName, alias);
             }
 
             BuildJoinCondition(joinClause.InnerKeySelector, joinClause.OuterKeySelector);

http://git-wip-us.apache.org/repos/asf/ignite/blob/f96b568e/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..bd57e3f 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ExpressionWalker.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ExpressionWalker.cs
@@ -38,26 +38,6 @@ namespace Apache.Ignite.Linq.Impl
         /// <summary>
         /// Gets the cache queryable.
         /// </summary>
-        public static ICacheQueryableInternal GetCacheQueryable(QuerySourceReferenceExpression expression)
-        {
-            Debug.Assert(expression != null);
-
-            var fromSource = expression.ReferencedQuerySource as IFromClause; 
-
-            if (fromSource != null)
-                return GetCacheQueryable(fromSource);
-
-            var joinSource = expression.ReferencedQuerySource as JoinClause;
-
-            if (joinSource != null)
-                return GetCacheQueryable(joinSource);
-
-            throw new NotSupportedException("Unexpected query source: " + expression.ReferencedQuerySource);
-        }
-
-        /// <summary>
-        /// Gets the cache queryable.
-        /// </summary>
         public static ICacheQueryableInternal GetCacheQueryable(IFromClause fromClause)
         {
             return GetCacheQueryable(fromClause.FromExpression);
@@ -84,7 +64,19 @@ namespace Apache.Ignite.Linq.Impl
             var srcRefExp = expression as QuerySourceReferenceExpression;
 
             if (srcRefExp != null)
-                return GetCacheQueryable(srcRefExp);
+            {
+                var fromSource = srcRefExp.ReferencedQuerySource as IFromClause;
+
+                if (fromSource != null)
+                    return GetCacheQueryable(fromSource);
+
+                var joinSource = srcRefExp.ReferencedQuerySource as JoinClause;
+
+                if (joinSource != null)
+                    return GetCacheQueryable(joinSource);
+
+                throw new NotSupportedException("Unexpected query source: " + srcRefExp.ReferencedQuerySource);
+            }
 
             var memberExpr = expression as MemberExpression;
 


[21/50] [abbrv] ignite git commit: IGNITE-2344 WebSessionFilter doesn't support session ID renewal

Posted by vo...@apache.org.
IGNITE-2344 WebSessionFilter doesn't support session ID renewal


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

Branch: refs/heads/ignite-3553
Commit: 58d054443d48f2ef48e935bd4e5ce24b5bb352ed
Parents: df1490e
Author: samaitra <sa...@gmail.com>
Authored: Mon Jul 25 10:54:35 2016 +0300
Committer: Anton Vinogradov <av...@apache.org>
Committed: Mon Jul 25 10:54:35 2016 +0300

----------------------------------------------------------------------
 .../ignite/internal/websession/WebSessionSelfTest.java       | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/58d05444/modules/web/src/test/java/org/apache/ignite/internal/websession/WebSessionSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/web/src/test/java/org/apache/ignite/internal/websession/WebSessionSelfTest.java b/modules/web/src/test/java/org/apache/ignite/internal/websession/WebSessionSelfTest.java
index 0ab1130..1a8c328 100644
--- a/modules/web/src/test/java/org/apache/ignite/internal/websession/WebSessionSelfTest.java
+++ b/modules/web/src/test/java/org/apache/ignite/internal/websession/WebSessionSelfTest.java
@@ -854,7 +854,7 @@ public class WebSessionSelfTest extends GridCommonAbstractTest {
         HashLoginService hashLoginService = new HashLoginService();
         hashLoginService.setName("Test Realm");
         createRealm();
-        hashLoginService.setConfig("realm.properties");
+        hashLoginService.setConfig("/tmp/realm.properties");
         ctx.getSecurityHandler().setLoginService(hashLoginService);
 
         srv.setHandler(ctx);
@@ -884,14 +884,14 @@ public class WebSessionSelfTest extends GridCommonAbstractTest {
     private void stopServerWithLoginService(@Nullable Server srv) throws Exception{
         if (srv != null){
             srv.stop();
-            File realmFile = new File("realm.properties");
+            File realmFile = new File("/tmp/realm.properties");
             realmFile.delete();
         }
     }
 
     /** Creates a realm file to store test user credentials */
     private void createRealm() throws Exception{
-        File realmFile = new File("realm.properties");
+        File realmFile = new File("/tmp/realm.properties");
         FileWriter fileWriter = new FileWriter(realmFile);
         fileWriter.append("admin:admin");
         fileWriter.flush();
@@ -1128,7 +1128,7 @@ public class WebSessionSelfTest extends GridCommonAbstractTest {
                 try {
                     req.login("admin", "admin");
                 } catch (Exception e) {
-                    X.printerrln("Login failed.");
+                    X.printerrln("Login failed due to exception.", e);
                 }
 
                 HttpSession session = req.getSession();


[41/50] [abbrv] ignite git commit: Merge remote-tracking branch 'remotes/community/ignite-1.6.3'

Posted by vo...@apache.org.
Merge remote-tracking branch 'remotes/community/ignite-1.6.3'


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

Branch: refs/heads/ignite-3553
Commit: 87a7c79b5a59470ae7ec59867b765b5e6c586140
Parents: 34d553a 4979c87
Author: Andrey Novikov <an...@apache.org>
Authored: Wed Jul 27 17:51:26 2016 +0700
Committer: Andrey Novikov <an...@apache.org>
Committed: Wed Jul 27 17:51:26 2016 +0700

----------------------------------------------------------------------
 .../visor/node/VisorNodeDataCollectorJob.java   | 25 +++++++++++++++++---
 1 file changed, 22 insertions(+), 3 deletions(-)
----------------------------------------------------------------------



[30/50] [abbrv] ignite git commit: IGNITE-3509 .NET: Add missing package-info, include LINQ and AspNet in doxygen

Posted by vo...@apache.org.
IGNITE-3509 .NET: Add missing package-info, include LINQ and AspNet in doxygen


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

Branch: refs/heads/ignite-3553
Commit: e70adde719009e99e56bb4398c6fa3e43cf6b2ca
Parents: 44cabd8
Author: Pavel Tupitsyn <pt...@apache.org>
Authored: Tue Jul 26 10:47:49 2016 +0300
Committer: Pavel Tupitsyn <pt...@apache.org>
Committed: Tue Jul 26 10:47:49 2016 +0300

----------------------------------------------------------------------
 .../Apache.Ignite.AspNet.csproj                 |   1 +
 .../dotnet/Apache.Ignite.AspNet/Package-Info.cs |  26 ++++
 .../Apache.Ignite.Core.csproj                   |   6 +
 .../Cache/Affinity/AffinityFunctionBase.cs      | 139 -------------------
 .../Configuration/Package-Info.cs               |  26 ++++
 .../Discovery/Package-Info.cs                   |  26 ++++
 .../Discovery/Tcp/Multicast/Package-Info.cs     |  26 ++++
 .../Discovery/Tcp/Package-Info.cs               |  26 ++++
 .../Discovery/Tcp/Static/Package-Info.cs        |  26 ++++
 .../Apache.Ignite.Core/Interop/Package-Info.cs  |  26 ++++
 .../Apache.Ignite.Linq.csproj                   |   1 +
 .../dotnet/Apache.Ignite.Linq/Package-Info.cs   |  26 ++++
 modules/platforms/dotnet/Apache.Ignite.dxg      |   4 +-
 13 files changed, 218 insertions(+), 141 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/e70adde7/modules/platforms/dotnet/Apache.Ignite.AspNet/Apache.Ignite.AspNet.csproj
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.AspNet/Apache.Ignite.AspNet.csproj b/modules/platforms/dotnet/Apache.Ignite.AspNet/Apache.Ignite.AspNet.csproj
index b18119e..0c273e0 100644
--- a/modules/platforms/dotnet/Apache.Ignite.AspNet/Apache.Ignite.AspNet.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.AspNet/Apache.Ignite.AspNet.csproj
@@ -48,6 +48,7 @@
   <ItemGroup>
     <Compile Include="IgniteOutputCacheProvider.cs" />
     <Compile Include="IgniteWebUtils.cs" />
+    <Compile Include="Package-Info.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
   </ItemGroup>
   <ItemGroup>

http://git-wip-us.apache.org/repos/asf/ignite/blob/e70adde7/modules/platforms/dotnet/Apache.Ignite.AspNet/Package-Info.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.AspNet/Package-Info.cs b/modules/platforms/dotnet/Apache.Ignite.AspNet/Package-Info.cs
new file mode 100644
index 0000000..5e74d3c
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.AspNet/Package-Info.cs
@@ -0,0 +1,26 @@
+\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.
+*/
+
+#pragma warning disable 1587   // invalid XML comment
+
+/// <summary>
+/// Ignite ASP.NET integration.
+/// </summary>
+namespace Apache.Ignite.AspNet
+{
+    // No-op.
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/e70adde7/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
index 2e374e5..59bf090 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
@@ -85,6 +85,11 @@
   <ItemGroup>
     <Compile Include="Binary\BinaryReflectiveSerializer.cs" />
     <Compile Include="Common\JavaException.cs" />
+    <Compile Include="DataStructures\Configuration\Package-Info.cs" />
+    <Compile Include="Discovery\Package-Info.cs" />
+    <Compile Include="Discovery\Tcp\Multicast\Package-Info.cs" />
+    <Compile Include="Discovery\Tcp\Package-Info.cs" />
+    <Compile Include="Discovery\Tcp\Static\Package-Info.cs" />
     <Compile Include="Impl\Binary\BinaryReflectiveSerializerInternal.cs" />
     <Compile Include="Impl\Binary\IBinarySerializerInternal.cs" />
     <Compile Include="Binary\Package-Info.cs" />
@@ -427,6 +432,7 @@
     <Compile Include="Impl\Unmanaged\UnmanagedTarget.cs" />
     <Compile Include="Impl\Unmanaged\UnmanagedUtils.cs" />
     <Compile Include="Interop\JavaObject.cs" />
+    <Compile Include="Interop\Package-Info.cs" />
     <Compile Include="Lifecycle\ClientReconnectEventArgs.cs" />
     <Compile Include="Lifecycle\Package-Info.cs" />
     <Compile Include="Messaging\Package-Info.cs" />

http://git-wip-us.apache.org/repos/asf/ignite/blob/e70adde7/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Affinity/AffinityFunctionBase.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Affinity/AffinityFunctionBase.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Affinity/AffinityFunctionBase.cs
deleted file mode 100644
index ce2e5e1..0000000
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Affinity/AffinityFunctionBase.cs
+++ /dev/null
@@ -1,139 +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.Core.Cache.Affinity
-{
-    using System;
-    using System.Collections.Generic;
-    using System.ComponentModel;
-    using Apache.Ignite.Core.Cluster;
-    using Apache.Ignite.Core.Common;
-
-    /// <summary>
-    /// Base class for predefined affinity functions.
-    /// </summary>
-    [Serializable]
-    public abstract class AffinityFunctionBase : IAffinityFunction
-    {
-        /// <summary> The default value for <see cref="Partitions"/> property. </summary>
-        public const int DefaultPartitions = 1024;
-
-        /** */
-        private int _partitions = DefaultPartitions;
-
-        /** */
-        private IAffinityFunction _baseFunction;
-
-
-        /// <summary>
-        /// Gets or sets the total number of partitions.
-        /// </summary>
-        [DefaultValue(DefaultPartitions)]
-        public virtual int Partitions
-        {
-            get { return _partitions; }
-            set { _partitions = value; }
-        }
-
-        /// <summary>
-        /// Gets partition number for a given key starting from 0. Partitioned caches
-        /// should make sure that keys are about evenly distributed across all partitions
-        /// from 0 to <see cref="Partitions" /> for best performance.
-        /// <para />
-        /// Note that for fully replicated caches it is possible to segment key sets among different
-        /// grid node groups. In that case each node group should return a unique partition
-        /// number. However, unlike partitioned cache, mappings of keys to nodes in
-        /// replicated caches are constant and a node cannot migrate from one partition
-        /// to another.
-        /// </summary>
-        /// <param name="key">Key to get partition for.</param>
-        /// <returns>
-        /// Partition number for a given key.
-        /// </returns>
-        public virtual int GetPartition(object key)
-        {
-            ThrowIfUninitialized();
-
-            return _baseFunction.GetPartition(key);
-        }
-
-        /// <summary>
-        /// Removes node from affinity. This method is called when it is safe to remove
-        /// disconnected node from affinity mapping.
-        /// </summary>
-        /// <param name="nodeId">The node identifier.</param>
-        public virtual void RemoveNode(Guid nodeId)
-        {
-            ThrowIfUninitialized();
-
-            _baseFunction.RemoveNode(nodeId);
-        }
-
-        /// <summary>
-        /// Gets affinity nodes for a partition. In case of replicated cache, all returned
-        /// nodes are updated in the same manner. In case of partitioned cache, the returned
-        /// list should contain only the primary and back up nodes with primary node being
-        /// always first.
-        /// <pare />
-        /// Note that partitioned affinity must obey the following contract: given that node
-        /// <code>N</code> is primary for some key <code>K</code>, if any other node(s) leave
-        /// grid and no node joins grid, node <code>N</code> will remain primary for key <code>K</code>.
-        /// </summary>
-        /// <param name="context">The affinity function context.</param>
-        /// <returns>
-        /// A collection of partitions, where each partition is a collection of nodes,
-        /// where first node is a primary node, and other nodes are backup nodes.
-        /// </returns>
-        public virtual IEnumerable<IEnumerable<IClusterNode>> AssignPartitions(AffinityFunctionContext context)
-        {
-            ThrowIfUninitialized();
-
-            return _baseFunction.AssignPartitions(context);
-        }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether to exclude same-host-neighbors from being backups of each other.
-        /// </summary>
-        public virtual bool ExcludeNeighbors { get; set; }
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="AffinityFunctionBase"/> class.
-        /// </summary>
-        internal AffinityFunctionBase()
-        {
-            // No-op.
-        }
-
-        /// <summary>
-        /// Sets the base function.
-        /// </summary>
-        /// <param name="baseFunc">The base function.</param>
-        internal void SetBaseFunction(IAffinityFunction baseFunc)
-        {
-            _baseFunction = baseFunc;
-        }
-
-        /// <summary>
-        /// Gets the direct usage error.
-        /// </summary>
-        private void ThrowIfUninitialized()
-        {
-            if (_baseFunction == null)
-                throw new IgniteException(GetType() + " has not yet been initialized.");
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/ignite/blob/e70adde7/modules/platforms/dotnet/Apache.Ignite.Core/DataStructures/Configuration/Package-Info.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/DataStructures/Configuration/Package-Info.cs b/modules/platforms/dotnet/Apache.Ignite.Core/DataStructures/Configuration/Package-Info.cs
new file mode 100644
index 0000000..aa6cdfd
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/DataStructures/Configuration/Package-Info.cs
@@ -0,0 +1,26 @@
+\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.
+*/
+
+#pragma warning disable 1587   // invalid XML comment
+
+/// <summary>
+/// Distributed data structures configuration.
+/// </summary>
+namespace Apache.Ignite.Core.DataStructures.Configuration
+{
+    // No-op.
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/e70adde7/modules/platforms/dotnet/Apache.Ignite.Core/Discovery/Package-Info.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Discovery/Package-Info.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Discovery/Package-Info.cs
new file mode 100644
index 0000000..c6fadec
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Discovery/Package-Info.cs
@@ -0,0 +1,26 @@
+\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.
+*/
+
+#pragma warning disable 1587   // invalid XML comment
+
+/// <summary>
+/// Node discovery API.
+/// </summary>
+namespace Apache.Ignite.Core.Discovery
+{
+    // No-op.
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/e70adde7/modules/platforms/dotnet/Apache.Ignite.Core/Discovery/Tcp/Multicast/Package-Info.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Discovery/Tcp/Multicast/Package-Info.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Discovery/Tcp/Multicast/Package-Info.cs
new file mode 100644
index 0000000..0a1c751
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Discovery/Tcp/Multicast/Package-Info.cs
@@ -0,0 +1,26 @@
+\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.
+*/
+
+#pragma warning disable 1587   // invalid XML comment
+
+/// <summary>
+/// Multicast TCP discovery.
+/// </summary>
+namespace Apache.Ignite.Core.Discovery.Tcp.Multicast
+{
+    // No-op.
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/e70adde7/modules/platforms/dotnet/Apache.Ignite.Core/Discovery/Tcp/Package-Info.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Discovery/Tcp/Package-Info.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Discovery/Tcp/Package-Info.cs
new file mode 100644
index 0000000..dc94d48
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Discovery/Tcp/Package-Info.cs
@@ -0,0 +1,26 @@
+\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.
+*/
+
+#pragma warning disable 1587   // invalid XML comment
+
+/// <summary>
+/// TCP node discovery API.
+/// </summary>
+namespace Apache.Ignite.Core.Discovery.Tcp
+{
+    // No-op.
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/e70adde7/modules/platforms/dotnet/Apache.Ignite.Core/Discovery/Tcp/Static/Package-Info.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Discovery/Tcp/Static/Package-Info.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Discovery/Tcp/Static/Package-Info.cs
new file mode 100644
index 0000000..bf3fa5c
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Discovery/Tcp/Static/Package-Info.cs
@@ -0,0 +1,26 @@
+\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.
+*/
+
+#pragma warning disable 1587   // invalid XML comment
+
+/// <summary>
+/// Static TCP discovery.
+/// </summary>
+namespace Apache.Ignite.Core.Discovery.Tcp.Static
+{
+    // No-op.
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/e70adde7/modules/platforms/dotnet/Apache.Ignite.Core/Interop/Package-Info.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Interop/Package-Info.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Interop/Package-Info.cs
new file mode 100644
index 0000000..51f26df
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Interop/Package-Info.cs
@@ -0,0 +1,26 @@
+\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.
+*/
+
+#pragma warning disable 1587   // invalid XML comment
+
+/// <summary>
+/// Platform interoperability.
+/// </summary>
+namespace Apache.Ignite.Core.Interop
+{
+    // No-op.
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/e70adde7/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..3abfa56 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj
@@ -69,6 +69,7 @@
     <Compile Include="Impl\QueryData.cs" />
     <Compile Include="Impl\SqlTypes.cs" />
     <Compile Include="Impl\ExpressionWalker.cs" />
+    <Compile Include="Package-Info.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
   </ItemGroup>
   <ItemGroup>

http://git-wip-us.apache.org/repos/asf/ignite/blob/e70adde7/modules/platforms/dotnet/Apache.Ignite.Linq/Package-Info.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Package-Info.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Package-Info.cs
new file mode 100644
index 0000000..1c1e370
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Package-Info.cs
@@ -0,0 +1,26 @@
+\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.
+*/
+
+#pragma warning disable 1587   // invalid XML comment
+
+/// <summary>
+/// Ignite cache LINQ provider.
+/// </summary>
+namespace Apache.Ignite.Linq
+{
+    // No-op.
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/e70adde7/modules/platforms/dotnet/Apache.Ignite.dxg
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.dxg b/modules/platforms/dotnet/Apache.Ignite.dxg
index 6337d83..012a1eb 100644
--- a/modules/platforms/dotnet/Apache.Ignite.dxg
+++ b/modules/platforms/dotnet/Apache.Ignite.dxg
@@ -2381,7 +2381,7 @@ GENERATE_LEGEND        = YES
 DOT_CLEANUP            = YES
 
 
-;INPUT=Apache.Ignite.Core
-;EXCLUDE=Apache.Ignite.Core/Impl
+;INPUT=Apache.Ignite.Core Apache.Ignite.Linq Apache.Ignite.AspNet
+;EXCLUDE=Apache.Ignite.Core/Impl Apache.Ignite.Linq/Impl
 ;OUTPUT_DIRECTORY=../../clients/target/dotnetdoc
 ;PROJECT_LOGO=../../../assembly/docfiles/ignite_logo.png


[32/50] [abbrv] ignite git commit: IGNITE-3323 Get rid of copypasted JB annotations, use dependency instead.

Posted by vo...@apache.org.
IGNITE-3323 Get rid of copypasted JB annotations, use dependency instead.


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

Branch: refs/heads/ignite-3553
Commit: 3af756d71a68ad195d48bcced50ea92ba3078fad
Parents: fb884ec
Author: Jens Hoffmann <jc...@gmail.com>
Authored: Tue Jul 26 13:04:38 2016 +0300
Committer: Anton Vinogradov <av...@apache.org>
Committed: Tue Jul 26 13:04:38 2016 +0300

----------------------------------------------------------------------
 modules/core/pom.xml                            |  6 ++++
 .../java/org/jetbrains/annotations/NotNull.java | 38 --------------------
 .../org/jetbrains/annotations/Nullable.java     | 33 -----------------
 .../org/jetbrains/annotations/package-info.java | 27 --------------
 parent/pom.xml                                  |  1 +
 5 files changed, 7 insertions(+), 98 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/3af756d7/modules/core/pom.xml
----------------------------------------------------------------------
diff --git a/modules/core/pom.xml b/modules/core/pom.xml
index 5e32caf..7eefa4a 100644
--- a/modules/core/pom.xml
+++ b/modules/core/pom.xml
@@ -69,6 +69,12 @@
         </dependency>
 
         <dependency>
+            <groupId>org.jetbrains</groupId>
+            <artifactId>annotations</artifactId>
+            <version>${jetbrains.annotations.version}</version>
+        </dependency>
+
+        <dependency>
             <groupId>mx4j</groupId>
             <artifactId>mx4j-tools</artifactId>
             <version>3.0.1</version>

http://git-wip-us.apache.org/repos/asf/ignite/blob/3af756d7/modules/core/src/main/java/org/jetbrains/annotations/NotNull.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/jetbrains/annotations/NotNull.java b/modules/core/src/main/java/org/jetbrains/annotations/NotNull.java
deleted file mode 100644
index 66c66f3..0000000
--- a/modules/core/src/main/java/org/jetbrains/annotations/NotNull.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2000-2012 JetBrains s.r.o.
- *
- * Licensed 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.
- */
-
-package org.jetbrains.annotations;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * An element annotated with NotNull claims <code>null</code> value is <em>forbidden</em>
- * to return (for methods), pass to (parameters) and hold (local variables and fields).
- * Apart from documentation purposes this annotation is intended to be used by static analysis tools
- * to validate against probable runtime errors and element contract violations.
- *
- * @author max
- */
-@Documented
-@Retention(RetentionPolicy.CLASS)
-@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE})
-public @interface NotNull {
-    String value() default "";
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/3af756d7/modules/core/src/main/java/org/jetbrains/annotations/Nullable.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/jetbrains/annotations/Nullable.java b/modules/core/src/main/java/org/jetbrains/annotations/Nullable.java
deleted file mode 100644
index defd5d8..0000000
--- a/modules/core/src/main/java/org/jetbrains/annotations/Nullable.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2000-2009 JetBrains s.r.o.
- *
- * Licensed 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.
- */
-
-package org.jetbrains.annotations;
-
-import java.lang.annotation.*;
-
-/**
- * An element annotated with Nullable claims <code>null</code> value is perfectly <em>valid</em>
- * to return (for methods), pass to (parameters) and hold (local variables and fields).
- * Apart from documentation purposes this annotation is intended to be used by static analysis tools
- * to validate against probable runtime errors and element contract violations.
- * @author max
- */
-@Documented
-@Retention(RetentionPolicy.CLASS)
-@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE})
-public @interface Nullable {
-    String value() default "";
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/3af756d7/modules/core/src/main/java/org/jetbrains/annotations/package-info.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/jetbrains/annotations/package-info.java b/modules/core/src/main/java/org/jetbrains/annotations/package-info.java
deleted file mode 100644
index 02e3be1..0000000
--- a/modules/core/src/main/java/org/jetbrains/annotations/package-info.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * 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.
- */
-
-/*
- * This software includes code from IntelliJ IDEA Community Edition
- * Copyright (C) JetBrains s.r.o.
- * https://www.jetbrains.com/idea/
- */
-
-/**
- * {@code Nullable} and {@code NotNull} annotations.
- */
-package org.jetbrains.annotations;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/3af756d7/parent/pom.xml
----------------------------------------------------------------------
diff --git a/parent/pom.xml b/parent/pom.xml
index ebd6bf0..17f66c9 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -78,6 +78,7 @@
         <jackson2.version>2.7.5</jackson2.version>
         <javax.cache.bundle.version>1.0.0_1</javax.cache.bundle.version>
         <javax.cache.version>1.0.0</javax.cache.version>
+        <jetbrains.annotations.version>15.0</jetbrains.annotations.version>
         <jetty.version>9.2.11.v20150529</jetty.version>
         <jms.spec.version>1.1.1</jms.spec.version>
         <jsch.bundle.version>0.1.53_1</jsch.bundle.version>


[26/50] [abbrv] ignite git commit: IGNITE-3504 .NET: Fix IBinaryObjectBuilder setter methods

Posted by vo...@apache.org.
IGNITE-3504 .NET: Fix IBinaryObjectBuilder setter methods


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

Branch: refs/heads/ignite-3553
Commit: 332c8c2f118226f3cd3c4dc1fa5564dd1d41d19c
Parents: 855281e
Author: Pavel Tupitsyn <pt...@apache.org>
Authored: Mon Jul 25 16:12:51 2016 +0300
Committer: Pavel Tupitsyn <pt...@apache.org>
Committed: Mon Jul 25 16:12:51 2016 +0300

----------------------------------------------------------------------
 .../Binary/BinaryBuilderSelfTest.cs             | 325 +++++++++++++++----
 .../Binary/IBinaryObjectBuilder.cs              |   2 +-
 .../Impl/Binary/BinaryObjectBuilder.cs          |  16 +-
 .../Impl/Binary/BinaryWriter.cs                 | 133 +++++---
 4 files changed, 345 insertions(+), 131 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/332c8c2f/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryBuilderSelfTest.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryBuilderSelfTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryBuilderSelfTest.cs
index 40ecc1e..80788a6 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryBuilderSelfTest.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryBuilderSelfTest.cs
@@ -595,7 +595,8 @@ namespace Apache.Ignite.Core.Tests.Binary
         [Test]
         public void TestPrimitiveFields()
         {
-            IBinaryObject binObj = _grid.GetBinary().GetBuilder(typeof(Primitives))
+            // Generic SetField method.
+            var binObj = _grid.GetBinary().GetBuilder(typeof(Primitives))
                 .SetField<byte>("fByte", 1)
                 .SetField("fBool", true)
                 .SetField<short>("fShort", 2)
@@ -604,16 +605,73 @@ namespace Apache.Ignite.Core.Tests.Binary
                 .SetField<long>("fLong", 4)
                 .SetField<float>("fFloat", 5)
                 .SetField<double>("fDouble", 6)
+                .SetField("fDecimal", 7.7m)
                 .SetHashCode(100)
                 .Build();
 
+            CheckPrimitiveFields1(binObj);
+
+            // Specific setter methods.
+            binObj = _grid.GetBinary().GetBuilder(typeof(Primitives))
+                .SetByteField("fByte", 1)
+                .SetBooleanField("fBool", true)
+                .SetShortField("fShort", 2)
+                .SetCharField("fChar", 'a')
+                .SetIntField("fInt", 3)
+                .SetLongField("fLong", 4)
+                .SetFloatField("fFloat", 5)
+                .SetDoubleField("fDouble", 6)
+                .SetDecimalField("fDecimal", 7.7m)
+                .SetHashCode(100)
+                .Build();
+
+            CheckPrimitiveFields1(binObj);
+
+            // Overwrite with generic methods.
+            binObj = binObj.ToBuilder()
+                .SetField<byte>("fByte", 7)
+                .SetField("fBool", false)
+                .SetField<short>("fShort", 8)
+                .SetField("fChar", 'b')
+                .SetField("fInt", 9)
+                .SetField<long>("fLong", 10)
+                .SetField<float>("fFloat", 11)
+                .SetField<double>("fDouble", 12)
+                .SetField("fDecimal", 13.13m)
+                .SetHashCode(200)
+                .Build();
+
+            CheckPrimitiveFields2(binObj);
+
+            // Overwrite with specific methods.
+            binObj = binObj.ToBuilder()
+                .SetByteField("fByte", 7)
+                .SetBooleanField("fBool", false)
+                .SetShortField("fShort", 8)
+                .SetCharField("fChar", 'b')
+                .SetIntField("fInt", 9)
+                .SetLongField("fLong", 10)
+                .SetFloatField("fFloat", 11)
+                .SetDoubleField("fDouble", 12)
+                .SetDecimalField("fDecimal", 13.13m)
+                .SetHashCode(200)
+                .Build();
+
+            CheckPrimitiveFields2(binObj);
+        }
+
+        /// <summary>
+        /// Checks the primitive fields values.
+        /// </summary>
+        private static void CheckPrimitiveFields1(IBinaryObject binObj)
+        {
             Assert.AreEqual(100, binObj.GetHashCode());
 
             IBinaryType meta = binObj.GetBinaryType();
 
             Assert.AreEqual(typeof(Primitives).Name, meta.TypeName);
 
-            Assert.AreEqual(8, meta.Fields.Count);
+            Assert.AreEqual(9, meta.Fields.Count);
 
             Assert.AreEqual(BinaryTypeNames.TypeNameByte, meta.GetFieldTypeName("fByte"));
             Assert.AreEqual(BinaryTypeNames.TypeNameBool, meta.GetFieldTypeName("fBool"));
@@ -623,6 +681,7 @@ namespace Apache.Ignite.Core.Tests.Binary
             Assert.AreEqual(BinaryTypeNames.TypeNameLong, meta.GetFieldTypeName("fLong"));
             Assert.AreEqual(BinaryTypeNames.TypeNameFloat, meta.GetFieldTypeName("fFloat"));
             Assert.AreEqual(BinaryTypeNames.TypeNameDouble, meta.GetFieldTypeName("fDouble"));
+            Assert.AreEqual(BinaryTypeNames.TypeNameDecimal, meta.GetFieldTypeName("fDecimal"));
 
             Assert.AreEqual(1, binObj.GetField<byte>("fByte"));
             Assert.AreEqual(true, binObj.GetField<bool>("fBool"));
@@ -632,6 +691,7 @@ namespace Apache.Ignite.Core.Tests.Binary
             Assert.AreEqual(4, binObj.GetField<long>("fLong"));
             Assert.AreEqual(5, binObj.GetField<float>("fFloat"));
             Assert.AreEqual(6, binObj.GetField<double>("fDouble"));
+            Assert.AreEqual(7.7m, binObj.GetField<decimal>("fDecimal"));
 
             Primitives obj = binObj.Deserialize<Primitives>();
 
@@ -643,20 +703,14 @@ namespace Apache.Ignite.Core.Tests.Binary
             Assert.AreEqual(4, obj.FLong);
             Assert.AreEqual(5, obj.FFloat);
             Assert.AreEqual(6, obj.FDouble);
+            Assert.AreEqual(7.7m, obj.FDecimal);
+        }
 
-            // Overwrite.
-            binObj = binObj.ToBuilder()
-                .SetField<byte>("fByte", 7)
-                .SetField("fBool", false)
-                .SetField<short>("fShort", 8)
-                .SetField("fChar", 'b')
-                .SetField("fInt", 9)
-                .SetField<long>("fLong", 10)
-                .SetField<float>("fFloat", 11)
-                .SetField<double>("fDouble", 12)
-                .SetHashCode(200)
-                .Build();
-
+        /// <summary>
+        /// Checks the primitive fields values.
+        /// </summary>
+        private static void CheckPrimitiveFields2(IBinaryObject binObj)
+        {
             Assert.AreEqual(200, binObj.GetHashCode());
 
             Assert.AreEqual(7, binObj.GetField<byte>("fByte"));
@@ -667,8 +721,9 @@ namespace Apache.Ignite.Core.Tests.Binary
             Assert.AreEqual(10, binObj.GetField<long>("fLong"));
             Assert.AreEqual(11, binObj.GetField<float>("fFloat"));
             Assert.AreEqual(12, binObj.GetField<double>("fDouble"));
+            Assert.AreEqual(13.13m, binObj.GetField<decimal>("fDecimal"));
 
-            obj = binObj.Deserialize<Primitives>();
+            var obj = binObj.Deserialize<Primitives>();
 
             Assert.AreEqual(7, obj.FByte);
             Assert.AreEqual(false, obj.FBool);
@@ -678,6 +733,7 @@ namespace Apache.Ignite.Core.Tests.Binary
             Assert.AreEqual(10, obj.FLong);
             Assert.AreEqual(11, obj.FFloat);
             Assert.AreEqual(12, obj.FDouble);
+            Assert.AreEqual(13.13m, obj.FDecimal);
         }
 
         /// <summary>
@@ -686,7 +742,8 @@ namespace Apache.Ignite.Core.Tests.Binary
         [Test]
         public void TestPrimitiveArrayFields()
         {
-            IBinaryObject binObj = _grid.GetBinary().GetBuilder(typeof(PrimitiveArrays))
+            // Generic SetField method.
+            var binObj = _grid.GetBinary().GetBuilder(typeof(PrimitiveArrays))
                 .SetField("fByte", new byte[] { 1 })
                 .SetField("fBool", new[] { true })
                 .SetField("fShort", new short[] { 2 })
@@ -695,16 +752,71 @@ namespace Apache.Ignite.Core.Tests.Binary
                 .SetField("fLong", new long[] { 4 })
                 .SetField("fFloat", new float[] { 5 })
                 .SetField("fDouble", new double[] { 6 })
+                .SetField("fDecimal", new decimal?[] { 7.7m })
                 .SetHashCode(100)
                 .Build();
 
+            CheckPrimitiveArrayFields1(binObj);
+
+            // Specific setters.
+            binObj = _grid.GetBinary().GetBuilder(typeof(PrimitiveArrays))
+                .SetByteArrayField("fByte", new byte[] {1})
+                .SetBooleanArrayField("fBool", new[] {true})
+                .SetShortArrayField("fShort", new short[] {2})
+                .SetCharArrayField("fChar", new[] {'a'})
+                .SetIntArrayField("fInt", new[] {3})
+                .SetLongArrayField("fLong", new long[] {4})
+                .SetFloatArrayField("fFloat", new float[] {5})
+                .SetDoubleArrayField("fDouble", new double[] {6})
+                .SetDecimalArrayField("fDecimal", new decimal?[] {7.7m})
+                .SetHashCode(100)
+                .Build();
+            
+            // Overwrite with generic setter.
+            binObj = _grid.GetBinary().GetBuilder(binObj)
+                .SetField("fByte", new byte[] { 7 })
+                .SetField("fBool", new[] { false })
+                .SetField("fShort", new short[] { 8 })
+                .SetField("fChar", new[] { 'b' })
+                .SetField("fInt", new[] { 9 })
+                .SetField("fLong", new long[] { 10 })
+                .SetField("fFloat", new float[] { 11 })
+                .SetField("fDouble", new double[] { 12 })
+                .SetField("fDecimal", new decimal?[] { 13.13m })
+                .SetHashCode(200)
+                .Build();
+
+            CheckPrimitiveArrayFields2(binObj);
+
+            // Overwrite with specific setters.
+            binObj = _grid.GetBinary().GetBuilder(binObj)
+                .SetByteArrayField("fByte", new byte[] { 7 })
+                .SetBooleanArrayField("fBool", new[] { false })
+                .SetShortArrayField("fShort", new short[] { 8 })
+                .SetCharArrayField("fChar", new[] { 'b' })
+                .SetIntArrayField("fInt", new[] { 9 })
+                .SetLongArrayField("fLong", new long[] { 10 })
+                .SetFloatArrayField("fFloat", new float[] { 11 })
+                .SetDoubleArrayField("fDouble", new double[] { 12 })
+                .SetDecimalArrayField("fDecimal", new decimal?[] { 13.13m })
+                .SetHashCode(200)
+                .Build();
+
+            CheckPrimitiveArrayFields2(binObj);
+        }
+
+        /// <summary>
+        /// Checks the primitive array fields.
+        /// </summary>
+        private static void CheckPrimitiveArrayFields1(IBinaryObject binObj)
+        {
             Assert.AreEqual(100, binObj.GetHashCode());
 
             IBinaryType meta = binObj.GetBinaryType();
 
             Assert.AreEqual(typeof(PrimitiveArrays).Name, meta.TypeName);
 
-            Assert.AreEqual(8, meta.Fields.Count);
+            Assert.AreEqual(9, meta.Fields.Count);
 
             Assert.AreEqual(BinaryTypeNames.TypeNameArrayByte, meta.GetFieldTypeName("fByte"));
             Assert.AreEqual(BinaryTypeNames.TypeNameArrayBool, meta.GetFieldTypeName("fBool"));
@@ -714,6 +826,7 @@ namespace Apache.Ignite.Core.Tests.Binary
             Assert.AreEqual(BinaryTypeNames.TypeNameArrayLong, meta.GetFieldTypeName("fLong"));
             Assert.AreEqual(BinaryTypeNames.TypeNameArrayFloat, meta.GetFieldTypeName("fFloat"));
             Assert.AreEqual(BinaryTypeNames.TypeNameArrayDouble, meta.GetFieldTypeName("fDouble"));
+            Assert.AreEqual(BinaryTypeNames.TypeNameArrayDecimal, meta.GetFieldTypeName("fDecimal"));
 
             Assert.AreEqual(new byte[] { 1 }, binObj.GetField<byte[]>("fByte"));
             Assert.AreEqual(new[] { true }, binObj.GetField<bool[]>("fBool"));
@@ -723,6 +836,7 @@ namespace Apache.Ignite.Core.Tests.Binary
             Assert.AreEqual(new long[] { 4 }, binObj.GetField<long[]>("fLong"));
             Assert.AreEqual(new float[] { 5 }, binObj.GetField<float[]>("fFloat"));
             Assert.AreEqual(new double[] { 6 }, binObj.GetField<double[]>("fDouble"));
+            Assert.AreEqual(new decimal?[] { 7.7m }, binObj.GetField<decimal?[]>("fDecimal"));
 
             PrimitiveArrays obj = binObj.Deserialize<PrimitiveArrays>();
 
@@ -734,20 +848,14 @@ namespace Apache.Ignite.Core.Tests.Binary
             Assert.AreEqual(new long[] { 4 }, obj.FLong);
             Assert.AreEqual(new float[] { 5 }, obj.FFloat);
             Assert.AreEqual(new double[] { 6 }, obj.FDouble);
+            Assert.AreEqual(new decimal?[] { 7.7m }, obj.FDecimal);
+        }
 
-            // Overwrite.
-            binObj = _grid.GetBinary().GetBuilder(binObj)
-                .SetField("fByte", new byte[] { 7 })
-                .SetField("fBool", new[] { false })
-                .SetField("fShort", new short[] { 8 })
-                .SetField("fChar", new[] { 'b' })
-                .SetField("fInt", new[] { 9 })
-                .SetField("fLong", new long[] { 10 })
-                .SetField("fFloat", new float[] { 11 })
-                .SetField("fDouble", new double[] { 12 })
-                .SetHashCode(200)
-                .Build();
-
+        /// <summary>
+        /// Checks the primitive array fields.
+        /// </summary>
+        private static void CheckPrimitiveArrayFields2(IBinaryObject binObj)
+        {
             Assert.AreEqual(200, binObj.GetHashCode());
 
             Assert.AreEqual(new byte[] { 7 }, binObj.GetField<byte[]>("fByte"));
@@ -758,8 +866,9 @@ namespace Apache.Ignite.Core.Tests.Binary
             Assert.AreEqual(new long[] { 10 }, binObj.GetField<long[]>("fLong"));
             Assert.AreEqual(new float[] { 11 }, binObj.GetField<float[]>("fFloat"));
             Assert.AreEqual(new double[] { 12 }, binObj.GetField<double[]>("fDouble"));
+            Assert.AreEqual(new decimal?[] { 13.13m }, binObj.GetField<decimal?[]>("fDecimal"));
 
-            obj = binObj.Deserialize<PrimitiveArrays>();
+            var obj = binObj.Deserialize<PrimitiveArrays>();
 
             Assert.AreEqual(new byte[] { 7 }, obj.FByte);
             Assert.AreEqual(new[] { false }, obj.FBool);
@@ -769,6 +878,7 @@ namespace Apache.Ignite.Core.Tests.Binary
             Assert.AreEqual(new long[] { 10 }, obj.FLong);
             Assert.AreEqual(new float[] { 11 }, obj.FFloat);
             Assert.AreEqual(new double[] { 12 }, obj.FDouble);
+            Assert.AreEqual(new decimal?[] { 13.13m }, obj.FDecimal);
         }
 
         /// <summary>
@@ -777,69 +887,141 @@ namespace Apache.Ignite.Core.Tests.Binary
         [Test]
         public void TestStringDateGuidEnum()
         {
-            DateTime? nDate = DateTime.Now;
+            DateTime? nDate = DateTime.Now.ToUniversalTime();
 
             Guid? nGuid = Guid.NewGuid();
 
+            // Generic setters.
             IBinaryObject binObj = _grid.GetBinary().GetBuilder(typeof(StringDateGuidEnum))
                 .SetField("fStr", "str")
                 .SetField("fNDate", nDate)
-                .SetGuidField("fNGuid", nGuid)
+                .SetTimestampField("fNTimestamp", nDate)
+                .SetField("fNGuid", nGuid)
                 .SetField("fEnum", TestEnum.One)
-                .SetField("fStrArr", new[] { "str" })
+                .SetStringArrayField("fStrArr", new[] { "str" })
+                .SetArrayField("fDateArr", new[] { nDate })
+                .SetTimestampArrayField("fTimestampArr", new[] { nDate })
+                .SetGuidArrayField("fGuidArr", new[] { nGuid })
+                .SetEnumArrayField("fEnumArr", new[] { TestEnum.One })
+                .SetHashCode(100)
+                .Build();
+
+            CheckStringDateGuidEnum1(binObj, nDate, nGuid);
+
+            // Specific setters.
+            binObj = _grid.GetBinary().GetBuilder(typeof(StringDateGuidEnum))
+                .SetStringField("fStr", "str")
+                .SetField("fNDate", nDate)
+                .SetTimestampField("fNTimestamp", nDate)
+                .SetGuidField("fNGuid", nGuid)
+                .SetEnumField("fEnum", TestEnum.One)
+                .SetStringArrayField("fStrArr", new[] { "str" })
                 .SetArrayField("fDateArr", new[] { nDate })
+                .SetTimestampArrayField("fTimestampArr", new[] { nDate })
                 .SetGuidArrayField("fGuidArr", new[] { nGuid })
-                .SetField("fEnumArr", new[] { TestEnum.One })
+                .SetEnumArrayField("fEnumArr", new[] { TestEnum.One })
                 .SetHashCode(100)
                 .Build();
 
+            CheckStringDateGuidEnum1(binObj, nDate, nGuid);
+
+            // Overwrite.
+            nDate = DateTime.Now.ToUniversalTime();
+            nGuid = Guid.NewGuid();
+
+            binObj = _grid.GetBinary().GetBuilder(typeof(StringDateGuidEnum))
+                .SetField("fStr", "str2")
+                .SetField("fNDate", nDate)
+                .SetTimestampField("fNTimestamp", nDate)
+                .SetField("fNGuid", nGuid)
+                .SetField("fEnum", TestEnum.Two)
+                .SetField("fStrArr", new[] { "str2" })
+                .SetArrayField("fDateArr", new[] { nDate })
+                .SetTimestampArrayField("fTimestampArr", new[] { nDate })
+                .SetField("fGuidArr", new[] { nGuid })
+                .SetField("fEnumArr", new[] { TestEnum.Two })
+                .SetHashCode(200)
+                .Build();
+
+            CheckStringDateGuidEnum2(binObj, nDate, nGuid);
+
+            // Overwrite with specific setters
+            binObj = _grid.GetBinary().GetBuilder(typeof(StringDateGuidEnum))
+                .SetStringField("fStr", "str2")
+                .SetField("fNDate", nDate)
+                .SetTimestampField("fNTimestamp", nDate)
+                .SetGuidField("fNGuid", nGuid)
+                .SetEnumField("fEnum", TestEnum.Two)
+                .SetStringArrayField("fStrArr", new[] { "str2" })
+                .SetArrayField("fDateArr", new[] { nDate })
+                .SetTimestampArrayField("fTimestampArr", new[] { nDate })
+                .SetGuidArrayField("fGuidArr", new[] { nGuid })
+                .SetEnumArrayField("fEnumArr", new[] { TestEnum.Two })
+                .SetHashCode(200)
+                .Build();
+
+            CheckStringDateGuidEnum2(binObj, nDate, nGuid);
+        }
+
+        /// <summary>
+        /// Checks the string date guid enum values.
+        /// </summary>
+        private void CheckStringDateGuidEnum1(IBinaryObject binObj, DateTime? nDate, Guid? nGuid)
+        {
             Assert.AreEqual(100, binObj.GetHashCode());
 
             IBinaryType meta = binObj.GetBinaryType();
 
             Assert.AreEqual(typeof(StringDateGuidEnum).Name, meta.TypeName);
 
-            Assert.AreEqual(8, meta.Fields.Count);
+            Assert.AreEqual(10, meta.Fields.Count);
 
             Assert.AreEqual(BinaryTypeNames.TypeNameString, meta.GetFieldTypeName("fStr"));
             Assert.AreEqual(BinaryTypeNames.TypeNameObject, meta.GetFieldTypeName("fNDate"));
+            Assert.AreEqual(BinaryTypeNames.TypeNameTimestamp, meta.GetFieldTypeName("fNTimestamp"));
             Assert.AreEqual(BinaryTypeNames.TypeNameGuid, meta.GetFieldTypeName("fNGuid"));
             Assert.AreEqual(BinaryTypeNames.TypeNameEnum, meta.GetFieldTypeName("fEnum"));
             Assert.AreEqual(BinaryTypeNames.TypeNameArrayString, meta.GetFieldTypeName("fStrArr"));
             Assert.AreEqual(BinaryTypeNames.TypeNameArrayObject, meta.GetFieldTypeName("fDateArr"));
+            Assert.AreEqual(BinaryTypeNames.TypeNameArrayTimestamp, meta.GetFieldTypeName("fTimestampArr"));
             Assert.AreEqual(BinaryTypeNames.TypeNameArrayGuid, meta.GetFieldTypeName("fGuidArr"));
             Assert.AreEqual(BinaryTypeNames.TypeNameArrayEnum, meta.GetFieldTypeName("fEnumArr"));
 
             Assert.AreEqual("str", binObj.GetField<string>("fStr"));
             Assert.AreEqual(nDate, binObj.GetField<DateTime?>("fNDate"));
+            Assert.AreEqual(nDate, binObj.GetField<DateTime?>("fNTimestamp"));
             Assert.AreEqual(nGuid, binObj.GetField<Guid?>("fNGuid"));
             Assert.AreEqual(TestEnum.One, binObj.GetField<TestEnum>("fEnum"));
-            Assert.AreEqual(new[] { "str" }, binObj.GetField<string[]>("fStrArr"));
-            Assert.AreEqual(new[] { nDate }, binObj.GetField<DateTime?[]>("fDateArr"));
-            Assert.AreEqual(new[] { nGuid }, binObj.GetField<Guid?[]>("fGuidArr"));
+            Assert.AreEqual(new[] {"str"}, binObj.GetField<string[]>("fStrArr"));
+            Assert.AreEqual(new[] {nDate}, binObj.GetField<DateTime?[]>("fDateArr"));
+            Assert.AreEqual(new[] {nDate}, binObj.GetField<DateTime?[]>("fTimestampArr"));
+            Assert.AreEqual(new[] {nGuid}, binObj.GetField<Guid?[]>("fGuidArr"));
             Assert.AreEqual(new[] {TestEnum.One}, binObj.GetField<TestEnum[]>("fEnumArr"));
 
             StringDateGuidEnum obj = binObj.Deserialize<StringDateGuidEnum>();
 
             Assert.AreEqual("str", obj.FStr);
             Assert.AreEqual(nDate, obj.FnDate);
+            Assert.AreEqual(nDate, obj.FnTimestamp);
             Assert.AreEqual(nGuid, obj.FnGuid);
             Assert.AreEqual(TestEnum.One, obj.FEnum);
-            Assert.AreEqual(new[] { "str" }, obj.FStrArr);
-            Assert.AreEqual(new[] { nDate }, obj.FDateArr);
-            Assert.AreEqual(new[] { nGuid }, obj.FGuidArr);
-            Assert.AreEqual(new[] { TestEnum.One }, obj.FEnumArr);
+            Assert.AreEqual(new[] {"str"}, obj.FStrArr);
+            Assert.AreEqual(new[] {nDate}, obj.FDateArr);
+            Assert.AreEqual(new[] {nGuid}, obj.FGuidArr);
+            Assert.AreEqual(new[] {TestEnum.One}, obj.FEnumArr);
 
             // Check builder field caching.
             var builder = _grid.GetBinary().GetBuilder(binObj);
 
             Assert.AreEqual("str", builder.GetField<string>("fStr"));
             Assert.AreEqual(nDate, builder.GetField<DateTime?>("fNDate"));
+            Assert.AreEqual(nDate, builder.GetField<DateTime?>("fNTimestamp"));
             Assert.AreEqual(nGuid, builder.GetField<Guid?>("fNGuid"));
             Assert.AreEqual(TestEnum.One, builder.GetField<TestEnum>("fEnum"));
-            Assert.AreEqual(new[] { "str" }, builder.GetField<string[]>("fStrArr"));
-            Assert.AreEqual(new[] { nDate }, builder.GetField<DateTime?[]>("fDateArr"));
-            Assert.AreEqual(new[] { nGuid }, builder.GetField<Guid?[]>("fGuidArr"));
+            Assert.AreEqual(new[] {"str"}, builder.GetField<string[]>("fStrArr"));
+            Assert.AreEqual(new[] {nDate}, builder.GetField<DateTime?[]>("fDateArr"));
+            Assert.AreEqual(new[] {nDate}, builder.GetField<DateTime?[]>("fTimestampArr"));
+            Assert.AreEqual(new[] {nGuid}, builder.GetField<Guid?[]>("fGuidArr"));
             Assert.AreEqual(new[] {TestEnum.One}, builder.GetField<TestEnum[]>("fEnumArr"));
 
             // Check reassemble.
@@ -847,55 +1029,51 @@ namespace Apache.Ignite.Core.Tests.Binary
 
             Assert.AreEqual("str", binObj.GetField<string>("fStr"));
             Assert.AreEqual(nDate, binObj.GetField<DateTime?>("fNDate"));
+            Assert.AreEqual(nDate, binObj.GetField<DateTime?>("fNTimestamp"));
             Assert.AreEqual(nGuid, binObj.GetField<Guid?>("fNGuid"));
             Assert.AreEqual(TestEnum.One, binObj.GetField<TestEnum>("fEnum"));
-            Assert.AreEqual(new[] { "str" }, binObj.GetField<string[]>("fStrArr"));
-            Assert.AreEqual(new[] { nDate }, binObj.GetField<DateTime?[]>("fDateArr"));
-            Assert.AreEqual(new[] { nGuid }, binObj.GetField<Guid?[]>("fGuidArr"));
+            Assert.AreEqual(new[] {"str"}, binObj.GetField<string[]>("fStrArr"));
+            Assert.AreEqual(new[] {nDate}, binObj.GetField<DateTime?[]>("fDateArr"));
+            Assert.AreEqual(new[] {nDate}, binObj.GetField<DateTime?[]>("fTimestampArr"));
+            Assert.AreEqual(new[] {nGuid}, binObj.GetField<Guid?[]>("fGuidArr"));
             Assert.AreEqual(new[] {TestEnum.One}, binObj.GetField<TestEnum[]>("fEnumArr"));
 
             obj = binObj.Deserialize<StringDateGuidEnum>();
 
             Assert.AreEqual("str", obj.FStr);
             Assert.AreEqual(nDate, obj.FnDate);
+            Assert.AreEqual(nDate, obj.FnTimestamp);
             Assert.AreEqual(nGuid, obj.FnGuid);
             Assert.AreEqual(TestEnum.One, obj.FEnum);
-            Assert.AreEqual(new[] { "str" }, obj.FStrArr);
-            Assert.AreEqual(new[] { nDate }, obj.FDateArr);
-            Assert.AreEqual(new[] { nGuid }, obj.FGuidArr);
-            Assert.AreEqual(new[] { TestEnum.One }, obj.FEnumArr);
-
-            // Overwrite.
-            nDate = DateTime.Now.ToUniversalTime();
-            nGuid = Guid.NewGuid();
-
-            binObj = builder
-                .SetField("fStr", "str2")
-                .SetTimestampField("fNDate", nDate)
-                .SetField("fNGuid", nGuid)
-                .SetField("fEnum", TestEnum.Two)
-                .SetField("fStrArr", new[] { "str2" })
-                .SetArrayField("fDateArr", new[] { nDate })
-                .SetField("fGuidArr", new[] { nGuid })
-                .SetField("fEnumArr", new[] { TestEnum.Two })
-                .SetHashCode(200)
-                .Build();
+            Assert.AreEqual(new[] {"str"}, obj.FStrArr);
+            Assert.AreEqual(new[] {nDate}, obj.FDateArr);
+            Assert.AreEqual(new[] {nGuid}, obj.FGuidArr);
+            Assert.AreEqual(new[] {TestEnum.One}, obj.FEnumArr);
+        }
 
+        /// <summary>
+        /// Checks the string date guid enum values.
+        /// </summary>
+        private static void CheckStringDateGuidEnum2(IBinaryObject binObj, DateTime? nDate, Guid? nGuid)
+        {
             Assert.AreEqual(200, binObj.GetHashCode());
 
             Assert.AreEqual("str2", binObj.GetField<string>("fStr"));
             Assert.AreEqual(nDate, binObj.GetField<DateTime?>("fNDate"));
+            Assert.AreEqual(nDate, binObj.GetField<DateTime?>("fNTimestamp"));
             Assert.AreEqual(nGuid, binObj.GetField<Guid?>("fNGuid"));
             Assert.AreEqual(TestEnum.Two, binObj.GetField<TestEnum>("fEnum"));
             Assert.AreEqual(new[] { "str2" }, binObj.GetField<string[]>("fStrArr"));
             Assert.AreEqual(new[] { nDate }, binObj.GetField<DateTime?[]>("fDateArr"));
+            Assert.AreEqual(new[] { nDate }, binObj.GetField<DateTime?[]>("fTimestampArr"));
             Assert.AreEqual(new[] { nGuid }, binObj.GetField<Guid?[]>("fGuidArr"));
-            Assert.AreEqual(new[] {TestEnum.Two}, binObj.GetField<TestEnum[]>("fEnumArr"));
+            Assert.AreEqual(new[] { TestEnum.Two }, binObj.GetField<TestEnum[]>("fEnumArr"));
 
-            obj = binObj.Deserialize<StringDateGuidEnum>();
+            var obj = binObj.Deserialize<StringDateGuidEnum>();
 
             Assert.AreEqual("str2", obj.FStr);
             Assert.AreEqual(nDate, obj.FnDate);
+            Assert.AreEqual(nDate, obj.FnTimestamp);
             Assert.AreEqual(nGuid, obj.FnGuid);
             Assert.AreEqual(TestEnum.Two, obj.FEnum);
             Assert.AreEqual(new[] { "str2" }, obj.FStrArr);
@@ -1524,6 +1702,7 @@ namespace Apache.Ignite.Core.Tests.Binary
         public long FLong;
         public float FFloat;
         public double FDouble;
+        public decimal FDecimal;
     }
 
     /// <summary>
@@ -1539,6 +1718,7 @@ namespace Apache.Ignite.Core.Tests.Binary
         public long[] FLong;
         public float[] FFloat;
         public double[] FDouble;
+        public decimal?[] FDecimal;
     }
 
     /// <summary>
@@ -1548,6 +1728,7 @@ namespace Apache.Ignite.Core.Tests.Binary
     {
         public string FStr;
         public DateTime? FnDate;
+        public DateTime? FnTimestamp;
         public Guid? FnGuid;
         public TestEnum FEnum;
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/332c8c2f/modules/platforms/dotnet/Apache.Ignite.Core/Binary/IBinaryObjectBuilder.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Binary/IBinaryObjectBuilder.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/IBinaryObjectBuilder.cs
index abb9149..740706a 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Binary/IBinaryObjectBuilder.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/IBinaryObjectBuilder.cs
@@ -62,7 +62,7 @@ namespace Apache.Ignite.Core.Binary
         /// <summary>
         /// Set explicit hash code. If builder creating object from scratch,
         /// then hash code initially set to 0. If builder is created from
-        /// exising binary object, then hash code of that object is used
+        /// existing binary object, then hash code of that object is used
         /// as initial value.
         /// </summary>
         /// <param name="hashCode">Hash code.</param>

http://git-wip-us.apache.org/repos/asf/ignite/blob/332c8c2f/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectBuilder.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectBuilder.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectBuilder.cs
index 083f557..646d563 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectBuilder.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectBuilder.cs
@@ -151,7 +151,7 @@ namespace Apache.Ignite.Core.Impl.Binary
         public IBinaryObjectBuilder SetBooleanField(string fieldName, bool val)
         {
             return SetField0(fieldName, new BinaryBuilderField(typeof (bool), val, BinaryUtils.TypeBool, 
-                (w, o) => w.WriteBoolean((bool) o)));
+                (w, o) => w.WriteBooleanField((bool) o)));
         }
  
         /** <inheritDoc /> */
@@ -165,7 +165,7 @@ namespace Apache.Ignite.Core.Impl.Binary
         public IBinaryObjectBuilder SetByteField(string fieldName, byte val)
         {
             return SetField0(fieldName, new BinaryBuilderField(typeof (byte), val, BinaryUtils.TypeByte,
-                (w, o) => w.WriteByte((byte) o)));
+                (w, o) => w.WriteByteField((byte) o)));
         }
  
         /** <inheritDoc /> */
@@ -179,7 +179,7 @@ namespace Apache.Ignite.Core.Impl.Binary
         public IBinaryObjectBuilder SetCharField(string fieldName, char val)
         {
             return SetField0(fieldName, new BinaryBuilderField(typeof (char), val, BinaryUtils.TypeChar,
-                (w, o) => w.WriteChar((char) o)));
+                (w, o) => w.WriteCharField((char) o)));
         }
  
         /** <inheritDoc /> */
@@ -221,7 +221,7 @@ namespace Apache.Ignite.Core.Impl.Binary
         public IBinaryObjectBuilder SetDoubleField(string fieldName, double val)
         {
             return SetField0(fieldName, new BinaryBuilderField(typeof (double), val, BinaryUtils.TypeDouble,
-                (w, o) => w.WriteDouble((double) o)));
+                (w, o) => w.WriteDoubleField((double) o)));
         }
  
         /** <inheritDoc /> */
@@ -249,7 +249,7 @@ namespace Apache.Ignite.Core.Impl.Binary
         public IBinaryObjectBuilder SetFloatField(string fieldName, float val)
         {
             return SetField0(fieldName, new BinaryBuilderField(typeof (float), val, BinaryUtils.TypeFloat,
-                (w, o) => w.WriteFloat((float) o)));
+                (w, o) => w.WriteFloatField((float) o)));
         }
  
         /** <inheritDoc /> */
@@ -277,7 +277,7 @@ namespace Apache.Ignite.Core.Impl.Binary
         public IBinaryObjectBuilder SetIntField(string fieldName, int val)
         {
             return SetField0(fieldName, new BinaryBuilderField(typeof (int), val, BinaryUtils.TypeInt,
-                (w, o) => w.WriteInt((int) o)));
+                (w, o) => w.WriteIntField((int) o)));
         }
  
         /** <inheritDoc /> */
@@ -291,7 +291,7 @@ namespace Apache.Ignite.Core.Impl.Binary
         public IBinaryObjectBuilder SetLongField(string fieldName, long val)
         {
             return SetField0(fieldName, new BinaryBuilderField(typeof (long), val, BinaryUtils.TypeLong,
-                (w, o) => w.WriteLong((long) o)));
+                (w, o) => w.WriteLongField((long) o)));
         }
  
         /** <inheritDoc /> */
@@ -305,7 +305,7 @@ namespace Apache.Ignite.Core.Impl.Binary
         public IBinaryObjectBuilder SetShortField(string fieldName, short val)
         {
             return SetField0(fieldName, new BinaryBuilderField(typeof (short), val, BinaryUtils.TypeShort,
-                (w, o) => w.WriteShort((short) o)));
+                (w, o) => w.WriteShortField((short) o)));
         }
  
         /** <inheritDoc /> */

http://git-wip-us.apache.org/repos/asf/ignite/blob/332c8c2f/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs
index 1ac98c4..a0b50ac 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs
@@ -26,6 +26,7 @@ namespace Apache.Ignite.Core.Impl.Binary
     using Apache.Ignite.Core.Impl.Binary.IO;
     using Apache.Ignite.Core.Impl.Binary.Metadata;
     using Apache.Ignite.Core.Impl.Binary.Structure;
+    using Apache.Ignite.Core.Impl.Common;
 
     /// <summary>
     /// Binary writer implementation.
@@ -87,11 +88,19 @@ namespace Apache.Ignite.Core.Impl.Binary
         public void WriteBoolean(string fieldName, bool val)
         {
             WriteFieldId(fieldName, BinaryUtils.TypeBool);
+            WriteBooleanField(val);
+        }
 
+        /// <summary>
+        /// Writes the boolean field.
+        /// </summary>
+        /// <param name="val">if set to <c>true</c> [value].</param>
+        internal void WriteBooleanField(bool val)
+        {
             _stream.WriteByte(BinaryUtils.TypeBool);
             _stream.WriteBool(val);
         }
-        
+
         /// <summary>
         /// Write boolean value.
         /// </summary>
@@ -142,7 +151,15 @@ namespace Apache.Ignite.Core.Impl.Binary
         public void WriteByte(string fieldName, byte val)
         {
             WriteFieldId(fieldName, BinaryUtils.TypeBool);
+            WriteByteField(val);
+        }
 
+        /// <summary>
+        /// Write byte field value.
+        /// </summary>
+        /// <param name="val">Byte value.</param>
+        internal void WriteByteField(byte val)
+        {
             _stream.WriteByte(BinaryUtils.TypeByte);
             _stream.WriteByte(val);
         }
@@ -197,7 +214,15 @@ namespace Apache.Ignite.Core.Impl.Binary
         public void WriteShort(string fieldName, short val)
         {
             WriteFieldId(fieldName, BinaryUtils.TypeShort);
+            WriteShortField(val);
+        }
 
+        /// <summary>
+        /// Write short field value.
+        /// </summary>
+        /// <param name="val">Short value.</param>
+        internal void WriteShortField(short val)
+        {
             _stream.WriteByte(BinaryUtils.TypeShort);
             _stream.WriteShort(val);
         }
@@ -252,7 +277,15 @@ namespace Apache.Ignite.Core.Impl.Binary
         public void WriteChar(string fieldName, char val)
         {
             WriteFieldId(fieldName, BinaryUtils.TypeChar);
+            WriteCharField(val);
+        }
 
+        /// <summary>
+        /// Write char field value.
+        /// </summary>
+        /// <param name="val">Char value.</param>
+        internal void WriteCharField(char val)
+        {
             _stream.WriteByte(BinaryUtils.TypeChar);
             _stream.WriteChar(val);
         }
@@ -307,7 +340,15 @@ namespace Apache.Ignite.Core.Impl.Binary
         public void WriteInt(string fieldName, int val)
         {
             WriteFieldId(fieldName, BinaryUtils.TypeInt);
+            WriteIntField(val);
+        }
 
+        /// <summary>
+        /// Writes the int field.
+        /// </summary>
+        /// <param name="val">The value.</param>
+        internal void WriteIntField(int val)
+        {
             _stream.WriteByte(BinaryUtils.TypeInt);
             _stream.WriteInt(val);
         }
@@ -362,7 +403,15 @@ namespace Apache.Ignite.Core.Impl.Binary
         public void WriteLong(string fieldName, long val)
         {
             WriteFieldId(fieldName, BinaryUtils.TypeLong);
+            WriteLongField(val);
+        }
 
+        /// <summary>
+        /// Writes the long field.
+        /// </summary>
+        /// <param name="val">The value.</param>
+        internal void WriteLongField(long val)
+        {
             _stream.WriteByte(BinaryUtils.TypeLong);
             _stream.WriteLong(val);
         }
@@ -417,7 +466,15 @@ namespace Apache.Ignite.Core.Impl.Binary
         public void WriteFloat(string fieldName, float val)
         {
             WriteFieldId(fieldName, BinaryUtils.TypeFloat);
+            WriteFloatField(val);
+        }
 
+        /// <summary>
+        /// Writes the float field.
+        /// </summary>
+        /// <param name="val">The value.</param>
+        internal void WriteFloatField(float val)
+        {
             _stream.WriteByte(BinaryUtils.TypeFloat);
             _stream.WriteFloat(val);
         }
@@ -472,7 +529,15 @@ namespace Apache.Ignite.Core.Impl.Binary
         public void WriteDouble(string fieldName, double val)
         {
             WriteFieldId(fieldName, BinaryUtils.TypeDouble);
+            WriteDoubleField(val);
+        }
 
+        /// <summary>
+        /// Writes the double field.
+        /// </summary>
+        /// <param name="val">The value.</param>
+        internal void WriteDoubleField(double val)
+        {
             _stream.WriteByte(BinaryUtils.TypeDouble);
             _stream.WriteDouble(val);
         }
@@ -1215,72 +1280,40 @@ namespace Apache.Ignite.Core.Impl.Binary
             // Types check sequence is designed to minimize comparisons for the most frequent types.
 
             if (type == typeof(int))
-            {
-                _stream.WriteByte(BinaryUtils.TypeInt);
-                _stream.WriteInt((int)(object)val);
-            }
+                WriteIntField(TypeCaster<int>.Cast(val));
             else if (type == typeof(long))
-            {
-                _stream.WriteByte(BinaryUtils.TypeLong);
-                _stream.WriteLong((long)(object)val);
-            }
+                WriteLongField(TypeCaster<long>.Cast(val));
             else if (type == typeof(bool))
-            {
-                _stream.WriteByte(BinaryUtils.TypeBool);
-                _stream.WriteBool((bool)(object)val);
-            }
+                WriteBooleanField(TypeCaster<bool>.Cast(val));
             else if (type == typeof(byte))
-            {
-                _stream.WriteByte(BinaryUtils.TypeByte);
-                _stream.WriteByte((byte)(object)val);
-            }
+                WriteByteField(TypeCaster<byte>.Cast(val));
             else if (type == typeof(short))
-            {
-                _stream.WriteByte(BinaryUtils.TypeShort);
-                _stream.WriteShort((short)(object)val);
-            }
-            else if (type == typeof (char))
-            {
-                _stream.WriteByte(BinaryUtils.TypeChar);
-                _stream.WriteChar((char)(object)val);
-            }
+                WriteShortField(TypeCaster<short>.Cast(val));
+            else if (type == typeof(char))
+                WriteCharField(TypeCaster<char>.Cast(val));
             else if (type == typeof(float))
-            {
-                _stream.WriteByte(BinaryUtils.TypeFloat);
-                _stream.WriteFloat((float)(object)val);
-            }
+                WriteFloatField(TypeCaster<float>.Cast(val));
             else if (type == typeof(double))
-            {
-                _stream.WriteByte(BinaryUtils.TypeDouble);
-                _stream.WriteDouble((double)(object)val);
-            }
+                WriteDoubleField(TypeCaster<double>.Cast(val));
             else if (type == typeof(sbyte))
             {
-                sbyte val0 = (sbyte)(object)val;
-
-                _stream.WriteByte(BinaryUtils.TypeByte);
-                _stream.WriteByte(*(byte*)&val0);
+                var val0 = TypeCaster<sbyte>.Cast(val);
+                WriteByteField(*(byte*)&val0);
             }
             else if (type == typeof(ushort))
             {
-                ushort val0 = (ushort)(object)val;
-
-                _stream.WriteByte(BinaryUtils.TypeShort);
-                _stream.WriteShort(*(short*)&val0);
+                var val0 = TypeCaster<ushort>.Cast(val);
+                WriteShortField(*(short*) &val0);
             }
             else if (type == typeof(uint))
             {
-                uint val0 = (uint)(object)val;
-
-                _stream.WriteByte(BinaryUtils.TypeInt);
-                _stream.WriteInt(*(int*)&val0);
+                var val0 = TypeCaster<uint>.Cast(val);
+                WriteIntField(*(int*)&val0);
             }
             else if (type == typeof(ulong))
             {
-                ulong val0 = (ulong)(object)val;
-
-                _stream.WriteByte(BinaryUtils.TypeLong);
-                _stream.WriteLong(*(long*)&val0);
+                var val0 = TypeCaster<ulong>.Cast(val);
+                WriteLongField(*(long*)&val0);
             }
             else
                 throw new BinaryObjectException("Unsupported object type [type=" + type.FullName + ", object=" + val + ']');


[06/50] [abbrv] ignite git commit: ignite-3300 Fixed potential race.

Posted by vo...@apache.org.
ignite-3300 Fixed potential race.


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

Branch: refs/heads/ignite-3553
Commit: eddd0a981ead389bed5608444a811b1c90f4098f
Parents: d88f778
Author: sboikov <sb...@gridgain.com>
Authored: Fri Jul 22 16:43:59 2016 +0300
Committer: sboikov <sb...@gridgain.com>
Committed: Fri Jul 22 16:43:59 2016 +0300

----------------------------------------------------------------------
 .../internal/processors/cache/GridCacheAffinityManager.java  | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/eddd0a98/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAffinityManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAffinityManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAffinityManager.java
index d182bb4..8633333 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAffinityManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAffinityManager.java
@@ -197,18 +197,18 @@ public class GridCacheAffinityManager extends GridCacheManagerAdapter {
     public int partition(Object key) {
         GridAffinityAssignmentCache aff0 = aff;
 
+        if (aff0 == null)
+            throw new IgniteException(FAILED_TO_FIND_CACHE_ERR_MSG + cctx.name());
+
         boolean keyObj = key instanceof KeyCacheObject;
 
-        if (key instanceof KeyCacheObject) {
+        if (keyObj) {
             int part = ((KeyCacheObject)key).partition();
 
             if (part != -1)
                 return part;
         }
 
-        if (aff0 == null)
-            throw new IgniteException(FAILED_TO_FIND_CACHE_ERR_MSG + cctx.name());
-
         int p = affFunction.partition(affinityKey(key));
 
         if (keyObj)


[04/50] [abbrv] ignite git commit: Merge remote-tracking branch 'remotes/community/ignite-1.6.3'

Posted by vo...@apache.org.
Merge remote-tracking branch 'remotes/community/ignite-1.6.3'


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

Branch: refs/heads/ignite-3553
Commit: 2007cf859c6f761d9258f2cb10ef1f695e67d9da
Parents: ab4963a ce53491
Author: Pavel Tupitsyn <pt...@apache.org>
Authored: Fri Jul 22 15:44:09 2016 +0300
Committer: Pavel Tupitsyn <pt...@apache.org>
Committed: Fri Jul 22 15:44:09 2016 +0300

----------------------------------------------------------------------
 .../Binary/BinaryBuilderSelfTest.cs             | 57 +++++++++++++++++---
 .../Binary/BinarySelfTest.cs                    |  3 +-
 .../Impl/Binary/BinaryReader.cs                 | 18 +++----
 .../Impl/Binary/Marshaller.cs                   | 44 ++++++++++-----
 .../Binary/Structure/BinaryStructureTracker.cs  |  7 +--
 5 files changed, 93 insertions(+), 36 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/2007cf85/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinarySelfTest.cs
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/ignite/blob/2007cf85/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReader.cs
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/ignite/blob/2007cf85/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs
----------------------------------------------------------------------


[50/50] [abbrv] ignite git commit: Merge branch 'ignite-1.7' into ignite-3553

Posted by vo...@apache.org.
Merge branch 'ignite-1.7' into ignite-3553


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

Branch: refs/heads/ignite-3553
Commit: 7d05b6500c2310a54d589429911a2c1756746a12
Parents: b77bee0 8386dd8
Author: vozerov-gridgain <vo...@gridgain.com>
Authored: Thu Jul 28 14:06:00 2016 +0300
Committer: vozerov-gridgain <vo...@gridgain.com>
Committed: Thu Jul 28 14:06:00 2016 +0300

----------------------------------------------------------------------
 .gitignore                                      |    1 +
 RELEASE_NOTES.txt                               |   17 +
 assembly/libs/README.txt                        |   39 +-
 .../examples/datagrid/CacheQueryExample.java    |   78 +-
 .../jdbc2/JdbcDistributedJoinsQueryTest.java    |  319 ++++
 .../JettyRestProcessorAbstractSelfTest.java     |  178 +-
 .../jdbc/suite/IgniteJdbcDriverTestSuite.java   |    2 +
 .../ignite/codegen/MessageCodeGenerator.java    |    6 +
 modules/core/pom.xml                            |    6 +
 .../src/main/java/org/apache/ignite/Ignite.java |    4 +-
 .../org/apache/ignite/IgniteFileSystem.java     |    9 +
 .../org/apache/ignite/IgniteJdbcDriver.java     |   14 +-
 .../org/apache/ignite/cache/QueryEntity.java    |   18 +
 .../ignite/cache/query/SqlFieldsQuery.java      |   53 +
 .../org/apache/ignite/cache/query/SqlQuery.java |   28 +
 .../store/jdbc/CacheAbstractJdbcStore.java      |  101 +-
 .../cache/store/jdbc/CacheJdbcPojoStore.java    |    7 +-
 .../store/jdbc/CacheJdbcPojoStoreFactory.java   |   26 +
 .../store/jdbc/JdbcTypesDefaultTransformer.java |  117 ++
 .../cache/store/jdbc/JdbcTypesTransformer.java  |   39 +
 .../configuration/IgniteConfiguration.java      |   42 +-
 .../internal/cluster/ClusterGroupAdapter.java   |   18 +-
 .../ignite/internal/jdbc2/JdbcConnection.java   |   12 +
 .../ignite/internal/jdbc2/JdbcQueryTask.java    |    9 +-
 .../ignite/internal/jdbc2/JdbcResultSet.java    |    4 +-
 .../ignite/internal/jdbc2/JdbcStatement.java    |    2 +-
 .../managers/communication/GridIoManager.java   |   34 +-
 .../communication/GridIoMessageFactory.java     |    2 +-
 .../managers/communication/GridIoPolicy.java    |    5 +-
 .../GridAffinityFunctionContextImpl.java        |    2 +-
 .../cache/GridCacheAffinityManager.java         |    2 +-
 .../processors/cache/GridCacheContext.java      |   19 +
 .../GridCacheDefaultAffinityKeyMapper.java      |   19 +
 .../processors/cache/GridCacheIoManager.java    |   21 +-
 .../processors/cache/IgniteCacheProxy.java      |    8 +
 .../binary/CacheObjectBinaryProcessorImpl.java  |    8 +
 .../distributed/GridCacheTxRecoveryFuture.java  |    7 +
 .../distributed/GridCacheTxRecoveryRequest.java |    1 -
 .../GridCacheTxRecoveryResponse.java            |   23 +-
 .../GridDistributedTxFinishRequest.java         |   19 +-
 .../GridDistributedTxFinishResponse.java        |    1 -
 .../GridDistributedTxPrepareRequest.java        |   26 +-
 .../GridDistributedTxPrepareResponse.java       |   20 +-
 .../dht/GridCachePartitionedConcurrentMap.java  |  142 +-
 .../dht/GridDhtPartitionTopology.java           |    2 +-
 .../dht/GridDhtPartitionTopologyImpl.java       |  176 +-
 .../dht/GridDhtPartitionsReservation.java       |    3 +-
 .../near/GridNearTxPrepareFutureAdapter.java    |    7 +
 .../cache/query/GridCacheQueryManager.java      |  110 +-
 .../cache/query/GridCacheQueryMarshallable.java |   37 +
 .../cache/query/GridCacheSqlQuery.java          |   33 +-
 .../cache/query/GridCacheTwoStepQuery.java      |  123 +-
 .../cache/transactions/IgniteTxHandler.java     |   55 +-
 .../IgniteTxImplicitSingleStateImpl.java        |   13 +
 .../IgniteTxRemoteSingleStateImpl.java          |   13 +
 .../transactions/IgniteTxRemoteStateImpl.java   |   38 +-
 .../cache/transactions/IgniteTxState.java       |    6 +
 .../cache/transactions/IgniteTxStateAware.java  |   34 +
 .../cache/transactions/IgniteTxStateImpl.java   |   12 +
 .../cacheobject/IgniteCacheObjectProcessor.java |    6 +
 .../IgniteCacheObjectProcessorImpl.java         |    5 +
 .../processors/closure/GridClosurePolicy.java   |   51 -
 .../closure/GridClosureProcessor.java           |   52 +-
 .../datastructures/GridCacheSemaphoreImpl.java  |  108 +-
 .../internal/processors/igfs/IgfsAsyncImpl.java |    6 +
 .../internal/processors/igfs/IgfsImpl.java      |    7 +
 .../platform/PlatformAbstractBootstrap.java     |    5 +
 .../processors/platform/PlatformBootstrap.java  |    6 +
 .../processors/platform/PlatformIgnition.java   |    7 +-
 .../platform/PlatformProcessorImpl.java         |   35 +-
 .../platform/cache/PlatformCache.java           |   12 +-
 .../affinity/PlatformAffinityFunction.java      |   27 +
 .../cache/affinity/PlatformAffinityUtils.java   |    2 +
 .../callback/PlatformCallbackGateway.java       |   13 +-
 .../callback/PlatformCallbackUtils.java         |    8 +
 .../platform/cluster/PlatformClusterGroup.java  |    7 +
 .../platform/compute/PlatformAbstractTask.java  |    1 +
 .../dotnet/PlatformDotNetBootstrap.java         |   12 +
 .../dotnet/PlatformDotNetCacheStore.java        |   27 +-
 .../PlatformDotNetConfigurationClosure.java     |   58 +-
 .../dotnet/PlatformDotNetConsoleStream.java     |   54 +
 .../services/PlatformAbstractService.java       |    3 +-
 .../utils/PlatformConfigurationUtils.java       |  106 +-
 .../platform/utils/PlatformFutureUtils.java     |    2 +
 .../platform/utils/PlatformUtils.java           |   12 +
 .../processors/query/GridQueryIndexing.java     |   51 +-
 .../processors/query/GridQueryProcessor.java    |  194 +--
 .../query/GridQueryTypeDescriptor.java          |    7 +
 .../messages/GridQueryCancelRequest.java        |    2 +-
 .../twostep/messages/GridQueryFailResponse.java |    2 +-
 .../messages/GridQueryNextPageRequest.java      |    2 +-
 .../messages/GridQueryNextPageResponse.java     |   12 +-
 .../h2/twostep/messages/GridQueryRequest.java   |   28 +-
 .../handlers/cache/GridCacheCommandHandler.java |  100 +-
 .../handlers/query/QueryCommandHandler.java     |    4 +
 .../rest/request/RestQueryRequest.java          |   17 +
 .../ignite/internal/util/IgniteUtils.java       |   47 +-
 .../internal/util/PartitionedReadOnlySet.java   |   71 -
 .../ignite/internal/util/lang/GridFunc.java     |   20 +
 .../offheap/unsafe/GridOffHeapSnapTreeMap.java  |   91 +-
 .../internal/visor/query/VisorQueryArgV2.java   |   49 +
 .../internal/visor/query/VisorQueryJob.java     |    1 +
 .../dotnet/PlatformDotNetAffinityFunction.java  |   15 +
 .../SpringApplicationContextResource.java       |    4 +-
 .../apache/ignite/resources/SpringResource.java |   15 +-
 .../ignite/scheduler/SchedulerFuture.java       |    5 +-
 .../java/org/jetbrains/annotations/NotNull.java |   38 -
 .../org/jetbrains/annotations/Nullable.java     |   33 -
 .../org/jetbrains/annotations/package-info.java |   27 -
 .../ignite/internal/ClusterGroupSelfTest.java   |   30 +-
 .../cache/GridCacheAbstractFullApiSelfTest.java |  127 +-
 .../IgniteTxExceptionAbstractSelfTest.java      |   45 +-
 .../IgniteSemaphoreAbstractSelfTest.java        |   25 +-
 .../dht/IgniteCacheConcurrentPutGetRemove.java  |  201 +++
 .../IgniteCacheSyncRebalanceModeSelfTest.java   |    2 +
 .../internal/processors/igfs/IgfsMock.java      |    8 +
 .../processors/igfs/IgfsStreamsSelfTest.java    |    5 +-
 ...niteMessagingConfigVariationFullApiTest.java |  484 ++++++
 .../GridCacheAtomicCommandHandlerSelfTest.java  |   39 +
 .../cache/GridCacheCommandHandlerSelfTest.java  |   20 +-
 .../ignite/platform/PlatformStringTestTask.java |   67 +
 .../jdbc/TcpDiscoveryJdbcIpFinderSelfTest.java  |    2 +-
 .../junits/common/GridCommonAbstractTest.java   |   39 +-
 ...essagingConfigVariationFullApiTestSuite.java |   72 +
 .../testsuites/IgniteRestHandlerTestSuite.java  |    2 +
 modules/docker/1.6.0/Dockerfile                 |   44 +
 modules/docker/1.6.0/run.sh                     |   51 +
 modules/docker/Dockerfile                       |   24 +-
 .../query/h2/opt/GridH2SpatialIndex.java        |   74 +-
 .../collections/HadoopHashMapSelfTest.java      |   68 +-
 .../processors/query/h2/IgniteH2Indexing.java   |  726 ++++++--
 .../query/h2/opt/GridH2AbstractKeyValueRow.java |   72 +-
 .../query/h2/opt/GridH2CollocationModel.java    |  783 +++++++++
 .../processors/query/h2/opt/GridH2Cursor.java   |   36 +-
 .../query/h2/opt/GridH2DefaultTableEngine.java  |   38 +
 .../query/h2/opt/GridH2IndexBase.java           | 1392 ++++++++++++++-
 .../query/h2/opt/GridH2MetaTable.java           |  383 ++++
 .../query/h2/opt/GridH2QueryContext.java        |  612 +++++++
 .../query/h2/opt/GridH2QueryType.java           |   49 +
 .../query/h2/opt/GridH2RetryException.java      |   32 +
 .../processors/query/h2/opt/GridH2Row.java      |   86 +-
 .../query/h2/opt/GridH2RowDescriptor.java       |   28 +-
 .../query/h2/opt/GridH2RowFactory.java          |  179 ++
 .../processors/query/h2/opt/GridH2Table.java    |  372 ++--
 .../query/h2/opt/GridH2TreeIndex.java           |  142 +-
 .../processors/query/h2/opt/GridH2Utils.java    |  133 --
 .../query/h2/opt/GridH2ValueCacheObject.java    |    3 +-
 .../query/h2/opt/GridLuceneIndex.java           |    7 +-
 .../processors/query/h2/sql/GridSqlAlias.java   |   12 +
 .../processors/query/h2/sql/GridSqlColumn.java  |   22 +-
 .../processors/query/h2/sql/GridSqlElement.java |   11 +
 .../query/h2/sql/GridSqlOperation.java          |    2 +-
 .../query/h2/sql/GridSqlOperationType.java      |    8 +-
 .../query/h2/sql/GridSqlQueryParser.java        |   97 +-
 .../query/h2/sql/GridSqlQuerySplitter.java      |  293 +++-
 .../processors/query/h2/sql/GridSqlSelect.java  |    9 +-
 .../processors/query/h2/sql/GridSqlTable.java   |   70 +
 .../query/h2/twostep/GridMapQueryExecutor.java  |  415 +++--
 .../query/h2/twostep/GridMergeIndex.java        |   75 +-
 .../h2/twostep/GridMergeIndexUnsorted.java      |    6 +-
 .../query/h2/twostep/GridMergeTable.java        |    4 +-
 .../h2/twostep/GridReduceQueryExecutor.java     |  338 ++--
 .../query/h2/twostep/GridThreadLocalTable.java  |   68 +-
 .../query/h2/twostep/msg/GridH2Array.java       |    9 +-
 .../query/h2/twostep/msg/GridH2Boolean.java     |   10 +-
 .../query/h2/twostep/msg/GridH2Byte.java        |    9 +-
 .../query/h2/twostep/msg/GridH2Bytes.java       |   11 +-
 .../query/h2/twostep/msg/GridH2CacheObject.java |    9 +-
 .../query/h2/twostep/msg/GridH2Date.java        |    9 +-
 .../query/h2/twostep/msg/GridH2Decimal.java     |   11 +-
 .../query/h2/twostep/msg/GridH2Double.java      |    9 +-
 .../query/h2/twostep/msg/GridH2Float.java       |    9 +-
 .../query/h2/twostep/msg/GridH2Geometry.java    |   11 +-
 .../h2/twostep/msg/GridH2IndexRangeRequest.java |  208 +++
 .../twostep/msg/GridH2IndexRangeResponse.java   |  279 +++
 .../query/h2/twostep/msg/GridH2Integer.java     |   20 +-
 .../query/h2/twostep/msg/GridH2JavaObject.java  |   11 +-
 .../query/h2/twostep/msg/GridH2Long.java        |    9 +-
 .../query/h2/twostep/msg/GridH2Null.java        |   15 +-
 .../h2/twostep/msg/GridH2QueryRequest.java      |  401 +++++
 .../query/h2/twostep/msg/GridH2RowMessage.java  |  116 ++
 .../query/h2/twostep/msg/GridH2RowRange.java    |  181 ++
 .../h2/twostep/msg/GridH2RowRangeBounds.java    |  188 ++
 .../query/h2/twostep/msg/GridH2Short.java       |    9 +-
 .../query/h2/twostep/msg/GridH2String.java      |    9 +-
 .../query/h2/twostep/msg/GridH2Time.java        |    9 +-
 .../query/h2/twostep/msg/GridH2Timestamp.java   |   11 +-
 .../query/h2/twostep/msg/GridH2Uuid.java        |    9 +-
 .../h2/twostep/msg/GridH2ValueMessage.java      |    2 +-
 .../twostep/msg/GridH2ValueMessageFactory.java  |   22 +-
 .../cache/CacheIndexingOffheapCleanupTest.java  |  178 ++
 ...idCacheReduceQueryMultithreadedSelfTest.java |  168 --
 .../cache/IgniteCacheAbstractQuerySelfTest.java |   30 +-
 .../IgniteCacheCrossCacheJoinRandomTest.java    |  442 +++++
 ...acheDistributedJoinCollocatedAndNotTest.java |  365 ++++
 ...acheDistributedJoinCustomAffinityMapper.java |  262 +++
 .../IgniteCacheDistributedJoinNoIndexTest.java  |  299 ++++
 ...ributedJoinPartitionedAndReplicatedTest.java |  487 ++++++
 ...CacheDistributedJoinQueryConditionsTest.java |  624 +++++++
 .../cache/IgniteCacheDistributedJoinTest.java   |  316 ++++
 ...PartitionedAndReplicatedCollocationTest.java |  399 +++++
 ...teCacheJoinPartitionedAndReplicatedTest.java |  316 ++++
 ...IgniteCacheJoinQueryWithAffinityKeyTest.java |  646 +++++++
 ...IgniteCachePrimitiveFieldsQuerySelfTest.java |  134 ++
 .../cache/IgniteCacheQueryLoadSelfTest.java     |   12 +-
 .../cache/IgniteCacheUnionDuplicatesTest.java   |  151 ++
 .../cache/IgniteCrossCachesJoinsQueryTest.java  | 1641 ++++++++++++++++++
 ...QueryNodeRestartDistributedJoinSelfTest.java |  476 +++++
 ...dCacheAbstractReduceFieldsQuerySelfTest.java |  420 -----
 ...ridCacheReduceFieldsQueryAtomicSelfTest.java |   38 -
 ...GridCacheReduceFieldsQueryLocalSelfTest.java |   37 -
 ...cheReduceFieldsQueryPartitionedSelfTest.java |   59 -
 ...acheReduceFieldsQueryReplicatedSelfTest.java |   37 -
 .../query/IgniteSqlSchemaIndexingTest.java      |    5 +-
 .../query/IgniteSqlSplitterSelfTest.java        |  833 ++++++++-
 .../h2/GridIndexingSpiAbstractSelfTest.java     |  145 +-
 .../query/h2/opt/GridH2TableSelfTest.java       |   10 +-
 .../h2/sql/AbstractH2CompareQueryTest.java      |  165 +-
 .../query/h2/sql/GridQueryParsingTest.java      |   28 +-
 .../H2CompareBigQueryDistributedJoinsTest.java  |   28 +
 .../query/h2/sql/H2CompareBigQueryTest.java     |  119 +-
 .../IgniteCacheQuerySelfTestSuite.java          |   27 +-
 .../IgniteCacheQuerySelfTestSuite2.java         |   10 -
 .../IgniteCacheWithIndexingTestSuite.java       |    2 +
 .../ignite/stream/kafka/KafkaStreamer.java      |    4 +-
 .../connect/IgniteSourceConnectorTest.java      |   58 +-
 .../cpp/common/include/ignite/common/utils.h    |   18 +
 modules/platforms/cpp/core-test/Makefile.am     |    1 +
 .../cpp/core-test/config/cache-query.xml        |   42 +-
 .../cpp/core-test/project/vs/core-test.vcxproj  |    3 +-
 .../project/vs/core-test.vcxproj.filters        |    5 +-
 .../cpp/core-test/src/cache_query_test.cpp      |  443 ++++-
 .../cpp/core-test/src/interop_test.cpp          |  148 ++
 .../core/include/ignite/cache/query/query_sql.h |   61 +-
 .../ignite/cache/query/query_sql_fields.h       |  111 +-
 modules/platforms/cpp/examples/README.txt       |    1 +
 .../cpp/jni/include/ignite/jni/exports.h        |    4 +
 .../platforms/cpp/jni/include/ignite/jni/java.h |   17 +-
 modules/platforms/cpp/jni/project/vs/module.def |    4 +
 modules/platforms/cpp/jni/src/exports.cpp       |   12 +
 modules/platforms/cpp/jni/src/java.cpp          |  120 +-
 .../cpp/odbc-test/config/queries-test.xml       |    9 +-
 .../cpp/odbc-test/src/configuration_test.cpp    |  110 +-
 .../cpp/odbc-test/src/queries_test.cpp          |   91 +-
 .../include/ignite/odbc/config/configuration.h  |  121 +-
 .../cpp/odbc/include/ignite/odbc/connection.h   |   38 +-
 .../cpp/odbc/src/config/configuration.cpp       |  227 ++-
 modules/platforms/cpp/odbc/src/connection.cpp   |   47 +-
 modules/platforms/cpp/odbc/src/odbc.cpp         |   27 +-
 .../Apache.Ignite.AspNet.csproj                 |   73 +
 .../Apache.Ignite.AspNet.nuspec                 |   57 +
 .../Apache.Ignite.AspNet.ruleset                |    9 +
 .../Apache.Ignite.AspNet.snk                    |  Bin 0 -> 596 bytes
 .../IgniteOutputCacheProvider.cs                |  220 +++
 .../Apache.Ignite.AspNet/IgniteWebUtils.cs      |   44 +
 .../dotnet/Apache.Ignite.AspNet/Package-Info.cs |   26 +
 .../Properties/AssemblyInfo.cs                  |   39 +
 .../Properties/AssemblyInfo.cs                  |    2 +-
 .../Apache.Ignite.Core.Tests.NuGet.csproj       |   14 +
 .../AspNetTest.cs                               |   73 +
 .../Properties/AssemblyInfo.cs                  |    2 +-
 .../SchemaTest.cs                               |   62 +
 .../install-package.ps1                         |   21 +-
 .../packages.config                             |    2 +
 .../Properties/AssemblyInfo.cs                  |    2 +-
 .../Apache.Ignite.Core.Tests.csproj             |   18 +-
 .../AspNet/IgniteOutputCacheProviderTest.cs     |  172 ++
 .../Binary/BinarySelfTest.cs                    |  164 +-
 .../Binary/BinaryStringTest.cs                  |  120 ++
 .../Cache/Affinity/AffinityFieldTest.cs         |  199 +++
 .../Affinity/AffinityFunctionSpringTest.cs      |    2 +-
 .../Cache/Affinity/AffinityFunctionTest.cs      |  485 ++++++
 .../Cache/Affinity/AffinityTest.cs              |  138 ++
 .../Cache/CacheAffinityFieldTest.cs             |  199 ---
 .../Cache/CacheAffinityTest.cs                  |  139 --
 .../Cache/CacheConfigurationTest.cs             |  134 +-
 .../Cache/Query/CacheLinqTest.cs                |   84 +-
 .../Cache/Query/CacheQueriesTest.cs             |  257 +--
 .../Continuous/ContinuousQueryAbstractTest.cs   |   18 +-
 .../Cache/Store/CacheStoreTest.cs               |   11 +-
 .../Compute/AbstractTaskTest.cs                 |    9 +
 .../Compute/BinarizableTaskTest.cs              |   13 +-
 .../Compute/ComputeApiTest.cs                   |  110 +-
 .../Compute/FailoverTaskSelfTest.cs             |    2 +-
 .../Compute/IgniteExceptionTaskSelfTest.cs      |   18 +-
 .../Compute/ResourceTaskTest.cs                 |    8 +-
 .../Compute/TaskAdapterTest.cs                  |   16 +-
 .../Compute/TaskResultTest.cs                   |   10 +-
 .../Config/Cache/Affinity/affinity-function.xml |    4 +-
 .../Config/cache-query-continuous.xml           |    1 +
 .../native-client-test-cache-affinity.xml       |    2 +-
 .../ConsoleRedirectTest.cs                      |  177 ++
 .../Apache.Ignite.Core.Tests/EventsTest.cs      |    4 +-
 .../Apache.Ignite.Core.Tests/ExceptionsTest.cs  |   81 +-
 .../IgniteConfigurationSectionTest.cs           |   32 +
 .../IgniteConfigurationSerializerTest.cs        |   21 +
 .../IgniteConfigurationTest.cs                  |   12 +-
 .../IgniteStartStopTest.cs                      |    3 +-
 .../Apache.Ignite.Core.Tests/LifecycleTest.cs   |    9 +
 .../Properties/AssemblyInfo.cs                  |    2 +-
 .../Apache.Ignite.Core.Tests/ReconnectTest.cs   |   13 +
 .../Services/ServiceProxyTest.cs                |    6 +-
 .../Services/ServicesTest.cs                    |   26 +-
 .../Apache.Ignite.Core.Tests/TestRunner.cs      |   46 +-
 .../Apache.Ignite.Core.Tests/TestUtils.cs       |   15 +-
 .../dotnet/Apache.Ignite.Core.Tests/app.config  |    2 +-
 .../Apache.Ignite.Core.Tests/custom_app.config  |   34 +
 .../Apache.Ignite.Core.Schema.nuspec            |   53 +
 .../Apache.Ignite.Core.csproj                   |   19 +-
 .../Apache.Ignite.Core.nuspec                   |    2 +-
 .../Binary/BinaryReflectiveSerializer.cs        |  193 +-
 .../Cache/Configuration/CacheConfiguration.cs   |   18 +-
 .../Cache/Query/SqlFieldsQuery.cs               |   27 +-
 .../Apache.Ignite.Core/Cache/Query/SqlQuery.cs  |   13 +
 .../Apache.Ignite.Core/Cluster/IClusterGroup.cs |    6 +
 .../Apache.Ignite.Core/Common/JavaException.cs  |   65 +
 .../Configuration/AtomicConfiguration.cs        |    9 +
 .../Configuration/Package-Info.cs               |   26 +
 .../Discovery/Package-Info.cs                   |   26 +
 .../Discovery/Tcp/Multicast/Package-Info.cs     |   26 +
 .../Discovery/Tcp/Package-Info.cs               |   26 +
 .../Discovery/Tcp/Static/Package-Info.cs        |   26 +
 .../Apache.Ignite.Core/Events/EventBase.cs      |    2 +-
 .../dotnet/Apache.Ignite.Core/IIgnite.cs        |   25 +
 .../Apache.Ignite.Core/IgniteConfiguration.cs   |    2 +
 .../IgniteConfigurationSection.xsd              |    9 +-
 .../dotnet/Apache.Ignite.Core/Ignition.cs       |   82 +-
 .../Impl/Binary/BinarizableSerializer.cs        |   22 +-
 .../Impl/Binary/BinaryFullTypeDescriptor.cs     |    8 +-
 .../Impl/Binary/BinaryReader.cs                 |   38 +-
 .../Impl/Binary/BinaryReaderExtensions.cs       |    2 +-
 .../Impl/Binary/BinaryReflectiveActions.cs      |   10 +-
 .../BinaryReflectiveSerializerInternal.cs       |  169 ++
 .../Binary/BinarySurrogateTypeDescriptor.cs     |    4 +-
 .../Impl/Binary/BinarySystemTypeSerializer.cs   |   20 +-
 .../Impl/Binary/BinaryUtils.cs                  |  180 +-
 .../Impl/Binary/BinaryWriter.cs                 |    2 +-
 .../Impl/Binary/DateTimeSerializer.cs           |   48 +
 .../Impl/Binary/IBinarySerializerInternal.cs    |   42 +
 .../Impl/Binary/IBinarySystemTypeSerializer.cs  |   34 -
 .../Impl/Binary/IBinaryTypeDescriptor.cs        |    2 +-
 .../Impl/Binary/Io/BinaryHeapStream.cs          |    2 +-
 .../Impl/Binary/Io/BinaryStreamBase.cs          |   14 -
 .../Impl/Binary/Marshaller.cs                   |   77 +-
 .../Impl/Binary/SerializableSerializer.cs       |   48 +
 .../Impl/Binary/UserSerializerProxy.cs          |   68 +
 .../Apache.Ignite.Core/Impl/Cache/CacheImpl.cs  |    6 +-
 .../Impl/Cache/Store/CacheStore.cs              |    4 +-
 .../Impl/Cluster/ClusterGroupImpl.cs            |    6 +
 .../Impl/Common/DelegateConverter.cs            |   44 +
 .../Impl/Compute/ComputeTaskHolder.cs           |    3 +-
 .../Apache.Ignite.Core/Impl/ExceptionUtils.cs   |   31 +-
 .../dotnet/Apache.Ignite.Core/Impl/Ignite.cs    |   48 +-
 .../Apache.Ignite.Core/Impl/IgniteProxy.cs      |   43 +-
 .../Apache.Ignite.Core/Impl/IgniteUtils.cs      |   47 +-
 .../Impl/LifecycleBeanHolder.cs                 |    2 +-
 .../Impl/Memory/PlatformMemoryStream.cs         |    3 +-
 .../Apache.Ignite.Core/Impl/NativeMethods.cs    |    6 +
 .../Impl/Services/ServiceProxyInvoker.cs        |   14 +-
 .../Impl/Services/Services.cs                   |   17 +-
 .../Impl/Unmanaged/IgniteJniNativeMethods.cs    |    9 +
 .../Impl/Unmanaged/UnmanagedCallbacks.cs        |   94 +-
 .../Impl/Unmanaged/UnmanagedUtils.cs            |   23 +
 .../Apache.Ignite.Core/Interop/Package-Info.cs  |   26 +
 .../Lifecycle/ClientReconnectEventArgs.cs       |   47 +
 .../Properties/AssemblyInfo.cs                  |    4 +-
 .../Transactions/TransactionConfiguration.cs    |   12 +
 .../Apache.Ignite.Linq.csproj                   |    2 +
 .../Apache.Ignite.Linq.nuspec                   |    6 +-
 .../Apache.Ignite.Linq/CacheExtensions.cs       |   24 +-
 .../Apache.Ignite.Linq/Impl/AliasDictionary.cs  |   74 +-
 .../Impl/CacheFieldsQueryExecutor.cs            |   63 +-
 .../Impl/CacheQueryExpressionVisitor.cs         |    4 +-
 .../Impl/CacheQueryModelVisitor.cs              |    6 +-
 .../Apache.Ignite.Linq/Impl/CacheQueryable.cs   |   10 +-
 .../Impl/CacheQueryableBase.cs                  |    7 +-
 .../Apache.Ignite.Linq/Impl/ExpressionWalker.cs |   34 +-
 .../dotnet/Apache.Ignite.Linq/Package-Info.cs   |   26 +
 .../Properties/AssemblyInfo.cs                  |    2 +-
 .../dotnet/Apache.Ignite.Linq/QueryOptions.cs   |   91 +
 modules/platforms/dotnet/Apache.Ignite.dxg      |    4 +-
 modules/platforms/dotnet/Apache.Ignite.sln      |   14 +
 .../dotnet/Apache.Ignite.sln.DotSettings        |    7 +-
 .../dotnet/Apache.Ignite/Config/Configurator.cs |   11 +-
 .../Apache.Ignite/Properties/AssemblyInfo.cs    |    2 +-
 .../Properties/AssemblyInfo.cs                  |    2 +-
 .../Properties/AssemblyInfo.cs                  |    2 +-
 .../http/jetty/GridJettyRestHandler.java        |    5 +
 .../processors/schedule/ScheduleFutureImpl.java |    7 +-
 .../schedule/GridScheduleSelfTest.java          |   33 +-
 .../apache/ignite/logger/slf4j/Slf4jLogger.java |    2 +-
 .../org/apache/ignite/spark/IgniteContext.scala |    8 +-
 .../org/apache/ignite/spark/IgniteRDD.scala     |   10 +-
 .../apache/ignite/spark/JavaIgniteContext.scala |    2 +-
 .../org/apache/ignite/spark/JavaIgniteRDD.scala |   12 +-
 .../ignite/spark/impl/IgniteAbstractRDD.scala   |    2 +-
 .../apache/ignite/spark/impl/IgniteSqlRDD.scala |    2 +-
 .../spark/JavaEmbeddedIgniteRDDSelfTest.java    |   19 +-
 .../org/apache/ignite/spark/IgniteRDDSpec.scala |   38 +-
 modules/spring/pom.xml                          |    7 -
 .../org/apache/ignite/IgniteSpringBean.java     |  143 +-
 .../GridResourceSpringBeanInjector.java         |   39 +-
 .../ignite/internal/IgniteSpringBeanTest.java   |   55 +
 .../GridSpringResourceInjectionSelfTest.java    |  311 +++-
 .../spring-resource-with-duplicate-beans.xml    |   30 +
 .../processors/resource/spring-resource.xml     |    2 +-
 .../org/apache/ignite/spring-injection-test.xml |    2 +-
 .../testsuites/IgniteSpringTestSuite.java       |    2 +
 .../cache/websession/WebSessionFilter.java      |   11 +-
 .../ignite/cache/websession/WebSessionV2.java   |   10 +-
 .../internal/websession/WebSessionSelfTest.java |    8 +-
 modules/yardstick/config/queries.sql            |    1 +
 .../ignite/yardstick/IgniteBenchmarkUtils.java  |   45 +
 .../IgniteSqlQueryDistributedJoinBenchmark.java |  184 ++
 ...lQueryDistributedJoinBroadcastBenchmark.java |   28 +
 .../IgniteCacheRandomOperationBenchmark.java    |   71 +-
 parent/pom.xml                                  |   25 +-
 417 files changed, 25610 insertions(+), 5087 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/7d05b650/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoMessageFactory.java
----------------------------------------------------------------------


[17/50] [abbrv] ignite git commit: ignite-1232 Distributed SQL joins implementation

Posted by vo...@apache.org.
ignite-1232 Distributed SQL joins implementation


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

Branch: refs/heads/ignite-3553
Commit: 68891e89dd0e0f19321d6a4d45ae7372279b8b08
Parents: eddd0a9
Author: sboikov <sb...@gridgain.com>
Authored: Fri Jul 22 17:07:58 2016 +0300
Committer: sboikov <sb...@gridgain.com>
Committed: Fri Jul 22 17:08:03 2016 +0300

----------------------------------------------------------------------
 .../examples/datagrid/CacheQueryExample.java    |   78 +-
 .../ignite/codegen/MessageCodeGenerator.java    |    6 +
 .../ignite/cache/query/SqlFieldsQuery.java      |   53 +
 .../org/apache/ignite/cache/query/SqlQuery.java |   25 +
 .../managers/communication/GridIoManager.java   |   32 +-
 .../communication/GridIoMessageFactory.java     |    2 +-
 .../managers/communication/GridIoPolicy.java    |    5 +-
 .../cache/GridCacheAffinityManager.java         |    2 +-
 .../processors/cache/GridCacheContext.java      |   19 +
 .../GridCacheDefaultAffinityKeyMapper.java      |   19 +
 .../processors/cache/IgniteCacheProxy.java      |    8 +
 .../binary/CacheObjectBinaryProcessorImpl.java  |    8 +
 .../dht/GridDhtPartitionsReservation.java       |    3 +-
 .../cache/query/GridCacheQueryManager.java      |   47 +-
 .../cache/query/GridCacheQueryMarshallable.java |   37 +
 .../cache/query/GridCacheSqlQuery.java          |   33 +-
 .../cache/query/GridCacheTwoStepQuery.java      |  123 +-
 .../cacheobject/IgniteCacheObjectProcessor.java |    6 +
 .../IgniteCacheObjectProcessorImpl.java         |    5 +
 .../processors/closure/GridClosurePolicy.java   |   51 -
 .../closure/GridClosureProcessor.java           |   52 +-
 .../processors/query/GridQueryIndexing.java     |   51 +-
 .../processors/query/GridQueryProcessor.java    |  192 +-
 .../query/GridQueryTypeDescriptor.java          |    7 +
 .../messages/GridQueryCancelRequest.java        |    2 +-
 .../twostep/messages/GridQueryFailResponse.java |    2 +-
 .../messages/GridQueryNextPageRequest.java      |    2 +-
 .../messages/GridQueryNextPageResponse.java     |   12 +-
 .../h2/twostep/messages/GridQueryRequest.java   |   28 +-
 .../ignite/internal/util/IgniteUtils.java       |   13 +
 .../ignite/internal/util/lang/GridFunc.java     |   20 +
 .../offheap/unsafe/GridOffHeapSnapTreeMap.java  |   91 +-
 .../jdbc/TcpDiscoveryJdbcIpFinderSelfTest.java  |    2 +-
 .../junits/common/GridCommonAbstractTest.java   |   39 +-
 .../query/h2/opt/GridH2SpatialIndex.java        |   74 +-
 .../processors/query/h2/IgniteH2Indexing.java   |  709 ++++++--
 .../query/h2/opt/GridH2AbstractKeyValueRow.java |   72 +-
 .../query/h2/opt/GridH2CollocationModel.java    |  783 +++++++++
 .../processors/query/h2/opt/GridH2Cursor.java   |   36 +-
 .../query/h2/opt/GridH2DefaultTableEngine.java  |   38 +
 .../query/h2/opt/GridH2IndexBase.java           | 1392 ++++++++++++++-
 .../query/h2/opt/GridH2MetaTable.java           |  383 ++++
 .../query/h2/opt/GridH2QueryContext.java        |  612 +++++++
 .../query/h2/opt/GridH2QueryType.java           |   49 +
 .../query/h2/opt/GridH2RetryException.java      |   32 +
 .../processors/query/h2/opt/GridH2Row.java      |   86 +-
 .../query/h2/opt/GridH2RowDescriptor.java       |   28 +-
 .../query/h2/opt/GridH2RowFactory.java          |  179 ++
 .../processors/query/h2/opt/GridH2Table.java    |  372 ++--
 .../query/h2/opt/GridH2TreeIndex.java           |  142 +-
 .../processors/query/h2/opt/GridH2Utils.java    |  133 --
 .../query/h2/opt/GridH2ValueCacheObject.java    |    3 +-
 .../query/h2/opt/GridLuceneIndex.java           |    7 +-
 .../processors/query/h2/sql/GridSqlAlias.java   |   12 +
 .../processors/query/h2/sql/GridSqlColumn.java  |   22 +-
 .../processors/query/h2/sql/GridSqlElement.java |   11 +
 .../query/h2/sql/GridSqlOperation.java          |    2 +-
 .../query/h2/sql/GridSqlOperationType.java      |    8 +-
 .../query/h2/sql/GridSqlQueryParser.java        |   97 +-
 .../query/h2/sql/GridSqlQuerySplitter.java      |  293 +++-
 .../processors/query/h2/sql/GridSqlSelect.java  |    9 +-
 .../processors/query/h2/sql/GridSqlTable.java   |   70 +
 .../query/h2/twostep/GridMapQueryExecutor.java  |  415 +++--
 .../query/h2/twostep/GridMergeIndex.java        |   75 +-
 .../h2/twostep/GridMergeIndexUnsorted.java      |    6 +-
 .../query/h2/twostep/GridMergeTable.java        |    4 +-
 .../h2/twostep/GridReduceQueryExecutor.java     |  338 ++--
 .../query/h2/twostep/GridThreadLocalTable.java  |   68 +-
 .../query/h2/twostep/msg/GridH2Array.java       |    9 +-
 .../query/h2/twostep/msg/GridH2Boolean.java     |   10 +-
 .../query/h2/twostep/msg/GridH2Byte.java        |    9 +-
 .../query/h2/twostep/msg/GridH2Bytes.java       |   11 +-
 .../query/h2/twostep/msg/GridH2CacheObject.java |    9 +-
 .../query/h2/twostep/msg/GridH2Date.java        |    9 +-
 .../query/h2/twostep/msg/GridH2Decimal.java     |   11 +-
 .../query/h2/twostep/msg/GridH2Double.java      |    9 +-
 .../query/h2/twostep/msg/GridH2Float.java       |    9 +-
 .../query/h2/twostep/msg/GridH2Geometry.java    |   11 +-
 .../h2/twostep/msg/GridH2IndexRangeRequest.java |  208 +++
 .../twostep/msg/GridH2IndexRangeResponse.java   |  279 +++
 .../query/h2/twostep/msg/GridH2Integer.java     |   20 +-
 .../query/h2/twostep/msg/GridH2JavaObject.java  |   11 +-
 .../query/h2/twostep/msg/GridH2Long.java        |    9 +-
 .../query/h2/twostep/msg/GridH2Null.java        |   15 +-
 .../h2/twostep/msg/GridH2QueryRequest.java      |  401 +++++
 .../query/h2/twostep/msg/GridH2RowMessage.java  |  116 ++
 .../query/h2/twostep/msg/GridH2RowRange.java    |  181 ++
 .../h2/twostep/msg/GridH2RowRangeBounds.java    |  188 ++
 .../query/h2/twostep/msg/GridH2Short.java       |    9 +-
 .../query/h2/twostep/msg/GridH2String.java      |    9 +-
 .../query/h2/twostep/msg/GridH2Time.java        |    9 +-
 .../query/h2/twostep/msg/GridH2Timestamp.java   |   11 +-
 .../query/h2/twostep/msg/GridH2Uuid.java        |    9 +-
 .../h2/twostep/msg/GridH2ValueMessage.java      |    2 +-
 .../twostep/msg/GridH2ValueMessageFactory.java  |   22 +-
 ...idCacheReduceQueryMultithreadedSelfTest.java |  168 --
 .../cache/IgniteCacheAbstractQuerySelfTest.java |   30 +-
 .../IgniteCacheCrossCacheJoinRandomTest.java    |  442 +++++
 ...acheDistributedJoinCollocatedAndNotTest.java |  365 ++++
 ...acheDistributedJoinCustomAffinityMapper.java |  262 +++
 .../IgniteCacheDistributedJoinNoIndexTest.java  |  299 ++++
 ...ributedJoinPartitionedAndReplicatedTest.java |  487 ++++++
 ...CacheDistributedJoinQueryConditionsTest.java |  624 +++++++
 .../cache/IgniteCacheDistributedJoinTest.java   |  316 ++++
 ...PartitionedAndReplicatedCollocationTest.java |  399 +++++
 ...teCacheJoinPartitionedAndReplicatedTest.java |   78 +-
 ...IgniteCacheJoinQueryWithAffinityKeyTest.java |  646 +++++++
 .../cache/IgniteCacheQueryLoadSelfTest.java     |   12 +-
 .../cache/IgniteCrossCachesJoinsQueryTest.java  | 1641 ++++++++++++++++++
 ...QueryNodeRestartDistributedJoinSelfTest.java |  476 +++++
 ...dCacheAbstractReduceFieldsQuerySelfTest.java |  420 -----
 ...ridCacheReduceFieldsQueryAtomicSelfTest.java |   38 -
 ...GridCacheReduceFieldsQueryLocalSelfTest.java |   37 -
 ...cheReduceFieldsQueryPartitionedSelfTest.java |   59 -
 ...acheReduceFieldsQueryReplicatedSelfTest.java |   37 -
 .../query/IgniteSqlSchemaIndexingTest.java      |    5 +-
 .../query/IgniteSqlSplitterSelfTest.java        |  833 ++++++++-
 .../h2/GridIndexingSpiAbstractSelfTest.java     |  145 +-
 .../query/h2/opt/GridH2TableSelfTest.java       |   10 +-
 .../h2/sql/AbstractH2CompareQueryTest.java      |  163 +-
 .../query/h2/sql/GridQueryParsingTest.java      |   17 +-
 .../H2CompareBigQueryDistributedJoinsTest.java  |   28 +
 .../query/h2/sql/H2CompareBigQueryTest.java     |  119 +-
 .../IgniteCacheQuerySelfTestSuite.java          |   25 +-
 .../IgniteCacheQuerySelfTestSuite2.java         |   10 -
 .../ignite/yardstick/IgniteBenchmarkUtils.java  |   45 +
 .../IgniteSqlQueryDistributedJoinBenchmark.java |  184 ++
 ...lQueryDistributedJoinBroadcastBenchmark.java |   28 +
 parent/pom.xml                                  |   24 +-
 129 files changed, 15201 insertions(+), 2502 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/examples/src/main/java/org/apache/ignite/examples/datagrid/CacheQueryExample.java
----------------------------------------------------------------------
diff --git a/examples/src/main/java/org/apache/ignite/examples/datagrid/CacheQueryExample.java b/examples/src/main/java/org/apache/ignite/examples/datagrid/CacheQueryExample.java
index 9200489..85d74e0 100644
--- a/examples/src/main/java/org/apache/ignite/examples/datagrid/CacheQueryExample.java
+++ b/examples/src/main/java/org/apache/ignite/examples/datagrid/CacheQueryExample.java
@@ -46,8 +46,11 @@ import org.apache.ignite.lang.IgniteBiPredicate;
  * limitations (not applied if data is queried from one node only):
  * <ul>
  *     <li>
- *         Joins will work correctly only if joined objects are stored in
+ *         Non-distributed joins will work correctly only if joined objects are stored in
  *         collocated mode. Refer to {@link AffinityKey} javadoc for more details.
+ *         <p>
+ *         To use distributed joins it is necessary to set query 'distributedJoin' flag using
+ *         {@link SqlFieldsQuery#setDistributedJoins(boolean)} or {@link SqlQuery#setDistributedJoins(boolean)}.
  *     </li>
  *     <li>
  *         Note that if you created query on to replicated cache, all data will
@@ -65,6 +68,9 @@ public class CacheQueryExample {
     /** Organizations cache name. */
     private static final String ORG_CACHE = CacheQueryExample.class.getSimpleName() + "Organizations";
 
+    /** Persons collocated with Organizations cache name. */
+    private static final String COLLOCATED_PERSON_CACHE = CacheQueryExample.class.getSimpleName() + "CollocatedPersons";
+
     /** Persons cache name. */
     private static final String PERSON_CACHE = CacheQueryExample.class.getSimpleName() + "Persons";
 
@@ -84,14 +90,20 @@ public class CacheQueryExample {
             orgCacheCfg.setCacheMode(CacheMode.PARTITIONED); // Default.
             orgCacheCfg.setIndexedTypes(Long.class, Organization.class);
 
+            CacheConfiguration<AffinityKey<Long>, Person> colPersonCacheCfg = new CacheConfiguration<>(COLLOCATED_PERSON_CACHE);
+
+            colPersonCacheCfg.setCacheMode(CacheMode.PARTITIONED); // Default.
+            colPersonCacheCfg.setIndexedTypes(AffinityKey.class, Person.class);
+
             CacheConfiguration<AffinityKey<Long>, Person> personCacheCfg = new CacheConfiguration<>(PERSON_CACHE);
 
             personCacheCfg.setCacheMode(CacheMode.PARTITIONED); // Default.
-            personCacheCfg.setIndexedTypes(AffinityKey.class, Person.class);
+            personCacheCfg.setIndexedTypes(Long.class, Person.class);
 
             // Auto-close cache at the end of the example.
             try (
                 IgniteCache<Long, Organization> orgCache = ignite.getOrCreateCache(orgCacheCfg);
+                IgniteCache<AffinityKey<Long>, Person> colPersonCache = ignite.getOrCreateCache(colPersonCacheCfg);
                 IgniteCache<AffinityKey<Long>, Person> personCache = ignite.getOrCreateCache(personCacheCfg)
             ) {
                 // Populate cache.
@@ -103,9 +115,12 @@ public class CacheQueryExample {
                 // Example for SQL-based querying employees based on salary ranges.
                 sqlQuery();
 
-                // Example for SQL-based querying employees for a given organization (includes SQL join).
+                // Example for SQL-based querying employees for a given organization (includes SQL join for collocated objects).
                 sqlQueryWithJoin();
 
+                // Example for SQL-based querying employees for a given organization (includes distributed SQL join).
+                sqlQueryWithDistributedJoin();
+
                 // Example for TEXT-based querying for a given string in peoples resumes.
                 textQuery();
 
@@ -121,6 +136,7 @@ public class CacheQueryExample {
             }
             finally {
                 // Distributed cache could be removed from cluster only by #destroyCache() call.
+                ignite.destroyCache(COLLOCATED_PERSON_CACHE);
                 ignite.destroyCache(PERSON_CACHE);
                 ignite.destroyCache(ORG_CACHE);
             }
@@ -133,7 +149,7 @@ public class CacheQueryExample {
      * Example for scan query based on a predicate.
      */
     private static void scanQuery() {
-        IgniteCache<BinaryObject, BinaryObject> cache = Ignition.ignite().cache(PERSON_CACHE).withKeepBinary();
+        IgniteCache<BinaryObject, BinaryObject> cache = Ignition.ignite().cache(COLLOCATED_PERSON_CACHE).withKeepBinary();
 
         ScanQuery<BinaryObject, BinaryObject> scan = new ScanQuery<>(
             new IgniteBiPredicate<BinaryObject, BinaryObject>() {
@@ -170,7 +186,7 @@ public class CacheQueryExample {
      * Example for SQL queries based on all employees working for a specific organization.
      */
     private static void sqlQueryWithJoin() {
-        IgniteCache<AffinityKey<Long>, Person> cache = Ignition.ignite().cache(PERSON_CACHE);
+        IgniteCache<AffinityKey<Long>, Person> cache = Ignition.ignite().cache(COLLOCATED_PERSON_CACHE);
 
         // SQL clause query which joins on 2 types to select people for a specific organization.
         String joinSql =
@@ -189,6 +205,32 @@ public class CacheQueryExample {
     }
 
     /**
+     * Example for SQL queries based on all employees working for a specific organization (query uses distributed join).
+     */
+    private static void sqlQueryWithDistributedJoin() {
+        IgniteCache<AffinityKey<Long>, Person> cache = Ignition.ignite().cache(PERSON_CACHE);
+
+        // SQL clause query which joins on 2 types to select people for a specific organization.
+        String joinSql =
+            "from Person, \"" + ORG_CACHE + "\".Organization as org " +
+            "where Person.orgId = org.id " +
+            "and lower(org.name) = lower(?)";
+
+        SqlQuery qry = new SqlQuery<AffinityKey<Long>, Person>(Person.class, joinSql).
+            setArgs("ApacheIgnite");
+
+        // Enable distributed joins for query.
+        qry.setDistributedJoins(true);
+
+        // Execute queries for find employees for different organizations.
+        print("Following people are 'ApacheIgnite' employees (distributed join): ", cache.query(qry).getAll());
+
+        qry.setArgs("Other");
+
+        print("Following people are 'Other' employees (distributed join): ", cache.query(qry).getAll());
+    }
+
+    /**
      * Example for TEXT queries using LUCENE-based indexing of people's resumes.
      */
     private static void textQuery() {
@@ -210,7 +252,7 @@ public class CacheQueryExample {
      * Example for SQL queries to calculate average salary for a specific organization.
      */
     private static void sqlQueryWithAggregation() {
-        IgniteCache<AffinityKey<Long>, Person> cache = Ignition.ignite().cache(PERSON_CACHE);
+        IgniteCache<AffinityKey<Long>, Person> cache = Ignition.ignite().cache(COLLOCATED_PERSON_CACHE);
 
         // Calculate average of salary of all persons in ApacheIgnite.
         // Note that we also join on Organization cache as well.
@@ -249,7 +291,7 @@ public class CacheQueryExample {
      * fields instead of whole key-value pairs.
      */
     private static void sqlFieldsQueryWithJoin() {
-        IgniteCache<AffinityKey<Long>, Person> cache = Ignition.ignite().cache(PERSON_CACHE);
+        IgniteCache<AffinityKey<Long>, Person> cache = Ignition.ignite().cache(COLLOCATED_PERSON_CACHE);
 
         // Execute query to get names of all employees.
         String sql =
@@ -263,7 +305,7 @@ public class CacheQueryExample {
         List<List<?>> res = cursor.getAll();
 
         // Print persons' names and organizations' names.
-        print("Names of all employees and organizations they belong to:", res);
+        print("Names of all employees and organizations they belong to: ", res);
     }
 
     /**
@@ -282,9 +324,11 @@ public class CacheQueryExample {
         orgCache.put(org1.id(), org1);
         orgCache.put(org2.id(), org2);
 
-        IgniteCache<AffinityKey<Long>, Person> personCache = Ignition.ignite().cache(PERSON_CACHE);
+        IgniteCache<AffinityKey<Long>, Person> colPersonCache = Ignition.ignite().cache(COLLOCATED_PERSON_CACHE);
+        IgniteCache<Long, Person> personCache = Ignition.ignite().cache(PERSON_CACHE);
 
-        // Clear cache before running the example.
+        // Clear caches before running the example.
+        colPersonCache.clear();
         personCache.clear();
 
         // People.
@@ -295,10 +339,16 @@ public class CacheQueryExample {
 
         // Note that in this example we use custom affinity key for Person objects
         // to ensure that all persons are collocated with their organizations.
-        personCache.put(p1.key(), p1);
-        personCache.put(p2.key(), p2);
-        personCache.put(p3.key(), p3);
-        personCache.put(p4.key(), p4);
+        colPersonCache.put(p1.key(), p1);
+        colPersonCache.put(p2.key(), p2);
+        colPersonCache.put(p3.key(), p3);
+        colPersonCache.put(p4.key(), p4);
+
+        // These Person objects are not collocated with their organizations.
+        personCache.put(p1.id, p1);
+        personCache.put(p2.id, p2);
+        personCache.put(p3.id, p3);
+        personCache.put(p4.id, p4);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/codegen/src/main/java/org/apache/ignite/codegen/MessageCodeGenerator.java
----------------------------------------------------------------------
diff --git a/modules/codegen/src/main/java/org/apache/ignite/codegen/MessageCodeGenerator.java b/modules/codegen/src/main/java/org/apache/ignite/codegen/MessageCodeGenerator.java
index a6ae0da..f0ba5ce 100644
--- a/modules/codegen/src/main/java/org/apache/ignite/codegen/MessageCodeGenerator.java
+++ b/modules/codegen/src/main/java/org/apache/ignite/codegen/MessageCodeGenerator.java
@@ -223,6 +223,12 @@ public class MessageCodeGenerator {
 //        gen.generateAndWrite(GridH2Uuid.class);
 //        gen.generateAndWrite(GridH2Geometry.class);
 //        gen.generateAndWrite(GridH2CacheObject.class);
+//        gen.generateAndWrite(GridH2IndexRangeRequest.class);
+//        gen.generateAndWrite(GridH2IndexRangeResponse.class);
+//        gen.generateAndWrite(GridH2RowRange.class);
+//        gen.generateAndWrite(GridH2RowRangeBounds.class);
+//        gen.generateAndWrite(GridH2QueryRequest.class);
+//        gen.generateAndWrite(GridH2RowMessage.class);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/core/src/main/java/org/apache/ignite/cache/query/SqlFieldsQuery.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/cache/query/SqlFieldsQuery.java b/modules/core/src/main/java/org/apache/ignite/cache/query/SqlFieldsQuery.java
index b2dd181..48dab6b 100644
--- a/modules/core/src/main/java/org/apache/ignite/cache/query/SqlFieldsQuery.java
+++ b/modules/core/src/main/java/org/apache/ignite/cache/query/SqlFieldsQuery.java
@@ -56,6 +56,12 @@ public final class SqlFieldsQuery extends Query<List<?>> {
     /** Collocation flag. */
     private boolean collocated;
 
+    /** */
+    private boolean enforceJoinOrder;
+
+    /** */
+    private boolean distributedJoins;
+
     /**
      * Constructs SQL fields query.
      *
@@ -141,6 +147,53 @@ public final class SqlFieldsQuery extends Query<List<?>> {
         return this;
     }
 
+    /**
+     * Checks if join order of tables if enforced.
+     *
+     * @return Flag value.
+     */
+    public boolean isEnforceJoinOrder() {
+        return enforceJoinOrder;
+    }
+
+    /**
+     * Sets flag to enforce join order of tables in the query. If set to {@code true}
+     * query optimizer will not reorder tables in join. By default is {@code false}.
+     * <p>
+     * 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.
+     *
+     * @param enforceJoinOrder Flag value.
+     * @return {@code this} For chaining.
+     */
+    public SqlFieldsQuery setEnforceJoinOrder(boolean enforceJoinOrder) {
+        this.enforceJoinOrder = enforceJoinOrder;
+
+        return this;
+    }
+
+    /**
+     * Specify if distributed joins are enabled for this query.
+     *
+     * @param distributedJoins Distributed joins enabled.
+     * @return {@code this} For chaining.
+     */
+    public SqlFieldsQuery setDistributedJoins(boolean distributedJoins) {
+        this.distributedJoins = distributedJoins;
+
+        return this;
+    }
+
+    /**
+     * Check if distributed joins are enabled for this query.
+     *
+     * @return {@code true} If distributed joind enabled.
+     */
+    public boolean isDistributedJoins() {
+        return distributedJoins;
+    }
+
     /** {@inheritDoc} */
     @Override public SqlFieldsQuery setPageSize(int pageSize) {
         return (SqlFieldsQuery)super.setPageSize(pageSize);

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/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 be3b390..e05ff13 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
@@ -43,6 +43,9 @@ public final class SqlQuery<K, V> extends Query<Cache.Entry<K, V>> {
     @GridToStringInclude
     private Object[] args;
 
+    /** */
+    private boolean distributedJoins;
+
     /**
      * Constructs query for the given type name and SQL query.
      *
@@ -142,11 +145,33 @@ public final class SqlQuery<K, V> extends Query<Cache.Entry<K, V>> {
 
     /**
      * @param type Type.
+     * @return {@code this} For chaining.
      */
     public SqlQuery setType(Class<?> type) {
         return setType(GridQueryProcessor.typeName(type));
     }
 
+    /**
+     * Specify if distributed joins are enabled for this query.
+     *
+     * @param distributedJoins Distributed joins enabled.
+     * @return {@code this} For chaining.
+     */
+    public SqlQuery setDistributedJoins(boolean distributedJoins) {
+        this.distributedJoins = distributedJoins;
+
+        return this;
+    }
+
+    /**
+     * Check if distributed joins are enabled for this query.
+     *
+     * @return {@code true} If distributed joind enabled.
+     */
+    public boolean isDistributedJoins() {
+        return distributedJoins;
+    }
+
     /** {@inheritDoc} */
     @Override public String toString() {
         return S.toString(SqlQuery.class, this);

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoManager.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoManager.java
index 4bc2eea..99cb7f4 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoManager.java
@@ -88,6 +88,7 @@ import static org.apache.ignite.events.EventType.EVT_NODE_JOINED;
 import static org.apache.ignite.events.EventType.EVT_NODE_LEFT;
 import static org.apache.ignite.internal.GridTopic.TOPIC_COMM_USER;
 import static org.apache.ignite.internal.managers.communication.GridIoPolicy.AFFINITY_POOL;
+import static org.apache.ignite.internal.managers.communication.GridIoPolicy.IDX_POOL;
 import static org.apache.ignite.internal.managers.communication.GridIoPolicy.IGFS_POOL;
 import static org.apache.ignite.internal.managers.communication.GridIoPolicy.MANAGEMENT_POOL;
 import static org.apache.ignite.internal.managers.communication.GridIoPolicy.MARSH_CACHE_POOL;
@@ -157,6 +158,9 @@ public class GridIoManager extends GridManagerAdapter<CommunicationSpi<Serializa
     /** IGFS pool. */
     private ExecutorService igfsPool;
 
+    /** Index pool. */
+    private ExecutorService idxPool;
+
     /** Discovery listener. */
     private GridLocalEventListener discoLsnr;
 
@@ -268,6 +272,13 @@ public class GridIoManager extends GridManagerAdapter<CommunicationSpi<Serializa
             0,
             new LinkedBlockingQueue<Runnable>());
 
+        if (IgniteComponentType.INDEXING.inClassPath()) {
+            int cpus = Runtime.getRuntime().availableProcessors();
+
+            idxPool = new IgniteThreadPoolExecutor("idx", ctx.gridName(),
+                cpus, cpus * 2, 3000L, new LinkedBlockingQueue<Runnable>(1000));
+        }
+
         getSpi().setListener(commLsnr = new CommunicationListener<Serializable>() {
             @Override public void onMessage(UUID nodeId, Serializable msg, IgniteRunnable msgC) {
                 try {
@@ -651,6 +662,7 @@ public class GridIoManager extends GridManagerAdapter<CommunicationSpi<Serializa
                 case AFFINITY_POOL:
                 case UTILITY_CACHE_POOL:
                 case MARSH_CACHE_POOL:
+                case IDX_POOL:
                 case IGFS_POOL:
                 {
                     if (msg.isOrdered())
@@ -689,7 +701,7 @@ public class GridIoManager extends GridManagerAdapter<CommunicationSpi<Serializa
      * @return Execution pool.
      * @throws IgniteCheckedException If failed.
      */
-    private Executor pool(byte plc) throws IgniteCheckedException {
+    public Executor pool(byte plc) throws IgniteCheckedException {
         switch (plc) {
             case P2P_POOL:
                 return p2pPool;
@@ -717,6 +729,11 @@ public class GridIoManager extends GridManagerAdapter<CommunicationSpi<Serializa
 
                 return igfsPool;
 
+            case IDX_POOL:
+                assert idxPool != null : "Indexing pool is not configured.";
+
+                return idxPool;
+
             default: {
                 assert plc >= 0 : "Negative policy: " + plc;
 
@@ -1357,6 +1374,19 @@ public class GridIoManager extends GridManagerAdapter<CommunicationSpi<Serializa
     /**
      * @param node Destination node.
      * @param topic Topic to send the message to.
+     * @param topicOrd GridTopic enumeration ordinal.
+     * @param msg Message to send.
+     * @param plc Type of processing.
+     * @throws IgniteCheckedException Thrown in case of any errors.
+     */
+    public void send(ClusterNode node, Object topic, int topicOrd, Message msg, byte plc)
+        throws IgniteCheckedException {
+        send(node, topic, topicOrd, msg, plc, false, 0, false, null);
+    }
+
+    /**
+     * @param node Destination node.
+     * @param topic Topic to send the message to.
      * @param msg Message to send.
      * @param plc Type of processing.
      * @param timeout Timeout to keep a message on receiving queue.

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoMessageFactory.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoMessageFactory.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoMessageFactory.java
index 5f60215..1eebfd4 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoMessageFactory.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoMessageFactory.java
@@ -752,7 +752,7 @@ public class GridIoMessageFactory implements MessageFactory {
 
             // [-3..119] [124] - this
             // [120..123] - DR
-            // [-4..-22] - SQL
+            // [-4..-22, -30..-35] - SQL
             default:
                 if (ext != null) {
                     for (MessageFactory factory : ext) {

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoPolicy.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoPolicy.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoPolicy.java
index 00590ba..70a7354 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoPolicy.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoPolicy.java
@@ -43,9 +43,12 @@ public class GridIoPolicy {
     /** Marshaller cache execution pool. */
     public static final byte MARSH_CACHE_POOL = 6;
 
-    /** Marshaller cache execution pool. */
+    /** IGFS pool. */
     public static final byte IGFS_POOL = 7;
 
+    /** Pool for handling distributed index range requests. */
+    public static final byte IDX_POOL = 8;
+
     /**
      * Defines the range of reserved pools that are not available for plugins.
      * @param key The key.

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAffinityManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAffinityManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAffinityManager.java
index 8633333..e0e8031 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAffinityManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAffinityManager.java
@@ -224,7 +224,7 @@ public class GridCacheAffinityManager extends GridCacheManagerAdapter {
      * @param key Key.
      * @return Affinity key.
      */
-    private Object affinityKey(Object key) {
+    public Object affinityKey(Object key) {
         if (key instanceof CacheObject && !(key instanceof BinaryObject))
             key = ((CacheObject)key).value(cctx.cacheObjectContext(), false);
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheContext.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheContext.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheContext.java
index 36d9104..fec43d0 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheContext.java
@@ -245,6 +245,9 @@ public class GridCacheContext<K, V> implements Externalizable {
     /** */
     private boolean deferredDel;
 
+    /** */
+    private boolean customAffMapper;
+
     /**
      * Empty constructor required for {@link Externalizable}.
      */
@@ -365,6 +368,13 @@ public class GridCacheContext<K, V> implements Externalizable {
     }
 
     /**
+     * @return {@code True} if custom {@link AffinityKeyMapper} is configured for cache.
+     */
+    public boolean customAffinityMapper() {
+        return customAffMapper;
+    }
+
+    /**
      * @param dynamicDeploymentId Dynamic deployment ID.
      */
     void dynamicDeploymentId(IgniteUuid dynamicDeploymentId) {
@@ -567,6 +577,13 @@ public class GridCacheContext<K, V> implements Externalizable {
     }
 
     /**
+     * @return {@code True} if cache is partitioned cache.
+     */
+    public boolean isPartitioned() {
+        return cacheCfg.getCacheMode() == CacheMode.PARTITIONED;
+    }
+
+    /**
      * @return {@code True} in case replication is enabled.
      */
     public boolean isDrEnabled() {
@@ -1132,6 +1149,8 @@ public class GridCacheContext<K, V> implements Externalizable {
      */
     public void cacheObjectContext(CacheObjectContext cacheObjCtx) {
         this.cacheObjCtx = cacheObjCtx;
+
+        customAffMapper = cacheCfg.getAffinityMapper().getClass() != cacheObjCtx.defaultAffMapper().getClass();
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheDefaultAffinityKeyMapper.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheDefaultAffinityKeyMapper.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheDefaultAffinityKeyMapper.java
index 5422bbd..4a2f039 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheDefaultAffinityKeyMapper.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheDefaultAffinityKeyMapper.java
@@ -34,6 +34,7 @@ import org.apache.ignite.internal.util.typedef.P1;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.resources.IgniteInstanceResource;
 import org.apache.ignite.resources.LoggerResource;
+import org.jetbrains.annotations.Nullable;
 
 /**
  * Default key affinity mapper. If key class has annotation {@link AffinityKeyMapped},
@@ -120,6 +121,24 @@ public class GridCacheDefaultAffinityKeyMapper implements AffinityKeyMapper {
     }
 
     /**
+     * @param cls Key class.
+     * @return Name of
+     */
+    @Nullable public String affinityKeyPropertyName(Class<?> cls) {
+        Field field = reflectCache.firstField(cls);
+
+        if (field != null)
+            return field.getName();
+
+        Method mtd = reflectCache.firstMethod(cls);
+
+        if (mtd != null)
+            return mtd.getName();
+
+        return null;
+    }
+
+    /**
      * @param ignite Ignite.
      */
     @IgniteInstanceResource

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheProxy.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheProxy.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheProxy.java
index 0d7bc6a..0005530 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheProxy.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheProxy.java
@@ -670,6 +670,10 @@ public class IgniteCacheProxy<K, V> extends AsyncSupportAdapter<IgniteCache<K, V
                     opCtxCall != null && opCtxCall.isKeepBinary());
 
             if (qry instanceof SqlQuery) {
+                if (isReplicatedDataNode() && ((SqlQuery)qry).isDistributedJoins())
+                    throw new CacheException("Queries using distributed JOINs have to be run on partitioned cache, " +
+                        "not on replicated.");
+
                 final SqlQuery p = (SqlQuery)qry;
 
                 if (isReplicatedDataNode() || ctx.isLocal() || qry.isLocal())
@@ -684,6 +688,10 @@ public class IgniteCacheProxy<K, V> extends AsyncSupportAdapter<IgniteCache<K, V
             }
 
             if (qry instanceof SqlFieldsQuery) {
+                if (isReplicatedDataNode() && ((SqlFieldsQuery)qry).isDistributedJoins())
+                    throw new CacheException("Queries using distributed JOINs have to be run on partitioned cache, " +
+                        "not on replicated.");
+
                 SqlFieldsQuery p = (SqlFieldsQuery)qry;
 
                 if (isReplicatedDataNode() || ctx.isLocal() || qry.isLocal())

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/binary/CacheObjectBinaryProcessorImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/binary/CacheObjectBinaryProcessorImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/binary/CacheObjectBinaryProcessorImpl.java
index 8400594..f4d1d6e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/binary/CacheObjectBinaryProcessorImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/binary/CacheObjectBinaryProcessorImpl.java
@@ -516,6 +516,14 @@ public class CacheObjectBinaryProcessorImpl extends IgniteCacheObjectProcessorIm
     }
 
     /** {@inheritDoc} */
+    @Override public String affinityField(String keyType) {
+        if (binaryCtx == null)
+            return null;
+
+        return binaryCtx.affinityKeyFieldName(typeId(keyType));
+    }
+
+    /** {@inheritDoc} */
     @Override public BinaryObjectBuilder builder(String clsName) {
         return new BinaryObjectBuilderImpl(binaryCtx, clsName);
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionsReservation.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionsReservation.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionsReservation.java
index d12247e..2f51c5a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionsReservation.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionsReservation.java
@@ -204,7 +204,8 @@ public class GridDhtPartitionsReservation implements GridReservable {
 
             if (reservations.compareAndSet(r, r - 1)) {
                 // If it was the last reservation and topology version changed -> attempt to evict partitions.
-                if (r == 1 && !topVer.equals(cctx.topology().topologyVersion()))
+                if (r == 1 && !cctx.kernalContext().isStopping() &&
+                    !topVer.equals(cctx.topology().topologyVersion()))
                     tryEvict(parts.get());
 
                 return;

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryManager.java
index 4367ee9..60e81e2 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryManager.java
@@ -75,7 +75,6 @@ import org.apache.ignite.internal.processors.datastructures.GridSetQueryPredicat
 import org.apache.ignite.internal.processors.datastructures.SetItemKey;
 import org.apache.ignite.internal.processors.platform.cache.PlatformCacheEntryFilter;
 import org.apache.ignite.internal.processors.query.GridQueryFieldMetadata;
-import org.apache.ignite.internal.processors.query.GridQueryFieldsResult;
 import org.apache.ignite.internal.processors.query.GridQueryIndexDescriptor;
 import org.apache.ignite.internal.processors.query.GridQueryIndexType;
 import org.apache.ignite.internal.processors.query.GridQueryProcessor;
@@ -270,25 +269,6 @@ public abstract class GridCacheQueryManager<K, V> extends GridCacheManagerAdapte
     }
 
     /**
-     * Gets number of objects of given type in index.
-     *
-     * @param valType Value type.
-     * @return Number of objects or -1 if type was not indexed at all.
-     * @throws IgniteCheckedException If failed.
-     */
-    public long size(Class<?> valType) throws IgniteCheckedException {
-        if (!enterBusy())
-            throw new IllegalStateException("Failed to get size (grid is stopping).");
-
-        try {
-            return qryProc.size(space, valType);
-        }
-        finally {
-            leaveBusy();
-        }
-    }
-
-    /**
      * Rebuilds all search indexes of given value type.
      *
      * @param typeName Value type name.
@@ -586,26 +566,7 @@ public abstract class GridCacheQueryManager<K, V> extends GridCacheManagerAdapte
         try {
             switch (qry.type()) {
                 case SQL:
-                    if (cctx.gridEvents().isRecordable(EVT_CACHE_QUERY_EXECUTED)) {
-                        cctx.gridEvents().record(new CacheQueryExecutedEvent<>(
-                            cctx.localNode(),
-                            "SQL query executed.",
-                            EVT_CACHE_QUERY_EXECUTED,
-                            CacheQueryType.SQL.name(),
-                            cctx.namex(),
-                            qry.queryClassName(),
-                            qry.clause(),
-                            null,
-                            null,
-                            args,
-                            subjId,
-                            taskName));
-                    }
-
-                    iter = qryProc.query(space, qry.clause(), F.asList(args),
-                        qry.queryClassName(), filter(qry));
-
-                    break;
+                    throw new IllegalStateException("Should never be called.");
 
                 case SCAN:
                     if (cctx.gridEvents().isRecordable(EVT_CACHE_QUERY_EXECUTED)) {
@@ -764,11 +725,7 @@ public abstract class GridCacheQueryManager<K, V> extends GridCacheManagerAdapte
             else {
                 assert qry.type() == SQL_FIELDS;
 
-                GridQueryFieldsResult qryRes = qryProc.queryFields(space, qry.clause(), F.asList(args), filter(qry));
-
-                res.metaData(qryRes.metaData());
-
-                res.onDone(qryRes.iterator());
+                throw new IllegalStateException("Should never be called.");
             }
         }
         catch (Exception e) {

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryMarshallable.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryMarshallable.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryMarshallable.java
new file mode 100644
index 0000000..d87936a
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryMarshallable.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.processors.cache.query;
+
+import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.marshaller.Marshaller;
+
+/**
+ * Message which needs to be marshalled and unmarshalled before sending or processing it.
+ */
+public interface GridCacheQueryMarshallable {
+    /**
+     * @param m Marshaller.
+     */
+    public void marshall(Marshaller m);
+
+    /**
+     * @param m Marshaller.
+     * @param ctx Context.
+     */
+    public void unmarshall(Marshaller m, GridKernalContext ctx);
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheSqlQuery.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheSqlQuery.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheSqlQuery.java
index 0733827..6b81ed1 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheSqlQuery.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheSqlQuery.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.processors.cache.query;
 import java.nio.ByteBuffer;
 import java.util.LinkedHashMap;
 import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteException;
 import org.apache.ignite.internal.GridDirectTransient;
 import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.util.tostring.GridToStringInclude;
@@ -35,7 +36,7 @@ import org.apache.ignite.plugin.extensions.communication.MessageWriter;
 /**
  * Query.
  */
-public class GridCacheSqlQuery implements Message {
+public class GridCacheSqlQuery implements Message, GridCacheQueryMarshallable {
     /** */
     private static final long serialVersionUID = 0L;
 
@@ -130,30 +131,34 @@ public class GridCacheSqlQuery implements Message {
         return params;
     }
 
-    /**
-     * @param m Marshaller.
-     * @throws IgniteCheckedException If failed.
-     */
-    public void marshallParams(Marshaller m) throws IgniteCheckedException {
+    /** {@inheritDoc} */
+    @Override public void marshall(Marshaller m) {
         if (paramsBytes != null)
             return;
 
         assert params != null;
 
-        paramsBytes = m.marshal(params);
+        try {
+            paramsBytes = m.marshal(params);
+        }
+        catch (IgniteCheckedException e) {
+            throw new IgniteException(e);
+        }
     }
 
-    /**
-     * @param m Marshaller.
-     * @throws IgniteCheckedException If failed.
-     */
-    public void unmarshallParams(Marshaller m, GridKernalContext ctx) throws IgniteCheckedException {
+    /** {@inheritDoc} */
+    @Override public void unmarshall(Marshaller m, GridKernalContext ctx) {
         if (params != null)
             return;
 
         assert paramsBytes != null;
 
-        params = m.unmarshal(paramsBytes, U.resolveClassLoader(ctx.config()));
+        try {
+            params = m.unmarshal(paramsBytes, U.resolveClassLoader(ctx.config()));
+        }
+        catch (IgniteCheckedException e) {
+            throw new IgniteException(e);
+        }
     }
 
     /** {@inheritDoc} */
@@ -271,4 +276,4 @@ public class GridCacheSqlQuery implements Message {
 
         return cp;
     }
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheTwoStepQuery.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheTwoStepQuery.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheTwoStepQuery.java
index da59c18..8dcba2f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheTwoStepQuery.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheTwoStepQuery.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.internal.processors.cache.query;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.Set;
 import org.apache.ignite.internal.util.tostring.GridToStringInclude;
@@ -45,24 +46,54 @@ public class GridCacheTwoStepQuery {
     private boolean explain;
 
     /** */
-    private Set<String> spaces;
+    private Collection<String> spaces;
 
     /** */
-    private final boolean skipMergeTbl;
+    private Set<String> schemas;
+
+    /** */
+    private Set<String> tbls;
+
+    /** */
+    private boolean distributedJoins;
+
+    /** */
+    private boolean skipMergeTbl;
+
+    /** */
+    private List<Integer> caches;
+
+    /** */
+    private List<Integer> extraCaches;
 
     /**
-     * @param spaces All spaces accessed in query.
-     * @param rdc Reduce query.
-     * @param skipMergeTbl {@code True} if reduce query can skip merge table creation and
-     *      get data directly from merge index.
+     * @param schemas Schema names in query.
+     * @param tbls Tables in query.
      */
-    public GridCacheTwoStepQuery(Set<String> spaces, GridCacheSqlQuery rdc, boolean skipMergeTbl) {
-        assert rdc != null;
+    public GridCacheTwoStepQuery(Set<String> schemas, Set<String> tbls) {
+        this.schemas = schemas;
+        this.tbls = tbls;
+    }
 
-        this.spaces = spaces;
-        this.rdc = rdc;
-        this.skipMergeTbl = skipMergeTbl;
+    /**
+     * Specify if distributed joins are enabled for this query.
+     *
+     * @param distributedJoins Distributed joins enabled.
+     */
+    public void distributedJoins(boolean distributedJoins) {
+        this.distributedJoins = distributedJoins;
+    }
+
+    /**
+     * Check if distributed joins are enabled for this query.
+     *
+     * @return {@code true} If distributed joind enabled.
+     */
+    public boolean distributedJoins() {
+        return distributedJoins;
     }
+
+
     /**
      * @return {@code True} if reduce query can skip merge table creation and get data directly from merge index.
      */
@@ -71,6 +102,13 @@ public class GridCacheTwoStepQuery {
     }
 
     /**
+     * @param skipMergeTbl Skip merge table.
+     */
+    public void skipMergeTable(boolean skipMergeTbl) {
+        this.skipMergeTbl = skipMergeTbl;
+    }
+
+    /**
      * @return If this is explain query.
      */
     public boolean explain() {
@@ -116,6 +154,13 @@ public class GridCacheTwoStepQuery {
     }
 
     /**
+     * @param rdc Reduce query.
+     */
+    public void reduceQuery(GridCacheSqlQuery rdc) {
+        this.rdc = rdc;
+    }
+
+    /**
      * @return Map queries.
      */
     public List<GridCacheSqlQuery> mapQueries() {
@@ -123,34 +168,84 @@ public class GridCacheTwoStepQuery {
     }
 
     /**
+     * @return Caches.
+     */
+    public List<Integer> caches() {
+        return caches;
+    }
+
+    /**
+     * @param caches Caches.
+     */
+    public void caches(List<Integer> caches) {
+        this.caches = caches;
+    }
+
+    /**
+     * @return Caches.
+     */
+    public List<Integer> extraCaches() {
+        return extraCaches;
+    }
+
+    /**
+     * @param extraCaches Caches.
+     */
+    public void extraCaches(List<Integer> extraCaches) {
+        this.extraCaches = extraCaches;
+    }
+
+    /**
      * @return Spaces.
      */
-    public Set<String> spaces() {
+    public Collection<String> spaces() {
         return spaces;
     }
 
     /**
      * @param spaces Spaces.
      */
-    public void spaces(Set<String> spaces) {
+    public void spaces(Collection<String> spaces) {
         this.spaces = spaces;
     }
 
     /**
+     * @return Schemas.
+     */
+    public Set<String> schemas() {
+        return schemas;
+    }
+
+    /**
      * @param args New arguments to copy with.
      * @return Copy.
      */
     public GridCacheTwoStepQuery copy(Object[] args) {
         assert !explain;
 
-        GridCacheTwoStepQuery cp = new GridCacheTwoStepQuery(spaces, rdc.copy(args), skipMergeTbl);
+        GridCacheTwoStepQuery cp = new GridCacheTwoStepQuery(schemas, tbls);
+
+        cp.caches = caches;
+        cp.extraCaches = extraCaches;
+        cp.spaces = spaces;
+        cp.rdc = rdc.copy(args);
+        cp.skipMergeTbl = skipMergeTbl;
         cp.pageSize = pageSize;
+        cp.distributedJoins = distributedJoins;
+
         for (int i = 0; i < mapQrys.size(); i++)
             cp.mapQrys.add(mapQrys.get(i).copy(args));
 
         return cp;
     }
 
+    /**
+     * @return Tables.
+     */
+    public Set<String> tables() {
+        return tbls;
+    }
+
     /** {@inheritDoc} */
     @Override public String toString() {
         return S.toString(GridCacheTwoStepQuery.class, this);

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessor.java
index b8ac301..c488b3f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessor.java
@@ -187,4 +187,10 @@ public interface IgniteCacheObjectProcessor extends GridProcessor {
      * @return Ignite binary interface.
      */
     public IgniteBinary binary();
+
+    /**
+     * @param keyType Key type name.
+     * @return Affinity filed name or {@code null}.
+     */
+    public String affinityField(String keyType);
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessorImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessorImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessorImpl.java
index 3203548..f62ce36 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessorImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessorImpl.java
@@ -83,6 +83,11 @@ public class IgniteCacheObjectProcessorImpl extends GridProcessorAdapter impleme
     }
 
     /** {@inheritDoc} */
+    @Override public String affinityField(String keyType) {
+        return null;
+    }
+
+    /** {@inheritDoc} */
     @Override public IgniteBinary binary() {
         return noOpBinary;
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/core/src/main/java/org/apache/ignite/internal/processors/closure/GridClosurePolicy.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/closure/GridClosurePolicy.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/closure/GridClosurePolicy.java
deleted file mode 100644
index c17cedd..0000000
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/closure/GridClosurePolicy.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.ignite.internal.processors.closure;
-
-import org.jetbrains.annotations.Nullable;
-
-/**
- * This enumeration defines different types of closure
- * processing by the closure processor.
- */
-public enum GridClosurePolicy {
-    /** Public execution pool. */
-    PUBLIC_POOL,
-
-    /** P2P execution pool. */
-    P2P_POOL,
-
-    /** System execution pool. */
-    SYSTEM_POOL,
-
-    /** IGFS pool. */
-    IGFS_POOL;
-
-    /** Enum values. */
-    private static final GridClosurePolicy[] VALS = values();
-
-    /**
-     * Efficiently gets enumerated value from its ordinal.
-     *
-     * @param ord Ordinal value.
-     * @return Enumerated value.
-     */
-    @Nullable public static GridClosurePolicy fromOrdinal(int ord) {
-        return ord >= 0 && ord < VALS.length ? VALS[ord] : null;
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/core/src/main/java/org/apache/ignite/internal/processors/closure/GridClosureProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/closure/GridClosureProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/closure/GridClosureProcessor.java
index f9b74c4..a4559c8 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/closure/GridClosureProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/closure/GridClosureProcessor.java
@@ -77,6 +77,9 @@ import org.jetbrains.annotations.Nullable;
 
 import static org.apache.ignite.compute.ComputeJobResultPolicy.FAILOVER;
 import static org.apache.ignite.compute.ComputeJobResultPolicy.REDUCE;
+import static org.apache.ignite.internal.managers.communication.GridIoPolicy.IGFS_POOL;
+import static org.apache.ignite.internal.managers.communication.GridIoPolicy.PUBLIC_POOL;
+import static org.apache.ignite.internal.managers.communication.GridIoPolicy.SYSTEM_POOL;
 import static org.apache.ignite.internal.processors.task.GridTaskThreadContextKey.TC_NO_FAILOVER;
 import static org.apache.ignite.internal.processors.task.GridTaskThreadContextKey.TC_SUBGRID;
 
@@ -87,15 +90,6 @@ public class GridClosureProcessor extends GridProcessorAdapter {
     /** Ignite version in which binarylizable versions of closures were introduced. */
     public static final IgniteProductVersion BINARYLIZABLE_CLOSURES_SINCE = IgniteProductVersion.fromString("1.6.0");
 
-    /** */
-    private final Executor sysPool;
-
-    /** */
-    private final Executor pubPool;
-
-    /** */
-    private final Executor igfsPool;
-
     /** Lock to control execution after stop. */
     private final GridSpinReadWriteLock busyLock = new GridSpinReadWriteLock();
 
@@ -107,10 +101,6 @@ public class GridClosureProcessor extends GridProcessorAdapter {
      */
     public GridClosureProcessor(GridKernalContext ctx) {
         super(ctx);
-
-        sysPool = ctx.getSystemExecutorService();
-        pubPool = ctx.getExecutorService();
-        igfsPool = ctx.getIgfsExecutorService();
     }
 
     /** {@inheritDoc} */
@@ -727,20 +717,8 @@ public class GridClosureProcessor extends GridProcessorAdapter {
      * @param plc Whether to get system or public pool.
      * @return Requested worker pool.
      */
-    private Executor pool(GridClosurePolicy plc) {
-        switch (plc) {
-            case PUBLIC_POOL:
-                return pubPool;
-
-            case SYSTEM_POOL:
-                return sysPool;
-
-            case IGFS_POOL:
-                return igfsPool;
-
-            default:
-                throw new IllegalArgumentException("Invalid closure execution policy: " + plc);
-        }
+    private Executor pool(byte plc) throws IgniteCheckedException {
+        return ctx.io().pool(plc);
     }
 
     /**
@@ -749,7 +727,7 @@ public class GridClosureProcessor extends GridProcessorAdapter {
      * @param plc Policy to choose executor pool.
      * @return Pool name.
      */
-    private String poolName(GridClosurePolicy plc) {
+    private String poolName(byte plc) {
         switch (plc) {
             case PUBLIC_POOL:
                 return "public";
@@ -761,7 +739,7 @@ public class GridClosureProcessor extends GridProcessorAdapter {
                 return "igfs";
 
             default:
-                throw new IllegalArgumentException("Invalid closure execution policy: " + plc);
+                return "unknown";
         }
     }
 
@@ -772,7 +750,7 @@ public class GridClosureProcessor extends GridProcessorAdapter {
      * @throws IgniteCheckedException Thrown in case of any errors.
      */
     private IgniteInternalFuture<?> runLocal(@Nullable final Runnable c, boolean sys) throws IgniteCheckedException {
-        return runLocal(c, sys ? GridClosurePolicy.SYSTEM_POOL : GridClosurePolicy.PUBLIC_POOL);
+        return runLocal(c, sys ? SYSTEM_POOL : PUBLIC_POOL);
     }
 
     /**
@@ -781,7 +759,7 @@ public class GridClosureProcessor extends GridProcessorAdapter {
      * @return Future.
      * @throws IgniteCheckedException Thrown in case of any errors.
      */
-    private IgniteInternalFuture<?> runLocal(@Nullable final Runnable c, GridClosurePolicy plc) throws IgniteCheckedException {
+    public IgniteInternalFuture<?> runLocal(@Nullable final Runnable c, byte plc) throws IgniteCheckedException {
         if (c == null)
             return new GridFinishedFuture();
 
@@ -857,7 +835,7 @@ public class GridClosureProcessor extends GridProcessorAdapter {
      * @return Future.
      */
     public IgniteInternalFuture<?> runLocalSafe(Runnable c, boolean sys) {
-        return runLocalSafe(c, sys ? GridClosurePolicy.SYSTEM_POOL : GridClosurePolicy.PUBLIC_POOL);
+        return runLocalSafe(c, sys ? SYSTEM_POOL : PUBLIC_POOL);
     }
 
     /**
@@ -868,7 +846,7 @@ public class GridClosureProcessor extends GridProcessorAdapter {
      * @param plc Policy to choose executor pool.
      * @return Future.
      */
-    public IgniteInternalFuture<?> runLocalSafe(Runnable c, GridClosurePolicy plc) {
+    public IgniteInternalFuture<?> runLocalSafe(Runnable c, byte plc) {
         try {
             return runLocal(c, plc);
         }
@@ -912,7 +890,7 @@ public class GridClosureProcessor extends GridProcessorAdapter {
      * @throws IgniteCheckedException Thrown in case of any errors.
      */
     private <R> IgniteInternalFuture<R> callLocal(@Nullable final Callable<R> c, boolean sys) throws IgniteCheckedException {
-        return callLocal(c, sys ? GridClosurePolicy.SYSTEM_POOL : GridClosurePolicy.PUBLIC_POOL);
+        return callLocal(c, sys ? SYSTEM_POOL : PUBLIC_POOL);
     }
 
     /**
@@ -922,7 +900,7 @@ public class GridClosureProcessor extends GridProcessorAdapter {
      * @return Future.
      * @throws IgniteCheckedException Thrown in case of any errors.
      */
-    private <R> IgniteInternalFuture<R> callLocal(@Nullable final Callable<R> c, GridClosurePolicy plc) throws IgniteCheckedException {
+    private <R> IgniteInternalFuture<R> callLocal(@Nullable final Callable<R> c, byte plc) throws IgniteCheckedException {
         if (c == null)
             return new GridFinishedFuture<>();
 
@@ -996,7 +974,7 @@ public class GridClosureProcessor extends GridProcessorAdapter {
      * @return Future.
      */
     public <R> IgniteInternalFuture<R> callLocalSafe(Callable<R> c, boolean sys) {
-        return callLocalSafe(c, sys ? GridClosurePolicy.SYSTEM_POOL : GridClosurePolicy.PUBLIC_POOL);
+        return callLocalSafe(c, sys ? SYSTEM_POOL : PUBLIC_POOL);
     }
 
     /**
@@ -1007,7 +985,7 @@ public class GridClosureProcessor extends GridProcessorAdapter {
      * @param plc Policy to choose executor pool.
      * @return Future.
      */
-    public <R> IgniteInternalFuture<R> callLocalSafe(Callable<R> c, GridClosurePolicy plc) {
+    public <R> IgniteInternalFuture<R> callLocalSafe(Callable<R> c, byte plc) {
         try {
             return callLocal(c, plc);
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexing.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexing.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexing.java
index 7697a12..643cb8c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexing.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexing.java
@@ -29,7 +29,6 @@ import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cache.CacheObject;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
-import org.apache.ignite.internal.processors.cache.query.GridCacheTwoStepQuery;
 import org.apache.ignite.internal.util.GridSpinBusyLock;
 import org.apache.ignite.internal.util.lang.GridCloseableIterator;
 import org.apache.ignite.lang.IgniteBiTuple;
@@ -58,17 +57,6 @@ public interface GridQueryIndexing {
     public void stop() throws IgniteCheckedException;
 
     /**
-     * Runs two step query.
-     *
-     * @param cctx Cache context.
-     * @param qry Query.
-     * @param keepCacheObjects If {@code true}, cache objects representation will be preserved.
-     * @return Cursor.
-     */
-    public Iterable<List<?>> queryTwoStep(GridCacheContext<?,?> cctx, GridCacheTwoStepQuery qry,
-        boolean keepCacheObjects);
-
-    /**
      * Parses SQL query into two step query and executes it.
      *
      * @param cctx Cache context.
@@ -92,12 +80,13 @@ public interface GridQueryIndexing {
      * @param spaceName Space name.
      * @param qry Query.
      * @param params Query parameters.
-     * @param filters Space name and key filters.
+     * @param filter Space name and key filter.
+     * @param enforceJoinOrder Enforce join order of tables in the query.
      * @return Query result.
      * @throws IgniteCheckedException If failed.
      */
-    public GridQueryFieldsResult queryFields(@Nullable String spaceName, String qry,
-        Collection<Object> params, IndexingQueryFilter filters) throws IgniteCheckedException;
+    public GridQueryFieldsResult queryLocalSqlFields(@Nullable String spaceName, String qry,
+        Collection<Object> params, IndexingQueryFilter filter, boolean enforceJoinOrder) throws IgniteCheckedException;
 
     /**
      * Executes regular query.
@@ -106,12 +95,12 @@ public interface GridQueryIndexing {
      * @param qry Query.
      * @param params Query parameters.
      * @param type Query return type.
-     * @param filters Space name and key filters.
+     * @param filter Space name and key filter.
      * @return Queried rows.
      * @throws IgniteCheckedException If failed.
      */
-    public <K, V> GridCloseableIterator<IgniteBiTuple<K, V>> query(@Nullable String spaceName, String qry,
-        Collection<Object> params, GridQueryTypeDescriptor type, IndexingQueryFilter filters) throws IgniteCheckedException;
+    public <K, V> GridCloseableIterator<IgniteBiTuple<K, V>> queryLocalSql(@Nullable String spaceName, String qry,
+        Collection<Object> params, GridQueryTypeDescriptor type, IndexingQueryFilter filter) throws IgniteCheckedException;
 
     /**
      * Executes text query.
@@ -119,35 +108,24 @@ public interface GridQueryIndexing {
      * @param spaceName Space name.
      * @param qry Text query.
      * @param type Query return type.
-     * @param filters Space name and key filter.
+     * @param filter Space name and key filter.
      * @return Queried rows.
      * @throws IgniteCheckedException If failed.
      */
-    public <K, V> GridCloseableIterator<IgniteBiTuple<K, V>> queryText(@Nullable String spaceName, String qry,
-        GridQueryTypeDescriptor type, IndexingQueryFilter filters) throws IgniteCheckedException;
-
-    /**
-     * Gets size of index for given type or -1 if it is a unknown type.
-     *
-     * @param spaceName Space name.
-     * @param desc Type descriptor.
-     * @param filters Filters.
-     * @return Objects number.
-     * @throws IgniteCheckedException If failed.
-     */
-    public long size(@Nullable String spaceName, GridQueryTypeDescriptor desc, IndexingQueryFilter filters)
-        throws IgniteCheckedException;
+    public <K, V> GridCloseableIterator<IgniteBiTuple<K, V>> queryLocalText(@Nullable String spaceName, String qry,
+        GridQueryTypeDescriptor type, IndexingQueryFilter filter) throws IgniteCheckedException;
 
     /**
      * Registers cache.
      *
+     * @param cctx Cache context.
      * @param ccfg Cache configuration.
      * @throws IgniteCheckedException If failed.
      */
-    public void registerCache(CacheConfiguration<?,?> ccfg) throws IgniteCheckedException;
+    public void registerCache(GridCacheContext<?,?> cctx, CacheConfiguration<?,?> ccfg) throws IgniteCheckedException;
 
     /**
-     * Deregisters cache.
+     * Unregisters cache.
      *
      * @param ccfg Cache configuration.
      * @throws IgniteCheckedException If failed to drop cache schema.
@@ -228,12 +206,11 @@ public interface GridQueryIndexing {
     /**
      * Returns backup filter.
      *
-     * @param caches List of caches.
      * @param topVer Topology version.
      * @param parts Partitions.
      * @return Backup filter.
      */
-    public IndexingQueryFilter backupFilter(List<String> caches, AffinityTopologyVersion topVer, int[] parts);
+    public IndexingQueryFilter backupFilter(AffinityTopologyVersion topVer, int[] parts);
 
     /**
      * Client disconnected callback.

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
index a42eb98..04c6cb0 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
@@ -28,6 +28,7 @@ import org.apache.ignite.cache.CacheTypeMetadata;
 import org.apache.ignite.cache.QueryEntity;
 import org.apache.ignite.cache.QueryIndex;
 import org.apache.ignite.cache.QueryIndexType;
+import org.apache.ignite.cache.affinity.AffinityKeyMapper;
 import org.apache.ignite.cache.query.QueryCursor;
 import org.apache.ignite.cache.query.SqlFieldsQuery;
 import org.apache.ignite.cache.query.SqlQuery;
@@ -42,11 +43,11 @@ import org.apache.ignite.internal.processors.cache.CacheEntryImpl;
 import org.apache.ignite.internal.processors.cache.CacheObject;
 import org.apache.ignite.internal.processors.cache.CacheObjectContext;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.GridCacheDefaultAffinityKeyMapper;
 import org.apache.ignite.internal.processors.cache.QueryCursorImpl;
 import org.apache.ignite.internal.processors.cache.binary.CacheObjectBinaryProcessorImpl;
 import org.apache.ignite.internal.processors.cache.query.CacheQueryFuture;
 import org.apache.ignite.internal.processors.cache.query.CacheQueryType;
-import org.apache.ignite.internal.processors.cache.query.GridCacheTwoStepQuery;
 import org.apache.ignite.internal.util.GridSpinBusyLock;
 import org.apache.ignite.internal.util.future.GridCompoundFuture;
 import org.apache.ignite.internal.util.future.GridFinishedFuture;
@@ -191,11 +192,13 @@ public class GridQueryProcessor extends GridProcessorAdapter {
     }
 
     /**
-     * @param ccfg Cache configuration.
+     * @param cctx Cache context.
      * @throws IgniteCheckedException If failed.
      */
-    public void initializeCache(CacheConfiguration<?, ?> ccfg) throws IgniteCheckedException {
-        idx.registerCache(ccfg);
+    private void initializeCache(GridCacheContext<?, ?> cctx) throws IgniteCheckedException {
+        CacheConfiguration<?,?> ccfg = cctx.config();
+
+        idx.registerCache(cctx, cctx.config());
 
         try {
             List<Class<?>> mustDeserializeClss = null;
@@ -275,10 +278,28 @@ public class GridQueryProcessor extends GridProcessorAdapter {
 
                         if (valCls != null)
                             altTypeId = new TypeId(ccfg.getName(), valCls);
+
+                        if (!cctx.customAffinityMapper() && qryEntity.getKeyType() != null) {
+                            // Need to setup affinity key for distributed joins.
+                            String affField = ctx.cacheObjects().affinityField(qryEntity.getKeyType());
+
+                            if (affField != null)
+                                desc.affinityKey(affField);
+                        }
                     }
                     else {
                         processClassMeta(qryEntity, desc, coCtx);
 
+                        AffinityKeyMapper keyMapper = cctx.config().getAffinityMapper();
+
+                        if (keyMapper instanceof GridCacheDefaultAffinityKeyMapper) {
+                            String affField =
+                                ((GridCacheDefaultAffinityKeyMapper)keyMapper).affinityKeyPropertyName(desc.keyCls);
+
+                            if (affField != null)
+                                desc.affinityKey(affField);
+                        }
+
                         typeId = new TypeId(ccfg.getName(), valCls);
                         altTypeId = new TypeId(ccfg.getName(), ctx.cacheObjects().typeId(qryEntity.getValueType()));
                     }
@@ -290,7 +311,6 @@ public class GridQueryProcessor extends GridProcessorAdapter {
                         types.put(altTypeId, desc);
 
                     desc.registered(idx.registerType(ccfg.getName(), desc));
-
                 }
             }
 
@@ -460,7 +480,7 @@ public class GridQueryProcessor extends GridProcessorAdapter {
             return;
 
         try {
-            initializeCache(cctx.config());
+            initializeCache(cctx);
         }
         finally {
             busyLock.leaveBusy();
@@ -501,33 +521,6 @@ public class GridQueryProcessor extends GridProcessorAdapter {
     }
 
     /**
-     * Returns number of objects of given type for given space of spi.
-     *
-     * @param space Space.
-     * @param valType Value type.
-     * @return Objects number or -1 if this type is unknown for given SPI and space.
-     * @throws IgniteCheckedException If failed.
-     */
-    public long size(@Nullable String space, Class<?> valType) throws IgniteCheckedException {
-        checkEnabled();
-
-        if (!busyLock.enterBusy())
-            throw new IllegalStateException("Failed to get space size (grid is stopping).");
-
-        try {
-            TypeDescriptor desc = types.get(new TypeId(space, valType));
-
-            if (desc == null || !desc.registered())
-                return -1;
-
-            return idx.size(space, desc, null);
-        }
-        finally {
-            busyLock.leaveBusy();
-        }
-    }
-
-    /**
      * Rebuilds all search indexes of given value type for given space of spi.
      *
      * @param space Space.
@@ -657,7 +650,7 @@ public class GridQueryProcessor extends GridProcessorAdapter {
             return;
 
         if (!busyLock.enterBusy())
-            throw new IllegalStateException("Failed to write to index (grid is stopping).");
+            return;
 
         try {
             if (coctx == null)
@@ -723,74 +716,6 @@ public class GridQueryProcessor extends GridProcessorAdapter {
     }
 
     /**
-     * @param space Space.
-     * @param clause Clause.
-     * @param params Parameters collection.
-     * @param resType Result type.
-     * @param filters Filters.
-     * @return Key/value rows.
-     * @throws IgniteCheckedException If failed.
-     */
-    @SuppressWarnings("unchecked")
-    public <K, V> GridCloseableIterator<IgniteBiTuple<K, V>> query(final String space, final String clause,
-        final Collection<Object> params, final String resType, final IndexingQueryFilter filters)
-        throws IgniteCheckedException {
-        checkEnabled();
-
-        if (!busyLock.enterBusy())
-            throw new IllegalStateException("Failed to execute query (grid is stopping).");
-
-        try {
-            final GridCacheContext<?, ?> cctx = ctx.cache().internalCache(space).context();
-
-            return executeQuery(cctx, new IgniteOutClosureX<GridCloseableIterator<IgniteBiTuple<K, V>>>() {
-                @Override public GridCloseableIterator<IgniteBiTuple<K, V>> applyx() throws IgniteCheckedException {
-                    TypeDescriptor type = typesByName.get(new TypeName(space, resType));
-
-                    if (type == null || !type.registered())
-                        throw new CacheException("Failed to find SQL table for type: " + resType);
-
-                    return idx.query(space, clause, params, type, filters);
-                }
-            }, false);
-        }
-        finally {
-            busyLock.leaveBusy();
-        }
-    }
-
-    /**
-     * @param space Space name.
-     * @param qry Query.
-     * @return Cursor.
-     */
-    public Iterable<List<?>> queryTwoStep(String space, final GridCacheTwoStepQuery qry) {
-        checkxEnabled();
-
-        if (!busyLock.enterBusy())
-            throw new IllegalStateException("Failed to execute query (grid is stopping).");
-
-        try {
-            final GridCacheContext<Object, Object> cctx = ctx.cache().internalCache(space).context();
-
-            return executeQuery(cctx, new IgniteOutClosureX<Iterable<List<?>>>() {
-                @Override public Iterable<List<?>> applyx() throws IgniteCheckedException {
-                    return idx.queryTwoStep(
-                        cctx,
-                        qry,
-                        cctx.keepBinary());
-                }
-            }, false);
-        }
-        catch (IgniteCheckedException e) {
-            throw new IgniteException(e);
-        }
-        finally {
-            busyLock.leaveBusy();
-        }
-    }
-
-    /**
      * @param cctx Cache context.
      * @param qry Query.
      * @return Cursor.
@@ -845,6 +770,7 @@ public class GridQueryProcessor extends GridProcessorAdapter {
     /**
      * @param cctx Cache context.
      * @param qry Query.
+     * @param keepBinary Keep binary flag.
      * @return Cursor.
      */
     public <K, V> Iterator<Cache.Entry<K, V>> queryLocal(
@@ -873,12 +799,12 @@ public class GridQueryProcessor extends GridProcessorAdapter {
                         if (typeDesc == null || !typeDesc.registered())
                             throw new CacheException("Failed to find SQL table for type: " + type);
 
-                        final GridCloseableIterator<IgniteBiTuple<K, V>> i = idx.query(
+                        final GridCloseableIterator<IgniteBiTuple<K, V>> i = idx.queryLocalSql(
                             space,
                             sqlQry,
                             F.asList(params),
                             typeDesc,
-                            idx.backupFilter(null, null, null));
+                            idx.backupFilter(null, null));
 
                         sendQueryExecutedEvent(
                             sqlQry,
@@ -963,8 +889,8 @@ public class GridQueryProcessor extends GridProcessorAdapter {
                     String sql = qry.getSql();
                     Object[] args = qry.getArgs();
 
-                    final GridQueryFieldsResult res = idx.queryFields(space, sql, F.asList(args),
-                        idx.backupFilter(null, null, null));
+                    final GridQueryFieldsResult res = idx.queryLocalSqlFields(space, sql, F.asList(args),
+                        idx.backupFilter(null, null), qry.isEnforceJoinOrder());
 
                     sendQueryExecutedEvent(sql, args);
 
@@ -1118,7 +1044,7 @@ public class GridQueryProcessor extends GridProcessorAdapter {
                     if (type == null || !type.registered())
                         throw new CacheException("Failed to find SQL table for type: " + resType);
 
-                    return idx.queryText(
+                    return idx.queryLocalText(
                         space,
                         clause,
                         type,
@@ -1132,35 +1058,6 @@ public class GridQueryProcessor extends GridProcessorAdapter {
     }
 
     /**
-     * @param space Space name.
-     * @param clause Clause.
-     * @param params Parameters collection.
-     * @param filters Key and value filters.
-     * @return Field rows.
-     * @throws IgniteCheckedException If failed.
-     */
-    public GridQueryFieldsResult queryFields(@Nullable final String space, final String clause,
-        final Collection<Object> params, final IndexingQueryFilter filters) throws IgniteCheckedException {
-        checkEnabled();
-
-        if (!busyLock.enterBusy())
-            throw new IllegalStateException("Failed to execute query (grid is stopping).");
-
-        try {
-            final GridCacheContext<?, ?> cctx = ctx.cache().internalCache(space).context();
-
-            return executeQuery(cctx, new IgniteOutClosureX<GridQueryFieldsResult>() {
-                @Override public GridQueryFieldsResult applyx() throws IgniteCheckedException {
-                    return idx.queryFields(space, clause, params, filters);
-                }
-            }, false);
-        }
-        finally {
-            busyLock.leaveBusy();
-        }
-    }
-
-    /**
      * Will be called when entry for key will be swapped.
      *
      * @param spaceName Space name.
@@ -1523,7 +1420,6 @@ public class GridQueryProcessor extends GridProcessorAdapter {
                 aliases,
                 coCtx);
 
-
             d.addProperty(prop, false);
         }
 
@@ -1770,7 +1666,7 @@ public class GridQueryProcessor extends GridProcessorAdapter {
             res = clo.apply();
 
             if (res instanceof CacheQueryFuture) {
-                CacheQueryFuture fut = (CacheQueryFuture) res;
+                CacheQueryFuture fut = (CacheQueryFuture)res;
 
                 err = fut.error();
             }
@@ -1782,6 +1678,11 @@ public class GridQueryProcessor extends GridProcessorAdapter {
 
             throw (IgniteCheckedException)err;
         }
+        catch (CacheException e) {
+            err = e;
+
+            throw e;
+        }
         catch (Exception e) {
             err = e;
 
@@ -2096,6 +1997,9 @@ public class GridQueryProcessor extends GridProcessorAdapter {
         /** */
         private boolean valTextIdx;
 
+        /** */
+        private String affKey;
+
         /** SPI can decide not to register this type. */
         private boolean registered;
 
@@ -2265,6 +2169,18 @@ public class GridQueryProcessor extends GridProcessorAdapter {
         }
 
         /** {@inheritDoc} */
+        @Override public String affinityKey() {
+            return affKey;
+        }
+
+        /**
+         * @param affKey Affinity key field.
+         */
+        void affinityKey(String affKey) {
+            this.affKey = affKey;
+        }
+
+        /** {@inheritDoc} */
         @Override public String toString() {
             return S.toString(TypeDescriptor.class, this);
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryTypeDescriptor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryTypeDescriptor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryTypeDescriptor.java
index 45919ef..b636841 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryTypeDescriptor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryTypeDescriptor.java
@@ -82,4 +82,11 @@ public interface GridQueryTypeDescriptor {
      * @return If string representation of value should be full-text indexed.
      */
     public boolean valueTextIndex();
+
+    /**
+     * Returns affinity key field name or {@code null} for default.
+     *
+     * @return Affinity key.
+     */
+    public String affinityKey();
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/core/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/messages/GridQueryCancelRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/messages/GridQueryCancelRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/messages/GridQueryCancelRequest.java
index ecc0abd..6706ab9 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/messages/GridQueryCancelRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/messages/GridQueryCancelRequest.java
@@ -117,4 +117,4 @@ public class GridQueryCancelRequest implements Message {
     @Override public byte fieldsCount() {
         return 1;
     }
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/core/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/messages/GridQueryFailResponse.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/messages/GridQueryFailResponse.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/messages/GridQueryFailResponse.java
index 499438d..bd9f7d1 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/messages/GridQueryFailResponse.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/messages/GridQueryFailResponse.java
@@ -143,4 +143,4 @@ public class GridQueryFailResponse implements Message {
     @Override public byte fieldsCount() {
         return 2;
     }
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/core/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/messages/GridQueryNextPageRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/messages/GridQueryNextPageRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/messages/GridQueryNextPageRequest.java
index 84cb57e..1feff5a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/messages/GridQueryNextPageRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/messages/GridQueryNextPageRequest.java
@@ -170,4 +170,4 @@ public class GridQueryNextPageRequest implements Message {
     @Override public byte fieldsCount() {
         return 3;
     }
-}
\ No newline at end of file
+}


[20/50] [abbrv] ignite git commit: ignite-3399 - minor improvements

Posted by vo...@apache.org.
ignite-3399 - minor improvements


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

Branch: refs/heads/ignite-3553
Commit: df1490e18e2271969ef6857d40e77427958e28ac
Parents: df5a842
Author: Sergi Vladykin <se...@gmail.com>
Authored: Mon Jul 25 09:08:00 2016 +0300
Committer: Sergi Vladykin <se...@gmail.com>
Committed: Mon Jul 25 09:08:00 2016 +0300

----------------------------------------------------------------------
 .../org/apache/ignite/internal/util/IgniteUtils.java  | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/df1490e1/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
index 31128ca..b58e3b3 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
@@ -1373,17 +1373,19 @@ public abstract class IgniteUtils {
      * @param includePrimitiveTypes Whether class resolution should include primitive types (i.e. "int" will resolve to int.class if flag is set)
      * @return Class or default given class if it can't be found.
      */
-    @Nullable
-    public static Class<?> classForName(@Nullable String cls, @Nullable Class<?> dflt, boolean includePrimitiveTypes) {
+    @Nullable public static Class<?> classForName(
+        @Nullable String cls,
+        @Nullable Class<?> dflt,
+        boolean includePrimitiveTypes
+    ) {
         Class<?> clazz;
         if (cls == null)
             clazz = dflt;
-        else if (includePrimitiveTypes && primitiveMap.containsKey(cls))
-            clazz = primitiveMap.get(cls);
-        else {
+        else if (!includePrimitiveTypes || cls.length() > 7 || (clazz = primitiveMap.get(cls)) == null) {
             try {
                 clazz = Class.forName(cls);
-            } catch (ClassNotFoundException ignore) {
+            }
+            catch (ClassNotFoundException ignore) {
                 clazz = dflt;
             }
         }


[29/50] [abbrv] ignite git commit: ignite-3560 Offheap resources cleanup on H2 table destroy.

Posted by vo...@apache.org.
ignite-3560 Offheap resources cleanup on H2 table destroy.


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

Branch: refs/heads/ignite-3553
Commit: 44cabd84152b5d956666b6c906eb43763ab7d72f
Parents: 856b536
Author: sboikov <sb...@gridgain.com>
Authored: Tue Jul 26 10:41:50 2016 +0300
Committer: sboikov <sb...@gridgain.com>
Committed: Tue Jul 26 10:41:50 2016 +0300

----------------------------------------------------------------------
 .../processors/query/h2/IgniteH2Indexing.java   |  31 ++--
 .../cache/CacheIndexingOffheapCleanupTest.java  | 178 +++++++++++++++++++
 .../IgniteCacheWithIndexingTestSuite.java       |   2 +
 3 files changed, 196 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/44cabd84/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
index 535881e..e1b21e2 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
@@ -735,8 +735,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
             U.close(stmt, log);
         }
 
-        if (tbl.luceneIdx != null)
-            U.closeQuiet(tbl.luceneIdx);
+        tbl.onDrop();
 
         tbl.schema.tbls.remove(tbl.name());
     }
@@ -1816,12 +1815,8 @@ public class IgniteH2Indexing implements GridQueryIndexing {
 
 //        unregisterMBean(); TODO https://issues.apache.org/jira/browse/IGNITE-2139
 
-        for (Schema schema : schemas.values()) {
-            for (TableDescriptor desc : schema.tbls.values()) {
-                if (desc.luceneIdx != null)
-                    U.closeQuiet(desc.luceneIdx);
-            }
-        }
+        for (Schema schema : schemas.values())
+            schema.onDrop();
 
         for (Connection c : conns)
             U.close(c, log);
@@ -2436,6 +2431,17 @@ public class IgniteH2Indexing implements GridQueryIndexing {
         }
 
         /**
+         *
+         */
+        void onDrop() {
+            dataTables.remove(tbl.identifier(), tbl);
+
+            tbl.destroy();
+
+            U.closeQuiet(luceneIdx);
+        }
+
+        /**
          * @param tbl Table.
          * @param idxName Index name.
          * @param cols Columns.
@@ -2668,13 +2674,8 @@ public class IgniteH2Indexing implements GridQueryIndexing {
          * Called after the schema was dropped.
          */
         public void onDrop() {
-            for (TableDescriptor tblDesc : tbls.values()) {
-                GridH2Table tbl = tblDesc.tbl;
-
-                dataTables.remove(tbl.identifier(), tbl);
-
-                tbl.destroy();
-            }
+            for (TableDescriptor tblDesc : tbls.values())
+                tblDesc.onDrop();
         }
     }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/44cabd84/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/CacheIndexingOffheapCleanupTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/CacheIndexingOffheapCleanupTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/CacheIndexingOffheapCleanupTest.java
new file mode 100644
index 0000000..3d7e30a
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/CacheIndexingOffheapCleanupTest.java
@@ -0,0 +1,178 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.processors.cache;
+
+import java.util.Map;
+import java.util.concurrent.ThreadLocalRandom;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.query.annotations.QuerySqlField;
+import org.apache.ignite.cache.query.annotations.QueryTextField;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.internal.IgniteKernal;
+import org.apache.ignite.internal.util.offheap.unsafe.GridUnsafeMemory;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+import static org.apache.ignite.cache.CacheMemoryMode.OFFHEAP_TIERED;
+
+/**
+ *
+ */
+public class CacheIndexingOffheapCleanupTest extends GridCommonAbstractTest {
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() throws Exception {
+        startGrid(0);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        stopAllGrids();
+
+        super.afterTest();
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCacheDestroy() throws Exception {
+        Ignite ignite = ignite(0);
+
+        for (int i = 0; i < 5; i++) {
+            log.info("Iteration: " + i);
+
+            IgniteCache cache = ignite.createCache(cacheConfiguration());
+
+            for (int k = 0; k < 100; k++)
+                cache.put(k, new TestType());
+
+            GridUnsafeMemory mem = schemaMemory(ignite, cache.getName());
+
+            assertTrue(mem.allocatedSize() > 0);
+
+            ignite.destroyCache(cache.getName());
+
+            assertEquals(0, mem.allocatedSize());
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testStopNode() throws Exception {
+        Ignite ignite = ignite(0);
+
+        IgniteCache cache = ignite.createCache(cacheConfiguration());
+
+        for (int k = 0; k < 100; k++)
+            cache.put(k, new TestType());
+
+        GridUnsafeMemory mem = schemaMemory(ignite, cache.getName());
+
+        assertTrue(mem.allocatedSize() > 0);
+
+        stopGrid(0);
+
+        assertEquals(0, mem.allocatedSize());
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testUndeploy() throws Exception {
+        Ignite ignite = ignite(0);
+
+        IgniteCache cache = ignite.createCache(cacheConfiguration());
+
+        for (int k = 0; k < 100; k++)
+            cache.put(k, new TestType());
+
+        GridUnsafeMemory mem = schemaMemory(ignite, cache.getName());
+
+        assertTrue(mem.allocatedSize() > 0);
+
+        ((IgniteKernal)ignite).context().query().onUndeploy("cache", U.detectClassLoader(TestType.class));
+
+        assertEquals(0, mem.allocatedSize());
+    }
+
+    /**
+     * @param ignite Node.
+     * @param cacheName Cache name.
+     * @return Memory.
+     */
+    private GridUnsafeMemory schemaMemory(Ignite ignite, String cacheName) {
+        Map<String, Object> schemas = GridTestUtils.getFieldValue(((IgniteKernal)ignite).context().query(),
+            "idx",
+            "schemas");
+
+        assertNotNull(schemas);
+
+        Object schema = schemas.get("\"" + cacheName + "\"");
+
+        assertNotNull(schema);
+
+        GridUnsafeMemory mem = GridTestUtils.getFieldValue(schema, "offheap");
+
+        assertNotNull(mem);
+
+        return mem;
+    }
+
+    /**
+     * @return Cache configuration.
+     */
+    private CacheConfiguration cacheConfiguration() {
+        CacheConfiguration ccfg = new CacheConfiguration();
+
+        ccfg.setName("cache");
+        ccfg.setMemoryMode(OFFHEAP_TIERED);
+        ccfg.setIndexedTypes(Integer.class, TestType.class);
+
+        return ccfg;
+    }
+
+    /**
+     *
+     */
+    static class TestType {
+        /** */
+        @QuerySqlField(index = true)
+        private int v1;
+
+        /** */
+        @QuerySqlField(index = true)
+        private String v2;
+
+        /** */
+        @QueryTextField
+        private String v3;
+
+        /**
+         *
+         */
+        public TestType() {
+            int v = ThreadLocalRandom.current().nextInt();
+
+            v1 = v;
+            v2 = String.valueOf(v);
+            v3 = v2;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/44cabd84/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheWithIndexingTestSuite.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheWithIndexingTestSuite.java b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheWithIndexingTestSuite.java
index a85b7a6..5cdd744 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheWithIndexingTestSuite.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheWithIndexingTestSuite.java
@@ -20,6 +20,7 @@ package org.apache.ignite.testsuites;
 import junit.framework.TestSuite;
 import org.apache.ignite.internal.processors.cache.CacheConfigurationP2PTest;
 import org.apache.ignite.internal.processors.cache.CacheIndexStreamerTest;
+import org.apache.ignite.internal.processors.cache.CacheIndexingOffheapCleanupTest;
 import org.apache.ignite.internal.processors.cache.CacheOperationsWithExpirationTest;
 import org.apache.ignite.internal.processors.cache.CacheRandomOperationsMultithreadedTest;
 import org.apache.ignite.internal.processors.cache.GridCacheOffHeapAndSwapSelfTest;
@@ -76,6 +77,7 @@ public class IgniteCacheWithIndexingTestSuite extends TestSuite {
         suite.addTestSuite(CacheRandomOperationsMultithreadedTest.class);
         suite.addTestSuite(IgniteCacheStarvationOnRebalanceTest.class);
         suite.addTestSuite(CacheOperationsWithExpirationTest.class);
+        suite.addTestSuite(CacheIndexingOffheapCleanupTest.class);
 
         return suite;
     }


[07/50] [abbrv] ignite git commit: ignite-1232 Distributed SQL joins implementation

Posted by vo...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/GridIndexingSpiAbstractSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/GridIndexingSpiAbstractSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/GridIndexingSpiAbstractSelfTest.java
index 0da71c8..fae3188 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/GridIndexingSpiAbstractSelfTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/GridIndexingSpiAbstractSelfTest.java
@@ -44,7 +44,7 @@ import org.apache.ignite.spi.IgniteSpiException;
 import org.apache.ignite.testframework.GridStringLogger;
 import org.apache.ignite.testframework.GridTestUtils;
 import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
-import org.h2.util.Utils;
+import org.h2.util.JdbcUtils;
 import org.jetbrains.annotations.Nullable;
 
 /**
@@ -101,8 +101,8 @@ public abstract class GridIndexingSpiAbstractSelfTest extends GridCommonAbstract
     protected void startIndexing(IgniteH2Indexing spi) throws Exception {
         spi.start(null, null);
 
-        spi.registerCache(cacheCfg("A"));
-        spi.registerCache(cacheCfg("B"));
+        spi.registerCache(null, cacheCfg("A"));
+        spi.registerCache(null, cacheCfg("B"));
     }
 
     private CacheConfiguration cacheCfg(String name) {
@@ -199,38 +199,38 @@ public abstract class GridIndexingSpiAbstractSelfTest extends GridCommonAbstract
     public void testSpi() throws Exception {
         IgniteH2Indexing spi = getIndexing();
 
-        assertEquals(-1, spi.size(typeAA.space(), typeAA, null));
-        assertEquals(-1, spi.size(typeAB.space(), typeAB, null));
-        assertEquals(-1, spi.size(typeBA.space(), typeBA, null));
+        assertEquals(-1, spi.size(typeAA.space(), typeAA));
+        assertEquals(-1, spi.size(typeAB.space(), typeAB));
+        assertEquals(-1, spi.size(typeBA.space(), typeBA));
 
         spi.registerType(typeAA.space(), typeAA);
 
-        assertEquals(0, spi.size(typeAA.space(), typeAA, null));
-        assertEquals(-1, spi.size(typeAB.space(), typeAB, null));
-        assertEquals(-1, spi.size(typeBA.space(), typeBA, null));
+        assertEquals(0, spi.size(typeAA.space(), typeAA));
+        assertEquals(-1, spi.size(typeAB.space(), typeAB));
+        assertEquals(-1, spi.size(typeBA.space(), typeBA));
 
         spi.registerType(typeAB.space(), typeAB);
 
-        assertEquals(0, spi.size(typeAA.space(), typeAA, null));
-        assertEquals(0, spi.size(typeAB.space(), typeAB, null));
-        assertEquals(-1, spi.size(typeBA.space(), typeBA, null));
+        assertEquals(0, spi.size(typeAA.space(), typeAA));
+        assertEquals(0, spi.size(typeAB.space(), typeAB));
+        assertEquals(-1, spi.size(typeBA.space(), typeBA));
 
         spi.registerType(typeBA.space(), typeBA);
 
         // Initially all is empty.
-        assertEquals(0, spi.size(typeAA.space(), typeAA, null));
-        assertEquals(0, spi.size(typeAB.space(), typeAB, null));
-        assertEquals(0, spi.size(typeBA.space(), typeBA, null));
+        assertEquals(0, spi.size(typeAA.space(), typeAA));
+        assertEquals(0, spi.size(typeAB.space(), typeAB));
+        assertEquals(0, spi.size(typeBA.space(), typeBA));
 
-        assertFalse(spi.query(typeAA.space(), "select * from A.A", Collections.emptySet(), typeAA, null).hasNext());
-        assertFalse(spi.query(typeAB.space(), "select * from A.B", Collections.emptySet(), typeAB, null).hasNext());
-        assertFalse(spi.query(typeBA.space(), "select * from B.A", Collections.emptySet(), typeBA, null).hasNext());
+        assertFalse(spi.queryLocalSql(typeAA.space(), "select * from A.A", Collections.emptySet(), typeAA, null).hasNext());
+        assertFalse(spi.queryLocalSql(typeAB.space(), "select * from A.B", Collections.emptySet(), typeAB, null).hasNext());
+        assertFalse(spi.queryLocalSql(typeBA.space(), "select * from B.A", Collections.emptySet(), typeBA, null).hasNext());
 
-        assertFalse(spi.query(typeBA.space(), "select * from B.A, A.B, A.A",
+        assertFalse(spi.queryLocalSql(typeBA.space(), "select * from B.A, A.B, A.A",
             Collections.emptySet(), typeBA, null).hasNext());
 
         try {
-            spi.query(typeBA.space(), "select aa.*, ab.*, ba.* from A.A aa, A.B ab, B.A ba",
+            spi.queryLocalSql(typeBA.space(), "select aa.*, ab.*, ba.* from A.A aa, A.B ab, B.A ba",
                 Collections.emptySet(), typeBA, null).hasNext();
 
             fail("Enumerations of aliases in select block must be prohibited");
@@ -239,10 +239,10 @@ public abstract class GridIndexingSpiAbstractSelfTest extends GridCommonAbstract
             // all fine
         }
 
-        assertFalse(spi.query(typeAB.space(), "select ab.* from A.B ab",
+        assertFalse(spi.queryLocalSql(typeAB.space(), "select ab.* from A.B ab",
             Collections.emptySet(), typeAB, null).hasNext());
 
-        assertFalse(spi.query(typeBA.space(), "select   ba.*   from B.A  as ba",
+        assertFalse(spi.queryLocalSql(typeBA.space(), "select   ba.*   from B.A  as ba",
             Collections.emptySet(), typeBA, null).hasNext());
 
         // Nothing to remove.
@@ -251,53 +251,53 @@ public abstract class GridIndexingSpiAbstractSelfTest extends GridCommonAbstract
 
         spi.store(typeAA.space(), typeAA, key(1), aa(1, "Vasya", 10), "v1".getBytes(), 0);
 
-        assertEquals(1, spi.size(typeAA.space(), typeAA, null));
-        assertEquals(0, spi.size(typeAB.space(), typeAB, null));
-        assertEquals(0, spi.size(typeBA.space(), typeBA, null));
+        assertEquals(1, spi.size(typeAA.space(), typeAA));
+        assertEquals(0, spi.size(typeAB.space(), typeAB));
+        assertEquals(0, spi.size(typeBA.space(), typeBA));
 
         spi.store(typeAB.space(), typeAB, key(1), ab(1, "Vasya", 20, "Some text about Vasya goes here."),
             "v2".getBytes(), 0);
 
         // In one space all keys must be unique.
-        assertEquals(0, spi.size(typeAA.space(), typeAA, null));
-        assertEquals(1, spi.size(typeAB.space(), typeAB, null));
-        assertEquals(0, spi.size(typeBA.space(), typeBA, null));
+        assertEquals(0, spi.size(typeAA.space(), typeAA));
+        assertEquals(1, spi.size(typeAB.space(), typeAB));
+        assertEquals(0, spi.size(typeBA.space(), typeBA));
 
         spi.store(typeBA.space(), typeBA, key(1), ba(2, "Petya", 25, true), "v3".getBytes(), 0);
 
         // No replacement because of different space.
-        assertEquals(0, spi.size(typeAA.space(), typeAA, null));
-        assertEquals(1, spi.size(typeAB.space(), typeAB, null));
-        assertEquals(1, spi.size(typeBA.space(), typeBA, null));
+        assertEquals(0, spi.size(typeAA.space(), typeAA));
+        assertEquals(1, spi.size(typeAB.space(), typeAB));
+        assertEquals(1, spi.size(typeBA.space(), typeBA));
 
         spi.store(typeBA.space(), typeBA, key(1), ba(2, "Kolya", 25, true), "v4".getBytes(), 0);
 
         // Replacement in the same table.
-        assertEquals(0, spi.size(typeAA.space(), typeAA, null));
-        assertEquals(1, spi.size(typeAB.space(), typeAB, null));
-        assertEquals(1, spi.size(typeBA.space(), typeBA, null));
+        assertEquals(0, spi.size(typeAA.space(), typeAA));
+        assertEquals(1, spi.size(typeAB.space(), typeAB));
+        assertEquals(1, spi.size(typeBA.space(), typeBA));
 
         spi.store(typeAA.space(), typeAA, key(2), aa(2, "Valera", 19), "v5".getBytes(), 0);
 
-        assertEquals(1, spi.size(typeAA.space(), typeAA, null));
-        assertEquals(1, spi.size(typeAB.space(), typeAB, null));
-        assertEquals(1, spi.size(typeBA.space(), typeBA, null));
+        assertEquals(1, spi.size(typeAA.space(), typeAA));
+        assertEquals(1, spi.size(typeAB.space(), typeAB));
+        assertEquals(1, spi.size(typeBA.space(), typeBA));
 
         spi.store(typeAA.space(), typeAA, key(3), aa(3, "Borya", 18), "v6".getBytes(), 0);
 
-        assertEquals(2, spi.size(typeAA.space(), typeAA, null));
-        assertEquals(1, spi.size(typeAB.space(), typeAB, null));
-        assertEquals(1, spi.size(typeBA.space(), typeBA, null));
+        assertEquals(2, spi.size(typeAA.space(), typeAA));
+        assertEquals(1, spi.size(typeAB.space(), typeAB));
+        assertEquals(1, spi.size(typeBA.space(), typeBA));
 
         spi.store(typeAB.space(), typeAB, key(4), ab(4, "Vitalya", 20, "Very Good guy"), "v7".getBytes(), 0);
 
-        assertEquals(2, spi.size(typeAA.space(), typeAA, null));
-        assertEquals(2, spi.size(typeAB.space(), typeAB, null));
-        assertEquals(1, spi.size(typeBA.space(), typeBA, null));
+        assertEquals(2, spi.size(typeAA.space(), typeAA));
+        assertEquals(2, spi.size(typeAB.space(), typeAB));
+        assertEquals(1, spi.size(typeBA.space(), typeBA));
 
         // Query data.
         Iterator<IgniteBiTuple<Integer, Map<String, Object>>> res =
-            spi.query(typeAA.space(), "from a order by age", Collections.emptySet(), typeAA, null);
+            spi.queryLocalSql(typeAA.space(), "from a order by age", Collections.emptySet(), typeAA, null);
 
         assertTrue(res.hasNext());
         assertEquals(aa(3, "Borya", 18).value(null, false), value(res.next()));
@@ -305,7 +305,7 @@ public abstract class GridIndexingSpiAbstractSelfTest extends GridCommonAbstract
         assertEquals(aa(2, "Valera", 19).value(null, false), value(res.next()));
         assertFalse(res.hasNext());
 
-        res = spi.query(typeAA.space(), "select aa.* from a aa order by aa.age",
+        res = spi.queryLocalSql(typeAA.space(), "select aa.* from a aa order by aa.age",
             Collections.emptySet(), typeAA, null);
 
         assertTrue(res.hasNext());
@@ -314,7 +314,7 @@ public abstract class GridIndexingSpiAbstractSelfTest extends GridCommonAbstract
         assertEquals(aa(2, "Valera", 19).value(null, false), value(res.next()));
         assertFalse(res.hasNext());
 
-        res = spi.query(typeAB.space(), "from b order by name", Collections.emptySet(), typeAB, null);
+        res = spi.queryLocalSql(typeAB.space(), "from b order by name", Collections.emptySet(), typeAB, null);
 
         assertTrue(res.hasNext());
         assertEquals(ab(1, "Vasya", 20, "Some text about Vasya goes here.").value(null, false), value(res.next()));
@@ -322,7 +322,7 @@ public abstract class GridIndexingSpiAbstractSelfTest extends GridCommonAbstract
         assertEquals(ab(4, "Vitalya", 20, "Very Good guy").value(null, false), value(res.next()));
         assertFalse(res.hasNext());
 
-        res = spi.query(typeAB.space(), "select bb.* from b as bb order by bb.name",
+        res = spi.queryLocalSql(typeAB.space(), "select bb.* from b as bb order by bb.name",
             Collections.emptySet(), typeAB, null);
 
         assertTrue(res.hasNext());
@@ -331,14 +331,15 @@ public abstract class GridIndexingSpiAbstractSelfTest extends GridCommonAbstract
         assertEquals(ab(4, "Vitalya", 20, "Very Good guy").value(null, false), value(res.next()));
         assertFalse(res.hasNext());
 
-        res = spi.query(typeBA.space(), "from a", Collections.emptySet(), typeBA, null);
+
+        res = spi.queryLocalSql(typeBA.space(), "from a", Collections.emptySet(), typeBA, null);
 
         assertTrue(res.hasNext());
         assertEquals(ba(2, "Kolya", 25, true).value(null, false), value(res.next()));
         assertFalse(res.hasNext());
 
         // Text queries
-        Iterator<IgniteBiTuple<Integer, Map<String, Object>>> txtRes = spi.queryText(typeAB.space(), "good",
+        Iterator<IgniteBiTuple<Integer, Map<String, Object>>> txtRes = spi.queryLocalText(typeAB.space(), "good",
             typeAB, null);
 
         assertTrue(txtRes.hasNext());
@@ -347,8 +348,8 @@ public abstract class GridIndexingSpiAbstractSelfTest extends GridCommonAbstract
 
         // Fields query
         GridQueryFieldsResult fieldsRes =
-            spi.queryFields("A", "select a.a.name n1, a.a.age a1, b.a.name n2, " +
-            "b.a.age a2 from a.a, b.a where a.a.id = b.a.id ", Collections.emptySet(), null);
+            spi.queryLocalSqlFields("A", "select a.a.name n1, a.a.age a1, b.a.name n2, " +
+            "b.a.age a2 from a.a, b.a where a.a.id = b.a.id ", Collections.emptySet(), null, false);
 
         String[] aliases = {"N1", "A1", "N2", "A2"};
         Object[] vals = { "Valera", 19, "Kolya", 25};
@@ -371,15 +372,15 @@ public abstract class GridIndexingSpiAbstractSelfTest extends GridCommonAbstract
         // Remove
         spi.remove(typeAA.space(), key(2), aa(2, "Valera", 19));
 
-        assertEquals(1, spi.size(typeAA.space(), typeAA, null));
-        assertEquals(2, spi.size(typeAB.space(), typeAB, null));
-        assertEquals(1, spi.size(typeBA.space(), typeBA, null));
+        assertEquals(1, spi.size(typeAA.space(), typeAA));
+        assertEquals(2, spi.size(typeAB.space(), typeAB));
+        assertEquals(1, spi.size(typeBA.space(), typeBA));
 
         spi.remove(typeBA.space(), key(1), ba(2, "Kolya", 25, true));
 
-        assertEquals(1, spi.size(typeAA.space(), typeAA, null));
-        assertEquals(2, spi.size(typeAB.space(), typeAB, null));
-        assertEquals(0, spi.size(typeBA.space(), typeBA, null));
+        assertEquals(1, spi.size(typeAA.space(), typeAA));
+        assertEquals(2, spi.size(typeAB.space(), typeAB));
+        assertEquals(0, spi.size(typeBA.space(), typeBA));
 
         boolean h2IdxOffheap = offheap();
 
@@ -389,9 +390,9 @@ public abstract class GridIndexingSpiAbstractSelfTest extends GridCommonAbstract
 
             spi.rebuildIndexes(typeAB.space(), typeAB);
 
-            assertEquals(1, spi.size(typeAA.space(), typeAA, null));
-            assertEquals(2, spi.size(typeAB.space(), typeAB, null));
-            assertEquals(0, spi.size(typeBA.space(), typeBA, null));
+            assertEquals(1, spi.size(typeAA.space(), typeAA));
+            assertEquals(2, spi.size(typeAB.space(), typeAB));
+            assertEquals(0, spi.size(typeBA.space(), typeBA));
 
             // For invalid space name/type should not fail.
             spi.rebuildIndexes("not_existing_space", typeAA);
@@ -401,22 +402,22 @@ public abstract class GridIndexingSpiAbstractSelfTest extends GridCommonAbstract
         // Unregister.
         spi.unregisterType(typeAA.space(), typeAA);
 
-        assertEquals(-1, spi.size(typeAA.space(), typeAA, null));
-        assertEquals(2, spi.size(typeAB.space(), typeAB, null));
-        assertEquals(0, spi.size(typeBA.space(), typeBA, null));
+        assertEquals(-1, spi.size(typeAA.space(), typeAA));
+        assertEquals(2, spi.size(typeAB.space(), typeAB));
+        assertEquals(0, spi.size(typeBA.space(), typeBA));
 
         spi.unregisterType(typeAB.space(), typeAB);
 
-        assertEquals(-1, spi.size(typeAA.space(), typeAA, null));
-        assertEquals(-1, spi.size(typeAB.space(), typeAB, null));
-        assertEquals(0, spi.size(typeBA.space(), typeBA, null));
+        assertEquals(-1, spi.size(typeAA.space(), typeAA));
+        assertEquals(-1, spi.size(typeAB.space(), typeAB));
+        assertEquals(0, spi.size(typeBA.space(), typeBA));
 
         spi.unregisterType(typeBA.space(), typeBA);
 
         // Should not store but should not fail as well.
         spi.store(typeAA.space(), typeAA, key(10), aa(1, "Fail", 100500), "v220".getBytes(), 0);
 
-        assertEquals(-1, spi.size(typeAA.space(), typeAA, null));
+        assertEquals(-1, spi.size(typeAA.space(), typeAA));
     }
 
     /**
@@ -447,7 +448,8 @@ public abstract class GridIndexingSpiAbstractSelfTest extends GridCommonAbstract
                 time = now;
                 range *= 3;
 
-                GridQueryFieldsResult res = spi.queryFields("A", sql, Arrays.<Object>asList(1, range), null);
+                GridQueryFieldsResult res = spi.queryLocalSqlFields("A", sql, Arrays.<Object>asList(1, range), null,
+                    false);
 
                 assert res.iterator().hasNext();
 
@@ -525,6 +527,11 @@ public abstract class GridIndexingSpiAbstractSelfTest extends GridCommonAbstract
         }
 
         /** {@inheritDoc} */
+        @Override public String affinityKey() {
+            return null;
+        }
+
+        /** {@inheritDoc} */
         @Override public String name() {
             return name;
         }
@@ -621,7 +628,7 @@ public abstract class GridIndexingSpiAbstractSelfTest extends GridCommonAbstract
 
         /** {@inheritDoc} */
         @Override public byte[] valueBytes(CacheObjectContext ctx) throws IgniteCheckedException {
-            return Utils.serialize(val, null);
+            return JdbcUtils.serialize(val, null);
         }
 
         /** {@inheritDoc} */

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TableSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TableSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TableSelfTest.java
index e70ee8d..8408ba0 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TableSelfTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TableSelfTest.java
@@ -33,6 +33,7 @@ import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.Callable;
 import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
 import org.h2.Driver;
 import org.h2.index.Index;
@@ -95,9 +96,9 @@ public class GridH2TableSelfTest extends GridCommonAbstractTest {
                 IndexColumn str = tbl.indexColumn(2, SortOrder.DESCENDING);
                 IndexColumn x = tbl.indexColumn(3, SortOrder.DESCENDING);
 
-                idxs.add(new GridH2TreeIndex(PK_NAME, tbl, true, 0, 1, id));
-                idxs.add(new GridH2TreeIndex(NON_UNIQUE_IDX_NAME, tbl, false, 0, 1, x, t));
-                idxs.add(new GridH2TreeIndex(STR_IDX_NAME, tbl, false, 0, 1, str));
+                idxs.add(new GridH2TreeIndex(PK_NAME, tbl, true, F.asList(id)));
+                idxs.add(new GridH2TreeIndex(NON_UNIQUE_IDX_NAME, tbl, false, F.asList(x, t, id)));
+                idxs.add(new GridH2TreeIndex(STR_IDX_NAME, tbl, false, F.asList(str, id)));
 
                 return idxs;
             }
@@ -120,7 +121,8 @@ public class GridH2TableSelfTest extends GridCommonAbstractTest {
      * @return New row.
      */
     private GridH2Row row(UUID id, long t, String str, long x) {
-        return new GridH2Row(ValueUuid.get(id.getMostSignificantBits(), id.getLeastSignificantBits()),
+        return GridH2RowFactory.create(
+            ValueUuid.get(id.getMostSignificantBits(), id.getLeastSignificantBits()),
             ValueTimestamp.get(new Timestamp(t)),
             ValueString.get(str),
             ValueLong.get(x));

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/AbstractH2CompareQueryTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/AbstractH2CompareQueryTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/AbstractH2CompareQueryTest.java
index ffe137f..f0ab123 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/AbstractH2CompareQueryTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/AbstractH2CompareQueryTest.java
@@ -30,6 +30,7 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.TreeMap;
+import junit.framework.AssertionFailedError;
 import org.apache.ignite.Ignite;
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.cache.CacheAtomicityMode;
@@ -49,18 +50,21 @@ import org.jetbrains.annotations.Nullable;
 
 /**
  * Abstract test framework to compare query results from h2 database instance and mixed ignite caches (replicated and partitioned)
- * which have the same data models and data content. 
+ * which have the same data models and data content.
  */
 public abstract class AbstractH2CompareQueryTest extends GridCommonAbstractTest {
     /** */
     private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
 
+    /** */
+    protected static final int SRVS = 4;
+
     /** Partitioned cache. */
     protected static IgniteCache pCache;
 
     /** Replicated cache. */
     protected static IgniteCache rCache;
-    
+
     /** H2 db connection. */
     protected static Connection conn;
 
@@ -77,14 +81,21 @@ public abstract class AbstractH2CompareQueryTest extends GridCommonAbstractTest
 
         c.setMarshaller(new OptimizedMarshaller(true));
 
-        c.setCacheConfiguration(createCache("part", CacheMode.PARTITIONED),
-            createCache("repl", CacheMode.REPLICATED)
-        );
+        c.setCacheConfiguration(cacheConfigurations());
 
         return c;
     }
 
     /**
+     * @return Cache configurations.
+     */
+    protected CacheConfiguration[] cacheConfigurations() {
+        return new CacheConfiguration[] {
+            createCache("part", CacheMode.PARTITIONED),
+            createCache("repl", CacheMode.REPLICATED)};
+    }
+
+    /**
      * Creates new cache configuration.
      *
      * @param name Cache name.
@@ -114,14 +125,14 @@ public abstract class AbstractH2CompareQueryTest extends GridCommonAbstractTest
     @Override protected void beforeTestsStarted() throws Exception {
         super.beforeTestsStarted();
 
-        Ignite ignite = startGrids(4);
+        Ignite ignite = startGrids(SRVS);
 
         pCache = ignite.cache("part");
-        
+
         rCache = ignite.cache("repl");
 
         awaitPartitionMapExchange();
-        
+
         conn = openH2Connection(false);
 
         initializeH2Schema();
@@ -130,22 +141,23 @@ public abstract class AbstractH2CompareQueryTest extends GridCommonAbstractTest
 
         checkAllDataEquals();
     }
-    
+
     /** {@inheritDoc} */
     @Override protected void afterTestsStopped() throws Exception {
         super.afterTestsStopped();
 
         Statement st = conn.createStatement();
-        
+
         st.execute("DROP ALL OBJECTS");
 
         conn.close();
-        
+
         stopAllGrids();
     }
 
     /**
      * Populate cache and h2 database with test data.
+     * @throws SQLException If failed.
      */
     protected abstract void initCacheAndDbData() throws Exception;
 
@@ -158,6 +170,7 @@ public abstract class AbstractH2CompareQueryTest extends GridCommonAbstractTest
      * Initialize h2 database schema.
      *
      * @throws SQLException If exception.
+     * @return Statement.
      */
     protected Statement initializeH2Schema() throws SQLException {
         Statement st = conn.createStatement();
@@ -175,11 +188,11 @@ public abstract class AbstractH2CompareQueryTest extends GridCommonAbstractTest
      * @return Pooled connection.
      * @throws SQLException In case of error.
      */
-    private Connection openH2Connection(boolean autocommit) throws SQLException {
+    protected Connection openH2Connection(boolean autocommit) throws SQLException {
         System.setProperty("h2.serializeJavaObject", "false");
-        
+
         String dbName = "test";
-        
+
         Connection conn = DriverManager.getConnection("jdbc:h2:mem:" + dbName + ";DB_CLOSE_DELAY=-1");
 
         conn.setAutoCommit(autocommit);
@@ -193,7 +206,7 @@ public abstract class AbstractH2CompareQueryTest extends GridCommonAbstractTest
      * @param sql SQL query.
      * @param args SQL arguments.
      * then results will compare as ordered queries.
-     * @return Result set after SQL query execution. 
+     * @return Result set after SQL query execution.
      * @throws SQLException If exception.
      */
     protected final List<List<?>> compareQueryRes0(String sql, @Nullable Object... args) throws SQLException {
@@ -201,14 +214,14 @@ public abstract class AbstractH2CompareQueryTest extends GridCommonAbstractTest
     }
 
     /**
-     * Execute given sql query on h2 database and on ignite cache and compare results. 
+     * Execute given sql query on h2 database and on ignite cache and compare results.
      * Expected that results are not ordered.
      *
      * @param cache Ignite cache.
      * @param sql SQL query.
      * @param args SQL arguments.
      * then results will compare as ordered queries.
-     * @return Result set after SQL query execution. 
+     * @return Result set after SQL query execution.
      * @throws SQLException If exception.
      */
     protected final List<List<?>> compareQueryRes0(IgniteCache cache, String sql, @Nullable Object... args) throws SQLException {
@@ -239,20 +252,80 @@ public abstract class AbstractH2CompareQueryTest extends GridCommonAbstractTest
      * then results will compare as ordered queries.
      * @return Result set after SQL query execution.
      * @throws SQLException If exception.
-     */    
+     */
+    @SuppressWarnings("unchecked")
+    protected static List<List<?>> compareQueryRes0(IgniteCache cache, String sql, @Nullable Object[] args,
+        Ordering ordering) throws SQLException {
+        return compareQueryRes0(cache, sql, false, args, ordering);
+    }
+
+    /**
+     * Execute given sql query on h2 database and on ignite cache and compare results.
+     *
+     * @param cache Ignite cache.
+     * @param sql SQL query.
+     * @param distrib Distributed SQL Join flag.
+     * @param args SQL arguments.
+     * @param ordering Expected ordering of SQL results. If {@link Ordering#ORDERED}
+     *      then results will compare as ordered queries.
+     * @return Result set after SQL query execution.
+     * @throws SQLException If exception.
+     */
+    protected static List<List<?>> compareQueryRes0(IgniteCache cache,
+        String sql,
+        boolean distrib,
+        @Nullable Object[] args,
+        Ordering ordering) throws SQLException {
+        return compareQueryRes0(cache, sql, distrib, false, args, ordering);
+    }
+
+    /**
+     * Execute given sql query on h2 database and on ignite cache and compare results.
+     *
+     * @param cache Ignite cache.
+     * @param sql SQL query.
+     * @param distrib Distributed SQL Join flag.
+     * @param enforceJoinOrder Enforce join order flag.
+     * @param args SQL arguments.
+     * @param ordering Expected ordering of SQL results. If {@link Ordering#ORDERED}
+     *      then results will compare as ordered queries.
+     * @return Result set after SQL query execution.
+     * @throws SQLException If exception.
+     */
     @SuppressWarnings("unchecked")
-    protected final List<List<?>> compareQueryRes0(IgniteCache cache, String sql, @Nullable Object[] args, Ordering ordering) throws SQLException {
+    protected static List<List<?>> compareQueryRes0(IgniteCache cache,
+        String sql,
+        boolean distrib,
+        boolean enforceJoinOrder,
+        @Nullable Object[] args,
+        Ordering ordering) throws SQLException {
         if (args == null)
             args = new Object[] {null};
-        
-        info("Sql query:\n" + sql + "\nargs=" + Arrays.toString(args));
 
         List<List<?>> h2Res = executeH2Query(sql, args);
 
-        List<List<?>> cacheRes = cache.query(new SqlFieldsQuery(sql).setArgs(args)).getAll();
+//        String plan = (String)((IgniteCache<Object, Object>)cache).query(new SqlFieldsQuery("explain " + sql)
+//            .setArgs(args)
+//            .setDistributedJoins(distrib))
+//            .getAll().get(0).get(0);
+//
+//        X.println("Plan : " + plan);
+
+        List<List<?>> cacheRes = cache.query(new SqlFieldsQuery(sql).
+            setArgs(args).
+            setDistributedJoins(distrib).
+            setEnforceJoinOrder(enforceJoinOrder)).getAll();
+
+        try {
+            assertRsEquals(h2Res, cacheRes, ordering);
+        }
+        catch (AssertionFailedError e) {
+            X.println("Sql query:\n" + sql + "\nargs=" + Arrays.toString(args));
+            X.println("[h2Res=" + h2Res + ", cacheRes=" + cacheRes + "]");
+
+            throw e;
+        }
 
-        assertRsEquals(h2Res, cacheRes, ordering);
-        
         return h2Res;
     }
 
@@ -264,11 +337,11 @@ public abstract class AbstractH2CompareQueryTest extends GridCommonAbstractTest
      * @return Result of SQL query on h2 database.
      * @throws SQLException If exception.
      */
-    private List<List<?>> executeH2Query(String sql, Object[] args) throws SQLException {
+    private static List<List<?>> executeH2Query(String sql, Object[] args) throws SQLException {
         List<List<?>> res = new ArrayList<>();
         ResultSet rs = null;
 
-        try(PreparedStatement st = conn.prepareStatement(sql)) {
+        try (PreparedStatement st = conn.prepareStatement(sql)) {
             for (int idx = 0; idx < args.length; idx++)
                 st.setObject(idx + 1, args[idx]);
 
@@ -278,17 +351,17 @@ public abstract class AbstractH2CompareQueryTest extends GridCommonAbstractTest
 
             int colCnt = meta.getColumnCount();
 
-            for (int i = 1; i <= colCnt; i++)
-                X.print(meta.getColumnLabel(i) + "  ");
-
-            X.println();
+//            for (int i = 1; i <= colCnt; i++)
+//                X.print(meta.getColumnLabel(i) + "  ");
+//
+//            X.println();
 
             while (rs.next()) {
                 List<Object> row = new ArrayList<>(colCnt);
-                
+
                 for (int i = 1; i <= colCnt; i++)
                     row.add(rs.getObject(i));
-                
+
                 res.add(row);
             }
         }
@@ -307,9 +380,9 @@ public abstract class AbstractH2CompareQueryTest extends GridCommonAbstractTest
      * @param ordering Expected ordering of SQL results. If {@link Ordering#ORDERED}
      * then results will compare as ordered queries.
      */
-    private void assertRsEquals(List<List<?>> rs1, List<List<?>> rs2, Ordering ordering) {
+    private static void assertRsEquals(List<List<?>> rs1, List<List<?>> rs2, Ordering ordering) {
         assertEquals("Rows count has to be equal.", rs1.size(), rs2.size());
-        
+
         switch (ordering){
             case ORDERED:
                 for (int rowNum = 0; rowNum < rs1.size(); rowNum++) {
@@ -329,7 +402,7 @@ public abstract class AbstractH2CompareQueryTest extends GridCommonAbstractTest
 
                 assertEquals("Unique rows count has to be equal.", rowsWithCnt1.size(), rowsWithCnt2.size());
 
-                X.println("Result size: " + rowsWithCnt1.size());
+                // X.println("Result size: " + rowsWithCnt1.size());
 
                 Iterator<Map.Entry<String,Integer>> iter1 = rowsWithCnt1.entrySet().iterator();
                 Iterator<Map.Entry<String,Integer>> iter2 = rowsWithCnt2.entrySet().iterator();
@@ -343,15 +416,15 @@ public abstract class AbstractH2CompareQueryTest extends GridCommonAbstractTest
 
                     assertTrue(iter2.hasNext());
 
-                    Map.Entry<String,Integer> e1 = iter1.next();
-                    Map.Entry<String,Integer> e2 = iter2.next();
+                    Map.Entry<String, Integer> e1 = iter1.next();
+                    Map.Entry<String, Integer> e2 = iter2.next();
 
                     assertEquals(e1.getKey(), e2.getKey());
                     assertEquals(e1.getValue(), e2.getValue());
                 }
-                
+
                 break;
-            default: 
+            default:
                 throw new IllegalStateException();
         }
     }
@@ -360,30 +433,30 @@ public abstract class AbstractH2CompareQueryTest extends GridCommonAbstractTest
      * @param rs Result set.
      * @return Map of unique rows at the result set to number of occuriances at the result set.
      */
-    private TreeMap<String, Integer> extractUniqueRowsWithCounts(Iterable<List<?>> rs) {
+    private static TreeMap<String, Integer> extractUniqueRowsWithCounts(Iterable<List<?>> rs) {
         TreeMap<String, Integer> res = new TreeMap<>();
 
         for (List<?> row : rs) {
             String rowStr = row.toString();
 
             Integer cnt = res.get(rowStr);
-            
+
             if (cnt == null)
                 cnt = 0;
-            
+
             res.put(rowStr, cnt + 1);
         }
 
         return res;
     }
-    
+
     /**
      * Ordering type.
      */
     protected enum Ordering {
         /** Random. */
-        RANDOM, 
+        RANDOM,
         /** Ordered. */
         ORDERED
     }
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/GridQueryParsingTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/GridQueryParsingTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/GridQueryParsingTest.java
index 9fb5765..d6ee511 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/GridQueryParsingTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/GridQueryParsingTest.java
@@ -147,6 +147,8 @@ public class GridQueryParsingTest extends GridCommonAbstractTest {
 
         checkQuery("select avg(old) from Person left join Address where Person.addrId = Address.id " +
             "and lower(Address.street) = lower(?)");
+        checkQuery("select avg(old) from Person right join Address where Person.addrId = Address.id " +
+            "and lower(Address.street) = lower(?)");
 
         checkQuery("select avg(old) from Person, Address where Person.addrId = Address.id " +
             "and lower(Address.street) = lower(?)");
@@ -164,6 +166,7 @@ public class GridQueryParsingTest extends GridCommonAbstractTest {
         checkQuery("select person.* from Person, Address a");
         checkQuery("select p.*, street from Person p, Address a");
         checkQuery("select p.name, a.street from Person p, Address a");
+        checkQuery("select p.name, a.street from Address a, Person p");
         checkQuery("select distinct p.name, a.street from Person p, Address a");
         checkQuery("select distinct name, street from Person, Address group by old");
         checkQuery("select distinct name, street from Person, Address");
@@ -254,20 +257,6 @@ public class GridQueryParsingTest extends GridCommonAbstractTest {
     /**
      *
      */
-    public void testExample1() throws Exception {
-        Query select = parse("select p.name n, max(p.old) maxOld, min(p.old) minOld from Person p group by p.name having maxOld > 10 and min(p.old) < 1");
-
-        GridSqlQueryParser ses = new GridSqlQueryParser();
-
-        GridSqlQuery gridSelect = ses.parse(select);
-
-        //System.out.println(select.getPlanSQL());
-        System.out.println(gridSelect.getSQL());
-    }
-
-    /**
-     *
-     */
     private JdbcConnection connection() throws Exception {
         GridKernalContext ctx = ((IgniteEx)ignite).context();
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/H2CompareBigQueryDistributedJoinsTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/H2CompareBigQueryDistributedJoinsTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/H2CompareBigQueryDistributedJoinsTest.java
new file mode 100644
index 0000000..267778f
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/H2CompareBigQueryDistributedJoinsTest.java
@@ -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.
+ */
+
+package org.apache.ignite.internal.processors.query.h2.sql;
+
+/**
+ * Executes SQL big query test using distributed joins mode.
+ */
+public class H2CompareBigQueryDistributedJoinsTest extends H2CompareBigQueryTest {
+    /** {@inheritDoc} */
+    @Override protected boolean distributedJoins() {
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/H2CompareBigQueryTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/H2CompareBigQueryTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/H2CompareBigQueryTest.java
index 255ac44..dc73e96 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/H2CompareBigQueryTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/H2CompareBigQueryTest.java
@@ -35,11 +35,11 @@ import org.apache.ignite.cache.query.annotations.QuerySqlField;
 import org.apache.ignite.configuration.CacheConfiguration;
 
 /**
- * Executes one big query (and subqueries of the big query) to compare query results from h2 database instance and 
+ * Executes one big query (and subqueries of the big query) to compare query results from h2 database instance and
  * mixed ignite caches (replicated and partitioned) which have the same data models and data content.
- * 
- * 
- * <pre> 
+ *
+ *
+ * <pre>
  *
  *  -------------------------------------> rootOrderId (virtual) <--------------------------
  *  |                                                                |                     |
@@ -75,20 +75,20 @@ public class H2CompareBigQueryTest extends AbstractH2CompareQueryTest {
 
     /** Dates count. */
     private static final int DATES_CNT = 5;
-    
+
     /** Full the big query. */
     private String bigQry = getBigQry();
 
     /**
      * Extracts the big query from file.
-     *  
+     *
      * @return Big query.
      */
     private String getBigQry() {
         String res = "";
 
         Reader isr = new InputStreamReader(getClass().getResourceAsStream("bigQuery.sql"));
-        
+
         try(BufferedReader reader = new BufferedReader(isr)) {
             for(String line; (line = reader.readLine()) != null; )
                 if (!line.startsWith("--")) // Skip commented lines.
@@ -96,21 +96,35 @@ public class H2CompareBigQueryTest extends AbstractH2CompareQueryTest {
         }
         catch (Throwable e) {
             e.printStackTrace();
-            
+
             fail();
         }
 
         return res;
     }
 
+    /**
+     * @return Use colocated data.
+     */
+    private boolean useColocatedData() {
+        return !distributedJoins();
+    }
+
+    /**
+     * @return Whehter to use distrubutedJoins or not.
+     */
+    protected boolean distributedJoins() {
+        return false;
+    }
+
     /** {@inheritDoc} */
     @Override protected void setIndexedTypes(CacheConfiguration<?, ?> cc, CacheMode mode) {
         if (mode == CacheMode.PARTITIONED)
             cc.setIndexedTypes(
                 Integer.class, CustOrder.class,
-                AffinityKey.class, ReplaceOrder.class,
-                AffinityKey.class, OrderParams.class,
-                AffinityKey.class, Cancel.class
+                useColocatedData() ? AffinityKey.class : Integer.class, ReplaceOrder.class,
+                useColocatedData() ? AffinityKey.class : Integer.class, OrderParams.class,
+                useColocatedData() ? AffinityKey.class : Integer.class, Cancel.class
             );
         else if (mode == CacheMode.REPLICATED)
             cc.setIndexedTypes(
@@ -142,8 +156,8 @@ public class H2CompareBigQueryTest extends AbstractH2CompareQueryTest {
                 // Generate 1 - 5 orders for 1 root order.
                 for (int i = 0; i < rootOrderId % 5; i++) {
                     int orderId = idGen.incrementAndGet();
-                    
-                    CustOrder order = new CustOrder(orderId, rootOrderId, dates.get(orderId % dates.size()) , 
+
+                    CustOrder order = new CustOrder(orderId, rootOrderId, dates.get(orderId % dates.size()) ,
                         orderId % 2 == 0 ? "CUSTOM" : "OTHER", orderId);
 
                     add(order);
@@ -151,23 +165,23 @@ public class H2CompareBigQueryTest extends AbstractH2CompareQueryTest {
                     pCache.put(order.orderId, order);
 
                     insertInDb(order);
-                }                
+                }
             }
         }};
 
         final Collection<OrderParams> params = new ArrayList<OrderParams>(){{
             for (CustOrder o : orders) {
-                OrderParams op = new OrderParams(idGen.incrementAndGet(), o.orderId, o.date, 
+                OrderParams op = new OrderParams(idGen.incrementAndGet(), o.orderId, o.date,
                     o.orderId % 2 == 0 ? "Algo 1" : "Algo 2");
 
                 add(op);
 
-                pCache.put(op.key(), op);
+                pCache.put(op.key(useColocatedData()), op);
 
                 insertInDb(op);
             }
         }};
-        
+
         final Collection<ReplaceOrder> replaces = new ArrayList<ReplaceOrder>(){{
             for (CustOrder o : orders) {
                 if (o.orderId % 7 == 0) {
@@ -176,7 +190,7 @@ public class H2CompareBigQueryTest extends AbstractH2CompareQueryTest {
 
                     add(replace);
 
-                    pCache.put(replace.key(), replace);
+                    pCache.put(replace.key(useColocatedData()), replace);
 
                     insertInDb(replace);
                 }
@@ -186,12 +200,12 @@ public class H2CompareBigQueryTest extends AbstractH2CompareQueryTest {
         final Collection<Cancel> cancels = new ArrayList<Cancel>(){{
             for (CustOrder o : orders) {
                 if (o.orderId % 9 == 0) {
-                    Cancel c = new Cancel(idGen.incrementAndGet(), o.orderId, 
+                    Cancel c = new Cancel(idGen.incrementAndGet(), o.orderId,
                         new Date(o.date.getTime() + 12 * 60 * 60 * 1000));// Plus a half of day.
 
                     add(c);
 
-                    pCache.put(c.key(), c); 
+                    pCache.put(c.key(useColocatedData()), c);
 
                     insertInDb(c);
                 }
@@ -203,7 +217,7 @@ public class H2CompareBigQueryTest extends AbstractH2CompareQueryTest {
                 int execShares = 10000 + rootOrderId;
                 int price = 1000 + rootOrderId;
                 int latsMkt = 3000 + rootOrderId;
-                
+
                 Exec exec = new Exec(rootOrderId, dates.get(rootOrderId % dates.size()), execShares, price, latsMkt);
 
                 add(exec);
@@ -232,8 +246,8 @@ public class H2CompareBigQueryTest extends AbstractH2CompareQueryTest {
      * @throws Exception If failed.
      */
     public void testBigQuery() throws Exception {
-        List<List<?>> res = compareQueryRes0(bigQry);
-        
+        List<List<?>> res = compareQueryRes0(pCache, bigQry, distributedJoins(), new Object[0], Ordering.RANDOM);
+
         assertTrue(!res.isEmpty()); // Ensure we set good testing data at database.
     }
 
@@ -241,6 +255,8 @@ public class H2CompareBigQueryTest extends AbstractH2CompareQueryTest {
     @Override protected Statement initializeH2Schema() throws SQLException {
         Statement st = super.initializeH2Schema();
 
+        final String keyType = useColocatedData() ? "other" : "int";
+
         st.execute("create table \"part\".CustOrder" +
             "  (" +
             "  _key int not null," +
@@ -255,7 +271,7 @@ public class H2CompareBigQueryTest extends AbstractH2CompareQueryTest {
 
         st.execute("create table \"part\".ReplaceOrder" +
             "  (" +
-            "  _key other not null," +
+            "  _key " + keyType + " not null," +
             "  _val other not null," +
             "  id int unique," +
             "  orderId int ," +
@@ -268,7 +284,7 @@ public class H2CompareBigQueryTest extends AbstractH2CompareQueryTest {
 
         st.execute("create table \"part\".OrderParams" +
             "  (" +
-            "  _key other not null," +
+            "  _key " + keyType + " not null," +
             "  _val other not null," +
             "  id int unique," +
             "  orderId int ," +
@@ -278,7 +294,7 @@ public class H2CompareBigQueryTest extends AbstractH2CompareQueryTest {
 
         st.execute("create table \"part\".Cancel" +
             "  (" +
-            "  _key other not null," +
+            "  _key " + keyType + " not null," +
             "  _val other not null," +
             "  id int unique," +
             "  date Date, " +
@@ -295,15 +311,15 @@ public class H2CompareBigQueryTest extends AbstractH2CompareQueryTest {
             "  price int," +
             "  lastMkt int" +
             "  )");
-        
+
         conn.commit();
-        
+
         return st;
     }
 
     /**
      * Insert {@link CustOrder} at h2 database.
-     *  
+     *
      * @param o CustOrder.
      */
     private void insertInDb(CustOrder o) throws SQLException {
@@ -311,7 +327,7 @@ public class H2CompareBigQueryTest extends AbstractH2CompareQueryTest {
             "insert into \"part\".CustOrder (_key, _val, orderId, rootOrderId, date, alias, archSeq, origOrderId) " +
                 "values(?, ?, ?, ?, ?, ?, ?, ?)")) {
             int i = 0;
-            
+
             st.setObject(++i, o.orderId);
             st.setObject(++i, o);
             st.setObject(++i, o.orderId);
@@ -335,8 +351,8 @@ public class H2CompareBigQueryTest extends AbstractH2CompareQueryTest {
             "insert into \"part\".ReplaceOrder (_key, _val, id, orderId, rootOrderId, date, alias, archSeq, refOrderId) " +
                 "values(?, ?, ?, ?, ?, ?, ?, ?, ?)")) {
             int i = 0;
-            
-            st.setObject(++i, o.key());
+
+            st.setObject(++i, o.key(useColocatedData()));
             st.setObject(++i, o);
             st.setObject(++i, o.id);
             st.setObject(++i, o.orderId);
@@ -359,15 +375,15 @@ public class H2CompareBigQueryTest extends AbstractH2CompareQueryTest {
         try(PreparedStatement st = conn.prepareStatement(
             "insert into \"part\".OrderParams (_key, _val, id, date, orderId, parentAlgo) values(?, ?, ?, ?, ?, ?)")) {
             int i = 0;
-            
-            st.setObject(++i, o.key());
+
+            st.setObject(++i, o.key(useColocatedData()));
             st.setObject(++i, o);
             st.setObject(++i, o.id);
             st.setObject(++i, o.date);
             st.setObject(++i, o.orderId);
             st.setObject(++i, o.parentAlgo);
 
-            st.executeUpdate();
+           st.executeUpdate();
         }
     }
 
@@ -380,8 +396,8 @@ public class H2CompareBigQueryTest extends AbstractH2CompareQueryTest {
         try(PreparedStatement st = conn.prepareStatement(
             "insert into \"part\".Cancel (_key, _val, id, date, refOrderId) values(?, ?, ?, ?, ?)")) {
             int i = 0;
-            
-            st.setObject(++i, o.key());
+
+            st.setObject(++i, o.key(useColocatedData()));
             st.setObject(++i, o);
             st.setObject(++i, o.id);
             st.setObject(++i, o.date);
@@ -401,7 +417,7 @@ public class H2CompareBigQueryTest extends AbstractH2CompareQueryTest {
             "insert into \"repl\".Exec (_key, _val, date, rootOrderId, execShares, price, lastMkt) " +
                 "values(?, ?, ?, ?, ?, ?, ?)")) {
             int i = 0;
-            
+
             st.setObject(++i, o.rootOrderId);
             st.setObject(++i, o);
             st.setObject(++i, o.date);
@@ -475,7 +491,7 @@ public class H2CompareBigQueryTest extends AbstractH2CompareQueryTest {
         /** Primary key. */
         @QuerySqlField(index = true)
         private int id;
-        
+
         /** Order id. */
         @QuerySqlField(index = true)
         private int orderId;
@@ -518,10 +534,11 @@ public class H2CompareBigQueryTest extends AbstractH2CompareQueryTest {
         }
 
         /**
-         * @return Afinity key.
+         * @param useColocatedData Use colocated data.
+         * @return Key.
          */
-        public AffinityKey<Integer> key() {
-            return new AffinityKey<>(id, orderId);
+        public Object key(boolean useColocatedData) {
+            return useColocatedData ? new AffinityKey<>(id, orderId) : id;
         }
 
         /** {@inheritDoc} */
@@ -569,10 +586,11 @@ public class H2CompareBigQueryTest extends AbstractH2CompareQueryTest {
         }
 
         /**
-         * @return Afinity key.
+         * @param useColocatedData Use colocated data.*
+         * @return Key.
          */
-        public AffinityKey<Integer> key() {
-            return new AffinityKey<>(id, orderId);
+        public Object key(boolean useColocatedData) {
+            return useColocatedData ? new AffinityKey<>(id, orderId) : id;
         }
 
         /** {@inheritDoc} */
@@ -603,7 +621,7 @@ public class H2CompareBigQueryTest extends AbstractH2CompareQueryTest {
         private Date date;
 
         /**
-         * @param id
+         * @param id ID.
          * @param refOrderId Reference order id.
          * @param date Date.
          */
@@ -614,10 +632,11 @@ public class H2CompareBigQueryTest extends AbstractH2CompareQueryTest {
         }
 
         /**
-         * @return Afinity key.
+         * @param useColocatedData Use colocated data.
+         * @return Key.
          */
-        public AffinityKey<Integer> key() {
-            return new AffinityKey<>(id, refOrderId);
+        public Object key(boolean useColocatedData) {
+            return useColocatedData ? new AffinityKey<>(id, refOrderId) : id;
         }
 
         /** {@inheritDoc} */
@@ -680,4 +699,4 @@ public class H2CompareBigQueryTest extends AbstractH2CompareQueryTest {
             return rootOrderId;
         }
     }
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java
index 1b1908d..65a94dd 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java
@@ -22,11 +22,19 @@ import org.apache.ignite.internal.processors.cache.GridCacheCrossCacheQuerySelfT
 import org.apache.ignite.internal.processors.cache.GridCacheQueryIndexDisabledSelfTest;
 import org.apache.ignite.internal.processors.cache.GridCacheQueryInternalKeysSelfTest;
 import org.apache.ignite.internal.processors.cache.GridCacheQuerySerializationSelfTest;
-import org.apache.ignite.internal.processors.cache.GridCacheReduceQueryMultithreadedSelfTest;
 import org.apache.ignite.internal.processors.cache.IgniteBinaryObjectFieldsQuerySelfTest;
 import org.apache.ignite.internal.processors.cache.IgniteBinaryWrappedObjectFieldsQuerySelfTest;
 import org.apache.ignite.internal.processors.cache.IgniteCacheCollocatedQuerySelfTest;
+import org.apache.ignite.internal.processors.cache.IgniteCacheCrossCacheJoinRandomTest;
+import org.apache.ignite.internal.processors.cache.IgniteCacheDistributedJoinCollocatedAndNotTest;
+import org.apache.ignite.internal.processors.cache.IgniteCacheDistributedJoinCustomAffinityMapper;
+import org.apache.ignite.internal.processors.cache.IgniteCacheDistributedJoinPartitionedAndReplicatedTest;
+import org.apache.ignite.internal.processors.cache.IgniteCacheDistributedJoinQueryConditionsTest;
+import org.apache.ignite.internal.processors.cache.IgniteCacheDistributedJoinTest;
 import org.apache.ignite.internal.processors.cache.IgniteCacheDuplicateEntityConfigurationSelfTest;
+import org.apache.ignite.internal.processors.cache.IgniteCacheDistributedJoinNoIndexTest;
+import org.apache.ignite.internal.processors.cache.IgniteCacheJoinPartitionedAndReplicatedTest;
+import org.apache.ignite.internal.processors.cache.IgniteCacheJoinQueryWithAffinityKeyTest;
 import org.apache.ignite.internal.processors.cache.IgniteCacheLargeResultSelfTest;
 import org.apache.ignite.internal.processors.cache.IgniteCacheOffheapEvictQueryTest;
 import org.apache.ignite.internal.processors.cache.IgniteCacheOffheapIndexScanTest;
@@ -41,6 +49,7 @@ import org.apache.ignite.internal.processors.cache.IgniteCacheQueryMultiThreaded
 import org.apache.ignite.internal.processors.cache.IgniteCacheQueryOffheapEvictsMultiThreadedSelfTest;
 import org.apache.ignite.internal.processors.cache.IgniteCacheQueryOffheapMultiThreadedSelfTest;
 import org.apache.ignite.internal.processors.cache.IgniteCacheSqlQueryMultiThreadedSelfTest;
+import org.apache.ignite.internal.processors.cache.IgniteCrossCachesJoinsQueryTest;
 import org.apache.ignite.internal.processors.cache.distributed.near.IgniteCacheAtomicNearEnabledQuerySelfTest;
 import org.apache.ignite.internal.processors.cache.distributed.near.IgniteCacheAtomicQuerySelfTest;
 import org.apache.ignite.internal.processors.cache.distributed.near.IgniteCacheClientQueryReplicatedNodeRestartSelfTest;
@@ -49,6 +58,7 @@ import org.apache.ignite.internal.processors.cache.distributed.near.IgniteCacheP
 import org.apache.ignite.internal.processors.cache.distributed.near.IgniteCachePartitionedSnapshotEnabledQuerySelfTest;
 import org.apache.ignite.internal.processors.cache.distributed.near.IgniteCacheQueryNoRebalanceSelfTest;
 import org.apache.ignite.internal.processors.cache.distributed.near.IgniteCacheQueryNodeFailTest;
+import org.apache.ignite.internal.processors.cache.distributed.near.IgniteCacheQueryNodeRestartDistributedJoinSelfTest;
 import org.apache.ignite.internal.processors.cache.distributed.near.IgniteCacheQueryNodeRestartSelfTest;
 import org.apache.ignite.internal.processors.cache.distributed.near.IgniteCacheQueryNodeRestartSelfTest2;
 import org.apache.ignite.internal.processors.cache.distributed.replicated.IgniteCacheReplicatedQueryP2PDisabledSelfTest;
@@ -106,9 +116,9 @@ public class IgniteCacheQuerySelfTestSuite extends TestSuite {
         suite.addTestSuite(IgniteCacheOffheapIndexScanTest.class);
         suite.addTestSuite(IgniteCacheQueryNodeRestartSelfTest.class);
         suite.addTestSuite(IgniteCacheQueryNodeRestartSelfTest2.class);
+        suite.addTestSuite(IgniteCacheQueryNodeRestartDistributedJoinSelfTest.class);
         suite.addTestSuite(IgniteCacheQueryNodeFailTest.class);
         suite.addTestSuite(IgniteCacheClientQueryReplicatedNodeRestartSelfTest.class);
-        suite.addTestSuite(GridCacheReduceQueryMultithreadedSelfTest.class);
         suite.addTestSuite(GridCacheCrossCacheQuerySelfTest.class);
         suite.addTestSuite(GridCacheQuerySerializationSelfTest.class);
         suite.addTestSuite(IgniteBinaryObjectFieldsQuerySelfTest.class);
@@ -116,6 +126,17 @@ public class IgniteCacheQuerySelfTestSuite extends TestSuite {
         suite.addTestSuite(IgniteCacheQueryH2IndexingLeakTest.class);
         suite.addTestSuite(IgniteCacheQueryNoRebalanceSelfTest.class);
 
+        suite.addTestSuite(IgniteCacheJoinQueryWithAffinityKeyTest.class);
+        suite.addTestSuite(IgniteCacheDistributedJoinCollocatedAndNotTest.class);
+        suite.addTestSuite(IgniteCacheDistributedJoinPartitionedAndReplicatedTest.class);
+        suite.addTestSuite(IgniteCacheDistributedJoinQueryConditionsTest.class);
+        suite.addTestSuite(IgniteCacheDistributedJoinTest.class);
+        suite.addTestSuite(IgniteCacheJoinPartitionedAndReplicatedTest.class);
+        suite.addTestSuite(IgniteCacheDistributedJoinNoIndexTest.class);
+        suite.addTestSuite(IgniteCrossCachesJoinsQueryTest.class);
+        suite.addTestSuite(IgniteCacheCrossCacheJoinRandomTest.class);
+        suite.addTestSuite(IgniteCacheDistributedJoinCustomAffinityMapper.class);
+
         return suite;
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite2.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite2.java b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite2.java
index e5f4027..7cb1adb 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite2.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite2.java
@@ -40,10 +40,6 @@ import org.apache.ignite.internal.processors.cache.distributed.replicated.Ignite
 import org.apache.ignite.internal.processors.cache.distributed.replicated.IgniteCacheReplicatedFieldsQuerySelfTest;
 import org.apache.ignite.internal.processors.cache.local.IgniteCacheLocalFieldsQuerySelfTest;
 import org.apache.ignite.internal.processors.cache.query.GridCacheSwapScanQuerySelfTest;
-import org.apache.ignite.internal.processors.cache.reducefields.GridCacheReduceFieldsQueryAtomicSelfTest;
-import org.apache.ignite.internal.processors.cache.reducefields.GridCacheReduceFieldsQueryLocalSelfTest;
-import org.apache.ignite.internal.processors.cache.reducefields.GridCacheReduceFieldsQueryPartitionedSelfTest;
-import org.apache.ignite.internal.processors.cache.reducefields.GridCacheReduceFieldsQueryReplicatedSelfTest;
 import org.apache.ignite.internal.processors.query.h2.sql.BaseH2CompareQueryTest;
 import org.apache.ignite.internal.processors.query.h2.sql.H2CompareBigQueryTest;
 import org.apache.ignite.spi.communication.tcp.GridOrderedMessageCancelSelfTest;
@@ -73,12 +69,6 @@ public class IgniteCacheQuerySelfTestSuite2 extends TestSuite {
         suite.addTestSuite(IgniteCachePartitionedFieldsQueryP2PEnabledSelfTest.class);
         suite.addTestSuite(IgniteCacheFieldsQueryNoDataSelfTest.class);
 
-        // Reduce fields queries.
-        suite.addTestSuite(GridCacheReduceFieldsQueryLocalSelfTest.class);
-        suite.addTestSuite(GridCacheReduceFieldsQueryPartitionedSelfTest.class);
-        suite.addTestSuite(GridCacheReduceFieldsQueryAtomicSelfTest.class);
-        suite.addTestSuite(GridCacheReduceFieldsQueryReplicatedSelfTest.class);
-
         suite.addTestSuite(GridCacheQueryIndexingDisabledSelfTest.class);
 
         suite.addTestSuite(GridCacheSwapScanQuerySelfTest.class);

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteBenchmarkUtils.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteBenchmarkUtils.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteBenchmarkUtils.java
index 0b7568b..af060d4 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteBenchmarkUtils.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteBenchmarkUtils.java
@@ -26,6 +26,10 @@ import org.apache.ignite.transactions.TransactionConcurrency;
 import org.apache.ignite.transactions.TransactionIsolation;
 import org.apache.ignite.transactions.TransactionOptimisticException;
 import org.apache.ignite.transactions.TransactionRollbackException;
+import org.apache.ignite.yardstick.cache.IgniteSqlQueryBenchmark;
+import org.apache.ignite.yardstick.cache.IgniteSqlQueryDistributedJoinBenchmark;
+import org.yardstickframework.BenchmarkDriverStartUp;
+import org.yardstickframework.BenchmarkServerStartUp;
 
 /**
  * Utils.
@@ -72,4 +76,45 @@ public class IgniteBenchmarkUtils {
             }
         }
     }
+
+//    /**
+//     * Starts driver for quick benchmarks testing.
+//     *
+//     * @param args Command line arguments.
+//     * @throws Exception If failed.
+//     */
+//    public static void main(String[] args) throws Exception {
+//        String[] args0 = {
+//            "-t", "1",
+//            "-w", "5",
+//            "-d", "5",
+//            "-dn", IgniteSqlQueryDistributedJoinBenchmark.class.getSimpleName(),
+//            "-r", "100",
+//            "-sn", "IgniteNode",
+//            "-bcj",
+//            "-cfg", "modules/yardstick/config/ignite-localhost-config.xml"};
+//
+//        BenchmarkDriverStartUp.main(args0);
+//    }
+
+    /**
+     * Starts driver for quick benchmarks testing.
+     *
+     * @param args Command line arguments.
+     * @throws Exception If failed.
+     */
+    public static void main(String[] args) throws Exception {
+        String[] args0 = {
+            "-t", "15",
+            "-w", "30",
+            "-d", "60",
+            "-cl",
+            "-pr", "ThroughputLatencyProbe",
+            "-dn", IgniteSqlQueryBenchmark.class.getSimpleName(),
+            "-r", "100000",
+            "-sn", "IgniteNode",
+            "-cfg", "modules/yardstick/config/ignite-localhost-config.xml"};
+
+        BenchmarkDriverStartUp.main(args0);
+    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryDistributedJoinBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryDistributedJoinBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryDistributedJoinBenchmark.java
new file mode 100644
index 0000000..1f009e6
--- /dev/null
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryDistributedJoinBenchmark.java
@@ -0,0 +1,184 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.yardstick.cache;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteDataStreamer;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.yardstick.cache.model.Organization;
+import org.apache.ignite.yardstick.cache.model.Person;
+import org.yardstickframework.BenchmarkConfiguration;
+
+import static org.yardstickframework.BenchmarkUtils.println;
+
+/**
+ * Ignite benchmark that performs query operations with joins.
+ */
+public class IgniteSqlQueryDistributedJoinBenchmark extends IgniteCacheAbstractBenchmark<Integer, Object> {
+    /** */
+    private int orgRange;
+
+    /** */
+    private boolean broadcast;
+
+    /** {@inheritDoc} */
+    @Override public void setUp(BenchmarkConfiguration cfg) throws Exception {
+        super.setUp(cfg);
+
+        broadcast = broadcastJoin();
+
+        println(cfg, "Populating query data...");
+
+        long start = System.nanoTime();
+
+        int personCnt = 0;
+
+        try (IgniteDataStreamer<Object, Object> dataLdr = ignite().dataStreamer(cache.getName())) {
+            orgRange = args.range() / 10;
+
+            if (orgRange <= 0)
+                throw new IllegalArgumentException();
+
+            // Populate organizations.
+            for (int orgId = 0; orgId < orgRange; orgId++) {
+                dataLdr.addData(orgId, new Organization(orgId, "org" + orgId));
+
+                if (orgId % 1000 == 0 && Thread.currentThread().isInterrupted())
+                    return;
+            }
+
+            dataLdr.flush();
+
+            // Populate persons.
+            for (int orgId = 0; orgId < orgRange; orgId++) {
+                int persons = orgId % 10 + 1;
+
+                for (int j = 0; j < persons; j++) {
+                    int personId = orgRange + personCnt++;
+
+                    Person p = new Person(personId,
+                        orgId,
+                        "firstName" + personId,
+                        "lastName" + personId, 1000);
+
+                    dataLdr.addData(personId, p);
+                }
+
+                if (personCnt % 100000 == 0)
+                    println(cfg, "Populated persons: " + personCnt);
+
+                if (orgId % 1000 == 0 && Thread.currentThread().isInterrupted())
+                    return;
+            }
+
+            dataLdr.close();
+        }
+
+        println(cfg, "Finished populating join query [orgCnt=" + orgRange +
+            ", personCnt=" + personCnt +
+            ", broadcastJoin=" + broadcast +
+            ", time=" + ((System.nanoTime() - start) / 1_000_000) + "ms]");
+
+        executeQueryJoin(0, broadcast, true);
+    }
+
+    /**
+     * @return Broadcast join flag.
+     */
+    protected boolean broadcastJoin() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean test(Map<Object, Object> ctx) throws Exception {
+        int orgId = nextRandom(orgRange);
+
+        Collection<List<?>> res = executeQueryJoin(orgId, broadcast, false);
+
+        int persons = orgId % 10 + 1;
+
+        if (res.size() != persons)
+            throw new Exception("Invalid join result [orgId=" + orgId + ", resSize=" + res.size() + ']');
+
+        for (List<?> l : res) {
+            int orgId0 = (Integer)l.get(1);
+
+            if (orgId != orgId0)
+                throw new Exception("Invalid join result [orgId=" + orgId + ", res=" + l + ']');
+        }
+
+        return true;
+    }
+
+    /**
+     * @param orgId Organization ID.
+     * @param broadcast Broadcast join flag.
+     * @param planOnly If {@code true} just prints query plan.
+     * @return Query results.
+     * @throws Exception If failed.
+     */
+    private Collection<List<?>> executeQueryJoin(int orgId, boolean broadcast, boolean planOnly) throws Exception {
+        SqlFieldsQuery qry;
+
+        String sql;
+
+        if (broadcast) {
+            sql = "select p.id, p.orgId, p.firstName, p.lastName, o.name " +
+                "from Person p " +
+                "join Organization o " +
+                "on p.orgId = o.id " +
+                "where o.id=?";
+        }
+        else {
+            sql = "select p.id, p.orgId, p.firstName, p.lastName, o.name " +
+                "from Organization o " +
+                "join Person p " +
+                "on p.orgId = o._key " +
+                "where o._key=?";
+        }
+
+        qry = new SqlFieldsQuery(planOnly ? ("explain " + sql) : sql);
+        qry.setDistributedJoins(true);
+        qry.setArgs(orgId);
+
+        if (planOnly) {
+            String plan = (String)cache.query(qry).getAll().get(0).get(0);
+
+            println("Query execution plan:\n" + plan);
+
+            if (broadcast) {
+                if (plan.contains("batched:unicast") || !plan.contains("batched:broadcast"))
+                    throw new Exception("Unexpected query plan: " + plan);
+            }
+            else if (!plan.contains("batched:unicast") || plan.contains("batched:broadcast"))
+                throw new Exception("Unexpected query plan: " + plan);
+
+            return null;
+        }
+        else
+            return cache.query(qry).getAll();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected IgniteCache<Integer, Object> cache() {
+        return ignite().cache("query");
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryDistributedJoinBroadcastBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryDistributedJoinBroadcastBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryDistributedJoinBroadcastBenchmark.java
new file mode 100644
index 0000000..8b03e3e
--- /dev/null
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryDistributedJoinBroadcastBenchmark.java
@@ -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.
+ */
+
+package org.apache.ignite.yardstick.cache;
+
+/**
+ *
+ */
+public class IgniteSqlQueryDistributedJoinBroadcastBenchmark extends IgniteSqlQueryDistributedJoinBenchmark {
+    /** {@inheritDoc} */
+    @Override protected boolean broadcastJoin() {
+        return true;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/parent/pom.xml
----------------------------------------------------------------------
diff --git a/parent/pom.xml b/parent/pom.xml
index 3e9a71c..ebd6bf0 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -35,8 +35,9 @@
 
     <properties>
         <ignite.edition>fabric</ignite.edition>
-
-        <!-- Build parameters. -->
+        <hadoop.version>2.4.1</hadoop.version>
+        <spark.version>1.5.2</spark.version>
+        <spring.version>4.1.0.RELEASE</spring.version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <maven.build.timestamp.format>MMMM d yyyy</maven.build.timestamp.format>
         <doxygen.exec>doxygen</doxygen.exec>
@@ -69,7 +70,7 @@
         <guava.version>18.0</guava.version>
         <guava14.version>14.0.1</guava14.version>
         <guava16.version>16.0.1</guava16.version>
-        <h2.version>1.3.175</h2.version>
+        <h2.version>1.4.191</h2.version>
         <hadoop.version>2.4.1</hadoop.version>
         <httpclient.version>4.5.1</httpclient.version>
         <httpcore.version>4.4.3</httpcore.version>
@@ -188,6 +189,23 @@
         </developer>
     </developers>
 
+    <repositories>
+        <repository>
+            <releases>
+                <enabled>false</enabled>
+            </releases>
+            <snapshots>
+                <enabled>true</enabled>
+                <updatePolicy>always</updatePolicy>
+                <checksumPolicy>ignore</checksumPolicy>
+            </snapshots>
+            <id>h2database.com</id>
+            <name>Snapshot repository on h2database.com</name>
+            <url>http://h2database.com/m2-repo</url>
+            <layout>default</layout>
+        </repository>
+    </repositories>
+
     <dependencyManagement>
         <dependencies>
             <dependency>


[38/50] [abbrv] ignite git commit: JB Annotations versions hotfix (cherry picked from commit bbdf145)

Posted by vo...@apache.org.
JB Annotations versions hotfix
(cherry picked from commit bbdf145)


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

Branch: refs/heads/ignite-3553
Commit: a32eb87da4f07f327c92f788a0169507e3b125d9
Parents: 42f42ea
Author: Anton Vinogradov <av...@apache.org>
Authored: Wed Jul 27 11:00:22 2016 +0300
Committer: Anton Vinogradov <av...@apache.org>
Committed: Wed Jul 27 13:36:33 2016 +0300

----------------------------------------------------------------------
 parent/pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/a32eb87d/parent/pom.xml
----------------------------------------------------------------------
diff --git a/parent/pom.xml b/parent/pom.xml
index 17f66c9..246c36b 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -78,7 +78,7 @@
         <jackson2.version>2.7.5</jackson2.version>
         <javax.cache.bundle.version>1.0.0_1</javax.cache.bundle.version>
         <javax.cache.version>1.0.0</javax.cache.version>
-        <jetbrains.annotations.version>15.0</jetbrains.annotations.version>
+        <jetbrains.annotations.version>13.0</jetbrains.annotations.version>
         <jetty.version>9.2.11.v20150529</jetty.version>
         <jms.spec.version>1.1.1</jms.spec.version>
         <jsch.bundle.version>0.1.53_1</jsch.bundle.version>


[15/50] [abbrv] ignite git commit: ignite-1232 Distributed SQL joins implementation

Posted by vo...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2CollocationModel.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2CollocationModel.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2CollocationModel.java
new file mode 100644
index 0000000..0f76316
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2CollocationModel.java
@@ -0,0 +1,783 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.processors.query.h2.opt;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import javax.cache.CacheException;
+import org.apache.ignite.internal.processors.query.h2.sql.GridSqlQueryParser;
+import org.apache.ignite.internal.util.typedef.F;
+import org.h2.command.dml.Query;
+import org.h2.command.dml.Select;
+import org.h2.command.dml.SelectUnion;
+import org.h2.expression.Comparison;
+import org.h2.expression.Expression;
+import org.h2.expression.ExpressionColumn;
+import org.h2.index.IndexCondition;
+import org.h2.index.ViewIndex;
+import org.h2.table.Column;
+import org.h2.table.IndexColumn;
+import org.h2.table.SubQueryInfo;
+import org.h2.table.Table;
+import org.h2.table.TableFilter;
+import org.h2.table.TableView;
+
+/**
+ * Collocation model for a query.
+ */
+public final class GridH2CollocationModel {
+    /** */
+    public static final int MULTIPLIER_COLLOCATED = 1;
+
+    /** */
+    private static final int MULTIPLIER_UNICAST = 50;
+
+    /** */
+    private static final int MULTIPLIER_BROADCAST = 200;
+
+    /** */
+    private static final int MULTIPLIER_REPLICATED_NOT_LAST = 10_000;
+
+    /** */
+    private final GridH2CollocationModel upper;
+
+    /** */
+    private final int filter;
+
+    /** */
+    private final boolean view;
+
+    /** */
+    private int multiplier;
+
+    /** */
+    private Type type;
+
+    /** */
+    private GridH2CollocationModel[] children;
+
+    /** */
+    private TableFilter[] childFilters;
+
+    /** */
+    private List<GridH2CollocationModel> unions;
+
+    /** */
+    private Select select;
+
+    /** */
+    private final boolean validate;
+
+    /**
+     * @param upper Upper.
+     * @param filter Filter.
+     * @param view This model will be a subquery (or top level query) and must contain child filters.
+     * @param validate Query validation flag.
+     */
+    private GridH2CollocationModel(GridH2CollocationModel upper, int filter, boolean view, boolean validate) {
+        this.upper = upper;
+        this.filter = filter;
+        this.view = view;
+        this.validate = validate;
+    }
+
+    /**
+     * @param upper Upper.
+     * @param filter Filter.
+     * @param unions Unions.
+     * @param view This model will be a subquery (or top level query) and must contain child filters.
+     * @param validate Query validation flag.
+     * @return Created child collocation model.
+     */
+    private static GridH2CollocationModel createChildModel(GridH2CollocationModel upper,
+        int filter,
+        List<GridH2CollocationModel> unions,
+        boolean view,
+        boolean validate) {
+        GridH2CollocationModel child = new GridH2CollocationModel(upper, filter, view, validate);
+
+        if (unions != null) {
+            // Bind created child to unions.
+            assert upper == null || upper.child(filter, false) != null || unions.isEmpty();
+
+            if (upper != null && unions.isEmpty()) {
+                assert upper.child(filter, false) == null;
+
+                upper.children[filter] = child;
+            }
+
+            unions.add(child);
+
+            child.unions = unions;
+        }
+        else if (upper != null) {
+            // Bind created child to upper model.
+            assert upper.child(filter, false) == null;
+
+            upper.children[filter] = child;
+        }
+
+        return child;
+    }
+
+    /**
+     * @param childFilters New child filters.
+     * @return {@code true} If child filters were updated.
+     */
+    private boolean childFilters(TableFilter[] childFilters) {
+        assert childFilters != null;
+        assert view;
+
+        Select select = childFilters[0].getSelect();
+
+        assert this.select == null || this.select == select;
+
+        if (this.select == null) {
+            this.select = select;
+
+            assert this.childFilters == null;
+        }
+        else if (Arrays.equals(this.childFilters, childFilters))
+            return false;
+
+        if (this.childFilters == null) {
+            // We have to clone because H2 reuses array and reorders elements.
+            this.childFilters = childFilters.clone();
+
+            children = new GridH2CollocationModel[childFilters.length];
+        }
+        else {
+            assert this.childFilters.length == childFilters.length;
+
+            // We have to copy because H2 reuses array and reorders elements.
+            System.arraycopy(childFilters, 0, this.childFilters, 0, childFilters.length);
+
+            Arrays.fill(children, null);
+        }
+
+        // Reset results.
+        type = null;
+        multiplier = 0;
+
+        return true;
+    }
+
+    /**
+     * @param i Index.
+     * @param f Table filter.
+     * @return {@code true} If the child is not a table or view.
+     */
+    private boolean isChildTableOrView(int i, TableFilter f) {
+        if (f == null)
+            f = childFilters[i];
+
+        Table t = f.getTable();
+
+        return t.isView() || t instanceof GridH2Table;
+    }
+
+    /**
+     * Do the needed calculations.
+     */
+    private void calculate() {
+        if (type != null)
+            return;
+
+        if (view) { // We are at (sub-)query model.
+            assert childFilters != null;
+
+            boolean collocated = true;
+            boolean partitioned = false;
+            int maxMultiplier = MULTIPLIER_COLLOCATED;
+
+            for (int i = 0; i < childFilters.length; i++) {
+                GridH2CollocationModel child = child(i, true);
+
+                Type t = child.type(true);
+
+                if (child.multiplier == MULTIPLIER_REPLICATED_NOT_LAST)
+                    maxMultiplier = child.multiplier;
+
+                if (t.isPartitioned()) {
+                    partitioned = true;
+
+                    if (!t.isCollocated()) {
+                        collocated = false;
+
+                        int m = child.multiplier(true);
+
+                        if (m > maxMultiplier) {
+                            maxMultiplier = m;
+
+                            if (maxMultiplier == MULTIPLIER_REPLICATED_NOT_LAST)
+                                break;
+                        }
+                    }
+                }
+            }
+
+            type = Type.of(partitioned, collocated);
+            multiplier = maxMultiplier;
+        }
+        else {
+            assert upper != null;
+            assert childFilters == null;
+
+            // We are at table instance.
+            GridH2Table tbl = (GridH2Table)upper.childFilters[filter].getTable();
+
+            // Only partitioned tables will do distributed joins.
+            if (!tbl.isPartitioned()) {
+                type = Type.REPLICATED;
+                multiplier = MULTIPLIER_COLLOCATED;
+
+                return;
+            }
+
+            // If we are the first partitioned table in a join, then we are "base" for all the rest partitioned tables
+            // which will need to get remote result (if there is no affinity condition). Since this query is broadcasted
+            // to all the affinity nodes the "base" does not need to get remote results.
+            if (!upper.findPartitionedTableBefore(filter)) {
+                type = Type.PARTITIONED_COLLOCATED;
+                multiplier = MULTIPLIER_COLLOCATED;
+            }
+            else {
+                // It is enough to make sure that our previous join by affinity key is collocated, then we are
+                // collocated. If we at least have affinity key condition, then we do unicast which is cheaper.
+                switch (upper.joinedWithCollocated(filter)) {
+                    case JOINED_WITH_COLLOCATED:
+                        type = Type.PARTITIONED_COLLOCATED;
+                        multiplier = MULTIPLIER_COLLOCATED;
+
+                        break;
+
+                    case HAS_AFFINITY_CONDITION:
+                        type = Type.PARTITIONED_NOT_COLLOCATED;
+                        multiplier = MULTIPLIER_UNICAST;
+
+                        break;
+
+                    case NONE:
+                        type = Type.PARTITIONED_NOT_COLLOCATED;
+                        multiplier = MULTIPLIER_BROADCAST;
+
+                        break;
+
+                    default:
+                        throw new IllegalStateException();
+                }
+            }
+
+            if (upper.previousReplicated(filter))
+                multiplier = MULTIPLIER_REPLICATED_NOT_LAST;
+        }
+    }
+
+    /**
+     * @param f Current filter.
+     * @return {@code true} If partitioned table was found.
+     */
+    private boolean findPartitionedTableBefore(int f) {
+        for (int i = 0; i < f; i++) {
+            GridH2CollocationModel child = child(i, true);
+
+            // The c can be null if it is not a GridH2Table and not a sub-query,
+            // it is a some kind of function table or anything else that considered replicated.
+            if (child != null && child.type(true).isPartitioned())
+                return true;
+        }
+
+        // We have to search globally in upper queries as well.
+        return upper != null && upper.findPartitionedTableBefore(filter);
+    }
+
+    /**
+     * @param f Current filter.
+     * @return {@code true} If previous table is REPLICATED.
+     */
+    private boolean previousReplicated(int f) {
+        if (f > 0 && child(f - 1, true).type(true) == Type.REPLICATED)
+            return true;
+
+        return upper != null && upper.previousReplicated(filter);
+    }
+
+    /**
+     * @param f Filter.
+     * @return Affinity join type.
+     */
+    private Affinity joinedWithCollocated(int f) {
+        TableFilter tf = childFilters[f];
+
+        GridH2Table tbl = (GridH2Table)tf.getTable();
+
+        if (validate) {
+            if (tbl.rowDescriptor().context().customAffinityMapper())
+                throw customAffinityError(tbl.spaceName());
+
+            if (F.isEmpty(tf.getIndexConditions())) {
+                throw new CacheException("Failed to prepare distributed join query: " +
+                    "join condition does not use index [joinedCache=" + tbl.spaceName() +
+                    ", plan=" + tf.getSelect().getPlanSQL() + ']');
+            }
+        }
+
+        IndexColumn affCol = tbl.getAffinityKeyColumn();
+
+        boolean affKeyCondFound = false;
+
+        if (affCol != null) {
+            ArrayList<IndexCondition> idxConditions = tf.getIndexConditions();
+
+            int affColId = affCol.column.getColumnId();
+
+            for (int i = 0; i < idxConditions.size(); i++) {
+                IndexCondition c = idxConditions.get(i);
+
+                int cmpType = c.getCompareType();
+
+                if ((cmpType == Comparison.EQUAL || cmpType == Comparison.EQUAL_NULL_SAFE) &&
+                    c.getColumn().getColumnId() == affColId && c.isEvaluatable()) {
+                    affKeyCondFound = true;
+
+                    Expression exp = c.getExpression();
+
+                    exp = exp.getNonAliasExpression();
+
+                    if (exp instanceof ExpressionColumn) {
+                        ExpressionColumn expCol = (ExpressionColumn)exp;
+
+                        // This is one of our previous joins.
+                        TableFilter prevJoin = expCol.getTableFilter();
+
+                        if (prevJoin != null) {
+                            GridH2CollocationModel cm = child(indexOf(prevJoin), true);
+
+                            if (cm != null) {
+                                Type t = cm.type(true);
+
+                                if (t.isPartitioned() && t.isCollocated() && isAffinityColumn(prevJoin, expCol, validate))
+                                    return Affinity.JOINED_WITH_COLLOCATED;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        return affKeyCondFound ? Affinity.HAS_AFFINITY_CONDITION : Affinity.NONE;
+    }
+
+    /**
+     * @param f Table filter.
+     * @return Index.
+     */
+    private int indexOf(TableFilter f) {
+        for (int i = 0; i < childFilters.length; i++) {
+            if (childFilters[i] == f)
+                return i;
+        }
+
+        throw new IllegalStateException();
+    }
+
+    /**
+     * @param f Table filter.
+     * @param expCol Expression column.
+     * @param validate Query validation flag.
+     * @return {@code true} It it is an affinity column.
+     */
+    private static boolean isAffinityColumn(TableFilter f, ExpressionColumn expCol, boolean validate) {
+        Column col = expCol.getColumn();
+
+        if (col == null)
+            return false;
+
+        Table t = col.getTable();
+
+        if (t.isView()) {
+            Query qry;
+
+            if (f.getIndex() != null)
+                qry = getSubQuery(f);
+            else
+                qry = GridSqlQueryParser.VIEW_QUERY.get((TableView)t);
+
+            return isAffinityColumn(qry, expCol, validate);
+        }
+
+        if (t instanceof GridH2Table) {
+            if (validate && ((GridH2Table)t).rowDescriptor().context().customAffinityMapper())
+                throw customAffinityError(((GridH2Table)t).spaceName());
+
+            IndexColumn affCol = ((GridH2Table)t).getAffinityKeyColumn();
+
+            return affCol != null && col.getColumnId() == affCol.column.getColumnId();
+        }
+
+        return false;
+    }
+
+    /**
+     * @param qry Query.
+     * @param expCol Expression column.
+     * @param validate Query validation flag.
+     * @return {@code true} It it is an affinity column.
+     */
+    private static boolean isAffinityColumn(Query qry, ExpressionColumn expCol, boolean validate) {
+        if (qry.isUnion()) {
+            SelectUnion union = (SelectUnion)qry;
+
+            return isAffinityColumn(union.getLeft(), expCol, validate) && isAffinityColumn(union.getRight(), expCol, validate);
+        }
+
+        Expression exp = qry.getExpressions().get(expCol.getColumn().getColumnId()).getNonAliasExpression();
+
+        if (exp instanceof ExpressionColumn) {
+            expCol = (ExpressionColumn)exp;
+
+            return isAffinityColumn(expCol.getTableFilter(), expCol, validate);
+        }
+
+        return false;
+    }
+
+    /**
+     * @return Multiplier.
+     */
+    public int calculateMultiplier() {
+        // We don't need multiplier for union here because it will be summarized in H2.
+        return multiplier(false);
+    }
+
+    /**
+     * @param withUnion With respect to union.
+     * @return Multiplier.
+     */
+    private int multiplier(boolean withUnion) {
+        calculate();
+
+        assert multiplier != 0;
+
+        if (withUnion && unions != null) {
+            int maxMultiplier = 0;
+
+            for (int i = 0; i < unions.size(); i++) {
+                int m = unions.get(i).multiplier(false);
+
+                if (m > maxMultiplier)
+                    maxMultiplier = m;
+            }
+
+            return maxMultiplier;
+        }
+
+        return multiplier;
+    }
+
+    /**
+     * @param withUnion With respect to union.
+     * @return Type.
+     */
+    private Type type(boolean withUnion) {
+        calculate();
+
+        assert type != null;
+
+        if (withUnion && unions != null) {
+            Type left = unions.get(0).type(false);
+
+            for (int i = 1; i < unions.size(); i++) {
+                Type right = unions.get(i).type(false);
+
+                if (!left.isCollocated() || !right.isCollocated()) {
+                    left = Type.PARTITIONED_NOT_COLLOCATED;
+
+                    break;
+                }
+                else if (!left.isPartitioned() && !right.isPartitioned())
+                    left = Type.REPLICATED;
+                else
+                    left = Type.PARTITIONED_COLLOCATED;
+            }
+
+            return left;
+        }
+
+        return type;
+    }
+
+    /**
+     * @param i Index.
+     * @param create Create child if needed.
+     * @return Child collocation.
+     */
+    private GridH2CollocationModel child(int i, boolean create) {
+        GridH2CollocationModel child = children[i];
+
+        if (child == null && create && isChildTableOrView(i, null)) {
+            TableFilter f = childFilters[i];
+
+            if (f.getTable().isView()) {
+                if (f.getIndex() == null) {
+                    // If we don't have view index yet, then we just creating empty model and it must be filled later.
+                    child = createChildModel(this, i, null, true, validate);
+                }
+                else
+                    child = buildCollocationModel(this, i, getSubQuery(f), null, validate);
+            }
+            else
+                child = createChildModel(this, i, null, false, validate);
+
+            assert child != null;
+            assert children[i] == child;
+        }
+
+        return child;
+    }
+
+    /**
+     * @param f Table filter.
+     * @return Sub-query.
+     */
+    private static Query getSubQuery(TableFilter f) {
+        return ((ViewIndex)f.getIndex()).getQuery();
+    }
+
+    /**
+     * @return Unions list.
+     */
+    private List<GridH2CollocationModel> getOrCreateUnions() {
+        if (unions == null) {
+            unions = new ArrayList<>(4);
+
+            unions.add(this);
+        }
+
+        return unions;
+    }
+
+    /**
+     * @param qctx Query context.
+     * @param info Sub-query info.
+     * @param filters Filters.
+     * @param filter Filter.
+     * @param validate Query validation flag.
+     * @return Collocation.
+     */
+    public static GridH2CollocationModel buildCollocationModel(GridH2QueryContext qctx, SubQueryInfo info,
+        TableFilter[] filters, int filter, boolean validate) {
+        GridH2CollocationModel cm;
+
+        if (info != null) {
+            // Go up until we reach the root query.
+            cm = buildCollocationModel(qctx, info.getUpper(), info.getFilters(), info.getFilter(), validate);
+        }
+        else {
+            // We are at the root query.
+            cm = qctx.queryCollocationModel();
+
+            if (cm == null) {
+                cm = createChildModel(null, -1, null, true, validate);
+
+                qctx.queryCollocationModel(cm);
+            }
+        }
+
+        assert cm.view;
+
+        Select select = filters[0].getSelect();
+
+        // Handle union. We have to rely on fact that select will be the same on uppermost select.
+        // For sub-queries we will drop collocation models, so that they will be recalculated anyways.
+        if (cm.select != null && cm.select != select) {
+            List<GridH2CollocationModel> unions = cm.getOrCreateUnions();
+
+            // Try to find this select in existing unions.
+            // Start with 1 because at 0 it always will be c.
+            for (int i = 1; i < unions.size(); i++) {
+                GridH2CollocationModel u = unions.get(i);
+
+                if (u.select == select) {
+                    cm = u;
+
+                    break;
+                }
+            }
+
+            // Nothing was found, need to create new child in union.
+            if (cm.select != select)
+                cm = createChildModel(cm.upper, cm.filter, unions, true, validate);
+        }
+
+        cm.childFilters(filters);
+
+        return cm.child(filter, true);
+    }
+
+    /**
+     * @param qry Query.
+     * @return {@code true} If the query is collocated.
+     */
+    public static boolean isCollocated(Query qry) {
+        GridH2CollocationModel mdl = buildCollocationModel(null, -1, qry, null, true);
+
+        Type type = mdl.type(true);
+
+        if (!type.isCollocated() && mdl.multiplier == MULTIPLIER_REPLICATED_NOT_LAST)
+            throw new CacheException("Failed to execute query: for distributed join " +
+                "all REPLICATED caches must be at the end of the joined tables list.");
+
+        return type.isCollocated();
+    }
+
+    /**
+     * @param upper Upper.
+     * @param filter Filter.
+     * @param qry Query.
+     * @param unions Unions.
+     * @param validate Query validation flag.
+     * @return Built model.
+     */
+    private static GridH2CollocationModel buildCollocationModel(GridH2CollocationModel upper,
+        int filter,
+        Query qry,
+        List<GridH2CollocationModel> unions,
+        boolean validate) {
+        if (qry.isUnion()) {
+            if (unions == null)
+                unions = new ArrayList<>();
+
+            SelectUnion union = (SelectUnion)qry;
+
+            GridH2CollocationModel left = buildCollocationModel(upper, filter, union.getLeft(), unions, validate);
+            GridH2CollocationModel right = buildCollocationModel(upper, filter, union.getRight(), unions, validate);
+
+            assert left != null;
+            assert right != null;
+
+            return upper != null ? upper : left;
+        }
+
+        Select select = (Select)qry;
+
+        List<TableFilter> list = new ArrayList<>();
+
+        for (TableFilter f = select.getTopTableFilter(); f != null; f = f.getJoin())
+            list.add(f);
+
+        TableFilter[] filters = list.toArray(new TableFilter[list.size()]);
+
+        GridH2CollocationModel cm = createChildModel(upper, filter, unions, true, validate);
+
+        cm.childFilters(filters);
+
+        for (int i = 0; i < filters.length; i++) {
+            TableFilter f = filters[i];
+
+            if (f.getTable().isView())
+                buildCollocationModel(cm, i, getSubQuery(f), null, validate);
+            else if (f.getTable() instanceof GridH2Table)
+                createChildModel(cm, i, null, false, validate);
+        }
+
+        return upper != null ? upper : cm;
+    }
+
+    /**
+     * @param cacheName Cache name.
+     * @return Error.
+     */
+    private static CacheException customAffinityError(String cacheName) {
+        return new CacheException("Failed to prepare distributed join query: can not use distributed joins for cache " +
+            "with custom AffinityKeyMapper configured. " +
+            "Please use AffinityKeyMapped annotation instead [cache=" + cacheName + ']');
+    }
+
+    /**
+     * Collocation type.
+     */
+    private enum Type {
+        /** */
+        PARTITIONED_COLLOCATED(true, true),
+
+        /** */
+        PARTITIONED_NOT_COLLOCATED(true, false),
+
+        /** */
+        REPLICATED(false, true);
+
+        /** */
+        private final boolean partitioned;
+
+        /** */
+        private final boolean collocated;
+
+        /**
+         * @param partitioned Partitioned.
+         * @param collocated Collocated.
+         */
+        Type(boolean partitioned, boolean collocated) {
+            this.partitioned = partitioned;
+            this.collocated = collocated;
+        }
+
+        /**
+         * @return {@code true} If partitioned.
+         */
+        public boolean isPartitioned() {
+            return partitioned;
+        }
+
+        /**
+         * @return {@code true} If collocated.
+         */
+        public boolean isCollocated() {
+            return collocated;
+        }
+
+        /**
+         * @param partitioned Partitioned.
+         * @param collocated Collocated.
+         * @return Type.
+         */
+        static Type of(boolean partitioned, boolean collocated) {
+            if (collocated)
+                return partitioned ? Type.PARTITIONED_COLLOCATED : Type.REPLICATED;
+
+            assert partitioned;
+
+            return Type.PARTITIONED_NOT_COLLOCATED;
+        }
+    }
+
+    /**
+     * Affinity of a table relative to previous joined tables.
+     */
+    private enum Affinity {
+        /** */
+        NONE,
+
+        /** */
+        HAS_AFFINITY_CONDITION,
+
+        /** */
+        JOINED_WITH_COLLOCATED
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Cursor.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Cursor.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Cursor.java
index a2f60c4..66d5736 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Cursor.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Cursor.java
@@ -28,23 +28,44 @@ import org.h2.result.SearchRow;
  */
 public class GridH2Cursor implements Cursor {
     /** */
-    private Iterator<GridH2Row> iter;
+    public static final Cursor EMPTY = new Cursor() {
+        @Override public Row get() {
+            return null;
+        }
+
+        @Override public SearchRow getSearchRow() {
+            return null;
+        }
+
+        @Override public boolean next() {
+            return false;
+        }
+
+        @Override public boolean previous() {
+            return false;
+        }
+    };
 
     /** */
-    private Row row;
+    protected Iterator<? extends Row> iter;
+
+    /** */
+    protected Row cur;
 
     /**
      * Constructor.
      *
      * @param iter Rows iterator.
      */
-    public GridH2Cursor(Iterator<GridH2Row> iter) {
+    public GridH2Cursor(Iterator<? extends Row> iter) {
+        assert iter != null;
+
         this.iter = iter;
     }
 
     /** {@inheritDoc} */
     @Override public Row get() {
-        return row;
+        return cur;
     }
 
     /** {@inheritDoc} */
@@ -54,12 +75,9 @@ public class GridH2Cursor implements Cursor {
 
     /** {@inheritDoc} */
     @Override public boolean next() {
-        row = null;
-
-        if (iter.hasNext())
-            row = iter.next();
+        cur = iter.hasNext() ? iter.next() : null;
 
-        return row != null;
+        return cur != null;
     }
 
     /** {@inheritDoc} */

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2DefaultTableEngine.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2DefaultTableEngine.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2DefaultTableEngine.java
new file mode 100644
index 0000000..f53f1b3
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2DefaultTableEngine.java
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.processors.query.h2.opt;
+
+import org.h2.api.TableEngine;
+import org.h2.command.ddl.CreateTableData;
+import org.h2.table.RegularTable;
+import org.h2.table.Table;
+
+/**
+ * Default table engine.
+ */
+public class GridH2DefaultTableEngine implements TableEngine {
+    /** {@inheritDoc} */
+    @Override public Table createTable(CreateTableData data) {
+        assert !data.persistData && !data.persistIndexes;
+
+        if (data.isHidden && data.id == 0 && "SYS".equals(data.tableName))
+            return new GridH2MetaTable(data);
+
+        return new RegularTable(data);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2IndexBase.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2IndexBase.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2IndexBase.java
index fbf7c7c..c29239f 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2IndexBase.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2IndexBase.java
@@ -17,49 +17,164 @@
 
 package org.apache.ignite.internal.processors.query.h2.opt;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentNavigableMap;
+import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import javax.cache.CacheException;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteInterruptedException;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.GridTopic;
+import org.apache.ignite.internal.IgniteInterruptedCheckedException;
+import org.apache.ignite.internal.managers.communication.GridIoPolicy;
+import org.apache.ignite.internal.managers.communication.GridMessageListener;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridReservable;
+import org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2IndexRangeRequest;
+import org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2IndexRangeResponse;
+import org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2RowMessage;
+import org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2RowRange;
+import org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2RowRangeBounds;
+import org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2ValueMessage;
+import org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2ValueMessageFactory;
+import org.apache.ignite.internal.util.GridSpinBusyLock;
 import org.apache.ignite.internal.util.lang.GridFilteredIterator;
+import org.apache.ignite.internal.util.typedef.CIX2;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.CU;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.lang.IgniteBiPredicate;
+import org.apache.ignite.lang.IgniteBiTuple;
+import org.apache.ignite.logger.NullLogger;
+import org.apache.ignite.plugin.extensions.communication.Message;
 import org.apache.ignite.spi.indexing.IndexingQueryFilter;
 import org.h2.engine.Session;
 import org.h2.index.BaseIndex;
+import org.h2.index.Cursor;
+import org.h2.index.IndexCondition;
+import org.h2.index.IndexLookupBatch;
+import org.h2.index.ViewIndex;
 import org.h2.message.DbException;
 import org.h2.result.Row;
 import org.h2.result.SearchRow;
-import org.h2.result.SortOrder;
+import org.h2.table.IndexColumn;
+import org.h2.table.TableFilter;
+import org.h2.util.DoneFuture;
 import org.h2.value.Value;
+import org.h2.value.ValueNull;
 import org.jetbrains.annotations.Nullable;
 
+import static java.util.Collections.emptyIterator;
+import static java.util.Collections.singletonList;
+import static org.apache.ignite.internal.processors.query.h2.opt.GridH2AbstractKeyValueRow.KEY_COL;
+import static org.apache.ignite.internal.processors.query.h2.opt.GridH2AbstractKeyValueRow.VAL_COL;
+import static org.apache.ignite.internal.processors.query.h2.opt.GridH2CollocationModel.buildCollocationModel;
+import static org.apache.ignite.internal.processors.query.h2.opt.GridH2QueryType.MAP;
+import static org.apache.ignite.internal.processors.query.h2.opt.GridH2QueryType.PREPARE;
+import static org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2IndexRangeResponse.STATUS_ERROR;
+import static org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2IndexRangeResponse.STATUS_NOT_FOUND;
+import static org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2IndexRangeResponse.STATUS_OK;
+import static org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2RowRangeBounds.rangeBounds;
+import static org.h2.result.Row.MEMORY_CALCULATE;
+
 /**
  * Index base.
  */
 public abstract class GridH2IndexBase extends BaseIndex {
     /** */
-    protected static final ThreadLocal<IndexingQueryFilter> filters = new ThreadLocal<>();
+    private static final Object EXPLICIT_NULL = new Object();
+
+    /** */
+    private static final AtomicLong idxIdGen = new AtomicLong();
+
+    /** */
+    protected final long idxId = idxIdGen.incrementAndGet();
+
+    /** */
+    private final ThreadLocal<Object> snapshot = new ThreadLocal<>();
+
+    /** */
+    private Object msgTopic;
+
+    /** */
+    private GridMessageListener msgLsnr;
 
     /** */
-    protected final int keyCol;
+    private IgniteLogger log;
 
     /** */
-    protected final int valCol;
+    private final CIX2<ClusterNode,Message> locNodeHnd = new CIX2<ClusterNode,Message>() {
+        @Override public void applyx(ClusterNode clusterNode, Message msg) throws IgniteCheckedException {
+            onMessage0(clusterNode.id(), msg);
+        }
+    };
 
     /**
-     * @param keyCol Key column.
-     * @param valCol Value column.
+     * @param tbl Table.
      */
-    protected GridH2IndexBase(int keyCol, int valCol) {
-        this.keyCol = keyCol;
-        this.valCol = valCol;
+    protected final void initDistributedJoinMessaging(GridH2Table tbl) {
+        final GridH2RowDescriptor desc = tbl.rowDescriptor();
+
+        if (desc != null && desc.context() != null) {
+            GridKernalContext ctx = desc.context().kernalContext();
+
+            log = ctx.log(getClass());
+
+            msgTopic = new IgniteBiTuple<>(GridTopic.TOPIC_QUERY, tbl.identifier() + '.' + getName());
+
+            msgLsnr = new GridMessageListener() {
+                @Override public void onMessage(UUID nodeId, Object msg) {
+                    GridSpinBusyLock l = desc.indexing().busyLock();
+
+                    if (!l.enterBusy())
+                        return;
+
+                    try {
+                        onMessage0(nodeId, msg);
+                    }
+                    finally {
+                        l.leaveBusy();
+                    }
+                }
+            };
+
+            ctx.io().addMessageListener(msgTopic, msgLsnr);
+        }
+        else {
+            msgTopic = null;
+            msgLsnr = null;
+            log = new NullLogger();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public final void close(Session ses) {
+        // No-op. Actual index destruction must happen in method destroy.
     }
 
     /**
-     * Sets key filters for current thread.
-     *
-     * @param fs Filters.
+     * Attempts to destroys index and release all the resources.
+     * We use this method instead of {@link #close(Session)} because that method
+     * is used by H2 internally.
      */
-    public static void setFiltersForThread(IndexingQueryFilter fs) {
-        filters.set(fs);
+    public void destroy() {
+        if (msgLsnr != null)
+            kernalContext().io().removeMessageListener(msgTopic, msgLsnr);
     }
 
     /**
@@ -92,71 +207,121 @@ public abstract class GridH2IndexBase extends BaseIndex {
      * Takes or sets existing snapshot to be used in current thread.
      *
      * @param s Optional existing snapshot to use.
+     * @param qctx Query context.
      * @return Snapshot.
      */
-    public Object takeSnapshot(@Nullable Object s) {
+    public final Object takeSnapshot(@Nullable Object s, GridH2QueryContext qctx) {
+        assert snapshot.get() == null;
+
+        if (s == null)
+            s = doTakeSnapshot();
+
+        if (s != null) {
+            if (s instanceof GridReservable && !((GridReservable)s).reserve())
+                return null;
+
+            snapshot.set(s);
+
+            if (qctx != null)
+                qctx.putSnapshot(idxId, s);
+        }
+
         return s;
     }
 
     /**
-     * Releases snapshot for current thread.
+     * @param ses Session.
      */
-    public void releaseSnapshot() {
-        // No-op.
+    private static void clearViewIndexCache(Session ses) {
+        Map<Object,ViewIndex> viewIdxCache = ses.getViewIndexCache(true);
+
+        if (!viewIdxCache.isEmpty())
+            viewIdxCache.clear();
     }
 
-    /** {@inheritDoc} */
-    @Override public int compareRows(SearchRow rowData, SearchRow compare) {
-        if (rowData == compare)
-            return 0;
+    /**
+     * @param ses Session.
+     * @param filters All joined table filters.
+     * @param filter Current filter.
+     * @return Multiplier.
+     */
+    public int getDistributedMultiplier(Session ses, TableFilter[] filters, int filter) {
+        GridH2QueryContext qctx = GridH2QueryContext.get();
 
-        for (int i = 0, len = indexColumns.length; i < len; i++) {
-            int index = columnIds[i];
+        // We do complex optimizations with respect to distributed joins only on prepare stage
+        // because on run stage reordering of joined tables by Optimizer is explicitly disabled
+        // and thus multiplier will be always the same, so it will not affect choice of index.
+        // Query expressions can not be distributed as well.
+        if (qctx == null || qctx.type() != PREPARE || !qctx.distributedJoins() || ses.isPreparingQueryExpression())
+            return GridH2CollocationModel.MULTIPLIER_COLLOCATED;
 
-            Value v1 = rowData.getValue(index);
-            Value v2 = compare.getValue(index);
+        // We have to clear this cache because normally sub-query plan cost does not depend on anything
+        // other than index condition masks and sort order, but in our case it can depend on order
+        // of previous table filters.
+        clearViewIndexCache(ses);
 
-            if (v1 == null || v2 == null)
-                return 0;
+        assert filters != null;
 
-            int c = compareValues(v1, v2, indexColumns[i].sortType);
+        GridH2CollocationModel c = buildCollocationModel(qctx, ses.getSubQueryInfo(), filters, filter, false);
 
-            if (c != 0)
-                return c;
-        }
-        return 0;
+        return c.calculateMultiplier();
+    }
+
+    /** {@inheritDoc} */
+    @Override public GridH2Table getTable() {
+        return (GridH2Table)super.getTable();
+    }
+
+    /**
+     * Takes and returns actual snapshot or {@code null} if snapshots are not supported.
+     *
+     * @return Snapshot or {@code null}.
+     */
+    @Nullable protected abstract Object doTakeSnapshot();
+
+    /**
+     * @return Thread local snapshot.
+     */
+    @SuppressWarnings("unchecked")
+    protected <T> T threadLocalSnapshot() {
+        return (T)snapshot.get();
     }
 
     /**
-     * @param a First value.
-     * @param b Second value.
-     * @param sortType Sort type.
-     * @return Comparison result.
+     * Releases snapshot for current thread.
      */
-    private int compareValues(Value a, Value b, int sortType) {
-        if (a == b)
-            return 0;
+    public void releaseSnapshot() {
+        Object s = snapshot.get();
+
+        assert s != null;
 
-        int comp = table.compareTypeSave(a, b);
+        snapshot.remove();
 
-        if ((sortType & SortOrder.DESCENDING) != 0)
-            comp = -comp;
+        if (s instanceof GridReservable)
+            ((GridReservable)s).release();
 
-        return comp;
+        if (s instanceof AutoCloseable)
+            U.closeQuiet((AutoCloseable)s);
     }
 
     /**
      * Filters rows from expired ones and using predicate.
      *
      * @param iter Iterator over rows.
+     * @param filter Optional filter.
      * @return Filtered iterator.
      */
-    protected Iterator<GridH2Row> filter(Iterator<GridH2Row> iter) {
-        IgniteBiPredicate<Object, Object> p = null;
+    protected Iterator<GridH2Row> filter(Iterator<GridH2Row> iter, IndexingQueryFilter filter) {
+        return new FilteringIterator(iter, U.currentTimeMillis(), filter, getTable().spaceName());
+    }
 
-        IndexingQueryFilter f = filters.get();
+    /**
+     * @return Filter for currently running query or {@code null} if none.
+     */
+    protected static IndexingQueryFilter threadLocalFilter() {
+        GridH2QueryContext qctx = GridH2QueryContext.get();
 
-        return new FilteringIterator(iter, U.currentTimeMillis(), f);
+        return qctx != null ? qctx.filter() : null;
     }
 
     /** {@inheritDoc} */
@@ -194,10 +359,1125 @@ public abstract class GridH2IndexBase extends BaseIndex {
         return false;
     }
 
+    /** {@inheritDoc} */
+    @Override public IndexLookupBatch createLookupBatch(TableFilter filter) {
+        GridH2QueryContext qctx = GridH2QueryContext.get();
+
+        if (qctx == null || !qctx.distributedJoins() || !getTable().isPartitioned())
+            return null;
+
+        IndexColumn affCol = getTable().getAffinityKeyColumn();
+
+        int affColId;
+        boolean ucast;
+
+        if (affCol != null) {
+            affColId = affCol.column.getColumnId();
+            int[] masks = filter.getMasks();
+            ucast = masks != null && masks[affColId] == IndexCondition.EQUALITY;
+        }
+        else {
+            affColId = -1;
+            ucast = false;
+        }
+
+        GridCacheContext<?,?> cctx = getTable().rowDescriptor().context();
+
+        return new DistributedLookupBatch(cctx, ucast, affColId);
+    }
+
+    /**
+     * @param nodes Nodes.
+     * @param msg Message.
+     */
+    private void send(Collection<ClusterNode> nodes, Message msg) {
+        if (!getTable().rowDescriptor().indexing().send(msgTopic,
+            -1,
+            nodes,
+            msg,
+            null,
+            locNodeHnd,
+            GridIoPolicy.IDX_POOL,
+            false))
+            throw new GridH2RetryException("Failed to send message to nodes: " + nodes + ".");
+    }
+
+    /**
+     * @param nodeId Source node ID.
+     * @param msg Message.
+     */
+    private void onMessage0(UUID nodeId, Object msg) {
+        ClusterNode node = kernalContext().discovery().node(nodeId);
+
+        if (node == null)
+            return;
+
+        try {
+            if (msg instanceof GridH2IndexRangeRequest)
+                onIndexRangeRequest(node, (GridH2IndexRangeRequest)msg);
+            else if (msg instanceof GridH2IndexRangeResponse)
+                onIndexRangeResponse(node, (GridH2IndexRangeResponse)msg);
+        }
+        catch (Throwable th) {
+            U.error(log, "Failed to handle message[nodeId=" + nodeId + ", msg=" + msg + "]", th);
+
+            if (th instanceof Error)
+                throw th;
+        }
+    }
+
+    /**
+     * @return Kernal context.
+     */
+    private GridKernalContext kernalContext() {
+        return getTable().rowDescriptor().context().kernalContext();
+    }
+
+    /**
+     * @param node Requesting node.
+     * @param msg Request message.
+     */
+    private void onIndexRangeRequest(ClusterNode node, GridH2IndexRangeRequest msg) {
+        GridH2QueryContext qctx = GridH2QueryContext.get(kernalContext().localNodeId(),
+            msg.originNodeId(),
+            msg.queryId(),
+            MAP);
+
+        GridH2IndexRangeResponse res = new GridH2IndexRangeResponse();
+
+        res.originNodeId(msg.originNodeId());
+        res.queryId(msg.queryId());
+        res.batchLookupId(msg.batchLookupId());
+
+        if (qctx == null)
+            res.status(STATUS_NOT_FOUND);
+        else {
+            try {
+                RangeSource src;
+
+                if (msg.bounds() != null) {
+                    // This is the first request containing all the search rows.
+                    ConcurrentNavigableMap<GridSearchRowPointer, GridH2Row> snapshot0 = qctx.getSnapshot(idxId);
+
+                    assert !msg.bounds().isEmpty() : "empty bounds";
+
+                    src = new RangeSource(msg.bounds(), snapshot0, qctx.filter());
+                }
+                else {
+                    // This is request to fetch next portion of data.
+                    src = qctx.getSource(node.id(), msg.batchLookupId());
+
+                    assert src != null;
+                }
+
+                List<GridH2RowRange> ranges = new ArrayList<>();
+
+                int maxRows = qctx.pageSize();
+
+                assert maxRows > 0 : maxRows;
+
+                while (maxRows > 0) {
+                    GridH2RowRange range = src.next(maxRows);
+
+                    if (range == null)
+                        break;
+
+                    ranges.add(range);
+
+                    if (range.rows() != null)
+                        maxRows -= range.rows().size();
+                }
+
+                if (src.hasMoreRows()) {
+                    // Save source for future fetches.
+                    if (msg.bounds() != null)
+                        qctx.putSource(node.id(), msg.batchLookupId(), src);
+                }
+                else if (msg.bounds() == null) {
+                    // Drop saved source.
+                    qctx.putSource(node.id(), msg.batchLookupId(), null);
+                }
+
+                assert !ranges.isEmpty();
+
+                res.ranges(ranges);
+                res.status(STATUS_OK);
+            }
+            catch (Throwable th) {
+                U.error(log, "Failed to process request: " + msg, th);
+
+                res.error(th.getClass() + ": " + th.getMessage());
+                res.status(STATUS_ERROR);
+            }
+        }
+
+        send(singletonList(node), res);
+    }
+
+    /**
+     * @param node Responded node.
+     * @param msg Response message.
+     */
+    private void onIndexRangeResponse(ClusterNode node, GridH2IndexRangeResponse msg) {
+        GridH2QueryContext qctx = GridH2QueryContext.get(kernalContext().localNodeId(),
+            msg.originNodeId(), msg.queryId(), MAP);
+
+        if (qctx == null)
+            return;
+
+        Map<ClusterNode, RangeStream> streams = qctx.getStreams(msg.batchLookupId());
+
+        if (streams == null)
+            return;
+
+        RangeStream stream = streams.get(node);
+
+        assert stream != null;
+
+        stream.onResponse(msg);
+    }
+
+    /**
+     * @param v1 First value.
+     * @param v2 Second value.
+     * @return {@code true} If they equal.
+     */
+    private boolean equal(Value v1, Value v2) {
+        return v1 == v2 || (v1 != null && v2 != null && v1.compareTypeSafe(v2, getDatabase().getCompareMode()) == 0);
+    }
+
+    /**
+     * @param qctx Query context.
+     * @param batchLookupId Batch lookup ID.
+     * @return Index range request.
+     */
+    private static GridH2IndexRangeRequest createRequest(GridH2QueryContext qctx, int batchLookupId) {
+        GridH2IndexRangeRequest req = new GridH2IndexRangeRequest();
+
+        req.originNodeId(qctx.originNodeId());
+        req.queryId(qctx.queryId());
+        req.batchLookupId(batchLookupId);
+
+        return req;
+    }
+
+    /**
+     * @param qctx Query context.
+     * @param cctx Cache context.
+     * @return Collection of nodes for broadcasting.
+     */
+    private List<ClusterNode> broadcastNodes(GridH2QueryContext qctx, GridCacheContext<?,?> cctx) {
+        Map<UUID, int[]> partMap = qctx.partitionsMap();
+
+        List<ClusterNode> res;
+
+        if (partMap == null)
+            res = new ArrayList<>(CU.affinityNodes(cctx, qctx.topologyVersion()));
+        else {
+            res = new ArrayList<>(partMap.size());
+
+            GridKernalContext ctx = kernalContext();
+
+            for (UUID nodeId : partMap.keySet()) {
+                ClusterNode node = ctx.discovery().node(nodeId);
+
+                if (node == null)
+                    throw new GridH2RetryException("Failed to find node.");
+
+                res.add(node);
+            }
+        }
+
+        if (F.isEmpty(res))
+            throw new GridH2RetryException("Failed to collect affinity nodes.");
+
+        return res;
+    }
+
+    /**
+     * @param cctx Cache context.
+     * @param qctx Query context.
+     * @param affKeyObj Affinity key.
+     * @return Cluster nodes or {@code null} if affinity key is a null value.
+     */
+    private ClusterNode rangeNode(GridCacheContext<?,?> cctx, GridH2QueryContext qctx, Object affKeyObj) {
+        assert affKeyObj != null && affKeyObj != EXPLICIT_NULL : affKeyObj;
+
+        ClusterNode node;
+
+        if (qctx.partitionsMap() != null) {
+            // If we have explicit partitions map, we have to use it to calculate affinity node.
+            UUID nodeId = qctx.nodeForPartition(cctx.affinity().partition(affKeyObj), cctx);
+
+            node = cctx.discovery().node(nodeId);
+        }
+        else // Get primary node for current topology version.
+            node = cctx.affinity().primary(affKeyObj, qctx.topologyVersion());
+
+        if (node == null) // Node was not found, probably topology changed and we need to retry the whole query.
+            throw new GridH2RetryException("Failed to find node.");
+
+        return node;
+    }
+
+    /**
+     * @param row Row.
+     * @return Row message.
+     */
+    private GridH2RowMessage toRowMessage(Row row) {
+        if (row == null)
+            return null;
+
+        int cols = row.getColumnCount();
+
+        assert cols > 0 : cols;
+
+        List<GridH2ValueMessage> vals = new ArrayList<>(cols);
+
+        for (int i = 0; i < cols; i++) {
+            try {
+                vals.add(GridH2ValueMessageFactory.toMessage(row.getValue(i)));
+            }
+            catch (IgniteCheckedException e) {
+                throw new CacheException(e);
+            }
+        }
+
+        GridH2RowMessage res = new GridH2RowMessage();
+
+        res.values(vals);
+
+        return res;
+    }
+
+    /**
+     * @param msg Row message.
+     * @return Search row.
+     */
+    private SearchRow toSearchRow(GridH2RowMessage msg) {
+        if (msg == null)
+            return null;
+
+        GridKernalContext ctx = kernalContext();
+
+        Value[] vals = new Value[getTable().getColumns().length];
+
+        assert vals.length > 0;
+
+        List<GridH2ValueMessage> msgVals = msg.values();
+
+        for (int i = 0; i < indexColumns.length; i++) {
+            if (i >= msgVals.size())
+                continue;
+
+            try {
+                vals[indexColumns[i].column.getColumnId()] = msgVals.get(i).value(ctx);
+            }
+            catch (IgniteCheckedException e) {
+                throw new CacheException(e);
+            }
+        }
+
+        return database.createRow(vals, MEMORY_CALCULATE);
+    }
+
+    /**
+     * @param row Search row.
+     * @return Row message.
+     */
+    private GridH2RowMessage toSearchRowMessage(SearchRow row) {
+        if (row == null)
+            return null;
+
+        List<GridH2ValueMessage> vals = new ArrayList<>(indexColumns.length);
+
+        for (IndexColumn idxCol : indexColumns) {
+            Value val = row.getValue(idxCol.column.getColumnId());
+
+            if (val == null)
+                break;
+
+            try {
+                vals.add(GridH2ValueMessageFactory.toMessage(val));
+            }
+            catch (IgniteCheckedException e) {
+                throw new CacheException(e);
+            }
+        }
+
+        GridH2RowMessage res = new GridH2RowMessage();
+
+        res.values(vals);
+
+        return res;
+    }
+
+    /**
+     * @param msg Message.
+     * @return Row.
+     */
+    private Row toRow(GridH2RowMessage msg) {
+        if (msg == null)
+            return null;
+
+        GridKernalContext ctx = kernalContext();
+
+        List<GridH2ValueMessage> vals = msg.values();
+
+        assert !F.isEmpty(vals) : vals;
+
+        Value[] vals0 = new Value[vals.size()];
+
+        for (int i = 0; i < vals0.length; i++) {
+            try {
+                vals0[i] = vals.get(i).value(ctx);
+            }
+            catch (IgniteCheckedException e) {
+                throw new CacheException(e);
+            }
+        }
+
+        return database.createRow(vals0, MEMORY_CALCULATE);
+    }
+
+    /**
+     * Simple cursor from a single node.
+     */
+    private static class UnicastCursor implements Cursor {
+        /** */
+        final int rangeId;
+
+        /** */
+        RangeStream stream;
+
+        /**
+         * @param rangeId Range ID.
+         * @param nodes Remote nodes.
+         * @param rangeStreams Range streams.
+         */
+        private UnicastCursor(int rangeId, Collection<ClusterNode> nodes, Map<ClusterNode,RangeStream> rangeStreams) {
+            assert nodes.size() == 1;
+
+            this.rangeId = rangeId;
+            this.stream = rangeStreams.get(F.first(nodes));
+
+            assert stream != null;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean next() {
+            return stream.next(rangeId);
+        }
+
+        /** {@inheritDoc} */
+        @Override public Row get() {
+            return stream.get(rangeId);
+        }
+
+        /** {@inheritDoc} */
+        @Override public SearchRow getSearchRow() {
+            return get();
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean previous() {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    /**
+     * Merge cursor from multiple nodes.
+     */
+    private class BroadcastCursor implements Cursor, Comparator<RangeStream> {
+        /** */
+        final int rangeId;
+
+        /** */
+        final RangeStream[] streams;
+
+        /** */
+        boolean first = true;
+
+        /** */
+        int off;
+
+        /**
+         * @param rangeId Range ID.
+         * @param nodes Remote nodes.
+         * @param rangeStreams Range streams.
+         */
+        private BroadcastCursor(int rangeId, Collection<ClusterNode> nodes, Map<ClusterNode,RangeStream> rangeStreams) {
+            assert nodes.size() > 1;
+
+            this.rangeId = rangeId;
+
+            streams = new RangeStream[nodes.size()];
+
+            int i = 0;
+
+            for (ClusterNode node : nodes) {
+                RangeStream stream = rangeStreams.get(node);
+
+                assert stream != null;
+
+                streams[i++] = stream;
+            }
+        }
+
+        /** {@inheritDoc} */
+        @Override public int compare(RangeStream o1, RangeStream o2) {
+            if (o1 == o2)
+                return 0;
+
+            // Nulls are at the beginning of array.
+            if (o1 == null)
+                return -1;
+
+            if (o2 == null)
+                return 1;
+
+            return compareRows(o1.get(rangeId), o2.get(rangeId));
+        }
+
+        /**
+         * Try to fetch the first row.
+         *
+         * @return {@code true} If we were able to find at least one row.
+         */
+        private boolean goFirst() {
+            // Fetch first row from all the streams and sort them.
+            for (int i = 0; i < streams.length; i++) {
+                if (!streams[i].next(rangeId)) {
+                    streams[i] = null;
+                    off++; // After sorting this offset will cut off all null elements at the beginning of array.
+                }
+            }
+
+            if (off == streams.length)
+                return false;
+
+            Arrays.sort(streams, this);
+
+            return true;
+        }
+
+        /**
+         * Fetch next row.
+         *
+         * @return {@code true} If we were able to find at least one row.
+         */
+        private boolean goNext() {
+            assert off != streams.length;
+
+            if (!streams[off].next(rangeId)) {
+                // Next row from current min stream was not found -> nullify that stream and bump offset forward.
+                streams[off] = null;
+
+                return ++off != streams.length;
+            }
+
+            // Bubble up current min stream with respect to fetched row to achieve correct sort order of streams.
+            for (int i = off, last = streams.length - 1; i < last; i++) {
+                if (compareRows(streams[i].get(rangeId), streams[i + 1].get(rangeId)) <= 0)
+                    break;
+
+                U.swap(streams, i, i + 1);
+            }
+
+            return true;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean next() {
+            if (first) {
+                first = false;
+
+                return goFirst();
+            }
+
+            return goNext();
+        }
+
+        /** {@inheritDoc} */
+        @Override public Row get() {
+            return streams[off].get(rangeId);
+        }
+
+        /** {@inheritDoc} */
+        @Override public SearchRow getSearchRow() {
+            return get();
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean previous() {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    /**
+     * Index lookup batch.
+     */
+    private class DistributedLookupBatch implements IndexLookupBatch {
+        /** */
+        final GridCacheContext<?,?> cctx;
+
+        /** */
+        final boolean ucast;
+
+        /** */
+        final int affColId;
+
+        /** */
+        GridH2QueryContext qctx;
+
+        /** */
+        int batchLookupId;
+
+        /** */
+        Map<ClusterNode, RangeStream> rangeStreams = Collections.emptyMap();
+
+        /** */
+        List<ClusterNode> broadcastNodes;
+
+        /** */
+        List<Future<Cursor>> res = Collections.emptyList();
+
+        /** */
+        boolean batchFull;
+
+        /** */
+        boolean findCalled;
+
+        /**
+         * @param cctx Cache Cache context.
+         * @param ucast Unicast or broadcast query.
+         * @param affColId Affinity column ID.
+         */
+        private DistributedLookupBatch(GridCacheContext<?,?> cctx, boolean ucast, int affColId) {
+            this.cctx = cctx;
+            this.ucast = ucast;
+            this.affColId = affColId;
+        }
+
+        /**
+         * @param firstRow First row.
+         * @param lastRow Last row.
+         * @return Affinity key or {@code null}.
+         */
+        private Object getAffinityKey(SearchRow firstRow, SearchRow lastRow) {
+            if (firstRow == null || lastRow == null)
+                return null;
+
+            Value affKeyFirst = firstRow.getValue(affColId);
+            Value affKeyLast = lastRow.getValue(affColId);
+
+            if (affKeyFirst != null && equal(affKeyFirst, affKeyLast))
+                return affKeyFirst == ValueNull.INSTANCE ? EXPLICIT_NULL : affKeyFirst.getObject();
+
+            if (affColId == KEY_COL)
+                return null;
+
+            // Try to extract affinity key from primary key.
+            Value pkFirst = firstRow.getValue(KEY_COL);
+            Value pkLast = lastRow.getValue(KEY_COL);
+
+            if (pkFirst == ValueNull.INSTANCE || pkLast == ValueNull.INSTANCE)
+                return EXPLICIT_NULL;
+
+            if (pkFirst == null || pkLast == null || !equal(pkFirst, pkLast))
+                return null;
+
+            Object pkAffKeyFirst = cctx.affinity().affinityKey(pkFirst.getObject());
+            Object pkAffKeyLast = cctx.affinity().affinityKey(pkLast.getObject());
+
+            if (pkAffKeyFirst == null || pkAffKeyLast == null)
+                throw new CacheException("Cache key without affinity key.");
+
+            if (pkAffKeyFirst.equals(pkAffKeyLast))
+                return pkAffKeyFirst;
+
+            return null;
+        }
+
+        /** {@inheritDoc} */
+        @SuppressWarnings("ForLoopReplaceableByForEach")
+        @Override public boolean addSearchRows(SearchRow firstRow, SearchRow lastRow) {
+            if (qctx == null || findCalled) {
+                if (qctx == null) {
+                    // It is the first call after query begin (may be after reuse),
+                    // reinitialize query context and result.
+                    qctx = GridH2QueryContext.get();
+                    res = new ArrayList<>();
+
+                    assert qctx != null;
+                    assert !findCalled;
+                }
+                else {
+                    // Cleanup after the previous lookup phase.
+                    assert batchLookupId != 0;
+
+                    findCalled = false;
+                    qctx.putStreams(batchLookupId, null);
+                    res.clear();
+                }
+
+                // Reinitialize for the next lookup phase.
+                batchLookupId = qctx.nextBatchLookupId();
+                rangeStreams = new HashMap<>();
+            }
+
+            Object affKey = affColId == -1 ? null : getAffinityKey(firstRow, lastRow);
+
+            List<ClusterNode> nodes;
+            Future<Cursor> fut;
+
+            if (affKey != null) {
+                // Affinity key is provided.
+                if (affKey == EXPLICIT_NULL) // Affinity key is explicit null, we will not find anything.
+                    return false;
+
+                nodes = F.asList(rangeNode(cctx, qctx, affKey));
+            }
+            else {
+                // Affinity key is not provided or is not the same in upper and lower bounds, we have to broadcast.
+                if (broadcastNodes == null)
+                    broadcastNodes = broadcastNodes(qctx, cctx);
+
+                nodes = broadcastNodes;
+            }
+
+            assert !F.isEmpty(nodes) : nodes;
+
+            final int rangeId = res.size();
+
+            // Create messages.
+            GridH2RowMessage first = toSearchRowMessage(firstRow);
+            GridH2RowMessage last = toSearchRowMessage(lastRow);
+
+            // Range containing upper and lower bounds.
+            GridH2RowRangeBounds rangeBounds = rangeBounds(rangeId, first, last);
+
+            // Add range to every message of every participating node.
+            for (int i = 0; i < nodes.size(); i++) {
+                ClusterNode node = nodes.get(i);
+                assert node != null;
+
+                RangeStream stream = rangeStreams.get(node);
+
+                List<GridH2RowRangeBounds> bounds;
+
+                if (stream == null) {
+                    stream = new RangeStream(qctx, node);
+
+                    stream.req = createRequest(qctx, batchLookupId);
+                    stream.req.bounds(bounds = new ArrayList<>());
+
+                    rangeStreams.put(node, stream);
+                }
+                else
+                    bounds = stream.req.bounds();
+
+                bounds.add(rangeBounds);
+
+                // If at least one node will have a full batch then we are ok.
+                if (bounds.size() >= qctx.pageSize())
+                    batchFull = true;
+            }
+
+            fut = new DoneFuture<>(nodes.size() == 1 ?
+                new UnicastCursor(rangeId, nodes, rangeStreams) :
+                new BroadcastCursor(rangeId, nodes, rangeStreams));
+
+            res.add(fut);
+
+            return true;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean isBatchFull() {
+            return batchFull;
+        }
+
+        /**
+         *
+         */
+        private void startStreams() {
+            if (rangeStreams.isEmpty()) {
+                assert res.isEmpty();
+
+                return;
+            }
+
+            qctx.putStreams(batchLookupId, rangeStreams);
+
+            // Start streaming.
+            for (RangeStream stream : rangeStreams.values())
+                stream.start();
+        }
+
+        /** {@inheritDoc} */
+        @Override public List<Future<Cursor>> find() {
+            batchFull = false;
+            findCalled = true;
+
+            startStreams();
+
+            return res;
+        }
+
+        /** {@inheritDoc} */
+        @Override public void reset(boolean beforeQry) {
+            if (beforeQry || qctx == null) // Query context can be null if addSearchRows was never called.
+                return;
+
+            assert batchLookupId != 0;
+
+            // Do cleanup after the query run.
+            qctx.putStreams(batchLookupId, null);
+            qctx = null; // The same query can be reused multiple times for different query contexts.
+            batchLookupId = 0;
+
+            rangeStreams = Collections.emptyMap();
+            broadcastNodes = null;
+            batchFull = false;
+            findCalled = false;
+            res = Collections.emptyList();
+        }
+
+        /** {@inheritDoc} */
+        @Override public String getPlanSQL() {
+            return ucast ? "unicast" : "broadcast";
+        }
+    }
+
+    /**
+     * Per node range stream.
+     */
+    private class RangeStream {
+        /** */
+        final GridH2QueryContext qctx;
+
+        /** */
+        final ClusterNode node;
+
+        /** */
+        GridH2IndexRangeRequest req;
+
+        /** */
+        int remainingRanges;
+
+        /** */
+        final BlockingQueue<GridH2IndexRangeResponse> respQueue = new LinkedBlockingQueue<>();
+
+        /** */
+        Iterator<GridH2RowRange> ranges = emptyIterator();
+
+        /** */
+        Cursor cursor = GridH2Cursor.EMPTY;
+
+        /** */
+        int cursorRangeId = -1;
+
+        /**
+         * @param qctx Query context.
+         * @param node Node.
+         */
+        RangeStream(GridH2QueryContext qctx, ClusterNode node) {
+            this.node = node;
+            this.qctx = qctx;
+        }
+
+        /**
+         * Start streaming.
+         */
+        private void start() {
+            remainingRanges = req.bounds().size();
+
+            assert remainingRanges > 0;
+
+            if (log.isDebugEnabled())
+                log.debug("Starting stream: [node=" + node + ", req=" + req + "]");
+
+            send(singletonList(node), req);
+        }
+
+        /**
+         * @param msg Response.
+         */
+        public void onResponse(GridH2IndexRangeResponse msg) {
+            respQueue.add(msg);
+        }
+
+        /**
+         * @return Response.
+         */
+        private GridH2IndexRangeResponse awaitForResponse() {
+            assert remainingRanges > 0;
+
+            final long start = U.currentTimeMillis();
+
+            for (int attempt = 0;; attempt++) {
+                if (qctx.isCleared())
+                    throw new GridH2RetryException("Query is cancelled.");
+
+                if (kernalContext().isStopping())
+                    throw new GridH2RetryException("Stopping node.");
+
+                GridH2IndexRangeResponse res;
+
+                try {
+                    res = respQueue.poll(500, TimeUnit.MILLISECONDS);
+                }
+                catch (InterruptedException e) {
+                    throw new GridH2RetryException("Interrupted.");
+                }
+
+                if (res != null) {
+                    switch (res.status()) {
+                        case STATUS_OK:
+                            List<GridH2RowRange> ranges0 = res.ranges();
+
+                            remainingRanges -= ranges0.size();
+
+                            if (ranges0.get(ranges0.size() - 1).isPartial())
+                                remainingRanges++;
+
+                            if (remainingRanges > 0) {
+                                if (req.bounds() != null)
+                                    req = createRequest(qctx, req.batchLookupId());
+
+                                // Prefetch next page.
+                                send(singletonList(node), req);
+                            }
+                            else
+                                req = null;
+
+                            return res;
+
+                        case STATUS_NOT_FOUND:
+                            if (req == null || req.bounds() == null) // We have already received the first response.
+                                throw new GridH2RetryException("Failure on remote node.");
+
+                            if (U.currentTimeMillis() - start > 30_000)
+                                throw new GridH2RetryException("Timeout.");
+
+                            try {
+                                U.sleep(20 * attempt);
+                            }
+                            catch (IgniteInterruptedCheckedException e) {
+                                throw new IgniteInterruptedException(e.getMessage());
+                            }
+
+                            // Retry to send the request once more after some time.
+                            send(singletonList(node), req);
+
+                            break;
+
+                        case STATUS_ERROR:
+                            throw new CacheException(res.error());
+
+                        default:
+                            throw new IllegalStateException();
+                    }
+                }
+
+                if (!kernalContext().discovery().alive(node))
+                    throw new GridH2RetryException("Node left: " + node);
+            }
+        }
+
+        /**
+         * @param rangeId Requested range ID.
+         * @return {@code true} If next row for the requested range was found.
+         */
+        private boolean next(final int rangeId) {
+            for (;;) {
+                if (rangeId == cursorRangeId) {
+                    if (cursor.next())
+                        return true;
+                }
+                else if (rangeId < cursorRangeId)
+                    return false;
+
+                cursor = GridH2Cursor.EMPTY;
+
+                while (!ranges.hasNext()) {
+                    if (remainingRanges == 0) {
+                        ranges = emptyIterator();
+
+                        return false;
+                    }
+
+                    ranges = awaitForResponse().ranges().iterator();
+                }
+
+                GridH2RowRange range = ranges.next();
+
+                cursorRangeId = range.rangeId();
+
+                if (!F.isEmpty(range.rows())) {
+                    final Iterator<GridH2RowMessage> it = range.rows().iterator();
+
+                    if (it.hasNext()) {
+                        cursor = new GridH2Cursor(new Iterator<Row>() {
+                            @Override public boolean hasNext() {
+                                return it.hasNext();
+                            }
+
+                            @Override public Row next() {
+                                // Lazily convert messages into real rows.
+                                return toRow(it.next());
+                            }
+
+                            @Override public void remove() {
+                                throw new UnsupportedOperationException();
+                            }
+                        });
+                    }
+                }
+            }
+        }
+
+        /**
+         * @param rangeId Requested range ID.
+         * @return Current row.
+         */
+        private Row get(int rangeId) {
+            assert rangeId == cursorRangeId;
+
+            return cursor.get();
+        }
+    }
+
+    /**
+     * Bounds iterator.
+     */
+    private class RangeSource {
+        /** */
+        Iterator<GridH2RowRangeBounds> boundsIter;
+
+        /** */
+        int curRangeId = -1;
+
+        /** */
+        Iterator<GridH2Row> curRange = emptyIterator();
+
+        /** */
+        final ConcurrentNavigableMap<GridSearchRowPointer, GridH2Row> tree;
+
+        /** */
+        final IndexingQueryFilter filter;
+
+        /**
+         * @param bounds Bounds.
+         * @param tree Snapshot.
+         * @param filter Filter.
+         */
+        RangeSource(
+            Iterable<GridH2RowRangeBounds> bounds,
+            ConcurrentNavigableMap<GridSearchRowPointer, GridH2Row> tree,
+            IndexingQueryFilter filter
+        ) {
+            this.filter = filter;
+            this.tree = tree;
+            boundsIter = bounds.iterator();
+        }
+
+        /**
+         * @return {@code true} If there are more rows in this source.
+         */
+        public boolean hasMoreRows() {
+            return boundsIter.hasNext() || curRange.hasNext();
+        }
+
+        /**
+         * @param maxRows Max allowed rows.
+         * @return Range.
+         */
+        public GridH2RowRange next(int maxRows) {
+            assert maxRows > 0 : maxRows;
+
+            for (;;) {
+                if (curRange.hasNext()) {
+                    // Here we are getting last rows from previously partially fetched range.
+                    List<GridH2RowMessage> rows = new ArrayList<>();
+
+                    GridH2RowRange nextRange = new GridH2RowRange();
+
+                    nextRange.rangeId(curRangeId);
+                    nextRange.rows(rows);
+
+                    do {
+                        rows.add(toRowMessage(curRange.next()));
+                    }
+                    while (rows.size() < maxRows && curRange.hasNext());
+
+                    if (curRange.hasNext())
+                        nextRange.setPartial();
+                    else
+                        curRange = emptyIterator();
+
+                    return nextRange;
+                }
+
+                curRange = emptyIterator();
+
+                if (!boundsIter.hasNext()) {
+                    boundsIter = emptyIterator();
+
+                    return null;
+                }
+
+                GridH2RowRangeBounds bounds = boundsIter.next();
+
+                curRangeId = bounds.rangeId();
+
+                SearchRow first = toSearchRow(bounds.first());
+                SearchRow last = toSearchRow(bounds.last());
+
+                ConcurrentNavigableMap<GridSearchRowPointer,GridH2Row> t = tree != null ? tree : treeForRead();
+
+                curRange = doFind0(t, first, true, last, filter);
+
+                if (!curRange.hasNext()) {
+                    // We have to return empty range here.
+                    GridH2RowRange emptyRange = new GridH2RowRange();
+
+                    emptyRange.rangeId(curRangeId);
+
+                    return emptyRange;
+                }
+            }
+        }
+    }
+
+    /**
+     * @return Snapshot for current thread if there is one.
+     */
+    protected ConcurrentNavigableMap<GridSearchRowPointer, GridH2Row> treeForRead() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @param t Tree.
+     * @param first Lower bound.
+     * @param includeFirst Whether lower bound should be inclusive.
+     * @param last Upper bound always inclusive.
+     * @param filter Filter.
+     * @return Iterator over rows in given range.
+     */
+    protected Iterator<GridH2Row> doFind0(ConcurrentNavigableMap<GridSearchRowPointer, GridH2Row> t,
+        @Nullable SearchRow first,
+        boolean includeFirst,
+        @Nullable SearchRow last,
+        IndexingQueryFilter filter) {
+        throw new UnsupportedOperationException();
+    }
+
     /**
      * Iterator which filters by expiration time and predicate.
      */
-    protected class FilteringIterator extends GridFilteredIterator<GridH2Row> {
+    protected static class FilteringIterator extends GridFilteredIterator<GridH2Row> {
         /** */
         private final IgniteBiPredicate<Object, Object> fltr;
 
@@ -210,15 +1490,19 @@ public abstract class GridH2IndexBase extends BaseIndex {
         /**
          * @param iter Iterator.
          * @param time Time for expired rows filtering.
+         * @param qryFilter Filter.
+         * @param spaceName Space name.
          */
-        protected FilteringIterator(Iterator<GridH2Row> iter, long time,
-            IndexingQueryFilter qryFilter) {
+        protected FilteringIterator(Iterator<GridH2Row> iter,
+            long time,
+            IndexingQueryFilter qryFilter,
+            String spaceName) {
             super(iter);
 
             this.time = time;
 
             if (qryFilter != null) {
-                this.fltr = qryFilter.forSpace(((GridH2Table)getTable()).spaceName());
+                this.fltr = qryFilter.forSpace(spaceName);
 
                 this.isValRequired = qryFilter.isValueRequired();
             } else {
@@ -242,8 +1526,8 @@ public abstract class GridH2IndexBase extends BaseIndex {
             if (fltr == null)
                 return true;
 
-            Object key = row.getValue(keyCol).getObject();
-            Object val = isValRequired ? row.getValue(valCol).getObject() : null;
+            Object key = row.getValue(KEY_COL).getObject();
+            Object val = isValRequired ? row.getValue(VAL_COL).getObject() : null;
 
             assert key != null;
             assert !isValRequired || val != null;


[09/50] [abbrv] ignite git commit: ignite-1232 Distributed SQL joins implementation

Posted by vo...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheJoinQueryWithAffinityKeyTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheJoinQueryWithAffinityKeyTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheJoinQueryWithAffinityKeyTest.java
new file mode 100644
index 0000000..44bca5e
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheJoinQueryWithAffinityKeyTest.java
@@ -0,0 +1,646 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.processors.cache;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ThreadLocalRandom;
+import javax.cache.CacheException;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.CacheKeyConfiguration;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.QueryEntity;
+import org.apache.ignite.cache.QueryIndex;
+import org.apache.ignite.cache.affinity.AffinityKeyMapped;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.cache.query.annotations.QuerySqlField;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.binary.BinaryMarshaller;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+import static org.apache.ignite.cache.CacheMode.REPLICATED;
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+
+/**
+ *
+ */
+@SuppressWarnings("unchecked")
+public class IgniteCacheJoinQueryWithAffinityKeyTest extends GridCommonAbstractTest {
+    /** */
+    private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
+
+    /** */
+    private static final int NODES = 5;
+
+    /** */
+    private boolean client;
+
+    /** */
+    private boolean escape;
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(gridName);
+
+        ((TcpDiscoverySpi)cfg.getDiscoverySpi()).setIpFinder(IP_FINDER);
+
+        CacheKeyConfiguration keyCfg = new CacheKeyConfiguration();
+
+        keyCfg.setTypeName(TestKeyWithAffinity.class.getName());
+        keyCfg.setAffinityKeyFieldName("affKey");
+
+        cfg.setCacheKeyConfiguration(keyCfg);
+
+        cfg.setClientMode(client);
+
+        return cfg;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        super.beforeTestsStarted();
+
+        startGridsMultiThreaded(NODES - 1);
+
+        client = true;
+
+        startGrid(NODES - 1);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTestsStopped() throws Exception {
+        stopAllGrids();
+
+        super.afterTestsStopped();
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoinQuery() throws Exception {
+        testJoinQuery(PARTITIONED, 0, false, true);
+
+        testJoinQuery(PARTITIONED, 1, false, true);
+
+        testJoinQuery(REPLICATED, 0, false, true);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoinQueryEscapeAll() throws Exception {
+        escape = true;
+
+        testJoinQuery();
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoinQueryWithAffinityKey() throws Exception {
+        testJoinQuery(PARTITIONED, 0, true, true);
+
+        testJoinQuery(PARTITIONED, 1, true, true);
+
+        testJoinQuery(REPLICATED, 0, true, true);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoinQueryWithAffinityKeyEscapeAll() throws Exception {
+        escape = true;
+
+        testJoinQueryWithAffinityKey();
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoinQueryWithAffinityKeyNotQueryField() throws Exception {
+        testJoinQuery(PARTITIONED, 0, true, false);
+
+        testJoinQuery(PARTITIONED, 1, true, false);
+
+        testJoinQuery(REPLICATED, 0, true, false);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoinQueryWithAffinityKeyNotQueryFieldEscapeAll() throws Exception {
+        escape = true;
+
+        testJoinQueryWithAffinityKeyNotQueryField();
+    }
+
+    /**
+     * @param cacheMode Cache mode.
+     * @param backups Number of backups.
+     * @param affKey If {@code true} uses key with affinity key field.
+     * @param includeAffKey If {@code true} includes affinity key field in query fields.
+     */
+    private void testJoinQuery(CacheMode cacheMode, int backups, final boolean affKey, boolean includeAffKey) {
+        CacheConfiguration ccfg = cacheConfiguration(cacheMode, backups, affKey, includeAffKey);
+
+        log.info("Test cache [mode=" + cacheMode + ", backups=" + backups + ']');
+
+        IgniteCache cache = ignite(0).createCache(ccfg);
+
+        try {
+            final PutData putData = putData(cache, affKey);
+
+            for (int i = 0; i < NODES; i++) {
+                log.info("Test node: " + i);
+
+                final IgniteCache cache0 = ignite(i).cache(ccfg.getName());
+
+                if (cacheMode == REPLICATED && !ignite(i).configuration().isClientMode()) {
+                    GridTestUtils.assertThrows(log, new Callable<Object>() {
+                        @Override public Object call() throws Exception {
+                            checkPersonAccountsJoin(cache0, putData.personAccounts, affKey);
+
+                            return null;
+                        }
+                    }, CacheException.class, "Queries using distributed JOINs have to be run on partitioned cache");
+                }
+                else {
+                    checkPersonAccountsJoin(cache0, putData.personAccounts, affKey);
+
+                    checkOrganizationPersonsJoin(cache0, putData.orgPersons);
+                }
+            }
+        }
+        finally {
+            ignite(0).destroyCache(ccfg.getName());
+        }
+    }
+
+    /**
+     * @param cache Cache.
+     * @param cnts Organizations per person counts.
+     */
+    private void checkOrganizationPersonsJoin(IgniteCache cache, Map<Integer, Integer> cnts) {
+        SqlFieldsQuery qry;
+
+        if (escape) {
+            qry = new SqlFieldsQuery("select o.\"name\", p.\"name\" " +
+                "from \"Organization\" o, \"Person\" p " +
+                "where p.\"orgId\" = o._key and o._key=?");
+        }
+        else {
+            qry = new SqlFieldsQuery("select o.name, p.name " +
+                "from Organization o, Person p " +
+                "where p.orgId = o._key and o._key=?");
+        }
+
+        qry.setDistributedJoins(true);
+
+        long total = 0;
+
+        for (int i = 0; i < cnts.size(); i++) {
+            qry.setArgs(i);
+
+            List<List<Object>> res = cache.query(qry).getAll();
+
+            assertEquals((int)cnts.get(i), res.size());
+
+            total += res.size();
+        }
+
+        SqlFieldsQuery qry2;
+
+        if (escape) {
+            qry2 = new SqlFieldsQuery("select count(*) " +
+                "from \"Organization\" o, \"Person\" p where p.\"orgId\" = o._key");
+        }
+        else {
+            qry2 = new SqlFieldsQuery("select count(*) " +
+                "from Organization o, Person p where p.orgId = o._key");
+        }
+
+        qry2.setDistributedJoins(true);
+
+        List<List<Object>> res = cache.query(qry2).getAll();
+
+        assertEquals(1, res.size());
+        assertEquals(total, res.get(0).get(0));
+    }
+
+    /**
+     * @param cache Cache.
+     * @param cnts Accounts per person counts.
+     * @param affKey If {@code true} uses key with affinity key field.
+     */
+    private void checkPersonAccountsJoin(IgniteCache cache, Map<Object, Integer> cnts, boolean affKey) {
+        String sql1;
+
+        if (escape) {
+            sql1 = "select p.\"name\" from \"Person\" p, \"" + (affKey ? "AccountKeyWithAffinity" : "Account") + "\" a " +
+                "where p._key = a.\"personKey\" and p._key=?";
+        }
+        else {
+            sql1 = "select p.name from Person p, " + (affKey ? "AccountKeyWithAffinity" : "Account") + " a " +
+                "where p._key = a.personKey and p._key=?";
+        }
+
+        SqlFieldsQuery qry1 = new SqlFieldsQuery(sql1);
+
+        qry1.setDistributedJoins(true);
+
+        String sql2;
+
+        if (escape) {
+            sql2 = "select p.\"name\" from \"Person\" p, \"" + (affKey ? "AccountKeyWithAffinity" : "Account") + "\" a " +
+                "where p.\"id\" = a.\"personId\" and p.\"id\"=?";
+        }
+        else {
+            sql2 = "select p.name from Person p, " + (affKey ? "AccountKeyWithAffinity" : "Account") + " a " +
+                "where p.id = a.personId and p.id=?";
+        }
+
+        SqlFieldsQuery qry2 = new SqlFieldsQuery(sql2);
+
+        qry2.setDistributedJoins(true);
+
+        Ignite ignite = (Ignite)cache.unwrap(Ignite.class);
+
+        boolean binary = ignite.configuration().getMarshaller() instanceof BinaryMarshaller;
+
+        long total = 0;
+
+        for (Map.Entry<Object, Integer> e : cnts.entrySet()) {
+            Object arg = binary ? ignite.binary().toBinary(e.getKey()) : e.getKey();
+
+            qry1.setArgs(arg);
+
+            List<List<Object>> res = cache.query(qry1).getAll();
+
+            assertEquals((int)e.getValue(), res.size());
+
+            total += res.size();
+
+            qry2.setArgs(((Id)e.getKey()).id());
+
+            res = cache.query(qry2).getAll();
+
+            assertEquals((int)e.getValue(), res.size());
+        }
+
+        SqlFieldsQuery[] qrys = new SqlFieldsQuery[2];
+
+
+        if (escape) {
+            qrys[0] = new SqlFieldsQuery("select count(*) " +
+                "from \"Person\" p, \"" + (affKey ? "AccountKeyWithAffinity" : "Account") + "\" a " +
+                "where p.\"id\" = a.\"personId\"");
+
+            qrys[1] = new SqlFieldsQuery("select count(*) " +
+                "from \"Person\" p, \"" + (affKey ? "AccountKeyWithAffinity" : "Account") + "\" a " +
+                "where p._key = a.\"personKey\"");
+        }
+        else {
+            qrys[0] = new SqlFieldsQuery("select count(*) " +
+                "from Person p, " + (affKey ? "AccountKeyWithAffinity" : "Account") + " a " +
+                "where p.id = a.personId");
+
+            qrys[1] = new SqlFieldsQuery("select count(*) " +
+                "from Person p, " + (affKey ? "AccountKeyWithAffinity" : "Account") + " a " +
+                "where p._key = a.personKey");
+        }
+
+        for (SqlFieldsQuery qry : qrys) {
+            qry.setDistributedJoins(true);
+
+            List<List<Object>> res = cache.query(qry).getAll();
+
+            assertEquals(1, res.size());
+            assertEquals(total, res.get(0).get(0));
+        }
+    }
+
+    /**
+     * @param cacheMode Cache mode.
+     * @param backups Number of backups.
+     * @param affKey If {@code true} uses key with affinity key field.
+     * @param includeAffKey If {@code true} includes affinity key field in query fields.
+     * @return Cache configuration.
+     */
+    private CacheConfiguration cacheConfiguration(CacheMode cacheMode,
+        int backups,
+        boolean affKey,
+        boolean includeAffKey) {
+        CacheConfiguration ccfg = new CacheConfiguration();
+
+        ccfg.setCacheMode(cacheMode);
+
+        if (cacheMode == PARTITIONED)
+            ccfg.setBackups(backups);
+
+        ccfg.setWriteSynchronizationMode(FULL_SYNC);
+
+        String personKeyType = affKey ? TestKeyWithAffinity.class.getName() : TestKey.class.getName();
+
+        QueryEntity account = new QueryEntity();
+        account.setKeyType(Integer.class.getName());
+        account.setValueType(affKey ? AccountKeyWithAffinity.class.getName() : Account.class.getName());
+        account.addQueryField("personKey", personKeyType, null);
+        account.addQueryField("personId", Integer.class.getName(), null);
+        account.setIndexes(F.asList(new QueryIndex("personKey"), new QueryIndex("personId")));
+
+        QueryEntity person = new QueryEntity();
+        person.setKeyType(personKeyType);
+        person.setValueType(Person.class.getName());
+        person.addQueryField("orgId", Integer.class.getName(), null);
+        person.addQueryField("id", Integer.class.getName(), null);
+        person.addQueryField("name", String.class.getName(), null);
+        person.setIndexes(F.asList(new QueryIndex("orgId"), new QueryIndex("id"), new QueryIndex("name")));
+
+        if (affKey && includeAffKey)
+            person.addQueryField("affKey", Integer.class.getName(), null);
+
+        QueryEntity org = new QueryEntity();
+        org.setKeyType(Integer.class.getName());
+        org.setValueType(Organization.class.getName());
+        org.addQueryField("name", String.class.getName(), null);
+        org.setIndexes(F.asList(new QueryIndex("name")));
+
+        ccfg.setQueryEntities(F.asList(account, person, org));
+
+        ccfg.setSqlEscapeAll(escape);
+
+        return ccfg;
+    }
+
+    /**
+     * @param cache Cache.
+     * @param affKey If {@code true} uses key with affinity key field.
+     * @return Put data counts.
+     */
+    private PutData putData(IgniteCache cache, boolean affKey) {
+        Map<Integer, Integer> orgPersons = new HashMap<>();
+        Map<Object, Integer> personAccounts = new HashMap<>();
+
+        final int ORG_CNT = 10;
+
+        for (int i = 0; i < ORG_CNT; i++)
+            cache.put(i, new Organization("org-" + i));
+
+        Set<Integer> personIds = new HashSet<>();
+        Set<Integer> accountIds = new HashSet<>();
+
+        for (int i = 0; i < ORG_CNT; i++) {
+            int persons = ThreadLocalRandom.current().nextInt(100);
+
+            for (int p = 0; p < persons; p++) {
+                int personId = ThreadLocalRandom.current().nextInt();
+
+                while (!personIds.add(personId))
+                    personId = ThreadLocalRandom.current().nextInt();
+
+                Object personKey = affKey ? new TestKeyWithAffinity(personId) : new TestKey(personId);
+
+                String name = "person-" + personId;
+
+                cache.put(personKey, new Person(i, name));
+
+                int accounts = ThreadLocalRandom.current().nextInt(10);
+
+                for (int a = 0; a < accounts; a++) {
+                    int accountId = ThreadLocalRandom.current().nextInt();
+
+                    while (!accountIds.add(accountId))
+                        accountId = ThreadLocalRandom.current().nextInt();
+
+                    cache.put(accountId, affKey ? new AccountKeyWithAffinity(personKey) : new Account(personKey));
+                }
+
+                personAccounts.put(personKey, accounts);
+            }
+
+            orgPersons.put(i, persons);
+        }
+
+        return new PutData(orgPersons, personAccounts);
+    }
+
+    /**
+     *
+     */
+    private static class PutData {
+        /** */
+        final Map<Integer, Integer> orgPersons;
+
+        /** */
+        final Map<Object, Integer> personAccounts;
+
+        /**
+         * @param orgPersons Organizations per person counts.
+         * @param personAccounts Accounts per person counts.
+         */
+        public PutData(Map<Integer, Integer> orgPersons, Map<Object, Integer> personAccounts) {
+            this.orgPersons = orgPersons;
+            this.personAccounts = personAccounts;
+        }
+    }
+
+    /**
+     *
+     */
+    public interface Id {
+        /**
+         * @return ID.
+         */
+        public int id();
+    }
+
+    /**
+     *
+     */
+    public static class TestKey implements Id {
+        /** */
+        private int id;
+
+        /**
+         * @param id Key.
+         */
+        public TestKey(int id) {
+            this.id = id;
+        }
+
+        /** {@inheritDoc} */
+        @Override public int id() {
+            return id;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean equals(Object o) {
+            if (this == o)
+                return true;
+
+            if (o == null || getClass() != o.getClass())
+                return false;
+
+            TestKey other = (TestKey)o;
+
+            return id == other.id;
+        }
+
+        /** {@inheritDoc} */
+        @Override public int hashCode() {
+            return id;
+        }
+    }
+
+    /**
+     *
+     */
+    public static class TestKeyWithAffinity implements Id {
+        /** */
+        private int id;
+
+        /** */
+        @AffinityKeyMapped
+        private int affKey;
+
+        /**
+         * @param id Key.
+         */
+        public TestKeyWithAffinity(int id) {
+            this.id = id;
+
+            affKey = id + 1;
+        }
+
+        /** {@inheritDoc} */
+        @Override public int id() {
+            return id;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean equals(Object o) {
+            if (this == o)
+                return true;
+
+            if (o == null || getClass() != o.getClass())
+                return false;
+
+            TestKeyWithAffinity other = (TestKeyWithAffinity)o;
+
+            return id == other.id;
+        }
+
+        /** {@inheritDoc} */
+        @Override public int hashCode() {
+            return id;
+        }
+    }
+
+    /**
+     *
+     */
+    private static class Account implements Serializable {
+        /** */
+        @QuerySqlField
+        private TestKey personKey;
+
+        /** */
+        @QuerySqlField
+        private int personId;
+
+        /**
+         * @param personKey Person key.
+         */
+        public Account(Object personKey) {
+            this.personKey = (TestKey)personKey;
+            personId = this.personKey.id;
+        }
+    }
+
+    /**
+     *
+     */
+    private static class AccountKeyWithAffinity implements Serializable {
+        /** */
+        @QuerySqlField
+        private TestKeyWithAffinity personKey;
+
+        /** */
+        @QuerySqlField
+        private int personId;
+
+        /**
+         * @param personKey Person key.
+         */
+        public AccountKeyWithAffinity(Object personKey) {
+            this.personKey = (TestKeyWithAffinity)personKey;
+            personId = this.personKey.id;
+        }
+    }
+
+    /**
+     *
+     */
+    private static class Person implements Serializable {
+        /** */
+        @QuerySqlField
+        int orgId;
+
+        /** */
+        @QuerySqlField
+        String name;
+
+        /**
+         * @param orgId Organization ID.
+         * @param name Name.
+         */
+        public Person(int orgId, String name) {
+            this.orgId = orgId;
+            this.name = name;
+        }
+    }
+
+    /**
+     *
+     */
+    private static class Organization implements Serializable {
+        /** */
+        @QuerySqlField
+        String name;
+
+        /**
+         * @param name Name.
+         */
+        public Organization(String name) {
+            this.name = name;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheQueryLoadSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheQueryLoadSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheQueryLoadSelfTest.java
index f55a833..ca612a8 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheQueryLoadSelfTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheQueryLoadSelfTest.java
@@ -25,13 +25,13 @@ import javax.cache.Cache;
 import javax.cache.integration.CompletionListenerFuture;
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
 import org.apache.ignite.cache.query.SqlQuery;
 import org.apache.ignite.cache.query.annotations.QuerySqlField;
 import org.apache.ignite.cache.store.CacheStoreAdapter;
 import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
-import org.apache.ignite.internal.IgniteKernal;
-import org.apache.ignite.internal.processors.cache.query.GridCacheQueryManager;
+import org.apache.ignite.internal.processors.query.GridQueryProcessor;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.P2;
 import org.apache.ignite.internal.util.typedef.internal.S;
@@ -109,11 +109,9 @@ public class IgniteCacheQueryLoadSelfTest extends GridCommonAbstractTest {
      * @throws IgniteCheckedException If failed.
      */
     private long size(Class<?> cls) throws IgniteCheckedException {
-        GridCacheQueryManager<Object, Object> qryMgr = ((IgniteKernal)grid()).internalCache().context().queries();
-
-        assert qryMgr != null;
-
-        return qryMgr.size(cls);
+        return (Long)grid().cache(null).query(
+            new SqlFieldsQuery("select count(*) from " + GridQueryProcessor.typeName(cls)).setLocal(true))
+            .getAll().get(0).get(0);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/68891e89/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCrossCachesJoinsQueryTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCrossCachesJoinsQueryTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCrossCachesJoinsQueryTest.java
new file mode 100644
index 0000000..0dd9281
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCrossCachesJoinsQueryTest.java
@@ -0,0 +1,1641 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.processors.cache;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import javax.cache.CacheException;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.CacheMemoryMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.QueryEntity;
+import org.apache.ignite.cache.QueryIndex;
+import org.apache.ignite.cache.affinity.AffinityKey;
+import org.apache.ignite.cache.query.Query;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.cache.query.SqlQuery;
+import org.apache.ignite.cache.query.annotations.QuerySqlField;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.processors.query.h2.sql.AbstractH2CompareQueryTest;
+import org.apache.ignite.internal.util.tostring.GridToStringInclude;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.internal.util.typedef.internal.SB;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.GridTestUtils;
+
+import static org.apache.ignite.cache.CacheMemoryMode.OFFHEAP_TIERED;
+import static org.apache.ignite.cache.CacheMemoryMode.ONHEAP_TIERED;
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+import static org.apache.ignite.cache.CacheMode.REPLICATED;
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+
+/**
+ *
+ */
+@SuppressWarnings({"unchecked", "PackageVisibleField", "serial"})
+public class IgniteCrossCachesJoinsQueryTest extends AbstractH2CompareQueryTest {
+    /** */
+    private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
+
+    /** */
+    private static final String PERSON_CACHE_NAME = "person";
+
+    /** */
+    private static final String ORG_CACHE_NAME = "org";
+
+    /** */
+    private static final String ACC_CACHE_NAME = "acc";
+
+    /** */
+    private static final int NODES = 5;
+
+    /** */
+    private boolean client;
+
+    /** */
+    private Data data;
+
+    /** Tested qry. */
+    private String qry;
+
+    /** Tested cache. */
+    private IgniteCache cache;
+
+    /** */
+    private boolean distributedJoins;
+
+    /** */
+    private static Random rnd;
+
+    /** */
+    private CacheMemoryMode memMode = ONHEAP_TIERED;
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(gridName);
+
+        ((TcpDiscoverySpi)cfg.getDiscoverySpi()).setIpFinder(IP_FINDER);
+
+        cfg.setClientMode(client);
+
+        return cfg;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected CacheConfiguration[] cacheConfigurations() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void setIndexedTypes(CacheConfiguration<?, ?> cc, CacheMode mode) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        long seed = System.currentTimeMillis();
+
+        rnd = new Random(seed);
+
+        log.info("Random seed: " + seed);
+
+        startGridsMultiThreaded(NODES - 1);
+
+        client = true;
+
+        startGrid(NODES - 1);
+
+        awaitPartitionMapExchange();
+
+        conn = openH2Connection(false);
+
+        initializeH2Schema();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void initCacheAndDbData() throws SQLException {
+        Statement st = conn.createStatement();
+
+        final String keyType = useCollocatedData() ? "other" : "int";
+
+        st.execute("create table \"" + ACC_CACHE_NAME + "\".Account" +
+            "  (" +
+            "  _key " + keyType + " not null," +
+            "  _val other not null," +
+            "  id int unique," +
+            "  personId int," +
+            "  personDateId TIMESTAMP," +
+            "  personStrId varchar(255)" +
+            "  )");
+
+        st.execute("create table \"" + PERSON_CACHE_NAME + "\".Person" +
+            "  (" +
+            "  _key " + keyType + " not null," +
+            "  _val other not null," +
+            "  id int unique," +
+            "  strId varchar(255) ," +
+            "  dateId TIMESTAMP ," +
+            "  orgId int," +
+            "  orgDateId TIMESTAMP," +
+            "  orgStrId varchar(255), " +
+            "  name varchar(255), " +
+            "  salary int" +
+            "  )");
+
+        st.execute("create table \"" + ORG_CACHE_NAME + "\".Organization" +
+            "  (" +
+            "  _key int not null," +
+            "  _val other not null," +
+            "  id int unique," +
+            "  strId varchar(255) ," +
+            "  dateId TIMESTAMP ," +
+            "  name varchar(255) " +
+            "  )");
+
+        conn.commit();
+
+        st.close();
+
+        for (Account account : data.accounts)
+            insertInDb(account);
+
+        for (Person person : data.persons)
+            insertInDb(person);
+
+        for (Organization org : data.orgs)
+            insertInDb(org);
+    }
+
+    /**
+     *
+     */
+    private void initCachesData() {
+        IgniteCache accCache = ignite(0).cache(ACC_CACHE_NAME);
+
+        for (Account account : data.accounts)
+            accCache.put(account.key(useCollocatedData()), account);
+
+        IgniteCache personCache = ignite(0).cache(PERSON_CACHE_NAME);
+
+        for (Person person : data.persons)
+            personCache.put(person.key(useCollocatedData()), person);
+
+        IgniteCache orgCache = ignite(0).cache(ORG_CACHE_NAME);
+
+        for (Organization org : data.orgs)
+            orgCache.put(org.id, org);
+    }
+
+    /**
+     * @param acc Account.
+     * @throws SQLException If failed.
+     */
+    private void insertInDb(Account acc) throws SQLException {
+        try (PreparedStatement st = conn.prepareStatement(
+            "insert into \"" + ACC_CACHE_NAME + "\".Account (_key, _val, id, personId, personDateId, personStrId) " +
+                "values(?, ?, ?, ?, ?, ?)")) {
+            int i = 0;
+
+            st.setObject(++i, acc.key(useCollocatedData()));
+            st.setObject(++i, acc);
+            st.setObject(++i, acc.id);
+            st.setObject(++i, acc.personId);
+            st.setObject(++i, acc.personDateId);
+            st.setObject(++i, acc.personStrId);
+
+            st.executeUpdate();
+        }
+    }
+
+    /**
+     * @param p Person.
+     * @throws SQLException If failed.
+     */
+    private void insertInDb(Person p) throws SQLException {
+        try (PreparedStatement st = conn.prepareStatement(
+            "insert into \"" + PERSON_CACHE_NAME + "\".Person (_key, _val, id, strId, dateId, name, orgId, orgDateId, " +
+                "orgStrId, salary) " +
+                "values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")) {
+            int i = 0;
+
+            st.setObject(++i, p.key(useCollocatedData()));
+            st.setObject(++i, p);
+            st.setObject(++i, p.id);
+            st.setObject(++i, p.strId);
+            st.setObject(++i, p.dateId);
+            st.setObject(++i, p.name);
+            st.setObject(++i, p.orgId);
+            st.setObject(++i, p.orgDateId);
+            st.setObject(++i, p.orgStrId);
+            st.setObject(++i, p.salary);
+
+            st.executeUpdate();
+        }
+    }
+
+    /**
+     * @param o Organization.
+     * @throws SQLException If failed.
+     */
+    private void insertInDb(Organization o) throws SQLException {
+        try (PreparedStatement st = conn.prepareStatement(
+            "insert into \"" + ORG_CACHE_NAME + "\".Organization (_key, _val, id, strId, dateId, name) " +
+                "values(?, ?, ?, ?, ?, ?)")) {
+            int i = 0;
+
+            st.setObject(++i, o.id);
+            st.setObject(++i, o);
+            st.setObject(++i, o.id);
+            st.setObject(++i, o.strId);
+            st.setObject(++i, o.dateId);
+            st.setObject(++i, o.name);
+
+            st.executeUpdate();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void checkAllDataEquals() throws Exception {
+        compareQueryRes0(ignite(0).cache(ACC_CACHE_NAME), "select _key, _val, id, personId, personDateId, personStrId " +
+            "from \"" + ACC_CACHE_NAME + "\".Account");
+
+        compareQueryRes0(ignite(0).cache(PERSON_CACHE_NAME), "select _key, _val, id, strId, dateId, name, orgId, " +
+            "orgDateId, orgStrId, salary from \"" + PERSON_CACHE_NAME + "\".Person");
+
+        compareQueryRes0(ignite(0).cache(ORG_CACHE_NAME), "select _key, _val, id, strId, dateId, name " +
+            "from \"" + ORG_CACHE_NAME + "\".Organization");
+    }
+
+    /** {@inheritDoc} */
+    @Override protected Statement initializeH2Schema() throws SQLException {
+        Statement st = conn.createStatement();
+
+        for (String cacheName : new String[]{"person", "acc", "org"})
+            st.execute("CREATE SCHEMA \"" + cacheName + "\"");
+
+        return st;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected long getTestTimeout() {
+        return 30 * 60_000;
+    }
+
+    /**
+     * @return Distributed joins flag.
+     */
+    protected boolean distributedJoins() {
+        return distributedJoins;
+    }
+
+    /**
+     * @return Use collocated data.
+     */
+    private boolean useCollocatedData() {
+        return !distributedJoins();
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testDistributedJoins1() throws Exception {
+        distributedJoins = true;
+
+        checkAllCacheCombinationsSet1(true);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testDistributedJoins1Offheap() throws Exception {
+        memMode = OFFHEAP_TIERED;
+
+        testDistributedJoins1();
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testDistributedJoins2() throws Exception {
+        distributedJoins = true;
+
+        checkAllCacheCombinationsSet2(true);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testDistributedJoins3() throws Exception {
+        distributedJoins = true;
+
+        checkAllCacheCombinationsSet3(true);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCollocatedJoins1() throws Exception {
+        distributedJoins = false;
+
+        checkAllCacheCombinationsSet1(true);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCollocatedJoins2() throws Exception {
+        distributedJoins = false;
+
+        checkAllCacheCombinationsSet2(true);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCollocatedJoins3() throws Exception {
+        distributedJoins = false;
+
+        checkAllCacheCombinationsSet3(true);
+    }
+
+    /**
+     * @param idx Index flag.
+     * @throws Exception If failed.
+     */
+    private void checkAllCacheCombinationsSet1(boolean idx) throws Exception {
+        List<List<TestCache>> types = cacheCombinations(TestCacheType.REPLICATED);
+
+        checkAllCacheCombinations(idx, types);
+    }
+
+    /**
+     * @param idx Index flag.
+     * @throws Exception If failed.
+     */
+    private void checkAllCacheCombinationsSet2(boolean idx) throws Exception {
+        List<List<TestCache>> types = cacheCombinations(TestCacheType.PARTITIONED_b0);
+
+        checkAllCacheCombinations(idx, types);
+    }
+
+    /**
+     * @param idx Index flag.
+     * @throws Exception If failed.
+     */
+    private void checkAllCacheCombinationsSet3(boolean idx) throws Exception {
+        List<List<TestCache>> types = cacheCombinations(TestCacheType.PARTITIONED_b1);
+
+        checkAllCacheCombinations(idx, types);
+    }
+
+    /**
+     * @param personType Person cache type.
+     * @return Cache combinations.
+     */
+    private List<List<TestCache>> cacheCombinations(TestCacheType personType) {
+        List<List<TestCache>> res = new ArrayList<>();
+
+        for (TestCacheType accCacheType : TestCacheType.values()) {
+            for (TestCacheType orgCacheType : TestCacheType.values()) {
+                List<TestCache> cacheTypes = new ArrayList<>(3);
+
+                cacheTypes.add(new TestCache("person", personType));
+                cacheTypes.add(new TestCache("acc", accCacheType));
+                cacheTypes.add(new TestCache("org", orgCacheType));
+
+                res.add(cacheTypes);
+            }
+        }
+
+        return res;
+    }
+
+    /**
+     * @param idx Index flag.
+     * @param cacheList Caches.
+     * @throws Exception If failed.
+     */
+    private void checkAllCacheCombinations(
+        boolean idx,
+        List<List<TestCache>> cacheList) throws Exception {
+        data = prepareData();
+
+        initCacheAndDbData();
+
+        try {
+            Map<TestConfig, Throwable> errors = new LinkedHashMap<>();
+            List<TestConfig> success = new ArrayList<>();
+
+            int cfgIdx = 0;
+
+            for (List<TestCache> caches : cacheList) {
+                assert caches.size() == 3 : caches;
+
+                TestCache personCache = caches.get(0);
+                TestCache accCache = caches.get(1);
+                TestCache orgCache = caches.get(2);
+
+                try {
+                    check(idx, personCache, accCache, orgCache);
+
+                    success.add(new TestConfig(cfgIdx, cache, personCache, accCache, orgCache, ""));
+                }
+                catch (Throwable e) {
+                    error("", e);
+
+                    errors.put(new TestConfig(cfgIdx, cache, personCache, accCache, orgCache, qry), e);
+                }
+
+                cfgIdx++;
+            }
+
+            if (!errors.isEmpty()) {
+                int total = cacheList.size();
+
+                SB sb = new SB("Test failed for the following " + errors.size() + " combination(s) (" + total + " total):\n");
+
+                for (Map.Entry<TestConfig, Throwable> e : errors.entrySet())
+                    sb.a(e.getKey()).a(", error=").a(e.getValue()).a("\n");
+
+                sb.a("Successfully finished combinations:\n");
+
+                for (TestConfig t : success)
+                    sb.a(t).a("\n");
+
+                sb.a("The following data has beed used for test:\n " + data);
+
+                fail(sb.toString());
+            }
+        }
+        finally {
+            for (String cacheName : new String[]{PERSON_CACHE_NAME, ACC_CACHE_NAME, ORG_CACHE_NAME})
+                ignite(0).destroyCache(cacheName);
+
+            Statement st = conn.createStatement();
+
+            st.execute("drop table \"" + ACC_CACHE_NAME + "\".Account");
+            st.execute("drop table \"" + PERSON_CACHE_NAME + "\".Person");
+            st.execute("drop table \"" + ORG_CACHE_NAME + "\".Organization");
+
+            conn.commit();
+
+            st.close();
+        }
+    }
+
+    /**
+     * @param idx Index flag.
+     * @param personCacheType Person cache personCacheType.
+     * @param accCacheType Account cache personCacheType.
+     * @param orgCacheType Organization cache personCacheType.
+     * @throws Exception If failed.
+     */
+    private void check(
+        boolean idx,
+        final TestCache personCacheType,
+        final TestCache accCacheType,
+        final TestCache orgCacheType) throws Exception {
+        info("Checking cross cache joins [accCache=" + accCacheType +
+            ", personCache=" + personCacheType +
+            ", orgCache=" + orgCacheType + "]");
+
+        Collection<TestCache> cacheTypes = F.asList(personCacheType, accCacheType, orgCacheType);
+
+        for (TestCache cache : cacheTypes) {
+            CacheConfiguration cc = cacheConfiguration(cache.cacheName,
+                cache.type.cacheMode,
+                cache.type.backups,
+                idx,
+                cache == accCacheType,
+                cache == personCacheType,
+                cache == orgCacheType
+            );
+
+            ignite(0).getOrCreateCache(cc);
+
+            info("Created cache [name=" + cache.cacheName + ", mode=" + cache.type + "]");
+        }
+
+        initCachesData();
+
+        // checkAllDataEquals();
+
+        List<String> cacheNames = new ArrayList<>();
+
+        cacheNames.add(personCacheType.cacheName);
+        cacheNames.add(orgCacheType.cacheName);
+        cacheNames.add(accCacheType.cacheName);
+
+        for (int i = 0; i < NODES; i++) {
+            Ignite testNode = ignite(i);
+
+            log.info("Test node [idx=" + i + ", isClient=" + testNode.configuration().isClientMode() + "]");
+
+            for (String cacheName : cacheNames) {
+                cache = testNode.cache(cacheName);
+
+                log.info("Use cache: " + cache.getName());
+
+                boolean distributeJoins0 = distributedJoins;
+
+                if (replicated(cache)) {
+                    if (!testNode.configuration().isClientMode())
+                        assertProperException(cache);
+
+                    boolean all3CachesAreReplicated =
+                        replicated(ignite(0).cache(ACC_CACHE_NAME)) &&
+                        replicated(ignite(0).cache(PERSON_CACHE_NAME)) &&
+                        replicated(ignite(0).cache(ORG_CACHE_NAME));
+
+                    // Queries running on replicated cache should not contain JOINs with partitioned tables.
+                    if (distributeJoins0 && !all3CachesAreReplicated)
+                        continue;
+                    else
+                        distributedJoins = false;
+                }
+
+                if (!cache.getName().equals(orgCacheType.cacheName))
+                    checkPersonAccountsJoin(cache, data.accountsPerPerson);
+
+                if (!cache.getName().equals(accCacheType.cacheName))
+                    checkOrganizationPersonsJoin(cache);
+
+                checkOrganizationPersonAccountJoin(cache);
+
+                checkUnion();
+                checkUnionAll();
+
+                if (!cache.getName().equals(orgCacheType.cacheName))
+                    checkPersonAccountCrossJoin(cache);
+
+                if (!cache.getName().equals(accCacheType.cacheName))
+                    checkPersonOrganizationGroupBy(cache);
+
+                if (!cache.getName().equals(orgCacheType.cacheName))
+                    checkPersonAccountGroupBy(cache);
+
+                checkGroupBy();
+
+                distributedJoins = distributeJoins0;
+            }
+        }
+    }
+
+    /**
+     * @param cache Cache.
+     * @return {@code True} if cache is replicated.
+     */
+    private boolean replicated(IgniteCache<?, ?> cache) {
+        return cache.getConfiguration(CacheConfiguration.class).getCacheMode() == REPLICATED;
+    }
+
+    /**
+     * @param cache Cache.
+     */
+    private void assertProperException(final IgniteCache cache) {
+        qry = "assertProperException";
+
+        GridTestUtils.assertThrows(log, new Callable<Object>() {
+            @Override public Object call() throws Exception {
+                cache.query(new SqlFieldsQuery("select p.name from " +
+                    "\"" + PERSON_CACHE_NAME + "\".Person p, " +
+                    "\"" + ACC_CACHE_NAME + "\".Account a " +
+                    "where p._key = a.personId").setDistributedJoins(true));
+
+                return null;
+            }
+        }, CacheException.class, "Queries using distributed JOINs have to be run on partitioned cache");
+
+        GridTestUtils.assertThrows(log, new Callable<Object>() {
+            @Override public Object call() throws Exception {
+                cache.query(new SqlQuery(Person.class,
+                    "from \"" + PERSON_CACHE_NAME + "\".Person , " +
+                        "\"" + ACC_CACHE_NAME + "\".Account  " +
+                        "where Person._key = Account.personId")
+                    .setDistributedJoins(true));
+
+                return null;
+            }
+        }, CacheException.class, "Queries using distributed JOINs have to be run on partitioned cache");
+    }
+
+    /**
+     * Organization ids: [0, 9]. Person ids: randoms at [10, 9999]. Accounts ids: randoms at [10000, 999_999]
+     *
+     * @return Data.
+     */
+    private Data prepareData() {
+        Map<Integer, Integer> personsPerOrg = new HashMap<>();
+        Map<Integer, Integer> accountsPerPerson = new HashMap<>();
+        Map<Integer, Integer> accountsPerOrg = new HashMap<>();
+        Map<Integer, Integer> maxSalaryPerOrg = new HashMap<>();
+
+        List<Organization> orgs = new ArrayList<>();
+        List<Person> persons = new ArrayList<>();
+        List<Account> accounts = new ArrayList<>();
+
+        final int ORG_CNT = 10;
+        final int MAX_PERSONS_PER_ORG = 20;
+        final int MAX_ACCOUNTS_PER_PERSON = 10;
+
+        for (int id = 0; id < ORG_CNT; id++)
+            orgs.add(new Organization(id, "org-" + id));
+
+        Set<Integer> personIds = new HashSet<>();
+        Set<Integer> accountIds = new HashSet<>();
+
+        for (int orgId = 0; orgId < ORG_CNT; orgId++) {
+            int personsCnt = rnd.nextInt(MAX_PERSONS_PER_ORG);
+
+            int accsPerOrg = 0;
+            int maxSalary = -1;
+
+            for (int p = 0; p < personsCnt; p++) {
+                int personId = rnd.nextInt(10_000) + 10;
+
+                while (!personIds.add(personId))
+                    personId = rnd.nextInt(10_000) + 10;
+
+                String name = "person-" + personId;
+
+                int salary = (rnd.nextInt(10) + 1) * 1000;
+
+                if (salary > maxSalary)
+                    maxSalary = salary;
+
+                persons.add(new Person(personId, orgId, name, salary));
+
+                int accountsCnt = rnd.nextInt(MAX_ACCOUNTS_PER_PERSON);
+
+                for (int a = 0; a < accountsCnt; a++) {
+                    int accountId = rnd.nextInt(100_000) + 10_000;
+
+                    while (!accountIds.add(accountId))
+                        accountId = rnd.nextInt(100_000) + 10_000;
+
+                    accounts.add(new Account(accountId, personId, orgId));
+                }
+
+                accountsPerPerson.put(personId, accountsCnt);
+
+                accsPerOrg += accountsCnt;
+            }
+
+            personsPerOrg.put(orgId, personsCnt);
+            accountsPerOrg.put(orgId, accsPerOrg);
+            maxSalaryPerOrg.put(orgId, maxSalary);
+        }
+
+        return new Data(orgs, persons, accounts, personsPerOrg, accountsPerPerson, accountsPerOrg, maxSalaryPerOrg);
+    }
+
+    /**
+     * @param cacheName Cache name.
+     * @param cacheMode Cache mode.
+     * @param backups Number of backups.
+     * @param idx Index flag.
+     * @param accountCache Account cache flag.
+     * @param personCache Person cache flag.
+     * @param orgCache Organization cache flag.
+     * @return Cache configuration.
+     */
+    private CacheConfiguration cacheConfiguration(String cacheName,
+        CacheMode cacheMode,
+        int backups,
+        boolean idx,
+        boolean accountCache,
+        boolean personCache,
+        boolean orgCache) {
+        CacheConfiguration ccfg = new CacheConfiguration();
+
+        ccfg.setName(cacheName);
+        ccfg.setMemoryMode(memMode);
+        ccfg.setCacheMode(cacheMode);
+
+        if (cacheMode == PARTITIONED)
+            ccfg.setBackups(backups);
+
+        ccfg.setWriteSynchronizationMode(FULL_SYNC);
+
+        List<QueryEntity> entities = new ArrayList<>();
+
+        if (accountCache) {
+            QueryEntity account = new QueryEntity();
+            account.setKeyType(useCollocatedData() ? AffinityKey.class.getName() : Integer.class.getName());
+            account.setValueType(Account.class.getName());
+            account.addQueryField("id", Integer.class.getName(), null);
+            account.addQueryField("personId", Integer.class.getName(), null);
+            account.addQueryField("personDateId", Date.class.getName(), null);
+            account.addQueryField("personStrId", String.class.getName(), null);
+
+            if (idx) {
+                account.setIndexes(F.asList(new QueryIndex("id"),
+                    new QueryIndex("personId"),
+                    new QueryIndex("personDateId"),
+                    new QueryIndex("personStrId")));
+            }
+
+            entities.add(account);
+        }
+
+        if (personCache) {
+            QueryEntity person = new QueryEntity();
+            person.setKeyType(useCollocatedData() ? AffinityKey.class.getName() : Integer.class.getName());
+            person.setValueType(Person.class.getName());
+            person.addQueryField("id", Integer.class.getName(), null);
+            person.addQueryField("dateId", Date.class.getName(), null);
+            person.addQueryField("strId", String.class.getName(), null);
+            person.addQueryField("orgId", Integer.class.getName(), null);
+            person.addQueryField("orgDateId", Date.class.getName(), null);
+            person.addQueryField("orgStrId", String.class.getName(), null);
+            person.addQueryField("name", String.class.getName(), null);
+            person.addQueryField("salary", Integer.class.getName(), null);
+
+            if (idx) {
+                person.setIndexes(F.asList(new QueryIndex("id"),
+                    new QueryIndex("dateId"),
+                    new QueryIndex("strId"),
+                    new QueryIndex("orgId"),
+                    new QueryIndex("orgDateId"),
+                    new QueryIndex("orgStrId"),
+                    new QueryIndex("name"),
+                    new QueryIndex("salary")));
+            }
+
+            entities.add(person);
+        }
+
+        if (orgCache) {
+            QueryEntity org = new QueryEntity();
+            org.setKeyType(Integer.class.getName());
+            org.setValueType(Organization.class.getName());
+            org.addQueryField("id", Integer.class.getName(), null);
+            org.addQueryField("dateId", Date.class.getName(), null);
+            org.addQueryField("strId", String.class.getName(), null);
+            org.addQueryField("name", String.class.getName(), null);
+
+            if (idx) {
+                org.setIndexes(F.asList(new QueryIndex("id"),
+                    new QueryIndex("dateId"),
+                    new QueryIndex("strId"),
+                    new QueryIndex("name")));
+            }
+
+            entities.add(org);
+        }
+
+        ccfg.setQueryEntities(entities);
+
+        return ccfg;
+    }
+
+    /**
+     * @param cache Cache.
+     */
+    private void checkOrganizationPersonsJoin(IgniteCache cache) {
+        if (skipQuery(cache, PERSON_CACHE_NAME, ORG_CACHE_NAME))
+            return;
+
+        qry = "checkOrganizationPersonsJoin";
+
+        SqlFieldsQuery qry = new SqlFieldsQuery("select o.name, p.name " +
+            "from \"" + ORG_CACHE_NAME + "\".Organization o, \"" + PERSON_CACHE_NAME + "\".Person p " +
+            "where p.orgId = o._key and o._key=?");
+
+        qry.setDistributedJoins(distributedJoins());
+
+        SqlQuery qry2 = null;
+
+        if (PERSON_CACHE_NAME.equals(cache.getName())) {
+            qry2 = new SqlQuery(Person.class,
+                "from \"" + ORG_CACHE_NAME + "\".Organization, \"" + PERSON_CACHE_NAME + "\".Person " +
+                    "where Person.orgId = Organization._key and Organization._key=?"
+            );
+
+            qry2.setDistributedJoins(distributedJoins());
+        }
+
+        long total = 0;
+
+        for (int i = 0; i < data.personsPerOrg.size(); i++) {
+            qry.setArgs(i);
+
+            if (qry2 != null)
+                qry2.setArgs(i);
+
+            List<List<Object>> res = cache.query(qry).getAll();
+
+            assertEquals((int)data.personsPerOrg.get(i), res.size());
+
+            if (qry2 != null) {
+                List<List<Object>> res2 = cache.query(qry2).getAll();
+
+                assertEquals((int)data.personsPerOrg.get(i), res2.size());
+            }
+
+            total += res.size();
+        }
+
+        SqlFieldsQuery qry3 = new SqlFieldsQuery("select count(*) " +
+            "from \"" + ORG_CACHE_NAME + "\".Organization o, \"" + PERSON_CACHE_NAME + "\".Person p where p.orgId = o._key");
+
+        qry3.setDistributedJoins(distributedJoins());
+
+        List<List<Object>> res = cache.query(qry3).getAll();
+
+        assertEquals(1, res.size());
+        assertEquals(total, res.get(0).get(0));
+    }
+
+    /**
+     * @param cache Cache.
+     * @param cnts Accounts per person counts.
+     */
+    private void checkPersonAccountsJoin(IgniteCache cache, Map<Integer, Integer> cnts) {
+        if (skipQuery(cache, PERSON_CACHE_NAME, ACC_CACHE_NAME))
+            return;
+
+        qry = "checkPersonAccountsJoin";
+
+        List<Query> qrys = new ArrayList<>();
+
+        qrys.add(new SqlFieldsQuery("select p.name from " +
+                "\"" + PERSON_CACHE_NAME + "\".Person p, " +
+                "\"" + ACC_CACHE_NAME + "\".Account a " +
+                "where p.id = a.personId and p.id=?")
+        );
+
+        qrys.add(new SqlFieldsQuery("select p.name from " +
+                "\"" + PERSON_CACHE_NAME + "\".Person p, " +
+                "\"" + ACC_CACHE_NAME + "\".Account a " +
+                "where p.dateId = a.personDateId and p.id=?")
+        );
+
+        qrys.add(new SqlFieldsQuery("select p.name from " +
+                "\"" + PERSON_CACHE_NAME + "\".Person p, " +
+                "\"" + ACC_CACHE_NAME + "\".Account a " +
+                "where p.strId = a.personStrId and p.id=?")
+        );
+
+        qrys.add(new SqlFieldsQuery("select p.name from " +
+                "\"" + PERSON_CACHE_NAME + "\".Person p, " +
+                "\"" + ACC_CACHE_NAME + "\".Account a " +
+                "where p.id = a.personId and p.id=?")
+        );
+
+        qrys.add(new SqlFieldsQuery("select p.name from " +
+                "\"" + PERSON_CACHE_NAME + "\".Person p, " +
+                "\"" + ACC_CACHE_NAME + "\".Account a " +
+                "where p.dateId = a.personDateId and p.id=?")
+        );
+
+        qrys.add(new SqlFieldsQuery("select p.name from " +
+                "\"" + PERSON_CACHE_NAME + "\".Person p, " +
+                "\"" + ACC_CACHE_NAME + "\".Account a " +
+                "where p.strId = a.personStrId and p.id=?")
+        );
+
+        if (PERSON_CACHE_NAME.equals(cache.getName())) {
+            qrys.add(new SqlQuery(Person.class,
+                    "from \"" + PERSON_CACHE_NAME + "\".Person , \"" + ACC_CACHE_NAME + "\".Account  " +
+                        "where Person.id = Account.personId and Person.id=?")
+            );
+
+            qrys.add(new SqlQuery(Person.class,
+                    "from \"" + PERSON_CACHE_NAME + "\".Person , \"" + ACC_CACHE_NAME + "\".Account  " +
+                        "where Person.id = Account.personId and Person.id=?")
+            );
+        }
+
+        List<Integer> keys = new ArrayList<>(cnts.keySet());
+
+        for (int i = 0; i < 10; i++) {
+            Integer key = keys.get(rnd.nextInt(keys.size()));
+
+            List<List<Object>> res;
+
+            for (Query q : qrys) {
+                if (q instanceof SqlFieldsQuery) {
+                    ((SqlFieldsQuery)q).setDistributedJoins(distributedJoins());
+
+                    ((SqlFieldsQuery)q).setArgs(key);
+                }
+                else {
+                    ((SqlQuery)q).setDistributedJoins(distributedJoins());
+
+                    ((SqlQuery)q).setArgs(key);
+                }
+
+                res = cache.query(q).getAll();
+
+                assertEquals((int)cnts.get(key), res.size());
+            }
+        }
+
+        qrys.clear();
+
+        qrys.add(new SqlFieldsQuery("select count(*) " +
+            "from \"" + PERSON_CACHE_NAME + "\".Person p, \"" + ACC_CACHE_NAME + "\".Account" + " a " +
+            "where p.id = a.personId"));
+
+        qrys.add(new SqlFieldsQuery("select count(*) " +
+            "from \"" + PERSON_CACHE_NAME + "\".Person p, \"" + ACC_CACHE_NAME + "\".Account" + " a " +
+            "where p.Dateid = a.personDateId"));
+
+        qrys.add(new SqlFieldsQuery("select count(*) " +
+            "from \"" + PERSON_CACHE_NAME + "\".Person p, \"" + ACC_CACHE_NAME + "\".Account" + " a " +
+            "where p.strId = a.personStrId"));
+
+        qrys.add(new SqlFieldsQuery("select count(*) " +
+            "from \"" + PERSON_CACHE_NAME + "\".Person p, \"" + ACC_CACHE_NAME + "\".Account" + " a " +
+            "where p.id = a.personId"));
+
+        long total = 0;
+
+        for (Integer cnt : data.accountsPerPerson.values())
+            total += cnt;
+
+        for (Query q : qrys) {
+            ((SqlFieldsQuery)q).setDistributedJoins(distributedJoins());
+
+            List<List<Object>> res = cache.query(q).getAll();
+
+            assertEquals(1, res.size());
+            assertEquals(total, res.get(0).get(0));
+        }
+    }
+
+    /**
+     * @param cache Cache.
+     * @throws Exception If failed.
+     */
+    private void checkOrganizationPersonAccountJoin(IgniteCache cache) throws Exception {
+        if (skipQuery(cache, PERSON_CACHE_NAME, ORG_CACHE_NAME, ACC_CACHE_NAME))
+            return;
+
+        qry = "checkOrganizationPersonAccountJoin";
+
+        List<String> sqlFields = new ArrayList<>();
+
+        sqlFields.add("select o.name, p.name, a._key " +
+            "from " +
+            "\"" + ORG_CACHE_NAME + "\".Organization o, " +
+            "\"" + PERSON_CACHE_NAME + "\".Person p, " +
+            "\"" + ACC_CACHE_NAME + "\".Account a " +
+            "where p.orgId = o.id and p.id = a.personId and o.id = ?");
+
+        sqlFields.add("select o.name, p.name, a._key " +
+            "from " +
+            "\"" + ORG_CACHE_NAME + "\".Organization o, " +
+            "\"" + PERSON_CACHE_NAME + "\".Person p, " +
+            "\"" + ACC_CACHE_NAME + "\".Account a " +
+            "where p.orgDateId = o.dateId and p.strId = a.personStrId and o.id = ?");
+
+        sqlFields.add("select o.name, p.name, a._key " +
+            "from " +
+            "\"" + ORG_CACHE_NAME + "\".Organization o, " +
+            "\"" + PERSON_CACHE_NAME + "\".Person p, " +
+            "\"" + ACC_CACHE_NAME + "\".Account a " +
+            "where p.orgStrId = o.strId and p.id = a.personId and o.id = ?");
+
+        for (Organization org : data.orgs) {
+            for (String sql : sqlFields)
+                compareQueryRes0(cache, sql, distributedJoins(), new Object[] {org.id}, Ordering.RANDOM);
+        }
+
+        if (ACC_CACHE_NAME.equals(cache.getName())) {
+            for (int orgId = 0; orgId < data.accountsPerOrg.size(); orgId++) {
+                SqlQuery q = new SqlQuery(Account.class, "from " +
+                    "\"" + ORG_CACHE_NAME + "\".Organization , " +
+                    "\"" + PERSON_CACHE_NAME + "\".Person , " +
+                    "\"" + ACC_CACHE_NAME + "\".Account  " +
+                    "where Person.orgId = Organization.id and Person.id = Account.personId and Organization.id = ?");
+
+                q.setDistributedJoins(distributedJoins());
+
+                q.setArgs(orgId);
+
+                List<List<Object>> res = cache.query(q).getAll();
+
+                assertEquals((int)data.accountsPerOrg.get(orgId), res.size());
+            }
+        }
+
+        String sql = "select count(*) " +
+            "from " +
+            "\"" + ORG_CACHE_NAME + "\".Organization o, " +
+            "\"" + PERSON_CACHE_NAME + "\".Person p, " +
+            "\"" + ACC_CACHE_NAME + "\".Account a " +
+            "where p.orgId = o.id and p.id = a.personId";
+
+        compareQueryRes0(cache, sql, distributedJoins(), new Object[0], Ordering.RANDOM);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    private void checkUnionAll() throws Exception {
+        if (skipQuery(cache, PERSON_CACHE_NAME, ACC_CACHE_NAME, ORG_CACHE_NAME))
+            return;
+
+        qry = "checkUnionAll";
+
+        String sql = "select a.id, p.name from " +
+            "\"" + PERSON_CACHE_NAME + "\".Person p, " +
+            "\"" + ACC_CACHE_NAME + "\".Account a " +
+            "where p.id = a.personId and p.id = ? " +
+            "union all " +
+            "select p.id, o.name from " +
+            "\"" + ORG_CACHE_NAME + "\".Organization o, " +
+            "\"" + PERSON_CACHE_NAME + "\".Person p " +
+            "where p.orgStrId = o.strId and o.id = ?";
+
+        for (int i = 0; i < 10; i++) {
+            Person person = data.persons.get(rnd.nextInt(data.persons.size()));
+
+            for (Organization org : data.orgs)
+                compareQueryRes0(cache, sql, distributedJoins(), new Object[] {person.id, org.id}, Ordering.RANDOM);
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    private void checkUnion() throws Exception {
+        if (skipQuery(cache, PERSON_CACHE_NAME, ACC_CACHE_NAME, ORG_CACHE_NAME))
+            return;
+
+        qry = "checkUnion";
+
+        String sql = "select a.id, p.name from " +
+            "\"" + PERSON_CACHE_NAME + "\".Person p, " +
+            "\"" + ACC_CACHE_NAME + "\".Account a " +
+            "where p.id = a.personId and p.id = ? " +
+            "union " +
+            "select p.id, o.name from " +
+            "\"" + ORG_CACHE_NAME + "\".Organization o, " +
+            "\"" + PERSON_CACHE_NAME + "\".Person p " +
+            "where p.orgStrId = o.strId and o.id = ?";
+
+        for (int i = 0; i < 10; i++) {
+            Person person = data.persons.get(rnd.nextInt(data.persons.size()));
+
+            for (Organization org : data.orgs)
+                compareQueryRes0(cache, sql, distributedJoins(), new Object[] {person.id, org.id}, Ordering.RANDOM);
+        }
+    }
+
+    /**
+     * @param cache Cache.
+     * @throws Exception If failed.
+     */
+    private void checkPersonAccountCrossJoin(IgniteCache cache) throws Exception {
+        if (skipQuery(cache, PERSON_CACHE_NAME, ACC_CACHE_NAME))
+            return;
+
+        qry = "checkPersonAccountCrossJoin";
+
+        String sql = "select p.name " +
+            "from \"" + PERSON_CACHE_NAME + "\".Person p " +
+            "cross join \"" + ACC_CACHE_NAME + "\".Account a";
+
+        compareQueryRes0(cache, sql, distributedJoins(), new Object[0], Ordering.RANDOM);
+    }
+
+    /**
+     * @param cache Cache.
+     */
+    private void checkPersonOrganizationGroupBy(IgniteCache cache) {
+        if (skipQuery(cache, PERSON_CACHE_NAME, ORG_CACHE_NAME))
+            return;
+
+        qry = "checkPersonOrganizationGroupBy";
+
+        // Max salary per organization.
+        SqlFieldsQuery q = new SqlFieldsQuery("select max(p.salary) " +
+            "from \"" + PERSON_CACHE_NAME + "\".Person p join \"" + ORG_CACHE_NAME + "\".Organization o " +
+            "on p.orgId = o.id " +
+            "group by o.name " +
+            "having o.id = ?");
+
+        q.setDistributedJoins(distributedJoins());
+
+        for (Map.Entry<Integer, Integer> e : data.maxSalaryPerOrg.entrySet()) {
+            Integer orgId = e.getKey();
+            Integer maxSalary = e.getValue();
+
+            q.setArgs(orgId);
+
+            List<List<?>> res = cache.query(q).getAll();
+
+            String errMsg = "Expected data [orgId=" + orgId + ", maxSalary=" + maxSalary + ", data=" + data + "]";
+
+            // MaxSalary == -1 means that there are no persons at organization.
+            if (maxSalary > 0) {
+                assertEquals(errMsg, 1, res.size());
+                assertEquals(errMsg, 1, res.get(0).size());
+                assertEquals(errMsg, maxSalary, res.get(0).get(0));
+            }
+            else
+                assertEquals(errMsg, 0, res.size());
+        }
+    }
+
+    /**
+     * @param cache Cache.
+     */
+    private void checkPersonAccountGroupBy(IgniteCache cache) {
+        if (skipQuery(cache, PERSON_CACHE_NAME, ACC_CACHE_NAME))
+            return;
+
+        qry = "checkPersonAccountGroupBy";
+
+        // Count accounts per person.
+        SqlFieldsQuery q = new SqlFieldsQuery("select count(a.id) " +
+            "from \"" + PERSON_CACHE_NAME + "\".Person p join \"" + ACC_CACHE_NAME + "\".Account a " +
+            "on p.strId = a.personStrId " +
+            "group by p.name " +
+            "having p.id = ?");
+
+        q.setDistributedJoins(distributedJoins());
+
+        List<Integer> keys = new ArrayList<>(data.accountsPerPerson.keySet());
+
+        for (int i = 0; i < 10; i++) {
+            Integer personId = keys.get(rnd.nextInt(keys.size()));
+            Integer cnt = data.accountsPerPerson.get(personId);
+
+            q.setArgs(personId);
+
+            List<List<?>> res = cache.query(q).getAll();
+
+            String errMsg = "Expected data [personId=" + personId + ", cnt=" + cnt + ", data=" + data + "]";
+
+            // Cnt == 0 means that there are no accounts for the person.
+            if (cnt > 0) {
+                assertEquals(errMsg, 1, res.size());
+                assertEquals(errMsg, 1, res.get(0).size());
+                assertEquals(errMsg, (long)cnt, res.get(0).get(0));
+            }
+            else
+                assertEquals(errMsg, 0, res.size());
+        }
+    }
+
+    /**
+     * @param cache Cache.
+     */
+    private void checkPersonAccountOrganizationGroupBy(IgniteCache cache) {
+        qry = "checkPersonAccountOrganizationGroupBy";
+
+        // Max count of accounts at org.
+        SqlFieldsQuery q = new SqlFieldsQuery("select max(count(a.id)) " +
+            "from " +
+            "\"" + PERSON_CACHE_NAME + "\".Person p " +
+            "\"" + ORG_CACHE_NAME + "\".Organization o " +
+            "\"" + ACC_CACHE_NAME + "\".Account a " +
+            "where p.id = a.personId and p.orgStrId = o.strId " +
+            "group by org.id " +
+            "having o.id = ?");
+
+        q.setDistributedJoins(distributedJoins());
+
+        for (Map.Entry<Integer, Integer> e : data.accountsPerPerson.entrySet()) {
+            Integer personId = e.getKey();
+            Integer cnt = e.getValue();
+
+            q.setArgs(personId);
+
+            List<List<?>> res = cache.query(q).getAll();
+
+            String errMsg = "Expected data [personId=" + personId + ", cnt=" + cnt + ", data=" + data + "]";
+
+            // Cnt == 0 means that there are no accounts for the person.
+            if (cnt > 0) {
+                assertEquals(errMsg, 1, res.size());
+                assertEquals(errMsg, 1, res.get(0).size());
+                assertEquals(errMsg, (long)cnt, res.get(0).get(0));
+            }
+            else
+                assertEquals(errMsg, 0, res.size());
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    private void checkGroupBy() throws Exception {
+        if (skipQuery(cache, PERSON_CACHE_NAME, ACC_CACHE_NAME, ORG_CACHE_NAME))
+            return;
+
+        qry = "checkGroupBy";
+
+        // Select persons with count of accounts of person at organization.
+        String sql = "select p.id, count(a.id) " +
+            "from " +
+            "\"" + PERSON_CACHE_NAME + "\".Person p, " +
+            "\"" + ORG_CACHE_NAME + "\".Organization o, " +
+            "\"" + ACC_CACHE_NAME + "\".Account a " +
+            "where p.id = a.personId and p.orgStrId = o.strId " +
+            "group by p.id " +
+            "having o.id = ?";
+
+        for (Organization org : data.orgs)
+            compareQueryRes0(cache, sql, distributedJoins(), new Object[] {org.id}, Ordering.RANDOM);
+    }
+
+    /**
+     * @param cache Cache used to run query.
+     * @param caches Cache names.
+     * @return {@code True} if skip query execution.
+     */
+    private boolean skipQuery(IgniteCache cache, String... caches) {
+        // Skip join replicated/partitioned caches executed on replicated cache.
+        Ignite node = (Ignite)cache.unwrap(Ignite.class);
+
+        if (!distributedJoins() && replicated(cache)) {
+            for (String cacheName : caches) {
+                if (!replicated(node.cache(cacheName)))
+                    return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     *
+     */
+    private enum TestCacheType {
+        /** */
+        REPLICATED(CacheMode.REPLICATED, 0),
+
+        /** */
+        PARTITIONED_b0(CacheMode.PARTITIONED, 0),
+
+        /** */
+        PARTITIONED_b1(CacheMode.PARTITIONED, 1);
+
+        /** */
+        final CacheMode cacheMode;
+
+        /** */
+        final int backups;
+
+        /**
+         * @param mode Cache mode.
+         * @param backups Backups.
+         */
+        TestCacheType(CacheMode mode, int backups) {
+            cacheMode = mode;
+            this.backups = backups;
+        }
+    }
+
+    /**
+     *
+     */
+    private static class TestCache {
+        /** */
+        @GridToStringInclude
+        final String cacheName;
+
+        /** */
+        @GridToStringInclude
+        final TestCacheType type;
+
+        /**
+         * @param cacheName Cache name.
+         * @param type Cache type.
+         */
+        public TestCache(String cacheName, TestCacheType type) {
+            this.cacheName = cacheName;
+            this.type = type;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return S.toString(TestCache.class, this);
+        }
+    }
+
+    /**
+     *
+     */
+    private static class Data {
+        /** */
+        final List<Organization> orgs;
+
+        /** */
+        final List<Person> persons;
+
+        /** */
+        final List<Account> accounts;
+
+        /** */
+        final Map<Integer, Integer> personsPerOrg;
+
+        /** PersonId to count of accounts that person has. */
+        final Map<Integer, Integer> accountsPerPerson;
+
+        /** */
+        final Map<Integer, Integer> accountsPerOrg;
+
+        /** */
+        final Map<Integer, Integer> maxSalaryPerOrg;
+
+        /**
+         * @param orgs Organizations.
+         * @param persons Persons.
+         * @param accounts Accounts.
+         * @param personsPerOrg Count of persons per organization.
+         * @param accountsPerPerson Count of accounts per person.
+         * @param accountsPerOrg Count of accounts per organization.
+         * @param maxSalaryPerOrg Maximum salary per organization.
+         */
+        Data(List<Organization> orgs,
+            List<Person> persons,
+            List<Account> accounts,
+            Map<Integer, Integer> personsPerOrg,
+            Map<Integer, Integer> accountsPerPerson,
+            Map<Integer, Integer> accountsPerOrg,
+             Map<Integer, Integer> maxSalaryPerOrg) {
+            this.orgs = orgs;
+            this.persons = persons;
+            this.accounts = accounts;
+            this.personsPerOrg = personsPerOrg;
+            this.accountsPerPerson = accountsPerPerson;
+            this.accountsPerOrg = accountsPerOrg;
+            this.maxSalaryPerOrg = maxSalaryPerOrg;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return "Data [" +
+                "orgs=" + orgs +
+                ", persons=" + persons +
+                ", accounts=" + accounts +
+                ", personsPerOrg=" + personsPerOrg +
+                ", accountsPerPerson=" + accountsPerPerson +
+                ", accountsPerOrg=" + accountsPerOrg +
+                ", maxSalaryPerOrg=" + maxSalaryPerOrg +
+                ']';
+        }
+    }
+
+    /**
+     *
+     */
+    private static class Account implements Serializable {
+        /** */
+        @QuerySqlField
+        private int id;
+
+        /** */
+        @QuerySqlField
+        private int personId;
+
+        /** */
+        @QuerySqlField
+        private Date personDateId;
+
+        /** */
+        @QuerySqlField
+        private String personStrId;
+
+        /** */
+        @QuerySqlField
+        private int orgId;
+
+        /**
+         * @param id ID.
+         * @param personId Person ID.
+         * @param orgId Organization ID.
+         */
+        Account(int id, int personId, int orgId) {
+            this.id = id;
+            this.personId = personId;
+            this.orgId = orgId;
+            personDateId = new Date(personId);
+            personStrId = "personId" + personId;
+        }
+
+        /**
+         * @param useCollocatedData Use colocated data.
+         * @return Key.
+         */
+        public Object key(boolean useCollocatedData) {
+            return useCollocatedData ? new AffinityKey<>(id, orgId) : id;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return "Account [" +
+                "id=" + id +
+                ", personId=" + personId +
+                ']';
+        }
+    }
+
+    /**
+     *
+     */
+    private static class Person implements Serializable {
+        /** */
+        @QuerySqlField
+        int id;
+
+        /** Date as ID. */
+        @QuerySqlField
+        Date dateId;
+
+        /** String as ID */
+        @QuerySqlField
+        String strId;
+
+        /** */
+        @QuerySqlField
+        int orgId;
+
+        /** */
+        @QuerySqlField
+        Date orgDateId;
+
+        /** */
+        @QuerySqlField
+        String orgStrId;
+
+        /** */
+        @QuerySqlField
+        String name;
+
+        /** */
+        @QuerySqlField
+        int salary;
+
+        /**
+         * @param id ID.
+         * @param orgId Organization ID.
+         * @param name Name.
+         * @param salary Salary.
+         */
+        Person(int id, int orgId, String name, int salary) {
+            this.id = id;
+            dateId = new Date(id);
+            strId = "personId" + id;
+            this.orgId = orgId;
+            orgDateId = new Date(orgId);
+            orgStrId = "orgId" + orgId;
+            this.name = name;
+            this.salary = salary;
+        }
+
+        /**
+         * @param useCollocatedData Use collocated data.
+         * @return Key.
+         */
+        public Object key(boolean useCollocatedData) {
+            return useCollocatedData ? new AffinityKey<>(id, orgId) : id;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return "Person [" +
+                "id=" + id +
+                ", orgId=" + orgId +
+                ", name='" + name + '\'' +
+                ", salary=" + salary +
+                ']';
+        }
+    }
+
+    /**
+     *
+     */
+    private static class Organization implements Serializable {
+        /** */
+        @QuerySqlField
+        int id;
+
+        /** */
+        @QuerySqlField
+        Date dateId;
+
+        /** */
+        @QuerySqlField
+        String strId;
+
+        /** */
+        @QuerySqlField
+        String name;
+
+        /**
+         * @param id ID.
+         * @param name Name.
+         */
+        Organization(int id, String name) {
+            this.id = id;
+            dateId = new Date(id);
+            strId = "orgId" + id;
+            this.name = name;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return "Organization [" +
+                "name='" + name + '\'' +
+                ", id=" + id +
+                ']';
+        }
+    }
+
+    /**
+     *
+     */
+    private static class TestConfig {
+        /** */
+        private final int idx;
+
+        /** */
+        private final IgniteCache testedCache;
+
+        /** */
+        private final TestCache personCache;
+
+        /** */
+        private final TestCache accCache;
+
+        /** */
+        private final TestCache orgCache;
+
+        /** */
+        private final String qry;
+
+        /**
+         * @param cfgIdx Tested configuration index.
+         * @param testedCache Tested testedCache.
+         * @param personCacheType Person testedCache personCacheType.
+         * @param accCacheType Account testedCache personCacheType.
+         * @param orgCacheType Organization testedCache personCacheType.
+         * @param testedQry Query.
+         */
+        TestConfig(int cfgIdx,
+            IgniteCache testedCache,
+            TestCache personCacheType,
+            TestCache accCacheType,
+            TestCache orgCacheType,
+            String testedQry) {
+            idx = cfgIdx;
+            this.testedCache = testedCache;
+            this.personCache = personCacheType;
+            this.accCache = accCacheType;
+            this.orgCache = orgCacheType;
+            qry = testedQry;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return "TestConfig [" +
+                "idx=" + idx +
+                ", testedCache=" + testedCache.getName() +
+                ", personCache=" + personCache +
+                ", accCache=" + accCache +
+                ", orgCache=" + orgCache +
+                ", qry=" + qry +
+                ']';
+        }
+    }
+}


[02/50] [abbrv] ignite git commit: Merge remote-tracking branch 'remotes/community/ignite-1.6.3'

Posted by vo...@apache.org.
Merge remote-tracking branch 'remotes/community/ignite-1.6.3'


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

Branch: refs/heads/ignite-3553
Commit: a989f04ec08638855f13ef4195ce7d9d1efee083
Parents: f7e8a6d 0f1a0cc
Author: Denis Magda <dm...@gridgain.com>
Authored: Fri Jul 22 14:20:16 2016 +0300
Committer: Denis Magda <dm...@gridgain.com>
Committed: Fri Jul 22 14:20:16 2016 +0300

----------------------------------------------------------------------
 .../binary/CacheObjectBinaryProcessorImpl.java  |   6 +
 .../internal/processors/igfs/IgfsImpl.java      |  77 ++---
 .../processors/igfs/IgfsModeResolver.java       |  94 +-----
 .../internal/processors/igfs/IgfsUtils.java     |  83 +++++
 .../BinaryObjectBuilderAdditionalSelfTest.java  |  38 ++-
 ...sCachePerBlockLruEvictionPolicySelfTest.java |   7 +-
 .../processors/igfs/IgfsMetricsSelfTest.java    |  54 +--
 .../igfs/IgfsModeResolverSelfTest.java          | 124 +++++--
 .../processors/igfs/IgfsModesSelfTest.java      |  61 ++--
 .../hadoop/fs/v2/IgniteHadoopFileSystem.java    |  22 +-
 .../igfs/HadoopFIleSystemFactorySelfTest.java   |   2 +-
 ...teHadoopFileSystemSecondaryModeSelfTest.java | 327 -------------------
 .../testsuites/IgniteHadoopTestSuite.java       |   2 -
 .../stream/kafka/connect/package-info.java      |  21 ++
 .../connect/serialization/package-info.java     |  21 ++
 15 files changed, 360 insertions(+), 579 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/a989f04e/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsImpl.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/ignite/blob/a989f04e/modules/core/src/test/java/org/apache/ignite/internal/processors/igfs/IgfsModesSelfTest.java
----------------------------------------------------------------------


[24/50] [abbrv] ignite git commit: ignite-3573 Support of Distributed joins queries in REST API

Posted by vo...@apache.org.
ignite-3573 Support of Distributed joins queries in REST API


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

Branch: refs/heads/ignite-3553
Commit: f245c3838bd2eda4aa11b0bd0d07223b9cb2f237
Parents: 0e15cf1
Author: agura <ag...@gridgain.com>
Authored: Mon Jul 25 14:31:10 2016 +0300
Committer: agura <ag...@gridgain.com>
Committed: Mon Jul 25 15:22:47 2016 +0300

----------------------------------------------------------------------
 .../JettyRestProcessorAbstractSelfTest.java     | 121 ++++++++++++++++++-
 .../handlers/query/QueryCommandHandler.java     |   4 +
 .../rest/request/RestQueryRequest.java          |  17 +++
 .../http/jetty/GridJettyRestHandler.java        |   5 +
 4 files changed, 142 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/f245c383/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAbstractSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAbstractSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAbstractSelfTest.java
index 0dd12ac..8d3ab74 100644
--- a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAbstractSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAbstractSelfTest.java
@@ -1687,6 +1687,32 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro
     /**
      * @throws Exception If failed.
      */
+    public void testDistributedJoinsQuery() throws Exception {
+        String qry = "select * from Person, \"organization\".Organization " +
+            "where \"organization\".Organization.id = Person.orgId " +
+            "and \"organization\".Organization.name = ?";
+
+        Map<String, String> params = new HashMap<>();
+        params.put("cmd", GridRestCommand.EXECUTE_SQL_QUERY.key());
+        params.put("type", "Person");
+        params.put("distributedJoins", "true");
+        params.put("pageSize", "10");
+        params.put("cacheName", "person");
+        params.put("qry", URLEncoder.encode(qry, CHARSET));
+        params.put("arg1", "o1");
+
+        String ret = content(params);
+
+        JsonNode items = jsonResponse(ret).get("items");
+
+        assertEquals(2, items.size());
+
+        assertFalse(queryCursorFound());
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
     public void testSqlFieldsQuery() throws Exception {
         String qry = "select concat(firstName, ' ', lastName) from Person";
 
@@ -1708,6 +1734,28 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro
     /**
      * @throws Exception If failed.
      */
+    public void testDistributedJoinsSqlFieldsQuery() throws Exception {
+        String qry = "select * from \"person\".Person p, \"organization\".Organization o where o.id = p.orgId";
+
+        Map<String, String> params = new HashMap<>();
+        params.put("cmd", GridRestCommand.EXECUTE_SQL_FIELDS_QUERY.key());
+        params.put("distributedJoins", "true");
+        params.put("pageSize", "10");
+        params.put("cacheName", "person");
+        params.put("qry", URLEncoder.encode(qry, CHARSET));
+
+        String ret = content(params);
+
+        JsonNode items = jsonResponse(ret).get("items");
+
+        assertEquals(4, items.size());
+
+        assertFalse(queryCursorFound());
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
     public void testSqlFieldsMetadataQuery() throws Exception {
         String qry = "select firstName, lastName from Person";
 
@@ -1831,6 +1879,20 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro
      * Init cache.
      */
     private void initCache() {
+        CacheConfiguration<Integer, Organization> orgCacheCfg = new CacheConfiguration<>("organization");
+
+        orgCacheCfg.setIndexedTypes(Integer.class, Organization.class);
+
+        IgniteCache<Integer, Organization> orgCache = ignite(0).getOrCreateCache(orgCacheCfg);
+
+        orgCache.clear();
+
+        Organization o1 = new Organization(1, "o1");
+        Organization o2 = new Organization(2, "o2");
+
+        orgCache.put(1, o1);
+        orgCache.put(2, o2);
+
         CacheConfiguration<Integer, Person> personCacheCfg = new CacheConfiguration<>("person");
 
         personCacheCfg.setIndexedTypes(Integer.class, Person.class);
@@ -1839,10 +1901,10 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro
 
         personCache.clear();
 
-        Person p1 = new Person("John", "Doe", 2000);
-        Person p2 = new Person("Jane", "Doe", 1000);
-        Person p3 = new Person("John", "Smith", 1000);
-        Person p4 = new Person("Jane", "Smith", 2000);
+        Person p1 = new Person(1, "John", "Doe", 2000);
+        Person p2 = new Person(1, "Jane", "Doe", 1000);
+        Person p3 = new Person(2, "John", "Smith", 1000);
+        Person p4 = new Person(2, "Jane", "Smith", 2000);
 
         personCache.put(p1.getId(), p1);
         personCache.put(p2.getId(), p2);
@@ -1856,6 +1918,43 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro
         assertEquals(2, personCache.query(qry).getAll().size());
     }
 
+
+    /**
+     * Organization class.
+     */
+    public static class Organization implements Serializable {
+        /** Organization ID (indexed). */
+        @QuerySqlField(index = true)
+        private Integer id;
+
+        /** First name (not-indexed). */
+        @QuerySqlField(index = true)
+        private String name;
+
+        /**
+         * @param id Id.
+         * @param name Name.
+         */
+        Organization(Integer id, String name) {
+            this.id = id;
+            this.name = name;
+        }
+
+        /**
+         * @return Id.
+         */
+        public Integer getId() {
+            return id;
+        }
+
+        /**
+         * @return Name.
+         */
+        public String getName() {
+            return name;
+        }
+    }
+
     /**
      * Person class.
      */
@@ -1867,6 +1966,10 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro
         @QuerySqlField(index = true)
         private Integer id;
 
+        /** Organization id. */
+        @QuerySqlField(index = true)
+        private Integer orgId;
+
         /** First name (not-indexed). */
         @QuerySqlField
         private String firstName;
@@ -1884,15 +1987,23 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro
          * @param lastName Last name.
          * @param salary Salary.
          */
-        Person(String firstName, String lastName, double salary) {
+        Person(Integer orgId, String firstName, String lastName, double salary) {
             id = PERSON_ID++;
 
+            this.orgId = orgId;
             this.firstName = firstName;
             this.lastName = lastName;
             this.salary = salary;
         }
 
         /**
+         * @return Organization ID.
+         */
+        public Integer getOrganizationId() {
+            return orgId;
+        }
+
+        /**
          * @return First name.
          */
         public String getFirstName() {

http://git-wip-us.apache.org/repos/asf/ignite/blob/f245c383/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/query/QueryCommandHandler.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/query/QueryCommandHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/query/QueryCommandHandler.java
index 67e146b..4317dd9 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/query/QueryCommandHandler.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/query/QueryCommandHandler.java
@@ -278,6 +278,8 @@ public class QueryCommandHandler extends GridRestCommandHandlerAdapter {
 
                         ((SqlQuery)qry).setArgs(req.arguments());
 
+                        ((SqlQuery)qry).setDistributedJoins(req.distributedJoins());
+
                         break;
 
                     case SQL_FIELDS:
@@ -285,6 +287,8 @@ public class QueryCommandHandler extends GridRestCommandHandlerAdapter {
 
                         ((SqlFieldsQuery)qry).setArgs(req.arguments());
 
+                        ((SqlFieldsQuery)qry).setDistributedJoins(req.distributedJoins());
+
                         break;
 
                     case SCAN:

http://git-wip-us.apache.org/repos/asf/ignite/blob/f245c383/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/request/RestQueryRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/request/RestQueryRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/request/RestQueryRequest.java
index a719776..7159c83 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/request/RestQueryRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/request/RestQueryRequest.java
@@ -32,6 +32,9 @@ public class RestQueryRequest extends GridRestRequest {
     /** Page size. */
     private Integer pageSize;
 
+    /** Distributed joins. */
+    private boolean distributedJoins;
+
     /** Cache name. */
     private String cacheName;
 
@@ -90,6 +93,20 @@ public class RestQueryRequest extends GridRestRequest {
     }
 
     /**
+     * @param distributedJoins New distributed joins.
+     */
+    public void distributedJoins(boolean distributedJoins) {
+        this.distributedJoins = distributedJoins;
+    }
+
+    /**
+     * @return Distributed joins.
+     */
+    public boolean distributedJoins() {
+        return distributedJoins;
+    }
+
+    /**
      * @param cacheName Cache name.
      */
     public void cacheName(String cacheName) {

http://git-wip-us.apache.org/repos/asf/ignite/blob/f245c383/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java
----------------------------------------------------------------------
diff --git a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java
index c2679df..c864a10 100644
--- a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java
+++ b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java
@@ -566,6 +566,11 @@ public class GridJettyRestHandler extends AbstractHandler {
                 if (pageSize != null)
                     restReq0.pageSize(Integer.parseInt(pageSize));
 
+                String distributedJoins = (String)params.get("distributedJoins");
+
+                if (distributedJoins != null)
+                    restReq0.distributedJoins(Boolean.parseBoolean(distributedJoins));
+
                 restReq0.cacheName((String)params.get("cacheName"));
 
                 if (cmd == EXECUTE_SQL_QUERY)


[34/50] [abbrv] ignite git commit: IgniteCacheRandomOperationBenchmark: removed unnecessary output.

Posted by vo...@apache.org.
IgniteCacheRandomOperationBenchmark: removed unnecessary output.


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

Branch: refs/heads/ignite-3553
Commit: 422c9a8320b4a11c64ab95311a9897fdb48e6ed6
Parents: e754724
Author: sboikov <sb...@gridgain.com>
Authored: Tue Jul 26 16:25:50 2016 +0300
Committer: sboikov <sb...@gridgain.com>
Committed: Tue Jul 26 16:25:50 2016 +0300

----------------------------------------------------------------------
 .../yardstick/cache/load/IgniteCacheRandomOperationBenchmark.java  | 2 --
 1 file changed, 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/422c9a83/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/load/IgniteCacheRandomOperationBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/load/IgniteCacheRandomOperationBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/load/IgniteCacheRandomOperationBenchmark.java
index c2947ba..e62df45 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/load/IgniteCacheRandomOperationBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/load/IgniteCacheRandomOperationBenchmark.java
@@ -885,8 +885,6 @@ public class IgniteCacheRandomOperationBenchmark extends IgniteAbstractBenchmark
             else {
                 String sql = rendomizeSql();
 
-                BenchmarkUtils.println(sql);
-
                 sq = new SqlFieldsQuery(sql);
             }