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

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

commit 45b86ef28402a01a35456e8f84fd337a3216702e
Author: amashenkov <[email protected]>
AuthorDate: Thu May 18 16:48:03 2023 +0300

    Add create/drop columns commands.
---
 .../internal/catalog/CatalogServiceImpl.java       | 139 +++++++++++++-
 .../commands/AlterTableAddColumnParams.java        |   4 +
 .../commands/AlterTableDropColumnParams.java       |   8 +
 .../internal/catalog/commands/CatalogUtils.java    |   8 +-
 .../internal/catalog/commands/ColumnParams.java    |  88 ++++++++-
 .../catalog/descriptors/HashIndexDescriptor.java   |  19 +-
 .../catalog/descriptors/IndexDescriptor.java       |  20 +-
 .../catalog/descriptors/SortedIndexDescriptor.java |  22 ++-
 .../catalog/descriptors/TableDescriptor.java       |  44 +++--
 .../DropColumnsEntry.java}                         |  40 ++--
 .../NewColumnsEntry.java}                          |  41 ++---
 .../internal/catalog/CatalogServiceSelfTest.java   | 205 ++++++++++++++++++++-
 .../runner/app/ItSchemaChangeTableViewTest.java    |   3 +
 .../engine/exec/ddl/DdlCommandHandlerWrapper.java  |  20 ++
 .../exec/ddl/DdlToCatalogCommandConverter.java     |  36 +++-
 .../distributed/schema/FullTableSchemaTest.java    |   2 +-
 16 files changed, 605 insertions(+), 94 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..b00da5d9fa 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,27 @@ 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.CatalogEvent;
 import org.apache.ignite.internal.catalog.events.CatalogEventParameters;
 import org.apache.ignite.internal.catalog.events.CreateTableEventParameters;
 import org.apache.ignite.internal.catalog.events.DropTableEventParameters;
+import org.apache.ignite.internal.catalog.storage.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,7 +58,10 @@ 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.IgniteInternalException;
 import org.apache.ignite.lang.TableAlreadyExistsException;
