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

jooger 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 85053746c2 IGNITE-23188: Incorporate table size estimation in query 
optimizer. (#4434)
85053746c2 is described below

commit 85053746c26b5538c2c4761999d5a85e6f776c22
Author: ygerzhedovich <[email protected]>
AuthorDate: Thu Oct 10 16:15:56 2024 +0300

    IGNITE-23188: Incorporate table size estimation in query optimizer. (#4434)
---
 .../internal/catalog/commands/CatalogUtils.java    |   2 +-
 ...AlterTableAlterColumnCommandValidationTest.java |   8 +-
 .../commands/CreateTableCommandValidationTest.java |   2 +-
 .../storage/CatalogEntrySerializationTest.java     |   2 +-
 .../ignite/internal/type/NativeTypeSpecTest.java   |   2 +-
 .../runner/app/ItIgniteNodeRestartTest.java        |   1 +
 .../org/apache/ignite/internal/app/IgniteImpl.java |   1 +
 modules/sql-engine/build.gradle                    |   1 +
 .../internal/sql/engine/ItCreateTableDdlTest.java  |   4 +-
 .../sql/engine/ItDynamicParameterTest.java         |   2 +-
 .../sql/engine/statistic/ItStatisticTest.java      |  92 +++++++
 .../internal/sql/engine/SqlQueryProcessor.java     |  15 +-
 .../sql/engine/exec/exp/agg/Accumulators.java      |   2 +-
 .../sql/engine/prepare/PlanningContext.java        |  49 +++-
 .../prepare/ddl/DdlSqlToCommandConverter.java      |   2 +-
 .../sql/engine/schema/IgniteStatistic.java         |  24 +-
 .../sql/engine/schema/SqlSchemaManagerImpl.java    |  18 +-
 .../sql/engine/statistic/SqlStatisticManager.java  |  36 +++
 .../engine/statistic/SqlStatisticManagerImpl.java  | 230 ++++++++++++++++++
 .../engine/exec/exp/ExpressionFactoryImplTest.java |   2 +-
 .../sql/engine/exec/row/SqlRowHandlerTest.java     |   2 +-
 .../sql/engine/framework/TestBuilders.java         |   6 +-
 .../engine/planner/JoinWithUsingPlannerTest.java   |   2 +-
 .../prepare/ddl/DdlSqlToCommandConverterTest.java  |  10 +-
 .../pruning/PartitionPruningPredicateSelfTest.java |   2 +-
 .../engine/schema/SqlSchemaManagerImplTest.java    |   6 +-
 .../statistic/SqlStatisticManagerImplTest.java     | 265 +++++++++++++++++++++
 .../internal/sql/engine/util/QueryChecker.java     |  13 +
 .../ignite/internal/systemview/api/SystemView.java |   2 +-
 .../internal/table/MutableRowTupleAdapterTest.java |   2 +-
 30 files changed, 752 insertions(+), 53 deletions(-)

diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CatalogUtils.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CatalogUtils.java
index f13a78f656..d398425439 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CatalogUtils.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CatalogUtils.java
@@ -598,7 +598,7 @@ public class CatalogUtils {
     /**
      * Check if provided column type can be persisted, or fail otherwise.
      */
-    // TODO: https://issues.apache.org/jira/browse/IGNITE-15200
+    // TODO: https://issues.apache.org/jira/browse/IGNITE-17373
     //  Remove this after interval type support is added.
     static void ensureTypeCanBeStored(String columnName, ColumnType 
columnType) {
         if (columnType == ColumnType.PERIOD || columnType == 
ColumnType.DURATION) {
diff --git 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/AlterTableAlterColumnCommandValidationTest.java
 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/AlterTableAlterColumnCommandValidationTest.java
index ea22cdebff..622307a616 100644
--- 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/AlterTableAlterColumnCommandValidationTest.java
+++ 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/AlterTableAlterColumnCommandValidationTest.java
@@ -309,7 +309,7 @@ public class AlterTableAlterColumnCommandValidationTest 
extends AbstractCommandV
         );
     }
 
-    // TODO: https://issues.apache.org/jira/browse/IGNITE-15200
+    // TODO: https://issues.apache.org/jira/browse/IGNITE-17373
     //  Include DURATION and PERIOD types after these types are supported.
     @ParameterizedTest
     @EnumSource(mode = Mode.EXCLUDE, value = ColumnType.class, names = 
{"DECIMAL", "NULL", "DURATION", "PERIOD"})
@@ -426,7 +426,7 @@ public class AlterTableAlterColumnCommandValidationTest 
extends AbstractCommandV
         );
     }
 
-    // TODO: https://issues.apache.org/jira/browse/IGNITE-15200
+    // TODO: https://issues.apache.org/jira/browse/IGNITE-17373
     //  Include DURATION and PERIOD types after these types are supported.
     @ParameterizedTest
     @EnumSource(mode = Mode.EXCLUDE, value = ColumnType.class, names = 
{"STRING", "BYTE_ARRAY", "NULL", "DURATION", "PERIOD"})
@@ -678,7 +678,7 @@ public class AlterTableAlterColumnCommandValidationTest 
extends AbstractCommandV
         );
     }
 
-    // TODO: https://issues.apache.org/jira/browse/IGNITE-15200
+    // TODO: https://issues.apache.org/jira/browse/IGNITE-17373
     //  Remove this after interval type support is added.
     @ParameterizedTest
     @EnumSource(value = ColumnType.class, names = {"PERIOD", "DURATION"}, mode 
= Mode.INCLUDE)
@@ -730,7 +730,7 @@ public class AlterTableAlterColumnCommandValidationTest 
extends AbstractCommandV
         List<Arguments> arguments = new ArrayList<>();
         for (ColumnType from : ColumnType.values()) {
             for (ColumnType to : ColumnType.values()) {
-                // TODO: https://issues.apache.org/jira/browse/IGNITE-15200
+                // TODO: https://issues.apache.org/jira/browse/IGNITE-17373
                 //  Remove this after interval type support is added.
                 if (from == DURATION || to == DURATION || from == PERIOD || to 
== PERIOD) {
                     continue;
diff --git 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/CreateTableCommandValidationTest.java
 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/CreateTableCommandValidationTest.java
index c78174428f..d74c3d25b0 100644
--- 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/CreateTableCommandValidationTest.java
+++ 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/commands/CreateTableCommandValidationTest.java
@@ -485,7 +485,7 @@ public class CreateTableCommandValidationTest extends 
AbstractCommandValidationT
         });
     }
 
