This is an automated email from the ASF dual-hosted git repository.

jark pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fluss.git

commit 74ab1f130a00fceb880dcf28c94e68fe5dc062ce
Author: Jark Wu <j...@apache.org>
AuthorDate: Wed Aug 6 10:55:05 2025 +0800

    [common] Support statistic-based Predicate interface
---
 LICENSE                                            |  27 ++++
 .../main/java/com/alibaba/fluss/predicate/And.java |  15 +++
 .../com/alibaba/fluss/predicate/CompareUtils.java  |  10 +-
 .../alibaba/fluss/predicate/CompoundPredicate.java |  16 +++
 .../java/com/alibaba/fluss/predicate/Contains.java |   2 +
 .../java/com/alibaba/fluss/predicate/EndsWith.java |   2 +
 .../java/com/alibaba/fluss/predicate/Equal.java    |   2 +
 .../java/com/alibaba/fluss/predicate/FieldRef.java |   2 +-
 .../alibaba/fluss/predicate/GreaterOrEqual.java    |   2 +
 .../com/alibaba/fluss/predicate/GreaterThan.java   |   2 +
 .../main/java/com/alibaba/fluss/predicate/In.java  |   2 +-
 .../com/alibaba/fluss/predicate/IsNotNull.java     |   2 +
 .../java/com/alibaba/fluss/predicate/IsNull.java   |   2 +
 .../com/alibaba/fluss/predicate/LeafFunction.java  |  27 +++-
 .../com/alibaba/fluss/predicate/LeafPredicate.java |  21 ++-
 .../alibaba/fluss/predicate/LeafUnaryFunction.java |   2 +-
 .../com/alibaba/fluss/predicate/LessOrEqual.java   |   2 +
 .../java/com/alibaba/fluss/predicate/LessThan.java |   2 +
 .../java/com/alibaba/fluss/predicate/NotEqual.java |   4 +
 .../java/com/alibaba/fluss/predicate/NotIn.java    |   5 +-
 .../predicate/NullFalseLeafBinaryFunction.java     |   2 +-
 .../main/java/com/alibaba/fluss/predicate/Or.java  |  17 ++-
 .../com/alibaba/fluss/predicate/Predicate.java     |  16 ++-
 .../alibaba/fluss/predicate/PredicateBuilder.java  |  47 ++++---
 .../alibaba/fluss/predicate/PredicateVisitor.java  |   9 +-
 .../com/alibaba/fluss/predicate/StartsWith.java    |   2 +
 .../fluss/predicate/UnsupportedExpression.java     |   3 +
 .../main/java/com/alibaba/fluss/types/RowType.java |  20 +--
 .../com/alibaba/fluss/utils/BinaryStringUtils.java |  10 +-
 .../fluss/predicate/PredicateBuilderTest.java      |  45 +++++--
 .../com/alibaba/fluss/predicate/PredicateTest.java | 150 ++++++++++++++++++++-
 31 files changed, 399 insertions(+), 71 deletions(-)

diff --git a/LICENSE b/LICENSE
index 7342382ec..5c274b510 100644
--- a/LICENSE
+++ b/LICENSE
@@ -359,6 +359,33 @@ Apache Kafka
 
./fluss-server/src/main/java/com/alibaba/fluss/server/utils/timer/TimingWheel.java
 
 Apache Paimon
+./fluss-common/src/main/java/com/alibaba/fluss/predicate/And.java
+./fluss-common/src/main/java/com/alibaba/fluss/predicate/CompareUtils.java
+./fluss-common/src/main/java/com/alibaba/fluss/predicate/CompoundPredicate.java
+./fluss-common/src/main/java/com/alibaba/fluss/predicate/Contains.java
+./fluss-common/src/main/java/com/alibaba/fluss/predicate/EndsWith.java
+./fluss-common/src/main/java/com/alibaba/fluss/predicate/Equal.java
+./fluss-common/src/main/java/com/alibaba/fluss/predicate/FieldRef.java
+./fluss-common/src/main/java/com/alibaba/fluss/predicate/FunctionVisitor.java
+./fluss-common/src/main/java/com/alibaba/fluss/predicate/GreaterOrEqual.java
+./fluss-common/src/main/java/com/alibaba/fluss/predicate/GreaterThan.java
+./fluss-common/src/main/java/com/alibaba/fluss/predicate/In.java
+./fluss-common/src/main/java/com/alibaba/fluss/predicate/IsNotNull.java
+./fluss-common/src/main/java/com/alibaba/fluss/predicate/IsNull.java
+./fluss-common/src/main/java/com/alibaba/fluss/predicate/LeafFunction.java
+./fluss-common/src/main/java/com/alibaba/fluss/predicate/LeafPredicate.java
+./fluss-common/src/main/java/com/alibaba/fluss/predicate/LeafUnaryFunction.java
+./fluss-common/src/main/java/com/alibaba/fluss/predicate/LessOrEqual.java
+./fluss-common/src/main/java/com/alibaba/fluss/predicate/LessThan.java
+./fluss-common/src/main/java/com/alibaba/fluss/predicate/NotEqual.java
+./fluss-common/src/main/java/com/alibaba/fluss/predicate/NotIn.java
+./fluss-common/src/main/java/com/alibaba/fluss/predicate/NullFalseLeafBinaryFunction.java
+./fluss-common/src/main/java/com/alibaba/fluss/predicate/Or.java
+./fluss-common/src/main/java/com/alibaba/fluss/predicate/PartitionPredicateVisitor.java
+./fluss-common/src/main/java/com/alibaba/fluss/predicate/Predicate.java
+./fluss-common/src/main/java/com/alibaba/fluss/predicate/PredicateBuilder.java
+./fluss-common/src/main/java/com/alibaba/fluss/predicate/PredicateVisitor.java
+./fluss-common/src/main/java/com/alibaba/fluss/predicate/StartsWith.java
 
./fluss-common/src/main/java/com/alibaba/fluss/row/encode/paimon/PaimonBinaryRowWriter.java
 
./fluss-flink/fluss-flink-common/src/main/java/com/alibaba/fluss/flink/lakehouse/paimon/reader/PaimonSnapshotScanner.java
 
diff --git a/fluss-common/src/main/java/com/alibaba/fluss/predicate/And.java 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/And.java
index 204bb7e3d..59f79cd69 100644
--- a/fluss-common/src/main/java/com/alibaba/fluss/predicate/And.java
+++ b/fluss-common/src/main/java/com/alibaba/fluss/predicate/And.java
@@ -46,6 +46,21 @@ public class And extends CompoundPredicate.Function {
         return true;
     }
 
