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(); } /**
