IGNITE-6242 SQL: Added ability to specify cache, key type and value type names 
for CREATE TABLE. This closes #2740.


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

Branch: refs/heads/ignite-3478
Commit: de942860f19b8b68b4f3f47bcea35487c1320d87
Parents: adbf068
Author: Alexander Paschenko <[email protected]>
Authored: Wed Sep 27 14:51:17 2017 +0300
Committer: devozerov <[email protected]>
Committed: Wed Sep 27 14:51:17 2017 +0300

----------------------------------------------------------------------
 .../processors/query/GridQueryProcessor.java    |  11 +-
 .../query/h2/ddl/DdlStatementsProcessor.java    |  12 +-
 .../query/h2/sql/GridSqlCreateTable.java        |  51 +++++++
 .../query/h2/sql/GridSqlQueryParser.java        |  34 +++++
 .../cache/index/H2DynamicTableSelfTest.java     | 149 ++++++++++++++++++-
 5 files changed, 247 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/de942860/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
index 2a13b25..e8cc852 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
@@ -39,6 +39,7 @@ import javax.cache.CacheException;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteDataStreamer;
 import org.apache.ignite.IgniteException;
+import org.apache.ignite.binary.BinaryType;
 import org.apache.ignite.binary.Binarylizable;
 import org.apache.ignite.cache.CacheAtomicityMode;
 import org.apache.ignite.cache.CacheKeyConfiguration;