+    @Override
+    public boolean test(
+            long rowCount,
+            InternalRow minValues,
+            InternalRow maxValues,
+            Long[] nullCounts,
+            List<Predicate> children) {
+        for (Predicate child : children) {
+            if (!child.test(rowCount, minValues, maxValues, nullCounts)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     @Override
     public Optional<Predicate> negate(List<Predicate> children) {
         List<Predicate> negatedChildren = new ArrayList<>();
diff --git 
a/fluss-common/src/main/java/com/alibaba/fluss/predicate/CompareUtils.java 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/CompareUtils.java
index 50380abf8..68b97a2ef 100644
--- a/fluss-common/src/main/java/com/alibaba/fluss/predicate/CompareUtils.java
+++ b/fluss-common/src/main/java/com/alibaba/fluss/predicate/CompareUtils.java
@@ -17,7 +17,6 @@
 
 package com.alibaba.fluss.predicate;
 
-import com.alibaba.fluss.row.BinaryString;
 import com.alibaba.fluss.types.DataType;
 
 import static java.lang.Math.min;
@@ -30,16 +29,9 @@ import static java.lang.Math.min;
 public class CompareUtils {
     private CompareUtils() {}
 
+    @SuppressWarnings("unchecked")
     public static int compareLiteral(DataType type, Object v1, Object v2) {
         if (v1 instanceof Comparable) {
-            // because BinaryString can not serialize so v1 or v2 may be 
BinaryString convert to
-            // String for compare
-            if (v1 instanceof BinaryString) {
-                v1 = ((BinaryString) v1).toString();
-            }
-            if (v2 instanceof BinaryString) {
-                v2 = ((BinaryString) v2).toString();
-            }
             return ((Comparable<Object>) v1).compareTo(v2);
         } else if (v1 instanceof byte[]) {
             return compare((byte[]) v1, (byte[]) v2);
diff --git 
a/fluss-common/src/main/java/com/alibaba/fluss/predicate/CompoundPredicate.java 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/CompoundPredicate.java
index 449598f2f..f38599326 100644
--- 
a/fluss-common/src/main/java/com/alibaba/fluss/predicate/CompoundPredicate.java
+++ 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/CompoundPredicate.java
@@ -34,6 +34,7 @@ import java.util.Optional;
  */
 public class CompoundPredicate implements Predicate {
 
+    private static final long serialVersionUID = 1L;
     private final Function function;
     private final List<Predicate> children;
 
@@ -55,6 +56,12 @@ public class CompoundPredicate implements Predicate {
         return function.test(row, children);
     }
 
+    @Override
+    public boolean test(
+            long rowCount, InternalRow minValues, InternalRow maxValues, 
Long[] nullCounts) {
+        return function.test(rowCount, minValues, maxValues, nullCounts, 
children);
+    }
+
     @Override
     public Optional<Predicate> negate() {
         return function.negate(children);
@@ -87,8 +94,17 @@ public class CompoundPredicate implements Predicate {
     /** Evaluate the predicate result based on multiple {@link Predicate}s. */
     public abstract static class Function implements Serializable {
 
+        private static final long serialVersionUID = 1L;
+
         public abstract boolean test(InternalRow row, List<Predicate> 
children);
 
+        public abstract boolean test(
+                long rowCount,
+                InternalRow minValues,
+                InternalRow maxValues,
+                Long[] nullCounts,
+                List<Predicate> children);
+
         public abstract Optional<Predicate> negate(List<Predicate> children);
 
         public abstract <T> T visit(FunctionVisitor<T> visitor, List<T> 
children);
diff --git 
a/fluss-common/src/main/java/com/alibaba/fluss/predicate/Contains.java 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/Contains.java
index cfdaf0a06..5c166813d 100644
--- a/fluss-common/src/main/java/com/alibaba/fluss/predicate/Contains.java
+++ b/fluss-common/src/main/java/com/alibaba/fluss/predicate/Contains.java
@@ -29,6 +29,8 @@ import java.util.Optional;
 /** A {@link NullFalseLeafBinaryFunction} to evaluate {@code filter like 
'%abc%'}. */
 public class Contains extends NullFalseLeafBinaryFunction {
 
+    private static final long serialVersionUID = 1L;
+
     public static final Contains INSTANCE = new Contains();
 
     private Contains() {}
diff --git 
a/fluss-common/src/main/java/com/alibaba/fluss/predicate/EndsWith.java 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/EndsWith.java
index a6af4c2e1..38f4a67f8 100644
--- a/fluss-common/src/main/java/com/alibaba/fluss/predicate/EndsWith.java
+++ b/fluss-common/src/main/java/com/alibaba/fluss/predicate/EndsWith.java
@@ -32,6 +32,8 @@ import java.util.Optional;
  */
 public class EndsWith extends NullFalseLeafBinaryFunction {
 
+    private static final long serialVersionUID = 1L;
+
     public static final EndsWith INSTANCE = new EndsWith();
 
     private EndsWith() {}
diff --git a/fluss-common/src/main/java/com/alibaba/fluss/predicate/Equal.java 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/Equal.java
index 26b181749..b7fbf776a 100644
--- a/fluss-common/src/main/java/com/alibaba/fluss/predicate/Equal.java
+++ b/fluss-common/src/main/java/com/alibaba/fluss/predicate/Equal.java
@@ -31,6 +31,8 @@ import static 
com.alibaba.fluss.predicate.CompareUtils.compareLiteral;
 /** A {@link NullFalseLeafBinaryFunction} to eval equal. */
 public class Equal extends NullFalseLeafBinaryFunction {
 
+    private static final long serialVersionUID = 1L;
+
     public static final Equal INSTANCE = new Equal();
 
     private Equal() {}
diff --git 
a/fluss-common/src/main/java/com/alibaba/fluss/predicate/FieldRef.java 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/FieldRef.java
index d98dc92ba..b9e179ddc 100644
--- a/fluss-common/src/main/java/com/alibaba/fluss/predicate/FieldRef.java
+++ b/fluss-common/src/main/java/com/alibaba/fluss/predicate/FieldRef.java
@@ -29,7 +29,7 @@ import java.util.Objects;
 /** A reference to a field in an input. */
 public class FieldRef implements Serializable {
 
-    private static final long serialVersionUID = 4982103776651292199L;
+    private static final long serialVersionUID = 1L;
 
     private final int index;
     private final String name;
diff --git 
a/fluss-common/src/main/java/com/alibaba/fluss/predicate/GreaterOrEqual.java 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/GreaterOrEqual.java
index 0a915355f..6f3ff9d85 100644
--- a/fluss-common/src/main/java/com/alibaba/fluss/predicate/GreaterOrEqual.java
+++ b/fluss-common/src/main/java/com/alibaba/fluss/predicate/GreaterOrEqual.java
@@ -31,6 +31,8 @@ import static 
com.alibaba.fluss.predicate.CompareUtils.compareLiteral;
 /** A {@link NullFalseLeafBinaryFunction} to eval greater or equal. */
 public class GreaterOrEqual extends NullFalseLeafBinaryFunction {
 
+    private static final long serialVersionUID = 1L;
+
     public static final GreaterOrEqual INSTANCE = new GreaterOrEqual();
 
     private GreaterOrEqual() {}
diff --git 
a/fluss-common/src/main/java/com/alibaba/fluss/predicate/GreaterThan.java 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/GreaterThan.java
index f92c84c9b..9f088a773 100644
--- a/fluss-common/src/main/java/com/alibaba/fluss/predicate/GreaterThan.java
+++ b/fluss-common/src/main/java/com/alibaba/fluss/predicate/GreaterThan.java
@@ -31,6 +31,8 @@ import static 
com.alibaba.fluss.predicate.CompareUtils.compareLiteral;
 /** A {@link LeafFunction} to eval greater. */
 public class GreaterThan extends NullFalseLeafBinaryFunction {
 
+    private static final long serialVersionUID = 1L;
+
     public static final GreaterThan INSTANCE = new GreaterThan();
 
     private GreaterThan() {}
diff --git a/fluss-common/src/main/java/com/alibaba/fluss/predicate/In.java 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/In.java
index 672a1c797..6697750ce 100644
--- a/fluss-common/src/main/java/com/alibaba/fluss/predicate/In.java
+++ b/fluss-common/src/main/java/com/alibaba/fluss/predicate/In.java
@@ -31,7 +31,7 @@ import static 
com.alibaba.fluss.predicate.CompareUtils.compareLiteral;
 /** A {@link LeafFunction} to eval in. */
 public class In extends LeafFunction {
 
-    private static final long serialVersionUID = -9115697441080586485L;
+    private static final long serialVersionUID = 1L;
 
     public static final In INSTANCE = new In();
 
diff --git 
a/fluss-common/src/main/java/com/alibaba/fluss/predicate/IsNotNull.java 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/IsNotNull.java
index 6249306b5..7facb9834 100644
--- a/fluss-common/src/main/java/com/alibaba/fluss/predicate/IsNotNull.java
+++ b/fluss-common/src/main/java/com/alibaba/fluss/predicate/IsNotNull.java
@@ -29,6 +29,8 @@ import java.util.Optional;
 /** A {@link NullFalseLeafBinaryFunction} to eval is not null. */
 public class IsNotNull extends LeafUnaryFunction {
 
+    private static final long serialVersionUID = 1L;
+
     public static final IsNotNull INSTANCE = new IsNotNull();
 
     private IsNotNull() {}
diff --git a/fluss-common/src/main/java/com/alibaba/fluss/predicate/IsNull.java 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/IsNull.java
index d97c8cb43..126df7872 100644
--- a/fluss-common/src/main/java/com/alibaba/fluss/predicate/IsNull.java
+++ b/fluss-common/src/main/java/com/alibaba/fluss/predicate/IsNull.java
@@ -29,6 +29,8 @@ import java.util.Optional;
 /** A {@link NullFalseLeafBinaryFunction} to eval is null. */
 public class IsNull extends LeafUnaryFunction {
 
+    private static final long serialVersionUID = 1L;
+
     public static final IsNull INSTANCE = new IsNull();
 
     private IsNull() {}
diff --git 
a/fluss-common/src/main/java/com/alibaba/fluss/predicate/LeafFunction.java 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/LeafFunction.java
index a897c902e..e2b00a3aa 100644
--- a/fluss-common/src/main/java/com/alibaba/fluss/predicate/LeafFunction.java
+++ b/fluss-common/src/main/java/com/alibaba/fluss/predicate/LeafFunction.java
@@ -30,8 +30,29 @@ import java.util.Optional;
 /** Function to test a field with literals. */
 public abstract class LeafFunction implements Serializable {
 
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Tests whether a field satisfies the condition based on the provided 
literals.
+     *
+     * @param type the data type of the field being tested
+     * @param field the value of the field to test
+     * @param literals the list of literals to test against the field
+     * @return true if the field satisfies the condition, false otherwise
+     */
     public abstract boolean test(DataType type, Object field, List<Object> 
literals);
 
+    /**
+     * Tests whether a set of rows satisfies the condition based on the 
provided statistics.
+     *
+     * @param type the data type of the field being tested
+     * @param rowCount the total number of rows
+     * @param min the minimum value of the field in the rows
+     * @param max the maximum value of the field in the rows
+     * @param nullCount the number of null values in the field, or null if 
unknown
+     * @param literals the literals to test against the field
+     * @return true if there is any row satisfies the condition, false 
otherwise
+     */
     public abstract boolean test(
             DataType type,
             long rowCount,
@@ -42,6 +63,9 @@ public abstract class LeafFunction implements Serializable {
 
     public abstract Optional<LeafFunction> negate();
 
+    public abstract <T> T visit(
+            FunctionVisitor<T> visitor, FieldRef fieldRef, List<Object> 
literals);
+
     @Override
     public int hashCode() {
         return this.getClass().getName().hashCode();
@@ -55,9 +79,6 @@ public abstract class LeafFunction implements Serializable {
         return o != null && getClass() == o.getClass();
     }
 
-    public abstract <T> T visit(
-            FunctionVisitor<T> visitor, FieldRef fieldRef, List<Object> 
literals);
-
     @Override
     public String toString() {
         return getClass().getSimpleName();
diff --git 
a/fluss-common/src/main/java/com/alibaba/fluss/predicate/LeafPredicate.java 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/LeafPredicate.java
index 343e6c0f3..58f6f7963 100644
--- a/fluss-common/src/main/java/com/alibaba/fluss/predicate/LeafPredicate.java
+++ b/fluss-common/src/main/java/com/alibaba/fluss/predicate/LeafPredicate.java
@@ -34,14 +34,14 @@ import java.util.Optional;
 /** Leaf node of a {@link Predicate} tree. Compares a field in the row with 
literals. */
 public class LeafPredicate implements Predicate {
 
-    private static final long serialVersionUID = -9033842253303772188L;
+    private static final long serialVersionUID = 1L;
 
     private final LeafFunction function;
     private final DataType type;
     private final int fieldIndex;
     private final String fieldName;
 
-    private List<Object> literals;
+    private final List<Object> literals;
 
     public LeafPredicate(
             LeafFunction function,
@@ -89,6 +89,23 @@ public class LeafPredicate implements Predicate {
         return function.test(type, get(row, fieldIndex, type), literals);
     }
 
+    @Override
+    public boolean test(
+            long rowCount, InternalRow minValues, InternalRow maxValues, 
Long[] nullCounts) {
+        Object min = get(minValues, fieldIndex, type);
+        Object max = get(maxValues, fieldIndex, type);
+        Long nullCount = nullCounts != null ? nullCounts[fieldIndex] : null;
+        if (nullCount == null || rowCount != nullCount) {
+            // not all null
+            // min or max is null
+            // unknown stats
+            if (min == null || max == null) {
+                return true;
+            }
+        }
+        return function.test(type, rowCount, min, max, nullCount, literals);
+    }
+
     @Override
     public Optional<Predicate> negate() {
         return function.negate()
diff --git 
a/fluss-common/src/main/java/com/alibaba/fluss/predicate/LeafUnaryFunction.java 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/LeafUnaryFunction.java
index b10fe40ff..e03710aa0 100644
--- 
a/fluss-common/src/main/java/com/alibaba/fluss/predicate/LeafUnaryFunction.java
+++ 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/LeafUnaryFunction.java
@@ -28,7 +28,7 @@ import java.util.List;
 /** Function to test a field. */
 public abstract class LeafUnaryFunction extends LeafFunction {
 
-    private static final long serialVersionUID = -155104972966998013L;
+    private static final long serialVersionUID = 1L;
 
     public abstract boolean test(DataType type, Object value);
 
diff --git 
a/fluss-common/src/main/java/com/alibaba/fluss/predicate/LessOrEqual.java 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/LessOrEqual.java
index 45dd3fda6..019ba9c31 100644
--- a/fluss-common/src/main/java/com/alibaba/fluss/predicate/LessOrEqual.java
+++ b/fluss-common/src/main/java/com/alibaba/fluss/predicate/LessOrEqual.java
@@ -31,6 +31,8 @@ import static 
com.alibaba.fluss.predicate.CompareUtils.compareLiteral;
 /** A {@link NullFalseLeafBinaryFunction} to eval less or equal. */
 public class LessOrEqual extends NullFalseLeafBinaryFunction {
 
+    private static final long serialVersionUID = 1L;
+
     public static final LessOrEqual INSTANCE = new LessOrEqual();
 
     private LessOrEqual() {}
diff --git 
a/fluss-common/src/main/java/com/alibaba/fluss/predicate/LessThan.java 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/LessThan.java
index ed1ee70c3..a8ef1f9c8 100644
--- a/fluss-common/src/main/java/com/alibaba/fluss/predicate/LessThan.java
+++ b/fluss-common/src/main/java/com/alibaba/fluss/predicate/LessThan.java
@@ -31,6 +31,8 @@ import static 
com.alibaba.fluss.predicate.CompareUtils.compareLiteral;
 /** A {@link NullFalseLeafBinaryFunction} to eval less or equal. */
 public class LessThan extends NullFalseLeafBinaryFunction {
 
+    private static final long serialVersionUID = 1L;
+
     public static final LessThan INSTANCE = new LessThan();
 
     private LessThan() {}
diff --git 
a/fluss-common/src/main/java/com/alibaba/fluss/predicate/NotEqual.java 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/NotEqual.java
index bca203a22..0ad0f0dc5 100644
--- a/fluss-common/src/main/java/com/alibaba/fluss/predicate/NotEqual.java
+++ b/fluss-common/src/main/java/com/alibaba/fluss/predicate/NotEqual.java
@@ -31,6 +31,8 @@ import static 
com.alibaba.fluss.predicate.CompareUtils.compareLiteral;
 /** A {@link NullFalseLeafBinaryFunction} to eval not equal. */
 public class NotEqual extends NullFalseLeafBinaryFunction {
 
+    private static final long serialVersionUID = 1L;
+
     public static final NotEqual INSTANCE = new NotEqual();
 
     private NotEqual() {}
@@ -43,6 +45,8 @@ public class NotEqual extends NullFalseLeafBinaryFunction {
     @Override
     public boolean test(
             DataType type, long rowCount, Object min, Object max, Long 
nullCount, Object literal) {
+        // ony when max == min == literal, the result is false,
+        // otherwise, the row set MAY contain the literal.
         return compareLiteral(type, literal, min) != 0 || compareLiteral(type, 
literal, max) != 0;
     }
 
diff --git a/fluss-common/src/main/java/com/alibaba/fluss/predicate/NotIn.java 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/NotIn.java
index 3bad7e56e..efd2788f1 100644
--- a/fluss-common/src/main/java/com/alibaba/fluss/predicate/NotIn.java
+++ b/fluss-common/src/main/java/com/alibaba/fluss/predicate/NotIn.java
@@ -31,7 +31,7 @@ import static 
com.alibaba.fluss.predicate.CompareUtils.compareLiteral;
 /** A {@link LeafFunction} to eval not in. */
 public class NotIn extends LeafFunction {
 
-    private static final long serialVersionUID = 8953845894700582887L;
+    private static final long serialVersionUID = 1L;
     public static final NotIn INSTANCE = new NotIn();
 
     private NotIn() {}
@@ -62,6 +62,9 @@ public class NotIn extends LeafFunction {
         }
         for (Object literal : literals) {
             if (literal == null
+                    // only if max == min == literal, the row set are all IN 
the literal, return
+                    // false; other cases, the row set MAY contain elements 
NOT IN the literal,
+                    // return true
                     || (compareLiteral(type, literal, min) == 0
                             && compareLiteral(type, literal, max) == 0)) {
                 return false;
diff --git 
a/fluss-common/src/main/java/com/alibaba/fluss/predicate/NullFalseLeafBinaryFunction.java
 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/NullFalseLeafBinaryFunction.java
index 2dfdf22d4..da170834e 100644
--- 
a/fluss-common/src/main/java/com/alibaba/fluss/predicate/NullFalseLeafBinaryFunction.java
+++ 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/NullFalseLeafBinaryFunction.java
@@ -28,7 +28,7 @@ import java.util.List;
 /** Function to test a field with a literal. */
 public abstract class NullFalseLeafBinaryFunction extends LeafFunction {
 
-    private static final long serialVersionUID = 5617091663961558170L;
+    private static final long serialVersionUID = 1L;
 
     public abstract boolean test(DataType type, Object field, Object literal);
 
diff --git a/fluss-common/src/main/java/com/alibaba/fluss/predicate/Or.java 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/Or.java
index 744717435..3b8ae43d2 100644
--- a/fluss-common/src/main/java/com/alibaba/fluss/predicate/Or.java
+++ b/fluss-common/src/main/java/com/alibaba/fluss/predicate/Or.java
@@ -30,7 +30,7 @@ import java.util.Optional;
 /** A {@link CompoundPredicate.Function} to eval or. */
 public class Or extends CompoundPredicate.Function {
 
-    private static final long serialVersionUID = -2110346319473699418L;
+    private static final long serialVersionUID = 1L;
 
     public static final Or INSTANCE = new Or();
 
@@ -46,6 +46,21 @@ public class Or extends CompoundPredicate.Function {
         return false;
     }
 
+    @Override
+    public boolean test(
+            long rowCount,
+            InternalRow minValues,
+            InternalRow maxValues,
+            Long[] nullCounts,
+            List<Predicate> children) {
+        for (Predicate child : children) {
+            if (child.test(rowCount, minValues, maxValues, nullCounts)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     @Override
     public Optional<Predicate> negate(List<Predicate> children) {
         List<Predicate> negatedChildren = new ArrayList<>();
diff --git 
a/fluss-common/src/main/java/com/alibaba/fluss/predicate/Predicate.java 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/Predicate.java
index 7083a33d5..ecb65d1be 100644
--- a/fluss-common/src/main/java/com/alibaba/fluss/predicate/Predicate.java
+++ b/fluss-common/src/main/java/com/alibaba/fluss/predicate/Predicate.java
@@ -17,6 +17,7 @@
 
 package com.alibaba.fluss.predicate;
 
+import com.alibaba.fluss.annotation.PublicEvolving;
 import com.alibaba.fluss.row.InternalRow;
 
 import java.io.Serializable;
@@ -30,19 +31,26 @@ import java.util.Optional;
  * Predicate which returns Boolean and provides testing by stats.
  *
  * @see PredicateBuilder
- * @since 0.4.0
+ * @since 0.8
  */
+@PublicEvolving
 public interface Predicate extends Serializable {
 
     /**
-     * Now only support test based on the specific input row. Todo: boolean 
test(long rowCount,
-     * InternalRow minValues, InternalRow maxValues, InternalArray 
nullCounts); Test based on the
-     * specific input row.
+     * Test based on the specific input row.
      *
      * @return return true when hit, false when not hit.
      */
     boolean test(InternalRow row);
 
+    /**
+     * Test based on the statistical information to determine whether a hit is 
possible.
+     *
+     * @return return true is likely to hit (there may also be false 
positives), return false is
+     *     absolutely not possible to hit.
+     */
+    boolean test(long rowCount, InternalRow minValues, InternalRow maxValues, 
Long[] nullCounts);
+
     /** @return the negation predicate of this predicate if possible. */
     Optional<Predicate> negate();
 
diff --git 
a/fluss-common/src/main/java/com/alibaba/fluss/predicate/PredicateBuilder.java 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/PredicateBuilder.java
index 975954c2a..525c0e996 100644
--- 
a/fluss-common/src/main/java/com/alibaba/fluss/predicate/PredicateBuilder.java
+++ 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/PredicateBuilder.java
@@ -17,6 +17,7 @@
 
 package com.alibaba.fluss.predicate;
 
+import com.alibaba.fluss.annotation.PublicEvolving;
 import com.alibaba.fluss.row.BinaryString;
 import com.alibaba.fluss.row.Decimal;
 import com.alibaba.fluss.row.TimestampLtz;
@@ -60,10 +61,14 @@ import static java.util.Collections.singletonList;
 /**
  * A utility class to create {@link Predicate} object for common filter 
conditions.
  *
- * @since 0.4.0
+ * @since 0.8
  */
+@PublicEvolving
 public class PredicateBuilder {
 
+    private static final LocalDate EPOCH_DAY =
+            Instant.ofEpochSecond(0).atOffset(ZoneOffset.UTC).toLocalDate();
+
     private final RowType rowType;
     private final List<String> fieldNames;
 
@@ -168,7 +173,7 @@ public class PredicateBuilder {
 
     public static Predicate and(List<Predicate> predicates) {
         checkArgument(
-                predicates.size() > 0,
+                !predicates.isEmpty(),
                 "There must be at least 1 inner predicate to construct an AND 
predicate");
         if (predicates.size() == 1) {
             return predicates.get(0);
@@ -199,8 +204,11 @@ public class PredicateBuilder {
 
     public static Predicate or(List<Predicate> predicates) {
         checkArgument(
-                predicates.size() > 0,
+                !predicates.isEmpty(),
                 "There must be at least 1 inner predicate to construct an OR 
predicate");
+        if (predicates.size() == 1) {
+            return predicates.get(0);
+        }
         return predicates.stream()
                 .reduce((a, b) -> new CompoundPredicate(Or.INSTANCE, 
Arrays.asList(a, b)))
                 .get();
@@ -280,9 +288,7 @@ public class PredicateBuilder {
                     throw new UnsupportedOperationException(
                             "Unexpected date literal of class " + 
o.getClass().getName());
                 }
-                LocalDate epochDay =
-                        
Instant.ofEpochSecond(0).atOffset(ZoneOffset.UTC).toLocalDate();
-                return (int) ChronoUnit.DAYS.between(epochDay, localDate);
+                return (int) ChronoUnit.DAYS.between(EPOCH_DAY, localDate);
             case TIME_WITHOUT_TIME_ZONE:
                 LocalTime localTime;
                 if (o instanceof java.sql.Time) {
@@ -409,10 +415,20 @@ public class PredicateBuilder {
                 .collect(Collectors.toList());
     }
 
+    /**
+     * Creates a {@link Predicate} that represents a condition where partition 
fields are equal to
+     * the specified partition values.
+     *
+     * @param partitionSpec A map containing partition field names as keys and 
their corresponding
+     *     values as strings.
+     * @param rowType The {@link RowType} describing the schema of the row, 
including field names
+     *     and types.
+     * @return A {@link Predicate} representing the equality conditions for 
the partition fields, or
+     *     {@code null} if no conditions are specified.
+     */
     @Nullable
-    public static Predicate partition(
-            Map<String, String> map, RowType rowType, String defaultPartValue) 
{
-        Map<String, Object> internalValues = convertSpecToInternal(map, 
rowType, defaultPartValue);
+    public static Predicate partition(Map<String, String> partitionSpec, 
RowType rowType) {
+        Map<String, Object> internalValues = 
convertSpecToInternal(partitionSpec, rowType);
         List<String> fieldNames = rowType.getFieldNames();
         Predicate predicate = null;
         PredicateBuilder builder = new PredicateBuilder(rowType);
@@ -430,24 +446,21 @@ public class PredicateBuilder {
         return predicate;
     }
 
-    public static Predicate partitions(
-            List<Map<String, String>> partitions, RowType rowType, String 
defaultPartValue) {
+    public static Predicate partitions(List<Map<String, String>> partitions, 
RowType rowType) {
         return PredicateBuilder.or(
                 partitions.stream()
-                        .map(p -> PredicateBuilder.partition(p, rowType, 
defaultPartValue))
+                        .map(p -> PredicateBuilder.partition(p, rowType))
                         .toArray(Predicate[]::new));
     }
 
     public static Map<String, Object> convertSpecToInternal(
-            Map<String, String> spec, RowType partType, String 
defaultPartValue) {
+            Map<String, String> spec, RowType partType) {
         Map<String, Object> partValues = new LinkedHashMap<>();
         for (Map.Entry<String, String> entry : spec.entrySet()) {
             partValues.put(
                     entry.getKey(),
-                    defaultPartValue.equals(entry.getValue())
-                            ? null
-                            : TypeUtils.castFromString(
-                                    entry.getValue(), 
partType.getField(entry.getKey()).getType()));
+                    TypeUtils.castFromString(
+                            entry.getValue(), 
partType.getField(entry.getKey()).getType()));
         }
         return partValues;
     }
diff --git 
a/fluss-common/src/main/java/com/alibaba/fluss/predicate/PredicateVisitor.java 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/PredicateVisitor.java
index 3e20e9c92..fb6e854d2 100644
--- 
a/fluss-common/src/main/java/com/alibaba/fluss/predicate/PredicateVisitor.java
+++ 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/PredicateVisitor.java
@@ -21,7 +21,14 @@ package com.alibaba.fluss.predicate;
  * Software Foundation (ASF) under the Apache License, Version 2.0. See the 
NOTICE file distributed with this work for
  * additional information regarding copyright ownership. */
 
-/** A visitor to visit {@link Predicate}. */
+import com.alibaba.fluss.annotation.PublicEvolving;
+
+/**
+ * A visitor to visit {@link Predicate}.
+ *
+ * @since 0.8
+ */
+@PublicEvolving
 public interface PredicateVisitor<T> {
 
     T visit(LeafPredicate predicate);
diff --git 
a/fluss-common/src/main/java/com/alibaba/fluss/predicate/StartsWith.java 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/StartsWith.java
index e3baa41fa..88205c156 100644
--- a/fluss-common/src/main/java/com/alibaba/fluss/predicate/StartsWith.java
+++ b/fluss-common/src/main/java/com/alibaba/fluss/predicate/StartsWith.java
@@ -32,6 +32,8 @@ import java.util.Optional;
  */
 public class StartsWith extends NullFalseLeafBinaryFunction {
 
+    private static final long serialVersionUID = 1L;
+
     public static final StartsWith INSTANCE = new StartsWith();
 
     private StartsWith() {}
diff --git 
a/fluss-common/src/main/java/com/alibaba/fluss/predicate/UnsupportedExpression.java
 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/UnsupportedExpression.java
index 74b50f2f4..f0eddff5a 100644
--- 
a/fluss-common/src/main/java/com/alibaba/fluss/predicate/UnsupportedExpression.java
+++ 
b/fluss-common/src/main/java/com/alibaba/fluss/predicate/UnsupportedExpression.java
@@ -19,6 +19,9 @@ package com.alibaba.fluss.predicate;
 
 /** Encounter an unsupported expression, the caller can choose to ignore this 
filter branch. */
 public class UnsupportedExpression extends RuntimeException {
+
+    private static final long serialVersionUID = 1L;
+
     public UnsupportedExpression(String message) {
         super(message);
     }
diff --git a/fluss-common/src/main/java/com/alibaba/fluss/types/RowType.java 
b/fluss-common/src/main/java/com/alibaba/fluss/types/RowType.java
index 840d0a86b..da215e165 100644
--- a/fluss-common/src/main/java/com/alibaba/fluss/types/RowType.java
+++ b/fluss-common/src/main/java/com/alibaba/fluss/types/RowType.java
@@ -67,6 +67,16 @@ public final class RowType extends DataType {
         return 
fields.stream().map(DataField::getName).collect(Collectors.toList());
     }
 
+    public DataField getField(String fieldName) {
+        for (DataField field : fields) {
+            if (field.getName().equals(fieldName)) {
+                return field;
+            }
+        }
+
+        throw new RuntimeException("Cannot find field: " + fieldName);
+    }
+
     public DataType getTypeAt(int i) {
         return fields.get(i).getType();
     }
@@ -255,14 +265,4 @@ public final class RowType extends DataType {
             return new RowType(isNullable, fields);
         }
     }
-
-    public DataField getField(String fieldName) {
-        for (DataField field : fields) {
-            if (field.getName().equals(fieldName)) {
-                return field;
-            }
-        }
-
-        throw new RuntimeException("Cannot find field: " + fieldName);
-    }
 }
diff --git 
a/fluss-common/src/main/java/com/alibaba/fluss/utils/BinaryStringUtils.java 
b/fluss-common/src/main/java/com/alibaba/fluss/utils/BinaryStringUtils.java
index 78c792920..70967c362 100644
--- a/fluss-common/src/main/java/com/alibaba/fluss/utils/BinaryStringUtils.java
+++ b/fluss-common/src/main/java/com/alibaba/fluss/utils/BinaryStringUtils.java
@@ -70,15 +70,15 @@ public class BinaryStringUtils {
         return date;
     }
 
-    /** Used by {@code CAST(x as TIMESTAMPNTZ)}. */
+    /** Used by {@code CAST(x as TIMESTAMP_NTZ)}. */
     public static TimestampNtz toTimestampNtz(BinaryString input, int 
precision)
             throws DateTimeException {
         return DateTimeUtils.parseTimestampData(input.toString(), precision);
     }
 
-    /** Used by {@code CAST(x as TIMESTAMPLTZ)}. */
-    public static TimestampLtz toTimestampltz(BinaryString input, int 
precision, TimeZone timeZone)
-            throws DateTimeException {
-        return DateTimeUtils.parseTimestampData(input.toString(), precision, 
timeZone);
+    /** Used by {@code CAST(x as TIMESTAMP_LTZ)}. */
+    public static TimestampLtz toTimestampLtz(
+            BinaryString input, int precision, TimeZone localTimeZone) throws 
DateTimeException {
+        return DateTimeUtils.parseTimestampData(input.toString(), precision, 
localTimeZone);
     }
 }
diff --git 
a/fluss-common/src/test/java/com/alibaba/fluss/predicate/PredicateBuilderTest.java
 
b/fluss-common/src/test/java/com/alibaba/fluss/predicate/PredicateBuilderTest.java
index acb193fe3..a47275bc6 100644
--- 
a/fluss-common/src/test/java/com/alibaba/fluss/predicate/PredicateBuilderTest.java
+++ 
b/fluss-common/src/test/java/com/alibaba/fluss/predicate/PredicateBuilderTest.java
@@ -17,14 +17,16 @@
 
 package com.alibaba.fluss.predicate;
 
-import com.alibaba.fluss.row.GenericRow;
 import com.alibaba.fluss.types.IntType;
 import com.alibaba.fluss.types.RowType;
 
 import org.junit.jupiter.api.Test;
 
+import javax.annotation.Nullable;
+
 import java.util.Arrays;
 
+import static com.alibaba.fluss.testutils.DataTestUtils.row;
 import static org.assertj.core.api.Assertions.assertThat;
 
 /** Tests for {@link PredicateBuilder}. */
@@ -35,11 +37,17 @@ public class PredicateBuilderTest {
         PredicateBuilder builder = new PredicateBuilder(RowType.of(new 
IntType()));
         Predicate predicate = builder.between(0, 1, 3);
 
-        assertThat(predicate.test(GenericRow.of(1))).isEqualTo(true);
-        assertThat(predicate.test(GenericRow.of(2))).isEqualTo(true);
-        assertThat(predicate.test(GenericRow.of(3))).isEqualTo(true);
-        assertThat(predicate.test(GenericRow.of(4))).isEqualTo(false);
-        assertThat(predicate.test(GenericRow.of((Object) 
null))).isEqualTo(false);
+        assertThat(predicate.test(row(1))).isEqualTo(true);
+        assertThat(predicate.test(row(2))).isEqualTo(true);
+        assertThat(predicate.test(row(3))).isEqualTo(true);
+        assertThat(predicate.test(row(4))).isEqualTo(false);
+        assertThat(predicate.test(row((Object) null))).isEqualTo(false);
+
+        assertThat(test(predicate, 3, 0, 5, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 3, 2, 5, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 3, 0, 2, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 3, 6, 7, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 1, null, null, 1L)).isEqualTo(false);
     }
 
     @Test
@@ -47,11 +55,17 @@ public class PredicateBuilderTest {
         PredicateBuilder builder = new PredicateBuilder(RowType.of(new 
IntType()));
         Predicate predicate = builder.between(0, 1, null);
 
-        assertThat(predicate.test(GenericRow.of(1))).isEqualTo(false);
-        assertThat(predicate.test(GenericRow.of(2))).isEqualTo(false);
-        assertThat(predicate.test(GenericRow.of(3))).isEqualTo(false);
-        assertThat(predicate.test(GenericRow.of(4))).isEqualTo(false);
-        assertThat(predicate.test(GenericRow.of((Object) 
null))).isEqualTo(false);
+        assertThat(predicate.test(row(1))).isEqualTo(false);
+        assertThat(predicate.test(row(2))).isEqualTo(false);
+        assertThat(predicate.test(row(3))).isEqualTo(false);
+        assertThat(predicate.test(row(4))).isEqualTo(false);
+        assertThat(predicate.test(row((Object) null))).isEqualTo(false);
+
+        assertThat(test(predicate, 3, 0, 5, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 3, 2, 5, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 3, 0, 2, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 3, 6, 7, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 1, null, null, 1L)).isEqualTo(false);
     }
 
     @Test
@@ -82,4 +96,13 @@ public class PredicateBuilderTest {
                                 builder.isNull(5),
                                 child3));
     }
+
+    static boolean test(
+            Predicate predicate,
+            long rowCount,
+            @Nullable Object min,
+            @Nullable Object max,
+            @Nullable Long nullCount) {
+        return predicate.test(rowCount, row(min), row(max), new Long[] 
{nullCount});
+    }
 }
diff --git 
a/fluss-common/src/test/java/com/alibaba/fluss/predicate/PredicateTest.java 
b/fluss-common/src/test/java/com/alibaba/fluss/predicate/PredicateTest.java
index 427fec8a8..f5e1e30aa 100644
--- a/fluss-common/src/test/java/com/alibaba/fluss/predicate/PredicateTest.java
+++ b/fluss-common/src/test/java/com/alibaba/fluss/predicate/PredicateTest.java
@@ -28,7 +28,9 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
+import static com.alibaba.fluss.predicate.PredicateBuilderTest.test;
 import static com.alibaba.fluss.row.BinaryString.fromString;
+import static com.alibaba.fluss.testutils.DataTestUtils.row;
 import static org.assertj.core.api.Assertions.assertThat;
 
 /** Test for {@link Predicate}s. */
@@ -43,6 +45,11 @@ public class PredicateTest {
         assertThat(predicate.test(GenericRow.of(5))).isEqualTo(true);
         assertThat(predicate.test(GenericRow.of((Object) 
null))).isEqualTo(false);
 
+        assertThat(test(predicate, 3, 0, 5, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 3, 0, 6, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 3, 6, 7, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 1, null, null, 1L)).isEqualTo(false);
+
         
assertThat(predicate.negate().orElse(null)).isEqualTo(builder.notEqual(0, 5));
     }
 
@@ -51,6 +58,10 @@ public class PredicateTest {
         PredicateBuilder builder = new PredicateBuilder(RowType.of(new 
IntType()));
         Predicate predicate = builder.equal(0, null);
 
+        assertThat(test(predicate, 3, 0, 5, 0L)).isEqualTo(false);
+        // null not equal to null
+        assertThat(test(predicate, 1, null, null, 1L)).isEqualTo(false);
+
         assertThat(predicate.test(GenericRow.of(4))).isEqualTo(false);
         assertThat(predicate.test(GenericRow.of((Object) 
null))).isEqualTo(false);
     }
@@ -64,6 +75,12 @@ public class PredicateTest {
         assertThat(predicate.test(GenericRow.of(5))).isEqualTo(false);
         assertThat(predicate.test(GenericRow.of((Object) 
null))).isEqualTo(false);
 
+        assertThat(test(predicate, 3, 0, 5, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 3, 0, 6, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 3, 6, 7, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 1, 5, 5, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 1, null, null, 1L)).isEqualTo(false);
+
         assertThat(predicate.negate().orElse(null)).isEqualTo(builder.equal(0, 
5));
     }
 
@@ -74,6 +91,9 @@ public class PredicateTest {
 
         assertThat(predicate.test(GenericRow.of(4))).isEqualTo(false);
         assertThat(predicate.test(GenericRow.of((Object) 
null))).isEqualTo(false);
+
+        assertThat(test(predicate, 3, 0, 5, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 1, null, null, 1L)).isEqualTo(false);
     }
 
     @Test
@@ -86,6 +106,12 @@ public class PredicateTest {
         assertThat(predicate.test(GenericRow.of(6))).isEqualTo(true);
         assertThat(predicate.test(GenericRow.of((Object) 
null))).isEqualTo(false);
 
+        assertThat(test(predicate, 3, 0, 4, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 3, 0, 5, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 3, 0, 6, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 1, 6, 7, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 1, null, null, 1L)).isEqualTo(false);
+
         
assertThat(predicate.negate().orElse(null)).isEqualTo(builder.lessOrEqual(0, 
5));
     }
 
@@ -96,6 +122,9 @@ public class PredicateTest {
 
         assertThat(predicate.test(GenericRow.of(4))).isEqualTo(false);
         assertThat(predicate.test(GenericRow.of((Object) 
null))).isEqualTo(false);
+
+        assertThat(test(predicate, 1, 6, 7, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 1, null, null, 1L)).isEqualTo(false);
     }
 
     @Test
@@ -108,6 +137,12 @@ public class PredicateTest {
         assertThat(predicate.test(GenericRow.of(6))).isEqualTo(true);
         assertThat(predicate.test(GenericRow.of((Object) 
null))).isEqualTo(false);
 
+        assertThat(test(predicate, 3, 0, 4, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 3, 0, 5, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 3, 0, 6, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 1, 6, 7, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 1, null, null, 1L)).isEqualTo(false);
+
         
assertThat(predicate.negate().orElse(null)).isEqualTo(builder.lessThan(0, 5));
     }
 
@@ -118,6 +153,9 @@ public class PredicateTest {
 
         assertThat(predicate.test(GenericRow.of(4))).isEqualTo(false);
         assertThat(predicate.test(GenericRow.of((Object) 
null))).isEqualTo(false);
+
+        assertThat(test(predicate, 1, 0, 4, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 1, null, null, 1L)).isEqualTo(false);
     }
 
     @Test
@@ -130,6 +168,12 @@ public class PredicateTest {
         assertThat(predicate.test(GenericRow.of(6))).isEqualTo(false);
         assertThat(predicate.test(GenericRow.of((Object) 
null))).isEqualTo(false);
 
+        assertThat(test(predicate, 3, 6, 7, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 3, 5, 7, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 3, 4, 7, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 1, 3, 5, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 1, null, null, 1L)).isEqualTo(false);
+
         
assertThat(predicate.negate().orElse(null)).isEqualTo(builder.greaterOrEqual(0, 
5));
     }
 
@@ -140,6 +184,9 @@ public class PredicateTest {
 
         assertThat(predicate.test(GenericRow.of(4))).isEqualTo(false);
         assertThat(predicate.test(GenericRow.of((Object) 
null))).isEqualTo(false);
+
+        assertThat(test(predicate, 1, 3, 5, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 1, null, null, 1L)).isEqualTo(false);
     }
 
     @Test
@@ -152,6 +199,12 @@ public class PredicateTest {
         assertThat(predicate.test(GenericRow.of(6))).isEqualTo(false);
         assertThat(predicate.test(GenericRow.of((Object) 
null))).isEqualTo(false);
 
+        assertThat(test(predicate, 3, 6, 7, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 3, 5, 7, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 3, 4, 7, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 1, 3, 5, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 1, null, null, 1L)).isEqualTo(false);
+
         
assertThat(predicate.negate().orElse(null)).isEqualTo(builder.greaterThan(0, 
5));
     }
 
@@ -162,6 +215,9 @@ public class PredicateTest {
 
         assertThat(predicate.test(GenericRow.of(4))).isEqualTo(false);
         assertThat(predicate.test(GenericRow.of((Object) 
null))).isEqualTo(false);
+
+        assertThat(test(predicate, 1, 3, 5, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 1, null, null, 1L)).isEqualTo(false);
     }
 
     @Test
@@ -172,6 +228,11 @@ public class PredicateTest {
         assertThat(predicate.test(GenericRow.of(4))).isEqualTo(false);
         assertThat(predicate.test(GenericRow.of((Object) 
null))).isEqualTo(true);
 
+        assertThat(test(predicate, 3, 6, 7, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 3, 5, 7, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 3, 5, 7, 1L)).isEqualTo(true);
+        assertThat(test(predicate, 1, null, null, 1L)).isEqualTo(true);
+
         
assertThat(predicate.negate().orElse(null)).isEqualTo(builder.isNotNull(0));
     }
 
@@ -183,6 +244,11 @@ public class PredicateTest {
         assertThat(predicate.test(GenericRow.of(4))).isEqualTo(true);
         assertThat(predicate.test(GenericRow.of((Object) 
null))).isEqualTo(false);
 
+        assertThat(test(predicate, 3, 6, 7, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 3, 5, 7, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 3, 5, 7, 1L)).isEqualTo(true);
+        assertThat(test(predicate, 1, null, null, 1L)).isEqualTo(false);
+
         
assertThat(predicate.negate().orElse(null)).isEqualTo(builder.isNull(0));
     }
 
@@ -196,6 +262,10 @@ public class PredicateTest {
         assertThat(predicate.test(GenericRow.of(2))).isEqualTo(false);
         assertThat(predicate.test(GenericRow.of(3))).isEqualTo(true);
         assertThat(predicate.test(GenericRow.of((Object) 
null))).isEqualTo(false);
+
+        assertThat(test(predicate, 3, 0, 5, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 3, 6, 7, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 1, null, null, 1L)).isEqualTo(false);
     }
 
     @Test
@@ -208,6 +278,10 @@ public class PredicateTest {
         assertThat(predicate.test(GenericRow.of(2))).isEqualTo(false);
         assertThat(predicate.test(GenericRow.of(3))).isEqualTo(true);
         assertThat(predicate.test(GenericRow.of((Object) 
null))).isEqualTo(false);
+
+        assertThat(test(predicate, 3, 0, 5, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 3, 6, 7, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 1, null, null, 1L)).isEqualTo(false);
     }
 
     @Test
@@ -220,6 +294,13 @@ public class PredicateTest {
         assertThat(predicate.test(GenericRow.of(2))).isEqualTo(true);
         assertThat(predicate.test(GenericRow.of(3))).isEqualTo(false);
         assertThat(predicate.test(GenericRow.of((Object) 
null))).isEqualTo(false);
+
+        assertThat(test(predicate, 3, 1, 1, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 3, 3, 3, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 3, 1, 3, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 3, 0, 5, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 3, 6, 7, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 1, null, null, 1L)).isEqualTo(false);
     }
 
     @Test
@@ -232,6 +313,13 @@ public class PredicateTest {
         assertThat(predicate.test(GenericRow.of(2))).isEqualTo(false);
         assertThat(predicate.test(GenericRow.of(3))).isEqualTo(false);
         assertThat(predicate.test(GenericRow.of((Object) 
null))).isEqualTo(false);
+
+        assertThat(test(predicate, 3, 1, 1, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 3, 3, 3, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 3, 1, 3, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 3, 0, 5, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 3, 6, 7, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 1, null, null, 1L)).isEqualTo(false);
     }
 
     @Test
@@ -239,8 +327,13 @@ public class PredicateTest {
         PredicateBuilder builder = new PredicateBuilder(RowType.of(new 
StringType()));
         Predicate predicate = builder.endsWith(0, ("bcc"));
         GenericRow row = GenericRow.of(fromString("aabbcc"));
-
         assertThat(predicate.test(row)).isEqualTo(true);
+
+        GenericRow max = row(fromString("aaba"));
+        GenericRow min = row(fromString("aabb"));
+        Long[] nullCounts = {null};
+        assertThat(predicate.test(10, min, max, nullCounts)).isEqualTo(true);
+        assertThat(predicate.test(10, min, max, new Long[] 
{10L})).isEqualTo(false);
     }
 
     @Test
@@ -248,8 +341,17 @@ public class PredicateTest {
         PredicateBuilder builder = new PredicateBuilder(RowType.of(new 
StringType()));
         Predicate predicate = builder.startsWith(0, ("aab"));
         GenericRow row = GenericRow.of(fromString("aabbcc"));
-
         assertThat(predicate.test(row)).isEqualTo(true);
+
+        GenericRow aaab = row(fromString("aaab"));
+        GenericRow aaba = row(fromString("aaba"));
+        GenericRow bbaa = row(fromString("bbaa"));
+        GenericRow ccbb = row(fromString("ccbb"));
+        Long[] nullCounts = {null};
+        assertThat(predicate.test(10, aaab, aaba, nullCounts)).isEqualTo(true);
+        assertThat(predicate.test(10, aaba, bbaa, nullCounts)).isEqualTo(true);
+        assertThat(predicate.test(10, bbaa, ccbb, 
nullCounts)).isEqualTo(false);
+        assertThat(predicate.test(10, aaab, aaba, new Long[] 
{10L})).isEqualTo(false);
     }
 
     @Test
@@ -258,9 +360,14 @@ public class PredicateTest {
         Predicate predicate = builder.contains(0, ("def"));
         GenericRow row1 = GenericRow.of(fromString("aabbdefcc"));
         GenericRow row2 = GenericRow.of(fromString("aabbdcefcc"));
-
         assertThat(predicate.test(row1)).isEqualTo(true);
         assertThat(predicate.test(row2)).isEqualTo(false);
+
+        GenericRow aaab = row(fromString("aaab"));
+        GenericRow aaba = row(fromString("aaba"));
+        Long[] nullCounts = {null};
+        assertThat(predicate.test(10, aaab, aaba, nullCounts)).isEqualTo(true);
+        assertThat(predicate.test(10, aaab, aaba, new Long[] 
{10L})).isEqualTo(false);
     }
 
     @Test
@@ -279,6 +386,11 @@ public class PredicateTest {
         assertThat(predicate.test(GenericRow.of(2))).isEqualTo(false);
         assertThat(predicate.test(GenericRow.of(3))).isEqualTo(true);
         assertThat(predicate.test(GenericRow.of((Object) 
null))).isEqualTo(false);
+
+        assertThat(test(predicate, 3, 0, 5, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 3, 6, 7, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 3, 29, 32, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 1, null, null, 1L)).isEqualTo(false);
     }
 
     @Test
@@ -298,6 +410,11 @@ public class PredicateTest {
         assertThat(predicate.test(GenericRow.of(2))).isEqualTo(false);
         assertThat(predicate.test(GenericRow.of(3))).isEqualTo(true);
         assertThat(predicate.test(GenericRow.of((Object) 
null))).isEqualTo(false);
+
+        assertThat(test(predicate, 3, 0, 5, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 3, 6, 7, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 3, 29, 32, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 1, null, null, 1L)).isEqualTo(false);
     }
 
     @Test
@@ -316,6 +433,14 @@ public class PredicateTest {
         assertThat(predicate.test(GenericRow.of(2))).isEqualTo(true);
         assertThat(predicate.test(GenericRow.of(3))).isEqualTo(false);
         assertThat(predicate.test(GenericRow.of((Object) 
null))).isEqualTo(false);
+
+        assertThat(test(predicate, 3, 1, 1, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 3, 3, 3, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 3, 1, 3, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 3, 0, 5, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 3, 6, 7, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 3, 29, 32, 0L)).isEqualTo(true);
+        assertThat(test(predicate, 1, null, null, 1L)).isEqualTo(false);
     }
 
     @Test
@@ -335,6 +460,14 @@ public class PredicateTest {
         assertThat(predicate.test(GenericRow.of(2))).isEqualTo(false);
         assertThat(predicate.test(GenericRow.of(3))).isEqualTo(false);
         assertThat(predicate.test(GenericRow.of((Object) 
null))).isEqualTo(false);
+
+        assertThat(test(predicate, 3, 1, 1, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 3, 3, 3, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 3, 1, 3, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 3, 0, 5, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 3, 6, 7, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 3, 29, 32, 0L)).isEqualTo(false);
+        assertThat(test(predicate, 1, null, null, 1L)).isEqualTo(false);
     }
 
     @Test
@@ -347,6 +480,10 @@ public class PredicateTest {
         assertThat(predicate.test(GenericRow.of(3, 5))).isEqualTo(true);
         assertThat(predicate.test(GenericRow.of(null, 5))).isEqualTo(false);
 
+        assertThat(predicate.test(3, row(3, 4), row(6, 6), new Long[] {0L, 
0L})).isEqualTo(true);
+        assertThat(predicate.test(3, row(3, 6), row(6, 8), new Long[] {0L, 
0L})).isEqualTo(false);
+        assertThat(predicate.test(3, row(6, 4), row(7, 6), new Long[] {0L, 
0L})).isEqualTo(false);
+
         assertThat(predicate.negate().orElse(null))
                 .isEqualTo(PredicateBuilder.or(builder.notEqual(0, 3), 
builder.notEqual(1, 5)));
     }
@@ -361,6 +498,10 @@ public class PredicateTest {
         assertThat(predicate.test(GenericRow.of(3, 5))).isEqualTo(true);
         assertThat(predicate.test(GenericRow.of(null, 5))).isEqualTo(true);
 
+        assertThat(predicate.test(3, row(3, 4), row(6, 6), new Long[] {0L, 
0L})).isEqualTo(true);
+        assertThat(predicate.test(3, row(3, 6), row(6, 8), new Long[] {0L, 
0L})).isEqualTo(true);
+        assertThat(predicate.test(3, row(6, 8), row(7, 10), new Long[] {0L, 
0L})).isEqualTo(false);
+
         assertThat(predicate.negate().orElse(null))
                 .isEqualTo(PredicateBuilder.and(builder.notEqual(0, 3), 
builder.notEqual(1, 5)));
     }
@@ -369,6 +510,9 @@ public class PredicateTest {
     public void testUnknownStats() {
         PredicateBuilder builder = new PredicateBuilder(RowType.of(new 
IntType()));
         Predicate predicate = builder.equal(0, 5);
+
+        assertThat(test(predicate, 3, null, null, null)).isEqualTo(true);
+        assertThat(test(predicate, 3, null, null, 3L)).isEqualTo(false);
     }
 
     @Test

Reply via email to