You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ag...@apache.org on 2018/01/22 19:48:50 UTC

[7/8] ignite git commit: IGNITE-5571: Decoupled SQL fields query from cache. This closes #2283.

IGNITE-5571: Decoupled SQL fields query from cache. This closes #2283.


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

Branch: refs/heads/ignite-6643
Commit: 53c03c19ae0ee85b72aa9d2a20bb96c9c51f3853
Parents: a6aa0d1
Author: Alexander Paschenko <al...@gmail.com>
Authored: Mon Jan 22 22:25:30 2018 +0300
Committer: devozerov <pp...@gmail.com>
Committed: Mon Jan 22 22:25:30 2018 +0300

----------------------------------------------------------------------
 .../jdbc/suite/IgniteJdbcDriverTestSuite.java   |   3 +
 .../jdbc/thin/JdbcThinAbstractSelfTest.java     |  79 +++
 .../jdbc/thin/JdbcThinLocalQueriesSelfTest.java |  73 +++
 .../jdbc2/JdbcQueryMultipleStatementsTask.java  |   2 +-
 .../processors/cache/IgniteCacheProxyImpl.java  |   2 +-
 .../odbc/jdbc/JdbcRequestHandler.java           |   4 +-
 .../odbc/odbc/OdbcRequestHandler.java           |   7 +-
 .../cache/ClientCacheSqlFieldsQueryRequest.java |   8 +-
 .../processors/query/GridQueryIndexing.java     |  18 +-
 .../processors/query/GridQueryProcessor.java    | 136 ++--
 .../internal/processors/query/QueryUtils.java   |   6 +-
 .../internal/visor/query/VisorQueryTask.java    |   2 +-
 ...IgniteClientCacheInitializationFailTest.java |   7 +-
 .../query/h2/DmlStatementsProcessor.java        |  21 +-
 .../query/h2/H2CachedStatementKey.java          | 108 ++++
 .../processors/query/h2/H2DmlPlanKey.java       |  81 ---
 .../processors/query/h2/H2StatementCache.java   |  28 +-
 .../processors/query/h2/IgniteH2Indexing.java   | 629 +++++++++++++------
 .../processors/query/h2/ParsingResult.java      | 102 +++
 .../query/h2/ddl/DdlStatementsProcessor.java    |  24 +
 .../query/h2/sql/GridSqlQueryParser.java        |  60 ++
 .../query/h2/twostep/GridMapQueryExecutor.java  |   2 +-
 .../cache/GridCacheCrossCacheQuerySelfTest.java |  12 +-
 ...CheckClusterStateBeforeExecuteQueryTest.java |   2 +-
 .../cache/SqlFieldsQuerySelfTest.java           |  53 ++
 .../IgniteCacheReplicatedQuerySelfTest.java     |  29 +-
 .../cache/index/AbstractSchemaSelfTest.java     |   3 +
 .../cache/index/DynamicColumnsAbstractTest.java |   2 +-
 .../DynamicIndexAbstractBasicSelfTest.java      |  29 +-
 .../index/DynamicIndexAbstractSelfTest.java     |  33 +-
 .../index/H2DynamicIndexingComplexTest.java     |   2 +-
 .../cache/index/H2DynamicTableSelfTest.java     |   8 +-
 .../local/IgniteCacheLocalQuerySelfTest.java    |  62 +-
 .../IgnitePersistentStoreSchemaLoadTest.java    |  12 +-
 .../query/IgniteCachelessQueriesSelfTest.java   | 420 +++++++++++++
 .../query/IgniteQueryDedicatedPoolTest.java     |  15 +-
 .../query/IgniteSqlDefaultValueTest.java        |   2 +-
 .../query/IgniteSqlNotNullConstraintTest.java   |   2 +-
 .../MultipleStatementsSqlQuerySelfTest.java     |   8 +-
 .../processors/query/SqlSchemaSelfTest.java     |  12 +-
 .../IgniteCacheQuerySelfTestSuite.java          |   2 +
 .../Client/Cache/SqlQueryTest.cs                |   3 +-
 42 files changed, 1582 insertions(+), 531 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/53c03c19/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 87eeb84..ff4d69f 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
@@ -48,6 +48,7 @@ import org.apache.ignite.jdbc.thin.JdbcThinDynamicIndexTransactionalReplicatedSe
 import org.apache.ignite.jdbc.thin.JdbcThinEmptyCacheSelfTest;
 import org.apache.ignite.jdbc.thin.JdbcThinErrorsSelfTest;
 import org.apache.ignite.jdbc.thin.JdbcThinInsertStatementSelfTest;
+import org.apache.ignite.jdbc.thin.JdbcThinLocalQueriesSelfTest;
 import org.apache.ignite.jdbc.thin.JdbcThinMergeStatementSelfTest;
 import org.apache.ignite.jdbc.thin.JdbcThinMetadataSelfTest;
 import org.apache.ignite.jdbc.thin.JdbcThinMissingLongArrayResultsTest;
