This is an automated email from the ASF dual-hosted git repository.

jiajunxie pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git


The following commit(s) were added to refs/heads/main by this push:
     new 6f79436c17 [CALCITE-5570] Support nested map type for SqlDataTypeSpec
6f79436c17 is described below

commit 6f79436c178beec639e559d9152c237bbf8ec3e8
Author: xiejiajun <jiajunbernou...@foxmail.com>
AuthorDate: Sun Mar 12 14:12:18 2023 +0800

    [CALCITE-5570] Support nested map type for SqlDataTypeSpec
---
 core/src/main/codegen/templates/Parser.jj          | 21 +++++
 .../org/apache/calcite/sql/SqlMapTypeNameSpec.java | 94 ++++++++++++++++++++++
 .../org/apache/calcite/test/SqlValidatorTest.java  | 20 +++++
 core/src/test/resources/sql/misc.iq                | 19 +++++
 .../apache/calcite/sql/parser/SqlParserTest.java   | 14 ++++
 .../org/apache/calcite/test/catalog/Fixture.java   |  6 ++
 .../test/catalog/MockCatalogReaderExtended.java    |  5 ++
 7 files changed, 179 insertions(+)

diff --git a/core/src/main/codegen/templates/Parser.jj 
b/core/src/main/codegen/templates/Parser.jj
index 269e11d2ba..f03f36409f 100644
--- a/core/src/main/codegen/templates/Parser.jj
+++ b/core/src/main/codegen/templates/Parser.jj
@@ -76,6 +76,7 @@ import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.SqlLiteral;
 import org.apache.calcite.sql.SqlMatchRecognize;
 import org.apache.calcite.sql.SqlMerge;
+import org.apache.calcite.sql.SqlMapTypeNameSpec;
 import org.apache.calcite.sql.SqlNode;
 import org.apache.calcite.sql.SqlNodeList;
 import org.apache.calcite.sql.SqlNumericLiteral;
@@ -5689,6 +5690,9 @@ SqlTypeNameSpec TypeName() :
         typeNameSpec = SqlTypeName(s)
     |
         typeNameSpec = RowTypeName()
+    |
+        LOOKAHEAD(2)
+        typeNameSpec = MapTypeName()
     |
         typeName = CompoundIdentifier() {
             typeNameSpec = new SqlUserDefinedTypeNameSpec(typeName, 
s.end(this));
@@ -5962,6 +5966,23 @@ SqlTypeNameSpec RowTypeName() :
     }
 }
 
