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 6e67aa031e IGNITE-20371 Move handlers of index-related commands from 
CatalogManager (#2563)
6e67aa031e is described below

commit 6e67aa031e4a4c2a0ecf1d0aef4b030dd5bf90b1
Author: korlov42 <[email protected]>
AuthorDate: Tue Sep 12 13:24:12 2023 +0300

    IGNITE-20371 Move handlers of index-related commands from CatalogManager 
(#2563)
---
 .../ignite/internal/catalog/CatalogManager.java    |  27 ---
 .../internal/catalog/CatalogManagerImpl.java       | 130 ------------
 .../catalog/CatalogParamsValidationUtils.java      |  79 +-------
 ...nd.java => IndexExistsValidationException.java} |  16 +-
 ....java => IndexNotFoundValidationException.java} |  15 +-
 .../commands/AbstractCreateIndexCommand.java       | 109 ++++++++++
 ...java => AbstractCreateIndexCommandBuilder.java} |  25 +--
 .../commands/AbstractCreateIndexCommandParams.java |  92 ---------
 ...TableCommand.java => AbstractIndexCommand.java} |  17 +-
 ...mmand.java => AbstractIndexCommandBuilder.java} |  17 +-
 .../commands/AbstractIndexCommandParams.java       |  80 --------
 .../catalog/commands/AbstractTableCommand.java     |   3 +-
 .../commands/AbstractTableCommandParams.java       |  79 --------
 .../internal/catalog/commands/CatalogUtils.java    |  39 ----
 .../catalog/commands/CreateHashIndexCommand.java   | 106 ++++++++++
 ...ams.java => CreateHashIndexCommandBuilder.java} |  25 +--
 .../catalog/commands/CreateSortedIndexCommand.java | 145 ++++++++++++++
 ...s.java => CreateSortedIndexCommandBuilder.java} |  28 ++-
 .../catalog/commands/CreateSortedIndexParams.java  |  61 ------
 .../catalog/commands/CreateTableCommand.java       |   1 +
 .../catalog/commands/DropIndexCommand.java         | 106 ++++++++++
 ...ogCommand.java => DropIndexCommandBuilder.java} |  12 +-
 .../descriptors/CatalogTableDescriptor.java        |   9 +
 .../internal/catalog/storage/AlterColumnEntry.java |   1 +
 .../internal/catalog/storage/DropColumnsEntry.java |   1 +
 .../internal/catalog/storage/NewColumnsEntry.java  |   1 +
 .../internal/catalog/CatalogManagerSelfTest.java   |  91 ++++-----
 .../catalog/CatalogManagerValidationTest.java      | 103 ----------
 .../commands/AbstractCommandValidationTest.java    |  78 ++++----
 .../CreateAbstractIndexCommandValidationTest.java  | 222 +++++++++++++++++++++
 .../CreateHashIndexCommandValidationTest.java}     |  27 ++-
 .../CreateSortedIndexCommandValidationTest.java    |  88 ++++++++
 .../commands/CreateTableCommandValidationTest.java |   6 +-
 .../commands/DropIndexCommandValidationTest.java   | 121 +++++++++++
 .../internal/catalog/BaseCatalogManagerTest.java   |  53 ++---
 .../RebalanceUtilUpdateAssignmentsTest.java        |   1 +
 .../internal/runner/app/ItTablesApiTest.java       |   5 +-
 .../internal/schema/CatalogDescriptorUtils.java    |   1 +
 .../internal/schema/CatalogSchemaManagerTest.java  |  10 +-
 .../CatalogToSchemaDescriptorConverterTest.java    |   1 +
 .../engine/exec/ddl/DdlCommandHandlerWrapper.java  |  24 +--
 .../exec/ddl/DdlToCatalogCommandConverter.java     |  16 +-
 .../engine/schema/CatalogSqlSchemaManagerTest.java |   1 +
 43 files changed, 1149 insertions(+), 923 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 b0ddcfc7af..ad8fdb90a6 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,10 +20,7 @@ package org.apache.ignite.internal.catalog;
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
 import org.apache.ignite.internal.catalog.commands.AlterZoneParams;
-import org.apache.ignite.internal.catalog.commands.CreateHashIndexParams;
-import org.apache.ignite.internal.catalog.commands.CreateSortedIndexParams;
 import org.apache.ignite.internal.catalog.commands.CreateZoneParams;
-import org.apache.ignite.internal.catalog.commands.DropIndexParams;
 import org.apache.ignite.internal.catalog.commands.DropZoneParams;
 import org.apache.ignite.internal.catalog.commands.RenameZoneParams;
 import org.apache.ignite.internal.manager.IgniteComponent;
