This is an automated email from the ASF dual-hosted git repository. amashenkov pushed a commit to branch ignite-19460 in repository https://gitbox.apache.org/repos/asf/ignite-3.git
commit de2f6f45a5f84ea44dd010fcec70244a20f5c6db Author: amashenkov <andrey.mashen...@gmail.com> AuthorDate: Wed May 17 22:39:25 2023 +0300 Add create index command. Add drop index command. --- .../ignite/internal/catalog/CatalogManager.java | 18 ++++ .../ignite/internal/catalog/CatalogService.java | 2 + .../internal/catalog/CatalogServiceImpl.java | 87 +++++++++++++++ ...Params.java => AbstractIndexCommandParams.java} | 60 +++++++---- .../commands/AbstractTableCommandParams.java | 10 +- .../internal/catalog/commands/CatalogUtils.java | 34 ++++++ .../catalog/commands/CreateIndexParams.java | 118 ++++++++++++++++++++ .../internal/catalog/commands/DropIndexParams.java | 36 +++++++ .../catalog/descriptors/HashIndexDescriptor.java | 5 +- .../catalog/descriptors/IndexDescriptor.java | 2 +- .../catalog/descriptors/SortedIndexDescriptor.java | 5 +- .../catalog/events/CreateIndexEventParameters.java | 47 ++++++++ .../catalog/events/DropIndexEventParameters.java | 43 ++++++++ .../DropIndexEntry.java} | 45 +++----- .../NewIndexEntry.java} | 46 ++++---- .../internal/catalog/CatalogServiceSelfTest.java | 120 +++++++++++++++++++++ .../engine/exec/ddl/DdlCommandHandlerWrapper.java | 14 +++ .../exec/ddl/DdlToCatalogCommandConverter.java | 45 ++++++++ .../distributed/schema/FullTableSchemaTest.java | 2 +- 19 files changed, 654 insertions(+), 85 deletions(-) diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogManager.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogManager.java index 2724d5d965..72da2432d1 100644 --- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogManager.java +++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogManager.java @@ -20,7 +20,9 @@ package org.apache.ignite.internal.catalog; import java.util.concurrent.CompletableFuture; import org.apache.ignite.internal.catalog.commands.AlterTableAddColumnParams; import org.apache.ignite.internal.catalog.commands.AlterTableDropColumnParams; +import org.apache.ignite.internal.catalog.commands.CreateIndexParams; import org.apache.ignite.internal.catalog.commands.CreateTableParams; +import org.apache.ignite.internal.catalog.commands.DropIndexParams; import org.apache.ignite.internal.catalog.commands.DropTableParams; import org.apache.ignite.internal.manager.IgniteComponent; @@ -59,4 +61,20 @@ public interface CatalogManager extends IgniteComponent, CatalogService { * @return Operation future. */ CompletableFuture<Void> dropColumn(AlterTableDropColumnParams params); + + /** + * Creates new index. + * + * @param params Parameters. + * @return Operation future. + */ + CompletableFuture<Void> createIndex(CreateIndexParams params); + + /** + * Drops index. + * + * @param params Parameters. + * @return Operation future. + */ + CompletableFuture<Void> dropIndex(DropIndexParams params); } diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogService.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogService.java index 70e70aaf38..629078cdfc 100644 --- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogService.java +++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogService.java @@ -41,6 +41,8 @@ public interface CatalogService { TableDescriptor table(int tableId, long timestamp); + IndexDescriptor index(String indexName, long timestamp); + IndexDescriptor index(int indexId, long timestamp); Collection<IndexDescriptor> tableIndexes(int tableId, long timestamp); diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogServiceImpl.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogServiceImpl.java index 4920647d4a..d3570ec8c3 100644 --- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogServiceImpl.java +++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogServiceImpl.java @@ -32,7 +32,9 @@ import java.util.concurrent.ConcurrentSkipListMap; import org.apache.ignite.internal.catalog.commands.AlterTableAddColumnParams; import org.apache.ignite.internal.catalog.commands.AlterTableDropColumnParams; import org.apache.ignite.internal.catalog.commands.CatalogUtils; +import org.apache.ignite.internal.catalog.commands.CreateIndexParams; import org.apache.ignite.internal.catalog.commands.CreateTableParams; +import org.apache.ignite.internal.catalog.commands.DropIndexParams; import org.apache.ignite.internal.catalog.commands.DropTableParams; import org.apache.ignite.internal.catalog.descriptors.IndexDescriptor; import org.apache.ignite.internal.catalog.descriptors.SchemaDescriptor; @@ -41,7 +43,9 @@ import org.apache.ignite.internal.catalog.events.CatalogEvent; import org.apache.ignite.internal.catalog.events.CatalogEventParameters; import org.apache.ignite.internal.catalog.events.CreateTableEventParameters; import org.apache.ignite.internal.catalog.events.DropTableEventParameters; +import org.apache.ignite.internal.catalog.storage.DropIndexEntry; import org.apache.ignite.internal.catalog.storage.DropTableEntry; +import org.apache.ignite.internal.catalog.storage.NewIndexEntry; import org.apache.ignite.internal.catalog.storage.NewTableEntry; import org.apache.ignite.internal.catalog.storage.ObjectIdGenUpdateEntry; import org.apache.ignite.internal.catalog.storage.UpdateEntry; @@ -55,6 +59,8 @@ import org.apache.ignite.internal.util.ArrayUtils; import org.apache.ignite.internal.util.PendingComparableValuesTracker; import org.apache.ignite.lang.ErrorGroups.Common; import org.apache.ignite.lang.IgniteInternalException; +import org.apache.ignite.lang.IndexAlreadyExistsException; +import org.apache.ignite.lang.IndexNotFoundException; import org.apache.ignite.lang.TableAlreadyExistsException; import org.apache.ignite.lang.TableNotFoundException; import org.jetbrains.annotations.Nullable; @@ -117,6 +123,12 @@ public class CatalogServiceImpl extends Producer<CatalogEvent, CatalogEventParam return catalogAt(timestamp).table(tableId); } + /** {@inheritDoc} */ + @Override + public IndexDescriptor index(String indexName, long timestamp) { + return catalogAt(timestamp).schema(PUBLIC).index(indexName); + } + /** {@inheritDoc} */ @Override public IndexDescriptor index(int indexId, long timestamp) { @@ -214,6 +226,53 @@ public class CatalogServiceImpl extends Producer<CatalogEvent, CatalogEventParam return failedFuture(new UnsupportedOperationException("Not implemented yet.")); } + /** {@inheritDoc} */ + @Override + public CompletableFuture<Void> createIndex(CreateIndexParams params) { + return saveUpdate(catalog -> { + String schemaName = Objects.requireNonNullElse(params.schemaName(), CatalogService.PUBLIC); + + SchemaDescriptor schema = Objects.requireNonNull(catalog.schema(schemaName), "No schema found: " + schemaName); + + if (schema.index(params.indexName()) != null) { + throw new IndexAlreadyExistsException(schemaName, params.indexName()); + } + + TableDescriptor table = schema.table(params.tableName()); + + if (table == null) { + throw new TableNotFoundException(schemaName, params.tableName()); + } + + IndexDescriptor index = CatalogUtils.fromParams(catalog.objectIdGenState(), table.id(), params); + + return List.of( + new NewIndexEntry(index), + new ObjectIdGenUpdateEntry(1) + ); + }); + } + + /** {@inheritDoc} */ + @Override + public CompletableFuture<Void> dropIndex(DropIndexParams params) { + return saveUpdate(catalog -> { + String schemaName = Objects.requireNonNullElse(params.schemaName(), CatalogService.PUBLIC); + + SchemaDescriptor schema = Objects.requireNonNull(catalog.schema(schemaName), "No schema found: " + schemaName); + + IndexDescriptor index = schema.index(params.indexName()); + + if (index == null) { + throw new IndexNotFoundException(schemaName, params.indexName()); + } + + return List.of( + new DropIndexEntry(index.id()) + ); + }); + } + private void registerCatalog(Catalog newCatalog) { catalogByVer.put(newCatalog.version(), newCatalog); catalogByTs.put(newCatalog.time(), newCatalog); @@ -308,6 +367,34 @@ public class CatalogServiceImpl extends Producer<CatalogEvent, CatalogEventParam new DropTableEventParameters(version, tableId) )); + } else if (entry instanceof NewIndexEntry) { + catalog = new Catalog( + version, + System.currentTimeMillis(), + catalog.objectIdGenState(), + new SchemaDescriptor( + schema.id(), + schema.name(), + version, + schema.tables(), + ArrayUtils.concat(schema.indexes(), ((NewIndexEntry) entry).descriptor()) + ) + ); + } else if (entry instanceof DropIndexEntry) { + int indexId = ((DropIndexEntry) entry).indexId(); + + catalog = new Catalog( + version, + System.currentTimeMillis(), + catalog.objectIdGenState(), + new SchemaDescriptor( + schema.id(), + schema.name(), + version, + schema.tables(), + Arrays.stream(schema.indexes()).filter(t -> t.id() != indexId).toArray(IndexDescriptor[]::new) + ) + ); } else if (entry instanceof ObjectIdGenUpdateEntry) { catalog = new Catalog( version, diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractTableCommandParams.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractIndexCommandParams.java similarity index 62% copy from modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractTableCommandParams.java copy to modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractIndexCommandParams.java index 0aa2b8ecfc..681fb51293 100644 --- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractTableCommandParams.java +++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractIndexCommandParams.java @@ -18,37 +18,46 @@ package org.apache.ignite.internal.catalog.commands; /** - * Abstract table ddl command. + * Abstract index ddl command. */ -public class AbstractTableCommandParams implements DdlCommandParams { - /** Table name. */ - protected String tableName; +public class AbstractIndexCommandParams implements DdlCommandParams { + /** Index name. */ + protected String indexName; - /** Quietly ignore this command if table is not exists. */ - protected boolean ifTableExists; - - /** Schema name where this new table will be created. */ + /** Schema name where this new index will be created. */ protected String schema; - public String tableName() { - return tableName; + /** Unique index flag. */ + protected boolean unique; + + /** Quietly ignore this command if index existence check failed. */ + protected boolean ifIndexExists; + + /** + * Returns index simple name. + */ + public String indexName() { + return indexName; } + /** + * Returns schema name. + */ public String schemaName() { return schema; } /** - * Quietly ignore if table is not exist. + * Returns {@code true} if index is unique, {@code false} otherwise. */ - public boolean ifTableExists() { - return ifTableExists; + public boolean isUnique() { + return unique; } /** * Parameters builder. */ - protected abstract static class AbstractBuilder<ParamT extends AbstractTableCommandParams, BuilderT> { + protected abstract static class AbstractBuilder<ParamT extends AbstractIndexCommandParams, BuilderT> { protected ParamT params; AbstractBuilder(ParamT params) { @@ -67,23 +76,32 @@ public class AbstractTableCommandParams implements DdlCommandParams { } /** - * Sets table schema. + * Sets index simple name. * - * @param tableName Table name. + * @param indexName Index simple name. * @return {@code this}. */ - public BuilderT tableName(String tableName) { - params.tableName = tableName; + public BuilderT indexName(String indexName) { + params.indexName = indexName; return (BuilderT) this; } /** * Set quietly ignore flag. * - * @param ifTableNotExists Flag. + * @param ifIndexExists Flag. + */ + public BuilderT ifIndexExists(boolean ifIndexExists) { + params.ifIndexExists = ifIndexExists; + + return (BuilderT) this; + } + + /** + * Sets unique flag. */ - public BuilderT ifTableExists(boolean ifTableNotExists) { - params.ifTableExists = ifTableNotExists; + public BuilderT unique() { + params.unique = true; return (BuilderT) this; } diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractTableCommandParams.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractTableCommandParams.java index 0aa2b8ecfc..d42f6b2463 100644 --- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractTableCommandParams.java +++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractTableCommandParams.java @@ -30,10 +30,16 @@ public class AbstractTableCommandParams implements DdlCommandParams { /** Schema name where this new table will be created. */ protected String schema; + /** + * Returns table simple name. + */ public String tableName() { return tableName; } + /** + * Returns schema name. + */ public String schemaName() { return schema; } @@ -67,9 +73,9 @@ public class AbstractTableCommandParams implements DdlCommandParams { } /** - * Sets table schema. + * Sets table simple name. * - * @param tableName Table name. + * @param tableName Table simple name. * @return {@code this}. */ public BuilderT tableName(String tableName) { diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CatalogUtils.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CatalogUtils.java index 1ead944291..991c90f2ae 100644 --- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CatalogUtils.java +++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CatalogUtils.java @@ -17,7 +17,13 @@ package org.apache.ignite.internal.catalog.commands; +import java.util.List; import java.util.stream.Collectors; +import java.util.stream.IntStream; +import org.apache.ignite.internal.catalog.descriptors.HashIndexDescriptor; +import org.apache.ignite.internal.catalog.descriptors.IndexColumnDescriptor; +import org.apache.ignite.internal.catalog.descriptors.IndexDescriptor; +import org.apache.ignite.internal.catalog.descriptors.SortedIndexDescriptor; import org.apache.ignite.internal.catalog.descriptors.TableColumnDescriptor; import org.apache.ignite.internal.catalog.descriptors.TableDescriptor; @@ -41,6 +47,34 @@ public class CatalogUtils { ); } + /** + * Converts CreateIndex command params to descriptor. + * + * @param id Index id. + * @param tableId Table id. + * @param params Parameters. + * @return Index descriptor. + */ + public static IndexDescriptor fromParams(int id, int tableId, CreateIndexParams params) { + switch (params.type()) { + case HASH: + return new HashIndexDescriptor(id, + params.indexName(), + tableId, + params.isUnique(), + params.columns() + ); + case SORTED: + List<IndexColumnDescriptor> columnDescriptors = IntStream.range(0, params.collations().size()) + .mapToObj(i -> new IndexColumnDescriptor(params.columns().get(i), params.collations().get(i))) + .collect(Collectors.toList()); + return new SortedIndexDescriptor(id, params.indexName(), tableId, params.isUnique(), columnDescriptors); + default: + throw new IllegalArgumentException("Unsupported index type: " + params.type()); + } + + } + private static TableColumnDescriptor fromParams(ColumnParams params) { return new TableColumnDescriptor(params.name(), params.type(), params.nullable(), params.defaultValueDefinition()); } diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateIndexParams.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateIndexParams.java new file mode 100644 index 0000000000..c3212cf338 --- /dev/null +++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateIndexParams.java @@ -0,0 +1,118 @@ +/* + * 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 java.util.List; +import org.apache.ignite.internal.catalog.descriptors.ColumnCollation; + +/** + * CREATE INDEX statement. + */ +public class CreateIndexParams extends AbstractIndexCommandParams { + public static Builder builder() { + return new Builder(); + } + + /** Type of the index to create. */ + public enum Type { + SORTED, HASH + } + + /** Table name. */ + private String tableName; + + private Type type; + + private List<String> columns; + + private List<ColumnCollation> collations; + + public List<String> columns() { + return columns; + } + + public List<ColumnCollation> collations() { + return collations; + } + + public Type type() { + return type; + } + + public String tableName() { + return tableName; + } + + /** + * Parameters builder. + */ + public static class Builder extends AbstractIndexCommandParams.AbstractBuilder<CreateIndexParams, CreateIndexParams.Builder> { + private Builder() { + super(new CreateIndexParams()); + } + + /** + * Set index type. + * + * @param type Index type. + * @return {@code this}. + */ + public Builder type(Type type) { + params.type = type; + + return this; + } + + /** + * Set table name. + * + * @param tableName Table name. + * @return {@code this}. + */ + public Builder tableName(String tableName) { + params.tableName = tableName; + + return this; + } + + /** + * Set columns names. + * + * @param columns Columns names. + * @return {@code this}. + */ + public Builder columns(List<String> columns) { + params.columns = columns; + + return this; + } + + /** + * Set columns collations. + * + * @param collations Columns collations. + * @return {@code this}. + */ + public Builder collations(List<ColumnCollation> collations) { + params.collations = collations; + + return this; + } + + } +} diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropIndexParams.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropIndexParams.java new file mode 100644 index 0000000000..aeefaabb1c --- /dev/null +++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropIndexParams.java @@ -0,0 +1,36 @@ +/* + * 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; + +/** + * DROP INDEX statement. + */ +public class DropIndexParams extends AbstractIndexCommandParams { + public static Builder builder() { + return new Builder(); + } + + /** + * Parameters builder. + */ + public static class Builder extends AbstractBuilder<DropIndexParams, Builder> { + Builder() { + super(new DropIndexParams()); + } + } +} diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/HashIndexDescriptor.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/HashIndexDescriptor.java index 8e2d96c3da..f486a5bdfc 100644 --- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/HashIndexDescriptor.java +++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/HashIndexDescriptor.java @@ -36,11 +36,12 @@ public class HashIndexDescriptor extends IndexDescriptor { * @param id Id of the index. * @param name Name of the index. * @param tableId Id of the table index belongs to. + * @param unique Unique flag. * @param columns A list of indexed columns. Must not contains duplicates. * @throws IllegalArgumentException If columns list contains duplicates. */ - public HashIndexDescriptor(int id, String name, int tableId, List<String> columns) { - super(id, name, tableId, true); + public HashIndexDescriptor(int id, String name, int tableId, boolean unique, List<String> columns) { + super(id, name, tableId, unique); this.columns = List.copyOf(Objects.requireNonNull(columns, "columns")); diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/IndexDescriptor.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/IndexDescriptor.java index 99feaf15e3..cf9609162d 100644 --- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/IndexDescriptor.java +++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/IndexDescriptor.java @@ -29,7 +29,7 @@ public abstract class IndexDescriptor extends ObjectDescriptor { private final int tableId; /** Unique constraint flag. */ - private boolean unique; + private final boolean unique; /** Write only flag. {@code True} when index is building. */ private boolean writeOnly; diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/SortedIndexDescriptor.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/SortedIndexDescriptor.java index cfc59ee4c4..c2fb6016ec 100644 --- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/SortedIndexDescriptor.java +++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/SortedIndexDescriptor.java @@ -36,11 +36,12 @@ public class SortedIndexDescriptor extends IndexDescriptor { * @param id Id of the index. * @param name Name of the index. * @param tableId Id of the table index belongs to. + * @param unique Unique flag. * @param columns A list of columns descriptors. * @throws IllegalArgumentException If columns list contains duplicates or columns size doesn't match the collations size. */ - public SortedIndexDescriptor(int id, String name, int tableId, List<IndexColumnDescriptor> columns) { - super(id, name, tableId, false); + public SortedIndexDescriptor(int id, String name, int tableId, boolean unique, List<IndexColumnDescriptor> columns) { + super(id, name, tableId, unique); this.columns = Objects.requireNonNull(columns, "columns"); } diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/events/CreateIndexEventParameters.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/events/CreateIndexEventParameters.java new file mode 100644 index 0000000000..673834eac7 --- /dev/null +++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/events/CreateIndexEventParameters.java @@ -0,0 +1,47 @@ +/* + * 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.events; + +import org.apache.ignite.internal.catalog.descriptors.IndexDescriptor; + +/** + * Create index event parameters contains a index descriptor for newly created index. + */ +public class CreateIndexEventParameters extends CatalogEventParameters { + + private final IndexDescriptor indexDescriptor; + + /** + * Constructor. + * + * @param causalityToken Causality token. + * @param indexDescriptor Newly created index descriptor. + */ + public CreateIndexEventParameters(long causalityToken, IndexDescriptor indexDescriptor) { + super(causalityToken); + + this.indexDescriptor = indexDescriptor; + } + + /** + * Gets index descriptor for newly created index. + */ + public IndexDescriptor indexDescriptor() { + return indexDescriptor; + } +} diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/events/DropIndexEventParameters.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/events/DropIndexEventParameters.java new file mode 100644 index 0000000000..0a79fe625e --- /dev/null +++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/events/DropIndexEventParameters.java @@ -0,0 +1,43 @@ +/* + * 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.events; + +/** + * Drop index event parameters contains an id of dropped index. + */ +public class DropIndexEventParameters extends CatalogEventParameters { + + private final int indexId; + + /** + * Constructor. + * + * @param causalityToken Causality token. + * @param indexId An id of dropped index. + */ + public DropIndexEventParameters(long causalityToken, int indexId) { + super(causalityToken); + + this.indexId = indexId; + } + + /** Returns an id of dropped index. */ + public int indexId() { + return indexId; + } +} diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/IndexDescriptor.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropIndexEntry.java similarity index 55% copy from modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/IndexDescriptor.java copy to modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropIndexEntry.java index 99feaf15e3..4336b74a9e 100644 --- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/IndexDescriptor.java +++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropIndexEntry.java @@ -15,41 +15,30 @@ * limitations under the License. */ -package org.apache.ignite.internal.catalog.descriptors; +package org.apache.ignite.internal.catalog.storage; import org.apache.ignite.internal.tostring.S; /** - * Index descriptor base class. + * Describes deletion of a index. */ -public abstract class IndexDescriptor extends ObjectDescriptor { - private static final long serialVersionUID = -8045949593661301287L; - - /** Table id. */ - private final int tableId; - - /** Unique constraint flag. */ - private boolean unique; - - /** Write only flag. {@code True} when index is building. */ - private boolean writeOnly; - - IndexDescriptor(int id, String name, int tableId, boolean unique) { - super(id, Type.INDEX, name); - this.tableId = tableId; - this.unique = unique; - } - - public int tableId() { - return tableId; - } - - public boolean unique() { - return unique; +public class DropIndexEntry implements UpdateEntry { + private static final long serialVersionUID = -604729846502020728L; + + private final int indexId; + + /** + * Constructs the object. + * + * @param indexId An id of a index to drop. + */ + public DropIndexEntry(int indexId) { + this.indexId = indexId; } - public boolean writeOnly() { - return writeOnly; + /** Returns an id of a index to drop. */ + public int indexId() { + return indexId; } /** {@inheritDoc} */ diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/IndexDescriptor.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewIndexEntry.java similarity index 55% copy from modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/IndexDescriptor.java copy to modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewIndexEntry.java index 99feaf15e3..1d67e7631a 100644 --- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/IndexDescriptor.java +++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewIndexEntry.java @@ -15,41 +15,31 @@ * limitations under the License. */ -package org.apache.ignite.internal.catalog.descriptors; +package org.apache.ignite.internal.catalog.storage; +import org.apache.ignite.internal.catalog.descriptors.IndexDescriptor; import org.apache.ignite.internal.tostring.S; /** - * Index descriptor base class. + * Describes addition of a new index. */ -public abstract class IndexDescriptor extends ObjectDescriptor { - private static final long serialVersionUID = -8045949593661301287L; - - /** Table id. */ - private final int tableId; - - /** Unique constraint flag. */ - private boolean unique; - - /** Write only flag. {@code True} when index is building. */ - private boolean writeOnly; - - IndexDescriptor(int id, String name, int tableId, boolean unique) { - super(id, Type.INDEX, name); - this.tableId = tableId; - this.unique = unique; - } - - public int tableId() { - return tableId; - } - - public boolean unique() { - return unique; +public class NewIndexEntry implements UpdateEntry { + private static final long serialVersionUID = 6717363577013237711L; + + private final IndexDescriptor descriptor; + + /** + * Constructs the object. + * + * @param descriptor A descriptor of a index to add. + */ + public NewIndexEntry(IndexDescriptor descriptor) { + this.descriptor = descriptor; } - public boolean writeOnly() { - return writeOnly; + /** Returns descriptor of a index to add. */ + public IndexDescriptor descriptor() { + return descriptor; } /** {@inheritDoc} */ diff --git a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogServiceSelfTest.java b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogServiceSelfTest.java index 003ab864f1..a108bd4e6b 100644 --- a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogServiceSelfTest.java +++ b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogServiceSelfTest.java @@ -23,11 +23,13 @@ import static org.apache.ignite.internal.testframework.matchers.CompletableFutur import static org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willBe; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.times; @@ -38,10 +40,15 @@ import static org.mockito.Mockito.when; import java.util.List; import java.util.concurrent.CompletableFuture; import org.apache.ignite.internal.catalog.commands.ColumnParams; +import org.apache.ignite.internal.catalog.commands.CreateIndexParams; +import org.apache.ignite.internal.catalog.commands.CreateIndexParams.Type; import org.apache.ignite.internal.catalog.commands.CreateTableParams; import org.apache.ignite.internal.catalog.commands.DefaultValue; import org.apache.ignite.internal.catalog.commands.DropTableParams; +import org.apache.ignite.internal.catalog.descriptors.ColumnCollation; +import org.apache.ignite.internal.catalog.descriptors.HashIndexDescriptor; import org.apache.ignite.internal.catalog.descriptors.SchemaDescriptor; +import org.apache.ignite.internal.catalog.descriptors.SortedIndexDescriptor; import org.apache.ignite.internal.catalog.descriptors.TableDescriptor; import org.apache.ignite.internal.catalog.events.CatalogEvent; import org.apache.ignite.internal.catalog.events.CatalogEventParameters; @@ -59,6 +66,7 @@ import org.apache.ignite.internal.metastorage.server.SimpleInMemoryKeyValueStora import org.apache.ignite.internal.vault.VaultManager; import org.apache.ignite.internal.vault.inmemory.InMemoryVaultService; import org.apache.ignite.lang.IgniteInternalException; +import org.apache.ignite.lang.IndexAlreadyExistsException; import org.apache.ignite.lang.NodeStoppingException; import org.apache.ignite.lang.TableAlreadyExistsException; import org.apache.ignite.lang.TableNotFoundException; @@ -76,6 +84,7 @@ import org.mockito.Mockito; public class CatalogServiceSelfTest { private static final String TABLE_NAME = "myTable"; private static final String TABLE_NAME_2 = "myTable2"; + private static final String INDEX_NAME = "myIndex"; private MetaStorageManager metastore; @@ -305,6 +314,117 @@ public class CatalogServiceSelfTest { assertThat(service.dropTable(params), willThrowFast(TableNotFoundException.class)); } + @Test + public void testCreateHashIndex() { + assertThat(service.createTable(simpleTable(TABLE_NAME)), willBe((Object) null)); + + CreateIndexParams params = CreateIndexParams.builder() + .indexName(INDEX_NAME) + .tableName(TABLE_NAME) + .type(Type.HASH) + .columns(List.of("VAL", "ID")) + .build(); + + assertThat(service.createIndex(params), willBe((Object) null)); + + // Validate catalog version from the past. + SchemaDescriptor schema = service.schema(1); + + assertNotNull(schema); + assertNull(schema.index(INDEX_NAME)); + assertNull(service.index(INDEX_NAME, 123L)); + assertNull(service.index(2, 123L)); + + // Validate actual catalog + schema = service.schema(2); + + assertNotNull(schema); + assertNull(service.index(1, System.currentTimeMillis())); + assertSame(schema.index(INDEX_NAME), service.index(INDEX_NAME, System.currentTimeMillis())); + assertSame(schema.index(INDEX_NAME), service.index(2, System.currentTimeMillis())); + + // Validate newly created hash index + HashIndexDescriptor index = (HashIndexDescriptor) schema.index(INDEX_NAME); + + assertEquals(2L, index.id()); + assertEquals(INDEX_NAME, index.name()); + assertEquals(schema.table(TABLE_NAME).id(), index.tableId()); + assertEquals(List.of("VAL", "ID"), index.columns()); + assertFalse(index.unique()); + assertFalse(index.writeOnly()); + } + + @Test + public void testCreateSortedIndex() { + assertThat(service.createTable(simpleTable(TABLE_NAME)), willBe((Object) null)); + + CreateIndexParams params = CreateIndexParams.builder() + .indexName(INDEX_NAME) + .tableName(TABLE_NAME) + .type(Type.SORTED) + .unique() + .columns(List.of("VAL", "ID")) + .collations(List.of(ColumnCollation.DESC_NULLS_FIRST, ColumnCollation.ASC_NULLS_LAST)) + .build(); + + assertThat(service.createIndex(params), willBe((Object) null)); + + // Validate catalog version from the past. + SchemaDescriptor schema = service.schema(1); + + assertNotNull(schema); + assertNull(schema.index(INDEX_NAME)); + assertNull(service.index(INDEX_NAME, 123L)); + assertNull(service.index(2, 123L)); + + // Validate actual catalog + schema = service.schema(2); + + assertNotNull(schema); + assertNull(service.index(1, System.currentTimeMillis())); + assertSame(schema.index(INDEX_NAME), service.index(INDEX_NAME, System.currentTimeMillis())); + assertSame(schema.index(INDEX_NAME), service.index(2, System.currentTimeMillis())); + + // Validate newly created sorted index + SortedIndexDescriptor index = (SortedIndexDescriptor) schema.index(INDEX_NAME); + + assertEquals(2L, index.id()); + assertEquals(INDEX_NAME, index.name()); + assertEquals(schema.table(TABLE_NAME).id(), index.tableId()); + assertEquals("VAL", index.columns().get(0).name()); + assertEquals("ID", index.columns().get(1).name()); + assertEquals(ColumnCollation.DESC_NULLS_FIRST, index.columns().get(0).collation()); + assertEquals(ColumnCollation.ASC_NULLS_LAST, index.columns().get(1).collation()); + assertTrue(index.unique()); + assertFalse(index.writeOnly()); + } + + @Test + public void testCreateIndexIfExistsFlag() { + assertThat(service.createTable(simpleTable(TABLE_NAME)), willBe((Object) null)); + + CreateIndexParams params = CreateIndexParams.builder() + .indexName(INDEX_NAME) + .tableName(TABLE_NAME) + .type(Type.HASH) + .columns(List.of("VAL")) + .ifIndexExists(true) + .build(); + + assertThat(service.createIndex(params), willBe((Object) null)); + assertThat(service.createIndex(params), willThrow(IndexAlreadyExistsException.class)); + + params = CreateIndexParams.builder() + .indexName(INDEX_NAME) + .tableName(TABLE_NAME) + .type(Type.HASH) + .columns(List.of("VAL")) + .ifIndexExists(false) + .build(); + + assertThat(service.createIndex(params), willThrow(IndexAlreadyExistsException.class)); + } + @Test public void operationWillBeRetriedFiniteAmountOfTimes() { UpdateLog updateLogMock = Mockito.mock(UpdateLog.class); diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ddl/DdlCommandHandlerWrapper.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ddl/DdlCommandHandlerWrapper.java index 21f39ea169..2a427d4619 100644 --- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ddl/DdlCommandHandlerWrapper.java +++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ddl/DdlCommandHandlerWrapper.java @@ -22,11 +22,15 @@ import java.util.concurrent.CompletableFuture; import org.apache.ignite.internal.catalog.CatalogManager; import org.apache.ignite.internal.distributionzones.DistributionZoneManager; import org.apache.ignite.internal.index.IndexManager; +import org.apache.ignite.internal.sql.engine.prepare.ddl.CreateIndexCommand; import org.apache.ignite.internal.sql.engine.prepare.ddl.CreateTableCommand; import org.apache.ignite.internal.sql.engine.prepare.ddl.DdlCommand; +import org.apache.ignite.internal.sql.engine.prepare.ddl.DropIndexCommand; import org.apache.ignite.internal.sql.engine.prepare.ddl.DropTableCommand; import org.apache.ignite.internal.storage.DataStorageManager; import org.apache.ignite.internal.table.distributed.TableManager; +import org.apache.ignite.lang.IndexAlreadyExistsException; +import org.apache.ignite.lang.IndexNotFoundException; import org.apache.ignite.lang.TableAlreadyExistsException; import org.apache.ignite.lang.TableNotFoundException; @@ -70,6 +74,16 @@ public class DdlCommandHandlerWrapper extends DdlCommandHandler { .thenCompose(res -> catalogManager.dropTable(DdlToCatalogCommandConverter.convert((DropTableCommand) cmd)) .handle(handleModificationResult(((DropTableCommand) cmd).ifTableExists(), TableNotFoundException.class)) ); + } else if (cmd instanceof CreateIndexCommand) { + return ddlCommandFuture + .thenCompose(res -> catalogManager.createIndex(DdlToCatalogCommandConverter.convert((CreateIndexCommand) cmd)) + .handle(handleModificationResult(((CreateIndexCommand) cmd).ifNotExists(), IndexAlreadyExistsException.class)) + ); + } else if (cmd instanceof DropIndexCommand) { + return ddlCommandFuture + .thenCompose(res -> catalogManager.dropIndex(DdlToCatalogCommandConverter.convert((DropIndexCommand) cmd)) + .handle(handleModificationResult(((DropIndexCommand) cmd).ifNotExists(), IndexNotFoundException.class)) + ); } return ddlCommandFuture; diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ddl/DdlToCatalogCommandConverter.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ddl/DdlToCatalogCommandConverter.java index e0e9f4f520..4a4cc96d82 100644 --- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ddl/DdlToCatalogCommandConverter.java +++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ddl/DdlToCatalogCommandConverter.java @@ -20,13 +20,20 @@ package org.apache.ignite.internal.sql.engine.exec.ddl; import java.util.List; import java.util.stream.Collectors; import org.apache.ignite.internal.catalog.commands.ColumnParams; +import org.apache.ignite.internal.catalog.commands.CreateIndexParams; import org.apache.ignite.internal.catalog.commands.CreateTableParams; import org.apache.ignite.internal.catalog.commands.DefaultValue; +import org.apache.ignite.internal.catalog.commands.DropIndexParams; import org.apache.ignite.internal.catalog.commands.DropTableParams; +import org.apache.ignite.internal.catalog.descriptors.ColumnCollation; import org.apache.ignite.internal.sql.engine.prepare.ddl.ColumnDefinition; +import org.apache.ignite.internal.sql.engine.prepare.ddl.CreateIndexCommand; +import org.apache.ignite.internal.sql.engine.prepare.ddl.CreateIndexCommand.Type; import org.apache.ignite.internal.sql.engine.prepare.ddl.CreateTableCommand; import org.apache.ignite.internal.sql.engine.prepare.ddl.DefaultValueDefinition; +import org.apache.ignite.internal.sql.engine.prepare.ddl.DropIndexCommand; import org.apache.ignite.internal.sql.engine.prepare.ddl.DropTableCommand; +import org.apache.ignite.internal.sql.engine.schema.IgniteIndex; import org.apache.ignite.internal.sql.engine.util.TypeUtils; /** @@ -56,6 +63,29 @@ class DdlToCatalogCommandConverter { .build(); } + static CreateIndexParams convert(CreateIndexCommand cmd) { + List<ColumnCollation> collations = cmd.collations() == null ? null + : cmd.collations().stream().map(DdlToCatalogCommandConverter::convert).collect(Collectors.toList()); + + return CreateIndexParams.builder() + .schemaName(cmd.schemaName()) + .indexName(cmd.indexName()) + + .tableName(cmd.tableName()) + .type(convert(cmd.type())) + .columns(cmd.columns()) + .collations(collations) + + .build(); + } + + static DropIndexParams convert(DropIndexCommand cmd) { + return DropIndexParams.builder() + .schemaName(cmd.schemaName()) + .indexName(cmd.indexName()) + .build(); + } + private static ColumnParams convert(ColumnDefinition def) { return new ColumnParams(def.name(), TypeUtils.columnType(def.type()), convert(def.defaultValueDefinition()), def.nullable()); } @@ -72,4 +102,19 @@ class DdlToCatalogCommandConverter { throw new IllegalArgumentException("Default value definition: " + def.type()); } } + + private static CreateIndexParams.Type convert(Type type) { + switch (type) { + case SORTED: + return CreateIndexParams.Type.SORTED; + case HASH: + return CreateIndexParams.Type.HASH; + default: + throw new IllegalArgumentException("Unsupported index type: " + type); + } + } + + private static ColumnCollation convert(IgniteIndex.Collation collation) { + return ColumnCollation.get(collation.asc, collation.nullsFirst); + } } diff --git a/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/schema/FullTableSchemaTest.java b/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/schema/FullTableSchemaTest.java index fbe2ce2830..ae1bd9276c 100644 --- a/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/schema/FullTableSchemaTest.java +++ b/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/schema/FullTableSchemaTest.java @@ -47,7 +47,7 @@ class FullTableSchemaTest { @NotNull private static HashIndexDescriptor someIndex(int id, String name) { - return new HashIndexDescriptor(id, name, 1, List.of("a")); + return new HashIndexDescriptor(id, name, 1, true, List.of("a")); } @NotNull