This is an automated email from the ASF dual-hosted git repository.
danny0405 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/master by this push:
new 5212d6c [CALCITE-3233] Support Row type for SqlDataTypeSpec
5212d6c is described below
commit 5212d6c47e36995943f4d955a1714bf03eb08e7e
Author: yuzhao.cyz <[email protected]>
AuthorDate: Fri Aug 9 14:45:07 2019 +0800
[CALCITE-3233] Support Row type for SqlDataTypeSpec
---
core/src/main/codegen/config.fmpp | 2 +
core/src/main/codegen/templates/Parser.jj | 86 +++++++++++++++-
.../org/apache/calcite/sql/SqlDataTypeSpec.java | 72 +++++++++-----
.../org/apache/calcite/sql/SqlRowTypeSpec.java | 109 +++++++++++++++++++++
.../org/apache/calcite/sql/SqlTypeNameSpec.java | 44 +++++++++
.../apache/calcite/sql/parser/SqlParserTest.java | 19 ++++
.../org/apache/calcite/test/SqlValidatorTest.java | 31 ++++++
.../org/apache/calcite/test/catalog/Fixture.java | 50 ++++++++++
.../test/catalog/MockCatalogReaderExtended.java | 13 +++
server/src/main/codegen/includes/parserImpls.ftl | 16 +--
site/_docs/reference.md | 78 +++++++++++++--
11 files changed, 474 insertions(+), 46 deletions(-)
diff --git a/core/src/main/codegen/config.fmpp
b/core/src/main/codegen/config.fmpp
index 8fc9cce..4708633 100644
--- a/core/src/main/codegen/config.fmpp
+++ b/core/src/main/codegen/config.fmpp
@@ -374,6 +374,8 @@ data: {
# List of methods for parsing custom data types.
# Return type of method implementation should be "SqlIdentifier".
+ # Return "SqlTypeNameSpec" if you want to customize sql unparsing
+ # and data type deriving.
# Example: SqlParseTimeStampZ().
dataTypeParserMethods: [
]
diff --git a/core/src/main/codegen/templates/Parser.jj
b/core/src/main/codegen/templates/Parser.jj
index 6c19c34..44fffed 100644
--- a/core/src/main/codegen/templates/Parser.jj
+++ b/core/src/main/codegen/templates/Parser.jj
@@ -82,6 +82,7 @@ import org.apache.calcite.sql.SqlOperator;
import org.apache.calcite.sql.SqlOrderBy;
import org.apache.calcite.sql.SqlPostfixOperator;
import org.apache.calcite.sql.SqlPrefixOperator;
+import org.apache.calcite.sql.SqlRowTypeSpec;
import org.apache.calcite.sql.SqlSampleSpec;
import org.apache.calcite.sql.SqlSelect;
import org.apache.calcite.sql.SqlSelectKeyword;
@@ -89,6 +90,7 @@ import org.apache.calcite.sql.SqlSetOption;
import org.apache.calcite.sql.SqlSnapshot;
import org.apache.calcite.sql.SqlTimeLiteral;
import org.apache.calcite.sql.SqlTimestampLiteral;
+import org.apache.calcite.sql.SqlTypeNameSpec;
import org.apache.calcite.sql.SqlUnnestOperator;
import org.apache.calcite.sql.SqlUpdate;
import org.apache.calcite.sql.SqlUtil;
@@ -4591,13 +4593,13 @@ SqlDataTypeSpec DataType() :
SqlIdentifier TypeName() :
{
final SqlTypeName sqlTypeName;
- final SqlIdentifier typeName;
+ final SqlIdentifier typeName; // typeName can be a SqlIdentifier or
SqlTypeNameSpec
final Span s = Span.of();
}
{
(
<#-- additional types are included here -->
-<#-- make custom data types in front of Calcite core data types -->
+<#-- put custom data types in front of Calcite core data types -->
<#list parser.dataTypeParserMethods as method>
LOOKAHEAD(2)
typeName = ${method}
@@ -4611,6 +4613,8 @@ SqlIdentifier TypeName() :
LOOKAHEAD(2)
typeName = CollectionsTypeName()
|
+ typeName = RowTypeName()
+ |
typeName = CompoundIdentifier()
)
{
@@ -4739,6 +4743,84 @@ SqlIdentifier CollectionsTypeName() :
}
/**
+* Parse a nullable option, default is true.
+*/
+boolean NullableOptDefaultTrue() :
+{
+}
+{
+ <NULL> { return true; }
+|
+ <NOT> <NULL> { return false; }
+|
+ { return true; }
+}
+
+/**
+* Parse a nullable option, default is false.
+*/
+boolean NullableOptDefaultFalse() :
+{
+}
+{
+ <NULL> { return true; }
+|
+ <NOT> <NULL> { return false; }
+|
+ { return false; }
+}
+
+/**
+* Parse a "name1 type1 [NULL | NOT NULL], name2 type2 [NULL | NOT NULL] ..."
list,
+* the field type default is not nullable.
+*/
+void FieldNameTypeCommaList(
+ List<SqlIdentifier> fieldNames,
+ List<SqlDataTypeSpec> fieldTypes) :
+{
+ SqlIdentifier fName;
+ SqlDataTypeSpec fType;
+ boolean nullable;
+}
+{
+ fName = SimpleIdentifier()
+ fType = DataType()
+ nullable = NullableOptDefaultFalse()
+ {
+ fieldNames.add(fName);
+ fieldTypes.add(fType.withNullable(nullable));
+ }
+ (
+ <COMMA>
+ fName = SimpleIdentifier()
+ fType = DataType()
+ nullable = NullableOptDefaultFalse()
+ {
+ fieldNames.add(fName);
+ fieldTypes.add(fType.withNullable(nullable));
+ }
+ )*
+}
+
+/**
+* Parse Row type with format: Row(name1 type1, name2 type2).
+* Every item type can have suffix of `NULL` or `NOT NULL` to indicate if this
type is nullable.
+* i.e. Row(f0 int not null, f1 varchar null).
+*/
+SqlIdentifier RowTypeName() :
+{
+ List<SqlIdentifier> fieldNames = new ArrayList<SqlIdentifier>();
+ List<SqlDataTypeSpec> fieldTypes = new ArrayList<SqlDataTypeSpec>();
+}
+{
+ <ROW>
+ <LPAREN> FieldNameTypeCommaList(fieldNames, fieldTypes) <RPAREN>
+ {
+ return new SqlRowTypeSpec(getPos(), fieldNames, fieldTypes);
+ }
+}
+
+/**
* Parses a CURSOR(query) expression. The parser allows these
* anywhere, but the validator restricts them to appear only as
* arguments to table functions.
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlDataTypeSpec.java
b/core/src/main/java/org/apache/calcite/sql/SqlDataTypeSpec.java
index 0f4f70e..ff21221 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlDataTypeSpec.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlDataTypeSpec.java
@@ -42,15 +42,19 @@ import static org.apache.calcite.util.Static.RESOURCE;
*
* <p>todo: This should really be a subtype of {@link SqlCall}.</p>
*
- * <p>In its full glory, we will have to support complex type expressions
+ * <p>we support complex type expressions
* like:</p>
*
* <blockquote><code>ROW(<br>
- * NUMBER(5, 2) NOT NULL AS foo,<br>
- * ROW(BOOLEAN AS b, MyUDT NOT NULL AS i) AS rec)</code></blockquote>
+ * foo NUMBER(5, 2) NOT NULL,<br>
+ * rec ROW(b BOOLEAN, i MyUDT NOT NULL))</code></blockquote>
*
- * <p>Currently it only supports simple datatypes like CHAR, VARCHAR and
DOUBLE,
+ * <p>Internally we use {@link SqlTypeNameSpec} to specify such complex data
types.
+ *
+ * <p>We support simple data types like CHAR, VARCHAR and DOUBLE,
* with optional precision and scale.</p>
+ *
+ * <p>Internally we use {@link SqlIdentifier} to specify simple data types.
*/
public class SqlDataTypeSpec extends SqlNode {
//~ Instance fields --------------------------------------------------------
@@ -212,7 +216,12 @@ public class SqlDataTypeSpec extends SqlNode {
int leftPrec,
int rightPrec) {
String name = typeName.getSimple();
- if (SqlTypeName.get(name) != null) {
+ if (typeName instanceof SqlTypeNameSpec) {
+ typeName.unparse(writer, leftPrec, rightPrec);
+ if (collectionsTypeName != null) {
+ writer.keyword(collectionsTypeName.getSimple());
+ }
+ } else if (SqlTypeName.get(name) != null) {
SqlTypeName sqlTypeName = SqlTypeName.get(name);
// we have a built-in data type
@@ -327,25 +336,33 @@ public class SqlDataTypeSpec extends SqlNode {
if (!typeName.isSimple()) {
return null;
}
- final String name = typeName.getSimple();
- final SqlTypeName sqlTypeName = SqlTypeName.get(name);
- if (sqlTypeName == null) {
- return null;
- }
-
- // NOTE jvs 15-Jan-2009: earlier validation is supposed to
- // have caught these, which is why it's OK for them
- // to be assertions rather than user-level exceptions.
RelDataType type;
- if ((precision >= 0) && (scale >= 0)) {
- assert sqlTypeName.allowsPrecScale(true, true);
- type = typeFactory.createSqlType(sqlTypeName, precision, scale);
- } else if (precision >= 0) {
- assert sqlTypeName.allowsPrecNoScale();
- type = typeFactory.createSqlType(sqlTypeName, precision);
+ if (typeName instanceof SqlTypeNameSpec) {
+ // Create type directly if this typeName is a SqlTypeNameSpec.
+ type = createTypeFromTypeNameSpec(typeFactory, (SqlTypeNameSpec)
typeName);
+ if (type == null) {
+ return null;
+ }
} else {
- assert sqlTypeName.allowsNoPrecNoScale();
- type = typeFactory.createSqlType(sqlTypeName);
+ final String name = typeName.getSimple();
+ final SqlTypeName sqlTypeName = SqlTypeName.get(name);
+ if (sqlTypeName == null) {
+ return null;
+ }
+
+ // NOTE jvs 15-Jan-2009: earlier validation is supposed to
+ // have caught these, which is why it's OK for them
+ // to be assertions rather than user-level exceptions.
+ if ((precision >= 0) && (scale >= 0)) {
+ assert sqlTypeName.allowsPrecScale(true, true);
+ type = typeFactory.createSqlType(sqlTypeName, precision, scale);
+ } else if (precision >= 0) {
+ assert sqlTypeName.allowsPrecNoScale();
+ type = typeFactory.createSqlType(sqlTypeName, precision);
+ } else {
+ assert sqlTypeName.allowsNoPrecNoScale();
+ type = typeFactory.createSqlType(sqlTypeName);
+ }
}
if (SqlTypeUtil.inCharFamily(type)) {
@@ -399,6 +416,17 @@ public class SqlDataTypeSpec extends SqlNode {
return type;
}
+
+ /**
+ * Create type from the type name specification directly.
+ * @param typeFactory type factory.
+ * @return the type.
+ */
+ private RelDataType createTypeFromTypeNameSpec(
+ RelDataTypeFactory typeFactory,
+ SqlTypeNameSpec typeNameSpec) {
+ return typeNameSpec.deriveType(typeFactory);
+ }
}
// End SqlDataTypeSpec.java
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlRowTypeSpec.java
b/core/src/main/java/org/apache/calcite/sql/SqlRowTypeSpec.java
new file mode 100644
index 0000000..e75166c
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/sql/SqlRowTypeSpec.java
@@ -0,0 +1,109 @@
+/*
+ * 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.rel.type.RelDataTypeFactory;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.util.Pair;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * A sql type specification of row type, the grammar definition in SQL-2011
IWD 9075-2:201?(E)
+ * 6.1 <data type> is as following:
+ * <blockquote><pre>
+ * <row type> ::=
+ * ROW <row type body>
+ * <row type body> ::=
+ * <left paren> <field definition>
+ * [ { <comma> <field definition> }... ]
+ * <right paren>
+ *
+ * <field definition> ::=
+ * <field name> <data type>
+ * </pre></blockquote>
+ *
+ * <p>We also support to add a [ NULL | NOT NULL ] suffix for every field
type, i.e.
+ * Row(f0 int null, f1 varchar not null), the default is not nullable.
+ */
+public class SqlRowTypeSpec extends SqlTypeNameSpec {
+
+ private final List<SqlIdentifier> fieldNames;
+ private final List<SqlDataTypeSpec> fieldTypes;
+
+ /**
+ * Creates a row type specification.
+ *
+ * @param pos The parser position.
+ * @param fieldNames The field names.
+ * @param fieldTypes The field data types.
+ */
+ public SqlRowTypeSpec(
+ SqlParserPos pos,
+ List<SqlIdentifier> fieldNames,
+ List<SqlDataTypeSpec> fieldTypes) {
+ super(SqlTypeName.ROW.getName(), pos);
+ Objects.requireNonNull(fieldNames);
+ Objects.requireNonNull(fieldTypes);
+ assert fieldNames.size() > 0; // there must be at least one field.
+ this.fieldNames = fieldNames;
+ this.fieldTypes = fieldTypes;
+ }
+
+ public List<SqlIdentifier> getFieldNames() {
+ return fieldNames;
+ }
+
+ public List<SqlDataTypeSpec> getFieldTypes() {
+ return fieldTypes;
+ }
+
+ public int getArity() {
+ return fieldNames.size();
+ }
+
+ @Override public void unparse(SqlWriter writer, int leftPrec, int rightPrec)
{
+ writer.print(SqlTypeName.ROW.getName());
+ SqlWriter.Frame frame = writer.startList(SqlWriter.FrameTypeEnum.FUN_CALL,
"(", ")");
+ for (Pair<SqlIdentifier, SqlDataTypeSpec> p : Pair.zip(this.fieldNames,
this.fieldTypes)) {
+ writer.sep(",", false);
+ p.left.unparse(writer, 0, 0);
+ p.right.unparse(writer, leftPrec, rightPrec);
+ if (p.right.getNullable() != null && p.right.getNullable()) {
+ // Row fields default is not nullable.
+ writer.print("NULL");
+ }
+ }
+ writer.endList(frame);
+ }
+
+ @Override public RelDataType deriveType(RelDataTypeFactory typeFactory) {
+ return typeFactory.createStructType(
+ fieldTypes.stream()
+ .map(dt -> dt.deriveType(typeFactory))
+ .collect(Collectors.toList()),
+ fieldNames.stream()
+ .map(SqlIdentifier::toString)
+ .collect(Collectors.toList()));
+ }
+}
+
+// End SqlRowTypeSpec.java
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlTypeNameSpec.java
b/core/src/main/java/org/apache/calcite/sql/SqlTypeNameSpec.java
new file mode 100644
index 0000000..18a4b90
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/sql/SqlTypeNameSpec.java
@@ -0,0 +1,44 @@
+/*
+ * 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.rel.type.RelDataTypeFactory;
+import org.apache.calcite.sql.parser.SqlParserPos;
+
+/**
+ * A <code>SqlTypeNameSpec</code> is a type name that allows user to
+ * customize sql node unparsing and data type deriving.
+ *
+ * <p>To customize sql node unparsing, override the method {@link
#unparse(SqlWriter, int, int)}.
+ */
+public abstract class SqlTypeNameSpec extends SqlIdentifier {
+
+ /**
+ * Creates a {@code SqlTypeNameSpec}.
+ *
+ * @param name Name of the type.
+ * @param pos Parser position, must not be null.
+ */
+ SqlTypeNameSpec(String name, SqlParserPos pos) {
+ super(name, pos);
+ }
+
+ public abstract RelDataType deriveType(RelDataTypeFactory typeFactory);
+}
+
+// End SqlTypeNameSpec.java
diff --git
a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
index 3070da0..f45d2fe 100644
--- a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
@@ -4710,6 +4710,25 @@ public class SqlParserTest {
"(?s).*Encountered \"<\" at line 1, column 20.\n.*");
}
+ @Test public void testCastAsRowType() {
+ checkExp("cast(a as row(f0 int, f1 varchar))",
+ "CAST(`A` AS ROW(`F0` INTEGER, `F1` VARCHAR))");
+ checkExp("cast(a as row(f0 int not null, f1 varchar null))",
+ "CAST(`A` AS ROW(`F0` INTEGER, `F1` VARCHAR NULL))");
+// // test nested row type.
+ checkExp("cast(a as row("
+ + "f0 row(ff0 int not null, ff1 varchar null) null, "
+ + "f1 timestamp not null))",
+ "CAST(`A` AS ROW("
+ + "`F0` ROW(`FF0` INTEGER, `FF1` VARCHAR NULL) NULL, "
+ + "`F1` TIMESTAMP))");
+ // test row type in collection data types.
+ checkExp("cast(a as row(f0 bigint not null, f1 decimal null) array)",
+ "CAST(`A` AS ROW(`F0` BIGINT, `F1` DECIMAL NULL) ARRAY)");
+ checkExp("cast(a as row(f0 varchar not null, f1 timestamp null) multiset)",
+ "CAST(`A` AS ROW(`F0` VARCHAR, `F1` TIMESTAMP NULL) MULTISET)");
+ }
+
@Test public void testMapValueConstructor() {
checkExp("map[1, 'x', 2, 'y']", "(MAP[1, 'x', 2, 'y'])");
checkExp("map [1, 'x', 2, 'y']", "(MAP[1, 'x', 2, 'y'])");
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 d92e78b..6c7921f 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -7710,6 +7710,37 @@ public class SqlValidatorTest extends
SqlValidatorTestCase {
.columnType("VARCHAR(5) NOT NULL ARRAY NOT NULL");
}
+ @Test public void testCastAsRowType() {
+ sql("select cast(a as row(f0 int, f1 varchar)) from COMPLEXTYPES.CTC_T1")
+ .withExtendedCatalog()
+ .columnType("RecordType(INTEGER NOT NULL F0, VARCHAR NOT NULL F1) NOT
NULL");
+ sql("select cast(b as row(f0 int not null, f1 varchar null))\n"
+ + "from COMPLEXTYPES.CTC_T1")
+ .withExtendedCatalog()
+ .columnType("RecordType(INTEGER NOT NULL F0, VARCHAR F1) NOT NULL");
+ // test nested row type.
+ sql("select "
+ + "cast(c as row("
+ + "f0 row(ff0 int not null, ff1 varchar null) null, "
+ + "f1 timestamp not null))"
+ + " from COMPLEXTYPES.CTC_T1")
+ .withExtendedCatalog()
+ .columnType("RecordType("
+ + "RecordType(INTEGER FF0, VARCHAR FF1) F0, "
+ + "TIMESTAMP(0) NOT NULL F1) NOT NULL");
+ // test row type in collection data types.
+ sql("select cast(d as row(f0 bigint not null, f1 decimal null) array)\n"
+ + "from COMPLEXTYPES.CTC_T1")
+ .withExtendedCatalog()
+ .columnType("RecordType(BIGINT NOT NULL F0, DECIMAL(19, 0) F1) NOT
NULL "
+ + "ARRAY NOT NULL");
+ sql("select cast(e as row(f0 varchar not null, f1 timestamp null)
multiset)\n"
+ + "from COMPLEXTYPES.CTC_T1")
+ .withExtendedCatalog()
+ .columnType("RecordType(VARCHAR NOT NULL F0, TIMESTAMP(0) F1) NOT NULL
"
+ + "MULTISET NOT NULL");
+ }
+
@Test public void testMultisetConstructor() {
sql("select multiset[1,null,2] as a from (values (1))")
.columnType("INTEGER MULTISET NOT NULL");
diff --git a/core/src/test/java/org/apache/calcite/test/catalog/Fixture.java
b/core/src/test/java/org/apache/calcite/test/catalog/Fixture.java
index 77451bc..45471bf 100644
--- a/core/src/test/java/org/apache/calcite/test/catalog/Fixture.java
+++ b/core/src/test/java/org/apache/calcite/test/catalog/Fixture.java
@@ -19,6 +19,7 @@ package org.apache.calcite.test.catalog;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeComparability;
import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rel.type.RelDataTypeField;
import org.apache.calcite.rel.type.RelDataTypeFieldImpl;
import org.apache.calcite.rel.type.StructKind;
import org.apache.calcite.sql.SqlIdentifier;
@@ -27,6 +28,7 @@ import org.apache.calcite.sql.type.ObjectSqlType;
import org.apache.calcite.sql.type.SqlTypeName;
import java.util.Arrays;
+import java.util.stream.Collectors;
/** Types used during initialization. */
final class Fixture {
@@ -34,6 +36,8 @@ final class Fixture {
final RelDataType intTypeNull;
final RelDataType bigintType;
final RelDataType bigintTypeNull;
+ final RelDataType decimalType;
+ final RelDataType varcharType;
final RelDataType varchar10Type;
final RelDataType varchar10TypeNull;
final RelDataType varchar20Type;
@@ -51,12 +55,24 @@ final class Fixture {
final RelDataType empRecordType;
final RelDataType empListType;
final ObjectSqlType addressType;
+ // Row(f0 int, f1 varchar)
+ final RelDataType recordType1;
+ // Row(f0 int not null, f1 varchar null)
+ final RelDataType recordType2;
+ // Row(f0 Row(ff0 int not null, ff1 varchar null) null, f1 timestamp not
null)
+ final RelDataType recordType3;
+ // Row(f0 bigint not null, f1 decimal null) array
+ final RelDataType recordType4;
+ // Row(f0 varchar not null, f1 timestamp null) multiset
+ final RelDataType recordType5;
Fixture(RelDataTypeFactory typeFactory) {
intType = typeFactory.createSqlType(SqlTypeName.INTEGER);
intTypeNull = typeFactory.createTypeWithNullability(intType, true);
bigintType = typeFactory.createSqlType(SqlTypeName.BIGINT);
bigintTypeNull = typeFactory.createTypeWithNullability(bigintType, true);
+ decimalType = typeFactory.createSqlType(SqlTypeName.DECIMAL);
+ varcharType = typeFactory.createSqlType(SqlTypeName.VARCHAR);
varchar10Type = typeFactory.createSqlType(SqlTypeName.VARCHAR, 10);
varchar10TypeNull = typeFactory.createTypeWithNullability(varchar10Type,
true);
varchar20Type = typeFactory.createSqlType(SqlTypeName.VARCHAR, 20);
@@ -124,6 +140,40 @@ final class Fixture {
new RelDataTypeFieldImpl("ZIP", 2, intType),
new RelDataTypeFieldImpl("STATE", 3, varchar20Type)),
RelDataTypeComparability.NONE);
+ // Row(f0 int, f1 varchar)
+ recordType1 = typeFactory.createStructType(
+ Arrays.asList(intType, varcharType),
+ Arrays.asList("f0", "f1"));
+ // Row(f0 int not null, f1 varchar null)
+ recordType2 = typeFactory.createStructType(
+ Arrays.asList(intType,
+ nullable(typeFactory, varcharType)),
+ Arrays.asList("f0", "f1"));
+ // Row(f0 Row(ff0 int not null, ff1 varchar null) null, f1 timestamp not
null)
+ recordType3 = typeFactory.createStructType(
+ Arrays.asList(
+ nullable(typeFactory,
+ typeFactory.createStructType(
+ recordType2.getFieldList().stream()
+
.map(RelDataTypeField::getType).collect(Collectors.toList()),
+ Arrays.asList("ff0", "ff1"))), timestampType),
+ Arrays.asList("f0", "f1"));
+ // Row(f0 bigint not null, f1 decimal null) array
+ recordType4 = typeFactory.createArrayType(
+ typeFactory.createStructType(
+ Arrays.asList(bigintType, nullable(typeFactory, decimalType)),
+ Arrays.asList("f0", "f1")),
+ -1);
+ // Row(f0 varchar not null, f1 timestamp null) multiset
+ recordType5 = typeFactory.createMultisetType(
+ typeFactory.createStructType(
+ Arrays.asList(varcharType, nullable(typeFactory, timestampType)),
+ Arrays.asList("f0", "f1")),
+ -1);
+ }
+
+ private static RelDataType nullable(RelDataTypeFactory typeFactory,
RelDataType type) {
+ return typeFactory.createTypeWithNullability(type, true);
}
}
diff --git
a/core/src/test/java/org/apache/calcite/test/catalog/MockCatalogReaderExtended.java
b/core/src/test/java/org/apache/calcite/test/catalog/MockCatalogReaderExtended.java
index 4311369..1d3f249 100644
---
a/core/src/test/java/org/apache/calcite/test/catalog/MockCatalogReaderExtended.java
+++
b/core/src/test/java/org/apache/calcite/test/catalog/MockCatalogReaderExtended.java
@@ -137,6 +137,19 @@ public class MockCatalogReaderExtended extends
MockCatalogReaderSimple {
registerTable(virtualColumnsTable1);
registerTable(virtualColumnsTable2);
+ // Register table with complex data type rows.
+ MockSchema complexTypeColumnsSchema = new MockSchema("COMPLEXTYPES");
+ registerSchema(complexTypeColumnsSchema);
+ final MockTable complexTypeColumnsTable =
+ MockTable.create(this, complexTypeColumnsSchema, "CTC_T1",
+ false, 100);
+ complexTypeColumnsTable.addColumn("A", f.recordType1);
+ complexTypeColumnsTable.addColumn("B", f.recordType2);
+ complexTypeColumnsTable.addColumn("C", f.recordType3);
+ complexTypeColumnsTable.addColumn("D", f.recordType4);
+ complexTypeColumnsTable.addColumn("E", f.recordType5);
+ registerTable(complexTypeColumnsTable);
+
return this;
}
}
diff --git a/server/src/main/codegen/includes/parserImpls.ftl
b/server/src/main/codegen/includes/parserImpls.ftl
index e135152..6fa9c3b 100644
--- a/server/src/main/codegen/includes/parserImpls.ftl
+++ b/server/src/main/codegen/includes/parserImpls.ftl
@@ -131,13 +131,7 @@ void TableElement(List<SqlNode> list) :
LOOKAHEAD(2) id = SimpleIdentifier()
(
type = DataType()
- (
- <NULL> { nullable = true; }
- |
- <NOT> <NULL> { nullable = false; }
- |
- { nullable = true; }
- )
+ nullable = NullableOptDefaultTrue()
(
[ <GENERATED> <ALWAYS> ] <AS> <LPAREN>
e = Expression(ExprContext.ACCEPT_SUB_QUERY) <RPAREN>
@@ -219,13 +213,7 @@ void AttributeDef(List<SqlNode> list) :
id = SimpleIdentifier()
(
type = DataType()
- (
- <NULL> { nullable = true; }
- |
- <NOT> <NULL> { nullable = false; }
- |
- { nullable = true; }
- )
+ nullable = NullableOptDefaultTrue()
)
[ <DEFAULT_> e = Expression(ExprContext.ACCEPT_SUB_QUERY) ]
{
diff --git a/site/_docs/reference.md b/site/_docs/reference.md
index d6f8a31..6df7bd0 100644
--- a/site/_docs/reference.md
+++ b/site/_docs/reference.md
@@ -1064,14 +1064,19 @@ Note:
### Non-scalar types
-| Type | Description
-|:-------- |:-----------------------------------------------------------
-| ANY | A value of an unknown type
-| ROW | Row with 1 or more columns
-| MAP | Collection of keys mapped to values
-| MULTISET | Unordered collection that may contain duplicates
-| ARRAY | Ordered, contiguous collection that may contain duplicates
-| CURSOR | Cursor over the result of executing a query
+| Type | Description | Example literals
+|:-------- |:---------------------------|:---------------
+| ANY | A value of an unknown type |
+| ROW | Row with 1 or more columns | Example: Row(f0 int null, f1 varchar)
+| MAP | Collection of keys mapped to values |
+| MULTISET | Unordered collection that may contain duplicates | Example: int
multiset
+| ARRAY | Ordered, contiguous collection that may contain duplicates |
Example: varchar(10) array
+| CURSOR | Cursor over the result of executing a query |
+
+Note:
+
+* Every `ROW` column type can have an optional [ NULL | NOT NULL ] suffix
+ to indicate if this column type is nullable, default is not nullable.
### Spatial types
@@ -1311,6 +1316,63 @@ Not implemented:
|:--------------- | :----------
| CAST(value AS type) | Converts a value to a given type.
+Supported data types:
+
+{% highlight sql %}
+type:
+ typeName [ '(' precision [, scale] ')' ]
+ [ CHARACTER SET charSetName ]
+ [ collectionsTypeName ]
+
+typeName:
+ sqlTypeName
+ | collectionsTypeName
+ | rowTypeName
+ | compoundIdentifier
+
+sqlTypeName:
+ char
+ | varchar
+ | DATE
+ | TIME
+ | TIMESTAMP
+ | GEOMETRY
+ | decimal
+ | BOOLEAN
+ | integer
+ | BINARY
+ | varbinary
+ | TINYINT
+ | SMALLINT
+ | BIGINT
+ | REAL
+ | double
+ | FLOAT
+ | ANY
+
+collectionsTypeName:
+ ARRAY | MULTISET
+
+rowTypeName:
+ ROW '('
+ fieldName1 fieldType1 [ [ NULL | NOT NULL ] ]
+ [ , fieldName2 fieldType2 [ [ NULL | NOT NULL ] ] ]*
+ ')'
+
+char:
+ CHARACTER | CHAR
+varchar:
+ char VARYING | VARCHAR
+decimal:
+ DECIMAL | DEC | NUMERIC
+integer:
+ INTEGER | INT
+varbinary:
+ BINARY VARYING | VARBINARY
+double:
+ DOUBLE [PRECISION]
+{% endhighlight %}
+
### Value constructors
| Operator syntax | Description