github-actions[bot] commented on code in PR #65782:
URL: https://github.com/apache/doris/pull/65782#discussion_r3610632183


##########
fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/helper/IcebergWriterHelper.java:
##########
@@ -178,8 +195,95 @@ private static Metrics buildDataFileMetrics(Table table, 
FileFormat fileFormat,
             }
         }
 
-        return new Metrics(commitData.getRowCount(), columnSizes, valueCounts,
-                nullValueCounts, null, lowerBounds, upperBounds);
+        // Physical file stats may contain every column, but manifest metrics 
must honor the table's metadata policy.
+        return new Metrics(commitData.getRowCount(),
+                filterDisabledMetrics(columnSizes, schema, metricsConfig),
+                filterLogicalMetrics(valueCounts, schema, metricsConfig, 
fieldParents),
+                filterLogicalMetrics(nullValueCounts, schema, metricsConfig, 
fieldParents),
+                null,
+                filterBounds(lowerBounds, schema, metricsConfig, fieldParents, 
true),
+                filterBounds(upperBounds, schema, metricsConfig, fieldParents, 
false));
+    }
+
+    private static <T> Map<Integer, T> filterDisabledMetrics(
+            Map<Integer, T> metrics, Schema schema, MetricsConfig 
metricsConfig) {
+        Map<Integer, T> filteredMetrics = new HashMap<>();
+        metrics.forEach((fieldId, value) -> {
+            if (MetricsUtil.metricsMode(schema, metricsConfig, fieldId) != 
MetricsModes.None.get()) {
+                filteredMetrics.put(fieldId, value);
+            }
+        });
+        return filteredMetrics;
+    }
+
+    private static <T> Map<Integer, T> filterLogicalMetrics(
+            Map<Integer, T> metrics, Schema schema, MetricsConfig 
metricsConfig,
+            Map<Integer, Integer> fieldParents) {
+        Map<Integer, T> filteredMetrics = new HashMap<>();
+        metrics.forEach((fieldId, value) -> {
+            // Definition-level values below list/map do not represent logical 
element counts.
+            if (!isInRepeatedField(fieldId, schema, fieldParents)
+                    && MetricsUtil.metricsMode(schema, metricsConfig, fieldId) 
!= MetricsModes.None.get()) {
+                filteredMetrics.put(fieldId, value);
+            }
+        });
+        return filteredMetrics;
+    }
+
+    private static Map<Integer, ByteBuffer> filterBounds(
+            Map<Integer, ByteBuffer> bounds, Schema schema, MetricsConfig 
metricsConfig,
+            Map<Integer, Integer> fieldParents, boolean lowerBound) {
+        Map<Integer, ByteBuffer> filteredBounds = new HashMap<>();
+        bounds.forEach((fieldId, value) -> {
+            if (isInRepeatedField(fieldId, schema, fieldParents)) {
+                return;
+            }
+            MetricsModes.MetricsMode mode = MetricsUtil.metricsMode(schema, 
metricsConfig, fieldId);
+            if (mode == MetricsModes.None.get() || mode == 
MetricsModes.Counts.get()) {
+                return;
+            }
+
+            ByteBuffer filteredValue = value;
+            if (mode instanceof MetricsModes.Truncate) {
+                Type type = schema.findType(fieldId);
+                int length = ((MetricsModes.Truncate) mode).length();
+                // Truncated upper bounds must round up so file pruning cannot 
exclude matching values.
+                filteredValue = truncateBound(type, value, length, lowerBound);
+            }
+            if (filteredValue != null) {
+                filteredBounds.put(fieldId, filteredValue);
+            }
+        });
+        return filteredBounds;
+    }
+
+    private static boolean isInRepeatedField(
+            int fieldId, Schema schema, Map<Integer, Integer> fieldParents) {
+        Integer parentId = fieldId;
+        while ((parentId = fieldParents.get(parentId)) != null) {
+            Types.NestedField parent = schema.findField(parentId);
+            if (parent != null && !parent.type().isStructType()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static ByteBuffer truncateBound(Type type, ByteBuffer value, int 
length, boolean lowerBound) {
+        switch (type.typeId()) {
+            case STRING:
+                String stringValue = Conversions.fromByteBuffer(type, 
value).toString();
+                String truncatedString = lowerBound
+                        ? UnicodeUtil.truncateStringMin(stringValue, length)
+                        : UnicodeUtil.truncateStringMax(stringValue, length);

Review Comment:
   [P2] Preserve ORC's no-successor upper-bound fallback
   
   This shared helper now applies the Parquet null result to ORC as well. For 
an ORC string maximum whose first retained code point is `U+10FFFF` (for 
example `U+10FFFF` followed by another character with `truncate(1)`), 
`UnicodeUtil.truncateStringMax` cannot increment the prefix and returns null, 
so `filterBounds` drops the upper bound. Iceberg 1.10.1's ORC path deliberately 
falls back to the original full maximum in this case, unlike its Parquet path; 
otherwise ORC manifests lose the requested upper metric and 
pruning/readable-metrics information. Please pass the file format into the 
bound filtering and retain the original ORC upper string value when no safe 
truncated successor exists, with an ORC-backed edge test. This is distinct from 
the prior thread about not truncating bounds at all: it is the ORC-only 
fallback lost by the new unified fix.



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