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


##########
fe/fe-core/src/main/java/org/apache/doris/datasource/lance/source/LancePredicateConverter.java:
##########
@@ -257,6 +268,60 @@ private Optional<Expression> convertIsNull(IsNullPredicate 
predicate) {
         return Optional.of(comparisonFunction(function, 
fieldReference(field)));
     }
 
+    private Optional<Expression> convertLike(LikePredicate predicate) {
+        if (predicate.getOp() != LikePredicate.Operator.LIKE) {
+            return Optional.empty();
+        }
+        return convertStringPredicate("like:str_str", predicate.getChild(0), 
predicate.getChild(1), true);
+    }
+
+    private Optional<Expression> convertStringFunction(FunctionCallExpr 
function) {
+        if (function.getFnName() == null || function.getChildren().size() != 
2) {
+            return Optional.empty();
+        }
+        String functionName = 
function.getFnName().getFunction().toLowerCase(Locale.ROOT);

Review Comment:
   Preserve the resolved function identity before dispatching by name. 
FunctionRegistry intentionally allows a UDF to shadow a built-in via 
prefer_udf_over_builtin, while a qualified call selects the UDF directly, and 
Nereids translates Java/Python UDFs to this same FunctionCallExpr with a 
non-BUILTIN catalog function. A two-argument UDF named starts_with, ends_with, 
or like can therefore reach this switch, be replaced by Lance's built-in, and 
be removed from the Doris residual in LanceScanNode; an empty residual can also 
enable limit pushdown. Please require a non-null resolved Doris BUILTIN before 
the switch (leaving unknown/UDF identities residual) and cover an analyzed 
same-named UDF.



##########
fe/fe-core/src/main/java/org/apache/doris/datasource/lance/source/LancePredicateConverter.java:
##########
@@ -257,6 +268,60 @@ private Optional<Expression> convertIsNull(IsNullPredicate 
predicate) {
         return Optional.of(comparisonFunction(function, 
fieldReference(field)));
     }
 
+    private Optional<Expression> convertLike(LikePredicate predicate) {
+        if (predicate.getOp() != LikePredicate.Operator.LIKE) {
+            return Optional.empty();
+        }
+        return convertStringPredicate("like:str_str", predicate.getChild(0), 
predicate.getChild(1), true);
+    }
+
+    private Optional<Expression> convertStringFunction(FunctionCallExpr 
function) {
+        if (function.getFnName() == null || function.getChildren().size() != 
2) {
+            return Optional.empty();
+        }
+        String functionName = 
function.getFnName().getFunction().toLowerCase(Locale.ROOT);
+        switch (functionName) {
+            case "like":
+                return convertStringPredicate(
+                        "like:str_str", function.getChild(0), 
function.getChild(1), true);
+            case "starts_with":
+                return convertStringPredicate(
+                        "starts_with:str_str", function.getChild(0), 
function.getChild(1), false);
+            case "ends_with":
+                return convertStringPredicate(
+                        "ends_with:str_str", function.getChild(0), 
function.getChild(1), false);
+            default:
+                return Optional.empty();
+        }
+    }
+
+    private Optional<Expression> convertStringPredicate(
+            String function, Expr input, Expr pattern, boolean 
rejectEscapedPattern) {
+        SlotRef slot = directSlot(input);
+        LiteralExpr literal = directLiteral(pattern);
+        ResolvedField field = slot == null ? null : findField(slot);
+        if (field == null || !isStringType(field.field.getType()) || !(literal 
instanceof StringLiteral)) {
+            return Optional.empty();
+        }
+        String patternValue = literal.getStringValue();
+        // Doris uses backslash as LIKE's default escape character, while the 
Substrait function
+        // has no escape argument. Keep escaped LIKE patterns in Doris rather 
than changing meaning.
+        if (rejectEscapedPattern && patternValue.indexOf('\\') >= 0) {

Review Comment:
   Keep embedded-NUL LIKE patterns residual. In the default SQL mode Doris 
decodes backslash-zero before this guard; a wildcard pattern then reaches 
hs_compile through re_pattern.c_str(), so the regex is truncated at NUL (for 
example, a<NUL>_ becomes ^a and matches abc). The exact lance-c 0.1.6 / 
DataFusion 53.1.0 / Arrow 58.3.0 consumer preserves the full NUL-bearing 
pattern, so pushdown filters abc out and the removed Doris residual cannot 
restore it. Please reject NUL here and add an analyzed end-to-end 
residual/result 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