IGNITE-22000 Get rid of unnecessary conversion of DDL commands (#3691)

diff --git a/modules/api/src/main/java/org/apache/ignite/sql/ColumnType.java b/modules/api/src/main/java/org/apache/ignite/sql/ColumnType.java
index 7ba731e..9a5bcda 100644
--- a/modules/api/src/main/java/org/apache/ignite/sql/ColumnType.java
+++ b/modules/api/src/main/java/org/apache/ignite/sql/ColumnType.java
@@ -114,6 +114,8 @@
     }
 
     ColumnType(int id, Class<?> clazz, boolean precisionDefined, boolean scaleDefined, boolean lengthDefined) {
+        assert !lengthDefined || (!precisionDefined && !scaleDefined);
+
         javaClass = clazz;
         this.precisionAllowed = precisionDefined;
         this.scaleAllowed = scaleDefined;
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 f9523f4..14e90a1 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
@@ -46,7 +46,7 @@
 import java.util.concurrent.ConcurrentSkipListMap;
 import java.util.concurrent.Flow.Publisher;
 import java.util.function.LongSupplier;
-import org.apache.ignite.internal.catalog.commands.AlterZoneSetDefaultCatalogCommand;
+import org.apache.ignite.internal.catalog.commands.AlterZoneSetDefaultCommand;
 import org.apache.ignite.internal.catalog.commands.CreateZoneCommand;
 import org.apache.ignite.internal.catalog.commands.StorageProfileParams;
 import org.apache.ignite.internal.catalog.descriptors.CatalogHashIndexDescriptor;
@@ -376,7 +376,7 @@
                                 List.of(StorageProfileParams.builder().storageProfile(CatalogService.DEFAULT_STORAGE_PROFILE).build())
                         )
                         .build(),
-                AlterZoneSetDefaultCatalogCommand.builder()
+                AlterZoneSetDefaultCommand.builder()
                         .zoneName(DEFAULT_ZONE_NAME)
                         .build()
         )).get(emptyCatalog);
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
index 5b5b401..08bebca 100644
--- 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
@@ -49,17 +49,24 @@
 
     protected final List<String> columns;
 
