| /* |
| * 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.catalog.commands; |
| |
| import static org.apache.ignite.internal.catalog.CatalogManagerImpl.INITIAL_CAUSALITY_TOKEN; |
| import static org.apache.ignite.internal.catalog.CatalogService.DEFAULT_STORAGE_PROFILE; |
| import static org.apache.ignite.internal.catalog.commands.CatalogUtils.DEFAULT_LENGTH; |
| import static org.apache.ignite.internal.catalog.commands.CatalogUtils.DEFAULT_PRECISION; |
| import static org.apache.ignite.internal.catalog.commands.CatalogUtils.DEFAULT_SCALE; |
| import static org.apache.ignite.internal.catalog.commands.CatalogUtils.SYSTEM_SCHEMAS; |
| import static org.apache.ignite.internal.catalog.commands.CatalogUtils.fromParams; |
| import static org.apache.ignite.sql.ColumnType.INT32; |
| import static org.hamcrest.MatcherAssert.assertThat; |
| import static org.hamcrest.Matchers.is; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.NoSuchElementException; |
| import java.util.Set; |
| import java.util.function.Consumer; |
| import java.util.stream.Collectors; |
| import java.util.stream.Stream; |
| import org.apache.ignite.internal.catalog.Catalog; |
| import org.apache.ignite.internal.catalog.CatalogCommand; |
| import org.apache.ignite.internal.catalog.descriptors.CatalogIndexDescriptor; |
| import org.apache.ignite.internal.catalog.descriptors.CatalogIndexStatus; |
| import org.apache.ignite.internal.catalog.descriptors.CatalogSchemaDescriptor; |
| import org.apache.ignite.internal.catalog.descriptors.CatalogSystemViewDescriptor; |
| import org.apache.ignite.internal.catalog.descriptors.CatalogTableColumnDescriptor; |
| import org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor; |
| import org.apache.ignite.internal.catalog.descriptors.CatalogZoneDescriptor; |
| import org.apache.ignite.internal.catalog.storage.UpdateEntry; |
| import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest; |
| import org.junit.jupiter.params.provider.Arguments; |
| |
| /** |
| * Abstract test class for all command-related validation tests. |
| * |
| * <p>Provides a convenient set of utility methods to reuse in test. |
| */ |
| abstract class AbstractCommandValidationTest extends BaseIgniteAbstractTest { |
| static final String SCHEMA_NAME = "PUBLIC"; |
| static final String TABLE_NAME = "TEST"; |
| static final String ZONE_NAME = "Default"; |
| static final TablePrimaryKey ID_PK = TableHashPrimaryKey.builder() |
| .columns(List.of("ID")) |
| .build(); |
| |
| private static final CatalogZoneDescriptor DEFAULT_ZONE = new CatalogZoneDescriptor( |
| 0, ZONE_NAME, 1, -1, -1, -1, -1, "", |
| fromParams(List.of(StorageProfileParams.builder().storageProfile(DEFAULT_STORAGE_PROFILE).build())) |
| ); |
| |
| static Stream<Arguments> nullAndBlankStrings() { |
| return Stream.of(null, "", " ", " ").map(Arguments::of); |
| } |
| |
| static Stream<Arguments> reservedSchemaNames() { |
| return SYSTEM_SCHEMAS.stream().map(Arguments::of); |
| } |
| |
| static Stream<Arguments> nullAndEmptyLists() { |
| return Stream.of(null, List.of()).map(Arguments::of); |
| } |
| |
| static Stream<Arguments> nullAndEmptySets() { |
| return Stream.of(null, Set.of()).map(Arguments::of); |
| } |
| |
| static Catalog emptyCatalog() { |
| return catalog(1, new CatalogTableDescriptor[0], new CatalogIndexDescriptor[0], new CatalogSystemViewDescriptor[0]); |
| } |
| |
| static Catalog catalogWithTable(String name) { |
| return catalog( |
| createTableCommand(name) |
| ); |
| } |
| |
| static Catalog catalogWithTable(Consumer<CreateTableCommandBuilder> tableDef) { |
| CreateTableCommandBuilder builder = CreateTableCommand.builder(); |
| |
| tableDef.accept(builder); |
| |
| return catalog(builder.build()); |
| } |
| |
| static Catalog catalogWithZone(String name) { |
| return catalog( |
| createZoneCommand(name) |
| ); |
| } |
| |
| static Catalog catalogWithZones(String zone1, String zone2) { |
| return catalog( |
| createZoneCommand(zone1), |
| createZoneCommand(zone2) |
| ); |
| } |
| |
| static Catalog catalogWithIndex(String name) { |
| return catalog( |
| createTableCommand(TABLE_NAME), |
| createIndexCommand(TABLE_NAME, name) |
| ); |
| } |
| |
| static CatalogCommand createIndexCommand(String tableName, String indexName) { |
| return CreateHashIndexCommand.builder() |
| .schemaName(SCHEMA_NAME) |
| .indexName(indexName) |
| .tableName(tableName) |
| .columns(List.of("VAL")) |
| .build(); |
| } |
| |
| static CatalogCommand createTableCommand(String tableName) { |
| return createTableCommand(ZONE_NAME, tableName); |
| } |
| |
| static CatalogCommand createTableCommand(String zoneName, String tableName) { |
| return CreateTableCommand.builder() |
| .schemaName(SCHEMA_NAME) |
| .tableName(tableName) |
| .zone(zoneName) |
| .columns(List.of( |
| ColumnParams.builder().name("ID").type(INT32).build(), |
| ColumnParams.builder().name("VAL").type(INT32).build() |
| )) |
| .primaryKey(ID_PK) |
| .build(); |
| } |
| |
| static CatalogCommand createZoneCommand(String zoneName) { |
| return CreateZoneCommand.builder() |
| .zoneName(zoneName) |
| .storageProfilesParams(List.of(StorageProfileParams.builder().storageProfile(DEFAULT_STORAGE_PROFILE).build())) |
| .build(); |
| } |
| |
| static CatalogCommand createZoneCommand(String zoneName, List<String> storageProfiles) { |
| List<StorageProfileParams> params = storageProfiles.stream() |
| .map(p -> StorageProfileParams.builder().storageProfile(p).build()) |
| .collect(Collectors.toList()); |
| |
| return CreateZoneCommand.builder() |
| .zoneName(zoneName) |
| .storageProfilesParams(params) |
| .build(); |
| } |
| |
| static Catalog applyCommandsToCatalog(Catalog catalog, CatalogCommand... commandsToApply) { |
| for (CatalogCommand command : commandsToApply) { |
| for (UpdateEntry updates : command.get(catalog)) { |
| catalog = updates.applyUpdate(catalog, INITIAL_CAUSALITY_TOKEN); |
| } |
| } |
| |
| return catalog; |
| } |
| |
| static Catalog catalog(CatalogCommand... commandsToApply) { |
| return applyCommandsToCatalog(emptyCatalog(), commandsToApply); |
| } |
| |
| static Catalog catalog( |
| int version, |
| CatalogTableDescriptor[] tables, |
| CatalogIndexDescriptor[] indexes, |
| CatalogSystemViewDescriptor[] systemViews |
| ) { |
| return new Catalog( |
| version, |
| 0L, |
| 1, |
| List.of(DEFAULT_ZONE), |
| List.of(new CatalogSchemaDescriptor( |
| 0, |
| SCHEMA_NAME, |
| tables, |
| indexes, |
| systemViews, |
| INITIAL_CAUSALITY_TOKEN |
| )), |
| DEFAULT_ZONE.id()); |
| } |
| |
| static CatalogTableDescriptor table(int tableId, int schemaId, int zoneId, int pkIndexId, String columnName) { |
| return new CatalogTableDescriptor( |
| tableId, |
| schemaId, |
| pkIndexId, |
| "TEST_TABLE", |
| zoneId, |
| List.of(tableColumn(columnName)), |
| List.of(columnName), |
| null, |
| DEFAULT_STORAGE_PROFILE |
| ); |
| } |
| |
| static CatalogTableColumnDescriptor tableColumn(String columnName) { |
| return new CatalogTableColumnDescriptor(columnName, INT32, false, DEFAULT_PRECISION, DEFAULT_SCALE, DEFAULT_LENGTH, null); |
| } |
| |
| /** |
| * Transitions a given index from {@link CatalogIndexStatus#REGISTERED} to {@link CatalogIndexStatus#STOPPING} state. |
| * |
| * @throws NoSuchElementException if the given index does not exist. |
| */ |
| static Catalog transitionIndexToStoppingState(Catalog catalog, String indexName) { |
| int indexId = findIndex(catalog, indexName).id(); |
| |
| CatalogCommand startIndexBuildingCommand = StartBuildingIndexCommand.builder() |
| .indexId(indexId) |
| .build(); |
| |
| CatalogCommand makeIndexAvailableCommand = MakeIndexAvailableCommand.builder() |
| .indexId(indexId) |
| .build(); |
| |
| CatalogCommand dropIndexCommand = DropIndexCommand.builder() |
| .schemaName(SCHEMA_NAME) |
| .indexName(indexName) |
| .build(); |
| |
| catalog = applyCommandsToCatalog(catalog, startIndexBuildingCommand, makeIndexAvailableCommand, dropIndexCommand); |
| |
| assertThat(findIndex(catalog, indexName).status(), is(CatalogIndexStatus.STOPPING)); |
| |
| return catalog; |
| } |
| |
| /** Creates a primary key with a hash index that consists of the given columns. */ |
| static TablePrimaryKey primaryKey(String column, String... columns) { |
| List<String> pkColumns = new ArrayList<>(); |
| pkColumns.add(column); |
| pkColumns.addAll(Arrays.asList(columns)); |
| |
| return TableHashPrimaryKey.builder() |
| .columns(pkColumns) |
| .build(); |
| } |
| |
| private static CatalogIndexDescriptor findIndex(Catalog catalog, String indexName) { |
| return catalog.indexes().stream() |
| .filter(index -> index.name().equals(indexName)) |
| .findAny() |
| .orElseThrow(); |
| } |
| } |