@@ -49,30 +46,6 @@ public interface CatalogManager extends IgniteComponent, 
CatalogService {
      */
     CompletableFuture<Void> execute(List<CatalogCommand> commands);
 
-    /**
-     * Creates new sorted index.
-     *
-     * @param params Parameters.
-     * @return Operation future.
-     */
-    CompletableFuture<Void> createIndex(CreateSortedIndexParams params);
-
-    /**
-     * Creates new hash index.
-     *
-     * @param params Parameters.
-     * @return Operation future.
-     */
-    CompletableFuture<Void> createIndex(CreateHashIndexParams params);
-
-    /**
-     * Drops index.
-     *
-     * @param params Parameters.
-     * @return Operation future.
-     */
-    CompletableFuture<Void> dropIndex(DropIndexParams params);
-
     /**
      * Creates new distribution zone.
      *
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogManagerImpl.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogManagerImpl.java
index eaf0cf8df9..345bd2ba14 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogManagerImpl.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogManagerImpl.java
@@ -21,10 +21,7 @@ import static java.util.concurrent.CompletableFuture.allOf;
 import static java.util.concurrent.CompletableFuture.completedFuture;
 import static java.util.concurrent.CompletableFuture.failedFuture;
 import static 
org.apache.ignite.internal.catalog.CatalogParamsValidationUtils.validateAlterZoneParams;
-import static 
org.apache.ignite.internal.catalog.CatalogParamsValidationUtils.validateCreateHashIndexParams;
-import static 
org.apache.ignite.internal.catalog.CatalogParamsValidationUtils.validateCreateSortedIndexParams;
 import static 
org.apache.ignite.internal.catalog.CatalogParamsValidationUtils.validateCreateZoneParams;
-import static 
org.apache.ignite.internal.catalog.CatalogParamsValidationUtils.validateDropIndexParams;
 import static 
org.apache.ignite.internal.catalog.CatalogParamsValidationUtils.validateDropZoneParams;
 import static 
org.apache.ignite.internal.catalog.CatalogParamsValidationUtils.validateRenameZoneParams;
 import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.fromParams;
@@ -40,27 +37,19 @@ import java.util.Objects;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ConcurrentSkipListMap;
 import java.util.function.LongSupplier;
-import 
org.apache.ignite.internal.catalog.commands.AbstractCreateIndexCommandParams;
 import org.apache.ignite.internal.catalog.commands.AlterZoneParams;
-import org.apache.ignite.internal.catalog.commands.CreateHashIndexParams;
-import org.apache.ignite.internal.catalog.commands.CreateSortedIndexParams;
 import org.apache.ignite.internal.catalog.commands.CreateZoneParams;
-import org.apache.ignite.internal.catalog.commands.DropIndexParams;
 import org.apache.ignite.internal.catalog.commands.DropZoneParams;
 import org.apache.ignite.internal.catalog.commands.RenameZoneParams;
-import 
org.apache.ignite.internal.catalog.descriptors.CatalogHashIndexDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.CatalogIndexDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.CatalogSchemaDescriptor;
-import 
org.apache.ignite.internal.catalog.descriptors.CatalogSortedIndexDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.CatalogZoneDescriptor;
 import org.apache.ignite.internal.catalog.events.CatalogEvent;
 import org.apache.ignite.internal.catalog.events.CatalogEventParameters;
 import org.apache.ignite.internal.catalog.storage.AlterZoneEntry;
-import org.apache.ignite.internal.catalog.storage.DropIndexEntry;
 import org.apache.ignite.internal.catalog.storage.DropZoneEntry;
 import org.apache.ignite.internal.catalog.storage.Fireable;
-import org.apache.ignite.internal.catalog.storage.NewIndexEntry;
 import org.apache.ignite.internal.catalog.storage.NewZoneEntry;
 import org.apache.ignite.internal.catalog.storage.ObjectIdGenUpdateEntry;
 import org.apache.ignite.internal.catalog.storage.UpdateEntry;
@@ -76,17 +65,9 @@ import org.apache.ignite.internal.logger.Loggers;
 import org.apache.ignite.internal.manager.EventListener;
 import org.apache.ignite.internal.manager.Producer;
 import org.apache.ignite.internal.util.PendingComparableValuesTracker;
-import org.apache.ignite.lang.ColumnNotFoundException;
 import org.apache.ignite.lang.ErrorGroups.Common;
 import org.apache.ignite.lang.ErrorGroups.DistributionZones;
-import org.apache.ignite.lang.ErrorGroups.Index;
-import org.apache.ignite.lang.IgniteException;
 import org.apache.ignite.lang.IgniteInternalException;
-import org.apache.ignite.lang.IndexAlreadyExistsException;
-import org.apache.ignite.lang.IndexNotFoundException;
-import org.apache.ignite.lang.SchemaNotFoundException;
-import org.apache.ignite.lang.TableAlreadyExistsException;
-import org.apache.ignite.lang.TableNotFoundException;
 import org.jetbrains.annotations.Nullable;
 
 /**
@@ -298,69 +279,6 @@ public class CatalogManagerImpl extends 
Producer<CatalogEvent, CatalogEventParam
         return saveUpdateAndWaitForActivation(new 
BulkUpdateProducer(List.copyOf(commands)));
     }
 
-    @Override
-    public CompletableFuture<Void> createIndex(CreateHashIndexParams params) {
-        return saveUpdateAndWaitForActivation(catalog -> {
-            validateCreateHashIndexParams(params);
-
-            CatalogSchemaDescriptor schema = getSchema(catalog, 
params.schemaName());
-
-            ensureNoTableOrIndexExistsWithSameName(schema, params.indexName());
-
-            CatalogTableDescriptor table = getTable(schema, 
params.tableName());
-
-            validateIndexColumns(table, params);
-
-            CatalogHashIndexDescriptor index = 
fromParams(catalog.objectIdGenState(), table.id(), params);
-
-            return List.of(
-                    new NewIndexEntry(index),
-                    new ObjectIdGenUpdateEntry(1)
-            );
-        });
-    }
-
-    @Override
-    public CompletableFuture<Void> createIndex(CreateSortedIndexParams params) 
{
-        return saveUpdateAndWaitForActivation(catalog -> {
-            validateCreateSortedIndexParams(params);
-
-            CatalogSchemaDescriptor schema = getSchema(catalog, 
params.schemaName());
-
-            ensureNoTableOrIndexExistsWithSameName(schema, params.indexName());
-
-            CatalogTableDescriptor table = getTable(schema, 
params.tableName());
-
-            validateIndexColumns(table, params);
-
-            CatalogSortedIndexDescriptor index = 
fromParams(catalog.objectIdGenState(), table.id(), params);
-
-            return List.of(
-                    new NewIndexEntry(index),
-                    new ObjectIdGenUpdateEntry(1)
-            );
-        });
-    }
-
-    @Override
-    public CompletableFuture<Void> dropIndex(DropIndexParams params) {
-        return saveUpdateAndWaitForActivation(catalog -> {
-            validateDropIndexParams(params);
-
-            CatalogSchemaDescriptor schema = getSchema(catalog, 
params.schemaName());
-
-            CatalogIndexDescriptor index = schema.index(params.indexName());
-
-            if (index == null) {
-                throw new IndexNotFoundException(schema.name(), 
params.indexName());
-            }
-
-            return List.of(
-                    new DropIndexEntry(index.id(), index.tableId())
-            );
-        });
-    }
-
     @Override
     public CompletableFuture<Void> createZone(CreateZoneParams params) {
         return saveUpdateAndWaitForActivation(catalog -> {
@@ -563,28 +481,6 @@ public class CatalogManagerImpl extends 
Producer<CatalogEvent, CatalogEventParam
         );
     }
 
-    private static CatalogSchemaDescriptor getSchema(Catalog catalog, 
@Nullable String schemaName) {
-        schemaName = Objects.requireNonNullElse(schemaName, 
DEFAULT_SCHEMA_NAME);
-
-        CatalogSchemaDescriptor schema = catalog.schema(schemaName);
-
-        if (schema == null) {
-            throw new SchemaNotFoundException(schemaName);
-        }
-
-        return schema;
-    }
-
-    private static CatalogTableDescriptor getTable(CatalogSchemaDescriptor 
schema, String tableName) {
-        CatalogTableDescriptor table = 
schema.table(Objects.requireNonNull(tableName, "tableName"));
-
-        if (table == null) {
-            throw new TableNotFoundException(schema.name(), tableName);
-        }
-
-        return table;
-    }
-
     private static CatalogZoneDescriptor getZone(Catalog catalog, String 
zoneName) {
         zoneName = Objects.requireNonNull(zoneName, "zoneName");
 
@@ -602,32 +498,6 @@ public class CatalogManagerImpl extends 
Producer<CatalogEvent, CatalogEventParam
         listen(evt, (EventListener<CatalogEventParameters>) closure);
     }
 
-    private static void 
ensureNoTableOrIndexExistsWithSameName(CatalogSchemaDescriptor schema, String 
name) {
-        if (schema.index(name) != null) {
-            throw new IndexAlreadyExistsException(schema.name(), name);
-        }
-
-        if (schema.table(name) != null) {
-            throw new TableAlreadyExistsException(schema.name(), name);
-        }
-    }
-
-    private static void validateIndexColumns(CatalogTableDescriptor table, 
AbstractCreateIndexCommandParams params) {
-        validateColumnsExistInTable(table, params.columns());
-
-        if (params.unique() && 
!params.columns().containsAll(table.colocationColumns())) {
-            throw new IgniteException(Index.INVALID_INDEX_DEFINITION_ERR, 
"Unique index must include all colocation columns");
-        }
-    }
-
-    private static void validateColumnsExistInTable(CatalogTableDescriptor 
table, Collection<String> columns) {
-        for (String column : columns) {
-            if (table.columnDescriptor(column) == null) {
-                throw new ColumnNotFoundException(column);
-            }
-        }
-    }
-
     private static class BulkUpdateProducer implements UpdateProducer {
         private final List<? extends UpdateProducer> commands;
 
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogParamsValidationUtils.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogParamsValidationUtils.java
index b32f9ad9a7..6434c01b40 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogParamsValidationUtils.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogParamsValidationUtils.java
@@ -17,31 +17,18 @@
 
 package org.apache.ignite.internal.catalog;
 
-import static java.util.stream.Collectors.toList;
 import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.MAX_PARTITION_COUNT;
 import static org.apache.ignite.lang.IgniteStringFormatter.format;
 
 import com.jayway.jsonpath.InvalidPathException;
 import com.jayway.jsonpath.JsonPath;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.function.Predicate;
-import 
org.apache.ignite.internal.catalog.commands.AbstractCreateIndexCommandParams;
-import org.apache.ignite.internal.catalog.commands.AbstractIndexCommandParams;
-import org.apache.ignite.internal.catalog.commands.AbstractTableCommandParams;
 import org.apache.ignite.internal.catalog.commands.AlterZoneParams;
 import org.apache.ignite.internal.catalog.commands.ColumnParams;
-import org.apache.ignite.internal.catalog.commands.CreateHashIndexParams;
-import org.apache.ignite.internal.catalog.commands.CreateSortedIndexParams;
 import org.apache.ignite.internal.catalog.commands.CreateZoneParams;
-import org.apache.ignite.internal.catalog.commands.DropIndexParams;
 import org.apache.ignite.internal.catalog.commands.DropZoneParams;
 import org.apache.ignite.internal.catalog.commands.RenameZoneParams;
 import org.apache.ignite.internal.catalog.descriptors.CatalogSchemaDescriptor;
-import org.apache.ignite.internal.util.CollectionUtils;
 import org.apache.ignite.lang.ErrorGroups.DistributionZones;
-import org.apache.ignite.lang.ErrorGroups.Index;
 import org.apache.ignite.lang.util.StringUtils;
 import org.jetbrains.annotations.Nullable;
 
@@ -87,24 +74,6 @@ public class CatalogParamsValidationUtils {
         );
     }
 
-    static void validateCreateHashIndexParams(CreateHashIndexParams params) {
-        validateCommonCreateIndexParams(params);
-    }
-
-    static void validateCreateSortedIndexParams(CreateSortedIndexParams 
params) {
-        validateCommonCreateIndexParams(params);
-
-        validateCollectionIsNotEmpty(params.collations(), 
Index.INVALID_INDEX_DEFINITION_ERR, "Columns collations not specified");
-
-        if (params.collations().size() != params.columns().size()) {
-            throw new 
CatalogValidationException(Index.INVALID_INDEX_DEFINITION_ERR, "Columns 
collations doesn't match number of columns");
-        }
-    }
-
-    static void validateDropIndexParams(DropIndexParams params) {
-        validateCommonIndexParams(params);
-    }
-
     static void validateDropZoneParams(DropZoneParams params) {
         validateZoneName(params.zoneName());
     }
@@ -169,7 +138,7 @@ public class CatalogParamsValidationUtils {
         validateZoneField(dataNodesAutoAdjustScaleDown, 0, null, "Invalid data 
nodes auto adjust scale down");
     }
 
-    static void validateZoneDataNodesAutoAdjustParametersCompatibility(
+    private static void validateZoneDataNodesAutoAdjustParametersCompatibility(
             @Nullable Integer autoAdjust,
             @Nullable Integer scaleUp,
             @Nullable Integer scaleDown
@@ -216,56 +185,12 @@ public class CatalogParamsValidationUtils {
         }
     }
 
-    private static void validateCommonIndexParams(AbstractIndexCommandParams 
params) {
-        validateNameField(params.indexName(), 
Index.INVALID_INDEX_DEFINITION_ERR, "Missing index name");
-    }
-
-    private static void 
validateCommonCreateIndexParams(AbstractCreateIndexCommandParams params) {
-        validateCommonIndexParams(params);
-
-        validateNameField(params.tableName(), 
Index.INVALID_INDEX_DEFINITION_ERR, "Missing table name");
-
-        validateColumns(
-                params.columns(),
-                Index.INVALID_INDEX_DEFINITION_ERR,
-                "Columns not specified",
-                "Duplicate columns are present: {}"
-        );
-    }
-
     private static void validateNameField(String name, int errorCode, String 
errorMessage) {
         if (StringUtils.nullOrBlank(name)) {
             throw new CatalogValidationException(errorCode, errorMessage);
         }
     }
 
-    private static void validateCollectionIsNotEmpty(Collection<?> collection, 
int errorCode, String errorMessage) {
-        if (CollectionUtils.nullOrEmpty(collection)) {
-            throw new CatalogValidationException(errorCode, errorMessage);
-        }
-    }
-
-    private static void validateColumns(
-            List<String> columns,
-            int errorCode,
-            String emptyColumnsErrorMessage,
-            String duplicateColumnsErrorMessageFormat
-    ) {
-        validateCollectionIsNotEmpty(columns, errorCode, 
emptyColumnsErrorMessage);
-
-        List<String> duplicates = columns.stream()
-                .filter(Predicate.not(new HashSet<>()::add))
-                .collect(toList());
-
-        if (!duplicates.isEmpty()) {
-            throw new CatalogValidationException(errorCode, 
duplicateColumnsErrorMessageFormat, duplicates);
-        }
-    }
-
-    private static void validateCommonTableParams(AbstractTableCommandParams 
params) {
-        validateIdentifier(params.tableName(), "Name of the table");
-    }
-
     /**
      * Validates given column parameters.
      *
@@ -290,7 +215,7 @@ public class CatalogParamsValidationUtils {
      */
     public static void 
ensureNoTableOrIndexExistsWithGivenName(CatalogSchemaDescriptor schema, String 
name) {
         if (schema.index(name) != null) {
-            throw new CatalogValidationException(format("Index with name 
'{}.{}' already exists", schema.name(), name));
+            throw new IndexExistsValidationException(format("Index with name 
'{}.{}' already exists", schema.name(), name));
         }
 
         if (schema.table(name) != null) {
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractCatalogCommand.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/IndexExistsValidationException.java
similarity index 64%
copy from 
modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractCatalogCommand.java
copy to 
modules/catalog/src/main/java/org/apache/ignite/internal/catalog/IndexExistsValidationException.java
index f9d81571a4..a5eb0ff6fb 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractCatalogCommand.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/IndexExistsValidationException.java
@@ -15,10 +15,16 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.catalog.commands;
+package org.apache.ignite.internal.catalog;
 
-import org.apache.ignite.internal.catalog.CatalogCommand;
-import org.apache.ignite.internal.catalog.UpdateProducer;
-
-abstract class AbstractCatalogCommand implements CatalogCommand, 
UpdateProducer {
+/**
+ * This exception is thrown when index cannot be created because another index 
with
+ * the same name already exists in the same schema.
+ *
+ * <p>This exception is used to properly handle IF NOT EXISTS flag in ddl 
command handler.
+ */
+public class IndexExistsValidationException extends CatalogValidationException 
{
+    IndexExistsValidationException(String message) {
+        super(message);
+    }
 }
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractCatalogCommand.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/IndexNotFoundValidationException.java
similarity index 66%
copy from 
modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractCatalogCommand.java
copy to 
modules/catalog/src/main/java/org/apache/ignite/internal/catalog/IndexNotFoundValidationException.java
index f9d81571a4..68763af0f0 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractCatalogCommand.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/IndexNotFoundValidationException.java
@@ -15,10 +15,15 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.catalog.commands;
+package org.apache.ignite.internal.catalog;
 
-import org.apache.ignite.internal.catalog.CatalogCommand;
-import org.apache.ignite.internal.catalog.UpdateProducer;
-
-abstract class AbstractCatalogCommand implements CatalogCommand, 
UpdateProducer {
+/**
+ * This exception is thrown when index that going to be deleted not found in 
the schema.
+ *
+ * <p>This exception is used to properly handle IF EXISTS flag in ddl command 
handler.
+ */
+public class IndexNotFoundValidationException extends 
CatalogValidationException {
+    public IndexNotFoundValidationException(String message) {
+        super(message);
+    }
 }
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractCreateIndexCommand.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractCreateIndexCommand.java
new file mode 100644
index 0000000000..70c2225baf
--- /dev/null
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractCreateIndexCommand.java
@@ -0,0 +1,109 @@
+/*
+ * 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.CatalogParamsValidationUtils.ensureNoTableOrIndexExistsWithGivenName;
+import static 
org.apache.ignite.internal.catalog.CatalogParamsValidationUtils.validateIdentifier;
+import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.schemaOrThrow;
+import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.tableOrThrow;
+import static org.apache.ignite.internal.util.CollectionUtils.copyOrNull;
+import static org.apache.ignite.internal.util.CollectionUtils.nullOrEmpty;
+import static org.apache.ignite.lang.IgniteStringFormatter.format;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.apache.ignite.internal.catalog.Catalog;
+import org.apache.ignite.internal.catalog.CatalogValidationException;
+import org.apache.ignite.internal.catalog.descriptors.CatalogIndexDescriptor;
+import org.apache.ignite.internal.catalog.descriptors.CatalogSchemaDescriptor;
+import org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor;
+import org.apache.ignite.internal.catalog.storage.NewIndexEntry;
+import org.apache.ignite.internal.catalog.storage.ObjectIdGenUpdateEntry;
+import org.apache.ignite.internal.catalog.storage.UpdateEntry;
+
+/**
+ * Abstract create index command.
+ *
+ * <p>Encapsulates common logic of index creation like validation and update 
entries generation.
+ */
+public abstract class AbstractCreateIndexCommand extends AbstractIndexCommand {
+    protected final String tableName;
+
+    protected final boolean unique;
+
+    protected final List<String> columns;
+
+    AbstractCreateIndexCommand(String schemaName, String indexName, String 
tableName, boolean unique, List<String> columns)
+            throws CatalogValidationException {
+        super(schemaName, indexName);
+
+        validate(tableName, columns);
+
+        this.tableName = tableName;
+        this.unique = unique;
+        this.columns = copyOrNull(columns);
+    }
+
+    protected abstract CatalogIndexDescriptor createDescriptor(int indexId, 
int tableId);
+
+    @Override
+    public List<UpdateEntry> get(Catalog catalog) {
+        CatalogSchemaDescriptor schema = schemaOrThrow(catalog, schemaName);
+
+        ensureNoTableOrIndexExistsWithGivenName(schema, indexName);
+
+        CatalogTableDescriptor table = tableOrThrow(schema, tableName);
+
+        assert columns != null;
+
+        for (String columnName : columns) {
+            if (table.column(columnName) == null) {
+                throw new CatalogValidationException(format(
+                        "Column with name '{}' not found in table '{}.{}'", 
columnName, schemaName, tableName));
+            }
+        }
+
+        if (unique && !new 
HashSet<>(columns).containsAll(table.colocationColumns())) {
+            throw new CatalogValidationException("Unique index must include 
all colocation columns");
+        }
+
+        return List.of(
+                new NewIndexEntry(createDescriptor(catalog.objectIdGenState(), 
table.id())),
+                new ObjectIdGenUpdateEntry(1)
+        );
+    }
+
+    private static void validate(String tableName, List<String> columns) {
+        validateIdentifier(tableName, "Name of the table");
+
+        if (nullOrEmpty(columns)) {
+            throw new CatalogValidationException("Columns not specified");
+        }
+
+        Set<String> columnNames = new HashSet<>();
+
+        for (String name : columns) {
+            validateIdentifier(name, "Name of the column");
+
+            if (!columnNames.add(name)) {
+                throw new CatalogValidationException(format("Column with name 
'{}' specified more than once", name));
+            }
+        }
+    }
+}
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/AbstractCreateIndexCommandBuilder.java
similarity index 59%
copy from 
modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropIndexParams.java
copy to 
modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractCreateIndexCommandBuilder.java
index 100f3c3fe1..35505cf8bb 100644
--- 
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/AbstractCreateIndexCommandBuilder.java
@@ -17,21 +17,16 @@
 
 package org.apache.ignite.internal.catalog.commands;
 
-/** DROP INDEX statement. */
-public class DropIndexParams extends AbstractCreateIndexCommandParams {
-    /** Creates parameters builder. */
-    public static Builder builder() {
-        return new Builder();
-    }
+import java.util.List;
 
-    private DropIndexParams() {
-        // No-op.
-    }
+/** Builder that covers attributes which is common among all types of indexes. 
*/
+interface AbstractCreateIndexCommandBuilder<T extends 
AbstractIndexCommandBuilder<T>> extends AbstractIndexCommandBuilder<T> {
+    /** A name of the table an index belongs to. Should not be null or blank. 
*/
+    T tableName(String tableName);
 
-    /** Parameters builder. */
-    public static class Builder extends AbstractIndexBuilder<DropIndexParams, 
Builder> {
-        private Builder() {
-            super(new DropIndexParams());
-        }
-    }
+    /** A flag denoting whether index keeps at most one row per every key or 
not. */
+    T unique(boolean unique);
+
+    /** List of the columns to index. There must be at least one column. */
+    T columns(List<String> columns);
 }
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractCreateIndexCommandParams.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractCreateIndexCommandParams.java
deleted file mode 100644
index 7224c5990a..0000000000
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractCreateIndexCommandParams.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * 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;
-
-/** Abstract create index ddl command. */
-public abstract class AbstractCreateIndexCommandParams extends 
AbstractIndexCommandParams {
-    /** Table name. */
-    protected String tableName;
-
-    /** Unique index flag. */
-    protected boolean unique;
-
-    /** Indexed columns. */
-    protected List<String> columns;
-
-    /** Returns table name. */
-    public String tableName() {
-        return tableName;
-    }
-
-    /** Returns {@code true} if index is unique, {@code false} otherwise. */
-    public boolean unique() {
-        return unique;
-    }
-
-    /** Returns index columns. */
-    public List<String> columns() {
-        return columns;
-    }
-
-    /** Parameters builder. */
-    protected abstract static class AbstractCreateIndexBuilder<ParamT extends 
AbstractCreateIndexCommandParams, BuilderT> extends
-            AbstractIndexBuilder<ParamT, BuilderT> {
-        AbstractCreateIndexBuilder(ParamT params) {
-            super(params);
-        }
-
-        /**
-         * Set table name.
-         *
-         * @param tableName Table name.
-         * @return {@code this}.
-         */
-        public BuilderT tableName(String tableName) {
-            params.tableName = tableName;
-
-            return (BuilderT) this;
-        }
-
-        /**
-         * Sets unique flag.
-         *
-         * @param uniq {@code true} if index is unique, {@code false} 
otherwise.
-         * @return {@code this}.
-         */
-        public BuilderT unique(boolean uniq) {
-            params.unique = uniq;
-
-            return (BuilderT) this;
-        }
-
-        /**
-         * Set columns names.
-         *
-         * @param columns Columns names.
-         * @return {@code this}.
-         * @throws NullPointerException If the columns is {@code null} or one 
of its elements.
-         */
-        public BuilderT columns(List<String> columns) {
-            params.columns = List.copyOf(columns);
-
-            return (BuilderT) this;
-        }
-    }
-}
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractTableCommand.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractIndexCommand.java
similarity index 69%
copy from 
modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractTableCommand.java
copy to 
modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractIndexCommand.java
index e0887ca70e..ea629b2749 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractTableCommand.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractIndexCommand.java
@@ -19,28 +19,29 @@ package org.apache.ignite.internal.catalog.commands;
 
 import static 
org.apache.ignite.internal.catalog.CatalogParamsValidationUtils.validateIdentifier;
 
+import org.apache.ignite.internal.catalog.CatalogCommand;
 import org.apache.ignite.internal.catalog.CatalogValidationException;
 
 /**
- * Abstract table-related command.
+ * Abstract index-related command.
  *
- * <p>Every table-related command, disregard it going to create new table or 
modify existing one,
- * should specify name of the table and namespace (schema) where to find 
existing/put new table.
+ * <p>Every index-related command, disregard it going to create new index or 
delete existing one,
+ * should specify name of the index and namespace (schema) where to find 
existing/put new index.
  */
-public abstract class AbstractTableCommand extends AbstractCatalogCommand {
+public abstract class AbstractIndexCommand implements CatalogCommand {
     protected final String schemaName;
 
-    protected final String tableName;
+    protected final String indexName;
 
-    AbstractTableCommand(String schemaName, String tableName) throws 
CatalogValidationException {
+    AbstractIndexCommand(String schemaName, String indexName) throws 
CatalogValidationException {
         this.schemaName = schemaName;
-        this.tableName = tableName;
+        this.indexName = indexName;
 
         validate();
     }
 
     private void validate() {
         validateIdentifier(schemaName, "Name of the schema");
-        validateIdentifier(tableName, "Name of the table");
+        validateIdentifier(indexName, "Name of the index");
     }
 }
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractCatalogCommand.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractIndexCommandBuilder.java
similarity index 59%
copy from 
modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractCatalogCommand.java
copy to 
modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractIndexCommandBuilder.java
index f9d81571a4..0e8b3557ea 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractCatalogCommand.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractIndexCommandBuilder.java
@@ -18,7 +18,20 @@
 package org.apache.ignite.internal.catalog.commands;
 
 import org.apache.ignite.internal.catalog.CatalogCommand;
-import org.apache.ignite.internal.catalog.UpdateProducer;
 
-abstract class AbstractCatalogCommand implements CatalogCommand, 
UpdateProducer {
+/**
+ * Abstract builder of index-related command.
+ *
+ * <p>Every index-related command, disregard it going to create new index or 
delete existing one,
+ * should specify name of the index and namespace (schema) where to find 
existing/put new index.
+ */
+interface AbstractIndexCommandBuilder<T extends 
AbstractIndexCommandBuilder<T>> {
+    /** A name of the schema an index belongs to. Should not be null or blank. 
*/
+    T schemaName(String schemaName);
+
+    /** A name of the index. Should not be null or blank. */
+    T indexName(String indexName);
+
+    /** Returns a command with specified parameters. */
+    CatalogCommand build();
 }
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractIndexCommandParams.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractIndexCommandParams.java
deleted file mode 100644
index 85f983f405..0000000000
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractIndexCommandParams.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * 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 org.jetbrains.annotations.Nullable;
-
-/** Abstract create index ddl command. */
-public abstract class AbstractIndexCommandParams implements DdlCommandParams {
-    /** Index name. */
-    protected String indexName;
-
-    /** Schema name, {@code null} means to use the default schema. */
-    protected @Nullable String schemaName;
-
-    /** Returns index name. */
-    public String indexName() {
-        return indexName;
-    }
-
-    /** Returns schema name, {@code null} means to use the default schema. */
-    public @Nullable String schemaName() {
-        return schemaName;
-    }
-
-    /** Parameters builder. */
-    protected abstract static class AbstractIndexBuilder<ParamT extends 
AbstractCreateIndexCommandParams, BuilderT> {
-        protected ParamT params;
-
-        /** Constructor. */
-        AbstractIndexBuilder(ParamT params) {
-            this.params = params;
-        }
-
-        /**
-         * Sets schema name.
-         *
-         * @param schemaName Schema name, {@code null} to use to use the 
default schema.
-         * @return {@code this}.
-         */
-        public BuilderT schemaName(@Nullable String schemaName) {
-            params.schemaName = schemaName;
-
-            return (BuilderT) this;
-        }
-
-        /**
-         * Sets index name.
-         *
-         * @param indexName Index name.
-         * @return {@code this}.
-         */
-        public BuilderT indexName(String indexName) {
-            params.indexName = indexName;
-
-            return (BuilderT) this;
-        }
-
-        /** Builds parameters. */
-        public ParamT build() {
-            ParamT params0 = params;
-            params = null;
-            return params0;
-        }
-    }
-}
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractTableCommand.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractTableCommand.java
index e0887ca70e..08e3366ff2 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractTableCommand.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractTableCommand.java
@@ -19,6 +19,7 @@ package org.apache.ignite.internal.catalog.commands;
 
 import static 
org.apache.ignite.internal.catalog.CatalogParamsValidationUtils.validateIdentifier;
 
+import org.apache.ignite.internal.catalog.CatalogCommand;
 import org.apache.ignite.internal.catalog.CatalogValidationException;
 
 /**
@@ -27,7 +28,7 @@ import 
org.apache.ignite.internal.catalog.CatalogValidationException;
  * <p>Every table-related command, disregard it going to create new table or 
modify existing one,
  * should specify name of the table and namespace (schema) where to find 
existing/put new table.
  */
-public abstract class AbstractTableCommand extends AbstractCatalogCommand {
+public abstract class AbstractTableCommand implements CatalogCommand {
     protected final String schemaName;
 
     protected final String tableName;
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
deleted file mode 100644
index 0afb2fcda2..0000000000
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractTableCommandParams.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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 org.jetbrains.annotations.Nullable;
-
-/** Abstract table ddl command. */
-public abstract class AbstractTableCommandParams implements DdlCommandParams {
-    /** Table name. */
-    protected String tableName;
-
-    /** Schema name, {@code null} means to use the default schema. */
-    protected @Nullable String schema;
-
-    /** Returns table name. */
-    public String tableName() {
-        return tableName;
-    }
-
-    /** Returns schema name, {@code null} means to use the default schema. */
-    public @Nullable String schemaName() {
-        return schema;
-    }
-
-    /** Parameters builder. */
-    protected abstract static class AbstractTableBuilder<ParamT extends 
AbstractTableCommandParams, BuilderT> {
-        protected ParamT params;
-
-        AbstractTableBuilder(ParamT params) {
-            this.params = params;
-        }
-
-        /**
-         * Sets schema name.
-         *
-         * @param schemaName Schema name, {@code null} to use to use the 
default schema.
-         * @return {@code this}.
-         */
-        public BuilderT schemaName(@Nullable String schemaName) {
-            params.schema = schemaName;
-
-            return (BuilderT) this;
-        }
-
-        /**
-         * Sets table name.
-         *
-         * @param tableName Table name.
-         * @return {@code this}.
-         */
-        public BuilderT tableName(String tableName) {
-            params.tableName = tableName;
-
-            return (BuilderT) this;
-        }
-
-        /** Builds parameters. */
-        public ParamT build() {
-            ParamT params0 = params;
-            params = null;
-            return params0;
-        }
-    }
-}
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 d1f753ead5..6d47260027 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,25 +17,18 @@
 
 package org.apache.ignite.internal.catalog.commands;
 
-import static java.util.stream.Collectors.toList;
 import static org.apache.ignite.lang.IgniteStringFormatter.format;
 
 import java.util.EnumMap;
 import java.util.EnumSet;
-import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
-import java.util.stream.IntStream;
 import org.apache.ignite.internal.catalog.Catalog;
 import org.apache.ignite.internal.catalog.CatalogValidationException;
 import org.apache.ignite.internal.catalog.TableNotFoundValidationException;
-import org.apache.ignite.internal.catalog.descriptors.CatalogColumnCollation;
 import 
org.apache.ignite.internal.catalog.descriptors.CatalogDataStorageDescriptor;
-import 
org.apache.ignite.internal.catalog.descriptors.CatalogHashIndexDescriptor;
-import 
org.apache.ignite.internal.catalog.descriptors.CatalogIndexColumnDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.CatalogSchemaDescriptor;
-import 
org.apache.ignite.internal.catalog.descriptors.CatalogSortedIndexDescriptor;
 import 
org.apache.ignite.internal.catalog.descriptors.CatalogTableColumnDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.CatalogZoneDescriptor;
@@ -147,38 +140,6 @@ public class CatalogUtils {
         ALTER_COLUMN_TYPE_TRANSITIONS.put(ColumnType.FLOAT, 
EnumSet.of(ColumnType.DOUBLE));
     }
 
-    /**
-     * Converts CreateIndex command params to hash index descriptor.
-     *
-     * @param id Index ID.
-     * @param tableId Table ID.
-     * @param params Parameters.
-     * @return Index descriptor.
-     */
-    public static CatalogHashIndexDescriptor fromParams(int id, int tableId, 
CreateHashIndexParams params) {
-        return new CatalogHashIndexDescriptor(id, params.indexName(), tableId, 
params.unique(), params.columns());
-    }
-
-    /**
-     * Converts CreateIndex command params to sorted index descriptor.
-     *
-     * @param id Index ID.
-     * @param tableId Table ID.
-     * @param params Parameters.
-     * @return Index descriptor.
-     */
-    public static CatalogSortedIndexDescriptor fromParams(int id, int tableId, 
CreateSortedIndexParams params) {
-        List<CatalogColumnCollation> collations = params.collations();
-
-        assert collations.size() == params.columns().size() : "tableId=" + 
tableId + ", indexId=" + id;
-
-        List<CatalogIndexColumnDescriptor> columnDescriptors = 
IntStream.range(0, collations.size())
-                .mapToObj(i -> new 
CatalogIndexColumnDescriptor(params.columns().get(i), collations.get(i)))
-                .collect(toList());
-
-        return new CatalogSortedIndexDescriptor(id, params.indexName(), 
tableId, params.unique(), columnDescriptors);
-    }
-
     /**
      * Converts CreateZone command params to descriptor.
      *
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateHashIndexCommand.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateHashIndexCommand.java
new file mode 100644
index 0000000000..45fdea47b2
--- /dev/null
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateHashIndexCommand.java
@@ -0,0 +1,106 @@
+/*
+ * 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.CatalogCommand;
+import org.apache.ignite.internal.catalog.CatalogValidationException;
+import 
org.apache.ignite.internal.catalog.descriptors.CatalogHashIndexDescriptor;
+import org.apache.ignite.internal.catalog.descriptors.CatalogIndexDescriptor;
+
+/**
+ * A command that adds a new hash index to the catalog.
+ */
+public class CreateHashIndexCommand extends AbstractCreateIndexCommand {
+    /** Returns builder to create a command to create a new hash index. */
+    public static CreateHashIndexCommandBuilder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Constructs the object.
+     *
+     * @param schemaName Name of the schema to create index in. Should not be 
null or blank.
+     * @param indexName Name of the index to create. Should not be null or 
blank.
+     * @param tableName Name of the table the index belong to. Should not be 
null or blank.
+     * @param unique A flag denoting whether index keeps at most one row per 
every key or not.
+     * @param columns List of the indexed columns. There should be at least 
one column.
+     * @throws CatalogValidationException if any of restrictions above is 
violated.
+     */
+    private CreateHashIndexCommand(String schemaName, String indexName, String 
tableName, boolean unique, List<String> columns)
+            throws CatalogValidationException {
+        super(schemaName, indexName, tableName, unique, columns);
+    }
+
+    @Override
+    protected CatalogIndexDescriptor createDescriptor(int indexId, int 
tableId) {
+        return new CatalogHashIndexDescriptor(
+                indexId, indexName, tableId, unique, columns
+        );
+    }
+
+    private static class Builder implements CreateHashIndexCommandBuilder {
+        private String schemaName;
+        private String indexName;
+        private String tableName;
+        private List<String> columns;
+        private boolean unique;
+
+        @Override
+        public Builder tableName(String tableName) {
+            this.tableName = tableName;
+
+            return this;
+        }
+
+        @Override
+        public Builder unique(boolean unique) {
+            this.unique = unique;
+
+            return this;
+        }
+
+        @Override
+        public Builder columns(List<String> columns) {
+            this.columns = columns;
+
+            return this;
+        }
+
+        @Override
+        public Builder schemaName(String schemaName) {
+            this.schemaName = schemaName;
+
+            return this;
+        }
+
+        @Override
+        public Builder indexName(String indexName) {
+            this.indexName = indexName;
+
+            return this;
+        }
+
+        @Override
+        public CatalogCommand build() {
+            return new CreateHashIndexCommand(
+                    schemaName, indexName, tableName, unique, columns
+            );
+        }
+    }
+}
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/CreateHashIndexCommandBuilder.java
similarity index 64%
copy from 
modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropIndexParams.java
copy to 
modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateHashIndexCommandBuilder.java
index 100f3c3fe1..d7e0e5abc4 100644
--- 
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/CreateHashIndexCommandBuilder.java
@@ -17,21 +17,12 @@
 
 package org.apache.ignite.internal.catalog.commands;
 
-/** DROP INDEX statement. */
-public class DropIndexParams extends AbstractCreateIndexCommandParams {
-    /** Creates parameters builder. */
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    private DropIndexParams() {
-        // No-op.
-    }
-
-    /** Parameters builder. */
-    public static class Builder extends AbstractIndexBuilder<DropIndexParams, 
Builder> {
-        private Builder() {
-            super(new DropIndexParams());
-        }
-    }
+/**
+ * Builder of a command that adds a new hash index to the catalog.
+ *
+ * <p>A builder is considered to be reusable, thus implementation have
+ * to make sure invocation of {@link #build()} method doesn't cause any
+ * side effects on builder's state or any object created by the same builder.
+ */
+public interface CreateHashIndexCommandBuilder extends 
AbstractCreateIndexCommandBuilder<CreateHashIndexCommandBuilder> {
 }
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateSortedIndexCommand.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateSortedIndexCommand.java
new file mode 100644
index 0000000000..88c0270997
--- /dev/null
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateSortedIndexCommand.java
@@ -0,0 +1,145 @@
+/*
+ * 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.util.CollectionUtils.copyOrNull;
+import static org.apache.ignite.internal.util.CollectionUtils.nullOrEmpty;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.ignite.internal.catalog.CatalogCommand;
+import org.apache.ignite.internal.catalog.CatalogValidationException;
+import org.apache.ignite.internal.catalog.descriptors.CatalogColumnCollation;
+import 
org.apache.ignite.internal.catalog.descriptors.CatalogIndexColumnDescriptor;
+import org.apache.ignite.internal.catalog.descriptors.CatalogIndexDescriptor;
+import 
org.apache.ignite.internal.catalog.descriptors.CatalogSortedIndexDescriptor;
+
+/**
+ * A command that adds a new sorted index to the catalog.
+ */
+public class CreateSortedIndexCommand extends AbstractCreateIndexCommand {
+    /** Returns builder to create a command to create a new sorted index. */
+    public static CreateSortedIndexCommandBuilder builder() {
+        return new Builder();
+    }
+
+    private final List<CatalogColumnCollation> collations;
+
+    /**
+     * Constructs the object.
+     *
+     * @param schemaName Name of the schema to create index in. Should not be 
null or blank.
+     * @param indexName Name of the index to create. Should not be null or 
blank.
+     * @param tableName Name of the table the index belong to. Should not be 
null or blank.
+     * @param unique A flag denoting whether index keeps at most one row per 
every key or not.
+     * @param columns List of the indexed columns. There should be at least 
one column.
+     * @param collations List of the columns collations. The size of this list 
should much size of the columns.
+     * @throws CatalogValidationException if any of restrictions above is 
violated.
+     */
+    private CreateSortedIndexCommand(String schemaName, String indexName, 
String tableName, boolean unique, List<String> columns,
+            List<CatalogColumnCollation> collations) throws 
CatalogValidationException {
+        super(schemaName, indexName, tableName, unique, columns);
+
+        this.collations = copyOrNull(collations);
+
+        validate();
+    }
+
+    @Override
+    protected CatalogIndexDescriptor createDescriptor(int indexId, int 
tableId) {
+        List<CatalogIndexColumnDescriptor> indexColumnDescriptors = new 
ArrayList<>(columns.size());
+
+        for (int i = 0; i < columns.size(); i++) {
+            indexColumnDescriptors.add(new CatalogIndexColumnDescriptor(
+                    columns.get(i), collations.get(i)
+            ));
+        }
+
+        return new CatalogSortedIndexDescriptor(
+                indexId, indexName, tableId, unique, indexColumnDescriptors
+        );
+    }
+
+    private void validate() {
+        if (nullOrEmpty(collations)) {
+            throw new CatalogValidationException("Collations not specified");
+        }
+
+        if (collations.size() != columns.size()) {
+            throw new CatalogValidationException("Columns collations doesn't 
match number of columns");
+        }
+    }
+
+    private static class Builder implements CreateSortedIndexCommandBuilder {
+        private String schemaName;
+        private String indexName;
+        private String tableName;
+        private List<String> columns;
+        private List<CatalogColumnCollation> collations;
+        private boolean unique;
+
+        @Override
+        public Builder tableName(String tableName) {
+            this.tableName = tableName;
+
+            return this;
+        }
+
+        @Override
+        public Builder unique(boolean unique) {
+            this.unique = unique;
+
+            return this;
+        }
+
+        @Override
+        public Builder columns(List<String> columns) {
+            this.columns = columns;
+
+            return this;
+        }
+
+        @Override
+        public Builder schemaName(String schemaName) {
+            this.schemaName = schemaName;
+
+            return this;
+        }
+
+        @Override
+        public Builder indexName(String indexName) {
+            this.indexName = indexName;
+
+            return this;
+        }
+
+        @Override
+        public CreateSortedIndexCommandBuilder 
collations(List<CatalogColumnCollation> collations) {
+            this.collations = collations;
+
+            return this;
+        }
+
+        @Override
+        public CatalogCommand build() {
+            return new CreateSortedIndexCommand(
+                    schemaName, indexName, tableName, unique, columns, 
collations
+            );
+        }
+    }
+}
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/CreateSortedIndexCommandBuilder.java
similarity index 54%
rename from 
modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropIndexParams.java
rename to 
modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateSortedIndexCommandBuilder.java
index 100f3c3fe1..665a78c466 100644
--- 
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/CreateSortedIndexCommandBuilder.java
@@ -17,21 +17,17 @@
 
 package org.apache.ignite.internal.catalog.commands;
 
-/** DROP INDEX statement. */
-public class DropIndexParams extends AbstractCreateIndexCommandParams {
-    /** Creates parameters builder. */
-    public static Builder builder() {
-        return new Builder();
-    }
+import java.util.List;
+import org.apache.ignite.internal.catalog.descriptors.CatalogColumnCollation;
 
-    private DropIndexParams() {
-        // No-op.
-    }
-
-    /** Parameters builder. */
-    public static class Builder extends AbstractIndexBuilder<DropIndexParams, 
Builder> {
-        private Builder() {
-            super(new DropIndexParams());
-        }
-    }
+/**
+ * Builder of a command that adds a new sorted index to the catalog.
+ *
+ * <p>A builder is considered to be reusable, thus implementation have
+ * to make sure invocation of {@link #build()} method doesn't cause any
+ * side effects on builder's state or any object created by the same builder.
+ */
+public interface CreateSortedIndexCommandBuilder extends 
AbstractCreateIndexCommandBuilder<CreateSortedIndexCommandBuilder> {
+    /** List of the columns collations. The size of this list should much size 
of the columns. */
+    CreateSortedIndexCommandBuilder collations(List<CatalogColumnCollation> 
collations);
 }
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateSortedIndexParams.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateSortedIndexParams.java
deleted file mode 100644
index 745a5de623..0000000000
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateSortedIndexParams.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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.CatalogColumnCollation;
-
-/** CREATE INDEX statement. */
-public class CreateSortedIndexParams extends AbstractCreateIndexCommandParams {
-    /** Creates parameters builder. */
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    private CreateSortedIndexParams() {
-        // No-op.
-    }
-
-    /** Columns collations. */
-    private List<CatalogColumnCollation> collations;
-
-    /** Gets columns collations. */
-    public List<CatalogColumnCollation> collations() {
-        return collations;
-    }
-
-    /** Parameters builder. */
-    public static class Builder extends 
AbstractCreateIndexBuilder<CreateSortedIndexParams, Builder> {
-        private Builder() {
-            super(new CreateSortedIndexParams());
-        }
-
-        /**
-         * Set columns collations.
-         *
-         * @param collations Columns collations.
-         * @return {@code this}.
-         * @throws NullPointerException If the columns is {@code null} or one 
of its elements.
-         */
-        public Builder collations(List<CatalogColumnCollation> collations) {
-            params.collations = List.copyOf(collations);
-
-            return this;
-        }
-    }
-}
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateTableCommand.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateTableCommand.java
index 2853a63a2f..4f23f8d7fd 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateTableCommand.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateTableCommand.java
@@ -105,6 +105,7 @@ public class CreateTableCommand extends 
AbstractTableCommand {
 
         CatalogTableDescriptor table = new CatalogTableDescriptor(
                 tableId,
+                pkIndexId,
                 tableName,
                 zone.id(),
                 CatalogTableDescriptor.INITIAL_TABLE_VERSION,
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropIndexCommand.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropIndexCommand.java
new file mode 100644
index 0000000000..a999db65e8
--- /dev/null
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropIndexCommand.java
@@ -0,0 +1,106 @@
+/*
+ * 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.commands.CatalogUtils.schemaOrThrow;
+import static org.apache.ignite.lang.IgniteStringFormatter.format;
+
+import java.util.List;
+import org.apache.ignite.internal.catalog.Catalog;
+import org.apache.ignite.internal.catalog.CatalogCommand;
+import org.apache.ignite.internal.catalog.CatalogValidationException;
+import org.apache.ignite.internal.catalog.IndexNotFoundValidationException;
+import org.apache.ignite.internal.catalog.descriptors.CatalogIndexDescriptor;
+import org.apache.ignite.internal.catalog.descriptors.CatalogSchemaDescriptor;
+import org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor;
+import org.apache.ignite.internal.catalog.storage.DropIndexEntry;
+import org.apache.ignite.internal.catalog.storage.UpdateEntry;
+
+/**
+ * A command that drops index with specified name.
+ */
+public class DropIndexCommand extends AbstractIndexCommand {
+    /** Returns builder to create a command to drop index with specified name. 
*/
+    public static DropIndexCommandBuilder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param schemaName Name of the schema to look up index in. Should not be 
null or blank.
+     * @param indexName Name of the index to drop. Should not be null or blank.
+     * @throws CatalogValidationException if any of restrictions above is 
violated.
+     */
+    private DropIndexCommand(String schemaName, String indexName) throws 
CatalogValidationException {
+        super(schemaName, indexName);
+    }
+
+    @Override
+    public List<UpdateEntry> get(Catalog catalog) {
+        CatalogSchemaDescriptor schema = schemaOrThrow(catalog, schemaName);
+
+        CatalogIndexDescriptor index = schema.index(indexName);
+
+        if (index == null) {
+            throw new IndexNotFoundValidationException(
+                    format("Index with name 'PUBLIC.TEST' not found", 
schemaName, indexName));
+        }
+
+        CatalogTableDescriptor table = catalog.table(index.tableId());
+
+        assert table != null : format("Index refers to non existing table 
[catalogVersion={}, indexId={}, tableId={}]",
+                catalog.version(), index.id(), index.tableId());
+
+        if (table.primaryKeyIndexId() == index.id()) {
+            throw new CatalogValidationException("Dropping primary key index 
is not allowed");
+        }
+
+        return List.of(
+                new DropIndexEntry(index.id(), index.tableId())
+        );
+    }
+
+    private static class Builder implements DropIndexCommandBuilder {
+        private String schemaName;
+
+        private String indexName;
+
+        @Override
+        public Builder schemaName(String schemaName) {
+            this.schemaName = schemaName;
+
+            return this;
+        }
+
+        @Override
+        public Builder indexName(String indexName) {
+            this.indexName = indexName;
+
+            return this;
+        }
+
+        @Override
+        public CatalogCommand build() {
+            return new DropIndexCommand(
+                    schemaName,
+                    indexName
+            );
+        }
+    }
+}
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractCatalogCommand.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropIndexCommandBuilder.java
similarity index 68%
rename from 
modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractCatalogCommand.java
rename to 
modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropIndexCommandBuilder.java
index f9d81571a4..fae04b24a6 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractCatalogCommand.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropIndexCommandBuilder.java
@@ -17,8 +17,12 @@
 
 package org.apache.ignite.internal.catalog.commands;
 
-import org.apache.ignite.internal.catalog.CatalogCommand;
-import org.apache.ignite.internal.catalog.UpdateProducer;
-
-abstract class AbstractCatalogCommand implements CatalogCommand, 
UpdateProducer {
+/**
+ * Builder of a command that drop specified index.
+ *
+ * <p>A builder is considered to be reusable, thus implementation have
+ * to make sure invocation of {@link #build()} method doesn't cause any
+ * side effects on builder's state or any object created by the same builder.
+ */
+public interface DropIndexCommandBuilder extends 
AbstractIndexCommandBuilder<DropIndexCommandBuilder> {
 }
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableDescriptor.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableDescriptor.java
index 2948aa90f7..4e88d325a2 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableDescriptor.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableDescriptor.java
@@ -39,6 +39,8 @@ public class CatalogTableDescriptor extends 
CatalogObjectDescriptor {
 
     private final int zoneId;
 
+    private final int pkIndexId;
+
     private final int tableVersion;
 
     private final List<CatalogTableColumnDescriptor> columns;
@@ -52,6 +54,7 @@ public class CatalogTableDescriptor extends 
CatalogObjectDescriptor {
      * Constructor.
      *
      * @param id Table id.
+     * @param pkIndexId Primary key index id.
      * @param name Table name.
      * @param zoneId Distribution zone ID.
      * @param tableVersion Version of the table.
@@ -61,6 +64,7 @@ public class CatalogTableDescriptor extends 
CatalogObjectDescriptor {
      */
     public CatalogTableDescriptor(
             int id,
+            int pkIndexId,
             String name,
             int zoneId,
             int tableVersion,
@@ -70,6 +74,7 @@ public class CatalogTableDescriptor extends 
CatalogObjectDescriptor {
     ) {
         super(id, Type.TABLE, name);
 
+        this.pkIndexId = pkIndexId;
         this.zoneId = zoneId;
         this.tableVersion = tableVersion;
         this.columns = Objects.requireNonNull(columns, "No columns defined.");
@@ -96,6 +101,10 @@ public class CatalogTableDescriptor extends 
CatalogObjectDescriptor {
         return zoneId;
     }
 
+    public int primaryKeyIndexId() {
+        return pkIndexId;
+    }
+
     public int tableVersion() {
         return tableVersion;
     }
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/AlterColumnEntry.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/AlterColumnEntry.java
index 5ef3f9965d..89e12ade76 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/AlterColumnEntry.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/AlterColumnEntry.java
@@ -90,6 +90,7 @@ public class AlterColumnEntry implements UpdateEntry, 
Fireable {
                                         ? table
                                         : new CatalogTableDescriptor(
                                                 table.id(),
+                                                table.primaryKeyIndexId(),
                                                 table.name(),
                                                 table.zoneId(),
                                                 table.tableVersion() + 1,
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropColumnsEntry.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropColumnsEntry.java
index a3c5dcadd0..375b3474b7 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropColumnsEntry.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropColumnsEntry.java
@@ -87,6 +87,7 @@ public class DropColumnsEntry implements UpdateEntry, 
Fireable {
                         Arrays.stream(schema.tables())
                                 .map(table -> table.id() == tableId ? new 
CatalogTableDescriptor(
                                         table.id(),
+                                        table.primaryKeyIndexId(),
                                         table.name(),
                                         table.zoneId(),
                                         table.tableVersion() + 1,
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewColumnsEntry.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewColumnsEntry.java
index 936e7c0bf5..e9e1b5cd2b 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewColumnsEntry.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewColumnsEntry.java
@@ -87,6 +87,7 @@ public class NewColumnsEntry implements UpdateEntry, Fireable 
{
                         Arrays.stream(schema.tables())
                                 .map(table -> table.id() == tableId ? new 
CatalogTableDescriptor(
                                         table.id(),
+                                        table.primaryKeyIndexId(),
                                         table.name(),
                                         table.zoneId(),
                                         table.tableVersion() + 1,
diff --git 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogManagerSelfTest.java
 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogManagerSelfTest.java
index c9f765836c..42b7c5caed 100644
--- 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogManagerSelfTest.java
+++ 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogManagerSelfTest.java
@@ -83,12 +83,10 @@ import 
org.apache.ignite.internal.catalog.commands.AlterTableAlterColumnCommandB
 import org.apache.ignite.internal.catalog.commands.AlterZoneParams;
 import org.apache.ignite.internal.catalog.commands.CatalogUtils;
 import org.apache.ignite.internal.catalog.commands.ColumnParams;
-import org.apache.ignite.internal.catalog.commands.CreateHashIndexParams;
-import org.apache.ignite.internal.catalog.commands.CreateSortedIndexParams;
 import org.apache.ignite.internal.catalog.commands.CreateZoneParams;
 import org.apache.ignite.internal.catalog.commands.DataStorageParams;
 import org.apache.ignite.internal.catalog.commands.DefaultValue;
-import org.apache.ignite.internal.catalog.commands.DropIndexParams;
+import org.apache.ignite.internal.catalog.commands.DropIndexCommand;
 import org.apache.ignite.internal.catalog.commands.DropZoneParams;
 import org.apache.ignite.internal.catalog.commands.RenameZoneParams;
 import 
org.apache.ignite.internal.catalog.descriptors.CatalogHashIndexDescriptor;
@@ -116,13 +114,7 @@ import 
org.apache.ignite.internal.distributionzones.DistributionZoneAlreadyExist
 import 
org.apache.ignite.internal.distributionzones.DistributionZoneNotFoundException;
 import org.apache.ignite.internal.hlc.HybridTimestamp;
 import org.apache.ignite.internal.manager.EventListener;
-import org.apache.ignite.lang.ColumnNotFoundException;
-import org.apache.ignite.lang.IgniteException;
 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.apache.ignite.sql.ColumnType;
 import org.hamcrest.TypeSafeMatcher;
 import org.jetbrains.annotations.Nullable;
@@ -821,7 +813,7 @@ public class CatalogManagerSelfTest extends 
BaseCatalogManagerTest {
     @Test
     public void testDropTableWithIndex() {
         assertThat(manager.execute(simpleTable(TABLE_NAME)), 
willBe(nullValue()));
-        assertThat(manager.createIndex(simpleIndex()), willBe(nullValue()));
+        assertThat(manager.execute(simpleIndex()), willBe(nullValue()));
 
         long beforeDropTimestamp = clock.nowLong();
 
@@ -862,7 +854,7 @@ public class CatalogManagerSelfTest extends 
BaseCatalogManagerTest {
     public void testCreateHashIndex() {
         assertThat(manager.execute(simpleTable(TABLE_NAME)), 
willBe(nullValue()));
 
-        assertThat(manager.createIndex(createHashIndexParams(INDEX_NAME, 
List.of("VAL", "ID"))), willBe(nullValue()));
+        assertThat(manager.execute(createHashIndexCommand(INDEX_NAME, 
List.of("VAL", "ID"))), willBe(nullValue()));
 
         // Validate catalog version from the past.
         CatalogSchemaDescriptor schema = manager.schema(1);
@@ -893,14 +885,14 @@ public class CatalogManagerSelfTest extends 
BaseCatalogManagerTest {
     public void testCreateSortedIndex() {
         assertThat(manager.execute(simpleTable(TABLE_NAME)), 
willBe(nullValue()));
 
-        CreateSortedIndexParams params = createSortedIndexParams(
+        CatalogCommand command = createSortedIndexCommand(
                 INDEX_NAME,
                 true,
                 List.of("VAL", "ID"),
                 List.of(DESC_NULLS_FIRST, ASC_NULLS_LAST)
         );
 
-        assertThat(manager.createIndex(params), willBe(nullValue()));
+        assertThat(manager.execute(command), willBe(nullValue()));
 
         // Validate catalog version from the past.
         CatalogSchemaDescriptor schema = manager.schema(1);
@@ -1032,9 +1024,9 @@ public class CatalogManagerSelfTest extends 
BaseCatalogManagerTest {
 
     @Test
     public void testCreateIndexEvents() {
-        CreateHashIndexParams createIndexParams = 
createHashIndexParams(INDEX_NAME, List.of("ID"));
+        CatalogCommand createIndexCmd = createHashIndexCommand(INDEX_NAME, 
List.of("ID"));
 
-        DropIndexParams dropIndexParams = 
DropIndexParams.builder().indexName(INDEX_NAME).build();
+        CatalogCommand dropIndexCmd = 
DropIndexCommand.builder().schemaName(SCHEMA_NAME).indexName(INDEX_NAME).build();
 
         EventListener<CatalogEventParameters> eventListener = 
mock(EventListener.class);
         when(eventListener.notify(any(), 
any())).thenReturn(completedFuture(false));
@@ -1043,7 +1035,7 @@ public class CatalogManagerSelfTest extends 
BaseCatalogManagerTest {
         manager.listen(CatalogEvent.INDEX_DROP, eventListener);
 
         // Try to create index without table.
-        assertThat(manager.createIndex(createIndexParams), 
willThrow(TableNotFoundException.class));
+        assertThat(manager.execute(createIndexCmd), 
willThrow(TableNotFoundValidationException.class));
         verifyNoInteractions(eventListener);
 
         // Create table with PK index.
@@ -1053,13 +1045,13 @@ public class CatalogManagerSelfTest extends 
BaseCatalogManagerTest {
         clearInvocations(eventListener);
 
         // Create index.
-        assertThat(manager.createIndex(createIndexParams), 
willCompleteSuccessfully());
+        assertThat(manager.execute(createIndexCmd), 
willCompleteSuccessfully());
         verify(eventListener).notify(any(CreateIndexEventParameters.class), 
isNull());
 
         clearInvocations(eventListener);
 
         // Drop index.
-        assertThat(manager.dropIndex(dropIndexParams), willBe(nullValue()));
+        assertThat(manager.execute(dropIndexCmd), willBe(nullValue()));
         verify(eventListener).notify(any(DropIndexEventParameters.class), 
isNull());
 
         clearInvocations(eventListener);
@@ -1068,7 +1060,7 @@ public class CatalogManagerSelfTest extends 
BaseCatalogManagerTest {
         assertThat(manager.execute(dropTableCommand(TABLE_NAME)), 
willBe(nullValue()));
 
         // Try drop index once again.
-        assertThat(manager.dropIndex(dropIndexParams), 
willThrow(IndexNotFoundException.class));
+        assertThat(manager.execute(dropIndexCmd), 
willThrow(IndexNotFoundValidationException.class));
 
         verify(eventListener).notify(any(DropIndexEventParameters.class), 
isNull());
     }
@@ -1425,7 +1417,7 @@ public class CatalogManagerSelfTest extends 
BaseCatalogManagerTest {
     void testGetTableIdOnDropIndexEvent() {
         assertThat(manager.execute(simpleTable(TABLE_NAME)), 
willBe(nullValue()));
 
-        assertThat(manager.createIndex(createHashIndexParams(INDEX_NAME, 
List.of("VAL"))), willBe(nullValue()));
+        assertThat(manager.execute(createHashIndexCommand(INDEX_NAME, 
List.of("VAL"))), willBe(nullValue()));
 
         int tableId = manager.table(TABLE_NAME, clock.nowLong()).id();
         int pkIndexId = manager.index(createPkIndexName(TABLE_NAME), 
clock.nowLong()).id();
@@ -1443,7 +1435,7 @@ public class CatalogManagerSelfTest extends 
BaseCatalogManagerTest {
 
         // Let's remove the index.
         assertThat(
-                
manager.dropIndex(DropIndexParams.builder().schemaName(SCHEMA_NAME).indexName(INDEX_NAME).build()),
+                
manager.execute(DropIndexCommand.builder().schemaName(SCHEMA_NAME).indexName(INDEX_NAME).build()),
                 willBe(nullValue())
         );
 
@@ -1469,7 +1461,7 @@ public class CatalogManagerSelfTest extends 
BaseCatalogManagerTest {
         assertThat(manager.execute(simpleTable(TABLE_NAME)), 
willBe(nullValue()));
         assertEquals(1, manager.latestCatalogVersion());
 
-        assertThat(manager.createIndex(simpleIndex()), willBe(nullValue()));
+        assertThat(manager.execute(simpleIndex()), willBe(nullValue()));
         assertEquals(2, manager.latestCatalogVersion());
     }
 
@@ -1486,7 +1478,7 @@ public class CatalogManagerSelfTest extends 
BaseCatalogManagerTest {
     @Test
     void testIndexes() {
         assertThat(manager.execute(simpleTable(TABLE_NAME)), 
willBe(nullValue()));
-        assertThat(manager.createIndex(simpleIndex()), willBe(nullValue()));
+        assertThat(manager.execute(simpleIndex()), willBe(nullValue()));
 
         assertThat(manager.indexes(0), empty());
         assertThat(manager.indexes(1), hasItems(index(1, 
createPkIndexName(TABLE_NAME))));
@@ -1562,16 +1554,16 @@ public class CatalogManagerSelfTest extends 
BaseCatalogManagerTest {
     @Test
     void testCreateIndexWithAlreadyExistingName() {
         assertThat(manager.execute(simpleTable(TABLE_NAME)), 
willCompleteSuccessfully());
-        assertThat(manager.createIndex(simpleIndex()), 
willCompleteSuccessfully());
+        assertThat(manager.execute(simpleIndex()), willCompleteSuccessfully());
 
         assertThat(
-                manager.createIndex(createHashIndexParams(INDEX_NAME, 
List.of("VAL"))),
-                willThrowFast(IndexAlreadyExistsException.class)
+                manager.execute(createHashIndexCommand(INDEX_NAME, 
List.of("VAL"))),
+                willThrowFast(IndexExistsValidationException.class)
         );
 
         assertThat(
-                manager.createIndex(createSortedIndexParams(INDEX_NAME, 
List.of("VAL"), List.of(ASC_NULLS_LAST))),
-                willThrowFast(IndexAlreadyExistsException.class)
+                manager.execute(createSortedIndexCommand(INDEX_NAME, 
List.of("VAL"), List.of(ASC_NULLS_LAST))),
+                willThrowFast(IndexExistsValidationException.class)
         );
     }
 
@@ -1580,26 +1572,26 @@ public class CatalogManagerSelfTest extends 
BaseCatalogManagerTest {
         assertThat(manager.execute(simpleTable(TABLE_NAME)), 
willCompleteSuccessfully());
 
         assertThat(
-                manager.createIndex(createHashIndexParams(TABLE_NAME, 
List.of("VAL"))),
-                willThrowFast(TableAlreadyExistsException.class)
+                manager.execute(createHashIndexCommand(TABLE_NAME, 
List.of("VAL"))),
+                willThrowFast(TableExistsValidationException.class)
         );
 
         assertThat(
-                manager.createIndex(createSortedIndexParams(TABLE_NAME, 
List.of("VAL"), List.of(ASC_NULLS_LAST))),
-                willThrowFast(TableAlreadyExistsException.class)
+                manager.execute(createSortedIndexCommand(TABLE_NAME, 
List.of("VAL"), List.of(ASC_NULLS_LAST))),
+                willThrowFast(TableExistsValidationException.class)
         );
     }
 
     @Test
     void testCreateIndexWithNotExistingTable() {
         assertThat(
-                manager.createIndex(createHashIndexParams(TABLE_NAME, 
List.of("VAL"))),
-                willThrowFast(TableNotFoundException.class)
+                manager.execute(createHashIndexCommand(TABLE_NAME, 
List.of("VAL"))),
+                willThrowFast(TableNotFoundValidationException.class)
         );
 
         assertThat(
-                manager.createIndex(createSortedIndexParams(TABLE_NAME, 
List.of("VAL"), List.of(ASC_NULLS_LAST))),
-                willThrowFast(TableNotFoundException.class)
+                manager.execute(createSortedIndexCommand(TABLE_NAME, 
List.of("VAL"), List.of(ASC_NULLS_LAST))),
+                willThrowFast(TableNotFoundValidationException.class)
         );
     }
 
@@ -1608,13 +1600,13 @@ public class CatalogManagerSelfTest extends 
BaseCatalogManagerTest {
         assertThat(manager.execute(simpleTable(TABLE_NAME)), 
willCompleteSuccessfully());
 
         assertThat(
-                manager.createIndex(createHashIndexParams(INDEX_NAME, 
List.of("fake"))),
-                willThrowFast(ColumnNotFoundException.class)
+                manager.execute(createHashIndexCommand(INDEX_NAME, 
List.of("fake"))),
+                willThrowFast(CatalogValidationException.class)
         );
 
         assertThat(
-                manager.createIndex(createSortedIndexParams(INDEX_NAME, 
List.of("fake"), List.of(ASC_NULLS_LAST))),
-                willThrowFast(ColumnNotFoundException.class)
+                manager.execute(createSortedIndexCommand(INDEX_NAME, 
List.of("fake"), List.of(ASC_NULLS_LAST))),
+                willThrowFast(CatalogValidationException.class)
         );
     }
 
@@ -1623,19 +1615,22 @@ public class CatalogManagerSelfTest extends 
BaseCatalogManagerTest {
         assertThat(manager.execute(simpleTable(TABLE_NAME)), 
willCompleteSuccessfully());
 
         assertThat(
-                manager.createIndex(createHashIndexParams(INDEX_NAME, true, 
List.of("VAL"))),
-                willThrowFast(IgniteException.class, "Unique index must 
include all colocation columns")
+                manager.execute(createHashIndexCommand(INDEX_NAME, true, 
List.of("VAL"))),
+                willThrowFast(CatalogValidationException.class, "Unique index 
must include all colocation columns")
         );
 
         assertThat(
-                manager.createIndex(createSortedIndexParams(INDEX_NAME, true, 
List.of("VAL"), List.of(ASC_NULLS_LAST))),
-                willThrowFast(IgniteException.class, "Unique index must 
include all colocation columns")
+                manager.execute(createSortedIndexCommand(INDEX_NAME, true, 
List.of("VAL"), List.of(ASC_NULLS_LAST))),
+                willThrowFast(CatalogValidationException.class, "Unique index 
must include all colocation columns")
         );
     }
 
     @Test
     void testDropNotExistingIndex() {
-        
assertThat(manager.dropIndex(DropIndexParams.builder().indexName(INDEX_NAME).build()),
 willThrowFast(IndexNotFoundException.class));
+        assertThat(
+                
manager.execute(DropIndexCommand.builder().schemaName(SCHEMA_NAME).indexName(INDEX_NAME).build()),
+                willThrowFast(IndexNotFoundValidationException.class)
+        );
     }
 
     @Test
@@ -1668,7 +1663,7 @@ public class CatalogManagerSelfTest extends 
BaseCatalogManagerTest {
     @Test
     void testDropColumnWithIndexColumns() {
         assertThat(manager.execute(simpleTable(TABLE_NAME)), 
willCompleteSuccessfully());
-        assertThat(manager.createIndex(simpleIndex()), 
willCompleteSuccessfully());
+        assertThat(manager.execute(simpleIndex()), willCompleteSuccessfully());
 
         assertThat(
                 manager.execute(dropColumnParams("VAL")),
@@ -1830,8 +1825,8 @@ public class CatalogManagerSelfTest extends 
BaseCatalogManagerTest {
         return createTableCommand(tableName, cols, 
List.of(cols.get(0).name()), List.of(cols.get(0).name()));
     }
 
-    private static CreateSortedIndexParams simpleIndex() {
-        return createSortedIndexParams(INDEX_NAME, List.of("VAL"), 
List.of(ASC_NULLS_LAST));
+    private static CatalogCommand simpleIndex() {
+        return createSortedIndexCommand(INDEX_NAME, List.of("VAL"), 
List.of(ASC_NULLS_LAST));
     }
 
     private static class TestColumnTypeParams {
diff --git 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogManagerValidationTest.java
 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogManagerValidationTest.java
index 2a9b680a47..34d01aa79d 100644
--- 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogManagerValidationTest.java
+++ 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogManagerValidationTest.java
@@ -17,26 +17,19 @@
 
 package org.apache.ignite.internal.catalog;
 
-import static 
org.apache.ignite.internal.catalog.CatalogService.DEFAULT_SCHEMA_NAME;
 import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.DEFAULT_FILTER;
 import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.DEFAULT_PARTITION_COUNT;
 import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.DEFAULT_REPLICA_COUNT;
 import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.IMMEDIATE_TIMER_VALUE;
 import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.INFINITE_TIMER_VALUE;
 import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.MAX_PARTITION_COUNT;
-import static 
org.apache.ignite.internal.catalog.descriptors.CatalogColumnCollation.ASC_NULLS_FIRST;
 import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureExceptionMatcher.willThrowFast;
 import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willBe;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.nullValue;
 
-import java.util.Arrays;
-import java.util.List;
 import org.apache.ignite.internal.catalog.commands.AlterZoneParams;
-import org.apache.ignite.internal.catalog.commands.CreateHashIndexParams;
-import org.apache.ignite.internal.catalog.commands.CreateSortedIndexParams;
 import org.apache.ignite.internal.catalog.commands.CreateZoneParams;
-import org.apache.ignite.internal.catalog.commands.DropIndexParams;
 import org.apache.ignite.internal.catalog.commands.DropZoneParams;
 import org.apache.ignite.internal.catalog.commands.RenameZoneParams;
 import org.jetbrains.annotations.Nullable;
@@ -557,102 +550,6 @@ public class CatalogManagerValidationTest extends 
BaseCatalogManagerTest {
         );
     }
 
-    @Test
-    void testValidateTableNameOnIndexCreation() {
-        assertThat(
-                
manager.createIndex(CreateHashIndexParams.builder().schemaName(DEFAULT_SCHEMA_NAME).indexName(INDEX_NAME).build()),
-                willThrowFast(CatalogValidationException.class, "Missing table 
name")
-        );
-
-        assertThat(
-                
manager.createIndex(CreateSortedIndexParams.builder().schemaName(DEFAULT_SCHEMA_NAME).indexName(INDEX_NAME).build()),
-                willThrowFast(CatalogValidationException.class, "Missing table 
name")
-        );
-    }
-
-    @Test
-    void testValidateIndexNameOnIndexCreation() {
-        assertThat(
-                
manager.createIndex(CreateHashIndexParams.builder().schemaName(DEFAULT_SCHEMA_NAME).tableName(TABLE_NAME).build()),
-                willThrowFast(CatalogValidationException.class, "Missing index 
name")
-        );
-
-        assertThat(
-                
manager.createIndex(CreateSortedIndexParams.builder().schemaName(DEFAULT_SCHEMA_NAME).tableName(TABLE_NAME).build()),
-                willThrowFast(CatalogValidationException.class, "Missing index 
name")
-        );
-    }
-
-    @Test
-    void testValidateIndexColumnsNotSpecifiedOnIndexCreation() {
-        assertThat(
-                manager.createIndex(createHashIndexParams(INDEX_NAME, null)),
-                willThrowFast(CatalogValidationException.class, "Columns not 
specified")
-        );
-
-        assertThat(
-                manager.createIndex(createSortedIndexParams(INDEX_NAME, null, 
null)),
-                willThrowFast(CatalogValidationException.class, "Columns not 
specified")
-        );
-
-        assertThat(
-                manager.createIndex(createHashIndexParams(INDEX_NAME, 
List.of())),
-                willThrowFast(CatalogValidationException.class, "Columns not 
specified")
-        );
-
-        assertThat(
-                manager.createIndex(createSortedIndexParams(INDEX_NAME, 
List.of(), null)),
-                willThrowFast(CatalogValidationException.class, "Columns not 
specified")
-        );
-    }
-
-    @Test
-    void testValidateIndexColumnsDuplicatesOnIndexCreation() {
-        assertThat(
-                manager.createIndex(createHashIndexParams(INDEX_NAME, 
Arrays.asList("key", "key"))),
-                willThrowFast(CatalogValidationException.class, "Duplicate 
columns are present")
-        );
-
-        assertThat(
-                manager.createIndex(createSortedIndexParams(INDEX_NAME, 
Arrays.asList("key", "key"), null)),
-                willThrowFast(CatalogValidationException.class, "Duplicate 
columns are present")
-        );
-    }
-
-    @Test
-    void testValidateIndexColumnsCollationsNotSpecifiedOnIndexCreation() {
-        assertThat(
-                manager.createIndex(createSortedIndexParams(INDEX_NAME, 
List.of("key"), null)),
-                willThrowFast(CatalogValidationException.class, "Columns 
collations not specified")
-        );
-
-        assertThat(
-                manager.createIndex(createSortedIndexParams(INDEX_NAME, 
List.of("key"), List.of())),
-                willThrowFast(CatalogValidationException.class, "Columns 
collations not specified")
-        );
-    }
-
-    @Test
-    void 
testValidateIndexColumnsCollationsNotScameSizeWithColumnsOnIndexCreation() {
-        assertThat(
-                manager.createIndex(createSortedIndexParams(INDEX_NAME, 
List.of("key", "val"), List.of(ASC_NULLS_FIRST))),
-                willThrowFast(CatalogValidationException.class, "Columns 
collations doesn't match number of columns")
-        );
-
-        assertThat(
-                manager.createIndex(createSortedIndexParams(INDEX_NAME, 
List.of("key"), List.of(ASC_NULLS_FIRST, ASC_NULLS_FIRST))),
-                willThrowFast(CatalogValidationException.class, "Columns 
collations doesn't match number of columns")
-        );
-    }
-
-    @Test
-    void testValidateIndexNameOnIndexDrop() {
-        assertThat(
-                
manager.dropIndex(DropIndexParams.builder().schemaName(DEFAULT_SCHEMA_NAME).build()),
-                willThrowFast(CatalogValidationException.class, "Missing index 
name")
-        );
-    }
-
     private static CreateZoneParams.Builder createZoneBuilder(String zoneName) 
{
         return CreateZoneParams.builder().zoneName(zoneName);
     }
diff --git 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/AbstractCommandValidationTest.java
 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/AbstractCommandValidationTest.java
index de1ea3cbd8..a6eaf5ae9e 100644
--- 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/AbstractCommandValidationTest.java
+++ 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/AbstractCommandValidationTest.java
@@ -24,10 +24,9 @@ import java.util.Set;
 import java.util.function.Consumer;
 import java.util.stream.Stream;
 import org.apache.ignite.internal.catalog.Catalog;
-import 
org.apache.ignite.internal.catalog.descriptors.CatalogHashIndexDescriptor;
+import org.apache.ignite.internal.catalog.CatalogCommand;
 import org.apache.ignite.internal.catalog.descriptors.CatalogIndexDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.CatalogSchemaDescriptor;
-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;
@@ -65,20 +64,17 @@ abstract class AbstractCommandValidationTest extends 
BaseIgniteAbstractTest {
     }
 
     static Catalog catalogWithTable(String name) {
-        int tableId = 0;
-        int tableVersion = 1;
-        int zoneId = 0;
-        List<CatalogTableColumnDescriptor> columns = List.of(
-                new CatalogTableColumnDescriptor("ID", INT32, false, -1, -1, 
-1, null),
-                new CatalogTableColumnDescriptor("VAL", INT32, true, -1, -1, 
-1, null)
+        return catalog(
+                CreateTableCommand.builder()
+                        .schemaName(SCHEMA_NAME)
+                        .tableName(name)
+                        .columns(List.of(
+                                
ColumnParams.builder().name("ID").type(INT32).build(),
+                                
ColumnParams.builder().name("VAL").type(INT32).build()
+                        ))
+                        .primaryKeyColumns(List.of("ID"))
+                        .build()
         );
-        List<String> pkColumns = List.of("ID");
-
-        CatalogTableDescriptor table = new CatalogTableDescriptor(
-                tableId, name, zoneId, tableVersion, columns, pkColumns, 
pkColumns
-        );
-
-        return catalog(new CatalogTableDescriptor[]{table}, new 
CatalogIndexDescriptor[0]);
     }
 
     static Catalog catalogWithTable(Consumer<CreateTableCommandBuilder> 
tableDef) {
@@ -86,29 +82,43 @@ abstract class AbstractCommandValidationTest extends 
BaseIgniteAbstractTest {
 
         tableDef.accept(builder);
 
-        Catalog catalog = emptyCatalog();
-        for (UpdateEntry entry : builder.build().get(catalog)) {
-            catalog = entry.applyUpdate(catalog);
-        }
-
-        return catalog;
+        return catalog(builder.build());
     }
 
     static Catalog catalogWithIndex(String name) {
-        CatalogTableColumnDescriptor id = new CatalogTableColumnDescriptor(
-                "ID", INT32, false, -1, -1, -1, null
-        );
-        CatalogTableColumnDescriptor val = new CatalogTableColumnDescriptor(
-                "VAL", INT32, true, -1, -1, -1, null
-        );
-        CatalogTableDescriptor table = new CatalogTableDescriptor(
-                0, TABLE_NAME, 0, 1, List.of(id, val), List.of("ID"), 
List.of("ID")
-        );
-        CatalogIndexDescriptor index = new CatalogHashIndexDescriptor(
-                1, name, 0, false, List.of("VAL")
-        );
+        return catalog(List.of(
+                CreateTableCommand.builder()
+                        .schemaName(SCHEMA_NAME)
+                        .tableName(TABLE_NAME)
+                        .columns(List.of(
+                                
ColumnParams.builder().name("ID").type(INT32).build(),
+                                
ColumnParams.builder().name("VAL").type(INT32).build()
+                        ))
+                        .primaryKeyColumns(List.of("ID"))
+                        .build(),
+                CreateHashIndexCommand.builder()
+                        .schemaName(SCHEMA_NAME)
+                        .indexName(name)
+                        .tableName(TABLE_NAME)
+                        .columns(List.of("VAL"))
+                        .build()
+        ));
+    }
 
-        return catalog(new CatalogTableDescriptor[]{table}, new 
CatalogIndexDescriptor[]{index});
+    static Catalog catalog(CatalogCommand commandToApply) {
+        return catalog(List.of(commandToApply));
+    }
+
+    static Catalog catalog(List<CatalogCommand> commandsToApply) {
+        Catalog catalog = emptyCatalog();
+
+        for (CatalogCommand command : commandsToApply) {
+            for (UpdateEntry updates : command.get(catalog)) {
+                catalog = updates.applyUpdate(catalog);
+            }
+        }
+
+        return catalog;
     }
 
     private static Catalog catalog(CatalogTableDescriptor[] tables, 
CatalogIndexDescriptor[] indexes) {
diff --git 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/CreateAbstractIndexCommandValidationTest.java
 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/CreateAbstractIndexCommandValidationTest.java
new file mode 100644
index 0000000000..eb80690bfb
--- /dev/null
+++ 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/CreateAbstractIndexCommandValidationTest.java
@@ -0,0 +1,222 @@
+/*
+ * 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.testframework.IgniteTestUtils.assertThrowsWithCause;
+import static org.apache.ignite.sql.ColumnType.INT32;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.ignite.internal.catalog.Catalog;
+import org.apache.ignite.internal.catalog.CatalogCommand;
+import org.apache.ignite.internal.catalog.CatalogValidationException;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+/**
+ * Tests to verify validation of {@link AbstractCreateIndexCommand}.
+ */
+@SuppressWarnings({"ThrowableNotThrown", "rawtypes", "unchecked"})
+public abstract class CreateAbstractIndexCommandValidationTest extends 
AbstractCommandValidationTest {
+    protected static final String INDEX_NAME = "IDX";
+
+    protected abstract <T extends AbstractCreateIndexCommandBuilder<T>> T 
prefilledBuilder();
+
+    @ParameterizedTest(name = "[{index}] ''{argumentsWithNames}''")
+    @MethodSource("nullAndBlankStrings")
+    void schemaNameMustNotBeNullOrBlank(String name) {
+        AbstractCreateIndexCommandBuilder builder = prefilledBuilder();
+
+        builder.schemaName(name);
+
+        assertThrowsWithCause(
+                builder::build,
+                CatalogValidationException.class,
+                "Name of the schema can't be null or blank"
+        );
+    }
+
+    @ParameterizedTest(name = "[{index}] ''{argumentsWithNames}''")
+    @MethodSource("nullAndBlankStrings")
+    void indexNameMustNotBeNullOrBlank(String name) {
+        AbstractCreateIndexCommandBuilder builder = prefilledBuilder();
+
+        builder.indexName(name);
+
+        assertThrowsWithCause(
+                builder::build,
+                CatalogValidationException.class,
+                "Name of the index can't be null or blank"
+        );
+    }
+
+    @ParameterizedTest(name = "[{index}] ''{argumentsWithNames}''")
+    @MethodSource("nullAndBlankStrings")
+    void tableNameMustNotBeNullOrBlank(String name) {
+        AbstractCreateIndexCommandBuilder builder = prefilledBuilder();
+
+        builder.tableName(name);
+
+        assertThrowsWithCause(
+                builder::build,
+                CatalogValidationException.class,
+                "Name of the table can't be null or blank"
+        );
+    }
+
+    @ParameterizedTest(name = "[{index}] {argumentsWithNames}")
+    @MethodSource("nullAndEmptyLists")
+    void indexShouldHaveAtLeastOneColumn(List<String> columns) {
+        AbstractCreateIndexCommandBuilder builder = prefilledBuilder();
+
+        builder.columns(columns);
+
+        assertThrowsWithCause(
+                builder::build,
+                CatalogValidationException.class,
+                "Columns not specified"
+        );
+    }
+
+    @ParameterizedTest(name = "[{index}] ''{argumentsWithNames}''")
+    @MethodSource("nullAndBlankStrings")
+    void columnNameMustNotBeNullOrBlank(String name) {
+        AbstractCreateIndexCommandBuilder builder = prefilledBuilder();
+
+        List<String> names = new ArrayList<>();
+        names.add(name);
+
+        builder.columns(names);
+
+        assertThrowsWithCause(
+                builder::build,
+                CatalogValidationException.class,
+                "Name of the column can't be null or blank"
+        );
+    }
+
+    @Test
+    void columnShouldNotHaveDuplicates() {
+        AbstractCreateIndexCommandBuilder builder = prefilledBuilder();
+
+        builder.columns(List.of("C", "C"));
+
+        assertThrowsWithCause(
+                builder::build,
+                CatalogValidationException.class,
+                "Column with name 'C' specified more than once"
+        );
+    }
+
+    @Test
+    void exceptionIsThrownIfSchemaNotExists() {
+        Catalog catalog = emptyCatalog();
+
+        CatalogCommand command = prefilledBuilder().schemaName(SCHEMA_NAME + 
"_UNK").build();
+
+        assertThrowsWithCause(
+                () -> command.get(catalog),
+                CatalogValidationException.class,
+                "Schema with name 'PUBLIC_UNK' not found"
+        );
+    }
+
+    @Test
+    void exceptionIsThrownIfTableNotExists() {
+        Catalog catalog = emptyCatalog();
+
+        CatalogCommand command = prefilledBuilder().build();
+
+        assertThrowsWithCause(
+                () -> command.get(catalog),
+                CatalogValidationException.class,
+                "Table with name 'PUBLIC.TEST' not found"
+        );
+    }
+
+    @Test
+    void exceptionIsThrownIfTableWithGivenNameAlreadyExists() {
+        Catalog catalog = catalogWithTable("TEST");
+
+        CatalogCommand command = prefilledBuilder()
+                .tableName("TEST")
+                .indexName("TEST")
+                .build();
+
+        assertThrowsWithCause(
+                () -> command.get(catalog),
+                CatalogValidationException.class,
+                "Table with name 'PUBLIC.TEST' already exists"
+        );
+    }
+
+    @Test
+    void exceptionIsThrownIfIndexWithGivenNameAlreadyExists() {
+        Catalog catalog = catalogWithIndex("IDX");
+
+        CatalogCommand command = prefilledBuilder().indexName("IDX").build();
+
+        assertThrowsWithCause(
+                () -> command.get(catalog),
+                CatalogValidationException.class,
+                "Index with name 'PUBLIC.IDX' already exists"
+        );
+    }
+
+    @Test
+    void exceptionIsThrownIfColumnWithGivenNameNotExists() {
+        String tableName = "TEST";
+
+        Catalog catalog = catalogWithTable(tableName);
+
+        CatalogCommand command = 
prefilledBuilder().columns(List.of("UNK")).build();
+
+        assertThrowsWithCause(
+                () -> command.get(catalog),
+                CatalogValidationException.class,
+                "Column with name 'UNK' not found in table 'PUBLIC.TEST'"
+        );
+    }
+
+    @Test
+    void exceptionIsThrownIfIndexIsUniqueAndNotAllColocationColumnsCovered() {
+        String tableName = "TEST";
+
+        ColumnParams c1 = 
ColumnParams.builder().name("C1").type(INT32).build();
+        ColumnParams c2 = 
ColumnParams.builder().name("C2").type(INT32).build();
+
+        Catalog catalog = catalogWithTable(builder -> builder
+                .schemaName(SCHEMA_NAME)
+                .tableName(tableName)
+                .columns(List.of(c1, c2))
+                .primaryKeyColumns(List.of(c1.name(), c2.name()))
+        );
+
+        CatalogCommand command = prefilledBuilder()
+                .unique(true)
+                .columns(List.of(c1.name()))
+                .build();
+
+        assertThrowsWithCause(
+                () -> command.get(catalog),
+                CatalogValidationException.class,
+                "Unique index must include all colocation columns"
+        );
+    }
+}
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateHashIndexParams.java
 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/CreateHashIndexCommandValidationTest.java
similarity index 62%
rename from 
modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateHashIndexParams.java
rename to 
modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/CreateHashIndexCommandValidationTest.java
index 2a765ddbef..300cbfb274 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateHashIndexParams.java
+++ 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/CreateHashIndexCommandValidationTest.java
@@ -17,21 +17,18 @@
 
 package org.apache.ignite.internal.catalog.commands;
 
-/** CREATE INDEX statement. */
-public class CreateHashIndexParams extends AbstractCreateIndexCommandParams {
-    /** Creates parameters builder. */
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    private CreateHashIndexParams() {
-        // No-op.
-    }
+import java.util.List;
 
-    /** Parameters builder. */
-    public static class Builder extends 
AbstractCreateIndexBuilder<CreateHashIndexParams, Builder> {
-        private Builder() {
-            super(new CreateHashIndexParams());
-        }
+/**
+ * Tests to verify validation of {@link CreateHashIndexCommand}.
+ */
+public class CreateHashIndexCommandValidationTest extends 
CreateAbstractIndexCommandValidationTest {
+    @Override
+    protected <T extends AbstractCreateIndexCommandBuilder<T>> T 
prefilledBuilder() {
+        return (T) CreateHashIndexCommand.builder()
+                .schemaName(SCHEMA_NAME)
+                .indexName(INDEX_NAME)
+                .tableName(TABLE_NAME)
+                .columns(List.of("VAL"));
     }
 }
diff --git 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/CreateSortedIndexCommandValidationTest.java
 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/CreateSortedIndexCommandValidationTest.java
new file mode 100644
index 0000000000..0be6ee32ea
--- /dev/null
+++ 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/CreateSortedIndexCommandValidationTest.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.catalog.commands;
+
+import static 
org.apache.ignite.internal.testframework.IgniteTestUtils.assertThrowsWithCause;
+
+import java.util.List;
+import org.apache.ignite.internal.catalog.CatalogValidationException;
+import org.apache.ignite.internal.catalog.descriptors.CatalogColumnCollation;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+/**
+ * Tests to verify validation of {@link CreateSortedIndexCommand}.
+ */
+@SuppressWarnings("ThrowableNotThrown")
+public class CreateSortedIndexCommandValidationTest extends 
CreateAbstractIndexCommandValidationTest {
+    @Override
+    protected <T extends AbstractCreateIndexCommandBuilder<T>> T 
prefilledBuilder() {
+        return (T) fillBuilder(CreateSortedIndexCommand.builder());
+    }
+
+    @ParameterizedTest(name = "[{index}] {argumentsWithNames}")
+    @MethodSource("nullAndEmptyLists")
+    void collationsShouldBeSpecified(List<CatalogColumnCollation> collations) {
+        CreateSortedIndexCommandBuilder builder = 
fillBuilder(CreateSortedIndexCommand.builder());
+
+        builder.collations(collations);
+
+        assertThrowsWithCause(
+                builder::build,
+                CatalogValidationException.class,
+                "Collations not specified"
+        );
+    }
+
+    @Test
+    void collationsSizeShouldMatchColumnsSize() {
+        CreateSortedIndexCommandBuilder builder = 
fillBuilder(CreateSortedIndexCommand.builder())
+                .columns(List.of("C1", "C2"));
+
+        List<CatalogColumnCollation> collations = List.of(
+                CatalogColumnCollation.ASC_NULLS_FIRST,
+                CatalogColumnCollation.ASC_NULLS_FIRST,
+                CatalogColumnCollation.ASC_NULLS_FIRST
+        );
+
+        builder.collations(collations);
+
+        assertThrowsWithCause(
+                builder::build,
+                CatalogValidationException.class,
+                "Columns collations doesn't match number of columns"
+        );
+
+        builder.collations(collations.subList(0, 1));
+
+        assertThrowsWithCause(
+                builder::build,
+                CatalogValidationException.class,
+                "Columns collations doesn't match number of columns"
+        );
+    }
+
+    private static CreateSortedIndexCommandBuilder 
fillBuilder(CreateSortedIndexCommandBuilder builder) {
+        return builder.schemaName(SCHEMA_NAME)
+                .indexName(INDEX_NAME)
+                .tableName(TABLE_NAME)
+                .columns(List.of("VAL"))
+                .collations(List.of(CatalogColumnCollation.ASC_NULLS_FIRST));
+    }
+}
diff --git 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/CreateTableCommandValidationTest.java
 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/CreateTableCommandValidationTest.java
index 5e0b9d1346..f4ad02a8f0 100644
--- 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/CreateTableCommandValidationTest.java
+++ 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/CreateTableCommandValidationTest.java
@@ -298,14 +298,14 @@ public class CreateTableCommandValidationTest extends 
AbstractCommandValidationT
     void exceptionIsThrownIfIndexWithGivenNameAlreadyExists() {
         CreateTableCommandBuilder builder = CreateTableCommand.builder();
 
-        Catalog catalog = catalogWithIndex("TEST");
+        Catalog catalog = catalogWithIndex("TEST_IDX");
 
-        CatalogCommand command = 
fillProperties(builder).tableName("TEST").build();
+        CatalogCommand command = 
fillProperties(builder).tableName("TEST_IDX").build();
 
         assertThrowsWithCause(
                 () -> command.get(catalog),
                 CatalogValidationException.class,
-                "Index with name 'PUBLIC.TEST' already exists"
+                "Index with name 'PUBLIC.TEST_IDX' already exists"
         );
     }
 
diff --git 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/DropIndexCommandValidationTest.java
 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/DropIndexCommandValidationTest.java
new file mode 100644
index 0000000000..0d83af17ac
--- /dev/null
+++ 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/DropIndexCommandValidationTest.java
@@ -0,0 +1,121 @@
+/*
+ * 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.testframework.IgniteTestUtils.assertThrowsWithCause;
+
+import java.util.List;
+import org.apache.ignite.internal.catalog.Catalog;
+import org.apache.ignite.internal.catalog.CatalogCommand;
+import org.apache.ignite.internal.catalog.CatalogValidationException;
+import org.apache.ignite.sql.ColumnType;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+/**
+ * Tests to verify validation of {@link DropIndexCommand}.
+ */
+@SuppressWarnings("ThrowableNotThrown")
+public class DropIndexCommandValidationTest extends 
AbstractCommandValidationTest {
+    @ParameterizedTest(name = "[{index}] ''{argumentsWithNames}''")
+    @MethodSource("nullAndBlankStrings")
+    void schemaNameMustNotBeNullOrBlank(String name) {
+        DropIndexCommandBuilder builder = DropIndexCommand.builder();
+
+        builder.indexName("TEST")
+                .schemaName(name);
+
+        assertThrowsWithCause(
+                builder::build,
+                CatalogValidationException.class,
+                "Name of the schema can't be null or blank"
+        );
+    }
+
+    @ParameterizedTest(name = "[{index}] ''{argumentsWithNames}''")
+    @MethodSource("nullAndBlankStrings")
+    void indexNameMustNotBeNullOrBlank(String name) {
+        DropIndexCommandBuilder builder = DropIndexCommand.builder();
+
+        builder.schemaName("TEST")
+                .indexName(name);
+
+        assertThrowsWithCause(
+                builder::build,
+                CatalogValidationException.class,
+                "Name of the index can't be null or blank"
+        );
+    }
+
+    @Test
+    void exceptionIsThrownIfSchemaNotExists() {
+        Catalog catalog = emptyCatalog();
+
+        CatalogCommand command = DropIndexCommand.builder()
+                .schemaName(SCHEMA_NAME + "_UNK")
+                .indexName("TEST")
+                .build();
+
+        assertThrowsWithCause(
+                () -> command.get(catalog),
+                CatalogValidationException.class,
+                "Schema with name 'PUBLIC_UNK' not found"
+        );
+    }
+
+    @Test
+    void exceptionIsThrownIfIndexWithGivenNameNotFound() {
+        Catalog catalog = emptyCatalog();
+
+        CatalogCommand command = DropIndexCommand.builder()
+                .schemaName(SCHEMA_NAME)
+                .indexName("TEST")
+                .build();
+
+        assertThrowsWithCause(
+                () -> command.get(catalog),
+                CatalogValidationException.class,
+                "Index with name 'PUBLIC.TEST' not found"
+        );
+    }
+
+    @Test
+    void exceptionIsThrownIfIndexIsPrimaryKey() {
+        ColumnParams columnParams = 
ColumnParams.builder().name("C").type(ColumnType.INT32).build();
+        Catalog catalog = catalog(
+                CreateTableCommand.builder()
+                        .schemaName(SCHEMA_NAME)
+                        .tableName(TABLE_NAME)
+                        .columns(List.of(columnParams))
+                        .primaryKeyColumns(List.of(columnParams.name()))
+                        .build()
+        );
+
+        CatalogCommand command = DropIndexCommand.builder()
+                .schemaName(SCHEMA_NAME)
+                .indexName(TABLE_NAME + "_PK")
+                .build();
+
+        assertThrowsWithCause(
+                () -> command.get(catalog),
+                CatalogValidationException.class,
+                "Dropping primary key index is not allowed"
+        );
+    }
+}
diff --git 
a/modules/catalog/src/testFixtures/java/org/apache/ignite/internal/catalog/BaseCatalogManagerTest.java
 
b/modules/catalog/src/testFixtures/java/org/apache/ignite/internal/catalog/BaseCatalogManagerTest.java
index f06c5a746e..ebedc6480c 100644
--- 
a/modules/catalog/src/testFixtures/java/org/apache/ignite/internal/catalog/BaseCatalogManagerTest.java
+++ 
b/modules/catalog/src/testFixtures/java/org/apache/ignite/internal/catalog/BaseCatalogManagerTest.java
@@ -30,8 +30,8 @@ import java.util.stream.Stream;
 import org.apache.ignite.internal.catalog.commands.AlterTableAddColumnCommand;
 import org.apache.ignite.internal.catalog.commands.AlterTableDropColumnCommand;
 import org.apache.ignite.internal.catalog.commands.ColumnParams;
-import org.apache.ignite.internal.catalog.commands.CreateHashIndexParams;
-import org.apache.ignite.internal.catalog.commands.CreateSortedIndexParams;
+import org.apache.ignite.internal.catalog.commands.CreateHashIndexCommand;
+import org.apache.ignite.internal.catalog.commands.CreateSortedIndexCommand;
 import org.apache.ignite.internal.catalog.commands.CreateTableCommand;
 import org.apache.ignite.internal.catalog.commands.DropTableCommand;
 import org.apache.ignite.internal.catalog.descriptors.CatalogColumnCollation;
@@ -100,62 +100,49 @@ public abstract class BaseCatalogManagerTest extends 
BaseIgniteAbstractTest {
         );
     }
 
-    protected static CreateHashIndexParams createHashIndexParams(
+    protected static CatalogCommand createHashIndexCommand(
             String indexName,
             boolean uniq,
             @Nullable List<String> indexColumns
     ) {
-        CreateHashIndexParams.Builder builder = CreateHashIndexParams.builder()
+        return CreateHashIndexCommand.builder()
                 .schemaName(DEFAULT_SCHEMA_NAME)
                 .tableName(TABLE_NAME)
-                .indexName(indexName);
-
-        builder.unique(uniq);
-
-        if (indexColumns != null) {
-            builder.columns(indexColumns);
-        }
-
-        return builder.build();
+                .indexName(indexName)
+                .unique(uniq)
+                .columns(indexColumns)
+                .build();
     }
 
-    protected static CreateHashIndexParams createHashIndexParams(
+    protected static CatalogCommand createHashIndexCommand(
             String indexName,
             @Nullable List<String> indexColumns
     ) {
-        return createHashIndexParams(indexName, false, indexColumns);
+        return createHashIndexCommand(indexName, false, indexColumns);
     }
 
-    protected static CreateSortedIndexParams createSortedIndexParams(
+    protected static CatalogCommand createSortedIndexCommand(
             String indexName,
-            boolean uniq,
+            boolean unique,
             @Nullable List<String> indexColumns,
             @Nullable List<CatalogColumnCollation> columnsCollations
     ) {
-        CreateSortedIndexParams.Builder builder = 
CreateSortedIndexParams.builder()
+        return CreateSortedIndexCommand.builder()
                 .schemaName(DEFAULT_SCHEMA_NAME)
                 .tableName(TABLE_NAME)
-                .indexName(indexName);
-
-        builder.unique(uniq);
-
-        if (indexColumns != null) {
-            builder.columns(indexColumns);
-        }
-
-        if (columnsCollations != null) {
-            builder.collations(columnsCollations);
-        }
-
-        return builder.build();
+                .indexName(indexName)
+                .unique(unique)
+                .columns(indexColumns)
+                .collations(columnsCollations)
+                .build();
     }
 
-    protected static CreateSortedIndexParams createSortedIndexParams(
+    protected static CatalogCommand createSortedIndexCommand(
             String indexName,
             @Nullable List<String> indexColumns,
             @Nullable List<CatalogColumnCollation> columnsCollations
     ) {
-        return createSortedIndexParams(indexName, false, indexColumns, 
columnsCollations);
+        return createSortedIndexCommand(indexName, false, indexColumns, 
columnsCollations);
     }
 
     protected static CatalogCommand createTableCommand(
diff --git 
a/modules/distribution-zones/src/test/java/org/apache/ignite/internal/distributionzones/rebalance/RebalanceUtilUpdateAssignmentsTest.java
 
b/modules/distribution-zones/src/test/java/org/apache/ignite/internal/distributionzones/rebalance/RebalanceUtilUpdateAssignmentsTest.java
index 1187527d71..99d96e5691 100644
--- 
a/modules/distribution-zones/src/test/java/org/apache/ignite/internal/distributionzones/rebalance/RebalanceUtilUpdateAssignmentsTest.java
+++ 
b/modules/distribution-zones/src/test/java/org/apache/ignite/internal/distributionzones/rebalance/RebalanceUtilUpdateAssignmentsTest.java
@@ -96,6 +96,7 @@ public class RebalanceUtilUpdateAssignmentsTest extends 
IgniteAbstractTest {
 
     private final CatalogTableDescriptor tableDescriptor = new 
CatalogTableDescriptor(
             1,
+            -1,
             "table1",
             0,
             1,
diff --git 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/ItTablesApiTest.java
 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/ItTablesApiTest.java
index cb96e88300..0acac49c2c 100644
--- 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/ItTablesApiTest.java
+++ 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/ItTablesApiTest.java
@@ -44,6 +44,7 @@ import java.util.stream.IntStream;
 import org.apache.ignite.Ignite;
 import org.apache.ignite.IgnitionManager;
 import org.apache.ignite.InitParameters;
+import org.apache.ignite.internal.catalog.IndexExistsValidationException;
 import org.apache.ignite.internal.schema.testutils.builder.SchemaBuilders;
 import org.apache.ignite.internal.schema.testutils.definition.ColumnDefinition;
 import org.apache.ignite.internal.schema.testutils.definition.ColumnType;
@@ -266,7 +267,7 @@ public class ItTablesApiTest extends IgniteAbstractTest {
         try {
             tryToCreateIndex(ignite0, TABLE_NAME, true);
         } catch (Throwable e) {
-            IgniteTestUtils.hasCause(e, IndexAlreadyExistsException.class, 
null);
+            IgniteTestUtils.hasCause(e, IndexExistsValidationException.class, 
null);
         }
 
         tryToCreateIndex(ignite0, TABLE_NAME, false);
@@ -303,7 +304,7 @@ public class ItTablesApiTest extends IgniteAbstractTest {
 
                     fail("Should not reach here");
                 } catch (Throwable e) {
-                    IgniteTestUtils.hasCause(e, 
IndexAlreadyExistsException.class, null);
+                    IgniteTestUtils.hasCause(e, 
IndexExistsValidationException.class, null);
                 }
 
                 addIndexIfNotExists(ignite, TABLE_NAME);
diff --git 
a/modules/schema/src/main/java/org/apache/ignite/internal/schema/CatalogDescriptorUtils.java
 
b/modules/schema/src/main/java/org/apache/ignite/internal/schema/CatalogDescriptorUtils.java
index 101cddf769..b7e9ab8466 100644
--- 
a/modules/schema/src/main/java/org/apache/ignite/internal/schema/CatalogDescriptorUtils.java
+++ 
b/modules/schema/src/main/java/org/apache/ignite/internal/schema/CatalogDescriptorUtils.java
@@ -64,6 +64,7 @@ public class CatalogDescriptorUtils {
 
         return new CatalogTableDescriptor(
                 config.id(),
+                -1,
                 config.name(),
                 config.zoneId(),
                 ((ExtendedTableView) config).schemaId(),
diff --git 
a/modules/schema/src/test/java/org/apache/ignite/internal/schema/CatalogSchemaManagerTest.java
 
b/modules/schema/src/test/java/org/apache/ignite/internal/schema/CatalogSchemaManagerTest.java
index 1d963a91ce..9eb728e92c 100644
--- 
a/modules/schema/src/test/java/org/apache/ignite/internal/schema/CatalogSchemaManagerTest.java
+++ 
b/modules/schema/src/test/java/org/apache/ignite/internal/schema/CatalogSchemaManagerTest.java
@@ -171,7 +171,9 @@ class CatalogSchemaManagerTest extends 
BaseIgniteAbstractTest {
                 new CatalogTableColumnDescriptor("k2", ColumnType.STRING, 
false, 0, 0, 0, null),
                 new CatalogTableColumnDescriptor("v1", ColumnType.INT32, 
false, 0, 0, 0, null)
         );
-        CatalogTableDescriptor tableDescriptor = new 
CatalogTableDescriptor(TABLE_ID, TABLE_NAME, 0, 1, columns, List.of("k1", 
"k2"), null);
+        CatalogTableDescriptor tableDescriptor = new CatalogTableDescriptor(
+                TABLE_ID, -1, TABLE_NAME, 0, 1, columns, List.of("k1", "k2"), 
null
+        );
 
         CompletableFuture<Boolean> future = tableCreatedListener()
                 .notify(new CreateTableEventParameters(CAUSALITY_TOKEN_1, 
CATALOG_VERSION_1, tableDescriptor), null);
@@ -240,7 +242,7 @@ class CatalogSchemaManagerTest extends 
BaseIgniteAbstractTest {
                 new CatalogTableColumnDescriptor("v2", ColumnType.STRING, 
false, 0, 0, 0, null)
         );
 
-        return new CatalogTableDescriptor(TABLE_ID, TABLE_NAME, 0, 2, columns, 
List.of("k1", "k2"), null);
+        return new CatalogTableDescriptor(TABLE_ID, -1, TABLE_NAME, 0, 2, 
columns, List.of("k1", "k2"), null);
     }
 
     private void completeCausalityToken(long causalityToken) {
@@ -276,7 +278,7 @@ class CatalogSchemaManagerTest extends 
BaseIgniteAbstractTest {
                 new CatalogTableColumnDescriptor("k2", ColumnType.STRING, 
false, 0, 0, 0, null)
         );
 
-        return new CatalogTableDescriptor(TABLE_ID, TABLE_NAME, 0, 2, columns, 
List.of("k1", "k2"), null);
+        return new CatalogTableDescriptor(TABLE_ID, -1, TABLE_NAME, 0, 2, 
columns, List.of("k1", "k2"), null);
     }
 
     @Test
@@ -314,7 +316,7 @@ class CatalogSchemaManagerTest extends 
BaseIgniteAbstractTest {
                 new CatalogTableColumnDescriptor("v1", ColumnType.INT64, 
false, 0, 0, 0, null)
         );
 
-        return new CatalogTableDescriptor(TABLE_ID, TABLE_NAME, 0, 2, columns, 
List.of("k1", "k2"), null);
+        return new CatalogTableDescriptor(TABLE_ID, -1, TABLE_NAME, 0, 2, 
columns, List.of("k1", "k2"), null);
     }
 
     @Test
diff --git 
a/modules/schema/src/test/java/org/apache/ignite/internal/schema/catalog/CatalogToSchemaDescriptorConverterTest.java
 
b/modules/schema/src/test/java/org/apache/ignite/internal/schema/catalog/CatalogToSchemaDescriptorConverterTest.java
index 8b58543309..ee87e1ab30 100644
--- 
a/modules/schema/src/test/java/org/apache/ignite/internal/schema/catalog/CatalogToSchemaDescriptorConverterTest.java
+++ 
b/modules/schema/src/test/java/org/apache/ignite/internal/schema/catalog/CatalogToSchemaDescriptorConverterTest.java
@@ -128,6 +128,7 @@ public class CatalogToSchemaDescriptorConverterTest extends 
AbstractSchemaConver
     public void convertTableDescriptor() {
         CatalogTableDescriptor tableDescriptor = new CatalogTableDescriptor(
                 1,
+                -1,
                 "test",
                 0,
                 1,
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 139e0ffc81..8e43f887f3 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
@@ -20,11 +20,10 @@ package org.apache.ignite.internal.sql.engine.exec.ddl;
 import java.util.Objects;
 import java.util.concurrent.CompletableFuture;
 import org.apache.ignite.internal.catalog.CatalogManager;
+import org.apache.ignite.internal.catalog.IndexExistsValidationException;
+import org.apache.ignite.internal.catalog.IndexNotFoundValidationException;
 import org.apache.ignite.internal.catalog.TableExistsValidationException;
 import org.apache.ignite.internal.catalog.TableNotFoundValidationException;
-import 
org.apache.ignite.internal.catalog.commands.AbstractCreateIndexCommandParams;
-import org.apache.ignite.internal.catalog.commands.CreateHashIndexParams;
-import org.apache.ignite.internal.catalog.commands.CreateSortedIndexParams;
 import 
org.apache.ignite.internal.distributionzones.DistributionZoneAlreadyExistsException;
 import org.apache.ignite.internal.distributionzones.DistributionZoneManager;
 import 
org.apache.ignite.internal.distributionzones.DistributionZoneNotFoundException;
@@ -117,19 +116,16 @@ public class DdlCommandHandlerWrapper extends 
DdlCommandHandler {
                     );
         } else if (cmd instanceof CreateIndexCommand) {
             return ddlCommandFuture
-                    .thenCompose(res -> {
-                        AbstractCreateIndexCommandParams params = 
DdlToCatalogCommandConverter.convert((CreateIndexCommand) cmd);
-                        if (params instanceof CreateSortedIndexParams) {
-                            return 
catalogManager.createIndex((CreateSortedIndexParams) params);
-                        } else {
-                            return 
catalogManager.createIndex((CreateHashIndexParams) params);
-                        }
-                    }).handle(handleModificationResult(((CreateIndexCommand) 
cmd).ifNotExists(), IndexAlreadyExistsException.class));
+                    .thenCompose(res -> 
catalogManager.execute(DdlToCatalogCommandConverter.convert((CreateIndexCommand)
 cmd))
+                            .handle(handleModificationResult(
+                                    ((CreateIndexCommand) cmd).ifNotExists(), 
IndexExistsValidationException.class)))
+                    .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))
-                    );
+                    .thenCompose(res -> 
catalogManager.execute(DdlToCatalogCommandConverter.convert((DropIndexCommand) 
cmd))
+                            .handle(handleModificationResult(
+                                    ((DropIndexCommand) cmd).ifNotExists(), 
IndexNotFoundValidationException.class))
+                    ).handle(handleModificationResult(((DropIndexCommand) 
cmd).ifNotExists(), IndexNotFoundException.class));
         } else if (cmd instanceof CreateZoneCommand) {
             CreateZoneCommand zoneCommand = (CreateZoneCommand) cmd;
 
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 5250403d44..fa82855561 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
@@ -27,19 +27,17 @@ import java.util.stream.Collectors;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.ignite.internal.catalog.CatalogCommand;
-import 
org.apache.ignite.internal.catalog.commands.AbstractCreateIndexCommandParams;
 import org.apache.ignite.internal.catalog.commands.AlterTableAddColumnCommand;
 import 
org.apache.ignite.internal.catalog.commands.AlterTableAlterColumnCommand;
 import 
org.apache.ignite.internal.catalog.commands.AlterTableAlterColumnCommandBuilder;
 import org.apache.ignite.internal.catalog.commands.AlterTableDropColumnCommand;
 import org.apache.ignite.internal.catalog.commands.AlterZoneParams;
 import org.apache.ignite.internal.catalog.commands.ColumnParams;
-import org.apache.ignite.internal.catalog.commands.CreateHashIndexParams;
-import org.apache.ignite.internal.catalog.commands.CreateSortedIndexParams;
+import org.apache.ignite.internal.catalog.commands.CreateHashIndexCommand;
+import org.apache.ignite.internal.catalog.commands.CreateSortedIndexCommand;
 import org.apache.ignite.internal.catalog.commands.CreateZoneParams;
 import org.apache.ignite.internal.catalog.commands.DataStorageParams;
 import org.apache.ignite.internal.catalog.commands.DefaultValue;
-import org.apache.ignite.internal.catalog.commands.DropIndexParams;
 import org.apache.ignite.internal.catalog.commands.DropZoneParams;
 import org.apache.ignite.internal.catalog.commands.RenameZoneParams;
 import org.apache.ignite.internal.catalog.descriptors.CatalogColumnCollation;
@@ -194,10 +192,10 @@ class DdlToCatalogCommandConverter {
     }
 
 
-    static AbstractCreateIndexCommandParams convert(CreateIndexCommand cmd) {
+    static CatalogCommand convert(CreateIndexCommand cmd) {
         switch (cmd.type()) {
             case HASH:
-                return CreateHashIndexParams.builder()
+                return CreateHashIndexCommand.builder()
                         .schemaName(cmd.schemaName())
                         .indexName(cmd.indexName())
 
@@ -210,7 +208,7 @@ class DdlToCatalogCommandConverter {
                         .map(DdlToCatalogCommandConverter::convert)
                         .collect(Collectors.toList());
 
-                return CreateSortedIndexParams.builder()
+                return CreateSortedIndexCommand.builder()
                         .schemaName(cmd.schemaName())
                         .indexName(cmd.indexName())
 
@@ -224,8 +222,8 @@ class DdlToCatalogCommandConverter {
         }
     }
 
-    static DropIndexParams convert(DropIndexCommand cmd) {
-        return DropIndexParams.builder()
+    static CatalogCommand convert(DropIndexCommand cmd) {
+        return 
org.apache.ignite.internal.catalog.commands.DropIndexCommand.builder()
                 .schemaName(cmd.schemaName())
                 .indexName(cmd.indexName())
                 .build();
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/schema/CatalogSqlSchemaManagerTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/schema/CatalogSqlSchemaManagerTest.java
index 3df3faca3e..b62817422c 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/schema/CatalogSqlSchemaManagerTest.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/schema/CatalogSqlSchemaManagerTest.java
@@ -577,6 +577,7 @@ public class CatalogSqlSchemaManagerTest extends 
BaseIgniteAbstractTest {
 
             return new CatalogTableDescriptor(
                     id,
+                    -1,
                     name,
                     zoneId,
                     CatalogTableDescriptor.INITIAL_TABLE_VERSION,


Reply via email to