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;
}