You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by is...@apache.org on 2022/02/08 08:41:28 UTC

[ignite] branch master updated: IGNITE-16492 Add schema-related tests

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

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


The following commit(s) were added to refs/heads/master by this push:
     new f1d90ac  IGNITE-16492 Add schema-related tests
f1d90ac is described below

commit f1d90ac30f02e99e26ebaeb38a77150f5af81a22
Author: Igor Sapego <is...@apache.org>
AuthorDate: Tue Feb 8 11:40:43 2022 +0300

    IGNITE-16492 Add schema-related tests
    
    This closes #9806
---
 modules/platforms/cpp/core-test/CMakeLists.txt     |   1 +
 .../cpp/core-test/config/cache-query-schema-32.xml |  52 ++++
 .../config/cache-query-schema-default.xml          |  60 ++++
 .../cpp/core-test/config/cache-query-schema.xml    |  34 +++
 .../cpp/core-test/src/cache_query_schema_test.cpp  | 312 +++++++++++++++++++
 modules/platforms/cpp/odbc-test/CMakeLists.txt     |   3 +-
 .../cpp/odbc-test/config/queries-schema-32.xml     |  52 ++++
 .../odbc-test/config/queries-schema-default.xml    |  68 +++++
 .../cpp/odbc-test/config/queries-schema.xml        |  34 +++
 .../cpp/odbc-test/src/sql_schema_test.cpp          | 337 +++++++++++++++++++++
 .../Cache/Query/CacheDmlQueriesTestSchema.cs       | 255 ++++++++++++++++
 11 files changed, 1207 insertions(+), 1 deletion(-)

diff --git a/modules/platforms/cpp/core-test/CMakeLists.txt b/modules/platforms/cpp/core-test/CMakeLists.txt
index 04791b1..188716e 100644
--- a/modules/platforms/cpp/core-test/CMakeLists.txt
+++ b/modules/platforms/cpp/core-test/CMakeLists.txt
@@ -62,6 +62,7 @@ set(SOURCES
         src/test_utils.cpp
         src/cluster_node_test.cpp
         src/affinity_test.cpp
+        src/cache_query_schema_test.cpp
         src/cluster_group_test.cpp)
 
 add_executable(${TARGET} ${SOURCES})
