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,