This is an automated email from the ASF dual-hosted git repository.
liurenjie1024 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-rust.git
The following commit(s) were added to refs/heads/main by this push:
new 9160bba feat: add BoundPredicateVisitor. Add AlwaysTrue and
AlwaysFalse to Predicate (#334)
9160bba is described below
commit 9160bba7476961034b6eb023ddaa136abe65417d
Author: Scott Donnelly <[email protected]>
AuthorDate: Tue Apr 16 13:53:51 2024 +0100
feat: add BoundPredicateVisitor. Add AlwaysTrue and AlwaysFalse to
Predicate (#334)
---
crates/iceberg/src/expr/mod.rs | 8 +-
crates/iceberg/src/expr/predicate.rs | 30 +
.../src/expr/visitors/bound_predicate_visitor.rs | 644 +++++++++++++++++++++
crates/iceberg/src/expr/visitors/mod.rs | 18 +
4 files changed, 696 insertions(+), 4 deletions(-)
diff --git a/crates/iceberg/src/expr/mod.rs b/crates/iceberg/src/expr/mod.rs
index dccafb7..3d77c4d 100644
--- a/crates/iceberg/src/expr/mod.rs
+++ b/crates/iceberg/src/expr/mod.rs
@@ -18,15 +18,14 @@
//! This module contains expressions.
mod term;
-
-use std::fmt::{Display, Formatter};
-
pub use term::*;
pub(crate) mod accessor;
mod predicate;
+pub(crate) mod visitors;
+pub use predicate::*;
use crate::spec::SchemaRef;
-pub use predicate::*;
+use std::fmt::{Display, Formatter};
/// Predicate operators used in expressions.
///
@@ -34,6 +33,7 @@ pub use predicate::*;
/// [`PredicateOperator::is_unary`], [`PredicateOperator::is_binary`],
[`PredicateOperator::is_set`]
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq)]
+#[non_exhaustive]
#[repr(u16)]
pub enum PredicateOperator {
// Unary operators
diff --git a/crates/iceberg/src/expr/predicate.rs
b/crates/iceberg/src/expr/predicate.rs
index 530923f..1163f3b 100644
--- a/crates/iceberg/src/expr/predicate.rs
+++ b/crates/iceberg/src/expr/predicate.rs
@@ -119,6 +119,10 @@ impl<T> UnaryExpression<T> {
pub(crate) fn op(&self) -> PredicateOperator {
self.op
}
+
+ pub(crate) fn term(&self) -> &T {
+ &self.term
+ }
}
/// Binary predicate, for example, `a > 10`.
@@ -147,12 +151,17 @@ impl<T> BinaryExpression<T> {
debug_assert!(op.is_binary());
Self { op, term, literal }
}
+
pub(crate) fn op(&self) -> PredicateOperator {
self.op
}
pub(crate) fn literal(&self) -> &Datum {
&self.literal
}
+
+ pub(crate) fn term(&self) -> &T {
+ &self.term
+ }
}
impl<T: Display> Display for BinaryExpression<T> {
@@ -200,12 +209,17 @@ impl<T> SetExpression<T> {
debug_assert!(op.is_set());
Self { op, term, literals }
}
+
pub(crate) fn op(&self) -> PredicateOperator {
self.op
}
pub(crate) fn literals(&self) -> &FnvHashSet<Datum> {
&self.literals
}
+
+ pub(crate) fn term(&self) -> &T {
+ &self.term
+ }
}
impl<T: Bind> Bind for SetExpression<T> {
@@ -232,6 +246,10 @@ impl<T: Display + Debug> Display for SetExpression<T> {
/// Unbound predicate expression before binding to a schema.
#[derive(Debug, PartialEq)]
pub enum Predicate {
+ /// AlwaysTrue predicate, for example, `TRUE`.
+ AlwaysTrue,
+ /// AlwaysFalse predicate, for example, `FALSE`.
+ AlwaysFalse,
/// And predicate, for example, `a > 10 AND b < 20`.
And(LogicalExpression<Predicate, 2>),
/// Or predicate, for example, `a > 10 OR b < 20`.
@@ -382,6 +400,8 @@ impl Bind for Predicate {
bound_literals,
)))
}
+ Predicate::AlwaysTrue => Ok(BoundPredicate::AlwaysTrue),
+ Predicate::AlwaysFalse => Ok(BoundPredicate::AlwaysFalse),
}
}
}
@@ -389,6 +409,12 @@ impl Bind for Predicate {
impl Display for Predicate {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
+ Predicate::AlwaysTrue => {
+ write!(f, "TRUE")
+ }
+ Predicate::AlwaysFalse => {
+ write!(f, "FALSE")
+ }
Predicate::And(expr) => {
write!(f, "({}) AND ({})", expr.inputs()[0], expr.inputs()[1])
}
@@ -476,6 +502,8 @@ impl Predicate {
/// ```
pub fn negate(self) -> Predicate {
match self {
+ Predicate::AlwaysTrue => Predicate::AlwaysFalse,
+ Predicate::AlwaysFalse => Predicate::AlwaysTrue,
Predicate::And(expr) => Predicate::Or(LogicalExpression::new(
expr.inputs.map(|expr| Box::new(expr.negate())),
)),
@@ -540,6 +568,8 @@ impl Predicate {
Predicate::Unary(expr) => Predicate::Unary(expr),
Predicate::Binary(expr) => Predicate::Binary(expr),
Predicate::Set(expr) => Predicate::Set(expr),
+ Predicate::AlwaysTrue => Predicate::AlwaysTrue,
+ Predicate::AlwaysFalse => Predicate::AlwaysFalse,
}
}
}
diff --git a/crates/iceberg/src/expr/visitors/bound_predicate_visitor.rs
b/crates/iceberg/src/expr/visitors/bound_predicate_visitor.rs
new file mode 100644
index 0000000..f29afbf
--- /dev/null
+++ b/crates/iceberg/src/expr/visitors/bound_predicate_visitor.rs
@@ -0,0 +1,644 @@
+// 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.
+
+use crate::expr::{BoundPredicate, BoundReference, PredicateOperator};
+use crate::spec::Datum;
+use crate::Result;
+use fnv::FnvHashSet;
+
+/// A visitor for [`BoundPredicate`]s. Visits in post-order.
+pub trait BoundPredicateVisitor {
+ /// The return type of this visitor
+ type T;
+
+ /// Called after an `AlwaysTrue` predicate is visited
+ fn always_true(&mut self) -> Result<Self::T>;
+
+ /// Called after an `AlwaysFalse` predicate is visited
+ fn always_false(&mut self) -> Result<Self::T>;
+
+ /// Called after an `And` predicate is visited
+ fn and(&mut self, lhs: Self::T, rhs: Self::T) -> Result<Self::T>;
+
+ /// Called after an `Or` predicate is visited
+ fn or(&mut self, lhs: Self::T, rhs: Self::T) -> Result<Self::T>;
+
+ /// Called after a `Not` predicate is visited
+ fn not(&mut self, inner: Self::T) -> Result<Self::T>;
+
+ /// Called after a predicate with an `IsNull` operator is visited
+ fn is_null(&mut self, reference: &BoundReference) -> Result<Self::T>;
+
+ /// Called after a predicate with a `NotNull` operator is visited
+ fn not_null(&mut self, reference: &BoundReference) -> Result<Self::T>;
+
+ /// Called after a predicate with an `IsNan` operator is visited
+ fn is_nan(&mut self, reference: &BoundReference) -> Result<Self::T>;
+
+ /// Called after a predicate with a `NotNan` operator is visited
+ fn not_nan(&mut self, reference: &BoundReference) -> Result<Self::T>;
+
+ /// Called after a predicate with a `LessThan` operator is visited
+ fn less_than(&mut self, reference: &BoundReference, literal: &Datum) ->
Result<Self::T>;
+
+ /// Called after a predicate with a `LessThanOrEq` operator is visited
+ fn less_than_or_eq(&mut self, reference: &BoundReference, literal: &Datum)
-> Result<Self::T>;
+
+ /// Called after a predicate with a `GreaterThan` operator is visited
+ fn greater_than(&mut self, reference: &BoundReference, literal: &Datum) ->
Result<Self::T>;
+
+ /// Called after a predicate with a `GreaterThanOrEq` operator is visited
+ fn greater_than_or_eq(
+ &mut self,
+ reference: &BoundReference,
+ literal: &Datum,
+ ) -> Result<Self::T>;
+
+ /// Called after a predicate with an `Eq` operator is visited
+ fn eq(&mut self, reference: &BoundReference, literal: &Datum) ->
Result<Self::T>;
+
+ /// Called after a predicate with a `NotEq` operator is visited
+ fn not_eq(&mut self, reference: &BoundReference, literal: &Datum) ->
Result<Self::T>;
+
+ /// Called after a predicate with a `StartsWith` operator is visited
+ fn starts_with(&mut self, reference: &BoundReference, literal: &Datum) ->
Result<Self::T>;
+
+ /// Called after a predicate with a `NotStartsWith` operator is visited
+ fn not_starts_with(&mut self, reference: &BoundReference, literal: &Datum)
-> Result<Self::T>;
+
+ /// Called after a predicate with an `In` operator is visited
+ fn r#in(&mut self, reference: &BoundReference, literals:
&FnvHashSet<Datum>)
+ -> Result<Self::T>;
+
+ /// Called after a predicate with a `NotIn` operator is visited
+ fn not_in(
+ &mut self,
+ reference: &BoundReference,
+ literals: &FnvHashSet<Datum>,
+ ) -> Result<Self::T>;
+}
+
+/// Visits a [`BoundPredicate`] with the provided visitor,
+/// in post-order
+pub(crate) fn visit<V: BoundPredicateVisitor>(
+ visitor: &mut V,
+ predicate: &BoundPredicate,
+) -> Result<V::T> {
+ match predicate {
+ BoundPredicate::AlwaysTrue => visitor.always_true(),
+ BoundPredicate::AlwaysFalse => visitor.always_false(),
+ BoundPredicate::And(expr) => {
+ let [left_pred, right_pred] = expr.inputs();
+
+ let left_result = visit(visitor, left_pred)?;
+ let right_result = visit(visitor, right_pred)?;
+
+ visitor.and(left_result, right_result)
+ }
+ BoundPredicate::Or(expr) => {
+ let [left_pred, right_pred] = expr.inputs();
+
+ let left_result = visit(visitor, left_pred)?;
+ let right_result = visit(visitor, right_pred)?;
+
+ visitor.or(left_result, right_result)
+ }
+ BoundPredicate::Not(expr) => {
+ let [inner_pred] = expr.inputs();
+
+ let inner_result = visit(visitor, inner_pred)?;
+
+ visitor.not(inner_result)
+ }
+ BoundPredicate::Unary(expr) => match expr.op() {
+ PredicateOperator::IsNull => visitor.is_null(expr.term()),
+ PredicateOperator::NotNull => visitor.not_null(expr.term()),
+ PredicateOperator::IsNan => visitor.is_nan(expr.term()),
+ PredicateOperator::NotNan => visitor.not_nan(expr.term()),
+ op => {
+ panic!("Unexpected op for unary predicate: {}", &op)
+ }
+ },
+ BoundPredicate::Binary(expr) => {
+ let reference = expr.term();
+ let literal = expr.literal();
+ match expr.op() {
+ PredicateOperator::LessThan => visitor.less_than(reference,
literal),
+ PredicateOperator::LessThanOrEq =>
visitor.less_than_or_eq(reference, literal),
+ PredicateOperator::GreaterThan =>
visitor.greater_than(reference, literal),
+ PredicateOperator::GreaterThanOrEq => {
+ visitor.greater_than_or_eq(reference, literal)
+ }
+ PredicateOperator::Eq => visitor.eq(reference, literal),
+ PredicateOperator::NotEq => visitor.not_eq(reference, literal),
+ PredicateOperator::StartsWith =>
visitor.starts_with(reference, literal),
+ PredicateOperator::NotStartsWith =>
visitor.not_starts_with(reference, literal),
+ op => {
+ panic!("Unexpected op for binary predicate: {}", &op)
+ }
+ }
+ }
+ BoundPredicate::Set(expr) => {
+ let reference = expr.term();
+ let literals = expr.literals();
+ match expr.op() {
+ PredicateOperator::In => visitor.r#in(reference, literals),
+ PredicateOperator::NotIn => visitor.not_in(reference,
literals),
+ op => {
+ panic!("Unexpected op for set predicate: {}", &op)
+ }
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::expr::visitors::bound_predicate_visitor::{visit,
BoundPredicateVisitor};
+ use crate::expr::{
+ BinaryExpression, Bind, BoundReference, Predicate, PredicateOperator,
Reference,
+ SetExpression, UnaryExpression,
+ };
+ use crate::spec::{Datum, NestedField, PrimitiveType, Schema, SchemaRef,
Type};
+ use fnv::FnvHashSet;
+ use std::ops::Not;
+ use std::sync::Arc;
+
+ struct TestEvaluator {}
+ impl BoundPredicateVisitor for TestEvaluator {
+ type T = bool;
+
+ fn always_true(&mut self) -> crate::Result<Self::T> {
+ Ok(true)
+ }
+
+ fn always_false(&mut self) -> crate::Result<Self::T> {
+ Ok(false)
+ }
+
+ fn and(&mut self, lhs: Self::T, rhs: Self::T) ->
crate::Result<Self::T> {
+ Ok(lhs && rhs)
+ }
+
+ fn or(&mut self, lhs: Self::T, rhs: Self::T) -> crate::Result<Self::T>
{
+ Ok(lhs || rhs)
+ }
+
+ fn not(&mut self, inner: Self::T) -> crate::Result<Self::T> {
+ Ok(!inner)
+ }
+
+ fn is_null(&mut self, _reference: &BoundReference) ->
crate::Result<bool> {
+ Ok(true)
+ }
+
+ fn not_null(&mut self, _reference: &BoundReference) ->
crate::Result<bool> {
+ Ok(false)
+ }
+
+ fn is_nan(&mut self, _reference: &BoundReference) ->
crate::Result<bool> {
+ Ok(true)
+ }
+
+ fn not_nan(&mut self, _reference: &BoundReference) ->
crate::Result<bool> {
+ Ok(false)
+ }
+
+ fn less_than(
+ &mut self,
+ _reference: &BoundReference,
+ _literal: &Datum,
+ ) -> crate::Result<bool> {
+ Ok(true)
+ }
+
+ fn less_than_or_eq(
+ &mut self,
+ _reference: &BoundReference,
+ _literal: &Datum,
+ ) -> crate::Result<bool> {
+ Ok(false)
+ }
+
+ fn greater_than(
+ &mut self,
+ _reference: &BoundReference,
+ _literal: &Datum,
+ ) -> crate::Result<bool> {
+ Ok(true)
+ }
+
+ fn greater_than_or_eq(
+ &mut self,
+ _reference: &BoundReference,
+ _literal: &Datum,
+ ) -> crate::Result<bool> {
+ Ok(false)
+ }
+
+ fn eq(&mut self, _reference: &BoundReference, _literal: &Datum) ->
crate::Result<bool> {
+ Ok(true)
+ }
+
+ fn not_eq(&mut self, _reference: &BoundReference, _literal: &Datum) ->
crate::Result<bool> {
+ Ok(false)
+ }
+
+ fn starts_with(
+ &mut self,
+ _reference: &BoundReference,
+ _literal: &Datum,
+ ) -> crate::Result<bool> {
+ Ok(true)
+ }
+
+ fn not_starts_with(
+ &mut self,
+ _reference: &BoundReference,
+ _literal: &Datum,
+ ) -> crate::Result<bool> {
+ Ok(false)
+ }
+
+ fn r#in(
+ &mut self,
+ _reference: &BoundReference,
+ _literals: &FnvHashSet<Datum>,
+ ) -> crate::Result<bool> {
+ Ok(true)
+ }
+
+ fn not_in(
+ &mut self,
+ _reference: &BoundReference,
+ _literals: &FnvHashSet<Datum>,
+ ) -> crate::Result<bool> {
+ Ok(false)
+ }
+ }
+
+ fn create_test_schema() -> SchemaRef {
+ let schema = Schema::builder()
+ .with_fields(vec![
+ Arc::new(NestedField::required(
+ 1,
+ "a",
+ Type::Primitive(PrimitiveType::Int),
+ )),
+ Arc::new(NestedField::required(
+ 2,
+ "b",
+ Type::Primitive(PrimitiveType::Float),
+ )),
+ Arc::new(NestedField::optional(
+ 3,
+ "c",
+ Type::Primitive(PrimitiveType::Float),
+ )),
+ ])
+ .build()
+ .unwrap();
+
+ let schema_arc = Arc::new(schema);
+ schema_arc.clone()
+ }
+
+ #[test]
+ fn test_always_true() {
+ let predicate = Predicate::AlwaysTrue;
+ let bound_predicate = predicate.bind(create_test_schema(),
false).unwrap();
+
+ let mut test_evaluator = TestEvaluator {};
+
+ let result = visit(&mut test_evaluator, &bound_predicate);
+
+ assert!(result.unwrap());
+ }
+
+ #[test]
+ fn test_always_false() {
+ let predicate = Predicate::AlwaysFalse;
+ let bound_predicate = predicate.bind(create_test_schema(),
false).unwrap();
+
+ let mut test_evaluator = TestEvaluator {};
+
+ let result = visit(&mut test_evaluator, &bound_predicate);
+
+ assert!(!result.unwrap());
+ }
+
+ #[test]
+ fn test_logical_and() {
+ let predicate = Predicate::AlwaysTrue.and(Predicate::AlwaysFalse);
+ let bound_predicate = predicate.bind(create_test_schema(),
false).unwrap();
+
+ let mut test_evaluator = TestEvaluator {};
+
+ let result = visit(&mut test_evaluator, &bound_predicate);
+
+ assert!(!result.unwrap());
+
+ let predicate = Predicate::AlwaysFalse.and(Predicate::AlwaysFalse);
+ let bound_predicate = predicate.bind(create_test_schema(),
false).unwrap();
+
+ let mut test_evaluator = TestEvaluator {};
+
+ let result = visit(&mut test_evaluator, &bound_predicate);
+
+ assert!(!result.unwrap());
+
+ let predicate = Predicate::AlwaysTrue.and(Predicate::AlwaysTrue);
+ let bound_predicate = predicate.bind(create_test_schema(),
false).unwrap();
+
+ let mut test_evaluator = TestEvaluator {};
+
+ let result = visit(&mut test_evaluator, &bound_predicate);
+
+ assert!(result.unwrap());
+ }
+
+ #[test]
+ fn test_logical_or() {
+ let predicate = Predicate::AlwaysTrue.or(Predicate::AlwaysFalse);
+ let bound_predicate = predicate.bind(create_test_schema(),
false).unwrap();
+
+ let mut test_evaluator = TestEvaluator {};
+
+ let result = visit(&mut test_evaluator, &bound_predicate);
+
+ assert!(result.unwrap());
+
+ let predicate = Predicate::AlwaysFalse.or(Predicate::AlwaysFalse);
+ let bound_predicate = predicate.bind(create_test_schema(),
false).unwrap();
+
+ let mut test_evaluator = TestEvaluator {};
+
+ let result = visit(&mut test_evaluator, &bound_predicate);
+
+ assert!(!result.unwrap());
+
+ let predicate = Predicate::AlwaysTrue.or(Predicate::AlwaysTrue);
+ let bound_predicate = predicate.bind(create_test_schema(),
false).unwrap();
+
+ let mut test_evaluator = TestEvaluator {};
+
+ let result = visit(&mut test_evaluator, &bound_predicate);
+
+ assert!(result.unwrap());
+ }
+
+ #[test]
+ fn test_not() {
+ let predicate = Predicate::AlwaysFalse.not();
+ let bound_predicate = predicate.bind(create_test_schema(),
false).unwrap();
+
+ let mut test_evaluator = TestEvaluator {};
+
+ let result = visit(&mut test_evaluator, &bound_predicate);
+
+ assert!(result.unwrap());
+
+ let predicate = Predicate::AlwaysTrue.not();
+ let bound_predicate = predicate.bind(create_test_schema(),
false).unwrap();
+
+ let mut test_evaluator = TestEvaluator {};
+
+ let result = visit(&mut test_evaluator, &bound_predicate);
+
+ assert!(!result.unwrap());
+ }
+
+ #[test]
+ fn test_is_null() {
+ let predicate = Predicate::Unary(UnaryExpression::new(
+ PredicateOperator::IsNull,
+ Reference::new("c"),
+ ));
+ let bound_predicate = predicate.bind(create_test_schema(),
false).unwrap();
+
+ let mut test_evaluator = TestEvaluator {};
+
+ let result = visit(&mut test_evaluator, &bound_predicate);
+
+ assert!(result.unwrap());
+ }
+
+ #[test]
+ fn test_not_null() {
+ let predicate = Predicate::Unary(UnaryExpression::new(
+ PredicateOperator::NotNull,
+ Reference::new("a"),
+ ));
+ let bound_predicate = predicate.bind(create_test_schema(),
false).unwrap();
+
+ let mut test_evaluator = TestEvaluator {};
+
+ let result = visit(&mut test_evaluator, &bound_predicate);
+
+ assert!(result.unwrap());
+ }
+
+ #[test]
+ fn test_is_nan() {
+ let predicate = Predicate::Unary(UnaryExpression::new(
+ PredicateOperator::IsNan,
+ Reference::new("b"),
+ ));
+ let bound_predicate = predicate.bind(create_test_schema(),
false).unwrap();
+
+ let mut test_evaluator = TestEvaluator {};
+
+ let result = visit(&mut test_evaluator, &bound_predicate);
+
+ assert!(result.unwrap());
+ }
+
+ #[test]
+ fn test_not_nan() {
+ let predicate = Predicate::Unary(UnaryExpression::new(
+ PredicateOperator::NotNan,
+ Reference::new("b"),
+ ));
+ let bound_predicate = predicate.bind(create_test_schema(),
false).unwrap();
+
+ let mut test_evaluator = TestEvaluator {};
+
+ let result = visit(&mut test_evaluator, &bound_predicate);
+
+ assert!(!result.unwrap());
+ }
+
+ #[test]
+ fn test_less_than() {
+ let predicate = Predicate::Binary(BinaryExpression::new(
+ PredicateOperator::LessThan,
+ Reference::new("a"),
+ Datum::int(10),
+ ));
+ let bound_predicate = predicate.bind(create_test_schema(),
false).unwrap();
+
+ let mut test_evaluator = TestEvaluator {};
+
+ let result = visit(&mut test_evaluator, &bound_predicate);
+
+ assert!(result.unwrap());
+ }
+
+ #[test]
+ fn test_less_than_or_eq() {
+ let predicate = Predicate::Binary(BinaryExpression::new(
+ PredicateOperator::LessThanOrEq,
+ Reference::new("a"),
+ Datum::int(10),
+ ));
+ let bound_predicate = predicate.bind(create_test_schema(),
false).unwrap();
+
+ let mut test_evaluator = TestEvaluator {};
+
+ let result = visit(&mut test_evaluator, &bound_predicate);
+
+ assert!(!result.unwrap());
+ }
+
+ #[test]
+ fn test_greater_than() {
+ let predicate = Predicate::Binary(BinaryExpression::new(
+ PredicateOperator::GreaterThan,
+ Reference::new("a"),
+ Datum::int(10),
+ ));
+ let bound_predicate = predicate.bind(create_test_schema(),
false).unwrap();
+
+ let mut test_evaluator = TestEvaluator {};
+
+ let result = visit(&mut test_evaluator, &bound_predicate);
+
+ assert!(result.unwrap());
+ }
+
+ #[test]
+ fn test_greater_than_or_eq() {
+ let predicate = Predicate::Binary(BinaryExpression::new(
+ PredicateOperator::GreaterThanOrEq,
+ Reference::new("a"),
+ Datum::int(10),
+ ));
+ let bound_predicate = predicate.bind(create_test_schema(),
false).unwrap();
+
+ let mut test_evaluator = TestEvaluator {};
+
+ let result = visit(&mut test_evaluator, &bound_predicate);
+
+ assert!(!result.unwrap());
+ }
+
+ #[test]
+ fn test_eq() {
+ let predicate = Predicate::Binary(BinaryExpression::new(
+ PredicateOperator::Eq,
+ Reference::new("a"),
+ Datum::int(10),
+ ));
+ let bound_predicate = predicate.bind(create_test_schema(),
false).unwrap();
+
+ let mut test_evaluator = TestEvaluator {};
+
+ let result = visit(&mut test_evaluator, &bound_predicate);
+
+ assert!(result.unwrap());
+ }
+
+ #[test]
+ fn test_not_eq() {
+ let predicate = Predicate::Binary(BinaryExpression::new(
+ PredicateOperator::NotEq,
+ Reference::new("a"),
+ Datum::int(10),
+ ));
+ let bound_predicate = predicate.bind(create_test_schema(),
false).unwrap();
+
+ let mut test_evaluator = TestEvaluator {};
+
+ let result = visit(&mut test_evaluator, &bound_predicate);
+
+ assert!(!result.unwrap());
+ }
+
+ #[test]
+ fn test_starts_with() {
+ let predicate = Predicate::Binary(BinaryExpression::new(
+ PredicateOperator::StartsWith,
+ Reference::new("a"),
+ Datum::int(10),
+ ));
+ let bound_predicate = predicate.bind(create_test_schema(),
false).unwrap();
+
+ let mut test_evaluator = TestEvaluator {};
+
+ let result = visit(&mut test_evaluator, &bound_predicate);
+
+ assert!(result.unwrap());
+ }
+
+ #[test]
+ fn test_not_starts_with() {
+ let predicate = Predicate::Binary(BinaryExpression::new(
+ PredicateOperator::NotStartsWith,
+ Reference::new("a"),
+ Datum::int(10),
+ ));
+ let bound_predicate = predicate.bind(create_test_schema(),
false).unwrap();
+
+ let mut test_evaluator = TestEvaluator {};
+
+ let result = visit(&mut test_evaluator, &bound_predicate);
+
+ assert!(!result.unwrap());
+ }
+
+ #[test]
+ fn test_in() {
+ let predicate = Predicate::Set(SetExpression::new(
+ PredicateOperator::In,
+ Reference::new("a"),
+ FnvHashSet::from_iter(vec![Datum::int(1)]),
+ ));
+ let bound_predicate = predicate.bind(create_test_schema(),
false).unwrap();
+
+ let mut test_evaluator = TestEvaluator {};
+
+ let result = visit(&mut test_evaluator, &bound_predicate);
+
+ assert!(result.unwrap());
+ }
+
+ #[test]
+ fn test_not_in() {
+ let predicate = Predicate::Set(SetExpression::new(
+ PredicateOperator::NotIn,
+ Reference::new("a"),
+ FnvHashSet::from_iter(vec![Datum::int(1)]),
+ ));
+ let bound_predicate = predicate.bind(create_test_schema(),
false).unwrap();
+
+ let mut test_evaluator = TestEvaluator {};
+
+ let result = visit(&mut test_evaluator, &bound_predicate);
+
+ assert!(!result.unwrap());
+ }
+}
diff --git a/crates/iceberg/src/expr/visitors/mod.rs
b/crates/iceberg/src/expr/visitors/mod.rs
new file mode 100644
index 0000000..242a55c
--- /dev/null
+++ b/crates/iceberg/src/expr/visitors/mod.rs
@@ -0,0 +1,18 @@
+// 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.
+
+pub(crate) mod bound_predicate_visitor;