Repository: clickhouse/clickhouse-java
Issue: ClickHouseColumn bug when parsing a tuple type

Hi.
There is a bug or unexpected behavior in ClickHouseColumn when parsing a tuple type:

`ClickHouseColumn.parse("col_name Tuple(s String, i Int64)")` is throwing the following exception:

```
java.lang.IllegalArgumentException: Unknown data type: s String
```

My use case retrieves the types with a `DESCRIBE TABLE` query and parsing them via `ClickHouseColumn.parse`.

Changed files:
clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseDataType.java, clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseColumn.java, clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseColumnTest.java

diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseColumn.java b/clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseColumn.java
index 34011606..aac9dc41 100644
--- a/clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseColumn.java
+++ b/clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseColumn.java
@@ -321,16 +321,18 @@ public final class ClickHouseColumn implements Serializable {
                     StringBuilder sb = new StringBuilder();
                     i = ClickHouseUtils.readNameOrQuotedString(args, i, len, sb);
                     String modifier = sb.toString();
+                    String normalizedModifier = modifier.toUpperCase();
                     sb.setLength(0);
                     boolean startsWithNot = false;
-                    if ("not".equalsIgnoreCase(modifier)) {
+                    if ("NOT".equals(normalizedModifier)) {
                         startsWithNot = true;
                         i = ClickHouseUtils.readNameOrQuotedString(args, i, len, sb);
                         modifier = sb.toString();
+                        normalizedModifier = modifier.toUpperCase();
                         sb.setLength(0);
                     }
 
-                    if ("null".equalsIgnoreCase(modifier)) {
+                    if ("NULL".equals(normalizedModifier)) {
                         if (nullable) {
                             throw new IllegalArgumentException("Nullable and NULL cannot be used together");
                         }
@@ -339,14 +341,14 @@ public final class ClickHouseColumn implements Serializable {
                         break;
                     } else if (startsWithNot) {
                         throw new IllegalArgumentException("Expect keyword NULL after NOT");
-                    } else if ("alias".equalsIgnoreCase(modifier) || "codec".equalsIgnoreCase(modifier)
-                            || "default".equalsIgnoreCase(modifier) || "materialized".equalsIgnoreCase(modifier)
-                            || "ttl".equalsIgnoreCase(modifier)) { // stop words
+                    } else if ("ALIAS".equals(normalizedModifier) || "CODEC".equals(normalizedModifier)
+                            || "DEFAULT".equals(normalizedModifier) || "MATERIALIZED".equals(normalizedModifier)
+                            || "TTL".equals(normalizedModifier)) { // stop words
                         i = ClickHouseUtils.skipContentsUntil(args, i, len, ',') - 1;
                         break;
                     } else {
                         if ((name == null || name.isEmpty())
-                                && !ClickHouseDataType.mayStartWith(builder.toString())) {
+                                && !ClickHouseDataType.mayStartWith(builder.toString(), normalizedModifier)) {
                             return readColumn(args, i - modifier.length(), len, builder.toString(), list);
                         } else {
                             builder.append(' ');
diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseDataType.java b/clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseDataType.java
index bf17c071..38de0871 100644
--- a/clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseDataType.java
+++ b/clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseDataType.java
@@ -181,19 +181,25 @@ public enum ClickHouseDataType {
     }
 
     /**
-     * Checks if any alias uses the given prefix.
+     * Checks if any alias uses the given prefixes.
      *
-     * @param prefix prefix to check
-     * @return true if any alias using the given prefix; false otherwise
+     * @param prefixes prefixes to check
+     * @return true if any alias using the given prefixes; false otherwise
      */
-    public static boolean mayStartWith(String prefix) {
-        if (prefix == null || prefix.isEmpty()) {
+    public static boolean mayStartWith(String... prefixes) {
+        if (prefixes == null || prefixes.length == 0) {
             return false;
         }
 
-        prefix = prefix.toUpperCase();
+        StringBuilder builder = new StringBuilder();
+        for (int i = 0, len = prefixes.length; i < len; i++) {
+            builder.append(prefixes[i].toUpperCase()).append(' ');
+        }
+        String prefix = builder.toString();
+        builder.setLength(builder.length() - 1);
+        String typeName = builder.toString();
         for (String alias : allAliases) {
-            if (alias.startsWith(prefix)) {
+            if (alias.startsWith(prefix) || alias.equals(typeName)) {
                 return true;
             }
         }
diff --git a/clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseColumnTest.java b/clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseColumnTest.java
index 1e041d4e..ecdea072 100644
--- a/clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseColumnTest.java
+++ b/clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseColumnTest.java
@@ -17,6 +17,14 @@ public class ClickHouseColumnTest {
     @DataProvider(name = "objectTypesProvider")
     private Object[][] getObjectTypes() {
         return new Object[][] {
+                { "Tuple(not NChar Large Object)" },
+                { "nchar Large Object" },
+                { "Tuple(int Int32)" },
+                { "a Tuple(i Int32)" },
+                { "b Tuple(i1 Int32)" },
+                { "Tuple(i Int32)" },
+                { "Tuple(i1 Int32)" },
+                { "Tuple(i Int32, a Array(Int32), m Map(LowCardinality(String), Int32))" },
                 { "Int8" }, { "TINYINT SIGNED" },
                 { "k1 Int8" }, { "k1 TINYINT SIGNED" },
                 { "k1 Nullable(Int8)" }, { "k1 Nullable( Int8 )" }, { "k1 TINYINT SIGNED null" },