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


##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FoldConstantRuleOnFE.java:
##########
@@ -675,12 +679,34 @@ public Expression visitIf(If ifExpr, 
ExpressionRewriteContext context) {
             return typeCoercionTrueValue;
         } else if (condition.equals(BooleanLiteral.FALSE) || 
condition.isNullLiteral()) {
             return typeCoercionFalseValue;
-        } else if (typeCoercionTrueValue.equals(typeCoercionFalseValue)) {
+        } else if (isSameBranch(typeCoercionTrueValue, 
typeCoercionFalseValue)) {
             return typeCoercionTrueValue;
         }
         return TypeCoercionUtils.ensureSameResultType(originIf, ifExpr, 
context);
     }
 
+    // Literal.equals takes NaNs of both signs as equal, but signbit() tells 
them apart, so branches
+    // holding a NaN literal, also as an element of a complex literal, are not 
merged into one
+    private static boolean isSameBranch(Expression branch, Expression other) {
+        return branch.equals(other) && !branch.anyMatch(expression -> 
holdsNaN((Expression) expression));

Review Comment:
   [P2] Make this signed-NaN distinction apply to every rewrite that treats 
structural equality as semantic equality. This private helper fixes only 
If/CASE result branches; `Literal.equals`/`hashCode` still canonicalize 
opposite NaN signs. For example, `NULLIF(signbit(IF(number=0, CAST('nan' AS 
DOUBLE), CAST(0 AS DOUBLE))), signbit(IF(number=0, CAST('-nan' AS DOUBLE), 
CAST(0 AS DOUBLE))))` is folded to NULL by `first.equals(second)`, but at 
`number=0` BE evaluates FALSE versus TRUE and returns FALSE. The same 
predicates under OR are deduplicated by `DistinctPredicatesRule` to the false 
first operand although the OR is true, and CASE `uniqueOperands` has the same 
problem. These are distinct from the resolved branch-result thread. Please use 
a shared raw-bit-aware equivalence (or conservatively skip equality rewrites 
for expressions holding NaNs) and cover dynamic NULLIF/OR/CASE cases.



##########
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/PercentileReservoir.java:
##########
@@ -67,22 +72,56 @@ private PercentileReservoir(NullableAggregateFunctionParams 
functionParams) {
 
     @Override
     public void checkLegalityBeforeTypeCoercion() {
+        checkLevel();
+    }
+
+    @Override
+    public void checkLegalityAfterRewrite() {
+        checkLevel();

Review Comment:
   [P2] Accept the planner's safe nullable-layout wrapper in this post-rewrite 
check. `CAST(percentile_reservoir_state(CAST(7 AS DOUBLE), 0.25) AS 
AGG_STATE<percentile_reservoir(DOUBLE NOT NULL, DOUBLE NULL)>)` was valid at 
the merge base. `ConvertAggStateCast` retargets the level subtype by wrapping 
`0.25` in `Nullable`, and `StateCombinator` forwards that child here; 
`evaluateWithoutContext` deliberately leaves `Nullable` intact, so this new 
literal-only check rejects it as "must be a constant" even though BE can safely 
materialize the non-null literal in a nullable column. Please validate the 
non-null literal beneath this wrapper while preserving the requested state 
layout, or avoid reapplying the literal-only check to this converted child, and 
add a full-rewrite test that makes the level subtype nullable.



##########
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DoubleLiteral.java:
##########
@@ -86,8 +86,10 @@ protected Expression uncheckedCastTo(DataType targetType) 
throws AnalysisExcepti
             return this;
         }
         if (targetType.isFloatType()) {
-            return new 
org.apache.doris.nereids.trees.expressions.literal.FloatLiteral(
-                    Float.parseFloat(String.valueOf(value)));
+            // narrow with a single rounding and keep the sign of a NaN, like 
static_cast<float> on BE

Review Comment:
   [P2] Preserve this sign across the fragment wire format, not only inside the 
literal objects. In `signbit(IF(number=0, CAST('-nan' AS DOUBLE), 0.0))`, 
folding creates a negative `DoubleLiteral` but leaves the slot-dependent IF. 
The legacy literal and `TFloatLiteral.value` still hold that sign, but the 
pinned Java Thrift binary/compact `writeDouble` paths use 
`Double.doubleToLongBits`, which canonicalizes every NaN to the positive 
pattern before BE receives the fragment. Normal folding therefore returns false 
at `number=0`, while `debug_skip_fold_constant` lets BE parse `-nan` and 
returns true. This is distinct from the FLOAT `toLegacyLiteral` issue: fixing 
that earlier conversion does not fix this DOUBLE wire boundary. Please retain a 
BE-evaluated sign-producing expression or use a compatibility-safe raw-bit 
representation, and add dynamic fold-on/off fragment-serialization coverage.



##########
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/PercentileReservoir.java:
##########
@@ -67,22 +72,56 @@ private PercentileReservoir(NullableAggregateFunctionParams 
functionParams) {
 
     @Override
     public void checkLegalityBeforeTypeCoercion() {
+        checkLevel();
+    }
+
+    @Override
+    public void checkLegalityAfterRewrite() {
+        checkLevel();
+    }
+
+    /**
+     * Execute the level literal that checkLevel() validated. BE would 
otherwise evaluate the constant
+     * expression itself wherever it is not folded (load planning, DISTINCT, 
debug_skip_fold_constant),
+     * and a cast such as FLOAT to DOUBLE can compute a different value there 
than the FE folding.
+     */
+    @Override
+    public Expression rewriteWhenAnalyze() {
+        return withChildren(ImmutableList.of(getArgument(0), checkLevel()));

Review Comment:
   [P2] Preserve compatibility with states serialized before this rewrite. The 
merge base already accepts a DECIMALV3 level such as `0.12345678901234567`; its 
retained/load path lets BE compute `(double)12345678901234567 / (double)10^17` 
(`0x3fbf9add3746f65f`), while this analyzer rewrite now executes 
`BigDecimal.doubleValue()`'s literal (`0x3fbf9add3746f65e`). Both states keep 
the same AggState type, but `QuantileReservoirSampler` serializes the raw level 
and rejects an exact mismatch when nonempty states merge. Existing persisted 
load-created rows or mixed-version writers can therefore start failing after 
upgrade. This is distinct from the existing decimal-cast thread: the conversion 
mismatch was pre-existing, but changing formerly retained writers to the FE 
value creates the new cross-version incompatibility. Please preserve the old 
representation for previously legal shapes or provide a compatible 
migration/canonicalization strategy, and test a pre-fix serialized state merged 
wit
 h a new one.



##########
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/FloatLiteral.java:
##########
@@ -66,6 +66,10 @@ protected Expression uncheckedCastTo(DataType targetType) 
throws AnalysisExcepti
             return this;
         }
         if (targetType.isDoubleType()) {
+            if (Float.isNaN(value)) {
+                // widening on BE keeps the sign of a NaN, which signbit() can 
observe
+                return new DoubleLiteral(Math.copySign(Double.NaN, 
Float.floatToRawIntBits(value) < 0 ? -1.0 : 1.0));

Review Comment:
   [P2] Preserve this sign when a folded FLOAT literal is translated, not only 
when FE widens it first. `FloatLiteral.toLegacyLiteral` still calls inherited 
`getDouble()`, which reparses `Float.toString(value)`; Java renders a negative 
FLOAT NaN as `NaN`, so the legacy literal becomes positive. A dynamic tree such 
as `signbit(CAST(IF(number=0, CAST('-nan(foo)' AS FLOAT), CAST(1 AS FLOAT)) AS 
DOUBLE))` therefore loses the sign during translation, whereas 
`debug_skip_fold_constant` lets BE parse the retained negative string. This is 
distinct from the resolved signed-NaN thread because its case widened and 
folded before translation. Please translate the primitive FLOAT value with its 
raw sign and add a slot-dependent fold-on/off case.



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