This is an automated email from the ASF dual-hosted git repository. jhyde pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/calcite.git
commit 8daba770396193f91d967d0e649d42f1057e0d95 Author: dasch-1 <[email protected]> AuthorDate: Wed Apr 22 12:43:20 2020 -0700 [CALCITE-3946] Add parser support for MULTISET/SET and VOLATILE modifiers in CREATE TABLE statements (Drew Schmitt) The syntax for these statements is: CREATE TABLE [SET|MULTISET] [VOLATILE] <table_name> [IF NOT EXISTS] (<column_name> <data_type>, ...); Support is added by extending the Babel parser. Rename 'isVolatile' to 'volatile_' (the 'is' prefix is for methods, not fields; the '_' suffix is necessary because 'volatile' is a Java keyword) and and 'SetType' to 'TableCollectionType'. Close apache/calcite#1938 --- babel/src/main/codegen/config.fmpp | 7 ++ babel/src/main/codegen/includes/parserImpls.ftl | 98 ++++++++++++++++++++++ .../java/org/apache/calcite/sql/babel/Babel.java | 24 ------ .../calcite/sql/babel/SqlBabelCreateTable.java | 58 ++++++------- .../calcite/sql/babel/TableCollectionType.java | 48 +++++++++++ .../org/apache/calcite/test/BabelParserTest.java | 26 ++++++ .../org/apache/calcite/sql/ddl/SqlCreateTable.java | 2 +- 7 files changed, 210 insertions(+), 53 deletions(-) diff --git a/babel/src/main/codegen/config.fmpp b/babel/src/main/codegen/config.fmpp index 503bd1c..95a9307 100644 --- a/babel/src/main/codegen/config.fmpp +++ b/babel/src/main/codegen/config.fmpp @@ -21,11 +21,17 @@ data: { # List of import statements. imports: [ + "org.apache.calcite.sql.SqlCreate", + "org.apache.calcite.sql.babel.SqlBabelCreateTable", + "org.apache.calcite.sql.babel.TableCollectionType", + "org.apache.calcite.sql.ddl.SqlDdlNodes", ] # List of keywords. keywords: [ + "IF" "SEMI" + "VOLATILE" ] # List of keywords from "keywords" section that are not reserved. @@ -861,6 +867,7 @@ data: { # List of methods for parsing extensions to "CREATE [OR REPLACE]" calls. # Each must accept arguments "(SqlParserPos pos, boolean replace)". createStatementParserMethods: [ + "SqlCreateTable" ] # List of methods for parsing extensions to "DROP" calls. diff --git a/babel/src/main/codegen/includes/parserImpls.ftl b/babel/src/main/codegen/includes/parserImpls.ftl index 55ab4c9..d4a5bb3 100644 --- a/babel/src/main/codegen/includes/parserImpls.ftl +++ b/babel/src/main/codegen/includes/parserImpls.ftl @@ -69,6 +69,104 @@ SqlNode DateaddFunctionCall() : } } +boolean IfNotExistsOpt() : +{ +} +{ + <IF> <NOT> <EXISTS> { return true; } +| + { return false; } +} + +TableCollectionType TableCollectionTypeOpt() : +{ +} +{ + <MULTISET> { return TableCollectionType.MULTISET; } +| + <SET> { return TableCollectionType.SET; } +| + { return TableCollectionType.UNSPECIFIED; } +} + +boolean VolatileOpt() : +{ +} +{ + <VOLATILE> { return true; } +| + { return false; } +} + +SqlNodeList ExtendColumnList() : +{ + final Span s; + List<SqlNode> list = new ArrayList<SqlNode>(); +} +{ + <LPAREN> { s = span(); } + ColumnWithType(list) + ( + <COMMA> ColumnWithType(list) + )* + <RPAREN> { + return new SqlNodeList(list, s.end(this)); + } +} + +void ColumnWithType(List<SqlNode> list) : +{ + SqlIdentifier id; + SqlDataTypeSpec type; + boolean nullable = true; + final Span s = Span.of(); +} +{ + id = CompoundIdentifier() + type = DataType() + [ + <NOT> <NULL> { + nullable = false; + } + ] + { + list.add(SqlDdlNodes.column(s.add(id).end(this), id, + type.withNullable(nullable), null, null)); + } +} + +SqlCreate SqlCreateTable(Span s, boolean replace) : +{ + final TableCollectionType tableCollectionType; + final boolean volatile_; + final boolean ifNotExists; + final SqlIdentifier id; + final SqlNodeList columnList; + final SqlNode query; +} +{ + tableCollectionType = TableCollectionTypeOpt() + volatile_ = VolatileOpt() + <TABLE> + ifNotExists = IfNotExistsOpt() + id = CompoundIdentifier() + ( + columnList = ExtendColumnList() + | + { columnList = null; } + ) + ( + <AS> query = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY) + | + { query = null; } + ) + { + return new SqlBabelCreateTable(s.end(this), replace, + tableCollectionType, volatile_, ifNotExists, id, columnList, query); + } +} + + /* Extra operators */ <DEFAULT, DQID, BTID> TOKEN : diff --git a/babel/src/main/java/org/apache/calcite/sql/babel/Babel.java b/babel/src/main/java/org/apache/calcite/sql/babel/Babel.java deleted file mode 100644 index 5b1760a..0000000 --- a/babel/src/main/java/org/apache/calcite/sql/babel/Babel.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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.babel; - -/** SQL parser that accepts a wide variety of dialects. */ -@SuppressWarnings("unused") -public class Babel { - // This class is currently a place-holder. Javadoc gets upset - // if there are no classes in babel/java/main. -} diff --git a/core/src/main/java/org/apache/calcite/sql/ddl/SqlCreateTable.java b/babel/src/main/java/org/apache/calcite/sql/babel/SqlBabelCreateTable.java similarity index 59% copy from core/src/main/java/org/apache/calcite/sql/ddl/SqlCreateTable.java copy to babel/src/main/java/org/apache/calcite/sql/babel/SqlBabelCreateTable.java index 0773004..511bef3 100644 --- a/core/src/main/java/org/apache/calcite/sql/ddl/SqlCreateTable.java +++ b/babel/src/main/java/org/apache/calcite/sql/babel/SqlBabelCreateTable.java @@ -14,48 +14,50 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.calcite.sql.ddl; +package org.apache.calcite.sql.babel; -import org.apache.calcite.sql.SqlCreate; import org.apache.calcite.sql.SqlIdentifier; -import org.apache.calcite.sql.SqlKind; import org.apache.calcite.sql.SqlNode; import org.apache.calcite.sql.SqlNodeList; -import org.apache.calcite.sql.SqlOperator; -import org.apache.calcite.sql.SqlSpecialOperator; import org.apache.calcite.sql.SqlWriter; +import org.apache.calcite.sql.ddl.SqlCreateTable; import org.apache.calcite.sql.parser.SqlParserPos; -import org.apache.calcite.util.ImmutableNullableList; - -import java.util.List; -import java.util.Objects; /** - * Parse tree for {@code CREATE TABLE} statement. + * Parse tree for {@code CREATE TABLE} statement, with extensions for particular + * SQL dialects supported by Babel. */ -public class SqlCreateTable extends SqlCreate { - public final SqlIdentifier name; - public final SqlNodeList columnList; - public final SqlNode query; - - private static final SqlOperator OPERATOR = - new SqlSpecialOperator("CREATE TABLE", SqlKind.CREATE_TABLE); - - /** Creates a SqlCreateTable. */ - SqlCreateTable(SqlParserPos pos, boolean replace, boolean ifNotExists, - SqlIdentifier name, SqlNodeList columnList, SqlNode query) { - super(OPERATOR, pos, replace, ifNotExists); - this.name = Objects.requireNonNull(name); - this.columnList = columnList; // may be null - this.query = query; // for "CREATE TABLE ... AS query"; may be null - } +public class SqlBabelCreateTable extends SqlCreateTable { + private final TableCollectionType tableCollectionType; + // CHECKSTYLE: IGNORE 2; can't use 'volatile' because it is a Java keyword + // but checkstyle does not like trailing '_'. + private final boolean volatile_; - public List<SqlNode> getOperandList() { - return ImmutableNullableList.of(name, columnList, query); + /** Creates a SqlBabelCreateTable. */ + public SqlBabelCreateTable(SqlParserPos pos, boolean replace, + TableCollectionType tableCollectionType, boolean volatile_, + boolean ifNotExists, SqlIdentifier name, SqlNodeList columnList, + SqlNode query) { + super(pos, replace, ifNotExists, name, columnList, query); + this.tableCollectionType = tableCollectionType; + this.volatile_ = volatile_; } @Override public void unparse(SqlWriter writer, int leftPrec, int rightPrec) { writer.keyword("CREATE"); + switch (tableCollectionType) { + case SET: + writer.keyword("SET"); + break; + case MULTISET: + writer.keyword("MULTISET"); + break; + default: + break; + } + if (volatile_) { + writer.keyword("VOLATILE"); + } writer.keyword("TABLE"); if (ifNotExists) { writer.keyword("IF NOT EXISTS"); diff --git a/babel/src/main/java/org/apache/calcite/sql/babel/TableCollectionType.java b/babel/src/main/java/org/apache/calcite/sql/babel/TableCollectionType.java new file mode 100644 index 0000000..df8b761 --- /dev/null +++ b/babel/src/main/java/org/apache/calcite/sql/babel/TableCollectionType.java @@ -0,0 +1,48 @@ +/* + * 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.babel; + +/** + * Enumerates the collection type of a table: {@code MULTISET} allows duplicates + * and {@code SET} does not. + * + * <p>This feature is supported in Teradata, which originally required rows in a + * table to be unique, and later added the {@code MULTISET} keyword to + * its {@code CREATE TABLE} command to allow the duplicate rows. + * + * <p>In other databases and in the SQL standard, {@code MULTISET} is the only + * supported option, so there is no explicit syntax. + */ +public enum TableCollectionType { + /** + * Table collection type is not specified. + * + * <p>Defaults to {@code MULTISET} in ANSI mode, + * and {@code SET} in Teradata mode. + */ + UNSPECIFIED, + + /** + * Duplicate rows are not permitted. + */ + SET, + + /** + * Duplicate rows are permitted, in compliance with the ANSI SQL:2011 standard. + */ + MULTISET, +} diff --git a/babel/src/test/java/org/apache/calcite/test/BabelParserTest.java b/babel/src/test/java/org/apache/calcite/test/BabelParserTest.java index cbd8277..b5d185f 100644 --- a/babel/src/test/java/org/apache/calcite/test/BabelParserTest.java +++ b/babel/src/test/java/org/apache/calcite/test/BabelParserTest.java @@ -255,4 +255,30 @@ class BabelParserTest extends SqlParserTest { + "FROM (VALUES (ROW(1, 2))) AS `TBL` (`X`, `Y`)"; sql(sql).ok(expected); } + + @Test void testCreateTableWithNoCollectionTypeSpecified() { + final String sql = "create table foo (bar integer not null, baz varchar(30))"; + final String expected = "CREATE TABLE `FOO` (`BAR` INTEGER NOT NULL, `BAZ` VARCHAR(30))"; + sql(sql).ok(expected); + } + + @Test void testCreateSetTable() { + final String sql = "create set table foo (bar int not null, baz varchar(30))"; + final String expected = "CREATE SET TABLE `FOO` (`BAR` INTEGER NOT NULL, `BAZ` VARCHAR(30))"; + sql(sql).ok(expected); + } + + @Test void testCreateMultisetTable() { + final String sql = "create multiset table foo (bar int not null, baz varchar(30))"; + final String expected = "CREATE MULTISET TABLE `FOO` " + + "(`BAR` INTEGER NOT NULL, `BAZ` VARCHAR(30))"; + sql(sql).ok(expected); + } + + @Test void testCreateVolatileTable() { + final String sql = "create volatile table foo (bar int not null, baz varchar(30))"; + final String expected = "CREATE VOLATILE TABLE `FOO` " + + "(`BAR` INTEGER NOT NULL, `BAZ` VARCHAR(30))"; + sql(sql).ok(expected); + } } diff --git a/core/src/main/java/org/apache/calcite/sql/ddl/SqlCreateTable.java b/core/src/main/java/org/apache/calcite/sql/ddl/SqlCreateTable.java index 0773004..6e03318 100644 --- a/core/src/main/java/org/apache/calcite/sql/ddl/SqlCreateTable.java +++ b/core/src/main/java/org/apache/calcite/sql/ddl/SqlCreateTable.java @@ -42,7 +42,7 @@ public class SqlCreateTable extends SqlCreate { new SqlSpecialOperator("CREATE TABLE", SqlKind.CREATE_TABLE); /** Creates a SqlCreateTable. */ - SqlCreateTable(SqlParserPos pos, boolean replace, boolean ifNotExists, + protected SqlCreateTable(SqlParserPos pos, boolean replace, boolean ifNotExists, SqlIdentifier name, SqlNodeList columnList, SqlNode query) { super(OPERATOR, pos, replace, ifNotExists); this.name = Objects.requireNonNull(name);
