ignite-1979 Support case insensitive nonquoted cache names in SQL - Fixes #324.

Signed-off-by: S.Vladykin <svlady...@gridgain.com>


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

Branch: refs/heads/ignite-1.5.1-2
Commit: da24df99525b796a79fdb55997efe1c9bd515a5d
Parents: a0cdb59
Author: vershov <vers...@gridgain.com>
Authored: Tue Dec 22 00:08:37 2015 +0300
Committer: S.Vladykin <svlady...@gridgain.com>
Committed: Tue Dec 22 00:08:37 2015 +0300

----------------------------------------------------------------------
 .../configuration/CacheConfiguration.java       |  44 +++-
 .../ignite/internal/util/IgniteUtils.java       |   6 +-
 .../cache/VisorCacheQueryConfiguration.java     |  11 +
 .../processors/query/h2/IgniteH2Indexing.java   | 118 ++++++---
 .../query/h2/sql/GridSqlQuerySplitter.java      |  48 ++--
 .../query/IgniteSqlSchemaIndexingTest.java      | 240 +++++++++++++++++++
 .../IgniteCacheQuerySelfTestSuite.java          |   2 +
 .../commands/cache/VisorCacheCommand.scala      |  13 +-
 8 files changed, 416 insertions(+), 66 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/da24df99/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
 
