IGNITE-9183: SQL: fixed UUID handling from DDL statements. This closes #4483.
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/a98dcb93 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/a98dcb93 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/a98dcb93 Branch: refs/heads/ignite-9273 Commit: a98dcb93ba45f9d2b69585a84653f2e0174ba2a4 Parents: eea44e2 Author: Ivan Daschinskiy <ivanda...@gmail.com> Authored: Mon Aug 27 18:54:56 2018 +0300 Committer: devozerov <voze...@gridgain.com> Committed: Mon Aug 27 18:54:56 2018 +0300 ---------------------------------------------------------------------- .../apache/ignite/IgniteSystemProperties.java | 3 + .../query/h2/ddl/DdlStatementsProcessor.java | 33 +++++- .../H2DynamicColumnsAbstractBasicSelfTest.java | 117 +++++++++++++++++++ .../cache/index/H2DynamicTableSelfTest.java | 91 ++++++++++++--- 4 files changed, 221 insertions(+), 23 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/a98dcb93/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java b/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java index 02c8b30..78842d6 100644 --- a/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java +++ b/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java @@ -483,6 +483,9 @@ public final class IgniteSystemProperties { /** SQL retry timeout. */ public static final String IGNITE_SQL_RETRY_TIMEOUT = "IGNITE_SQL_RETRY_TIMEOUT"; + /** Enable backward compatible handling of UUID through DDL. */ + public static final String IGNITE_SQL_UUID_DDL_BYTE_FORMAT = "IGNITE_SQL_UUID_DDL_BYTE_FORMAT"; + /** Maximum size for affinity assignment history. */ public static final String IGNITE_AFFINITY_HISTORY_SIZE = "IGNITE_AFFINITY_HISTORY_SIZE"; http://git-wip-us.apache.org/repos/asf/ignite/blob/a98dcb93/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 91ebece..0a0398b 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 @@ -25,8 +25,10 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.UUID; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteCluster; +import org.apache.ignite.IgniteSystemProperties; import org.apache.ignite.cache.QueryEntity; import org.apache.ignite.cache.QueryIndex; import org.apache.ignite.cache.QueryIndexType; @@ -94,6 +96,10 @@ public class DdlStatementsProcessor { /** Indexing. */ IgniteH2Indexing idx; + /** Is backward compatible handling of UUID through DDL enabled. */ + private static final boolean handleUuidAsByte = + IgniteSystemProperties.getBoolean(IgniteSystemProperties.IGNITE_SQL_UUID_DDL_BYTE_FORMAT, false); + /** * Initialize message handlers and this' fields needed for further operation. * @@ -401,7 +407,7 @@ public class DdlStatementsProcessor { } QueryField field = new QueryField(col.columnName(), - DataType.getTypeClassName(col.column().getType()), + getTypeClassName(col), col.column().isNullable(), col.defaultValue(), col.precision(), col.scale()); @@ -632,7 +638,7 @@ public class DdlStatementsProcessor { Column col = gridCol.column(); - res.addQueryField(e.getKey(), DataType.getTypeClassName(col.getType()), null); + res.addQueryField(e.getKey(), getTypeClassName(gridCol), null); if (!col.isNullable()) { if (notNullFields == null) @@ -671,7 +677,7 @@ public class DdlStatementsProcessor { if (!createTbl.wrapKey()) { GridSqlColumn pkCol = createTbl.columns().get(createTbl.primaryKeyColumns().iterator().next()); - keyTypeName = DataType.getTypeClassName(pkCol.column().getType()); + keyTypeName = getTypeClassName(pkCol); res.setKeyFieldName(pkCol.columnName()); } @@ -691,7 +697,7 @@ public class DdlStatementsProcessor { assert valCol != null; - valTypeName = DataType.getTypeClassName(valCol.column().getType()); + valTypeName = getTypeClassName(valCol); res.setValueFieldName(valCol.columnName()); } @@ -718,4 +724,23 @@ public class DdlStatementsProcessor { return cmd instanceof CreateIndex || cmd instanceof DropIndex || cmd instanceof CreateTable || cmd instanceof DropTable || cmd instanceof AlterTableAlterColumn; } + + /** + * Helper function for obtaining type class name for H2. + * + * @param col Column. + * @return Type class name. + */ + private static String getTypeClassName(GridSqlColumn col) { + int type = col.column().getType(); + + switch (type) { + case Value.UUID : + if (!handleUuidAsByte) + return UUID.class.getName(); + + default: + return DataType.getTypeClassName(type); + } + } } http://git-wip-us.apache.org/repos/asf/ignite/blob/a98dcb93/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicColumnsAbstractBasicSelfTest.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicColumnsAbstractBasicSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicColumnsAbstractBasicSelfTest.java index 650da7d..e74e9cd 100644 --- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicColumnsAbstractBasicSelfTest.java +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicColumnsAbstractBasicSelfTest.java @@ -21,6 +21,8 @@ import java.sql.SQLException; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Random; +import java.util.UUID; import org.apache.ignite.IgniteCache; import org.apache.ignite.Ignition; import org.apache.ignite.binary.BinaryObject; @@ -282,6 +284,76 @@ public abstract class H2DynamicColumnsAbstractBasicSelfTest extends DynamicColum } /** + * Tests that we can add dynamically UUID column to tables. + * + * @throws SQLException If failed. + */ + @SuppressWarnings("unchecked") + public void testAddColumnUUID() throws SQLException { + CacheConfiguration<Integer, Object> ccfg = defaultCacheConfiguration().setName("GuidTest") + .setIndexedTypes(Integer.class, GuidTest.class); + + Random rnd = new Random(); + + IgniteCache<Integer, Object> cache = ignite(nodeIndex()).getOrCreateCache(ccfg); + + run(cache, "ALTER TABLE \"GuidTest\".GuidTest ADD COLUMN GUID UUID"); + run(cache, "ALTER TABLE \"GuidTest\".GuidTest ADD COLUMN DATA BINARY(128)"); + + doSleep(500); + + QueryField c1 = c("GUID", Object.class.getName()); + QueryField c2 = c("DATA", byte[].class.getName()); + + checkTableState("GuidTest", "GUIDTEST", c1, c2); + + UUID guid1 = UUID.randomUUID(); + UUID guid2 = UUID.randomUUID(); + + // Populate random data for BINARY field. + byte[] data1 = new byte[128]; + rnd.nextBytes(data1); + byte[] data2 = new byte[128]; + rnd.nextBytes(data2); + + run(cache, "INSERT INTO \"GuidTest\".GuidTest (_key, id, guid, data) values " + + "(1, 1, ?, ?)", guid1.toString(), data1); + + cache.put(2, new GuidTest(2, guid2, data2)); + + List<List<?>> res = run(cache, "select _key, id, guid from \"GuidTest\".GuidTest order by id"); + + assertEquals(Arrays.asList(Arrays.asList(1, 1, guid1), Arrays.asList(2, 2, guid2)), res); + + // Additional check for BINARY field content. + res = run(cache, "select data from \"GuidTest\".GuidTest order by id"); + + assertTrue(Arrays.equals(data1, (byte[])res.get(0).get(0))); + assertTrue(Arrays.equals(data2, (byte[])res.get(1).get(0))); + + if (!Boolean.valueOf(GridTestProperties.getProperty(BINARY_MARSHALLER_USE_SIMPLE_NAME_MAPPER))) { + GuidTest val1 = (GuidTest)cache.get(1); + GuidTest val2 = (GuidTest)cache.get(2); + + assertEquals(guid1, val1.guid()); + assertEquals(guid2, val2.guid()); + assertTrue(Arrays.equals(data1, val1.data())); + assertTrue(Arrays.equals(data2, val2.data())); + } + else { + BinaryObject val1 = (BinaryObject)cache.withKeepBinary().get(1); + BinaryObject val2 = (BinaryObject)cache.withKeepBinary().get(2); + + assertEquals(guid1, val1.field("guid")); + assertEquals(guid2, val2.field("guid")); + assertTrue(Arrays.equals(data1, val1.field("data"))); + assertTrue(Arrays.equals(data2, val2.field("data"))); + } + + cache.destroy(); + } + + /** * Test addition of column with not null constraint. */ public void testAddNotNullColumn() throws SQLException { @@ -770,4 +842,49 @@ public abstract class H2DynamicColumnsAbstractBasicSelfTest extends DynamicColum this.state = state; } } + + /** */ + private final static class GuidTest { + /** */ + @QuerySqlField + private int id; + + /** */ + private UUID guid; + + /** */ + private byte[] data; + + /** + * @param id Id. + * @param guid Guid. + * @param data Data. + */ + public GuidTest(int id, UUID guid, byte[] data) { + this.id = id; + this.guid = guid; + this.data = data; + } + + /** + * @return Id. + */ + public int id() { + return id; + } + + /** + * @return Guid. + */ + public UUID guid() { + return guid; + } + + /** + * @return Data. + */ + public byte[] data() { + return data; + } + } } http://git-wip-us.apache.org/repos/asf/ignite/blob/a98dcb93/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 2fc69f6..82d10cd 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 java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Random; +import java.util.UUID; import java.util.concurrent.Callable; import javax.cache.CacheException; import org.apache.ignite.Ignite; @@ -43,6 +44,7 @@ import org.apache.ignite.cache.CacheWriteSynchronizationMode; import org.apache.ignite.cache.QueryEntity; import org.apache.ignite.cache.QueryIndex; 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.DataRegionConfiguration; import org.apache.ignite.configuration.DataStorageConfiguration; @@ -1259,7 +1261,7 @@ public class H2DynamicTableSelfTest extends AbstractSchemaSelfTest { * @throws SQLException if failed. */ public void testNoWrap() throws SQLException { - doTestKeyValueWrap(false, false); + doTestKeyValueWrap(false, false, false); } /** @@ -1267,7 +1269,7 @@ public class H2DynamicTableSelfTest extends AbstractSchemaSelfTest { * @throws SQLException if failed. */ public void testKeyWrap() throws SQLException { - doTestKeyValueWrap(true, false); + doTestKeyValueWrap(true, false, false); } /** @@ -1275,7 +1277,7 @@ public class H2DynamicTableSelfTest extends AbstractSchemaSelfTest { * @throws SQLException if failed. */ public void testValueWrap() throws SQLException { - doTestKeyValueWrap(false, true); + doTestKeyValueWrap(false, true, false); } /** @@ -1283,29 +1285,73 @@ public class H2DynamicTableSelfTest extends AbstractSchemaSelfTest { * @throws SQLException if failed. */ public void testKeyAndValueWrap() throws SQLException { - doTestKeyValueWrap(true, true); + doTestKeyValueWrap(true, true, false); + } + + /** + * Test behavior when neither key nor value should be wrapped. + * Key and value are UUID. + * @throws SQLException if failed. + */ + public void testUuidNoWrap() throws SQLException { + doTestKeyValueWrap(false, false, true); + } + + /** + * Test behavior when only key is wrapped. + * Key and value are UUID. + * @throws SQLException if failed. + */ + public void testUuidKeyWrap() throws SQLException { + doTestKeyValueWrap(true, false, true); + } + + /** + * Test behavior when only value is wrapped. + * Key and value are UUID. + * @throws SQLException if failed. + */ + public void testUuidValueWrap() throws SQLException { + doTestKeyValueWrap(false, true, true); + } + + /** + * Test behavior when both key and value is wrapped. + * Key and value are UUID. + * @throws SQLException if failed. + */ + public void testUuidKeyAndValueWrap() throws SQLException { + doTestKeyValueWrap(true, true, true); } /** * Test behavior for given combination of wrap flags. * @param wrapKey Whether key wrap should be enforced. * @param wrapVal Whether value wrap should be enforced. + * @param testUuid Whether should test with UUID as key and value. * @throws SQLException if failed. */ - private void doTestKeyValueWrap(boolean wrapKey, boolean wrapVal) throws SQLException { + private void doTestKeyValueWrap(boolean wrapKey, boolean wrapVal, boolean testUuid) throws SQLException { try { - String sql = String.format("CREATE TABLE T (\"id\" int primary key, \"x\" varchar) WITH " + - "\"wrap_key=%b,wrap_value=%b\"", wrapKey, wrapVal); + String sql = testUuid ? String.format("CREATE TABLE T (\"id\" UUID primary key, \"x\" UUID) WITH " + + "\"wrap_key=%b,wrap_value=%b\"", wrapKey, wrapVal) : + String.format("CREATE TABLE T (\"id\" int primary key, \"x\" varchar) WITH " + + "\"wrap_key=%b,wrap_value=%b\"", wrapKey, wrapVal); + + UUID guid = UUID.randomUUID(); if (wrapKey) - sql += ",\"key_type=tkey\""; + sql += ",\"key_type=" + (testUuid ? "tkey_guid" : "tkey") + "\""; if (wrapVal) - sql += ",\"value_type=tval\""; + sql += ",\"value_type=" + (testUuid ? "tval_guid" : "tval") + "\""; execute(sql); - execute("INSERT INTO T(\"id\", \"x\") values(1, 'a')"); + if(testUuid) + execute("INSERT INTO T(\"id\", \"x\") values('" + guid.toString() + "', '" + guid.toString() + "')"); + else + execute("INSERT INTO T(\"id\", \"x\") values(1, 'a')"); LinkedHashMap<String, String> resCols = new LinkedHashMap<>(); @@ -1331,20 +1377,27 @@ public class H2DynamicTableSelfTest extends AbstractSchemaSelfTest { LinkedHashMap<String, String> expCols = new LinkedHashMap<>(); - expCols.put("id", Integer.class.getName()); - expCols.put("x", String.class.getName()); + if (testUuid) { + expCols.put("id", Object.class.getName()); + expCols.put("x", Object.class.getName()); + } + else { + expCols.put("id", Integer.class.getName()); + expCols.put("x", String.class.getName()); + } assertEquals(expCols, resCols); - assertEqualsCollections(Arrays.asList(1, "a"), resData); + assertEqualsCollections(testUuid ? Arrays.asList(guid, guid) : Arrays.asList(1, "a") + , resData); - Object key = createKeyForWrapTest(1, wrapKey); + Object key = createKeyForWrapTest(testUuid ? guid : 1, wrapKey); Object val = client().cache(cacheName("T")).withKeepBinary().get(key); assertNotNull(val); - assertEquals(createValueForWrapTest("a", wrapVal), val); + assertEquals(createValueForWrapTest(testUuid ? guid : "a", wrapVal), val); } finally { execute("DROP TABLE IF EXISTS T"); @@ -1356,11 +1409,11 @@ public class H2DynamicTableSelfTest extends AbstractSchemaSelfTest { * @param wrap Whether key should be wrapped. * @return (optionally wrapped) key. */ - private Object createKeyForWrapTest(int key, boolean wrap) { + private Object createKeyForWrapTest(Object key, boolean wrap) { if (!wrap) return key; - return client().binary().builder("tkey").setField("id", key).build(); + return client().binary().builder(key instanceof UUID ? "tkey_guid" : "tkey").setField("id", key).build(); } /** @@ -1368,11 +1421,11 @@ public class H2DynamicTableSelfTest extends AbstractSchemaSelfTest { * @param wrap Whether value should be wrapped. * @return (optionally wrapped) value. */ - private Object createValueForWrapTest(String val, boolean wrap) { + private Object createValueForWrapTest(Object val, boolean wrap) { if (!wrap) return val; - return client().binary().builder("tval").setField("x", val).build(); + return client().binary().builder(val instanceof UUID ? "tval_guid" : "tval").setField("x", val).build(); } /**