Skip to content

Commit 09b6ea3

Browse files
committed
Add tests
1 parent 298946e commit 09b6ea3

File tree

4 files changed

+283
-74
lines changed

4 files changed

+283
-74
lines changed

crates/iceberg/src/expr/predicate.rs

Lines changed: 253 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,17 @@
1919
//! Predicate expressions are used to filter data, and evaluates to a boolean value. For example,
2020
//! `a > 10` is a predicate expression, and it evaluates to `true` if `a` is greater than `10`,
2121
22-
use crate::expr::{BoundReference, PredicateOperator, Reference};
23-
use crate::spec::Datum;
22+
use std::fmt::{Debug, Display, Formatter};
23+
use std::mem::MaybeUninit;
24+
use std::ops::Not;
25+
26+
use fnv::FnvHashSet;
2427
use itertools::Itertools;
25-
use std::collections::HashSet;
28+
2629
use crate::error::Result;
2730
use crate::expr::{Bind, BoundReference, PredicateOperator, Reference};
2831
use crate::spec::{Datum, SchemaRef};
2932
use crate::{Error, ErrorKind};
30-
use fnv::FnvHashSet;
31-
32-
use std::fmt::{Debug, Display, Formatter};
33-
use std::mem::MaybeUninit;
34-
use std::ops::Not;
3533

