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/01 11:29:42 UTC

[ignite] branch master updated: IGNITE-11160: SQL: simplified row hierarchy. This closes #5990.

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 bd49b14  IGNITE-11160: SQL: simplified row hierarchy. This closes #5990.
bd49b14 is described below

commit bd49b143b81a154cf5aa8c1829e9d774f85132dd
Author: devozerov <vo...@gridgain.com>
AuthorDate: Fri Feb 1 14:29:33 2019 +0300

    IGNITE-11160: SQL: simplified row hierarchy. This closes #5990.
---
 .../internal/processors/query/QueryUtils.java      |   9 +
 .../query/h2/opt/GridH2SpatialIndex.java           |  40 +++--
 .../processors/query/h2/ConnectionManager.java     |   4 +-
 .../internal/processors/query/h2/H2Cursor.java     |   8 +-
 .../internal/processors/query/h2/H2RowCache.java   |  16 +-
 .../processors/query/h2/H2TableDescriptor.java     |  29 ++-
 .../processors/query/h2/H2TableEngine.java         |  18 +-
 .../internal/processors/query/h2/H2Utils.java      |   6 +-
 .../processors/query/h2/IndexBuildClosure.java     |   4 +-
 .../query/h2/IndexRebuildPartialClosure.java       |   4 +-
 .../processors/query/h2/SchemaManager.java         |   5 +-
 .../query/h2/database/H2PkHashIndex.java           |  15 +-
 .../processors/query/h2/database/H2RowFactory.java |  94 ----------
 .../processors/query/h2/database/H2Tree.java       | 143 ++++++++++-----
 .../query/h2/database/H2TreeClientIndex.java       |  11 +-
 .../query/h2/database/H2TreeFilterClosure.java     |   7 +-
 .../processors/query/h2/database/H2TreeIndex.java  |  62 ++-----
 .../query/h2/database/InlineIndexHelper.java       |  11 +-
 .../h2/database/io/AbstractH2ExtrasInnerIO.java    |  22 +--
 .../h2/database/io/AbstractH2ExtrasLeafIO.java     |  22 +--
 .../query/h2/database/io/AbstractH2InnerIO.java    |  18 +-
 .../query/h2/database/io/AbstractH2LeafIO.java     |  18 +-
 .../processors/query/h2/database/io/H2IOUtils.java |   8 +-
 .../processors/query/h2/dml/DmlAstUtils.java       |  10 +-
 .../processors/query/h2/dml/UpdatePlan.java        |   7 +-
 .../processors/query/h2/dml/UpdatePlanBuilder.java |  11 +-
 .../processors/query/h2/opt/GridH2IndexBase.java   |  24 +--
 .../processors/query/h2/opt/GridH2MetaTable.java   |  81 +--------
 .../query/h2/opt/GridH2PlainRowFactory.java        | 196 --------------------
 .../processors/query/h2/opt/GridH2Row.java         | 133 --------------
 .../query/h2/opt/GridH2RowDescriptor.java          |  55 +++---
 .../query/h2/opt/GridH2SystemIndexFactory.java     |  36 ----
 .../processors/query/h2/opt/GridH2Table.java       |  51 +++---
 ...ridH2KeyValueRowOnheap.java => H2CacheRow.java} | 198 ++++++++++++++++-----
 .../{GridH2KeyRowOnheap.java => H2PlainRow.java}   |  49 +++--
 ...GridH2SearchRow.java => H2PlainRowFactory.java} |  40 ++++-
 ...GridH2KeyRowOnheap.java => H2PlainRowPair.java} |  50 +++---
 ...idH2KeyRowOnheap.java => H2PlainRowSingle.java} |  35 ++--
 .../{GridH2SearchRowAdapter.java => H2Row.java}    |  16 +-
 .../query/h2/opt/join/CursorIteratorWrapper.java   |  14 +-
 .../query/h2/opt/join/DistributedLookupBatch.java  |   6 +-
 .../processors/query/h2/opt/join/RangeSource.java  |  12 +-
 .../query/h2/twostep/GridMergeIndexSorted.java     |   4 +-
 .../query/h2/twostep/GridMergeIndexUnsorted.java   |   4 +-
 .../visor/verify/ValidateIndexesClosure.java       |   8 +-
 .../processors/cache/index/H2RowCacheSelfTest.java |  13 +-
 46 files changed, 604 insertions(+), 1023 deletions(-)

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 d7e9fdb..322bd8d 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
@@ -76,6 +76,15 @@ import static org.apache.ignite.internal.processors.cache.query.IgniteQueryError
  * Utility methods for queries.
  */
 public class QueryUtils {
+    /** */
+    public static final int DEFAULT_COLUMNS_COUNT = 2;
+
+    /** Key column. */
+    public static final int KEY_COL = 0;
+
+    /** Value column. */
+    public static final int VAL_COL = 1;
+
     /** Default schema. */
     public static final String DFLT_SCHEMA = "PUBLIC";
 
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 35e9424..4af31fb 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
@@ -27,6 +27,7 @@ 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.internal.processors.query.QueryUtils;
 import org.apache.ignite.internal.processors.query.h2.H2Cursor;
 import org.apache.ignite.internal.processors.query.h2.H2Utils;
 import org.apache.ignite.internal.util.GridCursorIteratorWrapper;
@@ -54,8 +55,6 @@ import org.h2.value.ValueGeometry;
 import org.locationtech.jts.geom.Envelope;
 import org.locationtech.jts.geom.Geometry;
 
-import static org.apache.ignite.internal.processors.query.h2.opt.GridH2KeyValueRowOnheap.KEY_COL;
-
 /**
  * Spatial index.
  */
@@ -77,7 +76,7 @@ public class GridH2SpatialIndex extends GridH2IndexBase implements SpatialIndex
     private final MVRTreeMap<Long>[] segments;
 
     /** */
-    private final Map<Long, GridH2Row> idToRow = new HashMap<>();
+    private final Map<Long, H2CacheRow> idToRow = new HashMap<>();
 
     /** */
     private final Map<Value, Long> keyToId = new HashMap<>();
@@ -160,8 +159,8 @@ public class GridH2SpatialIndex extends GridH2IndexBase implements SpatialIndex
     }
 
     /** {@inheritDoc} */
-    @Override public GridH2Row put(GridH2Row row) {
-        assert row instanceof GridH2KeyValueRowOnheap : "requires key to be at 0";
+    @Override public H2CacheRow put(H2CacheRow row) {
+        assert row instanceof H2CacheRow : "requires key to be at 0";
 
         Lock l = lock.writeLock();
 
@@ -170,7 +169,7 @@ public class GridH2SpatialIndex extends GridH2IndexBase implements SpatialIndex
         try {
             checkClosed();
 
-            Value key = row.getValue(KEY_COL);
+            Value key = row.getValue(QueryUtils.KEY_COL);
 
             assert key != null;
 
@@ -189,7 +188,7 @@ public class GridH2SpatialIndex extends GridH2IndexBase implements SpatialIndex
                 keyToId.put(key, rowId);
             }
 
-            GridH2Row old = idToRow.put(rowId, row);
+            H2CacheRow old = idToRow.put(rowId, row);
 
             segments[seg].put(getEnvelope(row, rowId), rowId);
 
@@ -204,8 +203,8 @@ public class GridH2SpatialIndex extends GridH2IndexBase implements SpatialIndex
     }
 
     /** {@inheritDoc} */
-    @Override public boolean putx(GridH2Row row) {
-        GridH2Row old = put(row);
+    @Override public boolean putx(H2CacheRow row) {
+        H2CacheRow old = put(row);
 
         return old != null;
     }
@@ -224,8 +223,13 @@ public class GridH2SpatialIndex extends GridH2IndexBase implements SpatialIndex
             (float) env.getMinY(), (float) env.getMaxY());
     }
 
