This is an automated email from the ASF dual-hosted git repository.
lzljs3620320 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git
The following commit(s) were added to refs/heads/master by this push:
new cafb215ac [flink] Support analysis statistics in flink (#4280)
cafb215ac is described below
commit cafb215acb2398b78e2e5c0dca167e7c722275fd
Author: herefree <[email protected]>
AuthorDate: Thu Oct 10 15:09:59 2024 +0800
[flink] Support analysis statistics in flink (#4280)
---
.../java/org/apache/paimon/flink/FlinkCatalog.java | 52 +++-
.../apache/paimon/flink/utils/TableStatsUtil.java | 295 +++++++++++++++++++++
.../paimon/flink/FlinkAnalyzeTableITCase.java | 186 +++++++++++++
3 files changed, 529 insertions(+), 4 deletions(-)
diff --git
a/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/FlinkCatalog.java
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/FlinkCatalog.java
index 0f46f2965..72151ec96 100644
---
a/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/FlinkCatalog.java
+++
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/FlinkCatalog.java
@@ -24,15 +24,19 @@ import org.apache.paimon.catalog.Catalog;
import org.apache.paimon.catalog.Identifier;
import org.apache.paimon.flink.procedure.ProcedureUtil;
import org.apache.paimon.flink.utils.FlinkCatalogPropertiesUtil;
+import org.apache.paimon.flink.utils.TableStatsUtil;
import org.apache.paimon.fs.Path;
import org.apache.paimon.manifest.PartitionEntry;
+import org.apache.paimon.operation.FileStoreCommit;
import org.apache.paimon.options.Options;
import org.apache.paimon.schema.Schema;
import org.apache.paimon.schema.SchemaChange;
import org.apache.paimon.schema.SchemaManager;
+import org.apache.paimon.stats.Statistics;
import org.apache.paimon.table.FileStoreTable;
import org.apache.paimon.table.FormatTable;
import org.apache.paimon.table.Table;
+import org.apache.paimon.table.sink.BatchWriteBuilder;
import org.apache.paimon.table.source.ReadBuilder;
import org.apache.paimon.utils.FileStorePathFactory;
import org.apache.paimon.utils.InternalRowPartitionComputer;
@@ -1239,8 +1243,8 @@ public class FlinkCatalog extends AbstractCatalog {
@Override
public final void alterTableStatistics(
ObjectPath tablePath, CatalogTableStatistics tableStatistics,
boolean ignoreIfNotExists)
- throws CatalogException {
- throw new UnsupportedOperationException();
+ throws CatalogException, TableNotExistException {
+ alterTableStatisticsInternal(tablePath, tableStatistics,
ignoreIfNotExists);
}
@Override
@@ -1248,8 +1252,48 @@ public class FlinkCatalog extends AbstractCatalog {
ObjectPath tablePath,
CatalogColumnStatistics columnStatistics,
boolean ignoreIfNotExists)
- throws CatalogException {
- throw new UnsupportedOperationException();
+ throws CatalogException, TableNotExistException {
+ alterTableStatisticsInternal(tablePath, columnStatistics,
ignoreIfNotExists);
+ }
+
+ private void alterTableStatisticsInternal(
+ ObjectPath tablePath, Object statistics, boolean ignoreIfNotExists)
+ throws TableNotExistException {
+ try {
+ Table table = catalog.getTable(toIdentifier(tablePath));
+ checkArgument(
+ table instanceof FileStoreTable, "Now only support analyze
FileStoreTable.");
+ if (!table.latestSnapshotId().isPresent()) {
+ LOG.info("Skipping analyze table because the snapshot is
null.");
+ return;
+ }
+
+ Statistics tableStats = null;
+ if (statistics instanceof CatalogColumnStatistics) {
+ tableStats =
+ TableStatsUtil.createTableColumnStats(
+ ((FileStoreTable) table),
(CatalogColumnStatistics) statistics);
+ } else if (statistics instanceof CatalogTableStatistics) {
+ tableStats =
+ TableStatsUtil.createTableStats(
+ ((FileStoreTable) table),
(CatalogTableStatistics) statistics);
+ }
+
+ if (tableStats != null) {
+ FileStoreTable fileStoreTable = (FileStoreTable) table;
+ FileStoreCommit commit =
+ fileStoreTable
+ .store()
+ .newCommit(
+ CoreOptions.createCommitUser(
+
fileStoreTable.coreOptions().toConfiguration()));
+ commit.commitStatistics(tableStats,
BatchWriteBuilder.COMMIT_IDENTIFIER);
+ }
+ } catch (Catalog.TableNotExistException e) {
+ if (!ignoreIfNotExists) {
+ throw new TableNotExistException(getName(), tablePath);
+ }
+ }
}
@Override
diff --git
a/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/utils/TableStatsUtil.java
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/utils/TableStatsUtil.java
new file mode 100644
index 000000000..c0ed45d95
--- /dev/null
+++
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/utils/TableStatsUtil.java
@@ -0,0 +1,295 @@
+/*
+ * 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.paimon.flink.utils;
+
+import org.apache.paimon.Snapshot;
+import org.apache.paimon.data.Decimal;
+import org.apache.paimon.io.DataFileMeta;
+import org.apache.paimon.stats.ColStats;
+import org.apache.paimon.stats.Statistics;
+import org.apache.paimon.table.FileStoreTable;
+import org.apache.paimon.table.source.DataSplit;
+import org.apache.paimon.types.DataField;
+import org.apache.paimon.types.DataTypeRoot;
+import org.apache.paimon.utils.Preconditions;
+
+import org.apache.flink.table.catalog.exceptions.CatalogException;
+import org.apache.flink.table.catalog.stats.CatalogColumnStatistics;
+import org.apache.flink.table.catalog.stats.CatalogColumnStatisticsDataBase;
+import org.apache.flink.table.catalog.stats.CatalogColumnStatisticsDataBinary;
+import org.apache.flink.table.catalog.stats.CatalogColumnStatisticsDataBoolean;
+import org.apache.flink.table.catalog.stats.CatalogColumnStatisticsDataDate;
+import org.apache.flink.table.catalog.stats.CatalogColumnStatisticsDataDouble;
+import org.apache.flink.table.catalog.stats.CatalogColumnStatisticsDataLong;
+import org.apache.flink.table.catalog.stats.CatalogColumnStatisticsDataString;
+import org.apache.flink.table.catalog.stats.CatalogTableStatistics;
+
+import javax.annotation.Nullable;
+
+import java.math.BigDecimal;
+import java.sql.Timestamp;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/** Utility methods for analysis table. */
+public class TableStatsUtil {
+
+ /** create Paimon statistics. */
+ @Nullable
+ public static Statistics createTableStats(
+ FileStoreTable table, CatalogTableStatistics
catalogTableStatistics) {
+ Snapshot snapshot = table.snapshotManager().latestSnapshot();
+ long mergedRecordCount = catalogTableStatistics.getRowCount();
+ if (snapshot.totalRecordCount() == null) {
+ return null;
+ }
+ long totalRecordCount = snapshot.totalRecordCount();
+ Preconditions.checkState(
+ totalRecordCount >= mergedRecordCount,
+ "totalRecordCount: $totalRecordCount should be greater or
equal than mergedRecordCount: $mergedRecordCount.");
+ long totalSize =
+ table.newScan().plan().splits().stream()
+ .flatMap(split -> ((DataSplit)
split).dataFiles().stream())
+ .mapToLong(DataFileMeta::fileSize)
+ .sum();
+ long mergedRecordSize =
+ (long) (totalSize * ((double) mergedRecordCount /
totalRecordCount));
+
+ // convert to paimon stats
+ return new Statistics(
+ snapshot.id(), snapshot.schemaId(), mergedRecordCount,
mergedRecordSize);
+ }
+
+ /** Create Paimon statistics from given Flink columnStatistics. */
+ @Nullable
+ public static Statistics createTableColumnStats(
+ FileStoreTable table, CatalogColumnStatistics columnStatistics) {
+ if (!table.statistics().isPresent()) {
+ return null;
+ }
+ Statistics statistics = table.statistics().get();
+ List<DataField> fields = table.schema().fields();
+ Map<String, ColStats<?>> tableColumnStatsMap = new
HashMap<>(fields.size());
+ for (DataField field : fields) {
+ CatalogColumnStatisticsDataBase catalogColumnStatisticsDataBase =
+
columnStatistics.getColumnStatisticsData().get(field.name());
+ if (catalogColumnStatisticsDataBase == null) {
+ continue;
+ }
+ tableColumnStatsMap.put(
+ field.name(), getPaimonColStats(field,
catalogColumnStatisticsDataBase));
+ }
+ statistics.colStats().putAll(tableColumnStatsMap);
+ return statistics;
+ }
+
+ /** Convert Flink ColumnStats to Paimon ColStats according to Paimon
column type. */
+ private static ColStats<?> getPaimonColStats(
+ DataField field, CatalogColumnStatisticsDataBase colStat) {
+ DataTypeRoot typeRoot = field.type().getTypeRoot();
+ if (colStat instanceof CatalogColumnStatisticsDataString) {
+ CatalogColumnStatisticsDataString stringColStat =
+ (CatalogColumnStatisticsDataString) colStat;
+ if (typeRoot.equals(DataTypeRoot.CHAR) ||
typeRoot.equals(DataTypeRoot.VARCHAR)) {
+ return ColStats.newColStats(
+ field.id(),
+ null != stringColStat.getNdv() ?
stringColStat.getNdv() : null,
+ null,
+ null,
+ null != stringColStat.getNullCount() ?
stringColStat.getNullCount() : null,
+ null != stringColStat.getAvgLength()
+ ? stringColStat.getAvgLength().longValue()
+ : null,
+ null != stringColStat.getMaxLength() ?
stringColStat.getMaxLength() : null);
+ }
+ } else if (colStat instanceof CatalogColumnStatisticsDataBoolean) {
+ CatalogColumnStatisticsDataBoolean booleanColStat =
+ (CatalogColumnStatisticsDataBoolean) colStat;
+ if (typeRoot.equals(DataTypeRoot.BOOLEAN)) {
+ return ColStats.newColStats(
+ field.id(),
+ (booleanColStat.getFalseCount() > 0 ? 1L : 0)
+ + (booleanColStat.getTrueCount() > 0 ? 1L : 0),
+ null,
+ null,
+ booleanColStat.getNullCount(),
+ null,
+ null);
+ }
+ } else if (colStat instanceof CatalogColumnStatisticsDataLong) {
+ CatalogColumnStatisticsDataLong longColStat =
(CatalogColumnStatisticsDataLong) colStat;
+ if (typeRoot.equals(DataTypeRoot.INTEGER)) {
+ return ColStats.newColStats(
+ field.id(),
+ null != longColStat.getNdv() ? longColStat.getNdv() :
null,
+ null != longColStat.getMin() ?
longColStat.getMin().intValue() : null,
+ null != longColStat.getMax() ?
longColStat.getMax().intValue() : null,
+ null != longColStat.getNullCount() ?
longColStat.getNullCount() : null,
+ null,
+ null);
+ } else if (typeRoot.equals(DataTypeRoot.TINYINT)) {
+ return ColStats.newColStats(
+ field.id(),
+ null != longColStat.getNdv() ? longColStat.getNdv() :
null,
+ null != longColStat.getMin() ?
longColStat.getMin().byteValue() : null,
+ null != longColStat.getMax() ?
longColStat.getMax().byteValue() : null,
+ null != longColStat.getNullCount() ?
longColStat.getNullCount() : null,
+ null,
+ null);
+
+ } else if (typeRoot.equals(DataTypeRoot.SMALLINT)) {
+ return ColStats.newColStats(
+ field.id(),
+ null != longColStat.getNdv() ? longColStat.getNdv() :
null,
+ null != longColStat.getMin() ?
longColStat.getMin().shortValue() : null,
+ null != longColStat.getMax() ?
longColStat.getMax().shortValue() : null,
+ null != longColStat.getNullCount() ?
longColStat.getNullCount() : null,
+ null,
+ null);
+ } else if (typeRoot.equals(DataTypeRoot.BIGINT)) {
+ return ColStats.newColStats(
+ field.id(),
+ null != longColStat.getNdv() ? longColStat.getNdv() :
null,
+ null != longColStat.getMin() ? longColStat.getMin() :
null,
+ null != longColStat.getMax() ? longColStat.getMax() :
null,
+ null != longColStat.getNullCount() ?
longColStat.getNullCount() : null,
+ null,
+ null);
+ } else if
(typeRoot.equals(DataTypeRoot.TIMESTAMP_WITH_LOCAL_TIME_ZONE)) {
+ return ColStats.newColStats(
+ field.id(),
+ null != longColStat.getNdv() ? longColStat.getNdv() :
null,
+ null != longColStat.getMin()
+ ?
org.apache.paimon.data.Timestamp.fromSQLTimestamp(
+ new Timestamp(longColStat.getMin()))
+ : null,
+ null != longColStat.getMax()
+ ?
org.apache.paimon.data.Timestamp.fromSQLTimestamp(
+ new Timestamp(longColStat.getMax()))
+ : null,
+ null != longColStat.getNullCount() ?
longColStat.getNullCount() : null,
+ null,
+ null);
+ }
+ } else if (colStat instanceof CatalogColumnStatisticsDataDouble) {
+ CatalogColumnStatisticsDataDouble doubleColumnStatsData =
+ (CatalogColumnStatisticsDataDouble) colStat;
+ if (typeRoot.equals(DataTypeRoot.FLOAT)) {
+ return ColStats.newColStats(
+ field.id(),
+ null != doubleColumnStatsData.getNdv()
+ ? doubleColumnStatsData.getNdv()
+ : null,
+ null != doubleColumnStatsData.getMin()
+ ? doubleColumnStatsData.getMin().floatValue()
+ : null,
+ null != doubleColumnStatsData.getMax()
+ ? doubleColumnStatsData.getMax().floatValue()
+ : null,
+ null != doubleColumnStatsData.getNullCount()
+ ? doubleColumnStatsData.getNullCount()
+ : null,
+ null,
+ null);
+ } else if (typeRoot.equals(DataTypeRoot.DOUBLE)) {
+ return ColStats.newColStats(
+ field.id(),
+ null != doubleColumnStatsData.getNdv()
+ ? doubleColumnStatsData.getNdv()
+ : null,
+ null != doubleColumnStatsData.getMin()
+ ? doubleColumnStatsData.getMin()
+ : null,
+ null != doubleColumnStatsData.getMax()
+ ? doubleColumnStatsData.getMax()
+ : null,
+ null != doubleColumnStatsData.getNullCount()
+ ? doubleColumnStatsData.getNullCount()
+ : null,
+ null,
+ null);
+ } else if (typeRoot.equals(DataTypeRoot.DECIMAL)) {
+ BigDecimal max =
BigDecimal.valueOf(doubleColumnStatsData.getMax());
+ BigDecimal min =
BigDecimal.valueOf(doubleColumnStatsData.getMin());
+ return ColStats.newColStats(
+ field.id(),
+ null != doubleColumnStatsData.getNdv()
+ ? doubleColumnStatsData.getNdv()
+ : null,
+ null != doubleColumnStatsData.getMin()
+ ? Decimal.fromBigDecimal(min, min.precision(),
min.scale())
+ : null,
+ null != doubleColumnStatsData.getMax()
+ ? Decimal.fromBigDecimal(max, max.precision(),
max.scale())
+ : null,
+ null != doubleColumnStatsData.getNullCount()
+ ? doubleColumnStatsData.getNullCount()
+ : null,
+ null,
+ null);
+ }
+ } else if (colStat instanceof CatalogColumnStatisticsDataDate) {
+ CatalogColumnStatisticsDataDate dateColumnStatsData =
+ (CatalogColumnStatisticsDataDate) colStat;
+ if (typeRoot.equals(DataTypeRoot.DATE)) {
+ return ColStats.newColStats(
+ field.id(),
+ null != dateColumnStatsData.getNdv() ?
dateColumnStatsData.getNdv() : null,
+ null != dateColumnStatsData.getMin()
+ ? new
Long(dateColumnStatsData.getMin().getDaysSinceEpoch())
+ .intValue()
+ : null,
+ null != dateColumnStatsData.getMax()
+ ? new
Long(dateColumnStatsData.getMax().getDaysSinceEpoch())
+ .intValue()
+ : null,
+ null != dateColumnStatsData.getNullCount()
+ ? dateColumnStatsData.getNullCount()
+ : null,
+ null,
+ null);
+ }
+ } else if (colStat instanceof CatalogColumnStatisticsDataBinary) {
+ CatalogColumnStatisticsDataBinary binaryColumnStatsData =
+ (CatalogColumnStatisticsDataBinary) colStat;
+ if (typeRoot.equals(DataTypeRoot.VARBINARY) ||
typeRoot.equals(DataTypeRoot.BINARY)) {
+ return ColStats.newColStats(
+ field.id(),
+ null,
+ null,
+ null,
+ null != binaryColumnStatsData.getNullCount()
+ ? binaryColumnStatsData.getNullCount()
+ : null,
+ null != binaryColumnStatsData.getAvgLength()
+ ?
binaryColumnStatsData.getAvgLength().longValue()
+ : null,
+ null != binaryColumnStatsData.getMaxLength()
+ ? binaryColumnStatsData.getMaxLength()
+ : null);
+ }
+ }
+ throw new CatalogException(
+ String.format(
+ "Flink does not support convert ColumnStats '%s' for
Paimon column "
+ + "type '%s' yet",
+ colStat, field.type()));
+ }
+}
diff --git
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/FlinkAnalyzeTableITCase.java
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/FlinkAnalyzeTableITCase.java
new file mode 100644
index 000000000..ad8a2d45a
--- /dev/null
+++
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/FlinkAnalyzeTableITCase.java
@@ -0,0 +1,186 @@
+/*
+ * 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.paimon.flink;
+
+import org.apache.paimon.catalog.Catalog;
+import org.apache.paimon.data.Decimal;
+import org.apache.paimon.stats.ColStats;
+import org.apache.paimon.stats.Statistics;
+import org.apache.paimon.utils.DateTimeUtils;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Map;
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/** IT cases for analyze table. */
+public class FlinkAnalyzeTableITCase extends CatalogITCaseBase {
+
+ @Test
+ public void testAnalyzeTable() throws Catalog.TableNotExistException {
+ sql(
+ "CREATE TABLE T ("
+ + " id STRING"
+ + ", name STRING"
+ + ", i INT"
+ + ", l bigint"
+ + ", PRIMARY KEY (id) NOT ENFORCED"
+ + " ) WITH ("
+ + " 'bucket' = '2'"
+ + " )");
+ sql("INSERT INTO T VALUES ('1', 'a', 1, 1)");
+ sql("INSERT INTO T VALUES ('2', 'aaa', 1, 2)");
+ sql("ANALYZE TABLE T COMPUTE STATISTICS");
+
+ Optional<Statistics> statisticsOpt = paimonTable("T").statistics();
+ assertThat(statisticsOpt.isPresent()).isTrue();
+ Statistics stats = statisticsOpt.get();
+
+ assertThat(stats.mergedRecordCount().isPresent()).isTrue();
+ Assertions.assertEquals(2L, stats.mergedRecordCount().getAsLong());
+
+ Assertions.assertTrue(stats.mergedRecordSize().isPresent());
+ Assertions.assertTrue(stats.colStats().isEmpty());
+ }
+
+ @Test
+ public void testAnalyzeTableColumn() throws Catalog.TableNotExistException
{
+ sql(
+ "CREATE TABLE T ("
+ + "id STRING, name STRING, bytes_col BYTES, int_col
INT, long_col bigint,\n"
+ + "float_col FLOAT, double_col DOUBLE, decimal_col
DECIMAL(10, 5), boolean_col BOOLEAN, date_col DATE,\n"
+ + "timestamp_col TIMESTAMP_LTZ, binary_col BINARY,
varbinary_col VARBINARY, char_col CHAR(20), varchar_col VARCHAR(20),\n"
+ + "tinyint_col TINYINT, smallint_col SMALLINT"
+ + ", PRIMARY KEY (id) NOT ENFORCED"
+ + " ) WITH ("
+ + " 'bucket' = '2'"
+ + " )");
+ sql(
+ "INSERT INTO T VALUES ('1', 'a', CAST('your_binary_data' AS
BYTES), 1, 1, 1.0, 1.0, 13.12345, true, cast('2020-01-01' as date),
cast('2024-01-01 00:00:00' as TIMESTAMP_LTZ), CAST('example binary1' AS
BINARY), CAST('example binary1' AS VARBINARY), 'a', 'a',CAST(1 AS TINYINT),
CAST(2 AS SMALLINT))");
+ sql(
+ "INSERT INTO T VALUES ('2', 'aaa', CAST('your_binary_data' AS
BYTES), 1, 1, 1.0, 5.0, 12.12345, true, cast('2021-01-02' as date),
cast('2024-01-02 00:00:00' as TIMESTAMP_LTZ), CAST('example binary1' AS
BINARY), CAST('example binary1' AS VARBINARY), 'aaa', 'aaa', CAST(2 AS
TINYINT), CAST(4 AS SMALLINT))");
+
+ sql(
+ "INSERT INTO T VALUES ('3', 'bbbb', CAST('data' AS BYTES), 4,
19, 7.0, 1.0, 14.12345, true, cast(NULL as date), cast('2024-01-02 05:00:00' as
TIMESTAMP_LTZ), CAST(NULL AS BINARY), CAST('example binary1' AS VARBINARY),
'aaa', 'aaa', CAST(NULL AS TINYINT), CAST(4 AS SMALLINT))");
+
+ sql(
+ "INSERT INTO T VALUES ('4', 'aa', CAST(NULL AS BYTES), 1, 1,
1.0, 1.0, 14.12345, false, cast(NULL as date), cast(NULL as TIMESTAMP_LTZ),
CAST(NULL AS BINARY), CAST('example' AS VARBINARY), 'aba', 'aaab', CAST(NULL AS
TINYINT), CAST(4 AS SMALLINT))");
+
+ sql("ANALYZE TABLE T COMPUTE STATISTICS FOR ALL COLUMNS");
+
+ Optional<Statistics> statisticsOpt = paimonTable("T").statistics();
+ assertThat(statisticsOpt.isPresent()).isTrue();
+ Statistics stats = statisticsOpt.get();
+
+ assertThat(stats.mergedRecordCount().isPresent()).isTrue();
+ Assertions.assertEquals(4L, stats.mergedRecordCount().getAsLong());
+
+ Map<String, ColStats<?>> colStats = stats.colStats();
+ Assertions.assertEquals(
+ ColStats.newColStats(0, 4L, null, null, 0L, 1L, 1L),
colStats.get("id"));
+ Assertions.assertEquals(
+ ColStats.newColStats(1, 4L, null, null, 0L, 2L, 4L),
colStats.get("name"));
+
+ Assertions.assertEquals(
+ ColStats.newColStats(2, null, null, null, 1L, null, null),
+ colStats.get("bytes_col"));
+
+ Assertions.assertEquals(
+ ColStats.newColStats(3, 2L, 1, 4, 0L, null, null),
colStats.get("int_col"));
+
+ Assertions.assertEquals(
+ ColStats.newColStats(4, 2L, 1L, 19L, 0L, null, null),
colStats.get("long_col"));
+
+ Assertions.assertEquals(
+ ColStats.newColStats(5, 2L, 1.0f, 7.0f, 0L, null, null),
colStats.get("float_col"));
+
+ Assertions.assertEquals(
+ ColStats.newColStats(6, 2L, 1.0d, 5.0d, 0L, null, null),
+ colStats.get("double_col"));
+
+ Assertions.assertEquals(
+ ColStats.newColStats(
+ 7,
+ 3L,
+ Decimal.fromBigDecimal(new
java.math.BigDecimal("12.12345"), 10, 5),
+ Decimal.fromBigDecimal(new
java.math.BigDecimal("14.12345"), 10, 5),
+ 0L,
+ null,
+ null),
+ colStats.get("decimal_col"));
+
+ Assertions.assertEquals(
+ ColStats.newColStats(8, 2L, null, null, 0L, null, null),
+ colStats.get("boolean_col"));
+
+ Assertions.assertEquals(
+ ColStats.newColStats(9, 2L, 18262, 18629, 2L, null, null),
+ colStats.get("date_col"));
+
+ Assertions.assertEquals(
+ ColStats.newColStats(
+ 10,
+ 3L,
+ DateTimeUtils.parseTimestampData("2024-01-01
00:00:00", 0),
+ DateTimeUtils.parseTimestampData("2024-01-02
05:00:00", 0),
+ 1L,
+ null,
+ null),
+ colStats.get("timestamp_col"));
+
+ Assertions.assertEquals(
+ ColStats.newColStats(11, null, null, null, 2L, null, null),
+ colStats.get("binary_col"));
+
+ Assertions.assertEquals(
+ ColStats.newColStats(12, null, null, null, 0L, null, null),
+ colStats.get("varbinary_col"));
+
+ Assertions.assertEquals(
+ ColStats.newColStats(13, 3L, null, null, 0L, 20L, 20L),
colStats.get("char_col"));
+
+ Assertions.assertEquals(
+ ColStats.newColStats(14, 3L, null, null, 0L, 2L, 4L),
colStats.get("varchar_col"));
+
+ Assertions.assertEquals(
+ ColStats.newColStats(
+ 15,
+ 2L,
+ new Integer(1).byteValue(),
+ new Integer(2).byteValue(),
+ 2L,
+ null,
+ null),
+ colStats.get("tinyint_col"));
+
+ Assertions.assertEquals(
+ ColStats.newColStats(
+ 16,
+ 2L,
+ new Integer(2).shortValue(),
+ new Integer(4).shortValue(),
+ 0L,
+ null,
+ null),
+ colStats.get("smallint_col"));
+ }
+}