3634
/// Logical expression, such as `AND`, `OR`, `NOT`.
3735
#[derive(PartialEq)]
@@ -490,7 +488,11 @@ impl Predicate {
490488
impl Not for Predicate {
491489
type Output = Predicate;
492490

493-
/// Create a predicate which is the reverse of this predicate. For example: `NOT (a > 10)`
491+
/// Create a predicate which is the reverse of this predicate. For example: `NOT (a > 10)`.
492+
///
493+
/// This is different from [`Predicate::negate()`] since it doesn't rewrite expression, but
494+
/// just adds a `NOT` operator.
495+
///
494496
/// # Example
495497
///
496498
///```rust
@@ -530,12 +532,46 @@ pub enum BoundPredicate {
530532
Set(SetExpression<BoundReference>),
531533
}
532534

535+
impl Display for BoundPredicate {
536+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
537+
match self {
538+
BoundPredicate::AlwaysTrue => {
539+
write!(f, "True")
540+
}
541+
BoundPredicate::AlwaysFalse => {
542+
write!(f, "False")
543+
}
544+
BoundPredicate::And(expr) => {
545+
write!(f, "({}) AND ({})", expr.inputs()[0], expr.inputs()[1])
546+
}
547+
BoundPredicate::Or(expr) => {
548+
write!(f, "({}) OR ({})", expr.inputs()[0], expr.inputs()[1])
549+
}
550+
BoundPredicate::Not(expr) => {
551+
write!(f, "NOT ({})", expr.inputs()[0])
552+
}
553+
BoundPredicate::Unary(expr) => {
554+
write!(f, "{}", expr)
555+
}
556+
BoundPredicate::Binary(expr) => {
557+
write!(f, "{}", expr)
558+
}
559+
BoundPredicate::Set(expr) => {
560+
write!(f, "{}", expr)
561+
}
562+
}
563+
}
564+
}
565+
533566
#[cfg(test)]
534567
mod tests {
568+
use std::ops::Not;
569+
use std::sync::Arc;
570+
571+
use crate::expr::Bind;
535572
use crate::expr::Reference;
536573
use crate::spec::Datum;
537-
use std::collections::HashSet;
538-
use std::ops::Not;
574+
use crate::spec::{NestedField, PrimitiveType, Schema, SchemaRef, Type};
539575

540576
#[test]
541577
fn test_predicate_negate_and() {
@@ -604,20 +640,15 @@ mod tests {
604640

605641
#[test]
606642
fn test_predicate_negate_set() {
607-
let expression = Reference::new("a").is_in(HashSet::from([Datum::long(5), Datum::long(6)]));
643+
let expression = Reference::new("a").is_in([Datum::long(5), Datum::long(6)]);
608644

609-
let expected =
610-
Reference::new("a").is_not_in(HashSet::from([Datum::long(5), Datum::long(6)]));
645+
let expected = Reference::new("a").is_not_in([Datum::long(5), Datum::long(6)]);
611646

612647
let result = expression.negate();
613648

614649
assert_eq!(result, expected);
615650
}
616651

617-
use crate::expr::{Bind, Reference};
618-
use crate::spec::{NestedField, PrimitiveType, Schema, SchemaRef, Type};
619-
use std::sync::Arc;
620-
621652
fn table_schema_simple() -> SchemaRef {
622653
Arc::new(
623654
Schema::builder()
@@ -640,4 +671,208 @@ mod tests {
640671
let bound_expr = expr.bind(schema, true).unwrap();
641672
assert_eq!(&format!("{bound_expr}"), "foo IS NULL");
642673
}
674+
675+
#[test]
676+
fn test_bind_is_null_required() {
677+
let schema = table_schema_simple();
678+
let expr = Reference::new("bar").is_null();
679+
let bound_expr = expr.bind(schema, true).unwrap();
680+
assert_eq!(&format!("{bound_expr}"), "False");
681+
}
682+
683+
#[test]
684+
fn test_bind_is_not_null() {
685+
let schema = table_schema_simple();
686+
let expr = Reference::new("foo").is_not_null();
687+
let bound_expr = expr.bind(schema, true).unwrap();
688+
assert_eq!(&format!("{bound_expr}"), "foo IS NOT NULL");
689+
}
690+
691+
#[test]
692+
fn test_bind_is_not_null_required() {
693+
let schema = table_schema_simple();
694+
let expr = Reference::new("bar").is_not_null();
695+
let bound_expr = expr.bind(schema, true).unwrap();
696+
assert_eq!(&format!("{bound_expr}"), "True");
697+
}
698+
699+
#[test]
700+
fn test_bind_less_than() {
701+
let schema = table_schema_simple();
702+
let expr = Reference::new("bar").less_than(Datum::int(10));
703+
let bound_expr = expr.bind(schema, true).unwrap();
704+
assert_eq!(&format!("{bound_expr}"), "bar < 10");
705+
}
706+
707+
#[test]
708+
fn test_bind_less_than_wrong_type() {
709+
let schema = table_schema_simple();
710+
let expr = Reference::new("bar").less_than(Datum::string("abcd"));
711+
let bound_expr = expr.bind(schema, true);
712+
assert!(bound_expr.is_err());
713+
}
714+
715+
#[test]
716+
fn test_bind_greater_than_or_eq() {
717+
let schema = table_schema_simple();
718+
let expr = Reference::new("bar").greater_than_or_equal_to(Datum::int(10));
719+
let bound_expr = expr.bind(schema, true).unwrap();
720+
assert_eq!(&format!("{bound_expr}"), "bar >= 10");
721+
}
722+
723+
#[test]
724+
fn test_bind_greater_than_or_eq_wrong_type() {
725+
let schema = table_schema_simple();
726+
let expr = Reference::new("bar").greater_than_or_equal_to(Datum::string("abcd"));
727+
let bound_expr = expr.bind(schema, true);
728+
assert!(bound_expr.is_err());
729+
}
730+
731+
#[test]
732+
fn test_bind_in() {
733+
let schema = table_schema_simple();
734+
let expr = Reference::new("bar").is_in([Datum::int(10), Datum::int(20)]);
735+
let bound_expr = expr.bind(schema, true).unwrap();
736+
assert_eq!(&format!("{bound_expr}"), "bar IN (20, 10)");
737+
}
738+
739+
#[test]
740+
fn test_bind_in_empty() {
741+
let schema = table_schema_simple();
742+
let expr = Reference::new("bar").is_in(vec![]);
743+
let bound_expr = expr.bind(schema, true).unwrap();
744+
assert_eq!(&format!("{bound_expr}"), "False");
745+
}
746+
747+
#[test]
748+
fn test_bind_in_one_literal() {
749+
let schema = table_schema_simple();
750+
let expr = Reference::new("bar").is_in(vec![Datum::int(10)]);
751+
let bound_expr = expr.bind(schema, true).unwrap();
752+
assert_eq!(&format!("{bound_expr}"), "bar = 10");
753+
}
754+
755+
#[test]
756+
fn test_bind_in_wrong_type() {
757+
let schema = table_schema_simple();
758+
let expr = Reference::new("bar").is_in(vec![Datum::int(10), Datum::string("abcd")]);
759+
let bound_expr = expr.bind(schema, true);
760+
assert!(bound_expr.is_err());
761+
}
762+
763+
#[test]
764+
fn test_bind_not_in() {
765+
let schema = table_schema_simple();
766+
let expr = Reference::new("bar").is_not_in([Datum::int(10), Datum::int(20)]);
767+
let bound_expr = expr.bind(schema, true).unwrap();
768+
assert_eq!(&format!("{bound_expr}"), "bar NOT IN (20, 10)");
769+
}
770+
771+
#[test]
772+
fn test_bind_not_in_empty() {
773+
let schema = table_schema_simple();
774+
let expr = Reference::new("bar").is_not_in(vec![]);
775+
let bound_expr = expr.bind(schema, true).unwrap();
776+
assert_eq!(&format!("{bound_expr}"), "True");
777+
}
778+
779+
#[test]
780+
fn test_bind_not_in_one_literal() {
781+
let schema = table_schema_simple();
782+
let expr = Reference::new("bar").is_not_in(vec![Datum::int(10)]);
783+
let bound_expr = expr.bind(schema, true).unwrap();
784+
assert_eq!(&format!("{bound_expr}"), "bar != 10");
785+
}
786+
787+
#[test]
788+
fn test_bind_not_in_wrong_type() {
789+
let schema = table_schema_simple();
790+
let expr = Reference::new("bar").is_not_in([Datum::int(10), Datum::string("abcd")]);
791+
let bound_expr = expr.bind(schema, true);
792+
assert!(bound_expr.is_err());
793+
}
794+
795+
#[test]
796+
fn test_bind_and() {
797+
let schema = table_schema_simple();
798+
let expr = Reference::new("bar")
799+
.less_than(Datum::int(10))
800+
.and(Reference::new("foo").is_null());
801+
let bound_expr = expr.bind(schema, true).unwrap();
802+
assert_eq!(&format!("{bound_expr}"), "(bar < 10) AND (foo IS NULL)");
803+
}
804+
805+
#[test]
806+
fn test_bind_and_always_false() {
807+
let schema = table_schema_simple();
808+
let expr = Reference::new("foo")
809+
.less_than(Datum::string("abcd"))
810+
.and(Reference::new("bar").is_null());
811+
let bound_expr = expr.bind(schema, true).unwrap();
812+
assert_eq!(&format!("{bound_expr}"), "False");
813+
}
814+
815+
#[test]
816+
fn test_bind_and_always_true() {
817+
let schema = table_schema_simple();
818+
let expr = Reference::new("foo")
819+
.less_than(Datum::string("abcd"))
820+
.and(Reference::new("bar").is_not_null());
821+
let bound_expr = expr.bind(schema, true).unwrap();
822+
assert_eq!(&format!("{bound_expr}"), r#"foo < "abcd""#);
823+
}
824+
825+
#[test]
826+
fn test_bind_or() {
827+
let schema = table_schema_simple();
828+
let expr = Reference::new("bar")
829+
.less_than(Datum::int(10))
830+
.or(Reference::new("foo").is_null());
831+
let bound_expr = expr.bind(schema, true).unwrap();
832+
assert_eq!(&format!("{bound_expr}"), "(bar < 10) OR (foo IS NULL)");
833+
}
834+
835+
#[test]
836+
fn test_bind_or_always_true() {
837+
let schema = table_schema_simple();
838+
let expr = Reference::new("foo")
839+
.less_than(Datum::string("abcd"))
840+
.or(Reference::new("bar").is_not_null());
841+
let bound_expr = expr.bind(schema, true).unwrap();
842+
assert_eq!(&format!("{bound_expr}"), "True");
843+
}
844+
845+
#[test]
846+
fn test_bind_or_always_false() {
847+
let schema = table_schema_simple();
848+
let expr = Reference::new("foo")
849+
.less_than(Datum::string("abcd"))
850+
.or(Reference::new("bar").is_null());
851+
let bound_expr = expr.bind(schema, true).unwrap();
852+
assert_eq!(&format!("{bound_expr}"), r#"foo < "abcd""#);
853+
}
854+
855+
#[test]
856+
fn test_bind_not() {
857+
let schema = table_schema_simple();
858+
let expr = !Reference::new("bar").less_than(Datum::int(10));
859+
let bound_expr = expr.bind(schema, true).unwrap();
860+
assert_eq!(&format!("{bound_expr}"), "NOT (bar < 10)");
861+
}
862+
863+
#[test]
864+
fn test_bind_not_always_true() {
865+
let schema = table_schema_simple();
866+
let expr = !Reference::new("bar").is_not_null();
867+
let bound_expr = expr.bind(schema, true).unwrap();
868+
assert_eq!(&format!("{bound_expr}"), "False");
869+
}
870+
871+
#[test]
872+
fn test_bind_not_always_false() {
873+
let schema = table_schema_simple();
874+
let expr = !Reference::new("bar").is_null();
875+
let bound_expr = expr.bind(schema, true).unwrap();
876+
assert_eq!(&format!("{bound_expr}"), r#"True"#);
877+
}
643878
}

0 commit comments

Comments
 (0)