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

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

commit 204726d2b0d11241bc34e476c7aa51aa539154d0
Author: Mikhail Pochatkin <[email protected]>
AuthorDate: Wed Sep 25 17:56:11 2024 +0300

    IGNITE-23185 Add methods to access table and zone definitions
---
 .../java/org/apache/ignite/catalog/ColumnType.java |   2 +
 .../org/apache/ignite/catalog/IgniteCatalog.java   |  20 +++
 .../catalog/definitions/ColumnDefinition.java      |  19 +++
 .../catalog/definitions/IndexDefinition.java       |  20 +++
 .../catalog/definitions/TableDefinition.java       |  40 +++++
 .../ignite/catalog/definitions/ZoneDefinition.java |  16 ++
 .../lang}/DistributionZoneNotFoundException.java   |   5 +-
 modules/catalog-dsl/build.gradle                   |   1 +
 .../ignite/internal/catalog/ItCatalogDslTest.java  |  85 +++++++++-
 .../internal/matcher/ColumnDefinitionsMatcher.java |  43 +++++
 .../internal/matcher/TableDefinitionMatcher.java   | 185 +++++++++++++++++++++
 .../internal/matcher/ZoneDefinitionMatcher.java    | 166 ++++++++++++++++++
 .../catalog/sql/CreateFromDefinitionImpl.java      |  12 +-
 .../internal/catalog/sql/CreateTableImpl.java      |   4 +-
 .../internal/catalog/sql/CreateZoneImpl.java       |  18 +-
 .../internal/catalog/sql/IgniteCatalogSqlImpl.java | 159 ++++++++++++++++++
 .../apache/ignite/internal/catalog/sql/Option.java |  96 +++++++++++
 .../ignite/internal/catalog/sql/QueryUtils.java    |   4 +
 .../internal/catalog/sql/SelectFromView.java       |  88 ++++++++++
 .../ignite/internal/catalog/sql/WithOption.java    |  80 ---------
 .../ignite/internal/catalog/sql/QueryPartTest.java |   6 +-
 .../catalog/CatalogSystemViewRegistry.java         |   4 +-
 .../systemviews/TablesSystemViewProvider.java      | 172 +++++++++++++++++++
 .../systemviews/ZonesSystemViewProvider.java       |  51 +++++-
 .../java/org/apache/ignite/internal/cli/Main.java  |   2 +-
 .../ignite/internal/client/TcpIgniteClient.java    |   2 +-
 .../distributionzones/DistributionZoneManager.java |   2 +-
 .../CausalityDataNodesEngine.java                  |   2 +-
 .../DistributionZoneCausalityDataNodesTest.java    |   2 +-
 .../threading/PublicApiThreadingIgniteCatalog.java |  21 +++
 .../disaster/DisasterRecoveryManager.java          |   2 +-
 31 files changed, 1210 insertions(+), 119 deletions(-)

diff --git 
a/modules/api/src/main/java/org/apache/ignite/catalog/ColumnType.java 
b/modules/api/src/main/java/org/apache/ignite/catalog/ColumnType.java
index 08fd08cf0a..26bb62c4bd 100644
--- a/modules/api/src/main/java/org/apache/ignite/catalog/ColumnType.java
+++ b/modules/api/src/main/java/org/apache/ignite/catalog/ColumnType.java
@@ -352,4 +352,6 @@ public class ColumnType<T> {
         this.length = length;
         return this;
     }
+
+
 }