+SqlTypeNameSpec MapTypeName() :
+{
+    SqlDataTypeSpec keyType = null;
+    SqlDataTypeSpec valType = null;
+}
+{
+    <MAP>
+    <LT>
+    keyType = DataType()
+    <COMMA>
+    valType = DataType()
+    <GT>
+    {
+        return new SqlMapTypeNameSpec(keyType, valType, getPos());
+    }
+}
+
 /**
 * Parse character types: char, varchar.
 */
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlMapTypeNameSpec.java 
b/core/src/main/java/org/apache/calcite/sql/SqlMapTypeNameSpec.java
new file mode 100644
index 0000000000..37520fc834
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/sql/SqlMapTypeNameSpec.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.sql;
+
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.sql.validate.SqlValidator;
+import org.apache.calcite.util.Litmus;
+
+/**
+ * Parse SQL MAP type, i.e. MAP&lt;INT NOT NULL, TIMESTAMP NULL&gt;, the key 
and value can specify a
+ * suffix to indicate if the type is nullable, default is not null.
+ *
+ * <p>MAP type does not belong to standard SQL.
+ */
+public class SqlMapTypeNameSpec extends SqlTypeNameSpec {
+
+  private final SqlDataTypeSpec keyType;
+  private final SqlDataTypeSpec valType;
+
+  /**
+   * Creates a {@code SqlMapTypeNameSpec}.
+   *
+   * @param keyType key type
+   * @param valType value type
+   * @param pos the parser position
+   */
+  public SqlMapTypeNameSpec(SqlDataTypeSpec keyType, SqlDataTypeSpec valType, 
SqlParserPos pos) {
+    super(new SqlIdentifier(SqlTypeName.MAP.getName(), pos), pos);
+    this.keyType = keyType;
+    this.valType = valType;
+  }
+
+  public SqlDataTypeSpec getKeyType() {
+    return keyType;
+  }
+
+  public SqlDataTypeSpec getValType() {
+    return valType;
+  }
+
+  @Override public RelDataType deriveType(SqlValidator validator) {
+    return validator
+        .getTypeFactory()
+        .createMapType(keyType.deriveType(validator), 
valType.deriveType(validator));
+  }
+
+  @Override public void unparse(SqlWriter writer, int leftPrec, int rightPrec) 
{
+    writer.keyword("MAP");
+    SqlWriter.Frame frame = writer.startList(SqlWriter.FrameTypeEnum.FUN_CALL, 
"<", ">");
+    writer.sep(","); // configures the writer
+    keyType.unparse(writer, leftPrec, rightPrec);
+    // Default is not null.
+    if (Boolean.TRUE.equals(keyType.getNullable())) {
+      writer.keyword("NULL");
+    }
+    writer.sep(",");
+    valType.unparse(writer, leftPrec, rightPrec);
+    // Default is not null.
+    if (Boolean.TRUE.equals(valType.getNullable())) {
+      writer.keyword("NULL");
+    }
+    writer.endList(frame);
+  }
+
+  @Override public boolean equalsDeep(SqlTypeNameSpec spec, Litmus litmus) {
+    if (!(spec instanceof SqlMapTypeNameSpec)) {
+      return litmus.fail("{} != {}", this, spec);
+    }
+    SqlMapTypeNameSpec that = (SqlMapTypeNameSpec) spec;
+    if (!this.keyType.equalsDeep(that.keyType, litmus)) {
+      return litmus.fail("{} != {}", this, spec);
+    }
+    if (!this.valType.equalsDeep(that.valType, litmus)) {
+      return litmus.fail("{} != {}", this, spec);
+    }
+    return litmus.succeed();
+  }
+}
diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java 
b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
index 8f0d1c002f..4303a610d6 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -7130,6 +7130,26 @@ public class SqlValidatorTest extends 
SqlValidatorTestCase {
         .fails("Unknown identifier 'MYUDT'");
   }
 