b/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
index be1240c..d52662e 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
@@ -365,15 +365,18 @@ public class CacheConfiguration<K, V> extends 
MutableConfiguration<K, V> {
     private IgnitePredicate<ClusterNode> nodeFilter;
 
     /** */
-    private boolean sqlEscapeAll;
+    private String sqlSchema;
 
     /** */
-    private transient Class<?>[] indexedTypes;
+    private boolean sqlEscapeAll;
 
     /** */
     private int sqlOnheapRowCacheSize = DFLT_SQL_ONHEAP_ROW_CACHE_SIZE;
 
     /** */
+    private transient Class<?>[] indexedTypes;
+
+    /** */
     private boolean snapshotableIdx;
 
     /** Copy on read flag. */
@@ -466,6 +469,7 @@ public class CacheConfiguration<K, V> extends 
MutableConfiguration<K, V> {
         rebalanceTimeout = cc.getRebalanceTimeout();
         rebalanceThrottle = cc.getRebalanceThrottle();
         snapshotableIdx = cc.isSnapshotableIndex();
+        sqlSchema = cc.getSqlSchema();
         sqlEscapeAll = cc.isSqlEscapeAll();
         sqlFuncCls = cc.getSqlFunctionClasses();
         sqlOnheapRowCacheSize = cc.getSqlOnheapRowCacheSize();
@@ -1770,7 +1774,7 @@ public class CacheConfiguration<K, V> extends 
MutableConfiguration<K, V> {
     }
 
     /**
-     * Gets timeout in milliseconds after which long query warning will be 
printed.
+     * Sets timeout in milliseconds after which long query warning will be 
printed.
      *
      * @param longQryWarnTimeout Timeout in milliseconds.
      * @return {@code this} for chaining.
@@ -1782,6 +1786,40 @@ public class CacheConfiguration<K, V> extends 
MutableConfiguration<K, V> {
     }
 
     /**
+     * Gets custom name of the sql schema. If custom sql schema is not set 
then {@code null} will be returned and
+     * quoted case sensitive name will be used as sql schema.
+     *
+     * @return Schema name for current cache according to SQL ANSI-99. Could 
be {@code null}.
+     */
+    @Nullable public String getSqlSchema() {
+        return sqlSchema;
+    }
+
+    /**
+     * Sets sql schema to be used for current cache. This name will correspond 
to SQL ANSI-99 standard.
+     * Nonquoted identifiers are not case sensitive. Quoted identifiers are 
case sensitive.
+     * <p/>
+     * Be aware of using the same string in case sensitive and case 
insensitive manner simultaneously, since
+     * behaviour for such case is not specified.
+     * <p/>
+     * When sqlSchema is not specified, quoted {@code cacheName} is used 
instead.
+     * <p/>
+     * {@code sqlSchema} could not be an empty string. Has to be {@code 
"\"\""} instead.
+     *
+     * @param sqlSchema Schema name for current cache according to SQL 
ANSI-99. Should not be {@code null}.
+     *
+     * @return {@code this} for chaining.
+     */
+    public CacheConfiguration<K, V> setSqlSchema(String sqlSchema) {
+        A.ensure((sqlSchema != null), "Schema could not be null.");
+        A.ensure(!sqlSchema.isEmpty(), "Schema could not be empty.");
+
+        this.sqlSchema = sqlSchema;
+
+        return this;
+    }
+
+    /**
      * If {@code true} all the SQL table and field names will be escaped with 
double quotes like
      * ({@code "tableName"."fieldsName"}). This enforces case sensitivity for 
field names and
      * also allows having special characters in table and field names.

http://git-wip-us.apache.org/repos/asf/ignite/blob/da24df99/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java 
b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
index e74b3f0..be4851d 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
@@ -4243,11 +4243,11 @@ public abstract class IgniteUtils {
     /**
      * Mask component name to make sure that it is not {@code null}.
      *
-     * @param cacheName Component name to mask, possibly {@code null}.
+     * @param name Component name to mask, possibly {@code null}.
      * @return Component name.
      */
-    public static String maskName(@Nullable String cacheName) {
-        return cacheName == null ? "default" : cacheName;
+    public static String maskName(@Nullable String name) {
+        return name == null ? "default" : name;
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/da24df99/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCacheQueryConfiguration.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCacheQueryConfiguration.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCacheQueryConfiguration.java
index cbb97a5..a779db4 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCacheQueryConfiguration.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCacheQueryConfiguration.java
@@ -35,6 +35,9 @@ public class VisorCacheQueryConfiguration implements 
Serializable {
     private long longQryWarnTimeout;
 
     /** */
+    private String sqlSchema;
+
+    /** */
     private boolean sqlEscapeAll;
 
     /** */
@@ -69,6 +72,7 @@ public class VisorCacheQueryConfiguration implements 
Serializable {
 
         cfg.sqlFuncClss = compactClasses(ccfg.getSqlFunctionClasses());
         cfg.longQryWarnTimeout = ccfg.getLongQueryWarningTimeout();
+        cfg.sqlSchema = ccfg.getSqlSchema();
         cfg.sqlEscapeAll = ccfg.isSqlEscapeAll();
         cfg.indexedTypes = compactClasses(ccfg.getIndexedTypes());
         cfg.sqlOnheapRowCacheSize = ccfg.getSqlOnheapRowCacheSize();
@@ -91,6 +95,13 @@ public class VisorCacheQueryConfiguration implements 
Serializable {
     }
 
     /**
+     * @return Schema name, which is used by SQL engine for SQL statements 
generation.
+     */
+    public String sqlSchema() {
+        return sqlSchema;
+    }
+
+    /**
      * @return {@code true} if SQL engine generate SQL statements with escaped 
names.
      */
     public boolean sqlEscapeAll() {

http://git-wip-us.apache.org/repos/asf/ignite/blob/da24df99/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 8625be9..dead526 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
@@ -195,6 +195,12 @@ public class IgniteH2Indexing implements GridQueryIndexing 
{
     /** */
     private static final Field COMMAND_FIELD;
 
+    /** */
+    private static final char ESC_CH = '\"';
+
+    /** */
+    private static final String ESC_STR = ESC_CH + "" + ESC_CH;
+
     /**
      * Command in H2 prepared statement.
      */
@@ -236,6 +242,9 @@ public class IgniteH2Indexing implements GridQueryIndexing {
     /** */
     private GridReduceQueryExecutor rdcQryExec;
 
+    /** space name -> schema name */
+    private final Map<String, String> space2schema = new 
ConcurrentHashMap8<>();
+
     /** */
     private final ThreadLocal<ConnectionWrapper> connCache = new 
ThreadLocal<ConnectionWrapper>() {
         @Nullable @Override public ConnectionWrapper get() {
@@ -360,7 +369,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
             try {
                 stmt = c.connection().createStatement();
 
-                stmt.executeUpdate("SET SCHEMA \"" + schema + '"');
+                stmt.executeUpdate("SET SCHEMA " + schema);
 
                 if (log.isDebugEnabled())
                     log.debug("Set schema: " + schema);
@@ -386,7 +395,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
      * @throws IgniteCheckedException If failed to create db schema.
      */
     private void createSchema(String schema) throws IgniteCheckedException {
-        executeStatement("INFORMATION_SCHEMA", "CREATE SCHEMA IF NOT EXISTS 
\"" + schema + '"');
+        executeStatement("INFORMATION_SCHEMA", "CREATE SCHEMA IF NOT EXISTS " 
+ schema);
 
         if (log.isDebugEnabled())
             log.debug("Created H2 schema for index database: " + schema);
@@ -399,7 +408,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
      * @throws IgniteCheckedException If failed to create db schema.
      */
     private void dropSchema(String schema) throws IgniteCheckedException {
-        executeStatement("INFORMATION_SCHEMA", "DROP SCHEMA IF EXISTS \"" + 
schema + '"');
+        executeStatement("INFORMATION_SCHEMA", "DROP SCHEMA IF EXISTS " + 
schema);
 
         if (log.isDebugEnabled())
             log.debug("Dropped H2 schema for index database: " + schema);
@@ -642,7 +651,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
         if (log.isDebugEnabled())
             log.debug("Removing query index table: " + tbl.fullTableName());
 
-        Connection c = connectionForThread(tbl.schema());
+        Connection c = connectionForThread(tbl.schemaName());
 
         Statement stmt = null;
 
@@ -767,6 +776,22 @@ public class IgniteH2Indexing implements GridQueryIndexing 
{
     }
 
     /**
+     * Stores rule for constructing schemaName according to cache 
configuration.
+     *
+     * @param ccfg Cache configuration.
+     * @return Proper schema name according to ANSI-99 standard.
+     */
+    private static String schemaNameFromCacheConf(CacheConfiguration<?,?> 
ccfg) {
+        if (ccfg.getSqlSchema() == null)
+            return escapeName(ccfg.getName(), true);
+
+        if (ccfg.getSqlSchema().charAt(0) == ESC_CH)
+            return ccfg.getSqlSchema();
+
+        return ccfg.isSqlEscapeAll() ? escapeName(ccfg.getSqlSchema(), true) : 
ccfg.getSqlSchema().toUpperCase();
+    }
+
+    /**
      * Executes sql query.
      *
      * @param conn Connection,.
@@ -865,9 +890,9 @@ public class IgniteH2Indexing implements GridQueryIndexing {
      * @return Result set.
      * @throws IgniteCheckedException If failed.
      */
-    private ResultSet executeQuery(String space, String qry, @Nullable 
Collection<Object> params,
-        TableDescriptor tbl) throws IgniteCheckedException {
-        Connection conn = connectionForThread(tbl.schema());
+    private ResultSet executeQuery(String space, String qry, @Nullable 
Collection<Object> params, TableDescriptor tbl)
+            throws IgniteCheckedException {
+        Connection conn = connectionForThread(tbl.schemaName());
 
         String sql = generateQuery(qry, tbl);
 
@@ -1027,7 +1052,8 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
                 }
 
                 try {
-                    twoStepQry = 
GridSqlQuerySplitter.split((JdbcPreparedStatement)stmt, qry.getArgs(), 
qry.isCollocated());
+                    twoStepQry = GridSqlQuerySplitter.split(
+                        (JdbcPreparedStatement)stmt, qry.getArgs(), 
qry.isCollocated(), this);
 
                     meta = meta(stmt.getMetaData());
                 }
@@ -1174,6 +1200,16 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
     }
 
     /**
+     * Returns empty string, if {@code nullableString} is empty.
+     *
+     * @param nullableString String for convertion. Could be null.
+     * @return Non null string. Could be empty.
+     */
+    private static String emptyIfNull(String nullableString) {
+        return nullableString == null ? "" : nullableString;
+    }
+
+    /**
      * Escapes name to be valid SQL identifier. Currently just replaces '.' 
and '$' sign with '_'.
      *
      * @param name Name.
@@ -1181,8 +1217,11 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
      * @return Escaped name.
      */
     private static String escapeName(String name, boolean escapeAll) {
+        if (name == null) // It is possible only for a cache name.
+            return ESC_STR;
+
         if (escapeAll)
-            return "\"" + name + "\"";
+            return ESC_CH + name + ESC_CH;
 
         SB sb = null;
 
@@ -1304,24 +1343,32 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
     /**
      * Gets database schema from space.
      *
-     * @param space Space name.
-     * @return Schema name.
+     * @param space Space name. {@code null} would be converted to an empty 
string.
+     * @return Schema name. Should not be null since we should not fail for an 
invalid space name.
      */
-    public static String schema(@Nullable String space) {
-        if (space == null)
-            return "";
-
-        return space;
+    private String schema(@Nullable String space) {
+        return emptyIfNull(space2schema.get(emptyIfNull(space)));
     }
 
     /**
-     * @param schema Schema.
-     * @return Space name.
+     * Gets space name from database schema.
+     *
+     * @param schemaName Schema name. Could not be null. Could be empty.
+     * @return Space name. Could be null.
      */
-    public static String space(String schema) {
-        assert schema != null;
+    public String space(String schemaName) {
+        assert schemaName != null;
+
+        Schema schema = schemas.get(schemaName);
+
+        // For the compatibility with conversion from """" to "" inside h2 lib
+        if (schema == null) {
+            assert schemaName.isEmpty() || schemaName.charAt(0) != ESC_CH;
+
+            schema = schemas.get(escapeName(schemaName, true));
+        }
 
-        return "".equals(schema) ? null : schema;
+        return schema.spaceName;
     }
 
     /** {@inheritDoc} */
@@ -1427,7 +1474,7 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
             rdcQryExec.start(ctx, this);
         }
 
-        // TODO https://issues.apache.org/jira/browse/IGNITE-751
+        // TODO https://issues.apache.org/jira/browse/IGNITE-2139
         // registerMBean(gridName, this, GridH2IndexingSpiMBean.class);
     }
 
@@ -1485,7 +1532,7 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
         if (log.isDebugEnabled())
             log.debug("Stopping cache query index...");
 
-//        unregisterMBean(); TODO
+//        unregisterMBean(); TODO 
https://issues.apache.org/jira/browse/IGNITE-2139
 
         for (Schema schema : schemas.values()) {
             for (TableDescriptor desc : schema.tbls.values()) {
@@ -1501,6 +1548,7 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
 
         conns.clear();
         schemas.clear();
+        space2schema.clear();
 
         try (Connection c = DriverManager.getConnection(dbUrl);
              Statement s = c.createStatement()) {
@@ -1516,12 +1564,14 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
 
     /** {@inheritDoc} */
     @Override public void registerCache(CacheConfiguration<?,?> ccfg) throws 
IgniteCheckedException {
-        String schema = schema(ccfg.getName());
+        String schema = schemaNameFromCacheConf(ccfg);
 
-        if (schemas.putIfAbsent(schema, new Schema(ccfg.getName(),
+        if (schemas.putIfAbsent(schema, new Schema(ccfg.getName(), schema,
             ccfg.getOffHeapMaxMemory() >= 0 || ccfg.getMemoryMode() == 
CacheMemoryMode.OFFHEAP_TIERED ?
             new GridUnsafeMemory(0) : null, ccfg)) != null)
-            throw new IgniteCheckedException("Cache already registered: " + 
U.maskName(ccfg.getName()));
+            throw new IgniteCheckedException("Schema for cache already 
registered: " + U.maskName(ccfg.getName()));
+
+        space2schema.put(emptyIfNull(ccfg.getName()), schema);
 
         createSchema(schema);
 
@@ -1531,10 +1581,10 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
     /** {@inheritDoc} */
     @Override public void unregisterCache(CacheConfiguration<?, ?> ccfg) {
         String schema = schema(ccfg.getName());
-
         Schema rmv = schemas.remove(schema);
 
         if (rmv != null) {
+            space2schema.remove(rmv.spaceName);
             mapQryExec.onCacheStop(ccfg.getName());
 
             try {
@@ -1855,15 +1905,14 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
             this.type = type;
             this.schema = schema;
 
-            fullTblName = '\"' + IgniteH2Indexing.schema(schema.spaceName) + 
"\"." +
-                escapeName(type.name(), schema.escapeAll());
+            fullTblName = schema.schemaName + "." + escapeName(type.name(), 
schema.escapeAll());
         }
 
         /**
          * @return Schema name.
          */
-        public String schema() {
-            return IgniteH2Indexing.schema(schema.spaceName);
+        public String schemaName() {
+            return schema.schemaName;
         }
 
         /**
@@ -2129,6 +2178,9 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
         private final String spaceName;
 
         /** */
+        private final String schemaName;
+
+        /** */
         private final GridUnsafeMemory offheap;
 
         /** */
@@ -2142,11 +2194,13 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
 
         /**
          * @param spaceName Space name.
+         * @param schemaName Schema name.
          * @param offheap Offheap memory.
          * @param ccfg Cache configuration.
          */
-        private Schema(@Nullable String spaceName, GridUnsafeMemory offheap, 
CacheConfiguration<?,?> ccfg) {
+        private Schema(@Nullable String spaceName, String schemaName, 
GridUnsafeMemory offheap, CacheConfiguration<?,?> ccfg) {
             this.spaceName = spaceName;
+            this.schemaName = schemaName;
             this.offheap = offheap;
             this.ccfg = ccfg;
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/da24df99/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java
----------------------------------------------------------------------
diff --git 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java
 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java
index 727c2c7..3d9c10a 100644
--- 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java
+++ 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java
@@ -138,18 +138,19 @@ public class GridSqlQuerySplitter {
      * @param stmt Prepared statement.
      * @param params Parameters.
      * @param collocated Collocated query.
+     * @param igniteH2Indexing Indexing implementation.
      * @return Two step query.
      */
-    public static GridCacheTwoStepQuery split(JdbcPreparedStatement stmt, 
Object[] params, boolean collocated) {
+    public static GridCacheTwoStepQuery split(JdbcPreparedStatement stmt, 
Object[] params, boolean collocated, IgniteH2Indexing igniteH2Indexing) {
         if (params == null)
             params = GridCacheSqlQuery.EMPTY_PARAMS;
 
-        Set<String> spaces = new HashSet<>();
+        Set<String> schemas = new HashSet<>();
 
         // Map query will be direct reference to the original query AST.
         // Thus all the modifications will be performed on the original AST, 
so we should be careful when
         // nullifying or updating things, have to make sure that we will not 
need them in the original form later.
-        final GridSqlSelect mapQry = 
wrapUnion(collectAllSpaces(GridSqlQueryParser.parse(stmt), spaces));
+        final GridSqlSelect mapQry = 
wrapUnion(collectAllSpaces(GridSqlQueryParser.parse(stmt), schemas));
 
         final boolean explain = mapQry.explain();
 
@@ -258,6 +259,11 @@ public class GridSqlQuerySplitter {
 
         map.parameterIndexes(toIntArray(paramIdxs));
 
+        Set<String> spaces = new HashSet<>(schemas.size());
+
+        for (String schema : schemas)
+            spaces.add(igniteH2Indexing.space(schema));
+
         // Build resulting two step query.
         GridCacheTwoStepQuery res = new GridCacheTwoStepQuery(spaces, rdc, 
rdcQry.simpleQuery()).addMapQuery(map);
 
@@ -309,25 +315,25 @@ public class GridSqlQuerySplitter {
 
     /**
      * @param qry Query.
-     * @param spaces Space names.
+     * @param schemas Shemas' names.
      * @return Query.
      */
-    private static GridSqlQuery collectAllSpaces(GridSqlQuery qry, Set<String> 
spaces) {
+    private static GridSqlQuery collectAllSpaces(GridSqlQuery qry, Set<String> 
schemas) {
         if (qry instanceof GridSqlUnion) {
             GridSqlUnion union = (GridSqlUnion)qry;
 
-            collectAllSpaces(union.left(), spaces);
-            collectAllSpaces(union.right(), spaces);
+            collectAllSpaces(union.left(), schemas);
+            collectAllSpaces(union.right(), schemas);
         }
         else {
             GridSqlSelect select = (GridSqlSelect)qry;
 
-            collectAllSpacesInFrom(select.from(), spaces);
+            collectAllSpacesInFrom(select.from(), schemas);
 
             for (GridSqlElement el : select.columns(false))
-                collectAllSpacesInSubqueries(el, spaces);
+                collectAllSpacesInSubqueries(el, schemas);
 
-            collectAllSpacesInSubqueries(select.where(), spaces);
+            collectAllSpacesInSubqueries(select.where(), schemas);
         }
 
         return qry;
@@ -335,26 +341,26 @@ public class GridSqlQuerySplitter {
 
     /**
      * @param from From element.
-     * @param spaces Space names.
+     * @param schemas Shemas' names.
      */
-    private static void collectAllSpacesInFrom(GridSqlElement from, 
Set<String> spaces) {
+    private static void collectAllSpacesInFrom(GridSqlElement from, 
Set<String> schemas) {
         assert from != null;
 
         if (from instanceof GridSqlJoin) {
             // Left and right.
-            collectAllSpacesInFrom(from.child(0), spaces);
-            collectAllSpacesInFrom(from.child(1), spaces);
+            collectAllSpacesInFrom(from.child(0), schemas);
+            collectAllSpacesInFrom(from.child(1), schemas);
         }
         else if (from instanceof GridSqlTable) {
             String schema = ((GridSqlTable)from).schema();
 
             if (schema != null)
-                spaces.add(IgniteH2Indexing.space(schema));
+                schemas.add(schema);
         }
         else if (from instanceof GridSqlSubquery)
-            collectAllSpaces(((GridSqlSubquery)from).select(), spaces);
+            collectAllSpaces(((GridSqlSubquery)from).select(), schemas);
         else if (from instanceof GridSqlAlias)
-            collectAllSpacesInFrom(from.child(), spaces);
+            collectAllSpacesInFrom(from.child(), schemas);
         else if (!(from instanceof GridSqlFunction))
             throw new IllegalStateException(from.getClass().getName() + " : " 
+ from.getSQL());
     }
@@ -362,18 +368,18 @@ public class GridSqlQuerySplitter {
     /**
      * Searches spaces in subqueries in SELECT and WHERE clauses.
      * @param el Element.
-     * @param spaces Space names.
+     * @param schemas Schemas' names.
      */
-    private static void collectAllSpacesInSubqueries(GridSqlElement el, 
Set<String> spaces) {
+    private static void collectAllSpacesInSubqueries(GridSqlElement el, 
Set<String> schemas) {
         if (el instanceof GridSqlAlias)
             el = el.child();
 
         if (el instanceof GridSqlOperation || el instanceof GridSqlFunction) {
             for (GridSqlElement child : el)
-                collectAllSpacesInSubqueries(child, spaces);
+                collectAllSpacesInSubqueries(child, schemas);
         }
         else if (el instanceof GridSqlSubquery)
-            collectAllSpaces(((GridSqlSubquery)el).select(), spaces);
+            collectAllSpaces(((GridSqlSubquery)el).select(), schemas);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/da24df99/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteSqlSchemaIndexingTest.java
----------------------------------------------------------------------
diff --git 
a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteSqlSchemaIndexingTest.java
 
b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteSqlSchemaIndexingTest.java
new file mode 100644
index 0000000..5c9acb5
--- /dev/null
+++ 
b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteSqlSchemaIndexingTest.java
@@ -0,0 +1,240 @@
+/*
+ * 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 java.util.List;
+import java.util.concurrent.Callable;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.cache.query.annotations.QuerySqlField;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing;
+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;
+
+/**
+ * Tests {@link IgniteH2Indexing} support {@link 
CacheConfiguration#setSqlSchema(String)} configuration.
+ */
+@SuppressWarnings("unchecked")
+public class IgniteSqlSchemaIndexingTest extends GridCommonAbstractTest {
+    /** */
+    private static final TcpDiscoveryIpFinder ipFinder = new 
TcpDiscoveryVmIpFinder(true);
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration() throws 
Exception {
+        IgniteConfiguration cfg = super.getConfiguration();
+
+        cfg.setPeerClassLoadingEnabled(false);
+
+        TcpDiscoverySpi disco = new TcpDiscoverySpi();
+
+        disco.setIpFinder(ipFinder);
+
+        cfg.setDiscoverySpi(disco);
+
+        return cfg;
+    }
+
+    /**
+     * @param name Cache name.
+     * @param partitioned Partition or replicated cache.
+     * @param idxTypes Indexed types.
+     * @return Cache configuration.
+     */
+    private static CacheConfiguration cacheConfig(String name, boolean 
partitioned, Class<?>... idxTypes) {
+        return new CacheConfiguration()
+            .setName(name)
+            .setCacheMode(partitioned ? CacheMode.PARTITIONED : 
CacheMode.REPLICATED)
+            .setAtomicityMode(CacheAtomicityMode.ATOMIC)
+            .setBackups(1)
+            .setIndexedTypes(Integer.class, Fact.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        stopAllGrids();
+    }
+
+    /**
+     * Tests collision for case insensitive sqlScheme.
+     *
+     * @throws Exception If failed.
+     */
+    public void testCaseSensitive() throws Exception {
+        //TODO rewrite with dynamic cache creation, and GRID start in 
#beforeTest after resolve of
+        //https://issues.apache.org/jira/browse/IGNITE-1094
+
+        GridTestUtils.assertThrows(log, new Callable<Object>() {
+            @Override public Object call() throws Exception {
+                final CacheConfiguration cfg = cacheConfig("InSensitiveCache", 
true, Integer.class, Integer.class)
+                    .setSqlSchema("InsensitiveCache");
+                final CacheConfiguration collisionCfg = 
cacheConfig("InsensitiveCache", true, Integer.class, Integer.class)
+                    .setSqlSchema("Insensitivecache");
+                IgniteConfiguration icfg = new IgniteConfiguration()
+                    .setLocalHost("127.0.0.1")
+                    .setCacheConfiguration(cfg, collisionCfg);
+
+                Ignition.start(icfg);
+
+                return null;
+            }
+        }, IgniteException.class, "Schema for cache already registered");
+    }
+
+    /**
+     * Tests unregistration of previous scheme.
+     *
+     * @throws Exception If failed.
+     */
+    public void testCacheUnregistration() throws Exception {
+        startGridsMultiThreaded(3, true);
+
+        final CacheConfiguration<Integer, Fact> cfg = 
cacheConfig("Insensitive_Cache", true, Integer.class, Fact.class)
+            .setSqlSchema("Insensitive_Cache");
+        final CacheConfiguration<Integer, Fact> collisionCfg = 
cacheConfig("InsensitiveCache", true, Integer.class, Fact.class)
+            .setSqlSchema("Insensitive_Cache");
+
+        IgniteCache<Integer, Fact> cache = ignite(0).createCache(cfg);
+
+        SqlFieldsQuery qry = new SqlFieldsQuery("select f.id, f.name " +
+            "from InSENSitive_Cache.Fact f");
+
+        cache.put(1, new Fact(1, "cacheInsensitive"));
+
+        for (List<?> row : cache.query(qry)) {
+            assertEquals(2, row.size());
+            assertEquals(1, row.get(0));
+            assertEquals("cacheInsensitive", row.get(1));
+        }
+
+        ignite(0).destroyCache(cache.getName());
+
+        cache = ignite(0).createCache(collisionCfg); // Previous collision 
should be removed by now.
+
+        cache.put(1, new Fact(1, "cacheInsensitive"));
+        cache.put(2, new Fact(2, "ThisIsANewCache"));
+        cache.put(3, new Fact(3, "With3RecordsAndAnotherName"));
+
+        assertEquals(3, cache.query(qry).getAll().size());
+
+        ignite(0).destroyCache(cache.getName());
+    }
+
+    /**
+     * Tests escapeAll and sqlSchema apposition.
+     *
+     * @throws Exception If failed.
+     */
+    public void testSchemaEscapeAll() throws Exception {
+        startGridsMultiThreaded(3, true);
+
+        final CacheConfiguration<Integer, Fact> cfg = 
cacheConfig("simpleSchema", true, Integer.class, Fact.class)
+            .setSqlSchema("SchemaName1")
+            .setSqlEscapeAll(true);
+
+        final CacheConfiguration<Integer, Fact> cfgEsc = 
cacheConfig("escapedSchema", true, Integer.class, Fact.class)
+            .setSqlSchema("\"SchemaName2\"")
+            .setSqlEscapeAll(true);
+
+        escapeCheckSchemaName(ignite(0).createCache(cfg), log, 
cfg.getSqlSchema());
+
+        escapeCheckSchemaName(ignite(0).createCache(cfgEsc), log, 
"SchemaName2");
+
+        ignite(0).destroyCache(cfg.getName());
+        ignite(0).destroyCache(cfgEsc.getName());
+    }
+
+    /**
+     * Executes query with and without escaped schema name.
+     * @param cache cache for querying
+     * @param log logger for assertThrows
+     * @param schemaName - schema name without quotes for testing
+     */
+    private static void escapeCheckSchemaName(final IgniteCache<Integer, Fact> 
cache, IgniteLogger log, String schemaName) {
+        final SqlFieldsQuery qryWrong = new SqlFieldsQuery("select f.id, 
f.name " +
+            "from " + schemaName.toUpperCase() + ".Fact f");
+
+        cache.put(1, new Fact(1, "cacheInsensitive"));
+
+        GridTestUtils.assertThrows(log, new Callable<Object>() {
+            @Override public Object call() throws Exception {
+                cache.query(qryWrong);
+                return null;
+            }
+        }, IgniteException.class, "Failed to parse query");
+
+        SqlFieldsQuery qryCorrect = new SqlFieldsQuery("select f.\"id\", 
f.\"name\" " +
+            "from \""+schemaName+"\".\"Fact\" f");
+
+        for ( List<?> row : cache.query(qryCorrect)) {
+            assertEquals(2, row.size());
+            assertEquals(1, row.get(0));
+            assertEquals("cacheInsensitive", row.get(1));
+        }
+    }
+
+    // TODO add tests with dynamic cache unregistration, after resolve of 
https://issues.apache.org/jira/browse/IGNITE-1094
+
+    /** Test class as query entity */
+    private static class Fact {
+        /** Primary key. */
+        @QuerySqlField
+        private int id;
+
+        @QuerySqlField
+        private String name;
+
+        /**
+         * Constructs a fact.
+         *
+         * @param id fact ID.
+         * @param name fact name.
+         */
+        Fact(int id, String name) {
+            this.id = id;
+            this.name = name;
+        }
+
+        /**
+         * Gets fact ID.
+         *
+         * @return fact ID.
+         */
+        public int getId() {
+            return id;
+        }
+
+        /**
+         * Gets fact name.
+         *
+         * @return Fact name.
+         */
+        public String getName() {
+            return name;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/da24df99/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 fc88c75..7c8d1d7 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
@@ -92,6 +92,7 @@ import 
org.apache.ignite.internal.processors.cache.reducefields.GridCacheReduceF
 import 
org.apache.ignite.internal.processors.cache.reducefields.GridCacheReduceFieldsQueryLocalSelfTest;
 import 
org.apache.ignite.internal.processors.cache.reducefields.GridCacheReduceFieldsQueryPartitionedSelfTest;
 import 
org.apache.ignite.internal.processors.cache.reducefields.GridCacheReduceFieldsQueryReplicatedSelfTest;
+import org.apache.ignite.internal.processors.query.IgniteSqlSchemaIndexingTest;
 import org.apache.ignite.internal.processors.query.IgniteSqlSplitterSelfTest;
 import 
org.apache.ignite.internal.processors.query.h2.sql.BaseH2CompareQueryTest;
 import org.apache.ignite.internal.processors.query.h2.sql.GridQueryParsingTest;
@@ -117,6 +118,7 @@ public class IgniteCacheQuerySelfTestSuite extends 
TestSuite {
 
         // Queries tests.
         suite.addTestSuite(IgniteSqlSplitterSelfTest.class);
+        suite.addTestSuite(IgniteSqlSchemaIndexingTest.class);
         suite.addTestSuite(GridCacheQueryIndexDisabledSelfTest.class);
         suite.addTestSuite(IgniteCacheQueryLoadSelfTest.class);
         suite.addTestSuite(IgniteCacheLocalQuerySelfTest.class);

http://git-wip-us.apache.org/repos/asf/ignite/blob/da24df99/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/cache/VisorCacheCommand.scala
----------------------------------------------------------------------
diff --git 
a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/cache/VisorCacheCommand.scala
 
b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/cache/VisorCacheCommand.scala
index 90c2de0..0d8d036 100644
--- 
a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/cache/VisorCacheCommand.scala
+++ 
b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/cache/VisorCacheCommand.scala
@@ -17,23 +17,21 @@
 
 package org.apache.ignite.visor.commands.cache
 
+import java.lang.{Boolean => JavaBoolean}
+import java.util.{Collection => JavaCollection, Collections, UUID}
+
 import org.apache.ignite._
 import org.apache.ignite.cluster.ClusterNode
 import org.apache.ignite.internal.util.typedef._
+import org.apache.ignite.internal.visor.cache._
+import org.apache.ignite.internal.visor.util.VisorTaskUtils._
 import org.apache.ignite.lang.IgniteBiTuple
 import org.apache.ignite.visor.VisorTag
 import org.apache.ignite.visor.commands.cache.VisorCacheCommand._
 import org.apache.ignite.visor.commands.common.VisorTextTable
 import org.apache.ignite.visor.visor._
-
 import org.jetbrains.annotations._
 
-import java.lang.{Boolean => JavaBoolean}
-import java.util.{Collection => JavaCollection, Collections, UUID}
-
-import org.apache.ignite.internal.visor.cache._
-import org.apache.ignite.internal.visor.util.VisorTaskUtils._
-
 import scala.collection.JavaConversions._
 import scala.language.{implicitConversions, reflectiveCalls}
 
@@ -890,6 +888,7 @@ object VisorCacheCommand {
         cacheT += ("Expiry Policy Factory Class Name", 
safe(cfg.expiryPolicyFactory()))
 
         cacheT +=("Query Execution Time Threshold", 
queryCfg.longQueryWarningTimeout())
+        cacheT +=("Query Schema Name", queryCfg.sqlSchema())
         cacheT +=("Query Escaped Names", bool2Str(queryCfg.sqlEscapeAll()))
         cacheT +=("Query Onheap Cache Size", queryCfg.sqlOnheapRowCacheSize())
 

Reply via email to