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

ppa 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 b9880a28a0 IGNITE-18156 Sql. Extend SQL grammar with CREATE/DROP ZONE 
statement (#1372)
b9880a28a0 is described below

commit b9880a28a0f13f826ae67de97dfe2c23897d74c3
Author: Pavel Pereslegin <[email protected]>
AuthorDate: Fri Nov 25 16:57:38 2022 +0300

    IGNITE-18156 Sql. Extend SQL grammar with CREATE/DROP ZONE statement (#1372)
---
 modules/sql-engine/src/main/codegen/config.fmpp    |  24 ++-
 .../src/main/codegen/includes/parserImpls.ftl      | 108 +++++++++++
 .../sql/engine/sql/IgniteSqlCreateZone.java        | 102 ++++++++++
 .../sql/engine/sql/IgniteSqlCreateZoneOption.java  | 119 ++++++++++++
 .../engine/sql/IgniteSqlCreateZoneOptionEnum.java  |  44 +++++
 .../internal/sql/engine/sql/IgniteSqlDropZone.java |  72 +++++++
 .../sql/engine/sql/AbstractDdlParserTest.java      |  61 ++++++
 .../internal/sql/engine/sql/SqlDdlParserTest.java  |  37 +---
 .../sql/engine/sql/SqlDdlZoneParserTest.java       | 208 +++++++++++++++++++++
 9 files changed, 738 insertions(+), 37 deletions(-)

diff --git a/modules/sql-engine/src/main/codegen/config.fmpp 
b/modules/sql-engine/src/main/codegen/config.fmpp
index 658ec120d3..bbd0330ded 100644
--- a/modules/sql-engine/src/main/codegen/config.fmpp
+++ b/modules/sql-engine/src/main/codegen/config.fmpp
@@ -36,7 +36,11 @@ 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.IgniteSqlCreateZoneOptionEnum",
       "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 +57,13 @@ data: {
 #     "KEY_TYPE" // already presented in Calcite
       "TREE"
       "HASH"
+      "PARTITIONS"
+      "REPLICAS"
+      "AFFINITY_FUNCTION"
+      "DATA_NODES_FILTER"
+      "DATA_NODES_AUTO_ADJUST"
+      "DATA_NODES_AUTO_ADJUST_SCALE_UP"
+      "DATA_NODES_AUTO_ADJUST_SCALE_DOWN"
     ]
 
     # List of non-reserved keywords to add;
@@ -546,6 +557,13 @@ data: {
       "WRITE"
       "YEAR"
       "ZONE"
+      "PARTITIONS"
+      "REPLICAS"
+      "AFFINITY_FUNCTION"
+      "DATA_NODES_FILTER"
+      "DATA_NODES_AUTO_ADJUST"
+      "DATA_NODES_AUTO_ADJUST_SCALE_UP"
+      "DATA_NODES_AUTO_ADJUST_SCALE_DOWN"
     ]
 
     # List of non-reserved keywords to add;
@@ -584,7 +602,8 @@ data: {
     # Example: "SqlCreateForeignSchema".
     createStatementParserMethods: [
       "SqlCreateTable",
-      "SqlCreateIndex"
+      "SqlCreateIndex",
+      "SqlCreateZone"
     ]
 
     # List of methods for parsing extensions to "DROP" calls.
@@ -592,7 +611,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..f176a33c82 100644
--- a/modules/sql-engine/src/main/codegen/includes/parserImpls.ftl
+++ b/modules/sql-engine/src/main/codegen/includes/parserImpls.ftl
@@ -424,3 +424,111 @@ SqlNode SqlAlterTable() :
 < NEGATE: "!" >
 |   < TILDE: "~" >
 }
+
+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);
+    }
+}
+
+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));
+    }
+}
+
+void CreateZoneOption(List<SqlNode> list) :
+{
+    final Span s;
+    final SqlLiteral key;
+    final SqlNode val;
+}
+{
+    (
+        (
+            key = CreateNumericZoneOptionKey() { s = span(); }
+            <EQ>
+            val = NumericLiteral()
+        )
+        |
+        (
+            key = CreateStringZoneOptionKey() { s = span(); }
+            <EQ>
+            val = StringLiteral()
+        )
+    )
+    {
+        list.add(new IgniteSqlCreateZoneOption(key, val, s.end(this)));
+    }
+}
+
+SqlLiteral CreateNumericZoneOptionKey() :
+{
+}
+{
+    <PARTITIONS>
+    {
+        return 
SqlLiteral.createSymbol(IgniteSqlCreateZoneOptionEnum.PARTITIONS, getPos());
+    }
+|
+    <REPLICAS>
+    {
+        return SqlLiteral.createSymbol(IgniteSqlCreateZoneOptionEnum.REPLICAS, 
getPos());
+    }
+|
+    <DATA_NODES_AUTO_ADJUST>
+    {
+        return 
SqlLiteral.createSymbol(IgniteSqlCreateZoneOptionEnum.DATA_NODES_AUTO_ADJUST, 
getPos());
+    }
+|
+    <DATA_NODES_AUTO_ADJUST_SCALE_UP>
+    {
+        return 
SqlLiteral.createSymbol(IgniteSqlCreateZoneOptionEnum.DATA_NODES_AUTO_ADJUST_SCALE_UP,
 getPos());
+    }
+|
+    <DATA_NODES_AUTO_ADJUST_SCALE_DOWN>
+    {
+        return 
SqlLiteral.createSymbol(IgniteSqlCreateZoneOptionEnum.DATA_NODES_AUTO_ADJUST_SCALE_DOWN,
 getPos());
+    }
+}
+
+SqlLiteral CreateStringZoneOptionKey() :
+{
+}
+{
+    <AFFINITY_FUNCTION> { return 
SqlLiteral.createSymbol(IgniteSqlCreateZoneOptionEnum.AFFINITY_FUNCTION, 
getPos()); }
+|
+    <DATA_NODES_FILTER> { return 
SqlLiteral.createSymbol(IgniteSqlCreateZoneOptionEnum.DATA_NODES_FILTER, 
getPos()); }
+}
+
+SqlDrop SqlDropZone(Span s, boolean replace) :
+{
+    final boolean ifExists;
+    final SqlIdentifier zoneId;
+}
+{
+    <ZONE> ifExists = IfExistsOpt() zoneId = CompoundIdentifier() {
+        return new IgniteSqlDropZone(s.end(this), ifExists, zoneId);
+    }
+}
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..56e308e595
--- /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 SqlCreateZone. */
+    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 distribution zone.
+     */
+    public SqlIdentifier name() {
+        return name;
+    }
+
+    /**
+     * Get list of the specified options to create distribution zone 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..4f40784864
--- /dev/null
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlCreateZoneOption.java
@@ -0,0 +1,119 @@
+/*
+ * 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.SqlKind;
+import org.apache.calcite.sql.SqlLiteral;
+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.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 distribution zone with. */
+public class IgniteSqlCreateZoneOption extends SqlCall {
+    private static final SqlOperator OPERATOR =
+            new SqlSpecialOperator("ZoneOption", SqlKind.OTHER);
+
+    /** Option key. */
+    private final SqlLiteral key;
+
+    /** Option value. */
+    private final SqlNode value;
+
+    /** Creates {@link IgniteSqlCreateZoneOption}. */
+    public IgniteSqlCreateZoneOption(SqlLiteral 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();
+    }
+
+    /** {@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 SqlLiteral 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/IgniteSqlCreateZoneOptionEnum.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlCreateZoneOptionEnum.java
new file mode 100644
index 0000000000..04d231ce1e
--- /dev/null
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlCreateZoneOptionEnum.java
@@ -0,0 +1,44 @@
+/*
+ * 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;
+
+/**
+ * Enumerates the options for CREATE ZONE statement.
+ */
+public enum IgniteSqlCreateZoneOptionEnum {
+    /** Number of partitions. */
+    PARTITIONS,
+
+    /** Number of replicas. */
+    REPLICAS,
+
+    /** Affinity function name. */
+    AFFINITY_FUNCTION,
+
+    /** An expression to filter data nodes. */
+    DATA_NODES_FILTER,
+
+    /** Data nodes auto adjust timeout. */
+    DATA_NODES_AUTO_ADJUST,
+
+    /** Data nodes scale up auto adjust timeout. */
+    DATA_NODES_AUTO_ADJUST_SCALE_UP,
+
+    /** Data nodes scale down auto adjust timeout. */
+    DATA_NODES_AUTO_ADJUST_SCALE_DOWN
+}
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..2df1fda7a2
--- /dev/null
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlDropZone.java
@@ -0,0 +1,72 @@
+/*
+ * 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 {
+    /** Zone name. */
+    private final SqlIdentifier name;
+
+    /** Sql operator. */
+    private static final SqlOperator OPERATOR = new SqlSpecialOperator("DROP 
ZONE", SqlKind.OTHER);
+
+    /** Constructor. */
+    public IgniteSqlDropZone(SqlParserPos pos, boolean ifExists, SqlIdentifier 
name) {
+        super(OPERATOR, pos, ifExists);
+
+        this.name = Objects.requireNonNull(name, "zone name");
+    }
+
+    /** {@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);
+    }
+
+    public SqlIdentifier name() {
+        return name;
+    }
+
+    public boolean ifExists() {
+        return ifExists;
+    }
+}
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/sql/AbstractDdlParserTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/sql/AbstractDdlParserTest.java
new file mode 100644
index 0000000000..1570d46e39
--- /dev/null
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/sql/AbstractDdlParserTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.function.Predicate;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.parser.SqlParseException;
+import org.apache.calcite.sql.parser.SqlParser;
+import 
org.apache.ignite.internal.generated.query.calcite.sql.IgniteSqlParserImpl;
+import org.hamcrest.CustomMatcher;
+import org.hamcrest.Matcher;
+
+/**
+ * Common methods to verify parsing of the DDL command.
+ */
+public abstract class AbstractDdlParserTest {
+    /**
+     * Parses a given statement and returns a resulting AST.
+     *
+     * @param stmt Statement to parse.
+     * @return An AST.
+     */
+    protected SqlNode parse(String stmt) throws SqlParseException {
+        SqlParser parser = SqlParser.create(stmt, 
SqlParser.config().withParserFactory(IgniteSqlParserImpl.FACTORY));
+
+        return parser.parseStmt();
+    }
+
+    /**
+     * Matcher to verify that an object of the expected type and matches the 
given predicate.
+     *
+     * @param desc Description for this matcher.
+     * @param cls  Expected class to verify the object is instance of.
+     * @param pred Addition check that would be applied to the object.
+     * @return {@code true} in case the object if instance of the given class 
and matches the predicat.
+     */
+    protected <T> Matcher<T> ofTypeMatching(String desc, Class<T> cls, 
Predicate<T> pred) {
+        return new CustomMatcher<T>(desc) {
+            /** {@inheritDoc} */
+            @Override
+            public boolean matches(Object item) {
+                return item != null && cls.isAssignableFrom(item.getClass()) 
&& pred.test((T) item);
+            }
+        };
+    }
+}
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..569467d05f 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
@@ -32,7 +32,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
-import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import org.apache.calcite.sql.SqlBasicCall;
 import org.apache.calcite.sql.SqlIdentifier;
@@ -42,9 +41,7 @@ import org.apache.calcite.sql.SqlNode;
 import org.apache.calcite.sql.ddl.SqlColumnDeclaration;
 import org.apache.calcite.sql.ddl.SqlKeyConstraint;
 import org.apache.calcite.sql.parser.SqlParseException;
-import org.apache.calcite.sql.parser.SqlParser;
 import org.apache.calcite.sql.pretty.SqlPrettyWriter;
-import 
org.apache.ignite.internal.generated.query.calcite.sql.IgniteSqlParserImpl;
 import org.hamcrest.CustomMatcher;
 import org.hamcrest.Matcher;
 import org.junit.jupiter.api.Test;
@@ -52,7 +49,7 @@ import org.junit.jupiter.api.Test;
 /**
  * Test suite to verify parsing of the DDL command.
  */
-public class SqlDdlParserTest {
+public class SqlDdlParserTest extends AbstractDdlParserTest {
     /**
      * Very simple case where only table name and a few columns are presented.
      */
@@ -544,18 +541,6 @@ public class SqlDdlParserTest {
         return (IgniteSqlCreateTable) node;
     }
 
-    /**
-     * Parses a given statement and returns a resulting AST.
-     *
-     * @param stmt Statement to parse.
-     * @return An AST.
-     */
-    private static SqlNode parse(String stmt) throws SqlParseException {
-        SqlParser parser = SqlParser.create(stmt, 
SqlParser.config().withParserFactory(IgniteSqlParserImpl.FACTORY));
-
-        return parser.parseStmt();
-    }
-
     /**
      * Matcher to verify name in the column declaration.
      *
@@ -573,25 +558,7 @@ public class SqlDdlParserTest {
         };
     }
 
-    /**
-     * Matcher to verify that an object of the expected type and matches the 
given predicate.
-     *
-     * @param desc Description for this matcher.
-     * @param cls  Expected class to verify the object is instance of.
-     * @param pred Addition check that would be applied to the object.
-     * @return {@code true} in case the object if instance of the given class 
and matches the predicat.
-     */
-    private static <T> Matcher<T> ofTypeMatching(String desc, Class<T> cls, 
Predicate<T> pred) {
-        return new CustomMatcher<T>(desc) {
-            /** {@inheritDoc} */
-            @Override
-            public boolean matches(Object item) {
-                return item != null && cls.isAssignableFrom(item.getClass()) 
&& pred.test((T) item);
-            }
-        };
-    }
-
-    private static void assertThatOptionPresent(List<SqlNode> optionList, 
String option, Object expVal) {
+    private void assertThatOptionPresent(List<SqlNode> optionList, String 
option, Object expVal) {
         assertThat(optionList, hasItem(ofTypeMatching(
                 option + "=" + expVal,
                 IgniteSqlCreateTableOption.class,
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/sql/SqlDdlZoneParserTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/sql/SqlDdlZoneParserTest.java
new file mode 100644
index 0000000000..442dcce2fa
--- /dev/null
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/sql/SqlDdlZoneParserTest.java
@@ -0,0 +1,208 @@
+/*
+ * 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.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import org.apache.calcite.sql.SqlLiteral;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.parser.SqlParseException;
+import org.apache.calcite.sql.pretty.SqlPrettyWriter;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test suite to verify parsing of the DDL "ZONE" commands.
+ */
+public class SqlDdlZoneParserTest extends AbstractDdlParserTest {
+    /**
+     * Parse simple CREATE ZONE statement.
+     */
+    @Test
+    public void createZoneNoOptions() throws SqlParseException {
+        // Simple name.
+        IgniteSqlCreateZone createZone = parseCreateZone("create zone 
test_zone");
+
+        assertThat(createZone.name().names, is(List.of("TEST_ZONE")));
+        assertFalse(createZone.ifNotExists());
+        assertNull(createZone.createOptionList());
+
+        // Fully qualified name.
+        createZone = parseCreateZone("create zone public.test_zone");
+        assertThat(createZone.name().names, is(List.of("PUBLIC", 
"TEST_ZONE")));
+
+        // Quoted identifier.
+        createZone = parseCreateZone("create zone \"public\".\"test_Zone\"");
+        assertThat(createZone.name().names, is(List.of("public", 
"test_Zone")));
+
+        createZone = parseCreateZone("create zone \"public-test_Zone\"");
+        assertThat(createZone.name().names, is(List.of("public-test_Zone")));
+    }
+
+    /**
+     * Parse CREATE ZONE IF NOT EXISTS statement.
+     */
+    @Test
+    public void createZoneIfNotExists() throws SqlParseException {
+        IgniteSqlCreateZone createZone = parseCreateZone("create zone if not 
exists test_zone");
+
+        assertTrue(createZone.ifNotExists());
+        assertNull(createZone.createOptionList());
+    }
+
+    /**
+     * Parse CREATE ZONE WITH ... statement.
+     */
+    @Test
+    public void createZoneWithOptions() throws SqlParseException {
+        IgniteSqlCreateZone createZone = parseCreateZone(
+                "create zone test_zone with "
+                        + "replicas=2, "
+                        + "partitions=3, "
+                        + "data_nodes_filter='(\"US\" || \"EU\") && \"SSD\"', "
+                        + "affinity_function='test_Affinity', "
+                        + "data_nodes_auto_adjust=1, "
+                        + "data_nodes_auto_adjust_scale_up=2, "
+                        + "data_nodes_auto_adjust_scale_down=3"
+        );
+
+        assertNotNull(createZone.createOptionList());
+
+        List<SqlNode> optList = createZone.createOptionList().getList();
+
+        assertThatZoneOptionPresent(optList, 
IgniteSqlCreateZoneOptionEnum.REPLICAS, 2);
+        assertThatZoneOptionPresent(optList, 
IgniteSqlCreateZoneOptionEnum.PARTITIONS, 3);
+        assertThatZoneOptionPresent(optList, 
IgniteSqlCreateZoneOptionEnum.AFFINITY_FUNCTION, "test_Affinity");
+        assertThatZoneOptionPresent(optList, 
IgniteSqlCreateZoneOptionEnum.DATA_NODES_FILTER, "(\"US\" || \"EU\") && 
\"SSD\"");
+        assertThatZoneOptionPresent(optList, 
IgniteSqlCreateZoneOptionEnum.DATA_NODES_AUTO_ADJUST, 1);
+
+        SqlPrettyWriter w = new SqlPrettyWriter();
+        createZone.unparse(w, 0, 0);
+
+        assertThat(w.toString(), equalTo("CREATE ZONE \"TEST_ZONE\" WITH "
+                + "REPLICAS = 2, "
+                + "PARTITIONS = 3, "
+                + "DATA_NODES_FILTER = '(\"US\" || \"EU\") && \"SSD\"', "
+                + "AFFINITY_FUNCTION = 'test_Affinity', "
+                + "DATA_NODES_AUTO_ADJUST = 1, "
+                + "DATA_NODES_AUTO_ADJUST_SCALE_UP = 2, "
+                + "DATA_NODES_AUTO_ADJUST_SCALE_DOWN = 3"));
+    }
+
+    /**
+     * Parse CREATE ZONE WITH unknown option.
+     */
+    @Test
+    public void createZoneWithInvalidOptions() {
+        // Unknown option.
+        assertThrows(SqlParseException.class, () -> parseCreateZone("create 
zone test_zone with foo='bar'"));
+
+        // Invalid option type.
+        String query = "create zone test_zone with %s=%s";
+
+        List<String> numericOptNames = Arrays.asList("PARTITIONS", "REPLICAS", 
"DATA_NODES_AUTO_ADJUST",
+                "DATA_NODES_AUTO_ADJUST_SCALE_UP", 
"DATA_NODES_AUTO_ADJUST_SCALE_DOWN");
+
+        for (String optName : numericOptNames) {
+            assertThrows(SqlParseException.class, () -> 
parseCreateZone(String.format(query, optName, "'bar'")));
+        }
+
+        List<String> stringOptNames = Arrays.asList("AFFINITY_FUNCTION", 
"DATA_NODES_FILTER");
+
+        for (String optName : stringOptNames) {
+            assertThrows(SqlParseException.class, () -> 
parseCreateZone(String.format(query, optName, "1")));
+        }
+    }
+
+    /**
+     * Parsing DROP ZONE statement.
+     */
+    @Test
+    public void dropZone() throws SqlParseException {
+        // Simple name.
+        SqlNode node = parse("drop zone test_zone");
+
+        assertThat(node, instanceOf(IgniteSqlDropZone.class));
+
+        IgniteSqlDropZone dropZone = (IgniteSqlDropZone) node;
+
+        assertThat(dropZone.name().names, is(List.of("TEST_ZONE")));
+        assertFalse(dropZone.ifExists());
+
+        // Fully qualified name.
+        dropZone = ((IgniteSqlDropZone) parse("drop zone public.test_zone"));
+
+        assertThat(dropZone.name().names, is(List.of("PUBLIC", "TEST_ZONE")));
+    }
+
+    /**
+     * Parsing DROP ZONE IF EXISTS statement.
+     */
+    @Test
+    public void dropZoneIfExists() throws SqlParseException {
+        IgniteSqlDropZone dropZone = (IgniteSqlDropZone) parse("drop zone if 
exists test_zone");
+
+        assertTrue(dropZone.ifExists());
+
+        SqlPrettyWriter w = new SqlPrettyWriter();
+        dropZone.unparse(w, 0, 0);
+
+        assertThat(w.toString(), equalTo("DROP ZONE IF EXISTS \"TEST_ZONE\""));
+    }
+
+    /**
+     * Parse CREATE ZONE statement.
+     *
+     * @param stmt Create zone query.
+     * @return {@link org.apache.calcite.sql.SqlCreate SqlCreate} node.
+     */
+    private IgniteSqlCreateZone parseCreateZone(String stmt) throws 
SqlParseException {
+        SqlNode node = parse(stmt);
+
+        assertThat(node, instanceOf(IgniteSqlCreateZone.class));
+
+        return (IgniteSqlCreateZone) node;
+    }
+
+    private void assertThatZoneOptionPresent(List<SqlNode> optionList, 
IgniteSqlCreateZoneOptionEnum name, Object expVal) {
+        assertThat(optionList, Matchers.hasItem(ofTypeMatching(
+                name + "=" + expVal,
+                IgniteSqlCreateZoneOption.class,
+                opt -> {
+                    if (name == 
opt.key().symbolValue(IgniteSqlCreateZoneOptionEnum.class)) {
+                        if (opt.value() instanceof SqlLiteral) {
+                            return Objects.equals(expVal, ((SqlLiteral) 
opt.value()).getValueAs(expVal.getClass()));
+                        }
+                    }
+
+                    return false;
+                }
+        )));
+    }
+}

Reply via email to