This is an automated email from the ASF dual-hosted git repository.
amashenkov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new 936e22f50d IGNITE-24019 Extend SQL parser grammar with CREATE/DROP
SCHEMA statements (#4963)
936e22f50d is described below
commit 936e22f50dabd8d34570a21640ace6762a29c852
Author: Pavel Pereslegin <[email protected]>
AuthorDate: Thu Dec 26 10:05:31 2024 +0300
IGNITE-24019 Extend SQL parser grammar with CREATE/DROP SCHEMA statements
(#4963)
---
modules/sql-engine/src/main/codegen/config.fmpp | 9 +-
.../src/main/codegen/includes/parserImpls.ftl | 40 ++++++
.../sql/engine/sql/IgniteSqlCreateSchema.java | 102 +++++++++++++++
.../sql/engine/sql/IgniteSqlDropSchema.java | 120 ++++++++++++++++++
.../engine/sql/IgniteSqlDropSchemaBehavior.java | 30 +++++
.../sql/engine/sql/SqlSchemaDdlParserTest.java | 139 +++++++++++++++++++++
6 files changed, 438 insertions(+), 2 deletions(-)
diff --git a/modules/sql-engine/src/main/codegen/config.fmpp
b/modules/sql-engine/src/main/codegen/config.fmpp
index f1ccd76678..f44a0fbb3c 100644
--- a/modules/sql-engine/src/main/codegen/config.fmpp
+++ b/modules/sql-engine/src/main/codegen/config.fmpp
@@ -42,10 +42,13 @@ data: {
"org.apache.ignite.internal.sql.engine.sql.IgniteSqlPrimaryKeyConstraint",
"org.apache.ignite.internal.sql.engine.sql.IgniteSqlPrimaryKeyIndexType",
"org.apache.ignite.internal.sql.engine.sql.IgniteSqlCreateZone",
+ "org.apache.ignite.internal.sql.engine.sql.IgniteSqlCreateSchema",
"org.apache.ignite.internal.sql.engine.sql.IgniteSqlZoneOption",
"org.apache.ignite.internal.sql.engine.sql.IgniteSqlDropIndex",
"org.apache.ignite.internal.sql.engine.sql.IgniteSqlDropTable",
"org.apache.ignite.internal.sql.engine.sql.IgniteSqlDropZone",
+ "org.apache.ignite.internal.sql.engine.sql.IgniteSqlDropSchema",
+ "org.apache.ignite.internal.sql.engine.sql.IgniteSqlDropSchemaBehavior",
"org.apache.ignite.internal.sql.engine.sql.IgniteSqlStartTransaction",
"org.apache.ignite.internal.sql.engine.sql.IgniteSqlStartTransactionMode",
"org.apache.ignite.internal.sql.engine.sql.IgniteSqlCommitTransaction",
@@ -322,7 +325,8 @@ data: {
createStatementParserMethods: [
"SqlCreateTable",
"SqlCreateIndex",
- "SqlCreateZone"
+ "SqlCreateZone",
+ "SqlCreateSchema"
]
# List of methods for parsing extensions to "DROP" calls.
@@ -331,7 +335,8 @@ data: {
dropStatementParserMethods: [
"SqlDropTable",
"SqlDropIndex",
- "SqlDropZone"
+ "SqlDropZone",
+ "SqlDropSchema"
]
# List of methods for parsing extensions to "DROP" calls.
diff --git a/modules/sql-engine/src/main/codegen/includes/parserImpls.ftl
b/modules/sql-engine/src/main/codegen/includes/parserImpls.ftl
index de8180fc4b..a98dc127b0 100644
--- a/modules/sql-engine/src/main/codegen/includes/parserImpls.ftl
+++ b/modules/sql-engine/src/main/codegen/includes/parserImpls.ftl
@@ -286,6 +286,20 @@ SqlCreate SqlCreateIndex(Span s, boolean replace) :
}
}
+SqlCreate SqlCreateSchema(Span s, boolean replace) :
+{
+ final boolean ifNotExists;
+ final SqlIdentifier id;
+}
+{
+ <SCHEMA> { s.add(this); }
+ ifNotExists = IfNotExistsOpt()
+ id = CompoundIdentifier()
+ {
+ return new IgniteSqlCreateSchema(s.end(this), ifNotExists, id);
+ }
+}
+
boolean IfExistsOpt() :
{
}
@@ -317,6 +331,32 @@ SqlDrop SqlDropIndex(Span s, boolean replace) :
}
}
+SqlDrop SqlDropSchema(Span s, boolean replace) :
+{
+ final SqlIdentifier schemaName;
+ final boolean ifExists;
+ IgniteSqlDropSchemaBehavior dropBehavior =
IgniteSqlDropSchemaBehavior.IMPLICIT_RESTRICT;
+}
+{
+ <SCHEMA> { s.add(this); }
+ ifExists = IfExistsOpt()
+ schemaName = CompoundIdentifier()
+ [
+ (
+ <CASCADE> {
+ dropBehavior = IgniteSqlDropSchemaBehavior.CASCADE;
+ }
+ |
+ <RESTRICT> {
+ dropBehavior = IgniteSqlDropSchemaBehavior.RESTRICT;
+ }
+ )
+ ]
+ {
+ return new IgniteSqlDropSchema(s.end(this), ifExists, schemaName,
dropBehavior);
+ }
+}
+
void InfixCast(List<Object> list, ExprContext exprContext, Span s) :
{
final SqlDataTypeSpec dt;
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlCreateSchema.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlCreateSchema.java
new file mode 100644
index 0000000000..1de9c2508a
--- /dev/null
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlCreateSchema.java
@@ -0,0 +1,102 @@
+/*
+ * 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.ignite.internal.sql.engine.sql;
+
+import java.util.List;
+import java.util.Objects;
+import org.apache.calcite.sql.SqlCall;
+import org.apache.calcite.sql.SqlCreate;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlLiteral;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.util.ImmutableNullableList;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Parse tree for {@code CREATE SCHEMA} statement.
+ */
+public class IgniteSqlCreateSchema extends SqlCreate {
+
+ /** CREATE SCHEMA operator. */
+ protected static class Operator extends IgniteDdlOperator {
+
+ /** Constructor. */
+ protected Operator(boolean existFlag) {
+ super("CREATE SCHEMA", SqlKind.OTHER_DDL, existFlag);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public SqlCall createCall(@Nullable SqlLiteral functionQualifier,
+ SqlParserPos pos, @Nullable SqlNode... operands) {
+
+ return new IgniteSqlCreateSchema(pos, existFlag(), (SqlIdentifier)
operands[0]);
+ }
+ }
+
+ private final SqlIdentifier name;
+
+ /** Creates a SqlCreateSchema. */
+ public IgniteSqlCreateSchema(
+ SqlParserPos pos,
+ boolean ifNotExists,
+ SqlIdentifier name
+ ) {
+ super(new Operator(ifNotExists), pos, false, ifNotExists);
+
+ this.name = Objects.requireNonNull(name, "name");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public IgniteDdlOperator getOperator() {
+ return (IgniteDdlOperator) super.getOperator();
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("nullness")
+ @Override
+ public List<SqlNode> getOperandList() {
+ return ImmutableNullableList.of(name);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
+ writer.keyword("CREATE");
+ writer.keyword("SCHEMA");
+ if (ifNotExists()) {
+ writer.keyword("IF NOT EXISTS");
+ }
+
+ name.unparse(writer, leftPrec, rightPrec);
+ }
+
+ /** Returns the schema name. */
+ public SqlIdentifier name() {
+ return name;
+ }
+
+ /** Returns whether {@code IF NOT EXISTS} was specified. */
+ public boolean ifNotExists() {
+ return ifNotExists;
+ }
+}
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlDropSchema.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlDropSchema.java
new file mode 100644
index 0000000000..b94dcbf315
--- /dev/null
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlDropSchema.java
@@ -0,0 +1,120 @@
+/*
+ * 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.ignite.internal.sql.engine.sql;
+
+import java.util.List;
+import java.util.Objects;
+import org.apache.calcite.sql.SqlCall;
+import org.apache.calcite.sql.SqlDrop;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlLiteral;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.util.ImmutableNullableList;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+/**
+ * Parse tree for {@code DROP SCHEMA} statement.
+ */
+public class IgniteSqlDropSchema extends SqlDrop {
+
+ /** DROP SCHEMA operator. */
+ protected static class Operator extends IgniteDdlOperator {
+ private final IgniteSqlDropSchemaBehavior dropBehavior;
+
+ /** Constructor. */
+ protected Operator(boolean existFlag, IgniteSqlDropSchemaBehavior
dropBehavior) {
+ super("DROP SCHEMA", SqlKind.OTHER_DDL, existFlag);
+
+ this.dropBehavior = dropBehavior;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public SqlCall createCall(@Nullable SqlLiteral functionQualifier,
SqlParserPos pos,
+ @Nullable SqlNode... operands) {
+ return new IgniteSqlDropSchema(pos, existFlag(), (SqlIdentifier)
operands[0], dropBehavior);
+ }
+ }
+
+ /** Schema name. */
+ private final SqlIdentifier name;
+
+ /** Drop behavior. */
+ private final IgniteSqlDropSchemaBehavior behavior;
+
+ /** Constructor. */
+ public IgniteSqlDropSchema(SqlParserPos pos, boolean ifExists,
SqlIdentifier name, IgniteSqlDropSchemaBehavior behavior) {
+ super(new Operator(ifExists, behavior), pos, ifExists);
+
+ this.name = Objects.requireNonNull(name, "schema name");
+ this.behavior = behavior;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public IgniteDdlOperator getOperator() {
+ return (IgniteDdlOperator) super.getOperator();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public List<SqlNode> getOperandList() {
+ return ImmutableNullableList.of(name);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
+ writer.keyword(getOperator().getName()); // "DROP ..."
+
+ if (ifExists) {
+ writer.keyword("IF EXISTS");
+ }
+
+ name.unparse(writer, leftPrec, rightPrec);
+
+ switch (behavior) {
+ case RESTRICT:
+ writer.keyword("RESTRICT");
+ break;
+ case CASCADE:
+ writer.keyword("CASCADE");
+ break;
+ case IMPLICIT_RESTRICT:
+ break;
+ default:
+ throw new IllegalStateException("Unexpected drop behavior: " +
behavior);
+ }
+ }
+
+ public SqlIdentifier name() {
+ return name;
+ }
+
+ public boolean ifExists() {
+ Operator operator = (Operator) getOperator();
+ return operator.existFlag();
+ }
+
+ public IgniteSqlDropSchemaBehavior behavior() {
+ return behavior;
+ }
+}
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlDropSchemaBehavior.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlDropSchemaBehavior.java
new file mode 100644
index 0000000000..4dec5a7775
--- /dev/null
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlDropSchemaBehavior.java
@@ -0,0 +1,30 @@
+/*
+ * 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.ignite.internal.sql.engine.sql;
+
+/**
+ * Drop schema behavior.
+ */
+public enum IgniteSqlDropSchemaBehavior {
+ /** Forces dropping all schema objects together at once. */
+ CASCADE,
+ /** Fails if the schema is not empty. */
+ RESTRICT,
+ /** If the user does not specify a reset behavior, the default behavior is
set to {@link #RESTRICT}. */
+ IMPLICIT_RESTRICT
+}
diff --git
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/sql/SqlSchemaDdlParserTest.java
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/sql/SqlSchemaDdlParserTest.java
new file mode 100644
index 0000000000..7a49f4d899
--- /dev/null
+++
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/sql/SqlSchemaDdlParserTest.java
@@ -0,0 +1,139 @@
+/*
+ * 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.ignite.internal.sql.engine.sql;
+
+import static
org.apache.ignite.internal.sql.engine.util.SqlTestUtils.assertThrowsSqlException;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.calcite.sql.SqlNode;
+import org.apache.ignite.lang.ErrorGroups.Sql;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests to verify parsing of the DDL "SCHEMA" commands.
+ */
+public class SqlSchemaDdlParserTest extends AbstractParserTest {
+ @Test
+ public void createSchema() {
+ IgniteSqlCreateSchema createSchema = parseCreateSchema("create schema
test_schema");
+
+ assertFalse(createSchema.ifNotExists());
+
+ expectUnparsed(createSchema, "CREATE SCHEMA \"TEST_SCHEMA\"");
+ }
+
+ @Test
+ public void createSchemaInvalidSyntax() {
+ assertThrowsSqlException(Sql.STMT_PARSE_ERR, "Encountered
\"cascade\"", () -> parse("create schema test cascade"));
+ }
+
+ @Test
+ public void createSchemaIfNotExists() {
+ IgniteSqlCreateSchema createSchema = parseCreateSchema("create schema
if not exists test_schema");
+
+ assertTrue(createSchema.ifNotExists());
+
+ expectUnparsed(createSchema, "CREATE SCHEMA IF NOT EXISTS
\"TEST_SCHEMA\"");
+ }
+
+ @Test
+ public void dropSchema() {
+ IgniteSqlDropSchema dropSchema = parseDropSchema("drop schema
test_schema");
+
+ assertFalse(dropSchema.ifExists());
+ assertSame(IgniteSqlDropSchemaBehavior.IMPLICIT_RESTRICT,
dropSchema.behavior());
+
+ expectUnparsed(dropSchema, "DROP SCHEMA \"TEST_SCHEMA\"");
+ }
+
+ @Test
+ public void dropSchemaIfExists() {
+ IgniteSqlDropSchema dropSchema = parseDropSchema("drop schema if
exists test_schema");
+
+ assertTrue(dropSchema.ifExists());
+ assertSame(IgniteSqlDropSchemaBehavior.IMPLICIT_RESTRICT,
dropSchema.behavior());
+
+ expectUnparsed(dropSchema, "DROP SCHEMA IF EXISTS \"TEST_SCHEMA\"");
+ }
+
+ @Test
+ public void dropSchemaBehavior() {
+ // CASCADE
+ {
+ IgniteSqlDropSchema dropSchema = parseDropSchema("drop schema
test_schema cascade");
+
+ assertFalse(dropSchema.ifExists());
+ assertSame(IgniteSqlDropSchemaBehavior.CASCADE,
dropSchema.behavior());
+
+ expectUnparsed(dropSchema, "DROP SCHEMA \"TEST_SCHEMA\" CASCADE");
+ }
+
+ // RESTRICT
+ {
+ IgniteSqlDropSchema dropSchema = parseDropSchema("drop schema
test_schema restrict");
+
+ assertFalse(dropSchema.ifExists());
+ assertSame(IgniteSqlDropSchemaBehavior.RESTRICT,
dropSchema.behavior());
+
+ expectUnparsed(dropSchema, "DROP SCHEMA \"TEST_SCHEMA\" RESTRICT");
+ }
+ }
+
+ @Test
+ public void dropSchemaIfExistsAndBehavior() {
+ // CASCADE
+ {
+ IgniteSqlDropSchema dropSchema = parseDropSchema("drop schema if
exists test_schema cascade");
+
+ assertTrue(dropSchema.ifExists());
+ assertSame(IgniteSqlDropSchemaBehavior.CASCADE,
dropSchema.behavior());
+
+ expectUnparsed(dropSchema, "DROP SCHEMA IF EXISTS \"TEST_SCHEMA\"
CASCADE");
+ }
+
+ // RESTRICT
+ {
+ IgniteSqlDropSchema dropSchema = parseDropSchema("drop schema if
exists test_schema restrict");
+
+ assertTrue(dropSchema.ifExists());
+ assertSame(IgniteSqlDropSchemaBehavior.RESTRICT,
dropSchema.behavior());
+
+ expectUnparsed(dropSchema, "DROP SCHEMA IF EXISTS \"TEST_SCHEMA\"
RESTRICT");
+ }
+ }
+
+ @Test
+ public void dropSchemaInvalidSyntax() {
+ assertThrowsSqlException(Sql.STMT_PARSE_ERR, "Encountered
\"cascade1\"", () -> parse("drop schema test cascade1"));
+ }
+
+ private static IgniteSqlCreateSchema parseCreateSchema(String stmt) {
+ SqlNode node = parse(stmt);
+
+ return assertInstanceOf(IgniteSqlCreateSchema.class, node);
+ }
+
+ private static IgniteSqlDropSchema parseDropSchema(String stmt) {
+ SqlNode node = parse(stmt);
+
+ return assertInstanceOf(IgniteSqlDropSchema.class, node);
+ }
+}