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


##########
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/VarBinaryLiteral.java:
##########
@@ -73,8 +79,39 @@ public Object getValue() {
         return byteValues;
     }
 
+    /**
+     * BE casts VARBINARY to a string type by keeping the bytes as they are, 
so CAST(X'616263' AS STRING) is
+     * 'abc', not the hex text '616263'. A Java string carries those bytes 
only when they are valid UTF-8; for
+     * other bytes, such as X'FF', this throws, and 
FoldConstantRuleOnFE.visitCast then leaves the cast for BE
+     * to evaluate (it turns only a CastException into NULL).
+     *
+     * <p>Valid UTF-8 with a NUL byte or a supplementary character is left for 
BE too, because FE does not
+     * evaluate such string literals like BE does: StringLikeLiteral.compareTo 
takes the bytes after a NUL for
+     * padding, so CAST(X'610062' AS STRING) = 'a' would fold to TRUE while BE 
compares all three bytes, and
+     * VARCHAR(n) truncation and trim_in work on UTF-16 units, so 
CAST(X'F09F9880' AS VARCHAR(1)) would keep
+     * half of U+1F600 where BE keeps the whole character.</p>
+     */
+    @Override
     protected String castValueToString() {
-        return toString();
+        String value = toUtf8String().orElseThrow(() -> new AnalysisException(
+                "Cannot cast VARBINARY " + computeToSql() + " to a string type 
on FE: it is not valid UTF-8"));
+        if (value.indexOf('\0') >= 0 || 
value.codePoints().anyMatch(Character::isSupplementaryCodePoint)) {
+            throw new AnalysisException("Cannot cast VARBINARY " + 
computeToSql()
+                    + " to a string type on FE: it has a NUL byte or a 
supplementary character");
+        }
+        return value;

Review Comment:
   [P2] Match BE semantics for bounded strings inside array casts
   
   `SELECT CAST([X'6162'] AS ARRAY<VARCHAR(1)>)[1] = 'a'` now returns `TRUE` 
with FE folding, whereas skipping folding returns `FALSE`. This uses the two 
ASCII bytes `ab`, so the NUL/supplementary guards do not apply.
   
   In the reduced `Project(ElementAt(Cast([X'6162'], ARRAY<VARCHAR(1)>), 1) = 
'a')`, `ArrayLiteral.uncheckedCastTo` calls the element's checked cast. This 
method returns `ab`, and `Literal.uncheckedCastToString` builds 
`VarcharLiteral("ab", 1)`, replacing the array cast with `['a']`. On BE, 
`create_array_wrapper` recursively invokes the string cast wrapper, which 
preserves both bytes and does not enforce the nested VARCHAR length. The parser 
adds `substr` only when the whole cast target is string-like, so no nested 
substring narrows `['ab']`; extracting its element and comparing with `a` 
returns false. The previous hex-text fold truncated `6162` to `6`, so this 
predicate previously also returned false.
   
   Defer these bounded recursive conversions or make FE element folding agree 
with BE, and add folded/unfolded bounded-array coverage. This is a nested 
length-semantics mismatch, distinct from the existing supplementary-character 
splitting report.



##########
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/VarBinaryLiteral.java:
##########
@@ -73,8 +79,39 @@ public Object getValue() {
         return byteValues;
     }
 
+    /**
+     * BE casts VARBINARY to a string type by keeping the bytes as they are, 
so CAST(X'616263' AS STRING) is
+     * 'abc', not the hex text '616263'. A Java string carries those bytes 
only when they are valid UTF-8; for
+     * other bytes, such as X'FF', this throws, and 
FoldConstantRuleOnFE.visitCast then leaves the cast for BE
+     * to evaluate (it turns only a CastException into NULL).
+     *
+     * <p>Valid UTF-8 with a NUL byte or a supplementary character is left for 
BE too, because FE does not
+     * evaluate such string literals like BE does: StringLikeLiteral.compareTo 
takes the bytes after a NUL for
+     * padding, so CAST(X'610062' AS STRING) = 'a' would fold to TRUE while BE 
compares all three bytes, and
+     * VARCHAR(n) truncation and trim_in work on UTF-16 units, so 
CAST(X'F09F9880' AS VARCHAR(1)) would keep
+     * half of U+1F600 where BE keeps the whole character.</p>
+     */
+    @Override
     protected String castValueToString() {
-        return toString();
+        String value = toUtf8String().orElseThrow(() -> new AnalysisException(
+                "Cannot cast VARBINARY " + computeToSql() + " to a string type 
on FE: it is not valid UTF-8"));
+        if (value.indexOf('\0') >= 0 || 
value.codePoints().anyMatch(Character::isSupplementaryCodePoint)) {

Review Comment:
   [P2] Allow deferred string constants in nullable IN subqueries
   
   With the default `enable_fold_constant_by_be=false`, `SELECT CAST(X'610062' 
AS STRING) IN (SELECT s FROM t2)` now fails with `varbinary type could not in 
join equal conditions` when `t2.s` is nullable STRING. The comparison operands 
are both STRING.
   
   The reduced rewritten plan is:
   ```text
   Project(mark)
     MarkJoin(mark: EqualTo(Cast(X'610062', STRING), t2.s))
       OneRowRelation(dummy)
       Scan(t2: s nullable STRING)
   ```
   This new guard retains the constant cast. `SubqueryToApply` keeps it as the 
compare expression, and `InApplyToJoin` preserves the nullable equality as a 
mark conjunct. `JoinUtils.isHashJoinCondition` rejects the slot-free constant 
operand, so `PushDownExpressionsInHashCondition` leaves this non-hash mark 
conjunct unchanged; the nested-loop projection rule also excludes mark joins. 
`CheckAfterRewrite` then rejects the binary literal descendant inside the 
STRING cast. Previously, folding to hex text removed that descendant and the 
query passed this check.
   
   Keep supported deferred STRING operands valid at this planner boundary, by 
checking the actual compared types or projecting the constant expression to a 
STRING slot, and cover a constant cast in a nullable `IN` subquery's SELECT 
result.



##########
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/VarBinaryLiteral.java:
##########
@@ -73,8 +79,39 @@ public Object getValue() {
         return byteValues;
     }
 
+    /**
+     * BE casts VARBINARY to a string type by keeping the bytes as they are, 
so CAST(X'616263' AS STRING) is
+     * 'abc', not the hex text '616263'. A Java string carries those bytes 
only when they are valid UTF-8; for
+     * other bytes, such as X'FF', this throws, and 
FoldConstantRuleOnFE.visitCast then leaves the cast for BE
+     * to evaluate (it turns only a CastException into NULL).
+     *
+     * <p>Valid UTF-8 with a NUL byte or a supplementary character is left for 
BE too, because FE does not
+     * evaluate such string literals like BE does: StringLikeLiteral.compareTo 
takes the bytes after a NUL for
+     * padding, so CAST(X'610062' AS STRING) = 'a' would fold to TRUE while BE 
compares all three bytes, and
+     * VARCHAR(n) truncation and trim_in work on UTF-16 units, so 
CAST(X'F09F9880' AS VARCHAR(1)) would keep
+     * half of U+1F600 where BE keeps the whole character.</p>
+     */
+    @Override
     protected String castValueToString() {
-        return toString();
+        String value = toUtf8String().orElseThrow(() -> new AnalysisException(

Review Comment:
   [P2] Preserve invalid bytes through optional BE constant folding
   
   With `enable_fold_constant_by_be=true`, `SELECT CAST(X'FF' AS STRING) = '�'` 
is newly folded to `TRUE`; full BE evaluation returns `FALSE`. The right 
operand is U+FFFD (`EFBFBD`), distinct from the input byte `FF`.
   
   A reduced triggering plan is:
   ```text
   Filter(EqualTo(Cast(X'FF', STRING), '�'))
     Scan(t)
   ```
   `visitCast` catches this `AnalysisException` and retains the cast, but the 
bottom-up `FoldConstantRule` then applies `FoldConstantRuleOnBE` to that child. 
BE returns raw `FF` through `PValues.string_value`; `getResultExpression` 
creates `StringLiteral(getStringValue(i))`, and the [protobuf Java 
getter](https://github.com/protocolbuffers/protobuf/blob/v33.4/java/core/src/main/java/com/google/protobuf/LazyStringArrayList.java#L89-L99)
 substitutes U+FFFD for invalid UTF-8. Parent `visitEqualTo` consequently 
produces `Filter(TRUE)`, admitting rows BE would reject. The previous hex-text 
fold compared `'FF'` with U+FFFD and returned `FALSE`.
   
   Keep unrepresentable casts out of BE constant replacement as well, or 
validate the raw returned bytes before constructing an FE string literal. Add 
predicate and byte-visible coverage with `enable_fold_constant_by_be` both on 
and off; the new FE-only unit test does not exercise this result transport.



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