This is an automated email from the ASF dual-hosted git repository.
korlov 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 1cbcdf7e960 IGNITE-25824 Need STORAGE_PROFILE Validation in CREATE
ZONE (#6200)
1cbcdf7e960 is described below
commit 1cbcdf7e96032f5e2fd397113e015a5aea22c091
Author: Mikhail Efremov <[email protected]>
AuthorDate: Mon Jul 14 15:30:15 2025 +0600
IGNITE-25824 Need STORAGE_PROFILE Validation in CREATE ZONE (#6200)
---
modules/sql-engine/build.gradle | 2 +-
.../internal/sql/api/ItSqlCreateZoneTest.java | 87 ++++++++++
.../internal/sql/engine/ItCreateTableDdlTest.java | 29 +++-
.../internal/sql/engine/SqlQueryProcessor.java | 9 +-
.../sql/engine/prepare/PrepareServiceImpl.java | 6 +-
.../ddl/ClusterWideStorageProfileValidator.java | 88 ++++++++++
.../prepare/ddl/DdlSqlToCommandConverter.java | 18 +-
.../prepare/ddl/StorageProfileValidator.java | 34 ++++
.../sql/engine/exec/ExecutionServiceImplTest.java | 2 +-
.../sql/engine/framework/TestBuilders.java | 2 +-
.../ddl/AbstractDdlSqlToCommandConverterTest.java | 4 +-
.../prepare/ddl/DdlSqlToCommandConverterTest.java | 6 +
.../DistributionZoneSqlToCommandConverterTest.java | 184 +++++++++++++++++----
13 files changed, 430 insertions(+), 41 deletions(-)
diff --git a/modules/sql-engine/build.gradle b/modules/sql-engine/build.gradle
index 31d060e12f4..e7ea8ed53ee 100644
--- a/modules/sql-engine/build.gradle
+++ b/modules/sql-engine/build.gradle
@@ -123,7 +123,7 @@ dependencies {
integrationTestImplementation libs.awaitility
integrationTestImplementation libs.netty.handler
integrationTestImplementation project(':ignite-api')
- integrationTestImplementation project(':ignite-schema')
+ integrationTestImplementation project(':ignite-metastorage')
integrationTestImplementation project(':ignite-catalog')
integrationTestImplementation project(':ignite-transactions')
integrationTestImplementation project(':ignite-storage-api')
diff --git
a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlCreateZoneTest.java
b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlCreateZoneTest.java
new file mode 100644
index 00000000000..b3cf44ad20e
--- /dev/null
+++
b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlCreateZoneTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.api;
+
+import static
org.apache.ignite.internal.TestDefaultProfilesNames.DEFAULT_AIMEM_PROFILE_NAME;
+import static
org.apache.ignite.internal.TestDefaultProfilesNames.DEFAULT_AIPERSIST_PROFILE_NAME;
+import static
org.apache.ignite.internal.TestDefaultProfilesNames.DEFAULT_ROCKSDB_PROFILE_NAME;
+import static
org.apache.ignite.internal.TestDefaultProfilesNames.DEFAULT_TEST_PROFILE_NAME;
+import static org.apache.ignite.internal.lang.IgniteStringFormatter.format;
+import static
org.apache.ignite.internal.testframework.IgniteTestUtils.assertThrowsWithCode;
+import static org.apache.ignite.lang.ErrorGroups.Sql.STMT_VALIDATION_ERR;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import java.util.List;
+import org.apache.ignite.internal.ClusterPerTestIntegrationTest;
+import org.apache.ignite.sql.SqlException;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
+
+@Timeout(60)
+class ItSqlCreateZoneTest extends ClusterPerTestIntegrationTest {
+ private static final String ZONE_MANE = "test_zone";
+ private static final String NOT_EXISTED_PROFILE_NAME =
"not-existed-profile";
+ private static final String EXTRA_PROFILE_NAME = "extra-profile";
+ /** Nodes bootstrap configuration pattern. */
+ private static final String NODE_BOOTSTRAP_CFG_TEMPLATE_WITH_EXTRA_PROFILE
= "ignite {\n"
+ + " network: {\n"
+ + " port: {},\n"
+ + " nodeFinder.netClusterNodes: [ {} ]\n"
+ + " },\n"
+ + " storage.profiles: {"
+ + " " + DEFAULT_TEST_PROFILE_NAME + ".engine: test, "
+ + " " + DEFAULT_AIPERSIST_PROFILE_NAME + ".engine:
aipersist, "
+ + " " + DEFAULT_AIMEM_PROFILE_NAME + ".engine: aimem, "
+ + " " + EXTRA_PROFILE_NAME + ".engine: aipersist, "
+ + " " + DEFAULT_ROCKSDB_PROFILE_NAME + ".engine: rocksdb"
+ + " },\n"
+ + " clientConnector.port: {},\n"
+ + " rest.port: {},\n"
+ + " failureHandler.dumpThreadsOnFailure: false\n"
+ + "}";
+
+ @Override
+ protected int initialNodes() {
+ return 1;
+ }
+
+ @Test
+ void testCreateZoneSucceedWithCorrectStorageProfileOnSameNode() {
+ assertDoesNotThrow(() -> createZoneQuery(0, "default"));
+ }
+
+ @Test
+ void testCreateZoneSucceedWithCorrectStorageProfileOnDifferentNode() {
+ cluster.startNode(1, NODE_BOOTSTRAP_CFG_TEMPLATE_WITH_EXTRA_PROFILE);
+ assertDoesNotThrow(() -> createZoneQuery(0, EXTRA_PROFILE_NAME));
+ }
+
+ @Test
+ void testCreateZoneFailedWithoutCorrectStorageProfileInCluster() {
+ assertThrowsWithCode(
+ SqlException.class,
+ STMT_VALIDATION_ERR,
+ () -> createZoneQuery(0, NOT_EXISTED_PROFILE_NAME),
+ "Some storage profiles don't exist [missedProfileNames=[" +
NOT_EXISTED_PROFILE_NAME + "]]."
+ );
+ }
+
+ private List<List<Object>> createZoneQuery(int nodeIdx, String
storageProfile) {
+ return executeSql(nodeIdx, format("CREATE ZONE IF NOT EXISTS {}
STORAGE PROFILES ['{}']", ZONE_MANE, storageProfile));
+ }
+}
diff --git
a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItCreateTableDdlTest.java
b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItCreateTableDdlTest.java
index 90a721aa13e..79b6f0b2613 100644
---
a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItCreateTableDdlTest.java
+++
b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItCreateTableDdlTest.java
@@ -31,7 +31,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -763,12 +763,33 @@ public class ItCreateTableDdlTest extends
BaseSqlIntegrationTest {
@Test
public void creatingTableOnZoneReferencingNonExistingProfile() {
+ String zoneName = "test_zone";
String tableName = "test_table";
+ String nonExistingProfileName = "no-such-profile";
- sql("CREATE ZONE test_zone STORAGE PROFILES ['no-such-profile']");
- sql("CREATE TABLE " + tableName + " (id INT PRIMARY KEY, val INT) ZONE
test_zone");
+ // Try to create zone with not existed storage profile.
+ assertThrowsSqlException(
+ STMT_VALIDATION_ERR,
+ "Some storage profiles don't exist [missedProfileNames=[" +
nonExistingProfileName + "]].",
+ () -> sql("CREATE ZONE \"" + zoneName + "\" STORAGE PROFILES
['" + nonExistingProfileName + "']")
+ );
+
+ // Check that the zone wasn't created and table creation fails with
zone not found reason.
+ assertThrowsSqlException(
+ STMT_VALIDATION_ERR,
+ "Distribution zone with name '" + zoneName + "' not found.",
+ () -> sql("CREATE TABLE " + tableName + " (id INT PRIMARY KEY,
val INT) ZONE \"" + zoneName + "\"")
+ );
+
+ // Try to create table with default zone and wrong storage profile.
+ assertThrowsSqlException(
+ STMT_VALIDATION_ERR,
+ "Zone with name 'Default' does not contain table's storage
profile [storageProfile='" + nonExistingProfileName + "'].",
+ () -> sql("CREATE TABLE " + tableName + " (id INT PRIMARY KEY,
val INT) STORAGE PROFILE '" + nonExistingProfileName + "'")
+ );
+ // Verify that there still no the desired table.
Table table = CLUSTER.aliveNode().tables().table(tableName);
- assertThat(table, is(notNullValue()));
+ assertThat(table, is(nullValue()));
}
}
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/SqlQueryProcessor.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/SqlQueryProcessor.java
index 7fc5a8ba4c5..f2e11594072 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/SqlQueryProcessor.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/SqlQueryProcessor.java
@@ -85,6 +85,8 @@ import
org.apache.ignite.internal.sql.engine.prepare.PrepareService;
import org.apache.ignite.internal.sql.engine.prepare.PrepareServiceImpl;
import org.apache.ignite.internal.sql.engine.prepare.QueryMetadata;
import org.apache.ignite.internal.sql.engine.prepare.QueryPlan;
+import
org.apache.ignite.internal.sql.engine.prepare.ddl.ClusterWideStorageProfileValidator;
+import
org.apache.ignite.internal.sql.engine.prepare.ddl.DdlSqlToCommandConverter;
import
org.apache.ignite.internal.sql.engine.prepare.pruning.PartitionPrunerImpl;
import org.apache.ignite.internal.sql.engine.schema.SqlSchemaManager;
import org.apache.ignite.internal.sql.engine.schema.SqlSchemaManagerImpl;
@@ -281,6 +283,10 @@ public class SqlQueryProcessor implements QueryProcessor,
SystemViewProvider {
metricManager.registerSource(sqlQueryMetricSource);
metricManager.enable(sqlQueryMetricSource);
+ var storageProfileValidator = new
ClusterWideStorageProfileValidator(logicalTopologyService);
+
+ var ddlSqlToCommandConverter = new
DdlSqlToCommandConverter(storageProfileValidator);
+
var prepareSvc = registerService(PrepareServiceImpl.create(
nodeName,
CACHE_FACTORY,
@@ -288,7 +294,8 @@ public class SqlQueryProcessor implements QueryProcessor,
SystemViewProvider {
metricManager,
clusterCfg,
nodeCfg,
- sqlSchemaManager
+ sqlSchemaManager,
+ ddlSqlToCommandConverter
));
var msgSrvc = registerService(new MessageServiceImpl(
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/PrepareServiceImpl.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/PrepareServiceImpl.java
index 7db2b2273be..21ac08029f3 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/PrepareServiceImpl.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/PrepareServiceImpl.java
@@ -144,6 +144,7 @@ public class PrepareServiceImpl implements PrepareService {
* @param clusterCfg Cluster SQL configuration.
* @param nodeCfg Node SQL configuration.
* @param schemaManager Schema manager to use on validation phase to bind
identifiers in AST with particular schema objects.
+ * @param ddlSqlToCommandConverter Converter from SQL DDL operators to
catalog commands.
*/
public static PrepareServiceImpl create(
String nodeName,
@@ -152,13 +153,14 @@ public class PrepareServiceImpl implements PrepareService
{
MetricManager metricManager,
SqlDistributedConfiguration clusterCfg,
SqlLocalConfiguration nodeCfg,
- SqlSchemaManager schemaManager
+ SqlSchemaManager schemaManager,
+ DdlSqlToCommandConverter ddlSqlToCommandConverter
) {
return new PrepareServiceImpl(
nodeName,
clusterCfg.planner().estimatedNumberOfQueries().value(),
cacheFactory,
- new DdlSqlToCommandConverter(),
+ ddlSqlToCommandConverter,
clusterCfg.planner().maxPlanningTimeMillis().value(),
nodeCfg.planner().threadCount().value(),
metricManager,
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/ClusterWideStorageProfileValidator.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/ClusterWideStorageProfileValidator.java
new file mode 100644
index 00000000000..0f522cac493
--- /dev/null
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/ClusterWideStorageProfileValidator.java
@@ -0,0 +1,88 @@
+/*
+ * 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.prepare.ddl;
+
+import static org.apache.ignite.internal.lang.IgniteStringFormatter.format;
+import static org.apache.ignite.lang.ErrorGroups.Sql.STMT_VALIDATION_ERR;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import org.apache.ignite.internal.cluster.management.topology.api.LogicalNode;
+import
org.apache.ignite.internal.cluster.management.topology.api.LogicalTopologyService;
+import
org.apache.ignite.internal.cluster.management.topology.api.LogicalTopologySnapshot;
+import org.apache.ignite.sql.SqlException;
+
+/**
+ * Storage profile names validator that checks presence of given to validate
profile names across cluster.
+ */
+public class ClusterWideStorageProfileValidator implements
StorageProfileValidator {
+ private final LogicalTopologyService logicalTopologyService;
+
+ public ClusterWideStorageProfileValidator(LogicalTopologyService
logicalTopologyService) {
+ this.logicalTopologyService = logicalTopologyService;
+ }
+
+ @Override
+ public void validate(Collection<String> storageProfiles) {
+ LogicalTopologySnapshot localLogicalTopologySnapshot =
logicalTopologyService.localLogicalTopology();
+
+ Set<String> missedStorageProfileNames =
findStorageProfileNotPresentedInLogicalTopologySnapshot(
+ storageProfiles,
+ localLogicalTopologySnapshot
+ );
+
+ if (!missedStorageProfileNames.isEmpty()) {
+ throw new SqlException(STMT_VALIDATION_ERR, format(
+ "Some storage profiles don't exist
[missedProfileNames={}].",
+ missedStorageProfileNames
+ ));
+ }
+ }
+
+
+ private static Set<String>
findStorageProfileNotPresentedInLogicalTopologySnapshot(
+ Collection<String> storageProfiles,
+ LogicalTopologySnapshot snapshot
+ ) {
+ Set<String> topologyWideProfiles =
extractStorageProfileNamesFromLogicalTopologySnapshot(snapshot);
+
+ Set<String> missedProfiles = new HashSet<>();
+
+ for (String profileName : storageProfiles) {
+ if (!topologyWideProfiles.contains(profileName)) {
+ missedProfiles.add(profileName);
+ }
+ }
+
+ return missedProfiles;
+ }
+
+ private static Set<String>
extractStorageProfileNamesFromLogicalTopologySnapshot(LogicalTopologySnapshot
snapshot) {
+ Set<LogicalNode> logicalNodes = snapshot.nodes();
+
+ // Assume default persistent + rocks + aimem profiles on each node in
average.
+ Set<String> topologyWideProfiles = new HashSet<>(logicalNodes.size() *
3);
+
+ for (LogicalNode logicalNode : logicalNodes) {
+ topologyWideProfiles.addAll(logicalNode.storageProfiles());
+ }
+
+ return topologyWideProfiles;
+ }
+}
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverter.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverter.java
index 735e0521ccf..548be018b93 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverter.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverter.java
@@ -162,10 +162,15 @@ public class DdlSqlToCommandConverter {
/** Zone options set. */
private final Set<String> knownZoneOptionNames;
+ /** Storage profiles validator. */
+ private final StorageProfileValidator storageProfileValidator;
+
/**
* Constructor.
+ *
+ * @param storageProfileValidator Storage profile names validator.
*/
- public DdlSqlToCommandConverter() {
+ public DdlSqlToCommandConverter(StorageProfileValidator
storageProfileValidator) {
knownZoneOptionNames = EnumSet.allOf(ZoneOptionEnum.class)
.stream()
.map(Enum::name)
@@ -206,6 +211,8 @@ public class DdlSqlToCommandConverter {
));
alterReplicasOptionInfo = new DdlOptionInfo<>(Integer.class,
this::checkPositiveNumber, AlterZoneCommandBuilder::replicas);
+
+ this.storageProfileValidator = storageProfileValidator;
}
/**
@@ -723,11 +730,20 @@ public class DdlSqlToCommandConverter {
List<StorageProfileParams> profiles =
extractProfiles(createZoneNode.storageProfiles());
+ Set<String> storageProfileNames = new HashSet<>(profiles.size());
+
+ for (StorageProfileParams profile : profiles) {
+ storageProfileNames.add(profile.storageProfile());
+ }
+
+ storageProfileValidator.validate(storageProfileNames);
+
builder.storageProfilesParams(profiles);
return builder.build();
}
+
/**
* Converts the given '{@code ALTER ZONE}' AST to the {@link
AlterZoneCommand} catalog command.
*/
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/StorageProfileValidator.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/StorageProfileValidator.java
new file mode 100644
index 00000000000..360f274b61a
--- /dev/null
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/StorageProfileValidator.java
@@ -0,0 +1,34 @@
+/*
+ * 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.prepare.ddl;
+
+import java.util.Collection;
+
+/**
+ * Common validator for storage profile names.
+ */
+@FunctionalInterface
+public interface StorageProfileValidator {
+
+ /**
+ * Checks that provided storage profile names are existing and valid.
+ *
+ * @param storageProfiles Storage profile names to check.
+ */
+ void validate(Collection<String> storageProfiles);
+}
diff --git
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/ExecutionServiceImplTest.java
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/ExecutionServiceImplTest.java
index 9d2d4925724..6d01feb0c41 100644
---
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/ExecutionServiceImplTest.java
+++
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/ExecutionServiceImplTest.java
@@ -241,7 +241,7 @@ public class ExecutionServiceImplTest extends
BaseIgniteAbstractTest {
}
private void setupCluster(CacheFactory mappingCacheFactory,
Function<String, QueryTaskExecutor> executorsFactory) {
- DdlSqlToCommandConverter converter = new DdlSqlToCommandConverter();
+ DdlSqlToCommandConverter converter = new
DdlSqlToCommandConverter(storageProfiles -> {});
testCluster = new TestCluster();
executionServices = nodeNames.stream()
diff --git
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestBuilders.java
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestBuilders.java
index 47620d6058a..a07353a4df0 100644
---
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestBuilders.java
+++
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestBuilders.java
@@ -744,7 +744,7 @@ public class TestBuilders {
ConcurrentMap<String, Long> tablesSize = new ConcurrentHashMap<>();
var schemaManager = createSqlSchemaManager(catalogManager,
tablesSize);
var prepareService = new PrepareServiceImpl(clusterName, 0,
CaffeineCacheFactory.INSTANCE,
- new DdlSqlToCommandConverter(), planningTimeout,
PLANNING_THREAD_COUNT,
+ new DdlSqlToCommandConverter(storageProfiles -> {}),
planningTimeout, PLANNING_THREAD_COUNT,
new NoOpMetricManager(), schemaManager);
Map<String, List<String>> systemViewsByNode = new HashMap<>();
diff --git
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AbstractDdlSqlToCommandConverterTest.java
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AbstractDdlSqlToCommandConverterTest.java
index bf6d605b918..bebcc90f235 100644
---
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AbstractDdlSqlToCommandConverterTest.java
+++
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AbstractDdlSqlToCommandConverterTest.java
@@ -43,9 +43,9 @@ import
org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
/**
* Common methods for {@link DdlSqlToCommandConverter} testing.
*/
-class AbstractDdlSqlToCommandConverterTest extends BaseIgniteAbstractTest {
+abstract class AbstractDdlSqlToCommandConverterTest extends
BaseIgniteAbstractTest {
/** DDL SQL to command converter. */
- final DdlSqlToCommandConverter converter = new DdlSqlToCommandConverter();
+ DdlSqlToCommandConverter converter;
final Catalog catalog = mock(Catalog.class);
diff --git
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverterTest.java
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverterTest.java
index fb020940dd5..b60ea68418d 100644
---
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverterTest.java
+++
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverterTest.java
@@ -91,6 +91,7 @@ import org.hamcrest.CustomMatcher;
import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
import org.jetbrains.annotations.Nullable;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.Test;
@@ -107,6 +108,11 @@ import org.mockito.Mockito;
public class DdlSqlToCommandConverterTest extends
AbstractDdlSqlToCommandConverterTest {
private static final Integer TEST_ZONE_ID = 100;
+ @BeforeEach
+ void setUp() {
+ converter = new DdlSqlToCommandConverter(storageProfiles -> {});
+ }
+
@Test
void testCheckDuplicates() {
assertThrows(
diff --git
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DistributionZoneSqlToCommandConverterTest.java
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DistributionZoneSqlToCommandConverterTest.java
index 5fb83f9a700..9a8ee625315 100644
---
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DistributionZoneSqlToCommandConverterTest.java
+++
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DistributionZoneSqlToCommandConverterTest.java
@@ -21,14 +21,20 @@ import static
org.apache.ignite.internal.catalog.CatalogService.DEFAULT_STORAGE_
import static org.apache.ignite.internal.lang.IgniteStringFormatter.format;
import static
org.apache.ignite.internal.testframework.IgniteTestUtils.assertThrows;
import static
org.apache.ignite.internal.testframework.IgniteTestUtils.assertThrowsWithCode;
+import static org.apache.ignite.lang.ErrorGroups.Sql.STMT_VALIDATION_ERR;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.apache.calcite.sql.SqlDdl;
import org.apache.calcite.sql.SqlNode;
@@ -46,16 +52,19 @@ import
org.apache.ignite.internal.catalog.storage.AlterZoneEntry;
import org.apache.ignite.internal.catalog.storage.DropZoneEntry;
import org.apache.ignite.internal.catalog.storage.NewZoneEntry;
import org.apache.ignite.internal.catalog.storage.SetDefaultZoneEntry;
+import org.apache.ignite.internal.cluster.management.topology.api.LogicalNode;
+import
org.apache.ignite.internal.cluster.management.topology.api.LogicalTopologyService;
+import
org.apache.ignite.internal.cluster.management.topology.api.LogicalTopologySnapshot;
+import org.apache.ignite.internal.network.ClusterNodeImpl;
import org.apache.ignite.internal.partitiondistribution.DistributionAlgorithm;
-import org.apache.ignite.lang.ErrorGroups.Sql;
+import org.apache.ignite.network.NetworkAddress;
import org.apache.ignite.sql.SqlException;
-import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
-import org.mockito.Mockito;
/**
* Tests the conversion of a sql zone definition to a command.
@@ -78,8 +87,34 @@ public class DistributionZoneSqlToCommandConverterTest
extends AbstractDdlSqlToC
ZoneOptionEnum.CONSISTENCY_MODE
);
- @BeforeAll
- public static void setUp() {
+ private static final String AIPERSIST_STORAGE_PROFILE =
"segmented_aipersist";
+
+ private static final String ROCKSDB_STORAGE_PROFILE = "lru_rocks";
+
+ private static final List<String> NODE_DEFAULT_STORAGE_PROFILES = List.of(
+ DEFAULT_STORAGE_PROFILE,
+ AIPERSIST_STORAGE_PROFILE,
+ ROCKSDB_STORAGE_PROFILE
+ );
+
+ private LogicalTopologyService logicalTopologyService;
+
+ @BeforeEach
+ public void setUp() {
+ // Default mock
+ logicalTopologyService = mock(LogicalTopologyService.class);
+
+ LogicalTopologySnapshot defaultLogicalTopologySnapshot = new
LogicalTopologySnapshot(
+ 0,
+ IntStream.range(0, 2)
+ .mapToObj(nodeIdx -> createLocalNode(nodeIdx,
NODE_DEFAULT_STORAGE_PROFILES))
+ .collect(Collectors.toList())
+ );
+
+
when(logicalTopologyService.localLogicalTopology()).thenReturn(defaultLogicalTopologySnapshot);
+
+ converter = new DdlSqlToCommandConverter(new
ClusterWideStorageProfileValidator(logicalTopologyService));
+
assertThat(ZoneOptionEnum.values().length, is(NUMERIC_OPTIONS.size() +
STRING_OPTIONS.size()));
}
@@ -169,7 +204,7 @@ public class DistributionZoneSqlToCommandConverterTest
extends AbstractDdlSqlToC
+ "distribution_algorithm='rendezvous', "
+ "data_nodes_filter='$[?(@.region == \"US\")]', "
+ "data_nodes_auto_adjust=300, "
- + "storage_profiles='lru_rocks ,
segmented_aipersist ' "
+ + "storage_profiles='" + ROCKSDB_STORAGE_PROFILE +
" , " + AIPERSIST_STORAGE_PROFILE + " ' "
: "CREATE ZONE test "
+ "(partitions 2, "
+ "replicas 5, "
@@ -177,7 +212,7 @@ public class DistributionZoneSqlToCommandConverterTest
extends AbstractDdlSqlToC
+ "distribution algorithm 'rendezvous', "
+ "nodes filter '$[?(@.region == \"US\")]', "
+ "auto adjust 300) "
- + "storage profiles ['lru_rocks',
'segmented_aipersist '] ";
+ + "storage profiles ['" + ROCKSDB_STORAGE_PROFILE
+ "' , '" + AIPERSIST_STORAGE_PROFILE + " '] ";
CatalogCommand cmd = convert(sql);
CatalogZoneDescriptor desc = invokeAndGetFirstEntry(cmd,
NewZoneEntry.class).descriptor();
@@ -192,8 +227,8 @@ public class DistributionZoneSqlToCommandConverterTest
extends AbstractDdlSqlToC
List<CatalogStorageProfileDescriptor> storageProfiles =
desc.storageProfiles().profiles();
assertThat(storageProfiles, hasSize(2));
- assertThat(storageProfiles.get(0).storageProfile(),
equalTo("lru_rocks"));
- assertThat(storageProfiles.get(1).storageProfile(),
equalTo("segmented_aipersist"));
+ assertThat(storageProfiles.get(0).storageProfile(),
equalTo(ROCKSDB_STORAGE_PROFILE));
+ assertThat(storageProfiles.get(1).storageProfile(),
equalTo(AIPERSIST_STORAGE_PROFILE));
}
// Check remaining options.
@@ -202,11 +237,11 @@ public class DistributionZoneSqlToCommandConverterTest
extends AbstractDdlSqlToC
? "CREATE ZONE test with "
+ "data_nodes_auto_adjust_scale_up=100, "
+ "data_nodes_auto_adjust_scale_down=200, "
- + "storage_profiles='lru_rocks'"
+ + "storage_profiles='" + ROCKSDB_STORAGE_PROFILE +
"'"
: "CREATE ZONE test "
+ "(auto scale up 100, "
+ "auto scale down 200) "
- + "storage profiles ['lru_rocks']";
+ + "storage profiles ['" + ROCKSDB_STORAGE_PROFILE
+ "']";
CatalogCommand cmd = convert(sql);
CatalogZoneDescriptor desc = invokeAndGetFirstEntry(cmd,
NewZoneEntry.class).descriptor();
@@ -251,6 +286,86 @@ public class DistributionZoneSqlToCommandConverterTest
extends AbstractDdlSqlToC
assertThat(desc.replicas(),
equalTo(DistributionAlgorithm.ALL_REPLICAS));
}
+ @ParameterizedTest(name = "with syntax = {0}")
+ @ValueSource(booleans = {true, false})
+ public void testSingleNonExistedStorageProfile(boolean withPresent) {
+ String nonExistedStorageProfileName = "not_existed_profile";
+
+ String sql = withPresent
+ ? "CREATE ZONE test WITH STORAGE_PROFILES='" +
nonExistedStorageProfileName + "'"
+ : "CREATE ZONE test STORAGE PROFILES ['" +
nonExistedStorageProfileName + "']";
+
+ expectStatementValidationError(
+ sql,
+ "Some storage profiles don't exist [missedProfileNames=[" +
nonExistedStorageProfileName + "]]."
+ );
+ }
+
+ @ParameterizedTest(name = "with syntax = {0}")
+ @ValueSource(booleans = {true, false})
+ public void testSeveralNonExistedStorageProfiles(boolean withPresent) {
+ String nonExistedStorageProfileName1 = "not_existed_profile_1";
+ String nonExistedStorageProfileName2 = "not_existed_profile_2";
+
+ String sql = withPresent
+ ? "CREATE ZONE test WITH STORAGE_PROFILES='" +
nonExistedStorageProfileName1 + ", " + nonExistedStorageProfileName2 + "'"
+ : "CREATE ZONE test STORAGE PROFILES ['" +
nonExistedStorageProfileName1 + "', '" + nonExistedStorageProfileName2 + "']";
+
+ expectStatementValidationError(
+ sql,
+ "Some storage profiles don't exist [missedProfileNames=["
+ + nonExistedStorageProfileName1 + ", "
+ + nonExistedStorageProfileName2 + "]]."
+ );
+ }
+
+ @ParameterizedTest(name = "with syntax = {0}")
+ @ValueSource(booleans = {true, false})
+ public void testNonExistedStorageProfilesAmongExistedOnes(boolean
withPresent) {
+ String nonExistedStorageProfileName = "not_existed_profile";
+
+ String sql = withPresent
+ ? "CREATE ZONE test WITH STORAGE_PROFILES='"
+ + AIPERSIST_STORAGE_PROFILE + ", "
+ + nonExistedStorageProfileName + ", "
+ + ROCKSDB_STORAGE_PROFILE + "'"
+ : "CREATE ZONE test STORAGE PROFILES ['"
+ + AIPERSIST_STORAGE_PROFILE + "', '"
+ + nonExistedStorageProfileName + "', '"
+ + ROCKSDB_STORAGE_PROFILE + "']";
+
+ expectStatementValidationError(
+ sql,
+ "Some storage profiles don't exist [missedProfileNames=[" +
nonExistedStorageProfileName + "]]."
+ );
+ }
+
+ @ParameterizedTest(name = "with syntax = {0}")
+ @ValueSource(booleans = {true, false})
+ public void
testExistedStorageProfileOnDisjointProfileSetsInLogicalTopologySnapshot(boolean
withPresent) throws SqlParseException {
+ when(logicalTopologyService.localLogicalTopology()).thenReturn(new
LogicalTopologySnapshot(
+ 0,
+ List.of(
+ createLocalNode(0, List.of(AIPERSIST_STORAGE_PROFILE)),
+ createLocalNode(1, List.of(ROCKSDB_STORAGE_PROFILE)),
+ createLocalNode(2, List.of(DEFAULT_STORAGE_PROFILE))
+ )
+ ));
+
+ String sql = withPresent
+ ? "CREATE ZONE test WITH STORAGE_PROFILES='" +
DEFAULT_STORAGE_PROFILE + "'"
+ : "CREATE ZONE test STORAGE PROFILES ['" +
DEFAULT_STORAGE_PROFILE + "']";
+
+ CatalogCommand cmd = convert(sql);
+
+ List<CatalogStorageProfileDescriptor> storageProfiles =
invokeAndGetFirstEntry(cmd, NewZoneEntry.class)
+ .descriptor()
+ .storageProfiles()
+ .profiles();
+ assertThat(storageProfiles, hasSize(1));
+ assertThat(storageProfiles.get(0).storageProfile(),
equalTo(DEFAULT_STORAGE_PROFILE));
+ }
+
private static List<Arguments> defaultQuorum() {
return List.of(
Arguments.of(1, 1),
@@ -345,7 +460,7 @@ public class DistributionZoneSqlToCommandConverterTest
extends AbstractDdlSqlToC
assertThat(cmd, instanceOf(RenameZoneCommand.class));
-
Mockito.when(catalog.zone("TEST")).thenReturn(mock(CatalogZoneDescriptor.class));
+
when(catalog.zone("TEST")).thenReturn(mock(CatalogZoneDescriptor.class));
AlterZoneEntry entry = invokeAndGetFirstEntry(cmd,
AlterZoneEntry.class);
@@ -361,7 +476,7 @@ public class DistributionZoneSqlToCommandConverterTest
extends AbstractDdlSqlToC
RenameZoneCommand zoneCmd = (RenameZoneCommand) cmd;
-
Mockito.when(catalog.zone("TEST")).thenReturn(mock(CatalogZoneDescriptor.class));
+
when(catalog.zone("TEST")).thenReturn(mock(CatalogZoneDescriptor.class));
AlterZoneEntry entry = invokeAndGetFirstEntry(cmd,
AlterZoneEntry.class);
@@ -381,10 +496,10 @@ public class DistributionZoneSqlToCommandConverterTest
extends AbstractDdlSqlToC
CatalogZoneDescriptor zoneMock = mock(CatalogZoneDescriptor.class);
- Mockito.when(zoneMock.name()).thenReturn("TEST");
- Mockito.when(zoneMock.filter()).thenReturn("");
+ when(zoneMock.name()).thenReturn("TEST");
+ when(zoneMock.filter()).thenReturn("");
- Mockito.when(catalog.zone("TEST")).thenReturn(zoneMock);
+ when(catalog.zone("TEST")).thenReturn(zoneMock);
CatalogZoneDescriptor desc = invokeAndGetFirstEntry(cmd,
AlterZoneEntry.class).descriptor();
@@ -425,10 +540,10 @@ public class DistributionZoneSqlToCommandConverterTest
extends AbstractDdlSqlToC
assertThat(cmd, instanceOf(AlterZoneCommand.class));
CatalogZoneDescriptor zoneMock = mock(CatalogZoneDescriptor.class);
- Mockito.when(zoneMock.name()).thenReturn("TEST");
- Mockito.when(zoneMock.filter()).thenReturn("");
+ when(zoneMock.name()).thenReturn("TEST");
+ when(zoneMock.filter()).thenReturn("");
- Mockito.when(catalog.zone("TEST")).thenReturn(zoneMock);
+ when(catalog.zone("TEST")).thenReturn(zoneMock);
CatalogZoneDescriptor desc = invokeAndGetFirstEntry(cmd,
AlterZoneEntry.class).descriptor();
@@ -455,10 +570,10 @@ public class DistributionZoneSqlToCommandConverterTest
extends AbstractDdlSqlToC
assertThat(cmd, instanceOf(AlterZoneCommand.class));
CatalogZoneDescriptor zoneMock = mock(CatalogZoneDescriptor.class);
- Mockito.when(zoneMock.name()).thenReturn("TEST");
- Mockito.when(zoneMock.filter()).thenReturn("");
+ when(zoneMock.name()).thenReturn("TEST");
+ when(zoneMock.filter()).thenReturn("");
- Mockito.when(catalog.zone("TEST")).thenReturn(zoneMock);
+ when(catalog.zone("TEST")).thenReturn(zoneMock);
CatalogZoneDescriptor desc = invokeAndGetFirstEntry(cmd,
AlterZoneEntry.class).descriptor();
@@ -477,10 +592,10 @@ public class DistributionZoneSqlToCommandConverterTest
extends AbstractDdlSqlToC
assertThat(cmd, instanceOf(AlterZoneCommand.class));
CatalogZoneDescriptor zoneMock = mock(CatalogZoneDescriptor.class);
- Mockito.when(zoneMock.name()).thenReturn("TEST");
- Mockito.when(zoneMock.filter()).thenReturn("");
+ when(zoneMock.name()).thenReturn("TEST");
+ when(zoneMock.filter()).thenReturn("");
- Mockito.when(catalog.zone("TEST")).thenReturn(zoneMock);
+ when(catalog.zone("TEST")).thenReturn(zoneMock);
CatalogZoneDescriptor desc = invokeAndGetFirstEntry(cmd,
AlterZoneEntry.class).descriptor();
@@ -494,7 +609,7 @@ public class DistributionZoneSqlToCommandConverterTest
extends AbstractDdlSqlToC
assertThat(cmd, instanceOf(AlterZoneSetDefaultCommand.class));
CatalogZoneDescriptor zoneMock = mock(CatalogZoneDescriptor.class);
- Mockito.when(catalog.zone("TEST")).thenReturn(zoneMock);
+ when(catalog.zone("TEST")).thenReturn(zoneMock);
SetDefaultZoneEntry entry = invokeAndGetFirstEntry(cmd,
SetDefaultZoneEntry.class);
@@ -546,7 +661,7 @@ public class DistributionZoneSqlToCommandConverterTest
extends AbstractDdlSqlToC
assertThat(cmd, instanceOf(DropZoneCommand.class));
CatalogZoneDescriptor zoneMock = mock(CatalogZoneDescriptor.class);
- Mockito.when(catalog.zone("TEST")).thenReturn(zoneMock);
+ when(catalog.zone("TEST")).thenReturn(zoneMock);
DropZoneEntry entry = invokeAndGetFirstEntry(cmd, DropZoneEntry.class);
@@ -676,9 +791,22 @@ public class DistributionZoneSqlToCommandConverterTest
extends AbstractDdlSqlToC
private void expectStatementValidationError(String sql, String
errorMessageFragment) {
assertThrowsWithCode(
SqlException.class,
- Sql.STMT_VALIDATION_ERR,
+ STMT_VALIDATION_ERR,
() -> convert(sql),
errorMessageFragment
);
}
+
+ private static LogicalNode createLocalNode(int nodeIdx, List<String>
storageProfiles) {
+ return new LogicalNode(
+ new ClusterNodeImpl(
+ UUID.randomUUID(),
+ "node" + nodeIdx,
+ new NetworkAddress("127.0.0.1", 3344 + nodeIdx)
+ ),
+ Map.of(),
+ Map.of(),
+ storageProfiles
+ );
+ }
}