-    // TODO: https://issues.apache.org/jira/browse/IGNITE-15200
+    // TODO: https://issues.apache.org/jira/browse/IGNITE-17373
     //  Remove this after interval type support is added.
     @ParameterizedTest
     @EnumSource(value = ColumnType.class, names = {"PERIOD", "DURATION"}, mode 
= Mode.INCLUDE)
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 5f0d30a6d9..87ae97584d 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
@@ -223,7 +223,7 @@ public class CatalogEntrySerializationTest extends 
BaseIgniteAbstractTest {
 
         list.add(UUID.randomUUID());
 
-        // TODO Include ignored values to test after 
https://issues.apache.org/jira/browse/IGNITE-15200
+        // TODO Include ignored values to test after 
https://issues.apache.org/jira/browse/IGNITE-17373
         //  list.add(Duration.of(11, ChronoUnit.HOURS));
         //  list.add(Period.of(5, 4, 3));
 
diff --git 
a/modules/core/src/test/java/org/apache/ignite/internal/type/NativeTypeSpecTest.java
 
b/modules/core/src/test/java/org/apache/ignite/internal/type/NativeTypeSpecTest.java
index b85568cd51..4fcbf549fe 100644
--- 
a/modules/core/src/test/java/org/apache/ignite/internal/type/NativeTypeSpecTest.java
+++ 
b/modules/core/src/test/java/org/apache/ignite/internal/type/NativeTypeSpecTest.java
@@ -30,7 +30,7 @@ import org.junit.jupiter.params.provider.EnumSource.Mode;
 public class NativeTypeSpecTest {
 
     @ParameterizedTest
-    // TODO: https://issues.apache.org/jira/browse/IGNITE-15200: Include 
PERIOD and DURATION after interval type support is added.
+    // TODO: https://issues.apache.org/jira/browse/IGNITE-17373: Include 
PERIOD and DURATION after interval type support is added.
     @EnumSource(value = ColumnType.class, mode = Mode.EXCLUDE, names = 
{"NULL", "PERIOD", "DURATION"})
     public void testFromColumnType(ColumnType columnType) {
         NativeTypeSpec typeSpec = NativeTypeSpec.fromColumnType(columnType);
diff --git 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/ItIgniteNodeRestartTest.java
 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/ItIgniteNodeRestartTest.java
index b7c7ce04cd..f69eb9145b 100644
--- 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/ItIgniteNodeRestartTest.java
+++ 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/ItIgniteNodeRestartTest.java
@@ -765,6 +765,7 @@ public class ItIgniteNodeRestartTest extends 
BaseIgniteRestartTest {
                 
nodeCfgMgr.configurationRegistry().getConfiguration(SqlNodeExtensionConfiguration.KEY).sql(),
                 transactionInflights,
                 txManager,
+                lowWatermark,
                 threadPoolsManager.commonScheduler()
         );
 
diff --git 
a/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java 
b/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java
index 86d849c175..1cb06bc363 100644
--- 
a/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java
+++ 
b/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java
@@ -1040,6 +1040,7 @@ public class IgniteImpl implements Ignite {
                 
nodeConfigRegistry.getConfiguration(SqlNodeExtensionConfiguration.KEY).sql(),
                 transactionInflights,
                 txManager,
+                lowWatermark,
                 threadPoolsManager.commonScheduler()
         );
 
diff --git a/modules/sql-engine/build.gradle b/modules/sql-engine/build.gradle
index 2ebfe00669..1ca9dee20c 100644
--- a/modules/sql-engine/build.gradle
+++ b/modules/sql-engine/build.gradle
@@ -52,6 +52,7 @@ dependencies {
     implementation project(':ignite-placement-driver-api')
     implementation project(':ignite-partition-replicator')
     implementation project(':ignite-partition-distribution')
+    implementation project(':ignite-low-watermark')
     implementation libs.jetbrains.annotations
     implementation libs.fastutil.core
     implementation libs.caffeine
diff --git 
a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItCreateTableDdlTest.java
 
b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItCreateTableDdlTest.java
index 602497a741..3ce9c24b7d 100644
--- 
a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItCreateTableDdlTest.java
+++ 
b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItCreateTableDdlTest.java
@@ -520,7 +520,7 @@ public class ItCreateTableDdlTest extends 
BaseSqlIntegrationTest {
         sql("DROP TABLE \"table\"\"Test\"\"\"");
     }
 
-    // TODO: https://issues.apache.org/jira/browse/IGNITE-15200
+    // TODO: https://issues.apache.org/jira/browse/IGNITE-17373
     //  Remove this after interval type support is added.
     @Test
     public void testCreateTableDoesNotAllowIntervals() {
@@ -537,7 +537,7 @@ public class ItCreateTableDdlTest extends 
BaseSqlIntegrationTest {
         );
     }
 
-    // TODO: https://issues.apache.org/jira/browse/IGNITE-15200
+    // TODO: https://issues.apache.org/jira/browse/IGNITE-17373
     //  Remove this after interval type support is added.
     @Test
     public void testAlterTableDoesNotAllowIntervals() {
diff --git 
a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItDynamicParameterTest.java
 
b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItDynamicParameterTest.java
index 9ca7946d86..8ee4f986d3 100644
--- 
a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItDynamicParameterTest.java
+++ 
b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItDynamicParameterTest.java
@@ -71,7 +71,7 @@ public class ItDynamicParameterTest extends 
BaseSqlIntegrationTest {
 
     @ParameterizedTest
     @EnumSource(value = ColumnType.class,
-            // TODO: https://issues.apache.org/jira/browse/IGNITE-15200
+            // TODO: https://issues.apache.org/jira/browse/IGNITE-17373
             names = {"DURATION", "PERIOD"},
             mode = Mode.EXCLUDE
     )
diff --git 
a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/statistic/ItStatisticTest.java
 
b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/statistic/ItStatisticTest.java
new file mode 100644
index 0000000000..243fd9727c
--- /dev/null
+++ 
b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/statistic/ItStatisticTest.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.sql.engine.statistic;
+
+import static 
org.apache.ignite.internal.sql.engine.util.QueryChecker.containsRowCount;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.ignite.internal.sql.BaseSqlIntegrationTest;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+/** Integration test to check SQL statistics. */
+public class ItStatisticTest extends BaseSqlIntegrationTest {
+    private SqlStatisticManagerImpl sqlStatisticManager;
+
+    @BeforeAll
+    void beforeAll() {
+        sqlStatisticManager = (SqlStatisticManagerImpl) 
queryProcessor().sqlStatisticManager();
+        sql("CREATE TABLE t(ID INTEGER PRIMARY KEY, VAL INTEGER)");
+    }
+
+    @AfterAll
+    void afterAll() {
+        sql("DROP TABLE IF EXISTS t");
+    }
+
+    @Test
+    public void testStatisticsRowCount() throws Exception {
+        // For test we should always update statistics.
+        long prevValueOfThreshold = 
sqlStatisticManager.setThresholdTimeToPostponeUpdateMs(0);
+        try {
+            insertAndUpdateRunQuery(500);
+            // Minimum row count is 1000, even we have less rows.
+            assertQuery(getUniqueQuery())
+                    .matches(containsRowCount("PUBLIC", "T", 1000))
+                    .check();
+
+            insertAndUpdateRunQuery(600);
+            // Should return actual number of rows in the table.
+            assertQuery(getUniqueQuery())
+                    .matches(containsRowCount("PUBLIC", "T", 1100))
+                    .check();
+
+            
sqlStatisticManager.setThresholdTimeToPostponeUpdateMs(Long.MAX_VALUE);
+            insertAndUpdateRunQuery(900);
+
+            // Statistics shouldn't be updated despite we inserted new rows.
+            assertQuery(getUniqueQuery())
+                    .matches(containsRowCount("PUBLIC", "T", 1100))
+                    .check();
+        } finally {
+            
sqlStatisticManager.setThresholdTimeToPostponeUpdateMs(prevValueOfThreshold);
+        }
+    }
+
+    private static AtomicInteger counter = new AtomicInteger(0);
+
+    private void insertAndUpdateRunQuery(int numberOfRecords) throws 
ExecutionException, TimeoutException, InterruptedException {
+        int start = counter.get();
+        int end = counter.addAndGet(numberOfRecords) - 1;
+        sql("INSERT INTO t SELECT x, x FROM system_range(?, ?)", start, end);
+
+        // run unique sql to update statistics
+        sql(getUniqueQuery());
+
+        // wait to update statistics
+        sqlStatisticManager.lastUpdateStatisticFuture().get(5, 
TimeUnit.SECONDS);
+    }
+
+    private static String getUniqueQuery() {
+        return "SELECT * FROM t where val=" + counter.incrementAndGet();
+    }
+}
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/SqlQueryProcessor.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/SqlQueryProcessor.java
index 2e44fd27b0..0280bc6f22 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/SqlQueryProcessor.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/SqlQueryProcessor.java
@@ -56,6 +56,7 @@ import org.apache.ignite.internal.hlc.ClockService;
 import org.apache.ignite.internal.hlc.HybridTimestamp;
 import org.apache.ignite.internal.lang.IgniteInternalException;
 import org.apache.ignite.internal.lang.NodeStoppingException;
+import org.apache.ignite.internal.lowwatermark.LowWatermark;
 import org.apache.ignite.internal.manager.ComponentContext;
 import org.apache.ignite.internal.metrics.MetricManager;
 import org.apache.ignite.internal.network.ClusterService;
@@ -98,6 +99,8 @@ import 
org.apache.ignite.internal.sql.engine.schema.SqlSchemaManagerImpl;
 import org.apache.ignite.internal.sql.engine.sql.ParsedResult;
 import org.apache.ignite.internal.sql.engine.sql.ParserService;
 import org.apache.ignite.internal.sql.engine.sql.ParserServiceImpl;
+import org.apache.ignite.internal.sql.engine.statistic.SqlStatisticManager;
+import org.apache.ignite.internal.sql.engine.statistic.SqlStatisticManagerImpl;
 import org.apache.ignite.internal.sql.engine.tx.QueryTransactionContext;
 import org.apache.ignite.internal.sql.engine.tx.QueryTransactionContextImpl;
 import org.apache.ignite.internal.sql.engine.tx.QueryTransactionWrapper;
@@ -176,6 +179,7 @@ public class SqlQueryProcessor implements QueryProcessor {
     private final ReplicaService replicaService;
 
     private final SqlSchemaManager sqlSchemaManager;
+    private final SqlStatisticManager sqlStatisticManager;
 
     private final FailureManager failureManager;
 
@@ -237,6 +241,7 @@ public class SqlQueryProcessor implements QueryProcessor {
             SqlLocalConfiguration nodeCfg,
             TransactionInflights transactionInflights,
             TxManager txManager,
+            LowWatermark lowWaterMark,
             ScheduledExecutorService commonScheduler
     ) {
         this.clusterSrvc = clusterSrvc;
@@ -258,9 +263,10 @@ public class SqlQueryProcessor implements QueryProcessor {
         this.transactionInflights = transactionInflights;
         this.txManager = txManager;
         this.commonScheduler = commonScheduler;
-
+        sqlStatisticManager = new SqlStatisticManagerImpl(tableManager, 
catalogManager, lowWaterMark);
         sqlSchemaManager = new SqlSchemaManagerImpl(
                 catalogManager,
+                sqlStatisticManager,
                 CACHE_FACTORY,
                 SCHEMA_CACHE_SIZE
         );
@@ -356,6 +362,8 @@ public class SqlQueryProcessor implements QueryProcessor {
         clusterSrvc.topologyService().addEventHandler(executionSrvc);
         clusterSrvc.topologyService().addEventHandler(mailboxRegistry);
 
+        registerService(sqlStatisticManager);
+
         this.executionSrvc = executionSrvc;
 
         services.forEach(LifecycleAware::start);
@@ -700,6 +708,11 @@ public class SqlQueryProcessor implements QueryProcessor {
         return metricManager;
     }
 
+    @TestOnly
+    public SqlStatisticManager sqlStatisticManager() {
+        return sqlStatisticManager;
+    }
+
     /** Performs additional validation of a parsed statement. **/
     private static void validateParsedStatement(
             SqlProperties properties,
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/agg/Accumulators.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/agg/Accumulators.java
index 3e3ae88b60..a0a8b641eb 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/agg/Accumulators.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/agg/Accumulators.java
@@ -109,7 +109,7 @@ public class Accumulators {
             case BIGINT:
                 return () -> DecimalAvg.FACTORY.apply(0);
             case DECIMAL:
-                // TODO: https://issues.apache.org/jira/browse/IGNITE-15200 
Add support for interval types.
+                // TODO: https://issues.apache.org/jira/browse/IGNITE-17373 
Add support for interval types.
                 return () -> DecimalAvg.FACTORY.apply(call.type.getScale());
             case DOUBLE:
             case REAL:
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/PlanningContext.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/PlanningContext.java
index 87b0eb4377..9a795ba187 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/PlanningContext.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/PlanningContext.java
@@ -25,6 +25,7 @@ import com.google.common.collect.Multimap;
 import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
 import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
 import java.lang.reflect.Method;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Objects;
 import java.util.Properties;
@@ -40,22 +41,30 @@ import org.apache.calcite.plan.RelOptSchema;
 import org.apache.calcite.plan.RelTraitDef;
 import org.apache.calcite.plan.volcano.VolcanoPlanner;
 import org.apache.calcite.prepare.CalciteCatalogReader;
+import org.apache.calcite.prepare.Prepare;
+import org.apache.calcite.prepare.Prepare.PreparingTable;
+import org.apache.calcite.prepare.RelOptTableImpl;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.metadata.Metadata;
 import org.apache.calcite.rel.metadata.MetadataDef;
 import org.apache.calcite.rel.metadata.MetadataHandler;
 import org.apache.calcite.rel.metadata.RelMetadataProvider;
 import org.apache.calcite.rel.metadata.UnboundMetadata;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.schema.SchemaPlus;
+import org.apache.calcite.schema.Table;
+import org.apache.calcite.schema.Wrapper;
 import org.apache.calcite.sql.SqlOperatorTable;
 import org.apache.calcite.sql.validate.SqlConformance;
+import org.apache.calcite.sql.validate.SqlValidatorUtil;
 import org.apache.calcite.tools.FrameworkConfig;
 import org.apache.calcite.tools.Frameworks;
 import org.apache.calcite.tools.RuleSet;
 import org.apache.calcite.util.CancelFlag;
 import org.apache.ignite.internal.sql.engine.metadata.cost.IgniteCostFactory;
 import org.apache.ignite.internal.sql.engine.rex.IgniteRexBuilder;
+import org.apache.ignite.internal.sql.engine.schema.IgniteDataSource;
 import org.apache.ignite.internal.sql.engine.schema.IgniteSchema;
 import org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
 import org.jetbrains.annotations.Nullable;
@@ -255,7 +264,7 @@ public final class PlanningContext implements Context {
         }
 
         //noinspection NestedAssignment
-        return catalogReader = new CalciteCatalogReader(
+        return catalogReader = new IgniteCatalogReader(
                 CalciteSchema.from(rootSchema),
                 CalciteSchema.from(dfltSchema).path(null),
                 IgniteTypeFactory.INSTANCE, CALCITE_CONNECTION_CONFIG);
@@ -310,6 +319,44 @@ public final class PlanningContext implements Context {
         return explicitTx;
     }
 
+    /** Present Catalog reader with supporting snapshot of rows count for 
tables. */
+    private static class IgniteCatalogReader extends CalciteCatalogReader {
+        private final HashMap<String, Double> cacheSizeOfTables = new 
HashMap<>();
+
+        public IgniteCatalogReader(CalciteSchema rootSchema, List<String> 
defaultSchema, RelDataTypeFactory typeFactory,
+                CalciteConnectionConfig config) {
+            super(rootSchema, defaultSchema, typeFactory, config);
+        }
+
+        // It's a copy-paste of original method with adding cache for table 
size to have statistic snapshot for whole planing stage.
+        @Override
+        public PreparingTable getTable(final List<String> names) {
+            CalciteSchema.TableEntry entry = 
SqlValidatorUtil.getTableEntry(this, names);
+            if (entry != null) {
+                Table table = entry.getTable();
+                if (table instanceof Wrapper) {
+                    final Prepare.PreparingTable relOptTable =
+                            ((Wrapper) 
table).unwrap(Prepare.PreparingTable.class);
+                    if (relOptTable != null) {
+                        return relOptTable;
+                    }
+                }
+                Double rowCount = null;
+                if (table instanceof IgniteDataSource) {
+                    IgniteDataSource ds = (IgniteDataSource) table;
+                    rowCount = cacheSizeOfTables.computeIfAbsent(
+                            ds.name(),
+                            k -> ds.getStatistic().getRowCount()
+                    );
+                }
+
+                return RelOptTableImpl.create(this,
+                        table.getRowType(typeFactory), entry, rowCount);
+            }
+            return null;
+        }
+    }
+
     /**
      * Planner context builder.
      */
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverter.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverter.java
index baac2f297b..df1942d700 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverter.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverter.java
@@ -396,7 +396,7 @@ public class DdlSqlToCommandConverter {
         String name = col.name.getSimple();
         RelDataType relType = planner.convert(col.dataType, nullable);
 
-        // TODO: https://issues.apache.org/jira/browse/IGNITE-15200
+        // TODO: https://issues.apache.org/jira/browse/IGNITE-17373
         //  Remove this after interval type support is added.
         if (SqlTypeUtil.isInterval(relType)) {
             String error = format(
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/IgniteStatistic.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/IgniteStatistic.java
index f4bea6d018..99b1a5961c 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/IgniteStatistic.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/IgniteStatistic.java
@@ -17,44 +17,30 @@
 
 package org.apache.ignite.internal.sql.engine.schema;
 
-import java.util.function.DoubleSupplier;
+import java.util.function.LongSupplier;
 import org.apache.calcite.schema.Statistic;
 import org.apache.ignite.internal.sql.engine.trait.IgniteDistribution;
-import org.jetbrains.annotations.Nullable;
 
 /**
  * Supported table statistics.
  */
 public class IgniteStatistic implements Statistic {
-
-    public static final double MIN_ROWS = 10_000.0;
-
-    private final DoubleSupplier rowCountSupplier;
+    private final LongSupplier rowCountSupplier;
 
     private final IgniteDistribution distribution;
 
-    private final DoubleSupplier minRows;
-
-    /** Constructor. */
-    public IgniteStatistic(DoubleSupplier rowCountSupplier, IgniteDistribution 
distribution) {
-        this(rowCountSupplier, distribution, () -> MIN_ROWS);
-    }
-
     /** Constructor. */
-    public IgniteStatistic(DoubleSupplier rowCountSupplier, IgniteDistribution 
distribution, @Nullable DoubleSupplier minRows) {
+    public IgniteStatistic(LongSupplier rowCountSupplier, IgniteDistribution 
distribution) {
         this.distribution = distribution;
         this.rowCountSupplier = rowCountSupplier;
-        this.minRows = minRows == null ? () -> MIN_ROWS : minRows;
     }
 
     /** {@inheritDoc} */
     @Override
     public final Double getRowCount() {
-        double localRowCnt = rowCountSupplier.getAsDouble();
+        long approximateRowCount = rowCountSupplier.getAsLong();
 
-        // Forbid zero result, to prevent zero cost for table and index scans.
-        double minRows = this.minRows.getAsDouble();
-        return Math.max(minRows, localRowCnt);
+        return (double) approximateRowCount;
     }
 
     /** {@inheritDoc} */
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 8abb1fdb36..9221b13cd2 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
@@ -58,6 +58,7 @@ import 
org.apache.ignite.internal.catalog.descriptors.CatalogZoneDescriptor;
 import org.apache.ignite.internal.lang.IgniteInternalException;
 import org.apache.ignite.internal.schema.DefaultValueGenerator;
 import org.apache.ignite.internal.sql.engine.schema.IgniteIndex.Type;
+import org.apache.ignite.internal.sql.engine.statistic.SqlStatisticManager;
 import org.apache.ignite.internal.sql.engine.trait.IgniteDistribution;
 import org.apache.ignite.internal.sql.engine.trait.IgniteDistributions;
 import org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
@@ -73,6 +74,7 @@ import org.apache.ignite.lang.ErrorGroups.Common;
 public class SqlSchemaManagerImpl implements SqlSchemaManager {
 
     private final CatalogManager catalogManager;
+    private final SqlStatisticManager sqlStatisticManager;
 
     private final Cache<Integer, SchemaPlus> schemaCache;
 
@@ -90,8 +92,13 @@ public class SqlSchemaManagerImpl implements 
SqlSchemaManager {
     private final Cache<Long, ActualIgniteTable> fullDataTableCache;
 
     /** Constructor. */
-    public SqlSchemaManagerImpl(CatalogManager catalogManager, CacheFactory 
factory, int cacheSize) {
+    public SqlSchemaManagerImpl(
+            CatalogManager catalogManager,
+            SqlStatisticManager sqlStatisticManager,
+            CacheFactory factory,
+            int cacheSize) {
         this.catalogManager = catalogManager;
+        this.sqlStatisticManager = sqlStatisticManager;
         this.schemaCache = factory.create(cacheSize);
         this.tableCache = factory.create(cacheSize);
         this.indexCache = factory.create(cacheSize);
@@ -441,7 +448,7 @@ public class SqlSchemaManagerImpl implements 
SqlSchemaManager {
 
         CatalogZoneDescriptor zoneDescriptor = getZoneDescriptor(catalog, 
table.zoneId());
 
-        return createTable(table, descriptor, tableIndexes, 
zoneDescriptor.partitions());
+        return createTable(table, descriptor, tableIndexes, 
zoneDescriptor.partitions(), sqlStatisticManager);
     }
 
     private Map<String, IgniteIndex> getIndexes(Catalog catalog, int tableId, 
int primaryKeyIndexId) {
@@ -487,7 +494,8 @@ public class SqlSchemaManagerImpl implements 
SqlSchemaManager {
             CatalogTableDescriptor catalogTableDescriptor,
             TableDescriptor tableDescriptor,
             Map<String, IgniteIndex> indexes,
-            int parititions
+            int parititions,
+            SqlStatisticManager sqlStatisticManager
     ) {
         IgniteIndex primaryIndex = indexes.values().stream()
                 .filter(IgniteIndex::primaryKey)
@@ -503,9 +511,7 @@ public class SqlSchemaManagerImpl implements 
SqlSchemaManager {
         int tableId = catalogTableDescriptor.id();
         String tableName = catalogTableDescriptor.name();
 
-        // TODO IGNITE-19558: The table is not available at planning stage.
-        // Let's fix table statistics keeping in mind IGNITE-19558 issue.
-        IgniteStatistic statistic = new IgniteStatistic(() -> 0.0d, 
tableDescriptor.distribution());
+        IgniteStatistic statistic = new IgniteStatistic(() -> 
sqlStatisticManager.tableSize(tableId), tableDescriptor.distribution());
 
         return new IgniteTableImpl(
                 tableName,
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/statistic/SqlStatisticManager.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/statistic/SqlStatisticManager.java
new file mode 100644
index 0000000000..6b98b1a6f0
--- /dev/null
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/statistic/SqlStatisticManager.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.sql.engine.statistic;
+
+import org.apache.ignite.internal.sql.engine.exec.LifecycleAware;
+
+/**
+ * Defined interface to manage SQL statistics.
+ */
+public interface SqlStatisticManager extends LifecycleAware {
+    /**
+     * Returns approximate number of rows in table by their id.
+     */
+    long tableSize(int tableId);
+
+    @Override
+    default void start(){}
+
+    @Override
+    default void stop(){}
+}
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/statistic/SqlStatisticManagerImpl.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/statistic/SqlStatisticManagerImpl.java
new file mode 100644
index 0000000000..1148eeb0d2
--- /dev/null
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/statistic/SqlStatisticManagerImpl.java
@@ -0,0 +1,230 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.sql.engine.statistic;
+
+import static org.apache.ignite.internal.event.EventListener.fromConsumer;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import org.apache.ignite.internal.catalog.CatalogService;
+import org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor;
+import org.apache.ignite.internal.catalog.events.CatalogEvent;
+import org.apache.ignite.internal.catalog.events.CreateTableEventParameters;
+import org.apache.ignite.internal.catalog.events.DropTableEventParameters;
+import org.apache.ignite.internal.event.EventListener;
+import org.apache.ignite.internal.logger.IgniteLogger;
+import org.apache.ignite.internal.logger.Loggers;
+import org.apache.ignite.internal.lowwatermark.LowWatermark;
+import 
org.apache.ignite.internal.lowwatermark.event.ChangeLowWatermarkEventParameters;
+import org.apache.ignite.internal.lowwatermark.event.LowWatermarkEvent;
+import org.apache.ignite.internal.table.LongPriorityQueue;
+import org.apache.ignite.internal.table.TableViewInternal;
+import org.apache.ignite.internal.table.distributed.TableManager;
+import org.apache.ignite.internal.util.FastTimestamps;
+import org.jetbrains.annotations.TestOnly;
+
+/**
+ * Statistic manager. Provide and manage update of statistics for SQL.
+ */
+public class SqlStatisticManagerImpl implements SqlStatisticManager {
+    private static final IgniteLogger LOG = 
Loggers.forClass(SqlStatisticManagerImpl.class);
+    private static final long DEFAULT_TABLE_SIZE = 1_000_000L;
+    private static final long MINIMUM_TABLE_SIZE = 1_000L;
+    private static final ActualSize DEFAULT_VALUE = new 
ActualSize(DEFAULT_TABLE_SIZE, 0L);
+
+    private final EventListener<ChangeLowWatermarkEventParameters> lwmListener 
= fromConsumer(this::onLwmChanged);
+    private final EventListener<DropTableEventParameters> 
dropTableEventListener = fromConsumer(this::onTableDrop);
+    private final EventListener<CreateTableEventParameters> 
createTableEventListener = fromConsumer(this::onTableCreate);
+
+    private volatile Future<Void> statisticUpdateFut = 
CompletableFuture.completedFuture(null);
+
+    /** A queue for deferred table destruction events. */
+    private final LongPriorityQueue<DestroyTableEvent> destructionEventsQueue 
= new LongPriorityQueue<>(DestroyTableEvent::catalogVersion);
+
+    private final TableManager tableManager;
+    private final CatalogService catalogService;
+    private final LowWatermark lowWatermark;
+
+    /* Contains all known table id's with statistics. */
+    private final ConcurrentMap<Integer, ActualSize> tableSizeMap = new 
ConcurrentHashMap<>();
+
+    private volatile long thresholdTimeToPostponeUpdateMs = 
TimeUnit.MINUTES.toMillis(1);
+
+    /** Constructor. */
+    public SqlStatisticManagerImpl(TableManager tableManager, CatalogService 
catalogService, LowWatermark lowWatermark) {
+        this.tableManager = tableManager;
+        this.catalogService = catalogService;
+        this.lowWatermark = lowWatermark;
+    }
+
+
+    /**
+     * Returns approximate number of rows in table by their id.
+     *
+     * <p>Returns the previous known value or {@value 
SqlStatisticManagerImpl#DEFAULT_TABLE_SIZE} as default value. Can start process 
to
+     * update asked statistics in background to have updated values for future 
requests.
+     *
+     * @return An approximate number of rows in a given table.
+     */
+    @Override
+    public long tableSize(int tableId) {
+        updateTableSizeStatistics(tableId);
+        long tableSize = tableSizeMap.getOrDefault(tableId, 
DEFAULT_VALUE).getSize();
+
+        return Math.max(tableSize, MINIMUM_TABLE_SIZE);
+    }
+
+    /** Update table size statistic in the background if it required. */
+    private void updateTableSizeStatistics(int tableId) {
+        TableViewInternal tableView = tableManager.cachedTable(tableId);
+        if (tableView == null) {
+            LOG.debug("There is no table to update statistics [id={}].", 
tableId);
+            return;
+        }
+
+        ActualSize tableSize = tableSizeMap.get(tableId);
+        if (tableSize == null) {
+            // has been concurrently cleaned up, no need more update statistic 
for the table.
+            return;
+        }
+        long currTimestamp = FastTimestamps.coarseCurrentTimeMillis();
+        long lastUpdateTime = tableSize.getTimestamp();
+
+        if (lastUpdateTime <= currTimestamp - thresholdTimeToPostponeUpdateMs) 
{
+            // Prevent to run update for the same table twice concurrently.
+            if (!tableSizeMap.replace(tableId, tableSize, new 
ActualSize(tableSize.getSize(), currTimestamp))) {
+                return;
+            }
+
+            // just request new table size in background.
+            statisticUpdateFut = tableView.internalTable().estimatedSize()
+                    .thenAccept(size -> {
+                        // the table can be concurrently dropped and we 
shouldn't put new value in this case.
+                        tableSizeMap.computeIfPresent(tableId, (k, v) -> new 
ActualSize(size, currTimestamp));
+                    }).exceptionally(e -> {
+                        LOG.info("Can't calculate size for table [id={}].", e, 
tableId);
+                        return null;
+                    });
+        }
+    }
+
+    @Override
+    public void start() {
+        catalogService.listen(CatalogEvent.TABLE_CREATE, 
createTableEventListener);
+        catalogService.listen(CatalogEvent.TABLE_DROP, dropTableEventListener);
+        lowWatermark.listen(LowWatermarkEvent.LOW_WATERMARK_CHANGED, 
lwmListener);
+
+        // Need to have all known tables for all available history of catalog.
+        int earliestVersion = catalogService.earliestCatalogVersion();
+        int latestVersion = catalogService.latestCatalogVersion();
+        for (int version = earliestVersion; version <= latestVersion; 
version++) {
+            Collection<CatalogTableDescriptor> tables = 
catalogService.tables(version);
+            for (CatalogTableDescriptor table : tables) {
+                tableSizeMap.putIfAbsent(table.id(), DEFAULT_VALUE);
+            }
+        }
+    }
+
+    @Override
+    public void stop() {
+        lowWatermark.removeListener(LowWatermarkEvent.LOW_WATERMARK_CHANGED, 
lwmListener);
+        catalogService.removeListener(CatalogEvent.TABLE_DROP, 
dropTableEventListener);
+        catalogService.removeListener(CatalogEvent.TABLE_CREATE, 
createTableEventListener);
+    }
+
+    private void onTableDrop(DropTableEventParameters parameters) {
+        int tableId = parameters.tableId();
+        int catalogVersion = parameters.catalogVersion();
+
+        destructionEventsQueue.enqueue(new DestroyTableEvent(catalogVersion, 
tableId));
+    }
+
+    private void onTableCreate(CreateTableEventParameters parameters) {
+        tableSizeMap.put(parameters.tableId(), DEFAULT_VALUE);
+    }
+
+    private void onLwmChanged(ChangeLowWatermarkEventParameters parameters) {
+        int earliestVersion = 
catalogService.activeCatalogVersion(parameters.newLowWatermark().longValue());
+        List<DestroyTableEvent> events = 
destructionEventsQueue.drainUpTo(earliestVersion);
+
+        events.forEach(event -> tableSizeMap.remove(event.tableId()));
+    }
+
+    /** Timestamped size. */
+    private static class ActualSize {
+        long timestamp;
+        long size;
+
+        public ActualSize(long size, long timestamp) {
+            this.timestamp = timestamp;
+            this.size = size;
+        }
+
+        public long getTimestamp() {
+            return timestamp;
+        }
+
+        public long getSize() {
+            return size;
+        }
+    }
+
+
+    /** Internal event. */
+    private static class DestroyTableEvent {
+        final int catalogVersion;
+        final int tableId;
+
+        DestroyTableEvent(int catalogVersion, int tableId) {
+            this.catalogVersion = catalogVersion;
+            this.tableId = tableId;
+        }
+
+        public int catalogVersion() {
+            return catalogVersion;
+        }
+
+        public int tableId() {
+            return tableId;
+        }
+    }
+
+    /**
+     * Set threshold time to postpone update statistics.
+     */
+    @TestOnly
+    public long setThresholdTimeToPostponeUpdateMs(long milliseconds) {
+        assert milliseconds >= 0;
+        long prevValue = thresholdTimeToPostponeUpdateMs;
+        thresholdTimeToPostponeUpdateMs = milliseconds;
+        return prevValue;
+    }
+
+    /**
+     * Returns feature for the last run update statistics to have ability wait 
update statistics.
+     */
+    @TestOnly
+    public Future<Void> lastUpdateStatisticFuture() {
+        return statisticUpdateFut;
+    }
+}
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/exp/ExpressionFactoryImplTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/exp/ExpressionFactoryImplTest.java
index c04f9aedcb..6808b836d1 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/exp/ExpressionFactoryImplTest.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/exp/ExpressionFactoryImplTest.java
@@ -869,7 +869,7 @@ public class ExpressionFactoryImplTest extends 
BaseIgniteAbstractTest {
         EnumSet<ColumnType> ignoredTypes = EnumSet.of(
                 // UUID literal doesn't exists.
                 ColumnType.UUID,
-                // TODO https://issues.apache.org/jira/browse/IGNITE-15200
+                // TODO https://issues.apache.org/jira/browse/IGNITE-17373
                 ColumnType.DURATION,
                 ColumnType.PERIOD
         );
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/row/SqlRowHandlerTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/row/SqlRowHandlerTest.java
index 35843dfaa0..d29c267d5e 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/row/SqlRowHandlerTest.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/row/SqlRowHandlerTest.java
@@ -336,7 +336,7 @@ public class SqlRowHandlerTest extends IgniteAbstractTest {
     }
 
     private static Set<ColumnType> columnTypes() {
-        // TODO Include ignored types to test after 
https://issues.apache.org/jira/browse/IGNITE-15200
+        // TODO Include ignored types to test after 
https://issues.apache.org/jira/browse/IGNITE-17373
         return EnumSet.complementOf(EnumSet.of(ColumnType.PERIOD, 
ColumnType.DURATION));
     }
 
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestBuilders.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestBuilders.java
index 3a613190a4..b4663de522 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestBuilders.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestBuilders.java
@@ -122,6 +122,7 @@ import 
org.apache.ignite.internal.sql.engine.schema.SqlSchemaManagerImpl;
 import org.apache.ignite.internal.sql.engine.schema.TableDescriptor;
 import org.apache.ignite.internal.sql.engine.schema.TableDescriptorImpl;
 import org.apache.ignite.internal.sql.engine.sql.ParserServiceImpl;
+import org.apache.ignite.internal.sql.engine.statistic.SqlStatisticManager;
 import org.apache.ignite.internal.sql.engine.trait.IgniteDistribution;
 import org.apache.ignite.internal.sql.engine.trait.IgniteDistributions;
 import org.apache.ignite.internal.sql.engine.util.Commons;
@@ -654,7 +655,10 @@ public class TestBuilders {
             CatalogManager catalogManager = 
CatalogTestUtils.createCatalogManagerWithTestUpdateLog(clusterName, clock);
 
             var parserService = new ParserServiceImpl();
-            var schemaManager = new SqlSchemaManagerImpl(catalogManager, 
CaffeineCacheFactory.INSTANCE, 0);
+
+            SqlStatisticManager sqlStatisticManager = tableId -> 10_000L;
+
+            var schemaManager = new SqlSchemaManagerImpl(catalogManager, 
sqlStatisticManager, CaffeineCacheFactory.INSTANCE, 0);
             var prepareService = new PrepareServiceImpl(clusterName, 0, 
CaffeineCacheFactory.INSTANCE,
                     new DdlSqlToCommandConverter(), PLANNING_TIMEOUT, 
PLANNING_THREAD_COUNT,
                     new NoOpMetricManager(), schemaManager);
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/JoinWithUsingPlannerTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/JoinWithUsingPlannerTest.java
index 816d639c76..eddc16096d 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/JoinWithUsingPlannerTest.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/JoinWithUsingPlannerTest.java
@@ -217,7 +217,7 @@ public class JoinWithUsingPlannerTest extends 
AbstractPlannerTest {
     private static Stream<Arguments> nativeTypesMatrix() {
         Set<ColumnType> unsupportedTypes = Set.of(
                 ColumnType.NULL,
-                // TODO Exclude interval types after 
https://issues.apache.org/jira/browse/IGNITE-15200
+                // TODO Exclude interval types after 
https://issues.apache.org/jira/browse/IGNITE-17373
                 ColumnType.PERIOD,
                 ColumnType.DURATION
         );
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverterTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverterTest.java
index d4bcb92e0e..8aec04ba97 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverterTest.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverterTest.java
@@ -301,7 +301,7 @@ public class DdlSqlToCommandConverterTest extends 
AbstractDdlSqlToCommandConvert
     }
 
     @TestFactory
-    @Disabled("https://issues.apache.org/jira/browse/IGNITE-15200";)
+    @Disabled("https://issues.apache.org/jira/browse/IGNITE-17373";)
     public Stream<DynamicTest> numericDefaultWithIntervalTypes() {
         List<DynamicTest> testItems = new ArrayList<>();
         PlanningContext ctx = createContext();
@@ -337,7 +337,7 @@ public class DdlSqlToCommandConverterTest extends 
AbstractDdlSqlToCommandConvert
     }
 
     @TestFactory
-    @Disabled("https://issues.apache.org/jira/browse/IGNITE-15200";)
+    @Disabled("https://issues.apache.org/jira/browse/IGNITE-17373";)
     public Stream<DynamicTest> nonIntervalDefaultsWithIntervalTypes() {
         List<DynamicTest> testItems = new ArrayList<>();
         PlanningContext ctx = createContext();
@@ -353,7 +353,7 @@ public class DdlSqlToCommandConverterTest extends 
AbstractDdlSqlToCommandConvert
         return testItems.stream();
     }
 
-    @Disabled("https://issues.apache.org/jira/browse/IGNITE-15200";)
+    @Disabled("https://issues.apache.org/jira/browse/IGNITE-17373";)
     @TestFactory
     public Stream<DynamicTest> intervalDefaultsWithIntervalTypes() {
         List<DynamicTest> testItems = new ArrayList<>();
@@ -667,7 +667,7 @@ public class DdlSqlToCommandConverterTest extends 
AbstractDdlSqlToCommandConvert
         assertThat(ex.getMessage(), containsString("String cannot be empty"));
     }
 
-    // TODO: https://issues.apache.org/jira/browse/IGNITE-15200
+    // TODO: https://issues.apache.org/jira/browse/IGNITE-17373
     //  Remove this after interval type support is added.
     @ParameterizedTest
     @MethodSource("intervalTypeNames")
@@ -692,7 +692,7 @@ public class DdlSqlToCommandConverterTest extends 
AbstractDdlSqlToCommandConvert
         }
     }
 
-    // TODO: https://issues.apache.org/jira/browse/IGNITE-15200
+    // TODO: https://issues.apache.org/jira/browse/IGNITE-17373
     //  Remove this after interval type support is added.
     @ParameterizedTest
     @MethodSource("intervalTypeNames")
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/pruning/PartitionPruningPredicateSelfTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/pruning/PartitionPruningPredicateSelfTest.java
index d5f10b16c4..8112f7a30a 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/pruning/PartitionPruningPredicateSelfTest.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/pruning/PartitionPruningPredicateSelfTest.java
@@ -77,7 +77,7 @@ public class PartitionPruningPredicateSelfTest extends 
BaseIgniteAbstractTest {
     private static List<ColumnType> columnTypes() {
         return Arrays.stream(ColumnType.values())
                 .filter(t -> t != ColumnType.NULL
-                        // TODO 
https://issues.apache.org/jira/browse/IGNITE-15200 Include interval types after 
this issue is resolved
+                        // TODO 
https://issues.apache.org/jira/browse/IGNITE-17373 Include interval types after 
this issue is resolved
                         && t != ColumnType.DURATION
                         && t != ColumnType.PERIOD
                 )
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/schema/SqlSchemaManagerImplTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/schema/SqlSchemaManagerImplTest.java
index d0f8384765..e83d01c700 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/schema/SqlSchemaManagerImplTest.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/schema/SqlSchemaManagerImplTest.java
@@ -80,6 +80,7 @@ import 
org.apache.ignite.internal.lang.IgniteInternalException;
 import org.apache.ignite.internal.manager.ComponentContext;
 import org.apache.ignite.internal.schema.DefaultValueGenerator;
 import org.apache.ignite.internal.sql.engine.schema.IgniteIndex.Type;
+import org.apache.ignite.internal.sql.engine.statistic.SqlStatisticManager;
 import org.apache.ignite.internal.sql.engine.trait.IgniteDistribution;
 import org.apache.ignite.internal.sql.engine.trait.IgniteDistributions;
 import org.apache.ignite.internal.sql.engine.util.RowTypeUtils;
@@ -105,11 +106,14 @@ public class SqlSchemaManagerImplTest extends 
BaseIgniteAbstractTest {
 
     private CatalogManager catalogManager;
     private SqlSchemaManagerImpl sqlSchemaManager;
+    private SqlStatisticManager sqlStatisticManager;
 
     @BeforeEach
     void init() {
+        sqlStatisticManager = tableId -> 10_000L;
+
         catalogManager = 
CatalogTestUtils.createCatalogManagerWithTestUpdateLog("test", new 
HybridClockImpl());
-        sqlSchemaManager = new SqlSchemaManagerImpl(catalogManager, 
CaffeineCacheFactory.INSTANCE, 200);
+        sqlSchemaManager = new SqlSchemaManagerImpl(catalogManager, 
sqlStatisticManager, CaffeineCacheFactory.INSTANCE, 200);
 
         assertThat(catalogManager.startAsync(new ComponentContext()), 
willCompleteSuccessfully());
     }
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
new file mode 100644
index 0000000000..636b882ec4
--- /dev/null
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/statistic/SqlStatisticManagerImplTest.java
@@ -0,0 +1,265 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.sql.engine.statistic;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ThreadLocalRandom;
+import org.apache.ignite.internal.catalog.CatalogManager;
+import org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor;
+import org.apache.ignite.internal.catalog.events.CatalogEvent;
+import org.apache.ignite.internal.catalog.events.CreateTableEventParameters;
+import org.apache.ignite.internal.catalog.events.DropTableEventParameters;
+import org.apache.ignite.internal.event.EventListener;
+import org.apache.ignite.internal.hlc.HybridTimestamp;
+import org.apache.ignite.internal.lowwatermark.LowWatermark;
+import 
org.apache.ignite.internal.lowwatermark.event.ChangeLowWatermarkEventParameters;
+import org.apache.ignite.internal.lowwatermark.event.LowWatermarkEvent;
+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.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+/**
+ * Tests of SqlStatisticManagerImpl.
+ */
+@ExtendWith(MockitoExtension.class)
+class SqlStatisticManagerImplTest extends BaseIgniteAbstractTest {
+    public static final long DEFAULT_TABLE_SIZE = 1_000_000L;
+    @Mock
+    private TableManager tableManager;
+
+    @Mock
+    private CatalogManager catalogManager;
+
+    @Mock
+    private TableViewInternal tableViewInternal;
+
+    @Mock
+    private InternalTable internalTable;
+
+    @Mock
+    private LowWatermark lowWatermark;
+
+    @Test
+    public void checkDefaultTableSize() {
+        int tableId = ThreadLocalRandom.current().nextInt();
+        // Preparing:
+        when(tableManager.cachedTable(tableId)).thenReturn(null);
+
+        SqlStatisticManagerImpl sqlStatisticManager = new 
SqlStatisticManagerImpl(tableManager, catalogManager, lowWatermark);
+        sqlStatisticManager.start();
+
+        // Test:
+        // return default value for unknown table.
+        assertEquals(DEFAULT_TABLE_SIZE, 
sqlStatisticManager.tableSize(tableId));
+    }
+
+    @Test
+    public void checkMinimumTableSize() {
+        int tableId = ThreadLocalRandom.current().nextInt();
+        prepareCatalogWithTable(tableId);
+
+        when(tableManager.cachedTable(tableId)).thenReturn(tableViewInternal);
+        when(tableViewInternal.internalTable()).thenReturn(internalTable);
+        
when(internalTable.estimatedSize()).thenReturn(CompletableFuture.completedFuture(10L));
+
+        SqlStatisticManagerImpl sqlStatisticManager = new 
SqlStatisticManagerImpl(tableManager, catalogManager, lowWatermark);
+        sqlStatisticManager.start();
+
+        // even table size is 10 it should return default minimum size
+        assertEquals(1_000L, sqlStatisticManager.tableSize(tableId));
+    }
+
+    @Test
+    public void checkTableSize() {
+        int tableId = ThreadLocalRandom.current().nextInt();
+        long tableSize = 999_888_777L;
+        // Preparing:
+        prepareCatalogWithTable(tableId);
+
+        when(tableManager.cachedTable(tableId)).thenReturn(tableViewInternal);
+        when(tableViewInternal.internalTable()).thenReturn(internalTable);
+        
when(internalTable.estimatedSize()).thenReturn(CompletableFuture.completedFuture(tableSize));
+
+        SqlStatisticManagerImpl sqlStatisticManager = new 
SqlStatisticManagerImpl(tableManager, catalogManager, lowWatermark);
+        sqlStatisticManager.start();
+
+        // Test:
+        assertEquals(tableSize, sqlStatisticManager.tableSize(tableId));
+        // The second time we should obtain the same value from a cache.
+        assertEquals(tableSize, sqlStatisticManager.tableSize(tableId));
+
+        verify(internalTable, times(1)).estimatedSize();
+    }
+
+    @Test
+    public void checkModifyTableSize() {
+        int tableId = ThreadLocalRandom.current().nextInt();
+        long tableSize1 = 999_888_777L;
+        long tableSize2 = 111_222_333L;
+        // Preparing:
+        prepareCatalogWithTable(tableId);
+
+        when(tableManager.cachedTable(tableId)).thenReturn(tableViewInternal);
+        when(tableViewInternal.internalTable()).thenReturn(internalTable);
+        when(internalTable.estimatedSize()).thenReturn(
+                CompletableFuture.completedFuture(tableSize1),
+                CompletableFuture.completedFuture(tableSize2));
+
+        SqlStatisticManagerImpl sqlStatisticManager = new 
SqlStatisticManagerImpl(tableManager, catalogManager, lowWatermark);
+        sqlStatisticManager.start();
+
+        // Test:
+        assertEquals(tableSize1, sqlStatisticManager.tableSize(tableId));
+        // The second time we should obtain the same value from a cache.
+        assertEquals(tableSize1, sqlStatisticManager.tableSize(tableId));
+
+        // Allow to refresh value.
+        sqlStatisticManager.setThresholdTimeToPostponeUpdateMs(0);
+        // Now we need obtain a fresh value of table size.
+        assertEquals(tableSize2, sqlStatisticManager.tableSize(tableId));
+
+        verify(internalTable, times(2)).estimatedSize();
+    }
+
+    @Test
+    public void checkLoadAllTablesOnStart() {
+        int minimumCatalogVersion = 1;
+        int maximumCatalogVersion = 10;
+
+        // Preparing:
+        
when(catalogManager.earliestCatalogVersion()).thenReturn(minimumCatalogVersion);
+        
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, ""));
+
+            
when(catalogManager.tables(catalogVersion)).thenReturn(catalogDescriptors);
+        }
+
+        when(tableManager.cachedTable(anyInt())).thenReturn(tableViewInternal);
+        when(tableViewInternal.internalTable()).thenReturn(internalTable);
+        
when(internalTable.estimatedSize()).thenReturn(CompletableFuture.completedFuture(99999L));
+
+        SqlStatisticManagerImpl sqlStatisticManager = new 
SqlStatisticManagerImpl(tableManager, catalogManager, lowWatermark);
+        sqlStatisticManager.start();
+
+        // Test:
+        // For known tables we got calculated table size.
+        for (int i = minimumCatalogVersion; i <= maximumCatalogVersion; i++) {
+            assertEquals(99999L, sqlStatisticManager.tableSize(i));
+            assertEquals(99999L, sqlStatisticManager.tableSize(i + 1));
+        }
+
+        // For unknown tables we got default size.
+        for (int i = maximumCatalogVersion + 2; i <= maximumCatalogVersion + 
10; i++) {
+            assertEquals(DEFAULT_TABLE_SIZE, sqlStatisticManager.tableSize(i));
+        }
+    }
+
+    @SuppressWarnings("PMD.UseNotifyAllInsteadOfNotify")
+    @Test
+    public void checkCleanupTableSizeCache() {
+        int tableId = ThreadLocalRandom.current().nextInt();
+        long tableSize = 999_888_777L;
+        // Preparing:
+        prepareCatalogWithTable(tableId);
+
+        ArgumentCaptor<EventListener<DropTableEventParameters>> 
tableDropCapture = ArgumentCaptor.forClass(EventListener.class);
+        doNothing().when(catalogManager).listen(eq(CatalogEvent.TABLE_DROP), 
tableDropCapture.capture());
+        doNothing().when(catalogManager).listen(eq(CatalogEvent.TABLE_CREATE), 
any());
+
+        ArgumentCaptor<EventListener<ChangeLowWatermarkEventParameters>> 
lowWatermarkCapture = ArgumentCaptor.forClass(EventListener.class);
+        
doNothing().when(lowWatermark).listen(eq(LowWatermarkEvent.LOW_WATERMARK_CHANGED),
 lowWatermarkCapture.capture());
+
+        when(tableManager.cachedTable(tableId)).thenReturn(tableViewInternal);
+        when(tableViewInternal.internalTable()).thenReturn(internalTable);
+        
when(internalTable.estimatedSize()).thenReturn(CompletableFuture.completedFuture(tableSize));
+
+        SqlStatisticManagerImpl sqlStatisticManager = new 
SqlStatisticManagerImpl(tableManager, catalogManager, lowWatermark);
+        sqlStatisticManager.start();
+
+        // Test:
+        assertEquals(tableSize, sqlStatisticManager.tableSize(tableId));
+
+        int catalogVersionBeforeDrop = 1;
+        int catalogVersionAfterDrop = 2;
+
+        // After table drop we still obtain the same value from a cache.
+        tableDropCapture.getValue().notify(new DropTableEventParameters(-1, 
catalogVersionAfterDrop, tableId));
+        assertEquals(tableSize, sqlStatisticManager.tableSize(tableId));
+
+        // After LWM has been changed data in case is removed and we get 
default value.
+        
when(catalogManager.activeCatalogVersion(catalogVersionBeforeDrop)).thenReturn(catalogVersionAfterDrop);
+        lowWatermarkCapture.getValue()
+                .notify(new 
ChangeLowWatermarkEventParameters(HybridTimestamp.hybridTimestamp(catalogVersionBeforeDrop)));
+
+        assertEquals(DEFAULT_TABLE_SIZE, 
sqlStatisticManager.tableSize(tableId));
+    }
+
+    @SuppressWarnings("PMD.UseNotifyAllInsteadOfNotify")
+    @Test
+    public void checkCacheForNewlyCreatedTable() {
+        int tableId = ThreadLocalRandom.current().nextInt();
+        long tableSize = 999_888_777L;
+        // Preparing:
+        ArgumentCaptor<EventListener<CreateTableEventParameters>> 
tableCreateCapture = ArgumentCaptor.forClass(EventListener.class);
+        doNothing().when(catalogManager).listen(eq(CatalogEvent.TABLE_DROP), 
any());
+        doNothing().when(catalogManager).listen(eq(CatalogEvent.TABLE_CREATE), 
tableCreateCapture.capture());
+
+        when(tableManager.cachedTable(tableId)).thenReturn(tableViewInternal);
+        when(tableViewInternal.internalTable()).thenReturn(internalTable);
+        
when(internalTable.estimatedSize()).thenReturn(CompletableFuture.completedFuture(tableSize));
+
+        SqlStatisticManagerImpl sqlStatisticManager = new 
SqlStatisticManagerImpl(tableManager, catalogManager, lowWatermark);
+        sqlStatisticManager.start();
+
+        // Test:
+        // 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, "");
+        tableCreateCapture.getValue().notify(new 
CreateTableEventParameters(-1, 0, catalogDescriptor));
+        // now table is created and size should be actual.
+        assertEquals(tableSize, sqlStatisticManager.tableSize(tableId));
+    }
+
+    private void prepareCatalogWithTable(int tableId) {
+        when(catalogManager.earliestCatalogVersion()).thenReturn(1);
+        when(catalogManager.latestCatalogVersion()).thenReturn(1);
+        CatalogTableDescriptor catalogDescriptor = new 
CatalogTableDescriptor(tableId, 1, 1, "", 1, List.of(), List.of(), null, "");
+        when(catalogManager.tables(1)).thenReturn(List.of(catalogDescriptor));
+    }
+}
diff --git 
a/modules/sql-engine/src/testFixtures/java/org/apache/ignite/internal/sql/engine/util/QueryChecker.java
 
b/modules/sql-engine/src/testFixtures/java/org/apache/ignite/internal/sql/engine/util/QueryChecker.java
index 96a1f6e1ee..82666590c4 100644
--- 
a/modules/sql-engine/src/testFixtures/java/org/apache/ignite/internal/sql/engine/util/QueryChecker.java
+++ 
b/modules/sql-engine/src/testFixtures/java/org/apache/ignite/internal/sql/engine/util/QueryChecker.java
@@ -59,6 +59,19 @@ public interface QueryChecker {
         return containsSubPlan("TableScan(table=[[" + schema + ", " + tblName 
+ "]]");
     }
 
+    /**
+     * Ignite Row count matcher.
+     *
+     * @param schema Schema name.
+     * @param sourceName Table or index name.
+     * @param rowCount expected row count.
+     * @return Matcher.
+     */
+    static Matcher<String> containsRowCount(String schema, String sourceName, 
long rowCount) {
+        return matchesOnce(".*(Table|Index)Scan\\(table=\\[\\[" + schema + ", 
" + sourceName
+                + "\\]\\].*\\]\\)\\:.*cumulative cost = IgniteCost 
\\[rowCount=" + rowCount + ".0");
+    }
+
     /**
      * Ignite index scan matcher.
      *
diff --git 
a/modules/system-view-api/src/main/java/org/apache/ignite/internal/systemview/api/SystemView.java
 
b/modules/system-view-api/src/main/java/org/apache/ignite/internal/systemview/api/SystemView.java
index 2516187a0d..44be1f55bc 100644
--- 
a/modules/system-view-api/src/main/java/org/apache/ignite/internal/systemview/api/SystemView.java
+++ 
b/modules/system-view-api/src/main/java/org/apache/ignite/internal/systemview/api/SystemView.java
@@ -49,7 +49,7 @@ import org.apache.ignite.internal.type.NativeType;
  *     <li>{@link java.time.LocalDate}</li>
  *     <li>{@link java.time.Instant}</li>
  *     <li>{@link java.util.UUID}</li>
- *     <li>TODO Add Interval type (java.time.Period, java.time.Duration) 
support https://issues.apache.org/jira/browse/IGNITE-15200</li>
+ *     <li>TODO Add Interval type (java.time.Period, java.time.Duration) 
support https://issues.apache.org/jira/browse/IGNITE-17373</li>
  * </ul>
  *
  * @param <T> System view data type.
diff --git 
a/modules/table/src/test/java/org/apache/ignite/internal/table/MutableRowTupleAdapterTest.java
 
b/modules/table/src/test/java/org/apache/ignite/internal/table/MutableRowTupleAdapterTest.java
index a31a53444e..09a1f16bff 100644
--- 
a/modules/table/src/test/java/org/apache/ignite/internal/table/MutableRowTupleAdapterTest.java
+++ 
b/modules/table/src/test/java/org/apache/ignite/internal/table/MutableRowTupleAdapterTest.java
@@ -478,7 +478,7 @@ public class MutableRowTupleAdapterTest extends 
AbstractMutableTupleTest {
             }
 
             if (columnType == ColumnType.PERIOD || columnType == 
ColumnType.DURATION) {
-                // TODO https://issues.apache.org/jira/browse/IGNITE-15200: 
Not supported yet.
+                // TODO https://issues.apache.org/jira/browse/IGNITE-17373: 
Not supported yet.
                 continue;
             }
 

Reply via email to