You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by pp...@apache.org on 2022/11/22 17:55:55 UTC
[ignite-3] 01/01: IGNITE-18156 Create/Drop Zone grammar.
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 <xx...@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,