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<INT NOT NULL, TIMESTAMP NULL>, 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");