@@ -1354,6 +1355,7 @@ public class GridQueryProcessor extends 
GridProcessorAdapter {
      * @param schemaName Schema name to create table in.
      * @param entity Entity to create table from.
      * @param templateName Template name.
+     * @param cacheName
      * @param cacheGroup Cache group name.
      * @param affinityKey Affinity key column name.
      * @param atomicityMode Atomicity mode.
@@ -1363,8 +1365,8 @@ public class GridQueryProcessor extends 
GridProcessorAdapter {
      * @throws IgniteCheckedException If failed.
      */
     @SuppressWarnings("unchecked")
-    public void dynamicTableCreate(String schemaName, QueryEntity entity, 
String templateName, String cacheGroup,
-        String affinityKey, @Nullable CacheAtomicityMode atomicityMode,
+    public void dynamicTableCreate(String schemaName, QueryEntity entity, 
String templateName, String cacheName,
+        String cacheGroup, String affinityKey, @Nullable CacheAtomicityMode 
atomicityMode,
         @Nullable CacheWriteSynchronizationMode writeSyncMode, int backups, 
boolean ifNotExists)
         throws IgniteCheckedException {
         assert !F.isEmpty(templateName);
@@ -1387,7 +1389,10 @@ public class GridQueryProcessor extends 
GridProcessorAdapter {
             throw new SchemaOperationException("Template cache already 
contains query entities which it should not: " +
                 templateName);
 
-        ccfg.setName(QueryUtils.createTableCacheName(schemaName, 
entity.getTableName()));
+        if (F.isEmpty(cacheName))
+            cacheName = QueryUtils.createTableCacheName(schemaName, 
entity.getTableName());
+
+        ccfg.setName(cacheName);
 
         if (!F.isEmpty(cacheGroup))
             ccfg.setGroupName(cacheGroup);

http://git-wip-us.apache.org/repos/asf/ignite/blob/de942860/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 03f4e1f..5105b26 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
@@ -182,9 +182,9 @@ public class DdlStatementsProcessor {
                     if (err != null)
                         throw err;
 
-                    ctx.query().dynamicTableCreate(cmd.schemaName(), e, 
cmd.templateName(), cmd.cacheGroup(),
-                        cmd.affinityKey(), cmd.atomicityMode(), 
cmd.writeSynchronizationMode(), cmd.backups(),
-                        cmd.ifNotExists());
+                    ctx.query().dynamicTableCreate(cmd.schemaName(), e, 
cmd.templateName(), cmd.cacheName(),
+                        cmd.cacheGroup(),cmd.affinityKey(), 
cmd.atomicityMode(),
+                        cmd.writeSynchronizationMode(), cmd.backups(), 
cmd.ifNotExists());
                 }
             }
             else if (stmt0 instanceof GridSqlDropTable) {
@@ -358,6 +358,12 @@ public class DdlStatementsProcessor {
         String valTypeName = 
QueryUtils.createTableValueTypeName(createTbl.schemaName(), 
createTbl.tableName());
         String keyTypeName = QueryUtils.createTableKeyTypeName(valTypeName);
 
+        if (!F.isEmpty(createTbl.keyTypeName()))
+            keyTypeName = createTbl.keyTypeName();
+
+        if (!F.isEmpty(createTbl.valueTypeName()))
+            valTypeName = createTbl.valueTypeName();
+
         res.setValueType(valTypeName);
         res.setKeyType(keyTypeName);
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/de942860/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlCreateTable.java
----------------------------------------------------------------------
diff --git 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlCreateTable.java
 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlCreateTable.java
index d28ee7a..b73214f 100644
--- 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlCreateTable.java
+++ 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlCreateTable.java
@@ -38,6 +38,15 @@ public class GridSqlCreateTable extends GridSqlStatement {
     /** Cache name upon which new cache configuration for this table must be 
based. */
     private String templateName;
 
+    /** Name of new cache associated with this table. */
+    private String cacheName;
+
+    /** Name of cache key type. */
+    private String keyTypeName;
+
+    /** Name of cache value type. */
+    private String valTypeName;
+
     /** Group to put new cache into. */
     private String cacheGrp;
 
@@ -80,6 +89,48 @@ public class GridSqlCreateTable extends GridSqlStatement {
     }
 
     /**
+     * @return Name of new cache associated with this table.
+     */
+    public String cacheName() {
+        return cacheName;
+    }
+
+    /**
+     * @param cacheName Name of new cache associated with this table.
+     */
+    public void cacheName(String cacheName) {
+        this.cacheName = cacheName;
+    }
+
+    /**
+     * @return Name of cache key type.
+     */
+    public String keyTypeName() {
+        return keyTypeName;
+    }
+
+    /**
+     * @param keyTypeName Name of cache key type.
+     */
+    public void keyTypeName(String keyTypeName) {
+        this.keyTypeName = keyTypeName;
+    }
+
+    /**
+     * @return Name of cache value type.
+     */
+    public String valueTypeName() {
+        return valTypeName;
+    }
+
+    /**
+     * @param valTypeName Name of cache value type.
+     */
+    public void valueTypeName(String valTypeName) {
+        this.valTypeName = valTypeName;
+    }
+
+    /**
      * @return Group to put new cache into.
      */
     public String cacheGroup() {

http://git-wip-us.apache.org/repos/asf/ignite/blob/de942860/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java
----------------------------------------------------------------------
diff --git 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java
 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java
index 3046c20..5cec625 100644
--- 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java
+++ 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java
@@ -441,6 +441,15 @@ public class GridSqlQueryParser {
     private static final String PARAM_WRITE_SYNC = 
"WRITE_SYNCHRONIZATION_MODE";
 
     /** */
+    private static final String PARAM_CACHE_NAME = "CACHE_NAME";
+
+    /** */
+    private static final String PARAM_KEY_TYPE = "KEY_TYPE";
+
+    /** */
+    private static final String PARAM_VAL_TYPE = "VALUE_TYPE";
+
+    /** */
     private final IdentityHashMap<Object, Object> h2ObjToGridObj = new 
IdentityHashMap<>();
 
     /** */
@@ -983,6 +992,10 @@ public class GridSqlQueryParser {
                 processExtraParam(e.getKey(), e.getValue(), res);
         }
 
+        if (!F.isEmpty(res.valueTypeName()) && F.eq(res.keyTypeName(), 
res.valueTypeName()))
+            throw new IgniteSQLException("Key and value type names " +
+                "should be different for CREATE TABLE: " + 
res.valueTypeName(), IgniteQueryErrorCode.PARSING);
+
         return res;
     }
 
@@ -1168,6 +1181,27 @@ public class GridSqlQueryParser {
 
                 break;
 
+            case PARAM_CACHE_NAME:
+                ensureNotEmpty(name, val);
+
+                res.cacheName(val);
+
+                break;
+
+            case PARAM_KEY_TYPE:
+                ensureNotEmpty(name, val);
+
+                res.keyTypeName(val);
+
+                break;
+
+            case PARAM_VAL_TYPE:
+                ensureNotEmpty(name, val);
+
+                res.valueTypeName(val);
+
+                break;
+
             case PARAM_CACHE_GROUP:
                 ensureNotEmpty(name, val);
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/de942860/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 0be6c70..773e7e0 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
@@ -31,6 +31,7 @@ import javax.cache.CacheException;
 import org.apache.ignite.Ignite;
 import org.apache.ignite.IgniteException;
 import org.apache.ignite.Ignition;
+import org.apache.ignite.binary.BinaryObject;
 import org.apache.ignite.cache.CacheAtomicityMode;
 import org.apache.ignite.cache.CacheMode;
 import org.apache.ignite.cache.CacheWriteSynchronizationMode;
@@ -42,6 +43,7 @@ import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.binary.BinaryMarshaller;
 import org.apache.ignite.internal.processors.cache.DynamicCacheDescriptor;
+import org.apache.ignite.internal.processors.cache.IgniteInternalCache;
 import org.apache.ignite.internal.processors.query.GridQueryProperty;
 import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor;
 import org.apache.ignite.internal.processors.query.IgniteSQLException;
@@ -52,7 +54,9 @@ import 
org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing;
 import 
org.apache.ignite.internal.processors.query.h2.ddl.DdlStatementsProcessor;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
 import 
org.apache.ignite.internal.processors.query.schema.SchemaOperationException;
+import org.apache.ignite.internal.util.GridStringBuilder;
 import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.testframework.GridTestUtils;
 import org.h2.jdbc.JdbcSQLException;
 
@@ -100,6 +104,7 @@ public class H2DynamicTableSelfTest extends 
AbstractSchemaSelfTest {
     @Override protected void afterTest() throws Exception {
         execute("DROP TABLE IF EXISTS PUBLIC.\"Person\"");
         execute("DROP TABLE IF EXISTS PUBLIC.\"City\"");
+        execute("DROP TABLE IF EXISTS PUBLIC.\"NameTest\"");
 
         super.afterTest();
     }
@@ -121,7 +126,8 @@ public class H2DynamicTableSelfTest extends 
AbstractSchemaSelfTest {
     }
 
     /**
-     * Test that {@code CREATE TABLE} actually creates new cache from 
template, H2 table and type descriptor on all nodes.
+     * Test that {@code CREATE TABLE} actually creates new cache from template,
+     * H2 table and type descriptor on all nodes.
      * @throws Exception if failed.
      */
     public void testCreateTableWithWriteSyncMode() throws Exception {
@@ -210,6 +216,141 @@ public class H2DynamicTableSelfTest extends 
AbstractSchemaSelfTest {
     }
 
     /**
+     * Test behavior only in case of cache name override.
+     */
+    public void testCustomCacheName() {
+        doTestCustomNames("cname", null, null);
+    }
+
+    /**
+     * Test behavior only in case of key type name override.
+     */
+    public void testCustomKeyTypeName() {
+        doTestCustomNames(null, "keytype", null);
+    }
+
+    /**
+     * Test behavior only in case of value type name override.
+     */
+    public void testCustomValueTypeName() {
+        doTestCustomNames(null, null, "valtype");
+    }
+
+    /**
+     * Test behavior only in case of cache and key type name override.
+     */
+    public void testCustomCacheAndKeyTypeName() {
+        doTestCustomNames("cname", "keytype", null);
+    }
+
+    /**
+     * Test behavior only in case of cache and value type name override.
+     */
+    public void testCustomCacheAndValueTypeName() {
+        doTestCustomNames("cname", null, "valtype");
+    }
+
+    /**
+     * Test behavior only in case of key and value type name override.
+     */
+    public void testCustomKeyAndValueTypeName() {
+        doTestCustomNames(null, "keytype", "valtype");
+    }
+
+    /**
+     * Test behavior only in case of cache, key, and value type name override.
+     */
+    public void testCustomCacheAndKeyAndValueTypeName() {
+        doTestCustomNames("cname", "keytype", "valtype");
+    }
+
+    /**
+     * Test that attempting to create a cache with a pre-existing name yields 
an error.
+     * @throws Exception if failed.
+     */
+    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+    public void testDuplicateCustomCacheName() throws Exception {
+        client().getOrCreateCache("new");
+
+        try {
+            GridTestUtils.assertThrows(null, new Callable<Object>() {
+                @Override public Object call() throws Exception {
+                    doTestCustomNames("new", null, null);return null;
+                }
+            }, IgniteSQLException.class, "Table already exists: NameTest");
+        }
+        finally {
+            client().destroyCache("new");
+        }
+    }
+
+    /**
+     * Test that appending supplied arguments to {@code CREATE TABLE} results 
in creating new cache that has settings
+     * as expected
+     * @param cacheName Cache name, or {@code null} if the name generated by 
default should be used.
+     * @param keyTypeName Key type name, or {@code null} if the name generated 
by default should be used.
+     * @param valTypeName Value type name, or {@code null} if the name 
generated by default should be used.
+     */
+    private void doTestCustomNames(String cacheName, String keyTypeName, 
String valTypeName) {
+        GridStringBuilder b = new GridStringBuilder("CREATE TABLE \"NameTest\" 
(id int primary key, x varchar) WITH ");
+
+        assert !F.isEmpty(cacheName) || !F.isEmpty(keyTypeName) || 
!F.isEmpty(valTypeName);
+
+        if (!F.isEmpty(cacheName))
+            b.a("\"cache_name=").a(cacheName).a('"').a(',');
+
+        if (!F.isEmpty(keyTypeName))
+            b.a("\"key_type=").a(keyTypeName).a('"').a(',');
+
+        if (!F.isEmpty(valTypeName))
+            b.a("\"value_type=").a(valTypeName).a('"');
+
+        String res = b.toString();
+
+        if (res.endsWith(","))
+            res = res.substring(0, res.length() - 1);
+
+        execute(client(), res);
+
+        String resCacheName = U.firstNotNull(cacheName, 
QueryUtils.createTableCacheName(QueryUtils.DFLT_SCHEMA,
+            "NameTest"));
+
+        IgniteInternalCache<BinaryObject, BinaryObject> cache = 
client().cachex(resCacheName);
+
+        assertNotNull(cache);
+
+        CacheConfiguration ccfg = cache.configuration();
+
+        assertEquals(1, ccfg.getQueryEntities().size());
+
+        QueryEntity e = (QueryEntity)ccfg.getQueryEntities().iterator().next();
+
+        if (!F.isEmpty(keyTypeName))
+            assertEquals(keyTypeName, e.getKeyType());
+        else
+            assertTrue(e.getKeyType().startsWith("SQL_PUBLIC"));
+
+        if (!F.isEmpty(valTypeName))
+            assertEquals(valTypeName, e.getValueType());
+        else
+            assertTrue(e.getValueType().startsWith("SQL_PUBLIC"));
+
+        execute(client(), "INSERT INTO \"NameTest\" (id, x) values (1, 'a')");
+
+        List<List<?>> qres = execute(client(), "SELECT id, x from 
\"NameTest\"");
+
+        assertEqualsCollections(Collections.singletonList(Arrays.asList(1, 
"a")), qres);
+
+        BinaryObject key = 
client().binary().builder(e.getKeyType()).setField("ID", 1).build();
+
+        BinaryObject val = 
(BinaryObject)client().cache(resCacheName).withKeepBinary().get(key);
+
+        BinaryObject exVal = 
client().binary().builder(e.getValueType()).setField("X", "a").build();
+
+        assertEquals(exVal, val);
+    }
+
+    /**
      * Perform a check on given table name considering case sensitivity.
      * @param tblName Table name to check.
      * @param sensitive Whether table should be created w/case sensitive name 
or not.
@@ -600,7 +741,7 @@ public class H2DynamicTableSelfTest extends 
AbstractSchemaSelfTest {
                 e.setKeyType("CityKey");
                 e.setValueType("City");
 
-                queryProcessor(client()).dynamicTableCreate("PUBLIC", e, 
CacheMode.PARTITIONED.name(), null,
+                queryProcessor(client()).dynamicTableCreate("PUBLIC", e, 
CacheMode.PARTITIONED.name(), null, null,
                     null, CacheAtomicityMode.ATOMIC, null, 10, false);
 
                 return null;
@@ -1015,8 +1156,8 @@ public class H2DynamicTableSelfTest extends 
AbstractSchemaSelfTest {
      * @param node Node.
      * @param sql Statement.
      */
-    private void execute(Ignite node, String sql) {
-        queryProcessor(node).querySqlFieldsNoCache(new 
SqlFieldsQuery(sql).setSchema("PUBLIC"), true);
+    private List<List<?>> execute(Ignite node, String sql) {
+        return queryProcessor(node).querySqlFieldsNoCache(new 
SqlFieldsQuery(sql).setSchema("PUBLIC"), true).getAll();
     }
 
     /**

Reply via email to