IGNITE-9279: SQL: custom predefined schemas. This closes #4550. This closes 
#4586.


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

Branch: refs/heads/ignite-9340
Commit: 277010cddf184fa749f5280487015efc0a928c4b
Parents: bab61f1
Author: devozerov <[email protected]>
Authored: Wed Aug 22 13:41:44 2018 +0300
Committer: devozerov <[email protected]>
Committed: Wed Aug 22 13:41:44 2018 +0300

----------------------------------------------------------------------
 .../jdbc/suite/IgniteJdbcDriverTestSuite.java   |   2 +
 ...bcThinComplexDmlDdlCustomSchemaSelfTest.java |  78 ++++++++
 .../thin/JdbcThinComplexDmlDdlSelfTest.java     |  20 +-
 .../configuration/IgniteConfiguration.java      |  33 ++++
 .../processors/cache/GridCacheProcessor.java    |  19 ++
 .../utils/PlatformConfigurationUtils.java       |  23 +++
 .../internal/processors/query/h2/H2Schema.java  |  31 ++-
 .../processors/query/h2/IgniteH2Indexing.java   | 132 +++++++------
 .../query/h2/ddl/DdlStatementsProcessor.java    |  97 +++++-----
 .../cache/index/H2DynamicTableSelfTest.java     |  29 ---
 .../query/SqlIllegalSchemaSelfTest.java         | 187 +++++++++++++++++++
 .../IgniteCacheQuerySelfTestSuite.java          |   2 +
 .../Config/full-config.xml                      |   4 +
 .../IgniteConfigurationSerializerTest.cs        |   6 +
 .../IgniteConfigurationTest.cs                  |  10 +-
 .../Apache.Ignite.Core/IgniteConfiguration.cs   |  34 ++++
 .../IgniteConfigurationSection.xsd              |  10 +
 17 files changed, 557 insertions(+), 160 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/277010cd/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
----------------------------------------------------------------------
diff --git 
a/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
 
b/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
index f3490aa..889a551 100644
--- 
a/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
+++ 
b/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
@@ -43,6 +43,7 @@ import 
org.apache.ignite.jdbc.thin.JdbcThinBulkLoadAtomicReplicatedSelfTest;
 import 
org.apache.ignite.jdbc.thin.JdbcThinBulkLoadTransactionalPartitionedNearSelfTest;
 import 
org.apache.ignite.jdbc.thin.JdbcThinBulkLoadTransactionalPartitionedSelfTest;
 import 
org.apache.ignite.jdbc.thin.JdbcThinBulkLoadTransactionalReplicatedSelfTest;
+import org.apache.ignite.jdbc.thin.JdbcThinComplexDmlDdlCustomSchemaSelfTest;
 import org.apache.ignite.jdbc.thin.JdbcThinComplexDmlDdlSelfTest;
 import 
org.apache.ignite.jdbc.thin.JdbcThinComplexDmlDdlSkipReducerOnUpdateSelfTest;
 import org.apache.ignite.jdbc.thin.JdbcThinComplexQuerySelfTest;