-    AbstractCreateIndexCommand(String schemaName, String indexName, String tableName, boolean unique, List<String> columns)
-            throws CatalogValidationException {
+    private final boolean ifNotExists;
+
+    AbstractCreateIndexCommand(String schemaName, String indexName, boolean ifNotExists, String tableName, boolean unique,
+            List<String> columns) throws CatalogValidationException {
         super(schemaName, indexName);
 
         validate(tableName, columns);
 
+        this.ifNotExists = ifNotExists;
         this.tableName = tableName;
         this.unique = unique;
         this.columns = copyOrNull(columns);
     }
 
+    public boolean ifNotExists() {
+        return ifNotExists;
+    }
+
     protected abstract CatalogIndexDescriptor createDescriptor(int indexId, int tableId, int creationCatalogVersion);
 
     @Override
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractCreateIndexCommandBuilder.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractCreateIndexCommandBuilder.java
index 35505cf..970ed63 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractCreateIndexCommandBuilder.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractCreateIndexCommandBuilder.java
@@ -21,6 +21,9 @@
 
 /** Builder that covers attributes which is common among all types of indexes. */
 interface AbstractCreateIndexCommandBuilder<T extends AbstractIndexCommandBuilder<T>> extends AbstractIndexCommandBuilder<T> {
+    /** Sets a flag indicating whether the {@code IF NOT EXISTS} was specified. */
+    T ifNotExists(boolean ifNotExists);
+
     /** A name of the table an index belongs to. Should not be null or blank. */
     T tableName(String tableName);
 
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractIndexCommand.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractIndexCommand.java
index ea629b2..c4e55df 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractIndexCommand.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractIndexCommand.java
@@ -40,6 +40,14 @@
         validate();
     }
 
+    public String schemaName() {
+        return schemaName;
+    }
+
+    public String indexName() {
+        return indexName;
+    }
+
     private void validate() {
         validateIdentifier(schemaName, "Name of the schema");
         validateIdentifier(indexName, "Name of the index");
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 5c35c0d..06af5ac 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
@@ -34,13 +34,20 @@
 
     protected final String tableName;
 
-    AbstractTableCommand(String schemaName, String tableName) throws CatalogValidationException {
+    protected final boolean ifTableExists;
+
+    AbstractTableCommand(String schemaName, String tableName, boolean ifTableExists) throws CatalogValidationException {
         this.schemaName = schemaName;
         this.tableName = tableName;
+        this.ifTableExists = ifTableExists;
 
         validate();
     }
 
+    public boolean ifTableExists() {
+        return ifTableExists;
+    }
+
     private void validate() {
         if (schemaName != null && CatalogUtils.isSystemSchema(schemaName)) {
             throw new CatalogValidationException(format("Operations with reserved schemas are not allowed, schema: {}", schemaName));
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractTableCommandBuilder.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractTableCommandBuilder.java
index 9de2e86..37b5450 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractTableCommandBuilder.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AbstractTableCommandBuilder.java
@@ -32,6 +32,9 @@
     /** A name of the table. Should not be null or blank. */
     T tableName(String tableName);
 
+    /** Sets a flag indicating whether the {@code IF EXISTS} was specified. */
+    T ifTableExists(boolean ifTableExists);
+
     /** Returns a command with specified parameters. */
     CatalogCommand build();
 }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterTableAddColumnCommand.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterTableAddColumnCommand.java
index e5e166a..dd21a5c 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterTableAddColumnCommand.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterTableAddColumnCommand.java
@@ -53,15 +53,17 @@
      *
      * @param tableName Name of the table to add new columns to. Should not be null or blank.
      * @param schemaName Name of the schema the table of interest belongs to. Should not be null or blank.
+     * @param ifTableExists Flag indicating whether the {@code IF EXISTS} was specified.
      * @param columns List of the columns to add to the table. There should be at least one column.
      * @throws CatalogValidationException if any of restrictions above is violated.
      */
     private AlterTableAddColumnCommand(
             String tableName,
             String schemaName,
+            boolean ifTableExists,
             List<ColumnParams> columns
     ) throws CatalogValidationException {
-        super(schemaName, tableName);
+        super(schemaName, tableName, ifTableExists);
 
         this.columns = copyOrNull(columns);
 
@@ -115,6 +117,8 @@
 
         private String tableName;
 
+        private boolean ifTableExists;
+
         @Override
         public AlterTableAddColumnCommandBuilder schemaName(String schemaName) {
             this.schemaName = schemaName;
@@ -130,6 +134,13 @@
         }
 
         @Override
+        public AlterTableAddColumnCommandBuilder ifTableExists(boolean ifTableExists) {
+            this.ifTableExists = ifTableExists;
+
+            return this;
+        }
+
+        @Override
         public AlterTableAddColumnCommandBuilder columns(List<ColumnParams> columns) {
             this.columns = columns;
 
@@ -141,6 +152,7 @@
             return new AlterTableAddColumnCommand(
                     tableName,
                     schemaName,
+                    ifTableExists,
                     columns
             );
         }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterTableAlterColumnCommand.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterTableAlterColumnCommand.java
index 6f7f3c4..392e6f3 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterTableAlterColumnCommand.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterTableAlterColumnCommand.java
@@ -65,6 +65,7 @@
     private AlterTableAlterColumnCommand(
             String tableName,
             String schemaName,
+            boolean ifTableExists,
             String columnName,
             @Nullable ColumnType type,
             @Nullable Integer precision,
@@ -73,7 +74,7 @@
             @Nullable Boolean nullable,
             @Nullable DeferredDefaultValue deferredDefault
     ) {
-        super(schemaName, tableName);
+        super(schemaName, tableName, ifTableExists);
 
         this.columnName = columnName;
         this.type = type;
@@ -159,6 +160,7 @@
     private static class Builder implements AlterTableAlterColumnCommandBuilder {
         private String tableName;
         private String schemaName;
+        private boolean ifTableExists;
         private String columnName;
         private @Nullable ColumnType type;
         private @Nullable Integer precision;
@@ -182,6 +184,13 @@
         }
 
         @Override
+        public AlterTableAlterColumnCommandBuilder ifTableExists(boolean ifTableExists) {
+            this.ifTableExists = ifTableExists;
+
+            return this;
+        }
+
+        @Override
         public Builder columnName(String columnName) {
             this.columnName = columnName;
 
@@ -234,6 +243,7 @@
             return new AlterTableAlterColumnCommand(
                     tableName,
                     schemaName,
+                    ifTableExists,
                     columnName,
                     type,
                     precision,
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterTableDropColumnCommand.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterTableDropColumnCommand.java
index e3ad9aa..9e6ec15 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterTableDropColumnCommand.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterTableDropColumnCommand.java
@@ -56,15 +56,17 @@
      *
      * @param tableName Name of the table to delete columns from. Should not be null or blank.
      * @param schemaName Name of the schema the table of interest belongs to. Should not be null or blank.
+     * @param ifTableExists Flag indicating whether the {@code IF EXISTS} was specified.
      * @param columns Set of the columns to delete. There should be at least one column.
      * @throws CatalogValidationException if any of restrictions above is violated.
      */
     private AlterTableDropColumnCommand(
             String tableName,
             String schemaName,
+            boolean ifTableExists,
             Set<String> columns
     ) throws CatalogValidationException {
-        super(schemaName, tableName);
+        super(schemaName, tableName, ifTableExists);
 
         // Set.copyOf() will throw NPE if any elements of the given set is null
         validate(columns);
@@ -146,6 +148,8 @@
 
         private String tableName;
 
+        private boolean ifTableExists;
+
         @Override
         public AlterTableDropColumnCommandBuilder schemaName(String schemaName) {
             this.schemaName = schemaName;
@@ -161,6 +165,13 @@
         }
 
         @Override
+        public AlterTableDropColumnCommandBuilder ifTableExists(boolean ifTableExists) {
+            this.ifTableExists = ifTableExists;
+
+            return this;
+        }
+
+        @Override
         public AlterTableDropColumnCommandBuilder columns(Set<String> columns) {
             this.columns = columns;
 
@@ -172,6 +183,7 @@
             return new AlterTableDropColumnCommand(
                     tableName,
                     schemaName,
+                    ifTableExists,
                     columns
             );
         }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterZoneCommand.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterZoneCommand.java
index 564fd85..3b5b7e4 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterZoneCommand.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterZoneCommand.java
@@ -44,6 +44,8 @@
         return new Builder();
     }
 
+    private final boolean ifExists;
+
     private final @Nullable Integer partitions;
 
     private final @Nullable Integer replicas;
@@ -62,6 +64,7 @@
      * Constructor.
      *
      * @param zoneName Name of the zone.
+     * @param ifExists Flag indicating whether the {@code IF EXISTS} was specified.
      * @param partitions Number of partitions.
      * @param replicas Number of replicas.
      * @param dataNodesAutoAdjust Timeout in seconds between node added or node left topology event itself and data nodes switch.
@@ -73,6 +76,7 @@
      */
     private AlterZoneCommand(
             String zoneName,
+            boolean ifExists,
             @Nullable Integer partitions,
             @Nullable Integer replicas,
             @Nullable Integer dataNodesAutoAdjust,
@@ -83,6 +87,7 @@
     ) throws CatalogValidationException {
         super(zoneName);
 
+        this.ifExists = ifExists;
         this.partitions = partitions;
         this.replicas = replicas;
         this.dataNodesAutoAdjust = dataNodesAutoAdjust;
@@ -94,6 +99,10 @@
         validate();
     }
 
+    public boolean ifExists() {
+        return ifExists;
+    }
+
     @Override
     public List<UpdateEntry> get(Catalog catalog) {
         CatalogZoneDescriptor zone = zoneOrThrow(catalog, zoneName);
@@ -156,6 +165,8 @@
     private static class Builder implements AlterZoneCommandBuilder {
         private String zoneName;
 
+        private boolean ifExists;
+
         private @Nullable Integer partitions;
 
         private @Nullable Integer replicas;
@@ -178,6 +189,13 @@
         }
 
         @Override
+        public AlterZoneCommandBuilder ifExists(boolean ifExists) {
+            this.ifExists = ifExists;
+
+            return this;
+        }
+
+        @Override
         public AlterZoneCommandBuilder partitions(Integer partitions) {
             this.partitions = partitions;
 
@@ -230,6 +248,7 @@
         public CatalogCommand build() {
             return new AlterZoneCommand(
                     zoneName,
+                    ifExists,
                     partitions,
                     replicas,
                     dataNodesAutoAdjust,
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterZoneCommandBuilder.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterZoneCommandBuilder.java
index ce81764..844be96 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterZoneCommandBuilder.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterZoneCommandBuilder.java
@@ -28,6 +28,8 @@
  * side effects on builder's state or any object created by the same builder.
  */
 public interface AlterZoneCommandBuilder extends AbstractZoneCommandBuilder<AlterZoneCommandBuilder> {
+    AlterZoneCommandBuilder ifExists(boolean ifExists);
+
     /**
      * Sets the number of partitions.
      *
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterZoneSetDefaultCatalogCommand.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterZoneSetDefaultCommand.java
similarity index 77%
rename from modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterZoneSetDefaultCatalogCommand.java
rename to modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterZoneSetDefaultCommand.java
index 81015cf..48818bc 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterZoneSetDefaultCatalogCommand.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterZoneSetDefaultCommand.java
@@ -31,20 +31,25 @@
 /**
  * A command that set specified zone as default.
  */
-public class AlterZoneSetDefaultCatalogCommand extends AbstractZoneCommand {
+public class AlterZoneSetDefaultCommand extends AbstractZoneCommand {
     /** Returns builder to create a command that set specified zone as default. */
     public static Builder builder() {
-        return new AlterZoneSetDefaultCatalogCommand.Builder();
+        return new AlterZoneSetDefaultCommand.Builder();
     }
 
+    private final boolean ifExists;
+
     /**
      * Constructor.
      *
      * @param zoneName Name of the zone.
+     * @param ifExists Flag indicating whether the {@code IF EXISTS} was specified.
      * @throws CatalogValidationException if any of restrictions above is violated.
      */
-    private AlterZoneSetDefaultCatalogCommand(String zoneName) throws CatalogValidationException {
+    private AlterZoneSetDefaultCommand(String zoneName, boolean ifExists) throws CatalogValidationException {
         super(zoneName);
+
+        this.ifExists = ifExists;
     }
 
     @Override
@@ -60,11 +65,16 @@
         return List.of(new SetDefaultZoneEntry(zone.id()));
     }
 
+    public boolean ifExists() {
+        return ifExists;
+    }
+
     /**
      * Builder of a command that set specified zone as default.
      */
     public static class Builder implements AbstractZoneCommandBuilder<Builder> {
         private String zoneName;
+        private boolean ifExists;
 
         @Override
         public Builder zoneName(String zoneName) {
@@ -73,9 +83,16 @@
             return this;
         }
 
+        /** Sets flag indicating whether the {@code IF EXISTS} was specified. */
+        public Builder ifExists(boolean ifExists) {
+            this.ifExists = ifExists;
+
+            return this;
+        }
+
         @Override
         public CatalogCommand build() {
-            return new AlterZoneSetDefaultCatalogCommand(zoneName);
+            return new AlterZoneSetDefaultCommand(zoneName, ifExists);
         }
     }
 }
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
index 45662b3..ece78e0 100644
--- 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
@@ -37,14 +37,15 @@
      *
      * @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 ifNotExists Flag indicating whether the {@code IF NOT EXISTS} was specified.
      * @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);
+    private CreateHashIndexCommand(String schemaName, String indexName, boolean ifNotExists, String tableName, boolean unique,
+            List<String> columns) throws CatalogValidationException {
+        super(schemaName, indexName, ifNotExists, tableName, unique, columns);
     }
 
     @Override
@@ -57,6 +58,7 @@
     private static class Builder implements CreateHashIndexCommandBuilder {
         private String schemaName;
         private String indexName;
+        private boolean ifNotExists;
         private String tableName;
         private List<String> columns;
         private boolean unique;
@@ -97,9 +99,16 @@
         }
 
         @Override
+        public CreateHashIndexCommandBuilder ifNotExists(boolean ifNotExists) {
+            this.ifNotExists = ifNotExists;
+
+            return this;
+        }
+
+        @Override
         public CatalogCommand build() {
             return new CreateHashIndexCommand(
-                    schemaName, indexName, tableName, unique, columns
+                    schemaName, indexName, ifNotExists, tableName, unique, columns
             );
         }
     }
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
index 34c9eb9..5bb0d3e 100644
--- 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
@@ -45,15 +45,16 @@
      *
      * @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 ifNotExists Flag indicating whether the {@code IF NOT EXISTS} was specified.
      * @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);
+    private CreateSortedIndexCommand(String schemaName, String indexName, boolean ifNotExists, String tableName, boolean unique,
+            List<String> columns, List<CatalogColumnCollation> collations) throws CatalogValidationException {
+        super(schemaName, indexName, ifNotExists, tableName, unique, columns);
 
         this.collations = copyOrNull(collations);
 
@@ -88,6 +89,7 @@
     private static class Builder implements CreateSortedIndexCommandBuilder {
         private String schemaName;
         private String indexName;
+        private boolean ifNotExists;
         private String tableName;
         private List<String> columns;
         private List<CatalogColumnCollation> collations;
@@ -129,6 +131,13 @@
         }
 
         @Override
+        public CreateSortedIndexCommandBuilder ifNotExists(boolean ifNotExists) {
+            this.ifNotExists = ifNotExists;
+
+            return this;
+        }
+
+        @Override
         public CreateSortedIndexCommandBuilder collations(List<CatalogColumnCollation> collations) {
             this.collations = collations;
 
@@ -138,7 +147,7 @@
         @Override
         public CatalogCommand build() {
             return new CreateSortedIndexCommand(
-                    schemaName, indexName, tableName, unique, columns, collations
+                    schemaName, indexName, ifNotExists, tableName, unique, columns, collations
             );
         }
     }
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 565cdce..8ee7784 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
@@ -76,6 +76,7 @@
      *
      * @param tableName Name of the table to create. Should not be null or blank.
      * @param schemaName Name of the schema to create table in. Should not be null or blank.
+     * @param ifNotExists Flag indicating whether the {@code IF NOT EXISTS} was specified.
      * @param primaryKey Primary key.
      * @param colocationColumns Name of the columns participating in distribution calculation.
      *      Should be subset of the primary key columns.
@@ -86,13 +87,14 @@
     private CreateTableCommand(
             String tableName,
             String schemaName,
+            boolean ifNotExists,
             TablePrimaryKey primaryKey,
             List<String> colocationColumns,
             List<ColumnParams> columns,
             @Nullable String zoneName,
             String storageProfile
     ) throws CatalogValidationException {
-        super(schemaName, tableName);
+        super(schemaName, tableName, ifNotExists);
 
         this.primaryKey = primaryKey;
         this.colocationColumns = copyOrNull(colocationColumns);
@@ -251,6 +253,8 @@
 
         private String tableName;
 
+        private boolean ifNotExists;
+
         private TablePrimaryKey primaryKey;
 
         private List<String> colocationColumns;
@@ -274,6 +278,13 @@
         }
 
         @Override
+        public CreateTableCommandBuilder ifTableExists(boolean ifNotExists) {
+            this.ifNotExists = ifNotExists;
+
+            return this;
+        }
+
+        @Override
         public CreateTableCommandBuilder columns(List<ColumnParams> columns) {
             this.columns = columns;
 
@@ -325,6 +336,7 @@
             return new CreateTableCommand(
                     tableName,
                     schemaName,
+                    ifNotExists,
                     primaryKey,
                     colocationColumns,
                     columns,
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateZoneCommand.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateZoneCommand.java
index 4086364..9b21663 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateZoneCommand.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateZoneCommand.java
@@ -51,6 +51,8 @@
         return new Builder();
     }
 
+    private final boolean ifNotExists;
+
     private final @Nullable Integer partitions;
 
     private final @Nullable Integer replicas;
@@ -69,6 +71,7 @@
      * Constructor.
      *
      * @param zoneName Name of the zone.
+     * @param ifNotExists Flag indicating whether the {@code IF NOT EXISTS} was specified.
      * @param partitions Number of partitions.
      * @param replicas Number of replicas.
      * @param dataNodesAutoAdjust Timeout in seconds between node added or node left topology event itself and data nodes switch.
@@ -80,6 +83,7 @@
      */
     private CreateZoneCommand(
             String zoneName,
+            boolean ifNotExists,
             @Nullable Integer partitions,
             @Nullable Integer replicas,
             @Nullable Integer dataNodesAutoAdjust,
@@ -89,7 +93,7 @@
             List<StorageProfileParams> storageProfileParams
     ) throws CatalogValidationException {
         super(zoneName);
-
+        this.ifNotExists = ifNotExists;
         this.partitions = partitions;
         this.replicas = replicas;
         this.dataNodesAutoAdjust = dataNodesAutoAdjust;
@@ -101,6 +105,10 @@
         validate();
     }
 
+    public boolean ifNotExists() {
+        return ifNotExists;
+    }
+
     @Override
     public List<UpdateEntry> get(Catalog catalog) {
         if (catalog.zone(zoneName) != null) {
@@ -158,6 +166,8 @@
     private static class Builder implements CreateZoneCommandBuilder {
         private String zoneName;
 
+        private boolean ifNotExists;
+
         private @Nullable Integer partitions;
 
         private @Nullable Integer replicas;
@@ -180,6 +190,13 @@
         }
 
         @Override
+        public CreateZoneCommandBuilder ifNotExists(boolean ifNotExists) {
+            this.ifNotExists = ifNotExists;
+
+            return this;
+        }
+
+        @Override
         public CreateZoneCommandBuilder partitions(Integer partitions) {
             this.partitions = partitions;
 
@@ -232,6 +249,7 @@
         public CatalogCommand build() {
             return new CreateZoneCommand(
                     zoneName,
+                    ifNotExists,
                     partitions,
                     replicas,
                     dataNodesAutoAdjust,
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateZoneCommandBuilder.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateZoneCommandBuilder.java
index 00ade11..8477058 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateZoneCommandBuilder.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateZoneCommandBuilder.java
@@ -28,6 +28,8 @@
  * side effects on builder's state or any object created by the same builder.
  */
 public interface CreateZoneCommandBuilder extends AbstractZoneCommandBuilder<CreateZoneCommandBuilder> {
+    CreateZoneCommandBuilder ifNotExists(boolean ifNotExists);
+
     /**
      * Sets the number of partitions.
      *
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
index ba20fb5..52cfbaf 100644
--- 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
@@ -50,15 +50,24 @@
         return new Builder();
     }
 
+    private final boolean ifExists;
+
     /**
      * 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.
+     * @param ifExists Flag indicating whether the {@code IF EXISTS} was specified.
      * @throws CatalogValidationException if any of restrictions above is violated.
      */
-    private DropIndexCommand(String schemaName, String indexName) throws CatalogValidationException {
+    private DropIndexCommand(String schemaName, String indexName, boolean ifExists) throws CatalogValidationException {
         super(schemaName, indexName);
+
+        this.ifExists = ifExists;
+    }
+
+    public boolean ifExists() {
+        return ifExists;
     }
 
     @Override
@@ -92,6 +101,8 @@
 
         private String indexName;
 
+        private boolean ifExists;
+
         @Override
         public Builder schemaName(String schemaName) {
             this.schemaName = schemaName;
@@ -107,10 +118,18 @@
         }
 
         @Override
+        public DropIndexCommandBuilder ifExists(boolean ifExists) {
+            this.ifExists = ifExists;
+
+            return this;
+        }
+
+        @Override
         public CatalogCommand build() {
             return new DropIndexCommand(
                     schemaName,
-                    indexName
+                    indexName,
+                    ifExists
             );
         }
     }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropIndexCommandBuilder.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropIndexCommandBuilder.java
index 51849af..d5c510f 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropIndexCommandBuilder.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropIndexCommandBuilder.java
@@ -25,4 +25,6 @@
  * side effects on builder's state or any object created by the same builder.
  */
 public interface DropIndexCommandBuilder extends AbstractIndexCommandBuilder<DropIndexCommandBuilder> {
+    /** Sets a flag indicating whether the {@code IF EXISTS} was specified. */
+    DropIndexCommandBuilder ifExists(boolean ifExists);
 }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropTableCommand.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropTableCommand.java
index b5365fd..ed2f713 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropTableCommand.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropTableCommand.java
@@ -41,8 +41,8 @@
         return new Builder();
     }
 
-    private DropTableCommand(String schemaName, String tableName) throws CatalogValidationException {
-        super(schemaName, tableName);
+    private DropTableCommand(String schemaName, String tableName, boolean ifExists) throws CatalogValidationException {
+        super(schemaName, tableName, ifExists);
     }
 
     @Override
@@ -75,6 +75,8 @@
 
         private String tableName;
 
+        private boolean ifExists;
+
         @Override
         public DropTableCommandBuilder schemaName(String schemaName) {
             this.schemaName = schemaName;
@@ -90,10 +92,18 @@
         }
 
         @Override
+        public DropTableCommandBuilder ifTableExists(boolean ifTableExists) {
+            this.ifExists = ifTableExists;
+
+            return this;
+        }
+
+        @Override
         public CatalogCommand build() {
             return new DropTableCommand(
                     schemaName,
-                    tableName
+                    tableName,
+                    ifExists
             );
         }
     }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropZoneCommand.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropZoneCommand.java
index 6ae466c..f1adb71 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropZoneCommand.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropZoneCommand.java
@@ -38,14 +38,19 @@
         return new Builder();
     }
 
+    private final boolean ifExists;
+
     /**
      * Constructor.
      *
      * @param zoneName Name of the zone.
+     * @param ifExists Flag indicating whether the {@code IF EXISTS} was specified.
      * @throws CatalogValidationException if any of restrictions above is violated.
      */
-    private DropZoneCommand(String zoneName) throws CatalogValidationException {
+    private DropZoneCommand(String zoneName, boolean ifExists) throws CatalogValidationException {
         super(zoneName);
+
+        this.ifExists = ifExists;
     }
 
     @Override
@@ -69,11 +74,16 @@
         return List.of(new DropZoneEntry(zone.id()));
     }
 
+    public boolean ifExists() {
+        return ifExists;
+    }
+
     /**
      * Implementation of {@link DropZoneCommandBuilder}.
      */
     private static class Builder implements DropZoneCommandBuilder {
         private String zoneName;
+        private boolean ifExists;
 
         @Override
         public DropZoneCommandBuilder zoneName(String zoneName) {
@@ -83,8 +93,15 @@
         }
 
         @Override
+        public DropZoneCommandBuilder ifExists(boolean ifExists) {
+            this.ifExists = ifExists;
+
+            return this;
+        }
+
+        @Override
         public CatalogCommand build() {
-            return new DropZoneCommand(zoneName);
+            return new DropZoneCommand(zoneName, ifExists);
         }
     }
 }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropZoneCommandBuilder.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropZoneCommandBuilder.java
index ad6e99e..1997bd6 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropZoneCommandBuilder.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropZoneCommandBuilder.java
@@ -25,5 +25,5 @@
  * side effects on builder's state or any object created by the same builder.
  */
 public interface DropZoneCommandBuilder extends AbstractZoneCommandBuilder<DropZoneCommandBuilder> {
-
+    DropZoneCommandBuilder ifExists(boolean ifExists);
 }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/RenameTableCommand.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/RenameTableCommand.java
index fdb9e1f..50efaa5 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/RenameTableCommand.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/RenameTableCommand.java
@@ -46,8 +46,9 @@
 
     private final String newTableName;
 
-    private RenameTableCommand(String schemaName, String tableName, String newTableName) throws CatalogValidationException {
-        super(schemaName, tableName);
+    private RenameTableCommand(String schemaName, String tableName, boolean ifExists, String newTableName)
+            throws CatalogValidationException {
+        super(schemaName, tableName, ifExists);
 
         validateIdentifier(newTableName, "New table name");
 
@@ -79,6 +80,8 @@
 
         private String tableName;
 
+        private boolean ifExists;
+
         private String newTableName;
 
         @Override
@@ -96,6 +99,13 @@
         }
 
         @Override
+        public RenameTableCommandBuilder ifTableExists(boolean ifExists) {
+            this.ifExists = ifExists;
+
+            return this;
+        }
+
+        @Override
         public RenameTableCommandBuilder newTableName(String newTableName) {
             this.newTableName = newTableName;
 
@@ -104,7 +114,7 @@
 
         @Override
         public CatalogCommand build() {
-            return new RenameTableCommand(schemaName, tableName, newTableName);
+            return new RenameTableCommand(schemaName, tableName, ifExists, newTableName);
         }
     }
 }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/RenameZoneCommand.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/RenameZoneCommand.java
index aa76139..89e0d89 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/RenameZoneCommand.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/RenameZoneCommand.java
@@ -39,23 +39,31 @@
         return new RenameZoneCommand.Builder();
     }
 
+    private final boolean ifExists;
+
     private final String newZoneName;
 
     /**
      * Constructor.
      *
      * @param zoneName Name of the zone.
+     * @param ifExists Flag indicating whether the {@code IF EXISTS} was specified.
      * @param newZoneName New name of the zone.
      * @throws CatalogValidationException if any of restrictions above is violated.
      */
-    private RenameZoneCommand(String zoneName, String newZoneName) throws CatalogValidationException {
+    private RenameZoneCommand(String zoneName, boolean ifExists, String newZoneName) throws CatalogValidationException {
         super(zoneName);
 
+        this.ifExists = ifExists;
         this.newZoneName = newZoneName;
 
         validate();
     }
 
+    public boolean ifExists() {
+        return ifExists;
+    }
+
     @Override
     public List<UpdateEntry> get(Catalog catalog) {
         CatalogZoneDescriptor zone = zoneOrThrow(catalog, zoneName);
@@ -88,6 +96,7 @@
      */
     private static class Builder implements RenameZoneCommandBuilder {
         private String zoneName;
+        private boolean ifExists;
         private String newZoneName;
 
         @Override
@@ -98,6 +107,13 @@
         }
 
         @Override
+        public RenameZoneCommandBuilder ifExists(boolean ifExists) {
+            this.ifExists = ifExists;
+
+            return this;
+        }
+
+        @Override
         public RenameZoneCommandBuilder newZoneName(String newZoneName) {
             this.newZoneName = newZoneName;
 
@@ -106,7 +122,7 @@
 
         @Override
         public CatalogCommand build() {
-            return new RenameZoneCommand(zoneName, newZoneName);
+            return new RenameZoneCommand(zoneName, ifExists, newZoneName);
         }
     }
 }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/RenameZoneCommandBuilder.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/RenameZoneCommandBuilder.java
index b3d3141..7ef5d1e 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/RenameZoneCommandBuilder.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/RenameZoneCommandBuilder.java
@@ -25,6 +25,8 @@
  * side effects on builder's state or any object created by the same builder.
  */
 public interface RenameZoneCommandBuilder extends AbstractZoneCommandBuilder<RenameZoneCommandBuilder> {
+    RenameZoneCommandBuilder ifExists(boolean ifExists);
+
     /** A new name of already existing zone. */
     RenameZoneCommandBuilder newZoneName(String newZoneName);
 }
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 072a6d6..e0a34fb 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
@@ -104,7 +104,7 @@
 import org.apache.ignite.internal.catalog.commands.AlterTableAlterColumnCommand;
 import org.apache.ignite.internal.catalog.commands.AlterTableAlterColumnCommandBuilder;
 import org.apache.ignite.internal.catalog.commands.AlterZoneCommand;
-import org.apache.ignite.internal.catalog.commands.AlterZoneSetDefaultCatalogCommand;
+import org.apache.ignite.internal.catalog.commands.AlterZoneSetDefaultCommand;
 import org.apache.ignite.internal.catalog.commands.CatalogUtils;
 import org.apache.ignite.internal.catalog.commands.ColumnParams;
 import org.apache.ignite.internal.catalog.commands.ColumnParams.Builder;
@@ -1141,7 +1141,7 @@
                         .zoneName("TEST_ZONE")
                         .storageProfilesParams(List.of(StorageProfileParams.builder().storageProfile(DEFAULT_STORAGE_PROFILE).build()))
                         .build(),
-                AlterZoneSetDefaultCatalogCommand.builder()
+                AlterZoneSetDefaultCommand.builder()
                         .zoneName("TEST_ZONE")
                         .build(),
                 simpleTable("T")
@@ -1386,7 +1386,7 @@
 
         // Set new zone as default.
         {
-            CatalogCommand setDefaultCmd = AlterZoneSetDefaultCatalogCommand.builder()
+            CatalogCommand setDefaultCmd = AlterZoneSetDefaultCommand.builder()
                     .zoneName(TEST_ZONE_NAME)
                     .build();
 
@@ -1415,7 +1415,7 @@
         {
             int lastVer =  manager.latestCatalogVersion();
 
-            CatalogCommand setDefaultCmd = AlterZoneSetDefaultCatalogCommand.builder()
+            CatalogCommand setDefaultCmd = AlterZoneSetDefaultCommand.builder()
                     .zoneName(TEST_ZONE_NAME)
                     .build();
 
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/QueryPrefetchCallback.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/QueryPrefetchCallback.java
index 7ef6a76..91d0a5a 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/QueryPrefetchCallback.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/QueryPrefetchCallback.java
@@ -17,9 +17,9 @@
 
 package org.apache.ignite.internal.sql.engine;
 
+import org.apache.ignite.internal.catalog.CatalogCommand;
 import org.apache.ignite.internal.sql.engine.exec.ddl.DdlCommandHandler;
 import org.apache.ignite.internal.sql.engine.exec.rel.AsyncRootNode;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.DdlCommand;
 import org.jetbrains.annotations.Nullable;
 
 /**
@@ -30,7 +30,7 @@
  *     <li>For {@code DML} queries, it is called after the cursor has finished prefetching
  *     the initial batch of rows (see {@link AsyncRootNode#startPrefetch}).</li>
  *     <li>For {@code DDL} queries, it is called after the corresponding DDL
- *     command has completed (see {@link DdlCommandHandler#handle(DdlCommand)}.</li>
+ *     command has completed (see {@link DdlCommandHandler#handle(CatalogCommand)}.</li>
  * </ol>
  *
  * <p>This callback is invoked asynchronously in the "{@code execution pool}".
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ddl/DdlCommandHandler.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ddl/DdlCommandHandler.java
index 105822d..e788d59 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ddl/DdlCommandHandler.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ddl/DdlCommandHandler.java
@@ -19,8 +19,6 @@
 
 import static java.util.concurrent.CompletableFuture.failedFuture;
 import static org.apache.ignite.internal.catalog.commands.CatalogUtils.clusterWideEnsuredActivationTsSafeForRoReads;
-import static org.apache.ignite.internal.util.CollectionUtils.nullOrEmpty;
-import static org.apache.ignite.internal.util.CompletableFutures.falseCompletedFuture;
 import static org.apache.ignite.internal.util.IgniteUtils.inBusyLock;
 import static org.apache.ignite.lang.ErrorGroups.Sql.STMT_VALIDATION_ERR;
 
@@ -30,6 +28,7 @@
 import java.util.function.BiFunction;
 import java.util.function.LongSupplier;
 import org.apache.ignite.internal.catalog.Catalog;
+import org.apache.ignite.internal.catalog.CatalogCommand;
 import org.apache.ignite.internal.catalog.CatalogManager;
 import org.apache.ignite.internal.catalog.DistributionZoneExistsValidationException;
 import org.apache.ignite.internal.catalog.DistributionZoneNotFoundValidationException;
@@ -37,6 +36,18 @@
 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.AbstractCreateIndexCommand;
+import org.apache.ignite.internal.catalog.commands.AlterTableAddColumnCommand;
+import org.apache.ignite.internal.catalog.commands.AlterTableAlterColumnCommand;
+import org.apache.ignite.internal.catalog.commands.AlterTableDropColumnCommand;
+import org.apache.ignite.internal.catalog.commands.AlterZoneCommand;
+import org.apache.ignite.internal.catalog.commands.AlterZoneSetDefaultCommand;
+import org.apache.ignite.internal.catalog.commands.CreateTableCommand;
+import org.apache.ignite.internal.catalog.commands.CreateZoneCommand;
+import org.apache.ignite.internal.catalog.commands.DropIndexCommand;
+import org.apache.ignite.internal.catalog.commands.DropTableCommand;
+import org.apache.ignite.internal.catalog.commands.DropZoneCommand;
+import org.apache.ignite.internal.catalog.commands.RenameZoneCommand;
 import org.apache.ignite.internal.catalog.descriptors.CatalogIndexDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.CatalogSchemaDescriptor;
 import org.apache.ignite.internal.catalog.events.CatalogEvent;
@@ -49,19 +60,6 @@
 import org.apache.ignite.internal.hlc.HybridTimestamp;
 import org.apache.ignite.internal.lang.NodeStoppingException;
 import org.apache.ignite.internal.sql.engine.exec.LifecycleAware;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.AlterColumnCommand;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.AlterTableAddCommand;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.AlterTableDropCommand;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.AlterZoneRenameCommand;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.AlterZoneSetCommand;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.AlterZoneSetDefaultCommand;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.CreateIndexCommand;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.CreateTableCommand;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.CreateZoneCommand;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.DdlCommand;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.DropIndexCommand;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.DropTableCommand;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.DropZoneCommand;
 import org.apache.ignite.internal.util.IgniteSpinBusyLock;
 import org.apache.ignite.sql.SqlException;
 
@@ -91,27 +89,27 @@
     }
 
     /** Handles ddl commands. */
-    public CompletableFuture<Boolean> handle(DdlCommand cmd) {
+    public CompletableFuture<Boolean> handle(CatalogCommand cmd) {
         if (cmd instanceof CreateTableCommand) {
             return handleCreateTable((CreateTableCommand) cmd);
         } else if (cmd instanceof DropTableCommand) {
             return handleDropTable((DropTableCommand) cmd);
-        } else if (cmd instanceof AlterTableAddCommand) {
-            return handleAlterAddColumn((AlterTableAddCommand) cmd);
-        } else if (cmd instanceof AlterTableDropCommand) {
-            return handleAlterDropColumn((AlterTableDropCommand) cmd);
-        } else if (cmd instanceof AlterColumnCommand) {
-            return handleAlterColumn((AlterColumnCommand) cmd);
-        } else if (cmd instanceof CreateIndexCommand) {
-            return handleCreateIndex((CreateIndexCommand) cmd);
+        } else if (cmd instanceof AlterTableAddColumnCommand) {
+            return handleAlterAddColumn((AlterTableAddColumnCommand) cmd);
+        } else if (cmd instanceof AlterTableDropColumnCommand) {
+            return handleAlterDropColumn((AlterTableDropColumnCommand) cmd);
+        } else if (cmd instanceof AlterTableAlterColumnCommand) {
+            return handleAlterColumn((AlterTableAlterColumnCommand) cmd);
+        } else if (cmd instanceof AbstractCreateIndexCommand) {
+            return handleCreateIndex((AbstractCreateIndexCommand) cmd);
         } else if (cmd instanceof DropIndexCommand) {
             return handleDropIndex((DropIndexCommand) cmd);
         } else if (cmd instanceof CreateZoneCommand) {
             return handleCreateZone((CreateZoneCommand) cmd);
-        } else if (cmd instanceof AlterZoneRenameCommand) {
-            return handleRenameZone((AlterZoneRenameCommand) cmd);
-        } else if (cmd instanceof AlterZoneSetCommand) {
-            return handleAlterZone((AlterZoneSetCommand) cmd);
+        } else if (cmd instanceof RenameZoneCommand) {
+            return handleRenameZone((RenameZoneCommand) cmd);
+        } else if (cmd instanceof AlterZoneCommand) {
+            return handleAlterZone((AlterZoneCommand) cmd);
         } else if (cmd instanceof AlterZoneSetDefaultCommand) {
             return handleAlterZoneSetDefault((AlterZoneSetDefaultCommand) cmd);
         } else if (cmd instanceof DropZoneCommand) {
@@ -125,69 +123,61 @@
 
     /** Handles create distribution zone command. */
     private CompletableFuture<Boolean> handleCreateZone(CreateZoneCommand cmd) {
-        return catalogManager.execute(DdlToCatalogCommandConverter.convert(cmd))
+        return catalogManager.execute(cmd)
                 .handle(handleModificationResult(cmd.ifNotExists(), DistributionZoneExistsValidationException.class));
     }
 
     /** Handles rename zone command. */
-    private CompletableFuture<Boolean> handleRenameZone(AlterZoneRenameCommand cmd) {
-        return catalogManager.execute(DdlToCatalogCommandConverter.convert(cmd))
+    private CompletableFuture<Boolean> handleRenameZone(RenameZoneCommand cmd) {
+        return catalogManager.execute(cmd)
                 .handle(handleModificationResult(cmd.ifExists(), DistributionZoneNotFoundValidationException.class));
     }
 
     /** Handles alter zone command. */
-    private CompletableFuture<Boolean> handleAlterZone(AlterZoneSetCommand cmd) {
-        return catalogManager.execute(DdlToCatalogCommandConverter.convert(cmd))
+    private CompletableFuture<Boolean> handleAlterZone(AlterZoneCommand cmd) {
+        return catalogManager.execute(cmd)
                 .handle(handleModificationResult(cmd.ifExists(), DistributionZoneNotFoundValidationException.class));
     }
 
     /** Handles alter zone set default command. */
     private CompletableFuture<Boolean> handleAlterZoneSetDefault(AlterZoneSetDefaultCommand cmd) {
-        return catalogManager.execute(DdlToCatalogCommandConverter.convert(cmd))
+        return catalogManager.execute(cmd)
                 .handle(handleModificationResult(cmd.ifExists(), DistributionZoneNotFoundValidationException.class));
     }
 
     /** Handles drop distribution zone command. */
     private CompletableFuture<Boolean> handleDropZone(DropZoneCommand cmd) {
-        return catalogManager.execute(DdlToCatalogCommandConverter.convert(cmd))
+        return catalogManager.execute(cmd)
                 .handle(handleModificationResult(cmd.ifExists(), DistributionZoneNotFoundValidationException.class));
     }
 
     /** Handles create table command. */
     private CompletableFuture<Boolean> handleCreateTable(CreateTableCommand cmd) {
-        return catalogManager.execute(DdlToCatalogCommandConverter.convert(cmd))
+        return catalogManager.execute(cmd)
                 .handle(handleModificationResult(cmd.ifTableExists(), TableExistsValidationException.class));
     }
 
     /** Handles drop table command. */
     private CompletableFuture<Boolean> handleDropTable(DropTableCommand cmd) {
-        return catalogManager.execute(DdlToCatalogCommandConverter.convert(cmd))
+        return catalogManager.execute(cmd)
                 .handle(handleModificationResult(cmd.ifTableExists(), TableNotFoundValidationException.class));
     }
 
     /** Handles add column command. */
-    private CompletableFuture<Boolean> handleAlterAddColumn(AlterTableAddCommand cmd) {
-        if (nullOrEmpty(cmd.columns())) {
-            return falseCompletedFuture();
-        }
-
-        return catalogManager.execute(DdlToCatalogCommandConverter.convert(cmd))
+    private CompletableFuture<Boolean> handleAlterAddColumn(AlterTableAddColumnCommand cmd) {
+        return catalogManager.execute(cmd)
                 .handle(handleModificationResult(cmd.ifTableExists(), TableNotFoundValidationException.class));
     }
 
     /** Handles drop column command. */
-    private CompletableFuture<Boolean> handleAlterDropColumn(AlterTableDropCommand cmd) {
-        if (nullOrEmpty(cmd.columns())) {
-            return falseCompletedFuture();
-        }
-
-        return catalogManager.execute(DdlToCatalogCommandConverter.convert(cmd))
+    private CompletableFuture<Boolean> handleAlterDropColumn(AlterTableDropColumnCommand cmd) {
+        return catalogManager.execute(cmd)
                 .handle(handleModificationResult(cmd.ifTableExists(), TableNotFoundValidationException.class));
     }
 
     /** Handles drop column command. */
-    private CompletableFuture<Boolean> handleAlterColumn(AlterColumnCommand cmd) {
-        return catalogManager.execute(DdlToCatalogCommandConverter.convert(cmd))
+    private CompletableFuture<Boolean> handleAlterColumn(AlterTableAlterColumnCommand cmd) {
+        return catalogManager.execute(cmd)
                 .handle(handleModificationResult(cmd.ifTableExists(), TableNotFoundValidationException.class));
     }
 
@@ -208,13 +198,13 @@
     }
 
     /** Handles create index command. */
-    private CompletableFuture<Boolean> handleCreateIndex(CreateIndexCommand cmd) {
-        return catalogManager.execute(DdlToCatalogCommandConverter.convert(cmd))
+    private CompletableFuture<Boolean> handleCreateIndex(AbstractCreateIndexCommand cmd) {
+        return catalogManager.execute(cmd)
                 .thenCompose(catalogVersion -> inBusyLock(busyLock, () -> waitTillIndexBecomesAvailableOrRemoved(cmd, catalogVersion)))
                 .handle(handleModificationResult(cmd.ifNotExists(), IndexExistsValidationException.class));
     }
 
-    private CompletionStage<Void> waitTillIndexBecomesAvailableOrRemoved(CreateIndexCommand cmd, Integer creationCatalogVersion) {
+    private CompletionStage<Void> waitTillIndexBecomesAvailableOrRemoved(AbstractCreateIndexCommand cmd, Integer creationCatalogVersion) {
         CompletableFuture<Void> future = inFlightFutures.registerFuture(new CompletableFuture<>());
 
         Catalog catalog = catalogManager.catalog(creationCatalogVersion);
@@ -282,8 +272,8 @@
 
     /** Handles drop index command. */
     private CompletableFuture<Boolean> handleDropIndex(DropIndexCommand cmd) {
-        return catalogManager.execute(DdlToCatalogCommandConverter.convert(cmd))
-                .handle(handleModificationResult(cmd.ifNotExists(), IndexNotFoundValidationException.class));
+        return catalogManager.execute(cmd)
+                .handle(handleModificationResult(cmd.ifExists(), IndexNotFoundValidationException.class));
     }
 
     @Override
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
deleted file mode 100644
index 0efd0a8..0000000
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ddl/DdlToCatalogCommandConverter.java
+++ /dev/null
@@ -1,282 +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.sql.engine.exec.ddl;
-
-import static org.apache.ignite.internal.distributionzones.DistributionZonesUtil.parseStorageProfiles;
-
-import java.util.List;
-import java.util.function.Function;
-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.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.AlterZoneCommand;
-import org.apache.ignite.internal.catalog.commands.AlterZoneSetDefaultCatalogCommand;
-import org.apache.ignite.internal.catalog.commands.ColumnParams;
-import org.apache.ignite.internal.catalog.commands.CreateHashIndexCommand;
-import org.apache.ignite.internal.catalog.commands.CreateSortedIndexCommand;
-import org.apache.ignite.internal.catalog.commands.DefaultValue;
-import org.apache.ignite.internal.catalog.commands.RenameZoneCommand;
-import org.apache.ignite.internal.catalog.commands.TableHashPrimaryKey;
-import org.apache.ignite.internal.catalog.commands.TablePrimaryKey;
-import org.apache.ignite.internal.catalog.commands.TableSortedPrimaryKey;
-import org.apache.ignite.internal.catalog.descriptors.CatalogColumnCollation;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.AlterColumnCommand;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.AlterTableAddCommand;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.AlterTableDropCommand;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.AlterZoneRenameCommand;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.AlterZoneSetCommand;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.AlterZoneSetDefaultCommand;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.ColumnDefinition;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.CreateIndexCommand;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.CreateTableCommand;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.CreateTableCommand.PrimaryKeyIndexType;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.CreateZoneCommand;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.DefaultValueDefinition;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.DropIndexCommand;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.DropTableCommand;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.DropZoneCommand;
-import org.apache.ignite.internal.sql.engine.schema.IgniteIndex;
-import org.apache.ignite.internal.sql.engine.util.TypeUtils;
-import org.apache.ignite.sql.ColumnType;
-
-/**
- * Converter for DDL command classes to Catalog command params classes.
- */
-class DdlToCatalogCommandConverter {
-    static CatalogCommand convert(CreateTableCommand cmd) {
-        List<ColumnParams> columns = cmd.columns().stream().map(DdlToCatalogCommandConverter::convert).collect(Collectors.toList());
-
-        PrimaryKeyIndexType pkIndexType = cmd.primaryIndexType();
-        TablePrimaryKey primaryKey;
-
-        switch (pkIndexType) {
-            case SORTED:
-                List<CatalogColumnCollation> collations = cmd.primaryKeyCollations().stream()
-                        .map(DdlToCatalogCommandConverter::convert)
-                        .collect(Collectors.toList());
-
-                primaryKey = TableSortedPrimaryKey.builder()
-                        .columns(cmd.primaryKeyColumns())
-                        .collations(collations)
-                        .build();
-                break;
-            case HASH:
-                primaryKey = TableHashPrimaryKey.builder()
-                        .columns(cmd.primaryKeyColumns())
-                        .build();
-                break;
-            default:
-                throw new IllegalArgumentException("Unexpected primary key index type: " + pkIndexType);
-        }
-
-        return org.apache.ignite.internal.catalog.commands.CreateTableCommand.builder()
-                .schemaName(cmd.schemaName())
-                .tableName(cmd.tableName())
-
-                .columns(columns)
-                .primaryKey(primaryKey)
-                .colocationColumns(cmd.colocationColumns())
-
-                .zone(cmd.zone())
-                .storageProfile(cmd.storageProfile())
-
-                .build();
-    }
-
-    static CatalogCommand convert(DropTableCommand cmd) {
-        return org.apache.ignite.internal.catalog.commands.DropTableCommand.builder()
-                .schemaName(cmd.schemaName())
-                .tableName(cmd.tableName())
-                .build();
-    }
-
-    static CatalogCommand convert(CreateZoneCommand cmd) {
-        return org.apache.ignite.internal.catalog.commands.CreateZoneCommand.builder()
-                .zoneName(cmd.zoneName())
-                .partitions(cmd.partitions())
-                .replicas(cmd.replicas())
-                .filter(cmd.nodeFilter())
-                .dataNodesAutoAdjust(cmd.dataNodesAutoAdjust())
-                .dataNodesAutoAdjustScaleUp(cmd.dataNodesAutoAdjustScaleUp())
-                .dataNodesAutoAdjustScaleDown(cmd.dataNodesAutoAdjustScaleDown())
-                .storageProfilesParams(parseStorageProfiles(cmd.storageProfiles()))
-                .build();
-    }
-
-    static CatalogCommand convert(DropZoneCommand cmd) {
-        return org.apache.ignite.internal.catalog.commands.DropZoneCommand.builder()
-                .zoneName(cmd.zoneName())
-                .build();
-    }
-
-    static CatalogCommand convert(AlterZoneRenameCommand cmd) {
-        return RenameZoneCommand.builder()
-                .zoneName(cmd.zoneName())
-                .newZoneName(cmd.newZoneName())
-                .build();
-    }
-
-    static CatalogCommand convert(AlterZoneSetCommand cmd) {
-        return AlterZoneCommand.builder()
-                .zoneName(cmd.zoneName())
-                .partitions(cmd.partitions())
-                .replicas(cmd.replicas())
-                .filter(cmd.nodeFilter())
-                .dataNodesAutoAdjust(cmd.dataNodesAutoAdjust())
-                .dataNodesAutoAdjustScaleUp(cmd.dataNodesAutoAdjustScaleUp())
-                .dataNodesAutoAdjustScaleDown(cmd.dataNodesAutoAdjustScaleDown())
-                .build();
-    }
-
-    static CatalogCommand convert(AlterZoneSetDefaultCommand cmd) {
-        return AlterZoneSetDefaultCatalogCommand.builder()
-                .zoneName(cmd.zoneName())
-                .build();
-    }
-
-    static CatalogCommand convert(AlterColumnCommand cmd) {
-        AlterTableAlterColumnCommandBuilder builder = AlterTableAlterColumnCommand.builder()
-                .schemaName(cmd.schemaName())
-                .tableName(cmd.tableName())
-                .columnName(cmd.columnName());
-
-        Boolean notNull = cmd.notNull();
-        if (notNull != null) {
-            builder.nullable(!notNull);
-        }
-
-        Function<ColumnType, DefaultValue> defaultValueResolver = cmd.defaultValueResolver();
-        if (defaultValueResolver != null) {
-            builder.deferredDefaultValue(defaultValueResolver::apply);
-        }
-
-        RelDataType type = cmd.type();
-
-        if (type != null) {
-            builder.type(TypeUtils.columnType(type));
-
-            if (type.getPrecision() != RelDataType.PRECISION_NOT_SPECIFIED) {
-                if (type.getSqlTypeName() == SqlTypeName.VARCHAR || type.getSqlTypeName() == SqlTypeName.VARBINARY) {
-                    builder.length(type.getPrecision());
-                } else {
-                    builder.precision(type.getPrecision());
-                }
-            }
-
-            if (type.getScale() != RelDataType.SCALE_NOT_SPECIFIED) {
-                builder.scale(type.getScale());
-            }
-        }
-
-        return builder.build();
-    }
-
-    static CatalogCommand convert(AlterTableAddCommand cmd) {
-        List<ColumnParams> columns = cmd.columns().stream().map(DdlToCatalogCommandConverter::convert).collect(Collectors.toList());
-
-        return AlterTableAddColumnCommand.builder()
-                .schemaName(cmd.schemaName())
-                .tableName(cmd.tableName())
-
-                .columns(columns)
-
-                .build();
-    }
-
-    static CatalogCommand convert(AlterTableDropCommand cmd) {
-        return AlterTableDropColumnCommand.builder()
-                .schemaName(cmd.schemaName())
-                .tableName(cmd.tableName())
-
-                .columns(cmd.columns())
-
-                .build();
-    }
-
-
-    static CatalogCommand convert(CreateIndexCommand cmd) {
-        switch (cmd.type()) {
-            case HASH:
-                return CreateHashIndexCommand.builder()
-                        .schemaName(cmd.schemaName())
-                        .indexName(cmd.indexName())
-
-                        .tableName(cmd.tableName())
-                        .columns(cmd.columns())
-
-                        .build();
-            case SORTED:
-                List<CatalogColumnCollation> collations = cmd.collations().stream()
-                        .map(DdlToCatalogCommandConverter::convert)
-                        .collect(Collectors.toList());
-
-                return CreateSortedIndexCommand.builder()
-                        .schemaName(cmd.schemaName())
-                        .indexName(cmd.indexName())
-
-                        .tableName(cmd.tableName())
-                        .columns(cmd.columns())
-                        .collations(collations)
-
-                        .build();
-            default:
-                throw new IllegalArgumentException("Unsupported index type: " + cmd.type());
-        }
-    }
-
-    static CatalogCommand convert(DropIndexCommand cmd) {
-        return org.apache.ignite.internal.catalog.commands.DropIndexCommand.builder()
-                .schemaName(cmd.schemaName())
-                .indexName(cmd.indexName())
-                .build();
-    }
-
-    private static ColumnParams convert(ColumnDefinition def) {
-        return ColumnParams.builder()
-                .name(def.name())
-                .type(TypeUtils.columnType(def.type()))
-                .nullable(def.nullable())
-                .precision(def.precision())
-                .scale(def.scale())
-                .length(def.length())
-                .defaultValue(convert(def.defaultValueDefinition()))
-                .build();
-    }
-
-    private static DefaultValue convert(DefaultValueDefinition def) {
-        switch (def.type()) {
-            case CONSTANT:
-                return DefaultValue.constant(((DefaultValueDefinition.ConstantValue) def).value());
-
-            case FUNCTION_CALL:
-                return DefaultValue.functionCall(((DefaultValueDefinition.FunctionCall) def).functionName());
-
-            default:
-                throw new IllegalArgumentException("Default value definition: " + def.type());
-        }
-    }
-
-    private static CatalogColumnCollation convert(IgniteIndex.Collation collation) {
-        return CatalogColumnCollation.get(collation.asc, collation.nullsFirst);
-    }
-}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/DdlPlan.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/DdlPlan.java
index 87d845f..85ebc6b 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/DdlPlan.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/DdlPlan.java
@@ -19,10 +19,10 @@
 
 import java.util.Collections;
 import java.util.List;
+import org.apache.ignite.internal.catalog.CatalogCommand;
 import org.apache.ignite.internal.sql.api.ColumnMetadataImpl;
 import org.apache.ignite.internal.sql.api.ResultSetMetadataImpl;
 import org.apache.ignite.internal.sql.engine.SqlQueryType;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.DdlCommand;
 import org.apache.ignite.sql.ColumnMetadata;
 import org.apache.ignite.sql.ColumnType;
 import org.apache.ignite.sql.ResultSetMetadata;
@@ -40,14 +40,14 @@
     private static final ParameterMetadata EMPTY_PARAMETERS = new ParameterMetadata(Collections.emptyList());
 
     private final PlanId id;
-    private final DdlCommand cmd;
+    private final CatalogCommand cmd;
 
-    DdlPlan(PlanId id, DdlCommand cmd) {
+    DdlPlan(PlanId id, CatalogCommand cmd) {
         this.id = id;
         this.cmd = cmd;
     }
 
-    public DdlCommand command() {
+    public CatalogCommand command() {
         return cmd;
     }
 
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AbstractTableDdlCommand.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AbstractTableDdlCommand.java
deleted file mode 100644
index 351260c..0000000
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AbstractTableDdlCommand.java
+++ /dev/null
@@ -1,64 +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.sql.engine.prepare.ddl;
-
-/**
- * Abstract table ddl command.
- */
-public class AbstractTableDdlCommand implements DdlCommand {
-    /** Table name. */
-    private String tblName;
-
-    /** Quietly ignore this command if table is not exists. */
-    protected boolean ifTableExists;
-
-    /** Schema name where this new table will be created. */
-    private String schemaName;
-
-    public String tableName() {
-        return tblName;
-    }
-
-    public void tableName(String tblName) {
-        this.tblName = tblName;
-    }
-
-    public String schemaName() {
-        return schemaName;
-    }
-
-    public void schemaName(String schemaName) {
-        this.schemaName = schemaName;
-    }
-
-    /**
-     * Quietly ignore if table exists flag.
-     */
-    public boolean ifTableExists() {
-        return ifTableExists;
-    }
-
-    /**
-     * Set quietly ignore flag.
-     *
-     * @param ifTableNotExists Flag.
-     */
-    public void ifTableExists(boolean ifTableNotExists) {
-        this.ifTableExists = ifTableNotExists;
-    }
-}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AbstractZoneDdlCommand.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AbstractZoneDdlCommand.java
deleted file mode 100644
index 2be4e10..0000000
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AbstractZoneDdlCommand.java
+++ /dev/null
@@ -1,45 +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.sql.engine.prepare.ddl;
-
-/**
- * Abstract zone ddl command.
- */
-public class AbstractZoneDdlCommand implements DdlCommand {
-    /** Table zone. */
-    private String zoneName;
-
-    /** Schema name where this new zone will be created. */
-    private String schemaName;
-
-    public String zoneName() {
-        return zoneName;
-    }
-
-    public void zoneName(String zoneName) {
-        this.zoneName = zoneName;
-    }
-
-    public String schemaName() {
-        return schemaName;
-    }
-
-    public void schemaName(String schemaName) {
-        this.schemaName = schemaName;
-    }
-}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AlterColumnCommand.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AlterColumnCommand.java
deleted file mode 100644
index d1fc39f..0000000
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AlterColumnCommand.java
+++ /dev/null
@@ -1,69 +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.sql.engine.prepare.ddl;
-
-import java.util.function.Function;
-import org.apache.calcite.rel.type.RelDataType;
-import org.apache.ignite.internal.catalog.commands.DefaultValue;
-import org.apache.ignite.sql.ColumnType;
-import org.jetbrains.annotations.Nullable;
-
-/**
- * ALTER TABLE ... ALTER COLUMN statement.
- */
-public class AlterColumnCommand extends AbstractTableDdlCommand {
-    private String columnName;
-
-    private RelDataType type;
-
-    private Boolean notNull;
-
-    private Function<ColumnType, DefaultValue> resolveDfltFunc;
-
-    public String columnName() {
-        return columnName;
-    }
-
-    void columnName(String name) {
-        columnName = name;
-    }
-
-    void type(RelDataType type) {
-        this.type = type;
-    }
-
-    @Nullable public RelDataType type() {
-        return type;
-    }
-
-    void notNull(boolean notNull) {
-        this.notNull = notNull;
-    }
-
-    @Nullable public Boolean notNull() {
-        return notNull;
-    }
-
-    void defaultValueResolver(Function<ColumnType, DefaultValue> resolveDfltFunc) {
-        this.resolveDfltFunc = resolveDfltFunc;
-    }
-
-    @Nullable public Function<ColumnType, DefaultValue> defaultValueResolver() {
-        return resolveDfltFunc;
-    }
-}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AlterTableAddCommand.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AlterTableAddCommand.java
deleted file mode 100644
index 0ed180e..0000000
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AlterTableAddCommand.java
+++ /dev/null
@@ -1,37 +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.sql.engine.prepare.ddl;
-
-import java.util.List;
-
-/**
- * ALTER TABLE ... ADD COLUMN statement.
- */
-@SuppressWarnings("AssignmentOrReturnOfFieldWithMutableType")
-public class AlterTableAddCommand extends AbstractTableDdlCommand {
-    /** Columns. */
-    private List<ColumnDefinition> cols;
-
-    public List<ColumnDefinition> columns() {
-        return cols;
-    }
-
-    public void columns(List<ColumnDefinition> cols) {
-        this.cols = cols;
-    }
-}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AlterTableDropCommand.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AlterTableDropCommand.java
deleted file mode 100644
index 6721b05..0000000
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AlterTableDropCommand.java
+++ /dev/null
@@ -1,42 +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.sql.engine.prepare.ddl;
-
-import java.util.Set;
-
-/**
- * ALTER TABLE ... DROP COLUMN statement.
- */
-@SuppressWarnings("AssignmentOrReturnOfFieldWithMutableType")
-public class AlterTableDropCommand extends AbstractTableDdlCommand {
-    /** Columns. */
-    private Set<String> cols;
-
-    public Set<String> columns() {
-        return cols;
-    }
-
-    /**
-     * Columns to drop.
-     *
-     * @param cols Columns.
-     */
-    public void columns(Set<String> cols) {
-        this.cols = cols;
-    }
-}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AlterZoneRenameCommand.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AlterZoneRenameCommand.java
deleted file mode 100644
index 33cd03c..0000000
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AlterZoneRenameCommand.java
+++ /dev/null
@@ -1,46 +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.sql.engine.prepare.ddl;
-
-/**
- * ALTER ZONE ... RENAME statement.
- */
-public class AlterZoneRenameCommand extends AbstractZoneDdlCommand {
-
-    /** Quietly ignore this command if zone does not exists. */
-    private boolean ifExists;
-
-    /** New zone name. */
-    private String newZoneName;
-
-    public boolean ifExists() {
-        return ifExists;
-    }
-
-    public void ifExists(boolean ifExists) {
-        this.ifExists = ifExists;
-    }
-
-    public String newZoneName() {
-        return newZoneName;
-    }
-
-    public void newZoneName(String newZoneName) {
-        this.newZoneName = newZoneName;
-    }
-}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AlterZoneSetCommand.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AlterZoneSetCommand.java
deleted file mode 100644
index 9c26def..0000000
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AlterZoneSetCommand.java
+++ /dev/null
@@ -1,103 +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.sql.engine.prepare.ddl;
-
-import org.jetbrains.annotations.Nullable;
-
-/**
- * ALTER ZONE ... SET statement.
- */
-public class AlterZoneSetCommand extends AbstractZoneDdlCommand {
-
-    /** Quietly ignore this command if zone does not exists. */
-    private boolean ifExists;
-
-    /** Number of partitions. */
-    private Integer partitions;
-
-    /** Replicas number. */
-    private Integer replicas;
-
-    /** Data nodes filter expression. */
-    private String nodeFiler;
-
-    /** Data nodes auto adjust timeout. */
-    private Integer dataNodesAutoAdjust;
-
-    /** Data nodes auto adjust scale up timeout. */
-    private Integer dataNodesAutoAdjustScaleUp;
-
-    /** Data nodes auto adjust scale down timeout. */
-    private Integer dataNodesAutoAdjustScaleDown;
-
-    public boolean ifExists() {
-        return ifExists;
-    }
-
-    public void ifExists(boolean ifExists) {
-        this.ifExists = ifExists;
-    }
-
-    @Nullable public Integer partitions() {
-        return partitions;
-    }
-
-    public void partitions(Integer partitions) {
-        this.partitions = partitions;
-    }
-
-    @Nullable public Integer replicas() {
-        return replicas;
-    }
-
-    public void replicas(Integer replicas) {
-        this.replicas = replicas;
-    }
-
-    @Nullable public String nodeFilter() {
-        return nodeFiler;
-    }
-
-    public void nodeFilter(String nodeFiler) {
-        this.nodeFiler = nodeFiler;
-    }
-
-    @Nullable public Integer dataNodesAutoAdjust() {
-        return dataNodesAutoAdjust;
-    }
-
-    public void dataNodesAutoAdjust(Integer dataNodesAutoAdjust) {
-        this.dataNodesAutoAdjust = dataNodesAutoAdjust;
-    }
-
-    @Nullable public Integer dataNodesAutoAdjustScaleUp() {
-        return dataNodesAutoAdjustScaleUp;
-    }
-
-    public void dataNodesAutoAdjustScaleUp(Integer dataNodesAutoAdjustScaleUp) {
-        this.dataNodesAutoAdjustScaleUp = dataNodesAutoAdjustScaleUp;
-    }
-
-    @Nullable public Integer dataNodesAutoAdjustScaleDown() {
-        return dataNodesAutoAdjustScaleDown;
-    }
-
-    public void dataNodesAutoAdjustScaleDown(Integer dataNodesAutoAdjustScaleDown) {
-        this.dataNodesAutoAdjustScaleDown = dataNodesAutoAdjustScaleDown;
-    }
-}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AlterZoneSetDefaultCommand.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AlterZoneSetDefaultCommand.java
deleted file mode 100644
index f5111e8..0000000
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AlterZoneSetDefaultCommand.java
+++ /dev/null
@@ -1,34 +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.sql.engine.prepare.ddl;
-
-/**
- * ALTER ZONE ... SET DEFAULT statement.
- */
-public class AlterZoneSetDefaultCommand extends AbstractZoneDdlCommand {
-    /** Quietly ignore this command if zone does not exists. */
-    private boolean ifExists;
-
-    public boolean ifExists() {
-        return ifExists;
-    }
-
-    public void ifExists(boolean ifExists) {
-        this.ifExists = ifExists;
-    }
-}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/ColumnDefinition.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/ColumnDefinition.java
deleted file mode 100644
index a9e8bb9..0000000
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/ColumnDefinition.java
+++ /dev/null
@@ -1,112 +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.sql.engine.prepare.ddl;
-
-import static org.apache.calcite.rel.type.RelDataType.PRECISION_NOT_SPECIFIED;
-import static org.apache.calcite.rel.type.RelDataType.SCALE_NOT_SPECIFIED;
-import static org.apache.ignite.internal.catalog.commands.CatalogUtils.DEFAULT_LENGTH;
-import static org.apache.ignite.internal.catalog.commands.CatalogUtils.defaultLength;
-import static org.apache.ignite.internal.sql.engine.util.TypeUtils.columnType;
-
-import java.util.Objects;
-import org.apache.calcite.rel.type.RelDataType;
-import org.apache.ignite.sql.ColumnType;
-import org.jetbrains.annotations.Nullable;
-
-/** Defines a particular column within table. */
-public class ColumnDefinition {
-    private final String name;
-
-    private final RelDataType type;
-
-    private final DefaultValueDefinition defaultValueDefinition;
-
-    private ColumnType colType;
-
-    /** Creates a column definition. */
-    public ColumnDefinition(String name, RelDataType type, DefaultValueDefinition defaultValueDefinition) {
-        this.name = Objects.requireNonNull(name, "name");
-        this.type = Objects.requireNonNull(type, "type");
-        this.defaultValueDefinition = Objects.requireNonNull(defaultValueDefinition, "defaultValueDefinition");
-    }
-
-    /**
-     * Get column's name.
-     */
-    public String name() {
-        return name;
-    }
-
-    /**
-     * Get column's type.
-     */
-    public RelDataType type() {
-        return type;
-    }
-
-    /**
-     * Returns default value definition.
-     *
-     * @param <T> Desired subtype of the definition.
-     * @return Default value definition.
-     */
-    @SuppressWarnings("unchecked")
-    public <T extends DefaultValueDefinition> T defaultValueDefinition() {
-        return (T) defaultValueDefinition;
-    }
-
-    /**
-     * Get nullable flag: {@code true} if this column accepts nulls.
-     */
-    public boolean nullable() {
-        return type.isNullable();
-    }
-
-    /**
-     * Get column's precision.
-     */
-    public @Nullable Integer precision() {
-        colType = Objects.requireNonNullElse(colType, Objects.requireNonNull(columnType(type()), "colType"));
-        Integer precision = colType.lengthAllowed() ? PRECISION_NOT_SPECIFIED : type.getPrecision();
-        precision = precision == PRECISION_NOT_SPECIFIED ? null : precision;
-
-        return type.getSqlTypeName().allowsPrec() ? precision : null;
-    }
-
-    /**
-     * Get column's scale.
-     */
-    public @Nullable Integer scale() {
-        colType = Objects.requireNonNullElse(colType, Objects.requireNonNull(columnType(type()), "colType"));
-        Integer scale = colType.lengthAllowed() ? SCALE_NOT_SPECIFIED : type.getScale();
-        scale = scale == SCALE_NOT_SPECIFIED ? null : scale;
-
-        return type.getSqlTypeName().allowsScale() ? scale : null;
-    }
-
-    /**
-     * Get column's length.
-     */
-    public @Nullable Integer length() {
-        colType = Objects.requireNonNullElse(colType, Objects.requireNonNull(columnType(type()), "colType"));
-        int length = type.getPrecision();
-        length = length == PRECISION_NOT_SPECIFIED ? defaultLength(colType, DEFAULT_LENGTH) : length;
-
-        return colType.lengthAllowed() ? length : null;
-    }
-}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/CreateIndexCommand.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/CreateIndexCommand.java
deleted file mode 100644
index a32fa4e..0000000
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/CreateIndexCommand.java
+++ /dev/null
@@ -1,117 +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.sql.engine.prepare.ddl;
-
-import java.util.List;
-import org.apache.ignite.internal.sql.engine.schema.IgniteIndex.Collation;
-
-/**
- * CREATE INDEX statement.
- */
-public class CreateIndexCommand implements DdlCommand {
-    /** Type of the index to create. */
-    public enum Type {
-        SORTED, HASH
-    }
-
-    /** Table name. */
-    private String tableName;
-
-    /** Schema name where this new Index will be created. */
-    private String schemaName;
-
-    /** Idx name. */
-    private String indexName;
-
-    private Type type;
-
-    /** Quietly ignore this command if index already exists. */
-    private boolean ifNotExists;
-
-    private List<String> columns;
-
-    private List<Collation> collations;
-
-    /** Return idx name. */
-    public String indexName() {
-        return indexName;
-    }
-
-    /** Set idx name. */
-    public void indexName(String indexName) {
-        this.indexName = indexName;
-    }
-
-    public List<String> columns() {
-        return columns;
-    }
-
-    public void columns(List<String> columns) {
-        this.columns = columns;
-    }
-
-    public List<Collation> collations() {
-        return collations;
-    }
-
-    public void collations(List<Collation> collations) {
-        this.collations = collations;
-    }
-
-    /**
-     * Quietly ignore this command if index already exists.
-     *
-     * @return Quietly ignore flag.
-     */
-    public boolean ifNotExists() {
-        return ifNotExists;
-    }
-
-    /**
-     * Quietly ignore this command if index already exists.
-     *
-     * @param ifNotExists Exists flag.
-     */
-    public void ifNotExists(boolean ifNotExists) {
-        this.ifNotExists = ifNotExists;
-    }
-
-    public void type(Type type) {
-        this.type = type;
-    }
-
-    public Type type() {
-        return type;
-    }
-
-    public String tableName() {
-        return tableName;
-    }
-
-    public void tableName(String tblName) {
-        this.tableName = tblName;
-    }
-
-    public String schemaName() {
-        return schemaName;
-    }
-
-    public void schemaName(String schemaName) {
-        this.schemaName = schemaName;
-    }
-}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/CreateTableCommand.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/CreateTableCommand.java
deleted file mode 100644
index f985626..0000000
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/CreateTableCommand.java
+++ /dev/null
@@ -1,159 +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.sql.engine.prepare.ddl;
-
-import java.util.List;
-import org.apache.ignite.internal.sql.engine.schema.IgniteIndex.Collation;
-import org.jetbrains.annotations.Nullable;
-
-/**
- * CREATE TABLE statement.
- */
-public class CreateTableCommand extends AbstractTableDdlCommand {
-
-    /** Primary key index type. */
-    public enum PrimaryKeyIndexType {
-        SORTED, HASH
-    }
-
-    private PrimaryKeyIndexType pkIndexType;
-
-    /** Primary key columns. */
-    private List<String> pkCols;
-
-    private List<Collation> pkColsCollations;
-
-    /** Colocation columns. */
-    private List<String> colocationCols;
-
-    /** Columns. */
-    private List<ColumnDefinition> cols;
-
-    private String zone;
-
-    private String storageProfile;
-
-    /**
-     * Get primary key index type.
-     */
-    public PrimaryKeyIndexType primaryIndexType() {
-        return pkIndexType;
-    }
-
-    /**
-     * Set primary key index type.
-     */
-    public void primaryIndexType(PrimaryKeyIndexType pkIndexType) {
-        this.pkIndexType = pkIndexType;
-    }
-
-    /**
-     * Get primary key columns.
-     */
-    public List<String> primaryKeyColumns() {
-        return pkCols;
-    }
-
-    /**
-     * Set primary key columns.
-     */
-    public void primaryKeyColumns(List<String> pkCols) {
-        this.pkCols = pkCols;
-    }
-
-    /**
-     * Get primary key column collations.
-     */
-    public List<Collation> primaryKeyCollations() {
-        return pkColsCollations;
-    }
-
-    /**
-     * Set primary key columns collations.
-     */
-    public void primaryKeyCollations(List<Collation> pkColsCollations) {
-        this.pkColsCollations = pkColsCollations;
-    }
-
-    /**
-     * Get table columns.
-     *
-     * @return Columns.
-     */
-    public List<ColumnDefinition> columns() {
-        return cols;
-    }
-
-    /**
-     * Set table columns.
-     *
-     * @param cols Columns.
-     */
-    public void columns(List<ColumnDefinition> cols) {
-        this.cols = cols;
-    }
-
-    /**
-     * Set colocation column names.
-     *
-     * @return Collocation column names.
-     */
-    @Nullable
-    public List<String> colocationColumns() {
-        return colocationCols;
-    }
-
-    /**
-     * Get colocation column names.
-     *
-     * @param colocationCols Colocation column names.
-     */
-    public void colocationColumns(List<String> colocationCols) {
-        this.colocationCols = colocationCols;
-    }
-
-    /**
-     * Get zone name.
-     */
-    @Nullable
-    public String zone() {
-        return zone;
-    }
-
-    /**
-     * Set zone name.
-     */
-    public void zone(String zoneName) {
-        this.zone = zoneName;
-    }
-
-    /**
-     * Get storage profile.
-     */
-    @Nullable
-    public String storageProfile() {
-        return storageProfile;
-    }
-
-    /**
-     * Set storage profile.
-     */
-    public void storageProfile(String storageProfile) {
-        this.storageProfile = storageProfile;
-    }
-}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/CreateZoneCommand.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/CreateZoneCommand.java
deleted file mode 100644
index 82d9e73..0000000
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/CreateZoneCommand.java
+++ /dev/null
@@ -1,124 +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.sql.engine.prepare.ddl;
-
-import org.jetbrains.annotations.Nullable;
-
-/**
- * CREATE ZONE statement.
- */
-public class CreateZoneCommand extends AbstractZoneDdlCommand {
-    /** Quietly ignore this command if zone is already exists. */
-    private boolean ifNotExists;
-
-    /** Replicas number. */
-    private Integer replicas;
-
-    /** Number of partitions. */
-    private Integer partitions;
-
-    /** Affinity function name. */
-    private String affinity;
-
-    /** Data nodes filter expression. */
-    private String nodeFilter;
-
-    /** Data nodes auto adjust timeout. */
-    private Integer dataNodesAutoAdjust;
-
-    /** Data nodes auto adjust scale up timeout. */
-    private Integer dataNodesAutoAdjustScaleUp;
-
-    /** Data nodes auto adjust scale down timeout. */
-    private Integer dataNodesAutoAdjustScaleDown;
-
-    /** Storage profiles. */
-    private String storageProfiles;
-
-    public String storageProfiles() {
-        return storageProfiles;
-    }
-
-    public void storageProfiles(String storageProfiles) {
-        this.storageProfiles = storageProfiles;
-    }
-
-    public boolean ifNotExists() {
-        return ifNotExists;
-    }
-
-    public void ifNotExists(boolean ifNotExists) {
-        this.ifNotExists = ifNotExists;
-    }
-
-    @Nullable public Integer replicas() {
-        return replicas;
-    }
-
-    public void replicas(Integer replicas) {
-        this.replicas = replicas;
-    }
-
-    @Nullable public Integer partitions() {
-        return partitions;
-    }
-
-    public void partitions(Integer partitions) {
-        this.partitions = partitions;
-    }
-
-    @Nullable public String affinity() {
-        return affinity;
-    }
-
-    public void affinity(String affinity) {
-        this.affinity = affinity;
-    }
-
-    @Nullable public String nodeFilter() {
-        return nodeFilter;
-    }
-
-    public void nodeFilter(String nodeFilter) {
-        this.nodeFilter = nodeFilter;
-    }
-
-    @Nullable public Integer dataNodesAutoAdjust() {
-        return dataNodesAutoAdjust;
-    }
-
-    public void dataNodesAutoAdjust(Integer dataNodesAutoAdjust) {
-        this.dataNodesAutoAdjust = dataNodesAutoAdjust;
-    }
-
-    @Nullable public Integer dataNodesAutoAdjustScaleUp() {
-        return dataNodesAutoAdjustScaleUp;
-    }
-
-    public void dataNodesAutoAdjustScaleUp(Integer dataNodesAutoAdjustScaleUp) {
-        this.dataNodesAutoAdjustScaleUp = dataNodesAutoAdjustScaleUp;
-    }
-
-    @Nullable public Integer dataNodesAutoAdjustScaleDown() {
-        return dataNodesAutoAdjustScaleDown;
-    }
-
-    public void dataNodesAutoAdjustScaleDown(Integer dataNodesAutoAdjustScaleDown) {
-        this.dataNodesAutoAdjustScaleDown = dataNodesAutoAdjustScaleDown;
-    }
-}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlCommand.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlCommand.java
deleted file mode 100644
index eb2316c..0000000
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlCommand.java
+++ /dev/null
@@ -1,22 +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.sql.engine.prepare.ddl;
-
-/** Common interface to group all DDL operations. */
-public interface DdlCommand {
-}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverter.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverter.java
index 5468cce..4f0ad6f 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverter.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverter.java
@@ -19,6 +19,9 @@
 
 import static org.apache.calcite.rel.type.RelDataType.PRECISION_NOT_SPECIFIED;
 import static org.apache.calcite.rel.type.RelDataType.SCALE_NOT_SPECIFIED;
+import static org.apache.ignite.internal.catalog.commands.CatalogUtils.DEFAULT_LENGTH;
+import static org.apache.ignite.internal.catalog.commands.CatalogUtils.defaultLength;
+import static org.apache.ignite.internal.distributionzones.DistributionZonesUtil.parseStorageProfiles;
 import static org.apache.ignite.internal.lang.IgniteStringFormatter.format;
 import static org.apache.ignite.internal.sql.engine.prepare.ddl.TableOptionEnum.PRIMARY_ZONE;
 import static org.apache.ignite.internal.sql.engine.prepare.ddl.TableOptionEnum.STORAGE_PROFILE;
@@ -33,8 +36,8 @@
 import static org.apache.ignite.internal.sql.engine.util.IgniteMath.convertToByteExact;
 import static org.apache.ignite.internal.sql.engine.util.IgniteMath.convertToIntExact;
 import static org.apache.ignite.internal.sql.engine.util.IgniteMath.convertToShortExact;
+import static org.apache.ignite.internal.sql.engine.util.TypeUtils.columnType;
 import static org.apache.ignite.internal.sql.engine.util.TypeUtils.fromInternal;
-import static org.apache.ignite.internal.util.CollectionUtils.nullOrEmpty;
 import static org.apache.ignite.lang.ErrorGroups.Sql.STMT_VALIDATION_ERR;
 
 import java.math.BigDecimal;
@@ -54,7 +57,6 @@
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
-import java.util.function.Function;
 import java.util.stream.Collectors;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.runtime.CalciteContextException;
@@ -80,12 +82,36 @@
 import org.apache.calcite.util.DateString;
 import org.apache.calcite.util.TimeString;
 import org.apache.calcite.util.TimestampString;
+import org.apache.ignite.internal.catalog.CatalogCommand;
+import org.apache.ignite.internal.catalog.commands.AlterTableAddColumnCommand;
+import org.apache.ignite.internal.catalog.commands.AlterTableAddColumnCommandBuilder;
+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.AlterTableDropColumnCommandBuilder;
+import org.apache.ignite.internal.catalog.commands.AlterZoneCommand;
+import org.apache.ignite.internal.catalog.commands.AlterZoneCommandBuilder;
+import org.apache.ignite.internal.catalog.commands.AlterZoneSetDefaultCommand;
+import org.apache.ignite.internal.catalog.commands.ColumnParams;
+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.CreateTableCommandBuilder;
+import org.apache.ignite.internal.catalog.commands.CreateZoneCommand;
+import org.apache.ignite.internal.catalog.commands.CreateZoneCommandBuilder;
 import org.apache.ignite.internal.catalog.commands.DefaultValue;
+import org.apache.ignite.internal.catalog.commands.DeferredDefaultValue;
+import org.apache.ignite.internal.catalog.commands.DropIndexCommand;
+import org.apache.ignite.internal.catalog.commands.DropTableCommand;
+import org.apache.ignite.internal.catalog.commands.DropTableCommandBuilder;
+import org.apache.ignite.internal.catalog.commands.DropZoneCommand;
+import org.apache.ignite.internal.catalog.commands.RenameZoneCommand;
+import org.apache.ignite.internal.catalog.commands.TableHashPrimaryKey;
+import org.apache.ignite.internal.catalog.commands.TablePrimaryKey;
+import org.apache.ignite.internal.catalog.commands.TableSortedPrimaryKey;
+import org.apache.ignite.internal.catalog.descriptors.CatalogColumnCollation;
 import org.apache.ignite.internal.sql.engine.prepare.IgnitePlanner;
 import org.apache.ignite.internal.sql.engine.prepare.PlanningContext;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.CreateIndexCommand.Type;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.CreateTableCommand.PrimaryKeyIndexType;
-import org.apache.ignite.internal.sql.engine.schema.IgniteIndex.Collation;
 import org.apache.ignite.internal.sql.engine.sql.IgniteSqlAlterColumn;
 import org.apache.ignite.internal.sql.engine.sql.IgniteSqlAlterTableAddColumn;
 import org.apache.ignite.internal.sql.engine.sql.IgniteSqlAlterTableDropColumn;
@@ -104,7 +130,6 @@
 import org.apache.ignite.internal.sql.engine.sql.IgniteSqlPrimaryKeyIndexType;
 import org.apache.ignite.internal.sql.engine.sql.IgniteSqlZoneOption;
 import org.apache.ignite.internal.sql.engine.util.Commons;
-import org.apache.ignite.internal.sql.engine.util.TypeUtils;
 import org.apache.ignite.lang.IgniteException;
 import org.apache.ignite.lang.SchemaNotFoundException;
 import org.apache.ignite.sql.ColumnType;
@@ -112,18 +137,17 @@
 import org.jetbrains.annotations.Nullable;
 
 /**
- * DdlSqlToCommandConverter.
+ * Converts the DDL AST tree to the appropriate catalog command.
  */
-// TODO: IGNITE-15859 Add documentation
 public class DdlSqlToCommandConverter {
     /** Mapping: Table option ID -> DDL option info. */
-    private final Map<TableOptionEnum, DdlOptionInfo<CreateTableCommand, ?>> tableOptionInfos;
+    private final Map<TableOptionEnum, DdlOptionInfo<CreateTableCommandBuilder, ?>> tableOptionInfos;
 
     /** Mapping: Zone option ID -> DDL option info. */
-    private final Map<ZoneOptionEnum, DdlOptionInfo<CreateZoneCommand, ?>> zoneOptionInfos;
+    private final Map<ZoneOptionEnum, DdlOptionInfo<CreateZoneCommandBuilder, ?>> zoneOptionInfos;
 
     /** Mapping: Zone option ID -> DDL option info. */
-    private final Map<ZoneOptionEnum, DdlOptionInfo<AlterZoneSetCommand, ?>> alterZoneOptionInfos;
+    private final Map<ZoneOptionEnum, DdlOptionInfo<AlterZoneCommandBuilder, ?>> alterZoneOptionInfos;
 
     /** Zone options set. */
     private final Set<String> knownZoneOptionNames;
@@ -138,46 +162,49 @@
                 .collect(Collectors.toSet());
 
         this.tableOptionInfos = new EnumMap<>(Map.of(
-                PRIMARY_ZONE, new DdlOptionInfo<>(String.class, null, CreateTableCommand::zone),
-                STORAGE_PROFILE, new DdlOptionInfo<>(String.class, this::checkEmptyString, CreateTableCommand::storageProfile)
+                PRIMARY_ZONE, new DdlOptionInfo<>(String.class, null, CreateTableCommandBuilder::zone),
+                STORAGE_PROFILE, new DdlOptionInfo<>(String.class, this::checkEmptyString, CreateTableCommandBuilder::storageProfile)
         ));
 
         // CREATE ZONE options.
         zoneOptionInfos = new EnumMap<>(Map.of(
-                REPLICAS, new DdlOptionInfo<>(Integer.class, this::checkPositiveNumber, CreateZoneCommand::replicas),
-                PARTITIONS, new DdlOptionInfo<>(Integer.class, this::checkPositiveNumber, CreateZoneCommand::partitions),
-                AFFINITY_FUNCTION, new DdlOptionInfo<>(String.class, null, CreateZoneCommand::affinity),
-                DATA_NODES_FILTER, new DdlOptionInfo<>(String.class, null, CreateZoneCommand::nodeFilter),
+                REPLICAS, new DdlOptionInfo<>(Integer.class, this::checkPositiveNumber, CreateZoneCommandBuilder::replicas),
+                PARTITIONS, new DdlOptionInfo<>(Integer.class, this::checkPositiveNumber, CreateZoneCommandBuilder::partitions),
+                // TODO https://issues.apache.org/jira/browse/IGNITE-22162 appropriate setter method should be used.
+                AFFINITY_FUNCTION, new DdlOptionInfo<>(String.class, null, (builder, params) -> {}),
+                DATA_NODES_FILTER, new DdlOptionInfo<>(String.class, null, CreateZoneCommandBuilder::filter),
                 DATA_NODES_AUTO_ADJUST,
-                new DdlOptionInfo<>(Integer.class, this::checkPositiveNumber, CreateZoneCommand::dataNodesAutoAdjust),
+                new DdlOptionInfo<>(Integer.class, this::checkPositiveNumber, CreateZoneCommandBuilder::dataNodesAutoAdjust),
                 DATA_NODES_AUTO_ADJUST_SCALE_UP,
-                new DdlOptionInfo<>(Integer.class, this::checkPositiveNumber, CreateZoneCommand::dataNodesAutoAdjustScaleUp),
+                new DdlOptionInfo<>(Integer.class, this::checkPositiveNumber, CreateZoneCommandBuilder::dataNodesAutoAdjustScaleUp),
                 DATA_NODES_AUTO_ADJUST_SCALE_DOWN,
-                new DdlOptionInfo<>(Integer.class, this::checkPositiveNumber, CreateZoneCommand::dataNodesAutoAdjustScaleDown),
-                STORAGE_PROFILES, new DdlOptionInfo<>(String.class, this::checkEmptyString, CreateZoneCommand::storageProfiles)
+                new DdlOptionInfo<>(Integer.class, this::checkPositiveNumber, CreateZoneCommandBuilder::dataNodesAutoAdjustScaleDown),
+                STORAGE_PROFILES, new DdlOptionInfo<>(String.class, this::checkEmptyString,
+                        (builder, params) -> builder.storageProfilesParams(parseStorageProfiles(params)))
         ));
 
         // ALTER ZONE options.
         alterZoneOptionInfos = new EnumMap<>(Map.of(
-                REPLICAS, new DdlOptionInfo<>(Integer.class, this::checkPositiveNumber, AlterZoneSetCommand::replicas),
-                PARTITIONS, new DdlOptionInfo<>(Integer.class, this::checkPositiveNumber, AlterZoneSetCommand::partitions),
-                DATA_NODES_FILTER, new DdlOptionInfo<>(String.class, null, AlterZoneSetCommand::nodeFilter),
+                REPLICAS, new DdlOptionInfo<>(Integer.class, this::checkPositiveNumber, AlterZoneCommandBuilder::replicas),
+                PARTITIONS, new DdlOptionInfo<>(Integer.class, this::checkPositiveNumber, AlterZoneCommandBuilder::partitions),
+                DATA_NODES_FILTER, new DdlOptionInfo<>(String.class, null, AlterZoneCommandBuilder::filter),
                 DATA_NODES_AUTO_ADJUST,
-                new DdlOptionInfo<>(Integer.class, this::checkPositiveNumber, AlterZoneSetCommand::dataNodesAutoAdjust),
+                new DdlOptionInfo<>(Integer.class, this::checkPositiveNumber, AlterZoneCommandBuilder::dataNodesAutoAdjust),
                 DATA_NODES_AUTO_ADJUST_SCALE_UP,
-                new DdlOptionInfo<>(Integer.class, this::checkPositiveNumber, AlterZoneSetCommand::dataNodesAutoAdjustScaleUp),
+                new DdlOptionInfo<>(Integer.class, this::checkPositiveNumber, AlterZoneCommandBuilder::dataNodesAutoAdjustScaleUp),
                 DATA_NODES_AUTO_ADJUST_SCALE_DOWN,
-                new DdlOptionInfo<>(Integer.class, this::checkPositiveNumber, AlterZoneSetCommand::dataNodesAutoAdjustScaleDown)
+                new DdlOptionInfo<>(Integer.class, this::checkPositiveNumber, AlterZoneCommandBuilder::dataNodesAutoAdjustScaleDown)
         ));
     }
 
     /**
-     * Converts a given ddl AST to a ddl command.
+     * Converts a given ddl AST to a catalog command.
      *
      * @param ddlNode Root node of the given AST.
      * @param ctx Planning context.
+     * @return Catalog command.
      */
-    public DdlCommand convert(SqlDdl ddlNode, PlanningContext ctx) {
+    public CatalogCommand convert(SqlDdl ddlNode, PlanningContext ctx) {
         if (ddlNode instanceof IgniteSqlCreateTable) {
             return convertCreateTable((IgniteSqlCreateTable) ddlNode, ctx);
         }
@@ -232,20 +259,12 @@
     }
 
     /**
-     * Converts a given CreateTable AST to a CreateTable command.
-     *
-     * @param createTblNode Root node of the given AST.
-     * @param ctx Planning context.
+     * Converts the given '{@code CREATE TABLE}' AST to the {@link CreateTableCommand} catalog command.
      */
-    private CreateTableCommand convertCreateTable(IgniteSqlCreateTable createTblNode, PlanningContext ctx) {
-        CreateTableCommand createTblCmd = new CreateTableCommand();
-
-        createTblCmd.schemaName(deriveSchemaName(createTblNode.name(), ctx));
-        createTblCmd.tableName(deriveObjectName(createTblNode.name(), ctx, "tableName"));
-        createTblCmd.ifTableExists(createTblNode.ifNotExists());
+    private CatalogCommand convertCreateTable(IgniteSqlCreateTable createTblNode, PlanningContext ctx) {
+        CreateTableCommandBuilder tblBuilder = CreateTableCommand.builder();
 
         if (createTblNode.createOptionList() != null) {
-
             for (SqlNode optionNode : createTblNode.createOptionList().getList()) {
                 IgniteSqlCreateTableOption option = (IgniteSqlCreateTableOption) optionNode;
 
@@ -254,12 +273,12 @@
                 String optionKey = option.key().getSimple().toUpperCase();
 
                 try {
-                    DdlOptionInfo<CreateTableCommand, ?> tblOptionInfo = tableOptionInfos.get(TableOptionEnum.valueOf(optionKey));
+                    DdlOptionInfo<CreateTableCommandBuilder, ?> tblOptionInfo = tableOptionInfos.get(TableOptionEnum.valueOf(optionKey));
 
-                    updateCommandOption("Table", optionKey, option.value(), tblOptionInfo, ctx.query(), createTblCmd);
+                    updateCommandOption("Table", optionKey, option.value(), tblOptionInfo, ctx.query(), tblBuilder);
                 } catch (IllegalArgumentException ignored) {
                     throw new SqlException(
-                            STMT_VALIDATION_ERR, String.format("Unexpected table option [option=%s, query=%s]", optionKey, ctx.query()));
+                            STMT_VALIDATION_ERR, format("Unexpected table option [option={}, query={}]", optionKey, ctx.query()));
                 }
             }
         }
@@ -281,9 +300,11 @@
             createTblNode.columnList().add(0, col);
         }
 
-        if (nullOrEmpty(pkConstraints)) {
+        if (pkConstraints.isEmpty()) {
             throw new SqlException(STMT_VALIDATION_ERR, "Table without PRIMARY KEY is not supported");
-        } else if (pkConstraints.size() > 1) {
+        }
+
+        if (pkConstraints.size() > 1) {
             throw new SqlException(STMT_VALIDATION_ERR, "Unexpected number of primary key constraints ["
                     + "expected at most one, but was " + pkConstraints.size() + "; "
                     + "querySql=\"" + ctx.query() + "\"]");
@@ -293,34 +314,47 @@
         SqlNodeList columnNodes = pkConstraint.getColumnList();
 
         List<String> pkColumns = new ArrayList<>(columnNodes.size());
-        List<Collation> pkCollations = new ArrayList<>(columnNodes.size());
+        List<CatalogColumnCollation> pkCollations = new ArrayList<>(columnNodes.size());
 
-        PrimaryKeyIndexType pkIndexType = convertPrimaryIndexType(pkConstraint.getIndexType());
-        boolean supportCollation = pkIndexType == PrimaryKeyIndexType.SORTED;
+        IgniteSqlPrimaryKeyIndexType pkIndexType = pkConstraint.getIndexType();
+        boolean supportCollation = pkIndexType == IgniteSqlPrimaryKeyIndexType.SORTED;
 
         parseColumnList(pkConstraint.getColumnList(), pkColumns, pkCollations, supportCollation);
 
-        createTblCmd.primaryIndexType(pkIndexType);
-        createTblCmd.primaryKeyColumns(pkColumns);
-        createTblCmd.primaryKeyCollations(pkCollations);
+        TablePrimaryKey primaryKey;
 
-        List<String> colocationCols = createTblNode.colocationColumns() == null
+        switch (pkIndexType) {
+            case SORTED:
+                primaryKey = TableSortedPrimaryKey.builder()
+                        .columns(pkColumns)
+                        .collations(pkCollations)
+                        .build();
+                break;
+
+            case HASH:
+            case IMPLICIT_HASH:
+                primaryKey = TableHashPrimaryKey.builder()
+                        .columns(pkColumns)
+                        .build();
+                break;
+
+            default:
+                throw new IllegalArgumentException("Unexpected primary key index type: " + pkIndexType);
+        }
+
+        List<String> colocationColumns = createTblNode.colocationColumns() == null
                 ? null
                 : createTblNode.colocationColumns().getList().stream()
                         .map(SqlIdentifier.class::cast)
                         .map(SqlIdentifier::getSimple)
                         .collect(Collectors.toList());
 
-        createTblCmd.colocationColumns(colocationCols);
-
         List<SqlColumnDeclaration> colDeclarations = createTblNode.columnList().getList().stream()
                 .filter(SqlColumnDeclaration.class::isInstance)
                 .map(SqlColumnDeclaration.class::cast)
                 .collect(Collectors.toList());
 
-        IgnitePlanner planner = ctx.planner();
-
-        List<ColumnDefinition> cols = new ArrayList<>(colDeclarations.size());
+        List<ColumnParams> columns = new ArrayList<>(colDeclarations.size());
 
         for (SqlColumnDeclaration col : colDeclarations) {
             if (!col.name.isSimple()) {
@@ -329,92 +363,125 @@
                         + "querySql=\"" + ctx.query() + "\"]");
             }
 
-            String name = col.name.getSimple();
-
-            RelDataType relType = planner.convert(col.dataType, !pkColumns.contains(name));
-
-            DefaultValueDefinition dflt = convertDefault(col.expression, relType, name);
-            cols.add(new ColumnDefinition(name, relType, dflt));
+            columns.add(convertColumnDeclaration(col, ctx.planner(), !pkColumns.contains(col.name.getSimple())));
         }
 
-        createTblCmd.columns(cols);
+        return tblBuilder.schemaName(deriveSchemaName(createTblNode.name(), ctx))
+                .tableName(deriveObjectName(createTblNode.name(), ctx, "tableName"))
+                .columns(columns)
+                .primaryKey(primaryKey)
+                .colocationColumns(colocationColumns)
+                .ifTableExists(createTblNode.ifNotExists())
+                .build();
+    }
 
-        return createTblCmd;
+    private static ColumnParams convertColumnDeclaration(SqlColumnDeclaration col, IgnitePlanner planner, boolean nullable) {
+        assert col.name.isSimple();
+
+        String name = col.name.getSimple();
+        RelDataType relType = planner.convert(col.dataType, nullable);
+        ColumnTypeParams typeParams = new ColumnTypeParams(relType);
+
+        return ColumnParams.builder()
+                .name(name)
+                .type(typeParams.colType)
+                .nullable(relType.isNullable())
+                .precision(typeParams.precision)
+                .scale(typeParams.scale)
+                .length(typeParams.length)
+                .defaultValue(convertDefault(col.expression, relType, name))
+                .build();
+    }
+
+    private static DefaultValue convertDefault(@Nullable SqlNode expression, RelDataType relType, String name) {
+        if (expression == null) {
+            return DefaultValue.constant(null);
+        }
+
+        if (expression instanceof SqlIdentifier) {
+            return DefaultValue.functionCall(((SqlIdentifier) expression).getSimple());
+        }
+
+        if (expression instanceof SqlLiteral) {
+            ColumnType columnType = columnType(relType);
+
+            Object val = fromLiteral(columnType, name, (SqlLiteral) expression, relType.getPrecision(), relType.getScale());
+            return DefaultValue.constant(val);
+        }
+
+        throw new IllegalArgumentException("Unsupported default expression: " + expression.getKind());
     }
 
     /**
-     * Converts a given IgniteSqlAlterTableAddColumn AST to a AlterTableAddCommand.
-     *
-     * @param alterTblNode Root node of the given AST.
-     * @param ctx Planning context.
+     * Converts the given `{@code ALTER TABLE ... ADD COLUMN}` AST into the {@link IgniteSqlAlterTableAddColumn} catalog command.
      */
-    private AlterTableAddCommand convertAlterTableAdd(IgniteSqlAlterTableAddColumn alterTblNode, PlanningContext ctx) {
-        AlterTableAddCommand alterTblCmd = new AlterTableAddCommand();
+    private CatalogCommand convertAlterTableAdd(IgniteSqlAlterTableAddColumn alterTblNode, PlanningContext ctx) {
+        AlterTableAddColumnCommandBuilder builder = AlterTableAddColumnCommand.builder();
 
-        alterTblCmd.schemaName(deriveSchemaName(alterTblNode.name(), ctx));
-        alterTblCmd.tableName(deriveObjectName(alterTblNode.name(), ctx, "table name"));
-        alterTblCmd.ifTableExists(alterTblNode.ifExists());
+        builder.schemaName(deriveSchemaName(alterTblNode.name(), ctx));
+        builder.tableName(deriveObjectName(alterTblNode.name(), ctx, "table name"));
+        builder.ifTableExists(alterTblNode.ifExists());
 
-        List<ColumnDefinition> cols = new ArrayList<>(alterTblNode.columns().size());
+        List<ColumnParams> columns = new ArrayList<>(alterTblNode.columns().size());
 
         for (SqlNode colNode : alterTblNode.columns()) {
             assert colNode instanceof SqlColumnDeclaration : colNode.getClass();
-
             SqlColumnDeclaration col = (SqlColumnDeclaration) colNode;
-
-            assert col.name.isSimple();
-
             Boolean nullable = col.dataType.getNullable();
-            RelDataType relType = ctx.planner().convert(col.dataType, nullable != null ? nullable : true);
-            String name = col.name.getSimple();
-            DefaultValueDefinition dflt = convertDefault(col.expression, relType, name);
 
-            cols.add(new ColumnDefinition(name, relType, dflt));
+            columns.add(convertColumnDeclaration(col, ctx.planner(), nullable != null ? nullable : true));
         }
 
-        alterTblCmd.columns(cols);
+        builder.columns(columns);
 
-        return alterTblCmd;
+        return builder.build();
     }
 
-    private static DefaultValueDefinition convertDefault(@Nullable SqlNode expression, RelDataType relType, String name) {
-        if (expression == null) {
-            return DefaultValueDefinition.constant(null);
-        } else if (expression instanceof SqlIdentifier) {
-            return DefaultValueDefinition.functionCall(((SqlIdentifier) expression).getSimple());
-        } else if (expression instanceof SqlLiteral) {
-            ColumnType columnType = TypeUtils.columnType(relType);
-            assert columnType != null : "RelType to columnType conversion should not return null";
+    /**
+     * Converts the given `{@code ALTER TABLE ... ALTER COLUMN}` AST into the {@link AlterTableAlterColumnCommand} catalog command.
+     */
+    private CatalogCommand convertAlterColumn(IgniteSqlAlterColumn alterColumnNode, PlanningContext ctx) {
+        AlterTableAlterColumnCommandBuilder builder = AlterTableAlterColumnCommand.builder();
 
-            Object val = fromLiteral(columnType, name, (SqlLiteral) expression, relType.getPrecision(), relType.getScale());
-            return DefaultValueDefinition.constant(val);
-        } else {
-            throw new IllegalArgumentException("Unsupported default expression: " + expression.getKind());
-        }
-    }
+        builder.schemaName(deriveSchemaName(alterColumnNode.name(), ctx));
+        builder.tableName(deriveObjectName(alterColumnNode.name(), ctx, "table name"));
+        builder.ifTableExists(alterColumnNode.ifExists());
+        builder.columnName(alterColumnNode.columnName().getSimple());
 
-    private AlterColumnCommand convertAlterColumn(IgniteSqlAlterColumn alterColumnNode, PlanningContext ctx) {
-        AlterColumnCommand cmd = new AlterColumnCommand();
+        RelDataType relType = null;
 
-        cmd.schemaName(deriveSchemaName(alterColumnNode.name(), ctx));
-        cmd.tableName(deriveObjectName(alterColumnNode.name(), ctx, "table name"));
-        cmd.ifTableExists(alterColumnNode.ifExists());
-        cmd.columnName(alterColumnNode.columnName().getSimple());
+        SqlDataTypeSpec colTypeSpec = alterColumnNode.dataType();
 
-        if (alterColumnNode.dataType() != null) {
-            cmd.type(ctx.planner().convert(alterColumnNode.dataType(), true));
+        if (colTypeSpec != null) {
+            relType = ctx.planner().convert(colTypeSpec, true);
+
+            ColumnTypeParams typeParams = new ColumnTypeParams(relType);
+
+            builder.type(typeParams.colType);
+
+            if (typeParams.length != null) {
+                builder.length(typeParams.length);
+            } else {
+                if (typeParams.precision != null) {
+                    builder.precision(typeParams.precision);
+                }
+
+                if (typeParams.scale != null) {
+                    builder.scale(typeParams.scale);
+                }
+            }
         }
 
-        if (alterColumnNode.notNull() != null) {
-            cmd.notNull(alterColumnNode.notNull());
+        Boolean notNull = alterColumnNode.notNull();
+
+        if (notNull != null) {
+            builder.nullable(!notNull);
         }
 
         if (alterColumnNode.expression() != null) {
             SqlNode expr = alterColumnNode.expression();
 
-            Function<ColumnType, DefaultValue> resolveDfltFunc;
-
-            @Nullable RelDataType relType = cmd.type();
+            DeferredDefaultValue resolveDfltFunc;
 
             int precision = relType == null ? PRECISION_NOT_SPECIFIED : relType.getPrecision();
             int scale = relType == null ? SCALE_NOT_SPECIFIED : relType.getScale();
@@ -426,126 +493,118 @@
                 throw new IllegalStateException("Invalid expression type " + expr.getKind());
             }
 
-            cmd.defaultValueResolver(resolveDfltFunc);
+            builder.deferredDefaultValue(resolveDfltFunc);
         }
 
-        return cmd;
+        return builder.build();
     }
 
     /**
-     * Converts a given IgniteSqlAlterTableDropColumn AST to a AlterTableDropCommand.
-     *
-     * @param alterTblNode Root node of the given AST.
-     * @param ctx Planning context.
+     * Converts the given '{@code ALTER TABLE ... DROP COLUMN}' AST to the {@link AlterTableDropColumnCommand} catalog command.
      */
-    private AlterTableDropCommand convertAlterTableDrop(IgniteSqlAlterTableDropColumn alterTblNode, PlanningContext ctx) {
-        AlterTableDropCommand alterTblCmd = new AlterTableDropCommand();
+    private CatalogCommand convertAlterTableDrop(IgniteSqlAlterTableDropColumn alterTblNode, PlanningContext ctx) {
+        AlterTableDropColumnCommandBuilder builder = AlterTableDropColumnCommand.builder();
 
-        alterTblCmd.schemaName(deriveSchemaName(alterTblNode.name(), ctx));
-        alterTblCmd.tableName(deriveObjectName(alterTblNode.name(), ctx, "table name"));
-        alterTblCmd.ifTableExists(alterTblNode.ifExists());
+        builder.schemaName(deriveSchemaName(alterTblNode.name(), ctx));
+        builder.tableName(deriveObjectName(alterTblNode.name(), ctx, "table name"));
+        builder.ifTableExists(alterTblNode.ifExists());
 
         Set<String> cols = new HashSet<>(alterTblNode.columns().size());
         alterTblNode.columns().forEach(c -> cols.add(((SqlIdentifier) c).getSimple()));
 
-        alterTblCmd.columns(cols);
+        builder.columns(cols);
 
-        return alterTblCmd;
+        return builder.build();
     }
 
     /**
-     * Converts a given DropTable AST to a DropTable command.
-     *
-     * @param dropTblNode Root node of the given AST.
-     * @param ctx Planning context.
+     * Converts the given '{@code DROP TABLE}' AST to the {@link DropTableCommand} catalog command.
      */
-    private DropTableCommand convertDropTable(IgniteSqlDropTable dropTblNode, PlanningContext ctx) {
-        DropTableCommand dropTblCmd = new DropTableCommand();
+    private CatalogCommand convertDropTable(IgniteSqlDropTable dropTblNode, PlanningContext ctx) {
+        DropTableCommandBuilder builder = DropTableCommand.builder();
 
-        dropTblCmd.schemaName(deriveSchemaName(dropTblNode.name(), ctx));
-        dropTblCmd.tableName(deriveObjectName(dropTblNode.name(), ctx, "tableName"));
-        dropTblCmd.ifTableExists(dropTblNode.ifExists);
-
-        return dropTblCmd;
+        return builder.schemaName(deriveSchemaName(dropTblNode.name(), ctx))
+                .tableName(deriveObjectName(dropTblNode.name(), ctx, "tableName"))
+                .ifTableExists(dropTblNode.ifExists)
+                .build();
     }
 
     /**
-     * Converts create index to appropriate wrapper.
+     * Converts '{@code CREATE INDEX}' AST to the appropriate catalog command.
      */
-    private CreateIndexCommand convertAddIndex(IgniteSqlCreateIndex sqlCmd, PlanningContext ctx) {
-        CreateIndexCommand createIdxCmd = new CreateIndexCommand();
-
-        createIdxCmd.schemaName(deriveSchemaName(sqlCmd.tableName(), ctx));
-        createIdxCmd.tableName(deriveObjectName(sqlCmd.tableName(), ctx, "table name"));
-        createIdxCmd.indexName(sqlCmd.indexName().getSimple());
-        createIdxCmd.type(convertIndexType(sqlCmd.type()));
-
+    private CatalogCommand convertAddIndex(IgniteSqlCreateIndex sqlCmd, PlanningContext ctx) {
+        boolean sortedIndex = sqlCmd.type() == IgniteSqlIndexType.SORTED || sqlCmd.type() == IgniteSqlIndexType.IMPLICIT_SORTED;
         SqlNodeList columnList = sqlCmd.columnList();
         List<String> columns = new ArrayList<>(columnList.size());
-        List<Collation> collations = new ArrayList<>(columnList.size());
-        boolean supportCollation = createIdxCmd.type() == Type.SORTED;
+        List<CatalogColumnCollation> collations = new ArrayList<>(columnList.size());
 
-        parseColumnList(columnList, columns, collations, supportCollation);
+        parseColumnList(columnList, columns, collations, sortedIndex);
 
-        createIdxCmd.columns(columns);
-        createIdxCmd.collations(collations);
-
-        createIdxCmd.ifNotExists(sqlCmd.ifNotExists());
-
-        return createIdxCmd;
+        if (sortedIndex) {
+            return CreateSortedIndexCommand.builder()
+                    .schemaName(deriveSchemaName(sqlCmd.tableName(), ctx))
+                    .tableName(deriveObjectName(sqlCmd.tableName(), ctx, "table name"))
+                    .ifNotExists(sqlCmd.ifNotExists())
+                    .indexName(sqlCmd.indexName().getSimple())
+                    .columns(columns)
+                    .collations(collations)
+                    .build();
+        } else {
+            return CreateHashIndexCommand.builder()
+                    .schemaName(deriveSchemaName(sqlCmd.tableName(), ctx))
+                    .tableName(deriveObjectName(sqlCmd.tableName(), ctx, "table name"))
+                    .ifNotExists(sqlCmd.ifNotExists())
+                    .indexName(sqlCmd.indexName().getSimple())
+                    .columns(columns)
+                    .build();
+        }
     }
 
     private static void parseColumnList(
             SqlNodeList columnList,
             List<String> columns,
-            List<Collation> collations,
+            List<CatalogColumnCollation> collations,
             boolean supportCollation
     ) {
         for (SqlNode col : columnList.getList()) {
-            boolean desc = false;
+            boolean asc = true;
 
             if (col.getKind() == SqlKind.DESCENDING) {
                 col = ((SqlCall) col).getOperandList().get(0);
 
-                desc = true;
+                asc = false;
             }
 
             String columnName = ((SqlIdentifier) col).getSimple();
             columns.add(columnName);
             if (supportCollation) {
-                collations.add(desc ? Collation.DESC_NULLS_FIRST : Collation.ASC_NULLS_LAST);
+                collations.add(CatalogColumnCollation.get(asc, !asc));
             }
         }
     }
 
     /**
-     * Converts drop index to appropriate wrapper.
+     * Converts '{@code DROP INDEX}' AST to the appropriate catalog command.
      */
-    private DropIndexCommand convertDropIndex(IgniteSqlDropIndex sqlCmd, PlanningContext ctx) {
-        DropIndexCommand dropCmd = new DropIndexCommand();
-
+    private CatalogCommand convertDropIndex(IgniteSqlDropIndex sqlCmd, PlanningContext ctx) {
         String schemaName = deriveSchemaName(sqlCmd.indexName(), ctx);
         String indexName = deriveObjectName(sqlCmd.indexName(), ctx, "index name");
 
-        dropCmd.schemaName(schemaName);
-        dropCmd.indexName(indexName);
-        dropCmd.ifNotExists(sqlCmd.ifExists());
-
-        return dropCmd;
+        return DropIndexCommand.builder()
+                .schemaName(schemaName)
+                .indexName(indexName)
+                .ifExists(sqlCmd.ifExists())
+                .build();
     }
 
     /**
-     * Converts a given CreateZone AST to a CreateZone command.
-     *
-     * @param createZoneNode Root node of the given AST.
-     * @param ctx Planning context.
+     * Converts the given '{@code CREATE ZONE}' AST to the {@link CreateZoneCommand} catalog command.
      */
-    private CreateZoneCommand convertCreateZone(IgniteSqlCreateZone createZoneNode, PlanningContext ctx) {
-        CreateZoneCommand createZoneCmd = new CreateZoneCommand();
+    private CatalogCommand convertCreateZone(IgniteSqlCreateZone createZoneNode, PlanningContext ctx) {
+        CreateZoneCommandBuilder builder = CreateZoneCommand.builder();
 
-        createZoneCmd.schemaName(deriveSchemaName(createZoneNode.name(), ctx));
-        createZoneCmd.zoneName(deriveObjectName(createZoneNode.name(), ctx, "zoneName"));
-        createZoneCmd.ifNotExists(createZoneNode.ifNotExists());
+        builder.zoneName(deriveObjectName(createZoneNode.name(), ctx, "zoneName"));
+        builder.ifNotExists(createZoneNode.ifNotExists());
 
         if (createZoneNode.createOptionList() == null) {
             throw new SqlException(STMT_VALIDATION_ERR, STORAGE_PROFILES + " option cannot be null");
@@ -560,7 +619,7 @@
 
             String optionName = option.key().getSimple().toUpperCase();
 
-            DdlOptionInfo<CreateZoneCommand, ?> zoneOptionInfo = null;
+            DdlOptionInfo<CreateZoneCommandBuilder, ?> zoneOptionInfo = null;
 
             if (remainingKnownOptions.remove(optionName)) {
                 zoneOptionInfo = zoneOptionInfos.get(ZoneOptionEnum.valueOf(optionName));
@@ -572,29 +631,25 @@
                 throw unexpectedZoneOption(ctx, optionName);
             }
 
-            updateCommandOption("Zone", optionName, (SqlLiteral) option.value(), zoneOptionInfo, ctx.query(), createZoneCmd);
+            updateCommandOption("Zone", optionName, option.value(), zoneOptionInfo, ctx.query(), builder);
         }
 
-        if (createZoneCmd.storageProfiles() == null) {
+        if (remainingKnownOptions.contains(STORAGE_PROFILES.name())) {
             throw new SqlException(STMT_VALIDATION_ERR, STORAGE_PROFILES + " option cannot be null");
         }
 
-        return createZoneCmd;
+        return builder.build();
     }
 
 
     /**
-     * Converts the given IgniteSqlAlterZoneSet AST node to a AlterZoneCommand.
-     *
-     * @param alterZoneSet Root node of the given AST.
-     * @param ctx Planning context.
+     * Converts the given '{@code ALTER ZONE}' AST to the {@link AlterZoneCommand} catalog command.
      */
-    private DdlCommand convertAlterZoneSet(IgniteSqlAlterZoneSet alterZoneSet, PlanningContext ctx) {
-        AlterZoneSetCommand alterZoneCmd = new AlterZoneSetCommand();
+    private CatalogCommand convertAlterZoneSet(IgniteSqlAlterZoneSet alterZoneSet, PlanningContext ctx) {
+        AlterZoneCommandBuilder builder = AlterZoneCommand.builder();
 
-        alterZoneCmd.schemaName(deriveSchemaName(alterZoneSet.name(), ctx));
-        alterZoneCmd.zoneName(deriveObjectName(alterZoneSet.name(), ctx, "zoneName"));
-        alterZoneCmd.ifExists(alterZoneSet.ifExists());
+        builder.zoneName(deriveObjectName(alterZoneSet.name(), ctx, "zoneName"));
+        builder.ifExists(alterZoneSet.ifExists());
 
         Set<String> remainingKnownOptions = new HashSet<>(knownZoneOptionNames);
 
@@ -608,64 +663,46 @@
                 throw duplicateZoneOption(ctx, optionName);
             }
 
-            DdlOptionInfo<AlterZoneSetCommand, ?> zoneOptionInfo = alterZoneOptionInfos.get(ZoneOptionEnum.valueOf(optionName));
+            DdlOptionInfo<AlterZoneCommandBuilder, ?> zoneOptionInfo = alterZoneOptionInfos.get(ZoneOptionEnum.valueOf(optionName));
 
             assert zoneOptionInfo != null : optionName;
             assert option.value() instanceof SqlLiteral : option.value();
 
-            updateCommandOption("Zone", optionName, (SqlLiteral) option.value(), zoneOptionInfo, ctx.query(), alterZoneCmd);
+            updateCommandOption("Zone", optionName, option.value(), zoneOptionInfo, ctx.query(), builder);
         }
 
-        return alterZoneCmd;
+        return builder.build();
     }
 
     /**
-     * Converts the given {@link IgniteSqlAlterZoneSetDefault} AST node to a {@link AlterZoneSetDefaultCommand}.
-     *
-     * @param alterZoneSetDefault Root node of the given AST.
-     * @param ctx Planning context.
+     * Converts the given '{@code ALTER ZONE ... SET DEFAULT}' AST node to the {@link AlterZoneSetDefaultCommand} catalog command.
      */
-    private DdlCommand convertAlterZoneSetDefault(IgniteSqlAlterZoneSetDefault alterZoneSetDefault, PlanningContext ctx) {
-        AlterZoneSetDefaultCommand cmd = new AlterZoneSetDefaultCommand();
-
-        cmd.schemaName(deriveSchemaName(alterZoneSetDefault.name(), ctx));
-        cmd.zoneName(deriveObjectName(alterZoneSetDefault.name(), ctx, "zoneName"));
-        cmd.ifExists(alterZoneSetDefault.ifExists());
-
-        return cmd;
+    private CatalogCommand convertAlterZoneSetDefault(IgniteSqlAlterZoneSetDefault alterZoneSetDefault, PlanningContext ctx) {
+        return AlterZoneSetDefaultCommand.builder()
+                .zoneName(deriveObjectName(alterZoneSetDefault.name(), ctx, "zoneName"))
+                .ifExists(alterZoneSetDefault.ifExists())
+                .build();
     }
 
     /**
-     * Converts the given IgniteSqlAlterZoneRenameTo AST node to a AlterZoneCommand.
-     *
-     * @param alterZoneRename Root node of the given AST.
-     * @param ctx Planning context.
+     * Converts the given '{@code ALTER ZONE ... RENAME TO}' AST node to the {@link RenameZoneCommand} catalog command.
      */
-    private DdlCommand convertAlterZoneRename(IgniteSqlAlterZoneRenameTo alterZoneRename, PlanningContext ctx) {
-        AlterZoneRenameCommand cmd = new AlterZoneRenameCommand();
-
-        cmd.schemaName(deriveSchemaName(alterZoneRename.name(), ctx));
-        cmd.zoneName(deriveObjectName(alterZoneRename.name(), ctx, "zoneName"));
-        cmd.newZoneName(alterZoneRename.newName().getSimple());
-        cmd.ifExists(alterZoneRename.ifExists());
-
-        return cmd;
+    private CatalogCommand convertAlterZoneRename(IgniteSqlAlterZoneRenameTo alterZoneRename, PlanningContext ctx) {
+        return RenameZoneCommand.builder()
+                .zoneName(deriveObjectName(alterZoneRename.name(), ctx, "zoneName"))
+                .newZoneName(alterZoneRename.newName().getSimple())
+                .ifExists(alterZoneRename.ifExists())
+                .build();
     }
 
     /**
-     * Converts a given DropZone AST to a DropZone command.
-     *
-     * @param dropZoneNode Root node of the given AST.
-     * @param ctx Planning context.
+     * Converts the given '{@code DROP ZONE}' AST to the {@link DropZoneCommand} catalog command.
      */
-    private DropZoneCommand convertDropZone(IgniteSqlDropZone dropZoneNode, PlanningContext ctx) {
-        DropZoneCommand dropZoneCmd = new DropZoneCommand();
-
-        dropZoneCmd.schemaName(deriveSchemaName(dropZoneNode.name(), ctx));
-        dropZoneCmd.zoneName(deriveObjectName(dropZoneNode.name(), ctx, "zoneName"));
-        dropZoneCmd.ifExists(dropZoneNode.ifExists());
-
-        return dropZoneCmd;
+    private CatalogCommand convertDropZone(IgniteSqlDropZone dropZoneNode, PlanningContext ctx) {
+        return DropZoneCommand.builder()
+                .zoneName(deriveObjectName(dropZoneNode.name(), ctx, "zoneName"))
+                .ifExists(dropZoneNode.ifExists())
+                .build();
     }
 
     /** Derives a schema name from the compound identifier. */
@@ -685,7 +722,9 @@
             schemaName = schemaId.getSimple();
         }
 
-        ensureSchemaExists(ctx, schemaName);
+        if (ctx.catalogReader().getRootSchema().getSubSchema(schemaName, true) == null) {
+            throw new SchemaNotFoundException(schemaName);
+        }
 
         return schemaName;
     }
@@ -707,27 +746,6 @@
         return objId.getSimple();
     }
 
-    private void ensureSchemaExists(PlanningContext ctx, String schemaName) {
-        if (ctx.catalogReader().getRootSchema().getSubSchema(schemaName, true) == null) {
-            throw new SchemaNotFoundException(schemaName);
-        }
-    }
-
-    /**
-     * Checks that there are no ID duplicates.
-     *
-     * @param set0 Set of string identifiers.
-     * @param set1 Set of string identifiers.
-     * @throws IllegalStateException If there is a duplicate ID.
-     */
-    static void checkDuplicates(Set<String> set0, Set<String> set1) {
-        for (String id : set1) {
-            if (set0.contains(id)) {
-                throw new IllegalStateException("Duplicate id: " + id);
-            }
-        }
-    }
-
     private <S, T> void updateCommandOption(
             String sqlObjName,
             Object optId,
@@ -755,11 +773,10 @@
             case LITERAL:
                 return valueFromLiteralAccordingToOptionType(sqlObjName, optId, optInfo, query, (SqlLiteral) value);
             default:
-                String msg = String.format(
-                        "Invalid %s value kind [kind=%s, expectedKind=(IDENTIFIER, LITERAL), query=%s]",
-                        sqlObjName.toLowerCase(),
-                        valueKind,
-                        query);
+                String msg = format(
+                        "Invalid {} value kind [kind={}, expectedKind=(IDENTIFIER, LITERAL), query={}]",
+                        sqlObjName.toLowerCase(), valueKind, query
+                );
                 throw new SqlException(STMT_VALIDATION_ERR, msg);
         }
     }
@@ -772,12 +789,10 @@
         try {
             optInfo.validator.accept(expectedValue);
         } catch (Throwable e) {
-            String msg = String.format(
-                    "%s option validation failed [option=%s, err=%s, query=%s]",
-                    sqlObjName,
-                    optId,
-                    e.getMessage(),
-                    query);
+            String msg = format(
+                    "{} option validation failed [option={}, err={}, query={}]",
+                    sqlObjName, optId, e.getMessage(), query
+            );
             throw new SqlException(STMT_VALIDATION_ERR, msg, e);
         }
     }
@@ -791,12 +806,10 @@
         try {
             return literalValue.getValueAs(optInfo.type);
         } catch (Throwable cause) {
-            String msg = String.format(
-                    "Invalid %s option type [option=%s, expectedType=%s, query=%s]",
-                    sqlObjName.toLowerCase(),
-                    optId,
-                    optInfo.type.getSimpleName(),
-                    query);
+            String msg = format(
+                    "Invalid {} option type [option={}, expectedType={}, query={}]",
+                    sqlObjName.toLowerCase(), optId, optInfo.type.getSimpleName(), query
+            );
             throw new SqlException(STMT_VALIDATION_ERR, msg, cause);
         }
     }
@@ -813,30 +826,6 @@
         }
     }
 
-    private Type convertIndexType(IgniteSqlIndexType type) {
-        switch (type) {
-            case SORTED:
-            case IMPLICIT_SORTED:
-                return Type.SORTED;
-            case HASH:
-                return Type.HASH;
-            default:
-                throw new AssertionError("Unknown index type [type=" + type + "]");
-        }
-    }
-
-    private PrimaryKeyIndexType convertPrimaryIndexType(IgniteSqlPrimaryKeyIndexType type) {
-        switch (type) {
-            case SORTED:
-                return PrimaryKeyIndexType.SORTED;
-            case HASH:
-            case IMPLICIT_HASH:
-                return PrimaryKeyIndexType.HASH;
-            default:
-                throw new AssertionError("Unknown index type [type=" + type + "]");
-        }
-    }
-
     /**
      * Creates a value of required type from the literal.
      */
@@ -988,11 +977,37 @@
 
     private static IgniteException unexpectedZoneOption(PlanningContext ctx, String optionName) {
         return new SqlException(STMT_VALIDATION_ERR,
-                String.format("Unexpected zone option [option=%s, query=%s]", optionName, ctx.query()));
+                format("Unexpected zone option [option={}, query={}]", optionName, ctx.query()));
     }
 
     private static IgniteException duplicateZoneOption(PlanningContext ctx, String optionName) {
         return new SqlException(STMT_VALIDATION_ERR,
-                String.format("Duplicate zone option has been specified [option=%s, query=%s]", optionName, ctx.query()));
+                format("Duplicate zone option has been specified [option={}, query={}]", optionName, ctx.query()));
+    }
+
+    /** Helper for obtaining scale, precision and length parameters uniformly. */
+    static class ColumnTypeParams {
+        final ColumnType colType;
+        @Nullable Integer precision = null;
+        @Nullable Integer scale = null;
+        @Nullable Integer length = null;
+
+        ColumnTypeParams(RelDataType relType) {
+            colType = columnType(relType);
+
+            if (colType.lengthAllowed()) {
+                length = relType.getPrecision() == PRECISION_NOT_SPECIFIED
+                        ? defaultLength(colType, DEFAULT_LENGTH)
+                        : relType.getPrecision();
+            } else {
+                if (relType.getSqlTypeName().allowsPrec() && relType.getPrecision() != PRECISION_NOT_SPECIFIED) {
+                    precision = relType.getPrecision();
+                }
+
+                if (relType.getSqlTypeName().allowsScale() && relType.getScale() != SCALE_NOT_SPECIFIED) {
+                    scale = relType.getScale();
+                }
+            }
+        }
     }
 }
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DefaultValueDefinition.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DefaultValueDefinition.java
deleted file mode 100644
index c10aa62..0000000
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DefaultValueDefinition.java
+++ /dev/null
@@ -1,97 +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.sql.engine.prepare.ddl;
-
-import java.util.Objects;
-import org.jetbrains.annotations.Nullable;
-
-/**
- * Definition of value provider to use as default.
- */
-@SuppressWarnings("PublicInnerClass")
-public class DefaultValueDefinition {
-    /**
-     * Defines value provider as functional provider.
-     *
-     * @param name Name of the function to invoke to generate the value
-     * @return Default value definition.
-     */
-    public static DefaultValueDefinition functionCall(String name) {
-        return new FunctionCall(Objects.requireNonNull(name, "name"));
-    }
-
-    /**
-     * Defines value provider as a constant value provider.
-     *
-     * @param value A value to use as default.
-     * @return Default value definition.
-     */
-    public static DefaultValueDefinition constant(@Nullable Object value) {
-        return new ConstantValue(value);
-    }
-
-    /** Types of the defaults. */
-    public enum Type {
-        /** Default is specified as a constant. */
-        CONSTANT,
-
-        /** Default is specified as a call to a function. */
-        FUNCTION_CALL
-    }
-
-    protected final Type type;
-
-    private DefaultValueDefinition(Type type) {
-        this.type = type;
-    }
-
-    /** Returns type of the default value. */
-    public Type type() {
-        return type;
-    }
-
-    /** Defines default value provider as a function call. */
-    public static class FunctionCall extends DefaultValueDefinition {
-        private final String functionName;
-
-        private FunctionCall(String functionName) {
-            super(Type.FUNCTION_CALL);
-            this.functionName = functionName;
-        }
-
-        /** Returns name of the function to use as value generator. */
-        public String functionName() {
-            return functionName;
-        }
-    }
-
-    /** Defines default value provider as a constant. */
-    public static class ConstantValue extends DefaultValueDefinition {
-        private final Object value;
-
-        private ConstantValue(Object value) {
-            super(Type.CONSTANT);
-            this.value = value;
-        }
-
-        /** Returns value to use as default. */
-        public Object value() {
-            return value;
-        }
-    }
-}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DropIndexCommand.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DropIndexCommand.java
deleted file mode 100644
index d96622d..0000000
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DropIndexCommand.java
+++ /dev/null
@@ -1,78 +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.sql.engine.prepare.ddl;
-
-/**
- * DROP INDEX statement.
- */
-public class DropIndexCommand implements DdlCommand {
-    /** Index name. */
-    private String indexName;
-
-    /** If exist flag. */
-    private boolean ifExists;
-
-    /** Schema name where this index will be dropped. */
-    private String schemaName;
-
-    /** Return idx name. */
-    public String indexName() {
-        return indexName;
-    }
-
-    /** Set idx name. */
-    public void indexName(String indexName) {
-        this.indexName = indexName;
-    }
-
-    /**
-     * Quietly ignore this command if index doesn't exist.
-     *
-     * @return Quietly ignore flag.
-     */
-    public boolean ifNotExists() {
-        return ifExists;
-    }
-
-    /**
-     * Quietly ignore this command if index doesn't exist.
-     *
-     * @param ifExist Exists flag.
-     */
-    public void ifNotExists(boolean ifExist) {
-        this.ifExists = ifExist;
-    }
-
-    /**
-     * Return index schema name.
-     *
-     * @return Schema name.
-     */
-    public String schemaName() {
-        return schemaName;
-    }
-
-    /**
-     * Sets index schema name.
-     *
-     * @param schemaName Schema name.
-     */
-    public void schemaName(String schemaName) {
-        this.schemaName = schemaName;
-    }
-}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DropTableCommand.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DropTableCommand.java
deleted file mode 100644
index 4349239..0000000
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DropTableCommand.java
+++ /dev/null
@@ -1,24 +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.sql.engine.prepare.ddl;
-
-/**
- * DROP TABLE statement.
- */
-public class DropTableCommand extends AbstractTableDdlCommand {
-}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DropZoneCommand.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DropZoneCommand.java
deleted file mode 100644
index 4186418..0000000
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DropZoneCommand.java
+++ /dev/null
@@ -1,34 +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.sql.engine.prepare.ddl;
-
-/**
- * DROP ZONE statement.
- */
-public class DropZoneCommand extends AbstractZoneDdlCommand {
-    /** Quietly ignore this command if the zone does not exist. */
-    private boolean ifExists;
-
-    public boolean ifExists() {
-        return ifExists;
-    }
-
-    public void ifExists(boolean ifExists) {
-        this.ifExists = ifExists;
-    }
-}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/TypeUtils.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/TypeUtils.java
index 0a3ed82..e8e7781 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/TypeUtils.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/TypeUtils.java
@@ -365,8 +365,7 @@
             case NULL:
                 return ColumnType.NULL;
             default:
-                assert false : "Unexpected type of result: " + type.getSqlTypeName();
-                return null;
+                throw new IllegalArgumentException("Unexpected type: " + type.getSqlTypeName());
         }
     }
 
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/ddl/DdlCommandHandlerExceptionHandlingTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/ddl/DdlCommandHandlerExceptionHandlingTest.java
index b5bcbae..468b60a 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/ddl/DdlCommandHandlerExceptionHandlingTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/ddl/DdlCommandHandlerExceptionHandlingTest.java
@@ -20,21 +20,23 @@
 import static org.apache.ignite.internal.catalog.CatalogService.DEFAULT_STORAGE_PROFILE;
 import static org.apache.ignite.internal.catalog.CatalogTestUtils.createTestCatalogManager;
 import static org.apache.ignite.internal.distributionzones.DistributionZonesTestUtil.createZone;
+import static org.apache.ignite.internal.distributionzones.DistributionZonesUtil.parseStorageProfiles;
 import static org.apache.ignite.internal.testframework.matchers.CompletableFutureExceptionMatcher.willThrow;
 import static org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willCompleteSuccessfully;
 import static org.apache.ignite.internal.util.IgniteUtils.stopAsync;
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import java.util.concurrent.CompletableFuture;
+import org.apache.ignite.internal.catalog.CatalogCommand;
 import org.apache.ignite.internal.catalog.CatalogManager;
 import org.apache.ignite.internal.catalog.DistributionZoneExistsValidationException;
 import org.apache.ignite.internal.catalog.DistributionZoneNotFoundValidationException;
+import org.apache.ignite.internal.catalog.commands.CreateZoneCommand;
+import org.apache.ignite.internal.catalog.commands.DropZoneCommand;
 import org.apache.ignite.internal.hlc.ClockWaiter;
 import org.apache.ignite.internal.hlc.HybridClock;
 import org.apache.ignite.internal.hlc.HybridClockImpl;
 import org.apache.ignite.internal.hlc.TestClockService;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.CreateZoneCommand;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.DropZoneCommand;
 import org.apache.ignite.internal.testframework.IgniteAbstractTest;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
@@ -79,17 +81,19 @@
 
     @Test
     public void testZoneNotFoundOnDrop1() {
-        DropZoneCommand cmd = new DropZoneCommand();
-        cmd.zoneName(ZONE_NAME);
+        CatalogCommand cmd = DropZoneCommand.builder()
+                .zoneName(ZONE_NAME)
+                .build();
 
         assertThat(commandHandler.handle(cmd), willThrow(DistributionZoneNotFoundValidationException.class));
     }
 
     @Test
     public void testZoneNotFoundOnDrop2() {
-        DropZoneCommand cmd = new DropZoneCommand();
-        cmd.zoneName(ZONE_NAME);
-        cmd.ifExists(true);
+        CatalogCommand cmd = DropZoneCommand.builder()
+                .zoneName(ZONE_NAME)
+                .ifExists(true)
+                .build();
 
         assertThat(commandHandler.handle(cmd), willCompleteSuccessfully());
     }
@@ -97,10 +101,11 @@
     private CompletableFuture<Boolean> handleCreateZoneCommand(boolean ifNotExists) {
         createZone(catalogManager, ZONE_NAME, null, null, null);
 
-        CreateZoneCommand cmd = new CreateZoneCommand();
-        cmd.zoneName(ZONE_NAME);
-        cmd.storageProfiles(DEFAULT_STORAGE_PROFILE);
-        cmd.ifNotExists(ifNotExists);
+        CatalogCommand cmd = CreateZoneCommand.builder()
+                .zoneName(ZONE_NAME)
+                .storageProfilesParams(parseStorageProfiles(DEFAULT_STORAGE_PROFILE))
+                .ifNotExists(ifNotExists)
+                .build();
 
         return commandHandler.handle(cmd);
     }
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/ddl/DistributionZoneDdlCommandHandlerTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/ddl/DistributionZoneDdlCommandHandlerTest.java
deleted file mode 100644
index a308596..0000000
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/ddl/DistributionZoneDdlCommandHandlerTest.java
+++ /dev/null
@@ -1,103 +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.sql.engine.exec.ddl;
-
-import static org.apache.ignite.internal.catalog.CatalogService.DEFAULT_STORAGE_PROFILE;
-import static org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willCompleteSuccessfully;
-import static org.apache.ignite.internal.util.CompletableFutures.nullCompletedFuture;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import org.apache.ignite.internal.catalog.CatalogManager;
-import org.apache.ignite.internal.catalog.commands.AlterZoneCommand;
-import org.apache.ignite.internal.catalog.commands.RenameZoneCommand;
-import org.apache.ignite.internal.hlc.ClockService;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.AlterZoneRenameCommand;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.AlterZoneSetCommand;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.CreateZoneCommand;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.DdlCommand;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.DropZoneCommand;
-import org.apache.ignite.internal.testframework.IgniteAbstractTest;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-/**
- * Tests distribution zone commands handling.
- */
-public class DistributionZoneDdlCommandHandlerTest extends IgniteAbstractTest {
-    private static final String ZONE_NAME = "test_zone";
-
-    private DdlCommandHandler commandHandler;
-
-    private CatalogManager catalogManager;
-
-    @BeforeEach
-    void before() {
-        catalogManager = mock(CatalogManager.class, invocation -> nullCompletedFuture());
-
-        commandHandler = new DdlCommandHandler(catalogManager, mock(ClockService.class, invocation -> nullCompletedFuture()), () -> 100);
-    }
-
-    @Test
-    public void testCreateZone() {
-        CreateZoneCommand cmd = new CreateZoneCommand();
-        cmd.zoneName(ZONE_NAME);
-        cmd.storageProfiles(DEFAULT_STORAGE_PROFILE);
-
-        invokeHandler(cmd);
-
-        verify(catalogManager).execute(any(org.apache.ignite.internal.catalog.commands.CreateZoneCommand.class));
-    }
-
-    @Test
-    public void testRenameZone() {
-        AlterZoneRenameCommand renameCmd = new AlterZoneRenameCommand();
-        renameCmd.zoneName(ZONE_NAME);
-        renameCmd.newZoneName(ZONE_NAME + "_new");
-
-        invokeHandler(renameCmd);
-
-        verify(catalogManager).execute(any(RenameZoneCommand.class));
-    }
-
-    @Test
-    public void testAlterZone() {
-        AlterZoneSetCommand cmd = new AlterZoneSetCommand();
-        cmd.zoneName(ZONE_NAME);
-
-        invokeHandler(cmd);
-
-        verify(catalogManager).execute(any(AlterZoneCommand.class));
-    }
-
-    @Test
-    public void testDropZone() {
-        DropZoneCommand cmd = new DropZoneCommand();
-        cmd.zoneName(ZONE_NAME);
-
-        invokeHandler(cmd);
-
-        verify(catalogManager).execute(any(org.apache.ignite.internal.catalog.commands.DropZoneCommand.class));
-    }
-
-    private void invokeHandler(DdlCommand cmd) {
-        assertThat(commandHandler.handle(cmd), willCompleteSuccessfully());
-    }
-}
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AbstractDdlSqlToCommandConverterTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AbstractDdlSqlToCommandConverterTest.java
index 0fa1d28..191503d 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AbstractDdlSqlToCommandConverterTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/ddl/AbstractDdlSqlToCommandConverterTest.java
@@ -19,6 +19,10 @@
 
 import static org.apache.calcite.tools.Frameworks.newConfigBuilder;
 import static org.apache.ignite.internal.sql.engine.util.Commons.FRAMEWORK_CONFIG;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.not;
+import static org.mockito.Mockito.mock;
 
 import java.util.List;
 import java.util.UUID;
@@ -26,19 +30,25 @@
 import org.apache.calcite.sql.parser.SqlParseException;
 import org.apache.calcite.sql.parser.SqlParser;
 import org.apache.calcite.tools.Frameworks;
+import org.apache.ignite.internal.catalog.Catalog;
+import org.apache.ignite.internal.catalog.CatalogCommand;
 import org.apache.ignite.internal.catalog.CatalogService;
+import org.apache.ignite.internal.catalog.storage.UpdateEntry;
 import org.apache.ignite.internal.generated.query.calcite.sql.IgniteSqlParserImpl;
 import org.apache.ignite.internal.sql.engine.prepare.PlanningContext;
 import org.apache.ignite.internal.sql.engine.schema.IgniteSchema;
 import org.apache.ignite.internal.sql.engine.util.BaseQueryContext;
 import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
+import org.hamcrest.Matchers;
 
 /**
  * Common methods for {@link DdlSqlToCommandConverter} testing.
  */
 class AbstractDdlSqlToCommandConverterTest extends BaseIgniteAbstractTest {
     /** DDL SQL to command converter. */
-    DdlSqlToCommandConverter converter = new DdlSqlToCommandConverter();
+    final DdlSqlToCommandConverter converter = new DdlSqlToCommandConverter();
+
+    final Catalog catalog = mock(Catalog.class);
 
     /**
      * Parses a given statement and returns a resulting AST.
@@ -67,4 +77,17 @@
                 .query("")
                 .build();
     }
+
+    /** Invokes command on a dummy catalog and returns the first entry in the result list. */
+    <T> T invokeAndGetFirstEntry(CatalogCommand cmd, Class<T> expected) {
+        List<UpdateEntry> entries = cmd.get(catalog);
+
+        assertThat(entries, not(empty()));
+
+        UpdateEntry entry = entries.get(0);
+
+        assertThat(entry, Matchers.instanceOf(expected));
+
+        return (T) entry;
+    }
 }
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverterTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverterTest.java
index dbbaa51..3eb9123 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverterTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverterTest.java
@@ -24,23 +24,28 @@
 import static org.apache.calcite.sql.type.SqlTypeName.NUMERIC_TYPES;
 import static org.apache.calcite.sql.type.SqlTypeName.REAL;
 import static org.apache.ignite.internal.catalog.CatalogService.DEFAULT_STORAGE_PROFILE;
+import static org.apache.ignite.internal.catalog.commands.CatalogUtils.fromParams;
+import static org.apache.ignite.internal.distributionzones.DistributionZonesUtil.parseStorageProfiles;
 import static org.apache.ignite.internal.lang.IgniteStringFormatter.format;
-import static org.apache.ignite.internal.sql.engine.prepare.ddl.DdlSqlToCommandConverter.checkDuplicates;
 import static org.apache.ignite.internal.sql.engine.util.SqlTestUtils.assertThrowsSqlException;
 import static org.apache.ignite.internal.sql.engine.util.SqlTestUtils.generateValueByType;
 import static org.apache.ignite.internal.sql.engine.util.TypeUtils.columnType;
 import static org.apache.ignite.internal.sql.engine.util.TypeUtils.fromInternal;
 import static org.apache.ignite.lang.ErrorGroups.Sql.STMT_VALIDATION_ERR;
 import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.allOf;
 import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.greaterThan;
 import static org.hamcrest.Matchers.hasItem;
 import static org.hamcrest.Matchers.hasSize;
 import static org.hamcrest.Matchers.startsWith;
 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
@@ -51,7 +56,6 @@
 import java.time.LocalTime;
 import java.time.Period;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
@@ -63,11 +67,21 @@
 import org.apache.calcite.sql.SqlDdl;
 import org.apache.calcite.sql.parser.SqlParseException;
 import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.ignite.internal.catalog.commands.CreateTableCommand;
+import org.apache.ignite.internal.catalog.commands.DefaultValue;
+import org.apache.ignite.internal.catalog.commands.DefaultValue.ConstantValue;
+import org.apache.ignite.internal.catalog.descriptors.CatalogColumnCollation;
+import org.apache.ignite.internal.catalog.descriptors.CatalogHashIndexDescriptor;
+import org.apache.ignite.internal.catalog.descriptors.CatalogIndexDescriptor.CatalogIndexDescriptorType;
+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;
+import org.apache.ignite.internal.catalog.storage.NewIndexEntry;
+import org.apache.ignite.internal.catalog.storage.NewTableEntry;
+import org.apache.ignite.internal.catalog.storage.UpdateEntry;
 import org.apache.ignite.internal.sql.engine.prepare.PlanningContext;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.CreateTableCommand.PrimaryKeyIndexType;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.DefaultValueDefinition.FunctionCall;
-import org.apache.ignite.internal.sql.engine.prepare.ddl.DefaultValueDefinition.Type;
-import org.apache.ignite.internal.sql.engine.schema.IgniteIndex.Collation;
 import org.apache.ignite.internal.sql.engine.util.Commons;
 import org.apache.ignite.internal.testframework.WithSystemProperty;
 import org.apache.ignite.lang.IgniteException;
@@ -83,11 +97,14 @@
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.CsvSource;
 import org.junit.jupiter.params.provider.ValueSource;
+import org.mockito.Mockito;
 
 /**
  * For {@link DdlSqlToCommandConverter} testing.
  */
 public class DdlSqlToCommandConverterTest extends AbstractDdlSqlToCommandConverterTest {
+    private static final Integer TEST_ZONE_ID = 100;
+
     @Test
     void testCheckDuplicates() {
         IllegalStateException exception = assertThrows(
@@ -147,28 +164,36 @@
 
         assertThat(cmd, Matchers.instanceOf(CreateTableCommand.class));
 
-        var createTable = (CreateTableCommand) cmd;
+        mockCatalogSchemaAndZone("TEST_ZONE");
+
+        List<UpdateEntry> entries = cmd.get(catalog);
+
+        assertThat(entries.size(), greaterThan(1));
+
+        CatalogTableDescriptor tblDesc = ((NewTableEntry) entries.get(0)).descriptor();
+
+        NewIndexEntry idxEntry = (NewIndexEntry) entries.get(1);
 
         assertThat(
-                createTable.columns(),
+                tblDesc.columns(),
                 allOf(
                         hasItem(columnThat("column with name \"VAL\"", cd -> "VAL".equals(cd.name()))),
                         hasItem(columnThat("implicit PK col", cd -> Commons.IMPLICIT_PK_COL_NAME.equals(cd.name())
-                                && !cd.nullable() && SqlTypeName.VARCHAR.equals(cd.type().getSqlTypeName())))
+                                && !cd.nullable() && ColumnType.STRING == cd.type()))
                 )
         );
 
         assertThat(
-                createTable.primaryKeyColumns(),
+                tblDesc.primaryKeyColumns(),
                 hasSize(1)
         );
 
         assertThat(
-                createTable.primaryKeyColumns(),
+                tblDesc.primaryKeyColumns(),
                 hasItem(Commons.IMPLICIT_PK_COL_NAME)
         );
 
-        assertEquals(createTable.primaryIndexType(), PrimaryKeyIndexType.HASH);
+        assertThat(idxEntry.descriptor().indexType(), is(CatalogIndexDescriptorType.HASH));
     }
 
     @ParameterizedTest
@@ -176,7 +201,7 @@
             "ASC, ASC_NULLS_LAST",
             "DESC, DESC_NULLS_FIRST"
     })
-    public void tableWithSortedPk(String sqlCol, Collation collation) throws SqlParseException {
+    public void tableWithSortedPk(String sqlCol, CatalogColumnCollation collation) throws SqlParseException {
         String query = format("CREATE TABLE t (id int, val int, PRIMARY KEY USING SORTED (id {}))", sqlCol);
         var node = parse(query);
 
@@ -186,11 +211,18 @@
 
         assertThat(cmd, Matchers.instanceOf(CreateTableCommand.class));
 
-        var createTable = (CreateTableCommand) cmd;
+        mockCatalogSchemaAndZone("TEST_ZONE");
 
-        assertEquals(createTable.primaryIndexType(), PrimaryKeyIndexType.SORTED);
-        assertEquals(createTable.primaryKeyColumns(), List.of("ID"));
-        assertEquals(createTable.primaryKeyCollations(), List.of(collation));
+        List<UpdateEntry> entries = cmd.get(catalog);
+
+        assertThat(entries.size(), greaterThan(1));
+
+        NewTableEntry tblEntry = (NewTableEntry) entries.get(0);
+        NewIndexEntry idxEntry = (NewIndexEntry) entries.get(1);
+
+        assertThat(idxEntry.descriptor().indexType(), is(CatalogIndexDescriptorType.SORTED));
+        assertThat(tblEntry.descriptor().primaryKeyColumns(), equalTo(List.of("ID")));
+        assertThat(((CatalogSortedIndexDescriptor) idxEntry.descriptor()).columns().get(0).collation(), is(collation));
     }
 
     @ParameterizedTest
@@ -219,11 +251,18 @@
 
         assertThat(cmd, Matchers.instanceOf(CreateTableCommand.class));
 
-        var createTable = (CreateTableCommand) cmd;
+        mockCatalogSchemaAndZone("TEST_ZONE");
 
-        assertEquals(createTable.primaryIndexType(), PrimaryKeyIndexType.HASH);
-        assertEquals(createTable.primaryKeyColumns(), List.of("ID"));
-        assertEquals(createTable.primaryKeyCollations(), Collections.emptyList());
+        List<UpdateEntry> entries = cmd.get(catalog);
+
+        assertThat(entries.size(), greaterThan(1));
+
+        NewTableEntry tblEntry = (NewTableEntry) entries.get(0);
+        NewIndexEntry idxEntry = (NewIndexEntry) entries.get(1);
+
+        assertThat(idxEntry.descriptor().indexType(), is(CatalogIndexDescriptorType.HASH));
+        assertThat(idxEntry.descriptor(), Matchers.instanceOf(CatalogHashIndexDescriptor.class));
+        assertThat(tblEntry.descriptor().primaryKeyColumns(), equalTo(List.of("ID")));
     }
 
     @Test
@@ -237,9 +276,11 @@
 
         assertThat(cmd, Matchers.instanceOf(CreateTableCommand.class));
 
-        var createTable = (CreateTableCommand) cmd;
+        mockCatalogSchemaAndZone("TEST_ZONE");
 
-        assertEquals("TEST_ZONE", createTable.zone());
+        NewTableEntry tblEntry = invokeAndGetFirstEntry(cmd, NewTableEntry.class);
+
+        assertThat(tblEntry.descriptor().zoneId(), is(TEST_ZONE_ID));
     }
 
     @Test
@@ -253,12 +294,13 @@
 
         assertThat(cmd, Matchers.instanceOf(CreateTableCommand.class));
 
-        var createTable = (CreateTableCommand) cmd;
+        mockCatalogSchemaAndZone("test_zone");
 
-        assertEquals("test_zone", createTable.zone());
+        NewTableEntry tblEntry = invokeAndGetFirstEntry(cmd, NewTableEntry.class);
+
+        assertThat(tblEntry.descriptor().zoneId(), is(TEST_ZONE_ID));
     }
 
-    @SuppressWarnings({"ThrowableNotThrown"})
     @TestFactory
     public Stream<DynamicTest> numericDefaultWithIntervalTypes() {
         List<DynamicTest> testItems = new ArrayList<>();
@@ -311,6 +353,7 @@
         return testItems.stream();
     }
 
+    @Disabled("https://issues.apache.org/jira/browse/IGNITE-15200")
     @TestFactory
     public Stream<DynamicTest> intervalDefaultsWithIntervalTypes() {
         List<DynamicTest> testItems = new ArrayList<>();
@@ -343,7 +386,7 @@
         return testItems.stream();
     }
 
-    @SuppressWarnings({"ThrowableNotThrown"})
+    @SuppressWarnings("ThrowableNotThrown")
     @Test
     public void testUuidWithDefaults() throws SqlParseException {
         PlanningContext ctx = createContext();
@@ -351,15 +394,23 @@
 
         String sql = format(template, "NULL");
         CreateTableCommand cmd = (CreateTableCommand) converter.convert((SqlDdl) parse(sql), ctx);
-        ColumnDefinition def = cmd.columns().get(1);
-        DefaultValueDefinition.ConstantValue defVal = def.defaultValueDefinition();
+
+        mockCatalogSchemaAndZone("TEST_ZONE");
+        CatalogTableDescriptor tblDesc = invokeAndGetFirstEntry(cmd, NewTableEntry.class).descriptor();
+
+        CatalogTableColumnDescriptor colDesc = tblDesc.columns().get(1);
+        ConstantValue defVal = (ConstantValue) colDesc.defaultValue();
+        assertNotNull(defVal);
         assertNull(defVal.value());
 
         UUID uuid = UUID.randomUUID();
         sql = format(template, "'" + uuid + "'");
         cmd = (CreateTableCommand) converter.convert((SqlDdl) parse(sql), ctx);
-        def = cmd.columns().get(1);
-        defVal = def.defaultValueDefinition();
+
+        tblDesc = invokeAndGetFirstEntry(cmd, NewTableEntry.class).descriptor();
+        colDesc = tblDesc.columns().get(1);
+        defVal = (ConstantValue) colDesc.defaultValue();
+        assertNotNull(defVal);
         assertEquals(uuid, defVal.value());
 
         String[] values = {"'01:01:02'", "'2020-01-02 01:01:01'", "'2020-01-02'", "true", "'true'", "x'01'", "INTERVAL '1' DAY"};
@@ -575,18 +626,20 @@
 
         assertThat(cmd, Matchers.instanceOf(CreateTableCommand.class));
 
-        var createTable = (CreateTableCommand) cmd;
+        mockCatalogSchemaAndZone("TEST_ZONE");
+
+        NewTableEntry tblEntry = invokeAndGetFirstEntry(cmd, NewTableEntry.class);
 
         assertThat(
-                createTable.columns(),
+                tblEntry.descriptor().columns(),
                 allOf(
                         hasItem(columnThat("column with name \"VAL\"", cd -> "VAL".equals(cd.name()))),
                         hasItem(columnThat("PK with functional default",
-                                cd -> "ID".equals(cd.name())
-                                        && !cd.nullable()
-                                        && SqlTypeName.VARCHAR.equals(cd.type().getSqlTypeName())
-                                        && cd.defaultValueDefinition().type() == Type.FUNCTION_CALL
-                                        && "GEN_RANDOM_UUID".equals(((FunctionCall) cd.defaultValueDefinition()).functionName())
+                                col -> "ID".equals(col.name())
+                                        && !col.nullable()
+                                        && ColumnType.STRING == col.type()
+                                        && col.defaultValue().type() == DefaultValue.Type.FUNCTION_CALL
+                                        && "GEN_RANDOM_UUID".equals(((DefaultValue.FunctionCall) col.defaultValue()).functionName())
                                 )
                         )
                 )
@@ -618,11 +671,13 @@
         assertThat(ex.getMessage(), containsString("String cannot be empty"));
     }
 
-    private static Matcher<ColumnDefinition> columnThat(String description, Function<ColumnDefinition, Boolean> checker) {
+    private static Matcher<CatalogTableColumnDescriptor> columnThat(String description,
+            Function<CatalogTableColumnDescriptor, Boolean> checker) {
         return new CustomMatcher<>(description) {
             @Override
             public boolean matches(Object actual) {
-                return actual instanceof ColumnDefinition && checker.apply((ColumnDefinition) actual) == Boolean.TRUE;
+                return actual instanceof CatalogTableColumnDescriptor
+                        && checker.apply((CatalogTableColumnDescriptor) actual) == Boolean.TRUE;
             }
         };
     }
@@ -645,7 +700,7 @@
         fillTestCase(type, val, testItems, acceptable, ctx, null);
     }
 
-    @SuppressWarnings({"ThrowableNotThrown"})
+    @SuppressWarnings("ThrowableNotThrown")
     private void fillTestCase(String type, String val, List<DynamicTest> testItems, boolean acceptable, PlanningContext ctx,
             @Nullable Object compare) {
         String template = "CREATE TABLE t (id INTEGER PRIMARY KEY, d {} DEFAULT {})";
@@ -654,8 +709,12 @@
         if (acceptable) {
             testItems.add(DynamicTest.dynamicTest(String.format("ALLOW: %s", sql), () -> {
                 CreateTableCommand cmd = (CreateTableCommand) converter.convert((SqlDdl) parse(sql), ctx);
-                ColumnDefinition def = cmd.columns().get(1);
-                DefaultValueDefinition.ConstantValue defVal = def.defaultValueDefinition();
+
+                mockCatalogSchemaAndZone("TEST_ZONE");
+                CatalogTableDescriptor tblDesc = invokeAndGetFirstEntry(cmd, NewTableEntry.class).descriptor();
+                CatalogTableColumnDescriptor columnDescriptor = tblDesc.columns().get(1);
+
+                ConstantValue defVal = (ConstantValue) columnDescriptor.defaultValue();
                 Object defaultValue = defVal.value();
                 if (compare != null) {
                     if (compare instanceof byte[]) {
@@ -671,4 +730,23 @@
                             converter.convert((SqlDdl) parse(sql), ctx))));
         }
     }
+
+    private void mockCatalogSchemaAndZone(String zoneName) {
+        CatalogSchemaDescriptor schemaMock = Mockito.mock(CatalogSchemaDescriptor.class);
+        CatalogZoneDescriptor zoneMock = Mockito.mock(CatalogZoneDescriptor.class);
+        Mockito.when(zoneMock.storageProfiles()).thenReturn(fromParams(parseStorageProfiles("default")));
+        Mockito.when(zoneMock.id()).thenReturn(TEST_ZONE_ID);
+        Mockito.when(catalog.schema("PUBLIC")).thenReturn(schemaMock);
+        Mockito.when(catalog.defaultZone()).thenReturn(zoneMock);
+        Mockito.when(catalog.zone(zoneName)).thenReturn(zoneMock);
+    }
+
+    /** Checks that there are no ID duplicates. */
+    private static void checkDuplicates(Set<String> set0, Set<String> set1) {
+        for (String id : set1) {
+            if (set0.contains(id)) {
+                throw new IllegalStateException("Duplicate id: " + id);
+            }
+        }
+    }
 }
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DistributionZoneSqlToCommandConverterTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DistributionZoneSqlToCommandConverterTest.java
index f53f391..dc52879 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DistributionZoneSqlToCommandConverterTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DistributionZoneSqlToCommandConverterTest.java
@@ -25,7 +25,9 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasSize;
 import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.mock;
 
 import java.util.Arrays;
 import java.util.List;
@@ -33,6 +35,17 @@
 import org.apache.calcite.sql.SqlDdl;
 import org.apache.calcite.sql.SqlNode;
 import org.apache.calcite.sql.parser.SqlParseException;
+import org.apache.ignite.internal.catalog.CatalogCommand;
+import org.apache.ignite.internal.catalog.commands.AlterZoneCommand;
+import org.apache.ignite.internal.catalog.commands.AlterZoneSetDefaultCommand;
+import org.apache.ignite.internal.catalog.commands.DropZoneCommand;
+import org.apache.ignite.internal.catalog.commands.RenameZoneCommand;
+import org.apache.ignite.internal.catalog.descriptors.CatalogStorageProfileDescriptor;
+import org.apache.ignite.internal.catalog.descriptors.CatalogZoneDescriptor;
+import org.apache.ignite.internal.catalog.storage.AlterZoneEntry;
+import org.apache.ignite.internal.catalog.storage.DropZoneEntry;
+import org.apache.ignite.internal.catalog.storage.NewZoneEntry;
+import org.apache.ignite.internal.catalog.storage.SetDefaultZoneEntry;
 import org.apache.ignite.lang.ErrorGroups.Sql;
 import org.apache.ignite.lang.IgniteException;
 import org.hamcrest.Matchers;
@@ -40,6 +53,7 @@
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.MethodSource;
 import org.junit.jupiter.params.provider.ValueSource;
+import org.mockito.Mockito;
 
 /**
  * Tests the conversion of a sql zone definition to a command.
@@ -61,52 +75,64 @@
 
         assertThat(node, instanceOf(SqlDdl.class));
 
-        DdlCommand cmd = converter.convert((SqlDdl) node, createContext());
+        CatalogCommand cmd = converter.convert((SqlDdl) node, createContext());
 
-        assertThat(cmd, Matchers.instanceOf(CreateZoneCommand.class));
+        NewZoneEntry newZoneEntry = invokeAndGetFirstEntry(cmd, NewZoneEntry.class);
 
-        CreateZoneCommand zoneCmd = (CreateZoneCommand) cmd;
-
-        assertThat(zoneCmd.zoneName(), equalTo("TEST"));
+        assertThat(newZoneEntry.descriptor().name(), equalTo("TEST"));
     }
 
     @Test
     public void testCreateZoneWithOptions() throws SqlParseException {
-        SqlNode node = parse("CREATE ZONE test with "
-                + "partitions=2, "
-                + "replicas=3, "
-                + "affinity_function='rendezvous', "
-                + "data_nodes_filter='\"attr1\" && \"attr2\"', "
-                + "data_nodes_auto_adjust_scale_up=100, "
-                + "data_nodes_auto_adjust_scale_down=200, "
-                + "data_nodes_auto_adjust=300, "
-                + "storage_profiles='lru_rocks, segmented_aipersist' "
-        );
+        // Check non-conflicting options.
+        {
+            SqlNode node = parse("CREATE ZONE test with "
+                    + "partitions=2, "
+                    + "replicas=3, "
+                    + "affinity_function='rendezvous', "
+                    + "data_nodes_filter='$[?(@.region == \"US\")]', "
+                    + "data_nodes_auto_adjust=300, "
+                    + "storage_profiles='lru_rocks, segmented_aipersist' "
+            );
 
-        assertThat(node, instanceOf(SqlDdl.class));
+            assertThat(node, instanceOf(SqlDdl.class));
 
-        DdlCommand cmd = converter.convert((SqlDdl) node, createContext());
+            CatalogCommand cmd = converter.convert((SqlDdl) node, createContext());
+            CatalogZoneDescriptor desc = invokeAndGetFirstEntry(cmd, NewZoneEntry.class).descriptor();
 
-        CreateZoneCommand createZone = (CreateZoneCommand) cmd;
+            assertThat(desc.partitions(), equalTo(2));
+            assertThat(desc.replicas(), equalTo(3));
+            // TODO https://issues.apache.org/jira/browse/IGNITE-22162
+            // assertThat(desc.affinity(), equalTo("rendezvous"));
+            assertThat(desc.filter(), equalTo("$[?(@.region == \"US\")]"));
+            assertThat(desc.dataNodesAutoAdjust(), equalTo(300));
 
-        assertThat(createZone.partitions(), equalTo(2));
-        assertThat(createZone.replicas(), equalTo(3));
-        assertThat(createZone.affinity(), equalTo("rendezvous"));
-        assertThat(createZone.nodeFilter(), equalTo("\"attr1\" && \"attr2\""));
-        assertThat(createZone.dataNodesAutoAdjustScaleUp(), equalTo(100));
-        assertThat(createZone.dataNodesAutoAdjustScaleDown(), equalTo(200));
-        assertThat(createZone.dataNodesAutoAdjust(), equalTo(300));
-        assertThat(createZone.storageProfiles(), equalTo("lru_rocks, segmented_aipersist"));
+            List<CatalogStorageProfileDescriptor> storageProfiles = desc.storageProfiles().profiles();
+            assertThat(storageProfiles, hasSize(2));
+            assertThat(storageProfiles.get(0).storageProfile(), equalTo("lru_rocks"));
+            assertThat(storageProfiles.get(1).storageProfile(), equalTo("segmented_aipersist"));
+        }
+
+        // Check remaining options.
+        {
+            SqlNode node = parse("CREATE ZONE test with "
+                    + "data_nodes_auto_adjust_scale_up=100, "
+                    + "data_nodes_auto_adjust_scale_down=200, "
+                    + "storage_profiles='lru_rocks'");
+
+            assertThat(node, instanceOf(SqlDdl.class));
+
+            CatalogCommand cmd = converter.convert((SqlDdl) node, createContext());
+            CatalogZoneDescriptor desc = invokeAndGetFirstEntry(cmd, NewZoneEntry.class).descriptor();
+
+            assertThat(desc.dataNodesAutoAdjustScaleUp(), equalTo(100));
+            assertThat(desc.dataNodesAutoAdjustScaleDown(), equalTo(200));
+        }
 
         // Check option validation.
-        node = parse("CREATE ZONE test with partitions=-1");
-        expectOptionValidationError((SqlDdl) node, "PARTITION");
-
-        node = parse("CREATE ZONE test with replicas=-1");
-        expectOptionValidationError((SqlDdl) node, "REPLICAS");
-
-        node = parse("CREATE ZONE test with storage_profiles='' ");
-        expectOptionValidationError((SqlDdl) node, "STORAGE_PROFILES");
+        expectOptionValidationError("CREATE ZONE test with partitions=-1", "PARTITION");
+        expectOptionValidationError("CREATE ZONE test with replicas=-1", "REPLICAS");
+        expectOptionValidationError("CREATE ZONE test with storage_profiles='' ", "STORAGE_PROFILES");
     }
 
     @Test
@@ -122,7 +148,7 @@
 
         assertThat(ex.getMessage(), containsString(STORAGE_PROFILES + " option cannot be null"));
 
-        SqlNode newNode =  parse("CREATE ZONE test with replicas=1");
+        SqlNode newNode = parse("CREATE ZONE test with replicas=1");
 
         assertThat(newNode, instanceOf(SqlDdl.class));
 
@@ -147,25 +173,32 @@
     public void testRenameZoneCommand() throws SqlParseException {
         SqlNode node = parse("ALTER ZONE test RENAME TO test2");
 
-        DdlCommand cmd = converter.convert((SqlDdl) node, createContext());
-        assertThat(cmd, Matchers.instanceOf(AlterZoneRenameCommand.class));
+        CatalogCommand cmd = converter.convert((SqlDdl) node, createContext());
 
-        AlterZoneRenameCommand zoneCmd = (AlterZoneRenameCommand) cmd;
-        assertThat(zoneCmd.zoneName(), equalTo("TEST"));
-        assertThat(zoneCmd.newZoneName(), equalTo("TEST2"));
-        assertThat(zoneCmd.ifExists(), is(false));
+        assertThat(cmd, instanceOf(RenameZoneCommand.class));
+
+        Mockito.when(catalog.zone("TEST")).thenReturn(mock(CatalogZoneDescriptor.class));
+
+        AlterZoneEntry entry = invokeAndGetFirstEntry(cmd, AlterZoneEntry.class);
+
+        assertThat(entry.descriptor().name(), equalTo("TEST2"));
+        assertThat(((RenameZoneCommand) cmd).ifExists(), is(false));
     }
 
     @Test
     public void testRenameZoneIfExistCommand() throws SqlParseException {
         SqlNode node = parse("ALTER ZONE IF EXISTS test RENAME TO test2");
 
-        DdlCommand cmd = converter.convert((SqlDdl) node, createContext());
-        assertThat(cmd, Matchers.instanceOf(AlterZoneRenameCommand.class));
+        CatalogCommand cmd = converter.convert((SqlDdl) node, createContext());
+        assertThat(cmd, Matchers.instanceOf(RenameZoneCommand.class));
 
-        AlterZoneRenameCommand zoneCmd = (AlterZoneRenameCommand) cmd;
-        assertThat(zoneCmd.zoneName(), equalTo("TEST"));
-        assertThat(zoneCmd.newZoneName(), equalTo("TEST2"));
+        RenameZoneCommand zoneCmd = (RenameZoneCommand) cmd;
+
+        Mockito.when(catalog.zone("TEST")).thenReturn(mock(CatalogZoneDescriptor.class));
+
+        AlterZoneEntry entry = invokeAndGetFirstEntry(cmd, AlterZoneEntry.class);
+
+        assertThat(entry.descriptor().name(), equalTo("TEST2"));
         assertThat(zoneCmd.ifExists(), is(true));
     }
 
@@ -173,59 +206,100 @@
     public void testAlterZoneCommand() throws SqlParseException {
         SqlNode node = parse("ALTER ZONE test SET replicas=3");
 
-        DdlCommand cmd = converter.convert((SqlDdl) node, createContext());
-        assertThat(cmd, Matchers.instanceOf(AlterZoneSetCommand.class));
+        CatalogCommand cmd = converter.convert((SqlDdl) node, createContext());
+        assertThat(cmd, Matchers.instanceOf(AlterZoneCommand.class));
 
-        AlterZoneSetCommand zoneCmd = (AlterZoneSetCommand) cmd;
-        assertThat(zoneCmd.zoneName(), equalTo("TEST"));
-        assertThat(zoneCmd.ifExists(), is(false));
+        CatalogZoneDescriptor zoneMock = mock(CatalogZoneDescriptor.class);
+
+        Mockito.when(zoneMock.name()).thenReturn("TEST");
+        Mockito.when(zoneMock.filter()).thenReturn("");
+
+        Mockito.when(catalog.zone("TEST")).thenReturn(zoneMock);
+
+        CatalogZoneDescriptor desc = invokeAndGetFirstEntry(cmd, AlterZoneEntry.class).descriptor();
+
+        assertThat(desc.name(), equalTo("TEST"));
+        assertThat(desc.replicas(), is(3));
+        assertThat(((AlterZoneCommand) cmd).ifExists(), is(false));
     }
 
     @Test
     public void testAlterZoneIfExistsCommand() throws SqlParseException {
         SqlNode node = parse("ALTER ZONE IF EXISTS test SET replicas=3");
 
-        DdlCommand cmd = converter.convert((SqlDdl) node, createContext());
-        assertThat(cmd, Matchers.instanceOf(AlterZoneSetCommand.class));
+        CatalogCommand cmd = converter.convert((SqlDdl) node, createContext());
 
-        AlterZoneSetCommand zoneCmd = (AlterZoneSetCommand) cmd;
-        assertThat(zoneCmd.zoneName(), equalTo("TEST"));
-        assertThat(zoneCmd.ifExists(), is(true));
+        assertThat(cmd, Matchers.instanceOf(AlterZoneCommand.class));
+        assertThat(((AlterZoneCommand) cmd).ifExists(), is(true));
     }
 
     @Test
     public void testAlterZoneSetCommand() throws SqlParseException {
-        SqlNode node = parse("ALTER ZONE test SET "
-                + "replicas=3, "
-                + "partitions=8, "
-                + "data_nodes_filter='\"attr1\" && \"attr2\"', "
-                + "data_nodes_auto_adjust_scale_up=100, "
-                + "data_nodes_auto_adjust_scale_down=200, "
-                + "data_nodes_auto_adjust=300");
+        // Check non-conflicting options.
+        {
+            SqlNode node = parse("ALTER ZONE test SET "
+                    + "replicas=3, "
+                    + "partitions=8, "
+                    + "data_nodes_filter='$[?(@.region == \"US\")]', "
+                    + "data_nodes_auto_adjust=300");
 
-        DdlCommand cmd = converter.convert((SqlDdl) node, createContext());
-        assertThat(cmd, Matchers.instanceOf(AlterZoneSetCommand.class));
+            CatalogCommand cmd = converter.convert((SqlDdl) node, createContext());
+            assertThat(cmd, Matchers.instanceOf(AlterZoneCommand.class));
 
-        AlterZoneSetCommand zoneCmd = (AlterZoneSetCommand) cmd;
-        assertThat(zoneCmd.zoneName(), equalTo("TEST"));
+            CatalogZoneDescriptor zoneMock = mock(CatalogZoneDescriptor.class);
+            Mockito.when(zoneMock.name()).thenReturn("TEST");
+            Mockito.when(zoneMock.filter()).thenReturn("");
 
-        assertThat(zoneCmd.replicas(), equalTo(3));
-        assertThat(zoneCmd.partitions(), equalTo(8));
-        assertThat(zoneCmd.nodeFilter(), equalTo("\"attr1\" && \"attr2\""));
-        assertThat(zoneCmd.dataNodesAutoAdjustScaleUp(), equalTo(100));
-        assertThat(zoneCmd.dataNodesAutoAdjustScaleDown(), equalTo(200));
-        assertThat(zoneCmd.dataNodesAutoAdjust(), equalTo(300));
+            Mockito.when(catalog.zone("TEST")).thenReturn(zoneMock);
+
+            CatalogZoneDescriptor desc = invokeAndGetFirstEntry(cmd, AlterZoneEntry.class).descriptor();
+
+            assertThat(desc.name(), equalTo("TEST"));
+
+            assertThat(desc.replicas(), equalTo(3));
+            assertThat(desc.partitions(), equalTo(8));
+            assertThat(desc.filter(), equalTo("$[?(@.region == \"US\")]"));
+            assertThat(desc.dataNodesAutoAdjust(), equalTo(300));
+        }
+
+        // Check remaining options.
+        {
+            SqlNode node = parse("ALTER ZONE test SET "
+                    + "data_nodes_auto_adjust_scale_up=100, "
+                    + "data_nodes_auto_adjust_scale_down=200");
+
+            CatalogCommand cmd = converter.convert((SqlDdl) node, createContext());
+            assertThat(cmd, Matchers.instanceOf(AlterZoneCommand.class));
+
+            CatalogZoneDescriptor zoneMock = mock(CatalogZoneDescriptor.class);
+            Mockito.when(zoneMock.name()).thenReturn("TEST");
+            Mockito.when(zoneMock.filter()).thenReturn("");
+
+            Mockito.when(catalog.zone("TEST")).thenReturn(zoneMock);
+
+            CatalogZoneDescriptor desc = invokeAndGetFirstEntry(cmd, AlterZoneEntry.class).descriptor();
+
+            assertThat(desc.name(), equalTo("TEST"));
+
+            assertThat(desc.dataNodesAutoAdjustScaleUp(), equalTo(100));
+            assertThat(desc.dataNodesAutoAdjustScaleDown(), equalTo(200));
+        }
     }
 
     @Test
     public void testAlterZoneSetDefault() throws SqlParseException {
         SqlNode node = parse("ALTER ZONE test SET DEFAULT");
 
-        DdlCommand cmd = converter.convert((SqlDdl) node, createContext());
+        CatalogCommand cmd = converter.convert((SqlDdl) node, createContext());
         assertThat(cmd, Matchers.instanceOf(AlterZoneSetDefaultCommand.class));
 
+        CatalogZoneDescriptor zoneMock = mock(CatalogZoneDescriptor.class);
+        Mockito.when(catalog.zone("TEST")).thenReturn(zoneMock);
+
+        SetDefaultZoneEntry entry = invokeAndGetFirstEntry(cmd, SetDefaultZoneEntry.class);
+
         AlterZoneSetDefaultCommand zoneCmd = (AlterZoneSetDefaultCommand) cmd;
-        assertThat(zoneCmd.zoneName(), equalTo("TEST"));
+        assertThat(entry.zoneId(), is(zoneMock.id()));
         assertThat(zoneCmd.ifExists(), is(false));
     }
 
@@ -233,21 +307,15 @@
     public void testAlterZoneSetDefaultIfExists() throws SqlParseException {
         SqlNode node = parse("ALTER ZONE IF EXISTS test SET DEFAULT");
 
-        DdlCommand cmd = converter.convert((SqlDdl) node, createContext());
+        CatalogCommand cmd = converter.convert((SqlDdl) node, createContext());
         assertThat(cmd, Matchers.instanceOf(AlterZoneSetDefaultCommand.class));
 
-        AlterZoneSetDefaultCommand zoneCmd = (AlterZoneSetDefaultCommand) cmd;
-        assertThat(zoneCmd.zoneName(), equalTo("TEST"));
-        assertThat(zoneCmd.ifExists(), is(true));
+        assertThat(((AlterZoneSetDefaultCommand) cmd).ifExists(), is(true));
     }
 
     @Test
     public void testAlterZoneCommandWithInvalidOptions() throws SqlParseException {
-        SqlNode node = parse("ALTER ZONE test SET replicas=2, data_nodes_auto_adjust=-100");
-
-        assertThat(node, instanceOf(SqlDdl.class));
-
-        expectOptionValidationError((SqlDdl) node, "DATA_NODES_AUTO_ADJUST");
+        expectOptionValidationError("ALTER ZONE test SET replicas=2, data_nodes_auto_adjust=-100", "DATA_NODES_AUTO_ADJUST");
     }
 
     @Test
@@ -265,13 +333,16 @@
 
         assertThat(node, instanceOf(SqlDdl.class));
 
-        DdlCommand cmd = converter.convert((SqlDdl) node, createContext());
+        CatalogCommand cmd = converter.convert((SqlDdl) node, createContext());
 
         assertThat(cmd, Matchers.instanceOf(DropZoneCommand.class));
 
-        DropZoneCommand zoneCmd = (DropZoneCommand) cmd;
+        CatalogZoneDescriptor zoneMock = mock(CatalogZoneDescriptor.class);
+        Mockito.when(catalog.zone("TEST")).thenReturn(zoneMock);
 
-        assertThat(zoneCmd.zoneName(), equalTo("TEST"));
+        DropZoneEntry entry = invokeAndGetFirstEntry(cmd, DropZoneEntry.class);
+
+        assertThat(entry.zoneId(), is(zoneMock.id()));
     }
 
     @ParameterizedTest
@@ -322,7 +393,8 @@
         return STRING_OPTIONS.stream();
     }
 
-    private void expectOptionValidationError(SqlDdl node, String invalidOption) {
+    private void expectOptionValidationError(String sql, String invalidOption) throws SqlParseException {
+        SqlDdl node = (SqlDdl) parse(sql);
         IgniteException ex = assertThrows(
                 IgniteException.class,
                 () -> converter.convert(node, createContext())
diff --git a/modules/sql-engine/src/testFixtures/java/org/apache/ignite/internal/sql/engine/util/NativeTypeValues.java b/modules/sql-engine/src/testFixtures/java/org/apache/ignite/internal/sql/engine/util/NativeTypeValues.java
index b595e2b..02072d9 100644
--- a/modules/sql-engine/src/testFixtures/java/org/apache/ignite/internal/sql/engine/util/NativeTypeValues.java
+++ b/modules/sql-engine/src/testFixtures/java/org/apache/ignite/internal/sql/engine/util/NativeTypeValues.java
@@ -120,9 +120,6 @@
     /** Returns a value of a {@link NativeType native type} that corresponds to the given {@link RelDataType}. */
     @Nullable
     public static Object value(int i, RelDataType type) {
-        ColumnType columnType = TypeUtils.columnType(type);
-
-        assert columnType != null : "Returned a null column type for " + type;
-        return value(i, columnType);
+        return value(i, TypeUtils.columnType(type));
     }
 }