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


##########
fe/fe-connector/fe-connector-iceberg/src/main/java/org/apache/doris/connector/iceberg/IcebergPredicateConverter.java:
##########
@@ -289,7 +283,175 @@ private Expression buildIn(ConnectorIn in) {
             }
             values.add(value);
         }
-        return in.isNegated() ? Expressions.notIn(colName, values) : 
Expressions.in(colName, values);
+        return buildLeafIn(field.type(), colName, values, in.isNegated());
+    }
+
+    // 
════════════════════════════════════════════════════════════════════════════════════════════════════
+    // FLOAT/DOUBLE leaves: Doris row semantics vs the total order iceberg 
prunes files by.
+    //
+    // Doris evaluates a ROW with "NaN is greater than everything, NaN = NaN" 
plus IEEE zero equality
+    // (-0.0 == 0.0). Iceberg prunes a FILE with Comparators.naturalOrder() 
(Double.compare), where NaN is
+    // not in the bounds at all -- the spec says "NaNs are not permitted as 
lower or upper bounds", they are
+    // recorded separately in nan_value_counts -- and -0.0 sorts strictly 
before +0.0. Handing iceberg a
+    // literal translation therefore prunes files that do hold matching rows, 
and a pruned file never becomes
+    // a split, so BE's residual filter cannot recover those rows: the query 
silently returns too few rows.
+    //
+    // The leaves below do not merely widen, they emit the expression 
EQUIVALENT to the Doris predicate under
+    // iceberg's order, and they spell out the NaN half on BOTH sides: a 
comparison Doris matches NaN with gets
+    // `OR isNaN`, one it does not gets `AND notNaN`. That symmetry is 
load-bearing, not decoration -- De Morgan
+    // maps each form onto the other, so `NOT` composes at any nesting depth. 
Leaving the `AND notNaN` off would
+    // still read correctly row by row, yet iceberg's RewriteNot would lower 
not(lessThan(d, 5)) to a BARE
+    // gtEq(d, 5) whose file-level evaluator prunes a {1.0, NaN} file -- the 
very bug, back through the negation.
+    // notNaN costs no pruning either: it only rules out a file that is 
entirely NaN.
+    //
+    // Cost: InclusiveMetricsEvaluator.isNaN only prunes a file that reports 
nan_value_count == 0, so files
+    // whose metrics omit NaN counts -- including every file Doris writes 
today, IcebergWriterHelper passes a
+    // null nanValueCounts -- stop being pruned by a float range predicate. 
This is the trade parquet-java and
+    // parquet-cpp already make ("stats may hold NaN -> do not prune"); 
writers that do report NaN counts
+    // (spark, flink, iceberg-java) keep pruning in full.
+    // 
════════════════════════════════════════════════════════════════════════════════════════════════════
+
+    /**
+     * {@code col OP value}, shared by all three modes. A non-floating column 
gets the plain 1:1 mapping; a
+     * FLOAT/DOUBLE column gets the NaN / signed-zero reconciliation described 
above.
+     */
+    private static Expression buildLeafComparison(Type type, String colName,
+            ConnectorComparison.Operator op, Object value) {
+        if (!isFloating(type)) {
+            return buildPlainComparison(colName, op, value);
+        }
+        // On a floating column extractIcebergLiteral only ever yields a 
Number: Double/Float from a float
+        // literal, Double from a decimal literal, Integer/Long from an 
integer one (`d > 0`).
+        double v = ((Number) value).doubleValue();
+        if (Double.isNaN(v)) {
+            // Expressions.*(col, NaN) throws ("Cannot create expression 
literal from NaN") -- iceberg models a
+            // NaN literal only through the unary isNaN/notNaN. Doris: NaN is 
the greatest value, NaN = NaN.
+            switch (op) {
+                case EQ:
+                case EQ_FOR_NULL:
+                case GE:
+                    return Expressions.isNaN(colName);
+                case NE:
+                case LT:
+                    return Expressions.notNaN(colName);

Review Comment:
   [P2] Keep NULL out of NaN-negative rewrite predicates
   
   These branches are not equivalent on nullable columns. [Iceberg 1.11.0 
evaluates `notNaN(null)` as 
true](https://github.com/apache/iceberg/blob/apache-iceberg-1.11.0/api/src/main/java/org/apache/iceberg/expressions/Evaluator.java#L100-L102),
 whereas Doris makes `NULL < NaN`, `NULL != NaN`, and `NULL NOT IN (NaN)` 
UNKNOWN. In REWRITE mode this expression becomes the `TableScan` file filter 
and residuals are ignored, so a null-only file is selected by a rewrite 
filtered with `d < CAST('NaN' AS DOUBLE)` even though no row satisfies the 
WHERE. The new equivalence test also omits null. Please preserve the non-null 
domain for these negative NaN forms (including NOT-IN and negated forms) and 
add nullable row/file-metrics coverage.



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