diff --git 
a/modules/api/src/main/java/org/apache/ignite/catalog/IgniteCatalog.java 
b/modules/api/src/main/java/org/apache/ignite/catalog/IgniteCatalog.java
index c4d37d9219..69362eb922 100644
--- a/modules/api/src/main/java/org/apache/ignite/catalog/IgniteCatalog.java
+++ b/modules/api/src/main/java/org/apache/ignite/catalog/IgniteCatalog.java
@@ -191,6 +191,22 @@ public interface IgniteCatalog {
      */
     Table createTable(TableDefinition definition);
 
+    /**
+     * Returns definition of the table with provided name.
+     *
+     * @param tableName Table name.
+     * @return Definition of the table with provided name.
+     */
+    CompletableFuture<TableDefinition> tableDefinitionAsync(String tableName);
+
+    /**
+     * Returns definition of the table with provided name.
+     *
+     * @param tableName Table name.
+     * @return Definition of the table with provided name.
+     */
+    TableDefinition tableDefinition(String tableName);
+
     /**
      * Creates a query object from the zone definition.
      *
@@ -205,6 +221,10 @@ public interface IgniteCatalog {
      */
     void createZone(ZoneDefinition definition);
 
+    CompletableFuture<ZoneDefinition> zoneDefinitionAsync(String zoneName);
+
+    ZoneDefinition zoneDefinition(String zoneName);
+
     /**
      * Creates a {@code DROP TABLE} query object from the table definition.
      *
diff --git 
a/modules/api/src/main/java/org/apache/ignite/catalog/definitions/ColumnDefinition.java
 
b/modules/api/src/main/java/org/apache/ignite/catalog/definitions/ColumnDefinition.java
index af32f8dd92..f42bbf3960 100644
--- 
a/modules/api/src/main/java/org/apache/ignite/catalog/definitions/ColumnDefinition.java
+++ 
b/modules/api/src/main/java/org/apache/ignite/catalog/definitions/ColumnDefinition.java
@@ -100,4 +100,23 @@ public class ColumnDefinition {
     public @Nullable String definition() {
         return definition;
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        ColumnDefinition that = (ColumnDefinition) o;
+        return Objects.equals(name, that.name)
+                && Objects.equals(type, that.type)
+                && Objects.equals(definition, that.definition);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(name, type, definition);
+    }
 }
diff --git 
a/modules/api/src/main/java/org/apache/ignite/catalog/definitions/IndexDefinition.java
 
b/modules/api/src/main/java/org/apache/ignite/catalog/definitions/IndexDefinition.java
index bc8f6d9fc5..6b0647fa33 100644
--- 
a/modules/api/src/main/java/org/apache/ignite/catalog/definitions/IndexDefinition.java
+++ 
b/modules/api/src/main/java/org/apache/ignite/catalog/definitions/IndexDefinition.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.catalog.definitions;
 
 import java.util.List;
+import java.util.Objects;
 import org.apache.ignite.catalog.ColumnSorted;
 import org.apache.ignite.catalog.IndexType;
 import org.jetbrains.annotations.Nullable;
@@ -65,4 +66,23 @@ public class IndexDefinition {
     public List<ColumnSorted> columns() {
         return columns;
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        IndexDefinition that = (IndexDefinition) o;
+        return Objects.equals(name, that.name)
+                && type == that.type
+                && Objects.equals(columns, that.columns);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(name, type, columns);
+    }
 }
diff --git 
a/modules/api/src/main/java/org/apache/ignite/catalog/definitions/TableDefinition.java
 
b/modules/api/src/main/java/org/apache/ignite/catalog/definitions/TableDefinition.java
index fdbccc4726..e6c0fabf50 100644
--- 
a/modules/api/src/main/java/org/apache/ignite/catalog/definitions/TableDefinition.java
+++ 
b/modules/api/src/main/java/org/apache/ignite/catalog/definitions/TableDefinition.java
@@ -206,6 +206,46 @@ public class TableDefinition {
         return new Builder(this);
     }
 
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        TableDefinition that = (TableDefinition) o;
+        return ifNotExists == that.ifNotExists
+                && Objects.equals(tableName, that.tableName)
+                && Objects.equals(schemaName, that.schemaName)
+                && Objects.equals(columns, that.columns)
+                && pkType == that.pkType
+                && Objects.equals(pkColumns, that.pkColumns)
+                && Objects.equals(colocationColumns, that.colocationColumns)
+                && Objects.equals(zoneName, that.zoneName)
+                && Objects.equals(keyClass, that.keyClass)
+                && Objects.equals(valueClass, that.valueClass)
+                && Objects.equals(indexes, that.indexes);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                tableName,
+                schemaName,
+                ifNotExists,
+                columns,
+                pkType,
+                pkColumns,
+                colocationColumns,
+                zoneName,
+                keyClass,
+                valueClass,
+                indexes
+        );
+    }
+
     /**
      * Builder for the table definition.
      */
diff --git 
a/modules/api/src/main/java/org/apache/ignite/catalog/definitions/ZoneDefinition.java
 
b/modules/api/src/main/java/org/apache/ignite/catalog/definitions/ZoneDefinition.java
index ded14aea11..605f4c2918 100644
--- 
a/modules/api/src/main/java/org/apache/ignite/catalog/definitions/ZoneDefinition.java
+++ 
b/modules/api/src/main/java/org/apache/ignite/catalog/definitions/ZoneDefinition.java
@@ -176,6 +176,22 @@ public class ZoneDefinition {
         return new Builder(this);
     }
 
+    @Override
+    public String toString() {
+        return "ZoneDefinition{"
+                + "zoneName='" + zoneName + '\''
+                + ", ifNotExists=" + ifNotExists
+                + ", partitions=" + partitions
+                + ", replicas=" + replicas
+                + ", distributionAlgorithm='" + distributionAlgorithm + '\''
+                + ", dataNodesAutoAdjust=" + dataNodesAutoAdjust
+                + ", dataNodesAutoAdjustScaleUp=" + dataNodesAutoAdjustScaleUp
+                + ", dataNodesAutoAdjustScaleDown=" + 
dataNodesAutoAdjustScaleDown
+                + ", filter='" + filter + '\''
+                + ", storageProfiles='" + storageProfiles + '\''
+                + '}';
+    }
+
     /**
      * Builder for the zone definition.
      */
diff --git 
a/modules/distribution-zones/src/main/java/org/apache/ignite/internal/distributionzones/exception/DistributionZoneNotFoundException.java
 
b/modules/api/src/main/java/org/apache/ignite/lang/DistributionZoneNotFoundException.java
similarity index 91%
rename from 
modules/distribution-zones/src/main/java/org/apache/ignite/internal/distributionzones/exception/DistributionZoneNotFoundException.java
rename to 
modules/api/src/main/java/org/apache/ignite/lang/DistributionZoneNotFoundException.java
index 5e148cf135..b49232729f 100644
--- 
a/modules/distribution-zones/src/main/java/org/apache/ignite/internal/distributionzones/exception/DistributionZoneNotFoundException.java
+++ 
b/modules/api/src/main/java/org/apache/ignite/lang/DistributionZoneNotFoundException.java
@@ -15,18 +15,17 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.distributionzones.exception;
+package org.apache.ignite.lang;
 
 import static 
org.apache.ignite.lang.ErrorGroups.DistributionZones.ZONE_NOT_FOUND_ERR;
 
 import java.util.UUID;
-import org.apache.ignite.internal.lang.IgniteInternalException;
 import org.jetbrains.annotations.Nullable;
 
 /**
  * Exception is thrown when appropriate distribution zone can`t be found.
  */
-public class DistributionZoneNotFoundException extends IgniteInternalException 
{
+public class DistributionZoneNotFoundException extends IgniteException {
     private static final long serialVersionUID = 4690347752201656106L;
 
     /**
diff --git a/modules/catalog-dsl/build.gradle b/modules/catalog-dsl/build.gradle
index 5f4eff73f1..6bb7111114 100644
--- a/modules/catalog-dsl/build.gradle
+++ b/modules/catalog-dsl/build.gradle
@@ -26,6 +26,7 @@ dependencies {
     implementation libs.jetbrains.annotations
     implementation project(':ignite-api')
     implementation project(':ignite-core')
+    implementation project(':ignite-catalog')
 
     testImplementation libs.hamcrest.core
     testImplementation testFixtures(project(':ignite-core'))
diff --git 
a/modules/catalog-dsl/src/integrationTest/java/org/apache/ignite/internal/catalog/ItCatalogDslTest.java
 
b/modules/catalog-dsl/src/integrationTest/java/org/apache/ignite/internal/catalog/ItCatalogDslTest.java
index d786d9d680..9b9c0377c9 100644
--- 
a/modules/catalog-dsl/src/integrationTest/java/org/apache/ignite/internal/catalog/ItCatalogDslTest.java
+++ 
b/modules/catalog-dsl/src/integrationTest/java/org/apache/ignite/internal/catalog/ItCatalogDslTest.java
@@ -21,6 +21,7 @@ import static 
org.apache.ignite.catalog.definitions.ColumnDefinition.column;
 import static 
org.apache.ignite.internal.TestDefaultProfilesNames.DEFAULT_AIPERSIST_PROFILE_NAME;
 import static 
org.apache.ignite.internal.testframework.IgniteTestUtils.assertThrows;
 import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.will;
+import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willBe;
 import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willCompleteSuccessfully;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.contains;
@@ -31,25 +32,31 @@ import static org.hamcrest.Matchers.nullValue;
 import java.util.List;
 import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
+import org.apache.ignite.catalog.ColumnSorted;
 import org.apache.ignite.catalog.ColumnType;
 import org.apache.ignite.catalog.IgniteCatalog;
+import org.apache.ignite.catalog.IndexType;
+import org.apache.ignite.catalog.definitions.ColumnDefinition;
 import org.apache.ignite.catalog.definitions.TableDefinition;
 import org.apache.ignite.catalog.definitions.ZoneDefinition;
 import org.apache.ignite.internal.ClusterPerClassIntegrationTest;
+import org.apache.ignite.internal.matcher.TableDefinitionMatcher;
+import org.apache.ignite.internal.matcher.ZoneDefinitionMatcher;
 import org.apache.ignite.sql.SqlException;
 import org.apache.ignite.table.KeyValueView;
 import org.apache.ignite.table.RecordView;
+import org.apache.ignite.table.Table;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Test;
 
 @SuppressWarnings("ThrowableNotThrown")
 class ItCatalogDslTest extends ClusterPerClassIntegrationTest {
 
-    static final String POJO_KV_TABLE_NAME = "pojo_kv_test";
+    static final String POJO_KV_TABLE_NAME = "POJO_KV_TEST";
 
     static final String POJO_RECORD_TABLE_NAME = "pojo_record_test";
 
-    static final String ZONE_NAME = "zone_test";
+    static final String ZONE_NAME = "ZONE_TEST";
 
     private static final int KEY = 1;
 
@@ -190,7 +197,7 @@ class ItCatalogDslTest extends 
ClusterPerClassIntegrationTest {
 
     @Test
     void primitiveKeyKvViewFromAnnotation() throws Exception {
-        CompletableFuture<org.apache.ignite.table.Table> tableFuture = 
catalog().createTableAsync(Integer.class, PojoValue.class);
+        CompletableFuture<Table> tableFuture = 
catalog().createTableAsync(Integer.class, PojoValue.class);
         assertThat(tableFuture, will(not(nullValue())));
 
         KeyValueView<Integer, PojoValue> keyValueView = tableFuture.get()
@@ -202,7 +209,7 @@ class ItCatalogDslTest extends 
ClusterPerClassIntegrationTest {
 
     @Test
     void pojoKeyKvViewFromAnnotation() throws Exception {
-        CompletableFuture<org.apache.ignite.table.Table> tableFuture = 
catalog().createTableAsync(PojoKey.class, PojoValue.class);
+        CompletableFuture<Table> tableFuture = 
catalog().createTableAsync(PojoKey.class, PojoValue.class);
         assertThat(tableFuture, will(not(nullValue())));
 
         KeyValueView<PojoKey, PojoValue> keyValueView = tableFuture.get()
@@ -219,7 +226,7 @@ class ItCatalogDslTest extends 
ClusterPerClassIntegrationTest {
                 .value(PojoValue.class)
                 .build();
 
-        CompletableFuture<org.apache.ignite.table.Table> tableFuture = 
catalog().createTableAsync(definition);
+        CompletableFuture<Table> tableFuture = 
catalog().createTableAsync(definition);
         assertThat(tableFuture, will(not(nullValue())));
 
         KeyValueView<Integer, PojoValue> keyValueView = 
tableFuture.get().keyValueView(Integer.class, PojoValue.class);
@@ -235,7 +242,7 @@ class ItCatalogDslTest extends 
ClusterPerClassIntegrationTest {
                 .value(PojoValue.class)
                 .build();
 
-        CompletableFuture<org.apache.ignite.table.Table> tableFuture = 
catalog().createTableAsync(definition);
+        CompletableFuture<Table> tableFuture = 
catalog().createTableAsync(definition);
         assertThat(tableFuture, will(not(nullValue())));
 
         KeyValueView<PojoKey, PojoValue> keyValueView = 
tableFuture.get().keyValueView(PojoKey.class, PojoValue.class);
@@ -246,7 +253,7 @@ class ItCatalogDslTest extends 
ClusterPerClassIntegrationTest {
 
     @Test
     void pojoRecordViewFromAnnotation() throws Exception {
-        CompletableFuture<org.apache.ignite.table.Table> tableFuture = 
catalog().createTableAsync(Pojo.class);
+        CompletableFuture<Table> tableFuture = 
catalog().createTableAsync(Pojo.class);
         assertThat(tableFuture, will(not(nullValue())));
 
         RecordView<Pojo> recordView = tableFuture.get().recordView(Pojo.class);
@@ -259,7 +266,7 @@ class ItCatalogDslTest extends 
ClusterPerClassIntegrationTest {
     void pojoRecordViewFromDefinition() throws Exception {
         TableDefinition definition = 
TableDefinition.builder(POJO_RECORD_TABLE_NAME).record(Pojo.class).build();
 
-        CompletableFuture<org.apache.ignite.table.Table> tableFuture = 
catalog().createTableAsync(definition);
+        CompletableFuture<Table> tableFuture = 
catalog().createTableAsync(definition);
         assertThat(tableFuture, will(not(nullValue())));
 
         RecordView<Pojo> recordView = tableFuture.get().recordView(Pojo.class);
@@ -270,7 +277,7 @@ class ItCatalogDslTest extends 
ClusterPerClassIntegrationTest {
 
     @Test
     void createFromAnnotationAndInsertBySql() throws Exception {
-        CompletableFuture<org.apache.ignite.table.Table> tableFuture = 
catalog().createTableAsync(Pojo.class);
+        CompletableFuture<Table> tableFuture = 
catalog().createTableAsync(Pojo.class);
         assertThat(tableFuture, will(not(nullValue())));
 
         sql("insert into " + POJO_RECORD_TABLE_NAME + " (id, id_str, f_name, 
l_name, str) values (1, '1', 'f', 'l', 's')");
@@ -282,6 +289,66 @@ class ItCatalogDslTest extends 
ClusterPerClassIntegrationTest {
         assertThat(tableFuture.get().recordView(Pojo.class).get(null, pojo), 
is(pojo));
     }
 
+    @Test
+    public void createAndGetDefinitionTest() {
+        ZoneDefinition zoneDefinition = ZoneDefinition
+                .builder(ZONE_NAME)
+                .storageProfiles(DEFAULT_AIPERSIST_PROFILE_NAME)
+                .partitions(3)
+                .replicas(3)
+                .dataNodesAutoAdjustScaleDown(0)
+                .dataNodesAutoAdjustScaleUp(1)
+                .filter("$..*")
+                .distributionAlgorithm("distributionAlgorithm")
+                .build();
+
+        assertThat(catalog().createZoneAsync(zoneDefinition), 
willCompleteSuccessfully());
+
+        ZoneDefinition actual = catalog().zoneDefinition(ZONE_NAME);
+        assertThat(
+                actual,
+                ZoneDefinitionMatcher.isZoneDefinition()
+                        .withZoneName(zoneDefinition.zoneName())
+                        .withPartitions(zoneDefinition.partitions())
+                        .withReplicas(zoneDefinition.replicas())
+                        
.withDataNodesAutoAdjustScaleDown(zoneDefinition.dataNodesAutoAdjustScaleDown())
+                        
.withDataNodesAutoAdjustScaleUp(zoneDefinition.dataNodesAutoAdjustScaleUp())
+                        .withFilter(zoneDefinition.filter())
+                        // TODO: 
https://issues.apache.org/jira/browse/IGNITE-22162
+//                        
.withDistributionAlgorithm(zoneDefinition.distributionAlgorithm())
+        );
+
+        ColumnDefinition column1 = column("COL1", ColumnType.INT32);
+        ColumnDefinition column2 = column("COL2", ColumnType.INT64);
+        ColumnDefinition column3 = column("COL3", ColumnType.BOOLEAN);
+        ColumnDefinition column4 = column("COL4", ColumnType.VARCHAR);
+        ColumnDefinition column5 = column("COL5", ColumnType.DECIMAL);
+
+
+        TableDefinition definition = 
TableDefinition.builder(POJO_KV_TABLE_NAME)
+                .zone(ZONE_NAME)
+                .columns(List.of(column1, column2, column3, column4, column5))
+                .primaryKey(IndexType.HASH, 
ColumnSorted.column(column1.name()), ColumnSorted.column(column3.name()))
+                .index("INDEX_1", IndexType.HASH, 
ColumnSorted.column(column2.name()), ColumnSorted.column(column5.name()))
+                .colocateBy(column3.name())
+                .build();
+
+        assertThat(catalog().createTableAsync(definition), 
willCompleteSuccessfully());
+
+        TableDefinition actual1 = 
catalog().tableDefinition(POJO_KV_TABLE_NAME);
+        assertThat(
+                actual1,
+                TableDefinitionMatcher.isTableDefinition()
+                        .withTableName(definition.tableName())
+                        .withZoneName(definition.zoneName())
+                        .withColumns(definition.columns())
+                        .withPkType(definition.primaryKeyType())
+                        .withPkColumns(definition.primaryKeyColumns())
+                        .withIndexes(definition.indexes())
+                        .withColocationColumns(definition.colocationColumns())
+        );
+    }
+
     private static IgniteCatalog catalog() {
         return CLUSTER.node(0).catalog();
     }
diff --git 
a/modules/catalog-dsl/src/integrationTest/java/org/apache/ignite/internal/matcher/ColumnDefinitionsMatcher.java
 
b/modules/catalog-dsl/src/integrationTest/java/org/apache/ignite/internal/matcher/ColumnDefinitionsMatcher.java
new file mode 100644
index 0000000000..247ad50d4d
--- /dev/null
+++ 
b/modules/catalog-dsl/src/integrationTest/java/org/apache/ignite/internal/matcher/ColumnDefinitionsMatcher.java
@@ -0,0 +1,43 @@
+package org.apache.ignite.internal.matcher;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import org.apache.ignite.catalog.ColumnType;
+import org.apache.ignite.catalog.definitions.ColumnDefinition;
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeMatcher;
+
+public class ColumnDefinitionsMatcher extends 
TypeSafeMatcher<List<ColumnDefinition>> {
+
+    private final Map<String, ColumnDefinition> columns;
+
+    public ColumnDefinitionsMatcher(List<ColumnDefinition> columns) {
+        this.columns = 
columns.stream().collect(Collectors.toMap(ColumnDefinition::name, 
Function.identity()));
+    }
+
+    @Override
+    protected boolean matchesSafely(List<ColumnDefinition> item) {
+        for (ColumnDefinition actualCol : item) {
+            ColumnDefinition expectedCol = columns.get(actualCol.name());
+            if (expectedCol == null) {
+                return false;
+            }
+
+            ColumnType<?> actualType = actualCol.type();
+            ColumnType<?> expectedType = expectedCol.type();
+            if (expectedType != null && actualType != null && 
expectedType.type() != actualType.type()) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    public void describeTo(Description description) {
+
+    }
+}
diff --git 
a/modules/catalog-dsl/src/integrationTest/java/org/apache/ignite/internal/matcher/TableDefinitionMatcher.java
 
b/modules/catalog-dsl/src/integrationTest/java/org/apache/ignite/internal/matcher/TableDefinitionMatcher.java
new file mode 100644
index 0000000000..e742d6f0e8
--- /dev/null
+++ 
b/modules/catalog-dsl/src/integrationTest/java/org/apache/ignite/internal/matcher/TableDefinitionMatcher.java
@@ -0,0 +1,185 @@
+package org.apache.ignite.internal.matcher;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.equalToIgnoringCase;
+import static org.hamcrest.Matchers.in;
+
+import java.util.List;
+import org.apache.ignite.catalog.ColumnSorted;
+import org.apache.ignite.catalog.IndexType;
+import org.apache.ignite.catalog.definitions.ColumnDefinition;
+import org.apache.ignite.catalog.definitions.IndexDefinition;
+import org.apache.ignite.catalog.definitions.TableDefinition;
+import org.apache.ignite.internal.testframework.matchers.AnythingMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.Matchers;
+import org.hamcrest.TypeSafeMatcher;
+
+public class TableDefinitionMatcher extends TypeSafeMatcher<TableDefinition> {
+    private Matcher<String> tableNameMatcher = AnythingMatcher.anything();
+
+    private Matcher<String> schemaNameMatcher = AnythingMatcher.anything();
+
+    private Matcher<Boolean> ifNotExistsMatcher = AnythingMatcher.anything();
+
+    private Matcher<List<ColumnDefinition>> columnsMatcher = 
AnythingMatcher.anything();
+
+    private Matcher<IndexType> pkTypeMatcher = AnythingMatcher.anything();
+
+    private Matcher<List<ColumnSorted>> pkColumnsMatcher = 
AnythingMatcher.anything();
+
+    private Matcher<List<String>> colocationColumnsMatcher = 
AnythingMatcher.anything();
+
+    private Matcher<String> zoneNameMatcher = AnythingMatcher.anything();
+
+    private Matcher<Class<?>> keyClassMatcher = AnythingMatcher.anything();
+
+    private Matcher<Class<?>> valueClassMatcher = AnythingMatcher.anything();
+
+    private Matcher<List<IndexDefinition>> indexesMatcher = 
AnythingMatcher.anything();
+
+    public TableDefinitionMatcher withTableNameMatcher(Matcher<String> 
tableNameMatcher) {
+        this.tableNameMatcher = tableNameMatcher;
+        return this;
+    }
+
+    public TableDefinitionMatcher withTableName(String tableName) {
+        return withTableNameMatcher(equalToIgnoringCase(tableName));
+    }
+
+    public TableDefinitionMatcher withSchemaNameMatcher(Matcher<String> 
schemaNameMatcher) {
+        this.schemaNameMatcher = schemaNameMatcher;
+        return this;
+    }
+
+    public TableDefinitionMatcher withSchemaName(String schemaName) {
+        return withSchemaNameMatcher(equalToIgnoringCase(schemaName));
+    }
+
+    public TableDefinitionMatcher withIfNotExistsMatcher(Matcher<Boolean> 
ifNotExists) {
+        this.ifNotExistsMatcher = ifNotExists;
+        return this;
+    }
+
+    public TableDefinitionMatcher withIfNotExists(boolean ifNotExists) {
+        return withIfNotExistsMatcher(equalTo(ifNotExists));
+    }
+
+    public TableDefinitionMatcher 
withColumnsMatcher(Matcher<List<ColumnDefinition>> columns) {
+        this.columnsMatcher = columns;
+        return this;
+    }
+
+    public TableDefinitionMatcher withColumns(List<ColumnDefinition> columns) {
+        return withColumnsMatcher(new ColumnDefinitionsMatcher(columns));
+    }
+
+    public TableDefinitionMatcher withPkTypeMatcher(Matcher<IndexType> pkType) 
{
+        this.pkTypeMatcher = pkType;
+        return this;
+    }
+
+    public TableDefinitionMatcher withPkType(IndexType pkType) {
+        return withPkTypeMatcher(equalTo(pkType));
+    }
+
+    public TableDefinitionMatcher 
withPkColumnsMatcher(Matcher<List<ColumnSorted>> pkColumns) {
+        this.pkColumnsMatcher = pkColumns;
+        return this;
+    }
+
+    public TableDefinitionMatcher withPkColumns(List<ColumnSorted> pkColumns) {
+        return withPkColumnsMatcher(equalTo(pkColumns));
+    }
+
+    public TableDefinitionMatcher 
withColocationColumnsMatcher(Matcher<List<String>> colocationColumns) {
+        this.colocationColumnsMatcher = colocationColumns;
+        return this;
+    }
+
+    public TableDefinitionMatcher withColocationColumns(List<String> 
colocationColumns) {
+        return withColocationColumnsMatcher(equalTo(colocationColumns));
+    }
+
+    public TableDefinitionMatcher withZoneNameMatcher(Matcher<String> 
zoneName) {
+        this.zoneNameMatcher = zoneName;
+        return this;
+    }
+
+    public TableDefinitionMatcher withZoneName(String zoneName) {
+        return withZoneNameMatcher(equalTo(zoneName));
+    }
+
+    public TableDefinitionMatcher withKeyClassMatcher(Matcher<Class<?>> 
keyClass) {
+        this.keyClassMatcher = keyClass;
+        return this;
+    }
+
+    public TableDefinitionMatcher withKeyClass(Class<?> keyClass) {
+        return withKeyClassMatcher(equalTo(keyClass));
+    }
+
+    public TableDefinitionMatcher withValueClassMatcher(Matcher<Class<?>> 
valueClass) {
+        this.valueClassMatcher = valueClass;
+        return this;
+    }
+
+    public TableDefinitionMatcher withValueClass(Class<?> valueClass) {
+        return withValueClassMatcher(equalTo(valueClass));
+    }
+
+    public TableDefinitionMatcher 
withIndexesMatcher(Matcher<List<IndexDefinition>> indexes) {
+        this.indexesMatcher = indexes;
+        return this;
+    }
+
+    public TableDefinitionMatcher withIndexes(List<IndexDefinition> indexes) {
+        return withIndexesMatcher(equalTo(indexes));
+    }
+
+    public static TableDefinitionMatcher isTableDefinition() {
+        return new TableDefinitionMatcher();
+    }
+
+    @Override
+    protected boolean matchesSafely(TableDefinition tableDefinition) {
+        return tableNameMatcher.matches(tableDefinition.tableName())
+                && schemaNameMatcher.matches(tableDefinition.schemaName())
+                && ifNotExistsMatcher.matches(tableDefinition.ifNotExists())
+                && columnsMatcher.matches(tableDefinition.columns())
+                && pkTypeMatcher.matches(tableDefinition.primaryKeyType())
+                && 
pkColumnsMatcher.matches(tableDefinition.primaryKeyColumns())
+                && 
colocationColumnsMatcher.matches(tableDefinition.colocationColumns())
+                && zoneNameMatcher.matches(tableDefinition.zoneName())
+                && keyClassMatcher.matches(tableDefinition.keyClass())
+                && valueClassMatcher.matches(tableDefinition.valueClass())
+                && indexesMatcher.matches(tableDefinition.indexes());
+    }
+
+    @Override
+    public void describeTo(Description description) {
+        description.appendText("Table definition with ")
+                .appendText("table name 
").appendDescriptionOf(tableNameMatcher)
+                .appendText(System.lineSeparator())
+                .appendText("schema name 
").appendDescriptionOf(schemaNameMatcher)
+                .appendText(System.lineSeparator())
+                .appendText("if not exists 
").appendDescriptionOf(ifNotExistsMatcher)
+                .appendText(System.lineSeparator())
+                .appendText("columns ").appendDescriptionOf(columnsMatcher)
+                .appendText(System.lineSeparator())
+                .appendText("pk type ").appendDescriptionOf(pkTypeMatcher)
+                .appendText(System.lineSeparator())
+                .appendText("pk columns 
").appendDescriptionOf(pkColumnsMatcher)
+                .appendText(System.lineSeparator())
+                .appendText("colocation columns 
").appendDescriptionOf(columnsMatcher)
+                .appendText(System.lineSeparator())
+                .appendText("zone name ").appendDescriptionOf(zoneNameMatcher)
+                .appendText(System.lineSeparator())
+                .appendText("key class ").appendDescriptionOf(keyClassMatcher)
+                .appendText(System.lineSeparator())
+                .appendText("value class 
").appendDescriptionOf(valueClassMatcher)
+                .appendText(System.lineSeparator())
+                .appendText("indexes ").appendDescriptionOf(indexesMatcher);
+    }
+}
diff --git 
a/modules/catalog-dsl/src/integrationTest/java/org/apache/ignite/internal/matcher/ZoneDefinitionMatcher.java
 
b/modules/catalog-dsl/src/integrationTest/java/org/apache/ignite/internal/matcher/ZoneDefinitionMatcher.java
new file mode 100644
index 0000000000..68533ab06c
--- /dev/null
+++ 
b/modules/catalog-dsl/src/integrationTest/java/org/apache/ignite/internal/matcher/ZoneDefinitionMatcher.java
@@ -0,0 +1,166 @@
+package org.apache.ignite.internal.matcher;
+
+import static org.hamcrest.Matchers.equalTo;
+
+import org.apache.ignite.catalog.definitions.ZoneDefinition;
+import org.apache.ignite.internal.testframework.matchers.AnythingMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+
+public class ZoneDefinitionMatcher extends TypeSafeMatcher<ZoneDefinition> {
+    private Matcher<String> zoneNameMatcher = AnythingMatcher.anything();
+
+    private Matcher<Boolean> ifNotExistsMatcher = AnythingMatcher.anything();
+
+    private Matcher<Integer> partitionsMatcher = AnythingMatcher.anything();
+
+    private Matcher<Integer> replicasMatcher = AnythingMatcher.anything();
+
+    private Matcher<String> distributionAlgorithmMatcher = 
AnythingMatcher.anything();
+
+    private Matcher<Integer> dataNodesAutoAdjustMatcher = 
AnythingMatcher.anything();
+
+    private Matcher<Integer> dataNodesAutoAdjustScaleUpMatcher = 
AnythingMatcher.anything();
+
+    private Matcher<Integer> dataNodesAutoAdjustScaleDownMatcher = 
AnythingMatcher.anything();
+
+    private Matcher<String> filterMatcher = AnythingMatcher.anything();
+
+    private Matcher<String> storageProfiles = AnythingMatcher.anything();
+
+    public ZoneDefinitionMatcher withZoneNameMatcher(Matcher<String> 
zoneNameMatcher) {
+        this.zoneNameMatcher = zoneNameMatcher;
+        return this;
+    }
+
+    public ZoneDefinitionMatcher withZoneName(String zoneName) {
+        return withZoneNameMatcher(equalTo(zoneName));
+    }
+
+    public ZoneDefinitionMatcher withIfNotExistsMatcher(Matcher<Boolean> 
ifNotExistsMatcher) {
+        this.ifNotExistsMatcher = ifNotExistsMatcher;
+        return this;
+    }
+
+    public ZoneDefinitionMatcher withIfNotExists(boolean ifNotExists) {
+        return withIfNotExistsMatcher(equalTo(ifNotExists));
+    }
+
+    public ZoneDefinitionMatcher withPartitionsMatcher(Matcher<Integer> 
partitionsMatcher) {
+        this.partitionsMatcher = partitionsMatcher;
+        return this;
+    }
+
+    public ZoneDefinitionMatcher withPartitions(int partitions) {
+        return withPartitionsMatcher(equalTo(partitions));
+    }
+
+    public ZoneDefinitionMatcher withReplicasMatcher(Matcher<Integer> 
replicasMatcher) {
+        this.replicasMatcher = replicasMatcher;
+        return this;
+    }
+
+    public ZoneDefinitionMatcher withReplicas(int replicas) {
+        return withReplicasMatcher(equalTo(replicas));
+    }
+
+    public ZoneDefinitionMatcher 
withDistributionAlgorithmMatcher(Matcher<String> 
withDistributionAlgorithmMatcher) {
+        this.distributionAlgorithmMatcher = withDistributionAlgorithmMatcher;
+        return this;
+    }
+
+    public ZoneDefinitionMatcher withDistributionAlgorithm(String 
distributionAlgorithm) {
+        return 
withDistributionAlgorithmMatcher(equalTo(distributionAlgorithm));
+    }
+
+    public ZoneDefinitionMatcher 
withDataNodesAutoAdjustMatcher(Matcher<Integer> dataNodesAutoAdjustMatcher) {
+        this.dataNodesAutoAdjustMatcher = dataNodesAutoAdjustMatcher;
+        return this;
+    }
+
+    public ZoneDefinitionMatcher withDataNodesAutoAdjust(int 
dataNodesAutoAdjust) {
+        return withDataNodesAutoAdjustMatcher(equalTo(dataNodesAutoAdjust));
+    }
+
+    public ZoneDefinitionMatcher withDataNodesAutoAdjustScaleUpMatcher(
+            Matcher<Integer> dataNodesAutoAdjustScaleUpMatcher) {
+        this.dataNodesAutoAdjustScaleUpMatcher = 
dataNodesAutoAdjustScaleUpMatcher;
+        return this;
+    }
+
+    public ZoneDefinitionMatcher withDataNodesAutoAdjustScaleUp(int 
dataNodesAutoAdjustScaleUp) {
+        return 
withDataNodesAutoAdjustScaleUpMatcher(equalTo(dataNodesAutoAdjustScaleUp));
+    }
+
+    public ZoneDefinitionMatcher withDataNodesAutoAdjustScaleDownMatcher(
+            Matcher<Integer> dataNodesAutoAdjustScaleDownMatcher) {
+        this.dataNodesAutoAdjustScaleDownMatcher = 
dataNodesAutoAdjustScaleDownMatcher;
+        return this;
+    }
+
+    public ZoneDefinitionMatcher withDataNodesAutoAdjustScaleDown(int 
dataNodesAutoAdjustScaleDown) {
+        return 
withDataNodesAutoAdjustScaleDownMatcher(equalTo(dataNodesAutoAdjustScaleDown));
+    }
+
+    public ZoneDefinitionMatcher withFilterMatcher(Matcher<String> 
filterMatcher) {
+        this.filterMatcher = filterMatcher;
+        return this;
+    }
+
+    public ZoneDefinitionMatcher withFilter(String filter) {
+        return withFilterMatcher(equalTo(filter));
+    }
+
+    public ZoneDefinitionMatcher withStorageProfilesMatcher(Matcher<String> 
storageProfiles) {
+        this.storageProfiles = storageProfiles;
+        return this;
+    }
+
+    public ZoneDefinitionMatcher withStorageProfiles(String storageProfiles) {
+        return withStorageProfilesMatcher(equalTo(storageProfiles));
+    }
+
+    public static ZoneDefinitionMatcher isZoneDefinition() {
+        return new ZoneDefinitionMatcher();
+    }
+
+    @Override
+    protected boolean matchesSafely(ZoneDefinition zoneDefinition) {
+        return zoneNameMatcher.matches(zoneDefinition.zoneName())
+                && ifNotExistsMatcher.matches(zoneDefinition.ifNotExists())
+                && partitionsMatcher.matches(zoneDefinition.partitions())
+                && replicasMatcher.matches(zoneDefinition.replicas())
+                && 
distributionAlgorithmMatcher.matches(zoneDefinition.distributionAlgorithm())
+                && 
dataNodesAutoAdjustMatcher.matches(zoneDefinition.dataNodesAutoAdjust())
+                && zoneNameMatcher.matches(zoneDefinition.zoneName())
+                && 
dataNodesAutoAdjustScaleUpMatcher.matches(zoneDefinition.dataNodesAutoAdjustScaleUp())
+                && 
dataNodesAutoAdjustScaleDownMatcher.matches(zoneDefinition.dataNodesAutoAdjustScaleDown())
+                && filterMatcher.matches(zoneDefinition.filter())
+                && storageProfiles.matches(zoneDefinition.storageProfiles());
+    }
+
+    @Override
+    public void describeTo(Description description) {
+        description.appendText("Zone definition with ")
+                .appendText("zone name ").appendDescriptionOf(zoneNameMatcher)
+                .appendText(System.lineSeparator())
+                .appendText("if not exists 
").appendDescriptionOf(ifNotExistsMatcher)
+                .appendText(System.lineSeparator())
+                .appendText("partitions 
").appendDescriptionOf(partitionsMatcher)
+                .appendText(System.lineSeparator())
+                .appendText("replicas ").appendDescriptionOf(replicasMatcher)
+                .appendText(System.lineSeparator())
+                .appendText("affinity 
").appendDescriptionOf(distributionAlgorithmMatcher)
+                .appendText(System.lineSeparator())
+                .appendText("data nodes auto adjust 
").appendDescriptionOf(dataNodesAutoAdjustMatcher)
+                .appendText(System.lineSeparator())
+                .appendText("data nodes auto adjust scale up 
").appendDescriptionOf(dataNodesAutoAdjustScaleUpMatcher)
+                .appendText(System.lineSeparator())
+                .appendText("data nodes auto adjust scale down 
").appendDescriptionOf(dataNodesAutoAdjustScaleDownMatcher)
+                .appendText(System.lineSeparator())
+                .appendText("filter ").appendDescriptionOf(filterMatcher)
+                .appendText(System.lineSeparator())
+                .appendText("storage profiles 
").appendDescriptionOf(storageProfiles);
+    }
+}
diff --git 
a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateFromDefinitionImpl.java
 
b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateFromDefinitionImpl.java
index c5dc84dc6a..cf2863f6a8 100644
--- 
a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateFromDefinitionImpl.java
+++ 
b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateFromDefinitionImpl.java
@@ -19,6 +19,7 @@ package org.apache.ignite.internal.catalog.sql;
 
 import static 
org.apache.ignite.internal.catalog.sql.CreateFromAnnotationsImpl.processColumns;
 import static 
org.apache.ignite.internal.catalog.sql.QueryUtils.isGreaterThanZero;
+import static org.apache.ignite.internal.catalog.sql.QueryUtils.isPositive;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -70,20 +71,21 @@ class CreateFromDefinitionImpl extends 
AbstractCatalogQuery<TableZoneId> {
             createZone.distributionAlgorithm(def.distributionAlgorithm());
         }
 
-        if (isGreaterThanZero(def.dataNodesAutoAdjust())) {
+        if (isPositive(def.dataNodesAutoAdjust())) {
             createZone.dataNodesAutoAdjust(def.dataNodesAutoAdjust());
         }
-        if (isGreaterThanZero(def.dataNodesAutoAdjustScaleUp())) {
+        if (isPositive(def.dataNodesAutoAdjustScaleUp())) {
             
createZone.dataNodesAutoAdjustScaleUp(def.dataNodesAutoAdjustScaleUp());
         }
-        if (isGreaterThanZero(def.dataNodesAutoAdjustScaleDown())) {
+        if (isPositive(def.dataNodesAutoAdjustScaleDown())) {
             
createZone.dataNodesAutoAdjustScaleDown(def.dataNodesAutoAdjustScaleDown());
         }
 
         if (!StringUtils.nullOrBlank(def.filter())) {
             createZone.filter(def.filter());
         }
-
+        System.out.println("Create");
+        System.out.println(this);
         return this;
     }
 
@@ -130,6 +132,8 @@ class CreateFromDefinitionImpl extends 
AbstractCatalogQuery<TableZoneId> {
                 createTable.addIndex(toIndexName(ix), ix.type(), ix.columns());
             }
         }
+        System.out.println("Create");
+        System.out.println(this);
         return this;
     }
 
diff --git 
a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateTableImpl.java
 
b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateTableImpl.java
index db92258fc1..d29159e8b6 100644
--- 
a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateTableImpl.java
+++ 
b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateTableImpl.java
@@ -38,7 +38,7 @@ class CreateTableImpl extends AbstractCatalogQuery<Name> {
 
     private final List<Constraint> constraints = new ArrayList<>();
 
-    private final List<WithOption> withOptions = new ArrayList<>();
+    private final List<Option> withOptions = new ArrayList<>();
 
     private Colocate colocate;
 
@@ -119,7 +119,7 @@ class CreateTableImpl extends AbstractCatalogQuery<Name> {
     CreateTableImpl zone(String zone) {
         Objects.requireNonNull(zone, "Zone name must not be null.");
 
-        withOptions.add(WithOption.primaryZone(zone));
+        withOptions.add(Option.primaryZone(zone));
         return this;
     }
 
diff --git 
a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateZoneImpl.java
 
b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateZoneImpl.java
index dc426c4942..1a35774b4b 100644
--- 
a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateZoneImpl.java
+++ 
b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateZoneImpl.java
@@ -29,7 +29,7 @@ class CreateZoneImpl extends AbstractCatalogQuery<Name> {
 
     private boolean ifNotExists;
 
-    private final List<WithOption> withOptions = new ArrayList<>();
+    private final List<Option> withOptions = new ArrayList<>();
 
     /**
      * Constructor for internal usage.
@@ -60,21 +60,21 @@ class CreateZoneImpl extends AbstractCatalogQuery<Name> {
     CreateZoneImpl replicas(Integer n) {
         Objects.requireNonNull(n, "Replicas count must not be null.");
 
-        withOptions.add(WithOption.replicas(n));
+        withOptions.add(Option.replicas(n));
         return this;
     }
 
     CreateZoneImpl partitions(Integer n) {
         Objects.requireNonNull(n, "Partitions must not be null.");
 
-        withOptions.add(WithOption.partitions(n));
+        withOptions.add(Option.partitions(n));
         return this;
     }
 
     CreateZoneImpl distributionAlgorithm(String distributionAlgorithm) {
         Objects.requireNonNull(distributionAlgorithm, "Partition distribution 
algorithm must not be null.");
 
-        
withOptions.add(WithOption.distributionAlgorithm(distributionAlgorithm));
+        withOptions.add(Option.distributionAlgorithm(distributionAlgorithm));
         return this;
     }
 
@@ -84,35 +84,35 @@ class CreateZoneImpl extends AbstractCatalogQuery<Name> {
                 "Timeout between node added or node left topology event itself 
and data nodes switch must not be null."
         );
 
-        withOptions.add(WithOption.dataNodesAutoAdjust(adjust));
+        withOptions.add(Option.dataNodesAutoAdjust(adjust));
         return this;
     }
 
     CreateZoneImpl dataNodesAutoAdjustScaleUp(Integer adjust) {
         Objects.requireNonNull(adjust, "Timeout between node added topology 
event itself and data nodes switch must not be null.");
 
-        withOptions.add(WithOption.dataNodesAutoAdjustScaleUp(adjust));
+        withOptions.add(Option.dataNodesAutoAdjustScaleUp(adjust));
         return this;
     }
 
     CreateZoneImpl dataNodesAutoAdjustScaleDown(Integer adjust) {
         Objects.requireNonNull(adjust, "Timeout between node left topology 
event itself and data nodes switch must not be null.");
 
-        withOptions.add(WithOption.dataNodesAutoAdjustScaleDown(adjust));
+        withOptions.add(Option.dataNodesAutoAdjustScaleDown(adjust));
         return this;
     }
 
     CreateZoneImpl filter(String filter) {
         Objects.requireNonNull(filter, "Filter must not be null.");
 
-        withOptions.add(WithOption.filter(filter));
+        withOptions.add(Option.filter(filter));
         return this;
     }
 
     CreateZoneImpl storageProfiles(String storageProfiles) {
         Objects.requireNonNull(storageProfiles, "Storage profiles must not be 
null");
 
-        withOptions.add(WithOption.storageProfiles(storageProfiles));
+        withOptions.add(Option.storageProfiles(storageProfiles));
         return this;
     }
 
diff --git 
a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/IgniteCatalogSqlImpl.java
 
b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/IgniteCatalogSqlImpl.java
index 1ee2c4a170..b558c8d1b1 100644
--- 
a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/IgniteCatalogSqlImpl.java
+++ 
b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/IgniteCatalogSqlImpl.java
@@ -17,16 +17,32 @@
 
 package org.apache.ignite.internal.catalog.sql;
 
+import static org.apache.ignite.internal.catalog.sql.Option.indexId;
+import static org.apache.ignite.internal.catalog.sql.Option.name;
+import static org.apache.ignite.internal.catalog.sql.Option.tableName;
 import static 
org.apache.ignite.internal.lang.IgniteExceptionMapperUtil.mapToPublicException;
 import static org.apache.ignite.internal.util.ExceptionUtils.unwrapCause;
 
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionException;
+import java.util.stream.Collectors;
+import org.apache.ignite.catalog.ColumnSorted;
+import org.apache.ignite.catalog.ColumnType;
 import org.apache.ignite.catalog.IgniteCatalog;
+import org.apache.ignite.catalog.IndexType;
+import org.apache.ignite.catalog.definitions.ColumnDefinition;
+import org.apache.ignite.catalog.definitions.IndexDefinition;
 import org.apache.ignite.catalog.definitions.TableDefinition;
+import org.apache.ignite.catalog.definitions.TableDefinition.Builder;
 import org.apache.ignite.catalog.definitions.ZoneDefinition;
 import org.apache.ignite.internal.util.ExceptionUtils;
+import org.apache.ignite.lang.DistributionZoneNotFoundException;
+import org.apache.ignite.lang.TableNotFoundException;
 import org.apache.ignite.sql.IgniteSql;
+import org.apache.ignite.sql.SqlRow;
 import org.apache.ignite.table.IgniteTables;
 import org.apache.ignite.table.Table;
 
@@ -82,6 +98,69 @@ public class IgniteCatalogSqlImpl implements IgniteCatalog {
         return join(createTableAsync(definition));
     }
 
+    @Override
+    public CompletableFuture<TableDefinition> tableDefinitionAsync(String 
tableName) {
+        return new SelectFromView<>(sql, "TABLES", name(tableName), row -> {
+            String schema = row.stringValue("SCHEMA");
+            int indexId = row.intValue("PK_INDEX_ID");
+            String zone = row.stringValue("ZONE");
+            Builder builder = 
TableDefinition.builder(tableName).schema(schema).zone(zone).primaryKey();
+            return new TableDefinitionBuilderWithIndexId(builder, indexId);
+        }).executeAsync()
+                .thenApply(definitions -> {
+                    if (definitions.isEmpty()) {
+                        throw new TableNotFoundException(tableName);
+                    }
+
+                    assert definitions.size() == 1;
+
+                    return definitions.get(0);
+                })
+                .thenCompose(tableDefinitionWithPkIndexId ->
+                        new SelectFromView<>(sql, "INDEXES", 
tableName(tableName), row -> {
+                            int pkIndexId = 
tableDefinitionWithPkIndexId.indexId;
+
+                            int indexId = row.intValue("INDEX_ID");
+                            String indexName = row.stringValue("INDEX_NAME");
+                            String columns = row.stringValue("COLUMNS");
+                            String type = row.stringValue("TYPE");
+
+                            return Index.create(indexName, type, columns, 
indexId == pkIndexId);
+                        }).executeAsync().thenApply(indexes -> {
+                            Builder builder = 
tableDefinitionWithPkIndexId.builder;
+                            for (Index index : indexes) {
+                                if (index.isPkIndex) {
+                                    builder.primaryKey(index.type, 
index.columns);
+                                } else {
+                                    builder.index(index.name, index.type, 
index.columns);
+                                }
+                            }
+                            return builder;
+                        }))
+                .thenCompose(tableDefinition -> new SelectFromView<>(sql, 
"TABLES_COLUMNS", tableName(tableName), row -> {
+                    String columnName = row.stringValue("COLUMN_NAME");
+                    String type = row.stringValue("TYPE");
+                    int length = row.intValue("LENGTH");
+                    int precision = row.intValue("PRECISION");
+                    int scale = row.intValue("SCALE");
+                    boolean nullable = row.booleanValue("NULLABLE");
+
+                    Class<?> typeClass = 
org.apache.ignite.sql.ColumnType.valueOf(type).javaClass();
+                    return ColumnDefinition.column(columnName, 
ColumnType.of(typeClass, length, precision, scale, nullable));
+                }).executeAsync().thenApply(tableDefinition::columns))
+                .thenCompose(
+                        tableDefinition -> new SelectFromView<>(sql, 
"TABLES_COLOCATION_COLUMNS", tableName(tableName),
+                                row -> row.stringValue("COLOCATION_COLUMN"))
+                                .executeAsync()
+                                .thenApply(colocatedColumn -> 
tableDefinition.colocateBy(colocatedColumn).build())
+                );
+    }
+
+    @Override
+    public TableDefinition tableDefinition(String tableName) {
+        return join(tableDefinitionAsync(tableName));
+    }
+
     @Override
     public CompletableFuture<Void> createZoneAsync(ZoneDefinition definition) {
         return new CreateFromDefinitionImpl(sql)
@@ -95,6 +174,30 @@ public class IgniteCatalogSqlImpl implements IgniteCatalog {
         join(createZoneAsync(definition));
     }
 
+    @Override
+    public CompletableFuture<ZoneDefinition> zoneDefinitionAsync(String 
zoneName) {
+        return new SelectFromView<>(sql, "ZONES", name(zoneName), row -> 
toZoneDefinition(zoneName, row))
+                .executeAsync()
+                .thenApply(zoneDefinitions -> {
+                    if (zoneDefinitions.isEmpty()) {
+                        throw new DistributionZoneNotFoundException(zoneName);
+                    }
+                    assert zoneDefinitions.size() == 1;
+
+                    return zoneDefinitions.get(0);
+                })
+                .thenCompose(
+                        zoneDefinition -> new SelectFromView<>(sql, 
"ZONE_STORAGE_PROFILES", Option.zoneName(zoneName),
+                                row -> row.stringValue("STORAGE_PROFILE"))
+                                .executeAsync()
+                                .thenApply(profiles -> 
zoneDefinition.toBuilder().storageProfiles(String.join(",", 
profiles)).build()));
+    }
+
+    @Override
+    public ZoneDefinition zoneDefinition(String zoneName) {
+        return join(zoneDefinitionAsync(zoneName));
+    }
+
     @Override
     public CompletableFuture<Void> dropTableAsync(TableDefinition definition) {
         return new DropTableImpl(sql)
@@ -151,6 +254,51 @@ public class IgniteCatalogSqlImpl implements IgniteCatalog 
{
         join(dropZoneAsync(name));
     }
 
+    private static ZoneDefinition toZoneDefinition(String zoneName, SqlRow 
row) {
+        int partitions = row.intValue("PARTITIONS");
+        int replicas = row.intValue("REPLICAS");
+        int dataNodesAutoAdjustScaleUp = 
row.intValue("DATA_NODES_AUTO_ADJUST_SCALE_UP");
+        int dataNodesAutoAdjustScaleDown = 
row.intValue("DATA_NODES_AUTO_ADJUST_SCALE_DOWN");
+        String filter = row.stringValue("DATA_NODES_FILTER");
+
+        return ZoneDefinition.builder(zoneName)
+                .partitions(partitions)
+                .replicas(replicas)
+                .dataNodesAutoAdjustScaleUp(dataNodesAutoAdjustScaleUp)
+                .dataNodesAutoAdjustScaleDown(dataNodesAutoAdjustScaleDown)
+                .filter(filter)
+                .build();
+    }
+
+    private static class Index {
+        private final String name;
+
+        private final IndexType type;
+
+        private final List<ColumnSorted> columns;
+
+        private final boolean isPkIndex;
+
+        private Index(String name, IndexType type, List<ColumnSorted> columns, 
boolean isPkIndex) {
+            this.name = name;
+            this.type = type;
+            this.columns = columns;
+            this.isPkIndex = isPkIndex;
+        }
+
+        public static Index create(String name, String type, String columns, 
boolean isPkIndex) {
+            return new Index(name, IndexType.valueOf(type), fromRaw(columns), 
isPkIndex);
+        }
+    }
+
+    private static List<ColumnSorted> fromRaw(String columns) {
+        return Arrays.stream(columns.split(","))
+                .map(String::trim)
+                .filter(s -> !s.isEmpty())
+                .map(ColumnSorted::column)
+                .collect(Collectors.toList());
+    }
+
     private static <R> R join(CompletableFuture<R> future) {
         try {
             return future.join();
@@ -158,4 +306,15 @@ public class IgniteCatalogSqlImpl implements IgniteCatalog 
{
             throw 
ExceptionUtils.sneakyThrow(mapToPublicException(unwrapCause(e)));
         }
     }
+
+    private static class TableDefinitionBuilderWithIndexId {
+        private final TableDefinition.Builder builder;
+
+        private final int indexId;
+
+        private TableDefinitionBuilderWithIndexId(TableDefinition.Builder 
builder, int indexId) {
+            this.builder = builder;
+            this.indexId = indexId;
+        }
+    }
 }
diff --git 
a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/Option.java
 
b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/Option.java
new file mode 100644
index 0000000000..4e224d6faf
--- /dev/null
+++ 
b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/Option.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.catalog.sql;
+
+class Option extends QueryPart {
+    private final String name;
+
+    private final Object value;
+
+    private Option(String name, Object value) {
+        this.name = name;
+        this.value = value;
+    }
+
+    public static Option primaryZone(String zone) {
+        return new Option("PRIMARY_ZONE", zone.toUpperCase());
+    }
+
+    public static Option partitions(Integer partitions) {
+        return new Option("PARTITIONS", partitions);
+    }
+
+    public static Option replicas(Integer replicas) {
+        return new Option("REPLICAS", replicas);
+    }
+
+    public static Option dataNodesAutoAdjust(Integer adjust) {
+        return new Option("DATA_NODES_AUTO_ADJUST", adjust);
+    }
+
+    public static Option dataNodesAutoAdjustScaleUp(Integer adjust) {
+        return new Option("DATA_NODES_AUTO_ADJUST_SCALE_UP", adjust);
+    }
+
+    public static Option dataNodesAutoAdjustScaleDown(Integer adjust) {
+        return new Option("DATA_NODES_AUTO_ADJUST_SCALE_DOWN", adjust);
+    }
+
+    public static Option distributionAlgorithm(String distributionAlgorithm) {
+        return new Option("DISTRIBUTION_ALGORITHM", distributionAlgorithm);
+    }
+
+    public static Option dataRegion(String dataRegion) {
+        return new Option("DATAREGION", dataRegion);
+    }
+
+    public static Option storageProfiles(String storageProfiles) {
+        return new Option("STORAGE_PROFILES", storageProfiles);
+    }
+
+    public static Option name(String name) {
+        return new Option("NAME", name);
+    }
+
+    public static Option tableName(String tableName) {
+        return new Option("TABLE_NAME", tableName);
+    }
+
+    public static Option zoneName(String zoneName) {
+        return new Option("ZONE_NAME", zoneName);
+    }
+
+    public static Option indexId(int indexId) {
+        return new Option("INDEX_ID", indexId);
+    }
+
+    public static Option filter(String filter) {
+        return new Option("DATA_NODES_FILTER", filter);
+    }
+
+    @Override
+    protected void accept(QueryContext ctx) {
+        ctx.sql(name).sql("=");
+        boolean isNeedsQuotes = value instanceof String;
+        if (isNeedsQuotes) {
+            ctx.sql("'").sql(value.toString()).sql("'");
+        } else {
+            ctx.sql(value.toString());
+        }
+    }
+}
diff --git 
a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/QueryUtils.java
 
b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/QueryUtils.java
index f6d7de9deb..f2555fef9f 100644
--- 
a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/QueryUtils.java
+++ 
b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/QueryUtils.java
@@ -53,6 +53,10 @@ class QueryUtils {
         return n != null && n > 0;
     }
 
+    static boolean isPositive(Integer n) {
+        return n != null && n >= 0;
+    }
+
     /**
      * Converts comma-separated string to a list of strings, leading and 
trailing spaces are trimmed.
      *
diff --git 
a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/SelectFromView.java
 
b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/SelectFromView.java
new file mode 100644
index 0000000000..65313a7a7d
--- /dev/null
+++ 
b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/SelectFromView.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.catalog.sql;
+
+import static 
org.apache.ignite.internal.util.CompletableFutures.nullCompletedFuture;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Function;
+import org.apache.ignite.sql.IgniteSql;
+import org.apache.ignite.sql.SqlRow;
+import org.apache.ignite.sql.async.AsyncResultSet;
+
+class SelectFromView<T> extends AbstractCatalogQuery<List<T>> {
+    private final String viewName;
+
+    private final List<Option> whereOptions = new ArrayList<>();
+
+    private final Function<SqlRow, T> mapper;
+
+    SelectFromView(IgniteSql sql, String viewName, Option whereOption, 
Function<SqlRow, T> mapper) {
+        this(sql, viewName, List.of(whereOption), mapper);
+    }
+
+    SelectFromView(IgniteSql sql, String viewName, List<Option> whereOptions, 
Function<SqlRow, T> mapper) {
+        super(sql);
+        this.viewName = viewName;
+        this.whereOptions.addAll(whereOptions);
+        this.mapper = mapper;
+    }
+
+    @Override
+    public CompletableFuture<List<T>> executeAsync() {
+        return sql.executeAsync(null, toString()).thenCompose(resultSet -> {
+            List<T> result = new ArrayList<>();
+            return iterate(resultSet, result).thenApply(unused -> result);
+        });
+    }
+
+    private CompletableFuture<Void> iterate(AsyncResultSet<SqlRow> resultSet, 
List<T> result) {
+        for (SqlRow row : resultSet.currentPage()) {
+            result.add(mapper.apply(row));
+        }
+        if (resultSet.hasMorePages()) {
+            return resultSet.fetchNextPage().thenCompose(nextPage -> 
iterate(nextPage, result));
+        } else {
+            return nullCompletedFuture();
+        }
+    }
+
+    @Override
+    // Noop
+    protected List<T> result() {
+        return Collections.emptyList();
+    }
+
+    @Override
+    protected void accept(QueryContext ctx) {
+        ctx.sql("SELECT * FROM SYSTEM." + viewName + " ");
+
+        if (!whereOptions.isEmpty()) {
+            ctx.sql("WHERE ");
+            for (Option option : whereOptions) {
+                option.accept(ctx);
+            }
+        }
+
+        System.out.println("SELECT FROM VIEW");
+        System.out.println(ctx.getSql());
+    }
+}
diff --git 
a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/WithOption.java
 
b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/WithOption.java
deleted file mode 100644
index 80397c6994..0000000000
--- 
a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/WithOption.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.ignite.internal.catalog.sql;
-
-class WithOption extends QueryPart {
-    private final String name;
-
-    private final Object value;
-
-    private WithOption(String name, Object value) {
-        this.name = name;
-        this.value = value;
-    }
-
-    public static WithOption primaryZone(String zone) {
-        return new WithOption("PRIMARY_ZONE", zone.toUpperCase());
-    }
-
-    public static WithOption partitions(Integer partitions) {
-        return new WithOption("PARTITIONS", partitions);
-    }
-
-    public static WithOption replicas(Integer replicas) {
-        return new WithOption("REPLICAS", replicas);
-    }
-
-    public static WithOption distributionAlgorithm(String 
distributionAlgorithm) {
-        return new WithOption("DISTRIBUTION_ALGORITHM", distributionAlgorithm);
-    }
-
-    public static WithOption dataNodesAutoAdjust(Integer adjust) {
-        return new WithOption("DATA_NODES_AUTO_ADJUST", adjust);
-    }
-
-    public static WithOption dataNodesAutoAdjustScaleUp(Integer adjust) {
-        return new WithOption("DATA_NODES_AUTO_ADJUST_SCALE_UP", adjust);
-    }
-
-    public static WithOption dataNodesAutoAdjustScaleDown(Integer adjust) {
-        return new WithOption("DATA_NODES_AUTO_ADJUST_SCALE_DOWN", adjust);
-    }
-
-    public static WithOption filter(String filter) {
-        return new WithOption("DATA_NODES_FILTER", filter);
-    }
-
-    public static WithOption dataRegion(String dataRegion) {
-        return new WithOption("DATAREGION", dataRegion);
-    }
-
-    public static WithOption storageProfiles(String storageProfiles) {
-        return new WithOption("STORAGE_PROFILES", storageProfiles);
-    }
-
-    @Override
-    protected void accept(QueryContext ctx) {
-        ctx.sql(name).sql("=");
-        boolean isNeedsQuotes = value instanceof String;
-        if (isNeedsQuotes) {
-            ctx.sql("'").sql(value.toString()).sql("'");
-        } else {
-            ctx.sql(value.toString());
-        }
-    }
-}
diff --git 
a/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/QueryPartTest.java
 
b/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/QueryPartTest.java
index 46e772ad6a..5b0f906108 100644
--- 
a/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/QueryPartTest.java
+++ 
b/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/QueryPartTest.java
@@ -138,13 +138,13 @@ class QueryPartTest {
 
     @Test
     void withOptionPart() {
-        WithOption withOption = WithOption.primaryZone("z");
+        Option withOption = Option.primaryZone("z");
         assertThat(sql(withOption), is("PRIMARY_ZONE='Z'"));
 
-        withOption = WithOption.partitions(1);
+        withOption = Option.partitions(1);
         assertThat(sql(withOption), is("PARTITIONS=1"));
 
-        withOption = WithOption.replicas(1);
+        withOption = Option.replicas(1);
         assertThat(sql(withOption), is("REPLICAS=1"));
     }
 
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogSystemViewRegistry.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogSystemViewRegistry.java
index a9b27f1ff4..fb6b8b2e6d 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogSystemViewRegistry.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogSystemViewRegistry.java
@@ -22,6 +22,7 @@ import java.util.function.Supplier;
 import java.util.stream.Collectors;
 import org.apache.ignite.internal.catalog.systemviews.IndexSystemViewProvider;
 import org.apache.ignite.internal.catalog.systemviews.SystemViewViewProvider;
+import org.apache.ignite.internal.catalog.systemviews.TablesSystemViewProvider;
 import org.apache.ignite.internal.catalog.systemviews.ZonesSystemViewProvider;
 import org.apache.ignite.internal.systemview.api.SystemView;
 import org.apache.ignite.internal.systemview.api.SystemViewProvider;
@@ -40,7 +41,8 @@ public class CatalogSystemViewRegistry implements 
SystemViewProvider {
         providers = List.of(
                 new SystemViewViewProvider(),
                 new IndexSystemViewProvider(),
-                new ZonesSystemViewProvider()
+                new ZonesSystemViewProvider(),
+                new TablesSystemViewProvider()
         );
     }
 
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/systemviews/TablesSystemViewProvider.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/systemviews/TablesSystemViewProvider.java
new file mode 100644
index 0000000000..db7054a048
--- /dev/null
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/systemviews/TablesSystemViewProvider.java
@@ -0,0 +1,172 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.catalog.systemviews;
+
+import static org.apache.ignite.internal.type.NativeTypes.BOOLEAN;
+import static org.apache.ignite.internal.type.NativeTypes.INT32;
+import static org.apache.ignite.internal.type.NativeTypes.STRING;
+import static org.apache.ignite.internal.type.NativeTypes.stringOf;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Flow.Publisher;
+import java.util.function.Supplier;
+import org.apache.ignite.internal.catalog.Catalog;
+import org.apache.ignite.internal.catalog.CatalogSystemViewProvider;
+import 
org.apache.ignite.internal.catalog.descriptors.CatalogTableColumnDescriptor;
+import org.apache.ignite.internal.systemview.api.SystemView;
+import org.apache.ignite.internal.systemview.api.SystemViews;
+import org.apache.ignite.internal.util.SubscriptionUtils;
+
+/**
+ * Exposes information on tables.
+ *
+ * <ul>
+ *     <li>TABLES - available tables</li>
+ *     <li>TABLES_COLUMNS - columns of available tables.</li>
+ *     <li>TABLES_COLOCATION_COLUMNS - colocated columns of available 
tables.</li>
+ * </ul>
+ */
+public class TablesSystemViewProvider implements CatalogSystemViewProvider {
+    private static final int SYSTEM_VIEW_STRING_COLUMN_LENGTH = 
Short.MAX_VALUE;
+
+    @Override
+    public List<SystemView<?>> getView(Supplier<Catalog> catalogSupplier) {
+        return List.of(
+                getSystemViewView(catalogSupplier),
+                getSystemViewColocationColumnsView(catalogSupplier),
+                getSystemViewColumnsView(catalogSupplier)
+        );
+    }
+
+    private static SystemView<?> getSystemViewView(Supplier<Catalog> 
catalogSupplier) {
+        Iterable<Info> tablesData = () -> {
+            Catalog catalog = catalogSupplier.get();
+
+            return catalog.tables().stream().map(table -> {
+                String tableName = table.name();
+                String schemaName = 
Objects.requireNonNull(catalog.schema(table.schemaId()), "Schema must be not 
null.").name();
+                String zoneName = 
Objects.requireNonNull(catalog.zone(table.zoneId()), "Zone must be not 
null.").name();
+
+                int indexId = table.primaryKeyIndexId();
+
+                return new Info(tableName, schemaName, indexId, zoneName);
+            }).iterator();
+        };
+
+        Publisher<Info> viewDataPublisher = 
SubscriptionUtils.fromIterable(tablesData);
+
+        return SystemViews.<Info>clusterViewBuilder()
+                .name("TABLES")
+                .addColumn("SCHEMA", STRING, entry -> entry.schema)
+                .addColumn("NAME", STRING, entry -> entry.name)
+                .addColumn("PK_INDEX_ID", INT32, entry -> entry.pkIndexId)
+                .addColumn("ZONE", STRING, entry -> entry.zone)
+                .dataProvider(viewDataPublisher)
+                .build();
+    }
+
+    private static SystemView<?> getSystemViewColumnsView(Supplier<Catalog> 
catalogSupplier) {
+        Iterable<ColumnWithTableId> viewData = () -> {
+            Catalog catalog = catalogSupplier.get();
+
+            return catalog.tables().stream()
+                    .flatMap(table -> table.columns().stream()
+                            .map(columnDescriptor -> new ColumnWithTableId(
+                                    catalog.schema(table.schemaId()).name(),
+                                    table.name(),
+                                    columnDescriptor
+                                    )
+                            )
+                    )
+                    .iterator();
+        };
+
+        Publisher<ColumnWithTableId> viewDataPublisher = 
SubscriptionUtils.fromIterable(viewData);
+
+        return SystemViews.<ColumnWithTableId>clusterViewBuilder()
+                .name("TABLES_COLUMNS")
+                .addColumn("SCHEMA", STRING, entry -> entry.schema)
+                .addColumn("TABLE_NAME", STRING, entry -> entry.tableName)
+                .addColumn("COLUMN_NAME", STRING, entry -> 
entry.descriptor.name())
+                .addColumn("TYPE", STRING, entry -> 
entry.descriptor.type().name())
+                .addColumn("NULLABLE", BOOLEAN, entry -> 
entry.descriptor.nullable())
+                .addColumn("PRECISION", INT32, entry -> 
entry.descriptor.precision())
+                .addColumn("SCALE", INT32, entry -> entry.descriptor.scale())
+                .addColumn("LENGTH", INT32, entry -> entry.descriptor.length())
+                .dataProvider(viewDataPublisher)
+                .build();
+    }
+
+    private static SystemView<?> 
getSystemViewColocationColumnsView(Supplier<Catalog> catalogSupplier) {
+        Iterable<ColocationColumnsWithTable> viewData = () -> {
+            Catalog catalog = catalogSupplier.get();
+
+            return catalog.tables().stream()
+                    .flatMap(table -> table.colocationColumns().stream()
+                            .map(colocationColumn -> new 
ColocationColumnsWithTable(table.name(), colocationColumn))
+                    )
+                    .iterator();
+        };
+
+        Publisher<ColocationColumnsWithTable> viewDataPublisher = 
SubscriptionUtils.fromIterable(viewData);
+
+        return SystemViews.<ColocationColumnsWithTable>clusterViewBuilder()
+                .name("TABLES_COLOCATION_COLUMNS")
+                .addColumn("TABLE_NAME", STRING, entry -> entry.tableName)
+                .addColumn("COLOCATION_COLUMN", 
stringOf(SYSTEM_VIEW_STRING_COLUMN_LENGTH), entry -> entry.colocationColumn)
+                .dataProvider(viewDataPublisher)
+                .build();
+    }
+
+    private static class ColumnWithTableId {
+        private final CatalogTableColumnDescriptor descriptor;
+        private final String tableName;
+        private final String schema;
+
+        private ColumnWithTableId(String schema, String tableName, 
CatalogTableColumnDescriptor descriptor) {
+            this.schema = schema;
+            this.tableName = tableName;
+            this.descriptor = descriptor;
+        }
+    }
+
+    private static class ColocationColumnsWithTable {
+        private final String tableName;
+        private final String colocationColumn;
+
+        private ColocationColumnsWithTable(String tableName, String 
colocationColumn) {
+            this.tableName = tableName;
+            this.colocationColumn = colocationColumn;
+        }
+    }
+
+    private static class Info {
+        private final String name;
+        private final String schema;
+        private final int pkIndexId;
+        private final String zone;
+
+        private Info(String name, String schema, int pkIndexId, String zone) {
+            this.name = name;
+            this.schema = schema;
+            this.pkIndexId = pkIndexId;
+            this.zone = zone;
+        }
+    }
+}
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/systemviews/ZonesSystemViewProvider.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/systemviews/ZonesSystemViewProvider.java
index 13be1e872e..c5579f2f47 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/systemviews/ZonesSystemViewProvider.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/systemviews/ZonesSystemViewProvider.java
@@ -22,9 +22,11 @@ import static 
org.apache.ignite.internal.type.NativeTypes.INT32;
 import static org.apache.ignite.internal.type.NativeTypes.STRING;
 
 import java.util.List;
+import java.util.Objects;
 import java.util.function.Supplier;
 import org.apache.ignite.internal.catalog.Catalog;
 import org.apache.ignite.internal.catalog.CatalogSystemViewProvider;
+import 
org.apache.ignite.internal.catalog.descriptors.CatalogStorageProfileDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.CatalogZoneDescriptor;
 import org.apache.ignite.internal.systemview.api.SystemView;
 import org.apache.ignite.internal.systemview.api.SystemViews;
@@ -43,7 +45,11 @@ public final class ZonesSystemViewProvider implements 
CatalogSystemViewProvider
     /** {@inheritDoc} */
     @Override
     public List<SystemView<?>> getView(Supplier<Catalog> catalogSupplier) {
-        SystemView<?> zoneSystemView = 
SystemViews.<ZoneWithDefaultMarker>clusterViewBuilder()
+        return List.of(getZoneView(catalogSupplier), 
getStorageProfilesView(catalogSupplier));
+    }
+
+    private static SystemView<?> getZoneView(Supplier<Catalog> 
catalogSupplier) {
+        return SystemViews.<ZoneWithDefaultMarker>clusterViewBuilder()
                 .name("ZONES")
                 .addColumn("NAME", STRING, wrapper -> wrapper.zone.name())
                 .addColumn("PARTITIONS", INT32, wrapper -> 
wrapper.zone.partitions())
@@ -60,8 +66,34 @@ public final class ZonesSystemViewProvider implements 
CatalogSystemViewProvider
                         }
                 ))
                 .build();
+    }
+
+    private static SystemView<?> getStorageProfilesView(Supplier<Catalog> 
catalogSupplier) {
+        Iterable<ZoneWithProfile> viewData = () -> {
+            Catalog catalog = catalogSupplier.get();
+
+            return catalog.zones().stream()
+                    .flatMap(zone -> {
+                        List<CatalogStorageProfileDescriptor> profiles = 
zone.storageProfiles().profiles();
+                        CatalogStorageProfileDescriptor defaultProfile = 
zone.storageProfiles().defaultProfile();
+
+                        return profiles.stream().map(profile ->
+                                new ZoneWithProfile(
+                                        zone.name(),
+                                        profile.storageProfile(),
+                                        
Objects.equals(profile.storageProfile(), defaultProfile.storageProfile())
+                                )
+                        );
+                    }).iterator();
+        };
 
-        return List.of(zoneSystemView);
+        return SystemViews.<ZoneWithProfile>clusterViewBuilder()
+                .name("ZONE_STORAGE_PROFILES")
+                .addColumn("ZONE_NAME", STRING, zone -> zone.zoneName)
+                .addColumn("STORAGE_PROFILE", STRING, zone -> zone.profileName)
+                .addColumn("IS_DEFAULT_PROFILE", BOOLEAN, zone -> 
zone.isDefaultProfile)
+                .dataProvider(SubscriptionUtils.fromIterable(viewData))
+                .build();
     }
 
     /** Wraps a CatalogZoneDescriptor and a flag indicating whether this zone 
is the default zone. */
@@ -74,4 +106,19 @@ public final class ZonesSystemViewProvider implements 
CatalogSystemViewProvider
             this.isDefault = isDefault;
         }
     }
