This is an automated email from the ASF dual-hosted git repository.
voonhous pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hudi.git
The following commit(s) were added to refs/heads/master by this push:
new bc874774af17 fix(common): name the offending expression in
BindVisitor's unsupported-predicate error (#19241)
bc874774af17 is described below
commit bc874774af1759c16d7ff42781c842485352cd03
Author: Vova Kolmakov <[email protected]>
AuthorDate: Fri Jul 10 18:05:25 2026 +0700
fix(common): name the offending expression in BindVisitor's
unsupported-predicate error (#19241)
* fix(common): name the offending expression in BindVisitor's
unsupported-predicate error
BindVisitor#visitPredicate interpolated `this`, the visitor, rather than
the predicate into its IllegalArgumentException, and omitted the space before
"cannot". Since BindVisitor has no toString() override the message read "The
expression org.apache.hudi.common.expression.BindVisitor@41a90fa8cannot be
visited as predicate". The sibling PartialBindVisitor#visitPredicate already
interpolates the predicate correctly.
StringStartsWithAny is the only Predicate that can reach that guard, and
the only class in Predicates.java without a toString() override, so the
corrected message would still have printed an identity hash. Added a null-safe
toString() to it, since HoodieBackedTableMetadata constructs it with a null
left operand.
Added TestBindVisitor covering the error message, and two TestPredicates
cases covering the new toString(), including the null left operand shape.
Closes #19240
* addressed review comments: fix toString rendering in Predicates
Drop the prefix and suffix from Collectors.joining in
StringStartsWithAny.toString() so the rendering matches the method-call shape
of the sibling StringStartsWith.toString(), and scope its null-safety comment
to the left operand, which is the only one HoodieBackedTableMetadata passes as
null. Correct the startWith typo in StringStartsWith.toString() and pin the
rendering with a test.
---------
Co-authored-by: Vova Kolmakov <[email protected]>
---
.../apache/hudi/common/expression/BindVisitor.java | 2 +-
.../apache/hudi/common/expression/Predicates.java | 10 +++-
.../hudi/common/expression/TestBindVisitor.java | 53 ++++++++++++++++++++++
.../hudi/common/expression/TestPredicates.java | 21 +++++++++
4 files changed, 84 insertions(+), 2 deletions(-)
diff --git
a/hudi-common/src/main/java/org/apache/hudi/common/expression/BindVisitor.java
b/hudi-common/src/main/java/org/apache/hudi/common/expression/BindVisitor.java
index 7ea2755922dc..2ee412dd4a77 100644
---
a/hudi-common/src/main/java/org/apache/hudi/common/expression/BindVisitor.java
+++
b/hudi-common/src/main/java/org/apache/hudi/common/expression/BindVisitor.java
@@ -182,6 +182,6 @@ public class BindVisitor implements
ExpressionVisitor<Expression> {
return Predicates.contains(left, right);
}
- throw new IllegalArgumentException("The expression " + this + "cannot be
visited as predicate");
+ throw new IllegalArgumentException("The expression " + predicate + "
cannot be visited as predicate");
}
}
diff --git
a/hudi-common/src/main/java/org/apache/hudi/common/expression/Predicates.java
b/hudi-common/src/main/java/org/apache/hudi/common/expression/Predicates.java
index 35ac60af973f..dd38a83e14d2 100644
---
a/hudi-common/src/main/java/org/apache/hudi/common/expression/Predicates.java
+++
b/hudi-common/src/main/java/org/apache/hudi/common/expression/Predicates.java
@@ -263,7 +263,7 @@ public class Predicates {
@Override
public String toString() {
- return getLeft().toString() + ".startWith(" + getRight().toString() +
")";
+ return getLeft().toString() + ".startsWith(" + getRight().toString() +
")";
}
@Override
@@ -486,6 +486,14 @@ public class Predicates {
return false;
}
+ @Override
+ public String toString() {
+ // Only the left expression is absent: HoodieBackedTableMetadata passes
null for the metadata table
+ // key filters, so it must not be dereferenced. The right operands are
always non-null literals.
+ return left + ".startsWithAny("
+ +
right.stream().map(Expression::toString).collect(Collectors.joining(",")) + ")";
+ }
+
public List<Expression> getRightChildren() {
return right;
}
diff --git
a/hudi-common/src/test/java/org/apache/hudi/common/expression/TestBindVisitor.java
b/hudi-common/src/test/java/org/apache/hudi/common/expression/TestBindVisitor.java
new file mode 100644
index 000000000000..ca29eada7b8e
--- /dev/null
+++
b/hudi-common/src/test/java/org/apache/hudi/common/expression/TestBindVisitor.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.expression;
+
+import org.apache.hudi.common.schema.internal.Types;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class TestBindVisitor {
+
+ private static Types.RecordType schema() {
+ ArrayList<Types.Field> fields = new ArrayList<>(1);
+ fields.add(Types.Field.get(0, true, "a", Types.StringType.get()));
+ return Types.RecordType.get(fields, "schema");
+ }
+
+ @Test
+ void testUnsupportedPredicateErrorNamesTheExpression() {
+ BindVisitor bindVisitor = new BindVisitor(schema(), true);
+ Predicates.StringStartsWithAny startsWithAny =
+ Predicates.startsWithAny(new NameReference("a"),
Collections.singletonList(Literal.from("Ja")));
+
+ IllegalArgumentException e =
+ assertThrows(IllegalArgumentException.class, () ->
startsWithAny.accept(bindVisitor));
+
+
assertTrue(e.getMessage().contains("NameReference(name=a).startsWithAny(Ja)"),
e::getMessage);
+ assertFalse(e.getMessage().contains(BindVisitor.class.getName()),
e::getMessage);
+ assertTrue(e.getMessage().contains(" cannot be visited as predicate"),
e::getMessage);
+ }
+}
diff --git
a/hudi-common/src/test/java/org/apache/hudi/common/expression/TestPredicates.java
b/hudi-common/src/test/java/org/apache/hudi/common/expression/TestPredicates.java
index 34eb80e792be..3c5b9f1c24c4 100644
---
a/hudi-common/src/test/java/org/apache/hudi/common/expression/TestPredicates.java
+++
b/hudi-common/src/test/java/org/apache/hudi/common/expression/TestPredicates.java
@@ -22,6 +22,7 @@ package org.apache.hudi.common.expression;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -29,6 +30,12 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
class TestPredicates {
+ @Test
+ void testStringStartsWithToString() {
+ Predicates.StringStartsWith predicate =
Predicates.startsWith(Literal.from("key"), Literal.from("k1"));
+ assertEquals("key.startsWith(k1)", predicate.toString());
+ }
+
@Test
void testStringStartsWithAnyWhenMatched() {
Expression left = Literal.from("key2_any");
@@ -52,4 +59,18 @@ class TestPredicates {
assertEquals(Expression.Operator.STARTS_WITH, predicate.getOperator());
assertFalse((boolean) predicate.eval(null));
}
+
+ @Test
+ void testStringStartsWithAnyToString() {
+ Predicates.StringStartsWithAny predicate =
+ Predicates.startsWithAny(Literal.from("key"),
Arrays.asList(Literal.from("k1"), Literal.from("k2")));
+ assertEquals("key.startsWithAny(k1,k2)", predicate.toString());
+ }
+
+ @Test
+ void testStringStartsWithAnyToStringIsNullSafeForAbsentLeft() {
+ Predicates.StringStartsWithAny predicate =
+ Predicates.startsWithAny(null,
Collections.singletonList(Literal.from("key1")));
+ assertEquals("null.startsWithAny(key1)", predicate.toString());
+ }
}