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 2019/02/15 05:26:31 UTC

[ignite] branch master updated: IGNITE-11279: SQL: Removed "prepared" from parser's cache.

This is an automated email from the ASF dual-hosted git repository.

vozerov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/master by this push:
     new 9860670  IGNITE-11279: SQL: Removed "prepared" from parser's cache.
9860670 is described below

commit 98606705ef26b30bd006c31c74989a9ede74054b
Author: devozerov <pp...@gmail.com>
AuthorDate: Fri Feb 15 08:25:49 2019 +0300

    IGNITE-11279: SQL: Removed "prepared" from parser's cache.
---
 .../internal/processors/cache/mvcc/MvccUtils.java  |  13 +--
 .../internal/processors/query/h2/H2Utils.java      |   6 +-
 .../processors/query/h2/IgniteH2Indexing.java      | 106 +++++++++++----------
 .../internal/processors/query/h2/QueryParser.java  |  62 +++++++++++-
 .../processors/query/h2/QueryParserResultDml.java  |  24 +++--
 .../processors/query/h2/dml/UpdatePlanBuilder.java |  94 ++----------------
 .../query/h2/sql/GridSqlQueryParser.java           |  40 ++++++++
 7 files changed, 191 insertions(+), 154 deletions(-)

diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccUtils.java
index f904966..225af81 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccUtils.java
@@ -19,6 +19,7 @@ package org.apache.ignite.internal.processors.cache.mvcc;
 
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteException;
+import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.configuration.TransactionConfiguration;
 import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.cluster.ClusterTopologyServerNotFoundException;
@@ -845,14 +846,14 @@ public class MvccUtils {
     /**
      * Throws atomicity modes compatibility validation exception.
      *
-     * @param ctx1 Cache context.
-     * @param ctx2 Another cache context.
+     * @param ccfg1 Config 1.
+     * @param ccfg2 Config 2.
      */
-    public static void throwAtomicityModesMismatchException(GridCacheContext ctx1, GridCacheContext ctx2) {
+    public static void throwAtomicityModesMismatchException(CacheConfiguration ccfg1, CacheConfiguration ccfg2) {
         throw new IgniteException("Caches with transactional_snapshot atomicity mode cannot participate in the same" +
-            " transaction with caches having another atomicity mode. [cacheName=" + ctx1.name() +
-            ", cacheMode=" + ctx1.config().getAtomicityMode() +
-            ", anotherCacheName=" + ctx2.name() + " anotherCacheMode=" + ctx2.config().getAtomicityMode() + ']');
+            " transaction with caches having another atomicity mode. [cacheName=" + ccfg1.getName() +
+            ", cacheMode=" + ccfg1.getAtomicityMode() + ", anotherCacheName=" + ccfg2.getName() +
+            " anotherCacheMode=" + ccfg2.getAtomicityMode() + ']');
     }
 
     /** */
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Utils.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Utils.java
index 50b8def..b86a481 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Utils.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Utils.java
@@ -775,9 +775,7 @@ public class H2Utils {
                 if (tbl != null) {
                     checkAndStartNotStartedCache(idx.kernalContext(), tbl);
 
-                    int cacheId = tbl.cacheId();
-
-                    caches0.add(cacheId);
+                    caches0.add(tbl.cacheId());
                 }
             }
         }
@@ -814,7 +812,7 @@ public class H2Utils {
                 cctx0 = cctx;
             }
             else if (cctx.mvccEnabled() != mvccEnabled)