diff --git a/modules/platforms/cpp/core-test/config/cache-query-schema-32.xml b/modules/platforms/cpp/core-test/config/cache-query-schema-32.xml
new file mode 100644
index 0000000..5c639121
--- /dev/null
+++ b/modules/platforms/cpp/core-test/config/cache-query-schema-32.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+    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.
+-->
+
+<!--
+    Ignite Spring configuration file to startup grid cache.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xsi:schemaLocation="
+        http://www.springframework.org/schema/beans
+        http://www.springframework.org/schema/beans/spring-beans.xsd
+        http://www.springframework.org/schema/util
+        http://www.springframework.org/schema/util/spring-util.xsd">
+    <import resource="cache-query-schema-default.xml"/>
+
+    <bean parent="grid.cfg">
+        <property name="memoryConfiguration">
+            <bean class="org.apache.ignite.configuration.MemoryConfiguration">
+                <property name="systemCacheInitialSize" value="#{10 * 1024 * 1024}"/>
+                <property name="systemCacheMaxSize" value="#{40 * 1024 * 1024}"/>
+                <property name="defaultMemoryPolicyName" value="dfltPlc"/>
+
+                <property name="memoryPolicies">
+                    <list>
+                        <bean class="org.apache.ignite.configuration.MemoryPolicyConfiguration">
+                            <property name="name" value="dfltPlc"/>
+                            <property name="maxSize" value="#{100 * 1024 * 1024}"/>
+                            <property name="initialSize" value="#{10 * 1024 * 1024}"/>
+                        </bean>
+                    </list>
+                </property>
+            </bean>
+        </property>
+    </bean>
+</beans>
diff --git a/modules/platforms/cpp/core-test/config/cache-query-schema-default.xml b/modules/platforms/cpp/core-test/config/cache-query-schema-default.xml
new file mode 100644
index 0000000..84c12e1
--- /dev/null
+++ b/modules/platforms/cpp/core-test/config/cache-query-schema-default.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+    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.
+-->
+
+<!--
+    Ignite Spring configuration file to startup grid cache.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xsi:schemaLocation="
+        http://www.springframework.org/schema/beans
+        http://www.springframework.org/schema/beans/spring-beans.xsd
+        http://www.springframework.org/schema/util
+        http://www.springframework.org/schema/util/spring-util.xsd">
+    <bean abstract="true" id="grid.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
+        <property name="localHost" value="127.0.0.1"/>
+        <property name="connectorConfiguration"><null/></property>
+
+        <property name="sqlSchemas">
+            <list>
+                <value>SCHEMA_1</value>
+                <value>SCHEMA_2</value>
+                <value>"ScHeMa3"</value>
+                <value>SCHEMA_4</value>
+            </list>
+        </property>
+
+        <property name="discoverySpi">
+            <bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi">
+                <property name="ipFinder">
+                    <bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder">
+                        <property name="addresses">
+                            <list>
+                                <!-- In distributed environment, replace with actual host IP address. -->
+                                <value>127.0.0.1:47500</value>
+                            </list>
+                        </property>
+                    </bean>
+                </property>
+                <property name="socketTimeout" value="300" />
+            </bean>
+        </property>
+    </bean>
+</beans>
diff --git a/modules/platforms/cpp/core-test/config/cache-query-schema.xml b/modules/platforms/cpp/core-test/config/cache-query-schema.xml
new file mode 100644
index 0000000..5e13ac4
--- /dev/null
+++ b/modules/platforms/cpp/core-test/config/cache-query-schema.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+    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.
+-->
+
+<!--
+    Ignite Spring configuration file to startup grid cache.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xsi:schemaLocation="
+        http://www.springframework.org/schema/beans
+        http://www.springframework.org/schema/beans/spring-beans.xsd
+        http://www.springframework.org/schema/util
+        http://www.springframework.org/schema/util/spring-util.xsd">
+    <import resource="cache-query-schema-default.xml"/>
+
+    <bean parent="grid.cfg"/>
+</beans>
diff --git a/modules/platforms/cpp/core-test/src/cache_query_schema_test.cpp b/modules/platforms/cpp/core-test/src/cache_query_schema_test.cpp
new file mode 100644
index 0000000..b796464
--- /dev/null
+++ b/modules/platforms/cpp/core-test/src/cache_query_schema_test.cpp
@@ -0,0 +1,312 @@
+/*
+ * 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.
+ */
+
+#include <stdint.h>
+
+#include <iterator>
+
+#include <boost/test/unit_test.hpp>
+
+#include <ignite/common/utils.h>
+
+#include "ignite/cache/cache.h"
+#include "ignite/cache/query/query_cursor.h"
+#include "ignite/cache/query/query_sql_fields.h"
+#include "ignite/ignite.h"
+#include "ignite/ignition.h"
+#include "ignite/test_utils.h"
+
+using namespace boost::unit_test;
+
+using namespace ignite;
+using namespace ignite::cache;
+using namespace ignite::cache::query;
+using namespace ignite::common;
+
+using ignite::impl::binary::BinaryUtils;
+
+/**
+ * Ensure that cursor is empy fails.
+ *
+ * @param cur Cursor.
+ */
+template<typename Cursor>
+void ChechEmptyCursorGetNextThrowsException(Cursor& cur)
+{
+    BOOST_REQUIRE(!cur.HasNext());
+    BOOST_CHECK_EXCEPTION(cur.GetNext(), IgniteError, ignite_test::IsGenericError);
+}
+
+/**
+ * Check single row through iteration.
+ *
+ * @param cur Cursor.
+ * @param c1 First column.
+ */
+template<typename T1>
+void CheckSingleRow(QueryFieldsCursor& cur, const T1& c1)
+{
+    BOOST_REQUIRE(cur.HasNext());
+
+    QueryFieldsRow row = cur.GetNext();
+
+    BOOST_REQUIRE_EQUAL(row.GetNext<T1>(), c1);
+
+    BOOST_REQUIRE(!row.HasNext());
+
+    ChechEmptyCursorGetNextThrowsException(cur);
+}
+
+/**
+ * Check row through iteration.
+ *
+ * @param cur Cursor.
+ * @param c1 First column.
+ */
+template<typename T1, typename T2>
+void CheckRow(QueryFieldsCursor& cur, const T1& c1, const T2& c2)
+{
+    BOOST_REQUIRE(cur.HasNext());
+
+    QueryFieldsRow row = cur.GetNext();
+
+    BOOST_REQUIRE_EQUAL(row.GetNext<T1>(), c1);
+    BOOST_REQUIRE_EQUAL(row.GetNext<T2>(), c2);
+
+    BOOST_REQUIRE(!row.HasNext());
+}
+
+/**
+ * Check single row through iteration.
+ *
+ * @param cur Cursor.
+ * @param c1 First column.
+ */
+template<typename T1, typename T2>
+void CheckSingleRow(QueryFieldsCursor& cur, const T1& c1, const T2& c2)
+{
+    CheckRow<T1, T2>(cur, c1, c2);
+
+    ChechEmptyCursorGetNextThrowsException(cur);
+}
+
+static const std::string TABLE_NAME = "T1";
+static const std::string SCHEMA_NAME_1 = "SCHEMA_1";
+static const std::string SCHEMA_NAME_2 = "SCHEMA_2";
+static const std::string SCHEMA_NAME_3 = "ScHeMa3";
+static const std::string Q_SCHEMA_NAME_3 = '"' + SCHEMA_NAME_3 + '"';
+static const std::string SCHEMA_NAME_4 = "SCHEMA_4";
+
+/**
+ * Test setup fixture.
+ */
+struct CacheQuerySchemaTestSuiteFixture
+{
+    Ignite StartNode(const char* name)
+    {
+#ifdef IGNITE_TESTS_32
+        return ignite_test::StartNode("cache-query-schema-32.xml", name);
+#else
+        return ignite_test::StartNode("cache-query-schema.xml", name);
+#endif
+    }
+
+    /**
+     * Constructor.
+     */
+    CacheQuerySchemaTestSuiteFixture() :
+        grid(StartNode("Node1"))
+    {
+        // No-op.
+    }
+
+    /**
+     * Destructor.
+     */
+    ~CacheQuerySchemaTestSuiteFixture()
+    {
+        Ignition::StopAll(true);
+    }
+
+    /** Perform SQL in cluster. */
+    QueryFieldsCursor Sql(const std::string& sql)
+    {
+        return grid
+            .GetOrCreateCache<int, int>("SchemaTestCache")
+            .Query(SqlFieldsQuery(sql));
+    }
+
+    std::string TableName(bool withSchema)
+    {
+        return withSchema ? "PUBLIC." + TABLE_NAME : TABLE_NAME;
+    }
+
+    template<typename Predicate>
+    void ExecuteStatementsAndVerify(Predicate& pred)
+    {
+        Sql("CREATE TABLE " + TableName(pred()) + " (id INT PRIMARY KEY, val INT)");
+
+        Sql("CREATE INDEX t1_idx_1 ON " + TableName(pred()) + "(val)");
+
+        Sql("INSERT INTO " + TableName(pred()) + " (id, val) VALUES(1, 2)");
+
+        QueryFieldsCursor cursor = Sql("SELECT * FROM " + TableName(pred()));
+        CheckSingleRow<int32_t, int32_t>(cursor, 1, 2);
+
+        Sql("UPDATE " + TableName(pred()) + " SET val = 5");
+        cursor = Sql("SELECT * FROM " + TableName(pred()));
+        CheckSingleRow<int32_t, int32_t>(cursor, 1, 5);
+
+        Sql("DELETE FROM " + TableName(pred()) + " WHERE id = 1");
+        cursor = Sql("SELECT COUNT(*) FROM " + TableName(pred()));
+        CheckSingleRow<int64_t>(cursor, 0);
+
+        cursor = Sql("SELECT COUNT(*) FROM SYS.TABLES WHERE schema_name = 'PUBLIC' "
+            "AND table_name = \'" + TABLE_NAME + "\'");
+        CheckSingleRow<int64_t>(cursor, 1);
+
+        Sql("DROP TABLE " + TableName(pred()));
+    }
+
+    /** Node started during the test. */
+    Ignite grid;
+};
+
+BOOST_FIXTURE_TEST_SUITE(CacheQuerySchemaTestSuite, CacheQuerySchemaTestSuiteFixture)
+
+bool TruePred()
+{
+    return true;
+}
+
+BOOST_AUTO_TEST_CASE(TestBasicOpsExplicitPublicSchema)
+{
+    ExecuteStatementsAndVerify(TruePred);
+}
+
+bool FalsePred()
+{
+    return false;
+}
+
+BOOST_AUTO_TEST_CASE(TestBasicOpsImplicitPublicSchema)
+{
+    ExecuteStatementsAndVerify(FalsePred);
+}
+
+struct MixedPred
+{
+    int i;
+
+    MixedPred() : i(0)
+    {
+        // No-op.
+    }
+
+    bool operator()()
+    {
+        return (++i & 1) == 0;
+    }
+};
+
+BOOST_AUTO_TEST_CASE(TestBasicOpsMixedPublicSchema)
+{
+    MixedPred pred;
+
+    ExecuteStatementsAndVerify(pred);
+}
+
+BOOST_AUTO_TEST_CASE(TestCreateDropNonExistingSchema)
+{
+    BOOST_CHECK_THROW(
+        Sql("CREATE TABLE UNKNOWN_SCHEMA." + TABLE_NAME + "(id INT PRIMARY KEY, val INT)"),
+        IgniteError
+    );
+
+    BOOST_CHECK_THROW(
+        Sql("DROP TABLE UNKNOWN_SCHEMA." + TABLE_NAME),
+        IgniteError
+    );
+}
+
+BOOST_AUTO_TEST_CASE(TestBasicOpsDiffSchemas)
+{
+    Sql("CREATE TABLE " + SCHEMA_NAME_1 + '.' + TABLE_NAME + " (s1_key INT PRIMARY KEY, s1_val INT)");
+    Sql("CREATE TABLE " + SCHEMA_NAME_2 + '.' + TABLE_NAME + " (s2_key INT PRIMARY KEY, s2_val INT)");
+    Sql("CREATE TABLE " + Q_SCHEMA_NAME_3 + '.' + TABLE_NAME + " (s3_key INT PRIMARY KEY, s3_val INT)");
+    Sql("CREATE TABLE " + SCHEMA_NAME_4 + '.' + TABLE_NAME + " (s4_key INT PRIMARY KEY, s4_val INT)");
+
+    Sql("INSERT INTO " + SCHEMA_NAME_1 + '.' + TABLE_NAME + " (s1_key, s1_val) VALUES (1, 2)");
+    Sql("INSERT INTO " + SCHEMA_NAME_2 + '.' + TABLE_NAME + " (s2_key, s2_val) VALUES (1, 2)");
+    Sql("INSERT INTO " + Q_SCHEMA_NAME_3 + '.' + TABLE_NAME + " (s3_key, s3_val) VALUES (1, 2)");
+    Sql("INSERT INTO " + SCHEMA_NAME_4 + '.' + TABLE_NAME + " (s4_key, s4_val) VALUES (1, 2)");
+
+    Sql("UPDATE " + SCHEMA_NAME_1 + '.' + TABLE_NAME + " SET s1_val = 5");
+    Sql("UPDATE " + SCHEMA_NAME_2 + '.' + TABLE_NAME + " SET s2_val = 5");
+    Sql("UPDATE " + Q_SCHEMA_NAME_3 + '.' + TABLE_NAME + " SET s3_val = 5");
+    Sql("UPDATE " + SCHEMA_NAME_4 + '.' + TABLE_NAME + " SET s4_val = 5");
+
+    Sql("DELETE FROM " + SCHEMA_NAME_1 + '.' + TABLE_NAME);
+    Sql("DELETE FROM " + SCHEMA_NAME_2 + '.' + TABLE_NAME);
+    Sql("DELETE FROM " + Q_SCHEMA_NAME_3 + '.' + TABLE_NAME);
+    Sql("DELETE FROM " + SCHEMA_NAME_4 + '.' + TABLE_NAME);
+
+    Sql("CREATE INDEX t1_idx_1 ON " + SCHEMA_NAME_1 + '.' + TABLE_NAME + "(s1_val)");
+    Sql("CREATE INDEX t1_idx_1 ON " + SCHEMA_NAME_2 + '.' + TABLE_NAME + "(s2_val)");
+    Sql("CREATE INDEX t1_idx_1 ON " + Q_SCHEMA_NAME_3 + '.' + TABLE_NAME + "(s3_val)");
+    Sql("CREATE INDEX t1_idx_1 ON " + SCHEMA_NAME_4 + '.' + TABLE_NAME + "(s4_val)");
+
+    Sql("SELECT * FROM " + SCHEMA_NAME_1 + '.' + TABLE_NAME);
+    Sql("SELECT * FROM " + SCHEMA_NAME_2 + '.' + TABLE_NAME);
+    Sql("SELECT * FROM " + Q_SCHEMA_NAME_3 + '.' + TABLE_NAME);
+    Sql("SELECT * FROM " + SCHEMA_NAME_4 + '.' + TABLE_NAME);
+
+    Sql("SELECT * FROM " + SCHEMA_NAME_1 + '.' + TABLE_NAME
+      + " JOIN " + SCHEMA_NAME_2 + '.' + TABLE_NAME
+      + " JOIN " + Q_SCHEMA_NAME_3 + '.' + TABLE_NAME
+      + " JOIN " + SCHEMA_NAME_4 + '.' + TABLE_NAME);
+
+    QueryFieldsCursor cursor = Sql("SELECT SCHEMA_NAME, KEY_ALIAS FROM SYS.TABLES ORDER BY SCHEMA_NAME");
+
+    CheckRow<std::string, std::string>(cursor, SCHEMA_NAME_1, "S1_KEY");
+    CheckRow<std::string, std::string>(cursor, SCHEMA_NAME_2, "S2_KEY");
+    CheckRow<std::string, std::string>(cursor, SCHEMA_NAME_4, "S4_KEY");
+    CheckRow<std::string, std::string>(cursor, SCHEMA_NAME_3, "S3_KEY");
+
+    ChechEmptyCursorGetNextThrowsException(cursor);
+
+    Sql("DROP TABLE " + SCHEMA_NAME_1 + '.' + TABLE_NAME);
+    Sql("DROP TABLE " + SCHEMA_NAME_2 + '.' + TABLE_NAME);
+    Sql("DROP TABLE " + Q_SCHEMA_NAME_3 + '.' + TABLE_NAME);
+    Sql("DROP TABLE " + SCHEMA_NAME_4 + '.' + TABLE_NAME);
+}
+
+BOOST_AUTO_TEST_CASE(TestCreateTblsInDiffSchemasForSameCache)
+{
+    std::string testCache = "cache1";
+
+    Sql("CREATE TABLE " + SCHEMA_NAME_1 + '.' + TABLE_NAME +
+        " (s1_key INT PRIMARY KEY, s1_val INT) WITH \"cache_name=" + testCache + '"');
+
+    BOOST_CHECK_THROW(
+        Sql("CREATE TABLE " + SCHEMA_NAME_2 + '.' + TABLE_NAME +
+            " (s1_key INT PRIMARY KEY, s2_val INT) WITH \"cache_name=" + testCache + '"'),
+        IgniteError
+    );
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/modules/platforms/cpp/odbc-test/CMakeLists.txt b/modules/platforms/cpp/odbc-test/CMakeLists.txt
index dd4273a..b3a8af9 100644
--- a/modules/platforms/cpp/odbc-test/CMakeLists.txt
+++ b/modules/platforms/cpp/odbc-test/CMakeLists.txt
@@ -28,7 +28,7 @@ find_package(Boost 1.53 REQUIRED COMPONENTS unit_test_framework chrono thread sy
 find_package(ODBC REQUIRED)
 
 include_directories(SYSTEM ${ODBC_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS} ${JNI_INCLUDE_DIRS})
-include_directories(include ../odbc/include ../network/include)
+include_directories(include ../odbc/include ../network/include ../network/src)
 
 set(SOURCES
         src/teamcity/teamcity_boost.cpp
@@ -68,6 +68,7 @@ set(SOURCES
         src/sql_parsing_test.cpp
         src/streaming_test.cpp
         src/cursor_binding_test.cpp
+        src/sql_schema_test.cpp
         src/test_server.cpp
         ../odbc/src/log.cpp
         ../odbc/src/cursor.cpp
diff --git a/modules/platforms/cpp/odbc-test/config/queries-schema-32.xml b/modules/platforms/cpp/odbc-test/config/queries-schema-32.xml
new file mode 100644
index 0000000..c564301
--- /dev/null
+++ b/modules/platforms/cpp/odbc-test/config/queries-schema-32.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  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.
+-->
+
+<!--
+    Ignite Spring configuration file to startup grid cache.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xsi:schemaLocation="
+        http://www.springframework.org/schema/beans
+        http://www.springframework.org/schema/beans/spring-beans.xsd
+        http://www.springframework.org/schema/util
+        http://www.springframework.org/schema/util/spring-util.xsd">
+    <import resource="queries-schema-default.xml"/>
+
+    <bean parent="grid.cfg">
+        <property name="memoryConfiguration">
+            <bean class="org.apache.ignite.configuration.MemoryConfiguration">
+                <property name="systemCacheInitialSize" value="#{10 * 1024 * 1024}"/>
+                <property name="systemCacheMaxSize" value="#{40 * 1024 * 1024}"/>
+                <property name="defaultMemoryPolicyName" value="dfltPlc"/>
+
+                <property name="memoryPolicies">
+                    <list>
+                        <bean class="org.apache.ignite.configuration.MemoryPolicyConfiguration">
+                            <property name="name" value="dfltPlc"/>
+                            <property name="maxSize" value="#{100 * 1024 * 1024}"/>
+                            <property name="initialSize" value="#{10 * 1024 * 1024}"/>
+                        </bean>
+                    </list>
+                </property>
+            </bean>
+        </property>
+    </bean>
+</beans>
diff --git a/modules/platforms/cpp/odbc-test/config/queries-schema-default.xml b/modules/platforms/cpp/odbc-test/config/queries-schema-default.xml
new file mode 100644
index 0000000..bcdaec5
--- /dev/null
+++ b/modules/platforms/cpp/odbc-test/config/queries-schema-default.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  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.
+-->
+
+<!--
+    Ignite Spring configuration file to startup grid cache.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xsi:schemaLocation="
+        http://www.springframework.org/schema/beans
+        http://www.springframework.org/schema/beans/spring-beans.xsd
+        http://www.springframework.org/schema/util
+        http://www.springframework.org/schema/util/spring-util.xsd">
+    <bean abstract="true" id="grid.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
+        <property name="localHost" value="127.0.0.1"/>
+        <property name="connectorConfiguration"><null/></property>
+
+        <property name="clientConnectorConfiguration">
+            <bean class="org.apache.ignite.configuration.ClientConnectorConfiguration">
+                <property name="host" value="127.0.0.1"/>
+                <property name="port" value="11110"/>
+                <property name="portRange" value="10"/>
+            </bean>
+        </property>
+
+        <property name="sqlSchemas">
+            <list>
+                <value>SCHEMA_1</value>
+                <value>SCHEMA_2</value>
+                <value>"ScHeMa3"</value>
+                <value>SCHEMA_4</value>
+            </list>
+        </property>
+
+        <property name="discoverySpi">
+            <bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi">
+                <property name="ipFinder">
+                    <bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder">
+                        <property name="addresses">
+                            <list>
+                                <!-- In distributed environment, replace with actual host IP address. -->
+                                <value>127.0.0.1:47500</value>
+                            </list>
+                        </property>
+                    </bean>
+                </property>
+                <property name="socketTimeout" value="300" />
+            </bean>
+        </property>
+    </bean>
+</beans>
diff --git a/modules/platforms/cpp/odbc-test/config/queries-schema.xml b/modules/platforms/cpp/odbc-test/config/queries-schema.xml
new file mode 100644
index 0000000..1a9513f
--- /dev/null
+++ b/modules/platforms/cpp/odbc-test/config/queries-schema.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  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.
+-->
+
+<!--
+    Ignite Spring configuration file to startup grid cache.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xsi:schemaLocation="
+        http://www.springframework.org/schema/beans
+        http://www.springframework.org/schema/beans/spring-beans.xsd
+        http://www.springframework.org/schema/util
+        http://www.springframework.org/schema/util/spring-util.xsd">
+    <import resource="queries-schema-default.xml"/>
+
+    <bean parent="grid.cfg"/>
+</beans>
diff --git a/modules/platforms/cpp/odbc-test/src/sql_schema_test.cpp b/modules/platforms/cpp/odbc-test/src/sql_schema_test.cpp
new file mode 100644
index 0000000..adf7e2c
--- /dev/null
+++ b/modules/platforms/cpp/odbc-test/src/sql_schema_test.cpp
@@ -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.
+ */
+
+#include <stdint.h>
+
+#include <iterator>
+
+#include <boost/test/unit_test.hpp>
+
+#include <ignite/common/utils.h>
+
+#include "ignite/cache/cache.h"
+#include "ignite/cache/query/query_cursor.h"
+#include "ignite/cache/query/query_sql_fields.h"
+#include "ignite/ignite.h"
+#include "ignite/ignition.h"
+
+#include "odbc_test_suite.h"
+#include "test_utils.h"
+#include "ignite/odbc/odbc_error.h"
+
+using namespace boost::unit_test;
+
+using namespace ignite;
+using namespace ignite::cache;
+using namespace ignite::cache::query;
+
+using namespace ignite_test;
+
+/**
+ * Ensure that cursor is empy fails.
+ *
+ * @param stmt Statement.
+ */
+void ChechEmptyCursorGetNextThrowsException(SQLHSTMT stmt)
+{
+    SQLRETURN ret = SQLFetch(stmt);
+
+    BOOST_REQUIRE_EQUAL(ret, SQL_NO_DATA);
+}
+
+/**
+ * Check single row through iteration.
+ *
+ * @param stmt Statement.
+ * @param c1 First column.
+ */
+void CheckSingleLongRow1(SQLHSTMT stmt, const int64_t c1)
+{
+    int64_t val1 = 0;
+
+    SQLRETURN ret = SQLFetch(stmt);
+    if (!SQL_SUCCEEDED(ret))
+        BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+    ret = SQLGetData(stmt, 1, SQL_C_SBIGINT, &val1, 0, 0);
+    if (!SQL_SUCCEEDED(ret))
+        BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+    BOOST_REQUIRE_EQUAL(val1, c1);
+
+    ChechEmptyCursorGetNextThrowsException(stmt);
+}
+
+/**
+ * Check row through iteration.
+ *
+ * @param stmt Statement.
+ * @param c1 First column.
+ * @param c2 Second column.
+ */
+void CheckStringRow2(SQLHSTMT stmt, const std::string& c1, const std::string& c2)
+{
+    char val1[1024] = { 0 };
+    char val2[1024] = { 0 };
+    SQLLEN val1Len = 0;
+    SQLLEN val2Len = 0;
+
+    SQLRETURN ret = SQLFetch(stmt);
+    if (!SQL_SUCCEEDED(ret))
+        BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+    ret = SQLGetData(stmt, 1, SQL_C_CHAR, val1, sizeof(val1), &val1Len);
+    if (!SQL_SUCCEEDED(ret))
+        BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+    ret = SQLGetData(stmt, 2, SQL_C_CHAR, val2, sizeof(val2), &val2Len);
+    if (!SQL_SUCCEEDED(ret))
+        BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+    BOOST_REQUIRE_EQUAL(std::string(val1, static_cast<size_t>(val1Len)), c1);
+    BOOST_REQUIRE_EQUAL(std::string(val2, static_cast<size_t>(val2Len)), c2);
+}
+
+/**
+ * Check single row through iteration.
+ *
+ * @param stmt Statement.
+ * @param c1 First column.
+ * @param c2 Second column.
+ */
+void CheckSingleIntRow2(SQLHSTMT stmt, int32_t c1, int32_t c2)
+{
+    int32_t val1 = 0;
+    int32_t val2 = 0;
+
+    SQLRETURN ret = SQLFetch(stmt);
+    if (!SQL_SUCCEEDED(ret))
+        BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+    ret = SQLGetData(stmt, 1, SQL_C_SLONG, &val1, 0, 0);
+    if (!SQL_SUCCEEDED(ret))
+        BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+    ret = SQLGetData(stmt, 2, SQL_C_SLONG, &val2, 0, 0);
+    if (!SQL_SUCCEEDED(ret))
+        BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+    BOOST_REQUIRE_EQUAL(val1, c1);
+    BOOST_REQUIRE_EQUAL(val2, c2);
+
+    ChechEmptyCursorGetNextThrowsException(stmt);
+}
+
+static const std::string TABLE_NAME = "T1";
+static const std::string SCHEMA_NAME_1 = "SCHEMA_1";
+static const std::string SCHEMA_NAME_2 = "SCHEMA_2";
+static const std::string SCHEMA_NAME_3 = "ScHeMa3";
+static const std::string Q_SCHEMA_NAME_3 = '"' + SCHEMA_NAME_3 + '"';
+static const std::string SCHEMA_NAME_4 = "SCHEMA_4";
+
+/**
+ * Test setup fixture.
+ */
+struct SchemaTestSuiteFixture : odbc::OdbcTestSuite
+{
+    /**
+     * Constructor.
+     */
+    SchemaTestSuiteFixture() :
+        grid(StartNode("queries-schema.xml", "Node1"))
+    {
+        Connect("DRIVER={Apache Ignite};address=127.0.0.1:11110;schema=PUBLIC");
+    }
+
+    /**
+     * Destructor.
+     */
+    ~SchemaTestSuiteFixture()
+    {
+        Ignition::StopAll(true);
+    }
+
+    /** Perform SQL in cluster. */
+    void Sql(const std::string& sql)
+    {
+        SQLFreeStmt(stmt, SQL_CLOSE);
+
+        SQLRETURN ret = ExecQuery(sql);
+
+        ODBC_THROW_ON_ERROR(ret, SQL_HANDLE_STMT, stmt);
+    }
+
+    std::string TableName(bool withSchema)
+    {
+        return withSchema ? "PUBLIC." + TABLE_NAME : TABLE_NAME;
+    }
+
+    template<typename Predicate>
+    void ExecuteStatementsAndVerify(Predicate& pred)
+    {
+        Sql("CREATE TABLE " + TableName(pred()) + " (id INT PRIMARY KEY, val INT)");
+
+        Sql("CREATE INDEX t1_idx_1 ON " + TableName(pred()) + "(val)");
+
+        Sql("INSERT INTO " + TableName(pred()) + " (id, val) VALUES(1, 2)");
+
+        Sql("SELECT * FROM " + TableName(pred()));
+        CheckSingleIntRow2(stmt, 1, 2);
+
+        Sql("UPDATE " + TableName(pred()) + " SET val = 5");
+        Sql("SELECT * FROM " + TableName(pred()));
+        CheckSingleIntRow2(stmt, 1, 5);
+
+        Sql("DELETE FROM " + TableName(pred()) + " WHERE id = 1");
+        Sql("SELECT COUNT(*) FROM " + TableName(pred()));
+        CheckSingleLongRow1(stmt, 0);
+
+        Sql("SELECT COUNT(*) FROM SYS.TABLES WHERE schema_name = 'PUBLIC' "
+            "AND table_name = \'" + TABLE_NAME + "\'");
+        CheckSingleLongRow1(stmt, 1);
+
+        Sql("DROP TABLE " + TableName(pred()));
+    }
+
+    /** Node started during the test. */
+    Ignite grid;
+};
+
+BOOST_FIXTURE_TEST_SUITE(SchemaTestSuite, SchemaTestSuiteFixture)
+
+bool TruePred()
+{
+    return true;
+}
+
+BOOST_AUTO_TEST_CASE(TestBasicOpsExplicitPublicSchema)
+{
+    ExecuteStatementsAndVerify(TruePred);
+}
+
+bool FalsePred()
+{
+    return false;
+}
+
+BOOST_AUTO_TEST_CASE(TestBasicOpsImplicitPublicSchema)
+{
+    ExecuteStatementsAndVerify(FalsePred);
+}
+
+struct MixedPred
+{
+    int i;
+
+    MixedPred() : i(0)
+    {
+        // No-op.
+    }
+
+    bool operator()()
+    {
+        return (++i & 1) == 0;
+    }
+};
+
+BOOST_AUTO_TEST_CASE(TestBasicOpsMixedPublicSchema)
+{
+    MixedPred pred;
+
+    ExecuteStatementsAndVerify(pred);
+}
+
+BOOST_AUTO_TEST_CASE(TestCreateDropNonExistingSchema)
+{
+    BOOST_CHECK_THROW(
+        Sql("CREATE TABLE UNKNOWN_SCHEMA." + TABLE_NAME + "(id INT PRIMARY KEY, val INT)"),
+        OdbcClientError
+    );
+
+    BOOST_CHECK_THROW(
+        Sql("DROP TABLE UNKNOWN_SCHEMA." + TABLE_NAME),
+        OdbcClientError
+    );
+}
+
+BOOST_AUTO_TEST_CASE(TestBasicOpsDiffSchemas)
+{
+    Sql("CREATE TABLE " + SCHEMA_NAME_1 + '.' + TABLE_NAME + " (s1_key INT PRIMARY KEY, s1_val INT)");
+    Sql("CREATE TABLE " + SCHEMA_NAME_2 + '.' + TABLE_NAME + " (s2_key INT PRIMARY KEY, s2_val INT)");
+    Sql("CREATE TABLE " + Q_SCHEMA_NAME_3 + '.' + TABLE_NAME + " (s3_key INT PRIMARY KEY, s3_val INT)");
+    Sql("CREATE TABLE " + SCHEMA_NAME_4 + '.' + TABLE_NAME + " (s4_key INT PRIMARY KEY, s4_val INT)");
+
+    Sql("INSERT INTO " + SCHEMA_NAME_1 + '.' + TABLE_NAME + " (s1_key, s1_val) VALUES (1, 2)");
+    Sql("INSERT INTO " + SCHEMA_NAME_2 + '.' + TABLE_NAME + " (s2_key, s2_val) VALUES (1, 2)");
+    Sql("INSERT INTO " + Q_SCHEMA_NAME_3 + '.' + TABLE_NAME + " (s3_key, s3_val) VALUES (1, 2)");
+    Sql("INSERT INTO " + SCHEMA_NAME_4 + '.' + TABLE_NAME + " (s4_key, s4_val) VALUES (1, 2)");
+
+    Sql("UPDATE " + SCHEMA_NAME_1 + '.' + TABLE_NAME + " SET s1_val = 5");
+    Sql("UPDATE " + SCHEMA_NAME_2 + '.' + TABLE_NAME + " SET s2_val = 5");
+    Sql("UPDATE " + Q_SCHEMA_NAME_3 + '.' + TABLE_NAME + " SET s3_val = 5");
+    Sql("UPDATE " + SCHEMA_NAME_4 + '.' + TABLE_NAME + " SET s4_val = 5");
+
+    Sql("DELETE FROM " + SCHEMA_NAME_1 + '.' + TABLE_NAME);
+    Sql("DELETE FROM " + SCHEMA_NAME_2 + '.' + TABLE_NAME);
+    Sql("DELETE FROM " + Q_SCHEMA_NAME_3 + '.' + TABLE_NAME);
+    Sql("DELETE FROM " + SCHEMA_NAME_4 + '.' + TABLE_NAME);
+
+    Sql("CREATE INDEX t1_idx_1 ON " + SCHEMA_NAME_1 + '.' + TABLE_NAME + "(s1_val)");
+    Sql("CREATE INDEX t1_idx_1 ON " + SCHEMA_NAME_2 + '.' + TABLE_NAME + "(s2_val)");
+    Sql("CREATE INDEX t1_idx_1 ON " + Q_SCHEMA_NAME_3 + '.' + TABLE_NAME + "(s3_val)");
+    Sql("CREATE INDEX t1_idx_1 ON " + SCHEMA_NAME_4 + '.' + TABLE_NAME + "(s4_val)");
+
+    Sql("SELECT * FROM " + SCHEMA_NAME_1 + '.' + TABLE_NAME);
+    Sql("SELECT * FROM " + SCHEMA_NAME_2 + '.' + TABLE_NAME);
+    Sql("SELECT * FROM " + Q_SCHEMA_NAME_3 + '.' + TABLE_NAME);
+    Sql("SELECT * FROM " + SCHEMA_NAME_4 + '.' + TABLE_NAME);
+
+    Sql("SELECT * FROM " + SCHEMA_NAME_1 + '.' + TABLE_NAME
+      + " JOIN " + SCHEMA_NAME_2 + '.' + TABLE_NAME
+      + " JOIN " + Q_SCHEMA_NAME_3 + '.' + TABLE_NAME
+      + " JOIN " + SCHEMA_NAME_4 + '.' + TABLE_NAME);
+
+    Sql("SELECT SCHEMA_NAME, KEY_ALIAS FROM SYS.TABLES ORDER BY SCHEMA_NAME");
+
+    CheckStringRow2(stmt, SCHEMA_NAME_1, "S1_KEY");
+    CheckStringRow2(stmt, SCHEMA_NAME_2, "S2_KEY");
+    CheckStringRow2(stmt, SCHEMA_NAME_4, "S4_KEY");
+    CheckStringRow2(stmt, SCHEMA_NAME_3, "S3_KEY");
+
+    ChechEmptyCursorGetNextThrowsException(stmt);
+
+    Sql("DROP TABLE " + SCHEMA_NAME_1 + '.' + TABLE_NAME);
+    Sql("DROP TABLE " + SCHEMA_NAME_2 + '.' + TABLE_NAME);
+    Sql("DROP TABLE " + Q_SCHEMA_NAME_3 + '.' + TABLE_NAME);
+    Sql("DROP TABLE " + SCHEMA_NAME_4 + '.' + TABLE_NAME);
+}
+
+BOOST_AUTO_TEST_CASE(TestCreateTblsInDiffSchemasForSameCache)
+{
+    std::string testCache = "cache1";
+
+    Sql("CREATE TABLE " + SCHEMA_NAME_1 + '.' + TABLE_NAME +
+        " (s1_key INT PRIMARY KEY, s1_val INT) WITH \"cache_name=" + testCache + '"');
+
+    BOOST_CHECK_THROW(
+        Sql("CREATE TABLE " + SCHEMA_NAME_2 + '.' + TABLE_NAME +
+            " (s1_key INT PRIMARY KEY, s2_val INT) WITH \"cache_name=" + testCache + '"'),
+        OdbcClientError
+    );
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheDmlQueriesTestSchema.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheDmlQueriesTestSchema.cs
new file mode 100644
index 0000000..f0f5611
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheDmlQueriesTestSchema.cs
@@ -0,0 +1,255 @@
+/*
+ * 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.
+ */
+
+namespace Apache.Ignite.Core.Tests.Cache.Query
+{
+    using System;
+    using System.Collections.Generic;
+    using System.Linq;
+    using Apache.Ignite.Core.Cache.Query;
+    using Apache.Ignite.Core.Common;
+    using NUnit.Framework;
+
+    /// <summary>
+    /// Tests Data Manipulation Language queries related to schema.
+    /// </summary>
+    public class CacheDmlQueriesTestSchema
+    {
+        /// Table name
+        private const string TableName = "T1";
+
+        private const string SchemaName1 = "SCHEMA_1";
+        private const string SchemaName2 = "SCHEMA_2";
+        private const string SchemaName3 = "ScHeMa3";
+        private const string SchemaName4 = "SCHEMA_4";
+
+        private const string QSchemaName4 = "\"" + SchemaName3 + "\"";
+
+        /// <summary>
+        /// Sets up test fixture.
+        /// </summary>
+        [TestFixtureSetUp]
+        public void FixtureSetUp()
+        {
+            var cfg = new IgniteConfiguration(TestUtils.GetTestConfiguration())
+            {
+                SqlSchemas = new List<string>
+                {
+                    SchemaName1,
+                    SchemaName2,
+                    QSchemaName4,
+                    SchemaName4
+                }
+            };
+
+            Ignition.Start(cfg);
+        }
+
+        /// <summary>
+        /// Tears down test fixture.
+        /// </summary>
+        [TestFixtureTearDown]
+        public void FixtureTearDown()
+        {
+            Ignition.StopAll(true);
+        }
+
+        /// <summary>
+        /// Schema explicitly defined.
+        /// </summary>
+        [Test]
+        public void TestBasicOpsExplicitPublicSchema()
+        {
+            ExecuteStmtsAndVerify(() => true);
+        }
+
+        /// <summary>
+        /// Schema is imlicit.
+        /// </summary>
+        [Test]
+        public void TestBasicOpsImplicitPublicSchema()
+        {
+            ExecuteStmtsAndVerify(() => false);
+        }
+
+        /// <summary>
+        /// Schema is mixed.
+        /// </summary>
+        [Test]
+        public void TestBasicOpsMixedPublicSchema()
+        {
+            int i = 0;
+
+            ExecuteStmtsAndVerify(() => ((++i & 1) == 0));
+        }
+
+        /// <summary>
+        /// Create and drop non-existing schema.
+        /// </summary>
+        [Test]
+        public void TestCreateDropNonExistingSchema()
+        {
+            Assert.Throws<IgniteException>(() =>
+                Sql("CREATE TABLE UNKNOWN_SCHEMA." + TableName + "(id INT PRIMARY KEY, val INT)"));
+
+            Assert.Throws<IgniteException>(() => Sql("DROP TABLE UNKNOWN_SCHEMA." + TableName));
+        }
+
+        /// <summary>
+        /// Create tables in different schemas for same cache.
+        /// </summary>
+        [Test]
+        public void TestCreateTblsInDiffSchemasForSameCache()
+        {
+            const string testCache = "cache1";
+
+            Sql("CREATE TABLE " + SchemaName1 + '.' + TableName
+                + " (s1_key INT PRIMARY KEY, s1_val INT) WITH \"cache_name=" + testCache + "\"");
+
+            Assert.Throws<IgniteException>(
+                () => Sql("CREATE TABLE " + SchemaName2 + '.' + TableName
+                          + " (s1_key INT PRIMARY KEY, s2_val INT) WITH \"cache_name=" + testCache + "\"")
+            );
+
+            Sql("DROP TABLE " + SchemaName1 + '.' + TableName);
+        }
+
+        /// <summary>
+        /// Basic test with different schemas.
+        /// </summary>
+        [Test]
+        public void TestBasicOpsDiffSchemas()
+        {
+            Sql("CREATE TABLE " + SchemaName1 + '.' + TableName + " (s1_key INT PRIMARY KEY, s1_val INT)");
+            Sql("CREATE TABLE " + SchemaName2 + '.' + TableName + " (s2_key INT PRIMARY KEY, s2_val INT)");
+            Sql("CREATE TABLE " + QSchemaName4 + '.' + TableName + " (s3_key INT PRIMARY KEY, s3_val INT)");
+            Sql("CREATE TABLE " + SchemaName4 + '.' + TableName + " (s4_key INT PRIMARY KEY, s4_val INT)");
+
+            Sql("INSERT INTO " + SchemaName1 + '.' + TableName + " (s1_key, s1_val) VALUES (1, 2)");
+            Sql("INSERT INTO " + SchemaName2 + '.' + TableName + " (s2_key, s2_val) VALUES (1, 2)");
+            Sql("INSERT INTO " + QSchemaName4 + '.' + TableName + " (s3_key, s3_val) VALUES (1, 2)");
+            Sql("INSERT INTO " + SchemaName4 + '.' + TableName + " (s4_key, s4_val) VALUES (1, 2)");
+
+            Sql("UPDATE " + SchemaName1 + '.' + TableName + " SET s1_val = 5");
+            Sql("UPDATE " + SchemaName2 + '.' + TableName + " SET s2_val = 5");
+            Sql("UPDATE " + QSchemaName4 + '.' + TableName + " SET s3_val = 5");
+            Sql("UPDATE " + SchemaName4 + '.' + TableName + " SET s4_val = 5");
+
+            Sql("DELETE FROM " + SchemaName1 + '.' + TableName);
+            Sql("DELETE FROM " + SchemaName2 + '.' + TableName);
+            Sql("DELETE FROM " + QSchemaName4 + '.' + TableName);
+            Sql("DELETE FROM " + SchemaName4 + '.' + TableName);
+
+            Sql("CREATE INDEX t1_idx_1 ON " + SchemaName1 + '.' + TableName + "(s1_val)");
+            Sql("CREATE INDEX t1_idx_1 ON " + SchemaName2 + '.' + TableName + "(s2_val)");
+            Sql("CREATE INDEX t1_idx_1 ON " + QSchemaName4 + '.' + TableName + "(s3_val)");
+            Sql("CREATE INDEX t1_idx_1 ON " + SchemaName4 + '.' + TableName + "(s4_val)");
+
+            Sql("SELECT * FROM " + SchemaName1 + '.' + TableName);
+            Sql("SELECT * FROM " + SchemaName2 + '.' + TableName);
+            Sql("SELECT * FROM " + QSchemaName4 + '.' + TableName);
+            Sql("SELECT * FROM " + SchemaName4 + '.' + TableName);
+
+            Sql("SELECT * FROM " + SchemaName1 + '.' + TableName
+                + " JOIN " + SchemaName2 + '.' + TableName
+                + " JOIN " + QSchemaName4 + '.' + TableName
+                + " JOIN " + SchemaName4 + '.' + TableName);
+
+            VerifyTables();
+
+            Sql("DROP TABLE " + SchemaName1 + '.' + TableName);
+            Sql("DROP TABLE " + SchemaName2 + '.' + TableName);
+            Sql("DROP TABLE " + QSchemaName4 + '.' + TableName);
+            Sql("DROP TABLE " + SchemaName4 + '.' + TableName);
+        }
+
+        /// <summary>
+        /// Verify tables.
+        /// </summary>
+        private static void VerifyTables()
+        {
+            Sql("SELECT SCHEMA_NAME, KEY_ALIAS FROM SYS.TABLES ORDER BY SCHEMA_NAME", res =>
+            {
+                Assert.AreEqual(new List<List<object>>
+                {
+                    new List<object> {SchemaName1, "S1_KEY"},
+                    new List<object> {SchemaName2, "S2_KEY"},
+                    new List<object> {SchemaName4, "S4_KEY"},
+                    new List<object> {SchemaName3, "S3_KEY"}
+                }, res);
+            });
+        }
+
+        /// <summary>
+        /// Get test table name.
+        /// </summary>
+        private static string GetTableName(bool withSchema)
+        {
+            return withSchema ? "PUBLIC." + TableName : TableName;
+        }
+
+        /// <summary>
+        /// Perform SQL query.
+        /// </summary>
+        private static void Sql(string qry, Action<IList<IList<object>>> validator = null)
+        {
+            var res = Ignition
+                .GetIgnite()
+                .GetOrCreateCache<int, int>("TestCache")
+                .Query(new SqlFieldsQuery(qry))
+                .GetAll();
+
+            if (validator != null)
+                validator.Invoke(res);
+        }
+
+        /// <summary>
+        /// Generate one-row result set.
+        /// </summary>
+        private static List<List<object>> OneRowList(params object[] vals)
+        {
+            return new List<List<object>> {vals.ToList()};
+        }
+
+        /// <summary>
+        /// Create/insert/update/delete/drop table in PUBLIC schema.
+        /// </summary>
+        private static void ExecuteStmtsAndVerify(Func<bool> withSchemaDecisionSup)
+        {
+            Sql("CREATE TABLE " + GetTableName(withSchemaDecisionSup()) + " (id INT PRIMARY KEY, val INT)");
+
+            Sql("CREATE INDEX t1_idx_1 ON " + GetTableName(withSchemaDecisionSup()) + "(val)");
+
+            Sql("INSERT INTO " + GetTableName(withSchemaDecisionSup()) + " (id, val) VALUES(1, 2)");
+            Sql("SELECT * FROM " + GetTableName(withSchemaDecisionSup()),
+                res => Assert.AreEqual(OneRowList(1, 2), res));
+
+            Sql("UPDATE " + GetTableName(withSchemaDecisionSup()) + " SET val = 5");
+            Sql("SELECT * FROM " + GetTableName(withSchemaDecisionSup()),
+                res => Assert.AreEqual(OneRowList(1, 5), res));
+
+            Sql("DELETE FROM " + GetTableName(withSchemaDecisionSup()) + " WHERE id = 1");
+            Sql("SELECT COUNT(*) FROM " + GetTableName(withSchemaDecisionSup()),
+                res => Assert.AreEqual(OneRowList(0), res));
+
+            Sql("SELECT COUNT(*) FROM SYS.TABLES WHERE schema_name = 'PUBLIC' " +
+                "AND table_name = \'" + TableName + "\'", res => Assert.AreEqual(OneRowList(1), res));
+
+            Sql("DROP TABLE " + GetTableName(withSchemaDecisionSup()));
+        }
+    }
+}
\ No newline at end of file