This is an automated email from the ASF dual-hosted git repository.

ppa pushed a commit to branch ignite-18156
in repository https://gitbox.apache.org/repos/asf/ignite-3.git

commit 462907b61f1260029fc89218257b4c9f47138828
Author: Pavel Pereslegin <xxt...@gmail.com>
AuthorDate: Tue Nov 22 20:54:17 2022 +0300

    IGNITE-18156 Create/Drop Zone grammar.
---
 modules/sql-engine/src/main/codegen/config.fmpp    |  10 +-
 .../src/main/codegen/includes/parserImpls.ftl      |  65 +++++++++
 .../sql/engine/sql/IgniteSqlCreateZone.java        | 102 ++++++++++++++
 .../sql/engine/sql/IgniteSqlCreateZoneOption.java  | 146 +++++++++++++++++++++
 .../internal/sql/engine/sql/IgniteSqlDropZone.java |  71 ++++++++++
 .../internal/sql/engine/sql/SqlDdlParserTest.java  |  87 ++++++++++++
 6 files changed, 479 insertions(+), 2 deletions(-)

diff --git a/modules/sql-engine/src/main/codegen/config.fmpp 
b/modules/sql-engine/src/main/codegen/config.fmpp
index 658ec120d3..1fbac8986d 100644
--- a/modules/sql-engine/src/main/codegen/config.fmpp
+++ b/modules/sql-engine/src/main/codegen/config.fmpp
@@ -36,7 +36,10 @@ data: {
       "org.apache.ignite.internal.sql.engine.sql.IgniteSqlCreateTable",
       "org.apache.ignite.internal.sql.engine.sql.IgniteSqlCreateIndex",
       "org.apache.ignite.internal.sql.engine.sql.IgniteSqlCreateTableOption",
+      "org.apache.ignite.internal.sql.engine.sql.IgniteSqlCreateZone",
+      "org.apache.ignite.internal.sql.engine.sql.IgniteSqlCreateZoneOption",
       "org.apache.ignite.internal.sql.engine.sql.IgniteSqlDropIndex",
+      "org.apache.ignite.internal.sql.engine.sql.IgniteSqlDropZone",
       
"org.apache.ignite.internal.sql.engine.sql.IgniteSqlIntervalTypeNameSpec",
       "org.apache.ignite.internal.sql.engine.sql.IgniteSqlIndexType",
       "org.apache.calcite.sql.ddl.SqlDdlNodes",
@@ -53,6 +56,7 @@ data: {
 #     "KEY_TYPE" // already presented in Calcite
       "TREE"
       "HASH"
+#      "ZONE"
     ]
 
     # List of non-reserved keywords to add;
@@ -584,7 +588,8 @@ data: {
     # Example: "SqlCreateForeignSchema".
     createStatementParserMethods: [
       "SqlCreateTable",
-      "SqlCreateIndex"
+      "SqlCreateIndex",
+      "SqlCreateZone"
     ]
 
     # List of methods for parsing extensions to "DROP" calls.
@@ -592,7 +597,8 @@ data: {
     # Example: "SqlDropSchema".
     dropStatementParserMethods: [
       "SqlDropTable",
-      "SqlDropIndex"
+      "SqlDropIndex",
+      "SqlDropZone"
     ]
 
     # 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 f3ef9c143a..df3a120cde 100644
--- a/modules/sql-engine/src/main/codegen/includes/parserImpls.ftl
+++ b/modules/sql-engine/src/main/codegen/includes/parserImpls.ftl
@@ -161,6 +161,71 @@ SqlNodeList TableElementList() :
     }
 }
 
+SqlCreate SqlCreateZone(Span s, boolean replace) :
+{
+        final boolean ifNotExists;
+        final SqlIdentifier id;
+        SqlNodeList optionList = null;
+}
+{
+    <ZONE>
+        ifNotExists = IfNotExistsOpt()
+        id = CompoundIdentifier()
+    [
+        <WITH> { s.add(this); } optionList = CreateZoneOptionList()
+    ]
+    {
+        return new IgniteSqlCreateZone(s.end(this), ifNotExists, id, 
optionList);
+    }
+}
+
+void CreateZoneOption(List<SqlNode> list) :
+{
+    final Span s;
+    final SqlIdentifier key;
+    final SqlNode val;
+}
+{
+    key = SimpleIdentifier() { s = span(); }
+    <EQ>
+    (
+        val = SimpleIdentifier()
+    |
+        val = Literal()
+    )
+    {
+        IgniteSqlCreateZoneOption.validate(list, key, getPos());
+
+        list.add(new IgniteSqlCreateZoneOption(key, val, s.end(this)));
+    }
+}
+
+SqlNodeList CreateZoneOptionList() :
+{
+    List<SqlNode> list = new ArrayList<SqlNode>();
+    final Span s = Span.of();
+}
+{
+    CreateZoneOption(list)
+    (
+        <COMMA> { s.add(this); } CreateZoneOption(list)
+    )*
+    {
+        return new SqlNodeList(list, s.end(this));
+    }
+}
+
+SqlDrop SqlDropZone(Span s, boolean replace) :
+{
+    final boolean ifExists;
+    final SqlIdentifier zoneId;
+}
+{
+    <ZONE> ifExists = IfExistsOpt() zoneId = SimpleIdentifier() {
+        return new IgniteSqlDropZone(s.end(this), ifExists, zoneId);
+    }
+}
+
 SqlCreate SqlCreateTable(Span s, boolean replace) :
 {
     final boolean ifNotExists;
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlCreateZone.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlCreateZone.java
new file mode 100644
index 0000000000..0a9c1f24bc
--- /dev/null
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlCreateZone.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.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.parser.SqlParserPos;
+import org.apache.calcite.util.ImmutableNullableList;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+/**
+ * Parse tree for {@code CREATE ZONE} statement with Ignite specific features.
+ */
+public class IgniteSqlCreateZone extends SqlCreate {
+    private final SqlIdentifier name;
+
+    private final @Nullable SqlNodeList createOptionList;
+
+    private static final SqlOperator OPERATOR = new SqlSpecialOperator("CREATE 
ZONE", SqlKind.OTHER);
+
+    /** Creates a SqlCreateTable. */
+    public IgniteSqlCreateZone(
+            SqlParserPos pos,
+            boolean ifNotExists,
+            SqlIdentifier name,
+            @Nullable SqlNodeList createOptionList
+    ) {
+        super(OPERATOR, pos, false, ifNotExists);
+
+        this.name = Objects.requireNonNull(name, "name");
+        this.createOptionList = createOptionList;
+    }
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("nullness")
+    @Override
+    public List<SqlNode> getOperandList() {
+        return ImmutableNullableList.of(name, createOptionList);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
+        writer.keyword("CREATE");
+        writer.keyword("ZONE");
+        if (ifNotExists) {
+            writer.keyword("IF NOT EXISTS");
+        }
+
+        name.unparse(writer, leftPrec, rightPrec);
+
+        if (createOptionList != null) {
+            writer.keyword("WITH");
+
+            createOptionList.unparse(writer, 0, 0);
+        }
+    }
+
+    /**
+     * Get name of the table.
+     */
+    public SqlIdentifier name() {
+        return name;
+    }
+
+    /**
+     * Get list of the specified options to create table with.
+     */
+    public SqlNodeList createOptionList() {
+        return createOptionList;
+    }
+
+    /**
+     * Get whether the IF NOT EXISTS is specified.
+     */
+    public boolean ifNotExists() {
+        return ifNotExists;
+    }
+}
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlCreateZoneOption.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlCreateZoneOption.java
new file mode 100644
index 0000000000..c29c6ae2a7
--- /dev/null
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlCreateZoneOption.java
@@ -0,0 +1,146 @@
+/*
+ * 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 org.apache.calcite.sql.SqlCall;
+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.SqlWriter;
+import org.apache.calcite.sql.parser.SqlParseException;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.sql.util.SqlVisitor;
+import org.apache.calcite.sql.validate.SqlValidator;
+import org.apache.calcite.sql.validate.SqlValidatorScope;
+import org.apache.calcite.util.Litmus;
+
+/** An AST node representing option to create zone with. */
+public class IgniteSqlCreateZoneOption extends SqlCall {
+    private static final SqlOperator OPERATOR =
+            new SqlSpecialOperator("ZoneOption", SqlKind.OTHER);
+
+    /** Option key. */
+    private final SqlIdentifier key;
+
+    /** Option value. */
+    private final SqlNode value;
+
+    /** Creates IgniteSqlCreateZoneOption. */
+    public IgniteSqlCreateZoneOption(SqlIdentifier key, SqlNode value, 
SqlParserPos pos) {
+        super(pos);
+
+        this.key = key;
+        this.value = value;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public SqlOperator getOperator() {
+        return OPERATOR;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public List<SqlNode> getOperandList() {
+        return List.of(key, value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public SqlNode clone(SqlParserPos pos) {
+        return new IgniteSqlCreateZoneOption(key, value, pos);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
+        key.unparse(writer, leftPrec, rightPrec);
+        writer.keyword("=");
+        value.unparse(writer, leftPrec, rightPrec);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void validate(SqlValidator validator, SqlValidatorScope scope) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Validation.
+     *
+     * @param nodes Sql nodes list.
+     * @param key Current sql node key.
+     * @param pos Parser position.
+     */
+    public static void validate(List<SqlNode> nodes, SqlNode key, SqlParserPos 
pos) {
+        String strKey = ((SqlIdentifier) key).getSimple();
+        String autoAdjust = "DATA_NODES_AUTO_ADJUST";
+
+        if (!strKey.startsWith(autoAdjust)) {
+            return;
+        }
+
+        boolean searchAutoAdjust = !autoAdjust.equals(strKey);
+
+        for (SqlNode node : nodes) {
+            String nodeKey = ((IgniteSqlCreateZoneOption) 
node).key().getSimple();
+
+            if (searchAutoAdjust ? nodeKey.equals(autoAdjust) : 
nodeKey.startsWith(autoAdjust)) {
+                throw new RuntimeException(new SqlParseException("Encountered 
\"" + strKey + "\"", pos, null, null, null));
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public <R> R accept(SqlVisitor<R> visitor) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equalsDeep(SqlNode node, Litmus litmus) {
+        if (!(node instanceof IgniteSqlCreateZoneOption)) {
+            return litmus.fail("{} != {}", this, node);
+        }
+
+        IgniteSqlCreateZoneOption that = (IgniteSqlCreateZoneOption) node;
+        if (key != that.key) {
+            return litmus.fail("{} != {}", this, node);
+        }
+
+        return value.equalsDeep(that.value, litmus);
+    }
+
+    /**
+     * Get option's key.
+     */
+    public SqlIdentifier key() {
+        return key;
+    }
+
+    /**
+     * Get option's value.
+     */
+    public SqlNode value() {
+        return value;
+    }
+}
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlDropZone.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlDropZone.java
new file mode 100644
index 0000000000..9c6a06c88c
--- /dev/null
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlDropZone.java
@@ -0,0 +1,71 @@
+/*
+ * 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.SqlDrop;
+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.SqlWriter;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.util.ImmutableNullableList;
+
+/**
+ * Parse tree for {@code DROP ZONE} statement.
+ */
+public class IgniteSqlDropZone extends SqlDrop {
+    /** Index name. */
+    private final SqlIdentifier zoneName;
+
+    /** Sql operator. */
+    private static final SqlOperator OPERATOR = new SqlSpecialOperator("DROP 
ZONE", SqlKind.OTHER);
+
+    /** Constructor. */
+    public IgniteSqlDropZone(SqlParserPos pos, boolean ifExists, SqlIdentifier 
zoneName) {
+        super(OPERATOR, pos, ifExists);
+        this.zoneName = Objects.requireNonNull(zoneName, "zone name");
+    }
+
+    /** {@inheritDoc} */
+    @Override public List<SqlNode> getOperandList() {
+        return ImmutableNullableList.of(zoneName);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void unparse(SqlWriter writer, int leftPrec, int 
rightPrec) {
+        writer.keyword(getOperator().getName()); // "DROP ..."
+
+        if (ifExists) {
+            writer.keyword("IF EXISTS");
+        }
+
+        zoneName.unparse(writer, leftPrec, rightPrec);
+    }
+
+    public SqlIdentifier zoneName() {
+        return zoneName;
+    }
+
+    public boolean ifExists() {
+        return ifExists;
+    }
+}
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/sql/SqlDdlParserTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/sql/SqlDdlParserTest.java
index 34735a318f..05246ef6f2 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/sql/SqlDdlParserTest.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/sql/SqlDdlParserTest.java
@@ -26,6 +26,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.nullValue;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
@@ -53,6 +54,72 @@ import org.junit.jupiter.api.Test;
  * Test suite to verify parsing of the DDL command.
  */
 public class SqlDdlParserTest {
+    /**
+     * Parsing CREATE ZONE statement.
+     */
+    @Test
+    public void createZone() throws SqlParseException {
+        String query = "create zone test_zone";
+
+        SqlNode node = parse(query);
+
+        assertThat(node, instanceOf(IgniteSqlCreateZone.class));
+
+        IgniteSqlCreateZone createZone = (IgniteSqlCreateZone) node;
+
+        assertThat(createZone.name().names, is(List.of("TEST_ZONE")));
+        assertNull(createZone.createOptionList());
+
+        query = "create zone test_zone with replicas=2, partitions=3, 
data_nodes_filter=nodes_filter, "
+                + "affinity_function=test_Affinity,data_nodes_auto_adjust=1";
+
+        createZone = (IgniteSqlCreateZone) parse(query);
+
+        assertNotNull(createZone.createOptionList());
+        assertThatZoneOptionPresent(createZone.createOptionList().getList(), 
"REPLICAS", 2);
+        assertThatZoneOptionPresent(createZone.createOptionList().getList(), 
"PARTITIONS", 3);
+        assertThatZoneOptionPresent(createZone.createOptionList().getList(), 
"AFFINITY_FUNCTION", "TEST_AFFINITY");
+        assertThatZoneOptionPresent(createZone.createOptionList().getList(), 
"DATA_NODES_FILTER", "NODES_FILTER");
+        assertThatZoneOptionPresent(createZone.createOptionList().getList(), 
"DATA_NODES_AUTO_ADJUST", 1);
+    }
+
+    @Test
+    public void createZoneAutoAdjustmentOption() throws SqlParseException {
+        String query = "create zone test_zone with 
data_nodes_auto_adjust_scale_up=1, data_nodes_auto_adjust_scale_down=2";
+
+        SqlNode node = parse(query);
+
+        assertThat(node, instanceOf(IgniteSqlCreateZone.class));
+
+        IgniteSqlCreateZone createZone = (IgniteSqlCreateZone) node;
+
+        assertThat(createZone.name().names, is(List.of("TEST_ZONE")));
+
+        assertThatZoneOptionPresent(createZone.createOptionList().getList(), 
"DATA_NODES_AUTO_ADJUST_SCALE_UP", 1);
+        assertThatZoneOptionPresent(createZone.createOptionList().getList(), 
"DATA_NODES_AUTO_ADJUST_SCALE_DOWN", 2);
+
+        // Invalid combination.
+        String invalidQuery = "create zone test_zone with 
data_nodes_auto_adjust=1, data_nodes_auto_adjust_scale_up=1";
+
+        var ex = assertThrows(SqlParseException.class, () -> 
parse(invalidQuery));
+        assertThat(ex.getMessage(), containsString("Encountered 
\"DATA_NODES_AUTO_ADJUST_SCALE_UP\""));
+    }
+
+    /**
+     * Parsing DROP ZONE statement.
+     */
+    @Test
+    public void dropZone() throws SqlParseException {
+        String query = "drop zone test_zone";
+        SqlNode node = parse(query);
+
+        assertThat(node, instanceOf(IgniteSqlDropZone.class));
+
+        IgniteSqlDropZone dropZone = (IgniteSqlDropZone) node;
+
+        assertThat(dropZone.zoneName().names, is(List.of("TEST_ZONE")));
+    }
+
     /**
      * Very simple case where only table name and a few columns are presented.
      */
@@ -591,6 +658,26 @@ public class SqlDdlParserTest {
         };
     }
 
+    private static void assertThatZoneOptionPresent(List<SqlNode> optionList, 
String option, Object expVal) {
+        assertThat(optionList, hasItem(ofTypeMatching(
+                option + "=" + expVal,
+                IgniteSqlCreateZoneOption.class,
+                opt -> {
+                    if (opt.key().getSimple().equals(option)) {
+                        if (opt.value() instanceof SqlLiteral) {
+                            return Objects.equals(expVal, ((SqlLiteral) 
opt.value()).getValueAs(expVal.getClass()));
+                        }
+
+                        if (opt.value() instanceof SqlIdentifier) {
+                            return Objects.equals(expVal, ((SqlIdentifier) 
opt.value()).getSimple());
+                        }
+                    }
+
+                    return false;
+                }
+        )));
+    }
+
     private static void assertThatOptionPresent(List<SqlNode> optionList, 
String option, Object expVal) {
         assertThat(optionList, hasItem(ofTypeMatching(
                 option + "=" + expVal,

Reply via email to