@@ -61,7 +70,6 @@ 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 +213,86 @@ 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);
+        } else if (params.columns().size() > 1 && params.ifColumnNotExists()) {
+            return failedFuture(new UnsupportedOperationException("Clause 'IF 
NOT EXISTS' is not supported when adding multiple columns."));
+        }
+
+        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);
+        } else if (params.columns().size() > 1 && params.ifColumnExists()) {
+            return failedFuture(new UnsupportedOperationException("Clause 'IF 
NOT EXISTS' is not supported when adding multiple columns."));
+        }
+
+        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());
+            }
+
+            Arrays.stream(schema.indexes())
+                    .filter(index -> index.tableId() == table.id())
+                    .flatMap(index -> index.columns().stream())
+                    .filter(col -> params.columns().contains(col))
+                    .findAny()
+                    .ifPresent(columnName -> {
+                        throw new IllegalArgumentException("Can't drop indexed 
column: column=" + columnName);
+                    });
+
+            for (String columnName : params.columns()) {
+                if (table.column(columnName) == null) {
+                    throw new ColumnNotFoundException(columnName);
+                }
+                if (table.isPrimaryKeyColumn(columnName)) {
+                    throw new IllegalArgumentException("Can't drop primary key 
column: column=" + columnName);
+                }
+            }
+
+            Set<String> columnsToDrop = table.columns().stream()
+                    .map(TableColumnDescriptor::name)
+                    .filter(col -> params.columns().contains(col))
+                    .collect(Collectors.toSet());
+
+            return List.of(
+                    new DropColumnsEntry(table.id(), columnsToDrop)
+            );
+        });
     }
 
     private void registerCatalog(Catalog newCatalog) {
@@ -307,7 +388,59 @@ 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()
+                            )
+                    );
+                } 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()
+                            )
+                    );
                 } 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..8bc80afe68 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,6 +23,7 @@ import java.util.List;
  * ALTER TABLE ... ADD COLUMN statement.
  */
 public class AlterTableAddColumnParams extends AbstractTableCommandParams {
+    /** Creates parameters builder. */
     public static Builder builder() {
         return new Builder();
     }
@@ -33,6 +34,9 @@ public class AlterTableAddColumnParams extends 
AbstractTableCommandParams {
     /** Columns. */
     private List<ColumnParams> cols;
 
+    /**
+     * Gets columns that should be added to a table.
+     */
     public List<ColumnParams> columns() {
         return cols;
     }
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..20802a9eef 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,12 +25,20 @@ import java.util.Set;
  */
 @SuppressWarnings("AssignmentOrReturnOfFieldWithMutableType")
 public class AlterTableDropColumnParams extends AbstractTableCommandParams {
+    /** Creates parameters builder. */
+    public static Builder builder() {
+        return new Builder();
+    }
+
     /** Quietly ignore this command if column is not exist. */
     private boolean ifColumnExists;
 
     /** Columns. */
     private Set<String> cols;
 
+    /**
+     * Gets columns that should be dropped from a table.
+     */
     public Set<String> columns() {
         return Collections.unmodifiableSet(cols);
     }
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..2b108c2220 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,21 +17,29 @@
 
 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;
+public class ColumnParams {
+    /** Creates parameters builder. */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /** Column name. */
+    private String name;
 
-    private final String name;
+    /** Column type. */
+    private ColumnType type;
 
-    private final ColumnType type;
+    /** Nullability flag. */
+    private boolean nullable;
 
-    private final boolean nullable;
+    /** Column default value. */
+    private DefaultValue defaultValueDefinition;
 
-    private final DefaultValue defaultValueDefinition;
+    private ColumnParams() {}
 
     /** Creates a column definition. */
     public ColumnParams(String name, ColumnType type, DefaultValue 
defaultValueDefinition, boolean nullable) {
@@ -86,4 +94,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..ac94378223 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,9 +17,7 @@
 
 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;
 
 /**
@@ -28,8 +26,6 @@ import org.apache.ignite.internal.tostring.S;
 public class HashIndexDescriptor extends IndexDescriptor {
     private static final long serialVersionUID = -6784028115063219759L;
 
-    private final List<String> columns;
-
     /**
      * Constructs a hash index descriptor.
      *
@@ -37,22 +33,13 @@ public class HashIndexDescriptor extends IndexDescriptor {
      * @param name Name of the index.
      * @param tableId Id of the table index belongs to.
      * @param columns A list of indexed columns. Must not contains duplicates.
+     * @param unique Unique flag.
      * @throws IllegalArgumentException If columns list contains duplicates.
      */
-    public HashIndexDescriptor(int id, String name, int tableId, List<String> 
columns) {
-        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");
-        }
+    public HashIndexDescriptor(int id, String name, int tableId, List<String> 
columns, boolean unique) {
+        super(id, name, tableId, columns, unique);
     }
 
-    /** Returns indexed columns. */
-    public List<String> columns() {
-        return columns;
-    }
 
     /** {@inheritDoc} */
     @Override
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..c365a6446f 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
@@ -17,6 +17,9 @@
 
 package org.apache.ignite.internal.catalog.descriptors;
 
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
 import org.apache.ignite.internal.tostring.S;
 
 /**
@@ -29,21 +32,34 @@ public abstract class IndexDescriptor extends 
ObjectDescriptor {
     private final int tableId;
 
     /** Unique constraint flag. */
-    private boolean unique;
+    private final boolean unique;
+
+    /** Index columns. */
+    private final List<String> columns;
 
     /** Write only flag. {@code True} when index is building. */
     private boolean writeOnly;
 
-    IndexDescriptor(int id, String name, int tableId, boolean unique) {
+    IndexDescriptor(int id, String name, int tableId, List<String> columns, 
boolean unique) {
         super(id, Type.INDEX, name);
         this.tableId = tableId;
         this.unique = unique;
+        this.columns = Objects.requireNonNull(columns, "columns");
+
+        if (Set.copyOf(this.columns).size() != this.columns.size()) {
+            throw new IllegalArgumentException("Indexed columns should be 
unique");
+        }
     }
 
     public int tableId() {
         return tableId;
     }
 
+    /** Returns indexed columns. */
+    public List<String> columns() {
+        return columns;
+    }
+
     public boolean unique() {
         return unique;
     }
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..858cbea637 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
@@ -20,6 +20,8 @@ package org.apache.ignite.internal.catalog.descriptors;
 
 import java.util.List;
 import java.util.Objects;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
 import org.apache.ignite.internal.tostring.S;
 
 /**
@@ -28,7 +30,7 @@ import org.apache.ignite.internal.tostring.S;
 public class SortedIndexDescriptor extends IndexDescriptor {
     private static final long serialVersionUID = 2085714310150728611L;
 
-    private final List<IndexColumnDescriptor> columns;
+    private final List<IndexColumnDescriptor> columnDescriptors;
 
     /**
      * Constructs a sorted description.
@@ -36,17 +38,23 @@ public class SortedIndexDescriptor extends IndexDescriptor {
      * @param id Id of the index.
      * @param name Name of the index.
      * @param tableId Id of the table index belongs to.
-     * @param columns A list of columns descriptors.
+     * @param unique Unique flag.
+     * @param columns A list of columns names.
+     * @param collations A list of columns collations.
      * @throws IllegalArgumentException If columns list contains duplicates or 
columns size doesn't match the collations size.
      */
-    public SortedIndexDescriptor(int id, String name, int tableId, 
List<IndexColumnDescriptor> columns) {
-        super(id, name, tableId, false);
+    public SortedIndexDescriptor(int id, String name, int tableId, boolean 
unique, List<String> columns, List<ColumnCollation> collations) {
+        super(id, name, tableId, columns, unique);
 
-        this.columns = Objects.requireNonNull(columns, "columns");
+        assert collations.size() == columns.size();
+
+        this.columnDescriptors = IntStream.range(0, 
Objects.requireNonNull(collations, "collations").size())
+                .mapToObj(i -> new IndexColumnDescriptor(columns.get(i), 
collations.get(i)))
+                .collect(Collectors.toList());
     }
 
-    public List<IndexColumnDescriptor> columns() {
-        return columns;
+    public List<IndexColumnDescriptor> columnsDecsriptors() {
+        return columnDescriptors;
     }
 
     /** {@inheritDoc} */
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/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..ab77a41205 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 addition of a new 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 A 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..04c352c4e9 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 a 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 A 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..6b744176ef 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,6 +28,7 @@ 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;
@@ -36,12 +37,16 @@ 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.CatalogEvent;
 import org.apache.ignite.internal.catalog.events.CatalogEventParameters;
@@ -58,6 +63,8 @@ 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;
@@ -76,6 +83,9 @@ import org.mockito.Mockito;
 public class CatalogServiceSelfTest {
     private static final String TABLE_NAME = "myTable";
     private static final String TABLE_NAME_2 = "myTable2";
+    public static final String COLUMN_NAME = "VAL";
+    private static final String NEW_COLUMN_NAME = "NEWCOL";
+    private static final String NEW_COLUMN_NAME_2 = "NEWCOL2";
 
     private MetaStorageManager metastore;
 
@@ -234,12 +244,14 @@ 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();
 
+        Thread.sleep(5);
+
         DropTableParams dropTableParams = 
DropTableParams.builder().schemaName("PUBLIC").tableName(TABLE_NAME).build();
 
         assertThat(service.dropTable(dropTableParams), willBe((Object) null));
@@ -305,6 +317,197 @@ 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()
+                .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()
+                .tableName(TABLE_NAME)
+                .columns(Set.of(COLUMN_NAME))
+                .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(COLUMN_NAME));
+
+        // Validate actual catalog
+        schema = service.activeSchema(System.currentTimeMillis());
+        assertNotNull(schema);
+        assertNotNull(schema.table(TABLE_NAME));
+
+        assertNull(schema.table(TABLE_NAME).column(COLUMN_NAME));
+    }
+
+    @Test
+    public void testAddColumnIfExists() {
+        assertThat(service.createTable(simpleTable(TABLE_NAME)), 
willBe((Object) null));
+
+        AlterTableAddColumnParams params = AlterTableAddColumnParams.builder()
+                .tableName(TABLE_NAME)
+                
.columns(List.of(ColumnParams.builder().name(NEW_COLUMN_NAME).type(ColumnType.INT32).nullable(true).build()))
+                .ifColumnNotExists(false)
+                .build();
+
+        assertThat(service.addColumn(params), willBe((Object) null));
+        assertThat(service.addColumn(params), 
willThrow(ColumnAlreadyExistsException.class));
+
+        params = AlterTableAddColumnParams.builder()
+                .tableName(TABLE_NAME)
+                
.columns(List.of(ColumnParams.builder().name(NEW_COLUMN_NAME).type(ColumnType.INT32).nullable(true).build()))
+                .ifColumnNotExists(true)
+                .build();
+
+        assertThat(service.addColumn(params), 
willThrow(ColumnAlreadyExistsException.class));
+    }
+
+    @Test
+    public void testAddDropMultipleColumns() {
+        assertThat(service.createTable(simpleTable(TABLE_NAME)), 
willBe((Object) null));
+
+        // Try to add multiple columns with 'IF NOT EXISTS' clause
+        AlterTableAddColumnParams addColumnParams = 
AlterTableAddColumnParams.builder()
+                .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()
+                ))
+                .ifColumnNotExists(true)
+                .build();
+
+        assertThat(service.addColumn(addColumnParams), 
willThrow(UnsupportedOperationException.class));
+
+        // Validate no column added.
+        SchemaDescriptor schema = 
service.activeSchema(System.currentTimeMillis());
+
+        assertNull(schema.table(TABLE_NAME).column(NEW_COLUMN_NAME));
+        assertNull(schema.table(TABLE_NAME).column(NEW_COLUMN_NAME_2));
+
+        // Add multiple columns.
+        addColumnParams = AlterTableAddColumnParams.builder()
+                .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()
+                ))
+                .ifColumnNotExists(false)
+                .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));
+
+        // Try to drop multiple columns with 'IF NOT EXISTS' clause
+        AlterTableDropColumnParams dropColumnParams = 
AlterTableDropColumnParams.builder()
+                .tableName(TABLE_NAME)
+                .columns(Set.of(NEW_COLUMN_NAME, NEW_COLUMN_NAME_2))
+                .ifColumnExists(true)
+                .build();
+
+        assertThat(service.dropColumn(dropColumnParams), 
willThrow(UnsupportedOperationException.class));
+
+        // Validate no column dropped.
+        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.
+        dropColumnParams = AlterTableDropColumnParams.builder()
+                .tableName(TABLE_NAME)
+                .columns(Set.of(NEW_COLUMN_NAME, NEW_COLUMN_NAME_2))
+                .ifColumnExists(false)
+                .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 adding of existed column
+        addColumnParams = AlterTableAddColumnParams.builder()
+                .tableName(TABLE_NAME)
+                .columns(List.of(
+                        
ColumnParams.builder().name(NEW_COLUMN_NAME).type(ColumnType.INT32).nullable(true).build(),
+                        
ColumnParams.builder().name(COLUMN_NAME).type(ColumnType.INT32).nullable(true).build()
+                ))
+                .build();
+
+        assertThat(service.addColumn(addColumnParams), 
willThrow(ColumnAlreadyExistsException.class));
+
+        // Check dropping of non-existing column
+        dropColumnParams = AlterTableDropColumnParams.builder()
+                .tableName(TABLE_NAME)
+                .columns(Set.of(NEW_COLUMN_NAME, COLUMN_NAME))
+                .build();
+
+        assertThat(service.dropColumn(dropColumnParams), 
willThrow(ColumnNotFoundException.class));
+    }
+
     @Test
     public void operationWillBeRetriedFiniteAmountOfTimes() {
         UpdateLog updateLogMock = Mockito.mock(UpdateLog.class);
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/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..f97ce9c8ed 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ddl/DdlCommandHandlerWrapper.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ddl/DdlCommandHandlerWrapper.java
@@ -22,11 +22,15 @@ import java.util.concurrent.CompletableFuture;
 import org.apache.ignite.internal.catalog.CatalogManager;
 import org.apache.ignite.internal.distributionzones.DistributionZoneManager;
 import org.apache.ignite.internal.index.IndexManager;
+import org.apache.ignite.internal.sql.engine.prepare.ddl.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;
 import org.apache.ignite.internal.storage.DataStorageManager;
 import org.apache.ignite.internal.table.distributed.TableManager;
+import org.apache.ignite.lang.ColumnAlreadyExistsException;
+import org.apache.ignite.lang.ColumnNotFoundException;
 import org.apache.ignite.lang.TableAlreadyExistsException;
 import org.apache.ignite.lang.TableNotFoundException;
 
@@ -70,6 +74,22 @@ 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))
+                            
.handle(handleModificationResult(addCommand.ifColumnNotExists(), 
ColumnAlreadyExistsException.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))
+                            
.handle(handleModificationResult(dropCommand.ifColumnExists(), 
ColumnNotFoundException.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..bce839b04c 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;
@@ -56,8 +60,38 @@ class DdlToCatalogCommandConverter {
                 .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())
+
+                .columns(columns)
+                .ifColumnNotExists(cmd.ifColumnNotExists())
+
+                .build();
+    }
+
+    static AlterTableDropColumnParams convert(AlterTableDropCommand cmd) {
+        return AlterTableDropColumnParams.builder()
+                .schemaName(cmd.schemaName())
+                .tableName(cmd.tableName())
+
+                .columns(cmd.columns())
+                .ifColumnExists(cmd.ifColumnExists())
+
+                .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) {
diff --git 
a/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/schema/FullTableSchemaTest.java
 
b/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/schema/FullTableSchemaTest.java
index fbe2ce2830..5b3d44d615 100644
--- 
a/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/schema/FullTableSchemaTest.java
+++ 
b/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/schema/FullTableSchemaTest.java
@@ -47,7 +47,7 @@ class FullTableSchemaTest {
 
     @NotNull
     private static HashIndexDescriptor someIndex(int id, String name) {
-        return new HashIndexDescriptor(id, name, 1, List.of("a"));
+        return new HashIndexDescriptor(id, name, 1, List.of("a"), true);
     }
 
     @NotNull

Reply via email to