-                MvccUtils.throwAtomicityModesMismatchException(cctx0, cctx);
+                MvccUtils.throwAtomicityModesMismatchException(cctx0.config(), cctx.config());
         }
 
         return mvccEnabled;
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 0b44cfc..7f24399 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
@@ -502,6 +502,8 @@ public class IgniteH2Indexing implements GridQueryIndexing {
             Prepared p = GridSqlQueryParser.prepared(stmt);
 
             if (GridSqlQueryParser.isDml(p)) {
+                QueryParserResultDml dml = parser.prepareDmlStatement(p);
+
                 SqlFieldsQuery fldsQry = new SqlFieldsQuery(qry);
 
                 if (params != null)
@@ -511,7 +513,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
                 fldsQry.setTimeout(qryTimeout, TimeUnit.MILLISECONDS);
                 fldsQry.setDataPageScanEnabled(dataPageScanEnabled);
 
-                UpdateResult updRes = executeUpdate(schemaName, conn, p, fldsQry, true, filter, cancel);
+                UpdateResult updRes = executeUpdate(schemaName, conn, dml, fldsQry, true, filter, cancel);
 
                 List<?> updResRow = Collections.singletonList(updRes.counter());
 
@@ -741,9 +743,9 @@ public class IgniteH2Indexing implements GridQueryIndexing {
 
         checkStatementStreamable(stmt);
 
-        Prepared p = GridSqlQueryParser.prepared(stmt);
+        QueryParserResultDml dml = parser.prepareDmlStatement(stmt);
 
-        UpdatePlan plan = updatePlan(schemaName, conn, p, null, true);
+        UpdatePlan plan = updatePlan(schemaName, conn, dml, null, true);
 
         IgniteDataStreamer<?, ?> streamer = cliCtx.streamerForCache(plan.cacheContext().name());
 
@@ -778,11 +780,9 @@ public class IgniteH2Indexing implements GridQueryIndexing {
         try {
             checkStatementStreamable(stmt);
 
-            Prepared p = GridSqlQueryParser.prepared(stmt);
-
-            assert p != null;
+            QueryParserResultDml dml = parser.prepareDmlStatement(stmt);
 
-            final UpdatePlan plan = updatePlan(schemaName, null, p, null, true);
+            final UpdatePlan plan = updatePlan(schemaName, null, dml, null, true);
 
             assert plan.isLocalSubquery();
 
@@ -1172,7 +1172,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
                     ctx0 = cctx;
                 }
                 else if (mvccEnabled != cctx.mvccEnabled())
-                    MvccUtils.throwAtomicityModesMismatchException(ctx0, cctx);
+                    MvccUtils.throwAtomicityModesMismatchException(ctx0.config(), cctx.config());
             }
         }
 
@@ -1505,42 +1505,31 @@ public class IgniteH2Indexing implements GridQueryIndexing {
         IndexingQueryFilter filter = (loc ? backupFilter(null, qry.getPartitions()) : null);
 
         if (dml != null) {
-            Prepared prepared = dml.prepared();
-
             Long qryId = registerRunningQuery(schemaName, cancel, sqlQry, loc, registerAsNewQry);
 
             boolean fail = false;
 
             try {
-                if (GridSqlQueryParser.isDml(prepared)) {
-                    try {
-                        Connection conn = connMgr.connectionForThread().connection(schemaName);
+                Connection conn = connMgr.connectionForThread().connection(schemaName);
 
-                        if (!loc)
-                            return executeUpdateDistributed(schemaName, conn, prepared, qry, cancel);
-                        else {
-                            UpdateResult updRes = executeUpdate(schemaName, conn, prepared, qry, true, filter, cancel);
+                if (!loc)
+                    return executeUpdateDistributed(schemaName, conn, dml , qry, cancel);
+                else {
+                    UpdateResult updRes = executeUpdate(schemaName, conn, dml , qry, true, filter, cancel);
 
-                            return Collections.singletonList(new QueryCursorImpl<>(new Iterable<List<?>>() {
-                                @SuppressWarnings("NullableProblems")
-                                @Override public Iterator<List<?>> iterator() {
-                                    return new IgniteSingletonIterator<>(Collections.singletonList(updRes.counter()));
-                                }
-                            }, cancel));
+                    return Collections.singletonList(new QueryCursorImpl<>(new Iterable<List<?>>() {
+                        @SuppressWarnings("NullableProblems")
+                        @Override public Iterator<List<?>> iterator() {
+                            return new IgniteSingletonIterator<>(Collections.singletonList(updRes.counter()));
                         }
-                    }
-                    catch (IgniteCheckedException e) {
-                        fail = true;
-
-                        throw new IgniteSQLException("Failed to execute DML statement [stmt=" + sqlQry +
-                            ", params=" + Arrays.deepToString(qry.getArgs()) + "]", e);
-                    }
+                    }, cancel));
                 }
-
+            }
+            catch (IgniteCheckedException e) {
                 fail = true;
 
-                throw new IgniteSQLException("Unsupported DDL/DML operation: " + prepared.getClass().getName(),
-                    IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+                throw new IgniteSQLException("Failed to execute DML statement [stmt=" + sqlQry +
+                    ", params=" + Arrays.deepToString(qry.getArgs()) + "]", e);
             }
             finally {
                 runningQryMgr.unregister(qryId, fail);
@@ -1692,9 +1681,9 @@ public class IgniteH2Indexing implements GridQueryIndexing {
 
         IndexingQueryFilter filter = backupFilter(topVer, parts);
 
-        Prepared prepared = GridSqlQueryParser.prepared(stmt);
+        QueryParserResultDml dml = parser.prepareDmlStatement(stmt);
 
-        UpdatePlan plan = updatePlan(schema, conn, prepared, fldsQry, loc);
+        UpdatePlan plan = updatePlan(schema, conn, dml, fldsQry, loc);
 
         GridCacheContext planCctx = plan.cacheContext();
 
@@ -1919,8 +1908,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
 
         H2Utils.setupConnection(conn, false, qry.isEnforceJoinOrder());
 
-        PreparedStatement stmt = preparedStatementWithParams(conn, qry.getSql(),
-            Arrays.asList(qry.getArgs()), true);
+        PreparedStatement stmt = preparedStatementWithParams(conn, qry.getSql(), Arrays.asList(qry.getArgs()), true);
 
         Connection c;
 
@@ -1931,7 +1919,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
             throw new IgniteCheckedException(e);
         }
 
-        return executeUpdate(schemaName, c, GridSqlQueryParser.prepared(stmt), qry, loc, filter, cancel);
+        return executeUpdate(schemaName, c, parser.prepareDmlStatement(stmt), qry, loc, filter, cancel);
     }
 
     /**
@@ -2472,7 +2460,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
     /**
      * @param schemaName Schema.
      * @param conn Connection.
-     * @param prepared Prepared statement.
+     * @param dml DML statement.
      * @param fieldsQry Initial query
      * @param cancel Query cancel.
      * @return Update result wrapped into {@link GridQueryFieldsResult}
@@ -2482,7 +2470,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
     private List<QueryCursorImpl<List<?>>> executeUpdateDistributed(
         String schemaName,
         Connection conn,
-        Prepared prepared,
+        QueryParserResultDml dml,
         SqlFieldsQuery fieldsQry,
         GridQueryCancel cancel
     ) throws IgniteCheckedException {
@@ -2493,7 +2481,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
 
             List<Object[]> argss = fieldsQry0.batchedArguments();
 
-            UpdatePlan plan = updatePlan(schemaName, conn, prepared, fieldsQry0, false);
+            UpdatePlan plan = updatePlan(schemaName, conn, dml, fieldsQry0, false);
 
             GridCacheContext<?, ?> cctx = plan.cacheContext();
 
@@ -2529,7 +2517,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
                     UpdateResult res;
 
                     try {
-                        res = executeUpdate(schemaName, conn, prepared, qry0, false, null, cancel);
+                        res = executeUpdate(schemaName, conn, dml, qry0, false, null, cancel);
 
                         cntPerRow[cntr++] = (int)res.counter();
 
@@ -2568,7 +2556,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
             return resCurs;
         }
         else {
-            UpdateResult res = executeUpdate(schemaName, conn, prepared, fieldsQry, false, null, cancel);
+            UpdateResult res = executeUpdate(schemaName, conn, dml, fieldsQry, false, null, cancel);
 
             res.throwIfError();
 
@@ -2586,7 +2574,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
      *
      * @param schemaName Schema.
      * @param conn Connection.
-     * @param prepared Prepared statement.
+     * @param dml DML command.
      * @param fieldsQry Original query.
      * @param loc Query locality flag.
      * @param filters Cache name and key filter.
@@ -2594,14 +2582,14 @@ public class IgniteH2Indexing implements GridQueryIndexing {
      * @return Update result (modified items count and failed keys).
      * @throws IgniteCheckedException if failed.
      */
-    private UpdateResult executeUpdate(String schemaName, Connection conn, Prepared prepared,
+    private UpdateResult executeUpdate(String schemaName, Connection conn, QueryParserResultDml dml,
         SqlFieldsQuery fieldsQry, boolean loc, IndexingQueryFilter filters, GridQueryCancel cancel)
         throws IgniteCheckedException {
         Object[] errKeys = null;
 
         long items = 0;
 
-        UpdatePlan plan = updatePlan(schemaName, conn, prepared, fieldsQry, loc);
+        UpdatePlan plan = updatePlan(schemaName, conn, dml, fieldsQry, loc);
 
         GridCacheContext<?, ?> cctx = plan.cacheContext();
 
@@ -2859,7 +2847,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
      *
      * @param schema Schema.
      * @param conn Connection.
-     * @param p Prepared statement.
+     * @param dml Command.
      * @param fieldsQry Original fields query.
      * @param loc Local query flag.
      * @return Update plan.
@@ -2868,22 +2856,40 @@ public class IgniteH2Indexing implements GridQueryIndexing {
     private UpdatePlan updatePlan(
         String schema,
         Connection conn,
-        Prepared p,
+        QueryParserResultDml dml,
         SqlFieldsQuery fieldsQry,
         boolean loc
     ) throws IgniteCheckedException {
+        // Disallow updates on SYSTEM schema.
         if (F.eq(QueryUtils.SCHEMA_SYS, schema))
             throw new IgniteSQLException("DML statements are not supported on " + schema + " schema",
                 IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
 
-        H2CachedStatementKey planKey = new H2CachedStatementKey(schema, p.getSQL(), fieldsQry, loc);
+        // Disallow updates inside transaction if this is not MVCC mode.
+        boolean inTx = ctx.cache().context().tm().inUserTx();
+
+        if (!dml.mvccEnabled() && !updateInTxAllowed && inTx) {
+            throw new IgniteSQLException("DML statements are not allowed inside a transaction over " +
+                "cache(s) with TRANSACTIONAL atomicity mode (change atomicity mode to " +
+                "TRANSACTIONAL_SNAPSHOT or disable this error message with system property " +
+                "\"IGNITE_ALLOW_DML_INSIDE_TRANSACTION\"");
+        }
+
+        H2CachedStatementKey planKey = new H2CachedStatementKey(schema, dml.statement().getSQL(), fieldsQry, loc);
 
         UpdatePlan res = updatePlanCache.get(planKey);
 
         if (res != null)
             return res;
 
-        res = UpdatePlanBuilder.planForStatement(p, loc, this, conn, fieldsQry, updateInTxAllowed);
+        res = UpdatePlanBuilder.planForStatement(
+            dml.statement(),
+            dml.mvccEnabled(),
+            loc,
+            this,
+            conn,
+            fieldsQry
+        );
 
         // Don't cache re-runs
         UpdatePlan oldRes = updatePlanCache.putIfAbsent(planKey, res);
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/QueryParser.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/QueryParser.java
index 54a3bbd..13810cb 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/QueryParser.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/QueryParser.java
@@ -21,14 +21,18 @@ import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteException;
 import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.IgniteSystemProperties;
+import org.apache.ignite.cache.CacheAtomicityMode;
 import org.apache.ignite.cache.query.SqlFieldsQuery;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.GridCacheContextInfo;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
 import org.apache.ignite.internal.processors.cache.query.GridCacheTwoStepQuery;
 import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
 import org.apache.ignite.internal.processors.cache.query.SqlFieldsQueryEx;
 import org.apache.ignite.internal.processors.query.GridQueryFieldMetadata;
 import org.apache.ignite.internal.processors.query.IgniteSQLException;
 import org.apache.ignite.internal.processors.query.h2.dml.DmlUtils;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
 import org.apache.ignite.internal.processors.query.h2.sql.GridSqlQueryParser;
 import org.apache.ignite.internal.processors.query.h2.sql.GridSqlQuerySplitter;
 import org.apache.ignite.internal.processors.query.h2.sql.GridSqlStatement;
@@ -308,8 +312,11 @@ public class QueryParser {
 
             return new QueryParserResult(newQry, remainingQry, null, null, cmd);
         }
-        else if (GridSqlQueryParser.isDml(prepared))
-            return new QueryParserResult(newQry, remainingQry, null ,new QueryParserResultDml(prepared), null);
+        else if (GridSqlQueryParser.isDml(prepared)) {
+            QueryParserResultDml dml = prepareDmlStatement(prepared);
+
+            return new QueryParserResult(newQry, remainingQry, null, dml, null);
+        }
         else if (!prepared.isQuery()) {
             throw new IgniteSQLException("Unsupported statement: " + newQry.getSql(),
                 IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
@@ -387,6 +394,57 @@ public class QueryParser {
     }
 
     /**
+     * Prepare DML statement.
+     *
+     * @param preparedStmt Prepared statement.
+     * @return Statement.
+     */
+    public QueryParserResultDml prepareDmlStatement(PreparedStatement preparedStmt) {
+        Prepared prep = GridSqlQueryParser.prepared(preparedStmt);
+
+        return prepareDmlStatement(prep);
+    }
+
+    /**
+     * Prepare DML statement.
+     *
+     * @param prepared Prepared.
+     * @return Statement.
+     */
+    public QueryParserResultDml prepareDmlStatement(Prepared prepared) {
+        // Prepare AST.
+        GridSqlQueryParser parser = new GridSqlQueryParser(false);
+
+        GridSqlStatement stmt = parser.parse(prepared);
+
+        List<GridH2Table> tbls = parser.tablesForDml();
+
+        // Check if caches are started because we may need to collect affinity info later on, so they needs to be
+        // available on local node.
+        for (GridH2Table h2tbl : tbls)
+            H2Utils.checkAndStartNotStartedCache(idx.kernalContext(), h2tbl);
+
+        // Check MVCC mode.
+        GridCacheContextInfo ctx = null;
+        boolean mvccEnabled = false;
+
+        for (GridH2Table h2tbl : tbls) {
+            GridCacheContextInfo curCtx = h2tbl.cacheInfo();
+            boolean curMvccEnabled = curCtx.config().getAtomicityMode() == CacheAtomicityMode.TRANSACTIONAL_SNAPSHOT;
+
+            if (ctx == null) {
+                ctx = curCtx;
+
+                mvccEnabled = curMvccEnabled;
+            }
+            else if (curMvccEnabled != mvccEnabled)
+                MvccUtils.throwAtomicityModesMismatchException(ctx.config(), curCtx.config());
+        }
+
+        return new QueryParserResultDml(stmt, mvccEnabled);
+    }
+
+    /**
      * Clear cached plans.
      */
     public void clearCache() {
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/QueryParserResultDml.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/QueryParserResultDml.java
index 5e91288..03067e0 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/QueryParserResultDml.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/QueryParserResultDml.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.internal.processors.query.h2;
 
+import org.apache.ignite.internal.processors.query.h2.sql.GridSqlStatement;
 import org.h2.command.Prepared;
 
 /**
@@ -24,21 +25,32 @@ import org.h2.command.Prepared;
  */
 public class QueryParserResultDml {
     /** Command. */
-    private final Prepared prepared;
+    private final GridSqlStatement stmt;
+
+    /** MVCC enabled flag. */
+    private final boolean mvccEnabled;
 
     /**
      * Constructor.
      *
-     * @param prepared Command.
+     * @param stmt Command.
      */
-    public QueryParserResultDml(Prepared prepared) {
-        this.prepared = prepared;
+    public QueryParserResultDml(GridSqlStatement stmt, boolean mvccEnabled) {
+        this.stmt = stmt;
+        this.mvccEnabled = mvccEnabled;
     }
 
     /**
      * @return Command.
      */
-    public Prepared prepared() {
-        return prepared;
+    public GridSqlStatement statement() {
+        return stmt;
+    }
+
+    /**
+     * @return MVCC enabled.
+     */
+    public boolean mvccEnabled() {
+        return mvccEnabled;
     }
 }
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlanBuilder.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlanBuilder.java
index c3f55d5..709ce17 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlanBuilder.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlanBuilder.java
@@ -32,7 +32,6 @@ import org.apache.ignite.binary.BinaryObject;
 import org.apache.ignite.binary.BinaryObjectBuilder;
 import org.apache.ignite.cache.query.SqlFieldsQuery;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
-import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
 import org.apache.ignite.internal.processors.cache.query.GridCacheTwoStepQuery;
 import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
 import org.apache.ignite.internal.processors.cache.query.SqlFieldsQueryEx;
@@ -45,8 +44,6 @@ import org.apache.ignite.internal.processors.query.h2.H2Utils;
 import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2RowDescriptor;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
-import org.apache.ignite.internal.processors.query.h2.sql.GridSqlAlias;
-import org.apache.ignite.internal.processors.query.h2.sql.GridSqlAst;
 import org.apache.ignite.internal.processors.query.h2.sql.GridSqlColumn;
 import org.apache.ignite.internal.processors.query.h2.sql.GridSqlConst;
 import org.apache.ignite.internal.processors.query.h2.sql.GridSqlDelete;
@@ -69,7 +66,6 @@ import org.apache.ignite.internal.util.typedef.internal.CU;
 import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.lang.IgniteClosure;
-import org.h2.command.Prepared;
 import org.h2.table.Column;
 import org.jetbrains.annotations.Nullable;
 
@@ -92,7 +88,8 @@ public final class UpdatePlanBuilder {
      * Generate SELECT statements to retrieve data for modifications from and find fast UPDATE or DELETE args,
      * if available.
      *
-     * @param prepared H2's {@link Prepared}.
+     * @param stmt Statement.
+     * @param mvccEnabled MVCC enabled flag.
      * @param loc Local query flag.
      * @param idx Indexing.
      * @param conn Connection.
@@ -101,98 +98,23 @@ public final class UpdatePlanBuilder {
      */
     @SuppressWarnings("ConstantConditions")
     public static UpdatePlan planForStatement(
-        Prepared prepared,
+        GridSqlStatement stmt,
+        boolean mvccEnabled,
         boolean loc,
         IgniteH2Indexing idx,
         @Nullable Connection conn,
-        @Nullable SqlFieldsQuery fieldsQry,
-        boolean dmlInsideTxAllowed
-    )
-        throws IgniteCheckedException {
-        assert !prepared.isQuery();
-
-        GridSqlQueryParser parser = new GridSqlQueryParser(false);
-
-        GridSqlStatement stmt = parser.parse(prepared);
-
-
-        List<GridH2Table> tbls = extractTablesParticipateAtQuery(parser);
-
-        GridCacheContext prevCctx = null;
-        boolean mvccEnabled = false;
-
-        for (GridH2Table h2tbl : tbls) {
-            H2Utils.checkAndStartNotStartedCache(idx.kernalContext(), h2tbl);
-
-            if (prevCctx == null) {
-                prevCctx = h2tbl.cacheContext();
-
-                assert prevCctx != null : h2tbl.cacheName() + " is not initted";
-
-                mvccEnabled = prevCctx.mvccEnabled();
-
-                if (!mvccEnabled && !dmlInsideTxAllowed && prevCctx.cache().context().tm().inUserTx()) {
-                    throw new IgniteSQLException("DML statements are not allowed inside a transaction over " +
-                        "cache(s) with TRANSACTIONAL atomicity mode (change atomicity mode to " +
-                        "TRANSACTIONAL_SNAPSHOT or disable this error message with system property " +
-                        "\"IGNITE_ALLOW_DML_INSIDE_TRANSACTION\" [cacheName=" + prevCctx.name() + ']');
-                }
-            }
-            else if (h2tbl.cacheContext().mvccEnabled() != mvccEnabled)
-                MvccUtils.throwAtomicityModesMismatchException(prevCctx, h2tbl.cacheContext());
-        }
-
+        @Nullable SqlFieldsQuery fieldsQry
+    ) throws IgniteCheckedException {
         if (stmt instanceof GridSqlMerge || stmt instanceof GridSqlInsert)
             return planForInsert(stmt, loc, idx, mvccEnabled, conn, fieldsQry);
         else if (stmt instanceof GridSqlUpdate || stmt instanceof GridSqlDelete)
             return planForUpdate(stmt, loc, idx, mvccEnabled, conn, fieldsQry);
         else
-            throw new IgniteSQLException("Unsupported operation: " + prepared.getSQL(),
+            throw new IgniteSQLException("Unsupported operation: " + stmt.getSQL(),
                 IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
     }
 
     /**
-     * Extract all tables participate at query
-     *
-     * @param parser Parser related to the query.
-     * @return List of tables participate at query.
-     * @throws IgniteSQLException in case query contains virtual tables.
-     */
-    private static List<GridH2Table> extractTablesParticipateAtQuery(GridSqlQueryParser parser) throws IgniteSQLException {
-        Collection<?> parserObjects = parser.objectsMap().values();
-
-        List<GridH2Table> tbls = new ArrayList<>(parserObjects.size());
-
-        // check all involved caches
-        for (Object o : parserObjects) {
-            if (o instanceof GridSqlMerge)
-                o = ((GridSqlMerge)o).into();
-            else if (o instanceof GridSqlInsert)
-                o = ((GridSqlInsert)o).into();
-            else if (o instanceof GridSqlUpdate)
-                o = ((GridSqlUpdate)o).target();
-            else if (o instanceof GridSqlDelete)
-                o = ((GridSqlDelete)o).from();
-
-            if (o instanceof GridSqlAlias)
-                o = GridSqlAlias.unwrap((GridSqlAst)o);
-
-            if (o instanceof GridSqlTable) {
-                GridH2Table h2tbl = ((GridSqlTable)o).dataTable();
-
-                if (h2tbl == null) { // Check for virtual tables.
-                    throw new IgniteSQLException("Operation not supported for table '" +
-                        ((GridSqlTable)o).tableName() + "'", IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
-                }
-
-                tbls.add(h2tbl);
-            }
-        }
-
-        return tbls;
-    }
-
-    /**
      * Prepare update plan for INSERT or MERGE.
      *
      * @param stmt INSERT or MERGE statement.
@@ -953,9 +875,9 @@ public final class UpdatePlanBuilder {
                     qry.mapQueries().size() == 1 && !qry.mapQueries().get(0).hasSubQueries(); // One w/o subqueries
 
                 if (distributed) {
+                    // TODO: This should be done during plan build.
                     List<Integer> cacheIds = H2Utils.collectCacheIds(idx, CU.cacheId(cacheName), qry.tables());
 
-                    H2Utils.collectMvccEnabled(idx, cacheIds);
                     H2Utils.checkQuery(idx, cacheIds, qry.mvccEnabled(), qry.forUpdate(), qry.tables());
 
                     return new DmlDistributedPlanInfo(qry.isReplicatedOnly(), cacheIds);
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 5b1609d..4d298ff 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
@@ -1803,6 +1803,46 @@ public class GridSqlQueryParser {
     }
 
     /**
+     * Extract all tables participating in DML statement.
+     *
+     * @return List of tables participate at query.
+     * @throws IgniteSQLException in case query contains virtual tables.
+     */
+    public List<GridH2Table> tablesForDml() throws IgniteSQLException {
+        Collection<?> parserObjects = h2ObjToGridObj.values();
+
+        List<GridH2Table> tbls = new ArrayList<>(parserObjects.size());
+
+        // check all involved caches
+        for (Object o : parserObjects) {
+            if (o instanceof GridSqlMerge)
+                o = ((GridSqlMerge) o).into();
+            else if (o instanceof GridSqlInsert)
+                o = ((GridSqlInsert) o).into();
+            else if (o instanceof GridSqlUpdate)
+                o = ((GridSqlUpdate) o).target();
+            else if (o instanceof GridSqlDelete)
+                o = ((GridSqlDelete) o).from();
+
+            if (o instanceof GridSqlAlias)
+                o = GridSqlAlias.unwrap((GridSqlAst)o);
+
+            if (o instanceof GridSqlTable) {
+                GridH2Table h2tbl = ((GridSqlTable)o).dataTable();
+
+                if (h2tbl == null) { // Check for virtual tables.
+                    throw new IgniteSQLException("Operation not supported for table '" +
+                        ((GridSqlTable)o).tableName() + "'", IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+                }
+
+                tbls.add(h2tbl);
+            }
+        }
+
+        return tbls;
+    }
+
+    /**
      * Parse query.
      *
      * @param prepared Prepared statement.