+  /**
+   * Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-5570";>[CALCITE-5570]
+   * Support nested map type for SqlDataTypeSpec</a>.
+   */
+  @Test void testCastMapType() {
+    sql("select cast(\"int2IntMapType\" as map<int,int>) from 
COMPLEXTYPES.CTC_T1")
+        .withExtendedCatalog()
+        .columnType("(INTEGER NOT NULL, INTEGER NOT NULL) MAP NOT NULL");
+    sql("select cast(\"int2varcharArrayMapType\" as map<int,varchar array>) "
+        + "from COMPLEXTYPES.CTC_T1")
+        .withExtendedCatalog()
+        .columnType("(INTEGER NOT NULL, VARCHAR NOT NULL ARRAY NOT NULL) MAP 
NOT NULL");
+    sql("select cast(\"varcharMultiset2IntIntMapType\" as map<varchar(5) 
multiset, map<int, int>>)"
+        + " from COMPLEXTYPES.CTC_T1")
+        .withExtendedCatalog()
+        .columnType("(VARCHAR(5) NOT NULL MULTISET NOT NULL, "
+            + "(INTEGER NOT NULL, INTEGER NOT NULL) MAP NOT NULL) MAP NOT 
NULL");
+  }
+
   @Test void testCastAsRowType() {
     sql("select cast(a as row(f0 int, f1 varchar)) from COMPLEXTYPES.CTC_T1")
         .withExtendedCatalog()
diff --git a/core/src/test/resources/sql/misc.iq 
b/core/src/test/resources/sql/misc.iq
index 9876587d19..7e10550455 100644
--- a/core/src/test/resources/sql/misc.iq
+++ b/core/src/test/resources/sql/misc.iq
@@ -2583,4 +2583,23 @@ select decimal'12.3' + 5.6;
 
 !ok
 
+# [CALCITE-5570] Support nested map type for SqlDataTypeSpec
+SELECT
+  cast(x as map<int, int>),
+  cast(y as map<int, varchar array>),
+  cast(z as map<varchar  multiset, map<int, int>>)
+FROM (
+  SELECT
+    map[1, 2, 3, 4] as x,
+    map[1, array['a', 'b'], 2, array['c', 'd']] as y,
+    map[multiset['A'], map[1,2], multiset['B'], map[3, 4]] as z
+);
++------------+----------------------+------------------------+
+| EXPR$0     | EXPR$1               | EXPR$2                 |
++------------+----------------------+------------------------+
+| {1=2, 3=4} | {1=[a, b], 2=[c, d]} | {[A]={1=2}, [B]={3=4}} |
++------------+----------------------+------------------------+
+(1 row)
+
+!ok
 # End misc.iq
diff --git 
a/testkit/src/main/java/org/apache/calcite/sql/parser/SqlParserTest.java 
b/testkit/src/main/java/org/apache/calcite/sql/parser/SqlParserTest.java
index d12b7394c2..2086a30002 100644
--- a/testkit/src/main/java/org/apache/calcite/sql/parser/SqlParserTest.java
+++ b/testkit/src/main/java/org/apache/calcite/sql/parser/SqlParserTest.java
@@ -6123,6 +6123,20 @@ public class SqlParserTest {
         .ok("CAST(`A` AS ROW(`F0` VARCHAR, `F1` TIMESTAMP NULL) MULTISET)");
   }
 
+  /**
+   * Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-5570";>[CALCITE-5570]
+   * Support nested map type for SqlDataTypeSpec</a>.
+   */
+  @Test void testCastAsMapType() {
+    expr("cast(a as map<int, int>)")
+        .ok("CAST(`A` AS MAP< INTEGER, INTEGER >)");
+    expr("cast(a as map<int, varchar array>)")
+        .ok("CAST(`A` AS MAP< INTEGER, VARCHAR ARRAY >)");
+    expr("cast(a as map<varchar multiset, map<int, int>>)")
+        .ok("CAST(`A` AS MAP< VARCHAR MULTISET, MAP< INTEGER, INTEGER > >)");
+  }
+
   @Test void testMapValueConstructor() {
     expr("map[1, 'x', 2, 'y']")
         .ok("(MAP[1, 'x', 2, 'y'])");
diff --git a/testkit/src/main/java/org/apache/calcite/test/catalog/Fixture.java 
b/testkit/src/main/java/org/apache/calcite/test/catalog/Fixture.java
index d0df596d2d..be6d20179f 100644
--- a/testkit/src/main/java/org/apache/calcite/test/catalog/Fixture.java
+++ b/testkit/src/main/java/org/apache/calcite/test/catalog/Fixture.java
@@ -149,6 +149,12 @@ final class Fixture extends AbstractFixture {
                   Arrays.asList(intArrayMultisetType, varchar5ArrayType),
                   Arrays.asList("f0", "f1"))),
           -1);
+  final RelDataType int2IntMapType =
+      typeFactory.createMapType(intType, intType);
+  final RelDataType int2varcharArrayMapType =
+      typeFactory.createMapType(intType, array(varcharType));
+  final RelDataType varcharMultiset2IntIntMapType =
+      typeFactory.createMapType(varchar5MultisetType, int2IntMapType);
 
   Fixture(RelDataTypeFactory typeFactory) {
     super(typeFactory);
diff --git 
a/testkit/src/main/java/org/apache/calcite/test/catalog/MockCatalogReaderExtended.java
 
b/testkit/src/main/java/org/apache/calcite/test/catalog/MockCatalogReaderExtended.java
index c4a4eada2e..3a70e306ae 100644
--- 
a/testkit/src/main/java/org/apache/calcite/test/catalog/MockCatalogReaderExtended.java
+++ 
b/testkit/src/main/java/org/apache/calcite/test/catalog/MockCatalogReaderExtended.java
@@ -225,6 +225,11 @@ public class MockCatalogReaderExtended extends 
MockCatalogReaderSimple {
         f.varchar5MultisetArrayType);
     complexTypeColumnsTable.addColumn("intArrayMultisetType", 
f.intArrayMultisetType);
     complexTypeColumnsTable.addColumn("rowArrayMultisetType", 
f.rowArrayMultisetType);
+    complexTypeColumnsTable.addColumn("int2IntMapType", f.int2IntMapType);
+    complexTypeColumnsTable.addColumn("int2varcharArrayMapType",
+        f.int2varcharArrayMapType);
+    complexTypeColumnsTable.addColumn("varcharMultiset2IntIntMapType",
+        f.varcharMultiset2IntIntMapType);
     registerTable(complexTypeColumnsTable);
 
     MockSchema nullableRowsSchema = new MockSchema("NULLABLEROWS");

Reply via email to