This is an automated email from the ASF dual-hosted git repository.

amashenkov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new 632a875007 IGNITE-19592 Implement ADD/DROP COLUMN DDL commands with 
using Catalog (#2119)
632a875007 is described below

commit 632a8750075af173a9e4ee0a22599b695752ce48
Author: Andrew V. Mashenkov <[email protected]>
AuthorDate: Thu Jun 1 10:21:17 2023 +0300

    IGNITE-19592 Implement ADD/DROP COLUMN DDL commands with using Catalog 
(#2119)
---
 .../internal/catalog/CatalogServiceImpl.java       | 151 ++++++++-
 .../commands/AlterTableAddColumnParams.java        |  27 +-
 .../commands/AlterTableDropColumnParams.java       |  29 +-
 .../internal/catalog/commands/CatalogUtils.java    |   8 +-
 .../internal/catalog/commands/ColumnParams.java    |  96 +++++-
 .../catalog/descriptors/HashIndexDescriptor.java   |  11 +-
 .../catalog/descriptors/IndexDescriptor.java       |   8 +
 .../catalog/descriptors/SortedIndexDescriptor.java |   6 +
 .../catalog/descriptors/TableDescriptor.java       |  44 ++-
 .../AddColumnEventParameters.java}                 |  50 ++-
 .../internal/catalog/events/CatalogEvent.java      |   5 +-
 .../DropColumnEventParameters.java}                |  49 ++-
 .../DropColumnsEntry.java}                         |  40 ++-
 .../NewColumnsEntry.java}                          |  41 ++-
 .../internal/catalog/CatalogServiceSelfTest.java   | 355 +++++++++++++++++++--
 .../runner/app/ItSchemaChangeTableViewTest.java    |   3 +
 .../internal/sql/api/ItSqlAsynchronousApiTest.java |   4 +-
 .../internal/sql/api/ItSqlSynchronousApiTest.java  |   4 +-
 .../internal/sql/engine/ItMixedQueriesTest.java    |   8 -
 .../internal/sql/internal/InternalSchemaTest.java  |   2 +
 .../engine/exec/ddl/DdlCommandHandlerWrapper.java  |  16 +
 .../exec/ddl/DdlToCatalogCommandConverter.java     |  38 ++-
 22 files changed, 770 insertions(+), 225 deletions(-)

diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogServiceImpl.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogServiceImpl.java
index 027a74484c..1cadf55516 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogServiceImpl.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogServiceImpl.java
@@ -27,21 +27,29 @@ import java.util.List;
 import java.util.Map.Entry;
 import java.util.NavigableMap;
 import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ConcurrentSkipListMap;
+import java.util.stream.Collectors;
 import org.apache.ignite.internal.catalog.commands.AlterTableAddColumnParams;
 import org.apache.ignite.internal.catalog.commands.AlterTableDropColumnParams;
 import org.apache.ignite.internal.catalog.commands.CatalogUtils;
+import org.apache.ignite.internal.catalog.commands.ColumnParams;
 import org.apache.ignite.internal.catalog.commands.CreateTableParams;
 import org.apache.ignite.internal.catalog.commands.DropTableParams;
 import org.apache.ignite.internal.catalog.descriptors.IndexDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.SchemaDescriptor;
+import org.apache.ignite.internal.catalog.descriptors.TableColumnDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.TableDescriptor;
+import org.apache.ignite.internal.catalog.events.AddColumnEventParameters;
 import org.apache.ignite.internal.catalog.events.CatalogEvent;
 import org.apache.ignite.internal.catalog.events.CatalogEventParameters;
 import org.apache.ignite.internal.catalog.events.CreateTableEventParameters;
+import org.apache.ignite.internal.catalog.events.DropColumnEventParameters;
 import org.apache.ignite.internal.catalog.events.DropTableEventParameters;
+import org.apache.ignite.internal.catalog.storage.DropColumnsEntry;
 import org.apache.ignite.internal.catalog.storage.DropTableEntry;
+import org.apache.ignite.internal.catalog.storage.NewColumnsEntry;
 import org.apache.ignite.internal.catalog.storage.NewTableEntry;
 import org.apache.ignite.internal.catalog.storage.ObjectIdGenUpdateEntry;
 import org.apache.ignite.internal.catalog.storage.UpdateEntry;
@@ -52,16 +60,20 @@ import org.apache.ignite.internal.logger.IgniteLogger;
 import org.apache.ignite.internal.logger.Loggers;
 import org.apache.ignite.internal.manager.Producer;
 import org.apache.ignite.internal.util.ArrayUtils;
+import org.apache.ignite.internal.util.CollectionUtils;
 import org.apache.ignite.internal.util.PendingComparableValuesTracker;
+import org.apache.ignite.lang.ColumnAlreadyExistsException;
+import org.apache.ignite.lang.ColumnNotFoundException;
 import org.apache.ignite.lang.ErrorGroups.Common;
+import org.apache.ignite.lang.ErrorGroups.Sql;
 import org.apache.ignite.lang.IgniteInternalException;
 import org.apache.ignite.lang.TableAlreadyExistsException;
 import org.apache.ignite.lang.TableNotFoundException;
+import org.apache.ignite.sql.SqlException;
 import org.jetbrains.annotations.Nullable;
 
 /**
  * Catalog service implementation.
- * TODO: IGNITE-19081 Introduce catalog events and make CatalogServiceImpl 
extends Producer.
  */
 public class CatalogServiceImpl extends Producer<CatalogEvent, 