@@ -164,6 +165,8 @@ public class IgniteJdbcDriverTestSuite extends TestSuite {
         suite.addTest(new TestSuite(JdbcThinMergeStatementSkipReducerOnUpdateSelfTest.class));
         suite.addTest(new TestSuite(JdbcThinComplexDmlDdlSkipReducerOnUpdateSelfTest.class));
 
+        suite.addTest(new TestSuite(JdbcThinLocalQueriesSelfTest.class));
+
         // Various commands.
         suite.addTest(new TestSuite(JdbcThinWalModeChangeSelfTest.class));
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/53c03c19/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinAbstractSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinAbstractSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinAbstractSelfTest.java
index 1c38df2..178cd3a 100644
--- a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinAbstractSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinAbstractSelfTest.java
@@ -17,11 +17,26 @@
 
 package org.apache.ignite.jdbc.thin;
 
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
 import java.sql.SQLFeatureNotSupportedException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 import java.util.concurrent.Callable;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.processors.odbc.ClientListenerProcessor;
+import org.apache.ignite.internal.processors.port.GridPortRecord;
+import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.testframework.GridTestUtils;
 import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.jetbrains.annotations.Nullable;
 
 /**
  * Connection test.
@@ -93,4 +108,68 @@ public class JdbcThinAbstractSelfTest extends GridCommonAbstractTest {
          */
         void run() throws Exception;
     }
