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 11636ac0f [core] Introduce metadata.stats-dense-store to reduce meta
size for multiple columns table (#4322)
11636ac0f is described below
commit 11636ac0f24e5722c4a8ea55ce39060593289e82
Author: Jingsong Lee <[email protected]>
AuthorDate: Tue Oct 15 12:17:35 2024 +0800
[core] Introduce metadata.stats-dense-store to reduce meta size for
multiple columns table (#4322)
---
docs/content/flink/sql-ddl.md | 3 +
.../shortcodes/generated/core_configuration.html | 6 +
.../main/java/org/apache/paimon/CoreOptions.java | 22 +-
.../org/apache/paimon/format/SimpleColStats.java | 6 +
.../statistics/NoneSimpleColStatsCollector.java | 4 +-
.../java/org/apache/paimon/table/SystemFields.java | 4 +
.../org/apache/paimon/utils/InternalRowUtils.java | 8 +-
.../org/apache/paimon/utils/ProjectedArray.java | 206 ++++++++++++++++
.../org/apache/paimon/append/AppendOnlyWriter.java | 8 +-
.../java/org/apache/paimon/io/DataFileMeta.java | 72 +++---
.../apache/paimon/io/DataFileMeta08Serializer.java | 1 +
...rializer.java => DataFileMeta09Serializer.java} | 57 +++--
.../apache/paimon/io/DataFileMetaSerializer.java | 6 +-
.../apache/paimon/io/KeyValueDataFileWriter.java | 15 +-
.../org/apache/paimon/io/RowDataFileWriter.java | 14 +-
.../apache/paimon/io/RowDataRollingFileWriter.java | 6 +-
.../org/apache/paimon/manifest/ManifestFile.java | 2 +-
.../org/apache/paimon/migrate/FileMetaUtils.java | 7 +-
.../paimon/operation/AppendOnlyFileStoreScan.java | 28 ++-
.../paimon/operation/AppendOnlyFileStoreWrite.java | 6 +-
.../paimon/operation/KeyValueFileStoreScan.java | 58 ++---
.../apache/paimon/stats/SimpleStatsConverter.java | 268 +++++----------------
...atsConverter.java => SimpleStatsEvolution.java} | 111 +++++----
...sConverters.java => SimpleStatsEvolutions.java} | 19 +-
.../org/apache/paimon/table/FileStoreTable.java | 6 -
.../sink/CommitMessageLegacyV2Serializer.java | 1 +
.../paimon/table/sink/CommitMessageSerializer.java | 30 ++-
.../org/apache/paimon/table/source/DataSplit.java | 6 +-
.../org/apache/paimon/table/system/FilesTable.java | 39 ++-
.../paimon/utils/StatsCollectorFactories.java | 9 +-
.../apache/paimon/append/AppendOnlyWriterTest.java | 16 +-
...nawareAppendTableCompactionCoordinatorTest.java | 3 +-
.../paimon/crosspartition/IndexBootstrapTest.java | 3 +-
.../apache/paimon/format/FileFormatSuffixTest.java | 3 +-
.../paimon/io/DataFileTestDataGenerator.java | 7 +-
.../org/apache/paimon/io/DataFileTestUtils.java | 9 +-
.../apache/paimon/io/RollingFileWriterTest.java | 20 +-
...festCommittableSerializerCompatibilityTest.java | 75 +++++-
.../ManifestCommittableSerializerTest.java | 3 +-
.../paimon/manifest/ManifestFileMetaTest.java | 4 +-
.../paimon/manifest/ManifestFileMetaTestBase.java | 6 +-
.../paimon/manifest/ManifestTestDataGenerator.java | 2 +-
.../org/apache/paimon/mergetree/LevelsTest.java | 3 +-
.../mergetree/compact/IntervalPartitionTest.java | 3 +-
.../mergetree/compact/UniversalCompactionTest.java | 2 +-
.../paimon/operation/ExpireSnapshotsTest.java | 3 +-
...rterTest.java => SimpleStatsEvolutionTest.java} | 16 +-
.../org/apache/paimon/stats/StatsTestUtils.java | 5 +-
.../paimon/table/AppendOnlyFileStoreTableTest.java | 25 +-
.../paimon/table/ColumnTypeFileMetaTestBase.java | 13 +-
.../paimon/table/FileMetaFilterTestBase.java | 46 ++--
.../paimon/table/PrimaryKeyFileStoreTableTest.java | 50 ++--
.../PrimaryKeyTableColumnTypeFileMetaTest.java | 13 +-
.../paimon/table/source/SplitGeneratorTest.java | 3 +-
.../org/apache/paimon/table/source/SplitTest.java | 115 ++++++++-
.../src/test/resources/compatibility/datasplit-v2 | Bin 0 -> 810 bytes
.../compatibility/manifest-committable-v3 | Bin 0 -> 2905 bytes
.../sink/CompactionTaskSimpleSerializerTest.java | 3 +-
.../source/FileStoreSourceSplitGeneratorTest.java | 3 +-
.../source/FileStoreSourceSplitSerializerTest.java | 3 +-
.../org/apache/paimon/spark/ScanHelperTest.scala | 27 ++-
61 files changed, 962 insertions(+), 550 deletions(-)
diff --git a/docs/content/flink/sql-ddl.md b/docs/content/flink/sql-ddl.md
index 363d74757..0324e6655 100644
--- a/docs/content/flink/sql-ddl.md
+++ b/docs/content/flink/sql-ddl.md
@@ -203,6 +203,9 @@ Paimon will automatically collect the statistics of the
data file for speeding u
The statistics collector mode can be configured by `'metadata.stats-mode'`, by
default is `'truncate(16)'`.
You can configure the field level by setting
`'fields.{field_name}.stats-mode'`.
+For the stats mode of `none`, we suggest that you configure
`metadata.stats-dense-store` = `true`, which will
+significantly reduce the storage size of the manifest.
+
### Field Default Value
Paimon table currently supports setting default values for fields in table
properties by `'fields.item_id.default-value'`,
diff --git a/docs/layouts/shortcodes/generated/core_configuration.html
b/docs/layouts/shortcodes/generated/core_configuration.html
index ed05e7e45..aeab93eda 100644
--- a/docs/layouts/shortcodes/generated/core_configuration.html
+++ b/docs/layouts/shortcodes/generated/core_configuration.html
@@ -471,6 +471,12 @@ Mainly to resolve data skew on primary keys. We recommend
starting with 64 mb wh
<td><p>Enum</p></td>
<td>Specify the merge engine for table with primary key.<br /><br
/>Possible values:<ul><li>"deduplicate": De-duplicate and keep the last
row.</li><li>"partial-update": Partial update non-null
fields.</li><li>"aggregation": Aggregate fields with same primary
key.</li><li>"first-row": De-duplicate and keep the first row.</li></ul></td>
</tr>
+ <tr>
+ <td><h5>metadata.stats-dense-store</h5></td>
+ <td style="word-wrap: break-word;">false</td>
+ <td>Boolean</td>
+ <td>Whether to store statistic densely in metadata (manifest
files), which will significantly reduce the storage size of metadata when the
none statistic mode is set.<br />Note, when this mode is enabled, the Paimon
sdk in reading engine requires at least version 0.9.1 or 1.0.0 or higher.</td>
+ </tr>
<tr>
<td><h5>metadata.stats-mode</h5></td>
<td style="word-wrap: break-word;">"truncate(16)"</td>
diff --git a/paimon-common/src/main/java/org/apache/paimon/CoreOptions.java
b/paimon-common/src/main/java/org/apache/paimon/CoreOptions.java
index 51aca09e8..1256c7ba0 100644
--- a/paimon-common/src/main/java/org/apache/paimon/CoreOptions.java
+++ b/paimon-common/src/main/java/org/apache/paimon/CoreOptions.java
@@ -1026,7 +1026,7 @@ public class CoreOptions implements Serializable {
public static final String STATS_MODE_SUFFIX = "stats-mode";
public static final ConfigOption<String> METADATA_STATS_MODE =
- key("metadata." + STATS_MODE_SUFFIX)
+ key("metadata.stats-mode")
.stringType()
.defaultValue("truncate(16)")
.withDescription(
@@ -1053,6 +1053,22 @@ public class CoreOptions implements Serializable {
+
STATS_MODE_SUFFIX))
.build());
+ public static final ConfigOption<Boolean> METADATA_STATS_DENSE_STORE =
+ key("metadata.stats-dense-store")
+ .booleanType()
+ .defaultValue(false)
+ .withDescription(
+ Description.builder()
+ .text(
+ "Whether to store statistic
densely in metadata (manifest files), which"
+ + " will significantly
reduce the storage size of metadata when the"
+ + " none statistic mode is
set.")
+ .linebreak()
+ .text(
+ "Note, when this mode is enabled,
the Paimon sdk in reading engine requires"
+ + " at least version 0.9.1
or 1.0.0 or higher.")
+ .build());
+
public static final ConfigOption<String> COMMIT_CALLBACKS =
key("commit.callbacks")
.stringType()
@@ -2233,6 +2249,10 @@ public class CoreOptions implements Serializable {
return options.get(ASYNC_FILE_WRITE);
}
+ public boolean statsDenseStore() {
+ return options.get(METADATA_STATS_DENSE_STORE);
+ }
+
/** Specifies the merge engine for table with primary key. */
public enum MergeEngine implements DescribedEnum {
DEDUPLICATE("deduplicate", "De-duplicate and keep the last row."),
diff --git
a/paimon-common/src/main/java/org/apache/paimon/format/SimpleColStats.java
b/paimon-common/src/main/java/org/apache/paimon/format/SimpleColStats.java
index 5d5891a8a..0b0062b75 100644
--- a/paimon-common/src/main/java/org/apache/paimon/format/SimpleColStats.java
+++ b/paimon-common/src/main/java/org/apache/paimon/format/SimpleColStats.java
@@ -33,6 +33,8 @@ import java.util.Objects;
*/
public class SimpleColStats {
+ public static final SimpleColStats NONE = new SimpleColStats(null, null,
null);
+
@Nullable private final Object min;
@Nullable private final Object max;
private final Long nullCount;
@@ -58,6 +60,10 @@ public class SimpleColStats {
return nullCount;
}
+ public boolean isNone() {
+ return min == null && max == null && nullCount == null;
+ }
+
@Override
public boolean equals(Object o) {
if (!(o instanceof SimpleColStats)) {
diff --git
a/paimon-common/src/main/java/org/apache/paimon/statistics/NoneSimpleColStatsCollector.java
b/paimon-common/src/main/java/org/apache/paimon/statistics/NoneSimpleColStatsCollector.java
index d4f645b49..57369c9cd 100644
---
a/paimon-common/src/main/java/org/apache/paimon/statistics/NoneSimpleColStatsCollector.java
+++
b/paimon-common/src/main/java/org/apache/paimon/statistics/NoneSimpleColStatsCollector.java
@@ -29,11 +29,11 @@ public class NoneSimpleColStatsCollector extends
AbstractSimpleColStatsCollector
@Override
public SimpleColStats result() {
- return new SimpleColStats(null, null, null);
+ return SimpleColStats.NONE;
}
@Override
public SimpleColStats convert(SimpleColStats source) {
- return new SimpleColStats(null, null, null);
+ return SimpleColStats.NONE;
}
}
diff --git
a/paimon-common/src/main/java/org/apache/paimon/table/SystemFields.java
b/paimon-common/src/main/java/org/apache/paimon/table/SystemFields.java
index 4ed212f36..155683981 100644
--- a/paimon-common/src/main/java/org/apache/paimon/table/SystemFields.java
+++ b/paimon-common/src/main/java/org/apache/paimon/table/SystemFields.java
@@ -55,4 +55,8 @@ public class SystemFields {
public static boolean isSystemField(int fieldId) {
return fieldId >= SYSTEM_FIELD_ID_START;
}
+
+ public static boolean isSystemField(String field) {
+ return field.startsWith(KEY_FIELD_PREFIX) ||
SYSTEM_FIELD_NAMES.contains(field);
+ }
}
diff --git
a/paimon-common/src/main/java/org/apache/paimon/utils/InternalRowUtils.java
b/paimon-common/src/main/java/org/apache/paimon/utils/InternalRowUtils.java
index 0d0383747..eaa077fa8 100644
--- a/paimon-common/src/main/java/org/apache/paimon/utils/InternalRowUtils.java
+++ b/paimon-common/src/main/java/org/apache/paimon/utils/InternalRowUtils.java
@@ -43,6 +43,8 @@ import org.apache.paimon.types.MultisetType;
import org.apache.paimon.types.RowType;
import org.apache.paimon.types.TimestampType;
+import javax.annotation.Nullable;
+
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
@@ -198,7 +200,11 @@ public class InternalRowUtils {
}
}
- public static InternalArray toStringArrayData(List<String> list) {
+ public static InternalArray toStringArrayData(@Nullable List<String> list)
{
+ if (list == null) {
+ return null;
+ }
+
return new
GenericArray(list.stream().map(BinaryString::fromString).toArray());
}
diff --git
a/paimon-common/src/main/java/org/apache/paimon/utils/ProjectedArray.java
b/paimon-common/src/main/java/org/apache/paimon/utils/ProjectedArray.java
new file mode 100644
index 000000000..2182ea1a3
--- /dev/null
+++ b/paimon-common/src/main/java/org/apache/paimon/utils/ProjectedArray.java
@@ -0,0 +1,206 @@
+/*
+ * 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.utils;
+
+import org.apache.paimon.data.BinaryString;
+import org.apache.paimon.data.Decimal;
+import org.apache.paimon.data.InternalArray;
+import org.apache.paimon.data.InternalMap;
+import org.apache.paimon.data.InternalRow;
+import org.apache.paimon.data.Timestamp;
+import org.apache.paimon.types.DataType;
+
+/**
+ * An implementation of {@link InternalArray} which provides a projected view
of the underlying
+ * {@link InternalArray}.
+ *
+ * <p>Projection includes both reducing the accessible fields and reordering
them.
+ *
+ * <p>Note: This class supports only top-level projections, not nested
projections.
+ */
+public class ProjectedArray implements InternalArray {
+
+ private final int[] indexMapping;
+
+ private InternalArray array;
+
+ private ProjectedArray(int[] indexMapping) {
+ this.indexMapping = indexMapping;
+ }
+
+ /**
+ * Replaces the underlying {@link InternalArray} backing this {@link
ProjectedArray}.
+ *
+ * <p>This method replaces the row data in place and does not return a new
object. This is done
+ * for performance reasons.
+ */
+ public ProjectedArray replaceArray(InternalArray array) {
+ this.array = array;
+ return this;
+ }
+
+ //
---------------------------------------------------------------------------------------------
+
+ @Override
+ public int size() {
+ return indexMapping.length;
+ }
+
+ @Override
+ public boolean isNullAt(int pos) {
+ if (indexMapping[pos] < 0) {
+ return true;
+ }
+ return array.isNullAt(indexMapping[pos]);
+ }
+
+ @Override
+ public boolean getBoolean(int pos) {
+ return array.getBoolean(indexMapping[pos]);
+ }
+
+ @Override
+ public byte getByte(int pos) {
+ return array.getByte(indexMapping[pos]);
+ }
+
+ @Override
+ public short getShort(int pos) {
+ return array.getShort(indexMapping[pos]);
+ }
+
+ @Override
+ public int getInt(int pos) {
+ return array.getInt(indexMapping[pos]);
+ }
+
+ @Override
+ public long getLong(int pos) {
+ return array.getLong(indexMapping[pos]);
+ }
+
+ @Override
+ public float getFloat(int pos) {
+ return array.getFloat(indexMapping[pos]);
+ }
+
+ @Override
+ public double getDouble(int pos) {
+ return array.getDouble(indexMapping[pos]);
+ }
+
+ @Override
+ public BinaryString getString(int pos) {
+ return array.getString(indexMapping[pos]);
+ }
+
+ @Override
+ public Decimal getDecimal(int pos, int precision, int scale) {
+ return array.getDecimal(indexMapping[pos], precision, scale);
+ }
+
+ @Override
+ public Timestamp getTimestamp(int pos, int precision) {
+ return array.getTimestamp(indexMapping[pos], precision);
+ }
+
+ @Override
+ public byte[] getBinary(int pos) {
+ return array.getBinary(indexMapping[pos]);
+ }
+
+ @Override
+ public InternalArray getArray(int pos) {
+ return array.getArray(indexMapping[pos]);
+ }
+
+ @Override
+ public InternalMap getMap(int pos) {
+ return array.getMap(indexMapping[pos]);
+ }
+
+ @Override
+ public InternalRow getRow(int pos, int numFields) {
+ return array.getRow(indexMapping[pos], numFields);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ throw new UnsupportedOperationException("Projected row data cannot be
compared");
+ }
+
+ @Override
+ public int hashCode() {
+ throw new UnsupportedOperationException("Projected row data cannot be
hashed");
+ }
+
+ @Override
+ public String toString() {
+ throw new UnsupportedOperationException("Projected row data cannot be
toString");
+ }
+
+ /**
+ * Create an empty {@link ProjectedArray} starting from a {@code
projection} array.
+ *
+ * <p>The array represents the mapping of the fields of the original
{@link DataType}. For
+ * example, {@code [0, 2, 1]} specifies to include in the following order
the 1st field, the 3rd
+ * field and the 2nd field of the row.
+ *
+ * @see Projection
+ * @see ProjectedArray
+ */
+ public static ProjectedArray from(int[] projection) {
+ return new ProjectedArray(projection);
+ }
+
+ @Override
+ public boolean[] toBooleanArray() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public byte[] toByteArray() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public short[] toShortArray() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int[] toIntArray() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long[] toLongArray() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public float[] toFloatArray() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public double[] toDoubleArray() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git
a/paimon-core/src/main/java/org/apache/paimon/append/AppendOnlyWriter.java
b/paimon-core/src/main/java/org/apache/paimon/append/AppendOnlyWriter.java
index 402c6c1f4..47502aa70 100644
--- a/paimon-core/src/main/java/org/apache/paimon/append/AppendOnlyWriter.java
+++ b/paimon-core/src/main/java/org/apache/paimon/append/AppendOnlyWriter.java
@@ -75,6 +75,7 @@ public class AppendOnlyWriter implements BatchRecordWriter,
MemoryOwner {
private final IOFunction<List<DataFileMeta>,
RecordReaderIterator<InternalRow>> bucketFileRead;
private final boolean forceCompact;
private final boolean asyncFileWrite;
+ private final boolean statsDenseStore;
private final List<DataFileMeta> newFiles;
private final List<DataFileMeta> deletedFiles;
private final List<DataFileMeta> compactBefore;
@@ -111,7 +112,8 @@ public class AppendOnlyWriter implements BatchRecordWriter,
MemoryOwner {
SimpleColStatsCollector.Factory[] statsCollectors,
MemorySize maxDiskSize,
FileIndexOptions fileIndexOptions,
- boolean asyncFileWrite) {
+ boolean asyncFileWrite,
+ boolean statsDenseStore) {
this.fileIO = fileIO;
this.schemaId = schemaId;
this.fileFormat = fileFormat;
@@ -122,6 +124,7 @@ public class AppendOnlyWriter implements BatchRecordWriter,
MemoryOwner {
this.bucketFileRead = bucketFileRead;
this.forceCompact = forceCompact;
this.asyncFileWrite = asyncFileWrite;
+ this.statsDenseStore = statsDenseStore;
this.newFiles = new ArrayList<>();
this.deletedFiles = new ArrayList<>();
this.compactBefore = new ArrayList<>();
@@ -286,7 +289,8 @@ public class AppendOnlyWriter implements BatchRecordWriter,
MemoryOwner {
statsCollectors,
fileIndexOptions,
FileSource.APPEND,
- asyncFileWrite);
+ asyncFileWrite,
+ statsDenseStore);
}
private void trySyncLatestCompaction(boolean blocking)
diff --git a/paimon-core/src/main/java/org/apache/paimon/io/DataFileMeta.java
b/paimon-core/src/main/java/org/apache/paimon/io/DataFileMeta.java
index 364d4d42e..bfda80db9 100644
--- a/paimon-core/src/main/java/org/apache/paimon/io/DataFileMeta.java
+++ b/paimon-core/src/main/java/org/apache/paimon/io/DataFileMeta.java
@@ -78,7 +78,11 @@ public class DataFileMeta {
new DataField(12, "_CREATION_TIME",
DataTypes.TIMESTAMP_MILLIS()),
new DataField(13, "_DELETE_ROW_COUNT", new
BigIntType(true)),
new DataField(14, "_EMBEDDED_FILE_INDEX",
newBytesType(true)),
- new DataField(15, "_FILE_SOURCE", new
TinyIntType(true))));
+ new DataField(15, "_FILE_SOURCE", new
TinyIntType(true)),
+ new DataField(
+ 16,
+ "_VALUE_STATS_COLS",
+
DataTypes.ARRAY(DataTypes.STRING().notNull()))));
public static final BinaryRow EMPTY_MIN_KEY = EMPTY_ROW;
public static final BinaryRow EMPTY_MAX_KEY = EMPTY_ROW;
@@ -114,27 +118,7 @@ public class DataFileMeta {
private final @Nullable FileSource fileSource;
- public static DataFileMeta forAppend(
- String fileName,
- long fileSize,
- long rowCount,
- SimpleStats rowStats,
- long minSequenceNumber,
- long maxSequenceNumber,
- long schemaId,
- @Nullable FileSource fileSource) {
- return forAppend(
- fileName,
- fileSize,
- rowCount,
- rowStats,
- minSequenceNumber,
- maxSequenceNumber,
- schemaId,
- Collections.emptyList(),
- null,
- fileSource);
- }
+ private final @Nullable List<String> valueStatsCols;
public static DataFileMeta forAppend(
String fileName,
@@ -146,7 +130,8 @@ public class DataFileMeta {
long schemaId,
List<String> extraFiles,
@Nullable byte[] embeddedIndex,
- @Nullable FileSource fileSource) {
+ @Nullable FileSource fileSource,
+ @Nullable List<String> valueStatsCols) {
return new DataFileMeta(
fileName,
fileSize,
@@ -163,7 +148,8 @@ public class DataFileMeta {
Timestamp.fromLocalDateTime(LocalDateTime.now()).toMillisTimestamp(),
0L,
embeddedIndex,
- fileSource);
+ fileSource,
+ valueStatsCols);
}
public DataFileMeta(
@@ -180,7 +166,8 @@ public class DataFileMeta {
int level,
@Nullable Long deleteRowCount,
@Nullable byte[] embeddedIndex,
- @Nullable FileSource fileSource) {
+ @Nullable FileSource fileSource,
+ @Nullable List<String> valueStatsCols) {
this(
fileName,
fileSize,
@@ -197,7 +184,8 @@ public class DataFileMeta {
Timestamp.fromLocalDateTime(LocalDateTime.now()).toMillisTimestamp(),
deleteRowCount,
embeddedIndex,
- fileSource);
+ fileSource,
+ valueStatsCols);
}
public DataFileMeta(
@@ -216,7 +204,8 @@ public class DataFileMeta {
Timestamp creationTime,
@Nullable Long deleteRowCount,
@Nullable byte[] embeddedIndex,
- @Nullable FileSource fileSource) {
+ @Nullable FileSource fileSource,
+ @Nullable List<String> valueStatsCols) {
this.fileName = fileName;
this.fileSize = fileSize;
@@ -237,6 +226,7 @@ public class DataFileMeta {
this.deleteRowCount = deleteRowCount;
this.fileSource = fileSource;
+ this.valueStatsCols = valueStatsCols;
}
public String fileName() {
@@ -334,6 +324,11 @@ public class DataFileMeta {
return Optional.ofNullable(fileSource);
}
+ @Nullable
+ public List<String> valueStatsCols() {
+ return valueStatsCols;
+ }
+
public DataFileMeta upgrade(int newLevel) {
checkArgument(newLevel > this.level);
return new DataFileMeta(
@@ -352,7 +347,8 @@ public class DataFileMeta {
creationTime,
deleteRowCount,
embeddedIndex,
- fileSource);
+ fileSource,
+ valueStatsCols);
}
public DataFileMeta rename(String newFileName) {
@@ -372,7 +368,8 @@ public class DataFileMeta {
creationTime,
deleteRowCount,
embeddedIndex,
- fileSource);
+ fileSource,
+ valueStatsCols);
}
public List<Path> collectFiles(DataFilePathFactory pathFactory) {
@@ -399,7 +396,8 @@ public class DataFileMeta {
creationTime,
deleteRowCount,
embeddedIndex,
- fileSource);
+ fileSource,
+ valueStatsCols);
}
public DataFileMeta copy(byte[] newEmbeddedIndex) {
@@ -419,7 +417,8 @@ public class DataFileMeta {
creationTime,
deleteRowCount,
newEmbeddedIndex,
- fileSource);
+ fileSource,
+ valueStatsCols);
}
@Override
@@ -446,7 +445,8 @@ public class DataFileMeta {
&& Objects.equals(extraFiles, that.extraFiles)
&& Objects.equals(creationTime, that.creationTime)
&& Objects.equals(deleteRowCount, that.deleteRowCount)
- && Objects.equals(fileSource, that.fileSource);
+ && Objects.equals(fileSource, that.fileSource)
+ && Objects.equals(valueStatsCols, that.valueStatsCols);
}
@Override
@@ -467,7 +467,8 @@ public class DataFileMeta {
extraFiles,
creationTime,
deleteRowCount,
- fileSource);
+ fileSource,
+ valueStatsCols);
}
@Override
@@ -477,7 +478,7 @@ public class DataFileMeta {
+ "minKey: %s, maxKey: %s, keyStats: %s, valueStats:
%s, "
+ "minSequenceNumber: %d, maxSequenceNumber: %d, "
+ "schemaId: %d, level: %d, extraFiles: %s,
creationTime: %s, "
- + "deleteRowCount: %d, fileSource: %s}",
+ + "deleteRowCount: %d, fileSource: %s, valueStatsCols:
%s}",
fileName,
fileSize,
rowCount,
@@ -493,7 +494,8 @@ public class DataFileMeta {
extraFiles,
creationTime,
deleteRowCount,
- fileSource);
+ fileSource,
+ valueStatsCols);
}
public static long getMaxSequenceNumber(List<DataFileMeta> fileMetas) {
diff --git
a/paimon-core/src/main/java/org/apache/paimon/io/DataFileMeta08Serializer.java
b/paimon-core/src/main/java/org/apache/paimon/io/DataFileMeta08Serializer.java
index c65f7e78e..03e4ed51f 100644
---
a/paimon-core/src/main/java/org/apache/paimon/io/DataFileMeta08Serializer.java
+++
b/paimon-core/src/main/java/org/apache/paimon/io/DataFileMeta08Serializer.java
@@ -132,6 +132,7 @@ public class DataFileMeta08Serializer implements
Serializable {
row.getTimestamp(12, 3),
row.isNullAt(13) ? null : row.getLong(13),
row.isNullAt(14) ? null : row.getBinary(14),
+ null,
null);
}
}
diff --git
a/paimon-core/src/main/java/org/apache/paimon/io/DataFileMeta08Serializer.java
b/paimon-core/src/main/java/org/apache/paimon/io/DataFileMeta09Serializer.java
similarity index 68%
copy from
paimon-core/src/main/java/org/apache/paimon/io/DataFileMeta08Serializer.java
copy to
paimon-core/src/main/java/org/apache/paimon/io/DataFileMeta09Serializer.java
index c65f7e78e..2f8d89f5b 100644
---
a/paimon-core/src/main/java/org/apache/paimon/io/DataFileMeta08Serializer.java
+++
b/paimon-core/src/main/java/org/apache/paimon/io/DataFileMeta09Serializer.java
@@ -23,6 +23,7 @@ import org.apache.paimon.data.GenericRow;
import org.apache.paimon.data.safe.SafeBinaryRow;
import org.apache.paimon.data.serializer.InternalRowSerializer;
import org.apache.paimon.data.serializer.InternalSerializers;
+import org.apache.paimon.manifest.FileSource;
import org.apache.paimon.stats.SimpleStats;
import org.apache.paimon.types.ArrayType;
import org.apache.paimon.types.BigIntType;
@@ -30,10 +31,12 @@ import org.apache.paimon.types.DataField;
import org.apache.paimon.types.DataTypes;
import org.apache.paimon.types.IntType;
import org.apache.paimon.types.RowType;
+import org.apache.paimon.types.TinyIntType;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import static org.apache.paimon.utils.InternalRowUtils.fromStringArrayData;
@@ -43,35 +46,37 @@ import static
org.apache.paimon.utils.SerializationUtils.newBytesType;
import static org.apache.paimon.utils.SerializationUtils.newStringType;
import static org.apache.paimon.utils.SerializationUtils.serializeBinaryRow;
-/** Serializer for {@link DataFileMeta} with safe deserializer. */
-public class DataFileMeta08Serializer implements Serializable {
+/** Serializer for {@link DataFileMeta} with 0.9 version. */
+public class DataFileMeta09Serializer implements Serializable {
private static final long serialVersionUID = 1L;
- protected final InternalRowSerializer rowSerializer;
+ public static final RowType SCHEMA =
+ new RowType(
+ false,
+ Arrays.asList(
+ new DataField(0, "_FILE_NAME",
newStringType(false)),
+ new DataField(1, "_FILE_SIZE", new
BigIntType(false)),
+ new DataField(2, "_ROW_COUNT", new
BigIntType(false)),
+ new DataField(3, "_MIN_KEY", newBytesType(false)),
+ new DataField(4, "_MAX_KEY", newBytesType(false)),
+ new DataField(5, "_KEY_STATS", SimpleStats.SCHEMA),
+ new DataField(6, "_VALUE_STATS",
SimpleStats.SCHEMA),
+ new DataField(7, "_MIN_SEQUENCE_NUMBER", new
BigIntType(false)),
+ new DataField(8, "_MAX_SEQUENCE_NUMBER", new
BigIntType(false)),
+ new DataField(9, "_SCHEMA_ID", new
BigIntType(false)),
+ new DataField(10, "_LEVEL", new IntType(false)),
+ new DataField(
+ 11, "_EXTRA_FILES", new ArrayType(false,
newStringType(false))),
+ new DataField(12, "_CREATION_TIME",
DataTypes.TIMESTAMP_MILLIS()),
+ new DataField(13, "_DELETE_ROW_COUNT", new
BigIntType(true)),
+ new DataField(14, "_EMBEDDED_FILE_INDEX",
newBytesType(true)),
+ new DataField(15, "_FILE_SOURCE", new
TinyIntType(true))));
- public DataFileMeta08Serializer() {
- this.rowSerializer = InternalSerializers.create(schemaFor08());
- }
+ protected final InternalRowSerializer rowSerializer;
- private static RowType schemaFor08() {
- List<DataField> fields = new ArrayList<>();
- fields.add(new DataField(0, "_FILE_NAME", newStringType(false)));
- fields.add(new DataField(1, "_FILE_SIZE", new BigIntType(false)));
- fields.add(new DataField(2, "_ROW_COUNT", new BigIntType(false)));
- fields.add(new DataField(3, "_MIN_KEY", newBytesType(false)));
- fields.add(new DataField(4, "_MAX_KEY", newBytesType(false)));
- fields.add(new DataField(5, "_KEY_STATS", SimpleStats.SCHEMA));
- fields.add(new DataField(6, "_VALUE_STATS", SimpleStats.SCHEMA));
- fields.add(new DataField(7, "_MIN_SEQUENCE_NUMBER", new
BigIntType(false)));
- fields.add(new DataField(8, "_MAX_SEQUENCE_NUMBER", new
BigIntType(false)));
- fields.add(new DataField(9, "_SCHEMA_ID", new BigIntType(false)));
- fields.add(new DataField(10, "_LEVEL", new IntType(false)));
- fields.add(new DataField(11, "_EXTRA_FILES", new ArrayType(false,
newStringType(false))));
- fields.add(new DataField(12, "_CREATION_TIME",
DataTypes.TIMESTAMP_MILLIS()));
- fields.add(new DataField(13, "_DELETE_ROW_COUNT", new
BigIntType(true)));
- fields.add(new DataField(14, "_EMBEDDED_FILE_INDEX",
newBytesType(true)));
- return new RowType(fields);
+ public DataFileMeta09Serializer() {
+ this.rowSerializer = InternalSerializers.create(SCHEMA);
}
public final void serializeList(List<DataFileMeta> records, DataOutputView
target)
@@ -99,7 +104,8 @@ public class DataFileMeta08Serializer implements
Serializable {
toStringArrayData(meta.extraFiles()),
meta.creationTime(),
meta.deleteRowCount().orElse(null),
- meta.embeddedIndex());
+ meta.embeddedIndex(),
+
meta.fileSource().map(FileSource::toByteValue).orElse(null));
rowSerializer.serialize(row, target);
}
@@ -132,6 +138,7 @@ public class DataFileMeta08Serializer implements
Serializable {
row.getTimestamp(12, 3),
row.isNullAt(13) ? null : row.getLong(13),
row.isNullAt(14) ? null : row.getBinary(14),
+ row.isNullAt(15) ? null :
FileSource.fromByteValue(row.getByte(15)),
null);
}
}
diff --git
a/paimon-core/src/main/java/org/apache/paimon/io/DataFileMetaSerializer.java
b/paimon-core/src/main/java/org/apache/paimon/io/DataFileMetaSerializer.java
index 209aaafd8..626201ca3 100644
--- a/paimon-core/src/main/java/org/apache/paimon/io/DataFileMetaSerializer.java
+++ b/paimon-core/src/main/java/org/apache/paimon/io/DataFileMetaSerializer.java
@@ -57,7 +57,8 @@ public class DataFileMetaSerializer extends
ObjectSerializer<DataFileMeta> {
meta.creationTime(),
meta.deleteRowCount().orElse(null),
meta.embeddedIndex(),
- meta.fileSource().map(FileSource::toByteValue).orElse(null));
+ meta.fileSource().map(FileSource::toByteValue).orElse(null),
+ toStringArrayData(meta.valueStatsCols()));
}
@Override
@@ -78,6 +79,7 @@ public class DataFileMetaSerializer extends
ObjectSerializer<DataFileMeta> {
row.getTimestamp(12, 3),
row.isNullAt(13) ? null : row.getLong(13),
row.isNullAt(14) ? null : row.getBinary(14),
- row.isNullAt(15) ? null :
FileSource.fromByteValue(row.getByte(15)));
+ row.isNullAt(15) ? null :
FileSource.fromByteValue(row.getByte(15)),
+ row.isNullAt(16) ? null :
fromStringArrayData(row.getArray(16)));
}
}
diff --git
a/paimon-core/src/main/java/org/apache/paimon/io/KeyValueDataFileWriter.java
b/paimon-core/src/main/java/org/apache/paimon/io/KeyValueDataFileWriter.java
index 064d72829..ba42f8720 100644
--- a/paimon-core/src/main/java/org/apache/paimon/io/KeyValueDataFileWriter.java
+++ b/paimon-core/src/main/java/org/apache/paimon/io/KeyValueDataFileWriter.java
@@ -32,6 +32,7 @@ import org.apache.paimon.manifest.FileSource;
import org.apache.paimon.stats.SimpleStats;
import org.apache.paimon.stats.SimpleStatsConverter;
import org.apache.paimon.types.RowType;
+import org.apache.paimon.utils.Pair;
import org.apache.paimon.utils.StatsCollectorFactories;
import org.slf4j.Logger;
@@ -41,6 +42,7 @@ import javax.annotation.Nullable;
import java.io.IOException;
import java.util.Arrays;
+import java.util.List;
import java.util.function.Function;
/**
@@ -102,7 +104,7 @@ public class KeyValueDataFileWriter
this.level = level;
this.keyStatsConverter = new SimpleStatsConverter(keyType);
- this.valueStatsConverter = new SimpleStatsConverter(valueType);
+ this.valueStatsConverter = new SimpleStatsConverter(valueType,
options.statsDenseStore());
this.keySerializer = new InternalRowSerializer(keyType);
this.fileSource = fileSource;
}
@@ -155,11 +157,13 @@ public class KeyValueDataFileWriter
int numKeyFields = keyType.getFieldCount();
SimpleColStats[] keyFieldStats = Arrays.copyOfRange(rowStats, 0,
numKeyFields);
- SimpleStats keyStats = keyStatsConverter.toBinary(keyFieldStats);
+ SimpleStats keyStats =
keyStatsConverter.toBinaryAllMode(keyFieldStats);
SimpleColStats[] valFieldStats =
Arrays.copyOfRange(rowStats, numKeyFields + 2,
rowStats.length);
- SimpleStats valueStats = valueStatsConverter.toBinary(valFieldStats);
+
+ Pair<List<String>, SimpleStats> valueStatsPair =
+ valueStatsConverter.toBinary(valFieldStats);
return new DataFileMeta(
path.getName(),
@@ -168,7 +172,7 @@ public class KeyValueDataFileWriter
minKey,
keySerializer.toBinaryRow(maxKey).copy(),
keyStats,
- valueStats,
+ valueStatsPair.getValue(),
minSeqNumber,
maxSeqNumber,
schemaId,
@@ -176,6 +180,7 @@ public class KeyValueDataFileWriter
deleteRecordCount,
// TODO: enable file filter for primary key table (e.g.
deletion table).
null,
- fileSource);
+ fileSource,
+ valueStatsPair.getKey());
}
}
diff --git
a/paimon-core/src/main/java/org/apache/paimon/io/RowDataFileWriter.java
b/paimon-core/src/main/java/org/apache/paimon/io/RowDataFileWriter.java
index f9c9b9502..8c2e8ec94 100644
--- a/paimon-core/src/main/java/org/apache/paimon/io/RowDataFileWriter.java
+++ b/paimon-core/src/main/java/org/apache/paimon/io/RowDataFileWriter.java
@@ -30,11 +30,13 @@ import org.apache.paimon.stats.SimpleStats;
import org.apache.paimon.stats.SimpleStatsConverter;
import org.apache.paimon.types.RowType;
import org.apache.paimon.utils.LongCounter;
+import org.apache.paimon.utils.Pair;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.Collections;
+import java.util.List;
import java.util.function.Function;
import static org.apache.paimon.io.DataFilePathFactory.dataFileToFileIndexPath;
@@ -63,7 +65,8 @@ public class RowDataFileWriter extends
StatsCollectingSingleFileWriter<InternalR
SimpleColStatsCollector.Factory[] statsCollectors,
FileIndexOptions fileIndexOptions,
FileSource fileSource,
- boolean asyncFileWrite) {
+ boolean asyncFileWrite,
+ boolean statsDenseStore) {
super(
fileIO,
factory,
@@ -76,7 +79,7 @@ public class RowDataFileWriter extends
StatsCollectingSingleFileWriter<InternalR
asyncFileWrite);
this.schemaId = schemaId;
this.seqNumCounter = seqNumCounter;
- this.statsArraySerializer = new SimpleStatsConverter(writeSchema);
+ this.statsArraySerializer = new SimpleStatsConverter(writeSchema,
statsDenseStore);
this.dataFileIndexWriter =
DataFileIndexWriter.create(
fileIO, dataFileToFileIndexPath(path), writeSchema,
fileIndexOptions);
@@ -103,7 +106,7 @@ public class RowDataFileWriter extends
StatsCollectingSingleFileWriter<InternalR
@Override
public DataFileMeta result() throws IOException {
- SimpleStats stats = statsArraySerializer.toBinary(fieldStats());
+ Pair<List<String>, SimpleStats> statsPair =
statsArraySerializer.toBinary(fieldStats());
DataFileIndexWriter.FileIndexResult indexResult =
dataFileIndexWriter == null
? DataFileIndexWriter.EMPTY_RESULT
@@ -112,7 +115,7 @@ public class RowDataFileWriter extends
StatsCollectingSingleFileWriter<InternalR
path.getName(),
fileIO.getFileSize(path),
recordCount(),
- stats,
+ statsPair.getRight(),
seqNumCounter.getValue() - super.recordCount(),
seqNumCounter.getValue() - 1,
schemaId,
@@ -120,6 +123,7 @@ public class RowDataFileWriter extends
StatsCollectingSingleFileWriter<InternalR
? Collections.emptyList()
:
Collections.singletonList(indexResult.independentIndexFile()),
indexResult.embeddedIndexBytes(),
- fileSource);
+ fileSource,
+ statsPair.getKey());
}
}
diff --git
a/paimon-core/src/main/java/org/apache/paimon/io/RowDataRollingFileWriter.java
b/paimon-core/src/main/java/org/apache/paimon/io/RowDataRollingFileWriter.java
index e60913d25..b929a4ae2 100644
---
a/paimon-core/src/main/java/org/apache/paimon/io/RowDataRollingFileWriter.java
+++
b/paimon-core/src/main/java/org/apache/paimon/io/RowDataRollingFileWriter.java
@@ -43,7 +43,8 @@ public class RowDataRollingFileWriter extends
RollingFileWriter<InternalRow, Dat
SimpleColStatsCollector.Factory[] statsCollectors,
FileIndexOptions fileIndexOptions,
FileSource fileSource,
- boolean asyncFileWrite) {
+ boolean asyncFileWrite,
+ boolean statsDenseStore) {
super(
() ->
new RowDataFileWriter(
@@ -62,7 +63,8 @@ public class RowDataRollingFileWriter extends
RollingFileWriter<InternalRow, Dat
statsCollectors,
fileIndexOptions,
fileSource,
- asyncFileWrite),
+ asyncFileWrite,
+ statsDenseStore),
targetFileSize);
}
}
diff --git
a/paimon-core/src/main/java/org/apache/paimon/manifest/ManifestFile.java
b/paimon-core/src/main/java/org/apache/paimon/manifest/ManifestFile.java
index 48216b0c4..38181a823 100644
--- a/paimon-core/src/main/java/org/apache/paimon/manifest/ManifestFile.java
+++ b/paimon-core/src/main/java/org/apache/paimon/manifest/ManifestFile.java
@@ -154,7 +154,7 @@ public class ManifestFile extends
ObjectsFile<ManifestEntry> {
fileIO.getFileSize(path),
numAddedFiles,
numDeletedFiles,
-
partitionStatsSerializer.toBinary(partitionStatsCollector.extract()),
+
partitionStatsSerializer.toBinaryAllMode(partitionStatsCollector.extract()),
numAddedFiles + numDeletedFiles > 0
? schemaId
: schemaManager.latest().get().id());
diff --git
a/paimon-core/src/main/java/org/apache/paimon/migrate/FileMetaUtils.java
b/paimon-core/src/main/java/org/apache/paimon/migrate/FileMetaUtils.java
index 4c299bd60..391c5f9bb 100644
--- a/paimon-core/src/main/java/org/apache/paimon/migrate/FileMetaUtils.java
+++ b/paimon-core/src/main/java/org/apache/paimon/migrate/FileMetaUtils.java
@@ -156,7 +156,7 @@ public class FileMetaUtils {
Pair<SimpleColStats[], SimpleStatsExtractor.FileInfo> fileInfo =
simpleStatsExtractor.extractWithFileInfo(fileIO, path);
- SimpleStats stats = statsArraySerializer.toBinary(fileInfo.getLeft());
+ SimpleStats stats =
statsArraySerializer.toBinaryAllMode(fileInfo.getLeft());
return DataFileMeta.forAppend(
fileName,
@@ -166,7 +166,10 @@ public class FileMetaUtils {
0,
0,
((FileStoreTable) table).schema().id(),
- FileSource.APPEND);
+ Collections.emptyList(),
+ null,
+ FileSource.APPEND,
+ null);
}
public static BinaryRow writePartitionValue(
diff --git
a/paimon-core/src/main/java/org/apache/paimon/operation/AppendOnlyFileStoreScan.java
b/paimon-core/src/main/java/org/apache/paimon/operation/AppendOnlyFileStoreScan.java
index a3bc9c22d..50a8c74dc 100644
---
a/paimon-core/src/main/java/org/apache/paimon/operation/AppendOnlyFileStoreScan.java
+++
b/paimon-core/src/main/java/org/apache/paimon/operation/AppendOnlyFileStoreScan.java
@@ -25,9 +25,8 @@ import org.apache.paimon.manifest.ManifestFile;
import org.apache.paimon.predicate.Predicate;
import org.apache.paimon.schema.SchemaManager;
import org.apache.paimon.schema.TableSchema;
-import org.apache.paimon.stats.SimpleStats;
-import org.apache.paimon.stats.SimpleStatsConverter;
-import org.apache.paimon.stats.SimpleStatsConverters;
+import org.apache.paimon.stats.SimpleStatsEvolution;
+import org.apache.paimon.stats.SimpleStatsEvolutions;
import org.apache.paimon.types.RowType;
import org.apache.paimon.utils.SnapshotManager;
@@ -42,7 +41,7 @@ import java.util.Map;
public class AppendOnlyFileStoreScan extends AbstractFileStoreScan {
private final BucketSelectConverter bucketSelectConverter;
- private final SimpleStatsConverters simpleStatsConverters;
+ private final SimpleStatsEvolutions simpleStatsEvolutions;
private final boolean fileIndexReadEnabled;
@@ -68,8 +67,8 @@ public class AppendOnlyFileStoreScan extends
AbstractFileStoreScan {
manifestFileFactory,
scanManifestParallelism);
this.bucketSelectConverter = bucketSelectConverter;
- this.simpleStatsConverters =
- new SimpleStatsConverters(sid ->
scanTableSchema(sid).fields(), schema.id());
+ this.simpleStatsEvolutions =
+ new SimpleStatsEvolutions(sid ->
scanTableSchema(sid).fields(), schema.id());
this.fileIndexReadEnabled = fileIndexReadEnabled;
}
@@ -86,15 +85,18 @@ public class AppendOnlyFileStoreScan extends
AbstractFileStoreScan {
return true;
}
- SimpleStatsConverter serializer =
- simpleStatsConverters.getOrCreate(entry.file().schemaId());
- SimpleStats stats = entry.file().valueStats();
+ SimpleStatsEvolution evolution =
simpleStatsEvolutions.getOrCreate(entry.file().schemaId());
+ SimpleStatsEvolution.Result stats =
+ evolution.evolution(
+ entry.file().valueStats(),
+ entry.file().rowCount(),
+ entry.file().valueStatsCols());
return filter.test(
entry.file().rowCount(),
- serializer.evolution(stats.minValues()),
- serializer.evolution(stats.maxValues()),
- serializer.evolution(stats.nullCounts(),
entry.file().rowCount()))
+ stats.minValues(),
+ stats.maxValues(),
+ stats.nullCounts())
&& (!fileIndexReadEnabled ||
testFileIndex(entry.file().embeddedIndex(), entry));
}
@@ -114,7 +116,7 @@ public class AppendOnlyFileStoreScan extends
AbstractFileStoreScan {
Predicate dataPredicate =
dataFilterMapping.computeIfAbsent(
entry.file().schemaId(),
- id ->
simpleStatsConverters.convertFilter(entry.file().schemaId(), filter));
+ id ->
simpleStatsEvolutions.convertFilter(entry.file().schemaId(), filter));
try (FileIndexPredicate predicate =
new FileIndexPredicate(embeddedIndexBytes, dataRowType)) {
diff --git
a/paimon-core/src/main/java/org/apache/paimon/operation/AppendOnlyFileStoreWrite.java
b/paimon-core/src/main/java/org/apache/paimon/operation/AppendOnlyFileStoreWrite.java
index 0a4d5d56a..3ce019c91 100644
---
a/paimon-core/src/main/java/org/apache/paimon/operation/AppendOnlyFileStoreWrite.java
+++
b/paimon-core/src/main/java/org/apache/paimon/operation/AppendOnlyFileStoreWrite.java
@@ -132,7 +132,8 @@ public abstract class AppendOnlyFileStoreWrite extends
MemoryFileStoreWrite<Inte
statsCollectors,
options.writeBufferSpillDiskSize(),
fileIndexOptions,
- options.asyncFileWrite());
+ options.asyncFileWrite(),
+ options.statsDenseStore());
}
protected abstract CompactManager getCompactManager(
@@ -193,7 +194,8 @@ public abstract class AppendOnlyFileStoreWrite extends
MemoryFileStoreWrite<Inte
statsCollectors,
fileIndexOptions,
FileSource.COMPACT,
- options.asyncFileWrite());
+ options.asyncFileWrite(),
+ options.statsDenseStore());
}
private RecordReaderIterator<InternalRow> createFilesIterator(
diff --git
a/paimon-core/src/main/java/org/apache/paimon/operation/KeyValueFileStoreScan.java
b/paimon-core/src/main/java/org/apache/paimon/operation/KeyValueFileStoreScan.java
index 3311161b5..8300bdcfa 100644
---
a/paimon-core/src/main/java/org/apache/paimon/operation/KeyValueFileStoreScan.java
+++
b/paimon-core/src/main/java/org/apache/paimon/operation/KeyValueFileStoreScan.java
@@ -21,15 +21,15 @@ package org.apache.paimon.operation;
import org.apache.paimon.CoreOptions.ChangelogProducer;
import org.apache.paimon.CoreOptions.MergeEngine;
import org.apache.paimon.KeyValueFileStore;
+import org.apache.paimon.io.DataFileMeta;
import org.apache.paimon.manifest.ManifestEntry;
import org.apache.paimon.manifest.ManifestFile;
import org.apache.paimon.predicate.Predicate;
import org.apache.paimon.schema.KeyValueFieldsExtractor;
import org.apache.paimon.schema.SchemaManager;
import org.apache.paimon.schema.TableSchema;
-import org.apache.paimon.stats.SimpleStats;
-import org.apache.paimon.stats.SimpleStatsConverter;
-import org.apache.paimon.stats.SimpleStatsConverters;
+import org.apache.paimon.stats.SimpleStatsEvolution;
+import org.apache.paimon.stats.SimpleStatsEvolutions;
import org.apache.paimon.table.source.ScanMode;
import org.apache.paimon.utils.SnapshotManager;
@@ -44,8 +44,8 @@ import static
org.apache.paimon.CoreOptions.MergeEngine.PARTIAL_UPDATE;
/** {@link FileStoreScan} for {@link KeyValueFileStore}. */
public class KeyValueFileStoreScan extends AbstractFileStoreScan {
- private final SimpleStatsConverters fieldKeyStatsConverters;
- private final SimpleStatsConverters fieldValueStatsConverters;
+ private final SimpleStatsEvolutions fieldKeyStatsConverters;
+ private final SimpleStatsEvolutions fieldValueStatsConverters;
private final BucketSelectConverter bucketSelectConverter;
private Predicate keyFilter;
@@ -75,11 +75,11 @@ public class KeyValueFileStoreScan extends
AbstractFileStoreScan {
scanManifestParallelism);
this.bucketSelectConverter = bucketSelectConverter;
this.fieldKeyStatsConverters =
- new SimpleStatsConverters(
+ new SimpleStatsEvolutions(
sid ->
keyValueFieldsExtractor.keyFields(scanTableSchema(sid)),
schema.id());
this.fieldValueStatsConverters =
- new SimpleStatsConverters(
+ new SimpleStatsEvolutions(
sid ->
keyValueFieldsExtractor.valueFields(scanTableSchema(sid)),
schema.id());
this.deletionVectorsEnabled = deletionVectorsEnabled;
@@ -101,30 +101,21 @@ public class KeyValueFileStoreScan extends
AbstractFileStoreScan {
/** Note: Keep this thread-safe. */
@Override
protected boolean filterByStats(ManifestEntry entry) {
- Predicate filter = null;
- SimpleStatsConverter serializer = null;
- SimpleStats stats = null;
- if (isValueFilterEnabled(entry)) {
- filter = valueFilter;
- serializer =
fieldValueStatsConverters.getOrCreate(entry.file().schemaId());
- stats = entry.file().valueStats();
+ DataFileMeta file = entry.file();
+ if (isValueFilterEnabled(entry) && !filterByValueFilter(entry)) {
+ return false;
}
- if (filter == null && keyFilter != null) {
- filter = keyFilter;
- serializer =
fieldKeyStatsConverters.getOrCreate(entry.file().schemaId());
- stats = entry.file().keyStats();
+ if (keyFilter != null) {
+ SimpleStatsEvolution.Result stats =
+ fieldKeyStatsConverters
+ .getOrCreate(file.schemaId())
+ .evolution(file.keyStats(), file.rowCount(), null);
+ return keyFilter.test(
+ file.rowCount(), stats.minValues(), stats.maxValues(),
stats.nullCounts());
}
- if (filter == null) {
- return true;
- }
-
- return filter.test(
- entry.file().rowCount(),
- serializer.evolution(stats.minValues()),
- serializer.evolution(stats.maxValues()),
- serializer.evolution(stats.nullCounts(),
entry.file().rowCount()));
+ return true;
}
private boolean isValueFilterEnabled(ManifestEntry entry) {
@@ -184,14 +175,13 @@ public class KeyValueFileStoreScan extends
AbstractFileStoreScan {
}
private boolean filterByValueFilter(ManifestEntry entry) {
- SimpleStatsConverter serializer =
- fieldValueStatsConverters.getOrCreate(entry.file().schemaId());
- SimpleStats stats = entry.file().valueStats();
+ DataFileMeta file = entry.file();
+ SimpleStatsEvolution.Result result =
+ fieldValueStatsConverters
+ .getOrCreate(file.schemaId())
+ .evolution(file.valueStats(), file.rowCount(),
file.valueStatsCols());
return valueFilter.test(
- entry.file().rowCount(),
- serializer.evolution(stats.minValues()),
- serializer.evolution(stats.maxValues()),
- serializer.evolution(stats.nullCounts(),
entry.file().rowCount()));
+ file.rowCount(), result.minValues(), result.maxValues(),
result.nullCounts());
}
private static boolean noOverlapping(List<ManifestEntry> entries) {
diff --git
a/paimon-core/src/main/java/org/apache/paimon/stats/SimpleStatsConverter.java
b/paimon-core/src/main/java/org/apache/paimon/stats/SimpleStatsConverter.java
index b76dc72b8..13bafd879 100644
---
a/paimon-core/src/main/java/org/apache/paimon/stats/SimpleStatsConverter.java
+++
b/paimon-core/src/main/java/org/apache/paimon/stats/SimpleStatsConverter.java
@@ -18,48 +18,78 @@
package org.apache.paimon.stats;
-import org.apache.paimon.casting.CastFieldGetter;
-import org.apache.paimon.casting.CastedRow;
import org.apache.paimon.data.BinaryArray;
-import org.apache.paimon.data.BinaryRow;
-import org.apache.paimon.data.BinaryString;
-import org.apache.paimon.data.Decimal;
import org.apache.paimon.data.GenericRow;
-import org.apache.paimon.data.InternalArray;
-import org.apache.paimon.data.InternalMap;
-import org.apache.paimon.data.InternalRow;
-import org.apache.paimon.data.Timestamp;
import org.apache.paimon.data.serializer.InternalRowSerializer;
import org.apache.paimon.format.SimpleColStats;
-import org.apache.paimon.types.DataType;
import org.apache.paimon.types.RowType;
-import org.apache.paimon.utils.ProjectedRow;
+import org.apache.paimon.utils.Pair;
-import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
/** Converter for array of {@link SimpleColStats}. */
public class SimpleStatsConverter {
+ private final RowType rowType;
+ private final boolean denseStore;
private final InternalRowSerializer serializer;
-
- @Nullable private final int[] indexMapping;
- @Nullable private final CastFieldGetter[] castFieldGetters;
+ private final Map<List<String>, InternalRowSerializer> serializers;
public SimpleStatsConverter(RowType type) {
- this(type, null, null);
+ this(type, false);
+ }
+
+ public SimpleStatsConverter(RowType type, boolean denseStore) {
+ // as stated in RollingFile.Writer#finish, col stats are not collected
currently so
+ // min/max values are all nulls
+ this.rowType =
+ type.copy(
+ type.getFields().stream()
+ .map(f -> f.newType(f.type().copy(true)))
+ .collect(Collectors.toList()));
+ this.denseStore = denseStore;
+ this.serializer = new InternalRowSerializer(rowType);
+ this.serializers = new HashMap<>();
}
- public SimpleStatsConverter(
- RowType type,
- @Nullable int[] indexMapping,
- @Nullable CastFieldGetter[] castFieldGetters) {
- RowType safeType = toAllFieldsNullableRowType(type);
- this.serializer = new InternalRowSerializer(safeType);
- this.indexMapping = indexMapping;
- this.castFieldGetters = castFieldGetters;
+ public Pair<List<String>, SimpleStats> toBinary(SimpleColStats[] stats) {
+ return denseStore ? toBinaryDenseMode(stats) : Pair.of(null,
toBinaryAllMode(stats));
+ }
+
+ private Pair<List<String>, SimpleStats> toBinaryDenseMode(SimpleColStats[]
stats) {
+ List<String> fields = new ArrayList<>();
+ List<Object> minValues = new ArrayList<>();
+ List<Object> maxValues = new ArrayList<>();
+ List<Long> nullCounts = new ArrayList<>();
+ for (int i = 0; i < stats.length; i++) {
+ SimpleColStats colStats = stats[i];
+ if (colStats.isNone()) {
+ continue;
+ }
+
+ fields.add(rowType.getFields().get(i).name());
+ minValues.add(colStats.min());
+ maxValues.add(colStats.max());
+ nullCounts.add(colStats.nullCount());
+ }
+
+ InternalRowSerializer serializer =
+ serializers.computeIfAbsent(
+ fields, key -> new
InternalRowSerializer(rowType.project(key)));
+
+ SimpleStats simpleStats =
+ new SimpleStats(
+
serializer.toBinaryRow(GenericRow.of(minValues.toArray())).copy(),
+
serializer.toBinaryRow(GenericRow.of(maxValues.toArray())).copy(),
+ BinaryArray.fromLongArray(nullCounts.toArray(new
Long[0])));
+ return Pair.of(fields.size() == rowType.getFieldCount() ? null :
fields, simpleStats);
}
- public SimpleStats toBinary(SimpleColStats[] stats) {
+ public SimpleStats toBinaryAllMode(SimpleColStats[] stats) {
int rowFieldCount = stats.length;
GenericRow minValues = new GenericRow(rowFieldCount);
GenericRow maxValues = new GenericRow(rowFieldCount);
@@ -74,192 +104,4 @@ public class SimpleStatsConverter {
serializer.toBinaryRow(maxValues).copy(),
BinaryArray.fromLongArray(nullCounts));
}
-
- public InternalRow evolution(BinaryRow values) {
- InternalRow row = values;
- if (indexMapping != null) {
- row = ProjectedRow.from(indexMapping).replaceRow(row);
- }
-
- if (castFieldGetters != null) {
- row = CastedRow.from(castFieldGetters).replaceRow(values);
- }
-
- return row;
- }
-
- public InternalArray evolution(BinaryArray nullCounts, @Nullable Long
rowCount) {
- if (indexMapping == null) {
- return nullCounts;
- }
-
- if (rowCount == null) {
- throw new RuntimeException("Schema Evolution for stats needs row
count.");
- }
-
- return new NullCountsEvoArray(indexMapping, nullCounts, rowCount);
- }
-
- private static RowType toAllFieldsNullableRowType(RowType rowType) {
- // as stated in RollingFile.Writer#finish, col stats are not collected
currently so
- // min/max values are all nulls
- return RowType.builder()
- .fields(
- rowType.getFields().stream()
- .map(f -> f.type().copy(true))
- .toArray(DataType[]::new),
- rowType.getFieldNames().toArray(new String[0]))
- .build();
- }
-
- private static class NullCountsEvoArray implements InternalArray {
-
- private final int[] indexMapping;
- private final InternalArray array;
- private final long notFoundValue;
-
- protected NullCountsEvoArray(int[] indexMapping, InternalArray array,
long notFoundValue) {
- this.indexMapping = indexMapping;
- this.array = array;
- this.notFoundValue = notFoundValue;
- }
-
- @Override
- public int size() {
- return indexMapping.length;
- }
-
- @Override
- public boolean isNullAt(int pos) {
- if (indexMapping[pos] < 0) {
- return false;
- }
- return array.isNullAt(indexMapping[pos]);
- }
-
- @Override
- public long getLong(int pos) {
- if (indexMapping[pos] < 0) {
- return notFoundValue;
- }
- return array.getLong(indexMapping[pos]);
- }
-
- // ============================= Unsupported Methods
================================
-
- @Override
- public boolean getBoolean(int pos) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public byte getByte(int pos) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public short getShort(int pos) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int getInt(int pos) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public float getFloat(int pos) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public double getDouble(int pos) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public BinaryString getString(int pos) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Decimal getDecimal(int pos, int precision, int scale) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Timestamp getTimestamp(int pos, int precision) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public byte[] getBinary(int pos) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public InternalArray getArray(int pos) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public InternalMap getMap(int pos) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public InternalRow getRow(int pos, int numFields) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean equals(Object o) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int hashCode() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public String toString() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean[] toBooleanArray() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public byte[] toByteArray() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public short[] toShortArray() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int[] toIntArray() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public long[] toLongArray() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public float[] toFloatArray() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public double[] toDoubleArray() {
- throw new UnsupportedOperationException();
- }
- }
}
diff --git
a/paimon-core/src/main/java/org/apache/paimon/stats/SimpleStatsConverter.java
b/paimon-core/src/main/java/org/apache/paimon/stats/SimpleStatsEvolution.java
similarity index 69%
copy from
paimon-core/src/main/java/org/apache/paimon/stats/SimpleStatsConverter.java
copy to
paimon-core/src/main/java/org/apache/paimon/stats/SimpleStatsEvolution.java
index b76dc72b8..d3f6d4cd6 100644
---
a/paimon-core/src/main/java/org/apache/paimon/stats/SimpleStatsConverter.java
+++
b/paimon-core/src/main/java/org/apache/paimon/stats/SimpleStatsEvolution.java
@@ -20,96 +20,101 @@ package org.apache.paimon.stats;
import org.apache.paimon.casting.CastFieldGetter;
import org.apache.paimon.casting.CastedRow;
-import org.apache.paimon.data.BinaryArray;
-import org.apache.paimon.data.BinaryRow;
import org.apache.paimon.data.BinaryString;
import org.apache.paimon.data.Decimal;
-import org.apache.paimon.data.GenericRow;
import org.apache.paimon.data.InternalArray;
import org.apache.paimon.data.InternalMap;
import org.apache.paimon.data.InternalRow;
import org.apache.paimon.data.Timestamp;
-import org.apache.paimon.data.serializer.InternalRowSerializer;
import org.apache.paimon.format.SimpleColStats;
-import org.apache.paimon.types.DataType;
import org.apache.paimon.types.RowType;
+import org.apache.paimon.utils.ProjectedArray;
import org.apache.paimon.utils.ProjectedRow;
import javax.annotation.Nullable;
-/** Converter for array of {@link SimpleColStats}. */
-public class SimpleStatsConverter {
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
- private final InternalRowSerializer serializer;
+/** Converter for array of {@link SimpleColStats}. */
+public class SimpleStatsEvolution {
+ private final List<String> fieldNames;
@Nullable private final int[] indexMapping;
@Nullable private final CastFieldGetter[] castFieldGetters;
- public SimpleStatsConverter(RowType type) {
- this(type, null, null);
- }
+ private final Map<List<String>, int[]> indexMappings;
- public SimpleStatsConverter(
- RowType type,
+ public SimpleStatsEvolution(
+ RowType rowType,
@Nullable int[] indexMapping,
@Nullable CastFieldGetter[] castFieldGetters) {
- RowType safeType = toAllFieldsNullableRowType(type);
- this.serializer = new InternalRowSerializer(safeType);
+ this.fieldNames = rowType.getFieldNames();
this.indexMapping = indexMapping;
this.castFieldGetters = castFieldGetters;
+ this.indexMappings = new HashMap<>();
}
- public SimpleStats toBinary(SimpleColStats[] stats) {
- int rowFieldCount = stats.length;
- GenericRow minValues = new GenericRow(rowFieldCount);
- GenericRow maxValues = new GenericRow(rowFieldCount);
- Long[] nullCounts = new Long[rowFieldCount];
- for (int i = 0; i < rowFieldCount; i++) {
- minValues.setField(i, stats[i].min());
- maxValues.setField(i, stats[i].max());
- nullCounts[i] = stats[i].nullCount();
- }
- return new SimpleStats(
- serializer.toBinaryRow(minValues).copy(),
- serializer.toBinaryRow(maxValues).copy(),
- BinaryArray.fromLongArray(nullCounts));
- }
+ public Result evolution(
+ SimpleStats stats, @Nullable Long rowCount, @Nullable List<String>
denseFields) {
+ InternalRow minValues = stats.minValues();
+ InternalRow maxValues = stats.maxValues();
+ InternalArray nullCounts = stats.nullCounts();
+
+ if (denseFields != null) {
+ int[] denseIndexMapping =
+ indexMappings.computeIfAbsent(
+ denseFields,
+ k ->
fieldNames.stream().mapToInt(denseFields::indexOf).toArray());
+ minValues =
ProjectedRow.from(denseIndexMapping).replaceRow(minValues);
+ maxValues =
ProjectedRow.from(denseIndexMapping).replaceRow(maxValues);
+ nullCounts =
ProjectedArray.from(denseIndexMapping).replaceArray(nullCounts);
+ }
- public InternalRow evolution(BinaryRow values) {
- InternalRow row = values;
if (indexMapping != null) {
- row = ProjectedRow.from(indexMapping).replaceRow(row);
+ minValues = ProjectedRow.from(indexMapping).replaceRow(minValues);
+ maxValues = ProjectedRow.from(indexMapping).replaceRow(maxValues);
+
+ if (rowCount == null) {
+ throw new RuntimeException("Schema Evolution for stats needs
row count.");
+ }
+
+ nullCounts = new NullCountsEvoArray(indexMapping, nullCounts,
rowCount);
}
if (castFieldGetters != null) {
- row = CastedRow.from(castFieldGetters).replaceRow(values);
+ minValues = CastedRow.from(castFieldGetters).replaceRow(minValues);
+ maxValues = CastedRow.from(castFieldGetters).replaceRow(maxValues);
}
- return row;
+ return new Result(minValues, maxValues, nullCounts);
}
- public InternalArray evolution(BinaryArray nullCounts, @Nullable Long
rowCount) {
- if (indexMapping == null) {
- return nullCounts;
+ /** Result to {@link SimpleStats} evolution. */
+ public static class Result {
+
+ private final InternalRow minValues;
+ private final InternalRow maxValues;
+ private final InternalArray nullCounts;
+
+ public Result(InternalRow minValues, InternalRow maxValues,
InternalArray nullCounts) {
+ this.minValues = minValues;
+ this.maxValues = maxValues;
+ this.nullCounts = nullCounts;
}
- if (rowCount == null) {
- throw new RuntimeException("Schema Evolution for stats needs row
count.");
+ public InternalRow minValues() {
+ return minValues;
}
- return new NullCountsEvoArray(indexMapping, nullCounts, rowCount);
- }
+ public InternalRow maxValues() {
+ return maxValues;
+ }
- private static RowType toAllFieldsNullableRowType(RowType rowType) {
- // as stated in RollingFile.Writer#finish, col stats are not collected
currently so
- // min/max values are all nulls
- return RowType.builder()
- .fields(
- rowType.getFields().stream()
- .map(f -> f.type().copy(true))
- .toArray(DataType[]::new),
- rowType.getFieldNames().toArray(new String[0]))
- .build();
+ public InternalArray nullCounts() {
+ return nullCounts;
+ }
}
private static class NullCountsEvoArray implements InternalArray {
@@ -118,7 +123,7 @@ public class SimpleStatsConverter {
private final InternalArray array;
private final long notFoundValue;
- protected NullCountsEvoArray(int[] indexMapping, InternalArray array,
long notFoundValue) {
+ private NullCountsEvoArray(int[] indexMapping, InternalArray array,
long notFoundValue) {
this.indexMapping = indexMapping;
this.array = array;
this.notFoundValue = notFoundValue;
diff --git
a/paimon-core/src/main/java/org/apache/paimon/stats/SimpleStatsConverters.java
b/paimon-core/src/main/java/org/apache/paimon/stats/SimpleStatsEvolutions.java
similarity index 86%
rename from
paimon-core/src/main/java/org/apache/paimon/stats/SimpleStatsConverters.java
rename to
paimon-core/src/main/java/org/apache/paimon/stats/SimpleStatsEvolutions.java
index 10694769b..a0814b8c0 100644
---
a/paimon-core/src/main/java/org/apache/paimon/stats/SimpleStatsConverters.java
+++
b/paimon-core/src/main/java/org/apache/paimon/stats/SimpleStatsEvolutions.java
@@ -37,28 +37,29 @@ import java.util.function.Function;
import static
org.apache.paimon.schema.SchemaEvolutionUtil.createIndexCastMapping;
/** Converters to create col stats array serializer. */
-public class SimpleStatsConverters {
+public class SimpleStatsEvolutions {
private final Function<Long, List<DataField>> schemaFields;
private final long tableSchemaId;
private final List<DataField> tableDataFields;
private final AtomicReference<List<DataField>> tableFields;
- private final ConcurrentMap<Long, SimpleStatsConverter> converters;
+ private final ConcurrentMap<Long, SimpleStatsEvolution> evolutions;
- public SimpleStatsConverters(Function<Long, List<DataField>> schemaFields,
long tableSchemaId) {
+ public SimpleStatsEvolutions(Function<Long, List<DataField>> schemaFields,
long tableSchemaId) {
this.schemaFields = schemaFields;
this.tableSchemaId = tableSchemaId;
this.tableDataFields = schemaFields.apply(tableSchemaId);
this.tableFields = new AtomicReference<>();
- this.converters = new ConcurrentHashMap<>();
+ this.evolutions = new ConcurrentHashMap<>();
}
- public SimpleStatsConverter getOrCreate(long dataSchemaId) {
- return converters.computeIfAbsent(
+ public SimpleStatsEvolution getOrCreate(long dataSchemaId) {
+ return evolutions.computeIfAbsent(
dataSchemaId,
id -> {
if (tableSchemaId == id) {
- return new SimpleStatsConverter(new
RowType(schemaFields.apply(id)));
+ return new SimpleStatsEvolution(
+ new RowType(schemaFields.apply(id)), null,
null);
}
// Get atomic schema fields.
@@ -66,10 +67,10 @@ public class SimpleStatsConverters {
tableFields.updateAndGet(v -> v == null ?
tableDataFields : v);
List<DataField> dataFields = schemaFields.apply(id);
IndexCastMapping indexCastMapping =
- createIndexCastMapping(schemaTableFields,
dataFields);
+ createIndexCastMapping(schemaTableFields,
schemaFields.apply(id));
@Nullable int[] indexMapping =
indexCastMapping.getIndexMapping();
// Create col stats array serializer with schema evolution
- return new SimpleStatsConverter(
+ return new SimpleStatsEvolution(
new RowType(dataFields),
indexMapping,
indexCastMapping.getCastMapping());
diff --git
a/paimon-core/src/main/java/org/apache/paimon/table/FileStoreTable.java
b/paimon-core/src/main/java/org/apache/paimon/table/FileStoreTable.java
index 3bd337294..01227dd35 100644
--- a/paimon-core/src/main/java/org/apache/paimon/table/FileStoreTable.java
+++ b/paimon-core/src/main/java/org/apache/paimon/table/FileStoreTable.java
@@ -21,10 +21,8 @@ package org.apache.paimon.table;
import org.apache.paimon.FileStore;
import org.apache.paimon.data.InternalRow;
import org.apache.paimon.fs.Path;
-import org.apache.paimon.io.DataFileMeta;
import org.apache.paimon.manifest.ManifestCacheFilter;
import org.apache.paimon.schema.TableSchema;
-import org.apache.paimon.stats.SimpleStats;
import org.apache.paimon.table.query.LocalTableQuery;
import org.apache.paimon.table.sink.RowKeyExtractor;
import org.apache.paimon.table.sink.TableCommitImpl;
@@ -104,10 +102,6 @@ public interface FileStoreTable extends DataTable {
LocalTableQuery newLocalTableQuery();
- default SimpleStats getSchemaFieldStats(DataFileMeta dataFileMeta) {
- return dataFileMeta.valueStats();
- }
-
boolean supportStreamingReadOverwrite();
RowKeyExtractor createRowKeyExtractor();
diff --git
a/paimon-core/src/main/java/org/apache/paimon/table/sink/CommitMessageLegacyV2Serializer.java
b/paimon-core/src/main/java/org/apache/paimon/table/sink/CommitMessageLegacyV2Serializer.java
index 989f32f50..3e351cd1d 100644
---
a/paimon-core/src/main/java/org/apache/paimon/table/sink/CommitMessageLegacyV2Serializer.java
+++
b/paimon-core/src/main/java/org/apache/paimon/table/sink/CommitMessageLegacyV2Serializer.java
@@ -154,6 +154,7 @@ public class CommitMessageLegacyV2Serializer {
row.getTimestamp(12, 3),
null,
null,
+ null,
null);
}
}
diff --git
a/paimon-core/src/main/java/org/apache/paimon/table/sink/CommitMessageSerializer.java
b/paimon-core/src/main/java/org/apache/paimon/table/sink/CommitMessageSerializer.java
index cc8abfa48..7918914b2 100644
---
a/paimon-core/src/main/java/org/apache/paimon/table/sink/CommitMessageSerializer.java
+++
b/paimon-core/src/main/java/org/apache/paimon/table/sink/CommitMessageSerializer.java
@@ -21,7 +21,9 @@ package org.apache.paimon.table.sink;
import org.apache.paimon.data.serializer.VersionedSerializer;
import org.apache.paimon.index.IndexFileMetaSerializer;
import org.apache.paimon.io.CompactIncrement;
+import org.apache.paimon.io.DataFileMeta;
import org.apache.paimon.io.DataFileMeta08Serializer;
+import org.apache.paimon.io.DataFileMeta09Serializer;
import org.apache.paimon.io.DataFileMetaSerializer;
import org.apache.paimon.io.DataIncrement;
import org.apache.paimon.io.DataInputDeserializer;
@@ -29,6 +31,7 @@ import org.apache.paimon.io.DataInputView;
import org.apache.paimon.io.DataOutputView;
import org.apache.paimon.io.DataOutputViewStreamWrapper;
import org.apache.paimon.io.IndexIncrement;
+import org.apache.paimon.utils.IOExceptionSupplier;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -42,7 +45,7 @@ import static
org.apache.paimon.utils.SerializationUtils.serializeBinaryRow;
/** {@link VersionedSerializer} for {@link CommitMessage}. */
public class CommitMessageSerializer implements
VersionedSerializer<CommitMessage> {
- private static final int CURRENT_VERSION = 3;
+ private static final int CURRENT_VERSION = 4;
private final DataFileMetaSerializer dataFileSerializer;
private final IndexFileMetaSerializer indexEntrySerializer;
@@ -104,30 +107,25 @@ public class CommitMessageSerializer implements
VersionedSerializer<CommitMessag
}
private CommitMessage deserialize(int version, DataInputView view) throws
IOException {
- if (version == CURRENT_VERSION) {
+ if (version >= 3) {
+ IOExceptionSupplier<List<DataFileMeta>> fileDeserializer =
+ () -> dataFileSerializer.deserializeList(view);
+ if (version == 3) {
+ DataFileMeta09Serializer serializer = new
DataFileMeta09Serializer();
+ fileDeserializer = () -> serializer.deserializeList(view);
+ }
return new CommitMessageImpl(
deserializeBinaryRow(view),
view.readInt(),
new DataIncrement(
- dataFileSerializer.deserializeList(view),
- dataFileSerializer.deserializeList(view),
- dataFileSerializer.deserializeList(view)),
+ fileDeserializer.get(), fileDeserializer.get(),
fileDeserializer.get()),
new CompactIncrement(
- dataFileSerializer.deserializeList(view),
- dataFileSerializer.deserializeList(view),
- dataFileSerializer.deserializeList(view)),
+ fileDeserializer.get(), fileDeserializer.get(),
fileDeserializer.get()),
new IndexIncrement(
indexEntrySerializer.deserializeList(view),
indexEntrySerializer.deserializeList(view)));
- } else if (version <= 2) {
- return deserialize08(version, view);
} else {
- throw new UnsupportedOperationException(
- "Expecting CommitMessageSerializer version to be smaller
or equal than "
- + CURRENT_VERSION
- + ", but found "
- + version
- + ".");
+ return deserialize08(version, view);
}
}
diff --git
a/paimon-core/src/main/java/org/apache/paimon/table/source/DataSplit.java
b/paimon-core/src/main/java/org/apache/paimon/table/source/DataSplit.java
index 067bf055c..1dac6584d 100644
--- a/paimon-core/src/main/java/org/apache/paimon/table/source/DataSplit.java
+++ b/paimon-core/src/main/java/org/apache/paimon/table/source/DataSplit.java
@@ -21,6 +21,7 @@ package org.apache.paimon.table.source;
import org.apache.paimon.data.BinaryRow;
import org.apache.paimon.io.DataFileMeta;
import org.apache.paimon.io.DataFileMeta08Serializer;
+import org.apache.paimon.io.DataFileMeta09Serializer;
import org.apache.paimon.io.DataFileMetaSerializer;
import org.apache.paimon.io.DataInputView;
import org.apache.paimon.io.DataInputViewStreamWrapper;
@@ -49,7 +50,7 @@ public class DataSplit implements Split {
private static final long serialVersionUID = 7L;
private static final long MAGIC = -2394839472490812314L;
- private static final int VERSION = 2;
+ private static final int VERSION = 3;
private long snapshotId = 0;
private BinaryRow partition;
@@ -316,6 +317,9 @@ public class DataSplit implements Split {
DataFileMeta08Serializer serializer = new
DataFileMeta08Serializer();
return serializer::deserialize;
} else if (version == 2) {
+ DataFileMeta09Serializer serializer = new
DataFileMeta09Serializer();
+ return serializer::deserialize;
+ } else if (version == 3) {
DataFileMetaSerializer serializer = new DataFileMetaSerializer();
return serializer::deserialize;
} else {
diff --git
a/paimon-core/src/main/java/org/apache/paimon/table/system/FilesTable.java
b/paimon-core/src/main/java/org/apache/paimon/table/system/FilesTable.java
index ed06afd3a..ab08d9e9f 100644
--- a/paimon-core/src/main/java/org/apache/paimon/table/system/FilesTable.java
+++ b/paimon-core/src/main/java/org/apache/paimon/table/system/FilesTable.java
@@ -34,9 +34,8 @@ import org.apache.paimon.predicate.Predicate;
import org.apache.paimon.reader.RecordReader;
import org.apache.paimon.schema.SchemaManager;
import org.apache.paimon.schema.TableSchema;
-import org.apache.paimon.stats.SimpleStats;
-import org.apache.paimon.stats.SimpleStatsConverter;
-import org.apache.paimon.stats.SimpleStatsConverters;
+import org.apache.paimon.stats.SimpleStatsEvolution;
+import org.apache.paimon.stats.SimpleStatsEvolutions;
import org.apache.paimon.table.FileStoreTable;
import org.apache.paimon.table.ReadonlyTable;
import org.apache.paimon.table.Table;
@@ -314,8 +313,8 @@ public class FilesTable implements ReadonlyTable {
List<Iterator<InternalRow>> iteratorList = new ArrayList<>();
// dataFilePlan.snapshotId indicates there's no files in the
table, use the newest
// schema id directly
- SimpleStatsConverters simpleStatsConverters =
- new SimpleStatsConverters(
+ SimpleStatsEvolutions simpleStatsEvolutions =
+ new SimpleStatsEvolutions(
sid -> schemaManager.schema(sid).fields(),
storeTable.schema().id());
RowDataToObjectArrayConverter partitionConverter =
@@ -352,8 +351,7 @@ public class FilesTable implements ReadonlyTable {
partitionConverter,
keyConverters,
file,
-
storeTable.getSchemaFieldStats(file),
- simpleStatsConverters)));
+ simpleStatsEvolutions)));
}
Iterator<InternalRow> rows =
Iterators.concat(iteratorList.iterator());
if (readType != null) {
@@ -372,10 +370,8 @@ public class FilesTable implements ReadonlyTable {
RowDataToObjectArrayConverter partitionConverter,
Function<Long, RowDataToObjectArrayConverter> keyConverters,
DataFileMeta dataFileMeta,
- SimpleStats stats,
- SimpleStatsConverters simpleStatsConverters) {
- StatsLazyGetter statsGetter =
- new StatsLazyGetter(stats, dataFileMeta,
simpleStatsConverters);
+ SimpleStatsEvolutions simpleStatsEvolutions) {
+ StatsLazyGetter statsGetter = new StatsLazyGetter(dataFileMeta,
simpleStatsEvolutions);
@SuppressWarnings("unchecked")
Supplier<Object>[] fields =
new Supplier[] {
@@ -426,32 +422,31 @@ public class FilesTable implements ReadonlyTable {
private static class StatsLazyGetter {
- private final SimpleStats stats;
private final DataFileMeta file;
- private final SimpleStatsConverters simpleStatsConverters;
+ private final SimpleStatsEvolutions simpleStatsEvolutions;
private Map<String, Long> lazyNullValueCounts;
private Map<String, Object> lazyLowerValueBounds;
private Map<String, Object> lazyUpperValueBounds;
- private StatsLazyGetter(
- SimpleStats stats, DataFileMeta file, SimpleStatsConverters
simpleStatsConverters) {
- this.stats = stats;
+ private StatsLazyGetter(DataFileMeta file, SimpleStatsEvolutions
simpleStatsEvolutions) {
this.file = file;
- this.simpleStatsConverters = simpleStatsConverters;
+ this.simpleStatsEvolutions = simpleStatsEvolutions;
}
private void initialize() {
- SimpleStatsConverter serializer =
simpleStatsConverters.getOrCreate(file.schemaId());
+ SimpleStatsEvolution evolution =
simpleStatsEvolutions.getOrCreate(file.schemaId());
// Create value stats
- InternalRow min = serializer.evolution(stats.minValues());
- InternalRow max = serializer.evolution(stats.maxValues());
- InternalArray nullCounts =
serializer.evolution(stats.nullCounts(), file.rowCount());
+ SimpleStatsEvolution.Result result =
+ evolution.evolution(file.valueStats(), file.rowCount(),
file.valueStatsCols());
+ InternalRow min = result.minValues();
+ InternalRow max = result.maxValues();
+ InternalArray nullCounts = result.nullCounts();
lazyNullValueCounts = new TreeMap<>();
lazyLowerValueBounds = new TreeMap<>();
lazyUpperValueBounds = new TreeMap<>();
for (int i = 0; i < min.getFieldCount(); i++) {
- DataField field =
simpleStatsConverters.tableDataFields().get(i);
+ DataField field =
simpleStatsEvolutions.tableDataFields().get(i);
String name = field.name();
DataType type = field.type();
lazyNullValueCounts.put(
diff --git
a/paimon-core/src/main/java/org/apache/paimon/utils/StatsCollectorFactories.java
b/paimon-core/src/main/java/org/apache/paimon/utils/StatsCollectorFactories.java
index 6d93224e5..ed23a246a 100644
---
a/paimon-core/src/main/java/org/apache/paimon/utils/StatsCollectorFactories.java
+++
b/paimon-core/src/main/java/org/apache/paimon/utils/StatsCollectorFactories.java
@@ -21,6 +21,8 @@ package org.apache.paimon.utils;
import org.apache.paimon.CoreOptions;
import org.apache.paimon.options.Options;
import org.apache.paimon.statistics.SimpleColStatsCollector;
+import org.apache.paimon.statistics.TruncateSimpleColStatsCollector;
+import org.apache.paimon.table.SystemFields;
import java.util.List;
@@ -37,15 +39,16 @@ public class StatsCollectorFactories {
SimpleColStatsCollector.Factory[] modes =
new SimpleColStatsCollector.Factory[fields.size()];
for (int i = 0; i < fields.size(); i++) {
+ String field = fields.get(i);
String fieldMode =
cfg.get(
- key(String.format(
- "%s.%s.%s",
- FIELDS_PREFIX, fields.get(i),
STATS_MODE_SUFFIX))
+ key(String.format("%s.%s.%s", FIELDS_PREFIX,
field, STATS_MODE_SUFFIX))
.stringType()
.noDefaultValue());
if (fieldMode != null) {
modes[i] = SimpleColStatsCollector.from(fieldMode);
+ } else if (SystemFields.isSystemField(field)) {
+ modes[i] = () -> new TruncateSimpleColStatsCollector(128);
} else {
modes[i] =
SimpleColStatsCollector.from(cfg.get(CoreOptions.METADATA_STATS_MODE));
}
diff --git
a/paimon-core/src/test/java/org/apache/paimon/append/AppendOnlyWriterTest.java
b/paimon-core/src/test/java/org/apache/paimon/append/AppendOnlyWriterTest.java
index a4ba849ad..04b5fa6e6 100644
---
a/paimon-core/src/test/java/org/apache/paimon/append/AppendOnlyWriterTest.java
+++
b/paimon-core/src/test/java/org/apache/paimon/append/AppendOnlyWriterTest.java
@@ -137,7 +137,7 @@ public class AppendOnlyWriterTest {
new SimpleColStats[] {
initStats(1, 1, 0), initStats("AAA", "AAA", 0),
initStats(PART, PART, 0)
};
-
assertThat(meta.valueStats()).isEqualTo(STATS_SERIALIZER.toBinary(expected));
+
assertThat(meta.valueStats()).isEqualTo(STATS_SERIALIZER.toBinaryAllMode(expected));
assertThat(meta.minSequenceNumber()).isEqualTo(0);
assertThat(meta.maxSequenceNumber()).isEqualTo(0);
@@ -200,7 +200,7 @@ public class AppendOnlyWriterTest {
initStats(String.format("%03d", start),
String.format("%03d", end - 1), 0),
initStats(PART, PART, 0)
};
-
assertThat(meta.valueStats()).isEqualTo(STATS_SERIALIZER.toBinary(expected));
+
assertThat(meta.valueStats()).isEqualTo(STATS_SERIALIZER.toBinaryAllMode(expected));
assertThat(meta.minSequenceNumber()).isEqualTo(start);
assertThat(meta.maxSequenceNumber()).isEqualTo(end - 1);
@@ -243,7 +243,7 @@ public class AppendOnlyWriterTest {
initStats(String.format("%03d", min),
String.format("%03d", max), 0),
initStats(PART, PART, 0)
};
-
assertThat(meta.valueStats()).isEqualTo(STATS_SERIALIZER.toBinary(expected));
+
assertThat(meta.valueStats()).isEqualTo(STATS_SERIALIZER.toBinaryAllMode(expected));
assertThat(meta.minSequenceNumber()).isEqualTo(min);
assertThat(meta.maxSequenceNumber()).isEqualTo(max);
@@ -633,7 +633,8 @@ public class AppendOnlyWriterTest {
options,
AppendOnlyWriterTest.SCHEMA.getFieldNames()),
MemorySize.MAX_VALUE,
new FileIndexOptions(),
- true);
+ true,
+ false);
writer.setMemoryPool(
new HeapMemorySegmentPool(options.writeBufferSize(),
options.pageSize()));
return Pair.of(writer, compactManager.allFiles());
@@ -649,7 +650,7 @@ public class AppendOnlyWriterTest {
fileName,
toCompact.stream().mapToLong(DataFileMeta::fileSize).sum(),
toCompact.stream().mapToLong(DataFileMeta::rowCount).sum(),
- STATS_SERIALIZER.toBinary(
+ STATS_SERIALIZER.toBinaryAllMode(
new SimpleColStats[] {
initStats(
toCompact.get(0).valueStats().minValues().getInt(0),
@@ -674,6 +675,9 @@ public class AppendOnlyWriterTest {
minSeq,
maxSeq,
toCompact.get(0).schemaId(),
- FileSource.APPEND);
+ Collections.emptyList(),
+ null,
+ FileSource.APPEND,
+ null);
}
}
diff --git
a/paimon-core/src/test/java/org/apache/paimon/append/UnawareAppendTableCompactionCoordinatorTest.java
b/paimon-core/src/test/java/org/apache/paimon/append/UnawareAppendTableCompactionCoordinatorTest.java
index d69773900..95826c195 100644
---
a/paimon-core/src/test/java/org/apache/paimon/append/UnawareAppendTableCompactionCoordinatorTest.java
+++
b/paimon-core/src/test/java/org/apache/paimon/append/UnawareAppendTableCompactionCoordinatorTest.java
@@ -190,6 +190,7 @@ public class UnawareAppendTableCompactionCoordinatorTest {
0,
0L,
null,
- FileSource.APPEND);
+ FileSource.APPEND,
+ null);
}
}
diff --git
a/paimon-core/src/test/java/org/apache/paimon/crosspartition/IndexBootstrapTest.java
b/paimon-core/src/test/java/org/apache/paimon/crosspartition/IndexBootstrapTest.java
index d9d8e1f69..be4147735 100644
---
a/paimon-core/src/test/java/org/apache/paimon/crosspartition/IndexBootstrapTest.java
+++
b/paimon-core/src/test/java/org/apache/paimon/crosspartition/IndexBootstrapTest.java
@@ -159,7 +159,8 @@ public class IndexBootstrapTest extends TableTestBase {
.toLocalDateTime()),
0L,
null,
- FileSource.APPEND);
+ FileSource.APPEND,
+ null);
}
private Pair<InternalRow, Integer> row(int pt, int col, int pk, int
bucket) {
diff --git
a/paimon-core/src/test/java/org/apache/paimon/format/FileFormatSuffixTest.java
b/paimon-core/src/test/java/org/apache/paimon/format/FileFormatSuffixTest.java
index 4aee88298..439fc4999 100644
---
a/paimon-core/src/test/java/org/apache/paimon/format/FileFormatSuffixTest.java
+++
b/paimon-core/src/test/java/org/apache/paimon/format/FileFormatSuffixTest.java
@@ -97,7 +97,8 @@ public class FileFormatSuffixTest extends
KeyValueFileReadWriteTest {
options, SCHEMA.getFieldNames()),
MemorySize.MAX_VALUE,
new FileIndexOptions(),
- true);
+ true,
+ false);
appendOnlyWriter.setMemoryPool(
new HeapMemorySegmentPool(options.writeBufferSize(),
options.pageSize()));
appendOnlyWriter.write(
diff --git
a/paimon-core/src/test/java/org/apache/paimon/io/DataFileTestDataGenerator.java
b/paimon-core/src/test/java/org/apache/paimon/io/DataFileTestDataGenerator.java
index 810cef860..7de4214be 100644
---
a/paimon-core/src/test/java/org/apache/paimon/io/DataFileTestDataGenerator.java
+++
b/paimon-core/src/test/java/org/apache/paimon/io/DataFileTestDataGenerator.java
@@ -156,15 +156,16 @@ public class DataFileTestDataGenerator {
kvs.size(),
minKey,
maxKey,
-
keyStatsSerializer.toBinary(keyStatsCollector.extract()),
-
valueStatsSerializer.toBinary(valueStatsCollector.extract()),
+
keyStatsSerializer.toBinaryAllMode(keyStatsCollector.extract()),
+
valueStatsSerializer.toBinaryAllMode(valueStatsCollector.extract()),
minSequenceNumber,
maxSequenceNumber,
0,
level,
kvs.stream().filter(kv ->
kv.valueKind().isRetract()).count(),
null,
- FileSource.APPEND),
+ FileSource.APPEND,
+ null),
kvs);
}
diff --git
a/paimon-core/src/test/java/org/apache/paimon/io/DataFileTestUtils.java
b/paimon-core/src/test/java/org/apache/paimon/io/DataFileTestUtils.java
index 03b9babfa..48c8d4487 100644
--- a/paimon-core/src/test/java/org/apache/paimon/io/DataFileTestUtils.java
+++ b/paimon-core/src/test/java/org/apache/paimon/io/DataFileTestUtils.java
@@ -56,7 +56,8 @@ public class DataFileTestUtils {
Timestamp.fromEpochMillis(100),
maxSeq - minSeq + 1,
null,
- FileSource.APPEND);
+ FileSource.APPEND,
+ null);
}
public static DataFileMeta newFile() {
@@ -74,7 +75,8 @@ public class DataFileTestUtils {
0,
0L,
null,
- FileSource.APPEND);
+ FileSource.APPEND,
+ null);
}
public static DataFileMeta newFile(
@@ -98,7 +100,8 @@ public class DataFileTestUtils {
level,
deleteRowCount,
null,
- FileSource.APPEND);
+ FileSource.APPEND,
+ null);
}
public static BinaryRow row(int i) {
diff --git
a/paimon-core/src/test/java/org/apache/paimon/io/RollingFileWriterTest.java
b/paimon-core/src/test/java/org/apache/paimon/io/RollingFileWriterTest.java
index df9f2ac0b..9bee36b7c 100644
--- a/paimon-core/src/test/java/org/apache/paimon/io/RollingFileWriterTest.java
+++ b/paimon-core/src/test/java/org/apache/paimon/io/RollingFileWriterTest.java
@@ -35,6 +35,7 @@ import org.apache.paimon.utils.LongCounter;
import org.apache.paimon.utils.StatsCollectorFactories;
import org.apache.hadoop.hive.ql.exec.vector.VectorizedRowBatch;
+import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
@@ -62,6 +63,10 @@ public class RollingFileWriterTest {
private RollingFileWriter<InternalRow, DataFileMeta> rollingFileWriter;
public void initialize(String identifier) {
+ initialize(identifier, false);
+ }
+
+ public void initialize(String identifier, boolean statsDenseStore) {
FileFormat fileFormat = FileFormat.fromIdentifier(identifier, new
Options());
rollingFileWriter =
new RollingFileWriter<>(
@@ -94,7 +99,8 @@ public class RollingFileWriterTest {
SCHEMA.getFieldNames()),
new FileIndexOptions(),
FileSource.APPEND,
- true),
+ true,
+ statsDenseStore),
TARGET_FILE_SIZE);
}
@@ -123,4 +129,16 @@ public class RollingFileWriterTest {
File[] files = dataDir.listFiles();
assertThat(files).isNotNull().hasSize(expected);
}
+
+ @Test
+ public void testStatsDenseStore() throws IOException {
+ initialize("parquet", true);
+ for (int i = 0; i < 1000; i++) {
+ rollingFileWriter.write(GenericRow.of(i));
+ }
+ rollingFileWriter.close();
+ DataFileMeta file = rollingFileWriter.result().get(0);
+ assertThat(file.valueStatsCols()).isNull();
+
assertThat(file.valueStats().minValues().getFieldCount()).isEqualTo(SCHEMA.getFieldCount());
+ }
}
diff --git
a/paimon-core/src/test/java/org/apache/paimon/manifest/ManifestCommittableSerializerCompatibilityTest.java
b/paimon-core/src/test/java/org/apache/paimon/manifest/ManifestCommittableSerializerCompatibilityTest.java
index 744904c71..bd272b745 100644
---
a/paimon-core/src/test/java/org/apache/paimon/manifest/ManifestCommittableSerializerCompatibilityTest.java
+++
b/paimon-core/src/test/java/org/apache/paimon/manifest/ManifestCommittableSerializerCompatibilityTest.java
@@ -74,7 +74,8 @@ public class ManifestCommittableSerializerCompatibilityTest {
Timestamp.fromLocalDateTime(LocalDateTime.parse("2022-03-02T20:20:12")),
11L,
new byte[] {1, 2, 4},
- FileSource.COMPACT);
+ FileSource.COMPACT,
+ Arrays.asList("field1", "field2", "field3"));
List<DataFileMeta> dataFiles = Collections.singletonList(dataFile);
LinkedHashMap<String, Pair<Integer, Integer>> dvRanges = new
LinkedHashMap<>();
@@ -105,6 +106,76 @@ public class
ManifestCommittableSerializerCompatibilityTest {
assertThat(deserialized).isEqualTo(manifestCommittable);
}
+ @Test
+ public void testCompatibilityToVersion3() throws IOException {
+ SimpleStats keyStats =
+ new SimpleStats(
+ singleColumn("min_key"),
+ singleColumn("max_key"),
+ fromLongArray(new Long[] {0L}));
+ SimpleStats valueStats =
+ new SimpleStats(
+ singleColumn("min_value"),
+ singleColumn("max_value"),
+ fromLongArray(new Long[] {0L}));
+ DataFileMeta dataFile =
+ new DataFileMeta(
+ "my_file",
+ 1024 * 1024,
+ 1024,
+ singleColumn("min_key"),
+ singleColumn("max_key"),
+ keyStats,
+ valueStats,
+ 15,
+ 200,
+ 5,
+ 3,
+ Arrays.asList("extra1", "extra2"),
+
Timestamp.fromLocalDateTime(LocalDateTime.parse("2022-03-02T20:20:12")),
+ 11L,
+ new byte[] {1, 2, 4},
+ FileSource.COMPACT,
+ null);
+ List<DataFileMeta> dataFiles = Collections.singletonList(dataFile);
+
+ LinkedHashMap<String, Pair<Integer, Integer>> dvRanges = new
LinkedHashMap<>();
+ dvRanges.put("dv_key1", Pair.of(1, 2));
+ dvRanges.put("dv_key2", Pair.of(3, 4));
+ IndexFileMeta indexFile =
+ new IndexFileMeta("my_index_type", "my_index_file", 1024 *
100, 1002, dvRanges);
+ List<IndexFileMeta> indexFiles = Collections.singletonList(indexFile);
+
+ CommitMessageImpl commitMessage =
+ new CommitMessageImpl(
+ singleColumn("my_partition"),
+ 11,
+ new DataIncrement(dataFiles, dataFiles, dataFiles),
+ new CompactIncrement(dataFiles, dataFiles, dataFiles),
+ new IndexIncrement(indexFiles));
+
+ ManifestCommittable manifestCommittable =
+ new ManifestCommittable(
+ 5,
+ 202020L,
+ Collections.singletonMap(5, 555L),
+ Collections.singletonList(commitMessage));
+
+ ManifestCommittableSerializer serializer = new
ManifestCommittableSerializer();
+ byte[] bytes = serializer.serialize(manifestCommittable);
+ ManifestCommittable deserialized = serializer.deserialize(3, bytes);
+ assertThat(deserialized).isEqualTo(manifestCommittable);
+
+ byte[] v2Bytes =
+ IOUtils.readFully(
+ ManifestCommittableSerializerCompatibilityTest.class
+ .getClassLoader()
+
.getResourceAsStream("compatibility/manifest-committable-v3"),
+ true);
+ deserialized = serializer.deserialize(2, v2Bytes);
+ assertThat(deserialized).isEqualTo(manifestCommittable);
+ }
+
@Test
public void testCompatibilityToVersion2() throws IOException {
SimpleStats keyStats =
@@ -134,6 +205,7 @@ public class ManifestCommittableSerializerCompatibilityTest
{
Timestamp.fromLocalDateTime(LocalDateTime.parse("2022-03-02T20:20:12")),
11L,
new byte[] {1, 2, 4},
+ null,
null);
List<DataFileMeta> dataFiles = Collections.singletonList(dataFile);
@@ -203,6 +275,7 @@ public class ManifestCommittableSerializerCompatibilityTest
{
Timestamp.fromLocalDateTime(LocalDateTime.parse("2022-03-02T20:20:12")),
null,
null,
+ null,
null);
List<DataFileMeta> dataFiles = Collections.singletonList(dataFile);
diff --git
a/paimon-core/src/test/java/org/apache/paimon/manifest/ManifestCommittableSerializerTest.java
b/paimon-core/src/test/java/org/apache/paimon/manifest/ManifestCommittableSerializerTest.java
index 099c38003..c179a2c0a 100644
---
a/paimon-core/src/test/java/org/apache/paimon/manifest/ManifestCommittableSerializerTest.java
+++
b/paimon-core/src/test/java/org/apache/paimon/manifest/ManifestCommittableSerializerTest.java
@@ -117,6 +117,7 @@ public class ManifestCommittableSerializerTest {
level,
0L,
null,
- FileSource.APPEND);
+ FileSource.APPEND,
+ null);
}
}
diff --git
a/paimon-core/src/test/java/org/apache/paimon/manifest/ManifestFileMetaTest.java
b/paimon-core/src/test/java/org/apache/paimon/manifest/ManifestFileMetaTest.java
index c2a7f821d..c3d6cc3be 100644
---
a/paimon-core/src/test/java/org/apache/paimon/manifest/ManifestFileMetaTest.java
+++
b/paimon-core/src/test/java/org/apache/paimon/manifest/ManifestFileMetaTest.java
@@ -300,13 +300,13 @@ public class ManifestFileMetaTest extends
ManifestFileMetaTestBase {
// case5:the sizes of some manifest files are greater than
suggestedMetaSize, while the
// sizes of other manifest files is less than. All manifest files have
no delete files
input.clear();
- input.addAll(Arrays.asList(manifest1, manifest2, manifest3));
+ input.addAll(Arrays.asList(manifest1, manifest2, manifest3,
manifest4));
List<ManifestFileMeta> newMetas5 = new ArrayList<>();
List<ManifestFileMeta> fullCompacted5 =
ManifestFileMerger.tryFullCompaction(
input, newMetas5, manifestFile, 1800, 100,
getPartitionType(), null)
.get();
- assertThat(fullCompacted5.size()).isEqualTo(2);
+ assertThat(fullCompacted5.size()).isEqualTo(3);
assertThat(newMetas5.size()).isEqualTo(1);
// trigger full compaction
diff --git
a/paimon-core/src/test/java/org/apache/paimon/manifest/ManifestFileMetaTestBase.java
b/paimon-core/src/test/java/org/apache/paimon/manifest/ManifestFileMetaTestBase.java
index a40d0538f..31be9346c 100644
---
a/paimon-core/src/test/java/org/apache/paimon/manifest/ManifestFileMetaTestBase.java
+++
b/paimon-core/src/test/java/org/apache/paimon/manifest/ManifestFileMetaTestBase.java
@@ -94,7 +94,8 @@ public abstract class ManifestFileMetaTestBase {
Timestamp.fromEpochMillis(200000),
0L, // not used
embeddedIndex, // not used
- FileSource.APPEND));
+ FileSource.APPEND,
+ null));
}
protected ManifestFileMeta makeManifest(ManifestEntry... entries) {
@@ -272,6 +273,7 @@ public abstract class ManifestFileMetaTestBase {
0, // not used
0L,
null,
- FileSource.APPEND));
+ FileSource.APPEND,
+ null));
}
}
diff --git
a/paimon-core/src/test/java/org/apache/paimon/manifest/ManifestTestDataGenerator.java
b/paimon-core/src/test/java/org/apache/paimon/manifest/ManifestTestDataGenerator.java
index 5294436ae..0283eda85 100644
---
a/paimon-core/src/test/java/org/apache/paimon/manifest/ManifestTestDataGenerator.java
+++
b/paimon-core/src/test/java/org/apache/paimon/manifest/ManifestTestDataGenerator.java
@@ -113,7 +113,7 @@ public class ManifestTestDataGenerator {
entries.size() * 100L,
numAddedFiles,
numDeletedFiles,
- serializer.toBinary(collector.extract()),
+ serializer.toBinaryAllMode(collector.extract()),
0);
}
diff --git
a/paimon-core/src/test/java/org/apache/paimon/mergetree/LevelsTest.java
b/paimon-core/src/test/java/org/apache/paimon/mergetree/LevelsTest.java
index a86aa445b..d804c9790 100644
--- a/paimon-core/src/test/java/org/apache/paimon/mergetree/LevelsTest.java
+++ b/paimon-core/src/test/java/org/apache/paimon/mergetree/LevelsTest.java
@@ -83,6 +83,7 @@ public class LevelsTest {
level,
0L,
null,
- FileSource.APPEND);
+ FileSource.APPEND,
+ null);
}
}
diff --git
a/paimon-core/src/test/java/org/apache/paimon/mergetree/compact/IntervalPartitionTest.java
b/paimon-core/src/test/java/org/apache/paimon/mergetree/compact/IntervalPartitionTest.java
index 05fce451a..bdee5c5f7 100644
---
a/paimon-core/src/test/java/org/apache/paimon/mergetree/compact/IntervalPartitionTest.java
+++
b/paimon-core/src/test/java/org/apache/paimon/mergetree/compact/IntervalPartitionTest.java
@@ -183,7 +183,8 @@ public class IntervalPartitionTest {
Timestamp.fromEpochMillis(100000),
0L,
null,
- FileSource.APPEND);
+ FileSource.APPEND,
+ null);
}
private List<Map<SortedRun, Integer>> toMultiset(List<List<SortedRun>>
sections) {
diff --git
a/paimon-core/src/test/java/org/apache/paimon/mergetree/compact/UniversalCompactionTest.java
b/paimon-core/src/test/java/org/apache/paimon/mergetree/compact/UniversalCompactionTest.java
index 25d263a93..793aea759 100644
---
a/paimon-core/src/test/java/org/apache/paimon/mergetree/compact/UniversalCompactionTest.java
+++
b/paimon-core/src/test/java/org/apache/paimon/mergetree/compact/UniversalCompactionTest.java
@@ -359,6 +359,6 @@ public class UniversalCompactionTest {
static DataFileMeta file(long size) {
return new DataFileMeta(
- "", size, 1, null, null, null, null, 0, 0, 0, 0, 0L, null,
FileSource.APPEND);
+ "", size, 1, null, null, null, null, 0, 0, 0, 0, 0L, null,
FileSource.APPEND, null);
}
}
diff --git
a/paimon-core/src/test/java/org/apache/paimon/operation/ExpireSnapshotsTest.java
b/paimon-core/src/test/java/org/apache/paimon/operation/ExpireSnapshotsTest.java
index 16a2ab56b..739d4b6bd 100644
---
a/paimon-core/src/test/java/org/apache/paimon/operation/ExpireSnapshotsTest.java
+++
b/paimon-core/src/test/java/org/apache/paimon/operation/ExpireSnapshotsTest.java
@@ -211,7 +211,8 @@ public class ExpireSnapshotsTest {
Timestamp.now(),
0L,
null,
- FileSource.APPEND);
+ FileSource.APPEND,
+ null);
ManifestEntry add = new ManifestEntry(FileKind.ADD, partition, 0, 1,
dataFile);
ManifestEntry delete = new ManifestEntry(FileKind.DELETE, partition,
0, 1, dataFile);
diff --git
a/paimon-core/src/test/java/org/apache/paimon/stats/SimpleStatsConverterTest.java
b/paimon-core/src/test/java/org/apache/paimon/stats/SimpleStatsEvolutionTest.java
similarity index 91%
rename from
paimon-core/src/test/java/org/apache/paimon/stats/SimpleStatsConverterTest.java
rename to
paimon-core/src/test/java/org/apache/paimon/stats/SimpleStatsEvolutionTest.java
index a88276c50..af4fb78b2 100644
---
a/paimon-core/src/test/java/org/apache/paimon/stats/SimpleStatsConverterTest.java
+++
b/paimon-core/src/test/java/org/apache/paimon/stats/SimpleStatsEvolutionTest.java
@@ -36,8 +36,8 @@ import java.util.Collections;
import static org.apache.paimon.io.DataFileTestUtils.row;
import static org.assertj.core.api.Assertions.assertThat;
-/** Tests for {@link SimpleStatsConverter}. */
-public class SimpleStatsConverterTest {
+/** Tests for {@link SimpleStatsEvolution}. */
+public class SimpleStatsEvolutionTest {
@Test
public void testFromBinary() {
@@ -73,8 +73,8 @@ public class SimpleStatsConverterTest {
SchemaEvolutionUtil.createIndexCastMapping(
tableSchema.fields(), dataSchema.fields());
int[] indexMapping = indexCastMapping.getIndexMapping();
- SimpleStatsConverter serializer =
- new SimpleStatsConverter(
+ SimpleStatsEvolution evolution =
+ new SimpleStatsEvolution(
tableSchema.logicalRowType(),
indexMapping,
indexCastMapping.getCastMapping());
@@ -83,10 +83,10 @@ public class SimpleStatsConverterTest {
Long[] nullCounts = new Long[] {1L, 0L, 10L, 100L};
SimpleStats stats =
new SimpleStats(minRowData, maxRowData,
BinaryArray.fromLongArray(nullCounts));
-
- InternalRow min = serializer.evolution(stats.minValues());
- InternalRow max = serializer.evolution(stats.maxValues());
- InternalArray nulls = serializer.evolution(stats.nullCounts(), 1000L);
+ SimpleStatsEvolution.Result result = evolution.evolution(stats, 1000L,
null);
+ InternalRow min = result.minValues();
+ InternalRow max = result.maxValues();
+ InternalArray nulls = result.nullCounts();
checkFieldStats(min, max, nulls, 0, 2, 99, 0L);
checkFieldStats(min, max, nulls, 1, 4, 97, 100L);
diff --git
a/paimon-core/src/test/java/org/apache/paimon/stats/StatsTestUtils.java
b/paimon-core/src/test/java/org/apache/paimon/stats/StatsTestUtils.java
index 0b2081579..20c1dc8bc 100644
--- a/paimon-core/src/test/java/org/apache/paimon/stats/StatsTestUtils.java
+++ b/paimon-core/src/test/java/org/apache/paimon/stats/StatsTestUtils.java
@@ -94,11 +94,12 @@ public class StatsTestUtils {
for (int i = 0; i < fieldCount; i++) {
array[i] = new SimpleColStats(null, null, 0L);
}
- return statsConverter.toBinary(array);
+ return statsConverter.toBinaryAllMode(array);
}
public static SimpleStats newSimpleStats(int min, int max) {
SimpleStatsConverter statsConverter = new
SimpleStatsConverter(RowType.of(new IntType()));
- return statsConverter.toBinary(new SimpleColStats[] {new
SimpleColStats(min, max, 0L)});
+ return statsConverter.toBinaryAllMode(
+ new SimpleColStats[] {new SimpleColStats(min, max, 0L)});
}
}
diff --git
a/paimon-core/src/test/java/org/apache/paimon/table/AppendOnlyFileStoreTableTest.java
b/paimon-core/src/test/java/org/apache/paimon/table/AppendOnlyFileStoreTableTest.java
index 102477fe4..e6503cc40 100644
---
a/paimon-core/src/test/java/org/apache/paimon/table/AppendOnlyFileStoreTableTest.java
+++
b/paimon-core/src/test/java/org/apache/paimon/table/AppendOnlyFileStoreTableTest.java
@@ -58,6 +58,8 @@ import org.apache.paimon.types.RowType;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
import java.util.ArrayList;
import java.util.Arrays;
@@ -220,10 +222,19 @@ public class AppendOnlyFileStoreTableTest extends
FileStoreTableTestBase {
.hasSameElementsAs(Arrays.asList("200|20", "201|21", "202|22",
"201|21"));
}
- @Test
- public void testBatchFilter() throws Exception {
- writeData();
- FileStoreTable table = createFileStoreTable();
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ public void testBatchFilter(boolean statsDenseStore) throws Exception {
+ Consumer<Options> optionsSetter =
+ options -> {
+ options.set(CoreOptions.METADATA_STATS_DENSE_STORE,
statsDenseStore);
+ if (statsDenseStore) {
+ options.set(CoreOptions.METADATA_STATS_MODE, "none");
+ options.set("fields.b.stats-mode", "full");
+ }
+ };
+ writeData(optionsSetter);
+ FileStoreTable table = createFileStoreTable(optionsSetter);
PredicateBuilder builder = new
PredicateBuilder(table.schema().logicalRowType());
Predicate predicate = builder.equal(2, 201L);
@@ -858,7 +869,11 @@ public class AppendOnlyFileStoreTableTest extends
FileStoreTableTestBase {
}
private void writeData() throws Exception {
- FileStoreTable table = createFileStoreTable();
+ writeData(options -> {});
+ }
+
+ private void writeData(Consumer<Options> optionsSetter) throws Exception {
+ FileStoreTable table = createFileStoreTable(optionsSetter);
StreamTableWrite write = table.newWrite(commitUser);
StreamTableCommit commit = table.newCommit(commitUser);
diff --git
a/paimon-core/src/test/java/org/apache/paimon/table/ColumnTypeFileMetaTestBase.java
b/paimon-core/src/test/java/org/apache/paimon/table/ColumnTypeFileMetaTestBase.java
index 12544a093..7f264fa81 100644
---
a/paimon-core/src/test/java/org/apache/paimon/table/ColumnTypeFileMetaTestBase.java
+++
b/paimon-core/src/test/java/org/apache/paimon/table/ColumnTypeFileMetaTestBase.java
@@ -26,8 +26,8 @@ import org.apache.paimon.predicate.Predicate;
import org.apache.paimon.predicate.PredicateBuilder;
import org.apache.paimon.schema.TableSchema;
import org.apache.paimon.stats.SimpleStats;
-import org.apache.paimon.stats.SimpleStatsConverter;
-import org.apache.paimon.stats.SimpleStatsConverters;
+import org.apache.paimon.stats.SimpleStatsEvolution;
+import org.apache.paimon.stats.SimpleStatsEvolutions;
import org.apache.paimon.table.source.DataSplit;
import org.apache.paimon.types.DataField;
@@ -236,12 +236,13 @@ public abstract class ColumnTypeFileMetaTestBase extends
SchemaEvolutionTableTes
List<String> filesName,
List<DataFileMeta> fileMetaList) {
Function<Long, List<DataField>> schemaFields = id ->
tableSchemas.get(id).fields();
- SimpleStatsConverters converters = new
SimpleStatsConverters(schemaFields, schemaId);
+ SimpleStatsEvolutions converters = new
SimpleStatsEvolutions(schemaFields, schemaId);
for (DataFileMeta fileMeta : fileMetaList) {
SimpleStats stats = getTableValueStats(fileMeta);
- SimpleStatsConverter serializer =
converters.getOrCreate(fileMeta.schemaId());
- InternalRow min = serializer.evolution(stats.minValues());
- InternalRow max = serializer.evolution(stats.maxValues());
+ SimpleStatsEvolution.Result result =
+
converters.getOrCreate(fileMeta.schemaId()).evolution(stats, null, null);
+ InternalRow min = result.minValues();
+ InternalRow max = result.maxValues();
assertThat(stats.minValues().getFieldCount()).isEqualTo(12);
if (filesName.contains(fileMeta.fileName())) {
checkTwoValues(min, max);
diff --git
a/paimon-core/src/test/java/org/apache/paimon/table/FileMetaFilterTestBase.java
b/paimon-core/src/test/java/org/apache/paimon/table/FileMetaFilterTestBase.java
index 3d1ebb3d5..182a42fe4 100644
---
a/paimon-core/src/test/java/org/apache/paimon/table/FileMetaFilterTestBase.java
+++
b/paimon-core/src/test/java/org/apache/paimon/table/FileMetaFilterTestBase.java
@@ -23,8 +23,8 @@ import org.apache.paimon.io.DataFileMeta;
import org.apache.paimon.predicate.Predicate;
import org.apache.paimon.predicate.PredicateBuilder;
import org.apache.paimon.stats.SimpleStats;
-import org.apache.paimon.stats.SimpleStatsConverter;
-import org.apache.paimon.stats.SimpleStatsConverters;
+import org.apache.paimon.stats.SimpleStatsEvolution;
+import org.apache.paimon.stats.SimpleStatsEvolutions;
import org.apache.paimon.table.source.DataSplit;
import org.apache.paimon.types.DataField;
@@ -78,14 +78,16 @@ public abstract class FileMetaFilterTestBase extends
SchemaEvolutionTableTestBas
.containsAll(filesName);
Function<Long, List<DataField>> schemaFields = id ->
schemas.get(id).fields();
- SimpleStatsConverters converters =
- new SimpleStatsConverters(schemaFields,
table.schema().id());
+ SimpleStatsEvolutions converters =
+ new SimpleStatsEvolutions(schemaFields,
table.schema().id());
for (DataFileMeta fileMeta : fileMetaList) {
SimpleStats stats = getTableValueStats(fileMeta);
- SimpleStatsConverter serializer =
- converters.getOrCreate(fileMeta.schemaId());
- InternalRow min =
serializer.evolution(stats.minValues());
- InternalRow max =
serializer.evolution(stats.maxValues());
+ SimpleStatsEvolution.Result result =
+ converters
+ .getOrCreate(fileMeta.schemaId())
+ .evolution(stats, 100L, null);
+ InternalRow min = result.minValues();
+ InternalRow max = result.maxValues();
assertThat(min.getFieldCount()).isEqualTo(6);
@@ -184,16 +186,18 @@ public abstract class FileMetaFilterTestBase extends
SchemaEvolutionTableTestBas
assertThat(filterAllSplits).isEqualTo(allSplits);
Function<Long, List<DataField>> schemaFields = id ->
schemas.get(id).fields();
- SimpleStatsConverters converters =
- new SimpleStatsConverters(schemaFields,
table.schema().id());
+ SimpleStatsEvolutions converters =
+ new SimpleStatsEvolutions(schemaFields,
table.schema().id());
Set<String> filterFileNames = new HashSet<>();
for (DataSplit dataSplit : filterAllSplits) {
for (DataFileMeta dataFileMeta :
dataSplit.dataFiles()) {
SimpleStats stats =
getTableValueStats(dataFileMeta);
- SimpleStatsConverter serializer =
-
converters.getOrCreate(dataFileMeta.schemaId());
- InternalRow min =
serializer.evolution(stats.minValues());
- InternalRow max =
serializer.evolution(stats.maxValues());
+ SimpleStatsEvolution.Result result =
+ converters
+
.getOrCreate(dataFileMeta.schemaId())
+ .evolution(stats, 100L, null);
+ InternalRow min = result.minValues();
+ InternalRow max = result.maxValues();
int minValue = min.getInt(1);
int maxValue = max.getInt(1);
if (minValue >= 14
@@ -263,15 +267,17 @@ public abstract class FileMetaFilterTestBase extends
SchemaEvolutionTableTestBas
Set<String> filterFileNames = new HashSet<>();
Function<Long, List<DataField>> schemaFields = id ->
schemas.get(id).fields();
- SimpleStatsConverters converters =
- new SimpleStatsConverters(schemaFields,
table.schema().id());
+ SimpleStatsEvolutions converters =
+ new SimpleStatsEvolutions(schemaFields,
table.schema().id());
for (DataSplit dataSplit : allSplits) {
for (DataFileMeta dataFileMeta :
dataSplit.dataFiles()) {
SimpleStats stats =
getTableValueStats(dataFileMeta);
- SimpleStatsConverter serializer =
-
converters.getOrCreate(dataFileMeta.schemaId());
- InternalRow min =
serializer.evolution(stats.minValues());
- InternalRow max =
serializer.evolution(stats.maxValues());
+ SimpleStatsEvolution.Result result =
+ converters
+
.getOrCreate(dataFileMeta.schemaId())
+ .evolution(stats, 100L, null);
+ InternalRow min = result.minValues();
+ InternalRow max = result.maxValues();
Integer minValue = min.isNullAt(3) ? null :
min.getInt(3);
Integer maxValue = max.isNullAt(3) ? null :
max.getInt(3);
if (minValue != null
diff --git
a/paimon-core/src/test/java/org/apache/paimon/table/PrimaryKeyFileStoreTableTest.java
b/paimon-core/src/test/java/org/apache/paimon/table/PrimaryKeyFileStoreTableTest.java
index 7edc41818..1ecfd6f91 100644
---
a/paimon-core/src/test/java/org/apache/paimon/table/PrimaryKeyFileStoreTableTest.java
+++
b/paimon-core/src/test/java/org/apache/paimon/table/PrimaryKeyFileStoreTableTest.java
@@ -359,10 +359,20 @@ public class PrimaryKeyFileStoreTableTest extends
FileStoreTableTestBase {
.isEqualTo(Arrays.asList("20001|21", "202|22"));
}
- @Test
- public void testBatchFilter() throws Exception {
- writeData();
- FileStoreTable table = createFileStoreTable();
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ public void testBatchFilter(boolean statsDenseStore) throws Exception {
+ Consumer<Options> optionsSetter =
+ options -> {
+ options.set(CoreOptions.METADATA_STATS_DENSE_STORE,
statsDenseStore);
+ if (statsDenseStore) {
+ // pk table doesn't need value stats
+ options.set(CoreOptions.METADATA_STATS_MODE, "none");
+ }
+ };
+ writeData(optionsSetter);
+ FileStoreTable table = createFileStoreTable(optionsSetter);
+
PredicateBuilder builder = new
PredicateBuilder(table.schema().logicalRowType());
Predicate predicate = and(builder.equal(2, 201L), builder.equal(1,
21));
@@ -604,7 +614,11 @@ public class PrimaryKeyFileStoreTableTest extends
FileStoreTableTestBase {
}
private void writeData() throws Exception {
- FileStoreTable table = createFileStoreTable();
+ writeData(options -> {});
+ }
+
+ private void writeData(Consumer<Options> optionsSetter) throws Exception {
+ FileStoreTable table = createFileStoreTable(optionsSetter);
StreamTableWrite write = table.newWrite(commitUser);
StreamTableCommit commit = table.newCommit(commitUser);
@@ -1416,15 +1430,23 @@ public class PrimaryKeyFileStoreTableTest extends
FileStoreTableTestBase {
.hasMessage("Unsupported streaming scan for read optimized
table");
}
- @Test
- public void testReadDeletionVectorTable() throws Exception {
- FileStoreTable table =
- createFileStoreTable(
- options -> {
- // let level has many files
- options.set(TARGET_FILE_SIZE, new MemorySize(1));
- options.set(DELETION_VECTORS_ENABLED, true);
- });
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ public void testReadDeletionVectorTable(boolean statsDenseStore) throws
Exception {
+ Consumer<Options> optionsSetter =
+ options -> {
+ // let level has many files
+ options.set(TARGET_FILE_SIZE, new MemorySize(1));
+ options.set(DELETION_VECTORS_ENABLED, true);
+
+ options.set(CoreOptions.METADATA_STATS_DENSE_STORE,
statsDenseStore);
+ if (statsDenseStore) {
+ options.set(CoreOptions.METADATA_STATS_MODE, "none");
+ options.set("fields.b.stats-mode", "full");
+ }
+ };
+
+ FileStoreTable table = createFileStoreTable(optionsSetter);
StreamTableWrite write = table.newWrite(commitUser);
IOManager ioManager = IOManager.create(tablePath.toString());
write.withIOManager(ioManager);
diff --git
a/paimon-core/src/test/java/org/apache/paimon/table/PrimaryKeyTableColumnTypeFileMetaTest.java
b/paimon-core/src/test/java/org/apache/paimon/table/PrimaryKeyTableColumnTypeFileMetaTest.java
index 45b67842b..489c1ba05 100644
---
a/paimon-core/src/test/java/org/apache/paimon/table/PrimaryKeyTableColumnTypeFileMetaTest.java
+++
b/paimon-core/src/test/java/org/apache/paimon/table/PrimaryKeyTableColumnTypeFileMetaTest.java
@@ -26,8 +26,8 @@ import org.apache.paimon.predicate.PredicateBuilder;
import org.apache.paimon.schema.SchemaManager;
import org.apache.paimon.schema.TableSchema;
import org.apache.paimon.stats.SimpleStats;
-import org.apache.paimon.stats.SimpleStatsConverter;
-import org.apache.paimon.stats.SimpleStatsConverters;
+import org.apache.paimon.stats.SimpleStatsEvolution;
+import org.apache.paimon.stats.SimpleStatsEvolutions;
import org.apache.paimon.table.source.DataSplit;
import org.apache.paimon.types.DataField;
@@ -122,12 +122,13 @@ public class PrimaryKeyTableColumnTypeFileMetaTest
extends ColumnTypeFileMetaTes
List<DataFileMeta> fileMetaList) {
Function<Long, List<DataField>> schemaFields =
id ->
tableSchemas.get(id).logicalTrimmedPrimaryKeysType().getFields();
- SimpleStatsConverters converters = new
SimpleStatsConverters(schemaFields, schemaId);
+ SimpleStatsEvolutions converters = new
SimpleStatsEvolutions(schemaFields, schemaId);
for (DataFileMeta fileMeta : fileMetaList) {
SimpleStats stats = getTableValueStats(fileMeta);
- SimpleStatsConverter serializer =
converters.getOrCreate(fileMeta.schemaId());
- InternalRow min = serializer.evolution(stats.minValues());
- InternalRow max = serializer.evolution(stats.maxValues());
+ SimpleStatsEvolution.Result result =
+
converters.getOrCreate(fileMeta.schemaId()).evolution(stats, null, null);
+ InternalRow min = result.minValues();
+ InternalRow max = result.maxValues();
assertThat(min.getFieldCount()).isEqualTo(4);
if (filesName.contains(fileMeta.fileName())) {
// parquet does not support padding
diff --git
a/paimon-core/src/test/java/org/apache/paimon/table/source/SplitGeneratorTest.java
b/paimon-core/src/test/java/org/apache/paimon/table/source/SplitGeneratorTest.java
index 14faaa671..a9e093dab 100644
---
a/paimon-core/src/test/java/org/apache/paimon/table/source/SplitGeneratorTest.java
+++
b/paimon-core/src/test/java/org/apache/paimon/table/source/SplitGeneratorTest.java
@@ -58,7 +58,8 @@ public class SplitGeneratorTest {
0,
0L,
null,
- FileSource.APPEND);
+ FileSource.APPEND,
+ null);
}
@Test
diff --git
a/paimon-core/src/test/java/org/apache/paimon/table/source/SplitTest.java
b/paimon-core/src/test/java/org/apache/paimon/table/source/SplitTest.java
index 4bc9b6f8b..c64a12ffa 100644
--- a/paimon-core/src/test/java/org/apache/paimon/table/source/SplitTest.java
+++ b/paimon-core/src/test/java/org/apache/paimon/table/source/SplitTest.java
@@ -26,6 +26,7 @@ import org.apache.paimon.io.DataFileMeta;
import org.apache.paimon.io.DataFileTestDataGenerator;
import org.apache.paimon.io.DataInputDeserializer;
import org.apache.paimon.io.DataOutputViewStreamWrapper;
+import org.apache.paimon.manifest.FileSource;
import org.apache.paimon.stats.SimpleStats;
import org.apache.paimon.utils.IOUtils;
import org.apache.paimon.utils.InstantiationUtil;
@@ -73,7 +74,7 @@ public class SplitTest {
}
@Test
- public void testSerializerCompatible() throws Exception {
+ public void testSerializerNormal() throws Exception {
SimpleStats keyStats =
new SimpleStats(
singleColumn("min_key"),
@@ -102,6 +103,58 @@ public class SplitTest {
Timestamp.fromLocalDateTime(LocalDateTime.parse("2022-03-02T20:20:12")),
11L,
new byte[] {1, 2, 4},
+ FileSource.COMPACT,
+ Arrays.asList("field1", "field2", "field3"));
+ List<DataFileMeta> dataFiles = Collections.singletonList(dataFile);
+
+ BinaryRow partition = new BinaryRow(1);
+ BinaryRowWriter binaryRowWriter = new BinaryRowWriter(partition);
+ binaryRowWriter.writeString(0, BinaryString.fromString("aaaaa"));
+ binaryRowWriter.complete();
+
+ DataSplit split =
+ DataSplit.builder()
+ .withSnapshot(18)
+ .withPartition(partition)
+ .withBucket(20)
+ .withDataFiles(dataFiles)
+ .withBucketPath("my path")
+ .build();
+
+ assertThat(InstantiationUtil.clone(split)).isEqualTo(split);
+ }
+
+ @Test
+ public void testSerializerCompatibleV1() throws Exception {
+ SimpleStats keyStats =
+ new SimpleStats(
+ singleColumn("min_key"),
+ singleColumn("max_key"),
+ fromLongArray(new Long[] {0L}));
+ SimpleStats valueStats =
+ new SimpleStats(
+ singleColumn("min_value"),
+ singleColumn("max_value"),
+ fromLongArray(new Long[] {0L}));
+
+ DataFileMeta dataFile =
+ new DataFileMeta(
+ "my_file",
+ 1024 * 1024,
+ 1024,
+ singleColumn("min_key"),
+ singleColumn("max_key"),
+ keyStats,
+ valueStats,
+ 15,
+ 200,
+ 5,
+ 3,
+ Arrays.asList("extra1", "extra2"),
+
Timestamp.fromLocalDateTime(LocalDateTime.parse("2022-03-02T20:20:12")),
+ 11L,
+ new byte[] {1, 2, 4},
+ null,
null);
List<DataFileMeta> dataFiles = Collections.singletonList(dataFile);
@@ -130,4 +183,64 @@ public class SplitTest {
InstantiationUtil.deserializeObject(v2Bytes,
DataSplit.class.getClassLoader());
assertThat(actual).isEqualTo(split);
}
+
+ @Test
+ public void testSerializerCompatibleV2() throws Exception {
+ SimpleStats keyStats =
+ new SimpleStats(
+ singleColumn("min_key"),
+ singleColumn("max_key"),
+ fromLongArray(new Long[] {0L}));
+ SimpleStats valueStats =
+ new SimpleStats(
+ singleColumn("min_value"),
+ singleColumn("max_value"),
+ fromLongArray(new Long[] {0L}));
+
+ DataFileMeta dataFile =
+ new DataFileMeta(
+ "my_file",
+ 1024 * 1024,
+ 1024,
+ singleColumn("min_key"),
+ singleColumn("max_key"),
+ keyStats,
+ valueStats,
+ 15,
+ 200,
+ 5,
+ 3,
+ Arrays.asList("extra1", "extra2"),
+
Timestamp.fromLocalDateTime(LocalDateTime.parse("2022-03-02T20:20:12")),
+ 11L,
+ new byte[] {1, 2, 4},
+ FileSource.COMPACT,
+ null);
+ List<DataFileMeta> dataFiles = Collections.singletonList(dataFile);
+
+ BinaryRow partition = new BinaryRow(1);
+ BinaryRowWriter binaryRowWriter = new BinaryRowWriter(partition);
+ binaryRowWriter.writeString(0, BinaryString.fromString("aaaaa"));
+ binaryRowWriter.complete();
+
+ DataSplit split =
+ DataSplit.builder()
+ .withSnapshot(18)
+ .withPartition(partition)
+ .withBucket(20)
+ .withDataFiles(dataFiles)
+ .withBucketPath("my path")
+ .build();
+
+ byte[] v2Bytes =
+ IOUtils.readFully(
+ SplitTest.class
+ .getClassLoader()
+
.getResourceAsStream("compatibility/datasplit-v2"),
+ true);
+
+ DataSplit actual =
+ InstantiationUtil.deserializeObject(v2Bytes,
DataSplit.class.getClassLoader());
+ assertThat(actual).isEqualTo(split);
+ }
}
diff --git a/paimon-core/src/test/resources/compatibility/datasplit-v2
b/paimon-core/src/test/resources/compatibility/datasplit-v2
new file mode 100644
index 000000000..8d2014984
Binary files /dev/null and
b/paimon-core/src/test/resources/compatibility/datasplit-v2 differ
diff --git
a/paimon-core/src/test/resources/compatibility/manifest-committable-v3
b/paimon-core/src/test/resources/compatibility/manifest-committable-v3
new file mode 100644
index 000000000..58ad2983c
Binary files /dev/null and
b/paimon-core/src/test/resources/compatibility/manifest-committable-v3 differ
diff --git
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/sink/CompactionTaskSimpleSerializerTest.java
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/sink/CompactionTaskSimpleSerializerTest.java
index 7cacb6c29..9f6e8159a 100644
---
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/sink/CompactionTaskSimpleSerializerTest.java
+++
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/sink/CompactionTaskSimpleSerializerTest.java
@@ -79,6 +79,7 @@ public class CompactionTaskSimpleSerializerTest {
0,
0L,
null,
- FileSource.APPEND);
+ FileSource.APPEND,
+ null);
}
}
diff --git
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/source/FileStoreSourceSplitGeneratorTest.java
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/source/FileStoreSourceSplitGeneratorTest.java
index ad30f6388..92c8dd94a 100644
---
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/source/FileStoreSourceSplitGeneratorTest.java
+++
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/source/FileStoreSourceSplitGeneratorTest.java
@@ -115,7 +115,8 @@ public class FileStoreSourceSplitGeneratorTest {
0, // not used
0L, // not used
null, // not used
- FileSource.APPEND));
+ FileSource.APPEND,
+ null));
}
return DataSplit.builder()
.withSnapshot(1)
diff --git
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/source/FileStoreSourceSplitSerializerTest.java
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/source/FileStoreSourceSplitSerializerTest.java
index f2c0732a2..9c884cd5c 100644
---
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/source/FileStoreSourceSplitSerializerTest.java
+++
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/source/FileStoreSourceSplitSerializerTest.java
@@ -88,7 +88,8 @@ public class FileStoreSourceSplitSerializerTest {
level,
0L,
null,
- FileSource.APPEND);
+ FileSource.APPEND,
+ null);
}
public static FileStoreSourceSplit newSourceSplit(
diff --git
a/paimon-spark/paimon-spark-common/src/test/scala/org/apache/paimon/spark/ScanHelperTest.scala
b/paimon-spark/paimon-spark-common/src/test/scala/org/apache/paimon/spark/ScanHelperTest.scala
index 637113930..fc787246f 100644
---
a/paimon-spark/paimon-spark-common/src/test/scala/org/apache/paimon/spark/ScanHelperTest.scala
+++
b/paimon-spark/paimon-spark-common/src/test/scala/org/apache/paimon/spark/ScanHelperTest.scala
@@ -26,6 +26,7 @@ import org.apache.paimon.table.source.{DataSplit, Split}
import org.junit.jupiter.api.Assertions
+import java.util
import java.util.{HashMap => JHashMap}
import scala.collection.JavaConverters._
@@ -42,7 +43,18 @@ class ScanHelperTest extends PaimonSparkTestBase {
0.until(fileNum).foreach {
i =>
val path = s"f$i.parquet"
- files += DataFileMeta.forAppend(path, 750000, 30000, null, 0, 29999,
1, FileSource.APPEND)
+ files += DataFileMeta.forAppend(
+ path,
+ 750000,
+ 30000,
+ null,
+ 0,
+ 29999,
+ 1,
+ new java.util.ArrayList[String](),
+ null,
+ FileSource.APPEND,
+ null)
}
val dataSplits = mutable.ArrayBuffer.empty[Split]
@@ -67,7 +79,18 @@ class ScanHelperTest extends PaimonSparkTestBase {
test("Paimon: reshuffle one split") {
val files = List(
- DataFileMeta.forAppend("f1.parquet", 750000, 30000, null, 0, 29999, 1,
FileSource.APPEND)
+ DataFileMeta.forAppend(
+ "f1.parquet",
+ 750000,
+ 30000,
+ null,
+ 0,
+ 29999,
+ 1,
+ new java.util.ArrayList[String](),
+ null,
+ FileSource.APPEND,
+ null)
).asJava
val dataSplits: Array[Split] = Array(