-    /** {@inheritDoc} */
-    @Override public GridH2Row remove(SearchRow row) {
+    /**
+     * Remove row.
+     *
+     * @param row Row.
+     * @return Old row.
+     */
+    private H2CacheRow remove(SearchRow row) {
         Lock l = lock.writeLock();
 
         l.lock();
@@ -233,7 +237,7 @@ public class GridH2SpatialIndex extends GridH2IndexBase implements SpatialIndex
         try {
             checkClosed();
 
-            Value key = row.getValue(KEY_COL);
+            Value key = row.getValue(QueryUtils.KEY_COL);
 
             assert key != null;
 
@@ -241,7 +245,7 @@ public class GridH2SpatialIndex extends GridH2IndexBase implements SpatialIndex
 
             assert rowId != null;
 
-            GridH2Row oldRow = idToRow.remove(rowId);
+            H2CacheRow oldRow = idToRow.remove(rowId);
 
             assert oldRow != null;
 
@@ -261,7 +265,7 @@ public class GridH2SpatialIndex extends GridH2IndexBase implements SpatialIndex
 
     /** {@inheritDoc} */
     @Override public boolean removex(SearchRow row) {
-        GridH2Row old = remove(row);
+        H2Row old = remove(row);
 
         return old != null;
     }
@@ -334,7 +338,7 @@ public class GridH2SpatialIndex extends GridH2IndexBase implements SpatialIndex
      * @return Iterator over rows.
      */
     @SuppressWarnings("unchecked")
-    private GridCursor<GridH2Row> rowIterator(Iterator<SpatialKey> i, TableFilter filter) {
+    private GridCursor<H2Row> rowIterator(Iterator<SpatialKey> i, TableFilter filter) {
         if (!i.hasNext())
             return H2Utils.EMPTY_CURSOR;
 
@@ -348,10 +352,10 @@ public class GridH2SpatialIndex extends GridH2IndexBase implements SpatialIndex
 
         IndexingQueryCacheFilter qryCacheFilter = qryFilter != null ? qryFilter.forCache(getTable().cacheName()) : null;
 
-        List<GridH2Row> rows = new ArrayList<>();
+        List<H2CacheRow> rows = new ArrayList<>();
 
         do {
-            GridH2Row row = idToRow.get(i.next().getId());
+            H2CacheRow row = idToRow.get(i.next().getId());
 
             assert row != null;
 
@@ -382,7 +386,7 @@ public class GridH2SpatialIndex extends GridH2IndexBase implements SpatialIndex
 
             final MVRTreeMap<Long> segment = segments[seg];
 
-            GridCursor<GridH2Row> iter = rowIterator(segment.keySet().iterator(), null);
+            GridCursor<H2Row> iter = rowIterator(segment.keySet().iterator(), null);
 
             return new SingleRowCursor(iter.next() ? iter.get() : null);
         }
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ConnectionManager.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ConnectionManager.java
index 5c45b22..9b00a63 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ConnectionManager.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ConnectionManager.java
@@ -32,7 +32,7 @@ import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.processors.query.IgniteSQLException;
 import org.apache.ignite.internal.processors.query.QueryUtils;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2DefaultTableEngine;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2PlainRowFactory;
+import org.apache.ignite.internal.processors.query.h2.opt.H2PlainRowFactory;
 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.timeout.GridTimeoutProcessor;
@@ -57,7 +57,7 @@ public class ConnectionManager {
     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" +
         ";MAX_OPERATION_MEMORY=0;BATCH_JOINS=1" +
-        ";ROW_FACTORY=\"" + GridH2PlainRowFactory.class.getName() + "\"" +
+        ";ROW_FACTORY=\"" + H2PlainRowFactory.class.getName() + "\"" +
         ";DEFAULT_TABLE_ENGINE=" + GridH2DefaultTableEngine.class.getName();
 
     /** The period of clean up the {@link #threadConns}. */
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Cursor.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Cursor.java
index 01b3504..23e0533 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Cursor.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Cursor.java
@@ -18,7 +18,7 @@
 package org.apache.ignite.internal.processors.query.h2;
 
 import org.apache.ignite.IgniteCheckedException;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
+import org.apache.ignite.internal.processors.query.h2.opt.H2Row;
 import org.apache.ignite.internal.util.lang.GridCursor;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.h2.index.Cursor;
@@ -31,7 +31,7 @@ import org.h2.result.SearchRow;
  */
 public class H2Cursor implements Cursor {
     /** */
-    private final GridCursor<GridH2Row> cursor;
+    private final GridCursor<H2Row> cursor;
 
     /** */
     private final long time = U.currentTimeMillis();
@@ -39,7 +39,7 @@ public class H2Cursor implements Cursor {
     /**
      * @param cursor Cursor.
      */
-    public H2Cursor(GridCursor<GridH2Row> cursor) {
+    public H2Cursor(GridCursor<H2Row> cursor) {
         assert cursor != null;
 
         this.cursor = cursor;
@@ -64,7 +64,7 @@ public class H2Cursor implements Cursor {
     @Override public boolean next() {
         try {
             while (cursor.next()) {
-                GridH2Row row = cursor.get();
+                H2Row row = cursor.get();
 
                 if (row.expireTime() > 0 && row.expireTime() <= time)
                     continue;
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2RowCache.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2RowCache.java
index 15f5501..c16fd9a 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2RowCache.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2RowCache.java
@@ -25,7 +25,7 @@ import org.apache.ignite.internal.pagemem.PageMemory;
 import org.apache.ignite.internal.processors.cache.CacheGroupContext;
 import org.apache.ignite.internal.processors.cache.GridCacheContextInfo;
 import org.apache.ignite.internal.processors.query.GridQueryRowCacheCleaner;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2KeyValueRowOnheap;
+import org.apache.ignite.internal.processors.query.h2.opt.H2CacheRow;
 import org.apache.ignite.internal.util.typedef.F;
 import org.jsr166.ConcurrentLinkedHashMap;
 
@@ -37,7 +37,7 @@ import static org.jsr166.ConcurrentLinkedHashMap.DFLT_LOAD_FACTOR;
  */
 public class H2RowCache implements GridQueryRowCacheCleaner {
     /** Cached rows. */
-    private final ConcurrentLinkedHashMap<Long, GridH2KeyValueRowOnheap> rows;
+    private final ConcurrentLinkedHashMap<Long, H2CacheRow> rows;
 
     /** Cache group ID. */
     private final CacheGroupContext grpCtx;
@@ -51,7 +51,7 @@ public class H2RowCache implements GridQueryRowCacheCleaner {
     public H2RowCache(CacheGroupContext grpCtx, int maxSize) {
         this.grpCtx = grpCtx;
 
-        rows = new ConcurrentLinkedHashMap<Long, GridH2KeyValueRowOnheap>(
+        rows = new ConcurrentLinkedHashMap<>(
             DFLT_INIT_CAP,
             DFLT_LOAD_FACTOR,
             Runtime.getRuntime().availableProcessors(),
@@ -66,8 +66,8 @@ public class H2RowCache implements GridQueryRowCacheCleaner {
      * @return Cached on-heap row.
      * @throws IgniteCheckedException On error.
      */
-    public GridH2KeyValueRowOnheap get(long link) throws IgniteCheckedException {
-        GridH2KeyValueRowOnheap row = rows.get(link);
+    public H2CacheRow get(long link) throws IgniteCheckedException {
+        H2CacheRow row = rows.get(link);
 
         if (row != null)
             touch(link);
@@ -80,7 +80,7 @@ public class H2RowCache implements GridQueryRowCacheCleaner {
      *
      * @param row Row.
      */
-    public void put(GridH2KeyValueRowOnheap row) {
+    public void put(H2CacheRow row) {
         rows.put(row.link(), row);
     }
 
@@ -125,10 +125,10 @@ public class H2RowCache implements GridQueryRowCacheCleaner {
     private void clearForCache(GridCacheContextInfo cacheInfo) {
         int cacheId = cacheInfo.cacheId();
 
-        Iterator<Map.Entry<Long, GridH2KeyValueRowOnheap>> iter = rows.entrySet().iterator();
+        Iterator<Map.Entry<Long, H2CacheRow>> iter = rows.entrySet().iterator();
 
         while (iter.hasNext()) {
-            GridH2KeyValueRowOnheap row = iter.next().getValue();
+            H2CacheRow row = iter.next().getValue();
 
             if (F.eq(cacheId, row.cacheId()))
                 iter.remove();
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TableDescriptor.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TableDescriptor.java
index 0358c98..2ad8ed7 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TableDescriptor.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TableDescriptor.java
@@ -31,10 +31,9 @@ import org.apache.ignite.internal.processors.query.GridQueryProperty;
 import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor;
 import org.apache.ignite.internal.processors.query.QueryUtils;
 import org.apache.ignite.internal.processors.query.h2.database.H2PkHashIndex;
-import org.apache.ignite.internal.processors.query.h2.database.H2RowFactory;
+import org.apache.ignite.internal.processors.query.h2.database.H2TreeIndexBase;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2IndexBase;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2RowDescriptor;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2SystemIndexFactory;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
 import org.apache.ignite.internal.processors.query.h2.opt.GridLuceneIndex;
 import org.apache.ignite.internal.util.typedef.internal.S;
@@ -45,12 +44,10 @@ import org.h2.table.Column;
 import org.h2.table.IndexColumn;
 import org.jetbrains.annotations.NotNull;
 
-import static org.apache.ignite.internal.processors.query.h2.opt.GridH2KeyValueRowOnheap.KEY_COL;
-
 /**
  * Information about table in database.
  */
-public class H2TableDescriptor implements GridH2SystemIndexFactory {
+public class H2TableDescriptor {
     /** PK index name. */
     public static final String PK_IDX_NAME = "_key_PK";
 
@@ -197,23 +194,16 @@ public class H2TableDescriptor implements GridH2SystemIndexFactory {
     }
 
     /**
-     * Create H2 row factory.
+     * Create list of indexes. First must be primary key, after that all unique indexes and only then non-unique
+     * indexes. All indexes must be subtypes of {@link H2TreeIndexBase}.
      *
-     * @param rowDesc Row descriptor.
-     * @return H2 row factory.
+     * @param tbl Table to create indexes for.
+     * @return List of indexes.
      */
-    H2RowFactory rowFactory(GridH2RowDescriptor rowDesc) {
-        if (cacheInfo.affinityNode())
-            return new H2RowFactory(rowDesc, cacheInfo.cacheContext());
-
-        return null;
-    }
-
-    /** {@inheritDoc} */
-    @Override public ArrayList<Index> createSystemIndexes(GridH2Table tbl) {
+    public ArrayList<Index> createSystemIndexes(GridH2Table tbl) {
         ArrayList<Index> idxs = new ArrayList<>();
 
-        IndexColumn keyCol = tbl.indexColumn(KEY_COL, SortOrder.ASCENDING);
+        IndexColumn keyCol = tbl.indexColumn(QueryUtils.KEY_COL, SortOrder.ASCENDING);
         IndexColumn affCol = tbl.getAffinityKeyColumn();
 
         if (affCol != null && H2Utils.equals(affCol, keyCol))
@@ -386,8 +376,9 @@ public class H2TableDescriptor implements GridH2SystemIndexFactory {
      * @param idxDesc Index descriptor.
      * @return Index.
      */
+    @SuppressWarnings("ZeroLengthArrayAllocation")
     public GridH2IndexBase createUserIndex(GridQueryIndexDescriptor idxDesc) {
-        IndexColumn keyCol = tbl.indexColumn(KEY_COL, SortOrder.ASCENDING);
+        IndexColumn keyCol = tbl.indexColumn(QueryUtils.KEY_COL, SortOrder.ASCENDING);
         IndexColumn affCol = tbl.getAffinityKeyColumn();
 
         List<IndexColumn> cols = new ArrayList<>(idxDesc.fields().size() + 2);
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TableEngine.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TableEngine.java
index 9e79fba..722cae5 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TableEngine.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TableEngine.java
@@ -20,7 +20,6 @@ package org.apache.ignite.internal.processors.query.h2;
 import java.sql.Connection;
 import java.sql.SQLException;
 import java.sql.Statement;
-import org.apache.ignite.internal.processors.query.h2.database.H2RowFactory;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2RowDescriptor;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
 import org.h2.api.TableEngine;
@@ -30,14 +29,12 @@ import org.h2.table.TableBase;
 /**
  * H2 Table engine.
  */
+@SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized")
 public class H2TableEngine implements TableEngine {
     /** */
     private static GridH2RowDescriptor rowDesc0;
 
     /** */
-    private static H2RowFactory rowFactory0;
-
-    /** */
     private static H2TableDescriptor tblDesc0;
 
     /** */
@@ -49,16 +46,18 @@ public class H2TableEngine implements TableEngine {
      * @param conn Connection.
      * @param sql DDL clause.
      * @param rowDesc Row descriptor.
-     * @param rowFactory Row factory.
      * @param tblDesc Table descriptor.
      * @throws SQLException If failed.
      * @return Created table.
      */
-    public static synchronized GridH2Table createTable(Connection conn, String sql, GridH2RowDescriptor rowDesc,
-        H2RowFactory rowFactory, H2TableDescriptor tblDesc)
+    public static synchronized GridH2Table createTable(
+        Connection conn,
+        String sql,
+        GridH2RowDescriptor rowDesc,
+        H2TableDescriptor tblDesc
+    )
         throws SQLException {
         rowDesc0 = rowDesc;
-        rowFactory0 = rowFactory;
         tblDesc0 = tblDesc;
 
         try {
@@ -73,14 +72,13 @@ public class H2TableEngine implements TableEngine {
         finally {
             resTbl0 = null;
             tblDesc0 = null;
-            rowFactory0 = null;
             rowDesc0 = null;
         }
     }
 
     /** {@inheritDoc} */
     @Override public TableBase createTable(CreateTableData createTblData) {
-        resTbl0 = new GridH2Table(createTblData, rowDesc0, rowFactory0, tblDesc0, tblDesc0.cacheInfo());
+        resTbl0 = new GridH2Table(createTblData, rowDesc0, tblDesc0, tblDesc0.cacheInfo());
 
         return resTbl0;
     }
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 902de13..7078b43 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
@@ -48,10 +48,10 @@ 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.GridH2RetryException;
-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.GridH2Table;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2ValueCacheObject;
+import org.apache.ignite.internal.processors.query.h2.opt.H2Row;
 import org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2RowMessage;
 import org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2ValueMessage;
 import org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2ValueMessageFactory;
@@ -108,14 +108,14 @@ public class H2Utils {
     private static final char ESC_CH = '\"';
 
     /** Empty cursor. */
-    public static final GridCursor<GridH2Row> EMPTY_CURSOR = new GridCursor<GridH2Row>() {
+    public static final GridCursor<H2Row> EMPTY_CURSOR = new GridCursor<H2Row>() {
         /** {@inheritDoc} */
         @Override public boolean next() {
             return false;
         }
 
         /** {@inheritDoc} */
-        @Override public GridH2Row get() {
+        @Override public H2Row get() {
             return null;
         }
     };
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IndexBuildClosure.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IndexBuildClosure.java
index 8d1923f..c5ce0b3 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IndexBuildClosure.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IndexBuildClosure.java
@@ -20,7 +20,7 @@ package org.apache.ignite.internal.processors.query.h2;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2IndexBase;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
+import org.apache.ignite.internal.processors.query.h2.opt.H2CacheRow;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2RowDescriptor;
 import org.apache.ignite.internal.processors.query.schema.SchemaIndexCacheVisitorClosure;
 
@@ -47,7 +47,7 @@ public class IndexBuildClosure implements SchemaIndexCacheVisitorClosure {
 
     /** {@inheritDoc} */
     @Override public void apply(CacheDataRow row) throws IgniteCheckedException {
-        GridH2Row row0 = rowDesc.createRow(row);
+        H2CacheRow row0 = rowDesc.createRow(row);
 
         idx.putx(row0);
     }
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IndexRebuildPartialClosure.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IndexRebuildPartialClosure.java
index 2672f06..3d65df9 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IndexRebuildPartialClosure.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IndexRebuildPartialClosure.java
@@ -20,7 +20,7 @@ package org.apache.ignite.internal.processors.query.h2;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2IndexBase;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
+import org.apache.ignite.internal.processors.query.h2.opt.H2CacheRow;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
 import org.apache.ignite.internal.processors.query.schema.SchemaIndexCacheVisitorClosure;
 
@@ -43,7 +43,7 @@ public class IndexRebuildPartialClosure implements SchemaIndexCacheVisitorClosur
         for (Map.Entry<GridH2Table, Collection<GridH2IndexBase>> tblIdxEntry : tblIdxs.entrySet()) {
             GridH2Table tbl = tblIdxEntry.getKey();
 
-            GridH2Row row0 = tbl.rowDescriptor().createRow(row);
+            H2CacheRow row0 = tbl.rowDescriptor().createRow(row);
 
             for (GridH2IndexBase idx : tblIdxEntry.getValue())
                 idx.putx(row0);
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/SchemaManager.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/SchemaManager.java
index c227551..f681036 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/SchemaManager.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/SchemaManager.java
@@ -44,7 +44,6 @@ import org.apache.ignite.internal.processors.query.IgniteSQLException;
 import org.apache.ignite.internal.processors.query.QueryField;
 import org.apache.ignite.internal.processors.query.QueryIndexDescriptorImpl;
 import org.apache.ignite.internal.processors.query.QueryUtils;
-import org.apache.ignite.internal.processors.query.h2.database.H2RowFactory;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2IndexBase;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2RowDescriptor;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
@@ -444,9 +443,7 @@ public class SchemaManager {
 
         GridH2RowDescriptor rowDesc = new GridH2RowDescriptor(tbl, tbl.type());
 
-        H2RowFactory rowFactory = tbl.rowFactory(rowDesc);
-
-        GridH2Table h2Tbl = H2TableEngine.createTable(conn, sql, rowDesc, rowFactory, tbl);
+        GridH2Table h2Tbl = H2TableEngine.createTable(conn, sql, rowDesc, tbl);
 
         for (GridH2IndexBase usrIdx : tbl.createUserIndexes())
             createInitialUserIndex(schemaName, tbl, usrIdx);
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2PkHashIndex.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2PkHashIndex.java
index 6aa9ebf..3216b94 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2PkHashIndex.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2PkHashIndex.java
@@ -33,7 +33,7 @@ import org.apache.ignite.internal.processors.cache.tree.CacheDataRowStore;
 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.GridH2QueryContext;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
+import org.apache.ignite.internal.processors.query.h2.opt.H2CacheRow;
 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.util.lang.GridCursor;
@@ -148,7 +148,7 @@ public class H2PkHashIndex extends GridH2IndexBase {
     }
 
     /** {@inheritDoc} */
-    @Override public GridH2Row put(GridH2Row row) {
+    @Override public H2CacheRow put(H2CacheRow row) {
         // Should not be called directly. Rows are inserted into underlying cache data stores.
         assert false;
 
@@ -156,7 +156,7 @@ public class H2PkHashIndex extends GridH2IndexBase {
     }
 
     /** {@inheritDoc} */
-    @Override public boolean putx(GridH2Row row) {
+    @Override public boolean putx(H2CacheRow row) {
         // Should not be called directly. Rows are inserted into underlying cache data stores.
         assert false;
 
@@ -164,15 +164,6 @@ public class H2PkHashIndex extends GridH2IndexBase {
     }
 
     /** {@inheritDoc} */
-    @Override public GridH2Row remove(SearchRow row) {
-        // Should not be called directly. Rows are removed from underlying cache data stores.
-
-        assert false;
-
-        throw DbException.getUnsupportedException("remove");
-    }
-
-    /** {@inheritDoc} */
     @Override public boolean removex(SearchRow row) {
         // Should not be called directly. Rows are removed from underlying cache data stores.
         assert false;
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2RowFactory.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2RowFactory.java
deleted file mode 100644
index fb7ff0b..0000000
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2RowFactory.java
+++ /dev/null
@@ -1,94 +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.database;
-
-import org.apache.ignite.IgniteCheckedException;
-import org.apache.ignite.internal.pagemem.PageIdUtils;
-import org.apache.ignite.internal.processors.cache.GridCacheContext;
-import org.apache.ignite.internal.processors.cache.persistence.CacheDataRowAdapter;
-import org.apache.ignite.internal.processors.cache.tree.mvcc.data.MvccDataRow;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2RowDescriptor;
-
-/**
- * Data store for H2 rows.
- */
-public class H2RowFactory {
-    /** */
-    private final GridCacheContext<?,?> cctx;
-
-    /** */
-    private final GridH2RowDescriptor rowDesc;
-
-    /**
-     * @param rowDesc Row descriptor.
-     * @param cctx Cache context.
-     */
-    public H2RowFactory(GridH2RowDescriptor rowDesc, GridCacheContext<?,?> cctx) {
-        this.rowDesc = rowDesc;
-        this.cctx = cctx;
-    }
-
-    /**
-     * !!! This method must be invoked in read or write lock of referring index page. It is needed to
-     * !!! make sure that row at this link will be invisible, when the link will be removed from
-     * !!! from all the index pages, so that row can be safely erased from the data page.
-     *
-     * @param link Link.
-     * @return Row.
-     * @throws IgniteCheckedException If failed.
-     */
-    public GridH2Row getRow(long link) throws IgniteCheckedException {
-        CacheDataRowAdapter row = new CacheDataRowAdapter(link);
-
-        row.initFromLink(
-            cctx.group(),
-            CacheDataRowAdapter.RowData.FULL,
-            true
-        );
-
-        return rowDesc.createRow(row);
-    }
-
-    /**
-     * @param link Link.
-     * @param mvccCrdVer Mvcc coordinator version.
-     * @param mvccCntr Mvcc counter.
-     * @param mvccOpCntr Mvcc operation counter.
-     * @return Row.
-     * @throws IgniteCheckedException If failed.
-     */
-    public GridH2Row getMvccRow(long link, long mvccCrdVer, long mvccCntr, int mvccOpCntr)
-        throws IgniteCheckedException {
-        int partId = PageIdUtils.partId(PageIdUtils.pageId(link));
-
-        MvccDataRow row = new MvccDataRow(
-            cctx.group(),
-            0,
-            link,
-            partId,
-            null,
-            mvccCrdVer,
-            mvccCntr,
-            mvccOpCntr,
-            true
-        );
-
-        return rowDesc.createRow(row);
-    }
-}
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2Tree.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2Tree.java
index 4df301a..a3d865c 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2Tree.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2Tree.java
@@ -26,25 +26,28 @@ import java.util.stream.Collectors;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.IgniteSystemProperties;
+import org.apache.ignite.internal.pagemem.PageIdUtils;
 import org.apache.ignite.internal.pagemem.PageMemory;
 import org.apache.ignite.internal.pagemem.wal.IgniteWriteAheadLogManager;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
+import org.apache.ignite.internal.processors.cache.persistence.CacheDataRowAdapter;
 import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
 import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
 import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusMetaIO;
 import org.apache.ignite.internal.processors.cache.persistence.tree.reuse.ReuseList;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.data.MvccDataRow;
 import org.apache.ignite.internal.processors.failure.FailureProcessor;
 import org.apache.ignite.internal.processors.query.h2.H2RowCache;
 import org.apache.ignite.internal.processors.query.h2.database.io.H2ExtrasInnerIO;
 import org.apache.ignite.internal.processors.query.h2.database.io.H2ExtrasLeafIO;
 import org.apache.ignite.internal.processors.query.h2.database.io.H2RowLinkIO;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2KeyValueRowOnheap;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2SearchRow;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
+import org.apache.ignite.internal.processors.query.h2.opt.H2Row;
+import org.apache.ignite.internal.processors.query.h2.opt.H2CacheRow;
 import org.apache.ignite.internal.stat.IoStatisticsHolder;
 import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.internal.util.typedef.internal.U;
-import org.h2.result.SearchRow;
 import org.h2.table.IndexColumn;
 import org.h2.value.Value;
 import org.jetbrains.annotations.Nullable;
@@ -52,10 +55,14 @@ import org.jetbrains.annotations.Nullable;
 import static org.apache.ignite.internal.processors.query.h2.database.InlineIndexHelper.CANT_BE_COMPARE;
 
 /**
+ * H2 tree index implementation.
  */
-public abstract class H2Tree extends BPlusTree<GridH2SearchRow, GridH2Row> {
-    /** */
-    private final H2RowFactory rowStore;
+public class H2Tree extends BPlusTree<H2Row, H2Row> {
+    /** Cache context. */
+    private final GridCacheContext cctx;
+
+    /** Owning table. */
+    private final GridH2Table table;
 
     /** */
     private final int inlineSize;
@@ -91,11 +98,7 @@ public abstract class H2Tree extends BPlusTree<GridH2SearchRow, GridH2Row> {
     private final IoStatisticsHolder stats;
 
     /** */
-    private final Comparator<Value> comp = new Comparator<Value>() {
-        @Override public int compare(Value o1, Value o2) {
-            return compareValues(o1, o2);
-        }
-    };
+    private final Comparator<Value> comp = this::compareValues;
 
     /** Row cache. */
     private final H2RowCache rowCache;
@@ -121,6 +124,8 @@ public abstract class H2Tree extends BPlusTree<GridH2SearchRow, GridH2Row> {
     /**
      * Constructor.
      *
+     * @param cctx Cache context.
+     * @param table Owning table.
      * @param name Tree name.
      * @param idxName Name of index.
      * @param cacheName Cache name.
@@ -129,7 +134,6 @@ public abstract class H2Tree extends BPlusTree<GridH2SearchRow, GridH2Row> {
      * @param grpId Cache group ID.
      * @param pageMem Page memory.
      * @param wal Write ahead log manager.
-     * @param rowStore Row data store.
      * @param metaPageId Meta page ID.
      * @param initNew Initialize new index.
      * @param rowCache Row cache.
@@ -142,6 +146,8 @@ public abstract class H2Tree extends BPlusTree<GridH2SearchRow, GridH2Row> {
      * @throws IgniteCheckedException If failed.
      */
     protected H2Tree(
+        GridCacheContext cctx,
+        GridH2Table table,
         String name,
         String idxName,
         String cacheName,
@@ -151,7 +157,6 @@ public abstract class H2Tree extends BPlusTree<GridH2SearchRow, GridH2Row> {
         PageMemory pageMem,
         IgniteWriteAheadLogManager wal,
         AtomicLong globalRmvId,
-        H2RowFactory rowStore,
         long metaPageId,
         boolean initNew,
         H2TreeIndex.IndexColumnsInfo unwrappedColsInfo,
@@ -167,6 +172,8 @@ public abstract class H2Tree extends BPlusTree<GridH2SearchRow, GridH2Row> {
     ) throws IgniteCheckedException {
         super(name, grpId, pageMem, wal, globalRmvId, metaPageId, reuseList, failureProcessor);
 
+        this.cctx = cctx;
+        this.table = table;
         this.stats = stats;
 
         if (!initNew) {
@@ -194,13 +201,10 @@ public abstract class H2Tree extends BPlusTree<GridH2SearchRow, GridH2Row> {
 
         this.mvccEnabled = mvccEnabled;
 
-        assert rowStore != null;
+        inlineIdxs = unwrappedPk ? unwrappedColsInfo.inlineIdx() : wrappedColsInfo.inlineIdx();
+        cols = unwrappedPk ? unwrappedColsInfo.cols() : wrappedColsInfo.cols();
 
-        this.rowStore = rowStore;
-        this.inlineIdxs = unwrappedPk ? unwrappedColsInfo.inlineIdx() : wrappedColsInfo.inlineIdx();
-        this.cols = unwrappedPk ? unwrappedColsInfo.cols() : wrappedColsInfo.cols();
-
-        this.columnIds = new int[cols.length];
+        columnIds = new int[cols.length];
 
         for (int i = 0; i < cols.length; i++)
             columnIds[i] = cols[i].column.getColumnId();
@@ -213,7 +217,7 @@ public abstract class H2Tree extends BPlusTree<GridH2SearchRow, GridH2Row> {
 
         initTree(initNew, inlineSize);
 
-        this.created = initNew;
+        created = initNew;
     }
 
     /**
@@ -223,21 +227,41 @@ public abstract class H2Tree extends BPlusTree<GridH2SearchRow, GridH2Row> {
      * @return Row.
      * @throws IgniteCheckedException if failed.
      */
-    public GridH2Row createRowFromLink(long link) throws IgniteCheckedException {
+    public H2Row createRow(long link) throws IgniteCheckedException {
         if (rowCache != null) {
-            GridH2Row row = rowCache.get(link);
+            H2CacheRow row = rowCache.get(link);
 
             if (row == null) {
-                row = rowStore.getRow(link);
+                row = createRow0(link);
 
-                if (row instanceof GridH2KeyValueRowOnheap)
-                    rowCache.put((GridH2KeyValueRowOnheap)row);
+                rowCache.put(row);
             }
 
             return row;
         }
         else
-            return rowStore.getRow(link);
+            return createRow0(link);
+    }
+
+    /**
+     * !!! This method must be invoked in read or write lock of referring index page. It is needed to
+     * !!! make sure that row at this link will be invisible, when the link will be removed from
+     * !!! from all the index pages, so that row can be safely erased from the data page.
+     *
+     * @param link Link.
+     * @return Row.
+     * @throws IgniteCheckedException If failed.
+     */
+    private H2CacheRow createRow0(long link) throws IgniteCheckedException {
+        CacheDataRowAdapter row = new CacheDataRowAdapter(link);
+
+        row.initFromLink(
+            cctx.group(),
+            CacheDataRowAdapter.RowData.FULL,
+            true
+        );
+
+        return table.rowDescriptor().createRow(row);
     }
 
     /**
@@ -248,27 +272,52 @@ public abstract class H2Tree extends BPlusTree<GridH2SearchRow, GridH2Row> {
      * @return Row.
      * @throws IgniteCheckedException if failed.
      */
-    public GridH2Row createRowFromLink(long link, long mvccCrdVer, long mvccCntr, int mvccOpCntr) throws IgniteCheckedException {
+    public H2Row createMvccRow(long link, long mvccCrdVer, long mvccCntr, int mvccOpCntr) throws IgniteCheckedException {
         if (rowCache != null) {
-            GridH2Row row = rowCache.get(link);
+            H2CacheRow row = rowCache.get(link);
 
             if (row == null) {
-                row = rowStore.getMvccRow(link, mvccCrdVer, mvccCntr, mvccOpCntr);
+                row = createMvccRow0(link, mvccCrdVer, mvccCntr, mvccOpCntr);
 
-                if (row instanceof GridH2KeyValueRowOnheap)
-                    rowCache.put((GridH2KeyValueRowOnheap)row);
+                rowCache.put(row);
             }
 
             return row;
         }
         else
-            return rowStore.getMvccRow(link, mvccCrdVer, mvccCntr, mvccOpCntr);
+            return createMvccRow0(link, mvccCrdVer, mvccCntr, mvccOpCntr);
+    }
+
+    /**
+     * @param link Link.
+     * @param mvccCrdVer Mvcc coordinator version.
+     * @param mvccCntr Mvcc counter.
+     * @param mvccOpCntr Mvcc operation counter.
+     * @return Row.
+     */
+    private H2CacheRow createMvccRow0(long link, long mvccCrdVer, long mvccCntr, int mvccOpCntr)
+        throws IgniteCheckedException {
+        int partId = PageIdUtils.partId(PageIdUtils.pageId(link));
+
+        MvccDataRow row = new MvccDataRow(
+            cctx.group(),
+            0,
+            link,
+            partId,
+            null,
+            mvccCrdVer,
+            mvccCntr,
+            mvccOpCntr,
+            true
+        );
+
+        return table.rowDescriptor().createRow(row);
     }
 
     /** {@inheritDoc} */
-    @Override public GridH2Row getRow(BPlusIO<GridH2SearchRow> io, long pageAddr, int idx, Object ignore)
+    @Override public H2Row getRow(BPlusIO<H2Row> io, long pageAddr, int idx, Object ignore)
         throws IgniteCheckedException {
-        return (GridH2Row)io.getLookupRow(this, pageAddr, idx);
+        return io.getLookupRow(this, pageAddr, idx);
     }
 
     /**
@@ -307,8 +356,8 @@ public abstract class H2Tree extends BPlusTree<GridH2SearchRow, GridH2Row> {
 
     /** {@inheritDoc} */
     @SuppressWarnings("ForLoopReplaceableByForEach")
-    @Override protected int compare(BPlusIO<GridH2SearchRow> io, long pageAddr, int idx,
-        GridH2SearchRow row) throws IgniteCheckedException {
+    @Override protected int compare(BPlusIO<H2Row> io, long pageAddr, int idx,
+        H2Row row) throws IgniteCheckedException {
         if (inlineSize() == 0)
             return compareRows(getRow(io, pageAddr, idx), row);
         else {
@@ -347,7 +396,7 @@ public abstract class H2Tree extends BPlusTree<GridH2SearchRow, GridH2Row> {
 
             inlineSizeRecomendation(row);
 
-            SearchRow rowData = getRow(io, pageAddr, idx);
+            org.h2.result.SearchRow rowData = getRow(io, pageAddr, idx);
 
             for (int i = lastIdxUsed, len = cols.length; i < len; i++) {
                 IndexColumn col = cols[i];
@@ -379,7 +428,7 @@ public abstract class H2Tree extends BPlusTree<GridH2SearchRow, GridH2Row> {
      * @param r2 Row 2.
      * @return Compare result: see {@link Comparator#compare(Object, Object)} for values.
      */
-    public int compareRows(GridH2SearchRow r1, GridH2SearchRow r2) {
+    public int compareRows(H2Row r1, H2Row r2) {
         assert !mvccEnabled || r2.indexSearchRow() || MvccUtils.mvccVersionIsValid(r2.mvccCoordinatorVersion(), r2.mvccCounter()) : r2;
         if (r1 == r2)
             return 0;
@@ -411,7 +460,7 @@ public abstract class H2Tree extends BPlusTree<GridH2SearchRow, GridH2Row> {
      * @param r2 Search row.
      * @return Comparison result.
      */
-    private int mvccCompare(H2RowLinkIO io, long pageAddr, int idx, GridH2SearchRow r2) {
+    private int mvccCompare(H2RowLinkIO io, long pageAddr, int idx, H2Row r2) {
         if (!mvccEnabled || r2.indexSearchRow())
             return 0;
 
@@ -429,7 +478,7 @@ public abstract class H2Tree extends BPlusTree<GridH2SearchRow, GridH2Row> {
      * @param r2 Second row.
      * @return Comparison result.
      */
-    private int mvccCompare(GridH2SearchRow r1, GridH2SearchRow r2) {
+    private int mvccCompare(H2Row r1, H2Row r2) {
         if (!mvccEnabled || r2.indexSearchRow())
             return 0;
 
@@ -450,9 +499,10 @@ public abstract class H2Tree extends BPlusTree<GridH2SearchRow, GridH2Row> {
      *
      * @param row Grid H2 row related to given inline indexes.
      */
-    private void inlineSizeRecomendation(SearchRow row) {
+    @SuppressWarnings({"ConditionalBreakInInfiniteLoop", "IfMayBeConditional"})
+    private void inlineSizeRecomendation(org.h2.result.SearchRow row) {
         //Do the check only for put operations.
-        if(!(row instanceof GridH2KeyValueRowOnheap))
+        if(!(row instanceof H2CacheRow))
             return;
 
         Long invokeCnt = inlineSizeCalculationCntr.get();
@@ -538,9 +588,10 @@ public abstract class H2Tree extends BPlusTree<GridH2SearchRow, GridH2Row> {
     /**
      *
      */
-    private class MetaPageInfo {
+    private static class MetaPageInfo {
         /** */
         int inlineSize;
+
         /** */
         boolean useUnwrappedPk;
 
@@ -573,7 +624,9 @@ public abstract class H2Tree extends BPlusTree<GridH2SearchRow, GridH2Row> {
      * @param v2 Second value.
      * @return Comparison result.
      */
-    public abstract int compareValues(Value v1, Value v2);
+    public int compareValues(Value v1, Value v2) {
+        return v1 == v2 ? 0 : table.compareTypeSafe(v1, v2);
+    }
 
     /**
      * @return {@code True} if index was created during curren node's lifetime, {@code False} if it was restored from
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeClientIndex.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeClientIndex.java
index df896ed..1c1104b 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeClientIndex.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeClientIndex.java
@@ -19,7 +19,7 @@ package org.apache.ignite.internal.processors.query.h2.database;
 
 import java.util.List;
 import org.apache.ignite.internal.processors.query.IgniteSQLException;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
+import org.apache.ignite.internal.processors.query.h2.opt.H2CacheRow;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
 import org.h2.engine.Session;
 import org.h2.index.Cursor;
@@ -78,17 +78,12 @@ public class H2TreeClientIndex extends H2TreeIndexBase {
     }
 
     /** {@inheritDoc} */
-    @Override public GridH2Row put(GridH2Row row) {
+    @Override public H2CacheRow put(H2CacheRow row) {
         throw SHOULDNT_BE_INVOKED_EXCEPTION;
     }
 
     /** {@inheritDoc} */
-    @Override public boolean putx(GridH2Row row) {
-        throw SHOULDNT_BE_INVOKED_EXCEPTION;
-    }
-
-    /** {@inheritDoc} */
-    @Override public GridH2Row remove(SearchRow row) {
+    @Override public boolean putx(H2CacheRow row) {
         throw SHOULDNT_BE_INVOKED_EXCEPTION;
     }
 
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeFilterClosure.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeFilterClosure.java
index ca662ac..6c539d8 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeFilterClosure.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeFilterClosure.java
@@ -27,8 +27,7 @@ import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
 import org.apache.ignite.internal.processors.cache.persistence.tree.io.DataPageIO;
 import org.apache.ignite.internal.processors.cache.tree.mvcc.search.MvccDataPageClosure;
 import org.apache.ignite.internal.processors.query.h2.database.io.H2RowLinkIO;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2SearchRow;
+import org.apache.ignite.internal.processors.query.h2.opt.H2Row;
 import org.apache.ignite.internal.transactions.IgniteTxMvccVersionCheckedException;
 import org.apache.ignite.internal.util.typedef.X;
 import org.apache.ignite.internal.util.typedef.internal.S;
@@ -41,7 +40,7 @@ import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.mvccVer
 /**
  *
  */
-public class H2TreeFilterClosure implements H2Tree.TreeRowClosure<GridH2SearchRow, GridH2Row>, MvccDataPageClosure {
+public class H2TreeFilterClosure implements H2Tree.TreeRowClosure<H2Row, H2Row>, MvccDataPageClosure {
     /** */
     private final MvccSnapshot mvccSnapshot;
 
@@ -70,7 +69,7 @@ public class H2TreeFilterClosure implements H2Tree.TreeRowClosure<GridH2SearchRo
     }
 
     /** {@inheritDoc} */
-    @Override public boolean apply(BPlusTree<GridH2SearchRow, GridH2Row> tree, BPlusIO<GridH2SearchRow> io,
+    @Override public boolean apply(BPlusTree<H2Row, H2Row> tree, BPlusIO<H2Row> io,
         long pageAddr, int idx)  throws IgniteCheckedException {
         return (filter  == null || applyFilter((H2RowLinkIO)io, pageAddr, idx))
             && (mvccSnapshot == null || applyMvcc((H2RowLinkIO)io, pageAddr, idx));
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
index 5e482eb..d52bec4 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
@@ -35,8 +35,8 @@ import org.apache.ignite.internal.processors.query.h2.H2Cursor;
 import org.apache.ignite.internal.processors.query.h2.H2RowCache;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2Cursor;
 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.GridH2SearchRow;
+import org.apache.ignite.internal.processors.query.h2.opt.H2CacheRow;
+import org.apache.ignite.internal.processors.query.h2.opt.H2Row;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
 import org.apache.ignite.internal.stat.IoStatisticsHolder;
 import org.apache.ignite.internal.stat.IoStatisticsType;
@@ -51,7 +51,6 @@ import org.h2.index.SingleRowCursor;
 import org.h2.message.DbException;
 import org.h2.result.SearchRow;
 import org.h2.table.IndexColumn;
-import org.h2.value.Value;
 import org.jetbrains.annotations.Nullable;
 
 /**
@@ -161,6 +160,8 @@ public class H2TreeIndex extends H2TreeIndexBase {
                 RootPage page = getMetaPage(i);
 
                 segments[i] = new H2Tree(
+                    cctx,
+                    tbl,
                     treeName,
                     idxName,
                     tblName,
@@ -170,7 +171,6 @@ public class H2TreeIndex extends H2TreeIndexBase {
                     cctx.dataRegion().pageMemory(),
                     cctx.shared().wal(),
                     cctx.offheap().globalRemoveId(),
-                    tbl.rowFactory(),
                     page.pageId().pageId(),
                     page.isAllocated(),
                     unwrappedColsInfo,
@@ -182,11 +182,8 @@ public class H2TreeIndex extends H2TreeIndexBase {
                     rowCache,
                     cctx.kernalContext().failure(),
                     log,
-                    stats) {
-                    @Override public int compareValues(Value v1, Value v2) {
-                        return v1 == v2 ? 0 : table.compareTypeSafe(v1, v2);
-                    }
-                };
+                    stats
+                );
             }
             finally {
                 db.checkpointReadUnlock();
@@ -277,8 +274,8 @@ public class H2TreeIndex extends H2TreeIndexBase {
 
     /** {@inheritDoc} */
     @Override public Cursor find(Session ses, SearchRow lower, SearchRow upper) {
-        assert lower == null || lower instanceof GridH2SearchRow : lower;
-        assert upper == null || upper instanceof GridH2SearchRow : upper;
+        assert lower == null || lower instanceof H2Row : lower;
+        assert upper == null || upper instanceof H2Row : upper;
 
         try {
             int seg = threadLocalSegment();
@@ -286,14 +283,14 @@ public class H2TreeIndex extends H2TreeIndexBase {
             H2Tree tree = treeForRead(seg);
 
             if (!cctx.mvccEnabled() && indexType.isPrimaryKey() && lower != null && upper != null &&
-                tree.compareRows((GridH2SearchRow)lower, (GridH2SearchRow)upper) == 0) {
-                GridH2Row row = tree.findOne((GridH2SearchRow)lower, filter(GridH2QueryContext.get()), null);
+                tree.compareRows((H2Row)lower, (H2Row)upper) == 0) {
+                H2Row row = tree.findOne((H2Row)lower, filter(GridH2QueryContext.get()), null);
 
                 return (row == null) ? GridH2Cursor.EMPTY : new SingleRowCursor(row);
             }
             else {
-                return new H2Cursor(tree.find((GridH2SearchRow)lower,
-                    (GridH2SearchRow)upper, filter(GridH2QueryContext.get()), null));
+                return new H2Cursor(tree.find((H2Row)lower,
+                    (H2Row)upper, filter(GridH2QueryContext.get()), null));
             }
         }
         catch (IgniteCheckedException e) {
@@ -302,7 +299,7 @@ public class H2TreeIndex extends H2TreeIndexBase {
     }
 
     /** {@inheritDoc} */
-    @Override public GridH2Row put(GridH2Row row) {
+    @Override public H2CacheRow put(H2CacheRow row) {
         try {
             InlineIndexHelper.setCurrentInlineIndexes(inlineIdxs);
 
@@ -312,7 +309,7 @@ public class H2TreeIndex extends H2TreeIndexBase {
 
             assert cctx.shared().database().checkpointLockIsHeldByThread();
 
-            return tree.put(row);
+            return (H2CacheRow)tree.put(row);
         }
         catch (IgniteCheckedException e) {
             throw DbException.convert(e);
@@ -323,7 +320,7 @@ public class H2TreeIndex extends H2TreeIndexBase {
     }
 
     /** {@inheritDoc} */
-    @Override public boolean putx(GridH2Row row) {
+    @Override public boolean putx(H2CacheRow row) {
         try {
             InlineIndexHelper.setCurrentInlineIndexes(inlineIdxs);
 
@@ -344,31 +341,8 @@ public class H2TreeIndex extends H2TreeIndexBase {
     }
 
     /** {@inheritDoc} */
-    @Override public GridH2Row remove(SearchRow row) {
-        assert row instanceof GridH2SearchRow : row;
-
-        try {
-            InlineIndexHelper.setCurrentInlineIndexes(inlineIdxs);
-
-            int seg = segmentForRow(row);
-
-            H2Tree tree = treeForRead(seg);
-
-            assert cctx.shared().database().checkpointLockIsHeldByThread();
-
-            return tree.remove((GridH2SearchRow)row);
-        }
-        catch (IgniteCheckedException e) {
-            throw DbException.convert(e);
-        }
-        finally {
-            InlineIndexHelper.clearCurrentInlineIndexes();
-        }
-    }
-
-    /** {@inheritDoc} */
     @Override public boolean removex(SearchRow row) {
-        assert row instanceof GridH2SearchRow : row;
+        assert row instanceof H2Row : row;
 
         try {
             InlineIndexHelper.setCurrentInlineIndexes(inlineIdxs);
@@ -379,7 +353,7 @@ public class H2TreeIndex extends H2TreeIndexBase {
 
             assert cctx.shared().database().checkpointLockIsHeldByThread();
 
-            return tree.removex((GridH2SearchRow)row);
+            return tree.removex((H2Row)row);
         }
         catch (IgniteCheckedException e) {
             throw DbException.convert(e);
@@ -447,7 +421,7 @@ public class H2TreeIndex extends H2TreeIndexBase {
     }
 
     /** {@inheritDoc} */
-    @Override protected BPlusTree.TreeRowClosure<GridH2SearchRow, GridH2Row> filter(GridH2QueryContext qctx) {
+    @Override protected BPlusTree.TreeRowClosure<H2Row, H2Row> filter(GridH2QueryContext qctx) {
         if (qctx == null) {
             assert !cctx.mvccEnabled();
 
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/InlineIndexHelper.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/InlineIndexHelper.java
index 2f88558..1defe43 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/InlineIndexHelper.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/InlineIndexHelper.java
@@ -24,6 +24,7 @@ import java.util.Comparator;
 import java.util.List;
 import org.apache.ignite.internal.pagemem.PageUtils;
 import org.apache.ignite.internal.util.GridUnsafe;
+import org.apache.ignite.internal.util.lang.GridTuple;
 import org.h2.result.SortOrder;
 import org.h2.table.IndexColumn;
 import org.h2.value.CompareMode;
@@ -53,10 +54,12 @@ public class InlineIndexHelper {
     /** Value for comparison meaning 'Not enough information to compare'. */
     public static final int CANT_BE_COMPARE = -2;
 
+    /** Charset. */
     private static final Charset CHARSET = StandardCharsets.UTF_8;
 
     /** PageContext for use in IO's */
-    private static final ThreadLocal<List<InlineIndexHelper>> currentIndex = new ThreadLocal<>();
+    private static final ThreadLocal<GridTuple<List<InlineIndexHelper>>> CUR_HELPER =
+        ThreadLocal.withInitial(GridTuple::new);
 
     /** */
     public static final List<Integer> AVAILABLE_TYPES = Arrays.asList(
@@ -207,21 +210,21 @@ public class InlineIndexHelper {
      * @return Page context for current thread.
      */
     public static List<InlineIndexHelper> getCurrentInlineIndexes() {
-        return currentIndex.get();
+        return CUR_HELPER.get().get();
     }
 
     /**
      * Sets page context for current thread.
      */
     public static void setCurrentInlineIndexes(List<InlineIndexHelper> inlineIdxs) {
-        currentIndex.set(inlineIdxs);
+        CUR_HELPER.get().set(inlineIdxs);
     }
 
     /**
      * Clears current context.
      */
     public static void clearCurrentInlineIndexes() {
-        currentIndex.remove();
+        CUR_HELPER.get().set(null);
     }
 
     /**
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2ExtrasInnerIO.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2ExtrasInnerIO.java
index fbca917..90b18dd 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2ExtrasInnerIO.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2ExtrasInnerIO.java
@@ -27,13 +27,13 @@ import org.apache.ignite.internal.processors.cache.persistence.tree.io.IOVersion
 import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO;
 import org.apache.ignite.internal.processors.query.h2.database.H2Tree;
 import org.apache.ignite.internal.processors.query.h2.database.InlineIndexHelper;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2SearchRow;
+import org.apache.ignite.internal.processors.query.h2.opt.H2CacheRow;
+import org.apache.ignite.internal.processors.query.h2.opt.H2Row;
 
 /**
  * Inner page for H2 row references.
  */
-public abstract class AbstractH2ExtrasInnerIO extends BPlusInnerIO<GridH2SearchRow> implements H2RowLinkIO {
+public abstract class AbstractH2ExtrasInnerIO extends BPlusInnerIO<H2Row> implements H2RowLinkIO {
     /** Payload size. */
     protected final int payloadSize;
 
@@ -64,13 +64,13 @@ public abstract class AbstractH2ExtrasInnerIO extends BPlusInnerIO<GridH2SearchR
      * @return IOVersions for given payload.
      */
     @SuppressWarnings("unchecked")
-    public static IOVersions<? extends BPlusInnerIO<GridH2SearchRow>> getVersions(int payload, boolean mvccEnabled) {
+    public static IOVersions<? extends BPlusInnerIO<H2Row>> getVersions(int payload, boolean mvccEnabled) {
         assert payload >= 0 && payload <= PageIO.MAX_PAYLOAD_SIZE;
 
         if (payload == 0)
             return mvccEnabled ? H2MvccInnerIO.VERSIONS : H2InnerIO.VERSIONS;
         else
-            return (IOVersions<BPlusInnerIO<GridH2SearchRow>>)PageIO.getInnerVersions((short)(payload - 1), mvccEnabled);
+            return (IOVersions<BPlusInnerIO<H2Row>>)PageIO.getInnerVersions((short)(payload - 1), mvccEnabled);
     }
 
     /**
@@ -97,8 +97,8 @@ public abstract class AbstractH2ExtrasInnerIO extends BPlusInnerIO<GridH2SearchR
 
     /** {@inheritDoc} */
     @SuppressWarnings("ForLoopReplaceableByForEach")
-    @Override public final void storeByOffset(long pageAddr, int off, GridH2SearchRow row) {
-        GridH2Row row0 = (GridH2Row)row;
+    @Override public final void storeByOffset(long pageAddr, int off, H2Row row) {
+        H2CacheRow row0 = (H2CacheRow)row;
 
         assert row0.link() != 0 : row0;
 
@@ -124,7 +124,7 @@ public abstract class AbstractH2ExtrasInnerIO extends BPlusInnerIO<GridH2SearchR
     }
 
     /** {@inheritDoc} */
-    @Override public final GridH2SearchRow getLookupRow(BPlusTree<GridH2SearchRow, ?> tree, long pageAddr, int idx)
+    @Override public final H2Row getLookupRow(BPlusTree<H2Row, ?> tree, long pageAddr, int idx)
         throws IgniteCheckedException {
         long link = getLink(pageAddr, idx);
 
@@ -135,14 +135,14 @@ public abstract class AbstractH2ExtrasInnerIO extends BPlusInnerIO<GridH2SearchR
             long mvccCntr = getMvccCounter(pageAddr, idx);
             int mvccOpCntr = getMvccOperationCounter(pageAddr, idx);
 
-            return ((H2Tree)tree).createRowFromLink(link, mvccCrdVer, mvccCntr, mvccOpCntr);
+            return ((H2Tree)tree).createMvccRow(link, mvccCrdVer, mvccCntr, mvccOpCntr);
         }
 
-        return ((H2Tree)tree).createRowFromLink(link);
+        return ((H2Tree)tree).createRow(link);
     }
 
     /** {@inheritDoc} */
-    @Override public final void store(long dstPageAddr, int dstIdx, BPlusIO<GridH2SearchRow> srcIo, long srcPageAddr, int srcIdx) {
+    @Override public final void store(long dstPageAddr, int dstIdx, BPlusIO<H2Row> srcIo, long srcPageAddr, int srcIdx) {
         int srcOff = srcIo.offset(srcIdx);
 
         byte[] payload = PageUtils.getBytes(srcPageAddr, srcOff, payloadSize);
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2ExtrasLeafIO.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2ExtrasLeafIO.java
index 9132795..4bf4c5a 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2ExtrasLeafIO.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2ExtrasLeafIO.java
@@ -27,13 +27,13 @@ import org.apache.ignite.internal.processors.cache.persistence.tree.io.IOVersion
 import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO;
 import org.apache.ignite.internal.processors.query.h2.database.H2Tree;
 import org.apache.ignite.internal.processors.query.h2.database.InlineIndexHelper;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2SearchRow;
+import org.apache.ignite.internal.processors.query.h2.opt.H2CacheRow;
+import org.apache.ignite.internal.processors.query.h2.opt.H2Row;
 
 /**
  * Leaf page for H2 row references.
  */
-public abstract class AbstractH2ExtrasLeafIO extends BPlusLeafIO<GridH2SearchRow> implements H2RowLinkIO {
+public abstract class AbstractH2ExtrasLeafIO extends BPlusLeafIO<H2Row> implements H2RowLinkIO {
     /** Payload size. */
     protected final int payloadSize;
 
@@ -64,13 +64,13 @@ public abstract class AbstractH2ExtrasLeafIO extends BPlusLeafIO<GridH2SearchRow
      * @return IOVersions for given payload.
      */
     @SuppressWarnings("unchecked")
-    public static IOVersions<? extends BPlusLeafIO<GridH2SearchRow>> getVersions(int payload, boolean mvccEnabled) {
+    public static IOVersions<? extends BPlusLeafIO<H2Row>> getVersions(int payload, boolean mvccEnabled) {
         assert payload >= 0 && payload <= PageIO.MAX_PAYLOAD_SIZE;
 
         if (payload == 0)
             return mvccEnabled ? H2MvccLeafIO.VERSIONS : H2LeafIO.VERSIONS;
         else
-            return (IOVersions<BPlusLeafIO<GridH2SearchRow>>)PageIO.getLeafVersions((short)(payload - 1), mvccEnabled);
+            return (IOVersions<BPlusLeafIO<H2Row>>)PageIO.getLeafVersions((short)(payload - 1), mvccEnabled);
     }
 
     /**
@@ -97,8 +97,8 @@ public abstract class AbstractH2ExtrasLeafIO extends BPlusLeafIO<GridH2SearchRow
 
     /** {@inheritDoc} */
     @SuppressWarnings("ForLoopReplaceableByForEach")
-    @Override public final void storeByOffset(long pageAddr, int off, GridH2SearchRow row) {
-        GridH2Row row0 = (GridH2Row)row;
+    @Override public final void storeByOffset(long pageAddr, int off, H2Row row) {
+        H2CacheRow row0 = (H2CacheRow)row;
 
         assert row0.link() != 0;
 
@@ -123,7 +123,7 @@ public abstract class AbstractH2ExtrasLeafIO extends BPlusLeafIO<GridH2SearchRow
     }
 
     /** {@inheritDoc} */
-    @Override public final void store(long dstPageAddr, int dstIdx, BPlusIO<GridH2SearchRow> srcIo, long srcPageAddr, int srcIdx) {
+    @Override public final void store(long dstPageAddr, int dstIdx, BPlusIO<H2Row> srcIo, long srcPageAddr, int srcIdx) {
         int srcOff = srcIo.offset(srcIdx);
 
         byte[] payload = PageUtils.getBytes(srcPageAddr, srcOff, payloadSize);
@@ -139,7 +139,7 @@ public abstract class AbstractH2ExtrasLeafIO extends BPlusLeafIO<GridH2SearchRow
     }
 
     /** {@inheritDoc} */
-    @Override public final GridH2SearchRow getLookupRow(BPlusTree<GridH2SearchRow, ?> tree, long pageAddr, int idx)
+    @Override public final H2Row getLookupRow(BPlusTree<H2Row, ?> tree, long pageAddr, int idx)
         throws IgniteCheckedException {
         long link = getLink(pageAddr, idx);
 
@@ -148,10 +148,10 @@ public abstract class AbstractH2ExtrasLeafIO extends BPlusLeafIO<GridH2SearchRow
             long mvccCntr = getMvccCounter(pageAddr, idx);
             int mvccOpCntr = getMvccOperationCounter(pageAddr, idx);
 
-            return ((H2Tree)tree).createRowFromLink(link, mvccCrdVer, mvccCntr, mvccOpCntr);
+            return ((H2Tree)tree).createMvccRow(link, mvccCrdVer, mvccCntr, mvccOpCntr);
         }
 
-        return ((H2Tree)tree).createRowFromLink(link);
+        return ((H2Tree)tree).createRow(link);
     }
 
     /** {@inheritDoc} */
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2InnerIO.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2InnerIO.java
index d1d569e..a782ffb 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2InnerIO.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2InnerIO.java
@@ -23,13 +23,13 @@ import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
 import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
 import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusInnerIO;
 import org.apache.ignite.internal.processors.query.h2.database.H2Tree;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2SearchRow;
+import org.apache.ignite.internal.processors.query.h2.opt.H2CacheRow;
+import org.apache.ignite.internal.processors.query.h2.opt.H2Row;
 
 /**
  * Inner page for H2 row references.
  */
-public abstract class AbstractH2InnerIO extends BPlusInnerIO<GridH2SearchRow> implements H2RowLinkIO {
+public abstract class AbstractH2InnerIO extends BPlusInnerIO<H2Row> implements H2RowLinkIO {
     /**
      * @param type Page type.
      * @param ver Page format version.
@@ -40,14 +40,14 @@ public abstract class AbstractH2InnerIO extends BPlusInnerIO<GridH2SearchRow> im
     }
 
     /** {@inheritDoc} */
-    @Override public void storeByOffset(long pageAddr, int off, GridH2SearchRow row) {
-        GridH2Row row0 = (GridH2Row)row;
+    @Override public void storeByOffset(long pageAddr, int off, H2Row row) {
+        H2CacheRow row0 = (H2CacheRow)row;
 
         H2IOUtils.storeRow(row0, pageAddr, off, storeMvccInfo());
     }
 
     /** {@inheritDoc} */
-    @Override public GridH2SearchRow getLookupRow(BPlusTree<GridH2SearchRow, ?> tree, long pageAddr, int idx)
+    @Override public H2Row getLookupRow(BPlusTree<H2Row, ?> tree, long pageAddr, int idx)
         throws IgniteCheckedException {
         long link = getLink(pageAddr, idx);
 
@@ -56,14 +56,14 @@ public abstract class AbstractH2InnerIO extends BPlusInnerIO<GridH2SearchRow> im
             long mvccCntr = getMvccCounter(pageAddr, idx);
             int mvccOpCntr = getMvccOperationCounter(pageAddr, idx);
 
-            return ((H2Tree)tree).createRowFromLink(link, mvccCrdVer, mvccCntr, mvccOpCntr);
+            return ((H2Tree)tree).createMvccRow(link, mvccCrdVer, mvccCntr, mvccOpCntr);
         }
 
-        return ((H2Tree)tree).createRowFromLink(link);
+        return ((H2Tree)tree).createRow(link);
     }
 
     /** {@inheritDoc} */
-    @Override public void store(long dstPageAddr, int dstIdx, BPlusIO<GridH2SearchRow> srcIo, long srcPageAddr, int srcIdx) {
+    @Override public void store(long dstPageAddr, int dstIdx, BPlusIO<H2Row> srcIo, long srcPageAddr, int srcIdx) {
         H2IOUtils.store(dstPageAddr, offset(dstIdx), srcIo, srcPageAddr, srcIdx, storeMvccInfo());
     }
 
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2LeafIO.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2LeafIO.java
index 07f114f..ccacb4e 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2LeafIO.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2LeafIO.java
@@ -23,13 +23,13 @@ import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
 import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
 import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusLeafIO;
 import org.apache.ignite.internal.processors.query.h2.database.H2Tree;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2SearchRow;
+import org.apache.ignite.internal.processors.query.h2.opt.H2CacheRow;
+import org.apache.ignite.internal.processors.query.h2.opt.H2Row;
 
 /**
  * Leaf page for H2 row references.
  */
-public abstract class AbstractH2LeafIO extends BPlusLeafIO<GridH2SearchRow> implements H2RowLinkIO {
+public abstract class AbstractH2LeafIO extends BPlusLeafIO<H2Row> implements H2RowLinkIO {
     /**
      * @param type Page type.
      * @param ver Page format version.
@@ -40,21 +40,21 @@ public abstract class AbstractH2LeafIO extends BPlusLeafIO<GridH2SearchRow> impl
     }
 
     /** {@inheritDoc} */
-    @Override public final void storeByOffset(long pageAddr, int off, GridH2SearchRow row) {
-        GridH2Row row0 = (GridH2Row)row;
+    @Override public final void storeByOffset(long pageAddr, int off, H2Row row) {
+        H2CacheRow row0 = (H2CacheRow)row;
 
         H2IOUtils.storeRow(row0, pageAddr, off, storeMvccInfo());
     }
 
     /** {@inheritDoc} */
-    @Override public final void store(long dstPageAddr, int dstIdx, BPlusIO<GridH2SearchRow> srcIo, long srcPageAddr, int srcIdx) {
+    @Override public final void store(long dstPageAddr, int dstIdx, BPlusIO<H2Row> srcIo, long srcPageAddr, int srcIdx) {
         assert srcIo == this;
 
         H2IOUtils.store(dstPageAddr, offset(dstIdx), srcIo, srcPageAddr, srcIdx, storeMvccInfo());
     }
 
     /** {@inheritDoc} */
-    @Override public final GridH2SearchRow getLookupRow(BPlusTree<GridH2SearchRow,?> tree, long pageAddr, int idx)
+    @Override public final H2Row getLookupRow(BPlusTree<H2Row,?> tree, long pageAddr, int idx)
         throws IgniteCheckedException {
         long link = getLink(pageAddr, idx);
 
@@ -63,10 +63,10 @@ public abstract class AbstractH2LeafIO extends BPlusLeafIO<GridH2SearchRow> impl
             long mvccCntr = getMvccCounter(pageAddr, idx);
             int mvccOpCntr = getMvccOperationCounter(pageAddr, idx);
 
-            return ((H2Tree)tree).createRowFromLink(link, mvccCrdVer, mvccCntr, mvccOpCntr);
+            return ((H2Tree)tree).createMvccRow(link, mvccCrdVer, mvccCntr, mvccOpCntr);
         }
 
-        return ((H2Tree)tree).createRowFromLink(link);
+        return ((H2Tree)tree).createRow(link);
     }
 
     /** {@inheritDoc} */
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2IOUtils.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2IOUtils.java
index 1070ad4..c9424ca 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2IOUtils.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2IOUtils.java
@@ -20,8 +20,8 @@ package org.apache.ignite.internal.processors.query.h2.database.io;
 import org.apache.ignite.internal.pagemem.PageUtils;
 import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
 import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2SearchRow;
+import org.apache.ignite.internal.processors.query.h2.opt.H2CacheRow;
+import org.apache.ignite.internal.processors.query.h2.opt.H2Row;
 
 /**
  *
@@ -38,7 +38,7 @@ class H2IOUtils {
      * @param off Offset.
      * @param storeMvcc {@code True} to store mvcc data.
      */
-    static void storeRow(GridH2Row row, long pageAddr, int off, boolean storeMvcc) {
+    static void storeRow(H2CacheRow row, long pageAddr, int off, boolean storeMvcc) {
         assert row.link() != 0;
 
         PageUtils.putLong(pageAddr, off, row.link());
@@ -66,7 +66,7 @@ class H2IOUtils {
      */
     static void store(long dstPageAddr,
         int dstOff,
-        BPlusIO<GridH2SearchRow> srcIo,
+        BPlusIO<H2Row> srcIo,
         long srcPageAddr,
         int srcIdx,
         boolean storeMvcc)
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/DmlAstUtils.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/DmlAstUtils.java
index ebf5848..7e7f224 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/DmlAstUtils.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/DmlAstUtils.java
@@ -24,7 +24,7 @@ import java.util.Set;
 import org.apache.ignite.IgniteException;
 import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
 import org.apache.ignite.internal.processors.query.IgniteSQLException;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2KeyValueRowOnheap;
+import org.apache.ignite.internal.processors.query.QueryUtils;
 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;
@@ -158,9 +158,9 @@ public final class DmlAstUtils {
 
         assert gridTbl != null : "Failed to determine target grid table for DELETE";
 
-        Column h2KeyCol = gridTbl.getColumn(GridH2KeyValueRowOnheap.KEY_COL);
+        Column h2KeyCol = gridTbl.getColumn(QueryUtils.KEY_COL);
 
-        Column h2ValCol = gridTbl.getColumn(GridH2KeyValueRowOnheap.VAL_COL);
+        Column h2ValCol = gridTbl.getColumn(QueryUtils.VAL_COL);
 
         GridSqlColumn keyCol = new GridSqlColumn(h2KeyCol, tbl, h2KeyCol.getName());
         keyCol.resultType(GridSqlType.fromColumn(h2KeyCol));
@@ -339,9 +339,9 @@ public final class DmlAstUtils {
 
         assert gridTbl != null : "Failed to determine target grid table for UPDATE";
 
-        Column h2KeyCol = gridTbl.getColumn(GridH2KeyValueRowOnheap.KEY_COL);
+        Column h2KeyCol = gridTbl.getColumn(QueryUtils.KEY_COL);
 
-        Column h2ValCol = gridTbl.getColumn(GridH2KeyValueRowOnheap.VAL_COL);
+        Column h2ValCol = gridTbl.getColumn(QueryUtils.VAL_COL);
 
         GridSqlColumn keyCol = new GridSqlColumn(h2KeyCol, tbl, h2KeyCol.getName());
         keyCol.resultType(GridSqlType.fromColumn(h2KeyCol));
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlan.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlan.java
index 5b595e9..9877e9f 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlan.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlan.java
@@ -48,7 +48,6 @@ import org.h2.table.Column;
 import org.jetbrains.annotations.Nullable;
 
 import static org.apache.ignite.internal.processors.query.h2.dml.UpdateMode.BULK_LOAD;
-import static org.apache.ignite.internal.processors.query.h2.opt.GridH2KeyValueRowOnheap.DEFAULT_COLUMNS_COUNT;
 
 /**
  * Update plan - where to take data to update cache from and how to construct new keys and values, if needed.
@@ -260,7 +259,7 @@ public final class UpdatePlan {
         Column[] tblCols = tbl.getColumns();
 
         // First 2 columns are _key and _val Skip 'em.
-        for (int i = DEFAULT_COLUMNS_COUNT; i < tblCols.length; i++) {
+        for (int i = QueryUtils.DEFAULT_COLUMNS_COUNT; i < tblCols.length; i++) {
             if (tbl.rowDescriptor().isKeyValueOrVersionColumn(i))
                 continue;
 
@@ -332,8 +331,8 @@ public final class UpdatePlan {
             throw new IgniteSQLException("New value for UPDATE must not be null", IgniteQueryErrorCode.NULL_VALUE);
 
         // Skip key and value - that's why we start off with 3rd column
-        for (int i = 0; i < tbl.getColumns().length - DEFAULT_COLUMNS_COUNT; i++) {
-            Column c = tbl.getColumn(i + DEFAULT_COLUMNS_COUNT);
+        for (int i = 0; i < tbl.getColumns().length - QueryUtils.DEFAULT_COLUMNS_COUNT; i++) {
+            Column c = tbl.getColumn(i + QueryUtils.DEFAULT_COLUMNS_COUNT);
 
             if (rowDesc.isKeyValueOrVersionColumn(c.getColumnId()))
                 continue;
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 a6b09c2..027ac7e 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
@@ -73,8 +73,6 @@ import org.h2.command.Prepared;
 import org.h2.table.Column;
 import org.jetbrains.annotations.Nullable;
 
-import static org.apache.ignite.internal.processors.query.h2.opt.GridH2KeyValueRowOnheap.DEFAULT_COLUMNS_COUNT;
-
 /**
  * Logic for building update plans performed by {@link DmlStatementsProcessor}.
  */
@@ -820,8 +818,8 @@ public final class UpdatePlanBuilder {
                 return true;
 
             // column ids 0..1 are _key, _val
-            if (colId >= DEFAULT_COLUMNS_COUNT) {
-                if (desc.isColumnKeyProperty(colId - DEFAULT_COLUMNS_COUNT))
+            if (colId >= QueryUtils.DEFAULT_COLUMNS_COUNT) {
+                if (desc.isColumnKeyProperty(colId - QueryUtils.DEFAULT_COLUMNS_COUNT))
                     return true;
             }
         }
@@ -874,9 +872,10 @@ public final class UpdatePlanBuilder {
             }
             else {
                 // Column ids 0..2 are _key, _val, _ver
-                assert colId >= DEFAULT_COLUMNS_COUNT : "Unexpected column [name=" + col + ", id=" + colId + "].";
+                assert colId >= QueryUtils.DEFAULT_COLUMNS_COUNT :
+                    "Unexpected column [name=" + col + ", id=" + colId + "].";
 
-                if (desc.isColumnKeyProperty(colId - DEFAULT_COLUMNS_COUNT))
+                if (desc.isColumnKeyProperty(colId - QueryUtils.DEFAULT_COLUMNS_COUNT))
                     hasKeyProps = true;
                 else
                     hasValProps = true;
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 9547b5f..ab9929d 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
@@ -27,6 +27,7 @@ import org.apache.ignite.internal.managers.communication.GridMessageListener;
 import org.apache.ignite.internal.processors.cache.CacheObject;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
+import org.apache.ignite.internal.processors.query.QueryUtils;
 import org.apache.ignite.internal.processors.query.h2.H2Cursor;
 import org.apache.ignite.internal.processors.query.h2.H2Utils;
 import org.apache.ignite.internal.processors.query.h2.opt.join.CursorIteratorWrapper;
@@ -74,7 +75,6 @@ import java.util.UUID;
 import static java.util.Collections.singletonList;
 import static org.apache.ignite.internal.processors.query.h2.opt.join.DistributedJoinMode.OFF;
 import static org.apache.ignite.internal.processors.query.h2.opt.join.CollocationModel.buildCollocationModel;
-import static org.apache.ignite.internal.processors.query.h2.opt.GridH2KeyValueRowOnheap.KEY_COL;
 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;
@@ -186,7 +186,7 @@ public abstract class GridH2IndexBase extends BaseIndex {
      * @param row Row.
      * @return Existing row or {@code null}.
      */
-    public abstract GridH2Row put(GridH2Row row);
+    public abstract H2CacheRow put(H2CacheRow row);
 
     /**
      * Puts row.
@@ -194,15 +194,7 @@ public abstract class GridH2IndexBase extends BaseIndex {
      * @param row Row.
      * @return {@code True} if existing row row has been replaced.
      */
-    public abstract boolean putx(GridH2Row row);
-
-    /**
-     * Remove row from index.
-     *
-     * @param row Row.
-     * @return Removed row.
-     */
-    public abstract GridH2Row remove(SearchRow row);
+    public abstract boolean putx(H2CacheRow row);
 
     /**
      * Removes row from index.
@@ -459,7 +451,7 @@ public abstract class GridH2IndexBase extends BaseIndex {
      * @param qctx Query context.
      * @return Row filter.
      */
-    protected BPlusTree.TreeRowClosure<GridH2SearchRow, GridH2Row> filter(GridH2QueryContext qctx) {
+    protected BPlusTree.TreeRowClosure<H2Row, H2Row> filter(GridH2QueryContext qctx) {
         throw new UnsupportedOperationException();
     }
 
@@ -589,7 +581,7 @@ public abstract class GridH2IndexBase extends BaseIndex {
 
         CacheObject key;
 
-        final Value keyColValue = row.getValue(KEY_COL);
+        final Value keyColValue = row.getValue(QueryUtils.KEY_COL);
 
         assert keyColValue != null;
 
@@ -612,15 +604,15 @@ public abstract class GridH2IndexBase extends BaseIndex {
      * @return Iterator.
      */
     @SuppressWarnings("unchecked")
-    public Iterator<GridH2Row> findForSegment(GridH2RowRangeBounds bounds, int segment,
-        BPlusTree.TreeRowClosure<GridH2SearchRow, GridH2Row> filter) {
+    public Iterator<H2Row> findForSegment(GridH2RowRangeBounds bounds, int segment,
+        BPlusTree.TreeRowClosure<H2Row, H2Row> filter) {
         SearchRow first = toSearchRow(bounds.first());
         SearchRow last = toSearchRow(bounds.last());
 
         IgniteTree t = treeForRead(segment);
 
         try {
-            GridCursor<GridH2Row> range = ((BPlusTree)t).find(first, last, filter, null);
+            GridCursor<H2Row> range = ((BPlusTree)t).find(first, last, filter, null);
 
             if (range == null)
                 range = H2Utils.EMPTY_CURSOR;
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
index b008c081..cc5a0ad 100644
--- 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
@@ -80,15 +80,15 @@ public class GridH2MetaTable extends TableBase {
 
     /** {@inheritDoc} */
     @Override public Row getTemplateRow() {
-        return new MetaRow();
+        return new H2PlainRow(4);
     }
 
     /** {@inheritDoc} */
     @Override public SearchRow getTemplateSimpleRow(boolean singleColumn) {
         if (singleColumn)
-            return GridH2PlainRowFactory.create((Value)null);
+            return H2PlainRowFactory.create((Value)null);
 
-        return new MetaRow();
+        return new H2PlainRow(4);
     }
 
     /** {@inheritDoc} */
@@ -217,81 +217,6 @@ public class GridH2MetaTable extends TableBase {
     }
 
     /**
-     * Get value row.
-     */
-    private static class MetaRow extends GridH2SearchRowAdapter {
-        /** */
-        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);
-            }
-        }
-
-        /** {@inheritDoc} */
-        @Override public boolean indexSearchRow() {
-            return false;
-        }
-    }
-
-    /**
      * Met index.
      */
     private static class MetaIndex extends BaseIndex {
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2PlainRowFactory.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2PlainRowFactory.java
deleted file mode 100644
index d24dc08..0000000
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2PlainRowFactory.java
+++ /dev/null
@@ -1,196 +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 org.apache.ignite.internal.util.tostring.GridToStringInclude;
-import org.apache.ignite.internal.util.typedef.internal.S;
-import org.h2.result.Row;
-import org.h2.result.RowFactory;
-import org.h2.value.Value;
-
-/**
- * Row factory.
- */
-public class GridH2PlainRowFactory extends RowFactory {
-    /**
-     * @param v Value.
-     * @return Row.
-     */
-    public static Row create(Value v) {
-        return new RowKey(v);
-    }
-
-    /**
-     * @param data Values.
-     * @return Row.
-     */
-    public static Row 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 Row createRow(Value[] data, int memory) {
-        return create(data);
-    }
-
-    /**
-     * Single value row.
-     */
-    private static final class RowKey extends GridH2SearchRowAdapter {
-        /** */
-        private Value key;
-
-        /**
-         * @param key Key.
-         */
-        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;
-        }
-
-        /** {@inheritDoc} */
-        @Override public boolean indexSearchRow() {
-            return true;
-        }
-
-        /** {@inheritDoc} */
-        @Override public String toString() {
-            return S.toString(RowKey.class, this);
-        }
-    }
-
-    /**
-     * Row of two values.
-     */
-    private static final class RowPair extends GridH2SearchRowAdapter  {
-        /** */
-        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;
-            }
-        }
-
-        /** {@inheritDoc} */
-        @Override public boolean indexSearchRow() {
-            return true;
-        }
-
-        /** {@inheritDoc} */
-        @Override public String toString() {
-            return S.toString(RowPair.class, this);
-        }
-    }
-
-    /**
-     * Simple array based row.
-     */
-    private static final class RowSimple extends GridH2SearchRowAdapter {
-        /** */
-        @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 boolean indexSearchRow() {
-            return true;
-        }
-
-        /** {@inheritDoc} */
-        @Override public String toString() {
-            return S.toString(RowSimple.class, this);
-        }
-    }
-}
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
deleted file mode 100644
index 7fb896f..0000000
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Row.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 org.apache.ignite.internal.processors.cache.CacheObject;
-import org.apache.ignite.internal.processors.cache.KeyCacheObject;
-import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
-import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
-
-/**
- * Row with locking support needed for unique key conflicts resolution.
- */
-public abstract class GridH2Row extends GridH2SearchRowAdapter implements CacheDataRow {
-    /** Row. */
-    protected final CacheDataRow row;
-
-    /**
-     * @param row Row.
-     */
-    GridH2Row(CacheDataRow row) {
-        this.row = row;
-    }
-
-    /** {@inheritDoc} */
-    @Override public KeyCacheObject key() {
-        return row.key();
-    }
-
-    /** {@inheritDoc} */
-    @Override public void key(KeyCacheObject key) {
-        row.key(key);
-    }
-
-    /** {@inheritDoc} */
-    @Override public CacheObject value() {
-        return row.value();
-    }
-
-    /** {@inheritDoc} */
-    @Override public GridCacheVersion version() {
-        return row.version();
-    }
-
-    /** {@inheritDoc} */
-    @Override public int partition() {
-        return row.partition();
-    }
-
-    /** {@inheritDoc} */
-    @Override public long expireTime() {
-        return row.expireTime();
-    }
-
-    /** {@inheritDoc} */
-    @Override public long link() {
-        return row.link();
-    }
-
-    /** {@inheritDoc} */
-    @Override public void link(long link) {
-        row.link(link);
-    }
-
-    /** {@inheritDoc} */
-    @Override public int hash() {
-        return row.hash();
-    }
-
-    /** {@inheritDoc} */
-    @Override public int cacheId() {
-        return row.cacheId();
-    }
-
-    /** {@inheritDoc} */
-    @Override public long mvccCoordinatorVersion() {
-        return row.mvccCoordinatorVersion();
-    }
-
-    /** {@inheritDoc} */
-    @Override public long mvccCounter() {
-        return row.mvccCounter();
-    }
-
-    /** {@inheritDoc} */
-    @Override public int mvccOperationCounter() {
-        return row.mvccOperationCounter();
-    }
-
-    /** {@inheritDoc} */
-    public byte mvccTxState() {
-        return row.mvccTxState();
-    }
-
-    /** {@inheritDoc} */
-    @Override public long newMvccCoordinatorVersion() {
-        return row.newMvccCoordinatorVersion();
-    }
-
-    /** {@inheritDoc} */
-    @Override public long newMvccCounter() {
-        return row.newMvccCounter();
-    }
-
-    /** {@inheritDoc} */
-    @Override public int newMvccOperationCounter() {
-        return row.newMvccOperationCounter();
-    }
-
-    /** {@inheritDoc} */
-    @Override public byte newMvccTxState() {
-        return row.newMvccTxState();
-    }
-
-    /** {@inheritDoc} */
-    @Override public boolean indexSearchRow() {
-        return false;
-    }
-}
\ No newline at end of file
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 cc9a1e4..652c950 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
@@ -27,19 +27,14 @@ import org.apache.ignite.internal.processors.cache.GridCacheContextInfo;
 import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
 import org.apache.ignite.internal.processors.query.GridQueryProperty;
 import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor;
+import org.apache.ignite.internal.processors.query.QueryUtils;
 import org.apache.ignite.internal.processors.query.h2.H2TableDescriptor;
-import org.apache.ignite.internal.processors.query.h2.H2Utils;
 import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing;
 import org.h2.message.DbException;
-import org.h2.result.SearchRow;
 import org.h2.value.DataType;
 import org.h2.value.Value;
 import org.jetbrains.annotations.Nullable;
 
-import static org.apache.ignite.internal.processors.query.h2.opt.GridH2KeyValueRowOnheap.DEFAULT_COLUMNS_COUNT;
-import static org.apache.ignite.internal.processors.query.h2.opt.GridH2KeyValueRowOnheap.KEY_COL;
-import static org.apache.ignite.internal.processors.query.h2.opt.GridH2KeyValueRowOnheap.VAL_COL;
-
 /**
  * Row descriptor.
  */
@@ -95,11 +90,10 @@ public class GridH2RowDescriptor {
     /**
      * Update metadata of this row descriptor according to current state of type descriptor.
      */
-    @SuppressWarnings("WeakerAccess")
+    @SuppressWarnings({"WeakerAccess", "ToArrayCallWithZeroLengthArrayArgument"})
     public final void refreshMetadataFromTypeDescriptor() {
-        Map<String, Class<?>> allFields = new LinkedHashMap<>();
 
-        allFields.putAll(type.fields());
+        Map<String, Class<?>> allFields = new LinkedHashMap<>(type.fields());
 
         fields = allFields.keySet().toArray(new String[allFields.size()]);
 
@@ -123,10 +117,10 @@ public class GridH2RowDescriptor {
         List<String> fieldsList = Arrays.asList(fields);
 
         keyAliasColId = (type.keyFieldName() != null) ?
-            DEFAULT_COLUMNS_COUNT + fieldsList.indexOf(type.keyFieldAlias()) : COL_NOT_EXISTS;
+            QueryUtils.DEFAULT_COLUMNS_COUNT + fieldsList.indexOf(type.keyFieldAlias()) : COL_NOT_EXISTS;
 
         valAliasColId = (type.valueFieldName() != null) ?
-            DEFAULT_COLUMNS_COUNT + fieldsList.indexOf(type.valueFieldAlias()) : COL_NOT_EXISTS;
+            QueryUtils.DEFAULT_COLUMNS_COUNT + fieldsList.indexOf(type.valueFieldAlias()) : COL_NOT_EXISTS;
     }
 
     /**
@@ -167,21 +161,17 @@ public class GridH2RowDescriptor {
     }
 
     /**
-     * Creates new row.
+     * Create new row for update operation.
      *
      * @param dataRow Data row.
      * @return Row.
      * @throws IgniteCheckedException If failed.
      */
-    public GridH2Row createRow(CacheDataRow dataRow) throws IgniteCheckedException {
-        GridH2Row row;
+    public H2CacheRow createRow(CacheDataRow dataRow) throws IgniteCheckedException {
+        H2CacheRow row;
 
         try {
-            if (dataRow.value() == null) { // Only can happen for remove operation, can create simple search row.
-                row = new GridH2KeyRowOnheap(dataRow, H2Utils.wrap(indexing().objectContext(), dataRow.key(), keyType));
-            }
-            else
-                row = new GridH2KeyValueRowOnheap(this, dataRow);
+            row = new H2CacheRow(this, dataRow);
         }
         catch (ClassCastException e) {
             throw new IgniteCheckedException("Failed to convert key to SQL type. " +
@@ -275,7 +265,7 @@ public class GridH2RowDescriptor {
      */
     public boolean isKeyColumn(int colId) {
         assert colId >= 0;
-        return colId == KEY_COL || colId == keyAliasColId;
+        return colId == QueryUtils.KEY_COL || colId == keyAliasColId;
     }
 
     /**
@@ -297,7 +287,7 @@ public class GridH2RowDescriptor {
      */
     public boolean isValueColumn(int colId) {
         assert colId >= 0;
-        return colId == VAL_COL || colId == valAliasColId;
+        return colId == QueryUtils.VAL_COL || colId == valAliasColId;
     }
 
     /**
@@ -322,7 +312,7 @@ public class GridH2RowDescriptor {
     public boolean isKeyValueOrVersionColumn(int colId) {
         assert colId >= 0;
 
-        if (colId < DEFAULT_COLUMNS_COUNT)
+        if (colId < QueryUtils.DEFAULT_COLUMNS_COUNT)
             return true;
 
         if (colId == keyAliasColId)
@@ -341,14 +331,15 @@ public class GridH2RowDescriptor {
      * @param mask Index Condition to check.
      * @return Result.
      */
+    @SuppressWarnings("IfMayBeConditional")
     public boolean checkKeyIndexCondition(int masks[], int mask) {
         assert masks != null;
         assert masks.length > 0;
 
         if (keyAliasColId < 0)
-            return (masks[KEY_COL] & mask) != 0;
+            return (masks[QueryUtils.KEY_COL] & mask) != 0;
         else
-            return (masks[KEY_COL] & mask) != 0 || (masks[keyAliasColId] & mask) != 0;
+            return (masks[QueryUtils.KEY_COL] & mask) != 0 || (masks[keyAliasColId] & mask) != 0;
     }
 
     /**
@@ -358,7 +349,7 @@ public class GridH2RowDescriptor {
      * @param row Source row.
      * @return Result.
      */
-    public SearchRow prepareProxyIndexRow(SearchRow row) {
+    public org.h2.result.SearchRow prepareProxyIndexRow(org.h2.result.SearchRow row) {
         if (row == null)
             return null;
 
@@ -367,10 +358,10 @@ public class GridH2RowDescriptor {
         for (int idx = 0; idx < data.length; idx++)
             data[idx] = row.getValue(idx);
 
-        copyAliasColumnData(data, KEY_COL, keyAliasColId);
-        copyAliasColumnData(data, VAL_COL, valAliasColId);
+        copyAliasColumnData(data, QueryUtils.KEY_COL, keyAliasColId);
+        copyAliasColumnData(data, QueryUtils.VAL_COL, valAliasColId);
 
-        return GridH2PlainRowFactory.create(data);
+        return H2PlainRowFactory.create(data);
     }
 
     /**
@@ -404,16 +395,16 @@ public class GridH2RowDescriptor {
      */
     public int getAlternativeColumnId(int colId) {
         if (keyAliasColId > 0) {
-            if (colId == KEY_COL)
+            if (colId == QueryUtils.KEY_COL)
                 return keyAliasColId;
             else if (colId == keyAliasColId)
-                return KEY_COL;
+                return QueryUtils.KEY_COL;
         }
         if (valAliasColId > 0) {
-            if (colId == VAL_COL)
+            if (colId == QueryUtils.VAL_COL)
                 return valAliasColId;
             else if (colId == valAliasColId)
-                return VAL_COL;
+                return QueryUtils.VAL_COL;
         }
 
         return colId;
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SystemIndexFactory.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SystemIndexFactory.java
deleted file mode 100644
index fa1e5cb..0000000
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SystemIndexFactory.java
+++ /dev/null
@@ -1,36 +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.util.ArrayList;
-import org.apache.ignite.internal.processors.query.h2.database.H2TreeIndexBase;
-import org.h2.index.Index;
-
-/**
- * Factory for system table indexes.
- */
-public interface GridH2SystemIndexFactory {
-    /**
-     * Create list of indexes. First must be primary key, after that all unique indexes and only then non-unique
-     * indexes. All indexes must be subtypes of {@link H2TreeIndexBase}.
-     *
-     * @param tbl Table to create indexes for.
-     * @return List of indexes.
-     */
-    ArrayList<Index> createSystemIndexes(GridH2Table tbl);
-}
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 40675d8..d6b4134 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
@@ -35,8 +35,9 @@ import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
 import org.apache.ignite.internal.processors.cache.query.QueryTable;
 import org.apache.ignite.internal.processors.query.IgniteSQLException;
 import org.apache.ignite.internal.processors.query.QueryField;
+import org.apache.ignite.internal.processors.query.QueryUtils;
+import org.apache.ignite.internal.processors.query.h2.H2TableDescriptor;
 import org.apache.ignite.internal.processors.query.h2.IndexRebuildPartialClosure;
-import org.apache.ignite.internal.processors.query.h2.database.H2RowFactory;
 import org.apache.ignite.internal.processors.query.h2.database.H2TreeIndex;
 import org.apache.ignite.internal.processors.query.h2.database.H2TreeIndexBase;
 import org.apache.ignite.internal.processors.query.h2.twostep.GridMapQueryExecutor;
@@ -61,7 +62,6 @@ import org.h2.value.DataType;
 import org.jetbrains.annotations.Nullable;
 
 import static org.apache.ignite.cache.CacheMode.PARTITIONED;
-import static org.apache.ignite.internal.processors.query.h2.opt.GridH2KeyValueRowOnheap.DEFAULT_COLUMNS_COUNT;
 
 /**
  * H2 Table implementation.
@@ -107,9 +107,6 @@ public class GridH2Table extends TableBase {
     private final LongAdder size = new LongAdder();
 
     /** */
-    private final H2RowFactory rowFactory;
-
-    /** */
     private volatile boolean rebuildFromHashInProgress;
 
     /** Identifier. */
@@ -129,16 +126,19 @@ public class GridH2Table extends TableBase {
      *
      * @param createTblData Table description.
      * @param desc Row descriptor.
-     * @param rowFactory Row factory.
-     * @param idxsFactory Indexes factory.
+     * @param tblDesc Indexes factory.
      * @param cacheInfo Cache context info.
      */
     @SuppressWarnings("ConstantConditions")
-    public GridH2Table(CreateTableData createTblData, GridH2RowDescriptor desc, H2RowFactory rowFactory,
-        GridH2SystemIndexFactory idxsFactory, GridCacheContextInfo cacheInfo) {
+    public GridH2Table(
+        CreateTableData createTblData,
+        GridH2RowDescriptor desc,
+        H2TableDescriptor tblDesc,
+        GridCacheContextInfo cacheInfo
+    ) {
         super(createTblData);
 
-        assert idxsFactory != null;
+        assert tblDesc != null;
 
         this.desc = desc;
         this.cacheInfo = cacheInfo;
@@ -146,14 +146,12 @@ public class GridH2Table extends TableBase {
         affKeyCol = calculateAffinityKeyColumn();
         affKeyColIsKey = affKeyCol != null && desc.isKeyColumn(affKeyCol.column.getColumnId());
 
-        this.rowFactory = rowFactory;
-
         identifier = new QueryTable(getSchema().getName(), getName());
 
         identifierStr = identifier.schema() + "." + identifier.table();
 
         // Indexes must be created in the end when everything is ready.
-        idxs = idxsFactory.createSystemIndexes(this);
+        idxs = tblDesc.createSystemIndexes(this);
 
         assert idxs != null;
 
@@ -197,7 +195,7 @@ public class GridH2Table extends TableBase {
 
         // If explicit affinity key field is not set, then use _KEY.
         if (affKeyFieldName == null)
-            return indexColumn(GridH2KeyValueRowOnheap.KEY_COL, SortOrder.ASCENDING);
+            return indexColumn(QueryUtils.KEY_COL, SortOrder.ASCENDING);
 
         // If explicit affinity key field is set, but is not found in the table, do not use anything.
         if (!doesColumnExist(affKeyFieldName))
@@ -207,7 +205,7 @@ public class GridH2Table extends TableBase {
 
         // If affinity key column is either _KEY or it's alias (QueryEntity.keyFieldName), normalize it to _KEY.
         if (desc.isKeyColumn(colId))
-            return indexColumn(GridH2KeyValueRowOnheap.KEY_COL, SortOrder.ASCENDING);
+            return indexColumn(QueryUtils.KEY_COL, SortOrder.ASCENDING);
 
         // Otherwise use column as is.
         return indexColumn(colId, SortOrder.ASCENDING);
@@ -550,8 +548,8 @@ public class GridH2Table extends TableBase {
     public void update(CacheDataRow row, @Nullable CacheDataRow prevRow, boolean prevRowAvailable) throws IgniteCheckedException {
         assert desc != null;
 
-        GridH2KeyValueRowOnheap row0 = (GridH2KeyValueRowOnheap)desc.createRow(row);
-        GridH2KeyValueRowOnheap prevRow0 = prevRow != null ? (GridH2KeyValueRowOnheap)desc.createRow(prevRow) :
+        H2CacheRow row0 = (H2CacheRow)desc.createRow(row);
+        H2CacheRow prevRow0 = prevRow != null ? (H2CacheRow)desc.createRow(prevRow) :
             null;
 
         row0.prepareValuesCache();
@@ -570,7 +568,7 @@ public class GridH2Table extends TableBase {
                 if (prevRowAvailable)
                     replaced = pk().putx(row0);
                 else {
-                    prevRow0 = (GridH2KeyValueRowOnheap)pk().put(row0);
+                    prevRow0 = (H2CacheRow)pk().put(row0);
 
                     replaced = prevRow0 != null;
                 }
@@ -610,7 +608,7 @@ public class GridH2Table extends TableBase {
      * @throws IgniteCheckedException If failed.
      */
     public boolean remove(CacheDataRow row) throws IgniteCheckedException {
-        GridH2Row row0 = desc.createRow(row);
+        H2CacheRow row0 = desc.createRow(row);
 
         lock(false);
 
@@ -648,7 +646,7 @@ public class GridH2Table extends TableBase {
      * @param row Row to add to index.
      * @param prevRow Previous row state, if any.
      */
-    private void addToIndex(GridH2IndexBase idx, GridH2Row row, GridH2Row prevRow) {
+    private void addToIndex(GridH2IndexBase idx, H2CacheRow row, H2CacheRow prevRow) {
         boolean replaced = idx.putx(row);
 
         // Row was not replaced, need to remove manually.
@@ -974,13 +972,6 @@ public class GridH2Table extends TableBase {
     }
 
     /**
-     * @return Data store.
-     */
-    public H2RowFactory rowFactory() {
-        return rowFactory;
-    }
-
-    /**
      * Creates proxy index for given target index.
      * Proxy index refers to alternative key and val columns.
      *
@@ -1112,7 +1103,7 @@ public class GridH2Table extends TableBase {
                 size --;
             }
 
-            assert size > DEFAULT_COLUMNS_COUNT;
+            assert size > QueryUtils.DEFAULT_COLUMNS_COUNT;
 
             Column[] newCols = new Column[size];
 
@@ -1168,9 +1159,9 @@ public class GridH2Table extends TableBase {
             StackTraceElement elem = elems[2];
 
             if (F.eq(elem.getClassName(), Insert.class.getName()) && F.eq(elem.getMethodName(), "prepare")) {
-                Column[] columns0 = new Column[safeColumns0.length - DEFAULT_COLUMNS_COUNT];
+                Column[] columns0 = new Column[safeColumns0.length - QueryUtils.DEFAULT_COLUMNS_COUNT];
 
-                System.arraycopy(safeColumns0, DEFAULT_COLUMNS_COUNT, columns0, 0, columns0.length);
+                System.arraycopy(safeColumns0, QueryUtils.DEFAULT_COLUMNS_COUNT, columns0, 0, columns0.length);
 
                 return columns0;
             }
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2KeyValueRowOnheap.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/H2CacheRow.java
similarity index 59%
rename from modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2KeyValueRowOnheap.java
rename to modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/H2CacheRow.java
index 3aec288..4670c02 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2KeyValueRowOnheap.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/H2CacheRow.java
@@ -19,8 +19,12 @@ package org.apache.ignite.internal.processors.query.h2.opt;
 
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteException;
+import org.apache.ignite.internal.processors.cache.CacheObject;
+import org.apache.ignite.internal.processors.cache.KeyCacheObject;
 import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
+import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
 import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor;
+import org.apache.ignite.internal.processors.query.QueryUtils;
 import org.apache.ignite.internal.processors.query.h2.H2Utils;
 import org.apache.ignite.internal.util.typedef.internal.SB;
 import org.h2.value.Value;
@@ -29,18 +33,12 @@ import org.h2.value.ValueNull;
 /**
  * Table row implementation based on {@link GridQueryTypeDescriptor}.
  */
-public class GridH2KeyValueRowOnheap extends GridH2Row {
-    /** */
-    public static final int DEFAULT_COLUMNS_COUNT = 2;
-
-    /** Key column. */
-    public static final int KEY_COL = 0;
+public class H2CacheRow extends H2Row implements CacheDataRow {
+    /** H2 row descriptor. */
+    private final GridH2RowDescriptor desc;
 
-    /** Value column. */
-    public static final int VAL_COL = 1;
-
-    /** */
-    protected final GridH2RowDescriptor desc;
+    /** Cache row. */
+    private final CacheDataRow row;
 
     /** */
     private Value[] valCache;
@@ -51,29 +49,32 @@ public class GridH2KeyValueRowOnheap extends GridH2Row {
      * @param desc Row descriptor.
      * @param row Row.
      */
-    public GridH2KeyValueRowOnheap(GridH2RowDescriptor desc, CacheDataRow row) {
-        super(row);
-
+    public H2CacheRow(GridH2RowDescriptor desc, CacheDataRow row) {
         this.desc = desc;
-    }
-
-    /** {@inheritDoc} */
-    @Override public boolean indexSearchRow() {
-        return false;
+        this.row = row;
     }
 
     /** {@inheritDoc} */
     @Override public int getColumnCount() {
-        return DEFAULT_COLUMNS_COUNT + desc.fieldsCount();
+        if (removedRow())
+            return 1;
+
+        return QueryUtils.DEFAULT_COLUMNS_COUNT + desc.fieldsCount();
     }
 
     /** {@inheritDoc} */
     @Override public Value getValue(int col) {
+        if (removedRow()) {
+            assert col == 0 : col;
+
+            return keyWrapped();
+        }
+
         switch (col) {
-            case KEY_COL:
+            case QueryUtils.KEY_COL:
                 return keyWrapped();
 
-            case VAL_COL:
+            case QueryUtils.VAL_COL:
                 return valueWrapped();
 
             default:
@@ -82,7 +83,7 @@ public class GridH2KeyValueRowOnheap extends GridH2Row {
                 else if (desc.isValueAliasColumn(col))
                     return valueWrapped();
 
-                return getValue0(col - DEFAULT_COLUMNS_COUNT);
+                return getValue0(col - QueryUtils.DEFAULT_COLUMNS_COUNT);
         }
     }
 
@@ -100,10 +101,7 @@ public class GridH2KeyValueRowOnheap extends GridH2Row {
 
         Object res = desc.columnValue(row.key(), row.value(), col);
 
-        if (res == null)
-            v = ValueNull.INSTANCE;
-        else
-            v = wrap(res, desc.fieldType(col));
+        v = res == null ? ValueNull.INSTANCE : wrap(res, desc.fieldType(col));
 
         setCached(col, v);
 
@@ -114,14 +112,14 @@ public class GridH2KeyValueRowOnheap extends GridH2Row {
      * Prepare values cache.
      */
     public void prepareValuesCache() {
-        this.valCache = new Value[desc.fieldsCount()];
+        valCache = new Value[desc.fieldsCount()];
     }
 
     /**
      * Clear values cache.
      */
     public void clearValuesCache() {
-        this.valCache = null;
+        valCache = null;
     }
 
     /**
@@ -175,35 +173,106 @@ public class GridH2KeyValueRowOnheap extends GridH2Row {
         }
     }
 
+    /**
+     * @return {@code True} if this is removed row (doesn't have value).
+     */
+    private boolean removedRow() {
+        return row.value() == null;
+    }
+
     /** {@inheritDoc} */
-    @Override public String toString() {
-        SB sb = new SB("Row@");
+    @Override public KeyCacheObject key() {
+        return row.key();
+    }
 
-        sb.a(Integer.toHexString(System.identityHashCode(this)));
+    /** {@inheritDoc} */
+    @Override public void key(KeyCacheObject key) {
+        row.key(key);
+    }
 
-        Value v = keyWrapped();
-        sb.a("[ key: ").a(v == null ? "nil" : v.getString());
+    /** {@inheritDoc} */
+    @Override public CacheObject value() {
+        return row.value();
+    }
 
-        v = valueWrapped();
-        sb.a(", val: ").a(v == null ? "nil" : v.getString());
+    /** {@inheritDoc} */
+    @Override public GridCacheVersion version() {
+        return row.version();
+    }
 
-        sb.a(" ][ ");
+    /** {@inheritDoc} */
+    @Override public int partition() {
+        return row.partition();
+    }
 
-        if (v != null) {
-            for (int i = DEFAULT_COLUMNS_COUNT, cnt = getColumnCount(); i < cnt; i++) {
-                v = getValue(i);
+    /** {@inheritDoc} */
+    @Override public long expireTime() {
+        return row.expireTime();
+    }
 
-                if (i != DEFAULT_COLUMNS_COUNT)
-                    sb.a(", ");
+    /** {@inheritDoc} */
+    @Override public long link() {
+        return row.link();
+    }
 
-                if (!desc.isKeyValueOrVersionColumn(i))
-                    sb.a(v == null ? "nil" : v.getString());
-            }
-        }
+    /** {@inheritDoc} */
+    @Override public void link(long link) {
+        row.link(link);
+    }
 
-        sb.a(" ]");
+    /** {@inheritDoc} */
+    @Override public int hash() {
+        return row.hash();
+    }
 
-        return sb.toString();
+    /** {@inheritDoc} */
+    @Override public int cacheId() {
+        return row.cacheId();
+    }
+
+    /** {@inheritDoc} */
+    @Override public long mvccCoordinatorVersion() {
+        return row.mvccCoordinatorVersion();
+    }
+
+    /** {@inheritDoc} */
+    @Override public long mvccCounter() {
+        return row.mvccCounter();
+    }
+
+    /** {@inheritDoc} */
+    @Override public int mvccOperationCounter() {
+        return row.mvccOperationCounter();
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte mvccTxState() {
+        return row.mvccTxState();
+    }
+
+    /** {@inheritDoc} */
+    @Override public long newMvccCoordinatorVersion() {
+        return row.newMvccCoordinatorVersion();
+    }
+
+    /** {@inheritDoc} */
+    @Override public long newMvccCounter() {
+        return row.newMvccCounter();
+    }
+
+    /** {@inheritDoc} */
+    @Override public int newMvccOperationCounter() {
+        return row.newMvccOperationCounter();
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte newMvccTxState() {
+        return row.newMvccTxState();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean indexSearchRow() {
+        return false;
     }
 
     /** {@inheritDoc} */
@@ -217,7 +286,7 @@ public class GridH2KeyValueRowOnheap extends GridH2Row {
     }
 
     /** {@inheritDoc} */
-    @Override public final int hashCode() {
+    @Override public int hashCode() {
         throw new UnsupportedOperationException();
     }
 
@@ -230,4 +299,35 @@ public class GridH2KeyValueRowOnheap extends GridH2Row {
     @Override public int headerSize() {
         throw new UnsupportedOperationException();
     }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        SB sb = new SB("Row@");
+
+        sb.a(Integer.toHexString(System.identityHashCode(this)));
+
+        Value v = keyWrapped();
+        sb.a("[ key: ").a(v == null ? "nil" : v.getString());
+
+        v = valueWrapped();
+        sb.a(", val: ").a(v == null ? "nil" : v.getString());
+
+        sb.a(" ][ ");
+
+        if (v != null) {
+            for (int i = QueryUtils.DEFAULT_COLUMNS_COUNT, cnt = getColumnCount(); i < cnt; i++) {
+                v = getValue(i);
+
+                if (i != QueryUtils.DEFAULT_COLUMNS_COUNT)
+                    sb.a(", ");
+
+                if (!desc.isKeyValueOrVersionColumn(i))
+                    sb.a(v == null ? "nil" : v.getString());
+            }
+        }
+
+        sb.a(" ]");
+
+        return sb.toString();
+    }
 }
\ No newline at end of file
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2KeyRowOnheap.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/H2PlainRow.java
similarity index 60%
copy from modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2KeyRowOnheap.java
copy to modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/H2PlainRow.java
index e152054..63700e4 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2KeyRowOnheap.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/H2PlainRow.java
@@ -17,58 +17,55 @@
 
 package org.apache.ignite.internal.processors.query.h2.opt;
 
-import org.apache.ignite.IgniteCheckedException;
-import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
+import org.apache.ignite.internal.util.tostring.GridToStringInclude;
+import org.apache.ignite.internal.util.typedef.internal.S;
 import org.h2.value.Value;
 
 /**
- * Heap-based key-only row for remove operations.
+ * Simple array based row.
  */
-public class GridH2KeyRowOnheap extends GridH2Row {
+public class H2PlainRow extends H2Row {
     /** */
-    private Value key;
+    @GridToStringInclude
+    private Value[] vals;
 
     /**
-     * @param row Row.
-     * @param key Key.
+     * @param vals Values.
      */
-    public GridH2KeyRowOnheap(CacheDataRow row, Value key) {
-        super(row);
+    @SuppressWarnings("AssignmentOrReturnOfFieldWithMutableType")
+    public H2PlainRow(Value[] vals) {
+        this.vals = vals;
+    }
 
-        this.key = key;
+    /**
+     * @param len Length.
+     */
+    public H2PlainRow(int len) {
+        vals = new Value[len];
     }
 
     /** {@inheritDoc} */
     @Override public int getColumnCount() {
-        return 1;
+        return vals.length;
     }
 
     /** {@inheritDoc} */
     @Override public Value getValue(int idx) {
-        assert idx == 0 : idx;
-
-        return key;
+        return vals[idx];
     }
 
     /** {@inheritDoc} */
     @Override public void setValue(int idx, Value v) {
-        assert idx == 0 : idx;
-
-        key = v;
-    }
-
-    /** {@inheritDoc} */
-    @Override public long expireTime() {
-        return 0;
+        vals[idx] = v;
     }
 
     /** {@inheritDoc} */
-    @Override public int size() throws IgniteCheckedException {
-        throw new UnsupportedOperationException();
+    @Override public boolean indexSearchRow() {
+        return true;
     }
 
     /** {@inheritDoc} */
-    @Override public int headerSize() {
-        throw new UnsupportedOperationException();
+    @Override public String toString() {
+        return S.toString(H2PlainRow.class, this);
     }
 }
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SearchRow.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/H2PlainRowFactory.java
similarity index 51%
rename from modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SearchRow.java
rename to modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/H2PlainRowFactory.java
index 5de6216..22c1d4f 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SearchRow.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/H2PlainRowFactory.java
@@ -17,16 +17,44 @@
 
 package org.apache.ignite.internal.processors.query.h2.opt;
 
-import org.apache.ignite.internal.processors.cache.mvcc.MvccVersionAware;
-import org.apache.ignite.internal.processors.query.h2.database.H2Tree;
 import org.h2.result.Row;
+import org.h2.result.RowFactory;
+import org.h2.value.Value;
 
 /**
- *
+ * Plain row factory.
  */
-public interface GridH2SearchRow extends Row, MvccVersionAware {
+public class H2PlainRowFactory extends RowFactory {
+    /**
+     * @param v Value.
+     * @return Row.
+     */
+    public static Row create(Value v) {
+        return new H2PlainRowSingle(v);
+    }
+
     /**
-     * @return {@code True} for rows used for index search (as opposed to rows stored in {@link H2Tree}.
+     * @param data Values.
+     * @return Row.
      */
-    public boolean indexSearchRow();
+    public static Row create(Value... data) {
+        switch (data.length) {
+            case 0:
+                throw new IllegalStateException("Zero columns row.");
+
+            case 1:
+                return new H2PlainRowSingle(data[0]);
+
+            case 2:
+                return new H2PlainRowPair(data[0], data[1]);
+
+            default:
+                return new H2PlainRow(data);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public Row createRow(Value[] data, int memory) {
+        return create(data);
+    }
 }
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2KeyRowOnheap.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/H2PlainRowPair.java
similarity index 60%
copy from modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2KeyRowOnheap.java
copy to modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/H2PlainRowPair.java
index e152054..360a25f 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2KeyRowOnheap.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/H2PlainRowPair.java
@@ -17,58 +17,56 @@
 
 package org.apache.ignite.internal.processors.query.h2.opt;
 
-import org.apache.ignite.IgniteCheckedException;
-import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
+import org.apache.ignite.internal.util.typedef.internal.S;
 import org.h2.value.Value;
 
 /**
- * Heap-based key-only row for remove operations.
+ * Row of two values.
  */
-public class GridH2KeyRowOnheap extends GridH2Row {
+public class H2PlainRowPair extends H2Row {
     /** */
-    private Value key;
+    private Value v1;
+
+    /** */
+    private Value v2;
 
     /**
-     * @param row Row.
-     * @param key Key.
+     * @param v1 First value.
+     * @param v2 Second value.
      */
-    public GridH2KeyRowOnheap(CacheDataRow row, Value key) {
-        super(row);
-
-        this.key = key;
+    public H2PlainRowPair(Value v1, Value v2) {
+        this.v1 = v1;
+        this.v2 = v2;
     }
 
     /** {@inheritDoc} */
     @Override public int getColumnCount() {
-        return 1;
+        return 2;
     }
 
     /** {@inheritDoc} */
     @Override public Value getValue(int idx) {
-        assert idx == 0 : idx;
-
-        return key;
+        return idx == 0 ? v1 : v2;
     }
 
     /** {@inheritDoc} */
     @Override public void setValue(int idx, Value v) {
-        assert idx == 0 : idx;
+        if (idx == 0)
+            v1 = v;
+        else {
+            assert idx == 1 : idx;
 
-        key = v;
-    }
-
-    /** {@inheritDoc} */
-    @Override public long expireTime() {
-        return 0;
+            v2 = v;
+        }
     }
 
     /** {@inheritDoc} */
-    @Override public int size() throws IgniteCheckedException {
-        throw new UnsupportedOperationException();
+    @Override public boolean indexSearchRow() {
+        return true;
     }
 
     /** {@inheritDoc} */
-    @Override public int headerSize() {
-        throw new UnsupportedOperationException();
+    @Override public String toString() {
+        return S.toString(H2PlainRowPair.class, this);
     }
 }
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2KeyRowOnheap.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/H2PlainRowSingle.java
similarity index 64%
rename from modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2KeyRowOnheap.java
rename to modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/H2PlainRowSingle.java
index e152054..70cc629 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2KeyRowOnheap.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/H2PlainRowSingle.java
@@ -17,25 +17,21 @@
 
 package org.apache.ignite.internal.processors.query.h2.opt;
 
-import org.apache.ignite.IgniteCheckedException;
-import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
+import org.apache.ignite.internal.util.typedef.internal.S;
 import org.h2.value.Value;
 
 /**
- * Heap-based key-only row for remove operations.
+ * Single value row.
  */
-public class GridH2KeyRowOnheap extends GridH2Row {
+public class H2PlainRowSingle extends H2Row {
     /** */
-    private Value key;
+    private Value v;
 
     /**
-     * @param row Row.
-     * @param key Key.
+     * @param v Value.
      */
-    public GridH2KeyRowOnheap(CacheDataRow row, Value key) {
-        super(row);
-
-        this.key = key;
+    public H2PlainRowSingle(Value v) {
+        this.v = v;
     }
 
     /** {@inheritDoc} */
@@ -47,28 +43,23 @@ public class GridH2KeyRowOnheap extends GridH2Row {
     @Override public Value getValue(int idx) {
         assert idx == 0 : idx;
 
-        return key;
+        return v;
     }
 
     /** {@inheritDoc} */
     @Override public void setValue(int idx, Value v) {
         assert idx == 0 : idx;
 
-        key = v;
-    }
-
-    /** {@inheritDoc} */
-    @Override public long expireTime() {
-        return 0;
+        this.v = v;
     }
 
     /** {@inheritDoc} */
-    @Override public int size() throws IgniteCheckedException {
-        throw new UnsupportedOperationException();
+    @Override public boolean indexSearchRow() {
+        return true;
     }
 
     /** {@inheritDoc} */
-    @Override public int headerSize() {
-        throw new UnsupportedOperationException();
+    @Override public String toString() {
+        return S.toString(H2PlainRowSingle.class, this);
     }
 }
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SearchRowAdapter.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/H2Row.java
similarity index 88%
rename from modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SearchRowAdapter.java
rename to modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/H2Row.java
index 2e512b1..83543a5 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SearchRowAdapter.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/H2Row.java
@@ -17,7 +17,9 @@
 
 package org.apache.ignite.internal.processors.query.h2.opt;
 
+import org.apache.ignite.internal.processors.cache.mvcc.MvccVersionAware;
 import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxState;
+import org.apache.ignite.internal.processors.query.h2.database.H2Tree;
 import org.h2.result.Row;
 import org.h2.result.SearchRow;
 import org.h2.store.Data;
@@ -30,7 +32,7 @@ import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_OP
 /**
  * Dummy H2 search row adadpter.
  */
-public abstract class GridH2SearchRowAdapter implements GridH2SearchRow {
+public abstract class H2Row implements Row, MvccVersionAware {
     /** {@inheritDoc} */
     @Override public void setKeyAndVersion(SearchRow old) {
         throw new UnsupportedOperationException();
@@ -125,4 +127,16 @@ public abstract class GridH2SearchRowAdapter implements GridH2SearchRow {
     @Override public byte mvccTxState() {
         return TxState.NA;
     }
+
+    /**
+     * @return Expire time.
+     */
+    public long expireTime() {
+        return 0;
+    }
+
+    /**
+     * @return {@code True} for rows used for index search (as opposed to rows stored in {@link H2Tree}.
+     */
+    public abstract boolean indexSearchRow();
 }
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/join/CursorIteratorWrapper.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/join/CursorIteratorWrapper.java
index 5a174e8..ddac22e 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/join/CursorIteratorWrapper.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/join/CursorIteratorWrapper.java
@@ -18,19 +18,19 @@
 package org.apache.ignite.internal.processors.query.h2.opt.join;
 
 import org.apache.ignite.internal.processors.query.h2.H2Cursor;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
+import org.apache.ignite.internal.processors.query.h2.opt.H2Row;
 
 import java.util.Iterator;
 
 /**
  *
  */
-public final class CursorIteratorWrapper implements Iterator<GridH2Row> {
+public final class CursorIteratorWrapper implements Iterator<H2Row> {
     /** */
     private final H2Cursor cursor;
 
     /** Next element. */
-    private GridH2Row next;
+    private H2Row next;
 
     /**
      * @param cursor Cursor.
@@ -41,7 +41,7 @@ public final class CursorIteratorWrapper implements Iterator<GridH2Row> {
         this.cursor = cursor;
 
         if (cursor.next())
-            next = (GridH2Row)cursor.get();
+            next = (H2Row)cursor.get();
     }
 
     /** {@inheritDoc} */
@@ -50,11 +50,11 @@ public final class CursorIteratorWrapper implements Iterator<GridH2Row> {
     }
 
     /** {@inheritDoc} */
-    @Override public GridH2Row next() {
-        GridH2Row res = next;
+    @Override public H2Row next() {
+        H2Row res = next;
 
         if (cursor.next())
-            next = (GridH2Row)cursor.get();
+            next = (H2Row)cursor.get();
         else
             next = null;
 
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/join/DistributedLookupBatch.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/join/DistributedLookupBatch.java
index 02d2e44..5411a4a 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/join/DistributedLookupBatch.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/join/DistributedLookupBatch.java
@@ -19,6 +19,7 @@ package org.apache.ignite.internal.processors.query.h2.opt.join;
 
 import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.query.QueryUtils;
 import org.apache.ignite.internal.processors.query.h2.H2Utils;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2IndexBase;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2QueryContext;
@@ -44,7 +45,6 @@ import java.util.UUID;
 import java.util.concurrent.Future;
 
 import static org.apache.ignite.internal.processors.query.h2.opt.join.DistributedJoinMode.LOCAL_ONLY;
-import static org.apache.ignite.internal.processors.query.h2.opt.GridH2KeyValueRowOnheap.KEY_COL;
 import static org.apache.ignite.internal.processors.query.h2.opt.GridH2RowDescriptor.COL_NOT_EXISTS;
 import static org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2RowRangeBounds.rangeBounds;
 
@@ -119,8 +119,8 @@ public class DistributedLookupBatch implements IndexLookupBatch {
             return null;
 
         // Try to extract affinity key from primary key.
-        Value pkFirst = firstRow.getValue(KEY_COL);
-        Value pkLast = lastRow.getValue(KEY_COL);
+        Value pkFirst = firstRow.getValue(QueryUtils.KEY_COL);
+        Value pkLast = lastRow.getValue(QueryUtils.KEY_COL);
 
         if (pkFirst == ValueNull.INSTANCE || pkLast == ValueNull.INSTANCE)
             return GridH2IndexBase.EXPLICIT_NULL;
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/join/RangeSource.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/join/RangeSource.java
index 8cce83d..405478e 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/join/RangeSource.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/join/RangeSource.java
@@ -17,12 +17,10 @@
 
 package org.apache.ignite.internal.processors.query.h2.opt.join;
 
-import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
 import org.apache.ignite.internal.processors.query.h2.H2Utils;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2IndexBase;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2SearchRow;
+import org.apache.ignite.internal.processors.query.h2.opt.H2Row;
 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;
@@ -50,10 +48,10 @@ public class RangeSource {
     private final int segment;
 
     /** */
-    private final BPlusTree.TreeRowClosure<GridH2SearchRow, GridH2Row> filter;
+    private final BPlusTree.TreeRowClosure<H2Row, H2Row> filter;
 
     /** Iterator. */
-    private Iterator<GridH2Row> iter = emptyIterator();
+    private Iterator<H2Row> iter = emptyIterator();
 
     /**
      * @param bounds Bounds.
@@ -64,7 +62,7 @@ public class RangeSource {
         GridH2IndexBase idx,
         Iterable<GridH2RowRangeBounds> bounds,
         int segment,
-        BPlusTree.TreeRowClosure<GridH2SearchRow, GridH2Row> filter
+        BPlusTree.TreeRowClosure<H2Row, H2Row> filter
     ) {
         this.idx = idx;
         this.segment = segment;
@@ -76,7 +74,7 @@ public class RangeSource {
     /**
      * @return {@code true} If there are more rows in this source.
      */
-    public boolean hasMoreRows() throws IgniteCheckedException {
+    public boolean hasMoreRows() {
         return boundsIter.hasNext() || iter.hasNext();
     }
 
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndexSorted.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndexSorted.java
index 9f5547a..880e305 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndexSorted.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndexSorted.java
@@ -33,7 +33,7 @@ import java.util.concurrent.locks.ReentrantLock;
 import org.apache.ignite.cluster.ClusterNode;
 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.GridH2PlainRowFactory;
+import org.apache.ignite.internal.processors.query.h2.opt.H2PlainRowFactory;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.h2.engine.Session;
@@ -370,7 +370,7 @@ public final class GridMergeIndexSorted extends GridMergeIndex {
             if (!iter.hasNext())
                 return false;
 
-            cur = GridH2PlainRowFactory.create(iter.next());
+            cur = H2PlainRowFactory.create(iter.next());
 
             return true;
         }
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 487d386..4e9d11a 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
@@ -27,7 +27,7 @@ import java.util.concurrent.atomic.AtomicInteger;
 import org.apache.ignite.cluster.ClusterNode;
 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.GridH2PlainRowFactory;
+import org.apache.ignite.internal.processors.query.h2.opt.H2PlainRowFactory;
 import org.h2.engine.Session;
 import org.h2.index.Cursor;
 import org.h2.index.IndexType;
@@ -139,7 +139,7 @@ public final class GridMergeIndexUnsorted extends GridMergeIndex {
             }
 
             @Override public Row next() {
-                return GridH2PlainRowFactory.create(iter.next());
+                return H2PlainRowFactory.create(iter.next());
             }
 
             @Override public void remove() {
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/visor/verify/ValidateIndexesClosure.java b/modules/indexing/src/main/java/org/apache/ignite/internal/visor/verify/ValidateIndexesClosure.java
index 9f2cda1..fc5f3fc 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/visor/verify/ValidateIndexesClosure.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/visor/verify/ValidateIndexesClosure.java
@@ -63,7 +63,7 @@ import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor;
 import org.apache.ignite.internal.processors.query.QueryTypeDescriptorImpl;
 import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing;
 import org.apache.ignite.internal.processors.query.h2.database.H2TreeIndexBase;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
+import org.apache.ignite.internal.processors.query.h2.opt.H2CacheRow;
 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.util.lang.GridIterator;
@@ -532,7 +532,7 @@ public class ValidateIndexesClosure implements IgniteCallable<VisorValidateIndex
 
                     GridH2RowDescriptor gridH2RowDesc = gridH2Tbl.rowDescriptor();
 
-                    GridH2Row h2Row = gridH2RowDesc.createRow(row);
+                    H2CacheRow h2Row = gridH2RowDesc.createRow(row);
 
                     ArrayList<Index> indexes = gridH2Tbl.getIndexes();
 
@@ -674,7 +674,7 @@ public class ValidateIndexesClosure implements IgniteCallable<VisorValidateIndex
                         CacheObjectUtils.unwrapBinaryIfNeeded(ctx.cacheObjectContext(), previousKey, true, true), e);
                 }
 
-                GridH2Row h2Row = (GridH2Row)cursor.get();
+                H2CacheRow h2Row = (H2CacheRow)cursor.get();
 
                 if (skipConditions) {
                     if (bothSkipConditions) {
@@ -697,7 +697,7 @@ public class ValidateIndexesClosure implements IgniteCallable<VisorValidateIndex
                     }
                 }
 
-                h2key = h2Row.key();
+                h2key = (KeyCacheObject)h2Row.key();
 
                 if (h2Row.link() != 0L) {
                     CacheDataRow cacheDataStoreRow = ctx.group().offheap().read(ctx, h2key);
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2RowCacheSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2RowCacheSelfTest.java
index edcfd24..9177c8c 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2RowCacheSelfTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2RowCacheSelfTest.java
@@ -33,8 +33,9 @@ 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.internal.IgniteEx;
+import org.apache.ignite.internal.processors.cache.KeyCacheObject;
 import org.apache.ignite.internal.processors.query.h2.H2RowCache;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2KeyValueRowOnheap;
+import org.apache.ignite.internal.processors.query.h2.opt.H2CacheRow;
 import org.apache.ignite.testframework.GridTestUtils;
 import org.jsr166.ConcurrentLinkedHashMap;
 import org.junit.Test;
@@ -362,12 +363,14 @@ public class H2RowCacheSelfTest extends AbstractIndexingCommonTest {
         grid().cache(cacheName)
             .query(new SqlQuery(Value.class, "_key = " + key)).getAll().size();
 
-        ConcurrentLinkedHashMap<Long, GridH2KeyValueRowOnheap> rowsMap = GridTestUtils.getFieldValue(rowCache, "rows");
+        ConcurrentLinkedHashMap<Long, H2CacheRow> rowsMap = GridTestUtils.getFieldValue(rowCache, "rows");
 
-        for (Map.Entry<Long, GridH2KeyValueRowOnheap> e : rowsMap.entrySet()) {
-            GridH2KeyValueRowOnheap val = e.getValue();
+        for (Map.Entry<Long, H2CacheRow> e : rowsMap.entrySet()) {
+            H2CacheRow val = e.getValue();
 
-            if ((Integer)val.key().value(null, false) == key)
+            KeyCacheObject rowKey = val.key();
+
+            if ((Integer)rowKey.value(null, false) == key)
                 return e.getKey();
         }