+
+    /**
+     * @param node Node to connect to.
+     * @param params Connection parameters.
+     * @return Thin JDBC connection to specified node.
+     */
+    static Connection connect(IgniteEx node, String params) throws SQLException {
+        Collection<GridPortRecord> recs = node.context().ports().records();
+
+        GridPortRecord cliLsnrRec = null;
+
+        for (GridPortRecord rec : recs) {
+            if (rec.clazz() == ClientListenerProcessor.class) {
+                cliLsnrRec = rec;
+
+                break;
+            }
+        }
+
+        assertNotNull(cliLsnrRec);
+
+        String connStr = "jdbc:ignite:thin://127.0.0.1:" + cliLsnrRec.port();
+
+        if (!F.isEmpty(params))
+            connStr += "/?" + params;
+
+        return DriverManager.getConnection(connStr);
+    }
+
+    /**
+     * @param sql Statement.
+     * @param args Arguments.
+     * @return Result set.
+     * @throws RuntimeException if failed.
+     */
+    static List<List<?>> execute(Connection conn, String sql, Object... args) throws SQLException {
+        try (PreparedStatement s = conn.prepareStatement(sql)) {
+            for (int i = 0; i < args.length; i++)
+                s.setObject(i + 1, args[i]);
+
+            if (s.execute()) {
+                List<List<?>> res = new ArrayList<>();
+
+                try (ResultSet rs = s.getResultSet()) {
+                    ResultSetMetaData meta = rs.getMetaData();
+
+                    int cnt = meta.getColumnCount();
+
+                    while (rs.next()) {
+                        List<Object> row = new ArrayList<>(cnt);
+
+                        for (int i = 1; i <= cnt; i++)
+                            row.add(rs.getObject(i));
+
+                        res.add(row);
+                    }
+                }
+
+                return res;
+            }
+            else
+                return Collections.emptyList();
+        }
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/53c03c19/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinLocalQueriesSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinLocalQueriesSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinLocalQueriesSelfTest.java
new file mode 100644
index 0000000..1e28e52
--- /dev/null
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinLocalQueriesSelfTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.jdbc.thin;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.Map;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.U;
+
+/**
+ * Test that replicated-only query is executed locally.
+ */
+public class JdbcThinLocalQueriesSelfTest extends JdbcThinAbstractSelfTest {
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() throws Exception {
+        super.beforeTest();
+
+        startGrid(0);
+
+        startGrid(1);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        stopAllGrids();
+
+        super.afterTest();
+    }
+
+    /**
+     *
+     */
+    public void testLocalThinJdbcQuery() throws SQLException {
+        try (Connection c = connect(grid(0), "replicatedOnly=true")) {
+            execute(c, "CREATE TABLE Company(id int primary key, name varchar) WITH " +
+                "\"template=replicated,cache_name=Company\"");
+
+            execute(c, "CREATE TABLE Person(id int primary key, name varchar, companyid int) WITH " +
+                "\"template=replicated,cache_name=Person\"");
+
+            execute(c, "insert into Company(id, name) values (1, 'Apple')");
+
+            execute(c, "insert into Person(id, name, companyid) values (2, 'John', 1)");
+
+            List<List<?>> res = execute(c, "SELECT p.id, p.name, c.name from Person p left join Company c on " +
+                "p.companyid = c.id");
+
+            assertEqualsCollections(F.asList(2, "John", "Apple"), res.get(0));
+
+            Map twoStepCache = U.field(grid(0).context().query().getIndexing(), "twoStepCache");
+
+            // No two step queries cached => local select.
+            assertEquals(0, twoStepCache.size());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/53c03c19/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcQueryMultipleStatementsTask.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcQueryMultipleStatementsTask.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcQueryMultipleStatementsTask.java
index f907525..3e5d575 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcQueryMultipleStatementsTask.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcQueryMultipleStatementsTask.java
@@ -123,7 +123,7 @@ class JdbcQueryMultipleStatementsTask implements IgniteCallable<List<JdbcStateme
 
         GridKernalContext ctx = ((IgniteKernal)ignite).context();
 
-        List<FieldsQueryCursor<List<?>>> curs = ctx.query().querySqlFieldsNoCache(qry, true, false);
+        List<FieldsQueryCursor<List<?>>> curs = ctx.query().querySqlFields(qry, true, false);
 
         List<JdbcStatementResultInfo> resultsInfo = new ArrayList<>(curs.size());
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/53c03c19/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheProxyImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheProxyImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheProxyImpl.java
index dc6793b..7f71c74 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheProxyImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheProxyImpl.java
@@ -604,7 +604,7 @@ public class IgniteCacheProxyImpl<K, V> extends AsyncSupportAdapter<IgniteCache<
 
             if (qry instanceof SqlFieldsQuery)
                 return (FieldsQueryCursor<R>)ctx.kernalContext().query().querySqlFields(ctx, (SqlFieldsQuery)qry,
-                    keepBinary);
+                    keepBinary, true).get(0);
 
             if (qry instanceof ScanQuery)
                 return query((ScanQuery)qry, null, projection(qry.isLocal()));

http://git-wip-us.apache.org/repos/asf/ignite/blob/53c03c19/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java
index d0f0073..11b50ec 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java
@@ -307,7 +307,7 @@ public class JdbcRequestHandler implements ClientListenerRequestHandler {
 
             qry.setSchema(schemaName);
 
-            List<FieldsQueryCursor<List<?>>> results = ctx.query().querySqlFieldsNoCache(qry, true,
+            List<FieldsQueryCursor<List<?>>> results = ctx.query().querySqlFields(qry, true,
                 protocolVer.compareTo(VER_2_3_0) < 0);
 
             if (results.size() == 1) {
@@ -531,7 +531,7 @@ public class JdbcRequestHandler implements ClientListenerRequestHandler {
     private void executeBatchedQuery(SqlFieldsQueryEx qry, List<Integer> updCntsAcc,
         IgniteBiTuple<Integer, String> firstErr) {
         try {
-            List<FieldsQueryCursor<List<?>>> qryRes = ctx.query().querySqlFieldsNoCache(qry, true, true);
+            List<FieldsQueryCursor<List<?>>> qryRes = ctx.query().querySqlFields(qry, true, true);
 
             for (FieldsQueryCursor<List<?>> cur : qryRes) {
                 assert !((QueryCursorImpl)cur).isQuery();

http://git-wip-us.apache.org/repos/asf/ignite/blob/53c03c19/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcRequestHandler.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcRequestHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcRequestHandler.java
index bfc8d9e..4ce99e3 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcRequestHandler.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcRequestHandler.java
@@ -248,7 +248,7 @@ public class OdbcRequestHandler implements ClientListenerRequestHandler {
 
             SqlFieldsQuery qry = makeQuery(req.schema(), sql, req.arguments(), req.timeout());
 
-            List<FieldsQueryCursor<List<?>>> cursors = ctx.query().querySqlFieldsNoCache(qry, true, false);
+            List<FieldsQueryCursor<List<?>>> cursors = ctx.query().querySqlFields(qry, true, false);
 
             OdbcQueryResults results = new OdbcQueryResults(cursors);
 
@@ -299,7 +299,8 @@ public class OdbcRequestHandler implements ClientListenerRequestHandler {
             // Getting meta and do the checks for the first execution.
             qry.setArgs(paramSet[0]);
 
-            QueryCursorImpl<List<?>> qryCur = (QueryCursorImpl<List<?>>)ctx.query().querySqlFieldsNoCache(qry, true);
+            QueryCursorImpl<List<?>> qryCur = (QueryCursorImpl<List<?>>)ctx.query()
+                .querySqlFields(qry, true, true).get(0);
 
             if (qryCur.isQuery())
                 throw new IgniteException("Batching of parameters only supported for DML statements. [query=" +
@@ -330,7 +331,7 @@ public class OdbcRequestHandler implements ClientListenerRequestHandler {
     private long executeQuery(SqlFieldsQuery qry, Object[] row) {
         qry.setArgs(row);
 
-        QueryCursor<List<?>> cur = ctx.query().querySqlFieldsNoCache(qry, true);
+        QueryCursor<List<?>> cur = ctx.query().querySqlFields(qry, true, true).get(0);
 
         return OdbcUtils.rowsAffected(cur);
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/53c03c19/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheSqlFieldsQueryRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheSqlFieldsQueryRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheSqlFieldsQueryRequest.java
index ca3595d..cfd4498 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheSqlFieldsQueryRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheSqlFieldsQueryRequest.java
@@ -17,6 +17,8 @@
 
 package org.apache.ignite.internal.processors.platform.client.cache;
 
+import java.util.List;
+import java.util.concurrent.TimeUnit;
 import org.apache.ignite.cache.query.FieldsQueryCursor;
 import org.apache.ignite.cache.query.SqlFieldsQuery;
 import org.apache.ignite.internal.binary.BinaryRawReaderEx;
@@ -28,9 +30,6 @@ import org.apache.ignite.internal.processors.platform.client.ClientConnectionCon
 import org.apache.ignite.internal.processors.platform.client.ClientResponse;
 import org.apache.ignite.internal.processors.query.QueryUtils;
 
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
 /**
  * Sql query request.
  */
@@ -101,8 +100,7 @@ public class ClientCacheSqlFieldsQueryRequest extends ClientCacheRequest {
                 }
             }
 
-            List<FieldsQueryCursor<List<?>>> curs = ctx.kernalContext().query()
-                    .querySqlFieldsNoCache(qry, true, true);
+            List<FieldsQueryCursor<List<?>>> curs = ctx.kernalContext().query().querySqlFields(qry, true, true);
 
             assert curs.size() == 1;
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/53c03c19/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 9624244..6b425a1 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
@@ -67,28 +67,22 @@ public interface GridQueryIndexing {
      * @param cacheName Cache name.
      * @param qry Query.
      * @param keepBinary Keep binary flag.
-     * @param mainCacheId Main cache ID.    @return Cursor.
      * @throws IgniteCheckedException If failed.
      */
     public <K, V> QueryCursor<Cache.Entry<K, V>> queryDistributedSql(String schemaName, String cacheName, SqlQuery qry,
-        boolean keepBinary, int mainCacheId) throws IgniteCheckedException;
+        boolean keepBinary) throws IgniteCheckedException;
 
     /**
-     * Parses SQL query into two step query and executes it.
-     *
+     * Detect whether SQL query should be executed in distributed or local manner and execute it.
      * @param schemaName Schema name.
      * @param qry Query.
      * @param keepBinary Keep binary flag.
-     * @param cancel Query cancel.
-     * @param mainCacheId Main cache ID.
-     * @param failOnMultipleStmts If {@code true} the method must throws exception when query contains
-     *      more then one SQL statement.
+     * @param failOnMultipleStmts Whether an exception should be thrown for multiple statements query.
+     * @param cancel Query cancel state handler.
      * @return Cursor.
-     * @throws IgniteCheckedException If failed.
      */
-    public List<FieldsQueryCursor<List<?>>> queryDistributedSqlFields(String schemaName, SqlFieldsQuery qry,
-        boolean keepBinary, GridQueryCancel cancel, @Nullable Integer mainCacheId, boolean failOnMultipleStmts)
-        throws IgniteCheckedException;
+    public List<FieldsQueryCursor<List<?>>> querySqlFields(String schemaName, SqlFieldsQuery qry, boolean keepBinary,
+        boolean failOnMultipleStmts, GridQueryCancel cancel);
 
     /**
      * Perform a MERGE statement using data streamer as receiver.

http://git-wip-us.apache.org/repos/asf/ignite/blob/53c03c19/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 ff8d072..cbb6b9b 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
@@ -703,7 +703,8 @@ public class GridQueryProcessor extends GridProcessorAdapter {
                     }
                 }
 
-                // Ensure that candidates has unique index names. Otherwise we will not be able to apply pending operations.
+                // Ensure that candidates has unique index names.
+                // Otherwise we will not be able to apply pending operations.
                 Map<String, QueryTypeDescriptorImpl> tblTypMap = new HashMap<>();
                 Map<String, QueryTypeDescriptorImpl> idxTypMap = new HashMap<>();
 
@@ -1967,11 +1968,15 @@ public class GridQueryProcessor extends GridProcessorAdapter {
     /**
      * Query SQL fields.
      *
-     * @param cctx Cache context.
      * @param qry Query.
      * @param keepBinary Keep binary flag.
      * @return Cursor.
      */
+    public List<FieldsQueryCursor<List<?>>> querySqlFields(final SqlFieldsQuery qry, final boolean keepBinary,
+        final boolean failOnMultipleStmts) {
+        return querySqlFields(null, qry, keepBinary, failOnMultipleStmts);
+    }
+
     @SuppressWarnings("unchecked")
     public FieldsQueryCursor<List<?>> querySqlFields(final GridCacheContext<?,?> cctx, final SqlFieldsQuery qry,
         final boolean keepBinary) {
@@ -1981,141 +1986,71 @@ public class GridQueryProcessor extends GridProcessorAdapter {
     /**
      * Query SQL fields.
      *
-     * @param cctx Cache context.
-     * @param qry Query.
-     * @param keepBinary Keep binary flag.
-     * @param failOnMultipleStmts If {@code true} the method must throws exception when query contains
-     *      more then one SQL statement.
-     * @return Cursor.
-     */
-    @SuppressWarnings("unchecked")
-    public List<FieldsQueryCursor<List<?>>> querySqlFields(final GridCacheContext<?,?> cctx, final SqlFieldsQuery qry,
-        final boolean keepBinary, final boolean failOnMultipleStmts) {
-        checkxEnabled();
-
-        validateSqlFieldsQuery(qry);
-
-        boolean loc = (qry.isReplicatedOnly() && cctx.isReplicatedAffinityNode()) || cctx.isLocal() || qry.isLocal();
-
-        if (!busyLock.enterBusy())
-            throw new IllegalStateException("Failed to execute query (grid is stopping).");
-
-        GridCacheContext oldCctx = curCache.get();
-
-        curCache.set(cctx);
-
-        try {
-            final String schemaName = qry.getSchema() != null ? qry.getSchema() : idx.schema(cctx.name());
-            final int mainCacheId = cctx.cacheId();
-
-            IgniteOutClosureX<List<FieldsQueryCursor<List<?>>>> clo;
-
-            if (loc) {
-                clo = new IgniteOutClosureX<List<FieldsQueryCursor<List<?>>>>() {
-                    @Override public List<FieldsQueryCursor<List<?>>> applyx() throws IgniteCheckedException {
-                        GridQueryCancel cancel = new GridQueryCancel();
-
-                        List<FieldsQueryCursor<List<?>>> cursors;
-
-                        if (cctx.config().getQueryParallelism() > 1) {
-                            qry.setDistributedJoins(true);
-
-                            cursors = idx.queryDistributedSqlFields(schemaName, qry,
-                                keepBinary, cancel, mainCacheId, true);
-                        }
-                        else {
-                            IndexingQueryFilter filter = idx.backupFilter(requestTopVer.get(), qry.getPartitions());
-
-                            cursors = new ArrayList<>(1);
-
-                            cursors.add(idx.queryLocalSqlFields(schemaName, qry, keepBinary, filter, cancel));
-                        }
-
-                        sendQueryExecutedEvent(qry.getSql(), qry.getArgs(), cctx.name());
-
-                        return cursors;
-                    }
-                };
-            }
-            else {
-                clo = new IgniteOutClosureX<List<FieldsQueryCursor<List<?>>>>() {
-                    @Override public List<FieldsQueryCursor<List<?>>> applyx() throws IgniteCheckedException {
-                        return idx.queryDistributedSqlFields(schemaName, qry, keepBinary, null, mainCacheId, failOnMultipleStmts);
-                    }
-                };
-            }
-
-            return executeQuery(GridCacheQueryType.SQL_FIELDS, qry.getSql(), cctx, clo, true);
-        }
-        catch (IgniteCheckedException e) {
-            throw new CacheException(e);
-        }
-        finally {
-            curCache.set(oldCctx);
-
-            busyLock.leaveBusy();
-        }
-    }
-
-    /**
-     * Query SQL fields without strict dependency on concrete cache.
-     *
      * @param qry Query.
      * @param keepBinary Keep binary flag.
      * @return Cursor.
      */
-    public FieldsQueryCursor<List<?>> querySqlFieldsNoCache(final SqlFieldsQuery qry,
-        final boolean keepBinary) {
-        return querySqlFieldsNoCache(qry, keepBinary, true).get(0);
+    public FieldsQueryCursor<List<?>> querySqlFields(final SqlFieldsQuery qry, final boolean keepBinary) {
+        return querySqlFields(null, qry, keepBinary, true).get(0);
     }
 
     /**
-     * Query SQL fields without strict dependency on concrete cache.
+     * Query SQL fields.
      *
+     * @param cctx Cache context.
      * @param qry Query.
      * @param keepBinary Keep binary flag.
      * @param failOnMultipleStmts If {@code true} the method must throws exception when query contains
      *      more then one SQL statement.
      * @return Cursor.
      */
-    public List<FieldsQueryCursor<List<?>>> querySqlFieldsNoCache(final SqlFieldsQuery qry,
-        final boolean keepBinary, final boolean failOnMultipleStmts) {
+    @SuppressWarnings("unchecked")
+    public List<FieldsQueryCursor<List<?>>> querySqlFields(@Nullable final GridCacheContext<?,?> cctx,
+        final SqlFieldsQuery qry, final boolean keepBinary, final boolean failOnMultipleStmts) {
         checkxEnabled();
 
         validateSqlFieldsQuery(qry);
 
-        if (qry.isLocal())
-            throw new IgniteException("Local query is not supported without specific cache.");
-
         if (!ctx.state().publicApiActiveState(true)) {
             throw new IgniteException("Can not perform the operation because the cluster is inactive. Note, that " +
                 "the cluster is considered inactive by default if Ignite Persistent Store is used to let all the nodes " +
                 "join the cluster. To activate the cluster call Ignite.active(true).");
         }
 
-        if (qry.getSchema() == null)
-            qry.setSchema(QueryUtils.DFLT_SCHEMA);
-
         if (!busyLock.enterBusy())
             throw new IllegalStateException("Failed to execute query (grid is stopping).");
 
+        GridCacheContext oldCctx = curCache.get();
+
+        curCache.set(cctx);
+
+        final String schemaName = qry.getSchema() != null ? qry.getSchema()
+            : (cctx != null ? idx.schema(cctx.name()) : QueryUtils.DFLT_SCHEMA);
+
         try {
             IgniteOutClosureX<List<FieldsQueryCursor<List<?>>>> clo =
                 new IgniteOutClosureX<List<FieldsQueryCursor<List<?>>>>() {
                 @Override public List<FieldsQueryCursor<List<?>>> applyx() throws IgniteCheckedException {
                     GridQueryCancel cancel = new GridQueryCancel();
 
-                    return idx.queryDistributedSqlFields(qry.getSchema(), qry, keepBinary, cancel, null,
-                        failOnMultipleStmts);
+                    List<FieldsQueryCursor<List<?>>> res =
+                        idx.querySqlFields(schemaName, qry, keepBinary, failOnMultipleStmts, cancel);
+
+                    if (cctx != null)
+                        sendQueryExecutedEvent(qry.getSql(), qry.getArgs(), cctx.name());
+
+                    return res;
                 }
             };
 
-            return executeQuery(GridCacheQueryType.SQL_FIELDS, qry.getSql(), null, clo, true);
+            return executeQuery(GridCacheQueryType.SQL_FIELDS, qry.getSql(), cctx, clo, true);
         }
         catch (IgniteCheckedException e) {
             throw new CacheException(e);
         }
         finally {
+            curCache.set(oldCctx);
+
             busyLock.leaveBusy();
         }
     }
@@ -2202,12 +2137,11 @@ public class GridQueryProcessor extends GridProcessorAdapter {
 
         try {
             final String schemaName = idx.schema(cctx.name());
-            final int mainCacheId = cctx.cacheId();
 
             return executeQuery(GridCacheQueryType.SQL, qry.getSql(), cctx,
                 new IgniteOutClosureX<QueryCursor<Cache.Entry<K, V>>>() {
                     @Override public QueryCursor<Cache.Entry<K, V>> applyx() throws IgniteCheckedException {
-                        return idx.queryDistributedSql(schemaName, cctx.name(), qry, keepBinary, mainCacheId);
+                        return idx.queryDistributedSql(schemaName, cctx.name(), qry, keepBinary);
                     }
                 }, true);
         }
@@ -2231,7 +2165,6 @@ public class GridQueryProcessor extends GridProcessorAdapter {
             throw new IllegalStateException("Failed to execute query (grid is stopping).");
 
         final String schemaName = idx.schema(cctx.name());
-        final int mainCacheId = cctx.cacheId();
 
         try {
             return executeQuery(GridCacheQueryType.SQL, qry.getSql(), cctx,
@@ -2251,7 +2184,7 @@ public class GridQueryProcessor extends GridProcessorAdapter {
                         if (cctx.config().getQueryParallelism() > 1) {
                             qry.setDistributedJoins(true);
 
-                            return idx.queryDistributedSql(schemaName, cctx.name(), qry, keepBinary, mainCacheId);
+                            return idx.queryDistributedSql(schemaName, cctx.name(), qry, keepBinary);
                         }
                         else
                             return idx.queryLocalSql(schemaName, cctx.name(), qry, idx.backupFilter(requestTopVer.get(),
@@ -2435,7 +2368,8 @@ public class GridQueryProcessor extends GridProcessorAdapter {
      * @param cols Columns to add.
      * @throws IgniteCheckedException If failed to update type descriptor.
      */
-    private void processDynamicAddColumn(QueryTypeDescriptorImpl d, List<QueryField> cols) throws IgniteCheckedException {
+    private void processDynamicAddColumn(QueryTypeDescriptorImpl d, List<QueryField> cols)
+        throws IgniteCheckedException {
         List<GridQueryProperty> props = new ArrayList<>(cols.size());
 
         for (QueryField col : cols) {

http://git-wip-us.apache.org/repos/asf/ignite/blob/53c03c19/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java
index 3397492..0cee831 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java
@@ -388,8 +388,10 @@ public class QueryUtils {
 
         // Key and value classes still can be available if they are primitive or JDK part.
         // We need that to set correct types for _key and _val columns.
-        Class<?> keyCls = U.classForName(qryEntity.findKeyType(), null);
-        Class<?> valCls = U.classForName(qryEntity.findValueType(), null);
+        // We better box these types - otherwise, if user provides, say, raw 'byte' for
+        // key or value (which they could), we'll deem key or value as Object which clearly is not right.
+        Class<?> keyCls = U.box(U.classForName(qryEntity.findKeyType(), null, true));
+        Class<?> valCls = U.box(U.classForName(qryEntity.findValueType(), null, true));
 
         // If local node has the classes and they are externalizable, we must use reflection properties.
         boolean keyMustDeserialize = mustDeserializeBinary(ctx, keyCls);

http://git-wip-us.apache.org/repos/asf/ignite/blob/53c03c19/modules/core/src/main/java/org/apache/ignite/internal/visor/query/VisorQueryTask.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/query/VisorQueryTask.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/query/VisorQueryTask.java
index 1daa1f2..2e32276 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/visor/query/VisorQueryTask.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/query/VisorQueryTask.java
@@ -90,7 +90,7 @@ public class VisorQueryTask extends VisorOneNodeTask<VisorQueryTaskArg, VisorEit
                 String cacheName = arg.getCacheName();
 
                 if (F.isEmpty(cacheName))
-                    qryCursors = ignite.context().query().querySqlFieldsNoCache(qry, true, false);
+                    qryCursors = ignite.context().query().querySqlFields(qry, true, false);
                 else {
                     IgniteCache<Object, Object> c = ignite.cache(cacheName);
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/53c03c19/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClientCacheInitializationFailTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClientCacheInitializationFailTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClientCacheInitializationFailTest.java
index 3f769d7..df27c5f 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClientCacheInitializationFailTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClientCacheInitializationFailTest.java
@@ -238,14 +238,13 @@ public class IgniteClientCacheInitializationFailTest extends GridCommonAbstractT
 
         /** {@inheritDoc} */
         @Override public <K, V> QueryCursor<Cache.Entry<K, V>> queryDistributedSql(String schemaName, String cacheName,
-            SqlQuery qry, boolean keepBinary, int mainCacheId) throws IgniteCheckedException {
+            SqlQuery qry, boolean keepBinary) throws IgniteCheckedException {
             return null;
         }
 
         /** {@inheritDoc} */
-        @Override public List<FieldsQueryCursor<List<?>>> queryDistributedSqlFields(String schemaName,
-            SqlFieldsQuery qry, boolean keepBinary, GridQueryCancel cancel,
-            @Nullable Integer mainCacheId, boolean failOnMultipleStmts) throws IgniteCheckedException {
+        @Override public List<FieldsQueryCursor<List<?>>> querySqlFields(String schemaName, SqlFieldsQuery qry,
+            boolean keepBinary, boolean failOnMultipleStmts, GridQueryCancel cancel) {
             return null;
         }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/53c03c19/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java
index c06fad7..72e80e2 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java
@@ -96,7 +96,7 @@ public class DmlStatementsProcessor {
     private static final int PLAN_CACHE_SIZE = 1024;
 
     /** Update plans cache. */
-    private final ConcurrentMap<H2DmlPlanKey, UpdatePlan> planCache =
+    private final ConcurrentMap<H2CachedStatementKey, UpdatePlan> planCache =
         new GridBoundedConcurrentLinkedHashMap<>(PLAN_CACHE_SIZE);
 
     /**
@@ -117,7 +117,7 @@ public class DmlStatementsProcessor {
      * @param cacheName Cache name.
      */
     public void onCacheStop(String cacheName) {
-        Iterator<Map.Entry<H2DmlPlanKey, UpdatePlan>> iter = planCache.entrySet().iterator();
+        Iterator<Map.Entry<H2CachedStatementKey, UpdatePlan>> iter = planCache.entrySet().iterator();
 
         while (iter.hasNext()) {
             UpdatePlan plan = iter.next().getValue();
@@ -353,7 +353,7 @@ public class DmlStatementsProcessor {
      *
      * @param schemaName Schema.
      * @param conn Connection.
-     * @param stmt Prepared statement.
+     * @param prepared H2 prepared command.
      * @param fieldsQry Fields query.
      * @param filters Cache name and key filter.
      * @param cancel Query cancel.
@@ -361,10 +361,10 @@ public class DmlStatementsProcessor {
      * @throws IgniteCheckedException if failed.
      */
     @SuppressWarnings("unchecked")
-    GridQueryFieldsResult updateSqlFieldsLocal(String schemaName, Connection conn, PreparedStatement stmt,
+    GridQueryFieldsResult updateSqlFieldsLocal(String schemaName, Connection conn, Prepared prepared,
         SqlFieldsQuery fieldsQry, IndexingQueryFilter filters, GridQueryCancel cancel)
         throws IgniteCheckedException {
-        UpdateResult res = updateSqlFields(schemaName, conn, GridSqlQueryParser.prepared(stmt), fieldsQry, true,
+        UpdateResult res = updateSqlFields(schemaName, conn, prepared, fieldsQry, true,
             filters, cancel);
 
         return new GridQueryFieldsResultAdapter(UPDATE_RESULT_META,
@@ -474,10 +474,8 @@ public class DmlStatementsProcessor {
      */
     @SuppressWarnings({"ConstantConditions", "unchecked"})
     private UpdateResult executeUpdateStatement(String schemaName, final GridCacheContext cctx, Connection c,
-        Prepared prepared, SqlFieldsQuery fieldsQry, boolean loc, IndexingQueryFilter filters, GridQueryCancel cancel)
-        throws IgniteCheckedException {
-        int mainCacheId = cctx.cacheId();
-
+        Prepared prepared, SqlFieldsQuery fieldsQry, boolean loc, IndexingQueryFilter filters,
+        GridQueryCancel cancel) throws IgniteCheckedException {
         Integer errKeysPos = null;
 
         UpdatePlan plan = getPlanForStatement(schemaName, c, prepared, fieldsQry, loc, errKeysPos);
@@ -510,7 +508,8 @@ public class DmlStatementsProcessor {
                 .setPageSize(fieldsQry.getPageSize())
                 .setTimeout(fieldsQry.getTimeout(), TimeUnit.MILLISECONDS);
 
-            cur = idx.queryDistributedSqlFields(schemaName, newFieldsQry, true, cancel, mainCacheId, true).get(0);
+            cur = (QueryCursorImpl<List<?>>)idx.querySqlFields(schemaName, newFieldsQry, true, true,
+                cancel).get(0);
         }
         else if (plan.hasRows())
             cur = plan.createRows(fieldsQry.getArgs());
@@ -602,7 +601,7 @@ public class DmlStatementsProcessor {
     @SuppressWarnings({"unchecked", "ConstantConditions"})
     private UpdatePlan getPlanForStatement(String schema, Connection conn, Prepared p, SqlFieldsQuery fieldsQry,
         boolean loc, @Nullable Integer errKeysPos) throws IgniteCheckedException {
-        H2DmlPlanKey planKey = new H2DmlPlanKey(schema, p.getSQL(), loc, fieldsQry);
+        H2CachedStatementKey planKey = H2CachedStatementKey.forDmlStatement(schema, p.getSQL(), fieldsQry, loc);
 
         UpdatePlan res = (errKeysPos == null ? planCache.get(planKey) : null);
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/53c03c19/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2CachedStatementKey.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2CachedStatementKey.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2CachedStatementKey.java
new file mode 100644
index 0000000..7b43f52
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2CachedStatementKey.java
@@ -0,0 +1,108 @@
+/*
+ * 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;
+
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.internal.processors.query.h2.dml.UpdatePlanBuilder;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+/**
+ * H2 cached statement key.
+ */
+class H2CachedStatementKey {
+    /** Schema name. */
+    private final String schemaName;
+
+    /** SQL. */
+    private final String sql;
+
+    /** Flags. */
+    private final byte flags;
+
+    /**
+     * Constructor.
+     *
+     * @param schemaName Schema name.
+     * @param sql SQL.
+     */
+    H2CachedStatementKey(String schemaName, String sql) {
+        this(schemaName, sql, null, false);
+    }
+
+    /**
+     * Build key with details relevant to DML plans cache.
+     *
+     * @param schemaName Schema name.
+     * @param sql SQL.
+     * @param fieldsQry Query with flags.
+     * @param loc DML {@code SELECT} Locality flag.
+     * @return Statement key.
+     * @see UpdatePlanBuilder
+     * @see DmlStatementsProcessor#getPlanForStatement
+     */
+    static H2CachedStatementKey forDmlStatement(String schemaName, String sql, SqlFieldsQuery fieldsQry, boolean loc) {
+        return new H2CachedStatementKey(schemaName, sql, fieldsQry, loc);
+    }
+
+    /**
+     * Full-fledged constructor.
+     *
+     * @param schemaName Schema name.
+     * @param sql SQL.
+     * @param fieldsQry Query with flags.
+     * @param loc DML {@code SELECT} Locality flag.
+     */
+    private H2CachedStatementKey(String schemaName, String sql, SqlFieldsQuery fieldsQry, boolean loc) {
+        this.schemaName = schemaName;
+        this.sql = sql;
+
+        if (fieldsQry == null || loc || !UpdatePlanBuilder.isSkipReducerOnUpdateQuery(fieldsQry))
+            this.flags = 0; // flags only relevant for server side updates.
+        else {
+            this.flags = (byte)(1 +
+                (fieldsQry.isDistributedJoins() ? 2 : 0) +
+                (fieldsQry.isEnforceJoinOrder() ? 4 : 0) +
+                (fieldsQry.isCollocated() ? 8 : 0));
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public int hashCode() {
+        return 31 * (31 * (schemaName != null ? schemaName.hashCode() : 0) + (sql != null ? sql.hashCode() : 0)) +
+            flags;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean equals(Object o) {
+        if (this == o)
+            return true;
+
+        if (o == null || getClass() != o.getClass())
+            return false;
+
+        H2CachedStatementKey other = (H2CachedStatementKey)o;
+
+        return F.eq(sql, other.sql) && F.eq(schemaName, other.schemaName) && flags == other.flags;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(H2CachedStatementKey.class, this);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/53c03c19/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2DmlPlanKey.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2DmlPlanKey.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2DmlPlanKey.java
deleted file mode 100644
index 455b5e5..0000000
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2DmlPlanKey.java
+++ /dev/null
@@ -1,81 +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;
-
-import org.apache.ignite.cache.query.SqlFieldsQuery;
-import org.apache.ignite.internal.processors.query.h2.dml.UpdatePlanBuilder;
-import org.apache.ignite.internal.util.typedef.F;
-import org.apache.ignite.internal.util.typedef.internal.S;
-
-/**
- * H2 DML plan key.
- */
-public class H2DmlPlanKey {
-    /** Schema name. */
-    private final String schemaName;
-
-    /** SQL. */
-    private final String sql;
-
-    /** Flags. */
-    private final byte flags;
-
-    /**
-     * Constructor.
-     *
-     * @param schemaName Schema name.
-     * @param sql SQL.
-     */
-    public H2DmlPlanKey(String schemaName, String sql, boolean loc, SqlFieldsQuery fieldsQry) {
-        this.schemaName = schemaName;
-        this.sql = sql;
-
-        if (loc || !UpdatePlanBuilder.isSkipReducerOnUpdateQuery(fieldsQry))
-            this.flags = 0; // flags only relevant for server side updates.
-        else {
-            this.flags = (byte)(1 +
-                (fieldsQry.isDistributedJoins() ? 2 : 0) +
-                (fieldsQry.isEnforceJoinOrder() ? 4 : 0) +
-                (fieldsQry.isCollocated() ? 8 : 0));
-        }
-    }
-
-    /** {@inheritDoc} */
-    @Override public int hashCode() {
-        return 31 * (31 * (schemaName != null ? schemaName.hashCode() : 0) + (sql != null ? sql.hashCode() : 0)) +
-            flags;
-    }
-
-    /** {@inheritDoc} */
-    @Override public boolean equals(Object o) {
-        if (this == o)
-            return true;
-
-        if (o == null || getClass() != o.getClass())
-            return false;
-
-        H2DmlPlanKey other = (H2DmlPlanKey)o;
-
-        return F.eq(sql, other.sql) && F.eq(schemaName, other.schemaName) && flags == other.flags;
-    }
-
-    /** {@inheritDoc} */
-    @Override public String toString() {
-        return S.toString(H2DmlPlanKey.class, this);
-    }
-}

http://git-wip-us.apache.org/repos/asf/ignite/blob/53c03c19/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2StatementCache.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2StatementCache.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2StatementCache.java
index d395112..673625f 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2StatementCache.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2StatementCache.java
@@ -17,16 +17,16 @@
 
 package org.apache.ignite.internal.processors.query.h2;
 
-import org.apache.ignite.internal.util.typedef.internal.U;
-
 import java.sql.PreparedStatement;
 import java.util.LinkedHashMap;
 import java.util.Map;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.jetbrains.annotations.Nullable;
 
 /**
  * Statement cache.
  */
-public class H2StatementCache extends LinkedHashMap<String, PreparedStatement> {
+class H2StatementCache extends LinkedHashMap<H2CachedStatementKey, PreparedStatement> {
     /** */
     private int size;
 
@@ -43,7 +43,7 @@ public class H2StatementCache extends LinkedHashMap<String, PreparedStatement> {
     }
 
     /** {@inheritDoc} */
-    @Override protected boolean removeEldestEntry(Map.Entry<String, PreparedStatement> eldest) {
+    @Override protected boolean removeEldestEntry(Map.Entry<H2CachedStatementKey, PreparedStatement> eldest) {
         boolean rmv = size() > size;
 
         if (rmv) {
@@ -56,6 +56,16 @@ public class H2StatementCache extends LinkedHashMap<String, PreparedStatement> {
     }
 
     /**
+     * Get statement for given schema and SQL.
+     * @param schemaName Schema name.
+     * @param sql SQL statement.
+     * @return Cached {@link PreparedStatement}, or {@code null} if none found.
+     */
+    @Nullable public PreparedStatement get(String schemaName, String sql) {
+        return get(new H2CachedStatementKey(schemaName, sql));
+    }
+
+    /**
      * The timestamp of the last usage of the cache.
      *
      * @return last usage timestamp
@@ -70,4 +80,14 @@ public class H2StatementCache extends LinkedHashMap<String, PreparedStatement> {
     public void updateLastUsage() {
         lastUsage = U.currentTimeMillis();
     }
+
+    /**
+     * Remove statement for given schema and SQL.
+     * @param schemaName Schema name.
+     * @param sql SQL statement.
+     * @return Cached {@link PreparedStatement}, or {@code null} if none found.
+     */
+    @Nullable public PreparedStatement remove(String schemaName, String sql) {
+        return remove(new H2CachedStatementKey(schemaName, sql));
+    }
 }