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

Reply via email to