CatalogEventParameters> implements CatalogManager {
     private static final int MAX_RETRY_COUNT = 10;
@@ -205,13 +217,84 @@ public class CatalogServiceImpl extends 
Producer<CatalogEvent, CatalogEventParam
     /** {@inheritDoc} */
     @Override
     public CompletableFuture<Void> addColumn(AlterTableAddColumnParams params) 
{
-        return failedFuture(new UnsupportedOperationException("Not implemented 
yet."));
+        if (params.columns().isEmpty()) {
+            return completedFuture(null);
+        }
+
+        return saveUpdate(catalog -> {
+            String schemaName = 
Objects.requireNonNullElse(params.schemaName(), CatalogService.PUBLIC);
+
+            SchemaDescriptor schema = 
Objects.requireNonNull(catalog.schema(schemaName), "No schema found: " + 
schemaName);
+
+            TableDescriptor table = schema.table(params.tableName());
+
+            if (table == null) {
+                throw new TableNotFoundException(schemaName, 
params.tableName());
+            }
+
+            List<TableColumnDescriptor> columnDescriptors = new ArrayList<>();
+
+            for (ColumnParams col : params.columns()) {
+                if (table.column(col.name()) != null) {
+                    throw new ColumnAlreadyExistsException(col.name());
+                }
+
+                columnDescriptors.add(CatalogUtils.fromParams(col));
+            }
+
+            return List.of(
+                    new NewColumnsEntry(table.id(), columnDescriptors)
+            );
+        });
     }
 
     /** {@inheritDoc} */
     @Override
     public CompletableFuture<Void> dropColumn(AlterTableDropColumnParams 
params) {
-        return failedFuture(new UnsupportedOperationException("Not implemented 
yet."));
+        if (params.columns().isEmpty()) {
+            return completedFuture(null);
+        }
+
+        return saveUpdate(catalog -> {
+            String schemaName = 
Objects.requireNonNullElse(params.schemaName(), CatalogService.PUBLIC);
+
+            SchemaDescriptor schema = 
Objects.requireNonNull(catalog.schema(schemaName), "No schema found: " + 
schemaName);
+
+            TableDescriptor table = schema.table(params.tableName());
+
+            if (table == null) {
+                throw new TableNotFoundException(schemaName, 
params.tableName());
+            }
+
+            for (String columnName : params.columns()) {
+                if (table.column(columnName) == null) {
+                    throw new ColumnNotFoundException(columnName);
+                }
+                if (table.isPrimaryKeyColumn(columnName)) {
+                    throw new SqlException(
+                            Sql.DROP_IDX_COLUMN_CONSTRAINT_ERR,
+                            "Can't drop primary key column: column=" + 
columnName
+                    );
+                }
+            }
+
+            Arrays.stream(schema.indexes())
+                    .filter(index -> index.tableId() == table.id())
+                    .forEach(index -> params.columns().stream()
+                            .filter(index::hasColumn)
+                            .findAny()
+                            .ifPresent(columnName -> {
+                                throw new SqlException(
+                                        Sql.DROP_IDX_COLUMN_CONSTRAINT_ERR,
+                                        "Can't drop indexed column: 
columnName=" + columnName + ", indexName="
+                                        + index.name()
+                                );
+                            }));
+
+            return List.of(
+                    new DropColumnsEntry(table.id(), params.columns())
+            );
+        });
     }
 
     private void registerCatalog(Catalog newCatalog) {
@@ -307,7 +390,69 @@ public class CatalogServiceImpl extends 
Producer<CatalogEvent, CatalogEventParam
                             CatalogEvent.TABLE_DROP,
                             new DropTableEventParameters(version, tableId)
                     ));
+                } else if (entry instanceof NewColumnsEntry) {
+                    int tableId = ((NewColumnsEntry) entry).tableId();
+                    List<TableColumnDescriptor> columnDescriptors = 
((NewColumnsEntry) entry).descriptors();
 
+                    catalog = new Catalog(
+                            version,
+                            System.currentTimeMillis(),
+                            catalog.objectIdGenState(),
+                            new SchemaDescriptor(
+                                    schema.id(),
+                                    schema.name(),
+                                    version,
+                                    Arrays.stream(schema.tables())
+                                            .map(table -> table.id() != tableId
+                                                    ? table
+                                                    : new TableDescriptor(
+                                                            table.id(),
+                                                            table.name(),
+                                                            
CollectionUtils.concat(table.columns(), columnDescriptors),
+                                                            
table.primaryKeyColumns(),
+                                                            
table.colocationColumns())
+                                            )
+                                            .toArray(TableDescriptor[]::new),
+                                    schema.indexes()
+                            )
+                    );
+
+                    eventFutures.add(fireEvent(
+                            CatalogEvent.TABLE_ALTER,
+                            new AddColumnEventParameters(version, tableId, 
columnDescriptors)
+                    ));
+                } else if (entry instanceof DropColumnsEntry) {
+                    int tableId = ((DropColumnsEntry) entry).tableId();
+                    Set<String> columns = ((DropColumnsEntry) entry).columns();
+
+                    catalog = new Catalog(
+                            version,
+                            System.currentTimeMillis(),
+                            catalog.objectIdGenState(),
+                            new SchemaDescriptor(
+                                    schema.id(),
+                                    schema.name(),
+                                    version,
+                                    Arrays.stream(schema.tables())
+                                            .map(table -> table.id() != tableId
+                                                    ? table
+                                                    : new TableDescriptor(
+                                                            table.id(),
+                                                            table.name(),
+                                                            
table.columns().stream().filter(col -> !columns.contains(col.name()))
+                                                                    
.collect(Collectors.toList()),
+                                                            
table.primaryKeyColumns(),
+                                                            
table.colocationColumns())
+                                            )
+                                            .toArray(TableDescriptor[]::new),
+                                    schema.indexes()
+                            )
+                    );
+
+                    eventFutures.add(fireEvent(
+                            CatalogEvent.TABLE_ALTER,
+                            new DropColumnEventParameters(version, tableId, 
columns)
+                    ));
                 } else if (entry instanceof ObjectIdGenUpdateEntry) {
                     catalog = new Catalog(
                             version,
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterTableAddColumnParams.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterTableAddColumnParams.java
index 61a7c9a8e3..a2cf790a86 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterTableAddColumnParams.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterTableAddColumnParams.java
@@ -23,27 +23,19 @@ import java.util.List;
  * ALTER TABLE ... ADD COLUMN statement.
  */
 public class AlterTableAddColumnParams extends AbstractTableCommandParams {
+    /** Creates parameters builder. */
     public static Builder builder() {
         return new Builder();
     }
 
-    /** Quietly ignore this command if column already exists. */
-    private boolean ifColumnNotExists;
-
     /** Columns. */
     private List<ColumnParams> cols;
 
-    public List<ColumnParams> columns() {
-        return cols;
-    }
-
     /**
-     * Not exists flag.
-     *
-     * @return Quietly ignore this command if column exists.
+     * Gets columns that should be added to a table.
      */
-    public boolean ifColumnNotExists() {
-        return ifColumnNotExists;
+    public List<ColumnParams> columns() {
+        return cols;
     }
 
     /**
@@ -64,16 +56,5 @@ public class AlterTableAddColumnParams extends 
AbstractTableCommandParams {
             params.cols = cols;
             return this;
         }
-
-        /**
-         * Set exists flag.
-         *
-         * @param ifColumnNotExists Quietly ignore this command if column 
exists.
-         * @return {@code this}.
-         */
-        public Builder ifColumnNotExists(boolean ifColumnNotExists) {
-            params.ifColumnNotExists = ifColumnNotExists;
-            return this;
-        }
     }
 }
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterTableDropColumnParams.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterTableDropColumnParams.java
index 379c54d215..243e2b93e8 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterTableDropColumnParams.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/AlterTableDropColumnParams.java
@@ -25,23 +25,19 @@ import java.util.Set;
  */
 @SuppressWarnings("AssignmentOrReturnOfFieldWithMutableType")
 public class AlterTableDropColumnParams extends AbstractTableCommandParams {
-    /** Quietly ignore this command if column is not exist. */
-    private boolean ifColumnExists;
+    /** Creates parameters builder. */
+    public static Builder builder() {
+        return new Builder();
+    }
 
     /** Columns. */
     private Set<String> cols;
 
-    public Set<String> columns() {
-        return Collections.unmodifiableSet(cols);
-    }
-
     /**
-     * Exists flag.
-     *
-     * @return Quietly ignore this command if column is not exist.
+     * Gets columns that should be dropped from a table.
      */
-    public boolean ifColumnExists() {
-        return ifColumnExists;
+    public Set<String> columns() {
+        return Collections.unmodifiableSet(cols);
     }
 
     /**
@@ -62,16 +58,5 @@ public class AlterTableDropColumnParams extends 
AbstractTableCommandParams {
             params.cols = cols;
             return this;
         }
-
-        /**
-         * Set exists flag.
-         *
-         * @param ifColumnExists Quietly ignore this command if column is not 
exist.
-         * @return {@code this}.
-         */
-        public Builder ifColumnExists(boolean ifColumnExists) {
-            params.ifColumnExists = ifColumnExists;
-            return this;
-        }
     }
 }
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CatalogUtils.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CatalogUtils.java
index 1ead944291..d462be6137 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CatalogUtils.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CatalogUtils.java
@@ -41,7 +41,13 @@ public class CatalogUtils {
         );
     }
 
-    private static TableColumnDescriptor fromParams(ColumnParams params) {
+    /**
+     * Converts AlterTableAdd command columns parameters to column descriptor.
+     *
+     * @param params Parameters.
+     * @return Column descriptor.
+     */
+    public static TableColumnDescriptor fromParams(ColumnParams params) {
         return new TableColumnDescriptor(params.name(), params.type(), 
params.nullable(), params.defaultValueDefinition());
     }
 }
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/ColumnParams.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/ColumnParams.java
index d8cf657e35..f69c44ab56 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/ColumnParams.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/ColumnParams.java
@@ -17,29 +17,26 @@
 
 package org.apache.ignite.internal.catalog.commands;
 
-import java.io.Serializable;
-import java.util.Objects;
 import org.apache.ignite.sql.ColumnType;
 
 /** Defines a particular column within table. */
-public class ColumnParams implements Serializable {
-    private static final long serialVersionUID = 5602599481844743521L;
-
-    private final String name;
+public class ColumnParams {
+    /** Creates parameters builder. */
+    public static Builder builder() {
+        return new Builder();
+    }
 
-    private final ColumnType type;
+    /** Column name. */
+    private String name;
 
-    private final boolean nullable;
+    /** Column type. */
+    private ColumnType type;
 
-    private final DefaultValue defaultValueDefinition;
+    /** Nullability flag. */
+    private boolean nullable;
 
-    /** Creates a column definition. */
-    public ColumnParams(String name, ColumnType type, DefaultValue 
defaultValueDefinition, boolean nullable) {
-        this.name = Objects.requireNonNull(name, "name");
-        this.type = Objects.requireNonNull(type, "type");
-        this.defaultValueDefinition = 
Objects.requireNonNull(defaultValueDefinition, "defaultValueDefinition");
-        this.nullable = nullable;
-    }
+    /** Column default value. */
+    private DefaultValue defaultValueDefinition = DefaultValue.constant(null);
 
     /**
      * Get column's name.
@@ -61,7 +58,6 @@ public class ColumnParams implements Serializable {
      * @param <T> Desired subtype of the definition.
      * @return Default value definition.
      */
-    @SuppressWarnings("unchecked")
     public <T extends DefaultValue> T defaultValueDefinition() {
         return (T) defaultValueDefinition;
     }
@@ -86,4 +82,70 @@ public class ColumnParams implements Serializable {
     public Integer scale() {
         return null;
     }
+
+    /** Parameters builder. */
+    public static class Builder {
+        private ColumnParams params;
+
+        private Builder() {
+            params = new ColumnParams();
+        }
+
+        /**
+         * Set column simple name.
+         *
+         * @param name Column simple name.
+         * @return {@code this}.
+         */
+        public Builder name(String name) {
+            params.name = name;
+
+            return this;
+        }
+
+        /**
+         * Set column type.
+         *
+         * @param type Column type.
+         * @return {@code this}.
+         */
+        public Builder type(ColumnType type) {
+            params.type = type;
+
+            return this;
+        }
+
+        /**
+         * Marks column as nullable.
+         *
+         * @return {@code this}.
+         */
+        public Builder nullable(boolean nullable) {
+            params.nullable = nullable;
+
+            return this;
+        }
+
+        /**
+         * Sets column default value.
+         *
+         * @return {@code this}.
+         */
+        public Builder defaultValue(DefaultValue defaultValue) {
+            params.defaultValueDefinition = defaultValue;
+
+            return this;
+        }
+
+        /**
+         * Builds parameters.
+         *
+         * @return Parameters.
+         */
+        public ColumnParams build() {
+            ColumnParams params0 = params;
+            params = null;
+            return params0;
+        }
+    }
 }
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/HashIndexDescriptor.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/HashIndexDescriptor.java
index 8e2d96c3da..e741e10ac4 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/HashIndexDescriptor.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/HashIndexDescriptor.java
@@ -17,7 +17,6 @@
 
 package org.apache.ignite.internal.catalog.descriptors;
 
-import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
 import org.apache.ignite.internal.tostring.S;
@@ -43,10 +42,6 @@ public class HashIndexDescriptor extends IndexDescriptor {
         super(id, name, tableId, true);
 
         this.columns = List.copyOf(Objects.requireNonNull(columns, "columns"));
-
-        if (new HashSet<>(columns).size() != columns.size()) {
-            throw new IllegalArgumentException("Indexed columns should be 
unique");
-        }
     }
 
     /** Returns indexed columns. */
@@ -54,6 +49,12 @@ public class HashIndexDescriptor extends IndexDescriptor {
         return columns;
     }
 
+    /** {@inheritDoc} */
+    @Override
+    public boolean hasColumn(String columnName) {
+        return columns.contains(columnName);
+    }
+
     /** {@inheritDoc} */
     @Override
     public String toString() {
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/IndexDescriptor.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/IndexDescriptor.java
index 99feaf15e3..8b63d1d08c 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/IndexDescriptor.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/IndexDescriptor.java
@@ -52,6 +52,14 @@ public abstract class IndexDescriptor extends 
ObjectDescriptor {
         return writeOnly;
     }
 
+    /**
+     * Checks if a column with given name is indexed.
+     *
+     * @param columnName Column name to check.
+     * @return {@code true} if index contains the column, {@code false} 
otherwise.
+     */
+    public abstract boolean hasColumn(String columnName);
+
     /** {@inheritDoc} */
     @Override
     public String toString() {
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/SortedIndexDescriptor.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/SortedIndexDescriptor.java
index cfc59ee4c4..f99ec4728e 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/SortedIndexDescriptor.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/SortedIndexDescriptor.java
@@ -49,6 +49,12 @@ public class SortedIndexDescriptor extends IndexDescriptor {
         return columns;
     }
 
+    /** {@inheritDoc} */
+    @Override
+    public boolean hasColumn(String columnName) {
+        return 
columns.stream().map(IndexColumnDescriptor::name).anyMatch(columnName::equals);
+    }
+
     /** {@inheritDoc} */
     @Override
     public String toString() {
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/TableDescriptor.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/TableDescriptor.java
index a9bfd56168..803397c07c 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/TableDescriptor.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/TableDescriptor.java
@@ -19,7 +19,6 @@ package org.apache.ignite.internal.catalog.descriptors;
 
 import java.io.IOException;
 import java.io.ObjectInputStream;
-import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -39,9 +38,9 @@ public class TableDescriptor extends ObjectDescriptor {
     private final int zoneId = 0;
     private final int engineId = 0;
 
-    private final TableColumnDescriptor[] columns;
-    private final String[] primaryKeyColumns;
-    private final String[] colocationColumns;
+    private final List<TableColumnDescriptor> columns;
+    private final List<String> primaryKeyColumns;
+    private final List<String> colocationColumns;
 
     @IgniteToStringExclude
     private transient Map<String, TableColumnDescriptor> columnsMap;
@@ -64,20 +63,19 @@ public class TableDescriptor extends ObjectDescriptor {
     ) {
         super(id, Type.TABLE, name);
 
-        this.columns = Objects.requireNonNull(columns, "No columns 
defined.").toArray(TableColumnDescriptor[]::new);
-        primaryKeyColumns = Objects.requireNonNull(pkCols, "No primary key 
columns.").toArray(String[]::new);
-        colocationColumns = colocationCols == null ? primaryKeyColumns : 
colocationCols.toArray(String[]::new);
+        this.columns = Objects.requireNonNull(columns, "No columns defined.");
+        primaryKeyColumns = Objects.requireNonNull(pkCols, "No primary key 
columns.");
+        colocationColumns = colocationCols == null ? pkCols : colocationCols;
 
         this.columnsMap = 
columns.stream().collect(Collectors.toMap(TableColumnDescriptor::name, 
Function.identity()));
 
         // TODO: IGNITE-19082 Throw proper exceptions.
         assert !columnsMap.isEmpty() : "No columns.";
-        assert primaryKeyColumns.length > 0 : "No primary key columns.";
-        assert colocationColumns.length > 0 : "No colocation columns.";
+        assert !primaryKeyColumns.isEmpty() : "No primary key columns.";
+        assert !colocationColumns.isEmpty() : "No colocation columns.";
 
-        assert Arrays.stream(primaryKeyColumns).noneMatch(c -> 
Objects.requireNonNull(columnsMap.get(c), c).nullable());
-        //noinspection ArrayEquality
-        assert primaryKeyColumns == colocationColumns || 
Set.of(primaryKeyColumns).containsAll(List.of(colocationColumns));
+        assert primaryKeyColumns.stream().noneMatch(c -> 
Objects.requireNonNull(columnsMap.get(c), c).nullable());
+        assert Set.copyOf(primaryKeyColumns).containsAll(colocationColumns);
     }
 
     public int zoneId() {
@@ -88,9 +86,29 @@ public class TableDescriptor extends ObjectDescriptor {
         return engineId;
     }
 
+    public List<String> primaryKeyColumns() {
+        return primaryKeyColumns;
+    }
+
+    public List<String> colocationColumns() {
+        return colocationColumns;
+    }
+
+    public List<TableColumnDescriptor> columns() {
+        return columns;
+    }
+
+    public TableColumnDescriptor column(String name) {
+        return columnsMap.get(name);
+    }
+
+    public boolean isPrimaryKeyColumn(String name) {
+        return primaryKeyColumns.contains(name);
+    }
+
     private void readObject(ObjectInputStream in) throws IOException, 
ClassNotFoundException {
         in.defaultReadObject();
-        this.columnsMap = 
Arrays.stream(columns).collect(Collectors.toMap(TableColumnDescriptor::name, 
Function.identity()));
+        this.columnsMap = 
columns.stream().collect(Collectors.toMap(TableColumnDescriptor::name, 
Function.identity()));
     }
 
     /** {@inheritDoc} */
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/IndexDescriptor.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/events/AddColumnEventParameters.java
similarity index 50%
copy from 
modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/IndexDescriptor.java
copy to 
modules/catalog/src/main/java/org/apache/ignite/internal/catalog/events/AddColumnEventParameters.java
index 99feaf15e3..0423ab171c 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/IndexDescriptor.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/events/AddColumnEventParameters.java
@@ -15,46 +15,42 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.catalog.descriptors;
+package org.apache.ignite.internal.catalog.events;
 
-import org.apache.ignite.internal.tostring.S;
+import java.util.List;
+import org.apache.ignite.internal.catalog.descriptors.TableColumnDescriptor;
 
 /**
- * Index descriptor base class.
+ * Add column event parameters contains descriptors of added columns.
  */
-public abstract class IndexDescriptor extends ObjectDescriptor {
-    private static final long serialVersionUID = -8045949593661301287L;
+public class AddColumnEventParameters extends CatalogEventParameters {
 
-    /** Table id. */
     private final int tableId;
+    private final List<TableColumnDescriptor> columnDescriptors;
+
+    /**
+     * Constructor.
+     *
+     * @param causalityToken Causality token.
+     * @param tableId An id of table, which columns are added to.
+     * @param columnDescriptors New columns descriptors.
+     */
+    public AddColumnEventParameters(long causalityToken, int tableId, 
List<TableColumnDescriptor> columnDescriptors) {
+        super(causalityToken);
 
-    /** Unique constraint flag. */
-    private boolean unique;
-
-    /** Write only flag. {@code True} when index is building. */
-    private boolean writeOnly;
-
-    IndexDescriptor(int id, String name, int tableId, boolean unique) {
-        super(id, Type.INDEX, name);
         this.tableId = tableId;
-        this.unique = unique;
+        this.columnDescriptors = columnDescriptors;
     }
 
+    /** Returns table id. */
     public int tableId() {
         return tableId;
     }
 
-    public boolean unique() {
-        return unique;
-    }
-
-    public boolean writeOnly() {
-        return writeOnly;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public String toString() {
-        return S.toString(this);
+    /**
+     * Returns descriptors of columns to add.
+     */
+    public List<TableColumnDescriptor> descriptors() {
+        return columnDescriptors;
     }
 }
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/events/CatalogEvent.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/events/CatalogEvent.java
index d88347aeca..96691d597f 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/events/CatalogEvent.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/events/CatalogEvent.java
@@ -27,5 +27,8 @@ public enum CatalogEvent implements Event {
     TABLE_CREATE,
 
     /** This event is fired, when a table was dropped in Catalog. */
-    TABLE_DROP
+    TABLE_DROP,
+
+    /** This event is fired, when a column was added to or dropped from a 
table. */
+    TABLE_ALTER,
 }
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/IndexDescriptor.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/events/DropColumnEventParameters.java
similarity index 53%
copy from 
modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/IndexDescriptor.java
copy to 
modules/catalog/src/main/java/org/apache/ignite/internal/catalog/events/DropColumnEventParameters.java
index 99feaf15e3..ddf491d064 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/IndexDescriptor.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/events/DropColumnEventParameters.java
@@ -15,46 +15,41 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.catalog.descriptors;
+package org.apache.ignite.internal.catalog.events;
 
-import org.apache.ignite.internal.tostring.S;
+import java.util.Collection;
 
 /**
- * Index descriptor base class.
+ * Drop column event parameters contains descriptors of dropped columns.
  */
-public abstract class IndexDescriptor extends ObjectDescriptor {
-    private static final long serialVersionUID = -8045949593661301287L;
+public class DropColumnEventParameters extends CatalogEventParameters {
 
-    /** Table id. */
     private final int tableId;
+    private final Collection<String> columns;
+
+    /**
+     * Constructor.
+     *
+     * @param causalityToken Causality token.
+     * @param tableId An id of table, which columns are dropped from.
+     * @param columns Names of columns to drop.
+     */
+    public DropColumnEventParameters(long causalityToken, int tableId, 
Collection<String> columns) {
+        super(causalityToken);
 
-    /** Unique constraint flag. */
-    private boolean unique;
-
-    /** Write only flag. {@code True} when index is building. */
-    private boolean writeOnly;
-
-    IndexDescriptor(int id, String name, int tableId, boolean unique) {
-        super(id, Type.INDEX, name);
         this.tableId = tableId;
-        this.unique = unique;
+        this.columns = columns;
     }
 
+    /** Returns table id. */
     public int tableId() {
         return tableId;
     }
 
-    public boolean unique() {
-        return unique;
-    }
-
-    public boolean writeOnly() {
-        return writeOnly;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public String toString() {
-        return S.toString(this);
+    /**
+     * Returns names of columns to drop.
+     */
+    public Collection<String> columns() {
+        return columns;
     }
 }
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/IndexDescriptor.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropColumnsEntry.java
similarity index 62%
copy from 
modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/IndexDescriptor.java
copy to 
modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropColumnsEntry.java
index 99feaf15e3..bf1d009fdf 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/IndexDescriptor.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropColumnsEntry.java
@@ -15,41 +15,39 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.catalog.descriptors;
+package org.apache.ignite.internal.catalog.storage;
 
+import java.util.Set;
 import org.apache.ignite.internal.tostring.S;
 
 /**
- * Index descriptor base class.
+ * Describes dropping of columns.
  */
-public abstract class IndexDescriptor extends ObjectDescriptor {
-    private static final long serialVersionUID = -8045949593661301287L;
+public class DropColumnsEntry implements UpdateEntry {
+    private static final long serialVersionUID = 2970125889493580121L;
 
-    /** Table id. */
     private final int tableId;
-
-    /** Unique constraint flag. */
-    private boolean unique;
-
-    /** Write only flag. {@code True} when index is building. */
-    private boolean writeOnly;
-
-    IndexDescriptor(int id, String name, int tableId, boolean unique) {
-        super(id, Type.INDEX, name);
+    private final Set<String> columns;
+
+    /**
+     * Constructs the object.
+     *
+     * @param tableId Table id.
+     * @param columns Names of columns to drop.
+     */
+    public DropColumnsEntry(int tableId, Set<String> columns) {
         this.tableId = tableId;
-        this.unique = unique;
+        this.columns = columns;
     }
 
+    /** Returns table id. */
     public int tableId() {
         return tableId;
     }
 
-    public boolean unique() {
-        return unique;
-    }
-
-    public boolean writeOnly() {
-        return writeOnly;
+    /** Returns name of columns to drop. */
+    public Set<String> columns() {
+        return columns;
     }
 
     /** {@inheritDoc} */
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/IndexDescriptor.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewColumnsEntry.java
similarity index 57%
copy from 
modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/IndexDescriptor.java
copy to 
modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewColumnsEntry.java
index 99feaf15e3..7fec534e5d 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/IndexDescriptor.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewColumnsEntry.java
@@ -15,41 +15,40 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.catalog.descriptors;
+package org.apache.ignite.internal.catalog.storage;
 
+import java.util.List;
+import org.apache.ignite.internal.catalog.descriptors.TableColumnDescriptor;
 import org.apache.ignite.internal.tostring.S;
 
 /**
- * Index descriptor base class.
+ * Describes addition of new columns.
  */
-public abstract class IndexDescriptor extends ObjectDescriptor {
-    private static final long serialVersionUID = -8045949593661301287L;
+public class NewColumnsEntry implements UpdateEntry {
+    private static final long serialVersionUID = 2970125889493580121L;
 
-    /** Table id. */
     private final int tableId;
-
-    /** Unique constraint flag. */
-    private boolean unique;
-
-    /** Write only flag. {@code True} when index is building. */
-    private boolean writeOnly;
-
-    IndexDescriptor(int id, String name, int tableId, boolean unique) {
-        super(id, Type.INDEX, name);
+    private final List<TableColumnDescriptor> descriptors;
+
+    /**
+     * Constructs the object.
+     *
+     * @param tableId Table id.
+     * @param descriptors Descriptors of columns to add.
+     */
+    public NewColumnsEntry(int tableId, List<TableColumnDescriptor> 
descriptors) {
         this.tableId = tableId;
-        this.unique = unique;
+        this.descriptors = descriptors;
     }
 
+    /** Returns table id. */
     public int tableId() {
         return tableId;
     }
 
-    public boolean unique() {
-        return unique;
-    }
-
-    public boolean writeOnly() {
-        return writeOnly;
+    /** Returns descriptors of columns to add. */
+    public List<TableColumnDescriptor> descriptors() {
+        return descriptors;
     }
 
     /** {@inheritDoc} */
diff --git 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogServiceSelfTest.java
 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogServiceSelfTest.java
index 003ab864f1..a2353ae015 100644
--- 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogServiceSelfTest.java
+++ 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogServiceSelfTest.java
@@ -28,24 +28,32 @@ import static 
org.junit.jupiter.api.Assertions.assertNotSame;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertSame;
 import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.CompletableFuture;
+import org.apache.ignite.internal.catalog.commands.AlterTableAddColumnParams;
+import org.apache.ignite.internal.catalog.commands.AlterTableDropColumnParams;
 import org.apache.ignite.internal.catalog.commands.ColumnParams;
 import org.apache.ignite.internal.catalog.commands.CreateTableParams;
 import org.apache.ignite.internal.catalog.commands.DefaultValue;
 import org.apache.ignite.internal.catalog.commands.DropTableParams;
 import org.apache.ignite.internal.catalog.descriptors.SchemaDescriptor;
+import org.apache.ignite.internal.catalog.descriptors.TableColumnDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.TableDescriptor;
+import org.apache.ignite.internal.catalog.events.AddColumnEventParameters;
 import org.apache.ignite.internal.catalog.events.CatalogEvent;
 import org.apache.ignite.internal.catalog.events.CatalogEventParameters;
 import org.apache.ignite.internal.catalog.events.CreateTableEventParameters;
+import org.apache.ignite.internal.catalog.events.DropColumnEventParameters;
 import org.apache.ignite.internal.catalog.events.DropTableEventParameters;
 import org.apache.ignite.internal.catalog.storage.ObjectIdGenUpdateEntry;
 import org.apache.ignite.internal.catalog.storage.UpdateLog;
@@ -58,11 +66,14 @@ import 
org.apache.ignite.internal.metastorage.impl.StandaloneMetaStorageManager;
 import 
org.apache.ignite.internal.metastorage.server.SimpleInMemoryKeyValueStorage;
 import org.apache.ignite.internal.vault.VaultManager;
 import org.apache.ignite.internal.vault.inmemory.InMemoryVaultService;
+import org.apache.ignite.lang.ColumnAlreadyExistsException;
+import org.apache.ignite.lang.ColumnNotFoundException;
 import org.apache.ignite.lang.IgniteInternalException;
 import org.apache.ignite.lang.NodeStoppingException;
 import org.apache.ignite.lang.TableAlreadyExistsException;
 import org.apache.ignite.lang.TableNotFoundException;
 import org.apache.ignite.sql.ColumnType;
+import org.apache.ignite.sql.SqlException;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -74,8 +85,12 @@ import org.mockito.Mockito;
  * Catalog service self test.
  */
 public class CatalogServiceSelfTest {
+    private static final String SCHEMA_NAME = CatalogService.PUBLIC;
+    private static final String ZONE_NAME = "ZONE";
     private static final String TABLE_NAME = "myTable";
     private static final String TABLE_NAME_2 = "myTable2";
+    private static final String NEW_COLUMN_NAME = "NEWCOL";
+    private static final String NEW_COLUMN_NAME_2 = "NEWCOL2";
 
     private MetaStorageManager metastore;
 
@@ -119,7 +134,7 @@ public class CatalogServiceSelfTest {
         assertNull(service.index(0, System.currentTimeMillis()));
 
         SchemaDescriptor schema = service.schema(0);
-        assertEquals(CatalogService.PUBLIC, schema.name());
+        assertEquals(SCHEMA_NAME, schema.name());
 
         assertEquals(0, schema.id());
         assertEquals(0, schema.version());
@@ -130,14 +145,14 @@ public class CatalogServiceSelfTest {
     @Test
     public void testCreateTable() {
         CreateTableParams params = CreateTableParams.builder()
-                .schemaName("PUBLIC")
+                .schemaName(SCHEMA_NAME)
                 .tableName(TABLE_NAME)
                 .ifTableExists(true)
-                .zone("ZONE")
+                .zone(ZONE_NAME)
                 .columns(List.of(
-                        new ColumnParams("key1", ColumnType.INT32, 
DefaultValue.constant(null), false),
-                        new ColumnParams("key2", ColumnType.INT32, 
DefaultValue.constant(null), false),
-                        new ColumnParams("val", ColumnType.INT32, 
DefaultValue.constant(null), true)
+                        
ColumnParams.builder().name("key1").type(ColumnType.INT32).build(),
+                        
ColumnParams.builder().name("key2").type(ColumnType.INT32).build(),
+                        
ColumnParams.builder().name("val").type(ColumnType.INT32).nullable(true).build()
                 ))
                 .primaryKeyColumns(List.of("key1", "key2"))
                 .colocationColumns(List.of("key2"))
@@ -152,7 +167,7 @@ public class CatalogServiceSelfTest {
 
         assertNotNull(schema);
         assertEquals(0, schema.id());
-        assertEquals(CatalogService.PUBLIC, schema.name());
+        assertEquals(SCHEMA_NAME, schema.name());
         assertEquals(0, schema.version());
         assertSame(schema, service.activeSchema(0L));
         assertSame(schema, service.activeSchema(123L));
@@ -166,7 +181,7 @@ public class CatalogServiceSelfTest {
 
         assertNotNull(schema);
         assertEquals(0, schema.id());
-        assertEquals(CatalogService.PUBLIC, schema.name());
+        assertEquals(SCHEMA_NAME, schema.name());
         assertEquals(1, schema.version());
         assertSame(schema, service.activeSchema(System.currentTimeMillis()));
 
@@ -191,7 +206,7 @@ public class CatalogServiceSelfTest {
 
         assertNotNull(schema);
         assertEquals(0, schema.id());
-        assertEquals(CatalogService.PUBLIC, schema.name());
+        assertEquals(SCHEMA_NAME, schema.name());
         assertEquals(2, schema.version());
         assertSame(schema, service.activeSchema(System.currentTimeMillis()));
 
@@ -209,8 +224,8 @@ public class CatalogServiceSelfTest {
         CreateTableParams params = CreateTableParams.builder()
                 .tableName(TABLE_NAME)
                 .columns(List.of(
-                        new ColumnParams("key", ColumnType.INT32, 
DefaultValue.constant(null), false),
-                        new ColumnParams("val", ColumnType.INT32, 
DefaultValue.constant(null), false)
+                        
ColumnParams.builder().name("key").type(ColumnType.INT32).build(),
+                        
ColumnParams.builder().name("val").type(ColumnType.INT32).build()
                 ))
                 .primaryKeyColumns(List.of("key"))
                 .ifTableExists(true)
@@ -223,8 +238,8 @@ public class CatalogServiceSelfTest {
                 CreateTableParams.builder()
                         .tableName(TABLE_NAME)
                         .columns(List.of(
-                                new ColumnParams("key", ColumnType.INT32, 
DefaultValue.constant(null), false),
-                                new ColumnParams("val", ColumnType.INT32, 
DefaultValue.constant(null), false)
+                                
ColumnParams.builder().name("key").type(ColumnType.INT32).build(),
+                                
ColumnParams.builder().name("val").type(ColumnType.INT32).build()
                         ))
                         .primaryKeyColumns(List.of("key"))
                         .ifTableExists(false)
@@ -234,13 +249,15 @@ public class CatalogServiceSelfTest {
     }
 
     @Test
-    public void testDropTable() {
+    public void testDropTable() throws InterruptedException {
         assertThat(service.createTable(simpleTable(TABLE_NAME)), 
willBe((Object) null));
         assertThat(service.createTable(simpleTable(TABLE_NAME_2)), 
willBe((Object) null));
 
         long beforeDropTimestamp = System.currentTimeMillis();
 
-        DropTableParams dropTableParams = 
DropTableParams.builder().schemaName("PUBLIC").tableName(TABLE_NAME).build();
+        Thread.sleep(5);
+
+        DropTableParams dropTableParams = 
DropTableParams.builder().schemaName(SCHEMA_NAME).tableName(TABLE_NAME).build();
 
         assertThat(service.dropTable(dropTableParams), willBe((Object) null));
 
@@ -249,7 +266,7 @@ public class CatalogServiceSelfTest {
 
         assertNotNull(schema);
         assertEquals(0, schema.id());
-        assertEquals(CatalogService.PUBLIC, schema.name());
+        assertEquals(SCHEMA_NAME, schema.name());
         assertEquals(2, schema.version());
         assertSame(schema, service.activeSchema(beforeDropTimestamp));
 
@@ -264,7 +281,7 @@ public class CatalogServiceSelfTest {
 
         assertNotNull(schema);
         assertEquals(0, schema.id());
-        assertEquals(CatalogService.PUBLIC, schema.name());
+        assertEquals(SCHEMA_NAME, schema.name());
         assertEquals(3, schema.version());
         assertSame(schema, service.activeSchema(System.currentTimeMillis()));
 
@@ -279,10 +296,11 @@ public class CatalogServiceSelfTest {
     @Test
     public void testDropTableIfExistsFlag() {
         CreateTableParams createTableParams = CreateTableParams.builder()
+                .schemaName(SCHEMA_NAME)
                 .tableName(TABLE_NAME)
                 .columns(List.of(
-                        new ColumnParams("key", ColumnType.INT32, 
DefaultValue.constant(null), false),
-                        new ColumnParams("val", ColumnType.INT32, 
DefaultValue.constant(null), false)
+                        
ColumnParams.builder().name("key").type(ColumnType.INT32).build(),
+                        
ColumnParams.builder().name("val").type(ColumnType.INT32).build()
                 ))
                 .primaryKeyColumns(List.of("key"))
                 .build();
@@ -290,6 +308,7 @@ public class CatalogServiceSelfTest {
         assertThat(service.createTable(createTableParams), willBe((Object) 
null));
 
         DropTableParams params = DropTableParams.builder()
+                .schemaName(SCHEMA_NAME)
                 .tableName(TABLE_NAME)
                 .ifTableExists(true)
                 .build();
@@ -298,6 +317,7 @@ public class CatalogServiceSelfTest {
         assertThat(service.dropTable(params), 
willThrowFast(TableNotFoundException.class));
 
         params = DropTableParams.builder()
+                .schemaName(SCHEMA_NAME)
                 .tableName(TABLE_NAME)
                 .ifTableExists(false)
                 .build();
@@ -305,6 +325,236 @@ public class CatalogServiceSelfTest {
         assertThat(service.dropTable(params), 
willThrowFast(TableNotFoundException.class));
     }
 
+    @Test
+    public void testAddColumn() throws InterruptedException {
+        assertThat(service.createTable(simpleTable(TABLE_NAME)), 
willBe((Object) null));
+
+        AlterTableAddColumnParams params = AlterTableAddColumnParams.builder()
+                .schemaName(SCHEMA_NAME)
+                .tableName(TABLE_NAME)
+                .columns(List.of(ColumnParams.builder()
+                        .name(NEW_COLUMN_NAME)
+                        .type(ColumnType.STRING)
+                        .nullable(true)
+                        .defaultValue(DefaultValue.constant("Ignite!"))
+                        .build()
+                ))
+                .build();
+
+        long beforeAddedTimestamp = System.currentTimeMillis();
+
+        Thread.sleep(5);
+
+        assertThat(service.addColumn(params), willBe((Object) null));
+
+        // Validate catalog version from the past.
+        SchemaDescriptor schema = service.activeSchema(beforeAddedTimestamp);
+        assertNotNull(schema);
+        assertNotNull(schema.table(TABLE_NAME));
+
+        assertNull(schema.table(TABLE_NAME).column(NEW_COLUMN_NAME));
+
+        // Validate actual catalog
+        schema = service.activeSchema(System.currentTimeMillis());
+        assertNotNull(schema);
+        assertNotNull(schema.table(TABLE_NAME));
+
+        // Validate column descriptor.
+        TableColumnDescriptor column = 
schema.table(TABLE_NAME).column(NEW_COLUMN_NAME);
+
+        assertEquals(NEW_COLUMN_NAME, column.name());
+        assertEquals(ColumnType.STRING, column.type());
+        assertTrue(column.nullable());
+
+        assertEquals(DefaultValue.Type.CONSTANT, column.defaultValue().type());
+        assertEquals("Ignite!", ((DefaultValue.ConstantValue) 
column.defaultValue()).value());
+
+        assertEquals(0, column.length());
+        assertEquals(0, column.precision());
+        assertEquals(0, column.scale());
+    }
+
+    @Test
+    public void testDropColumn() throws InterruptedException {
+        assertThat(service.createTable(simpleTable(TABLE_NAME)), 
willBe((Object) null));
+
+        // Validate dropping column
+        AlterTableDropColumnParams params = 
AlterTableDropColumnParams.builder()
+                .schemaName(SCHEMA_NAME)
+                .tableName(TABLE_NAME)
+                .columns(Set.of("VAL"))
+                .build();
+
+        long beforeAddedTimestamp = System.currentTimeMillis();
+
+        Thread.sleep(5);
+
+        assertThat(service.dropColumn(params), willBe((Object) null));
+
+        // Validate catalog version from the past.
+        SchemaDescriptor schema = service.activeSchema(beforeAddedTimestamp);
+        assertNotNull(schema);
+        assertNotNull(schema.table(TABLE_NAME));
+
+        assertNotNull(schema.table(TABLE_NAME).column("VAL"));
+
+        // Validate actual catalog
+        schema = service.activeSchema(System.currentTimeMillis());
+        assertNotNull(schema);
+        assertNotNull(schema.table(TABLE_NAME));
+
+        assertNull(schema.table(TABLE_NAME).column("VAL"));
+    }
+
+    @Test
+    public void testDropColumnIfTableExistsFlag() {
+        assertNull(service.table(TABLE_NAME, System.currentTimeMillis()));
+
+        AlterTableAddColumnParams params = AlterTableAddColumnParams.builder()
+                .schemaName(SCHEMA_NAME)
+                .tableName(TABLE_NAME)
+                
.columns(List.of(ColumnParams.builder().name(NEW_COLUMN_NAME).type(ColumnType.INT32).nullable(true).build()))
+                .ifTableExists(false)
+                .build();
+
+        assertThat(service.addColumn(params), 
willThrow(TableNotFoundException.class));
+
+        params = AlterTableAddColumnParams.builder()
+                .schemaName(SCHEMA_NAME)
+                .tableName(TABLE_NAME)
+                
.columns(List.of(ColumnParams.builder().name(NEW_COLUMN_NAME).type(ColumnType.INT32).nullable(true).build()))
+                .ifTableExists(true)
+                .build();
+
+        assertThat(service.addColumn(params), 
willThrow(TableNotFoundException.class));
+    }
+
+    @Test
+    public void testDropIndexedColumn() {
+        assertThat(service.createTable(simpleTable(TABLE_NAME)), 
willBe((Object) null));
+
+        // Try to drop indexed column
+        AlterTableDropColumnParams params = 
AlterTableDropColumnParams.builder()
+                .schemaName(SCHEMA_NAME)
+                .tableName(TABLE_NAME)
+                .columns(Set.of("VAL"))
+                .build();
+
+        //TODO: uncomment "https://issues.apache.org/jira/browse/IGNITE-19460";
+        // assertThat(service.createIndex("CREATE INDEX myIndex ON myTable 
(VAL)"), willBe((Object) null));
+        // assertThat(service.dropColumn(params), 
willThrow(IllegalArgumentException.class));
+
+        // Try to drop PK column
+        params = AlterTableDropColumnParams.builder()
+                .schemaName(SCHEMA_NAME)
+                .tableName(TABLE_NAME)
+                .columns(Set.of("ID"))
+                .build();
+
+        assertThat(service.dropColumn(params), willThrow(SqlException.class));
+
+        // Validate actual catalog
+        SchemaDescriptor schema = 
service.activeSchema(System.currentTimeMillis());
+        assertNotNull(schema);
+        assertNotNull(schema.table(TABLE_NAME));
+        assertEquals(1, schema.version());
+
+        assertNotNull(schema.table(TABLE_NAME).column("ID"));
+        assertNotNull(schema.table(TABLE_NAME).column("VAL"));
+    }
+
+    @Test
+    public void testAddColumnIfTableExistsFlag() {
+        assertNull(service.table(TABLE_NAME, System.currentTimeMillis()));
+
+        AlterTableAddColumnParams params = AlterTableAddColumnParams.builder()
+                .schemaName(SCHEMA_NAME)
+                .tableName(TABLE_NAME)
+                
.columns(List.of(ColumnParams.builder().name(NEW_COLUMN_NAME).type(ColumnType.INT32).nullable(true).build()))
+                .ifTableExists(false)
+                .build();
+
+        assertThat(service.addColumn(params), 
willThrow(TableNotFoundException.class));
+
+        params = AlterTableAddColumnParams.builder()
+                .schemaName(SCHEMA_NAME)
+                .tableName(TABLE_NAME)
+                
.columns(List.of(ColumnParams.builder().name(NEW_COLUMN_NAME).type(ColumnType.INT32).nullable(true).build()))
+                .ifTableExists(true)
+                .build();
+
+        assertThat(service.addColumn(params), 
willThrow(TableNotFoundException.class));
+    }
+
+    @Test
+    public void testAddDropMultipleColumns() {
+        assertThat(service.createTable(simpleTable(TABLE_NAME)), 
willBe((Object) null));
+
+        // Add duplicate column.
+        AlterTableAddColumnParams addColumnParams = 
AlterTableAddColumnParams.builder()
+                .schemaName(SCHEMA_NAME)
+                .tableName(TABLE_NAME)
+                .columns(List.of(
+                        
ColumnParams.builder().name(NEW_COLUMN_NAME).type(ColumnType.INT32).nullable(true).build(),
+                        
ColumnParams.builder().name("VAL").type(ColumnType.INT32).nullable(true).build()
+                ))
+                .build();
+
+        assertThat(service.addColumn(addColumnParams), 
willThrow(ColumnAlreadyExistsException.class));
+
+        // Validate no column added.
+        SchemaDescriptor schema = 
service.activeSchema(System.currentTimeMillis());
+
+        assertNull(schema.table(TABLE_NAME).column(NEW_COLUMN_NAME));
+
+        // Add multiple columns.
+        addColumnParams = AlterTableAddColumnParams.builder()
+                .schemaName(SCHEMA_NAME)
+                .tableName(TABLE_NAME)
+                .columns(List.of(
+                        
ColumnParams.builder().name(NEW_COLUMN_NAME).type(ColumnType.INT32).nullable(true).build(),
+                        
ColumnParams.builder().name(NEW_COLUMN_NAME_2).type(ColumnType.INT32).nullable(true).build()
+                ))
+                .build();
+
+        assertThat(service.addColumn(addColumnParams), willBe((Object) null));
+
+        // Validate both columns added.
+        schema = service.activeSchema(System.currentTimeMillis());
+
+        assertNotNull(schema.table(TABLE_NAME).column(NEW_COLUMN_NAME));
+        assertNotNull(schema.table(TABLE_NAME).column(NEW_COLUMN_NAME_2));
+
+        // Drop multiple columns.
+        AlterTableDropColumnParams dropColumnParams = 
AlterTableDropColumnParams.builder()
+                .schemaName(SCHEMA_NAME)
+                .tableName(TABLE_NAME)
+                .columns(Set.of(NEW_COLUMN_NAME, NEW_COLUMN_NAME_2))
+                .build();
+
+        assertThat(service.dropColumn(dropColumnParams), willBe((Object) 
null));
+
+        // Validate both columns dropped.
+        schema = service.activeSchema(System.currentTimeMillis());
+
+        assertNull(schema.table(TABLE_NAME).column(NEW_COLUMN_NAME));
+        assertNull(schema.table(TABLE_NAME).column(NEW_COLUMN_NAME_2));
+
+        // Check dropping of non-existing column
+        dropColumnParams = AlterTableDropColumnParams.builder()
+                .schemaName(SCHEMA_NAME)
+                .tableName(TABLE_NAME)
+                .columns(Set.of(NEW_COLUMN_NAME, "VAL"))
+                .build();
+
+        assertThat(service.dropColumn(dropColumnParams), 
willThrow(ColumnNotFoundException.class));
+
+        // Validate no column dropped.
+        schema = service.activeSchema(System.currentTimeMillis());
+
+        assertNotNull(schema.table(TABLE_NAME).column("VAL"));
+    }
+
     @Test
     public void operationWillBeRetriedFiniteAmountOfTimes() {
         UpdateLog updateLogMock = Mockito.mock(UpdateLog.class);
@@ -356,16 +606,16 @@ public class CatalogServiceSelfTest {
     }
 
     @Test
-    public void testCreateTableEvents() {
+    public void testTableEvents() {
         CreateTableParams params = CreateTableParams.builder()
-                .schemaName("PUBLIC")
+                .schemaName(SCHEMA_NAME)
                 .tableName(TABLE_NAME)
                 .ifTableExists(true)
-                .zone("ZONE")
+                .zone(ZONE_NAME)
                 .columns(List.of(
-                        new ColumnParams("key1", ColumnType.INT32, 
DefaultValue.constant(null), false),
-                        new ColumnParams("key2", ColumnType.INT32, 
DefaultValue.constant(null), false),
-                        new ColumnParams("val", ColumnType.INT32, 
DefaultValue.constant(null), true)
+                        
ColumnParams.builder().name("key1").type(ColumnType.INT32).build(),
+                        
ColumnParams.builder().name("key2").type(ColumnType.INT32).build(),
+                        
ColumnParams.builder().name("val").type(ColumnType.INT32).nullable(true).build()
                 ))
                 .primaryKeyColumns(List.of("key1", "key2"))
                 .colocationColumns(List.of("key2"))
@@ -393,14 +643,61 @@ public class CatalogServiceSelfTest {
         verifyNoMoreInteractions(eventListener);
     }
 
+    @Test
+    public void testColumnEvents() {
+        AlterTableAddColumnParams addColumnParams = 
AlterTableAddColumnParams.builder()
+                .schemaName(SCHEMA_NAME)
+                .tableName(TABLE_NAME)
+                .columns(List.of(ColumnParams.builder()
+                        .name(NEW_COLUMN_NAME)
+                        .type(ColumnType.INT32)
+                        .defaultValue(DefaultValue.constant(42))
+                        .nullable(true)
+                        .build()
+                ))
+                .ifTableExists(true)
+                .build();
+
+        AlterTableDropColumnParams dropColumnParams = 
AlterTableDropColumnParams.builder()
+                .schemaName(SCHEMA_NAME)
+                .tableName(TABLE_NAME)
+                .columns(Set.of(NEW_COLUMN_NAME))
+                .build();
+
+        EventListener<CatalogEventParameters> eventListener = 
Mockito.mock(EventListener.class);
+        when(eventListener.notify(any(), 
any())).thenReturn(completedFuture(false));
+
+        service.listen(CatalogEvent.TABLE_ALTER, eventListener);
+
+        // Try to add column without table.
+        assertThat(service.addColumn(addColumnParams), 
willThrow(TableNotFoundException.class));
+        verifyNoInteractions(eventListener);
+
+        // Create table.
+        assertThat(service.createTable(simpleTable(TABLE_NAME)), 
willBe((Object) null));
+
+        // Add column.
+        assertThat(service.addColumn(addColumnParams), willBe((Object) null));
+        verify(eventListener).notify(any(AddColumnEventParameters.class), 
ArgumentMatchers.isNull());
+
+        // Drop column.
+        assertThat(service.dropColumn(dropColumnParams), willBe((Object) 
null));
+        verify(eventListener).notify(any(DropColumnEventParameters.class), 
ArgumentMatchers.isNull());
+
+        // Try drop column once again.
+        assertThat(service.dropColumn(dropColumnParams), 
willThrow(ColumnNotFoundException.class));
+
+        verifyNoMoreInteractions(eventListener);
+    }
+
     private static CreateTableParams simpleTable(String name) {
         return CreateTableParams.builder()
-                .schemaName("PUBLIC")
+                .schemaName(SCHEMA_NAME)
                 .tableName(name)
-                .zone("ZONE")
+                .zone(ZONE_NAME)
                 .columns(List.of(
-                        new ColumnParams("ID", ColumnType.INT32, 
DefaultValue.constant(null), false),
-                        new ColumnParams("VAL", ColumnType.INT32, 
DefaultValue.constant(null), true)
+                        
ColumnParams.builder().name("ID").type(ColumnType.INT32).build(),
+                        
ColumnParams.builder().name("VAL").type(ColumnType.INT32).nullable(true).build()
                 ))
                 .primaryKeyColumns(List.of("ID"))
                 .build();
diff --git 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/ItSchemaChangeTableViewTest.java
 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/ItSchemaChangeTableViewTest.java
index e389f99377..9906221940 100644
--- 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/ItSchemaChangeTableViewTest.java
+++ 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/ItSchemaChangeTableViewTest.java
@@ -29,6 +29,7 @@ import 
org.apache.ignite.internal.schema.SchemaMismatchException;
 import org.apache.ignite.table.RecordView;
 import org.apache.ignite.table.Table;
 import org.apache.ignite.table.Tuple;
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 
 /**
@@ -151,7 +152,9 @@ class ItSchemaChangeTableViewTest extends 
AbstractSchemaChangeTest {
 
     /**
      * Rename column then add a new column with same name.
+     * TODO IGNITE-19486: Add similar test for KV view.
      */
+    @Disabled("https://issues.apache.org/jira/browse/IGNITE-19486";)
     @Test
     void testRenameThenAddColumnWithSameName() throws Exception {
         List<Ignite> grid = startGrid();
diff --git 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlAsynchronousApiTest.java
 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlAsynchronousApiTest.java
index b2ed3dbeb3..6c70d570f8 100644
--- 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlAsynchronousApiTest.java
+++ 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlAsynchronousApiTest.java
@@ -135,7 +135,7 @@ public class ItSqlAsynchronousApiTest extends 
ClusterPerClassIntegrationTest {
         checkDdl(false, ses, "CREATE TABLE IF NOT EXISTS TEST(ID INT PRIMARY 
KEY, VAL VARCHAR)");
 
         // ADD COLUMN
-        checkDdl(true, ses, "ALTER TABLE TEST ADD COLUMN IF NOT EXISTS VAL1 
VARCHAR");
+        checkDdl(true, ses, "ALTER TABLE TEST ADD COLUMN VAL1 VARCHAR");
         checkError(
                 TableNotFoundException.class,
                 "The table does not exist 
[name=\"PUBLIC\".\"NOT_EXISTS_TABLE\"]",
@@ -149,7 +149,6 @@ public class ItSqlAsynchronousApiTest extends 
ClusterPerClassIntegrationTest {
                 ses,
                 "ALTER TABLE TEST ADD COLUMN VAL1 INT"
         );
-        checkDdl(false, ses, "ALTER TABLE TEST ADD COLUMN IF NOT EXISTS VAL1 
INT");
 
         // CREATE INDEX
         checkDdl(true, ses, "CREATE INDEX TEST_IDX ON TEST(VAL0)");
@@ -214,7 +213,6 @@ public class ItSqlAsynchronousApiTest extends 
ClusterPerClassIntegrationTest {
                 ses,
                 "ALTER TABLE TEST DROP COLUMN VAL1"
         );
-        checkDdl(false, ses, "ALTER TABLE TEST DROP COLUMN IF EXISTS VAL1");
 
         // DROP TABLE
         checkDdl(false, ses, "DROP TABLE IF EXISTS NOT_EXISTS_TABLE");
diff --git 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlSynchronousApiTest.java
 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlSynchronousApiTest.java
index aea1ab9e6d..976ea3c716 100644
--- 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlSynchronousApiTest.java
+++ 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlSynchronousApiTest.java
@@ -117,7 +117,7 @@ public class ItSqlSynchronousApiTest extends 
ClusterPerClassIntegrationTest {
         checkDdl(false, ses, "CREATE TABLE IF NOT EXISTS TEST(ID INT PRIMARY 
KEY, VAL VARCHAR)");
 
         // ADD COLUMN
-        checkDdl(true, ses, "ALTER TABLE TEST ADD COLUMN IF NOT EXISTS VAL1 
VARCHAR");
+        checkDdl(true, ses, "ALTER TABLE TEST ADD COLUMN VAL1 VARCHAR");
         checkError(
                 TableNotFoundException.class,
                 "The table does not exist 
[name=\"PUBLIC\".\"NOT_EXISTS_TABLE\"]",
@@ -131,7 +131,6 @@ public class ItSqlSynchronousApiTest extends 
ClusterPerClassIntegrationTest {
                 ses,
                 "ALTER TABLE TEST ADD COLUMN VAL1 INT"
         );
-        checkDdl(false, ses, "ALTER TABLE TEST ADD COLUMN IF NOT EXISTS VAL1 
INT");
 
         // CREATE INDEX
         checkDdl(true, ses, "CREATE INDEX TEST_IDX ON TEST(VAL0)");
@@ -196,7 +195,6 @@ public class ItSqlSynchronousApiTest extends 
ClusterPerClassIntegrationTest {
                 ses,
                 "ALTER TABLE TEST DROP COLUMN VAL1"
         );
-        checkDdl(false, ses, "ALTER TABLE TEST DROP COLUMN IF EXISTS VAL1");
 
         // DROP TABLE
         checkDdl(false, ses, "DROP TABLE IF EXISTS NOT_EXISTS_TABLE");
diff --git 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItMixedQueriesTest.java
 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItMixedQueriesTest.java
index 1ec09e4bcc..4adbfc5e96 100644
--- 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItMixedQueriesTest.java
+++ 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItMixedQueriesTest.java
@@ -224,10 +224,6 @@ public class ItMixedQueriesTest extends 
ClusterPerClassIntegrationTest {
 
         assertQuery(selectAllQry).columnNames("ID", "VAL", "NEW_COL").check();
 
-        sql("alter table test_tbl add column if not exists new_col int");
-
-        assertQuery(selectAllQry).columnNames("ID", "VAL", "NEW_COL").check();
-
         sql("alter table test_tbl drop column new_col");
 
         assertQuery(selectAllQry).columnNames("ID", "VAL").check();
@@ -236,10 +232,6 @@ public class ItMixedQueriesTest extends 
ClusterPerClassIntegrationTest {
         assertThrows(Exception.class, () -> sql("alter table test_tbl drop 
column new_col"));
 
         assertQuery(selectAllQry).columnNames("ID", "VAL").check();
-
-        sql("alter table test_tbl drop column if exists new_col");
-
-        assertQuery(selectAllQry).columnNames("ID", "VAL").check();
     }
 
     /** Quantified predicates test. */
diff --git 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/internal/InternalSchemaTest.java
 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/internal/InternalSchemaTest.java
index 755562b335..4507ca8d6e 100644
--- 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/internal/InternalSchemaTest.java
+++ 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/internal/InternalSchemaTest.java
@@ -31,6 +31,7 @@ import 
org.apache.ignite.internal.testframework.IgniteTestUtils;
 import org.apache.ignite.sql.IgniteSql;
 import org.apache.ignite.sql.ResultSet;
 import org.apache.ignite.sql.Session;
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 
 /** Tests for internal manipulations with schema. */
@@ -38,6 +39,7 @@ public class InternalSchemaTest extends 
ClusterPerClassIntegrationTest {
     /**
      * Checks that schema version is updated even if column names are 
intersected.
      */
+    @Disabled("https://issues.apache.org/jira/browse/IGNITE-19612";)
     @Test
     public void checkSchemaUpdatedWithEqAlterColumn() {
         IgniteSql sql = igniteSql();
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ddl/DdlCommandHandlerWrapper.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ddl/DdlCommandHandlerWrapper.java
index 21f39ea169..72cf3f2e6c 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ddl/DdlCommandHandlerWrapper.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ddl/DdlCommandHandlerWrapper.java
@@ -22,6 +22,8 @@ import java.util.concurrent.CompletableFuture;
 import org.apache.ignite.internal.catalog.CatalogManager;
 import org.apache.ignite.internal.distributionzones.DistributionZoneManager;
 import org.apache.ignite.internal.index.IndexManager;
+import org.apache.ignite.internal.sql.engine.prepare.ddl.AlterTableAddCommand;
+import org.apache.ignite.internal.sql.engine.prepare.ddl.AlterTableDropCommand;
 import org.apache.ignite.internal.sql.engine.prepare.ddl.CreateTableCommand;
 import org.apache.ignite.internal.sql.engine.prepare.ddl.DdlCommand;
 import org.apache.ignite.internal.sql.engine.prepare.ddl.DropTableCommand;
@@ -70,6 +72,20 @@ public class DdlCommandHandlerWrapper extends 
DdlCommandHandler {
                     .thenCompose(res -> 
catalogManager.dropTable(DdlToCatalogCommandConverter.convert((DropTableCommand)
 cmd))
                             
.handle(handleModificationResult(((DropTableCommand) cmd).ifTableExists(), 
TableNotFoundException.class))
                     );
+        } else if (cmd instanceof AlterTableAddCommand) {
+            AlterTableAddCommand addCommand = (AlterTableAddCommand) cmd;
+
+            return ddlCommandFuture
+                    .thenCompose(res -> 
catalogManager.addColumn(DdlToCatalogCommandConverter.convert(addCommand))
+                            
.handle(handleModificationResult(addCommand.ifTableExists(), 
TableNotFoundException.class))
+                    );
+        } else if (cmd instanceof AlterTableDropCommand) {
+            AlterTableDropCommand dropCommand = (AlterTableDropCommand) cmd;
+
+            return ddlCommandFuture
+                    .thenCompose(res -> 
catalogManager.dropColumn(DdlToCatalogCommandConverter.convert(dropCommand))
+                            
.handle(handleModificationResult(dropCommand.ifTableExists(), 
TableNotFoundException.class))
+                    );
         }
 
         return ddlCommandFuture;
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ddl/DdlToCatalogCommandConverter.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ddl/DdlToCatalogCommandConverter.java
index e0e9f4f520..07f875b872 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ddl/DdlToCatalogCommandConverter.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ddl/DdlToCatalogCommandConverter.java
@@ -19,10 +19,14 @@ package org.apache.ignite.internal.sql.engine.exec.ddl;
 
 import java.util.List;
 import java.util.stream.Collectors;
+import org.apache.ignite.internal.catalog.commands.AlterTableAddColumnParams;
+import org.apache.ignite.internal.catalog.commands.AlterTableDropColumnParams;
 import org.apache.ignite.internal.catalog.commands.ColumnParams;
 import org.apache.ignite.internal.catalog.commands.CreateTableParams;
 import org.apache.ignite.internal.catalog.commands.DefaultValue;
 import org.apache.ignite.internal.catalog.commands.DropTableParams;
+import org.apache.ignite.internal.sql.engine.prepare.ddl.AlterTableAddCommand;
+import org.apache.ignite.internal.sql.engine.prepare.ddl.AlterTableDropCommand;
 import org.apache.ignite.internal.sql.engine.prepare.ddl.ColumnDefinition;
 import org.apache.ignite.internal.sql.engine.prepare.ddl.CreateTableCommand;
 import 
org.apache.ignite.internal.sql.engine.prepare.ddl.DefaultValueDefinition;
@@ -39,6 +43,7 @@ class DdlToCatalogCommandConverter {
         return CreateTableParams.builder()
                 .schemaName(cmd.schemaName())
                 .tableName(cmd.tableName())
+                .ifTableExists(cmd.ifTableExists())
 
                 .columns(columns)
                 .colocationColumns(cmd.colocationColumns())
@@ -53,11 +58,42 @@ class DdlToCatalogCommandConverter {
         return DropTableParams.builder()
                 .schemaName(cmd.schemaName())
                 .tableName(cmd.tableName())
+                .ifTableExists(cmd.ifTableExists())
                 .build();
     }
 
+    static AlterTableAddColumnParams convert(AlterTableAddCommand cmd) {
+        List<ColumnParams> columns = 
cmd.columns().stream().map(DdlToCatalogCommandConverter::convert).collect(Collectors.toList());
+
+        return AlterTableAddColumnParams.builder()
+                .schemaName(cmd.schemaName())
+                .tableName(cmd.tableName())
+                .ifTableExists(cmd.ifTableExists())
+
+                .columns(columns)
+
+                .build();
+    }
+
+    static AlterTableDropColumnParams convert(AlterTableDropCommand cmd) {
+        return AlterTableDropColumnParams.builder()
+                .schemaName(cmd.schemaName())
+                .tableName(cmd.tableName())
+                .ifTableExists(cmd.ifTableExists())
+
+                .columns(cmd.columns())
+
+                .build();
+    }
+
+
     private static ColumnParams convert(ColumnDefinition def) {
-        return new ColumnParams(def.name(), TypeUtils.columnType(def.type()), 
convert(def.defaultValueDefinition()), def.nullable());
+        return ColumnParams.builder()
+                .name(def.name())
+                .type(TypeUtils.columnType(def.type()))
+                .nullable(def.nullable())
+                .defaultValue(convert(def.defaultValueDefinition()))
+                .build();
     }
 
     private static DefaultValue convert(DefaultValueDefinition def) {

Reply via email to