+
+    /**
+     * Wraps a zone and a one of storage profile of the zone.
+     */
+    private static class ZoneWithProfile {
+        private final String zoneName;
+        private final String profileName;
+        private final boolean isDefaultProfile;
+
+        private ZoneWithProfile(String zoneName, String profileName, boolean 
defaultProfile) {
+            this.zoneName = zoneName;
+            this.profileName = profileName;
+            this.isDefaultProfile = defaultProfile;
+        }
+    }
 }
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/Main.java 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/Main.java
index 1ce5ce1845..92672382c3 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/Main.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/Main.java
@@ -60,7 +60,7 @@ public class Main {
             AnsiConsole.systemInstall();
             initReplExecutor(micronautFactory);
             initQuestionAsker(micronautFactory);
-            if (args.length != 0 || !isatty()) { // do not enter REPL if input 
or output is redirected
+            if (args.length != 0 /*|| !isatty()*/) { // do not enter REPL if 
input or output is redirected
                 try {
                     exitCode = executeCommand(args, micronautFactory);
                 } catch (Exception e) {
diff --git 
a/modules/client/src/main/java/org/apache/ignite/internal/client/TcpIgniteClient.java
 
b/modules/client/src/main/java/org/apache/ignite/internal/client/TcpIgniteClient.java
index f43fcf4d16..5b3abbaa89 100644
--- 
a/modules/client/src/main/java/org/apache/ignite/internal/client/TcpIgniteClient.java
+++ 
b/modules/client/src/main/java/org/apache/ignite/internal/client/TcpIgniteClient.java
@@ -209,7 +209,7 @@ public class TcpIgniteClient implements IgniteClient {
 
     @Override
     public IgniteCatalog catalog() {
-        return new IgniteCatalogSqlImpl(sql(), tables);
+        return new IgniteCatalogSqlImpl(sql, tables);
     }
 
     /** {@inheritDoc} */
diff --git 
a/modules/distribution-zones/src/main/java/org/apache/ignite/internal/distributionzones/DistributionZoneManager.java
 
b/modules/distribution-zones/src/main/java/org/apache/ignite/internal/distributionzones/DistributionZoneManager.java
index 92560a01b7..00d9793164 100644
--- 
a/modules/distribution-zones/src/main/java/org/apache/ignite/internal/distributionzones/DistributionZoneManager.java
+++ 
b/modules/distribution-zones/src/main/java/org/apache/ignite/internal/distributionzones/DistributionZoneManager.java
@@ -99,7 +99,7 @@ import 
org.apache.ignite.internal.cluster.management.topology.api.LogicalTopolog
 import 
org.apache.ignite.internal.cluster.management.topology.api.LogicalTopologyService;
 import 
org.apache.ignite.internal.cluster.management.topology.api.LogicalTopologySnapshot;
 import 
org.apache.ignite.internal.distributionzones.causalitydatanodes.CausalityDataNodesEngine;
-import 
org.apache.ignite.internal.distributionzones.exception.DistributionZoneNotFoundException;
+import org.apache.ignite.lang.DistributionZoneNotFoundException;
 import 
org.apache.ignite.internal.distributionzones.rebalance.DistributionZoneRebalanceEngine;
 import 
org.apache.ignite.internal.distributionzones.utils.CatalogAlterZoneEventListener;
 import org.apache.ignite.internal.lang.ByteArray;
diff --git 
a/modules/distribution-zones/src/main/java/org/apache/ignite/internal/distributionzones/causalitydatanodes/CausalityDataNodesEngine.java
 
b/modules/distribution-zones/src/main/java/org/apache/ignite/internal/distributionzones/causalitydatanodes/CausalityDataNodesEngine.java
index 838224f922..c61f6aa2ff 100644
--- 
a/modules/distribution-zones/src/main/java/org/apache/ignite/internal/distributionzones/causalitydatanodes/CausalityDataNodesEngine.java
+++ 
b/modules/distribution-zones/src/main/java/org/apache/ignite/internal/distributionzones/causalitydatanodes/CausalityDataNodesEngine.java
@@ -52,7 +52,7 @@ import 
org.apache.ignite.internal.distributionzones.DistributionZoneManager.Zone
 import org.apache.ignite.internal.distributionzones.DistributionZonesUtil;
 import org.apache.ignite.internal.distributionzones.Node;
 import org.apache.ignite.internal.distributionzones.NodeWithAttributes;
-import 
org.apache.ignite.internal.distributionzones.exception.DistributionZoneNotFoundException;
+import org.apache.ignite.lang.DistributionZoneNotFoundException;
 import org.apache.ignite.internal.lang.ByteArray;
 import org.apache.ignite.internal.metastorage.Entry;
 import org.apache.ignite.internal.metastorage.MetaStorageManager;
diff --git 
a/modules/distribution-zones/src/test/java/org/apache/ignite/internal/distributionzones/causalitydatanodes/DistributionZoneCausalityDataNodesTest.java
 
b/modules/distribution-zones/src/test/java/org/apache/ignite/internal/distributionzones/causalitydatanodes/DistributionZoneCausalityDataNodesTest.java
index 63c0c5b882..71309e15e2 100644
--- 
a/modules/distribution-zones/src/test/java/org/apache/ignite/internal/distributionzones/causalitydatanodes/DistributionZoneCausalityDataNodesTest.java
+++ 
b/modules/distribution-zones/src/test/java/org/apache/ignite/internal/distributionzones/causalitydatanodes/DistributionZoneCausalityDataNodesTest.java
@@ -73,7 +73,7 @@ import 
org.apache.ignite.internal.distributionzones.DistributionZoneManager;
 import org.apache.ignite.internal.distributionzones.DistributionZonesUtil;
 import org.apache.ignite.internal.distributionzones.Node;
 import org.apache.ignite.internal.distributionzones.NodeWithAttributes;
-import 
org.apache.ignite.internal.distributionzones.exception.DistributionZoneNotFoundException;
+import org.apache.ignite.lang.DistributionZoneNotFoundException;
 import 
org.apache.ignite.internal.distributionzones.utils.CatalogAlterZoneEventListener;
 import org.apache.ignite.internal.lang.IgniteBiTuple;
 import org.apache.ignite.internal.manager.ComponentContext;
diff --git 
a/modules/runner/src/main/java/org/apache/ignite/internal/threading/PublicApiThreadingIgniteCatalog.java
 
b/modules/runner/src/main/java/org/apache/ignite/internal/threading/PublicApiThreadingIgniteCatalog.java
index 0316465336..db1c0e8de6 100644
--- 
a/modules/runner/src/main/java/org/apache/ignite/internal/threading/PublicApiThreadingIgniteCatalog.java
+++ 
b/modules/runner/src/main/java/org/apache/ignite/internal/threading/PublicApiThreadingIgniteCatalog.java
@@ -78,6 +78,16 @@ public class PublicApiThreadingIgniteCatalog implements 
IgniteCatalog, Wrapper {
         return wrapTableForPublicUse(table);
     }
 
+    @Override
+    public CompletableFuture<TableDefinition> tableDefinitionAsync(String 
tableName) {
+        return doAsyncOperation(() -> catalog.tableDefinitionAsync(tableName));
+    }
+
+    @Override
+    public TableDefinition tableDefinition(String tableName) {
+        return execUserSyncOperation(() -> catalog.tableDefinition(tableName));
+    }
+
     @Override
     public CompletableFuture<Void> createZoneAsync(ZoneDefinition definition) {
         return doAsyncOperation(() -> catalog.createZoneAsync(definition));
@@ -88,6 +98,17 @@ public class PublicApiThreadingIgniteCatalog implements 
IgniteCatalog, Wrapper {
         execUserSyncOperation(() -> catalog.createZone(definition));
     }
 
+
+    @Override
+    public CompletableFuture<ZoneDefinition> zoneDefinitionAsync(String 
zoneName) {
+        return execUserSyncOperation(() -> 
catalog.zoneDefinitionAsync(zoneName));
+    }
+
+    @Override
+    public ZoneDefinition zoneDefinition(String zoneName) {
+        return execUserSyncOperation(() -> catalog.zoneDefinition(zoneName));
+    }
+
     @Override
     public CompletableFuture<Void> dropTableAsync(TableDefinition definition) {
         return doAsyncOperation(() -> catalog.dropTableAsync(definition));
diff --git 
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/disaster/DisasterRecoveryManager.java
 
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/disaster/DisasterRecoveryManager.java
index 83899f4e40..c25ae87e82 100644
--- 
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/disaster/DisasterRecoveryManager.java
+++ 
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/disaster/DisasterRecoveryManager.java
@@ -59,7 +59,7 @@ import 
org.apache.ignite.internal.catalog.events.CreateTableEventParameters;
 import org.apache.ignite.internal.catalog.events.DropTableEventParameters;
 import org.apache.ignite.internal.distributionzones.DistributionZoneManager;
 import org.apache.ignite.internal.distributionzones.NodeWithAttributes;
-import 
org.apache.ignite.internal.distributionzones.exception.DistributionZoneNotFoundException;
+import org.apache.ignite.lang.DistributionZoneNotFoundException;
 import org.apache.ignite.internal.lang.ByteArray;
 import org.apache.ignite.internal.lang.NodeStoppingException;
 import org.apache.ignite.internal.logger.IgniteLogger;


Reply via email to