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

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


The following commit(s) were added to refs/heads/main by this push:
     new 1540b414c12 IGNITE-24632 add CatalogTableDescriptor.Builder (#6648)
1540b414c12 is described below

commit 1540b414c1244ed112db504278715fdd4e3fe07c
Author: Viacheslav Blinov <[email protected]>
AuthorDate: Mon Sep 29 13:42:03 2025 +0300

    IGNITE-24632 add CatalogTableDescriptor.Builder (#6648)
---
 .../catalog/commands/CreateTableCommand.java       |  22 +-
 .../internal/catalog/commands/DefaultValue.java    |   2 +-
 .../descriptors/CatalogTableDescriptor.java        | 305 ++++++++++++++++-----
 .../CatalogTableDescriptorSerializers.java         |  52 ++--
 .../internal/catalog/storage/AlterColumnEntry.java |  43 +--
 .../internal/catalog/storage/DropColumnsEntry.java |  42 +--
 .../internal/catalog/storage/NewColumnsEntry.java  |  37 +--
 .../internal/catalog/storage/RenameTableEntry.java |  36 +--
 .../{RenameTableEntry.java => UpdateTable.java}    |  79 +++---
 .../ignite/internal/catalog/CatalogTableTest.java  |  11 +-
 .../commands/AbstractCommandValidationTest.java    |  21 +-
 .../catalog/commands/CatalogUtilsTest.java         |  12 +-
 .../descriptors/CatalogTableDescriptorTest.java    | 134 ++++++++-
 .../storage/CatalogEntrySerializationTest.java     |  22 +-
 .../catalog/storage/TestTableDescriptors.java      | 124 +++++----
 .../ignite/client/handler/FakeCatalogService.java  |  23 +-
 .../RebalanceUtilUpdateAssignmentsTest.java        |  30 +-
 .../schema/CatalogValidationSchemasSource.java     |  12 +-
 .../schemacompat/SchemaCompatibilityValidator.java |   8 +-
 .../schema/CatalogValidationSchemasSourceTest.java |  20 +-
 .../ignite/internal/schema/SchemaManager.java      |   2 +-
 .../apache/ignite/internal/schema/SchemaUtils.java |   2 +-
 .../ignite/internal/schema/SchemaManagerTest.java  |  45 +--
 .../CatalogToSchemaDescriptorConverterTest.java    |  37 +--
 .../sql/engine/schema/SqlSchemaManagerImpl.java    |   6 +-
 .../statistic/SqlStatisticManagerImplTest.java     |  49 +++-
 .../internal/storage/BaseMvTableStorageTest.java   |  33 +--
 .../storage/index/AbstractIndexStorageTest.java    |  28 +-
 .../table/distributed/index/IndexMeta.java         |   2 +-
 .../replicator/PartitionReplicaListener.java       |   2 +-
 .../distributed/schema/SchemaVersionsImpl.java     |   2 +-
 .../index/BaseIndexMetaStorageTest.java            |   2 +-
 .../raft/PartitionCommandListenerTest.java         |   2 +-
 .../PartitionReplicaListenerIndexLockingTest.java  |   2 +-
 ...itionReplicaListenerSortedIndexLockingTest.java |   2 +-
 .../replication/PartitionReplicaListenerTest.java  |  41 +--
 .../ZonePartitionReplicaListenerTest.java          |  37 ++-
 .../apache/ignite/distributed/ItTxTestCluster.java |   2 +-
 .../table/impl/DummyInternalTableImpl.java         |   2 +-
 39 files changed, 800 insertions(+), 533 deletions(-)

diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateTableCommand.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateTableCommand.java
index 5eeb58b2271..5fdb49c48c7 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateTableCommand.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateTableCommand.java
@@ -137,17 +137,17 @@ public class CreateTableCommand extends 
AbstractTableCommand {
         int tableId = id++;
         int pkIndexId = id++;
 
-        CatalogTableDescriptor table = new CatalogTableDescriptor(
-                tableId,
-                schema.id(),
-                pkIndexId,
-                tableName,
-                zone.id(),
-                
columns.stream().map(CatalogUtils::fromParams).collect(toList()),
-                primaryKey.columns(),
-                colocationColumns,
-                storageProfile
-        );
+        CatalogTableDescriptor table = CatalogTableDescriptor.builder()
+                .id(tableId)
+                .schemaId(schema.id())
+                .primaryKeyIndexId(pkIndexId)
+                .name(tableName)
+                .zoneId(zone.id())
+                
.columns(columns.stream().map(CatalogUtils::fromParams).collect(toList()))
+                .primaryKeyColumns(primaryKey.columns())
+                .colocationColumns(colocationColumns)
+                .storageProfile(storageProfile)
+                .build();
 
         String indexName = primaryKey.name();
         if (indexName == null) {
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DefaultValue.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DefaultValue.java
index d1bb90a2949..71cffaf8cac 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DefaultValue.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DefaultValue.java
@@ -166,7 +166,7 @@ public abstract class DefaultValue {
 
             ConstantValue that = (ConstantValue) o;
 
-            return Objects.equals(value, that.value);
+            return Objects.deepEquals(value, that.value);
         }
 
         /** {@inheritDoc} */
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableDescriptor.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableDescriptor.java
index a6cbb0fd739..453276d84cf 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableDescriptor.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableDescriptor.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.internal.catalog.descriptors;
 
 import static 
org.apache.ignite.internal.catalog.CatalogManager.INITIAL_TIMESTAMP;
+import static org.apache.ignite.internal.lang.IgniteStringFormatter.format;
 
 import it.unimi.dsi.fastutil.ints.AbstractInt2ObjectMap.BasicEntry;
 import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
@@ -63,43 +64,6 @@ public class CatalogTableDescriptor extends 
CatalogObjectDescriptor implements M
 
     private final String storageProfile;
 
-    /**
-     * Constructor for new table.
-     *
-     * @param id Table ID.
-     * @param pkIndexId Primary key index ID.
-     * @param name Table name.
-     * @param zoneId Distribution zone ID.
-     * @param columns Table column descriptors.
-     * @param pkCols Primary key column names.
-     * @param storageProfile Storage profile.
-     */
-    public CatalogTableDescriptor(
-            int id,
-            int schemaId,
-            int pkIndexId,
-            String name,
-            int zoneId,
-            List<CatalogTableColumnDescriptor> columns,
-            List<String> pkCols,
-            @Nullable List<String> colocationCols,
-            String storageProfile
-    ) {
-        this(
-                id,
-                schemaId,
-                pkIndexId,
-                name,
-                zoneId,
-                columns,
-                pkCols,
-                colocationCols,
-                new CatalogTableSchemaVersions(new TableVersion(columns)),
-                storageProfile,
-                INITIAL_TIMESTAMP
-        );
-    }
-
     /**
      * Internal constructor.
      *
@@ -112,7 +76,7 @@ public class CatalogTableDescriptor extends 
CatalogObjectDescriptor implements M
      * @param storageProfile Storage profile.
      * @param timestamp Token of the update of the descriptor.
      */
-    CatalogTableDescriptor(
+    private CatalogTableDescriptor(
             int id,
             int schemaId,
             int pkIndexId,
@@ -130,8 +94,8 @@ public class CatalogTableDescriptor extends 
CatalogObjectDescriptor implements M
         this.schemaId = schemaId;
         this.pkIndexId = pkIndexId;
         this.zoneId = zoneId;
-        this.columns = Objects.requireNonNull(columns, "No columns defined.");
-        this.primaryKeyColumns = Objects.requireNonNull(pkCols, "No primary 
key columns.");
+        this.columns = columns;
+        this.primaryKeyColumns = pkCols;
 
         Map<String, Int2ObjectMap.Entry<CatalogTableColumnDescriptor>> 
columnMap = IgniteUtils.newHashMap(columns.size());
         for (int i = 0; i < columns.size(); i++) {
@@ -146,32 +110,27 @@ public class CatalogTableDescriptor extends 
CatalogObjectDescriptor implements M
     }
 
     /**
-     * Creates new table descriptor, using existing one as a template.
+     * Creates a builder of copy of this table descriptor prepopulated with 
parameters of this descriptor.
+     *
+     * @return new Builder.
      */
-    public CatalogTableDescriptor newDescriptor(
-            String name,
-            int tableVersion,
-            List<CatalogTableColumnDescriptor> columns,
-            HybridTimestamp timestamp,
-            String storageProfile
-    ) {
-        CatalogTableSchemaVersions newSchemaVersions = tableVersion == 
schemaVersions.latestVersion()
-                ? schemaVersions
-                : schemaVersions.append(new TableVersion(columns), 
tableVersion);
-
-        return new CatalogTableDescriptor(
-                id(),
-                schemaId,
-                pkIndexId,
-                name,
-                zoneId,
-                columns,
-                primaryKeyColumns,
-                colocationColumns,
-                newSchemaVersions,
-                storageProfile,
-                timestamp
-        );
+    public Builder copyBuilder() {
+        return new Builder()
+                .id(id())
+                .name(name())
+                .timestamp(updateTimestamp())
+                .zoneId(zoneId())
+                .schemaId(schemaId())
+                .primaryKeyIndexId(primaryKeyIndexId())
+                .schemaVersions(schemaVersions)
+                .columns(columns)
+                .primaryKeyColumns(primaryKeyColumns())
+                .colocationColumns(colocationColumns())
+                .storageProfile(storageProfile());
+    }
+
+    public static Builder builder() {
+        return new Builder();
     }
 
     /**
@@ -203,9 +162,9 @@ public class CatalogTableDescriptor extends 
CatalogObjectDescriptor implements M
     }
 
     /**
-     * Returns a version of this table descriptor.
+     * Returns the latest version of this table descriptor schema.
      */
-    public int tableVersion() {
+    public int latestSchemaVersion() {
         return schemaVersions.latestVersion();
     }
 
@@ -282,4 +241,216 @@ public class CatalogTableDescriptor extends 
CatalogObjectDescriptor implements M
         return storageProfile;
     }
 
+    /**
+     * {@code CatalogTableDescriptor} builder static inner class.
+     */
+    public static final class Builder {
+        private int id;
+        private String name;
+        private int zoneId;
+        private int schemaId;
+        private int pkIndexId;
+        private CatalogTableSchemaVersions schemaVersions;
+        private List<CatalogTableColumnDescriptor> columns;
+        private List<String> primaryKeyColumns;
+        @Nullable private List<String> colocationColumns;
+        private String storageProfile;
+        private HybridTimestamp timestamp = INITIAL_TIMESTAMP;
+        private int latestSchemaVersion = 0;
+
+        /**
+         * Sets the {@code id} and returns a reference to this Builder 
enabling method chaining.
+         *
+         * @param id the {@code id} to set
+         * @return a reference to this Builder
+         */
+        public Builder id(int id) {
+            this.id = id;
+            return this;
+        }
+
+        /**
+         * Sets the {@code name} and returns a reference to this Builder 
enabling method chaining.
+         *
+         * @param name the {@code name} to set
+         * @return a reference to this Builder
+         */
+        public Builder name(String name) {
+            this.name = name;
+            return this;
+        }
+
+        /**
+         * Sets the {@code timestamp} and returns a reference to this Builder 
enabling method chaining.
+         *
+         * @param timestamp the {@code timestamp} to set
+         * @return a reference to this Builder
+         */
+        public Builder timestamp(HybridTimestamp timestamp) {
+            this.timestamp = timestamp;
+            return this;
+        }
+
+        /**
+         * Sets the {@code zoneId} and returns a reference to this Builder 
enabling method chaining.
+         *
+         * @param zoneId the {@code zoneId} to set
+         * @return a reference to this Builder
+         */
+        public Builder zoneId(int zoneId) {
+            this.zoneId = zoneId;
+            return this;
+        }
+
+        /**
+         * Sets the {@code schemaId} and returns a reference to this Builder 
enabling method chaining.
+         *
+         * @param schemaId the {@code schemaId} to set
+         * @return a reference to this Builder
+         */
+        public Builder schemaId(int schemaId) {
+            this.schemaId = schemaId;
+            return this;
+        }
+
+        /**
+         * Sets the {@code primaryKeyIndexId} and returns a reference to this 
Builder enabling method chaining.
+         *
+         * @param primaryKeyIndexId the {@code primaryKeyIndexId} to set
+         * @return a reference to this Builder
+         */
+        public Builder primaryKeyIndexId(int primaryKeyIndexId) {
+            this.pkIndexId = primaryKeyIndexId;
+            return this;
+        }
+
+        /**
+         * Sets the {@code schemaVersions} and returns a reference to this 
Builder enabling method chaining.
+         *
+         * @param schemaVersions the {@code schemaVersions} to set
+         * @return a reference to this Builder
+         */
+        public Builder schemaVersions(CatalogTableSchemaVersions 
schemaVersions) {
+            this.schemaVersions = schemaVersions;
+            return this;
+        }
+
+        /**
+         * Sets the {@code columns} and returns a reference to this Builder 
enabling method chaining.
+         *
+         * @param columns the {@code columns} to set
+         * @return a reference to this Builder
+         */
+        public Builder columns(List<CatalogTableColumnDescriptor> columns) {
+            this.columns = columns;
+            return this;
+        }
+
+        /**
+         * Sets the {@code primaryKeyColumns} and returns a reference to this 
Builder enabling method chaining.
+         *
+         * @param primaryKeyColumns the {@code primaryKeyColumns} to set
+         * @return a reference to this Builder
+         */
+        public Builder primaryKeyColumns(List<String> primaryKeyColumns) {
+            this.primaryKeyColumns = primaryKeyColumns;
+            return this;
+        }
+
+        /**
+         * Sets the {@code colocationColumns} and returns a reference to this 
Builder enabling method chaining.
+         *
+         * @param colocationColumns the {@code colocationColumns} to set
+         * @return a reference to this Builder
+         */
+        public Builder colocationColumns(@Nullable List<String> 
colocationColumns) {
+            this.colocationColumns = colocationColumns;
+            return this;
+        }
+
+        /**
+         * Sets the {@code storageProfile} and returns a reference to this 
Builder enabling method chaining.
+         *
+         * @param storageProfile the {@code storageProfile} to set
+         * @return a reference to this Builder
+         */
+        public Builder storageProfile(String storageProfile) {
+            this.storageProfile = storageProfile;
+            return this;
+        }
+
+        /**
+         * Sets the {@code latestSchemaVersion} and returns a reference to 
this Builder enabling method chaining.
+         *
+         * @param latestSchemaVersion the {@code latestSchemaVersion} to set.
+         * @return a reference to this Builder.
+         */
+        public Builder latestSchemaVersion(int latestSchemaVersion) {
+            this.latestSchemaVersion = latestSchemaVersion;
+            return this;
+        }
+
+        /**
+         * Returns a {@code CatalogTableDescriptor} built from the parameters 
previously set.
+         *
+         * @return a {@code CatalogTableDescriptor} built with parameters of 
this {@code CatalogTableDescriptor.Builder}
+         */
+        public CatalogTableDescriptor build() {
+            Objects.requireNonNull(columns, "No columns defined.");
+            if (columns.isEmpty()) {
+                throw new IllegalArgumentException("No columns defined.");
+            }
+
+            Objects.requireNonNull(primaryKeyColumns, "No primary key 
columns.");
+            if (primaryKeyColumns.isEmpty()) {
+                throw new IllegalArgumentException("No primary key columns.");
+            }
+
+            if (schemaVersions == null) {
+                // in case we are creating a new table from scratch, if schema 
version is not defined we make a default one
+                schemaVersions = new CatalogTableSchemaVersions(new 
TableVersion(columns));
+            }
+
+            if (latestSchemaVersion == 0) {
+                latestSchemaVersion = schemaVersions.latestVersion();
+            } else if (latestSchemaVersion < schemaVersions.latestVersion()) {
+                throw new IllegalArgumentException(format(
+                        "Latest schema version {} should not be less than a 
previous version {}.",
+                        latestSchemaVersion,
+                        schemaVersions.latestVersion()
+                ));
+            }
+
+            if (latestSchemaVersion == schemaVersions.latestVersion()) {
+                TableVersion latestTableVersion = 
Objects.requireNonNull(schemaVersions.get(schemaVersions.latestVersion()));
+                if (!Objects.equals(latestTableVersion.columns(), columns)) {
+                    throw new IllegalArgumentException(format(
+                            "Latest schema version columns do not match 
descriptor definition columns. "
+                                    + "Schema columns: {}, table columns: {}.",
+                            latestTableVersion.columns(),
+                            columns
+                    ));
+                }
+            }
+
+            // TODO: https://issues.apache.org/jira/browse/IGNITE-26501
+            CatalogTableSchemaVersions newSchemaVersions = latestSchemaVersion 
== schemaVersions.latestVersion()
+                    ? schemaVersions
+                    : schemaVersions.append(new TableVersion(columns), 
latestSchemaVersion);
+
+            return new CatalogTableDescriptor(
+                    id,
+                    schemaId,
+                    pkIndexId,
+                    name,
+                    zoneId,
+                    columns,
+                    primaryKeyColumns,
+                    colocationColumns,
+                    newSchemaVersions,
+                    storageProfile,
+                    timestamp
+            );
+        }
+    }
 }
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableDescriptorSerializers.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableDescriptorSerializers.java
index 85ca271f11a..4195dff0f41 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableDescriptorSerializers.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableDescriptorSerializers.java
@@ -94,20 +94,20 @@ public class CatalogTableDescriptorSerializers {
                 }
             }
 
-            return new CatalogTableDescriptor(
-                    id,
-                    schemaId,
-                    pkIndexId,
-                    name,
-                    zoneId,
-                    columns,
-                    primaryKeyColumns,
-                    colocationColumns,
-                    schemaVersions,
-                    storageProfile,
+            return CatalogTableDescriptor.builder()
+                    .id(id)
+                    .schemaId(schemaId)
+                    .primaryKeyIndexId(pkIndexId)
+                    .name(name)
+                    .zoneId(zoneId)
+                    .columns(columns)
+                    .primaryKeyColumns(primaryKeyColumns)
+                    .colocationColumns(colocationColumns)
+                    .schemaVersions(schemaVersions)
+                    .storageProfile(storageProfile)
                     // Here we use the initial timestamp because it's old 
storage.
-                    INITIAL_TIMESTAMP
-            );
+                    .timestamp(INITIAL_TIMESTAMP)
+                    .build();
         }
 
         @Override
@@ -233,19 +233,19 @@ public class CatalogTableDescriptorSerializers {
                 }
             }
 
-            return new CatalogTableDescriptor(
-                    id,
-                    schemaId,
-                    pkIndexId,
-                    name,
-                    zoneId,
-                    columns,
-                    primaryKeyColumns,
-                    colocationColumns,
-                    schemaVersions,
-                    storageProfile,
-                    updateTimestamp
-            );
+            return CatalogTableDescriptor.builder()
+                    .id(id)
+                    .schemaId(schemaId)
+                    .primaryKeyIndexId(pkIndexId)
+                    .name(name)
+                    .zoneId(zoneId)
+                    .columns(columns)
+                    .primaryKeyColumns(primaryKeyColumns)
+                    .colocationColumns(colocationColumns)
+                    .schemaVersions(schemaVersions)
+                    .storageProfile(storageProfile)
+                    .timestamp(updateTimestamp)
+                    .build();
         }
 
         @Override
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/AlterColumnEntry.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/AlterColumnEntry.java
index 94fb545ff77..a58c30be0ff 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/AlterColumnEntry.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/AlterColumnEntry.java
@@ -18,27 +18,21 @@
 package org.apache.ignite.internal.catalog.storage;
 
 import static java.util.stream.Collectors.toList;
-import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.defaultZoneIdOpt;
-import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.replaceSchema;
-import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.replaceTable;
-import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.schemaOrThrow;
-import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.tableOrThrow;
 
-import org.apache.ignite.internal.catalog.Catalog;
-import org.apache.ignite.internal.catalog.descriptors.CatalogSchemaDescriptor;
+import java.util.List;
 import 
org.apache.ignite.internal.catalog.descriptors.CatalogTableColumnDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor;
+import 
org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor.Builder;
 import org.apache.ignite.internal.catalog.events.AlterColumnEventParameters;
 import org.apache.ignite.internal.catalog.events.CatalogEvent;
 import org.apache.ignite.internal.catalog.events.CatalogEventParameters;
 import 
org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntryType;
-import org.apache.ignite.internal.hlc.HybridTimestamp;
 import org.apache.ignite.internal.tostring.S;
 
 /**
  * Describes a column replacement.
  */
-public class AlterColumnEntry implements UpdateEntry, Fireable {
+public class AlterColumnEntry extends UpdateTable implements Fireable {
     private final int tableId;
 
     private final CatalogTableColumnDescriptor column;
@@ -55,6 +49,7 @@ public class AlterColumnEntry implements UpdateEntry, 
Fireable {
     }
 
     /** Returns an id the table to be modified. */
+    @Override
     public int tableId() {
         return tableId;
     }
@@ -80,28 +75,14 @@ public class AlterColumnEntry implements UpdateEntry, 
Fireable {
     }
 
     @Override
-    public Catalog applyUpdate(Catalog catalog, HybridTimestamp timestamp) {
-        CatalogTableDescriptor table = tableOrThrow(catalog, tableId);
-        CatalogSchemaDescriptor schema = schemaOrThrow(catalog, 
table.schemaId());
-
-        CatalogTableDescriptor newTable = table.newDescriptor(
-                table.name(),
-                table.tableVersion() + 1,
-                table.columns().stream()
-                        .map(source -> source.name().equals(column.name()) ? 
column : source)
-                        .collect(toList()),
-                timestamp,
-                table.storageProfile()
-        );
-
-        return new Catalog(
-                catalog.version(),
-                catalog.time(),
-                catalog.objectIdGenState(),
-                catalog.zones(),
-                replaceSchema(replaceTable(schema, newTable), 
catalog.schemas()),
-                defaultZoneIdOpt(catalog)
-        );
+    public Builder newTableDescriptor(CatalogTableDescriptor table) {
+        List<CatalogTableColumnDescriptor> updatedTableColumns = 
table.columns().stream()
+                .map(source -> source.name().equals(column.name()) ? column : 
source)
+                .collect(toList());
+
+        return table.copyBuilder()
+                .latestSchemaVersion(newSchemaVersion(table))
+                .columns(updatedTableColumns);
     }
 
     @Override
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropColumnsEntry.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropColumnsEntry.java
index 208329cb532..9a5f1f464f7 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropColumnsEntry.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropColumnsEntry.java
@@ -18,27 +18,22 @@
 package org.apache.ignite.internal.catalog.storage;
 
 import static java.util.stream.Collectors.toList;
-import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.defaultZoneIdOpt;
-import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.replaceSchema;
-import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.replaceTable;
-import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.schemaOrThrow;
-import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.tableOrThrow;
 
+import java.util.List;
 import java.util.Set;
-import org.apache.ignite.internal.catalog.Catalog;
-import org.apache.ignite.internal.catalog.descriptors.CatalogSchemaDescriptor;
+import 
org.apache.ignite.internal.catalog.descriptors.CatalogTableColumnDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor;
+import 
org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor.Builder;
 import org.apache.ignite.internal.catalog.events.CatalogEvent;
 import org.apache.ignite.internal.catalog.events.CatalogEventParameters;
 import org.apache.ignite.internal.catalog.events.DropColumnEventParameters;
 import 
org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntryType;
-import org.apache.ignite.internal.hlc.HybridTimestamp;
 import org.apache.ignite.internal.tostring.S;
 
 /**
  * Describes dropping of columns.
  */
-public class DropColumnsEntry implements UpdateEntry, Fireable {
+public class DropColumnsEntry extends UpdateTable implements Fireable {
     private final int tableId;
     private final Set<String> columns;
 
@@ -54,6 +49,7 @@ public class DropColumnsEntry implements UpdateEntry, 
Fireable {
     }
 
     /** Returns table id. */
+    @Override
     public int tableId() {
         return tableId;
     }
@@ -79,28 +75,14 @@ public class DropColumnsEntry implements UpdateEntry, 
Fireable {
     }
 
     @Override
-    public Catalog applyUpdate(Catalog catalog, HybridTimestamp timestamp) {
-        CatalogTableDescriptor table = tableOrThrow(catalog, tableId);
-        CatalogSchemaDescriptor schema = schemaOrThrow(catalog, 
table.schemaId());
-
-        CatalogTableDescriptor newTable = table.newDescriptor(
-                table.name(),
-                table.tableVersion() + 1,
-                table.columns().stream()
-                        .filter(col -> !columns.contains(col.name()))
-                        .collect(toList()),
-                timestamp,
-                table.storageProfile()
-        );
+    public Builder newTableDescriptor(CatalogTableDescriptor table) {
+        List<CatalogTableColumnDescriptor> updatedTableColumns = 
table.columns().stream()
+                .filter(col -> !columns.contains(col.name()))
+                .collect(toList());
 
-        return new Catalog(
-                catalog.version(),
-                catalog.time(),
-                catalog.objectIdGenState(),
-                catalog.zones(),
-                replaceSchema(replaceTable(schema, newTable), 
catalog.schemas()),
-                defaultZoneIdOpt(catalog)
-        );
+        return table.copyBuilder()
+                .latestSchemaVersion(newSchemaVersion(table))
+                .columns(updatedTableColumns);
     }
 
     @Override
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewColumnsEntry.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewColumnsEntry.java
index 3650341797b..121d20ab1e6 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewColumnsEntry.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewColumnsEntry.java
@@ -17,29 +17,21 @@
 
 package org.apache.ignite.internal.catalog.storage;
 
-import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.defaultZoneIdOpt;
-import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.replaceSchema;
-import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.replaceTable;
-import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.schemaOrThrow;
-import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.tableOrThrow;
-
 import java.util.List;
-import org.apache.ignite.internal.catalog.Catalog;
-import org.apache.ignite.internal.catalog.descriptors.CatalogSchemaDescriptor;
 import 
org.apache.ignite.internal.catalog.descriptors.CatalogTableColumnDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor;
+import 
org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor.Builder;
 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.storage.serialization.MarshallableEntryType;
-import org.apache.ignite.internal.hlc.HybridTimestamp;
 import org.apache.ignite.internal.tostring.S;
 import org.apache.ignite.internal.util.CollectionUtils;
 
 /**
  * Describes addition of new columns.
  */
-public class NewColumnsEntry implements UpdateEntry, Fireable {
+public class NewColumnsEntry extends UpdateTable implements Fireable {
     private final int tableId;
     private final List<CatalogTableColumnDescriptor> descriptors;
 
@@ -55,6 +47,7 @@ public class NewColumnsEntry implements UpdateEntry, Fireable 
{
     }
 
     /** Returns table id. */
+    @Override
     public int tableId() {
         return tableId;
     }
@@ -80,26 +73,12 @@ public class NewColumnsEntry implements UpdateEntry, 
Fireable {
     }
 
     @Override
-    public Catalog applyUpdate(Catalog catalog, HybridTimestamp timestamp) {
-        CatalogTableDescriptor table = tableOrThrow(catalog, tableId);
-        CatalogSchemaDescriptor schema = schemaOrThrow(catalog, 
table.schemaId());
-
-        CatalogTableDescriptor newTable = table.newDescriptor(
-                table.name(),
-                table.tableVersion() + 1,
-                CollectionUtils.concat(table.columns(), descriptors),
-                timestamp,
-                table.storageProfile()
-        );
+    public Builder newTableDescriptor(CatalogTableDescriptor table) {
+        List<CatalogTableColumnDescriptor> updatedTableColumns = 
CollectionUtils.concat(table.columns(), descriptors);
 
-        return new Catalog(
-                catalog.version(),
-                catalog.time(),
-                catalog.objectIdGenState(),
-                catalog.zones(),
-                replaceSchema(replaceTable(schema, newTable), 
catalog.schemas()),
-                defaultZoneIdOpt(catalog)
-        );
+        return table.copyBuilder()
+                .latestSchemaVersion(newSchemaVersion(table))
+                .columns(updatedTableColumns);
     }
 
     @Override
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/RenameTableEntry.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/RenameTableEntry.java
index 499db4ee611..532997a3e54 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/RenameTableEntry.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/RenameTableEntry.java
@@ -17,23 +17,15 @@
 
 package org.apache.ignite.internal.catalog.storage;
 
-import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.defaultZoneIdOpt;
-import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.replaceSchema;
-import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.replaceTable;
-import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.schemaOrThrow;
-import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.tableOrThrow;
-
-import org.apache.ignite.internal.catalog.Catalog;
-import org.apache.ignite.internal.catalog.descriptors.CatalogSchemaDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor;
+import 
org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor.Builder;
 import org.apache.ignite.internal.catalog.events.CatalogEvent;
 import org.apache.ignite.internal.catalog.events.CatalogEventParameters;
 import org.apache.ignite.internal.catalog.events.RenameTableEventParameters;
 import 
org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntryType;
-import org.apache.ignite.internal.hlc.HybridTimestamp;
 
 /** Entry representing a rename of a table. */
-public class RenameTableEntry implements UpdateEntry, Fireable {
+public class RenameTableEntry extends UpdateTable implements Fireable {
     private final int tableId;
 
     private final String newTableName;
@@ -43,6 +35,7 @@ public class RenameTableEntry implements UpdateEntry, 
Fireable {
         this.newTableName = newTableName;
     }
 
+    @Override
     public int tableId() {
         return tableId;
     }
@@ -67,25 +60,8 @@ public class RenameTableEntry implements UpdateEntry, 
Fireable {
     }
 
     @Override
-    public Catalog applyUpdate(Catalog catalog, HybridTimestamp timestamp) {
-        CatalogTableDescriptor table = tableOrThrow(catalog, tableId);
-        CatalogSchemaDescriptor schema = schemaOrThrow(catalog, 
table.schemaId());
-
-        CatalogTableDescriptor newTable = table.newDescriptor(
-                newTableName,
-                table.tableVersion() + 1,
-                table.columns(),
-                timestamp,
-                table.storageProfile()
-        );
-
-        return new Catalog(
-                catalog.version(),
-                catalog.time(),
-                catalog.objectIdGenState(),
-                catalog.zones(),
-                replaceSchema(replaceTable(schema, newTable), 
catalog.schemas()),
-                defaultZoneIdOpt(catalog)
-        );
+    public Builder newTableDescriptor(CatalogTableDescriptor table) {
+        return table.copyBuilder()
+                .name(newTableName);
     }
 }
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/RenameTableEntry.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/UpdateTable.java
similarity index 55%
copy from 
modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/RenameTableEntry.java
copy to 
modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/UpdateTable.java
index 499db4ee611..35ccda098e5 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/RenameTableEntry.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/UpdateTable.java
@@ -26,66 +26,49 @@ import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.tableOrTh
 import org.apache.ignite.internal.catalog.Catalog;
 import org.apache.ignite.internal.catalog.descriptors.CatalogSchemaDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor;
-import org.apache.ignite.internal.catalog.events.CatalogEvent;
-import org.apache.ignite.internal.catalog.events.CatalogEventParameters;
-import org.apache.ignite.internal.catalog.events.RenameTableEventParameters;
-import 
org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntryType;
 import org.apache.ignite.internal.hlc.HybridTimestamp;
 
-/** Entry representing a rename of a table. */
-public class RenameTableEntry implements UpdateEntry, Fireable {
-    private final int tableId;
-
-    private final String newTableName;
-
-    public RenameTableEntry(int tableId, String newTableName) {
-        this.tableId = tableId;
-        this.newTableName = newTableName;
-    }
-
-    public int tableId() {
-        return tableId;
-    }
-
-    public String newTableName() {
-        return newTableName;
-    }
-
-    @Override
-    public int typeId() {
-        return MarshallableEntryType.RENAME_TABLE.id();
-    }
-
-    @Override
-    public CatalogEvent eventType() {
-        return CatalogEvent.TABLE_ALTER;
-    }
-
-    @Override
-    public CatalogEventParameters createEventParameters(long causalityToken, 
int catalogVersion) {
-        return new RenameTableEventParameters(causalityToken, catalogVersion, 
tableId, newTableName);
-    }
-
+/**
+ * This class declares an abstract method for creating a new table descriptor 
and
+ * provides default implementation for applying the table descriptor changes 
to a catalog.
+ */
+abstract class UpdateTable implements UpdateEntry {
     @Override
-    public Catalog applyUpdate(Catalog catalog, HybridTimestamp timestamp) {
-        CatalogTableDescriptor table = tableOrThrow(catalog, tableId);
+    public final Catalog applyUpdate(Catalog catalog, HybridTimestamp 
timestamp) {
+        CatalogTableDescriptor table = tableOrThrow(catalog, tableId());
         CatalogSchemaDescriptor schema = schemaOrThrow(catalog, 
table.schemaId());
 
-        CatalogTableDescriptor newTable = table.newDescriptor(
-                newTableName,
-                table.tableVersion() + 1,
-                table.columns(),
-                timestamp,
-                table.storageProfile()
-        );
+        CatalogTableDescriptor modifiedTable = newTableDescriptor(table)
+                .timestamp(timestamp)
+                .build();
+
+        CatalogSchemaDescriptor modifiedSchemaDescriptor = 
replaceTable(schema, modifiedTable);
 
         return new Catalog(
                 catalog.version(),
                 catalog.time(),
                 catalog.objectIdGenState(),
                 catalog.zones(),
-                replaceSchema(replaceTable(schema, newTable), 
catalog.schemas()),
+                replaceSchema(modifiedSchemaDescriptor, catalog.schemas()),
                 defaultZoneIdOpt(catalog)
         );
     }
+
+    /**
+     * Creates a {@link CatalogTableDescriptor.Builder} with modifications of 
this table.
+     * The timestamp of this table will be updated automatically.
+     *
+     * @param table previous table descriptor definition.
+     *
+     * @return builder with the updated fields for this table descriptor, 
created with {@link CatalogTableDescriptor#copyBuilder()}
+     **/
+    abstract CatalogTableDescriptor.Builder 
newTableDescriptor(CatalogTableDescriptor table);
+
+    /** Returns table id for a table affected by an update table command. */
+    abstract int tableId();
+
+    /** Returns a new schema version for provided {@link 
CatalogTableDescriptor}. */
+    static int newSchemaVersion(CatalogTableDescriptor table) {
+        return table.latestSchemaVersion() + 1;
+    }
 }
diff --git 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogTableTest.java
 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogTableTest.java
index d307d0163fa..bd4f80eea30 100644
--- 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogTableTest.java
+++ 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogTableTest.java
@@ -571,8 +571,6 @@ public class CatalogTableTest extends 
BaseCatalogManagerTest {
         assertThat(table(prevVersion, TABLE_NAME_2), is(nullValue()));
         assertThat(table(curVersion, TABLE_NAME), is(nullValue()));
 
-        assertThat(curDescriptor.tableVersion(), 
is(prevDescriptor.tableVersion() + 1));
-
         // Assert that all other properties have been left intact.
         assertThat(curDescriptor.id(), is(prevDescriptor.id()));
         assertThat(curDescriptor.columns(), is(prevDescriptor.columns()));
@@ -580,6 +578,7 @@ public class CatalogTableTest extends 
BaseCatalogManagerTest {
         assertThat(curDescriptor.primaryKeyColumns(), 
is(prevDescriptor.primaryKeyColumns()));
         assertThat(curDescriptor.primaryKeyIndexId(), 
is(prevDescriptor.primaryKeyIndexId()));
         assertThat(curDescriptor.schemaId(), is(prevDescriptor.schemaId()));
+        assertThat(curDescriptor.latestSchemaVersion(), 
is(prevDescriptor.latestSchemaVersion()));
     }
 
     @Test
@@ -610,7 +609,7 @@ public class CatalogTableTest extends 
BaseCatalogManagerTest {
 
         CatalogTableDescriptor table = actualTable(TABLE_NAME);
 
-        assertThat(table.tableVersion(), is(2));
+        assertThat(table.latestSchemaVersion(), is(2));
     }
 
     @Test
@@ -619,7 +618,7 @@ public class CatalogTableTest extends 
BaseCatalogManagerTest {
 
         CatalogTableDescriptor table = actualTable(TABLE_NAME);
 
-        assertThat(table.tableVersion(), is(1));
+        assertThat(table.latestSchemaVersion(), is(1));
     }
 
     @Test
@@ -630,7 +629,7 @@ public class CatalogTableTest extends 
BaseCatalogManagerTest {
 
         CatalogTableDescriptor table = actualTable(TABLE_NAME);
 
-        assertThat(table.tableVersion(), is(2));
+        assertThat(table.latestSchemaVersion(), is(2));
     }
 
     @Test
@@ -719,7 +718,7 @@ public class CatalogTableTest extends 
BaseCatalogManagerTest {
 
         CatalogTableDescriptor table = actualTable(TABLE_NAME);
 
-        assertThat(table.tableVersion(), is(2));
+        assertThat(table.latestSchemaVersion(), is(2));
     }
 
     /**
diff --git 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/AbstractCommandValidationTest.java
 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/AbstractCommandValidationTest.java
index a2a8c1d710b..0a6ced81dc4 100644
--- 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/AbstractCommandValidationTest.java
+++ 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/AbstractCommandValidationTest.java
@@ -254,17 +254,16 @@ abstract class AbstractCommandValidationTest extends 
BaseIgniteAbstractTest {
     }
 
     static CatalogTableDescriptor table(int tableId, int schemaId, int zoneId, 
int pkIndexId, String columnName) {
-        return new CatalogTableDescriptor(
-                tableId,
-                schemaId,
-                pkIndexId,
-                "TEST_TABLE",
-                zoneId,
-                List.of(tableColumn(columnName)),
-                List.of(columnName),
-                null,
-                DEFAULT_STORAGE_PROFILE
-        );
+        return CatalogTableDescriptor.builder()
+                .id(tableId)
+                .schemaId(schemaId)
+                .primaryKeyIndexId(pkIndexId)
+                .name("TEST_TABLE")
+                .zoneId(zoneId)
+                .columns(List.of(tableColumn(columnName)))
+                .primaryKeyColumns(List.of(columnName))
+                .storageProfile(DEFAULT_STORAGE_PROFILE)
+                .build();
     }
 
     static CatalogTableColumnDescriptor tableColumn(String columnName) {
diff --git 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/CatalogUtilsTest.java
 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/CatalogUtilsTest.java
index 276b2b72560..ac189027fda 100644
--- 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/CatalogUtilsTest.java
+++ 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/CatalogUtilsTest.java
@@ -108,13 +108,11 @@ public class CatalogUtilsTest extends 
BaseIgniteAbstractTest {
 
         assertThat(fooTable, is(notNullValue()));
 
-        CatalogTableDescriptor bazTable = fooTable.newDescriptor(
-                "baz",
-                fooTable.tableVersion(),
-                fooTable.columns(),
-                fooTable.updateTimestamp(),
-                fooTable.storageProfile()
-        );
+        int tableVersion = fooTable.latestSchemaVersion();
+        CatalogTableDescriptor bazTable = fooTable.copyBuilder()
+                .name("baz")
+                .latestSchemaVersion(tableVersion + 1)
+                .build();
 
         CatalogSchemaDescriptor updatedSchema = replaceTable(schema, bazTable);
 
diff --git 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableDescriptorTest.java
 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableDescriptorTest.java
index 6cdec507993..5254cde15cc 100644
--- 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableDescriptorTest.java
+++ 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableDescriptorTest.java
@@ -17,30 +17,38 @@
 
 package org.apache.ignite.internal.catalog.descriptors;
 
-import static 
org.apache.ignite.internal.catalog.CatalogService.DEFAULT_STORAGE_PROFILE;
+import static 
org.apache.ignite.internal.catalog.CatalogManager.INITIAL_TIMESTAMP;
+import static 
org.apache.ignite.internal.testframework.IgniteTestUtils.assertThrows;
+import static org.assertj.core.api.SoftAssertions.assertSoftly;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.not;
 import static org.hamcrest.Matchers.startsWith;
 
+import java.util.Collections;
 import java.util.List;
+import org.apache.ignite.internal.catalog.CatalogService;
+import 
org.apache.ignite.internal.catalog.descriptors.CatalogTableSchemaVersions.TableVersion;
+import org.apache.ignite.internal.hlc.HybridTimestamp;
 import org.apache.ignite.sql.ColumnType;
 import org.junit.jupiter.api.Test;
 
 class CatalogTableDescriptorTest {
     @Test
     void toStringContainsTypeAndFields() {
-        var descriptor = new CatalogTableDescriptor(
-                1,
-                2,
-                3,
-                "table1",
-                4,
-                List.of(new CatalogTableColumnDescriptor("pkCol", 
ColumnType.STRING, false, 0, 0, 10, null)),
-                List.of("pkCol"),
-                null,
-                DEFAULT_STORAGE_PROFILE
+        List<CatalogTableColumnDescriptor> columns = List.of(
+                new CatalogTableColumnDescriptor("pkCol", ColumnType.STRING, 
false, 0, 0, 10, null)
         );
+        var descriptor = CatalogTableDescriptor.builder()
+                .id(1)
+                .schemaId(2)
+                .primaryKeyIndexId(3)
+                .name("table1")
+                .zoneId(4)
+                .columns(columns)
+                .primaryKeyColumns(List.of("pkCol"))
+                .storageProfile(CatalogService.DEFAULT_STORAGE_PROFILE)
+                .build();
 
         String toString = descriptor.toString();
 
@@ -52,4 +60,108 @@ class CatalogTableDescriptorTest {
         assertThat(toString, containsString("zoneId=4"));
         assertThat(toString, not(containsString("schemaVersions=")));
     }
+
+    @Test
+    void itHasCorrectValues() {
+        List<CatalogTableColumnDescriptor> columns = List.of(
+                new CatalogTableColumnDescriptor("pkCol", ColumnType.STRING, 
false, 0, 0, 10, null)
+        );
+        var descriptor = CatalogTableDescriptor.builder()
+                .id(1)
+                .schemaId(2)
+                .primaryKeyIndexId(3)
+                .name("table1")
+                .zoneId(4)
+                .columns(columns)
+                .primaryKeyColumns(List.of("pkCol"))
+                .storageProfile(CatalogService.DEFAULT_STORAGE_PROFILE)
+                .build();
+
+        assertSoftly(assertions -> {
+            
assertions.assertThat(descriptor.colocationColumns()).containsExactly("pkCol");
+            
assertions.assertThat(descriptor.updateTimestamp()).isEqualTo(INITIAL_TIMESTAMP);
+            
assertions.assertThat(descriptor.isPrimaryKeyColumn("pkCol")).isTrue();
+            
assertions.assertThat(descriptor.latestSchemaVersion()).isEqualTo(CatalogTableDescriptor.INITIAL_TABLE_VERSION);
+            
assertions.assertThat(descriptor.schemaVersions().latestVersion()).isEqualTo(CatalogTableDescriptor.INITIAL_TABLE_VERSION);
+            
assertions.assertThat(descriptor.storageProfile()).isEqualTo(CatalogService.DEFAULT_STORAGE_PROFILE);
+        });
+
+        var descriptorV2 = descriptor.copyBuilder()
+                .latestSchemaVersion(2)
+                .timestamp(HybridTimestamp.MAX_VALUE)
+                .build();
+
+        assertSoftly(assertions -> {
+            
assertions.assertThat(descriptor.colocationColumns()).containsExactly("pkCol");
+            
assertions.assertThat(descriptorV2.updateTimestamp()).isEqualTo(HybridTimestamp.MAX_VALUE);
+            
assertions.assertThat(descriptorV2.isPrimaryKeyColumn("pkCol")).isTrue();
+            
assertions.assertThat(descriptorV2.latestSchemaVersion()).isEqualTo(2);
+
+            // TODO: https://issues.apache.org/jira/browse/IGNITE-26501
+            
assertions.assertThat(descriptorV2.schemaVersions().latestVersion()).isEqualTo(2);
+            
assertions.assertThat(descriptorV2.storageProfile()).isEqualTo(CatalogService.DEFAULT_STORAGE_PROFILE);
+        });
+    }
+
+    @Test
+    @SuppressWarnings("ThrowableNotThrown")
+    void descriptorValidatesArguments() {
+        List<CatalogTableColumnDescriptor> columns = List.of(
+                new CatalogTableColumnDescriptor("pkCol", ColumnType.STRING, 
false, 0, 0, 10, null)
+        );
+        CatalogTableDescriptor.Builder baseBuilder = 
CatalogTableDescriptor.builder()
+                .id(1)
+                .schemaId(2)
+                .primaryKeyIndexId(3)
+                .name("table1")
+                .zoneId(4)
+                .columns(columns)
+                .primaryKeyColumns(List.of("pkCol"))
+                .storageProfile(CatalogService.DEFAULT_STORAGE_PROFILE);
+
+        assertThrows(NullPointerException.class, () -> {
+            baseBuilder
+                    .columns(null)
+                    .build();
+        }, "No columns defined.");
+
+        assertThrows(IllegalArgumentException.class, () -> {
+            baseBuilder
+                    .columns(Collections.emptyList())
+                    .build();
+        }, "No columns defined.");
+
+        assertThrows(NullPointerException.class, () -> {
+            baseBuilder
+                    .columns(columns)
+                    .primaryKeyColumns(null)
+                    .build();
+        }, "No primary key columns.");
+
+        assertThrows(IllegalArgumentException.class, () -> {
+            baseBuilder
+                    .primaryKeyColumns(List.of("pkCol"))
+                    .latestSchemaVersion(-1)
+                    .build();
+        }, "Latest schema version -1 should not be less than a previous 
version");
+
+        assertThrows(NullPointerException.class, () -> {
+            baseBuilder
+                    .latestSchemaVersion(1)
+                    .storageProfile(null)
+                    .build();
+        }, "No storage profile.");
+
+        List<CatalogTableColumnDescriptor> wrongSchemaVersionColumns = List.of(
+                columns.get(0),
+                new CatalogTableColumnDescriptor("val", ColumnType.STRING, 
false, 0, 0, 10, null)
+        );
+
+        assertThrows(IllegalArgumentException.class, () -> {
+            baseBuilder
+                    .storageProfile(CatalogService.DEFAULT_STORAGE_PROFILE)
+                    .schemaVersions(new CatalogTableSchemaVersions(new 
TableVersion(wrongSchemaVersionColumns)))
+                    .build();
+        }, "Latest schema version columns do not match descriptor definition 
columns.");
+    }
 }
diff --git 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogEntrySerializationTest.java
 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogEntrySerializationTest.java
index 851e151cc92..f1535ce7e5a 100644
--- 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogEntrySerializationTest.java
+++ 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogEntrySerializationTest.java
@@ -559,17 +559,17 @@ public class CatalogEntrySerializationTest extends 
BaseIgniteAbstractTest {
             List<String> pkCols,
             @Nullable List<String> colCols
     ) {
-        return new CatalogTableDescriptor(
-                1,
-                3,
-                1,
-                name,
-                17,
-                columns,
-                pkCols,
-                colCols,
-                "default"
-        );
+        return CatalogTableDescriptor.builder()
+                .id(1)
+                .schemaId(3)
+                .primaryKeyIndexId(1)
+                .name(name)
+                .zoneId(17)
+                .columns(columns)
+                .primaryKeyColumns(pkCols)
+                .colocationColumns(colCols)
+                .storageProfile("default")
+                .build();
     }
 
     private static CatalogSchemaDescriptor newSchemaDescriptor(String name) {
diff --git 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/TestTableDescriptors.java
 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/TestTableDescriptors.java
index 6380d683ca5..cd6c1b55169 100644
--- 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/TestTableDescriptors.java
+++ 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/TestTableDescriptors.java
@@ -17,11 +17,10 @@
 
 package org.apache.ignite.internal.catalog.storage;
 
-import static org.apache.ignite.internal.hlc.HybridTimestamp.hybridTimestamp;
-
 import java.util.ArrayList;
 import java.util.List;
 import org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor;
+import org.apache.ignite.internal.hlc.HybridTimestamp;
 
 /**
  * Random {@link CatalogTableDescriptor}s for testing.
@@ -48,72 +47,85 @@ final class TestTableDescriptors {
     private static List<CatalogTableDescriptor> tablesV0(TestDescriptorState 
state) {
         List<CatalogTableDescriptor> list = new ArrayList<>();
 
-        CatalogTableDescriptor table1 = new CatalogTableDescriptor(
-                state.id(),
-                state.id(),
-                state.id(),
-                state.name("TABLE"),
-                100,
-                TestTableColumnDescriptors.columns(state),
-                List.of("C0"),
-                null,
-                "S1"
-        );
+        CatalogTableDescriptor table1 = CatalogTableDescriptor.builder()
+                .id(state.id())
+                .schemaId(state.id())
+                .primaryKeyIndexId(state.id())
+                .name(state.name("TABLE"))
+                .zoneId(100)
+                .columns(TestTableColumnDescriptors.columns(state))
+                .primaryKeyColumns(List.of("C0"))
+                .colocationColumns(null)
+                .storageProfile("S1")
+                .build();
 
         list.add(table1);
-        list.add(list.get(0).newDescriptor(
-                table1.name() + "_1", 2,
-                TestTableColumnDescriptors.columns(state).subList(0, 10),
-                hybridTimestamp(1232L),
-                "S1")
+        list.add(table1.copyBuilder()
+                .name(table1.name() + "_1")
+                .columns(TestTableColumnDescriptors.columns(state).subList(0, 
10))
+                .timestamp(HybridTimestamp.hybridTimestamp(1232L))
+                .storageProfile("S1")
+                .latestSchemaVersion(2)
+                .build()
         );
         list.add(
-                list.get(list.size() - 1)
-                        .newDescriptor(table1.name() + "_2", 3,
-                                
TestTableColumnDescriptors.columns(state).subList(0, 20), 
hybridTimestamp(21232L), "S1")
+                list.get(list.size() - 1).copyBuilder()
+                        .name(table1.name() + "_2")
+                        
.columns(TestTableColumnDescriptors.columns(state).subList(0, 20))
+                        .timestamp(HybridTimestamp.hybridTimestamp(21232L))
+                        .storageProfile("S1")
+                        .latestSchemaVersion(3)
+                        .build()
         );
 
-        CatalogTableDescriptor table2 = new CatalogTableDescriptor(
-                state.id(),
-                state.id(),
-                state.id(),
-                state.name("TABLE"),
-                101,
-                TestTableColumnDescriptors.columns(state), List.of("C4"), null,
-                "S2"
-        );
+        CatalogTableDescriptor table2 = CatalogTableDescriptor.builder()
+                .id(state.id())
+                .schemaId(state.id())
+                .primaryKeyIndexId(state.id())
+                .name(state.name("TABLE"))
+                .zoneId(101)
+                .columns(TestTableColumnDescriptors.columns(state))
+                .primaryKeyColumns(List.of("C4"))
+                .storageProfile("S2")
+                .build();
 
         list.add(table2);
-        list.add(list.get(list.size() - 1).newDescriptor(
-                table2.name() + "_1", 2,
-                TestTableColumnDescriptors.columns(state).subList(0, 10),
-                hybridTimestamp(4567L), "S2")
+        list.add(table2.copyBuilder()
+                .name(table2.name() + "_1")
+                .columns(TestTableColumnDescriptors.columns(state).subList(0, 
10))
+                .timestamp(HybridTimestamp.hybridTimestamp(4567L))
+                .storageProfile("S2")
+                .latestSchemaVersion(2)
+                .build()
         );
-        list.add(list.get(list.size() - 1).newDescriptor(
-                table2.name() + "_2", 3,
-                TestTableColumnDescriptors.columns(state).subList(0, 20),
-                hybridTimestamp(8833L),
-                "S2")
+        list.add(list.get(list.size() - 1).copyBuilder()
+                .name(table2.name() + "_2")
+                .columns(TestTableColumnDescriptors.columns(state).subList(0, 
20))
+                .timestamp(HybridTimestamp.hybridTimestamp(8833L))
+                .storageProfile("S2")
+                .latestSchemaVersion(3)
+                .build()
         );
 
-        CatalogTableDescriptor table3 = new CatalogTableDescriptor(
-                state.id(),
-                state.id(),
-                state.id(),
-                state.name("TABLE"),
-                102,
-                TestTableColumnDescriptors.columns(state),
-                List.of("C1", "C2", "C3"),
-                List.of("C2", "C3"),
-                "S3"
-        );
+        CatalogTableDescriptor table3 = CatalogTableDescriptor.builder()
+                .id(state.id())
+                .schemaId(state.id())
+                .primaryKeyIndexId(state.id())
+                .name(state.name("TABLE"))
+                .zoneId(102)
+                .columns(TestTableColumnDescriptors.columns(state))
+                .primaryKeyColumns(List.of("C1", "C2", "C3"))
+                .colocationColumns(List.of("C2", "C3"))
+                .storageProfile("S3")
+                .build();
         list.add(table3);
-        list.add(list.get(list.size() - 1).newDescriptor(
-                table3.name() + "_1",
-                2,
-                TestTableColumnDescriptors.columns(state),
-                hybridTimestamp(123234L),
-                "S4")
+        list.add(table3.copyBuilder()
+                .name(table3.name() + "_1")
+                .columns(TestTableColumnDescriptors.columns(state))
+                .timestamp(HybridTimestamp.hybridTimestamp(123234L))
+                .storageProfile("S4")
+                .latestSchemaVersion(2)
+                .build()
         );
 
         return list;
diff --git 
a/modules/client-handler/src/testFixtures/java/org/apache/ignite/client/handler/FakeCatalogService.java
 
b/modules/client-handler/src/testFixtures/java/org/apache/ignite/client/handler/FakeCatalogService.java
index a5ec9740d2c..f7de14273fc 100644
--- 
a/modules/client-handler/src/testFixtures/java/org/apache/ignite/client/handler/FakeCatalogService.java
+++ 
b/modules/client-handler/src/testFixtures/java/org/apache/ignite/client/handler/FakeCatalogService.java
@@ -36,6 +36,7 @@ import 
org.apache.ignite.internal.catalog.descriptors.ConsistencyMode;
 import org.apache.ignite.internal.catalog.events.CatalogEvent;
 import org.apache.ignite.internal.catalog.events.CatalogEventParameters;
 import org.apache.ignite.internal.event.EventListener;
+import org.mockito.Mockito;
 
 /**
  * Fake catalog service.
@@ -69,17 +70,17 @@ public class FakeCatalogService implements CatalogService {
         lenient().doAnswer(invocation -> {
             int tableId = invocation.getArgument(0);
 
-            return new CatalogTableDescriptor(
-                    tableId,
-                    0,
-                    0,
-                    "table",
-                    zoneIdProvider.applyAsInt(tableId),
-                    List.of(mock(CatalogTableColumnDescriptor.class)),
-                    List.of(),
-                    null,
-                    DEFAULT_STORAGE_PROFILE
-            );
+            int zoneId = zoneIdProvider.applyAsInt(tableId);
+            return CatalogTableDescriptor.builder()
+                    .id(tableId)
+                    .schemaId(0)
+                    .primaryKeyIndexId(0)
+                    .name("table")
+                    .zoneId(zoneId)
+                    
.columns(List.of(Mockito.mock(CatalogTableColumnDescriptor.class)))
+                    .primaryKeyColumns(List.of("pk"))
+                    .storageProfile(DEFAULT_STORAGE_PROFILE)
+                    .build();
         }).when(catalog).table(anyInt());
 
         lenient().doAnswer(invocation -> new CatalogZoneDescriptor(
diff --git 
a/modules/distribution-zones/src/test/java/org/apache/ignite/internal/distributionzones/rebalance/RebalanceUtilUpdateAssignmentsTest.java
 
b/modules/distribution-zones/src/test/java/org/apache/ignite/internal/distributionzones/rebalance/RebalanceUtilUpdateAssignmentsTest.java
index 61998f3f0d7..26e7dafa420 100644
--- 
a/modules/distribution-zones/src/test/java/org/apache/ignite/internal/distributionzones/rebalance/RebalanceUtilUpdateAssignmentsTest.java
+++ 
b/modules/distribution-zones/src/test/java/org/apache/ignite/internal/distributionzones/rebalance/RebalanceUtilUpdateAssignmentsTest.java
@@ -18,7 +18,6 @@
 package org.apache.ignite.internal.distributionzones.rebalance;
 
 import static java.util.stream.Collectors.toSet;
-import static 
org.apache.ignite.internal.catalog.CatalogService.DEFAULT_STORAGE_PROFILE;
 import static org.apache.ignite.internal.hlc.HybridTimestamp.hybridTimestamp;
 import static 
org.apache.ignite.internal.metastorage.server.KeyValueUpdateContext.kvContext;
 import static 
org.apache.ignite.internal.partitiondistribution.Assignments.toBytes;
@@ -44,6 +43,7 @@ import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.stream.IntStream;
 import java.util.stream.Stream;
+import org.apache.ignite.internal.catalog.CatalogService;
 import 
org.apache.ignite.internal.catalog.descriptors.CatalogTableColumnDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.ConsistencyMode;
@@ -105,17 +105,23 @@ public class RebalanceUtilUpdateAssignmentsTest extends 
IgniteAbstractTest {
     @Mock
     private MetaStorageManager metaStorageManager;
 
-    private final CatalogTableDescriptor tableDescriptor = new 
CatalogTableDescriptor(
-            1,
-            -1,
-            -1,
-            "table1",
-            0,
-            List.of(new CatalogTableColumnDescriptor("k1", ColumnType.INT32, 
false, 0, 0, 0, null)),
-            List.of("k1"),
-            null,
-            DEFAULT_STORAGE_PROFILE
-    );
+    private final CatalogTableDescriptor tableDescriptor;
+
+    {
+        List<CatalogTableColumnDescriptor> columns = List.of(
+                new CatalogTableColumnDescriptor("k1", ColumnType.INT32, 
false, 0, 0, 0, null)
+        );
+        tableDescriptor = CatalogTableDescriptor.builder()
+                .id(1)
+                .schemaId(-1)
+                .primaryKeyIndexId(-1)
+                .name("table1")
+                .zoneId(0)
+                .columns(columns)
+                .primaryKeyColumns(List.of("k1"))
+                .storageProfile(CatalogService.DEFAULT_STORAGE_PROFILE)
+                .build();
+    }
 
     private final HybridClock clock = new HybridClockImpl();
 
diff --git 
a/modules/partition-replicator/src/main/java/org/apache/ignite/internal/partition/replicator/schema/CatalogValidationSchemasSource.java
 
b/modules/partition-replicator/src/main/java/org/apache/ignite/internal/partition/replicator/schema/CatalogValidationSchemasSource.java
index 4533b0d66a2..2dd18c495ea 100644
--- 
a/modules/partition-replicator/src/main/java/org/apache/ignite/internal/partition/replicator/schema/CatalogValidationSchemasSource.java
+++ 
b/modules/partition-replicator/src/main/java/org/apache/ignite/internal/partition/replicator/schema/CatalogValidationSchemasSource.java
@@ -102,15 +102,15 @@ public class CatalogValidationSchemasSource implements 
ValidationSchemasSource {
 
                     @Override
                     public boolean test(CatalogTableDescriptor 
tableDescriptor) {
-                        if (tableDescriptor.tableVersion() == prevVersion) {
+                        if (tableDescriptor.latestSchemaVersion() == 
prevVersion) {
                             return false;
                         }
 
-                        assert prevVersion == Integer.MIN_VALUE || 
tableDescriptor.tableVersion() == prevVersion + 1
+                        assert prevVersion == Integer.MIN_VALUE || 
tableDescriptor.latestSchemaVersion() == prevVersion + 1
                                 : String.format("Table version is expected to 
be prevVersion+1, but version is %d and prevVersion is %d",
-                                        tableDescriptor.tableVersion(), 
prevVersion);
+                                        tableDescriptor.latestSchemaVersion(), 
prevVersion);
 
-                        prevVersion = tableDescriptor.tableVersion();
+                        prevVersion = tableDescriptor.latestSchemaVersion();
 
                         return true;
                     }
@@ -123,14 +123,14 @@ public class CatalogValidationSchemasSource implements 
ValidationSchemasSource {
             int toTableVersion
     ) {
         return tableVersionsBetween(tableId, fromCatalogVersion, 
catalogService.latestCatalogVersion())
-                .takeWhile(tableDescriptor -> tableDescriptor.tableVersion() 
<= toTableVersion)
+                .takeWhile(tableDescriptor -> 
tableDescriptor.latestSchemaVersion() <= toTableVersion)
                 
.map(CatalogValidationSchemasSource::fullSchemaFromTableDescriptor)
                 .collect(toList());
     }
 
     private static FullTableSchema 
fullSchemaFromTableDescriptor(CatalogTableDescriptor tableDescriptor) {
         return new FullTableSchema(
-                tableDescriptor.tableVersion(),
+                tableDescriptor.latestSchemaVersion(),
                 tableDescriptor.id(),
                 tableDescriptor.name(),
                 tableDescriptor.columns()
diff --git 
a/modules/partition-replicator/src/main/java/org/apache/ignite/internal/partition/replicator/schemacompat/SchemaCompatibilityValidator.java
 
b/modules/partition-replicator/src/main/java/org/apache/ignite/internal/partition/replicator/schemacompat/SchemaCompatibilityValidator.java
index 65c41c506dd..31cddfe735b 100644
--- 
a/modules/partition-replicator/src/main/java/org/apache/ignite/internal/partition/replicator/schemacompat/SchemaCompatibilityValidator.java
+++ 
b/modules/partition-replicator/src/main/java/org/apache/ignite/internal/partition/replicator/schemacompat/SchemaCompatibilityValidator.java
@@ -249,11 +249,11 @@ public class SchemaCompatibilityValidator {
             throw 
IncompatibleSchemaVersionException.tableDropped(tableAtBeginTs.name());
         }
 
-        if (tableAtOpTs.tableVersion() != tableAtBeginTs.tableVersion()) {
+        if (tableAtOpTs.latestSchemaVersion() != 
tableAtBeginTs.latestSchemaVersion()) {
             throw IncompatibleSchemaVersionException.schemaChanged(
                     tableAtBeginTs.name(),
-                    tableAtBeginTs.tableVersion(),
-                    tableAtOpTs.tableVersion()
+                    tableAtBeginTs.latestSchemaVersion(),
+                    tableAtOpTs.latestSchemaVersion()
             );
         }
     }
@@ -287,7 +287,7 @@ public class SchemaCompatibilityValidator {
 
         assert table != null : "No table " + tableId + " at " + txTs;
 
-        if (table.tableVersion() != requestSchemaVersion) {
+        if (table.latestSchemaVersion() != requestSchemaVersion) {
             throw new InternalSchemaVersionMismatchException();
         }
     }
diff --git 
a/modules/partition-replicator/src/test/java/org/apache/ignite/internal/partition/replicator/schema/CatalogValidationSchemasSourceTest.java
 
b/modules/partition-replicator/src/test/java/org/apache/ignite/internal/partition/replicator/schema/CatalogValidationSchemasSourceTest.java
index d9447b48fdc..b96b861a12d 100644
--- 
a/modules/partition-replicator/src/test/java/org/apache/ignite/internal/partition/replicator/schema/CatalogValidationSchemasSourceTest.java
+++ 
b/modules/partition-replicator/src/test/java/org/apache/ignite/internal/partition/replicator/schema/CatalogValidationSchemasSourceTest.java
@@ -17,7 +17,6 @@
 
 package org.apache.ignite.internal.partition.replicator.schema;
 
-import static 
org.apache.ignite.internal.catalog.CatalogService.DEFAULT_STORAGE_PROFILE;
 import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureCompletedMatcher.completedFuture;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.empty;
@@ -36,7 +35,6 @@ import static org.mockito.Mockito.when;
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
 import org.apache.ignite.internal.catalog.Catalog;
-import org.apache.ignite.internal.catalog.CatalogManager;
 import org.apache.ignite.internal.catalog.CatalogService;
 import 
org.apache.ignite.internal.catalog.descriptors.CatalogTableColumnDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor;
@@ -125,13 +123,21 @@ class CatalogValidationSchemasSourceTest extends 
BaseIgniteAbstractTest {
                 new CatalogTableColumnDescriptor("v1", ColumnType.INT32, 
false, 0, 0, 0, null)
         );
 
-        CatalogTableDescriptor descriptor = new CatalogTableDescriptor(
-                tableId, -1, -1, "test", 0, columns, List.of("k1"), null, 
DEFAULT_STORAGE_PROFILE
-        );
+        CatalogTableDescriptor descriptor = CatalogTableDescriptor.builder()
+                .id(tableId)
+                .schemaId(-1)
+                .primaryKeyIndexId(-1)
+                .name("test")
+                .zoneId(0)
+                .columns(columns)
+                .primaryKeyColumns(List.of("k1"))
+                .storageProfile(CatalogService.DEFAULT_STORAGE_PROFILE)
+                .build();
 
         for (int ver = CatalogTableDescriptor.INITIAL_TABLE_VERSION + 1; ver 
<= tableVersion; ver++) {
-            descriptor = descriptor.newDescriptor(
-                    "test", ver, columns, CatalogManager.INITIAL_TIMESTAMP, 
DEFAULT_STORAGE_PROFILE);
+            descriptor = descriptor.copyBuilder()
+                    .latestSchemaVersion(ver)
+                    .build();
         }
 
         return descriptor;
diff --git 
a/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaManager.java
 
b/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaManager.java
index 45b29980226..cde708250a0 100644
--- 
a/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaManager.java
+++ 
b/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaManager.java
@@ -145,7 +145,7 @@ public class SchemaManager implements IgniteComponent {
 
         try {
             int tableId = tableDescriptor.id();
-            int newSchemaVersion = tableDescriptor.tableVersion();
+            int newSchemaVersion = tableDescriptor.latestSchemaVersion();
 
             if (searchSchemaByVersion(tableId, newSchemaVersion) != null) {
                 return falseCompletedFuture();
diff --git 
a/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaUtils.java
 
b/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaUtils.java
index b57fdeea3f4..d3da3f7c680 100644
--- 
a/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaUtils.java
+++ 
b/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaUtils.java
@@ -59,7 +59,7 @@ public class SchemaUtils {
      * @return Schema descriptor.
      */
     public static SchemaDescriptor 
prepareSchemaDescriptor(CatalogTableDescriptor tableDescriptor) {
-        return CatalogToSchemaDescriptorConverter.convert(tableDescriptor, 
tableDescriptor.tableVersion());
+        return CatalogToSchemaDescriptorConverter.convert(tableDescriptor, 
tableDescriptor.latestSchemaVersion());
     }
 
     /**
diff --git 
a/modules/schema/src/test/java/org/apache/ignite/internal/schema/SchemaManagerTest.java
 
b/modules/schema/src/test/java/org/apache/ignite/internal/schema/SchemaManagerTest.java
index 695f3dcdab1..36e3016bb0b 100644
--- 
a/modules/schema/src/test/java/org/apache/ignite/internal/schema/SchemaManagerTest.java
+++ 
b/modules/schema/src/test/java/org/apache/ignite/internal/schema/SchemaManagerTest.java
@@ -18,8 +18,6 @@
 package org.apache.ignite.internal.schema;
 
 import static java.util.concurrent.CompletableFuture.completedFuture;
-import static 
org.apache.ignite.internal.catalog.CatalogManager.INITIAL_TIMESTAMP;
-import static 
org.apache.ignite.internal.catalog.CatalogService.DEFAULT_STORAGE_PROFILE;
 import static 
org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor.INITIAL_TABLE_VERSION;
 import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureCompletedMatcher.completedFuture;
 import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureExceptionMatcher.willTimeoutFast;
@@ -122,9 +120,16 @@ class SchemaManagerTest extends BaseIgniteAbstractTest {
                 new CatalogTableColumnDescriptor("k2", ColumnType.STRING, 
false, 0, 0, 0, null),
                 new CatalogTableColumnDescriptor("v1", ColumnType.INT32, 
false, 0, 0, 0, null)
         );
-        CatalogTableDescriptor tableDescriptor = new CatalogTableDescriptor(
-                TABLE_ID, -1, -1, TABLE_NAME, 0, columns, List.of("k1", "k2"), 
null, DEFAULT_STORAGE_PROFILE
-        );
+        CatalogTableDescriptor tableDescriptor = 
CatalogTableDescriptor.builder()
+                .id(TABLE_ID)
+                .schemaId(-1)
+                .primaryKeyIndexId(-1)
+                .name(TABLE_NAME)
+                .zoneId(0)
+                .columns(columns)
+                .primaryKeyColumns(List.of("k1", "k2"))
+                .storageProfile(CatalogService.DEFAULT_STORAGE_PROFILE)
+                .build();
 
         Catalog catalog = mock(Catalog.class);
         when(catalogService.catalog(CATALOG_VERSION_1)).thenReturn(catalog);
@@ -159,23 +164,19 @@ class SchemaManagerTest extends BaseIgniteAbstractTest {
                 new CatalogTableColumnDescriptor("v2", ColumnType.STRING, 
false, 0, 0, 0, null)
         );
 
-        return new CatalogTableDescriptor(
-                TABLE_ID,
-                -1,
-                -1,
-                TABLE_NAME,
-                0,
-                columns,
-                List.of("k1", "k2"),
-                null,
-                DEFAULT_STORAGE_PROFILE
-        ).newDescriptor(
-                TABLE_NAME,
-                INITIAL_TABLE_VERSION + 1,
-                columns,
-                INITIAL_TIMESTAMP,
-                DEFAULT_STORAGE_PROFILE
-        );
+        CatalogTableDescriptor catalogTableDescriptor = 
CatalogTableDescriptor.builder()
+                .id(TABLE_ID)
+                .schemaId(-1)
+                .primaryKeyIndexId(-1)
+                .name(TABLE_NAME)
+                .zoneId(0)
+                .columns(columns)
+                .primaryKeyColumns(List.of("k1", "k2"))
+                .storageProfile(CatalogService.DEFAULT_STORAGE_PROFILE)
+                .build();
+        return catalogTableDescriptor.copyBuilder()
+                .latestSchemaVersion(INITIAL_TABLE_VERSION + 1)
+                .build();
     }
 
     private void completeCausalityToken(long causalityToken) {
diff --git 
a/modules/schema/src/test/java/org/apache/ignite/internal/schema/catalog/CatalogToSchemaDescriptorConverterTest.java
 
b/modules/schema/src/test/java/org/apache/ignite/internal/schema/catalog/CatalogToSchemaDescriptorConverterTest.java
index 04a2b41b675..ec43ac6d486 100644
--- 
a/modules/schema/src/test/java/org/apache/ignite/internal/schema/catalog/CatalogToSchemaDescriptorConverterTest.java
+++ 
b/modules/schema/src/test/java/org/apache/ignite/internal/schema/catalog/CatalogToSchemaDescriptorConverterTest.java
@@ -17,7 +17,6 @@
 
 package org.apache.ignite.internal.schema.catalog;
 
-import static 
org.apache.ignite.internal.catalog.CatalogService.DEFAULT_STORAGE_PROFILE;
 import static org.apache.ignite.internal.schema.SchemaTestUtils.specToType;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.equalTo;
@@ -25,6 +24,7 @@ import static org.hamcrest.Matchers.is;
 
 import java.util.ArrayList;
 import java.util.List;
+import org.apache.ignite.internal.catalog.CatalogService;
 import org.apache.ignite.internal.catalog.commands.DefaultValue;
 import 
org.apache.ignite.internal.catalog.descriptors.CatalogTableColumnDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor;
@@ -122,24 +122,25 @@ public class CatalogToSchemaDescriptorConverterTest 
extends AbstractSchemaConver
 
     @Test
     public void convertTableDescriptor() {
-        CatalogTableDescriptor tableDescriptor = new CatalogTableDescriptor(
-                1,
-                -1,
-                -1,
-                "test",
-                0,
-                List.of(
-                        new CatalogTableColumnDescriptor("C1", 
ColumnType.INT32, false, 0, 0, 0, null),
-                        new CatalogTableColumnDescriptor("K2", 
ColumnType.INT32, false, 0, 0, 0, null),
-                        new CatalogTableColumnDescriptor("C2", 
ColumnType.INT32, false, 0, 0, 0, null),
-                        new CatalogTableColumnDescriptor("K1", 
ColumnType.INT32, false, 0, 0, 0, null)
-                ),
-                List.of("K1", "K2"),
-                List.of("K2"),
-                DEFAULT_STORAGE_PROFILE
+        List<CatalogTableColumnDescriptor> columns = List.of(
+                new CatalogTableColumnDescriptor("C1", ColumnType.INT32, 
false, 0, 0, 0, null),
+                new CatalogTableColumnDescriptor("K2", ColumnType.INT32, 
false, 0, 0, 0, null),
+                new CatalogTableColumnDescriptor("C2", ColumnType.INT32, 
false, 0, 0, 0, null),
+                new CatalogTableColumnDescriptor("K1", ColumnType.INT32, 
false, 0, 0, 0, null)
         );
-
-        SchemaDescriptor schema = 
CatalogToSchemaDescriptorConverter.convert(tableDescriptor, 
tableDescriptor.tableVersion());
+        CatalogTableDescriptor tableDescriptor = 
CatalogTableDescriptor.builder()
+                .id(1)
+                .schemaId(-1)
+                .primaryKeyIndexId(-1)
+                .name("test")
+                .zoneId(0)
+                .columns(columns)
+                .primaryKeyColumns(List.of("K1", "K2"))
+                .colocationColumns(List.of("K2"))
+                .storageProfile(CatalogService.DEFAULT_STORAGE_PROFILE)
+                .build();
+
+        SchemaDescriptor schema = 
CatalogToSchemaDescriptorConverter.convert(tableDescriptor, 
tableDescriptor.latestSchemaVersion());
 
         assertThat(schema.keyColumns().size(), equalTo(2));
         assertThat(schema.keyColumns().get(0).name(), equalTo("K1"));
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/SqlSchemaManagerImpl.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/SqlSchemaManagerImpl.java
index 251e1c31406..1655be5bd86 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/SqlSchemaManagerImpl.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/SqlSchemaManagerImpl.java
@@ -187,7 +187,7 @@ public class SqlSchemaManagerImpl implements 
SqlSchemaManager {
                 throw new IgniteInternalException(Common.INTERNAL_ERR, "Table 
with given id not found: " + tableId);
             }
 
-            long tableKey = cacheKey(tableDescriptor.id(), 
tableDescriptor.tableVersion());
+            long tableKey = cacheKey(tableDescriptor.id(), 
tableDescriptor.latestSchemaVersion());
 
             IgniteTableImpl igniteTable = tableCache.get(tableKey, (x) -> {
                 TableDescriptor descriptor = 
createTableDescriptorForTable(catalog, tableDescriptor);
@@ -229,7 +229,7 @@ public class SqlSchemaManagerImpl implements 
SqlSchemaManager {
 
         // Assemble sql-engine.TableDescriptors as they are required by 
indexes.
         for (CatalogTableDescriptor tableDescriptor : 
schemaDescriptor.tables()) {
-            long tableKey = cacheKey(tableDescriptor.id(), 
tableDescriptor.tableVersion());
+            long tableKey = cacheKey(tableDescriptor.id(), 
tableDescriptor.latestSchemaVersion());
 
             // Load cached table by (id, version)
             IgniteTableImpl igniteTable = tableCache.get(tableKey, (k) -> {
@@ -539,7 +539,7 @@ public class SqlSchemaManagerImpl implements 
SqlSchemaManager {
         return new IgniteTableImpl(
                 tableName,
                 tableId,
-                catalogTableDescriptor.tableVersion(),
+                catalogTableDescriptor.latestSchemaVersion(),
                 tableDescriptor,
                 primaryKeyColumns,
                 statistic,
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/statistic/SqlStatisticManagerImplTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/statistic/SqlStatisticManagerImplTest.java
index 1a0b8a0c636..5de61e2ebe0 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/statistic/SqlStatisticManagerImplTest.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/statistic/SqlStatisticManagerImplTest.java
@@ -37,6 +37,7 @@ import java.util.concurrent.Future;
 import java.util.concurrent.ThreadLocalRandom;
 import org.apache.ignite.internal.catalog.Catalog;
 import org.apache.ignite.internal.catalog.CatalogManager;
+import 
org.apache.ignite.internal.catalog.descriptors.CatalogTableColumnDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor;
 import org.apache.ignite.internal.catalog.events.CatalogEvent;
 import org.apache.ignite.internal.catalog.events.CreateTableEventParameters;
@@ -50,6 +51,7 @@ import org.apache.ignite.internal.table.InternalTable;
 import org.apache.ignite.internal.table.TableViewInternal;
 import org.apache.ignite.internal.table.distributed.TableManager;
 import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
+import org.apache.ignite.sql.ColumnType;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.ArgumentCaptor;
@@ -76,6 +78,9 @@ class SqlStatisticManagerImplTest extends 
BaseIgniteAbstractTest {
     @Mock
     private LowWatermark lowWatermark;
 
+    private static final CatalogTableColumnDescriptor pkCol =
+            new CatalogTableColumnDescriptor("pkCol", ColumnType.STRING, 
false, 0, 0, 10, null);
+
     @Test
     public void checkDefaultTableSize() {
         int tableId = ThreadLocalRandom.current().nextInt();
@@ -153,8 +158,26 @@ class SqlStatisticManagerImplTest extends 
BaseIgniteAbstractTest {
         
when(catalogManager.latestCatalogVersion()).thenReturn(maximumCatalogVersion);
         for (int catalogVersion = minimumCatalogVersion; catalogVersion <= 
maximumCatalogVersion; catalogVersion++) {
             List<CatalogTableDescriptor> catalogDescriptors = new 
ArrayList<>();
-            catalogDescriptors.add(new CatalogTableDescriptor(catalogVersion, 
1, 1, "", 1, List.of(), List.of(), null, ""));
-            catalogDescriptors.add(new CatalogTableDescriptor(catalogVersion + 
1, 1, 1, "", 1, List.of(), List.of(), null, ""));
+            catalogDescriptors.add(CatalogTableDescriptor.builder()
+                    .id(catalogVersion)
+                    .schemaId(1)
+                    .primaryKeyIndexId(1)
+                    .name("")
+                    .zoneId(1)
+                    .columns(List.of(pkCol))
+                    .primaryKeyColumns(List.of(pkCol.name()))
+                    .storageProfile("")
+                    .build());
+            catalogDescriptors.add(CatalogTableDescriptor.builder()
+                    .id(catalogVersion + 1)
+                    .schemaId(1)
+                    .primaryKeyIndexId(1)
+                    .name("")
+                    .zoneId(1)
+                    .columns(List.of(pkCol))
+                    .primaryKeyColumns(List.of(pkCol.name()))
+                    .storageProfile("")
+                    .build());
 
             Catalog catalog = mock(Catalog.class);
             when(catalogManager.catalog(catalogVersion)).thenReturn(catalog);
@@ -244,7 +267,16 @@ class SqlStatisticManagerImplTest extends 
BaseIgniteAbstractTest {
         // it's not created TABLE, so size will be as default value.
         assertEquals(DEFAULT_TABLE_SIZE, 
sqlStatisticManager.tableSize(tableId));
 
-        CatalogTableDescriptor catalogDescriptor = new 
CatalogTableDescriptor(tableId, 1, 1, "", 1, List.of(), List.of(), null, "");
+        CatalogTableDescriptor catalogDescriptor = 
CatalogTableDescriptor.builder()
+                .id(tableId)
+                .schemaId(1)
+                .primaryKeyIndexId(1)
+                .name("")
+                .zoneId(1)
+                .columns(List.of(pkCol))
+                .primaryKeyColumns(List.of(pkCol.name()))
+                .storageProfile("")
+                .build();
         tableCreateCapture.getValue().notify(new 
CreateTableEventParameters(-1, 0, catalogDescriptor));
         // now table is created and size should be actual.
         assertEquals(tableSize, sqlStatisticManager.tableSize(tableId));
@@ -304,7 +336,16 @@ class SqlStatisticManagerImplTest extends 
BaseIgniteAbstractTest {
         when(catalogManager.latestCatalogVersion()).thenReturn(1);
         Catalog catalog = mock(Catalog.class);
         when(catalogManager.catalog(1)).thenReturn(catalog);
-        CatalogTableDescriptor catalogDescriptor = new 
CatalogTableDescriptor(tableId, 1, 1, "", 1, List.of(), List.of(), null, "");
+        CatalogTableDescriptor catalogDescriptor = 
CatalogTableDescriptor.builder()
+                .id(tableId)
+                .schemaId(1)
+                .primaryKeyIndexId(1)
+                .name("")
+                .zoneId(1)
+                .columns(List.of(pkCol))
+                .primaryKeyColumns(List.of(pkCol.name()))
+                .storageProfile("")
+                .build();
         when(catalog.tables()).thenReturn(List.of(catalogDescriptor));
     }
 }
diff --git 
a/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/BaseMvTableStorageTest.java
 
b/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/BaseMvTableStorageTest.java
index 1e1b0af1747..c58c3a105ec 100644
--- 
a/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/BaseMvTableStorageTest.java
+++ 
b/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/BaseMvTableStorageTest.java
@@ -18,7 +18,6 @@
 package org.apache.ignite.internal.storage;
 
 import static java.util.stream.Collectors.toList;
-import static 
org.apache.ignite.internal.catalog.CatalogService.DEFAULT_STORAGE_PROFILE;
 import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.pkIndexName;
 import static 
org.apache.ignite.internal.catalog.descriptors.CatalogColumnCollation.ASC_NULLS_LAST;
 import static 
org.apache.ignite.internal.catalog.descriptors.CatalogIndexStatus.AVAILABLE;
@@ -34,12 +33,14 @@ import static org.mockito.Mockito.when;
 
 import java.util.List;
 import org.apache.ignite.internal.catalog.Catalog;
+import org.apache.ignite.internal.catalog.CatalogService;
 import org.apache.ignite.internal.catalog.commands.CatalogUtils;
 import org.apache.ignite.internal.catalog.commands.ColumnParams;
 import 
org.apache.ignite.internal.catalog.descriptors.CatalogHashIndexDescriptor;
 import 
org.apache.ignite.internal.catalog.descriptors.CatalogIndexColumnDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.CatalogIndexDescriptor;
 import 
org.apache.ignite.internal.catalog.descriptors.CatalogSortedIndexDescriptor;
+import 
org.apache.ignite.internal.catalog.descriptors.CatalogTableColumnDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor;
 import org.apache.ignite.internal.hlc.HybridTimestamp;
 import org.apache.ignite.internal.schema.BinaryRow;
@@ -145,22 +146,22 @@ public abstract class BaseMvTableStorageTest extends 
BaseMvStoragesTest {
 
         String pkColumnName = "INTKEY";
 
-        CatalogTableDescriptor tableDescriptor = new CatalogTableDescriptor(
-                tableId,
-                schemaId,
-                pkIndexId,
-                TABLE_NAME,
-                zoneId,
-                List.of(
-                        
CatalogUtils.fromParams(ColumnParams.builder().name(pkColumnName).type(INT32).build()),
-                        
CatalogUtils.fromParams(ColumnParams.builder().name("STRKEY").length(100).type(STRING).build()),
-                        
CatalogUtils.fromParams(ColumnParams.builder().name("INTVAL").type(INT32).build()),
-                        
CatalogUtils.fromParams(ColumnParams.builder().name("STRVAL").length(100).type(STRING).build())
-                ),
-                List.of(pkColumnName),
-                null,
-                DEFAULT_STORAGE_PROFILE
+        List<CatalogTableColumnDescriptor> columns = List.of(
+                
CatalogUtils.fromParams(ColumnParams.builder().name(pkColumnName).type(INT32).build()),
+                
CatalogUtils.fromParams(ColumnParams.builder().name("STRKEY").length(100).type(STRING).build()),
+                
CatalogUtils.fromParams(ColumnParams.builder().name("INTVAL").type(INT32).build()),
+                
CatalogUtils.fromParams(ColumnParams.builder().name("STRVAL").length(100).type(STRING).build())
         );
+        CatalogTableDescriptor tableDescriptor = 
CatalogTableDescriptor.builder()
+                .id(tableId)
+                .schemaId(schemaId)
+                .primaryKeyIndexId(pkIndexId)
+                .name(TABLE_NAME)
+                .zoneId(zoneId)
+                .columns(columns)
+                .primaryKeyColumns(List.of(pkColumnName))
+                .storageProfile(CatalogService.DEFAULT_STORAGE_PROFILE)
+                .build();
 
         CatalogSortedIndexDescriptor sortedIndex = new 
CatalogSortedIndexDescriptor(
                 sortedIndexId,
diff --git 
a/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/index/AbstractIndexStorageTest.java
 
b/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/index/AbstractIndexStorageTest.java
index 1aa8674188e..f05456a0624 100644
--- 
a/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/index/AbstractIndexStorageTest.java
+++ 
b/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/index/AbstractIndexStorageTest.java
@@ -19,7 +19,6 @@ package org.apache.ignite.internal.storage.index;
 
 import static java.util.stream.Collectors.toList;
 import static java.util.stream.Collectors.toUnmodifiableList;
-import static 
org.apache.ignite.internal.catalog.CatalogService.DEFAULT_STORAGE_PROFILE;
 import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.pkIndexName;
 import static 
org.apache.ignite.internal.storage.BaseMvStoragesTest.getOrCreateMvPartition;
 import static 
org.apache.ignite.internal.storage.util.StorageUtils.initialRowIdToBuild;
@@ -51,10 +50,12 @@ import java.util.Random;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Stream;
 import org.apache.ignite.internal.catalog.Catalog;
+import org.apache.ignite.internal.catalog.CatalogService;
 import org.apache.ignite.internal.catalog.commands.CatalogUtils;
 import org.apache.ignite.internal.catalog.commands.ColumnParams;
 import org.apache.ignite.internal.catalog.descriptors.CatalogIndexDescriptor;
 import 
org.apache.ignite.internal.catalog.descriptors.CatalogIndexDescriptor.CatalogIndexDescriptorType;
+import 
org.apache.ignite.internal.catalog.descriptors.CatalogTableColumnDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor;
 import org.apache.ignite.internal.hlc.HybridClock;
 import org.apache.ignite.internal.hlc.HybridClockImpl;
@@ -161,17 +162,20 @@ public abstract class AbstractIndexStorageTest<S extends 
IndexStorage, D extends
         int zoneId = catalogId.getAndIncrement();
         int pkIndexId = catalogId.getAndIncrement();
 
-        CatalogTableDescriptor tableDescriptor = new CatalogTableDescriptor(
-                tableId,
-                schemaId,
-                pkIndexId,
-                TABLE_NAME,
-                zoneId,
-                Stream.concat(Stream.of(pkColumn), 
ALL_TYPES_COLUMN_PARAMS.stream()).map(CatalogUtils::fromParams).collect(toList()),
-                List.of(pkColumn.name()),
-                null,
-                DEFAULT_STORAGE_PROFILE
-        );
+        List<CatalogTableColumnDescriptor> columns = 
Stream.concat(Stream.of(pkColumn), ALL_TYPES_COLUMN_PARAMS.stream())
+                .map(CatalogUtils::fromParams)
+                .collect(toList());
+        List<String> pkCols = List.of(pkColumn.name());
+        CatalogTableDescriptor tableDescriptor = 
CatalogTableDescriptor.builder()
+                .id(tableId)
+                .schemaId(schemaId)
+                .primaryKeyIndexId(pkIndexId)
+                .name(TABLE_NAME)
+                .zoneId(zoneId)
+                .columns(columns)
+                .primaryKeyColumns(pkCols)
+                .storageProfile(CatalogService.DEFAULT_STORAGE_PROFILE)
+                .build();
 
         when(catalog.table(eq(SCHEMA_NAME), 
eq(TABLE_NAME))).thenReturn(tableDescriptor);
         when(catalog.table(eq(tableId))).thenReturn(tableDescriptor);
diff --git 
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/index/IndexMeta.java
 
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/index/IndexMeta.java
index bbceb07f314..f14d8c44581 100644
--- 
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/index/IndexMeta.java
+++ 
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/index/IndexMeta.java
@@ -94,7 +94,7 @@ public class IndexMeta {
                 catalog.version(),
                 catalogIndexDescriptor.id(),
                 catalogIndexDescriptor.tableId(),
-                catalogTableDescriptor.tableVersion(),
+                catalogTableDescriptor.latestSchemaVersion(),
                 catalogIndexDescriptor.name(),
                 MetaIndexStatus.convert(catalogIndexDescriptor.status()),
                 Map.of(
diff --git 
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/replicator/PartitionReplicaListener.java
 
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/replicator/PartitionReplicaListener.java
index 669a2a58ccc..cfc7b675670 100644
--- 
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/replicator/PartitionReplicaListener.java
+++ 
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/replicator/PartitionReplicaListener.java
@@ -3864,7 +3864,7 @@ public class PartitionReplicaListener implements 
ReplicaListener, ReplicaTablePr
 
         assert table != null : "tableId=" + tableId() + ", catalogVersion=" + 
catalog.version();
 
-        return table.tableVersion();
+        return table.latestSchemaVersion();
     }
 
     private static @Nullable BinaryRow binaryRow(@Nullable TimedBinaryRow 
timedBinaryRow) {
diff --git 
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/schema/SchemaVersionsImpl.java
 
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/schema/SchemaVersionsImpl.java
index 37321ce756c..5e566df2271 100644
--- 
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/schema/SchemaVersionsImpl.java
+++ 
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/schema/SchemaVersionsImpl.java
@@ -54,7 +54,7 @@ public class SchemaVersionsImpl implements SchemaVersions {
     @Override
     public CompletableFuture<Integer> schemaVersionAt(HybridTimestamp 
timestamp, int tableId) {
         return tableDescriptor(tableId, timestamp)
-                .thenApply(CatalogTableDescriptor::tableVersion);
+                .thenApply(CatalogTableDescriptor::latestSchemaVersion);
     }
 
     private CompletableFuture<CatalogTableDescriptor> tableDescriptor(int 
tableId, HybridTimestamp timestamp) {
diff --git 
a/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/index/BaseIndexMetaStorageTest.java
 
b/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/index/BaseIndexMetaStorageTest.java
index adc10f1546c..fd09a558f1e 100644
--- 
a/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/index/BaseIndexMetaStorageTest.java
+++ 
b/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/index/BaseIndexMetaStorageTest.java
@@ -208,7 +208,7 @@ abstract class BaseIndexMetaStorageTest extends 
BaseIgniteAbstractTest {
     }
 
     int latestTableVersionFromCatalog(int tableId) {
-        return getTableStrict(catalogManager, tableId, 
clock.nowLong()).tableVersion();
+        return getTableStrict(catalogManager, tableId, 
clock.nowLong()).latestSchemaVersion();
     }
 
     void updateTableVersion(String tableName) {
diff --git 
a/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/raft/PartitionCommandListenerTest.java
 
b/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/raft/PartitionCommandListenerTest.java
index bbb73fd760b..272b15a936e 100644
--- 
a/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/raft/PartitionCommandListenerTest.java
+++ 
b/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/raft/PartitionCommandListenerTest.java
@@ -287,7 +287,7 @@ public class PartitionCommandListenerTest extends 
BaseIgniteAbstractTest {
 
         int tableVersion = SCHEMA.version();
 
-        
lenient().when(tableDescriptor.tableVersion()).thenReturn(tableVersion);
+        
lenient().when(tableDescriptor.latestSchemaVersion()).thenReturn(tableVersion);
         lenient().when(catalog.table(anyInt())).thenReturn(tableDescriptor);
 
         indexMetaStorage = mock(IndexMetaStorage.class);
diff --git 
a/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/replication/PartitionReplicaListenerIndexLockingTest.java
 
b/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/replication/PartitionReplicaListenerIndexLockingTest.java
index 5ff851166ca..ad5e88d2261 100644
--- 
a/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/replication/PartitionReplicaListenerIndexLockingTest.java
+++ 
b/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/replication/PartitionReplicaListenerIndexLockingTest.java
@@ -243,7 +243,7 @@ public class PartitionReplicaListenerIndexLockingTest 
extends IgniteAbstractTest
         when(catalogService.catalog(anyInt())).thenReturn(catalog);
 
         CatalogTableDescriptor tableDescriptor = 
mock(CatalogTableDescriptor.class);
-        
when(tableDescriptor.tableVersion()).thenReturn(schemaDescriptor.version());
+        
when(tableDescriptor.latestSchemaVersion()).thenReturn(schemaDescriptor.version());
 
         when(catalog.table(anyInt())).thenReturn(tableDescriptor);
         when(catalog.table(anyInt())).thenReturn(tableDescriptor);
diff --git 
a/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/replication/PartitionReplicaListenerSortedIndexLockingTest.java
 
b/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/replication/PartitionReplicaListenerSortedIndexLockingTest.java
index 54acdd64e82..6464c29d946 100644
--- 
a/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/replication/PartitionReplicaListenerSortedIndexLockingTest.java
+++ 
b/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/replication/PartitionReplicaListenerSortedIndexLockingTest.java
@@ -217,7 +217,7 @@ public class PartitionReplicaListenerSortedIndexLockingTest 
extends IgniteAbstra
         
when(catalogService.catalogReadyFuture(anyInt())).thenReturn(nullCompletedFuture());
 
         CatalogTableDescriptor tableDescriptor = 
mock(CatalogTableDescriptor.class);
-        
when(tableDescriptor.tableVersion()).thenReturn(schemaDescriptor.version());
+        
when(tableDescriptor.latestSchemaVersion()).thenReturn(schemaDescriptor.version());
 
         when(catalog.table(anyInt())).thenReturn(tableDescriptor);
         when(catalog.table(anyInt())).thenReturn(tableDescriptor);
diff --git 
a/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/replication/PartitionReplicaListenerTest.java
 
b/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/replication/PartitionReplicaListenerTest.java
index a67e31e8b37..442b02a220c 100644
--- 
a/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/replication/PartitionReplicaListenerTest.java
+++ 
b/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/replication/PartitionReplicaListenerTest.java
@@ -22,7 +22,6 @@ import static 
java.util.concurrent.CompletableFuture.completedFuture;
 import static java.util.concurrent.CompletableFuture.failedFuture;
 import static java.util.stream.Collectors.toList;
 import static java.util.stream.Collectors.toMap;
-import static 
org.apache.ignite.internal.catalog.CatalogService.DEFAULT_STORAGE_PROFILE;
 import static 
org.apache.ignite.internal.catalog.events.CatalogEvent.INDEX_BUILDING;
 import static org.apache.ignite.internal.hlc.HybridTimestamp.hybridTimestamp;
 import static 
org.apache.ignite.internal.lang.IgniteSystemProperties.COLOCATION_FEATURE_FLAG;
@@ -448,18 +447,26 @@ public class PartitionReplicaListenerTest extends 
IgniteAbstractTest {
     /** Key-value marshaller using schema version 2. */
     private KvMarshaller<TestKey, TestValue> kvMarshallerVersion2;
 
-    private final CatalogTableDescriptor tableDescriptor = new 
CatalogTableDescriptor(
-            TABLE_ID, 1, 2, TABLE_NAME, 1,
-            List.of(
-                    new CatalogTableColumnDescriptor("intKey", 
ColumnType.INT32, false, 0, 0, 0, null),
-                    new CatalogTableColumnDescriptor("strKey", 
ColumnType.STRING, false, 0, 0, 0, null),
-                    new CatalogTableColumnDescriptor("intVal", 
ColumnType.INT32, false, 0, 0, 0, null),
-                    new CatalogTableColumnDescriptor("strVal", 
ColumnType.STRING, false, 0, 0, 0, null)
-            ),
-            List.of("intKey", "strKey"),
-            null,
-            DEFAULT_STORAGE_PROFILE
-    );
+    private final CatalogTableDescriptor tableDescriptor;
+
+    {
+        List<CatalogTableColumnDescriptor> columns = List.of(
+                new CatalogTableColumnDescriptor("intKey", ColumnType.INT32, 
false, 0, 0, 0, null),
+                new CatalogTableColumnDescriptor("strKey", ColumnType.STRING, 
false, 0, 0, 0, null),
+                new CatalogTableColumnDescriptor("intVal", ColumnType.INT32, 
false, 0, 0, 0, null),
+                new CatalogTableColumnDescriptor("strVal", ColumnType.STRING, 
false, 0, 0, 0, null)
+        );
+        tableDescriptor = CatalogTableDescriptor.builder()
+                .id(TABLE_ID)
+                .schemaId(1)
+                .primaryKeyIndexId(2)
+                .name(TABLE_NAME)
+                .zoneId(1)
+                .columns(columns)
+                .primaryKeyColumns(List.of("intKey", "strKey"))
+                .storageProfile(CatalogService.DEFAULT_STORAGE_PROFILE)
+                .build();
+    }
 
     /** Placement driver. */
     private TestPlacementDriver placementDriver;
@@ -2482,8 +2489,8 @@ public class PartitionReplicaListenerTest extends 
IgniteAbstractTest {
 
         when(tableVersion1.name()).thenReturn(TABLE_NAME);
         when(tableVersion2.name()).thenReturn(TABLE_NAME_2);
-        when(tableVersion1.tableVersion()).thenReturn(CURRENT_SCHEMA_VERSION);
-        when(tableVersion2.tableVersion()).thenReturn(NEXT_SCHEMA_VERSION);
+        
when(tableVersion1.latestSchemaVersion()).thenReturn(CURRENT_SCHEMA_VERSION);
+        
when(tableVersion2.latestSchemaVersion()).thenReturn(NEXT_SCHEMA_VERSION);
 
         Catalog catalog1 = mock(Catalog.class);
         when(catalog1.table(TABLE_ID)).thenReturn(tableVersion1);
@@ -2604,7 +2611,7 @@ public class PartitionReplicaListenerTest extends 
IgniteAbstractTest {
 
     private void makeTableBeDroppedAfter(HybridTimestamp txBeginTs, int 
tableId) {
         CatalogTableDescriptor tableVersion1 = 
mock(CatalogTableDescriptor.class);
-        when(tableVersion1.tableVersion()).thenReturn(CURRENT_SCHEMA_VERSION);
+        
when(tableVersion1.latestSchemaVersion()).thenReturn(CURRENT_SCHEMA_VERSION);
         when(tableVersion1.name()).thenReturn(TABLE_NAME);
 
         when(catalog.table(tableId)).thenReturn(tableVersion1);
@@ -2830,7 +2837,7 @@ public class PartitionReplicaListenerTest extends 
IgniteAbstractTest {
 
     private void makeSchemaBeNextVersion() {
         CatalogTableDescriptor tableVersion2 = 
mock(CatalogTableDescriptor.class);
-        when(tableVersion2.tableVersion()).thenReturn(NEXT_SCHEMA_VERSION);
+        
when(tableVersion2.latestSchemaVersion()).thenReturn(NEXT_SCHEMA_VERSION);
         when(tableVersion2.name()).thenReturn(TABLE_NAME_2);
 
         when(catalog.table(eq(TABLE_ID))).thenReturn(tableVersion2);
diff --git 
a/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/replication/ZonePartitionReplicaListenerTest.java
 
b/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/replication/ZonePartitionReplicaListenerTest.java
index 9b011cc0e96..bacda373b05 100644
--- 
a/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/replication/ZonePartitionReplicaListenerTest.java
+++ 
b/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/replication/ZonePartitionReplicaListenerTest.java
@@ -24,7 +24,6 @@ import static 
java.util.concurrent.CompletableFuture.failedFuture;
 import static java.util.concurrent.TimeUnit.SECONDS;
 import static java.util.stream.Collectors.toList;
 import static java.util.stream.Collectors.toMap;
-import static 
org.apache.ignite.internal.catalog.CatalogService.DEFAULT_STORAGE_PROFILE;
 import static org.apache.ignite.internal.hlc.HybridTimestamp.hybridTimestamp;
 import static 
org.apache.ignite.internal.lang.IgniteSystemProperties.COLOCATION_FEATURE_FLAG;
 import static 
org.apache.ignite.internal.lang.IgniteSystemProperties.colocationEnabled;
@@ -403,18 +402,26 @@ public class ZonePartitionReplicaListenerTest extends 
IgniteAbstractTest {
     /** Key-value marshaller for tests. */
     private KvMarshaller<TestKey, TestValue> kvMarshaller;
 
-    private final CatalogTableDescriptor tableDescriptor = new 
CatalogTableDescriptor(
-            TABLE_ID, 1, 2, TABLE_NAME, 1,
-            List.of(
-                    new CatalogTableColumnDescriptor("intKey", 
ColumnType.INT32, false, 0, 0, 0, null),
-                    new CatalogTableColumnDescriptor("strKey", 
ColumnType.STRING, false, 0, 0, 0, null),
-                    new CatalogTableColumnDescriptor("intVal", 
ColumnType.INT32, false, 0, 0, 0, null),
-                    new CatalogTableColumnDescriptor("strVal", 
ColumnType.STRING, false, 0, 0, 0, null)
-            ),
-            List.of("intKey", "strKey"),
-            null,
-            DEFAULT_STORAGE_PROFILE
-    );
+    private final CatalogTableDescriptor tableDescriptor;
+
+    {
+        List<CatalogTableColumnDescriptor> columns = List.of(
+                new CatalogTableColumnDescriptor("intKey", ColumnType.INT32, 
false, 0, 0, 0, null),
+                new CatalogTableColumnDescriptor("strKey", ColumnType.STRING, 
false, 0, 0, 0, null),
+                new CatalogTableColumnDescriptor("intVal", ColumnType.INT32, 
false, 0, 0, 0, null),
+                new CatalogTableColumnDescriptor("strVal", ColumnType.STRING, 
false, 0, 0, 0, null)
+        );
+        tableDescriptor = CatalogTableDescriptor.builder()
+                .id(TABLE_ID)
+                .schemaId(1)
+                .primaryKeyIndexId(2)
+                .name(TABLE_NAME)
+                .zoneId(1)
+                .columns(columns)
+                .primaryKeyColumns(List.of("intKey", "strKey"))
+                .storageProfile(CatalogService.DEFAULT_STORAGE_PROFILE)
+                .build();
+    }
 
     /** Placement driver. */
     private TestPlacementDriver placementDriver;
@@ -1036,7 +1043,7 @@ public class ZonePartitionReplicaListenerTest extends 
IgniteAbstractTest {
 
     private void makeTableBeDroppedAfter(HybridTimestamp txBeginTs, int 
tableId) {
         CatalogTableDescriptor tableVersion1 = 
mock(CatalogTableDescriptor.class);
-        when(tableVersion1.tableVersion()).thenReturn(CURRENT_SCHEMA_VERSION);
+        
when(tableVersion1.latestSchemaVersion()).thenReturn(CURRENT_SCHEMA_VERSION);
         when(tableVersion1.name()).thenReturn(TABLE_NAME);
 
         when(catalog.table(tableId)).thenReturn(tableVersion1);
@@ -1173,7 +1180,7 @@ public class ZonePartitionReplicaListenerTest extends 
IgniteAbstractTest {
 
     private void makeSchemaBeNextVersion() {
         CatalogTableDescriptor tableVersion2 = 
mock(CatalogTableDescriptor.class);
-        when(tableVersion2.tableVersion()).thenReturn(NEXT_SCHEMA_VERSION);
+        
when(tableVersion2.latestSchemaVersion()).thenReturn(NEXT_SCHEMA_VERSION);
         when(tableVersion2.name()).thenReturn(TABLE_NAME_2);
 
         when(catalog.table(eq(TABLE_ID))).thenReturn(tableVersion2);
diff --git 
a/modules/table/src/testFixtures/java/org/apache/ignite/distributed/ItTxTestCluster.java
 
b/modules/table/src/testFixtures/java/org/apache/ignite/distributed/ItTxTestCluster.java
index f1e481000b8..f53682bb5f6 100644
--- 
a/modules/table/src/testFixtures/java/org/apache/ignite/distributed/ItTxTestCluster.java
+++ 
b/modules/table/src/testFixtures/java/org/apache/ignite/distributed/ItTxTestCluster.java
@@ -640,7 +640,7 @@ public class ItTxTestCluster {
 
         CatalogTableDescriptor tableDescriptor = 
mock(CatalogTableDescriptor.class);
         when(tableDescriptor.id()).thenReturn(tableId);
-        when(tableDescriptor.tableVersion()).thenReturn(SCHEMA_VERSION);
+        when(tableDescriptor.latestSchemaVersion()).thenReturn(SCHEMA_VERSION);
 
         lenient().when(catalog.table(eq(tableId))).thenReturn(tableDescriptor);
 
diff --git 
a/modules/table/src/testFixtures/java/org/apache/ignite/internal/table/impl/DummyInternalTableImpl.java
 
b/modules/table/src/testFixtures/java/org/apache/ignite/internal/table/impl/DummyInternalTableImpl.java
index 50ea759096e..3dd865fea1d 100644
--- 
a/modules/table/src/testFixtures/java/org/apache/ignite/internal/table/impl/DummyInternalTableImpl.java
+++ 
b/modules/table/src/testFixtures/java/org/apache/ignite/internal/table/impl/DummyInternalTableImpl.java
@@ -462,7 +462,7 @@ public class DummyInternalTableImpl extends 
InternalTableImpl {
         lenient().when(catalogService.catalog(anyInt())).thenReturn(catalog);
         
lenient().when(catalogService.activeCatalog(anyLong())).thenReturn(catalog);
         lenient().when(catalog.table(anyInt())).thenReturn(tableDescriptor);
-        lenient().when(tableDescriptor.tableVersion()).thenReturn(1);
+        lenient().when(tableDescriptor.latestSchemaVersion()).thenReturn(1);
 
         CatalogIndexDescriptor indexDescriptor = 
mock(CatalogIndexDescriptor.class);
         lenient().when(indexDescriptor.id()).thenReturn(pkStorage.get().id());

Reply via email to