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 fb6f43192c [CALCITE-5688] Support TRUNCATE TABLE DDL statement in server parser fb6f43192c is described below commit fb6f43192c4253caea63c0705067a9aa12a3fa74 Author: xiejiajun <jiajunbernou...@foxmail.com> AuthorDate: Sun Jun 18 10:10:51 2023 +0800 [CALCITE-5688] Support TRUNCATE TABLE DDL statement in server parser Co-authored-by: sumeetgajjar <sumeetgajja...@gmail.com> --- core/src/main/codegen/default_config.fmpp | 6 ++ core/src/main/codegen/templates/Parser.jj | 28 +++++++++ .../main/java/org/apache/calcite/sql/SqlKind.java | 5 +- .../java/org/apache/calcite/sql/SqlTruncate.java | 33 +++++++++++ .../org/apache/calcite/sql/ddl/SqlDdlNodes.java | 6 ++ .../apache/calcite/sql/ddl/SqlTruncateTable.java | 66 ++++++++++++++++++++++ server/src/main/codegen/config.fmpp | 8 +++ server/src/main/codegen/includes/parserImpls.ftl | 19 +++++++ .../apache/calcite/server/ServerDdlExecutor.java | 38 +++++++++++++ .../org/apache/calcite/test/ServerParserTest.java | 11 ++++ .../java/org/apache/calcite/test/ServerTest.java | 15 +++++ server/src/test/resources/sql/table.iq | 19 +++++++ 12 files changed, 253 insertions(+), 1 deletion(-) diff --git a/core/src/main/codegen/default_config.fmpp b/core/src/main/codegen/default_config.fmpp index 8c6fa48470..6a172a6ef7 100644 --- a/core/src/main/codegen/default_config.fmpp +++ b/core/src/main/codegen/default_config.fmpp @@ -424,6 +424,12 @@ parser: { dropStatementParserMethods: [ ] + # List of methods for parsing extensions to "TRUNCATE" calls. + # Each must accept arguments "(SqlParserPos pos)". + # Example: "SqlTruncate". + truncateStatementParserMethods: [ + ] + # Binary operators tokens. # Example: "< INFIX_CAST: \"::\" >". binaryOperatorsTokens: [ diff --git a/core/src/main/codegen/templates/Parser.jj b/core/src/main/codegen/templates/Parser.jj index 56e44c06e8..822f6a407b 100644 --- a/core/src/main/codegen/templates/Parser.jj +++ b/core/src/main/codegen/templates/Parser.jj @@ -1136,6 +1136,10 @@ SqlNode SqlStmt() : <#if (parser.dropStatementParserMethods!default.parser.dropStatementParserMethods)?size != 0> stmt = SqlDrop() | +</#if> +<#if (parser.truncateStatementParserMethods!default.parser.truncateStatementParserMethods)?size != 0> + stmt = SqlTruncate() + | </#if> stmt = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY) | @@ -4354,6 +4358,30 @@ SqlDrop SqlDrop() : } </#if> +<#if (parser.truncateStatementParserMethods!default.parser.truncateStatementParserMethods)?size != 0> +/** + * Parses a TRUNCATE statement. + */ +SqlTruncate SqlTruncate() : +{ + final Span s; + final SqlTruncate truncate; +} +{ + <TRUNCATE> { s = span(); } + ( +<#-- additional literal parser methods are included here --> +<#list (parser.truncateStatementParserMethods!default.parser.truncateStatementParserMethods) as method> + truncate = ${method}(s) + <#sep>|</#sep> +</#list> + ) + { + return truncate; + } +} +</#if> + /** * Parses a literal expression, allowing continued string literals. * Usually returns an SqlLiteral, but a continued string literal diff --git a/core/src/main/java/org/apache/calcite/sql/SqlKind.java b/core/src/main/java/org/apache/calcite/sql/SqlKind.java index b7cbd3f65f..c98a0ccf49 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlKind.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlKind.java @@ -1158,6 +1158,9 @@ public enum SqlKind { /** {@code DROP TABLE} DDL statement. */ DROP_TABLE, + /** {@code TRUNCATE TABLE} DDL statement. */ + TRUNCATE_TABLE, + /** {@code CREATE VIEW} DDL statement. */ CREATE_VIEW, @@ -1269,7 +1272,7 @@ public enum SqlKind { public static final EnumSet<SqlKind> DDL = EnumSet.of(COMMIT, ROLLBACK, ALTER_SESSION, CREATE_SCHEMA, CREATE_FOREIGN_SCHEMA, DROP_SCHEMA, - CREATE_TABLE, ALTER_TABLE, DROP_TABLE, + CREATE_TABLE, ALTER_TABLE, DROP_TABLE, TRUNCATE_TABLE, CREATE_FUNCTION, DROP_FUNCTION, CREATE_VIEW, ALTER_VIEW, DROP_VIEW, CREATE_MATERIALIZED_VIEW, ALTER_MATERIALIZED_VIEW, diff --git a/core/src/main/java/org/apache/calcite/sql/SqlTruncate.java b/core/src/main/java/org/apache/calcite/sql/SqlTruncate.java new file mode 100644 index 0000000000..d8e75bb312 --- /dev/null +++ b/core/src/main/java/org/apache/calcite/sql/SqlTruncate.java @@ -0,0 +1,33 @@ +/* + * 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.sql.parser.SqlParserPos; + +/** + * Base class for an TRUNCATE statements parse tree nodes. The portion of the statement covered by + * this class is "TRUNCATE". Subclasses handle whatever comes afterwards. + */ +public abstract class SqlTruncate extends SqlDdl { + + /** + * Creates a SqlTruncate. + */ + protected SqlTruncate(SqlOperator operator, SqlParserPos pos) { + super(operator, pos); + } +} diff --git a/core/src/main/java/org/apache/calcite/sql/ddl/SqlDdlNodes.java b/core/src/main/java/org/apache/calcite/sql/ddl/SqlDdlNodes.java index 8454809418..acc2fd6d3b 100644 --- a/core/src/main/java/org/apache/calcite/sql/ddl/SqlDdlNodes.java +++ b/core/src/main/java/org/apache/calcite/sql/ddl/SqlDdlNodes.java @@ -101,6 +101,12 @@ public class SqlDdlNodes { return new SqlDropTable(pos, ifExists, name); } + /** Creates a TRUNCATE TABLE. */ + public static SqlTruncateTable truncateTable(SqlParserPos pos, + SqlIdentifier name, boolean continueIdentity) { + return new SqlTruncateTable(pos, name, continueIdentity); + } + /** Creates a DROP VIEW. */ public static SqlDrop dropView(SqlParserPos pos, boolean ifExists, SqlIdentifier name) { diff --git a/core/src/main/java/org/apache/calcite/sql/ddl/SqlTruncateTable.java b/core/src/main/java/org/apache/calcite/sql/ddl/SqlTruncateTable.java new file mode 100644 index 0000000000..2d82107efc --- /dev/null +++ b/core/src/main/java/org/apache/calcite/sql/ddl/SqlTruncateTable.java @@ -0,0 +1,66 @@ +/* + * 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.ddl; + +import org.apache.calcite.sql.SqlIdentifier; +import org.apache.calcite.sql.SqlKind; +import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.SqlOperator; +import org.apache.calcite.sql.SqlSpecialOperator; +import org.apache.calcite.sql.SqlTruncate; +import org.apache.calcite.sql.SqlWriter; +import org.apache.calcite.sql.parser.SqlParserPos; + +import com.google.common.collect.ImmutableList; + +import java.util.List; + +/** + * Parse tree for {@code TRUNCATE TABLE} statement. + */ +public class SqlTruncateTable extends SqlTruncate { + + private static final SqlOperator OPERATOR = + new SqlSpecialOperator("TRUNCATE TABLE", SqlKind.TRUNCATE_TABLE); + public final SqlIdentifier name; + public final boolean continueIdentify; + + /** + * Creates a SqlTruncateTable. + */ + public SqlTruncateTable(SqlParserPos pos, SqlIdentifier name, boolean continueIdentify) { + super(OPERATOR, pos); + this.name = name; + this.continueIdentify = continueIdentify; + } + + @Override public List<SqlNode> getOperandList() { + return ImmutableList.of(name); + } + + @Override public void unparse(SqlWriter writer, int leftPrec, int rightPrec) { + writer.keyword("TRUNCATE"); + writer.keyword("TABLE"); + name.unparse(writer, leftPrec, rightPrec); + if (continueIdentify) { + writer.keyword("CONTINUE IDENTITY"); + } else { + writer.keyword("RESTART IDENTITY"); + + } + } +} diff --git a/server/src/main/codegen/config.fmpp b/server/src/main/codegen/config.fmpp index 448357e9ec..731d569cb2 100644 --- a/server/src/main/codegen/config.fmpp +++ b/server/src/main/codegen/config.fmpp @@ -29,6 +29,7 @@ data: { "org.apache.calcite.schema.ColumnStrategy" "org.apache.calcite.sql.SqlCreate" "org.apache.calcite.sql.SqlDrop" + "org.apache.calcite.sql.SqlTruncate" "org.apache.calcite.sql.ddl.SqlDdlNodes" ] @@ -82,6 +83,13 @@ data: { "SqlDropFunction" ] + # List of methods for parsing extensions to "TRUNCATE" calls. + # Each must accept arguments "(SqlParserPos pos)". + # Example: "SqlTruncateTable". + truncateStatementParserMethods: [ + "SqlTruncateTable" + ] + # List of files in @includes directory that have parser method # implementations for parsing custom SQL statements, literals or types # given as part of "statementParserMethods", "literalParserMethods" or diff --git a/server/src/main/codegen/includes/parserImpls.ftl b/server/src/main/codegen/includes/parserImpls.ftl index 5802a728f0..00e1f386e9 100644 --- a/server/src/main/codegen/includes/parserImpls.ftl +++ b/server/src/main/codegen/includes/parserImpls.ftl @@ -376,6 +376,25 @@ SqlDrop SqlDropTable(Span s, boolean replace) : } } +SqlTruncate SqlTruncateTable(Span s) : +{ + final SqlIdentifier id; + final boolean continueIdentity; +} +{ + <TABLE> id = CompoundIdentifier() + ( + <CONTINUE> <IDENTITY> { continueIdentity = true; } + | + <RESTART> <IDENTITY> { continueIdentity = false; } + | + { continueIdentity = true; } + ) + { + return SqlDdlNodes.truncateTable(s.end(this), id, continueIdentity); + } +} + SqlDrop SqlDropView(Span s, boolean replace) : { final boolean ifExists; diff --git a/server/src/main/java/org/apache/calcite/server/ServerDdlExecutor.java b/server/src/main/java/org/apache/calcite/server/ServerDdlExecutor.java index c028ae386e..467fc8ce27 100644 --- a/server/src/main/java/org/apache/calcite/server/ServerDdlExecutor.java +++ b/server/src/main/java/org/apache/calcite/server/ServerDdlExecutor.java @@ -65,6 +65,7 @@ import org.apache.calcite.sql.ddl.SqlCreateType; import org.apache.calcite.sql.ddl.SqlCreateView; import org.apache.calcite.sql.ddl.SqlDropObject; import org.apache.calcite.sql.ddl.SqlDropSchema; +import org.apache.calcite.sql.ddl.SqlTruncateTable; import org.apache.calcite.sql.dialect.CalciteSqlDialect; import org.apache.calcite.sql.fun.SqlStdOperatorTable; import org.apache.calcite.sql.parser.SqlAbstractParserImpl; @@ -182,6 +183,24 @@ public class ServerDdlExecutor extends DdlExecutorImpl { null, null, null, null, null); } + /** Erase the table date that calcite-sever created. */ + static void erase(SqlIdentifier name, CalcitePrepare.Context context) { + // Directly clearing data is more efficient than executing SQL + final Pair<CalciteSchema, String> pair = schema(context, true, name); + final CalciteSchema calciteSchema = pair.left; + final String tblName = pair.right; + final Table table = calciteSchema + .getTable(tblName, context.config().caseSensitive()) + .getTable(); + if (table instanceof MutableArrayTable) { + MutableArrayTable mutableArrayTable = (MutableArrayTable) table; + mutableArrayTable.rows.clear(); + } else { + // Not calcite-server created, so not support truncate. + throw new UnsupportedOperationException("Only MutableArrayTable support truncate"); + } + } + /** Populates the table called {@code name} by executing {@code query}. */ static void populate(SqlIdentifier name, SqlNode query, CalcitePrepare.Context context) { @@ -341,6 +360,25 @@ public class ServerDdlExecutor extends DdlExecutorImpl { } } + /** + * Executes a {@code TRUNCATE TABLE} command. + */ + public void execute(SqlTruncateTable truncate, + CalcitePrepare.Context context) { + final Pair<CalciteSchema, String> pair = schema(context, true, truncate.name); + if (pair.left.plus().getTable(pair.right) == null) { + throw SqlUtil.newContextException(truncate.name.getParserPosition(), + RESOURCE.tableNotFound(pair.right)); + } + + if (!truncate.continueIdentify) { + // Calcite not support RESTART IDENTIFY + throw new UnsupportedOperationException("RESTART IDENTIFY is not supported"); + } + + erase(truncate.name, context); + } + /** Executes a {@code CREATE MATERIALIZED VIEW} command. */ public void execute(SqlCreateMaterializedView create, CalcitePrepare.Context context) { diff --git a/server/src/test/java/org/apache/calcite/test/ServerParserTest.java b/server/src/test/java/org/apache/calcite/test/ServerParserTest.java index 64ab59ef9c..bcbd8f5656 100644 --- a/server/src/test/java/org/apache/calcite/test/ServerParserTest.java +++ b/server/src/test/java/org/apache/calcite/test/ServerParserTest.java @@ -287,6 +287,17 @@ class ServerParserTest extends SqlParserTest { .ok("DROP TABLE IF EXISTS `X`"); } + @Test void testTruncateTable() { + sql("truncate table x") + .ok("TRUNCATE TABLE `X` CONTINUE IDENTITY"); + + sql("truncate table x continue identity") + .ok("TRUNCATE TABLE `X` CONTINUE IDENTITY"); + + sql("truncate table x restart identity") + .ok("TRUNCATE TABLE `X` RESTART IDENTITY"); + } + @Test void testDropView() { sql("drop view x") .ok("DROP VIEW `X`"); diff --git a/server/src/test/java/org/apache/calcite/test/ServerTest.java b/server/src/test/java/org/apache/calcite/test/ServerTest.java index bec633d68e..cc5fb2a4f7 100644 --- a/server/src/test/java/org/apache/calcite/test/ServerTest.java +++ b/server/src/test/java/org/apache/calcite/test/ServerTest.java @@ -34,6 +34,7 @@ import org.apache.calcite.sql.ddl.SqlCreateView; import org.apache.calcite.sql.ddl.SqlDropFunction; import org.apache.calcite.sql.ddl.SqlDropMaterializedView; import org.apache.calcite.sql.ddl.SqlDropSchema; +import org.apache.calcite.sql.ddl.SqlTruncateTable; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -102,6 +103,7 @@ class ServerTest { executor.execute((SqlDropMaterializedView) o, context); executor.execute((SqlDropFunction) o, context); executor.execute((SqlDropSchema) o, context); + executor.execute((SqlTruncateTable) o, context); } @Test void testStatement() throws Exception { @@ -223,6 +225,19 @@ class ServerTest { } } + @Test void testTruncateTable() throws Exception { + try (Connection c = connect(); + Statement s = c.createStatement()) { + final boolean b = s.execute("create table t (i int not null)"); + assertThat(b, is(false)); + + final String errMsg = + assertThrows(SQLException.class, + () -> s.execute("truncate table t restart identity")).getMessage(); + assertThat(errMsg, containsString("RESTART IDENTIFY is not supported")); + } + } + @Test void testCreateFunction() throws Exception { try (Connection c = connect(); Statement s = c.createStatement()) { diff --git a/server/src/test/resources/sql/table.iq b/server/src/test/resources/sql/table.iq index 54f814a0ae..5f916c7254 100755 --- a/server/src/test/resources/sql/table.iq +++ b/server/src/test/resources/sql/table.iq @@ -50,6 +50,21 @@ select * from t; !ok + +truncate table t; +(0 rows modified) + +!update + +select * from t; ++---+---+ +| I | J | ++---+---+ ++---+---+ +(0 rows) + +!ok + drop table t; (0 rows modified) @@ -215,4 +230,8 @@ drop table if exists t; !update +truncate table t; +Table 'T' not found +!error + # End table.iq