suxiaogang223 commented on code in PR #65868:
URL: https://github.com/apache/doris/pull/65868#discussion_r3675323476


##########
fe/fe-core/src/main/java/org/apache/doris/mtmv/MTMVPartitionUtil.java:
##########
@@ -361,13 +362,40 @@ public static boolean 
isSyncWithPartitions(MTMVRefreshContext context, String mt
     public static String generatePartitionName(PartitionKeyDesc desc) {
         Matcher matcher = PARTITION_NAME_PATTERN.matcher(desc.toSql());
         String partitionName = PARTITION_NAME_PREFIX + 
matcher.replaceAll("").replaceAll("\\,", "_");
+        // The legacy readable name removes SQL quotes. Without this marker, a 
typed NULL and
+        // the string literal "NULL" both become p_NULL even though they are 
distinct list
+        // partition values. Preserve the existing NULL partition name and 
disambiguate only
+        // the literal value.
+        if (containsLiteralNull(desc)) {

Review Comment:
   Correction to the previous reply: the unit test covers typed NULL, the 
literal string NULL, and the legal string NULL,literal that collided with the 
previous readable suffix.



##########
fe/fe-core/src/main/java/org/apache/doris/datasource/paimon/PaimonWriteTarget.java:
##########
@@ -0,0 +1,123 @@
+// 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.doris.datasource.paimon;
+
+import org.apache.doris.catalog.Column;
+import org.apache.doris.catalog.Type;
+import org.apache.doris.common.AnalysisException;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.paimon.table.FileStoreTable;
+import org.apache.paimon.table.Table;
+import org.apache.paimon.types.DataField;
+import org.apache.paimon.types.DataTypeRoot;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+/**
+ * One immutable view of the current Paimon write target.
+ *
+ * <p>A time-travel snapshot attached to the Doris table is read-side state 
and must not affect
+ * sink analysis. Capturing the latest remote table once also keeps schema 
binding, writer
+ * distribution and the serialized JNI table on the same table generation.
+ */
+public final class PaimonWriteTarget {
+    private final PaimonExternalTable dorisTable;
+    private final FileStoreTable table;
+    private final List<Column> schema;
+    private final Map<String, Column> columnsByName;
+    private final Map<String, Type> columnTypes;
+    private final Set<String> partitionColumnNames;
+
+    private PaimonWriteTarget(PaimonExternalTable dorisTable, FileStoreTable 
table) {
+        this.dorisTable = dorisTable;
+        this.table = table;
+        PaimonExternalCatalog catalog = (PaimonExternalCatalog) 
dorisTable.getCatalog();
+
+        ImmutableList.Builder<Column> schemaBuilder = ImmutableList.builder();
+        Map<String, Column> columns = new 
TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+        Map<String, Type> types = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+        for (DataField field : table.rowType().getFields()) {
+            Type type = PaimonUtil.paimonTypeToDorisType(

Review Comment:
   Fixed in d3197e7866. paimonTypeToDorisType now maps Paimon VARIANT to Doris 
VARIANT, and recursive ARRAY and ROW mapping therefore preserves nested 
VARIANT. PaimonWriteTargetTest constructs the production write target consumed 
by sink binding and verifies both the target Column and coercion type are 
VARIANT; the existing Paimon complex-type regression covers the write path.



##########
fe/fe-core/src/main/java/org/apache/doris/datasource/paimon/PaimonUtil.java:
##########
@@ -150,71 +145,129 @@ public static List<InternalRow> read(
         return rows;
     }
 
-    public static PaimonPartitionInfo generatePartitionInfo(List<Column> 
partitionColumns,
-            List<Partition> paimonPartitions, boolean legacyPartitionName) {
+    public static PaimonPartitionInfo generatePartitionInfo(Table table, 
List<Column> partitionColumns,
+            List<PartitionEntry> partitionEntries) {
 
-        if (CollectionUtils.isEmpty(partitionColumns) || 
paimonPartitions.isEmpty()) {
+        if (CollectionUtils.isEmpty(partitionColumns) || 
partitionEntries.isEmpty()) {
             return PaimonPartitionInfo.EMPTY;
         }
 
-        Map<String, PartitionItem> nameToPartitionItem = Maps.newHashMap();
-        Map<String, Partition> nameToPartition = Maps.newHashMap();
-        PaimonPartitionInfo partitionInfo = new 
PaimonPartitionInfo(nameToPartitionItem, nameToPartition);
+        CoreOptions options = new CoreOptions(table.options());
+        RowType partitionType = table.rowType().project(table.partitionKeys());
+        if (partitionType.getFields().stream().anyMatch(field ->
+                field.type().getTypeRoot() == 
DataTypeRoot.TIMESTAMP_WITH_LOCAL_TIME_ZONE)) {
+            // This metadata is cached by table snapshot, but LTZ values are 
represented as
+            // session-local civil times in Doris. Caching those bounds would 
let one session
+            // reuse pruning metadata produced in another time zone. Keep scan 
correctness by
+            // delegating pruning to Paimon until this cache can carry a 
time-zone-independent
+            // typed representation.
+            return PaimonPartitionInfo.UNPRUNABLE;
+        }
+        InternalRowPartitionComputer partitionComputer = new 
InternalRowPartitionComputer(
+                options.partitionDefaultName(),
+                partitionType,
+                table.partitionKeys().toArray(new String[0]),
+                options.legacyPartitionName());
         List<Type> types = partitionColumns.stream()
                 .map(Column::getType)
                 .collect(Collectors.toList());
+        List<PaimonPartitionCandidate> candidates = 
Lists.newArrayListWithExpectedSize(partitionEntries.size());
+        Map<String, Map<String, String>> displayNameToTypedSpec = 
Maps.newHashMap();
+
+        for (PartitionEntry partitionEntry : partitionEntries) {
+            Map<String, String> typedSpec = getPartitionInfoMap(

Review Comment:
   Fixed in d3197e7866. NTZ values are now formatted through Doris DateLiteral 
using the mapped DATETIMEV2 scale instead of manually translating ISO text. 
Partition-info tests cover TIMESTAMP(9) to DATETIMEV2(6), exact microsecond 
normalization, TIMESTAMP(0), and assert that the result remains PRUNABLE.



##########
fe/fe-core/src/main/java/org/apache/doris/datasource/paimon/PaimonWriteTarget.java:
##########
@@ -0,0 +1,123 @@
+// 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.doris.datasource.paimon;
+
+import org.apache.doris.catalog.Column;
+import org.apache.doris.catalog.Type;
+import org.apache.doris.common.AnalysisException;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.paimon.table.FileStoreTable;
+import org.apache.paimon.table.Table;
+import org.apache.paimon.types.DataField;
+import org.apache.paimon.types.DataTypeRoot;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+/**
+ * One immutable view of the current Paimon write target.
+ *
+ * <p>A time-travel snapshot attached to the Doris table is read-side state 
and must not affect
+ * sink analysis. Capturing the latest remote table once also keeps schema 
binding, writer
+ * distribution and the serialized JNI table on the same table generation.
+ */
+public final class PaimonWriteTarget {
+    private final PaimonExternalTable dorisTable;
+    private final FileStoreTable table;
+    private final List<Column> schema;
+    private final Map<String, Column> columnsByName;
+    private final Map<String, Type> columnTypes;
+    private final Set<String> partitionColumnNames;
+
+    private PaimonWriteTarget(PaimonExternalTable dorisTable, FileStoreTable 
table) {
+        this.dorisTable = dorisTable;
+        this.table = table;
+        PaimonExternalCatalog catalog = (PaimonExternalCatalog) 
dorisTable.getCatalog();
+
+        ImmutableList.Builder<Column> schemaBuilder = ImmutableList.builder();
+        Map<String, Column> columns = new 
TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+        Map<String, Type> types = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+        for (DataField field : table.rowType().getFields()) {
+            Type type = PaimonUtil.paimonTypeToDorisType(
+                    field.type(), catalog.getEnableMappingVarbinary(), false);
+            // Doris exposes external-table columns as nullable. The real 
Paimon nullability and
+            // defaults remain in the pinned FileStoreTable and are enforced 
by the Paimon writer.
+            Column column = new Column(field.name(), type, true, null, true,
+                    field.description(), true, -1);
+            PaimonUtil.updatePaimonColumnUniqueId(column, field);
+            if (field.type().getTypeRoot() == 
DataTypeRoot.TIMESTAMP_WITH_LOCAL_TIME_ZONE) {
+                column.setWithTZExtraInfo();
+            }
+            schemaBuilder.add(column);
+            columns.put(field.name(), column);

Review Comment:
   Fixed in d3197e7866. PaimonWriteTarget now rejects top-level fields that 
collide case-insensitively before either binding map can overwrite an entry. 
PaimonWriteTargetTest covers payload and PAYLOAD and verifies that the analysis 
error names both conflicting fields.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to