@@ -194,6 +195,7 @@ public class IgniteJdbcDriverTestSuite extends TestSuite {
         suite.addTest(new 
TestSuite(JdbcThinUpdateStatementSkipReducerOnUpdateSelfTest.class));
         suite.addTest(new 
TestSuite(JdbcThinMergeStatementSkipReducerOnUpdateSelfTest.class));
         suite.addTest(new 
TestSuite(JdbcThinComplexDmlDdlSkipReducerOnUpdateSelfTest.class));
+        suite.addTest(new 
TestSuite(JdbcThinComplexDmlDdlCustomSchemaSelfTest.class));
 
         suite.addTest(new TestSuite(JdbcThinLocalQueriesSelfTest.class));
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/277010cd/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinComplexDmlDdlCustomSchemaSelfTest.java
----------------------------------------------------------------------
diff --git 
a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinComplexDmlDdlCustomSchemaSelfTest.java
 
b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinComplexDmlDdlCustomSchemaSelfTest.java
new file mode 100644
index 0000000..8fd9356
--- /dev/null
+++ 
b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinComplexDmlDdlCustomSchemaSelfTest.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.jdbc.thin;
+
+import org.apache.ignite.configuration.IgniteConfiguration;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+
+/**
+ * Base class for complex SQL tests based on JDBC driver.
+ */
+public class JdbcThinComplexDmlDdlCustomSchemaSelfTest extends 
JdbcThinComplexDmlDdlSelfTest {
+    /** Simple schema. */
+    private static final String SCHEMA_1 = "SCHEMA_1";
+
+    /** Complex schema. */
+    private static final String SCHEMA_2 = "\"SCHEMA 2\"";
+
+    /** Current schema. */
+    private String curSchema = SCHEMA_1;
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String 
igniteInstanceName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+        cfg.setSqlSchemas(SCHEMA_1, SCHEMA_2);
+
+        return cfg;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected Connection createConnection() throws SQLException {
+        return DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1/" + 
curSchema);
+    }
+
+    /**
+     * Test create/select/drop flow on escaped schema.
+     *
+     * @throws Exception If failed.
+     */
+    public void testCreateSelectDropEscapedSchema() throws Exception {
+        try {
+            curSchema = SCHEMA_2;
+
+            testCreateSelectDrop();
+        }
+        finally {
+            curSchema = SCHEMA_1;
+        }
+    }
+
+    /**
+     * Test multiple iterations.
+     *
+     * @throws Exception If failed.
+     */
+    public void testMultiple() throws Exception {
+        testCreateSelectDrop();
+        testCreateSelectDrop();
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/277010cd/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinComplexDmlDdlSelfTest.java
----------------------------------------------------------------------
diff --git 
a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinComplexDmlDdlSelfTest.java
 
b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinComplexDmlDdlSelfTest.java
index 9c40948..36ee34a 100644
--- 
a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinComplexDmlDdlSelfTest.java
+++ 
b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinComplexDmlDdlSelfTest.java
@@ -33,12 +33,10 @@ import org.apache.ignite.cache.CacheMode;
 import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.internal.processors.cache.DynamicCacheDescriptor;
-import org.apache.ignite.lang.IgniteCallable;
 import org.apache.ignite.lang.IgnitePredicate;
 import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
 import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
 import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
-import org.apache.ignite.testframework.GridTestUtils;
 import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
 import org.jetbrains.annotations.NotNull;
 
@@ -82,9 +80,8 @@ public class JdbcThinComplexDmlDdlSelfTest extends 
GridCommonAbstractTest {
     /**
      * @param name Cache name.
      * @return Cache configuration.
-     * @throws Exception In case of error.
      */
-    private CacheConfiguration cacheConfiguration(@NotNull String name) throws 
Exception {
+    private CacheConfiguration cacheConfiguration(@NotNull String name) {
         CacheConfiguration cfg = defaultCacheConfiguration();
 
         cfg.setName(name);
@@ -110,8 +107,6 @@ public class JdbcThinComplexDmlDdlSelfTest extends 
GridCommonAbstractTest {
     /** {@inheritDoc} */
     @Override protected void beforeTest() throws Exception {
         super.beforeTest();
-
-        conn = createConnection();
     }
 
     /** {@inheritDoc} */
@@ -133,14 +128,8 @@ public class JdbcThinComplexDmlDdlSelfTest extends 
GridCommonAbstractTest {
      * @throws Exception If failed.
      */
     @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
-    public void testCreateSelect() throws Exception {
-        GridTestUtils.assertThrows(null, new IgniteCallable<Object>() {
-            @Override public Object call() throws Exception {
-                sql(new ResultChecker(new Object[][] {}), "SELECT * from 
Person");
-
-                return null;
-            }
-        }, SQLException.class, "Table \"PERSON\" not found");
+    public void testCreateSelectDrop() throws Exception {
+        conn = createConnection();
 
         sql(new UpdateChecker(0),
             "CREATE TABLE person (id int, name varchar, age int, company 
varchar, city varchar, " +
@@ -228,6 +217,9 @@ public class JdbcThinComplexDmlDdlSelfTest extends 
GridCommonAbstractTest {
         assert cnt[0] == 34 : "Invalid rows count";
 
         sql(new UpdateChecker(0), "DROP INDEX idx");
+
+        sql(new UpdateChecker(0), "DROP TABLE city");
+        sql(new UpdateChecker(0), "DROP TABLE person");
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/277010cd/modules/core/src/main/java/org/apache/ignite/configuration/IgniteConfiguration.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/configuration/IgniteConfiguration.java
 
b/modules/core/src/main/java/org/apache/ignite/configuration/IgniteConfiguration.java
index cc3ea10..ab54709 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/configuration/IgniteConfiguration.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/configuration/IgniteConfiguration.java
@@ -496,6 +496,9 @@ public class IgniteConfiguration {
     /** Communication failure resolver */
     private CommunicationFailureResolver commFailureRslvr;
 
+    /** SQL schemas to be created on node start. */
+    private String[] sqlSchemas;
+
     /**
      * Creates valid grid configuration with all default values.
      */
@@ -594,6 +597,7 @@ public class IgniteConfiguration {
         sndRetryCnt = cfg.getNetworkSendRetryCount();
         sndRetryDelay = cfg.getNetworkSendRetryDelay();
         sqlConnCfg = cfg.getSqlConnectorConfiguration();
+        sqlSchemas = cfg.getSqlSchemas();
         sslCtxFactory = cfg.getSslContextFactory();
         storeSesLsnrs = cfg.getCacheStoreSessionListenerFactories();
         stripedPoolSize = cfg.getStripedPoolSize();
@@ -3001,6 +3005,35 @@ public class IgniteConfiguration {
         return this;
     }
 
+    /**
+     * Gets SQL schemas to be created on node startup.
+     * <p>
+     * See {@link #setSqlSchemas(String...)} for more information.
+     *
+     * @return SQL schemas to be created on node startup.
+     */
+    public String[] getSqlSchemas() {
+        return sqlSchemas;
+    }
+
+    /**
+     * Sets SQL schemas to be created on node startup. Schemas are created on 
local node only and are not propagated
+     * to other cluster nodes. Created schemas cannot be dropped.
+     * <p>
+     * By default schema names are case-insensitive, i.e. {@code my_schema} 
and {@code My_Schema} represents the same
+     * object. Use quotes to enforce case sensitivity (e.g. {@code 
"My_Schema"}).
+     * <p>
+     * Property is ignored if {@code ignite-indexing} module is not in 
classpath.
+     *
+     * @param sqlSchemas SQL schemas to be created on node startup.
+     * @return {@code this} for chaining.
+     */
+    public IgniteConfiguration setSqlSchemas(String... sqlSchemas) {
+        this.sqlSchemas = sqlSchemas;
+
+        return this;
+    }
+
     /** {@inheritDoc} */
     @Override public String toString() {
         return S.toString(IgniteConfiguration.class, this);

http://git-wip-us.apache.org/repos/asf/ignite/blob/277010cd/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 6818e50..272aad4 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
@@ -541,6 +541,25 @@ public class GridCacheProcessor extends 
GridProcessorAdapter {
             throw new IgniteCheckedException("Using cache group names reserved 
for datastructures is not allowed for " +
                 "other cache types [cacheName=" + cc.getName() + ", 
groupName=" + cc.getGroupName() +
                 ", cacheType=" + cacheType + "]");
+
+        // Make sure we do not use sql schema for system views.
+        if (ctx.query().moduleEnabled()) {
+            String schema = QueryUtils.normalizeSchemaName(cc.getName(), 
cc.getSqlSchema());
+
+            if (F.eq(schema, QueryUtils.SCHEMA_SYS)) {
+                if (cc.getSqlSchema() == null) {
+                    // Conflict on cache name.
+                    throw new IgniteCheckedException("SQL schema name derived 
from cache name is reserved (" +
+                        "please set explicit SQL schema name through 
CacheConfiguration.setSqlSchema() or choose " +
+                        "another cache name) [cacheName=" + cc.getName() + ", 
schemaName=" + cc.getSqlSchema() + "]");
+                }
+                else {
+                    // Conflict on schema name.
+                    throw new IgniteCheckedException("SQL schema name is 
reserved (please choose another one) [" +
+                        "cacheName=" + cc.getName() + ", schemaName=" + 
cc.getSqlSchema() + ']');
+                }
+            }
+        }
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/277010cd/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformConfigurationUtils.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformConfigurationUtils.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformConfigurationUtils.java
index b4f82a4..d73e89d 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformConfigurationUtils.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformConfigurationUtils.java
@@ -642,6 +642,19 @@ public class PlatformConfigurationUtils {
         if (in.readBoolean())
             cfg.setAuthenticationEnabled(in.readBoolean());
 
+        int sqlSchemasCnt = in.readInt();
+
+        if (sqlSchemasCnt == -1)
+            cfg.setSqlSchemas((String[])null);
+        else {
+            String[] sqlSchemas = new String[sqlSchemasCnt];
+
+            for (int i = 0; i < sqlSchemasCnt; i++)
+                sqlSchemas[i] = in.readString();
+
+            cfg.setSqlSchemas(sqlSchemas);
+        }
+
         Object consId = in.readObjectDetached();
 
         if (consId instanceof Serializable) {
@@ -1167,6 +1180,16 @@ public class PlatformConfigurationUtils {
         w.writeBoolean(cfg.isActiveOnStart());
         w.writeBoolean(true);
         w.writeBoolean(cfg.isAuthenticationEnabled());
+
+        if (cfg.getSqlSchemas() == null)
+            w.writeInt(-1);
+        else {
+            w.writeInt(cfg.getSqlSchemas().length);
+
+            for (String schema : cfg.getSqlSchemas())
+                w.writeString(schema);
+        }
+
         w.writeObject(cfg.getConsistentId());
 
         // Thread pools.

http://git-wip-us.apache.org/repos/asf/ignite/blob/277010cd/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Schema.java
----------------------------------------------------------------------
diff --git 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Schema.java
 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Schema.java
index 2fdf32d..ab7cb4b 100644
--- 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Schema.java
+++ 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Schema.java
@@ -34,6 +34,9 @@ public class H2Schema {
     /** */
     private final ConcurrentMap<H2TypeKey, H2TableDescriptor> typeToTbl = new 
ConcurrentHashMap<>();
 
+    /** Whether schema is predefined and cannot be dorpped. */
+    private final boolean predefined;
+
     /** Usage count. */
     private int usageCnt;
 
@@ -41,9 +44,11 @@ public class H2Schema {
      * Constructor.
      *
      * @param schemaName Schema name.
+     * @param predefined Predefined flag.
      */
-    public H2Schema(String schemaName) {
+    public H2Schema(String schemaName, boolean predefined) {
         this.schemaName = schemaName;
+        this.predefined = predefined;
     }
 
     /**
@@ -55,20 +60,19 @@ public class H2Schema {
 
     /**
      * Increments counter for number of caches having this schema.
-     *
-     * @return New value of caches counter.
      */
-    public int incrementUsageCount() {
-        return ++usageCnt;
+    public void incrementUsageCount() {
+        if (!predefined)
+            ++usageCnt;
     }
 
     /**
      * Increments counter for number of caches having this schema.
      *
-     * @return New value of caches counter.
+     * @return If schema is no longer used.
      */
-    public int decrementUsageCount() {
-        return --usageCnt;
+    public boolean decrementUsageCount() {
+        return !predefined && --usageCnt == 0;
     }
 
     /**
@@ -128,14 +132,9 @@ public class H2Schema {
     }
 
     /**
-     * Called after the schema was dropped.
+     * @return {@code True} if schema is predefined.
      */
-    public void dropAll() {
-        for (H2TableDescriptor tbl : tbls.values())
-            tbl.onDrop();
-
-        tbls.clear();
-
-        typeToTbl.clear();
+    public boolean predefined() {
+        return predefined;
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/277010cd/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 5f74c04..44cda44 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
@@ -579,12 +579,53 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
     }
 
     /**
+     * Create and register schema if needed.
+     *
+     * @param schemaName Schema name.
+     * @param predefined Whether this is predefined schema.
+     */
+    private void createSchemaIfNeeded(String schemaName, boolean predefined) {
+        assert Thread.holdsLock(schemaMux);
+
+        if (!predefined)
+            predefined = isSchemaPredefined(schemaName);
+
+        H2Schema schema = new H2Schema(schemaName, predefined);
+
+        H2Schema oldSchema = schemas.putIfAbsent(schemaName, schema);
+
+        if (oldSchema == null)
+            createSchema0(schemaName);
+        else
+            schema = oldSchema;
+
+        schema.incrementUsageCount();
+    }
+
+    /**
+     * Check if schema is predefined.
+     *
+     * @param schemaName Schema name.
+     * @return {@code True} if predefined.
+     */
+    private boolean isSchemaPredefined(String schemaName) {
+        if (F.eq(QueryUtils.DFLT_SCHEMA, schemaName))
+            return true;
+
+        for (H2Schema schema : schemas.values()) {
+            if (F.eq(schema.schemaName(), schemaName) && schema.predefined())
+                return true;
+        }
+
+        return false;
+    }
+
+    /**
      * Creates DB schema if it has not been created yet.
      *
      * @param schema Schema name.
-     * @throws IgniteCheckedException If failed to create db schema.
      */
-    private void createSchema(String schema) throws IgniteCheckedException {
+    private void createSchema0(String schema) {
         executeSystemStatement("CREATE SCHEMA IF NOT EXISTS " + 
H2Utils.withQuotes(schema));
 
         if (log.isDebugEnabled())
@@ -595,9 +636,8 @@ public class IgniteH2Indexing implements GridQueryIndexing {
      * Creates DB schema if it has not been created yet.
      *
      * @param schema Schema name.
-     * @throws IgniteCheckedException If failed to create db schema.
      */
-    private void dropSchema(String schema) throws IgniteCheckedException {
+    private void dropSchema(String schema) {
         executeSystemStatement("DROP SCHEMA IF EXISTS " + 
H2Utils.withQuotes(schema));
 
         if (log.isDebugEnabled())
@@ -1617,15 +1657,9 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
             return Collections.singletonList(H2Utils.zeroCursor());
         }
         else {
-            try {
-                FieldsQueryCursor<List<?>> cursor = 
ddlProc.runDdlStatement(sql, cmd);
+            FieldsQueryCursor<List<?>> cursor = ddlProc.runDdlStatement(sql, 
cmd);
 
-                return Collections.singletonList(cursor);
-            }
-            catch (IgniteCheckedException e) {
-                throw new IgniteSQLException("Failed to execute DDL statement 
[stmt=" + sql + "]: "
-                    + e.getMessage(), e);
-            }
+            return Collections.singletonList(cursor);
         }
     }
 
@@ -1783,12 +1817,7 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
                     throw new IgniteSQLException("DDL statements are not 
supported for LOCAL caches",
                         IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
 
-                try {
-                    return 
Collections.singletonList(ddlProc.runDdlStatement(sqlQry, prepared));
-                }
-                catch (IgniteCheckedException e) {
-                    throw new IgniteSQLException("Failed to execute DDL 
statement [stmt=" + sqlQry + ']', e);
-                }
+                return 
Collections.singletonList(ddlProc.runDdlStatement(sqlQry, prepared));
             }
 
             if (prepared instanceof NoOperation) {
@@ -2236,6 +2265,7 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
      * @param type Type descriptor.
      * @throws IgniteCheckedException If validation failed.
      */
+    @SuppressWarnings("CollectionAddAllCanBeReplacedWithConstructor")
     private void validateTypeDescriptor(GridQueryTypeDescriptor type)
         throws IgniteCheckedException {
         assert type != null;
@@ -2567,7 +2597,24 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
         else {
             this.ctx = ctx;
 
-            schemas.put(QueryUtils.DFLT_SCHEMA, new 
H2Schema(QueryUtils.DFLT_SCHEMA));
+            // Register PUBLIC schema which is always present.
+            schemas.put(QueryUtils.DFLT_SCHEMA, new 
H2Schema(QueryUtils.DFLT_SCHEMA, true));
+
+            // Register additional schemas.
+            String[] additionalSchemas = ctx.config().getSqlSchemas();
+
+            if (!F.isEmpty(additionalSchemas)) {
+                synchronized (schemaMux) {
+                    for (String schema : additionalSchemas) {
+                        if (F.isEmpty(schema))
+                            continue;
+
+                        schema = QueryUtils.normalizeSchemaName(null, schema);
+
+                        createSchemaIfNeeded(schema, true);
+                    }
+                }
+            }
 
             valCtx = new CacheQueryObjectValueContext(ctx);
 
@@ -2598,7 +2645,7 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
             if (sysViewsEnabled) {
                 try {
                     synchronized (schemaMux) {
-                        createSchema(QueryUtils.SCHEMA_SYS);
+                        createSchema0(QueryUtils.SCHEMA_SYS);
                     }
 
                     Connection c = connectionForSchema(QueryUtils.SCHEMA_SYS);
@@ -2802,7 +2849,7 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
     }
 
     /** {@inheritDoc} */
-    @Override public void stop() throws IgniteCheckedException {
+    @Override public void stop() {
         if (log.isDebugEnabled())
             log.debug("Stopping cache query index...");
 
@@ -2843,34 +2890,13 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
         }
     }
 
-    /**
-     * Whether this is default schema.
-     *
-     * @param schemaName Schema name.
-     * @return {@code True} if default.
-     */
-    private boolean isDefaultSchema(String schemaName) {
-        return F.eq(schemaName, QueryUtils.DFLT_SCHEMA);
-    }
-
     /** {@inheritDoc} */
     @Override public void registerCache(String cacheName, String schemaName, 
GridCacheContext<?, ?> cctx)
         throws IgniteCheckedException {
         rowCache.onCacheRegistered(cctx);
 
-        if (!isDefaultSchema(schemaName)) {
-            synchronized (schemaMux) {
-                H2Schema schema = new H2Schema(schemaName);
-
-                H2Schema oldSchema = schemas.putIfAbsent(schemaName, schema);
-
-                if (oldSchema == null)
-                    createSchema(schemaName);
-                else
-                    schema = oldSchema;
-
-                schema.incrementUsageCount();
-            }
+        synchronized (schemaMux) {
+            createSchemaIfNeeded(schemaName, false);
         }
 
         cacheName2schema.put(cacheName, schemaName);
@@ -2915,17 +2941,15 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
                 }
             }
 
-            if (!isDefaultSchema(schemaName)) {
-                synchronized (schemaMux) {
-                    if (schema.decrementUsageCount() == 0) {
-                        schemas.remove(schemaName);
+            synchronized (schemaMux) {
+                if (schema.decrementUsageCount()) {
+                    schemas.remove(schemaName);
 
-                        try {
-                            dropSchema(schemaName);
-                        }
-                        catch (IgniteCheckedException e) {
-                            U.error(log, "Failed to drop schema on cache stop 
(will ignore): " + cacheName, e);
-                        }
+                    try {
+                        dropSchema(schemaName);
+                    }
+                    catch (IgniteException e) {
+                        U.error(log, "Failed to drop schema on cache stop 
(will ignore): " + cacheName, e);
                     }
                 }
             }

http://git-wip-us.apache.org/repos/asf/ignite/blob/277010cd/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ddl/DdlStatementsProcessor.java
----------------------------------------------------------------------
diff --git 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ddl/DdlStatementsProcessor.java
 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ddl/DdlStatementsProcessor.java
index c617d30..91ebece 100644
--- 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ddl/DdlStatementsProcessor.java
+++ 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ddl/DdlStatementsProcessor.java
@@ -111,10 +111,9 @@ public class DdlStatementsProcessor {
      * @param sql Original SQL.
      * @param cmd Command.
      * @return Result.
-     * @throws IgniteCheckedException On error.
      */
     @SuppressWarnings("unchecked")
-    public FieldsQueryCursor<List<?>> runDdlStatement(String sql, SqlCommand 
cmd) throws IgniteCheckedException {
+    public FieldsQueryCursor<List<?>> runDdlStatement(String sql, SqlCommand 
cmd) {
         IgniteInternalFuture fut = null;
 
         try {
@@ -123,7 +122,7 @@ public class DdlStatementsProcessor {
             if (cmd instanceof SqlCreateIndexCommand) {
                 SqlCreateIndexCommand cmd0 = (SqlCreateIndexCommand)cmd;
 
-                GridH2Table tbl = idx.dataTable(cmd0.schemaName(), 
cmd0.tableName());
+                GridH2Table tbl = dataTableWithRetry(cmd0.schemaName(), 
cmd0.tableName());
 
                 if (tbl == null)
                     throw new 
SchemaOperationException(SchemaOperationException.CODE_TABLE_NOT_FOUND, 
cmd0.tableName());
@@ -161,7 +160,7 @@ public class DdlStatementsProcessor {
             else if (cmd instanceof SqlDropIndexCommand) {
                 SqlDropIndexCommand cmd0 = (SqlDropIndexCommand)cmd;
 
-                GridH2Table tbl = idx.dataTableForIndex(cmd0.schemaName(), 
cmd0.indexName());
+                GridH2Table tbl = 
dataTableForIndexWithRetry(cmd0.schemaName(), cmd0.indexName());
 
                 if (tbl != null) {
                     isDdlSupported(tbl);
@@ -180,13 +179,7 @@ public class DdlStatementsProcessor {
             else if (cmd instanceof SqlAlterTableCommand) {
                 SqlAlterTableCommand cmd0 = (SqlAlterTableCommand)cmd;
 
-                GridH2Table tbl = idx.dataTable(cmd0.schemaName(), 
cmd0.tableName());
-
-                if (tbl == null) {
-                    ctx.cache().createMissingQueryCaches();
-
-                    tbl = idx.dataTable(cmd0.schemaName(), cmd0.tableName());
-                }
+                GridH2Table tbl = dataTableWithRetry(cmd0.schemaName(), 
cmd0.tableName());
 
                 if (tbl == null) {
                     throw new 
SchemaOperationException(SchemaOperationException.CODE_TABLE_NOT_FOUND,
@@ -255,11 +248,9 @@ public class DdlStatementsProcessor {
      * @param sql SQL.
      * @param prepared Prepared.
      * @return Cursor on query results.
-     * @throws IgniteCheckedException On error.
      */
     @SuppressWarnings({"unchecked", "ThrowableResultOfMethodCallIgnored"})
-    public FieldsQueryCursor<List<?>> runDdlStatement(String sql, Prepared 
prepared)
-        throws IgniteCheckedException {
+    public FieldsQueryCursor<List<?>> runDdlStatement(String sql, Prepared 
prepared) {
         IgniteInternalFuture fut = null;
 
         try {
@@ -270,7 +261,7 @@ public class DdlStatementsProcessor {
 
                 isDdlOnSchemaSupported(cmd.schemaName());
 
-                GridH2Table tbl = idx.dataTable(cmd.schemaName(), 
cmd.tableName());
+                GridH2Table tbl = dataTableWithRetry(cmd.schemaName(), 
cmd.tableName());
 
                 if (tbl == null)
                     throw new 
SchemaOperationException(SchemaOperationException.CODE_TABLE_NOT_FOUND, 
cmd.tableName());
@@ -309,7 +300,7 @@ public class DdlStatementsProcessor {
 
                 isDdlOnSchemaSupported(cmd.schemaName());
 
-                GridH2Table tbl = idx.dataTableForIndex(cmd.schemaName(), 
cmd.indexName());
+                GridH2Table tbl = dataTableForIndexWithRetry(cmd.schemaName(), 
cmd.indexName());
 
                 if (tbl != null) {
                     isDdlSupported(tbl);
@@ -332,11 +323,7 @@ public class DdlStatementsProcessor {
 
                 isDdlOnSchemaSupported(cmd.schemaName());
 
-                if (!F.eq(QueryUtils.DFLT_SCHEMA, cmd.schemaName()))
-                    throw new SchemaOperationException("CREATE TABLE can only 
be executed on " +
-                        QueryUtils.DFLT_SCHEMA + " schema.");
-
-                GridH2Table tbl = idx.dataTable(cmd.schemaName(), 
cmd.tableName());
+                GridH2Table tbl = dataTableWithRetry(cmd.schemaName(), 
cmd.tableName());
 
                 if (tbl != null) {
                     if (!cmd.ifNotExists())
@@ -369,17 +356,7 @@ public class DdlStatementsProcessor {
 
                 isDdlOnSchemaSupported(cmd.schemaName());
 
-                if (!F.eq(QueryUtils.DFLT_SCHEMA, cmd.schemaName()))
-                    throw new SchemaOperationException("DROP TABLE can only be 
executed on " +
-                        QueryUtils.DFLT_SCHEMA + " schema.");
-
-                GridH2Table tbl = idx.dataTable(cmd.schemaName(), 
cmd.tableName());
-
-                if (tbl == null && cmd.ifExists()) {
-                    ctx.cache().createMissingQueryCaches();
-
-                    tbl = idx.dataTable(cmd.schemaName(), cmd.tableName());
-                }
+                GridH2Table tbl = dataTableWithRetry(cmd.schemaName(), 
cmd.tableName());
 
                 if (tbl == null) {
                     if (!cmd.ifExists())
@@ -394,13 +371,7 @@ public class DdlStatementsProcessor {
 
                 isDdlOnSchemaSupported(cmd.schemaName());
 
-                GridH2Table tbl = idx.dataTable(cmd.schemaName(), 
cmd.tableName());
-
-                if (tbl == null && cmd.ifTableExists()) {
-                    ctx.cache().createMissingQueryCaches();
-
-                    tbl = idx.dataTable(cmd.schemaName(), cmd.tableName());
-                }
+                GridH2Table tbl = dataTableWithRetry(cmd.schemaName(), 
cmd.tableName());
 
                 if (tbl == null) {
                     if (!cmd.ifTableExists())
@@ -455,13 +426,7 @@ public class DdlStatementsProcessor {
 
                 isDdlOnSchemaSupported(cmd.schemaName());
 
-                GridH2Table tbl = idx.dataTable(cmd.schemaName(), 
cmd.tableName());
-
-                if (tbl == null && cmd.ifTableExists()) {
-                    ctx.cache().createMissingQueryCaches();
-
-                    tbl = idx.dataTable(cmd.schemaName(), cmd.tableName());
-                }
+                GridH2Table tbl = dataTableWithRetry(cmd.schemaName(), 
cmd.tableName());
 
                 if (tbl == null) {
                     if (!cmd.ifTableExists())
@@ -533,6 +498,46 @@ public class DdlStatementsProcessor {
     }
 
     /**
+     * Get table by name optionally creating missing query caches.
+     *
+     * @param schemaName Schema name.
+     * @param tableName Table name.
+     * @return Table or {@code null} if none found.
+     * @throws IgniteCheckedException If failed.
+     */
+    private GridH2Table dataTableWithRetry(String schemaName, String 
tableName) throws IgniteCheckedException {
+        GridH2Table tbl = idx.dataTable(schemaName, tableName);
+
+        if (tbl == null) {
+            ctx.cache().createMissingQueryCaches();
+
+            tbl = idx.dataTable(schemaName, tableName);
+        }
+
+        return tbl;
+    }
+
+    /**
+     * Get table by name optionally creating missing query caches.
+     *
+     * @param schemaName Schema name.
+     * @param indexName Index name.
+     * @return Table or {@code null} if none found.
+     * @throws IgniteCheckedException If failed.
+     */
+    private GridH2Table dataTableForIndexWithRetry(String schemaName, String 
indexName) throws IgniteCheckedException {
+        GridH2Table tbl = idx.dataTableForIndex(schemaName, indexName);
+
+        if (tbl == null) {
+            ctx.cache().createMissingQueryCaches();
+
+            tbl = idx.dataTableForIndex(schemaName, indexName);
+        }
+
+        return tbl;
+    }
+
+    /**
      * Check if schema supports DDL statement.
      *
      * @param schemaName Schema name.

http://git-wip-us.apache.org/repos/asf/ignite/blob/277010cd/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicTableSelfTest.java
----------------------------------------------------------------------
diff --git 
a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicTableSelfTest.java
 
b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicTableSelfTest.java
index d132ebb..2fc69f6 100644
--- 
a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicTableSelfTest.java
+++ 
b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicTableSelfTest.java
@@ -1423,24 +1423,6 @@ public class H2DynamicTableSelfTest extends 
AbstractSchemaSelfTest {
     }
 
     /**
-     * Test that {@code CREATE TABLE} in non-public schema causes an exception.
-     *
-     * @throws Exception if failed.
-     */
-    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
-    public void testCreateTableInNonPublicSchema() throws Exception {
-        GridTestUtils.assertThrows(null, new Callable<Object>() {
-            @Override public Object call() throws Exception {
-                execute("CREATE TABLE \"cache_idx\".\"Person\" (\"id\" int, 
\"city\" varchar," +
-                    " \"name\" varchar, \"surname\" varchar, \"age\" int, 
PRIMARY KEY (\"id\", \"city\")) WITH " +
-                    "\"template=cache\"");
-
-                return null;
-            }
-        }, IgniteSQLException.class, "CREATE TABLE can only be executed on 
PUBLIC schema.");
-    }
-
-    /**
      * Execute {@code CREATE TABLE} w/given params expecting a particular 
error.
      * @param params Engine parameters.
      * @param expErrMsg Expected error message.
@@ -1473,17 +1455,6 @@ public class H2DynamicTableSelfTest extends 
AbstractSchemaSelfTest {
     }
 
     /**
-     * Test that {@code DROP TABLE} on non-public schema causes an exception.
-     *
-     * @throws Exception if failed.
-     */
-    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
-    public void testDropTableNotPublicSchema() throws Exception {
-       assertDdlCommandThrows("DROP TABLE \"cache_idx\".\"Person\"",
-           "DROP TABLE can only be executed on PUBLIC schema.");
-    }
-
-    /**
      * Test that {@link IgniteH2Indexing#tables(String)} method
      * only returns tables belonging to given cache.
      *

http://git-wip-us.apache.org/repos/asf/ignite/blob/277010cd/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/SqlIllegalSchemaSelfTest.java
----------------------------------------------------------------------
diff --git 
a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/SqlIllegalSchemaSelfTest.java
 
b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/SqlIllegalSchemaSelfTest.java
new file mode 100644
index 0000000..e56f8a2
--- /dev/null
+++ 
b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/SqlIllegalSchemaSelfTest.java
@@ -0,0 +1,187 @@
+/*
+ * 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;
+
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+import javax.cache.CacheException;
+import java.util.concurrent.Callable;
+
+/**
+ * Tests for illegal SQL schemas in node and cache configurations.
+ */
+@SuppressWarnings({"ThrowableNotThrown", "unchecked"})
+public class SqlIllegalSchemaSelfTest extends GridCommonAbstractTest {
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        stopAllGrids();
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testBadCacheName() throws Exception {
+        IgniteConfiguration cfg = getConfiguration();
+
+        cfg.setCacheConfiguration(new 
CacheConfiguration().setName(QueryUtils.SCHEMA_SYS));
+
+        GridTestUtils.assertThrows(log, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                Ignition.start(cfg);
+
+                return null;
+            }
+        }, IgniteException.class, "SQL schema name derived from cache name is 
reserved (please set explicit SQL " +
+            "schema name through CacheConfiguration.setSqlSchema() or choose 
another cache name) [cacheName=IGNITE, " +
+            "schemaName=null]");
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testBadCacheNameDynamic() throws Exception {
+        Ignite node = startGrid();
+
+        GridTestUtils.assertThrows(log, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                node.getOrCreateCache(new 
CacheConfiguration().setName(QueryUtils.SCHEMA_SYS));
+
+                return null;
+            }
+        }, CacheException.class, "SQL schema name derived from cache name is 
reserved (please set explicit SQL " +
+            "schema name through CacheConfiguration.setSqlSchema() or choose 
another cache name) [" +
+            "cacheName=IGNITE, schemaName=null]");
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testBadSchemaLower() throws Exception {
+        IgniteConfiguration cfg = getConfiguration();
+
+        cfg.setCacheConfiguration(new CacheConfiguration().setName("CACHE")
+            .setSqlSchema(QueryUtils.SCHEMA_SYS.toLowerCase()));
+
+        GridTestUtils.assertThrows(log, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                Ignition.start(cfg);
+
+                return null;
+            }
+        }, IgniteException.class, "SQL schema name is reserved (please choose 
another one) [cacheName=CACHE, " +
+            "schemaName=ignite]");
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testBadSchemaLowerDynamic() throws Exception {
+        Ignite node = startGrid();
+
+        GridTestUtils.assertThrows(log, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                node.getOrCreateCache(
+                    new 
CacheConfiguration().setName("CACHE").setSqlSchema(QueryUtils.SCHEMA_SYS.toLowerCase())
+                );
+
+                return null;
+            }
+        }, CacheException.class, "SQL schema name is reserved (please choose 
another one) [cacheName=CACHE, schemaName=ignite]");
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testBadSchemaUpper() throws Exception {
+        IgniteConfiguration cfg = getConfiguration();
+
+        cfg.setCacheConfiguration(new CacheConfiguration().setName("CACHE")
+            .setSqlSchema(QueryUtils.SCHEMA_SYS.toUpperCase()));
+
+        GridTestUtils.assertThrows(log, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                Ignition.start(cfg);
+
+                return null;
+            }
+        }, IgniteException.class, "SQL schema name is reserved (please choose 
another one) [cacheName=CACHE, " +
+            "schemaName=IGNITE]");
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testBadSchemaUpperDynamic() throws Exception {
+        Ignite node = startGrid();
+
+        GridTestUtils.assertThrows(log, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                node.getOrCreateCache(
+                    new 
CacheConfiguration().setName("CACHE").setSqlSchema(QueryUtils.SCHEMA_SYS.toUpperCase())
+                );
+
+                return null;
+            }
+        }, CacheException.class, "SQL schema name is reserved (please choose 
another one) [cacheName=CACHE, " +
+            "schemaName=IGNITE]");
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testBadSchemaQuoted() throws Exception {
+        IgniteConfiguration cfg = getConfiguration();
+
+        cfg.setCacheConfiguration(new CacheConfiguration().setName("CACHE")
+            .setSqlSchema("\"" + QueryUtils.SCHEMA_SYS.toUpperCase() + "\""));
+
+        GridTestUtils.assertThrows(log, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                Ignition.start(cfg);
+
+                return null;
+            }
+        }, IgniteException.class, "SQL schema name is reserved (please choose 
another one) [cacheName=CACHE, " +
+            "schemaName=\"IGNITE\"]");
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testBadSchemaQuotedDynamic() throws Exception {
+        Ignite node = startGrid();
+
+        GridTestUtils.assertThrows(log, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                node.getOrCreateCache(
+                    new CacheConfiguration().setName("CACHE")
+                        .setSqlSchema("\"" + 
QueryUtils.SCHEMA_SYS.toUpperCase() + "\"")
+                );
+
+                return null;
+            }
+        }, CacheException.class, "SQL schema name is reserved (please choose 
another one) [cacheName=CACHE, " +
+            "schemaName=\"IGNITE\"]");
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/277010cd/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java
----------------------------------------------------------------------
diff --git 
a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java
 
b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java
index d70e5c3..a658caf 100644
--- 
a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java
+++ 
b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java
@@ -176,6 +176,7 @@ import 
org.apache.ignite.internal.processors.query.IgniteSqlSkipReducerOnUpdateD
 import org.apache.ignite.internal.processors.query.IgniteSqlSplitterSelfTest;
 import org.apache.ignite.internal.processors.query.LazyQuerySelfTest;
 import 
org.apache.ignite.internal.processors.query.MultipleStatementsSqlQuerySelfTest;
+import org.apache.ignite.internal.processors.query.SqlIllegalSchemaSelfTest;
 import org.apache.ignite.internal.processors.query.SqlSystemViewsSelfTest;
 import org.apache.ignite.internal.processors.query.SqlPushDownFunctionTest;
 import org.apache.ignite.internal.processors.query.SqlSchemaSelfTest;
@@ -221,6 +222,7 @@ public class IgniteCacheQuerySelfTestSuite extends 
TestSuite {
         
suite.addTestSuite(ClientConnectorConfigurationValidationSelfTest.class);
 
         suite.addTestSuite(SqlSchemaSelfTest.class);
+        suite.addTestSuite(SqlIllegalSchemaSelfTest.class);
         suite.addTestSuite(MultipleStatementsSqlQuerySelfTest.class);
 
         // Misc tests.

http://git-wip-us.apache.org/repos/asf/ignite/blob/277010cd/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Config/full-config.xml
----------------------------------------------------------------------
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Config/full-config.xml 
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Config/full-config.xml
index d50cf4f..b091a49 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Config/full-config.xml
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Config/full-config.xml
@@ -47,6 +47,10 @@
         <string>-Xms1g</string>
         <string>-Xmx4g</string>
     </jvmOptions>
+    <sqlSchemas>
+        <string>SCHEMA_1</string>
+        <string>schema_2</string>
+    </sqlSchemas>
     <lifecycleHandlers>
         <iLifecycleHandler 
type='Apache.Ignite.Core.Tests.IgniteConfigurationSerializerTest+LifecycleBean' 
foo='15' />
     </lifecycleHandlers>

http://git-wip-us.apache.org/repos/asf/ignite/blob/277010cd/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationSerializerTest.cs
----------------------------------------------------------------------
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationSerializerTest.cs
 
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationSerializerTest.cs
index 226106f..e2ece20 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationSerializerTest.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationSerializerTest.cs
@@ -99,6 +99,12 @@ namespace Apache.Ignite.Core.Tests
             Assert.AreEqual(new TimeSpan(1, 2, 3), 
cfg.LongQueryWarningTimeout);
             Assert.IsFalse(cfg.IsActiveOnStart);
             Assert.IsTrue(cfg.AuthenticationEnabled);
+
+            Assert.IsNotNull(cfg.SqlSchemas);
+            Assert.AreEqual(2, cfg.SqlSchemas.Count);
+            Assert.IsTrue(cfg.SqlSchemas.Contains("SCHEMA_1"));
+            Assert.IsTrue(cfg.SqlSchemas.Contains("schema_2"));
+
             Assert.AreEqual("someId012", cfg.ConsistentId);
             Assert.IsFalse(cfg.RedirectJavaConsoleOutput);
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/277010cd/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationTest.cs
----------------------------------------------------------------------
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationTest.cs 
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationTest.cs
index 6c772f4..a03d09c 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationTest.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationTest.cs
@@ -19,6 +19,7 @@
 namespace Apache.Ignite.Core.Tests
 {
     using System;
+    using System.Collections.Generic;
     using System.ComponentModel;
     using System.IO;
     using System.Linq;
@@ -240,6 +241,11 @@ namespace Apache.Ignite.Core.Tests
                 Assert.AreEqual(sql.ThreadPoolSize, resSql.ThreadPoolSize);
 
                 AssertExtensions.ReflectionEqual(cfg.DataStorageConfiguration, 
resCfg.DataStorageConfiguration);
+
+                Assert.IsNotNull(resCfg.SqlSchemas);
+                Assert.AreEqual(2, resCfg.SqlSchemas.Count);
+                Assert.IsTrue(resCfg.SqlSchemas.Contains("SCHEMA_3"));
+                Assert.IsTrue(resCfg.SqlSchemas.Contains("schema_4"));
             }
         }
 
@@ -829,7 +835,9 @@ namespace Apache.Ignite.Core.Tests
                         }
                     }
                 },
-                AuthenticationEnabled = false
+                AuthenticationEnabled = false,
+
+                SqlSchemas = new List<string> { "SCHEMA_3", "schema_4" }
             };
         }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/277010cd/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfiguration.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfiguration.cs 
b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfiguration.cs
index 7d8cfc7..9bcf763 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfiguration.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfiguration.cs
@@ -305,6 +305,19 @@ namespace Apache.Ignite.Core
             writer.WriteTimeSpanAsLongNullable(_longQueryWarningTimeout);
             writer.WriteBooleanNullable(_isActiveOnStart);
             writer.WriteBooleanNullable(_authenticationEnabled);
+
+            if (SqlSchemas == null)
+                writer.WriteInt(-1);
+            else
+            {
+                writer.WriteInt(SqlSchemas.Count);
+
+                foreach (string sqlSchema in SqlSchemas)
+                {
+                    writer.WriteString(sqlSchema);
+                }
+            }
+
             writer.WriteObjectDetached(ConsistentId);
 
             // Thread pools
@@ -657,6 +670,19 @@ namespace Apache.Ignite.Core
             _longQueryWarningTimeout = r.ReadTimeSpanNullable();
             _isActiveOnStart = r.ReadBooleanNullable();
             _authenticationEnabled = r.ReadBooleanNullable();
+
+            int sqlSchemasCnt = r.ReadInt();
+
+            if (sqlSchemasCnt == -1)
+                SqlSchemas = null;
+            else
+            {
+                SqlSchemas = new List<string>(sqlSchemasCnt);
+
+                for (int i = 0; i < sqlSchemasCnt; i++)
+                    SqlSchemas.Add(r.ReadString());
+            }
+
             ConsistentId = r.ReadObject<object>();
 
             // Thread pools
@@ -1529,5 +1555,13 @@ namespace Apache.Ignite.Core
         /// <see cref="NoOpFailureHandler"/>, <see 
cref="StopNodeOrHaltFailureHandler"/>, <see cref="StopNodeFailureHandler"/>.
         /// </summary>
         public IFailureHandler FailureHandler { get; set; }
+
+        /// <summary>
+        /// Gets or sets SQL schemas to be created on node startup. Schemas 
are created on local node only and are not propagated.
+        /// to other cluster nodes. Created schemas cannot be dropped.
+        /// <para/>
+        /// By default schema names are case-insensitive. Use quotes to 
enforce case sensitivity.
+        /// </summary>
+        public ICollection<String> SqlSchemas { get; set; }
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/277010cd/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd
----------------------------------------------------------------------
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd 
b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd
index f16ef43..8db5afa 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd
@@ -2029,6 +2029,16 @@
                         </xs:attribute>
                     </xs:complexType>
                 </xs:element>
+                <xs:element name="sqlSchemas" minOccurs="0">
+                    <xs:annotation>
+                        <xs:documentation>SQL schemas to be created on node 
startup. Schemas are created on local node only and are not 
propagated.</xs:documentation>
+                    </xs:annotation>
+                    <xs:complexType>
+                        <xs:sequence>
+                            <xs:element maxOccurs="unbounded" name="string" 
type="xs:string" />
+                        </xs:sequence>
+                    </xs:complexType>
+                </xs:element>
             </xs:all>
             <xs:attribute name="igniteInstanceName" type="xs:string">
                 <xs:annotation>

Reply via email to