You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2020/06/01 17:16:53 UTC
[calcite] 04/06: [CALCITE-3946] Add parser support for MULTISET/SET
and VOLATILE modifiers in CREATE TABLE statements (Drew Schmitt)
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 <da...@google.com>
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);