Inline offheap indexes (simple types + String).

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

Branch: refs/heads/ignite-3477
Commit: 8f836cb0efbda3805672788be911131d46dde9b6
Parents: 7663c22
Author: Konstantin Dudkov <kdud...@ya.ru>
Authored: Thu Feb 16 14:18:05 2017 +0300
Committer: Konstantin Dudkov <kdud...@ya.ru>
Committed: Thu Feb 16 14:25:32 2017 +0300

----------------------------------------------------------------------
 .../benchmarks/jmh/tree/BPlusTreeBenchmark.java |  13 +-
 .../apache/ignite/IgniteSystemProperties.java   |   7 +
 .../org/apache/ignite/cache/QueryIndex.java     |  21 ++
 .../configuration/CacheConfiguration.java       |  35 ++-
 .../internal/pagemem/wal/record/WALRecord.java  |   5 +-
 .../delta/MetaPageInitRootInlineRecord.java     |  62 +++++
 .../record/delta/MetaPageInitRootRecord.java    |   3 +-
 .../cache/database/MetadataStorage.java         |  15 +-
 .../cache/database/tree/BPlusTree.java          |  72 ++++-
 .../cache/database/tree/io/BPlusIO.java         |   3 +-
 .../cache/database/tree/io/BPlusInnerIO.java    |  24 +-
 .../cache/database/tree/io/BPlusLeafIO.java     |   9 +-
 .../cache/database/tree/io/BPlusMetaIO.java     |  45 ++-
 .../cache/database/tree/io/DataPageIO.java      |   4 +-
 .../cache/database/tree/io/DataPagePayload.java |   2 +-
 .../cache/database/tree/io/PageIO.java          |  60 ++++
 .../query/GridQueryIndexDescriptor.java         |   7 +
 .../processors/query/GridQueryProcessor.java    |  47 +++-
 .../processors/query/h2/IgniteH2Indexing.java   |  23 +-
 .../processors/query/h2/database/H2Tree.java    |  57 +++-
 .../query/h2/database/H2TreeIndex.java          | 236 +++++++++++++++-
 .../query/h2/database/InlineIndexHelper.java    | 279 +++++++++++++++++++
 .../query/h2/database/io/H2ExtrasInnerIO.java   | 135 +++++++++
 .../query/h2/database/io/H2ExtrasLeafIO.java    | 132 +++++++++
 .../processors/query/h2/opt/GridH2Row.java      |   2 +-
 .../processors/query/h2/opt/GridH2Table.java    |   3 +-
 .../h2/GridIndexingSpiAbstractSelfTest.java     |   5 +
 .../query/h2/database/H2TreeIndexTest.java      |  59 ++++
 .../h2/database/InlineIndexHelperTest.java      |  46 +++
 29 files changed, 1314 insertions(+), 97 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/8f836cb0/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/tree/BPlusTreeBenchmark.java
----------------------------------------------------------------------
diff --git 
a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/tree/BPlusTreeBenchmark.java
 
b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/tree/BPlusTreeBenchmark.java
index d1bca73..7355850 100644
--- 
a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/tree/BPlusTreeBenchmark.java
+++ 
b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/tree/BPlusTreeBenchmark.java
@@ -17,7 +17,6 @@
 
 package org.apache.ignite.internal.benchmarks.jmh.tree;
 
