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 2018/11/13 12:10:39 UTC

ignite git commit: IGNITE-8386: SQL: unwrapped key fields for PK index. Previously we built PK index over the whole "_KEY". This worked fine for primitive keys, but led to unusable PK index in case of composite key (what affected affinity co-location cas

Repository: ignite
Updated Branches:
  refs/heads/master 0f387681b -> c3462ffff


IGNITE-8386: SQL: unwrapped key fields for PK index. Previously we built PK index over the whole "_KEY". This worked fine for primitive keys, but led to unusable PK index in case of composite key (what affected affinity co-location cases as well because in this case key is complex by default). Now we build index of _KEY fields rather than _KEY itself. This is only possible for caches created with "CREATE TABLE", because in this case generated keys do not contain any hidden non-indexed fields, which might broke PK uniqueness property otherwise. In future we will unwrap PKs embedded to secondary indexes as well (see IGNITE-10217). This closes #4184.


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

Branch: refs/heads/master
Commit: c3462fffff3b09c52c4c0bcb55867015449a2fb6
Parents: 0f38768
Author: Yuriy Gerzhedovich <yg...@gridgain.com>
Authored: Tue Nov 13 15:10:31 2018 +0300
Committer: devozerov <vo...@gridgain.com>
Committed: Tue Nov 13 15:10:31 2018 +0300

----------------------------------------------------------------------
 modules/compatibility/pom.xml                   |   7 +
 .../IgnitePKIndexesMigrationToUnwrapPkTest.java | 247 ++++++++++++++
 .../testframework/junits/Dependency.java        |  28 +-
 .../junits/IgniteCompatibilityAbstractTest.java |  10 +-
 .../processors/cache/GridCacheProcessor.java    |   9 +-
 .../cache/persistence/tree/io/BPlusMetaIO.java  |  20 +-
 .../processors/query/GridQueryIndexing.java     |   2 +-
 .../processors/query/GridQueryProcessor.java    |  16 +-
 ...IgniteClientCacheInitializationFailTest.java |   2 +-
 .../processors/query/h2/H2TableDescriptor.java  | 123 ++++++-
 .../processors/query/h2/IgniteH2Indexing.java   |  13 +-
 .../processors/query/h2/database/H2Tree.java    |  69 +++-
 .../query/h2/database/H2TreeIndex.java          |  87 ++++-
 .../index/ComplexPrimaryKeyUnwrapSelfTest.java  | 337 +++++++++++++++++++
 .../IgniteBinaryCacheQueryTestSuite.java        |  10 +-
 15 files changed, 914 insertions(+), 66 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/c3462fff/modules/compatibility/pom.xml
----------------------------------------------------------------------
diff --git a/modules/compatibility/pom.xml b/modules/compatibility/pom.xml
index 7f24efe..68948e0 100644
--- a/modules/compatibility/pom.xml
+++ b/modules/compatibility/pom.xml
@@ -48,6 +48,13 @@
         </dependency>
 
         <dependency>
+            <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-indexing</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
             <version>${guava.version}</version>

http://git-wip-us.apache.org/repos/asf/ignite/blob/c3462fff/modules/compatibility/src/test/java/org/apache/ignite/compatibility/persistence/IgnitePKIndexesMigrationToUnwrapPkTest.java
----------------------------------------------------------------------
diff --git a/modules/compatibility/src/test/java/org/apache/ignite/compatibility/persistence/IgnitePKIndexesMigrationToUnwrapPkTest.java b/modules/compatibility/src/test/java/org/apache/ignite/compatibility/persistence/IgnitePKIndexesMigrationToUnwrapPkTest.java
new file mode 100644
index 0000000..22ccae0
--- /dev/null
+++ b/modules/compatibility/src/test/java/org/apache/ignite/compatibility/persistence/IgnitePKIndexesMigrationToUnwrapPkTest.java
@@ -0,0 +1,247 @@
+
+/*
+ * 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.compatibility.persistence;
+
+import java.util.Collection;
+import java.util.List;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.compatibility.testframework.junits.Dependency;
+import org.apache.ignite.configuration.DataRegionConfiguration;
+import org.apache.ignite.configuration.DataStorageConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.processors.cache.GridCacheAbstractFullApiSelfTest;
+import org.apache.ignite.lang.IgniteInClosure;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Test to check that starting node with PK index of the old format present doesn't break anything.
+ */
+public class IgnitePKIndexesMigrationToUnwrapPkTest extends IgnitePersistenceCompatibilityAbstractTest {
+    /** */
+    private static String TABLE_NAME = "TEST_IDX_TABLE";
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+        new ConfigurationClosure().apply(cfg);
+
+        return cfg;
+    }
+
+    /** {@inheritDoc} */
+    @NotNull @Override protected Collection<Dependency> getDependencies(String igniteVer) {
+        Collection<Dependency> dependencies = super.getDependencies(igniteVer);
+
+        dependencies.add(new Dependency("indexing", null, "ignite-indexing", igniteVer));
+        dependencies.add(new Dependency("h2", "com.h2database", "h2", "1.4.195", true));
+
+        return dependencies;
+    }
+
+    /**
+     * Tests opportunity to read data from previous Ignite DB version.
+     *
+     * @throws Exception If failed.
+     */
+    public void testSecondaryIndexesMigration_2_5() throws Exception {
+        doTestStartupWithOldVersion("2.5.0");
+    }
+
+    /**
+     * Tests opportunity to read data from previous Ignite DB version.
+     *
+     * @throws Exception If failed.
+     */
+    public void testSecondaryIndexesMigration_2_4() throws Exception {
+        doTestStartupWithOldVersion("2.4.0");
+    }
+
+    /**
+     * Tests opportunity to read data from previous Ignite DB version.
+     *
+     * @param ver 3-digits version of ignite
+     * @throws Exception If failed.
+     */
+    @SuppressWarnings("unchecked")
+    private void doTestStartupWithOldVersion(String ver) throws Exception {
+        try {
+            startGrid(1, ver, new ConfigurationClosure(), new PostStartupClosure(true));
+
+            stopAllGrids();
+
+            IgniteEx igniteEx = startGrid(0);
+
+            new PostStartupClosure(false).apply(igniteEx);
+
+            igniteEx.active(true);
+
+            assertDontUsingPkIndex(igniteEx, TABLE_NAME);
+
+            String newTblName = TABLE_NAME + "_NEW";
+
+            initializeTable(igniteEx, newTblName);
+
+            checkUsingIndexes(igniteEx, newTblName);
+
+            igniteEx.active(false);
+        }
+        finally {
+            stopAllGrids();
+        }
+    }
+
+    /** */
+    private static class PostStartupClosure implements IgniteInClosure<Ignite> {
+
+        /** */
+        boolean createTable;
+
+        /**
+         * @param createTable {@code true} In case table should be created
+         */
+        public PostStartupClosure(boolean createTable) {
+            this.createTable = createTable;
+        }
+
+        /** {@inheritDoc} */
+        @Override public void apply(Ignite ignite) {
+            ignite.active(true);
+
+            IgniteEx igniteEx = (IgniteEx)ignite;
+
+            if (createTable)
+                initializeTable(igniteEx, TABLE_NAME);
+
+            assertDontUsingPkIndex(igniteEx, TABLE_NAME);
+
+            ignite.active(false);
+        }
+    }
+
+    /**
+     * @param igniteEx Ignite instance.
+     * @param tblName Table name.
+     */
+    @NotNull private static void initializeTable(IgniteEx igniteEx, String tblName) {
+        executeSql(igniteEx, "CREATE TABLE " + tblName + " (id int, name varchar, age int, company varchar, city varchar, " +
+            "primary key (id, name, city))");
+
+        executeSql(igniteEx, "INSERT INTO " + tblName + " (id, name, age, company, city) VALUES(1,'name',2,'company', 'city')");
+
+    }
+
+    /**
+     * Run SQL statement on specified node.
+     *
+     * @param node node to execute query.
+     * @param stmt Statement to run.
+     * @param args arguments of statements
+     * @return Run result.
+     */
+    private static List<List<?>> executeSql(IgniteEx node, String stmt, Object... args) {
+        return node.context().query().querySqlFields(new SqlFieldsQuery(stmt).setArgs(args), true).getAll();
+    }
+
+    /**
+     * Check using PK indexes for few cases.
+     *
+     * @param ignite Ignite instance.
+     * @param tblName name of table which should be checked to using PK indexes.
+     */
+    private static void checkUsingIndexes(IgniteEx ignite, String tblName) {
+        String explainSQL = "explain SELECT * FROM " + tblName + " WHERE ";
+
+        List<List<?>> results = executeSql(ignite, explainSQL + "id=1");
+
+        assertUsingPkIndex(results);
+
+        results = executeSql(ignite, explainSQL + "id=1 and name='name'");
+
+        assertUsingPkIndex(results);
+
+        results = executeSql(ignite, explainSQL + "id=1 and name='name' and city='city' and age=2");
+
+        assertUsingPkIndex(results);
+    }
+
+    /**
+     * Check that explain plan result shown using PK index and don't use scan.
+     *
+     * @param results Result list of explain of query.
+     */
+    private static void assertUsingPkIndex(List<List<?>> results) {
+        assertEquals(2, results.size());
+
+        String explainPlan = (String)results.get(0).get(0);
+
+        assertTrue(explainPlan.contains("\"_key_PK"));
+
+        assertFalse(explainPlan.contains("_SCAN_"));
+    }
+
+    /**
+     * Check that explain plan result shown don't use PK index and use scan.
+     *
+     * @param igniteEx Ignite instance.
+     * @param tblName Name of table.
+     */
+    private static void assertDontUsingPkIndex(IgniteEx igniteEx, String tblName) {
+        List<List<?>> results = executeSql(igniteEx, "explain SELECT * FROM " + tblName + " WHERE id=1");
+
+        assertEquals(2, results.size());
+
+        String explainPlan = (String)results.get(0).get(0);
+
+        System.out.println(explainPlan);
+
+        assertFalse(explainPlan, explainPlan.contains("\"_key_PK\""));
+
+        assertTrue(explainPlan, explainPlan.contains("_SCAN_"));
+    }
+
+    /** */
+    private static class ConfigurationClosure implements IgniteInClosure<IgniteConfiguration> {
+        /** {@inheritDoc} */
+        @Override public void apply(IgniteConfiguration cfg) {
+            cfg.setLocalHost("127.0.0.1");
+
+            TcpDiscoverySpi disco = new TcpDiscoverySpi();
+            disco.setIpFinder(GridCacheAbstractFullApiSelfTest.LOCAL_IP_FINDER);
+
+            cfg.setDiscoverySpi(disco);
+
+            cfg.setPeerClassLoadingEnabled(false);
+
+            DataStorageConfiguration memCfg = new DataStorageConfiguration()
+                .setDefaultDataRegionConfiguration(
+                    new DataRegionConfiguration().setPersistenceEnabled(true)
+                        .setInitialSize(1024 * 1024 * 10).setMaxSize(1024 * 1024 * 15))
+                .setSystemRegionInitialSize(1024 * 1024 * 10)
+                .setSystemRegionMaxSize(1024 * 1024 * 15);
+
+            cfg.setDataStorageConfiguration(memCfg);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/c3462fff/modules/compatibility/src/test/java/org/apache/ignite/compatibility/testframework/junits/Dependency.java
----------------------------------------------------------------------
diff --git a/modules/compatibility/src/test/java/org/apache/ignite/compatibility/testframework/junits/Dependency.java b/modules/compatibility/src/test/java/org/apache/ignite/compatibility/testframework/junits/Dependency.java
index 56da0e9..a30fb1e 100644
--- a/modules/compatibility/src/test/java/org/apache/ignite/compatibility/testframework/junits/Dependency.java
+++ b/modules/compatibility/src/test/java/org/apache/ignite/compatibility/testframework/junits/Dependency.java
@@ -40,6 +40,9 @@ public class Dependency {
     /** Test flag. Test jar should have {@code true} value. Default is {@code false}. */
     private boolean test;
 
+    /** */
+    private String exludePathFromClassPath;
+
     /**
      * Creates dependency.
      *
@@ -71,13 +74,36 @@ public class Dependency {
      * @param version Version. Null means default Ignite version is to be used. M
      */
     public Dependency(String locModuleName, @Nullable String grpName, String artifactName, @Nullable String version) {
-        this.locModuleName = locModuleName;
+        this(locModuleName, grpName, artifactName, version, false);
+    }
+
+    /**
+     * @param excludeName Local module name or part of exclude path.
+     * @param grpName Group name. Null means ignite default group name.
+     * @param artifactName Artifact name (artifact ID) without group na
+     * @param version Version. Null means default Ignite version is to be used. M
+     * @param exludeNotLocModule {@code true} In case param @excludeName should exclude path instead of local module.
+     */
+    public Dependency(String excludeName, @Nullable String grpName, String artifactName, @Nullable String version,
+        boolean exludeNotLocModule) {
+        if (exludeNotLocModule)
+            this.exludePathFromClassPath = excludeName;
+        else
+            this.locModuleName = excludeName;
+
         this.groupName = grpName;
         this.artifactName = artifactName;
         this.version = version;
     }
 
     /**
+     * @return Path to exclude form classpath.
+     */
+    public String excludePathFromClassPath() {
+        return exludePathFromClassPath;
+    }
+
+    /**
      * @return path based on local module name to exclude from classpath
      */
     public String localPathTemplate() {

http://git-wip-us.apache.org/repos/asf/ignite/blob/c3462fff/modules/compatibility/src/test/java/org/apache/ignite/compatibility/testframework/junits/IgniteCompatibilityAbstractTest.java
----------------------------------------------------------------------
diff --git a/modules/compatibility/src/test/java/org/apache/ignite/compatibility/testframework/junits/IgniteCompatibilityAbstractTest.java b/modules/compatibility/src/test/java/org/apache/ignite/compatibility/testframework/junits/IgniteCompatibilityAbstractTest.java
index 2301717..71ff9ff 100644
--- a/modules/compatibility/src/test/java/org/apache/ignite/compatibility/testframework/junits/IgniteCompatibilityAbstractTest.java
+++ b/modules/compatibility/src/test/java/org/apache/ignite/compatibility/testframework/junits/IgniteCompatibilityAbstractTest.java
@@ -21,6 +21,7 @@ import java.io.File;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Objects;
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.CountDownLatch;
@@ -168,14 +169,16 @@ public abstract class IgniteCompatibilityAbstractTest extends GridCommonAbstract
 
                 final Collection<Dependency> dependencies = getDependencies(ver);
 
-                Set<String> excluded = dependencies.stream().map(Dependency::localPathTemplate).collect(Collectors.toSet());
+                Set<String> excludedModules = dependencies.stream().map(Dependency::localPathTemplate).collect(Collectors.toSet());
+
+                Set<String> excludedPaths = dependencies.stream().map(Dependency::excludePathFromClassPath).filter(Objects::nonNull).collect(Collectors.toSet());
 
                 StringBuilder pathBuilder = new StringBuilder();
 
                 for (URL url : CompatibilityTestsUtils.classLoaderUrls(CLASS_LOADER)) {
                     String path = url.getPath();
 
-                    if (excluded.stream().noneMatch(path::contains))
+                    if (excludedModules.stream().noneMatch(path::contains) && excludedPaths.stream().noneMatch(path::contains))
                         pathBuilder.append(path).append(File.pathSeparator);
                 }
 
@@ -243,6 +246,9 @@ public abstract class IgniteCompatibilityAbstractTest extends GridCommonAbstract
         dependencies.add(new Dependency("core", "ignite-core"));
         dependencies.add(new Dependency("core", "ignite-core", true));
 
+        //Just to exclude indexing module
+        dependencies.add(new Dependency("indexing", "ignite-core"));
+
         return dependencies;
     }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/c3462fff/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java
index 2e2fa47..7ba4ff9 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java
@@ -1257,7 +1257,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
                     boolean rmvIdx = !cache.context().group().persistenceEnabled();
 
                     ctx.query().onCacheStop0(cctx, rmvIdx);
-                    ctx.query().onCacheStart0(cctx, desc.schema());
+                    ctx.query().onCacheStart0(cctx, desc.schema(), desc.sql());
                 }
             }
         }
@@ -2138,7 +2138,8 @@ public class GridCacheProcessor extends GridProcessorAdapter {
                                 cctx,
                                 cacheInfo.getCacheDescriptor().schema() != null
                                     ? cacheInfo.getCacheDescriptor().schema()
-                                    : new QuerySchema()
+                                    : new QuerySchema(),
+                                cacheInfo.getCacheDescriptor().sql()
                             );
                         }
 
