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);
+    }
+}

Reply via email to