-import java.nio.ByteBuffer;
 import java.util.concurrent.ConcurrentLinkedDeque;
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.atomic.AtomicLong;
@@ -269,12 +268,7 @@ public class BPlusTreeBenchmark extends 
JmhAbstractBenchmark {
             throws IgniteCheckedException {
             Long row = srcIo.getLookupRow(null, src, srcIdx);
 
-            store(dst, dstIdx, row, null);
-        }
-
-        /** {@inheritDoc} */
-        @Override public void storeByOffset(ByteBuffer buf, int off, Long row) 
throws IgniteCheckedException {
-            throw new UnsupportedOperationException();
+            store(dst, dstIdx, row, null, false);
         }
 
         /** {@inheritDoc} */
@@ -308,11 +302,6 @@ public class BPlusTreeBenchmark extends 
JmhAbstractBenchmark {
         }
 
         /** {@inheritDoc} */
-        @Override public void storeByOffset(ByteBuffer buf, int off, Long row) 
throws IgniteCheckedException {
-            throw new UnsupportedOperationException();
-        }
-
-        /** {@inheritDoc} */
         @Override public void storeByOffset(long pageAddr, int off, Long row) {
             PageUtils.putLong(pageAddr, off, row);
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/8f836cb0/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java 
b/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
index b229729..504b589 100644
--- a/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
+++ b/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
@@ -540,6 +540,13 @@ public final class IgniteSystemProperties {
     public static final String IGNITE_UNWRAP_BINARY_FOR_INDEXING_SPI = 
"IGNITE_UNWRAP_BINARY_FOR_INDEXING_SPI";
 
     /**
+     * System property to specify maximum payload size in bytes for {@code 
H2TreeIndex}.
+     * <p>
+     * Defaults to {@code 0}, meaning that inline index store is disabled.
+     */
+    public static final String IGNITE_MAX_INDEX_PAYLOAD_SIZE = 
"IGNITE_MAX_INDEX_PAYLOAD_SIZE";
+
+    /**
      * Enforces singleton.
      */
     private IgniteSystemProperties() {

http://git-wip-us.apache.org/repos/asf/ignite/blob/8f836cb0/modules/core/src/main/java/org/apache/ignite/cache/QueryIndex.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/cache/QueryIndex.java 
b/modules/core/src/main/java/org/apache/ignite/cache/QueryIndex.java
index af11999..bbe2872 100644
--- a/modules/core/src/main/java/org/apache/ignite/cache/QueryIndex.java
+++ b/modules/core/src/main/java/org/apache/ignite/cache/QueryIndex.java
@@ -40,6 +40,9 @@ public class QueryIndex implements Serializable {
     /** */
     private QueryIndexType type;
 
+    /** */
+    private int inlineSize = -1;
+
     /**
      * Creates an empty index. Should be populated via setters.
      */
@@ -224,4 +227,22 @@ public class QueryIndex implements Serializable {
     public void setIndexType(QueryIndexType type) {
         this.type = type;
     }
+
+    /**
+     * Gets inline size.
+     *
+     * @return inline size.
+     */
+    public int getInlineSize() {
+        return inlineSize;
+    }
+
+    /**
+     * Sets inline size.
+     *
+     * @param inlineSize Inline size.
+     */
+    public void setInlineSize(int inlineSize) {
+        this.inlineSize = inlineSize;
+    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/8f836cb0/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
 
b/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
index b3be1cf..6c56fc5 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
@@ -2456,10 +2456,11 @@ public class CacheConfiguration<K, V> extends 
MutableConfiguration<K, V> {
          *
          * @param idxName Index name.
          * @param type Index type.
+         * @param inlineSize Inline size.
          * @return Index descriptor.
          */
-        public IndexDescriptor addIndex(String idxName, GridQueryIndexType 
type) {
-            IndexDescriptor idx = new IndexDescriptor(type);
+        public IndexDescriptor addIndex(String idxName, GridQueryIndexType 
type, int inlineSize) {
+            IndexDescriptor idx = new IndexDescriptor(type, inlineSize);
 
             if (indexes.put(idxName, idx) != null)
                 throw new CacheException("Index with name '" + idxName + "' 
already exists.");
@@ -2468,6 +2469,17 @@ public class CacheConfiguration<K, V> extends 
MutableConfiguration<K, V> {
         }
 
         /**
+         * Adds index.
+         *
+         * @param idxName Index name.
+         * @param type Index type.
+         * @return Index descriptor.
+         */
+        public IndexDescriptor addIndex(String idxName, GridQueryIndexType 
type) {
+            return addIndex(idxName, type, -1);
+        }
+
+        /**
          * Adds field to index.
          *
          * @param idxName Index name.
@@ -2594,13 +2606,25 @@ public class CacheConfiguration<K, V> extends 
MutableConfiguration<K, V> {
         /** */
         private final GridQueryIndexType type;
 
+        /** */
+        private final int inlineSize;
+
         /**
          * @param type Type.
+         * @param inlineSize Inline size.
          */
-        private IndexDescriptor(GridQueryIndexType type) {
+        private IndexDescriptor(GridQueryIndexType type, int inlineSize) {
             assert type != null;
 
             this.type = type;
+            this.inlineSize = inlineSize;
+        }
+
+        /**
+         * @param type Type.
+         */
+        private IndexDescriptor(GridQueryIndexType type) {
+            this(type, -1);
         }
 
         /** {@inheritDoc} */
@@ -2642,6 +2666,11 @@ public class CacheConfiguration<K, V> extends 
MutableConfiguration<K, V> {
         }
 
         /** {@inheritDoc} */
+        @Override public int inlineSize() {
+            return inlineSize;
+        }
+
+        /** {@inheritDoc} */
         @Override public String toString() {
             return S.toString(IndexDescriptor.class, this);
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/8f836cb0/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/WALRecord.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/WALRecord.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/WALRecord.java
index f761f68..9c2c88a 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/WALRecord.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/WALRecord.java
@@ -157,7 +157,10 @@ public abstract class WALRecord {
         SWITCH_SEGMENT_RECORD,
 
         /** */
-        DATA_PAGE_UPDATE_RECORD
+        DATA_PAGE_UPDATE_RECORD,
+
+        /** init */
+        BTREE_META_PAGE_INIT_ROOT2
         ;
 
         /** */

http://git-wip-us.apache.org/repos/asf/ignite/blob/8f836cb0/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/MetaPageInitRootInlineRecord.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/MetaPageInitRootInlineRecord.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/MetaPageInitRootInlineRecord.java
new file mode 100644
index 0000000..212f5b9
--- /dev/null
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/MetaPageInitRootInlineRecord.java
@@ -0,0 +1,62 @@
+/*
+ * 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.pagemem.wal.record.delta;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.pagemem.PageMemory;
+import 
org.apache.ignite.internal.processors.cache.database.tree.io.BPlusMetaIO;
+
+/**
+ *
+ */
+public class MetaPageInitRootInlineRecord extends MetaPageInitRootRecord {
+
+    /** */
+    private final int inlineSize;
+
+    /**
+     * @param cacheId
+     * @param pageId Meta page ID.
+     * @param rootId
+     * @param inlineSize Inline size.
+     */
+    public MetaPageInitRootInlineRecord(int cacheId, long pageId, long rootId, 
int inlineSize) {
+        super(cacheId, pageId, rootId);
+        this.inlineSize = inlineSize;
+    }
+
+    /**
+     * @return Inline size.
+     */
+    public int inlineSize() {
+        return inlineSize;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void applyDelta(PageMemory pageMem, long pageAddr) throws 
IgniteCheckedException {
+        BPlusMetaIO io = BPlusMetaIO.VERSIONS.forPage(pageAddr);
+
+        io.initRoot(pageAddr, rootId, pageMem.pageSize());
+        io.setInlineSize(pageAddr, inlineSize);
+    }
+
+    /** {@inheritDoc} */
+    @Override public RecordType type() {
+        return RecordType.BTREE_META_PAGE_INIT_ROOT2;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/8f836cb0/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/MetaPageInitRootRecord.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/MetaPageInitRootRecord.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/MetaPageInitRootRecord.java
index 4d56db0..280310f 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/MetaPageInitRootRecord.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/MetaPageInitRootRecord.java
@@ -26,7 +26,7 @@ import 
org.apache.ignite.internal.processors.cache.database.tree.io.BPlusMetaIO;
  */
 public class MetaPageInitRootRecord extends PageDeltaRecord {
     /** */
-    private long rootId;
+    protected long rootId;
 
     /**
      * @param pageId Meta page ID.
@@ -44,6 +44,7 @@ public class MetaPageInitRootRecord extends PageDeltaRecord {
         BPlusMetaIO io = BPlusMetaIO.VERSIONS.forPage(pageAddr);
 
         io.initRoot(pageAddr, rootId, pageMem.pageSize());
+        io.setInlineSize(pageAddr, 0);
     }
 
     /** {@inheritDoc} */

http://git-wip-us.apache.org/repos/asf/ignite/blob/8f836cb0/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MetadataStorage.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MetadataStorage.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MetadataStorage.java
index faf7608..47c3254 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MetadataStorage.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MetadataStorage.java
@@ -192,7 +192,7 @@ public class MetadataStorage implements MetaStore {
         /** {@inheritDoc} */
         @Override protected int compare(final BPlusIO<IndexItem> io, final 
long pageAddr, final int idx,
             final IndexItem row) throws IgniteCheckedException {
-            final int off = ((IndexIO)io).getOffset(idx);
+            final int off = ((IndexIO)io).getOffset(pageAddr, idx);
 
             int shift = 0;
 
@@ -214,7 +214,7 @@ public class MetadataStorage implements MetaStore {
         /** {@inheritDoc} */
         @Override protected IndexItem getRow(final BPlusIO<IndexItem> io, 
final long pageAddr,
             final int idx) throws IgniteCheckedException {
-            return readRow(pageAddr, ((IndexIO)io).getOffset(idx));
+            return readRow(pageAddr, ((IndexIO)io).getOffset(pageAddr, idx));
         }
     }
 
@@ -323,10 +323,11 @@ public class MetadataStorage implements MetaStore {
      */
     private interface IndexIO {
         /**
+         * @param pageAddr Page address.
          * @param idx Index.
          * @return Offset in buffer according to {@code idx}.
          */
-        int getOffset(int idx);
+        int getOffset(long pageAddr, int idx);
     }
 
     /**
@@ -355,7 +356,7 @@ public class MetadataStorage implements MetaStore {
         @Override public void store(final long dstPageAddr, final int dstIdx, 
final BPlusIO<IndexItem> srcIo,
             final long srcPageAddr,
             final int srcIdx) throws IgniteCheckedException {
-            storeRow(dstPageAddr, offset(dstIdx), srcPageAddr, 
((IndexIO)srcIo).getOffset(srcIdx));
+            storeRow(dstPageAddr, offset(dstIdx), srcPageAddr, 
((IndexIO)srcIo).getOffset(srcPageAddr, srcIdx));
         }
 
         /** {@inheritDoc} */
@@ -365,7 +366,7 @@ public class MetadataStorage implements MetaStore {
         }
 
         /** {@inheritDoc} */
-        @Override public int getOffset(final int idx) {
+        @Override public int getOffset(long pageAddr, final int idx) {
             return offset(idx);
         }
     }
@@ -398,7 +399,7 @@ public class MetadataStorage implements MetaStore {
             final BPlusIO<IndexItem> srcIo,
             final long srcPageAddr,
             final int srcIdx) throws IgniteCheckedException {
-            storeRow(dstPageAddr, offset(dstIdx), srcPageAddr, 
((IndexIO)srcIo).getOffset(srcIdx));
+            storeRow(dstPageAddr, offset(dstIdx), srcPageAddr, 
((IndexIO)srcIo).getOffset(srcPageAddr, srcIdx));
         }
 
         /** {@inheritDoc} */
@@ -409,7 +410,7 @@ public class MetadataStorage implements MetaStore {
         }
 
         /** {@inheritDoc} */
-        @Override public int getOffset(final int idx) {
+        @Override public int getOffset(long pageAddr, final int idx) {
             return offset(idx);
         }
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/8f836cb0/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/BPlusTree.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/BPlusTree.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/BPlusTree.java
index 518d0d8..aa61fbd 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/BPlusTree.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/BPlusTree.java
@@ -38,7 +38,7 @@ import 
org.apache.ignite.internal.pagemem.wal.record.delta.FixRemoveId;
 import org.apache.ignite.internal.pagemem.wal.record.delta.InsertRecord;
 import 
org.apache.ignite.internal.pagemem.wal.record.delta.MetaPageAddRootRecord;
 import 
org.apache.ignite.internal.pagemem.wal.record.delta.MetaPageCutRootRecord;
-import 
org.apache.ignite.internal.pagemem.wal.record.delta.MetaPageInitRootRecord;
+import 
org.apache.ignite.internal.pagemem.wal.record.delta.MetaPageInitRootInlineRecord;
 import org.apache.ignite.internal.pagemem.wal.record.delta.NewRootInitRecord;
 import org.apache.ignite.internal.pagemem.wal.record.delta.RecycleRecord;
 import org.apache.ignite.internal.pagemem.wal.record.delta.RemoveRecord;
@@ -103,16 +103,16 @@ public abstract class BPlusTree<L, T extends L> extends 
DataStructure implements
     private final float maxFill;
 
     /** */
-    private final long metaPageId;
+    protected final long metaPageId;
 
     /** */
-    private final boolean canGetRowFromInner;
+    private boolean canGetRowFromInner;
 
     /** */
-    private final IOVersions<? extends BPlusInnerIO<L>> innerIos;
+    private IOVersions<? extends BPlusInnerIO<L>> innerIos;
 
     /** */
-    private final IOVersions<? extends BPlusLeafIO<L>> leafIos;
+    private IOVersions<? extends BPlusLeafIO<L>> leafIos;
 
     /** */
     private final AtomicLong globalRmvId;
@@ -661,7 +661,7 @@ public abstract class BPlusTree<L, T extends L> extends 
DataStructure implements
      */
     private class InitRoot extends PageHandler<Long, Bool> {
         /** {@inheritDoc} */
-        @Override public Bool run(Page meta, PageIO iox, long pageAddr, Long 
rootId, int lvl)
+        @Override public Bool run(Page meta, PageIO iox, long pageAddr, Long 
rootId, int inlineSize)
             throws IgniteCheckedException {
             assert rootId != null;
 
@@ -669,9 +669,10 @@ public abstract class BPlusTree<L, T extends L> extends 
DataStructure implements
             BPlusMetaIO io = (BPlusMetaIO)iox;
 
             io.initRoot(pageAddr, rootId, pageSize());
+            io.setInlineSize(pageAddr, inlineSize);
 
             if (needWalDeltaRecord(meta))
-                wal.log(new MetaPageInitRootRecord(cacheId, meta.id(), 
rootId));
+                wal.log(new MetaPageInitRootInlineRecord(cacheId, meta.id(), 
rootId, inlineSize));
 
             assert io.getRootLevel(pageAddr) == 0;
             assert io.getFirstPageId(pageAddr, 0) == rootId;
@@ -694,7 +695,7 @@ public abstract class BPlusTree<L, T extends L> extends 
DataStructure implements
      * @param leafIos Leaf IO versions.
      * @throws IgniteCheckedException If failed.
      */
-    public BPlusTree(
+    protected BPlusTree(
         String name,
         int cacheId,
         PageMemory pageMem,
@@ -705,6 +706,29 @@ public abstract class BPlusTree<L, T extends L> extends 
DataStructure implements
         IOVersions<? extends BPlusInnerIO<L>> innerIos,
         IOVersions<? extends BPlusLeafIO<L>> leafIos
     ) throws IgniteCheckedException {
+        this(name, cacheId, pageMem, wal, globalRmvId, metaPageId, reuseList);
+        setIos(innerIos, leafIos);
+    }
+
+    /**
+     * @param name Tree name.
+     * @param cacheId Cache ID.
+     * @param pageMem Page memory.
+     * @param wal Write ahead log manager.
+     * @param globalRmvId Remove ID.
+     * @param metaPageId Meta page ID.
+     * @param reuseList Reuse list.
+     * @throws IgniteCheckedException If failed.
+     */
+    protected BPlusTree(
+        String name,
+        int cacheId,
+        PageMemory pageMem,
+        IgniteWriteAheadLogManager wal,
+        AtomicLong globalRmvId,
+        long metaPageId,
+        ReuseList reuseList
+    ) throws IgniteCheckedException {
         super(cacheId, pageMem, wal);
 
         assert !F.isEmpty(name);
@@ -713,13 +737,8 @@ public abstract class BPlusTree<L, T extends L> extends 
DataStructure implements
         minFill = 0f; // Testing worst case when merge happens only on empty 
page.
         maxFill = 0f; // Avoiding random effects on testing.
 
-        assert innerIos != null;
-        assert leafIos != null;
         assert metaPageId != 0L;
 
-        this.canGetRowFromInner = innerIos.latest().canGetRow(); // TODO 
refactor
-        this.innerIos = innerIos;
-        this.leafIos = leafIos;
         this.metaPageId = metaPageId;
         this.name = name;
         this.reuseList = reuseList;
@@ -727,6 +746,20 @@ public abstract class BPlusTree<L, T extends L> extends 
DataStructure implements
     }
 
     /**
+     * @param innerIos Inner IO versions.
+     * @param leafIos Leaf IO versions.
+     */
+    protected void setIos(IOVersions<? extends BPlusInnerIO<L>> innerIos,
+        IOVersions<? extends BPlusLeafIO<L>> leafIos) {
+        assert innerIos != null;
+        assert leafIos != null;
+
+        this.canGetRowFromInner = innerIos.latest().canGetRow(); // TODO 
refactor
+        this.innerIos = innerIos;
+        this.leafIos = leafIos;
+    }
+
+    /**
      * @return Tree name.
      */
     public final String getName() {
@@ -748,6 +781,17 @@ public abstract class BPlusTree<L, T extends L> extends 
DataStructure implements
      * @throws IgniteCheckedException If failed.
      */
     protected final void initTree(boolean initNew) throws 
IgniteCheckedException {
+        initTree(initNew, 0);
+    }
+
+    /**
+     * Initialize new tree.
+     *
+     * @param initNew {@code True} if new tree should be created.
+     * @param inlineSize Inline size.
+     * @throws IgniteCheckedException If failed.
+     */
+    protected final void initTree(boolean initNew, int inlineSize) throws 
IgniteCheckedException {
         if (initNew) {
             // Allocate the first leaf page, it will be our root.
             long rootId = allocatePage(null);
@@ -758,7 +802,7 @@ public abstract class BPlusTree<L, T extends L> extends 
DataStructure implements
 
             // Initialize meta page with new root page.
             try (Page meta = page(metaPageId)) {
-                Bool res = writePage(pageMem, meta, this, initRoot, 
BPlusMetaIO.VERSIONS.latest(), wal, rootId, 0, FALSE);
+                Bool res = writePage(pageMem, meta, this, initRoot, 
BPlusMetaIO.VERSIONS.latest(), wal, rootId, inlineSize, FALSE);
 
                 assert res == TRUE: res;
             }

http://git-wip-us.apache.org/repos/asf/ignite/blob/8f836cb0/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/BPlusIO.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/BPlusIO.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/BPlusIO.java
index 5186808..bcf2908 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/BPlusIO.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/BPlusIO.java
@@ -51,7 +51,6 @@ public abstract class BPlusIO<L> extends PageIO {
      * @param ver Page format version.
      * @param leaf If this is a leaf IO.
      * @param canGetRow If we can get full row from this page.
-     * @param itemSize Single item size on page.
      */
     protected BPlusIO(int type, int ver, boolean leaf, boolean canGetRow, int 
itemSize) {
         super(type, ver);
@@ -194,7 +193,7 @@ public abstract class BPlusIO<L> extends PageIO {
      * @param idx Index of element.
      * @return Offset from byte buffer begin in bytes.
      */
-    protected abstract int offset(int idx);
+    public abstract int offset(int idx);
 
     /**
      * Store the needed info about the row in the page. Leaf and inner pages 
can store different info.

http://git-wip-us.apache.org/repos/asf/ignite/blob/8f836cb0/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/BPlusInnerIO.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/BPlusInnerIO.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/BPlusInnerIO.java
index b15c2dc..60fd24c 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/BPlusInnerIO.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/BPlusInnerIO.java
@@ -49,7 +49,7 @@ public abstract class BPlusInnerIO<L> extends BPlusIO<L> {
         // The structure of the page is the following:
         // |ITEMS_OFF|w|A|x|B|y|C|z|
         // where capital letters are data items, lowercase letters are 8 byte 
page references.
-        return (pageSize - ITEMS_OFF - 8) / (itemSize + 8);
+        return (pageSize - ITEMS_OFF - 8) / (getItemSize() + 8);
     }
 
     /**
@@ -58,7 +58,7 @@ public abstract class BPlusInnerIO<L> extends BPlusIO<L> {
      * @return Page ID.
      */
     public final long getLeft(long pageAddr, int idx) {
-        return PageUtils.getLong(pageAddr, (offset(idx, SHIFT_LEFT)));
+        return PageUtils.getLong(pageAddr, (offset0(idx, SHIFT_LEFT)));
     }
 
     /**
@@ -67,7 +67,7 @@ public abstract class BPlusInnerIO<L> extends BPlusIO<L> {
      * @param pageId Page ID.
      */
     public final void setLeft(long pageAddr, int idx, long pageId) {
-        PageUtils.putLong(pageAddr, offset(idx, SHIFT_LEFT), pageId);
+        PageUtils.putLong(pageAddr, offset0(idx, SHIFT_LEFT), pageId);
 
         assert pageId == getLeft(pageAddr, idx);
     }
@@ -78,7 +78,7 @@ public abstract class BPlusInnerIO<L> extends BPlusIO<L> {
      * @return Page ID.
      */
     public final long getRight(long pageAddr, int idx) {
-        return PageUtils.getLong(pageAddr, offset(idx, SHIFT_RIGHT));
+        return PageUtils.getLong(pageAddr, offset0(idx, SHIFT_RIGHT));
     }
 
     /**
@@ -87,7 +87,7 @@ public abstract class BPlusInnerIO<L> extends BPlusIO<L> {
      * @param pageId Page ID.
      */
     private void setRight(long pageAddr, int idx, long pageId) {
-        PageUtils.putLong(pageAddr, offset(idx, SHIFT_RIGHT), pageId);
+        PageUtils.putLong(pageAddr, offset0(idx, SHIFT_RIGHT), pageId);
 
         assert pageId == getRight(pageAddr, idx);
     }
@@ -97,17 +97,17 @@ public abstract class BPlusInnerIO<L> extends BPlusIO<L> {
         boolean cpLeft) throws IgniteCheckedException {
         assert srcIdx != dstIdx || srcPageAddr != dstPageAddr;
 
-        cnt *= itemSize + 8; // From items to bytes.
+        cnt *= getItemSize() + 8; // From items to bytes.
 
         if (dstIdx > srcIdx) {
             PageHandler.copyMemory(srcPageAddr, dstPageAddr, offset(srcIdx), 
offset(dstIdx), cnt);
 
             if (cpLeft)
-                PageUtils.putLong(dstPageAddr, offset(dstIdx, SHIFT_LEFT), 
PageUtils.getLong(srcPageAddr, (offset(srcIdx, SHIFT_LEFT))));
+                PageUtils.putLong(dstPageAddr, offset0(dstIdx, SHIFT_LEFT), 
PageUtils.getLong(srcPageAddr, (offset0(srcIdx, SHIFT_LEFT))));
         }
         else {
             if (cpLeft)
-                PageUtils.putLong(dstPageAddr, offset(dstIdx, SHIFT_LEFT), 
PageUtils.getLong(srcPageAddr, (offset(srcIdx, SHIFT_LEFT))));
+                PageUtils.putLong(dstPageAddr, offset0(dstIdx, SHIFT_LEFT), 
PageUtils.getLong(srcPageAddr, (offset0(srcIdx, SHIFT_LEFT))));
 
             PageHandler.copyMemory(srcPageAddr, dstPageAddr, offset(srcIdx), 
offset(dstIdx), cnt);
         }
@@ -118,13 +118,13 @@ public abstract class BPlusInnerIO<L> extends BPlusIO<L> {
      * @param shift It can be either link itself or left or right page ID.
      * @return Offset from byte buffer begin in bytes.
      */
-    private int offset(int idx, int shift) {
-        return shift + (8 + itemSize) * idx;
+    private int offset0(int idx, int shift) {
+        return shift + (8 + getItemSize()) * idx;
     }
 
     /** {@inheritDoc} */
-    @Override protected final int offset(int idx) {
-        return offset(idx, SHIFT_LINK);
+    @Override public final int offset(int idx) {
+        return offset0(idx, SHIFT_LINK);
     }
 
     // Methods for B+Tree logic.

http://git-wip-us.apache.org/repos/asf/ignite/blob/8f836cb0/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/BPlusLeafIO.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/BPlusLeafIO.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/BPlusLeafIO.java
index f3dccee..f6011b3 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/BPlusLeafIO.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/BPlusLeafIO.java
@@ -35,7 +35,7 @@ public abstract class BPlusLeafIO<L> extends BPlusIO<L> {
 
     /** {@inheritDoc} */
     @Override public int getMaxCount(long pageAddr, int pageSize) {
-        return (pageSize - ITEMS_OFF) / itemSize;
+        return (pageSize - ITEMS_OFF) / getItemSize();
     }
 
     /** {@inheritDoc} */
@@ -43,13 +43,14 @@ public abstract class BPlusLeafIO<L> extends BPlusIO<L> {
         boolean cpLeft) throws IgniteCheckedException {
         assert srcIdx != dstIdx || srcPageAddr != dstPageAddr;
 
-        PageHandler.copyMemory(srcPageAddr, dstPageAddr, offset(srcIdx), 
offset(dstIdx), cnt * itemSize);
+        PageHandler.copyMemory(srcPageAddr, dstPageAddr, offset(srcIdx), 
offset(dstIdx),
+            cnt * getItemSize());
     }
 
     /** {@inheritDoc} */
-    @Override protected final int offset(int idx) {
+    @Override public final int offset(int idx) {
         assert idx >= 0: idx;
 
-        return ITEMS_OFF + idx * itemSize;
+        return ITEMS_OFF + idx * getItemSize();
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/8f836cb0/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/BPlusMetaIO.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/BPlusMetaIO.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/BPlusMetaIO.java
index 8850863..6755820 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/BPlusMetaIO.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/BPlusMetaIO.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.internal.processors.cache.database.tree.io;
 
+import org.apache.ignite.IgniteException;
 import org.apache.ignite.internal.pagemem.PageUtils;
 
 /**
@@ -25,20 +26,38 @@ import org.apache.ignite.internal.pagemem.PageUtils;
 public class BPlusMetaIO extends PageIO {
     /** */
     public static final IOVersions<BPlusMetaIO> VERSIONS = new IOVersions<>(
-        new BPlusMetaIO(1)
+        new BPlusMetaIO(1), new BPlusMetaIO(2)
     );
 
     /** */
     private static final int LVLS_OFF = COMMON_HEADER_END;
 
     /** */
-    private static final int REFS_OFF = LVLS_OFF + 1;
+    private final int refsOff;
+
+    /** */
+    private final int inlineSizeOff;
 
     /**
      * @param ver Page format version.
      */
     private BPlusMetaIO(int ver) {
         super(T_BPLUS_META, ver);
+
+        switch (ver) {
+            case 1:
+                inlineSizeOff = -1;
+                refsOff = LVLS_OFF + 1;
+                break;
+
+            case 2:
+                inlineSizeOff = LVLS_OFF + 1;
+                refsOff = inlineSizeOff + 2;
+                break;
+
+            default:
+                throw new IgniteException("invalid IO version: " + ver);
+        }
     }
 
     /**
@@ -65,7 +84,7 @@ public class BPlusMetaIO extends PageIO {
      * @return Max levels possible for this page size.
      */
     private int getMaxLevels(long pageAddr, int pageSize) {
-        return (pageSize - REFS_OFF) / 8;
+        return (pageSize - refsOff) / 8;
     }
 
     /**
@@ -85,8 +104,8 @@ public class BPlusMetaIO extends PageIO {
      * @param lvl Level.
      * @return Offset for page reference.
      */
-    private static int offset(int lvl) {
-        return lvl * 8 + REFS_OFF;
+    private int offset(int lvl) {
+        return lvl * 8 + refsOff;
     }
 
     /**
@@ -144,4 +163,20 @@ public class BPlusMetaIO extends PageIO {
 
         setLevelsCount(pageAddr, lvl, pageSize); // Decrease tree height.
     }
+
+    /**
+     * @param pageAddr Page address.
+     * @param size Offset size.
+     */
+    public void setInlineSize(long pageAddr, int size) {
+        if (getVersion() > 1)
+            PageUtils.putShort(pageAddr, inlineSizeOff, (short)size);
+    }
+
+    /**
+     * @param pageAddr Page address.
+     */
+    public int getInlineSize(long pageAddr) {
+        return getVersion() > 1 ? PageUtils.getShort(pageAddr, inlineSizeOff) 
: 0;
+    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/8f836cb0/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/DataPageIO.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/DataPageIO.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/DataPageIO.java
index 945f4dc..190349a 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/DataPageIO.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/DataPageIO.java
@@ -452,10 +452,12 @@ public class DataPageIO extends PageIO {
     }
 
     /**
+     * Sets position to start of actual fragment data and limit to it's end.
+     *
      * @param pageAddr Page address.
      * @param itemId Item to position on.
      * @param pageSize Page size.
-     * @return Size and offset of actual fragment data, and link to the next 
fragment if data is fragmented.
+     * @return {@link DataPagePayload} object.
      */
     public DataPagePayload readPayload(final long pageAddr, final int itemId, 
final int pageSize) {
         int dataOff = getDataOffset(pageAddr, itemId, pageSize);

http://git-wip-us.apache.org/repos/asf/ignite/blob/8f836cb0/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/DataPagePayload.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/DataPagePayload.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/DataPagePayload.java
index 7dedc00..277bdc7 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/DataPagePayload.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/DataPagePayload.java
@@ -56,7 +56,7 @@ public class DataPagePayload {
     }
 
     /**
-     * @return Next link.
+     * @return Link to the next fragment or {@code 0} if it is the last 
fragment or the data row is not fragmented.
      */
     public long nextLink() {
         return nextLink;

http://git-wip-us.apache.org/repos/asf/ignite/blob/8f836cb0/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/PageIO.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/PageIO.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/PageIO.java
index f6ac905..6f50bf7 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/PageIO.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/PageIO.java
@@ -18,6 +18,8 @@
 package org.apache.ignite.internal.processors.cache.database.tree.io;
 
 import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.internal.pagemem.Page;
 import org.apache.ignite.internal.pagemem.PageMemory;
@@ -76,6 +78,15 @@ public abstract class PageIO {
     /** */
     private static IOVersions<? extends BPlusLeafIO<?>> h2LeafIOs;
 
+    /** Maximum payload size. */
+    public static final short MAX_PAYLOAD_SIZE = 2048;
+
+    /** */
+    private static List<IOVersions<? extends BPlusInnerIO<?>>> h2ExtraInnerIOs 
= new ArrayList<>(MAX_PAYLOAD_SIZE);
+
+    /** */
+    private static List<IOVersions<? extends BPlusLeafIO<?>>> h2ExtraLeafIOs = 
new ArrayList<>(MAX_PAYLOAD_SIZE);
+
     /** */
     public static final int TYPE_OFF = 0;
 
@@ -147,6 +158,14 @@ public abstract class PageIO {
     /** */
     public static final short T_PAGE_UPDATE_TRACKING = 15;
 
+    /** Index for payload == 1. */
+    public static final short T_H2_EX_REF_LEAF_START = 10000;
+    public static final short T_H2_EX_REF_LEAF_END = T_H2_EX_REF_LEAF_START + 
MAX_PAYLOAD_SIZE - 1;
+
+    /** */
+    public static final short T_H2_EX_REF_INNER_START = 20000;
+    public static final short T_H2_EX_REF_INNER_END = T_H2_EX_REF_INNER_START 
+ MAX_PAYLOAD_SIZE - 1;
+
     /** */
     private final int ver;
 
@@ -290,6 +309,40 @@ public abstract class PageIO {
     }
 
     /**
+     * Registers extra inner IO versions.
+     */
+    public static void registerH2ExtraInner(
+        IOVersions<? extends BPlusInnerIO<?>> innerExtIOs
+    ) {
+        h2ExtraInnerIOs.add(innerExtIOs);
+    }
+
+    /**
+     * Registers extra inner IO versions.
+     */
+    public static void registerH2ExtraLeaf(
+        IOVersions<? extends BPlusLeafIO<?>> leafExtIOs
+    ) {
+        h2ExtraLeafIOs.add(leafExtIOs);
+    }
+
+    /**
+     * @param idx Index.
+     * @return IOVersions for given idx.
+     */
+    public static IOVersions<? extends BPlusInnerIO<?>> getInnerVersions(int 
idx) {
+        return h2ExtraInnerIOs.get(idx);
+    }
+
+    /**
+     * @param idx Index.
+     * @return IOVersions for given idx.
+     */
+    public static IOVersions<? extends BPlusLeafIO<?>> getLeafVersions(int 
idx) {
+        return h2ExtraLeafIOs.get(idx);
+    }
+
+    /**
      * Registers IOs for testing.
      *
      * @param innerIO Inner IO.
@@ -402,6 +455,13 @@ public abstract class PageIO {
      */
     @SuppressWarnings("unchecked")
     public static <Q extends BPlusIO<?>> Q getBPlusIO(int type, int ver) 
throws IgniteCheckedException {
+
+        if (type >= T_H2_EX_REF_LEAF_START && type <= T_H2_EX_REF_LEAF_END)
+            return (Q)h2ExtraLeafIOs.get(type - 
T_H2_EX_REF_LEAF_START).forVersion(ver);
+
+        if (type >= T_H2_EX_REF_INNER_START && type <= T_H2_EX_REF_INNER_END)
+            return (Q)h2ExtraInnerIOs.get(type - 
T_H2_EX_REF_INNER_START).forVersion(ver);
+
         switch (type) {
             case T_H2_REF_INNER:
                 if (h2InnerIOs == null)

http://git-wip-us.apache.org/repos/asf/ignite/blob/8f836cb0/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexDescriptor.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexDescriptor.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexDescriptor.java
index fe58112..bed2ffe 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexDescriptor.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexDescriptor.java
@@ -47,4 +47,11 @@ public interface GridQueryIndexDescriptor {
      * @return Type.
      */
     public GridQueryIndexType type();
+
+    /**
+     * Gets inline size for SORTED index.
+     *
+     * @return Inline size.
+     */
+    public int inlineSize();
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/8f836cb0/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
index cf85a52..03a6f9b 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
@@ -17,12 +17,11 @@
 
 package org.apache.ignite.internal.processors.query;
 
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-import java.util.concurrent.TimeUnit;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.math.BigDecimal;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
 import java.sql.Time;
 import java.sql.Timestamp;
 import java.util.ArrayList;
@@ -40,6 +39,7 @@ import java.util.TreeSet;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
 import javax.cache.Cache;
 import javax.cache.CacheException;
 import org.apache.ignite.IgniteCheckedException;
@@ -71,9 +71,9 @@ import 
org.apache.ignite.internal.processors.cache.CacheEntryImpl;
 import org.apache.ignite.internal.processors.cache.CacheObject;
 import org.apache.ignite.internal.processors.cache.CacheObjectContext;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
-import org.apache.ignite.internal.processors.cache.KeyCacheObject;
 import 
org.apache.ignite.internal.processors.cache.GridCacheDefaultAffinityKeyMapper;
 import org.apache.ignite.internal.processors.cache.IgniteCacheProxy;
+import org.apache.ignite.internal.processors.cache.KeyCacheObject;
 import org.apache.ignite.internal.processors.cache.QueryCursorImpl;
 import 
org.apache.ignite.internal.processors.cache.binary.CacheObjectBinaryProcessorImpl;
 import org.apache.ignite.internal.processors.cache.query.CacheQueryFuture;
@@ -1700,7 +1700,7 @@ public class GridQueryProcessor extends 
GridProcessorAdapter {
                     idxName = QueryEntity.defaultIndexName(idx);
 
                 if (idx.getIndexType() == QueryIndexType.SORTED || 
idx.getIndexType() == QueryIndexType.GEOSPATIAL) {
-                    d.addIndex(idxName, idx.getIndexType() == 
QueryIndexType.SORTED ? SORTED : GEO_SPATIAL);
+                    d.addIndex(idxName, idx.getIndexType() == 
QueryIndexType.SORTED ? SORTED : GEO_SPATIAL, idx.getInlineSize());
 
                     int i = 0;
 
@@ -2453,11 +2453,13 @@ public class GridQueryProcessor extends 
GridProcessorAdapter {
          *
          * @param idxName Index name.
          * @param type Index type.
+         * @param inlineSize Inline size.
          * @return Index descriptor.
          * @throws IgniteCheckedException In case of error.
          */
-        public IndexDescriptor addIndex(String idxName, GridQueryIndexType 
type) throws IgniteCheckedException {
-            IndexDescriptor idx = new IndexDescriptor(type);
+        public IndexDescriptor addIndex(String idxName, GridQueryIndexType 
type,
+            int inlineSize) throws IgniteCheckedException {
+            IndexDescriptor idx = new IndexDescriptor(type, inlineSize);
 
             if (indexes.put(idxName, idx) != null)
                 throw new IgniteCheckedException("Index with name '" + idxName 
+ "' already exists.");
@@ -2466,6 +2468,18 @@ public class GridQueryProcessor extends 
GridProcessorAdapter {
         }
 
         /**
+         * Adds index.
+         *
+         * @param idxName Index name.
+         * @param type Index type.
+         * @return Index descriptor.
+         * @throws IgniteCheckedException In case of error.
+         */
+        public IndexDescriptor addIndex(String idxName, GridQueryIndexType 
type) throws IgniteCheckedException {
+            return addIndex(idxName, type, 0);
+        }
+
+        /**
          * Adds field to index.
          *
          * @param idxName Index name.
@@ -2653,13 +2667,25 @@ public class GridQueryProcessor extends 
GridProcessorAdapter {
         /** */
         private final GridQueryIndexType type;
 
+        /** */
+        private int inlineSize;
+
         /**
          * @param type Type.
+         * @param inlineSize Inline size.
          */
-        private IndexDescriptor(GridQueryIndexType type) {
+        private IndexDescriptor(GridQueryIndexType type, int inlineSize) {
             assert type != null;
 
             this.type = type;
+            this.inlineSize = inlineSize;
+        }
+
+        /**
+         * @param type Type.
+         */
+        private IndexDescriptor(GridQueryIndexType type) {
+            this(type, 0);
         }
 
         /** {@inheritDoc} */
@@ -2701,6 +2727,11 @@ public class GridQueryProcessor extends 
GridProcessorAdapter {
         }
 
         /** {@inheritDoc} */
+        @Override public int inlineSize() {
+            return inlineSize;
+        }
+
+        /** {@inheritDoc} */
         @Override public String toString() {
             return S.toString(IndexDescriptor.class, this);
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/8f836cb0/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
----------------------------------------------------------------------
diff --git 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
index 98b5b7f..b0e7956 100644
--- 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
+++ 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
@@ -85,8 +85,8 @@ import 
org.apache.ignite.internal.processors.cache.database.tree.io.PageIO;
 import 
org.apache.ignite.internal.processors.cache.query.GridCacheQueryMarshallable;
 import org.apache.ignite.internal.processors.cache.query.GridCacheTwoStepQuery;
 import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
-import org.apache.ignite.internal.processors.query.GridQueryCancel;
 import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.processors.query.GridQueryCancel;
 import org.apache.ignite.internal.processors.query.GridQueryFieldMetadata;
 import org.apache.ignite.internal.processors.query.GridQueryFieldsResult;
 import 
org.apache.ignite.internal.processors.query.GridQueryFieldsResultAdapter;
@@ -96,11 +96,13 @@ import 
org.apache.ignite.internal.processors.query.GridQueryProperty;
 import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor;
 import org.apache.ignite.internal.processors.query.IgniteSQLException;
 import org.apache.ignite.internal.processors.query.h2.database.H2PkHashIndex;
-import 
org.apache.ignite.internal.processors.query.h2.opt.GridH2DefaultTableEngine;
 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.io.H2ExtrasInnerIO;
+import 
org.apache.ignite.internal.processors.query.h2.database.io.H2ExtrasLeafIO;
 import org.apache.ignite.internal.processors.query.h2.database.io.H2InnerIO;
 import org.apache.ignite.internal.processors.query.h2.database.io.H2LeafIO;
+import 
org.apache.ignite.internal.processors.query.h2.opt.GridH2DefaultTableEngine;
 import 
org.apache.ignite.internal.processors.query.h2.opt.GridH2KeyValueRowOffheap;
 import 
org.apache.ignite.internal.processors.query.h2.opt.GridH2KeyValueRowOnheap;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2QueryContext;
@@ -209,13 +211,16 @@ import static 
org.apache.ignite.internal.processors.query.h2.opt.GridH2QueryType
  */
 @SuppressWarnings({"UnnecessaryFullyQualifiedName", 
"NonFinalStaticVariableUsedInClassInitialization"})
 public class IgniteH2Indexing implements GridQueryIndexing {
+
     /**
      * Register IO for indexes.
      */
     static {
         PageIO.registerH2(H2InnerIO.VERSIONS, H2LeafIO.VERSIONS);
+        H2ExtrasInnerIO.register();
+        H2ExtrasLeafIO.register();
     }
-
+    
     /** Default DB options. */
     private static final String DB_OPTIONS = 
";LOCK_MODE=3;MULTI_THREADED=1;DB_CLOSE_ON_EXIT=FALSE" +
         
";DEFAULT_LOCK_TIMEOUT=10000;FUNCTIONS_IN_SCHEMA=true;OPTIMIZE_REUSE_RESULTS=0;QUERY_CACHE_SIZE=0"
 +
@@ -2596,7 +2601,8 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
                 "_key_PK",
                 tbl,
                 true,
-                treeIndexColumns(new ArrayList<IndexColumn>(2), keyCol, 
affCol)));
+                treeIndexColumns(new ArrayList<IndexColumn>(2), keyCol, 
affCol),
+                -1));
 
             if (type().valueClass() == String.class) {
                 try {
@@ -2641,7 +2647,7 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
 
                         cols = treeIndexColumns(cols, keyCol, affCol);
 
-                        idxs.add(createSortedIndex(cacheId, name, tbl, false, 
cols));
+                        idxs.add(createSortedIndex(cacheId, name, tbl, false, 
cols, idx.inlineSize()));
                     }
                     else if (idx.type() == GEO_SPATIAL)
                         idxs.add(createH2SpatialIndex(tbl, name, 
cols.toArray(new IndexColumn[cols.size()])));
@@ -2653,7 +2659,7 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
             // Add explicit affinity key index if nothing alike was found.
             if (affCol != null && !affIdxFound) {
                 idxs.add(createSortedIndex(cacheId, "AFFINITY_KEY", tbl, false,
-                    treeIndexColumns(new ArrayList<IndexColumn>(2), affCol, 
keyCol)));
+                    treeIndexColumns(new ArrayList<IndexColumn>(2), affCol, 
keyCol), -1));
             }
 
             return idxs;
@@ -2672,7 +2678,8 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
             String name,
             GridH2Table tbl,
             boolean pk,
-            List<IndexColumn> cols
+            List<IndexColumn> cols,
+            int inlineSize
         ) {
             try {
                 GridCacheSharedContext<Object, Object> scctx = 
ctx.cache().context();
@@ -2682,7 +2689,7 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
                 if (log.isInfoEnabled())
                     log.info("Creating cache index [cacheId=" + cctx.cacheId() 
+ ", idxName=" + name + ']');
 
-                return new H2TreeIndex(cctx, tbl, name, pk, cols);
+                return new H2TreeIndex(cctx, tbl, name, pk, cols, inlineSize);
             }
             catch (IgniteCheckedException e) {
                 throw new IgniteException(e);

http://git-wip-us.apache.org/repos/asf/ignite/blob/8f836cb0/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2Tree.java
----------------------------------------------------------------------
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 19e05ed..dd37492 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
@@ -19,14 +19,17 @@ package 
org.apache.ignite.internal.processors.query.h2.database;
 
 import java.util.concurrent.atomic.AtomicLong;
 import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.pagemem.Page;
 import org.apache.ignite.internal.pagemem.PageMemory;
 import org.apache.ignite.internal.pagemem.wal.IgniteWriteAheadLogManager;
 import org.apache.ignite.internal.processors.cache.database.tree.BPlusTree;
 import org.apache.ignite.internal.processors.cache.database.tree.io.BPlusIO;
+import 
org.apache.ignite.internal.processors.cache.database.tree.io.BPlusMetaIO;
 import 
org.apache.ignite.internal.processors.cache.database.tree.reuse.ReuseList;
-import org.apache.ignite.internal.processors.query.h2.database.io.H2InnerIO;
-import org.apache.ignite.internal.processors.query.h2.database.io.H2LeafIO;
+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.opt.GridH2Row;
+import org.apache.ignite.internal.util.typedef.internal.U;
 import org.h2.result.SearchRow;
 
 /**
@@ -35,6 +38,9 @@ public abstract class H2Tree extends BPlusTree<SearchRow, 
GridH2Row> {
     /** */
     private final H2RowFactory rowStore;
 
+    /** */
+    private final int inlineSize;
+
     /**
      * @param name Tree name.
      * @param reuseList Reuse list.
@@ -46,7 +52,7 @@ public abstract class H2Tree extends BPlusTree<SearchRow, 
GridH2Row> {
      * @param initNew Initialize new index.
      * @throws IgniteCheckedException If failed.
      */
-    public H2Tree(
+    protected H2Tree(
         String name,
         ReuseList reuseList,
         int cacheId,
@@ -55,15 +61,25 @@ public abstract class H2Tree extends BPlusTree<SearchRow, 
GridH2Row> {
         AtomicLong globalRmvId,
         H2RowFactory rowStore,
         long metaPageId,
-        boolean initNew
+        boolean initNew,
+        int inlineSize
     ) throws IgniteCheckedException {
-        super(name, cacheId, pageMem, wal, globalRmvId, metaPageId, reuseList, 
H2InnerIO.VERSIONS, H2LeafIO.VERSIONS);
+        super(name, cacheId, pageMem, wal, globalRmvId, metaPageId, reuseList);
+
+        if (!initNew) {
+            // Page is ready - read inline size from it.
+            inlineSize = getMetaInlineSize();
+        }
+
+        this.inlineSize = inlineSize;
 
         assert rowStore != null;
 
         this.rowStore = rowStore;
 
-        initTree(initNew);
+        setIos(H2ExtrasInnerIO.getVersions(inlineSize), 
H2ExtrasLeafIO.getVersions(inlineSize));
+
+        initTree(initNew, inlineSize);
     }
 
     /**
@@ -78,6 +94,35 @@ public abstract class H2Tree extends BPlusTree<SearchRow, 
GridH2Row> {
         throws IgniteCheckedException {
         return (GridH2Row)io.getLookupRow(this, pageAddr, idx);
     }
+
+    /**
+     * @return Inline size.
+     */
+    public int inlineSize() {
+        return inlineSize;
+    }
+
+    /**
+     * @return Inline size.
+     * @throws IgniteCheckedException
+     */
+    private int getMetaInlineSize() throws IgniteCheckedException {
+        try (Page meta = page(metaPageId)) {
+            long pageAddr = readLock(meta); // Meta can't be removed.
+
+            assert pageAddr != 0 : "Failed to read lock meta page [page=" + 
meta + ", metaPageId=" +
+                U.hexLong(metaPageId) + ']';
+
+            try {
+                BPlusMetaIO io = BPlusMetaIO.VERSIONS.forPage(pageAddr);
+
+                return io.getInlineSize(pageAddr);
+            }
+            finally {
+                readUnlock(meta, pageAddr);
+            }
+        }
+    }
 }
 
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/8f836cb0/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
----------------------------------------------------------------------
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 9bcdab5..be5be0a 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
@@ -17,18 +17,24 @@
 
 package org.apache.ignite.internal.processors.query.h2.database;
 
+import java.util.ArrayList;
 import java.util.List;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteException;
+import org.apache.ignite.IgniteSystemProperties;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import 
org.apache.ignite.internal.processors.cache.database.IgniteCacheDatabaseSharedManager;
 import org.apache.ignite.internal.processors.cache.database.RootPage;
 import org.apache.ignite.internal.processors.cache.database.tree.BPlusTree;
 import org.apache.ignite.internal.processors.cache.database.tree.io.BPlusIO;
-import org.apache.ignite.internal.processors.query.h2.*;
-import org.apache.ignite.internal.processors.query.h2.opt.*;
-import org.apache.ignite.internal.util.*;
-import org.apache.ignite.internal.util.lang.*;
+import org.apache.ignite.internal.processors.cache.database.tree.io.PageIO;
+import org.apache.ignite.internal.processors.query.h2.H2Cursor;
+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.GridH2Table;
+import org.apache.ignite.internal.util.IgniteTree;
+import org.apache.ignite.internal.util.lang.GridCursor;
+import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.lang.IgniteBiPredicate;
 import org.apache.ignite.spi.indexing.IndexingQueryFilter;
 import org.h2.engine.Session;
@@ -39,15 +45,25 @@ import org.h2.result.SearchRow;
 import org.h2.result.SortOrder;
 import org.h2.table.IndexColumn;
 import org.h2.table.TableFilter;
+import org.h2.value.Value;
 import org.jetbrains.annotations.Nullable;
 
 /**
  * H2 Index over {@link BPlusTree}.
  */
 public class H2TreeIndex extends GridH2IndexBase {
+    /** Default value for {@code IGNITE_MAX_INDEX_PAYLOAD_SIZE} */
+    public static final int IGNITE_MAX_INDEX_PAYLOAD_SIZE_DEFAULT = 0;
+
+    /** PageContext for use in IO's */
+    private static final ThreadLocal<H2TreeIndex> currentIndex = new 
ThreadLocal<>();
+
     /** */
     private final H2Tree tree;
 
+    /** */
+    private final List<InlineIndexHelper> inlineIdxs;
+
     /** Cache context. */
     private GridCacheContext<?, ?> cctx;
 
@@ -57,14 +73,16 @@ public class H2TreeIndex extends GridH2IndexBase {
      * @param name Index name.
      * @param pk Primary key.
      * @param colsList Index columns.
+     * @param inlineSize Inline size.
      * @throws IgniteCheckedException If failed.
      */
     public H2TreeIndex(
-        GridCacheContext<?,?> cctx,
+        GridCacheContext<?, ?> cctx,
         GridH2Table tbl,
         String name,
         boolean pk,
-        List<IndexColumn> colsList
+        List<IndexColumn> colsList,
+        int inlineSize
     ) throws IgniteCheckedException {
         this.cctx = cctx;
         IndexColumn[] cols = colsList.toArray(new 
IndexColumn[colsList.size()]);
@@ -83,34 +101,151 @@ public class H2TreeIndex extends GridH2IndexBase {
 
             RootPage page = cctx.offheap().rootPageForIndex(name);
 
+            inlineIdxs = getAvailableInlineColumns(cols);
+
             tree = new H2Tree(name, cctx.offheap().reuseListForIndex(name), 
cctx.cacheId(),
                 dbMgr.pageMemory(), cctx.shared().wal(), 
cctx.offheap().globalRemoveId(),
-                tbl.rowFactory(), page.pageId().pageId(), page.isAllocated()) {
+                tbl.rowFactory(), page.pageId().pageId(), page.isAllocated(), 
computeInlineSize(inlineIdxs, inlineSize)) {
                 @Override protected int compare(BPlusIO<SearchRow> io, long 
pageAddr, int idx, SearchRow row)
                     throws IgniteCheckedException {
-                    return compareRows(getRow(io, pageAddr, idx), row);
+
+                    if (inlineSize() == 0)
+                        return compareRows(getRow(io, pageAddr, idx), row);
+                    else {
+                        int off = io.offset(idx);
+
+                        int fieldOff = 0;
+
+                        int lastIdxUsed = 0;
+
+                        for (int i = 0; i < inlineIdxs.size(); i++) {
+                            InlineIndexHelper inlineIdx = inlineIdxs.get(i);
+
+                            Value v2 = row.getValue(inlineIdx.columnIndex());
+
+                            if (v2 == null)
+                                return 0;
+
+                            Value v1 = inlineIdx.get(pageAddr, off + fieldOff, 
inlineSize() - fieldOff);
+
+                            if (v1 == null)
+                                break;
+
+                            int c = compareValues(v1, v2, 
inlineIdx.sortType());
+
+                            if (!canRelyOnCompare(c, v1, v2, inlineIdx))
+                                break;
+
+                            lastIdxUsed++;
+
+                            if (c != 0)
+                                return c;
+
+                            fieldOff += inlineIdx.fullSize(pageAddr, off + 
fieldOff);
+
+                            if (fieldOff > inlineSize())
+                                break;
+                        }
+
+                        SearchRow rowData = getRow(io, pageAddr, idx);
+
+                        for (int i = lastIdxUsed, len = indexColumns.length; i 
< len; i++) {
+                            int idx0 = columnIds[i];
+
+                            Value v2 = row.getValue(idx0);
+                            if (v2 == null) {
+                                // Can't compare further.
+                                return 0;
+                            }
+
+                            Value v1 = rowData.getValue(idx0);
+
+                            int c = compareValues(v1, v2, 
indexColumns[i].sortType);
+                            if (c != 0)
+                                return c;
+                        }
+
+                        return 0;
+                    }
                 }
             };
         }
-        else
+        else {
             // We need indexes on the client node, but index will not contain 
any data.
             tree = null;
+            inlineIdxs = null;
+        }
 
         initDistributedJoinMessaging(tbl);
     }
 
     /**
+     * @param cols Columns array.
+     * @return List of {@link InlineIndexHelper} objects.
+     */
+    private List<InlineIndexHelper> getAvailableInlineColumns(IndexColumn[] 
cols) {
+
+        // todo: null
+        List<InlineIndexHelper> res = new ArrayList<>();
+
+        for (int i = 0; i < cols.length; i++) {
+            IndexColumn col = cols[i];
+
+            if 
(!InlineIndexHelper.AVAILABLE_TYPES.contains(col.column.getType()))
+                break;
+
+            InlineIndexHelper idx = new 
InlineIndexHelper(col.column.getType(), col.column.getColumnId(), col.sortType);
+
+            res.add(idx);
+        }
+
+        return res;
+    }
+
+    /**
+     * @return Tree updated in current thread.
+     */
+    public static H2TreeIndex getCurrentIndex() {
+        return currentIndex.get();
+    }
+
+    /**
+     * @param a First value.
+     * @param b Second Value.
+     * @param sortType Sort type.
+     * @return Compare result.
+     */
+    private int compareValues(Value a, Value b, int sortType) {
+        if (a == b)
+            return 0;
+
+        int comp = table.compareTypeSafe(a, b);
+
+        if ((sortType & SortOrder.DESCENDING) != 0)
+            comp = -comp;
+
+        return comp;
+    }
+
+    /**
      * @return Tree.
      */
     public H2Tree tree() {
         return tree;
     }
 
+    /**
+     * @return InlineIndexHelper list.
+     */
+    public List<InlineIndexHelper> inlineIndexes() {
+        return inlineIdxs;
+    }
+
     /** {@inheritDoc} */
     @Override public Cursor find(Session ses, SearchRow lower, SearchRow 
upper) {
         try {
             IndexingQueryFilter f = threadLocalFilter();
-            IgniteBiPredicate<Object,Object> p = null;
+            IgniteBiPredicate<Object, Object> p = null;
 
             if (f != null) {
                 String spaceName = getTable().spaceName();
@@ -138,41 +273,59 @@ public class H2TreeIndex extends GridH2IndexBase {
     /** {@inheritDoc} */
     @Override public GridH2Row put(GridH2Row row) {
         try {
+            currentIndex.set(this);
+
             return tree.put(row);
         }
         catch (IgniteCheckedException e) {
             throw DbException.convert(e);
         }
+        finally {
+            currentIndex.remove();
+        }
     }
 
     /** {@inheritDoc} */
     @Override public boolean putx(GridH2Row row) {
         try {
+            currentIndex.set(this);
+
             return tree.putx(row);
         }
         catch (IgniteCheckedException e) {
             throw DbException.convert(e);
         }
+        finally {
+            currentIndex.remove();
+        }
     }
 
     /** {@inheritDoc} */
     @Override public GridH2Row remove(SearchRow row) {
         try {
+            currentIndex.set(this);
             return tree.remove(row);
         }
         catch (IgniteCheckedException e) {
             throw DbException.convert(e);
         }
+        finally {
+            currentIndex.remove();
+        }
     }
 
     /** {@inheritDoc} */
     @Override public void removex(SearchRow row) {
         try {
+            currentIndex.set(this);
             tree.removex(row);
         }
         catch (IgniteCheckedException e) {
             throw DbException.convert(e);
         }
+        finally {
+            currentIndex.remove();
+        }
     }
 
     /** {@inheritDoc} */
@@ -262,4 +415,67 @@ public class H2TreeIndex extends GridH2IndexBase {
             throw DbException.convert(e);
         }
     }
+
+    /**
+     * @param inlineIdxs Inline index helpers.
+     * @param cfgInlineSize Inline size from cache config.
+     * @return Inline size.
+     */
+    private int computeInlineSize(List<InlineIndexHelper> inlineIdxs, int 
cfgInlineSize) {
+        int maxSize = PageIO.MAX_PAYLOAD_SIZE;
+
+        int propSize = 
IgniteSystemProperties.getInteger(IgniteSystemProperties.IGNITE_MAX_INDEX_PAYLOAD_SIZE,
+            IGNITE_MAX_INDEX_PAYLOAD_SIZE_DEFAULT);
+
+        if (cfgInlineSize == 0)
+            return 0;
+
+        if (F.isEmpty(inlineIdxs))
+            return 0;
+
+        if (cfgInlineSize == -1) {
+            if (propSize == 0)
+                return 0;
+
+            int size = 0;
+
+            for (int i = 0; i < inlineIdxs.size(); i++) {
+                InlineIndexHelper idxHelper = inlineIdxs.get(i);
+                if (idxHelper.size() <= 0) {
+                    size = propSize;
+                    break;
+                }
+                // 1 byte type + size
+                size += idxHelper.size() + 1;
+            }
+
+            return Math.min(maxSize, size);
+        }
+        else
+            return Math.min(maxSize, cfgInlineSize);
+    }
+
+    /**
+     * @param c Compare result.
+     * @param shortVal Short value.
+     * @param v2 Second value;
+     * @param inlineIdx Index helper.
+     * @return {@code true} if we can rely on compare result.
+     */
+    protected static boolean canRelyOnCompare(int c, Value shortVal, Value v2, 
InlineIndexHelper inlineIdx) {
+        if (inlineIdx.type() == Value.STRING) {
+            if (c == 0 && shortVal.getType() != Value.NULL && v2.getType() != 
Value.NULL)
+                return false;
+
+            if (shortVal.getType() != Value.NULL
+                && v2.getType() != Value.NULL
+                && ((c < 0 && inlineIdx.sortType() == SortOrder.ASCENDING) || 
(c > 0 && inlineIdx.sortType() == SortOrder.DESCENDING))
+                && shortVal.getString().length() <= v2.getString().length()) {
+                // Can't rely on compare, should use full string.
+                return false;
+            }
+        }
+
+        return true;
+    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/8f836cb0/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/InlineIndexHelper.java
----------------------------------------------------------------------
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
new file mode 100644
index 0000000..ff44df5
--- /dev/null
+++ 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/InlineIndexHelper.java
@@ -0,0 +1,279 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.h2.database;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
+import org.apache.ignite.internal.pagemem.PageUtils;
+import org.h2.table.IndexColumn;
+import org.h2.value.Value;
+import org.h2.value.ValueBoolean;
+import org.h2.value.ValueByte;
+import org.h2.value.ValueInt;
+import org.h2.value.ValueLong;
+import org.h2.value.ValueNull;
+import org.h2.value.ValueString;
+
+/**
+ * Helper class for in-page indexes.
+ */
+public class InlineIndexHelper {
+    private static final Charset CHARSET = StandardCharsets.UTF_8;
+
+    /** */
+    public static final List<Integer> AVAILABLE_TYPES = Arrays.asList(
+        Value.BOOLEAN,
+        Value.BYTE,
+        Value.SHORT,
+        Value.INT,
+        Value.LONG,
+        Value.STRING
+    );
+
+    /** */
+    private final int type;
+
+    /** */
+    private final int colIdx;
+
+    /** */
+    private final int sortType;
+
+    /**
+     * @param type Index type (see {@link Value}).
+     * @param colIdx Index column index.
+     * @param sortType Column sort type (see {@link IndexColumn#sortType}).
+     */
+    public InlineIndexHelper(int type, int colIdx, int sortType) {
+        this.type = type;
+        this.colIdx = colIdx;
+        this.sortType = sortType;
+    }
+
+    /**
+     * @return Index type.
+     */
+    public int type() {
+        return type;
+    }
+
+    /**
+     * @return Column index.
+     */
+    public int columnIndex() {
+        return colIdx;
+    }
+
+    /**
+     * @return Sort type.
+     */
+    public int sortType() {
+        return sortType;
+    }
+
+    /**
+     * @return Value size.
+     */
+    public short size() {
+        switch (type) {
+            case Value.BOOLEAN:
+            case Value.BYTE:
+                return 1;
+
+            case Value.SHORT:
+                return 2;
+
+            case Value.INT:
+                return 4;
+
+            case Value.LONG:
+                return 8;
+
+            case Value.STRING:
+                return -1;
+
+            default:
+                throw new UnsupportedOperationException("no get operation for 
fast index type " + type);
+        }
+    }
+
+    /**
+     * @param pageAddr Page address.
+     * @param off Offset.
+     * @return Full size in page.
+     */
+    public int fullSize(long pageAddr, int off) {
+        int type = PageUtils.getByte(pageAddr, off);
+
+        if (type == Value.NULL)
+            return 1;
+
+        switch (type) {
+            case Value.BOOLEAN:
+            case Value.BYTE:
+            case Value.INT:
+            case Value.SHORT:
+            case Value.LONG:
+                return size() + 1;
+
+            case Value.STRING:
+                return PageUtils.getShort(pageAddr, off + 1) + 3;
+
+            default:
+                throw new UnsupportedOperationException("no get operation for 
fast index type " + type);
+        }
+    }
+
+    /**
+     * @param pageAddr Page address.
+     * @param off Offset.
+     * @return Value.
+     */
+    public Value get(long pageAddr, int off, int maxSize) {
+        if (size() > 0 && size() + 1 > maxSize)
+            return null;
+
+        int type = PageUtils.getByte(pageAddr, off);
+
+        if (type == Value.UNKNOWN)
+            return null;
+
+        if (type == Value.NULL)
+            return ValueNull.INSTANCE;
+
+        if (this.type != type)
+            throw new UnsupportedOperationException("invalid fast index type " 
+ type);
+
+        switch (this.type) {
+            case Value.BOOLEAN:
+                return ValueBoolean.get(PageUtils.getByte(pageAddr, off + 1) 
!= 0);
+
+            case Value.BYTE:
+                return ValueByte.get(PageUtils.getByte(pageAddr, off + 1));
+
+            case Value.SHORT:
+                return ValueInt.get(PageUtils.getShort(pageAddr, off + 1));
+
+            case Value.INT:
+                return ValueInt.get(PageUtils.getInt(pageAddr, off + 1));
+
+            case Value.LONG:
+                return ValueLong.get(PageUtils.getLong(pageAddr, off + 1));
+
+            case Value.STRING:
+                short size = PageUtils.getShort(pageAddr, off + 1);
+                return ValueString.get(new String(PageUtils.getBytes(pageAddr, 
off + 3, size), CHARSET));
+
+            default:
+                throw new UnsupportedOperationException("no get operation for 
fast index type " + type);
+        }
+    }
+
+    /**
+     * @param pageAddr Page address.
+     * @param off Offset.
+     * @param val Value.
+     * @return NUmber of bytes saved.
+     */
+    public int put(long pageAddr, int off, Value val, int maxSize) {
+        if (size() > 0 && size() + 1 > maxSize)
+            return 0;
+
+        if (val.getType() == Value.NULL) {
+            PageUtils.putByte(pageAddr, off, (byte)Value.NULL);
+            return 1;
+        }
+
+        if (val.getType() != type)
+            throw new UnsupportedOperationException("value type doesn't 
match");
+
+        switch (type) {
+            case Value.BOOLEAN:
+                PageUtils.putByte(pageAddr, off, (byte)val.getType());
+                PageUtils.putByte(pageAddr, off + 1, (byte)(val.getBoolean() ? 
1 : 0));
+                return size() + 1;
+
+            case Value.BYTE:
+                PageUtils.putByte(pageAddr, off, (byte)val.getType());
+                PageUtils.putByte(pageAddr, off + 1, val.getByte());
+                return size() + 1;
+
+            case Value.SHORT:
+                PageUtils.putByte(pageAddr, off, (byte)val.getType());
+                PageUtils.putShort(pageAddr, off + 1, val.getShort());
+                return size() + 1;
+
+            case Value.INT:
+                PageUtils.putByte(pageAddr, off, (byte)val.getType());
+                PageUtils.putInt(pageAddr, off + 1, val.getInt());
+                return size() + 1;
+
+            case Value.LONG:
+                PageUtils.putByte(pageAddr, off, (byte)val.getType());
+                PageUtils.putLong(pageAddr, off + 1, val.getLong());
+                return size() + 1;
+
+            case Value.STRING:
+                byte[] s;
+                if (val.getString().getBytes(CHARSET).length + 3 <= maxSize)
+                    s = val.getString().getBytes(CHARSET);
+                else
+                    s = toBytes(val.getString(), maxSize - 3);
+
+                if (s == null) {
+                    // Can't fit anything to
+                    PageUtils.putByte(pageAddr, off, (byte)Value.UNKNOWN);
+                    return 0;
+                }
+                else {
+                    PageUtils.putByte(pageAddr, off, (byte)val.getType());
+                    PageUtils.putShort(pageAddr, off + 1, (short)s.length);
+                    PageUtils.putBytes(pageAddr, off + 3, s);
+                    return s.length + 3;
+                }
+
+            default:
+                throw new UnsupportedOperationException("no get operation for 
fast index type " + type);
+        }
+    }
+
+    /**
+     * Convert String to byte[] with size limit, according to UTF-8 encoding.
+     *
+     * @param s String.
+     * @param limit Size limit.
+     * @return byte[].
+     */
+    public static byte[] toBytes(String s, int limit) {
+        byte[] bytes = s.getBytes(CHARSET);
+        if (bytes.length <= limit)
+            return bytes;
+
+        for (int i = bytes.length - 1; i > 0; i--) {
+            if ((bytes[i] & 0xc0) != 0x80 && i <= limit) {
+                byte[] res = new byte[i];
+                System.arraycopy(bytes, 0, res, 0, i);
+                return res;
+            }
+        }
+
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/8f836cb0/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2ExtrasInnerIO.java
----------------------------------------------------------------------
diff --git 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2ExtrasInnerIO.java
 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2ExtrasInnerIO.java
new file mode 100644
index 0000000..5a8a681
--- /dev/null
+++ 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2ExtrasInnerIO.java
@@ -0,0 +1,135 @@
+/*
+ * 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.io;
+
+import java.util.List;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.pagemem.PageUtils;
+import org.apache.ignite.internal.processors.cache.database.tree.BPlusTree;
+import org.apache.ignite.internal.processors.cache.database.tree.io.BPlusIO;
+import 
org.apache.ignite.internal.processors.cache.database.tree.io.BPlusInnerIO;
+import org.apache.ignite.internal.processors.cache.database.tree.io.IOVersions;
+import org.apache.ignite.internal.processors.cache.database.tree.io.PageIO;
+import org.apache.ignite.internal.processors.query.h2.database.H2Tree;
+import org.apache.ignite.internal.processors.query.h2.database.H2TreeIndex;
+import 
org.apache.ignite.internal.processors.query.h2.database.InlineIndexHelper;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
+import org.h2.result.SearchRow;
+
+/**
+ * Inner page for H2 row references.
+ */
+public class H2ExtrasInnerIO extends BPlusInnerIO<SearchRow> {
+    /** Payload size. */
+    private final int payloadSize;
+
+    /** */
+    public static void register() {
+        for (short payload = 1; payload <= PageIO.MAX_PAYLOAD_SIZE; payload++)
+            
PageIO.registerH2ExtraInner(getVersions((short)(PageIO.T_H2_EX_REF_INNER_START 
+ payload - 1), payload));
+    }
+
+    /**
+     * @param payload Payload size.
+     * @return IOVersions for given payload.
+     */
+    public static IOVersions<? extends BPlusInnerIO<SearchRow>> 
getVersions(int payload) {
+        assert payload >= 0 && payload <= PageIO.MAX_PAYLOAD_SIZE;
+
+        if (payload == 0)
+            return H2InnerIO.VERSIONS;
+        else
+            return 
(IOVersions<BPlusInnerIO<SearchRow>>)PageIO.getInnerVersions((short)(payload - 
1));
+    }
+
+    /** */
+    private static IOVersions<H2ExtrasInnerIO> getVersions(short type, short 
payload) {
+        return new IOVersions<>(new H2ExtrasInnerIO(type, 1, payload));
+    }
+
+    /**
+     * @param type Page type.
+     * @param ver Page format version.
+     * @param payloadSize Payload size.
+     */
+    private H2ExtrasInnerIO(short type, int ver, int payloadSize) {
+        super(type, ver, true, 8 + payloadSize);
+        this.payloadSize = payloadSize;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void storeByOffset(long pageAddr, int off, SearchRow row) 
{
+        GridH2Row row0 = (GridH2Row)row;
+
+        assert row0.link != 0 : row0;
+
+        H2TreeIndex currentIdx = H2TreeIndex.getCurrentIndex();
+        assert currentIdx != null;
+        List<InlineIndexHelper> inlineIdx = currentIdx.inlineIndexes();
+
+        assert inlineIdx != null;
+
+        int fieldOff = 0;
+
+        for (int i = 0; i < inlineIdx.size(); i++) {
+            InlineIndexHelper idx = inlineIdx.get(i);
+            int size = idx.put(pageAddr, off + fieldOff, 
row.getValue(idx.columnIndex()), payloadSize - fieldOff);
+            if (size == 0)
+                break;
+            fieldOff += size;
+        }
+
+        PageUtils.putLong(pageAddr, off + payloadSize, row0.link);
+    }
+
+    /** {@inheritDoc} */
+    @Override public SearchRow getLookupRow(BPlusTree<SearchRow, ?> tree, long 
pageAddr, int idx)
+        throws IgniteCheckedException {
+        long link = getLink(pageAddr, idx);
+
+        assert link != 0;
+
+        GridH2Row r0 = ((H2Tree)tree).getRowFactory().getRow(link);
+
+        return r0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void store(long dstPageAddr, int dstIdx, 
BPlusIO<SearchRow> srcIo, long srcPageAddr, int srcIdx) {
+        int srcOff = srcIo.offset(srcIdx);
+
+        byte[] payload = PageUtils.getBytes(srcPageAddr, srcOff, payloadSize);
+        long link = PageUtils.getLong(srcPageAddr, srcOff + payloadSize);
+
+        assert link != 0;
+
+        int dstOff = offset(dstIdx);
+
+        PageUtils.putBytes(dstPageAddr, dstOff, payload);
+        PageUtils.putLong(dstPageAddr, dstOff + payloadSize, link);
+    }
+
+    /**
+     * @param pageAddr Page address.
+     * @param idx Index.
+     * @return Link to row.
+     */
+    private long getLink(long pageAddr, int idx) {
+        return PageUtils.getLong(pageAddr, offset(idx) + payloadSize);
+    }
+}

Reply via email to