@@ -2194,7 +2195,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
     ) throws IgniteCheckedException {
         GridCacheContext cacheCtx = prepareCacheContext(startCfg, desc, reqNearCfg, exchTopVer, disabledAfterStart);
 
-        ctx.query().onCacheStart(cacheCtx, desc.schema() != null ? desc.schema() : new QuerySchema());
+        ctx.query().onCacheStart(cacheCtx, desc.schema() != null ? desc.schema() : new QuerySchema(), desc.sql());
 
         if (cacheCtx.isRecoveryMode())
             finishRecovery(exchTopVer, cacheCtx);
@@ -2555,7 +2556,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
 
         grp.onCacheStarted(cacheCtx);
 
-        ctx.query().onCacheStart(cacheCtx, desc.schema() != null ? desc.schema() : new QuerySchema());
+        ctx.query().onCacheStart(cacheCtx, desc.schema() != null ? desc.schema() : new QuerySchema(), desc.sql());
 
         if (log.isInfoEnabled()) {
             log.info("Started cache in recovery mode [name=" + cfg.getName() +

http://git-wip-us.apache.org/repos/asf/ignite/blob/c3462fff/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/BPlusMetaIO.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/BPlusMetaIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/BPlusMetaIO.java
index 623951b..fef58fd 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/BPlusMetaIO.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/BPlusMetaIO.java
@@ -28,7 +28,7 @@ import org.apache.ignite.internal.util.GridStringBuilder;
 public class BPlusMetaIO extends PageIO {
     /** */
     public static final IOVersions<BPlusMetaIO> VERSIONS = new IOVersions<>(
-        new BPlusMetaIO(1), new BPlusMetaIO(2)
+        new BPlusMetaIO(1), new BPlusMetaIO(2), new BPlusMetaIO(3)
     );
 
     /** */
@@ -40,6 +40,9 @@ public class BPlusMetaIO extends PageIO {
     /** */
     private final int inlineSizeOff;
 
+    /** */
+    private boolean unwrappedPk;
+
     /**
      * @param ver Page format version.
      */
@@ -50,11 +53,19 @@ public class BPlusMetaIO extends PageIO {
             case 1:
                 inlineSizeOff = -1;
                 refsOff = LVLS_OFF + 1;
+                unwrappedPk = false;
                 break;
 
             case 2:
                 inlineSizeOff = LVLS_OFF + 1;
                 refsOff = inlineSizeOff + 2;
+                unwrappedPk = false;
+                break;
+
+            case 3:
+                inlineSizeOff = LVLS_OFF + 1;
+                refsOff = inlineSizeOff + 2;
+                unwrappedPk = true;
                 break;
 
             default:
@@ -182,6 +193,13 @@ public class BPlusMetaIO extends PageIO {
         return getVersion() > 1 ? PageUtils.getShort(pageAddr, inlineSizeOff) : 0;
     }
 
+    /**
+     * @return {@code true} In case use unwrapped PK.
+     */
+    public boolean unwrappedPk() {
+        return unwrappedPk;
+    }
+
     /** {@inheritDoc} */
     @Override protected void printPage(long addr, int pageSize, GridStringBuilder sb) throws IgniteCheckedException {
         sb.a("BPlusMeta [\n\tlevelsCnt=").a(getLevelsCount(addr))

http://git-wip-us.apache.org/repos/asf/ignite/blob/c3462fff/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexing.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexing.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexing.java
index a8ccc7e..60b0221 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexing.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexing.java
@@ -259,7 +259,7 @@ public interface GridQueryIndexing {
      * @throws IgniteCheckedException If failed.
      * @return {@code True} if type was registered, {@code false} if for some reason it was rejected.
      */
-    public boolean registerType(GridCacheContext cctx, GridQueryTypeDescriptor desc) throws IgniteCheckedException;
+    public boolean registerType(GridCacheContext cctx, GridQueryTypeDescriptor desc, boolean isSql) throws IgniteCheckedException;
 
     /**
      * Updates index. Note that key is unique for cache, so if cache contains multiple indexes

http://git-wip-us.apache.org/repos/asf/ignite/blob/c3462fff/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 7650fce..24f79fc 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
@@ -675,9 +675,10 @@ public class GridQueryProcessor extends GridProcessorAdapter {
      * Use with {@link #busyLock} where appropriate.
      * @param cctx Cache context.
      * @param schema Initial schema.
+     * @param isSql {@code true} in case create cache initialized from SQL.
      * @throws IgniteCheckedException If failed.
      */
-    public void onCacheStart0(GridCacheContext<?, ?> cctx, QuerySchema schema)
+    public void onCacheStart0(GridCacheContext<?, ?> cctx, QuerySchema schema, boolean isSql)
         throws IgniteCheckedException {
 
         cctx.shared().database().checkpointReadLock();
@@ -798,7 +799,7 @@ public class GridQueryProcessor extends GridProcessorAdapter {
                 }
 
                 // Ready to register at this point.
-                registerCache0(cacheName, schemaName, cctx, cands);
+                registerCache0(cacheName, schemaName, cctx, cands, isSql);
 
                 // Warn about possible implicit deserialization.
                 if (!mustDeserializeClss.isEmpty()) {
@@ -849,9 +850,10 @@ public class GridQueryProcessor extends GridProcessorAdapter {
      *
      * @param cctx Cache context.
      * @param schema Index states.
+     * @param isSql {@code true} in case create cache initialized from SQL.
      * @throws IgniteCheckedException If failed.
      */
-    public void onCacheStart(GridCacheContext cctx, QuerySchema schema) throws IgniteCheckedException {
+    public void onCacheStart(GridCacheContext cctx, QuerySchema schema, boolean isSql) throws IgniteCheckedException {
         if (idx == null)
             return;
 
@@ -859,7 +861,7 @@ public class GridQueryProcessor extends GridProcessorAdapter {
             return;
 
         try {
-            onCacheStart0(cctx, schema);
+            onCacheStart0(cctx, schema, isSql);
         }
         finally {
             busyLock.leaveBusy();
@@ -1582,13 +1584,15 @@ public class GridQueryProcessor extends GridProcessorAdapter {
      * @param schemaName Schema name.
      * @param cctx Cache context.
      * @param cands Candidates.
+     * @param isSql {@code true} in case create cache initialized from SQL.
      * @throws IgniteCheckedException If failed.
      */
     private void registerCache0(
         String cacheName,
         String schemaName,
         GridCacheContext<?, ?> cctx,
-        Collection<QueryTypeCandidate> cands
+        Collection<QueryTypeCandidate> cands,
+        boolean isSql
     ) throws IgniteCheckedException {
         synchronized (stateMux) {
             if (idx != null)
@@ -1623,7 +1627,7 @@ public class GridQueryProcessor extends GridProcessorAdapter {
                     }
 
                     if (idx != null)
-                        idx.registerType(cctx, desc);
+                        idx.registerType(cctx, desc, isSql);
                 }
 
                 cacheNames.add(CU.mask(cacheName));

http://git-wip-us.apache.org/repos/asf/ignite/blob/c3462fff/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClientCacheInitializationFailTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClientCacheInitializationFailTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClientCacheInitializationFailTest.java
index 4336a25..69b1655 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClientCacheInitializationFailTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClientCacheInitializationFailTest.java
@@ -329,7 +329,7 @@ public class IgniteClientCacheInitializationFailTest extends GridCommonAbstractT
 
         /** {@inheritDoc} */
         @Override public boolean registerType(GridCacheContext cctx,
-            GridQueryTypeDescriptor desc) throws IgniteCheckedException {
+            GridQueryTypeDescriptor desc, boolean isSql) throws IgniteCheckedException {
             return false;
         }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/c3462fff/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TableDescriptor.java
----------------------------------------------------------------------
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 988ec2f..aa66939 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
@@ -17,12 +17,18 @@
 
 package org.apache.ignite.internal.processors.query.h2;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteException;
 import org.apache.ignite.cache.QueryIndexType;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.query.GridQueryIndexDescriptor;
+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.opt.GridH2IndexBase;
@@ -36,10 +42,7 @@ import org.h2.index.Index;
 import org.h2.result.SortOrder;
 import org.h2.table.Column;
 import org.h2.table.IndexColumn;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
+import org.jetbrains.annotations.NotNull;
 
 import static org.apache.ignite.internal.processors.query.h2.opt.GridH2KeyValueRowOnheap.KEY_COL;
 
@@ -47,6 +50,15 @@ import static org.apache.ignite.internal.processors.query.h2.opt.GridH2KeyValueR
  * Information about table in database.
  */
 public class H2TableDescriptor implements GridH2SystemIndexFactory {
+    /** PK index name. */
+    public static final String PK_IDX_NAME = "_key_PK";
+
+    /** PK hashindex name */
+    public static final String PK_HASH_IDX_NAME = "_key_PK_hash";
+
+    /** Affinity key index name */
+    public static final String AFFINITY_KEY_IDX_NAME = "AFFINITY_KEY";
+
     /** Indexing. */
     private final IgniteH2Indexing idx;
 
@@ -71,6 +83,9 @@ public class H2TableDescriptor implements GridH2SystemIndexFactory {
     /** */
     private H2PkHashIndex pkHashIdx;
 
+    /** Flag of table has been created from SQL*/
+    private boolean isSql;
+
     /**
      * Constructor.
      *
@@ -78,13 +93,15 @@ public class H2TableDescriptor implements GridH2SystemIndexFactory {
      * @param schema Schema.
      * @param type Type descriptor.
      * @param cctx Cache context.
+     * @param isSql {@code true} in case table has been created from SQL.
      */
     public H2TableDescriptor(IgniteH2Indexing idx, H2Schema schema, GridQueryTypeDescriptor type,
-        GridCacheContext cctx) {
+        GridCacheContext cctx, boolean isSql) {
         this.idx = idx;
         this.type = type;
         this.schema = schema;
         this.cctx = cctx;
+        this.isSql = isSql;
 
         fullTblName = H2Utils.withQuotes(schema.schemaName()) + "." + H2Utils.withQuotes(type.tableName());
     }
@@ -187,12 +204,15 @@ public class H2TableDescriptor implements GridH2SystemIndexFactory {
         if (affCol != null && H2Utils.equals(affCol, keyCol))
             affCol = null;
 
-        GridH2RowDescriptor desc = tbl.rowDescriptor();
+        List<IndexColumn> unwrappedKeyCols = extractKeyColumns(tbl, keyCol, affCol);
+
+        List<IndexColumn> wrappedKeyCols = H2Utils.treeIndexColumns(tbl.rowDescriptor(),
+            new ArrayList<>(2), keyCol, affCol);
 
         Index hashIdx = createHashIndex(
             tbl,
-            "_key_PK_hash",
-            H2Utils.treeIndexColumns(desc, new ArrayList<IndexColumn>(2), keyCol, affCol)
+            PK_HASH_IDX_NAME,
+            wrappedKeyCols
         );
 
         if (hashIdx != null)
@@ -200,11 +220,12 @@ public class H2TableDescriptor implements GridH2SystemIndexFactory {
 
         // Add primary key index.
         Index pkIdx = idx.createSortedIndex(
-            "_key_PK",
+            PK_IDX_NAME,
             tbl,
             true,
             false,
-            H2Utils.treeIndexColumns(desc, new ArrayList<IndexColumn>(2), keyCol, affCol),
+            unwrappedKeyCols,
+            wrappedKeyCols,
             -1
         );
 
@@ -219,8 +240,6 @@ public class H2TableDescriptor implements GridH2SystemIndexFactory {
             }
         }
 
-        boolean affIdxFound = false;
-
         GridQueryIndexDescriptor textIdx = type.textIndex();
 
         if (textIdx != null) {
@@ -234,6 +253,8 @@ public class H2TableDescriptor implements GridH2SystemIndexFactory {
 
         // Locate index where affinity column is first (if any).
         if (affCol != null) {
+            boolean affIdxFound = false;
+
             for (GridQueryIndexDescriptor idxDesc : type.indexes().values()) {
                 if (idxDesc.type() != QueryIndexType.SORTED)
                     continue;
@@ -247,18 +268,76 @@ public class H2TableDescriptor implements GridH2SystemIndexFactory {
 
                 affIdxFound |= H2Utils.equals(idxCol, affCol);
             }
-        }
 
-        // Add explicit affinity key index if nothing alike was found.
-        if (affCol != null && !affIdxFound) {
-            idxs.add(idx.createSortedIndex("AFFINITY_KEY", tbl, false, true,
-                H2Utils.treeIndexColumns(desc, new ArrayList<IndexColumn>(2), affCol, keyCol), -1));
+            // Add explicit affinity key index if nothing alike was found.
+            if (!affIdxFound) {
+                List<IndexColumn> columns = H2Utils.treeIndexColumns(tbl.rowDescriptor(), new ArrayList<>(2), affCol, keyCol);
+                idxs.add(idx.createSortedIndex(
+                    AFFINITY_KEY_IDX_NAME,
+                    tbl,
+                    false,
+                    true,
+                    columns,
+                    columns,
+                    -1)
+                );
+            }
         }
 
         return idxs;
     }
 
     /**
+     * Create list of affinity and key index columns. Key, if it possible, partitions into simple components.
+     *
+     * @param tbl GridH2Table instance
+     * @param keyCol Key index column.
+     * @param affCol Affinity index column.
+     *
+     * @return List of key and affinity columns. Key's, if it possible, splitted into simple components.
+     */
+    @NotNull private List<IndexColumn> extractKeyColumns(GridH2Table tbl, IndexColumn keyCol, IndexColumn affCol) {
+        ArrayList<IndexColumn> keyCols;
+
+        if (isSql) {
+            keyCols = new ArrayList<>(type.fields().size() + 1);
+
+            // Check if key is simple type.
+            if(QueryUtils.isSqlType(type.keyClass()))
+                keyCols.add(keyCol);
+            else {
+                for (String propName : type.fields().keySet()) {
+                    GridQueryProperty prop = type.property(propName);
+
+                    if (prop.key()) {
+                        Column col = tbl.getColumn(propName);
+
+                        keyCols.add(tbl.indexColumn(col.getColumnId(), SortOrder.ASCENDING));
+                    }
+                }
+
+                // If key is object but the user has not specified any particular columns,
+                // we have to fall back to whole-key index.
+                if (keyCols.isEmpty())
+                    keyCols.add(keyCol);
+            }
+
+        }
+        else {
+            keyCols = new ArrayList<>(2);
+
+            keyCols.add(keyCol);
+        }
+
+        if (affCol != null && !H2Utils.containsColumn(keyCols, affCol))
+            keyCols.add(affCol);
+        else
+            keyCols.trimToSize();
+
+        return Collections.unmodifiableList(keyCols);
+    }
+
+    /**
      * Get collection of user indexes.
      *
      * @return User indexes.
@@ -301,7 +380,15 @@ public class H2TableDescriptor implements GridH2SystemIndexFactory {
         if (idxDesc.type() == QueryIndexType.SORTED) {
             cols = H2Utils.treeIndexColumns(desc, cols, keyCol, affCol);
 
-            return idx.createSortedIndex(idxDesc.name(), tbl, false, false, cols, idxDesc.inlineSize());
+            return idx.createSortedIndex(
+                idxDesc.name(),
+                tbl,
+                false,
+                false,
+                cols,
+                cols,
+                idxDesc.inlineSize()
+            );
         }
         else if (idxDesc.type() == QueryIndexType.GEOSPATIAL)
             return H2Utils.createSpatialIndex(tbl, idxDesc.name(), cols.toArray(new IndexColumn[cols.size()]));

http://git-wip-us.apache.org/repos/asf/ignite/blob/c3462fff/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 7d93ecf..283e503 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
@@ -993,13 +993,13 @@ public class IgniteH2Indexing implements GridQueryIndexing {
      * @param tbl Table.
      * @param pk Primary key flag.
      * @param affinityKey Affinity key flag.
-     * @param cols Columns.
+     * @param unwrappedCols Unwrapped index columns for complex types.
+     * @param wrappedCols Index columns as is complex types.
      * @param inlineSize Index inline size.
      * @return Index.
      */
     GridH2IndexBase createSortedIndex(String name, GridH2Table tbl, boolean pk, boolean affinityKey,
-        List<IndexColumn> cols,
-        int inlineSize) {
+        List<IndexColumn> unwrappedCols, List<IndexColumn> wrappedCols, int inlineSize) {
         try {
             GridCacheContext cctx = tbl.cache();
 
@@ -1010,7 +1010,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
 
             H2RowCache cache = rowCache.forGroup(cctx.groupId());
 
-            return new H2TreeIndex(cctx, cache, tbl, name, pk, affinityKey, cols, inlineSize, segments);
+            return new H2TreeIndex(cctx, cache, tbl, name, pk, affinityKey, unwrappedCols, wrappedCols, inlineSize, segments);
         }
         catch (IgniteCheckedException e) {
             throw new IgniteException(e);
@@ -2828,9 +2828,10 @@ public class IgniteH2Indexing implements GridQueryIndexing {
      * This implementation doesn't support type reregistration.
      *
      * @param type Type description.
+     * @param isSql {@code true} in case table has been created from SQL.
      * @throws IgniteCheckedException In case of error.
      */
-    @Override public boolean registerType(GridCacheContext cctx, GridQueryTypeDescriptor type)
+    @Override public boolean registerType(GridCacheContext cctx, GridQueryTypeDescriptor type, boolean isSql)
         throws IgniteCheckedException {
         validateTypeDescriptor(type);
 
@@ -2838,7 +2839,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
 
         H2Schema schema = schemas.get(schemaName);
 
-        H2TableDescriptor tbl = new H2TableDescriptor(this, schema, type, cctx);
+        H2TableDescriptor tbl = new H2TableDescriptor(this, schema, type, cctx, isSql);
 
         try {
             Connection conn = connectionForThread(schemaName);

http://git-wip-us.apache.org/repos/asf/ignite/blob/c3462fff/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 4c6c0c0..71cea7e 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
@@ -108,6 +108,8 @@ public abstract class H2Tree extends BPlusTree<GridH2SearchRow, GridH2Row> {
     /** */
     private final IgniteLogger log;
 
+    private boolean unwrappedPk;
+
     /**
      * Constructor.
      *
@@ -143,9 +145,8 @@ public abstract class H2Tree extends BPlusTree<GridH2SearchRow, GridH2Row> {
         H2RowFactory rowStore,
         long metaPageId,
         boolean initNew,
-        IndexColumn[] cols,
-        List<InlineIndexHelper> inlineIdxs,
-        int inlineSize,
+        H2TreeIndex.IndexColumnsInfo unwrappedColsInfo,
+        H2TreeIndex.IndexColumnsInfo wrappedColsInfo,
         AtomicInteger maxCalculatedInlineSize,
         boolean pk,
         boolean affinityKey,
@@ -157,15 +158,23 @@ public abstract class H2Tree extends BPlusTree<GridH2SearchRow, GridH2Row> {
         super(name, grpId, pageMem, wal, globalRmvId, metaPageId, reuseList, failureProcessor);
 
         if (!initNew) {
-            // Page is ready - read inline size from it.
-            inlineSize = getMetaInlineSize();
+            // Page is ready - read meta information.
+            MetaPageInfo metaInfo = getMetaInfo();
+
+            inlineSize = metaInfo.inlineSize();
+
+            unwrappedPk = metaInfo.useUnwrappedPk();
+        }
+        else {
+            unwrappedPk = true;
+
+            inlineSize = unwrappedColsInfo.inlineSize();
         }
 
         this.idxName = idxName;
         this.cacheName = cacheName;
         this.tblName = tblName;
 
-        this.inlineSize = inlineSize;
         this.maxCalculatedInlineSize = maxCalculatedInlineSize;
 
         this.pk = pk;
@@ -176,8 +185,8 @@ public abstract class H2Tree extends BPlusTree<GridH2SearchRow, GridH2Row> {
         assert rowStore != null;
 
         this.rowStore = rowStore;
-        this.inlineIdxs = inlineIdxs;
-        this.cols = cols;
+        this.inlineIdxs = unwrappedPk ? unwrappedColsInfo.inlineIdx() : wrappedColsInfo.inlineIdx();
+        this.cols = unwrappedPk ? unwrappedColsInfo.cols() : wrappedColsInfo.cols();
 
         this.columnIds = new int[cols.length];
 
@@ -259,7 +268,7 @@ public abstract class H2Tree extends BPlusTree<GridH2SearchRow, GridH2Row> {
      * @return Inline size.
      * @throws IgniteCheckedException If failed.
      */
-    private int getMetaInlineSize() throws IgniteCheckedException {
+    private MetaPageInfo getMetaInfo() throws IgniteCheckedException {
         final long metaPage = acquirePage(metaPageId);
 
         try {
@@ -271,7 +280,7 @@ public abstract class H2Tree extends BPlusTree<GridH2SearchRow, GridH2Row> {
             try {
                 BPlusMetaIO io = BPlusMetaIO.VERSIONS.forPage(pageAddr);
 
-                return io.getInlineSize(pageAddr);
+                return new MetaPageInfo(io.getInlineSize(pageAddr), io.unwrappedPk());
             }
             finally {
                 readUnlock(metaPageId, metaPage, pageAddr);
@@ -501,6 +510,46 @@ public abstract class H2Tree extends BPlusTree<GridH2SearchRow, GridH2Row> {
     }
 
     /**
+     * @return {@code true} In case use unwrapped columns for PK
+     */
+    public boolean unwrappedPk() {
+        return unwrappedPk;
+    }
+
+    /**
+     *
+     */
+    private class MetaPageInfo {
+        /** */
+        int inlineSize;
+        /** */
+        boolean useUnwrappedPk;
+
+        /**
+         * @param inlineSize Inline size.
+         * @param useUnwrappedPk {@code true} In case use unwrapped PK for indexes.
+         */
+        public MetaPageInfo(int inlineSize, boolean useUnwrappedPk) {
+            this.inlineSize = inlineSize;
+            this.useUnwrappedPk = useUnwrappedPk;
+        }
+
+        /**
+         * @return Inline size.
+         */
+        public int inlineSize() {
+            return inlineSize;
+        }
+
+        /**
+         * @return {@code true} In case use unwrapped PK for indexes.
+         */
+        public boolean useUnwrappedPk() {
+            return useUnwrappedPk;
+        }
+    }
+
+    /**
      * @param v1 First value.
      * @param v2 Second value.
      * @return Comparison result.

http://git-wip-us.apache.org/repos/asf/ignite/blob/c3462fff/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 e9cca9e..b7179b7 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
@@ -93,12 +93,15 @@ public class H2TreeIndex extends GridH2IndexBase {
 
     /**
      * @param cctx Cache context.
+     * @param rowCache Row cache.
      * @param tbl Table.
      * @param idxName Index name.
      * @param pk Primary key.
      * @param affinityKey {@code true} for affinity key.
-     * @param colsList Index columns.
+     * @param unwrappedColsList Unwrapped index columns for complex types.
+     * @param wrappedColsList Index columns as is.
      * @param inlineSize Inline size.
+     * @param segmentsCnt Count of tree segments.
      * @throws IgniteCheckedException If failed.
      */
     public H2TreeIndex(
@@ -108,7 +111,8 @@ public class H2TreeIndex extends GridH2IndexBase {
         String idxName,
         boolean pk,
         boolean affinityKey,
-        List<IndexColumn> colsList,
+        List<IndexColumn> unwrappedColsList,
+        List<IndexColumn> wrappedColsList,
         int inlineSize,
         int segmentsCnt
     ) throws IgniteCheckedException {
@@ -124,12 +128,8 @@ public class H2TreeIndex extends GridH2IndexBase {
         this.tblName = tbl.getName();
         this.idxName = idxName;
 
-        IndexColumn[] cols = colsList.toArray(new IndexColumn[colsList.size()]);
+        this.table = tbl;
 
-        IndexColumn.mapColumns(cols, tbl);
-
-        initBaseIndex(tbl, 0, idxName, cols,
-            pk ? IndexType.createPrimaryKey(false, false) : IndexType.createNonUnique(false, false, false));
 
         GridQueryTypeDescriptor typeDesc = tbl.rowDescriptor().type();
 
@@ -139,9 +139,13 @@ public class H2TreeIndex extends GridH2IndexBase {
 
         treeName = BPlusTree.treeName(treeName, "H2Tree");
 
-        if (cctx.affinityNode()) {
-            inlineIdxs = getAvailableInlineColumns(cols);
+        IndexColumnsInfo unwrappedColsInfo = new IndexColumnsInfo(unwrappedColsList, inlineSize);
+
+        IndexColumnsInfo wrappedColsInfo = new IndexColumnsInfo(wrappedColsList, inlineSize);
 
+        IndexColumn[] cols;
+
+        if (cctx.affinityNode()) {
             segments = new H2Tree[segmentsCnt];
 
             IgniteCacheDatabaseSharedManager db = cctx.shared().database();
@@ -167,9 +171,8 @@ public class H2TreeIndex extends GridH2IndexBase {
                         tbl.rowFactory(),
                         page.pageId().pageId(),
                         page.isAllocated(),
-                        cols,
-                        inlineIdxs,
-                        computeInlineSize(inlineIdxs, inlineSize),
+                        unwrappedColsInfo,
+                        wrappedColsInfo,
                         maxCalculatedInlineSize,
                         pk,
                         affinityKey,
@@ -186,13 +189,28 @@ public class H2TreeIndex extends GridH2IndexBase {
                     db.checkpointReadUnlock();
                 }
             }
+
+            boolean useUnwrappedCols = segments[0].unwrappedPk();
+
+            IndexColumnsInfo colsInfo = useUnwrappedCols ? unwrappedColsInfo : wrappedColsInfo;
+
+            cols = colsInfo.cols();
+
+            inlineIdxs = colsInfo.inlineIdx();
         }
         else {
             // We need indexes on the client node, but index will not contain any data.
             segments = null;
             inlineIdxs = null;
+
+            cols = unwrappedColsInfo.cols();
         }
 
+        IndexColumn.mapColumns(cols, tbl);
+
+        initBaseIndex(tbl, 0, idxName, cols,
+            pk ? IndexType.createPrimaryKey(false, false) : IndexType.createNonUnique(false, false, false));
+
         initDistributedJoinMessaging(tbl);
     }
 
@@ -540,4 +558,49 @@ public class H2TreeIndex extends GridH2IndexBase {
         for (int pos = 0; pos < inlineHelpers.size(); ++pos)
             inlineIdxs.set(pos, inlineHelpers.get(pos));
     }
+
+    /**
+     *
+     */
+    public class IndexColumnsInfo {
+        /** */
+        private final int inlineSize;
+        /** */
+        private final IndexColumn[] cols;
+        /** */
+        private final List<InlineIndexHelper> inlineIdx;
+
+        /**
+         * @param colsList Index columns list
+         * @param cfgInlineSize Inline size from cache config.
+         */
+        public IndexColumnsInfo(List<IndexColumn> colsList, int cfgInlineSize) {
+            this.cols = colsList.toArray(new IndexColumn[colsList.size()]);
+
+            this.inlineIdx = getAvailableInlineColumns(cols);
+
+            this.inlineSize = computeInlineSize(inlineIdx, cfgInlineSize);
+        }
+
+        /**
+         * @return Inline size.
+         */
+        public int inlineSize() {
+            return inlineSize;
+        }
+
+        /**
+         * @return Index columns.
+         */
+        public IndexColumn[] cols() {
+            return cols;
+        }
+
+        /**
+         * @return Inline indexes.
+         */
+        public List<InlineIndexHelper> inlineIdx() {
+            return inlineIdx;
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/c3462fff/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/ComplexPrimaryKeyUnwrapSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/ComplexPrimaryKeyUnwrapSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/ComplexPrimaryKeyUnwrapSelfTest.java
new file mode 100644
index 0000000..8f4eaf0
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/ComplexPrimaryKeyUnwrapSelfTest.java
@@ -0,0 +1,337 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.cache.index;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.ignite.cache.QueryEntity;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.cache.query.annotations.QuerySqlField;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+/**
+ * Test of creating and using PK indexes for tables created through SQL.
+ */
+@SuppressWarnings({"unchecked", "ThrowableResultOfMethodCallIgnored"})
+public class ComplexPrimaryKeyUnwrapSelfTest extends GridCommonAbstractTest {
+
+    /** Counter to generate unique table names. */
+    private static int tblCnt = 0;
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        super.beforeTestsStarted();
+
+        startGrid(0);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTestsStopped() throws Exception {
+        super.afterTestsStopped();
+
+        stopAllGrids();
+    }
+
+    /**
+     * Test using PK indexes for complex primary key.
+     */
+    public void testComplexPk() {
+        String tblName = createTableName();
+
+        executeSql("CREATE TABLE " + tblName + " (id int, name varchar, age int, company varchar, city varchar, " +
+            "primary key (id, name, city))");
+
+        checkUsingIndexes(tblName, "1");
+    }
+
+    /**
+     * Test using PK indexes for simple primary key.
+     */
+    public void testSimplePk() {
+        //ToDo: IGNITE-8386: need to add DATE type into the test.
+        HashMap<String, String> types = new HashMap() {
+            {
+                put("boolean", "1");
+                put("char", "'1'");
+                put("varchar", "'1'");
+                put("real", "1");
+                put("number", "1");
+                put("int", "1");
+                put("long", "1");
+                put("float", "1");
+                put("double", "1");
+                put("tinyint", "1");
+                put("smallint", "1");
+                put("bigint", "1");
+                put("varchar_ignorecase", "'1'");
+                put("time", "'11:11:11'");
+                put("timestamp", "'20018-11-02 11:11:11'");
+                put("uuid", "'1'");
+            }
+        };
+        for (Map.Entry<String, String> entry : types.entrySet()) {
+
+            String tblName = createTableName();
+
+            String type = entry.getKey();
+            String value = entry.getValue();
+
+            executeSql("CREATE TABLE " + tblName +
+                " (id " + type + " , name varchar, age int, company varchar, city varchar," +
+                " primary key (id))");
+
+            checkUsingIndexes(tblName, value);
+        }
+    }
+
+    /**
+     * Test using PK indexes for simple primary key and affinity key.
+     */
+    public void testSimplePkWithAffinityKey() {
+        //ToDo: IGNITE-8386: need to add DATE type into the test.
+        HashMap<String, String> types = new HashMap() {
+            {
+                put("boolean", "1");
+                put("char", "'1'");
+                put("varchar", "'1'");
+                put("real", "1");
+                put("number", "1");
+                put("int", "1");
+                put("long", "1");
+                put("float", "1");
+                put("double", "1");
+                put("tinyint", "1");
+                put("smallint", "1");
+                put("bigint", "1");
+                put("varchar_ignorecase", "'1'");
+                put("time", "'11:11:11'");
+                put("timestamp", "'20018-11-02 11:11:11'");
+                put("uuid", "'1'");
+            }
+        };
+        for (Map.Entry<String, String> entry : types.entrySet()) {
+
+            String tblName = createTableName();
+
+            String type = entry.getKey();
+            String value = entry.getValue();
+
+            executeSql("CREATE TABLE " + tblName +
+                " (id " + type + " , name varchar, age int, company varchar, city varchar," +
+                " primary key (id)) WITH \"affinity_key=id\"");
+
+            checkUsingIndexes(tblName, value);
+        }
+    }
+
+    /**
+     * Test using PK indexes for wrapped primary key.
+     */
+    public void testWrappedPk() {
+        String tblName = createTableName();
+
+        executeSql("CREATE TABLE " + tblName + " (id int, name varchar, age int, company varchar, city varchar, " +
+            "primary key (id)) WITH \"wrap_key=true\"");
+
+        checkUsingIndexes(tblName, "1");
+    }
+
+    /**
+     * Check using PK indexes for few cases.
+     *
+     * @param tblName name of table which should be checked to using PK indexes.
+     */
+    private void checkUsingIndexes(String tblName, String idValue) {
+        String explainSQL = "explain SELECT * FROM " + tblName + " WHERE ";
+
+        List<List<?>> results = executeSql(explainSQL + "id=" + idValue);
+
+        assertUsingPkIndex(results);
+
+        results = executeSql(explainSQL + "id=" + idValue + " and name=''");
+
+        assertUsingPkIndex(results);
+
+        results = executeSql(explainSQL + "id=" + idValue + " and name='' and city='' and age=0");
+
+        assertUsingPkIndex(results);
+    }
+
+    /**
+     * Test don't using PK indexes for table created through cache API.
+     */
+    public void testIndexesForCachesCreatedThroughCashApi() {
+        String tblName = TestValue.class.getSimpleName();
+
+        CacheConfiguration ccfg = new CacheConfiguration(DEFAULT_CACHE_NAME);
+
+        ccfg.setSqlSchema("PUBLIC");
+        ccfg.setName(tblName);
+
+        QueryEntity qryEntity = new QueryEntity(TestKey.class, TestValue.class);
+
+        ccfg.setQueryEntities(F.asList(qryEntity));
+
+        node().createCache(ccfg);
+
+        List<List<?>> results = executeSql("explain SELECT * FROM " + tblName + " WHERE id=1");
+
+        assertDontUsingPkIndex(results);
+    }
+
+    /**
+     * Check that explain plan result shown using PK index and don't use scan.
+     *
+     * @param results result of execut explain plan query.
+     */
+    private void assertUsingPkIndex(List<List<?>> results) {
+        assertEquals(2, results.size());
+
+        String explainPlan = (String)results.get(0).get(0);
+
+        assertTrue(explainPlan.contains("\"_key_PK"));
+
+        assertFalse(explainPlan.contains("_SCAN_"));
+    }
+
+    /**
+     * Check that explain plan result shown don't use PK index and use scan.
+     *
+     * @param results result of execut explain plan query.
+     */
+    private void assertDontUsingPkIndex(List<List<?>> results) {
+        assertEquals(2, results.size());
+
+        String explainPlan = (String)results.get(0).get(0);
+
+        System.out.println(explainPlan);
+
+        assertFalse(explainPlan.contains("\"_key_PK\""));
+
+        assertTrue(explainPlan.contains("_SCAN_"));
+    }
+
+    /**
+     * Create unique table name.
+     *
+     * @return unique name of table.
+     */
+    private String createTableName() {
+        return "TST_TABLE_" + tblCnt++;
+    }
+
+    /**
+     * Run SQL statement on default node.
+     *
+     * @param stmt Statement to run.
+     * @param args arguments of statements
+     * @return Run result.
+     */
+    private List<List<?>> executeSql(String stmt, Object... args) {
+        return executeSql(node(), stmt, args);
+    }
+
+    /**
+     * Run SQL statement on specified node.
+     *
+     * @param node node to execute query.
+     * @param stmt Statement to run.
+     * @param args arguments of statements
+     * @return Run result.
+     */
+    private List<List<?>> executeSql(IgniteEx node, String stmt, Object... args) {
+        return node.context().query().querySqlFields(new SqlFieldsQuery(stmt).setArgs(args), true).getAll();
+    }
+
+    /**
+     * @return Node to initiate operations from.
+     */
+    private IgniteEx node() {
+        return grid(0);
+    }
+
+    /**
+     *
+     */
+    static class TestKey {
+        /** */
+        @QuerySqlField
+        private int id;
+
+        /**
+         * @param id ID.
+         */
+        public TestKey(int id) {
+            this.id = id;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean equals(Object o) {
+            if (this == o)
+                return true;
+
+            if (o == null || getClass() != o.getClass())
+                return false;
+
+            TestKey testKey = (TestKey)o;
+
+            return id == testKey.id;
+        }
+
+        /** {@inheritDoc} */
+        @Override public int hashCode() {
+            return id;
+        }
+    }
+
+    /**
+     *
+     */
+    static class TestValue {
+        /** */
+        @QuerySqlField()
+        private String name;
+        /** */
+        @QuerySqlField()
+        private String company;
+        /** */
+        @QuerySqlField()
+        private String city;
+        /** */
+        @QuerySqlField()
+        private int age;
+
+        /**
+         * @param age Age.
+         * @param name Name.
+         * @param company Company.
+         * @param city City.
+         */
+        public TestValue(int age, String name, String company, String city) {
+            this.age = age;
+            this.name = name;
+            this.company = company;
+            this.city = city;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/c3462fff/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite.java b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite.java
index 8213380..aeb49e1 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite.java
@@ -120,6 +120,7 @@ import org.apache.ignite.internal.processors.cache.distributed.replicated.Ignite
 import org.apache.ignite.internal.processors.cache.distributed.replicated.IgniteCacheReplicatedQuerySelfTest;
 import org.apache.ignite.internal.processors.cache.encryption.EncryptedSqlTableTest;
 import org.apache.ignite.internal.processors.cache.index.BasicIndexTest;
+import org.apache.ignite.internal.processors.cache.index.ComplexPrimaryKeyUnwrapSelfTest;
 import org.apache.ignite.internal.processors.cache.index.DuplicateKeyValueClassesSelfTest;
 import org.apache.ignite.internal.processors.cache.index.DynamicIndexClientBasicSelfTest;
 import org.apache.ignite.internal.processors.cache.index.DynamicIndexServerBasicSelfTest;
@@ -127,10 +128,6 @@ import org.apache.ignite.internal.processors.cache.index.DynamicIndexServerCoord
 import org.apache.ignite.internal.processors.cache.index.DynamicIndexServerNodeFIlterBasicSelfTest;
 import org.apache.ignite.internal.processors.cache.index.DynamicIndexServerNodeFilterCoordinatorBasicSelfTest;
 import org.apache.ignite.internal.processors.cache.index.H2ConnectionLeaksSelfTest;
-import org.apache.ignite.internal.processors.cache.index.H2DynamicIndexingComplexClientAtomicPartitionedNoBackupsTest;
-import org.apache.ignite.internal.processors.cache.index.H2DynamicIndexingComplexClientTransactionalPartitionedNoBackupsTest;
-import org.apache.ignite.internal.processors.cache.index.H2DynamicIndexingComplexServerAtomicPartitionedNoBackupsTest;
-import org.apache.ignite.internal.processors.cache.index.H2DynamicIndexingComplexServerTransactionalPartitionedNoBackupsTest;
 import org.apache.ignite.internal.processors.cache.index.H2DynamicColumnsClientBasicSelfTest;
 import org.apache.ignite.internal.processors.cache.index.H2DynamicColumnsServerBasicSelfTest;
 import org.apache.ignite.internal.processors.cache.index.H2DynamicColumnsServerCoordinatorBasicSelfTest;
@@ -140,12 +137,16 @@ import org.apache.ignite.internal.processors.cache.index.H2DynamicIndexAtomicRep
 import org.apache.ignite.internal.processors.cache.index.H2DynamicIndexTransactionalPartitionedNearSelfTest;
 import org.apache.ignite.internal.processors.cache.index.H2DynamicIndexTransactionalPartitionedSelfTest;
 import org.apache.ignite.internal.processors.cache.index.H2DynamicIndexTransactionalReplicatedSelfTest;
+import org.apache.ignite.internal.processors.cache.index.H2DynamicIndexingComplexClientAtomicPartitionedNoBackupsTest;
 import org.apache.ignite.internal.processors.cache.index.H2DynamicIndexingComplexClientAtomicPartitionedTest;
 import org.apache.ignite.internal.processors.cache.index.H2DynamicIndexingComplexClientAtomicReplicatedTest;
+import org.apache.ignite.internal.processors.cache.index.H2DynamicIndexingComplexClientTransactionalPartitionedNoBackupsTest;
 import org.apache.ignite.internal.processors.cache.index.H2DynamicIndexingComplexClientTransactionalPartitionedTest;
 import org.apache.ignite.internal.processors.cache.index.H2DynamicIndexingComplexClientTransactionalReplicatedTest;
+import org.apache.ignite.internal.processors.cache.index.H2DynamicIndexingComplexServerAtomicPartitionedNoBackupsTest;
 import org.apache.ignite.internal.processors.cache.index.H2DynamicIndexingComplexServerAtomicPartitionedTest;
 import org.apache.ignite.internal.processors.cache.index.H2DynamicIndexingComplexServerAtomicReplicatedTest;
+import org.apache.ignite.internal.processors.cache.index.H2DynamicIndexingComplexServerTransactionalPartitionedNoBackupsTest;
 import org.apache.ignite.internal.processors.cache.index.H2DynamicIndexingComplexServerTransactionalPartitionedTest;
 import org.apache.ignite.internal.processors.cache.index.H2DynamicIndexingComplexServerTransactionalReplicatedTest;
 import org.apache.ignite.internal.processors.cache.index.H2DynamicTableSelfTest;
@@ -242,6 +243,7 @@ public class IgniteBinaryCacheQueryTestSuite extends TestSuite {
 
         suite.addTestSuite(AffinityKeyNameAndValueFieldNameConflictTest.class);
         suite.addTestSuite(DmlInsideTransactionTest.class);
+        suite.addTestSuite(ComplexPrimaryKeyUnwrapSelfTest.class);
 
         suite.addTestSuite(PartitionedSqlTest.class);
         suite.addTestSuite(ReplicatedSqlTest.class);