This is an automated email from the ASF dual-hosted git repository.
zhangstar333 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new f895ef03c7a [opt](paimon) support paimon table with
partition.legacy-name in date type (#61076)
f895ef03c7a is described below
commit f895ef03c7acd97fbaa5853bd4983c0c0c05647b
Author: zhangstar333 <[email protected]>
AuthorDate: Tue Mar 10 11:08:58 2026 +0800
[opt](paimon) support paimon table with partition.legacy-name in date type
(#61076)
### What problem does this PR solve?
Problem Summary:
```
mysql> select * from test_partition_legacy_false;
ERROR 1105 (HY000): errCode = 2, detailMessage = failed to load paimon
snapshot 1772691627468.test_partition_legacy.test_partition_legacy_false: For
input string: "2026-03-01"
mysql>
```
when partition.legacy-name = false, the partition date value is
"2026-03-01" not 20513, so can't parse it as int value
---
.../create_preinstalled_scripts/paimon/run12.sql | 54 ++++++++++++++++++++
.../datasource/paimon/PaimonMetadataCache.java | 8 +--
.../apache/doris/datasource/paimon/PaimonUtil.java | 21 ++++++--
.../paimon/paimon_partition_legacy.out | 41 +++++++++++++++
.../paimon/paimon_partition_legacy.groovy | 59 ++++++++++++++++++++++
5 files changed, 176 insertions(+), 7 deletions(-)
diff --git
a/docker/thirdparties/docker-compose/iceberg/scripts/create_preinstalled_scripts/paimon/run12.sql
b/docker/thirdparties/docker-compose/iceberg/scripts/create_preinstalled_scripts/paimon/run12.sql
new file mode 100644
index 00000000000..c7b26a6b4c6
--- /dev/null
+++
b/docker/thirdparties/docker-compose/iceberg/scripts/create_preinstalled_scripts/paimon/run12.sql
@@ -0,0 +1,54 @@
+use paimon;
+create database if not exists test_partition_legacy;
+use test_partition_legacy;
+
+drop table if exists test_partition_legacy_true;
+CREATE TABLE test_partition_legacy_true (
+ dt DATE,
+ user_id BIGINT,
+ event_name STRING,
+ event_value DOUBLE
+) USING paimon
+PARTITIONED BY (dt)
+TBLPROPERTIES (
+ 'primary-key' = 'dt, user_id',
+ 'bucket' = '1',
+ 'merge-engine' = 'deduplicate',
+ 'partition.legacy-name' = 'true'
+);
+
+INSERT INTO test_partition_legacy_true (dt, user_id, event_name, event_value)
VALUES
+ (CAST('2026-02-13' AS DATE), CAST(1001 AS BIGINT), 'click', CAST(1.5 AS
DOUBLE)),
+ (CAST('2026-02-13' AS DATE), CAST(1002 AS BIGINT), 'view', CAST(2.0 AS
DOUBLE)),
+ (CAST('2026-02-13' AS DATE), CAST(1003 AS BIGINT), 'purchase', CAST(99.9
AS DOUBLE)),
+ (CAST('2026-03-01' AS DATE), CAST(2001 AS BIGINT), 'click', CAST(3.0 AS
DOUBLE)),
+ (CAST('2026-03-01' AS DATE), CAST(2002 AS BIGINT), 'view', CAST(4.5 AS
DOUBLE)),
+ (CAST('2026-03-02' AS DATE), CAST(3001 AS BIGINT), 'click', CAST(5.0 AS
DOUBLE)),
+ (CAST('2026-03-02' AS DATE), CAST(3002 AS BIGINT), 'purchase', CAST(188.0
AS DOUBLE)),
+ (CAST('2026-03-02' AS DATE), CAST(3003 AS BIGINT), 'view', CAST(6.5 AS
DOUBLE));
+
+
+drop table if exists test_partition_legacy_false;
+CREATE TABLE test_partition_legacy_false (
+ dt DATE,
+ user_id BIGINT,
+ event_name STRING,
+ event_value DOUBLE
+) USING paimon
+PARTITIONED BY (dt)
+TBLPROPERTIES (
+ 'primary-key' = 'dt, user_id',
+ 'bucket' = '1',
+ 'merge-engine' = 'deduplicate',
+ 'partition.legacy-name' = 'false'
+);
+
+INSERT INTO test_partition_legacy_false (dt, user_id, event_name, event_value)
VALUES
+ (CAST('2026-02-13' AS DATE), CAST(1001 AS BIGINT), 'click', CAST(1.5 AS
DOUBLE)),
+ (CAST('2026-02-13' AS DATE), CAST(1002 AS BIGINT), 'view', CAST(2.0 AS
DOUBLE)),
+ (CAST('2026-02-13' AS DATE), CAST(1003 AS BIGINT), 'purchase', CAST(99.9
AS DOUBLE)),
+ (CAST('2026-03-01' AS DATE), CAST(2001 AS BIGINT), 'click', CAST(3.0 AS
DOUBLE)),
+ (CAST('2026-03-01' AS DATE), CAST(2002 AS BIGINT), 'view', CAST(4.5 AS
DOUBLE)),
+ (CAST('2026-03-02' AS DATE), CAST(3001 AS BIGINT), 'click', CAST(5.0 AS
DOUBLE)),
+ (CAST('2026-03-02' AS DATE), CAST(3002 AS BIGINT), 'purchase', CAST(188.0
AS DOUBLE)),
+ (CAST('2026-03-02' AS DATE), CAST(3003 AS BIGINT), 'view', CAST(6.5 AS
DOUBLE));
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/datasource/paimon/PaimonMetadataCache.java
b/fe/fe-core/src/main/java/org/apache/doris/datasource/paimon/PaimonMetadataCache.java
index 7f118490fdd..222e5b19ea7 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/datasource/paimon/PaimonMetadataCache.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/datasource/paimon/PaimonMetadataCache.java
@@ -105,7 +105,8 @@ public class PaimonMetadataCache {
PaimonSnapshot latestSnapshot = loadLatestSnapshot(paimonTable,
nameMapping);
List<Column> partitionColumns =
getPaimonSchemaCacheValue(nameMapping,
latestSnapshot.getSchemaId()).getPartitionColumns();
- PaimonPartitionInfo partitionInfo = loadPartitionInfo(nameMapping,
partitionColumns);
+ boolean legacyPartitionName =
PaimonUtil.isLegacyPartitionName(paimonTable);
+ PaimonPartitionInfo partitionInfo = loadPartitionInfo(nameMapping,
partitionColumns, legacyPartitionName);
return new PaimonSnapshotCacheValue(partitionInfo, latestSnapshot);
} catch (Exception e) {
throw new CacheException("failed to load paimon snapshot %s.%s.%s:
%s",
@@ -132,7 +133,8 @@ public class PaimonMetadataCache {
return (PaimonSchemaCacheValue) schemaCacheValue.get();
}
- private PaimonPartitionInfo loadPartitionInfo(NameMapping nameMapping,
List<Column> partitionColumns)
+ private PaimonPartitionInfo loadPartitionInfo(NameMapping nameMapping,
List<Column> partitionColumns,
+ boolean legacyPartitionName)
throws AnalysisException {
if (CollectionUtils.isEmpty(partitionColumns)) {
return PaimonPartitionInfo.EMPTY;
@@ -140,7 +142,7 @@ public class PaimonMetadataCache {
PaimonExternalCatalog externalCatalog = (PaimonExternalCatalog)
Env.getCurrentEnv().getCatalogMgr()
.getCatalogOrAnalysisException(nameMapping.getCtlId());
List<Partition> paimonPartitions =
externalCatalog.getPaimonPartitions(nameMapping);
- return PaimonUtil.generatePartitionInfo(partitionColumns,
paimonPartitions);
+ return PaimonUtil.generatePartitionInfo(partitionColumns,
paimonPartitions, legacyPartitionName);
}
private PaimonSnapshot loadLatestSnapshot(Table paimonTable, NameMapping
nameMapping) {
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/datasource/paimon/PaimonUtil.java
b/fe/fe-core/src/main/java/org/apache/doris/datasource/paimon/PaimonUtil.java
index d4c9ddf6c2f..a80da01cf96 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/datasource/paimon/PaimonUtil.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/datasource/paimon/PaimonUtil.java
@@ -105,11 +105,20 @@ public class PaimonUtil {
private static final String SYS_TABLE_TYPE_AUDIT_LOG = "audit_log";
private static final String SYS_TABLE_TYPE_BINLOG = "binlog";
private static final String TABLE_READ_SEQUENCE_NUMBER_ENABLED =
"table-read.sequence-number.enabled";
+ private static final String PARTITION_LEGACY_NAME =
"partition.legacy-name";
public static boolean isDigitalString(String value) {
return value != null && DIGITAL_REGEX.matcher(value).matches();
}
+ /**
+ * Extract the legacy partition name configuration from Paimon table
options.
+ */
+ public static boolean isLegacyPartitionName(Table paimonTable) {
+ return Boolean.parseBoolean(
+ paimonTable.options().getOrDefault(PARTITION_LEGACY_NAME,
"true"));
+ }
+
public static List<InternalRow> read(
Table table, @Nullable int[] projection, @Nullable Predicate
predicate,
Pair<ConfigOption<?>, String>... dynamicOptions)
@@ -141,7 +150,7 @@ public class PaimonUtil {
}
public static PaimonPartitionInfo generatePartitionInfo(List<Column>
partitionColumns,
- List<Partition> paimonPartitions) {
+ List<Partition> paimonPartitions, boolean legacyPartitionName) {
if (CollectionUtils.isEmpty(partitionColumns) ||
paimonPartitions.isEmpty()) {
return PaimonPartitionInfo.EMPTY;
@@ -161,8 +170,11 @@ public class PaimonUtil {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> entry : spec.entrySet()) {
sb.append(entry.getKey()).append("=");
- // Paimon stores DATE type as days since 1970-01-01 (epoch),
so we convert the integer to a date string.
- if (columnNameToType.getOrDefault(entry.getKey(),
Type.NULL).isDateV2()) {
+ // When partition.legacy-name = true (default), Paimon stores
DATE type as days since
+ // 1970-01-01 (epoch integer), so we need to convert the
integer to a date string.
+ // When partition.legacy-name = false, the value is already a
human read date string.
+ if (legacyPartitionName
+ && columnNameToType.getOrDefault(entry.getKey(),
Type.NULL).isDateV2()) {
sb.append(DateTimeUtils.formatDate(Integer.parseInt(entry.getValue()))).append("/");
} else {
sb.append(entry.getValue()).append("/");
@@ -565,7 +577,8 @@ public class PaimonUtil {
return null;
}
return value.toString();
- // case binary, varbinary should not supported, because if return
string with utf8,
+ // case binary:
+ // case varbinary: should not supported, because if return string
with utf8,
// the data maybe be corrupted
case DATE:
if (value == null) {
diff --git
a/regression-test/data/external_table_p0/paimon/paimon_partition_legacy.out
b/regression-test/data/external_table_p0/paimon/paimon_partition_legacy.out
new file mode 100644
index 00000000000..101b6d3e243
--- /dev/null
+++ b/regression-test/data/external_table_p0/paimon/paimon_partition_legacy.out
@@ -0,0 +1,41 @@
+-- This file is automatically generated. You should know what you did if you
want to edit this
+-- !order --
+2026-02-13 1001 click 1.5
+2026-02-13 1002 view 2
+2026-03-01 2001 click 3
+2026-03-01 2002 view 4.5
+2026-03-02 3001 click 5
+2026-03-02 3003 view 6.5
+2026-02-13 1003 purchase 99.90000000000001
+2026-03-02 3002 purchase 188
+
+-- !order --
+2026-02-13 1001 click 1.5
+2026-02-13 1002 view 2
+2026-03-01 2001 click 3
+2026-03-01 2002 view 4.5
+2026-03-02 3001 click 5
+2026-03-02 3003 view 6.5
+2026-02-13 1003 purchase 99.90000000000001
+2026-03-02 3002 purchase 188
+
+-- !order --
+2026-02-13 1001 click 1.5
+2026-02-13 1002 view 2
+2026-03-01 2001 click 3
+2026-03-01 2002 view 4.5
+2026-03-02 3001 click 5
+2026-03-02 3003 view 6.5
+2026-02-13 1003 purchase 99.90000000000001
+2026-03-02 3002 purchase 188
+
+-- !order --
+2026-02-13 1001 click 1.5
+2026-02-13 1002 view 2
+2026-03-01 2001 click 3
+2026-03-01 2002 view 4.5
+2026-03-02 3001 click 5
+2026-03-02 3003 view 6.5
+2026-02-13 1003 purchase 99.90000000000001
+2026-03-02 3002 purchase 188
+
diff --git
a/regression-test/suites/external_table_p0/paimon/paimon_partition_legacy.groovy
b/regression-test/suites/external_table_p0/paimon/paimon_partition_legacy.groovy
new file mode 100644
index 00000000000..0dca3a7ac41
--- /dev/null
+++
b/regression-test/suites/external_table_p0/paimon/paimon_partition_legacy.groovy
@@ -0,0 +1,59 @@
+// 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.
+
+suite("paimon_partition_legacy", "p0,external") {
+
+ logger.info("start paimon test")
+ String enabled = context.config.otherConfigs.get("enablePaimonTest")
+ if (enabled == null || !enabled.equalsIgnoreCase("true")) {
+ logger.info("disabled paimon test")
+ return
+ }
+
+ try {
+ String catalog_name = "test_partition_legacy_catalog"
+ String minio_port =
context.config.otherConfigs.get("iceberg_minio_port")
+ String externalEnvIp = context.config.otherConfigs.get("externalEnvIp")
+
+ sql """drop catalog if exists ${catalog_name}"""
+ sql """CREATE CATALOG ${catalog_name} PROPERTIES (
+ 'type'='paimon',
+ 'warehouse' = 's3://warehouse/wh/',
+ "s3.access_key" = "admin",
+ "s3.secret_key" = "password",
+ "s3.endpoint" = "http://${externalEnvIp}:${minio_port}",
+ "s3.region" = "us-east-1"
+ );"""
+
+ logger.info("catalog " + catalog_name + " created")
+ sql """switch ${catalog_name};"""
+ logger.info("switched to catalog " + catalog_name)
+ sql """use test_partition_legacy;"""
+
+ sql """set force_jni_scanner=true"""
+ qt_order """ select * from test_partition_legacy_true order by
event_value; """
+ qt_order """ select * from test_partition_legacy_false order by
event_value; """
+
+ sql """set force_jni_scanner=false"""
+ qt_order """ select * from test_partition_legacy_true order by
event_value; """
+ qt_order """ select * from test_partition_legacy_false order by
event_value; """
+
+ } finally {
+ sql """set force_jni_scanner=false"""
+ }
+
+}
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]