| /* |
| * 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. |
| */ |
| |
| #include "iceberg/expression/predicate.h" |
| |
| #include <limits> |
| #include <memory> |
| |
| #include "iceberg/expression/expressions.h" |
| #include "iceberg/schema.h" |
| #include "iceberg/test/matchers.h" |
| #include "iceberg/type.h" |
| |
| namespace iceberg { |
| |
| class PredicateTest : public ::testing::Test { |
| protected: |
| void SetUp() override { |
| // Create a simple test schema with various field types |
| schema_ = std::make_shared<Schema>( |
| std::vector<SchemaField>{SchemaField::MakeRequired(1, "id", int64()), |
| SchemaField::MakeOptional(2, "name", string()), |
| SchemaField::MakeRequired(3, "age", int32()), |
| SchemaField::MakeOptional(4, "salary", float64()), |
| SchemaField::MakeRequired(5, "active", boolean()), |
| SchemaField::MakeRequired(6, "age2", int32())}, |
| /*schema_id=*/0); |
| } |
| |
| std::shared_ptr<Schema> schema_; |
| }; |
| |
| TEST_F(PredicateTest, LogicalOperationsAndOr) { |
| auto true_expr = Expressions::AlwaysTrue(); |
| auto false_expr = Expressions::AlwaysFalse(); |
| auto pred1 = Expressions::Equal("age", Literal::Int(25)); |
| auto pred2 = Expressions::Equal("name", Literal::String("test")); |
| |
| // Test AND operations |
| auto and_true_true = Expressions::And(true_expr, true_expr); |
| EXPECT_EQ(and_true_true->op(), Expression::Operation::kTrue); |
| |
| auto and_true_pred = Expressions::And(true_expr, pred1); |
| EXPECT_EQ(and_true_pred->op(), Expression::Operation::kEq); |
| |
| auto and_pred_true = Expressions::And(pred1, true_expr); |
| EXPECT_EQ(and_pred_true->op(), Expression::Operation::kEq); |
| |
| auto and_false_pred = Expressions::And(false_expr, pred1); |
| EXPECT_EQ(and_false_pred->op(), Expression::Operation::kFalse); |
| |
| auto and_pred_false = Expressions::And(pred1, false_expr); |
| EXPECT_EQ(and_pred_false->op(), Expression::Operation::kFalse); |
| |
| // Test OR operations |
| auto or_false_false = Expressions::Or(false_expr, false_expr); |
| EXPECT_EQ(or_false_false->op(), Expression::Operation::kFalse); |
| |
| auto or_false_pred = Expressions::Or(false_expr, pred1); |
| EXPECT_EQ(or_false_pred->op(), Expression::Operation::kEq); |
| |
| auto or_pred_false = Expressions::Or(pred1, false_expr); |
| EXPECT_EQ(or_pred_false->op(), Expression::Operation::kEq); |
| |
| auto or_true_pred = Expressions::Or(true_expr, pred1); |
| EXPECT_EQ(or_true_pred->op(), Expression::Operation::kTrue); |
| |
| auto or_pred_true = Expressions::Or(pred1, true_expr); |
| EXPECT_EQ(or_pred_true->op(), Expression::Operation::kTrue); |
| } |
| |
| TEST_F(PredicateTest, ConstantExpressions) { |
| auto always_true = Expressions::AlwaysTrue(); |
| auto always_false = Expressions::AlwaysFalse(); |
| |
| EXPECT_EQ(always_true->op(), Expression::Operation::kTrue); |
| EXPECT_EQ(always_false->op(), Expression::Operation::kFalse); |
| } |
| |
| TEST_F(PredicateTest, UnaryPredicateFactory) { |
| auto is_null_name = Expressions::IsNull("name"); |
| EXPECT_EQ(is_null_name->op(), Expression::Operation::kIsNull); |
| EXPECT_EQ(is_null_name->reference()->name(), "name"); |
| |
| auto not_null_name = Expressions::NotNull("active"); |
| EXPECT_EQ(not_null_name->op(), Expression::Operation::kNotNull); |
| EXPECT_EQ(not_null_name->reference()->name(), "active"); |
| |
| auto is_nan_name = Expressions::IsNaN("salary"); |
| EXPECT_EQ(is_nan_name->op(), Expression::Operation::kIsNan); |
| EXPECT_EQ(is_nan_name->reference()->name(), "salary"); |
| |
| auto not_nan_name = Expressions::NotNaN("salary"); |
| EXPECT_EQ(not_nan_name->op(), Expression::Operation::kNotNan); |
| EXPECT_EQ(not_nan_name->reference()->name(), "salary"); |
| } |
| |
| TEST_F(PredicateTest, ComparisonPredicateFactory) { |
| auto lt_name = Expressions::LessThan("age", Literal::Int(30)); |
| EXPECT_EQ(lt_name->op(), Expression::Operation::kLt); |
| EXPECT_EQ(lt_name->reference()->name(), "age"); |
| |
| auto lte_name = Expressions::LessThanOrEqual("salary", Literal::Double(50000.0)); |
| EXPECT_EQ(lte_name->op(), Expression::Operation::kLtEq); |
| EXPECT_EQ(lte_name->reference()->name(), "salary"); |
| |
| auto gt_name = Expressions::GreaterThan("id", Literal::Long(1000)); |
| EXPECT_EQ(gt_name->op(), Expression::Operation::kGt); |
| EXPECT_EQ(gt_name->reference()->name(), "id"); |
| |
| auto gte_name = Expressions::GreaterThanOrEqual("age", Literal::Int(18)); |
| EXPECT_EQ(gte_name->op(), Expression::Operation::kGtEq); |
| EXPECT_EQ(gte_name->reference()->name(), "age"); |
| |
| auto eq_name = Expressions::Equal("name", Literal::String("test")); |
| EXPECT_EQ(eq_name->op(), Expression::Operation::kEq); |
| EXPECT_EQ(eq_name->reference()->name(), "name"); |
| |
| auto neq_name = Expressions::NotEqual("active", Literal::Boolean(false)); |
| EXPECT_EQ(neq_name->op(), Expression::Operation::kNotEq); |
| EXPECT_EQ(neq_name->reference()->name(), "active"); |
| } |
| |
| TEST_F(PredicateTest, StringPredicateFactory) { |
| auto starts_name = Expressions::StartsWith("name", "John"); |
| EXPECT_EQ(starts_name->op(), Expression::Operation::kStartsWith); |
| EXPECT_EQ(starts_name->reference()->name(), "name"); |
| |
| auto not_starts_name = Expressions::NotStartsWith("name", "Jane"); |
| EXPECT_EQ(not_starts_name->op(), Expression::Operation::kNotStartsWith); |
| EXPECT_EQ(not_starts_name->reference()->name(), "name"); |
| } |
| |
| TEST_F(PredicateTest, SetPredicateFactory) { |
| std::vector<Literal> values = {Literal::Int(10), Literal::Int(20), Literal::Int(30)}; |
| std::initializer_list<Literal> init_values = {Literal::String("a"), |
| Literal::String("b")}; |
| |
| auto in_name_vec = Expressions::In("age", values); |
| EXPECT_EQ(in_name_vec->op(), Expression::Operation::kIn); |
| EXPECT_EQ(in_name_vec->reference()->name(), "age"); |
| |
| auto in_name_init = Expressions::In("name", init_values); |
| EXPECT_EQ(in_name_init->op(), Expression::Operation::kIn); |
| EXPECT_EQ(in_name_init->reference()->name(), "name"); |
| |
| auto not_in_name_vec = Expressions::NotIn("age", values); |
| EXPECT_EQ(not_in_name_vec->op(), Expression::Operation::kNotIn); |
| EXPECT_EQ(not_in_name_vec->reference()->name(), "age"); |
| |
| auto not_in_name_init = Expressions::NotIn("name", init_values); |
| EXPECT_EQ(not_in_name_init->op(), Expression::Operation::kNotIn); |
| EXPECT_EQ(not_in_name_init->reference()->name(), "name"); |
| } |
| |
| TEST_F(PredicateTest, GenericPredicateFactory) { |
| auto pred_single = |
| Expressions::Predicate(Expression::Operation::kEq, "age", Literal::Int(25)); |
| EXPECT_EQ(pred_single->op(), Expression::Operation::kEq); |
| EXPECT_EQ(pred_single->reference()->name(), "age"); |
| |
| std::vector<Literal> values = {Literal::Int(10), Literal::Int(20)}; |
| auto pred_multi = Expressions::Predicate(Expression::Operation::kIn, "age", values); |
| EXPECT_EQ(pred_multi->op(), Expression::Operation::kIn); |
| EXPECT_EQ(pred_multi->reference()->name(), "age"); |
| |
| auto pred_unary = Expressions::Predicate(Expression::Operation::kIsNull, "name"); |
| EXPECT_EQ(pred_unary->op(), Expression::Operation::kIsNull); |
| EXPECT_EQ(pred_unary->reference()->name(), "name"); |
| } |
| |
| TEST_F(PredicateTest, TransformFactory) { |
| auto bucket_transform = Expressions::Bucket("id", 10); |
| EXPECT_NE(bucket_transform, nullptr); |
| EXPECT_EQ(bucket_transform->reference()->name(), "id"); |
| |
| auto year_transform = Expressions::Year("timestamp_field"); |
| EXPECT_NE(year_transform, nullptr); |
| EXPECT_EQ(year_transform->reference()->name(), "timestamp_field"); |
| |
| auto month_transform = Expressions::Month("timestamp_field"); |
| EXPECT_NE(month_transform, nullptr); |
| EXPECT_EQ(month_transform->reference()->name(), "timestamp_field"); |
| |
| auto day_transform = Expressions::Day("timestamp_field"); |
| EXPECT_NE(day_transform, nullptr); |
| EXPECT_EQ(day_transform->reference()->name(), "timestamp_field"); |
| |
| auto hour_transform = Expressions::Hour("timestamp_field"); |
| EXPECT_NE(hour_transform, nullptr); |
| EXPECT_EQ(hour_transform->reference()->name(), "timestamp_field"); |
| |
| auto truncate_transform = Expressions::Truncate("string_field", 5); |
| EXPECT_NE(truncate_transform, nullptr); |
| EXPECT_EQ(truncate_transform->reference()->name(), "string_field"); |
| } |
| |
| TEST_F(PredicateTest, ReferenceFactory) { |
| auto ref = Expressions::Ref("test_field"); |
| EXPECT_EQ(ref->name(), "test_field"); |
| EXPECT_EQ(ref->ToString(), "ref(name=\"test_field\")"); |
| } |
| |
| TEST_F(PredicateTest, NamedReferenceBasics) { |
| auto ref = Expressions::Ref("id"); |
| EXPECT_EQ(ref->name(), "id"); |
| EXPECT_EQ(ref->ToString(), "ref(name=\"id\")"); |
| EXPECT_EQ(ref->reference(), ref); |
| } |
| |
| TEST_F(PredicateTest, NamedReferenceBind) { |
| auto ref = Expressions::Ref("id"); |
| auto bound_result = ref->Bind(*schema_, /*case_sensitive=*/true); |
| ASSERT_THAT(bound_result, IsOk()); |
| |
| auto bound_ref = bound_result.value(); |
| EXPECT_EQ(bound_ref->name(), "id"); |
| EXPECT_EQ(bound_ref->field().field_id(), 1); |
| EXPECT_EQ(bound_ref->type()->type_id(), TypeId::kLong); |
| EXPECT_FALSE(bound_ref->MayProduceNull()); |
| } |
| |
| TEST_F(PredicateTest, NamedReferenceBindNonExistentField) { |
| auto ref = Expressions::Ref("non_existent_field"); |
| auto bound_result = ref->Bind(*schema_, /*case_sensitive=*/true); |
| EXPECT_THAT(bound_result, IsError(ErrorKind::kInvalidExpression)); |
| } |
| |
| TEST_F(PredicateTest, BoundReferenceEquality) { |
| auto ref1 = Expressions::Ref("id"); |
| auto ref2 = Expressions::Ref("id"); |
| auto ref3 = Expressions::Ref("name"); |
| |
| auto bound1 = ref1->Bind(*schema_, true).value(); |
| auto bound2 = ref2->Bind(*schema_, true).value(); |
| auto bound3 = ref3->Bind(*schema_, true).value(); |
| |
| // Same field should be equal |
| EXPECT_TRUE(bound1->Equals(*bound2)); |
| EXPECT_TRUE(bound2->Equals(*bound1)); |
| |
| // Different fields should not be equal |
| EXPECT_FALSE(bound1->Equals(*bound3)); |
| EXPECT_FALSE(bound3->Equals(*bound1)); |
| } |
| |
| TEST_F(PredicateTest, UnboundPredicateCreation) { |
| auto is_null_pred = Expressions::IsNull("name"); |
| EXPECT_EQ(is_null_pred->op(), Expression::Operation::kIsNull); |
| EXPECT_EQ(is_null_pred->reference()->name(), "name"); |
| |
| auto not_null_pred = Expressions::NotNull("name"); |
| EXPECT_EQ(not_null_pred->op(), Expression::Operation::kNotNull); |
| |
| auto equal_pred = Expressions::Equal("age", Literal::Int(25)); |
| EXPECT_EQ(equal_pred->op(), Expression::Operation::kEq); |
| |
| auto greater_than_pred = Expressions::GreaterThan("salary", Literal::Double(50000.0)); |
| EXPECT_EQ(greater_than_pred->op(), Expression::Operation::kGt); |
| } |
| |
| TEST_F(PredicateTest, UnboundPredicateToString) { |
| auto equal_pred = Expressions::Equal("age", Literal::Int(25)); |
| EXPECT_EQ(equal_pred->ToString(), "ref(name=\"age\") == 25"); |
| |
| auto is_null_pred = Expressions::IsNull("name"); |
| EXPECT_EQ(is_null_pred->ToString(), "is_null(ref(name=\"name\"))"); |
| |
| auto in_pred = Expressions::In("age", {Literal::Int(10), Literal::Int(20)}); |
| EXPECT_EQ(in_pred->ToString(), "ref(name=\"age\") in [10, 20]"); |
| |
| auto starts_with_pred = Expressions::StartsWith("name", "John"); |
| EXPECT_EQ(starts_with_pred->ToString(), "ref(name=\"name\") startsWith \"John\""); |
| } |
| |
| TEST_F(PredicateTest, UnboundPredicateEquality) { |
| auto pred = Expressions::GreaterThan("age", Literal::Int(25)); |
| auto same = Expressions::GreaterThan("age", Literal::Int(25)); |
| auto different_op = Expressions::GreaterThanOrEqual("age", Literal::Int(25)); |
| auto different_ref = Expressions::GreaterThan("id", Literal::Int(25)); |
| auto different_literal = Expressions::GreaterThan("age", Literal::Int(26)); |
| |
| EXPECT_TRUE(pred->Equals(*same)); |
| EXPECT_TRUE(same->Equals(*pred)); |
| EXPECT_FALSE(pred->Equals(*different_op)); |
| EXPECT_FALSE(pred->Equals(*different_ref)); |
| EXPECT_FALSE(pred->Equals(*different_literal)); |
| |
| auto bucket = [](std::string name, |
| int32_t num_buckets) -> std::shared_ptr<UnboundTerm<BoundTransform>> { |
| return Expressions::Bucket(std::move(name), num_buckets); |
| }; |
| |
| auto transformed = Expressions::Equal(bucket("id", 16), Literal::Int(3)); |
| auto same_transformed = Expressions::Equal(bucket("id", 16), Literal::Int(3)); |
| auto different_transform = Expressions::Equal(bucket("id", 32), Literal::Int(3)); |
| auto different_transform_ref = Expressions::Equal(bucket("age", 16), Literal::Int(3)); |
| |
| EXPECT_TRUE(transformed->Equals(*same_transformed)); |
| EXPECT_FALSE(transformed->Equals(*different_transform)); |
| EXPECT_FALSE(transformed->Equals(*different_transform_ref)); |
| } |
| |
| TEST_F(PredicateTest, UnboundPredicateNegate) { |
| auto equal_pred = Expressions::Equal("age", Literal::Int(25)); |
| auto negated_result = equal_pred->Negate(); |
| ASSERT_THAT(negated_result, IsOk()); |
| |
| auto negated_pred = negated_result.value(); |
| EXPECT_EQ(negated_pred->op(), Expression::Operation::kNotEq); |
| |
| auto is_null_pred = Expressions::IsNull("name"); |
| auto negated_null_result = is_null_pred->Negate(); |
| ASSERT_THAT(negated_null_result, IsOk()); |
| |
| auto negated_null_pred = negated_null_result.value(); |
| EXPECT_EQ(negated_null_pred->op(), Expression::Operation::kNotNull); |
| |
| auto in_pred = Expressions::In("age", {Literal::Int(10), Literal::Int(20)}); |
| auto negated_in_result = in_pred->Negate(); |
| ASSERT_THAT(negated_in_result, IsOk()); |
| |
| auto negated_in_pred = negated_in_result.value(); |
| EXPECT_EQ(negated_in_pred->op(), Expression::Operation::kNotIn); |
| } |
| |
| TEST_F(PredicateTest, UnboundPredicateBindUnary) { |
| auto is_null_pred = Expressions::IsNull("name"); |
| auto bound_result = is_null_pred->Bind(*schema_, /*case_sensitive=*/true); |
| ASSERT_THAT(bound_result, IsOk()); |
| |
| auto bound_pred = bound_result.value(); |
| EXPECT_EQ(bound_pred->op(), Expression::Operation::kIsNull); |
| |
| // Test NOT NULL on non-nullable field - should return AlwaysTrue |
| auto not_null_required = Expressions::NotNull("age"); // age is required |
| auto bound_not_null_result = not_null_required->Bind(*schema_, /*case_sensitive=*/true); |
| ASSERT_THAT(bound_not_null_result, IsOk()); |
| |
| auto bound_not_null = bound_not_null_result.value(); |
| EXPECT_EQ(bound_not_null->op(), Expression::Operation::kTrue); |
| |
| // Test IS NULL on non-nullable field - should return AlwaysFalse |
| auto is_null_required = Expressions::IsNull("age"); // age is required |
| auto bound_is_null_result = is_null_required->Bind(*schema_, /*case_sensitive=*/true); |
| ASSERT_THAT(bound_is_null_result, IsOk()); |
| |
| auto bound_is_null = bound_is_null_result.value(); |
| EXPECT_EQ(bound_is_null->op(), Expression::Operation::kFalse); |
| } |
| |
| TEST_F(PredicateTest, UnboundPredicateBindLiteral) { |
| auto equal_pred = Expressions::Equal("age", Literal::Int(25)); |
| auto bound_result = equal_pred->Bind(*schema_, /*case_sensitive=*/true); |
| ASSERT_THAT(bound_result, IsOk()); |
| |
| auto bound_pred = bound_result.value(); |
| EXPECT_EQ(bound_pred->op(), Expression::Operation::kEq); |
| |
| // Test binding with type conversion |
| auto equal_long_pred = |
| Expressions::Equal("id", Literal::Int(123)); // int to long conversion |
| auto bound_long_result = equal_long_pred->Bind(*schema_, /*case_sensitive=*/true); |
| ASSERT_THAT(bound_long_result, IsOk()); |
| |
| auto bound_long_pred = bound_long_result.value(); |
| EXPECT_EQ(bound_long_pred->op(), Expression::Operation::kEq); |
| } |
| |
| TEST_F(PredicateTest, UnboundPredicateBindIn) { |
| // Test IN operation with single value (should become equality) |
| auto in_single = Expressions::In("age", {Literal::Int(25)}); |
| auto bound_single_result = in_single->Bind(*schema_, /*case_sensitive=*/true); |
| ASSERT_THAT(bound_single_result, IsOk()); |
| |
| auto bound_single = bound_single_result.value(); |
| EXPECT_EQ(bound_single->op(), Expression::Operation::kEq); |
| |
| // Test NOT IN operation with single value (should become inequality) |
| auto not_in_single = Expressions::NotIn("age", {Literal::Int(25)}); |
| auto bound_not_single_result = not_in_single->Bind(*schema_, /*case_sensitive=*/true); |
| ASSERT_THAT(bound_not_single_result, IsOk()); |
| |
| auto bound_not_single = bound_not_single_result.value(); |
| EXPECT_EQ(bound_not_single->op(), Expression::Operation::kNotEq); |
| |
| // Test IN operation with multiple values (should stay as IN) |
| auto in_multi = Expressions::In("age", {Literal::Int(25), Literal::Int(30)}); |
| auto bound_multi_result = in_multi->Bind(*schema_, true); |
| ASSERT_THAT(bound_multi_result, IsOk()); |
| |
| auto bound_multi = bound_multi_result.value(); |
| EXPECT_EQ(bound_multi->op(), Expression::Operation::kIn); |
| } |
| |
| TEST_F(PredicateTest, FloatingPointNaNPredicates) { |
| auto is_nan_float = Expressions::IsNaN("salary"); // salary is float64 |
| auto bound_nan_result = is_nan_float->Bind(*schema_, /*case_sensitive=*/true); |
| ASSERT_THAT(bound_nan_result, IsOk()); |
| |
| auto bound_nan = bound_nan_result.value(); |
| EXPECT_EQ(bound_nan->op(), Expression::Operation::kIsNan); |
| |
| auto is_nan_int = Expressions::IsNaN("age"); // age is int32 |
| auto bound_nan_int_result = is_nan_int->Bind(*schema_, /*case_sensitive=*/true); |
| EXPECT_THAT(bound_nan_int_result, IsError(ErrorKind::kInvalidExpression)); |
| } |
| |
| TEST_F(PredicateTest, StringStartsWithPredicates) { |
| auto starts_with = Expressions::StartsWith("name", "John"); // name is string |
| auto bound_starts_result = starts_with->Bind(*schema_, /*case_sensitive=*/true); |
| ASSERT_THAT(bound_starts_result, IsOk()); |
| |
| auto bound_starts = bound_starts_result.value(); |
| EXPECT_EQ(bound_starts->op(), Expression::Operation::kStartsWith); |
| |
| auto starts_with_int = Expressions::StartsWith("age", "test"); // age is int32 |
| auto bound_starts_int_result = starts_with_int->Bind(*schema_, /*case_sensitive=*/true); |
| EXPECT_THAT(bound_starts_int_result, IsError(ErrorKind::kInvalidExpression)); |
| } |
| |
| TEST_F(PredicateTest, LiteralConversionEdgeCases) { |
| auto large_value_lt = |
| Expressions::LessThan("age", Literal::Long(std::numeric_limits<int64_t>::max())); |
| auto bound_large_result = large_value_lt->Bind(*schema_, /*case_sensitive=*/true); |
| ASSERT_THAT(bound_large_result, IsOk()); |
| |
| auto bound_large = bound_large_result.value(); |
| EXPECT_EQ(bound_large->op(), Expression::Operation::kTrue); |
| } |
| |
| TEST_F(PredicateTest, ComplexExpressionCombinations) { |
| auto eq_pred = Expressions::Equal("age", Literal::Int(25)); |
| auto null_pred = Expressions::IsNull("name"); |
| auto in_pred = |
| Expressions::In("id", {Literal::Long(1), Literal::Long(2), Literal::Long(3)}); |
| |
| // Test AND combinations |
| auto and_eq_null = Expressions::And(eq_pred, null_pred); |
| EXPECT_EQ(and_eq_null->op(), Expression::Operation::kAnd); |
| |
| auto and_eq_in = Expressions::And(eq_pred, in_pred); |
| EXPECT_EQ(and_eq_in->op(), Expression::Operation::kAnd); |
| |
| // Test OR combinations |
| auto or_null_in = Expressions::Or(null_pred, in_pred); |
| EXPECT_EQ(or_null_in->op(), Expression::Operation::kOr); |
| |
| // Test nested combinations |
| auto nested = Expressions::And(and_eq_null, or_null_in); |
| EXPECT_EQ(nested->op(), Expression::Operation::kAnd); |
| } |
| |
| TEST_F(PredicateTest, BoundUnaryPredicateNegate) { |
| auto is_null_pred = Expressions::IsNull("name"); |
| auto bound_null = is_null_pred->Bind(*schema_, /*case_sensitive=*/true).value(); |
| |
| auto negated_result = bound_null->Negate(); |
| ASSERT_THAT(negated_result, IsOk()); |
| auto negated = negated_result.value(); |
| EXPECT_EQ(negated->op(), Expression::Operation::kNotNull); |
| |
| // Double negation should return the original predicate |
| auto double_neg_result = negated->Negate(); |
| ASSERT_THAT(double_neg_result, IsOk()); |
| auto double_neg = double_neg_result.value(); |
| EXPECT_EQ(double_neg->op(), Expression::Operation::kIsNull); |
| } |
| |
| TEST_F(PredicateTest, BoundUnaryPredicateEquals) { |
| auto is_null_name1 = Expressions::IsNull("name"); |
| auto is_null_name2 = Expressions::IsNull("name"); |
| auto is_null_age = Expressions::IsNull("age"); |
| auto not_null_name = Expressions::NotNull("name"); |
| |
| auto bound_null1 = is_null_name1->Bind(*schema_, true).value(); |
| auto bound_null2 = is_null_name2->Bind(*schema_, true).value(); |
| auto bound_null_age = is_null_age->Bind(*schema_, true).value(); |
| auto bound_not_null = not_null_name->Bind(*schema_, true).value(); |
| |
| // Same predicate should be equal |
| EXPECT_TRUE(bound_null1->Equals(*bound_null2)); |
| EXPECT_TRUE(bound_null2->Equals(*bound_null1)); |
| |
| // Different fields should not be equal |
| EXPECT_FALSE(bound_null1->Equals(*bound_null_age)); |
| |
| // Different operations should not be equal |
| EXPECT_FALSE(bound_null1->Equals(*bound_not_null)); |
| } |
| |
| TEST_F(PredicateTest, BoundLiteralPredicateNegate) { |
| auto eq_pred = Expressions::Equal("age", Literal::Int(25)); |
| auto bound_eq = eq_pred->Bind(*schema_, true).value(); |
| |
| auto negated_result = bound_eq->Negate(); |
| ASSERT_THAT(negated_result, IsOk()); |
| |
| auto negated = negated_result.value(); |
| EXPECT_EQ(negated->op(), Expression::Operation::kNotEq); |
| |
| // Test less than negation |
| auto lt_pred = Expressions::LessThan("age", Literal::Int(30)); |
| auto bound_lt = lt_pred->Bind(*schema_, true).value(); |
| auto neg_lt_result = bound_lt->Negate(); |
| ASSERT_THAT(neg_lt_result, IsOk()); |
| EXPECT_EQ(neg_lt_result.value()->op(), Expression::Operation::kGtEq); |
| } |
| |
| TEST_F(PredicateTest, BoundLiteralPredicateEquals) { |
| auto eq1 = Expressions::Equal("age", Literal::Int(25)); |
| auto eq2 = Expressions::Equal("age", Literal::Int(25)); |
| auto eq3 = Expressions::Equal("age", Literal::Int(30)); |
| auto neq = Expressions::NotEqual("age", Literal::Int(25)); |
| |
| auto bound_eq1 = eq1->Bind(*schema_, true).value(); |
| auto bound_eq2 = eq2->Bind(*schema_, true).value(); |
| auto bound_eq3 = eq3->Bind(*schema_, true).value(); |
| auto bound_neq = neq->Bind(*schema_, true).value(); |
| |
| // Same predicate should be equal |
| EXPECT_TRUE(bound_eq1->Equals(*bound_eq2)); |
| |
| // Different literal values should not be equal |
| EXPECT_FALSE(bound_eq1->Equals(*bound_eq3)); |
| |
| // Different operations should not be equal |
| EXPECT_FALSE(bound_eq1->Equals(*bound_neq)); |
| } |
| |
| TEST_F(PredicateTest, BoundLiteralPredicateIntegerEquivalence) { |
| // Test that < 6 is equivalent to <= 5 |
| auto lt_6 = Expressions::LessThan("age", Literal::Int(6)); |
| auto lte_5 = Expressions::LessThanOrEqual("age", Literal::Int(5)); |
| auto bound_lt = lt_6->Bind(*schema_, true).value(); |
| auto bound_lte = lte_5->Bind(*schema_, true).value(); |
| EXPECT_TRUE(bound_lt->Equals(*bound_lte)); |
| EXPECT_TRUE(bound_lte->Equals(*bound_lt)); |
| |
| // Test that > 5 is equivalent to >= 6 |
| auto gt_5 = Expressions::GreaterThan("age", Literal::Int(5)); |
| auto gte_6 = Expressions::GreaterThanOrEqual("age", Literal::Int(6)); |
| auto bound_gt = gt_5->Bind(*schema_, true).value(); |
| auto bound_gte = gte_6->Bind(*schema_, true).value(); |
| EXPECT_TRUE(bound_gt->Equals(*bound_gte)); |
| EXPECT_TRUE(bound_gte->Equals(*bound_gt)); |
| |
| // Test that < 6 is not equivalent to <= 6 |
| auto lte_6 = Expressions::LessThanOrEqual("age", Literal::Int(6)); |
| auto bound_lte_6 = lte_6->Bind(*schema_, true).value(); |
| EXPECT_FALSE(bound_lt->Equals(*bound_lte_6)); |
| } |
| |
| TEST_F(PredicateTest, BoundSetPredicateToString) { |
| auto in_pred = |
| Expressions::In("age", {Literal::Int(10), Literal::Int(20), Literal::Int(30)}); |
| auto bound_in = in_pred->Bind(*schema_, true).value(); |
| |
| auto str = bound_in->ToString(); |
| // The set order might vary, but should contain the key elements |
| // BoundReference uses field_id in ToString, so check for id=3 (age field) |
| EXPECT_TRUE(str.find("id=3") != std::string::npos); |
| EXPECT_TRUE(str.find("in") != std::string::npos); |
| |
| auto not_in_pred = |
| Expressions::NotIn("name", {Literal::String("a"), Literal::String("b")}); |
| auto bound_not_in = not_in_pred->Bind(*schema_, true).value(); |
| |
| auto not_in_str = bound_not_in->ToString(); |
| // Check for id=2 (name field) |
| EXPECT_TRUE(not_in_str.find("id=2") != std::string::npos); |
| EXPECT_TRUE(not_in_str.find("not in") != std::string::npos); |
| } |
| |
| TEST_F(PredicateTest, BoundSetPredicateNegate) { |
| auto in_pred = Expressions::In("age", {Literal::Int(10), Literal::Int(20)}); |
| auto bound_in = in_pred->Bind(*schema_, true).value(); |
| |
| auto negated_result = bound_in->Negate(); |
| ASSERT_THAT(negated_result, IsOk()); |
| |
| auto negated = negated_result.value(); |
| EXPECT_EQ(negated->op(), Expression::Operation::kNotIn); |
| |
| // Test double negation |
| auto double_neg_result = negated->Negate(); |
| ASSERT_THAT(double_neg_result, IsOk()); |
| EXPECT_EQ(double_neg_result.value()->op(), Expression::Operation::kIn); |
| } |
| |
| TEST_F(PredicateTest, BoundSetPredicateEquals) { |
| auto in1 = Expressions::In("age", {Literal::Int(10), Literal::Int(20)}); |
| auto in2 = |
| Expressions::In("age", {Literal::Int(20), Literal::Int(10)}); // Different order |
| auto in3 = |
| Expressions::In("age", {Literal::Int(10), Literal::Int(30)}); // Different values |
| auto in4 = Expressions::In("age2", {Literal::Int(10), Literal::Int(20)}); |
| |
| auto bound_in1 = in1->Bind(*schema_, /*case_sensitive=*/true).value(); |
| auto bound_in2 = in2->Bind(*schema_, /*case_sensitive=*/true).value(); |
| auto bound_in3 = in3->Bind(*schema_, /*case_sensitive=*/true).value(); |
| auto bound_in4 = in4->Bind(*schema_, /*case_sensitive=*/true).value(); |
| |
| // Same values in different order should be equal (unordered_set) |
| EXPECT_TRUE(bound_in1->Equals(*bound_in2)); |
| EXPECT_TRUE(bound_in2->Equals(*bound_in1)); |
| |
| // Different values should not be equal |
| EXPECT_FALSE(bound_in1->Equals(*bound_in3)); |
| |
| // Same values on a different term should not be equal |
| EXPECT_FALSE(bound_in1->Equals(*bound_in4)); |
| } |
| |
| namespace { |
| |
| std::shared_ptr<BoundPredicate> AssertAndCastToBoundPredicate( |
| std::shared_ptr<Expression> expr) { |
| auto bound_pred = std::dynamic_pointer_cast<BoundPredicate>(expr); |
| EXPECT_NE(bound_pred, nullptr) << "Expected a BoundPredicate, got " << expr->ToString(); |
| return bound_pred; |
| } |
| |
| } // namespace |
| |
| TEST_F(PredicateTest, BoundUnaryPredicateTestIsNull) { |
| ICEBERG_UNWRAP_OR_FAIL(auto is_null_pred, Expressions::IsNull("name")->Bind( |
| *schema_, /*case_sensitive=*/true)); |
| auto bound_pred = AssertAndCastToBoundPredicate(is_null_pred); |
| EXPECT_THAT(bound_pred->Test(Literal::Null(string())), HasValue(testing::Eq(true))); |
| EXPECT_THAT(bound_pred->Test(Literal::String("test")), HasValue(testing::Eq(false))); |
| } |
| |
| TEST_F(PredicateTest, BoundUnaryPredicateTestNotNull) { |
| ICEBERG_UNWRAP_OR_FAIL(auto not_null_pred, Expressions::NotNull("name")->Bind( |
| *schema_, /*case_sensitive=*/true)); |
| auto bound_pred = AssertAndCastToBoundPredicate(not_null_pred); |
| EXPECT_THAT(bound_pred->Test(Literal::String("test")), HasValue(testing::Eq(true))); |
| EXPECT_THAT(bound_pred->Test(Literal::Null(string())), HasValue(testing::Eq(false))); |
| } |
| |
| TEST_F(PredicateTest, BoundUnaryPredicateTestIsNaN) { |
| ICEBERG_UNWRAP_OR_FAIL(auto is_nan_pred, Expressions::IsNaN("salary")->Bind( |
| *schema_, /*case_sensitive=*/true)); |
| auto bound_pred = AssertAndCastToBoundPredicate(is_nan_pred); |
| |
| // Test with NaN values |
| EXPECT_THAT(bound_pred->Test(Literal::Float(std::numeric_limits<float>::quiet_NaN())), |
| HasValue(testing::Eq(true))); |
| EXPECT_THAT(bound_pred->Test(Literal::Double(std::numeric_limits<double>::quiet_NaN())), |
| HasValue(testing::Eq(true))); |
| |
| // Test with regular values |
| EXPECT_THAT(bound_pred->Test(Literal::Float(3.14f)), HasValue(testing::Eq(false))); |
| EXPECT_THAT(bound_pred->Test(Literal::Double(2.718)), HasValue(testing::Eq(false))); |
| |
| // Test with infinity |
| EXPECT_THAT(bound_pred->Test(Literal::Float(std::numeric_limits<float>::infinity())), |
| HasValue(testing::Eq(false))); |
| } |
| |
| TEST_F(PredicateTest, BoundUnaryPredicateTestNotNaN) { |
| ICEBERG_UNWRAP_OR_FAIL(auto not_nan_pred, Expressions::NotNaN("salary")->Bind( |
| *schema_, /*case_sensitive=*/true)); |
| auto bound_pred = AssertAndCastToBoundPredicate(not_nan_pred); |
| |
| // Test with regular values |
| EXPECT_THAT(bound_pred->Test(Literal::Double(100.5)), HasValue(testing::Eq(true))); |
| |
| // Test with NaN |
| EXPECT_THAT(bound_pred->Test(Literal::Double(std::numeric_limits<double>::quiet_NaN())), |
| HasValue(testing::Eq(false))); |
| |
| // Test with infinity (should be true as infinity is not NaN) |
| EXPECT_THAT(bound_pred->Test(Literal::Double(std::numeric_limits<double>::infinity())), |
| HasValue(testing::Eq(true))); |
| } |
| |
| TEST_F(PredicateTest, BoundLiteralPredicateTestComparison) { |
| // Test less than |
| ICEBERG_UNWRAP_OR_FAIL(auto lt_pred, Expressions::LessThan("age", Literal::Int(30)) |
| ->Bind(*schema_, /*case_sensitive=*/true)); |
| auto bound_lt = AssertAndCastToBoundPredicate(lt_pred); |
| EXPECT_THAT(bound_lt->Test(Literal::Int(20)), HasValue(testing::Eq(true))); |
| EXPECT_THAT(bound_lt->Test(Literal::Int(30)), HasValue(testing::Eq(false))); |
| EXPECT_THAT(bound_lt->Test(Literal::Int(40)), HasValue(testing::Eq(false))); |
| |
| // Test less than or equal |
| ICEBERG_UNWRAP_OR_FAIL(auto lte_pred, |
| Expressions::LessThanOrEqual("age", Literal::Int(30)) |
| ->Bind(*schema_, /*case_sensitive=*/true)); |
| auto bound_lte = AssertAndCastToBoundPredicate(lte_pred); |
| EXPECT_THAT(bound_lte->Test(Literal::Int(20)), HasValue(testing::Eq(true))); |
| EXPECT_THAT(bound_lte->Test(Literal::Int(30)), HasValue(testing::Eq(true))); |
| EXPECT_THAT(bound_lte->Test(Literal::Int(40)), HasValue(testing::Eq(false))); |
| |
| // Test greater than |
| ICEBERG_UNWRAP_OR_FAIL(auto gt_pred, Expressions::GreaterThan("age", Literal::Int(30)) |
| ->Bind(*schema_, /*case_sensitive=*/true)); |
| auto bound_gt = AssertAndCastToBoundPredicate(gt_pred); |
| EXPECT_THAT(bound_gt->Test(Literal::Int(20)), HasValue(testing::Eq(false))); |
| EXPECT_THAT(bound_gt->Test(Literal::Int(30)), HasValue(testing::Eq(false))); |
| EXPECT_THAT(bound_gt->Test(Literal::Int(40)), HasValue(testing::Eq(true))); |
| |
| // Test greater than or equal |
| ICEBERG_UNWRAP_OR_FAIL(auto gte_pred, |
| Expressions::GreaterThanOrEqual("age", Literal::Int(30)) |
| ->Bind(*schema_, /*case_sensitive=*/true)); |
| auto bound_gte = AssertAndCastToBoundPredicate(gte_pred); |
| EXPECT_THAT(bound_gte->Test(Literal::Int(20)), HasValue(testing::Eq(false))); |
| EXPECT_THAT(bound_gte->Test(Literal::Int(30)), HasValue(testing::Eq(true))); |
| EXPECT_THAT(bound_gte->Test(Literal::Int(40)), HasValue(testing::Eq(true))); |
| } |
| |
| TEST_F(PredicateTest, BoundLiteralPredicateTestEquality) { |
| // Test equal |
| ICEBERG_UNWRAP_OR_FAIL(auto eq_pred, Expressions::Equal("age", Literal::Int(25)) |
| ->Bind(*schema_, /*case_sensitive=*/true)); |
| auto bound_eq = AssertAndCastToBoundPredicate(eq_pred); |
| EXPECT_THAT(bound_eq->Test(Literal::Int(25)), HasValue(testing::Eq(true))); |
| EXPECT_THAT(bound_eq->Test(Literal::Int(26)), HasValue(testing::Eq(false))); |
| EXPECT_THAT(bound_eq->Test(Literal::Int(24)), HasValue(testing::Eq(false))); |
| |
| // Test not equal |
| ICEBERG_UNWRAP_OR_FAIL(auto neq_pred, Expressions::NotEqual("age", Literal::Int(25)) |
| ->Bind(*schema_, /*case_sensitive=*/true)); |
| auto bound_neq = AssertAndCastToBoundPredicate(neq_pred); |
| EXPECT_THAT(bound_neq->Test(Literal::Int(25)), HasValue(testing::Eq(false))); |
| EXPECT_THAT(bound_neq->Test(Literal::Int(26)), HasValue(testing::Eq(true))); |
| EXPECT_THAT(bound_neq->Test(Literal::Int(24)), HasValue(testing::Eq(true))); |
| } |
| |
| TEST_F(PredicateTest, BoundLiteralPredicateTestWithDifferentTypes) { |
| // Test with double |
| ICEBERG_UNWRAP_OR_FAIL(auto gt_pred, |
| Expressions::GreaterThan("salary", Literal::Double(50000.0)) |
| ->Bind(*schema_, /*case_sensitive=*/true)); |
| auto bound_double = AssertAndCastToBoundPredicate(gt_pred); |
| EXPECT_THAT(bound_double->Test(Literal::Double(60000.0)), HasValue(testing::Eq(true))); |
| EXPECT_THAT(bound_double->Test(Literal::Double(40000.0)), HasValue(testing::Eq(false))); |
| EXPECT_THAT(bound_double->Test(Literal::Double(50000.0)), HasValue(testing::Eq(false))); |
| |
| // Test with string |
| ICEBERG_UNWRAP_OR_FAIL(auto str_eq_pred, |
| Expressions::Equal("name", Literal::String("Alice")) |
| ->Bind(*schema_, /*case_sensitive=*/true)); |
| auto bound_string = AssertAndCastToBoundPredicate(str_eq_pred); |
| EXPECT_THAT(bound_string->Test(Literal::String("Alice")), HasValue(testing::Eq(true))); |
| EXPECT_THAT(bound_string->Test(Literal::String("Bob")), HasValue(testing::Eq(false))); |
| EXPECT_THAT(bound_string->Test(Literal::String("alice")), |
| HasValue(testing::Eq(false))); // Case sensitive |
| |
| // Test with boolean |
| ICEBERG_UNWRAP_OR_FAIL(auto bool_eq_pred, |
| Expressions::Equal("active", Literal::Boolean(true)) |
| ->Bind(*schema_, /*case_sensitive=*/true)); |
| auto bound_bool = AssertAndCastToBoundPredicate(bool_eq_pred); |
| EXPECT_THAT(bound_bool->Test(Literal::Boolean(true)), HasValue(testing::Eq(true))); |
| EXPECT_THAT(bound_bool->Test(Literal::Boolean(false)), HasValue(testing::Eq(false))); |
| } |
| |
| TEST_F(PredicateTest, BoundLiteralPredicateTestStartsWith) { |
| ICEBERG_UNWRAP_OR_FAIL( |
| auto starts_with_pred, |
| Expressions::StartsWith("name", "Jo")->Bind(*schema_, /*case_sensitive=*/true)); |
| auto bound_pred = AssertAndCastToBoundPredicate(starts_with_pred); |
| |
| // Test strings that start with "Jo" |
| EXPECT_THAT(bound_pred->Test(Literal::String("John")), HasValue(testing::Eq(true))); |
| EXPECT_THAT(bound_pred->Test(Literal::String("Joe")), HasValue(testing::Eq(true))); |
| EXPECT_THAT(bound_pred->Test(Literal::String("Jo")), HasValue(testing::Eq(true))); |
| |
| // Test strings that don't start with "Jo" |
| EXPECT_THAT(bound_pred->Test(Literal::String("Alice")), HasValue(testing::Eq(false))); |
| EXPECT_THAT(bound_pred->Test(Literal::String("Bob")), HasValue(testing::Eq(false))); |
| EXPECT_THAT(bound_pred->Test(Literal::String("")), HasValue(testing::Eq(false))); |
| |
| // Test empty prefix |
| ICEBERG_UNWRAP_OR_FAIL( |
| auto empty_prefix_pred, |
| Expressions::StartsWith("name", "")->Bind(*schema_, /*case_sensitive=*/true)); |
| auto bound_empty = AssertAndCastToBoundPredicate(empty_prefix_pred); |
| |
| // All strings should start with empty prefix |
| EXPECT_THAT(bound_empty->Test(Literal::String("test")), HasValue(testing::Eq(true))); |
| EXPECT_THAT(bound_empty->Test(Literal::String("")), HasValue(testing::Eq(true))); |
| } |
| |
| TEST_F(PredicateTest, BoundLiteralPredicateTestNotStartsWith) { |
| ICEBERG_UNWRAP_OR_FAIL( |
| auto not_starts_with_pred, |
| Expressions::NotStartsWith("name", "Jo")->Bind(*schema_, /*case_sensitive=*/true)); |
| auto bound_pred = AssertAndCastToBoundPredicate(not_starts_with_pred); |
| |
| // Test strings that don't start with "Jo" |
| EXPECT_THAT(bound_pred->Test(Literal::String("Alice")), HasValue(testing::Eq(true))); |
| EXPECT_THAT(bound_pred->Test(Literal::String("Bob")), HasValue(testing::Eq(true))); |
| EXPECT_THAT(bound_pred->Test(Literal::String("")), HasValue(testing::Eq(true))); |
| |
| // Test strings that start with "Jo" |
| EXPECT_THAT(bound_pred->Test(Literal::String("John")), HasValue(testing::Eq(false))); |
| EXPECT_THAT(bound_pred->Test(Literal::String("Joe")), HasValue(testing::Eq(false))); |
| EXPECT_THAT(bound_pred->Test(Literal::String("Jo")), HasValue(testing::Eq(false))); |
| } |
| |
| TEST_F(PredicateTest, BoundSetPredicateTestIn) { |
| ICEBERG_UNWRAP_OR_FAIL( |
| auto in_pred, |
| Expressions::In("age", {Literal::Int(10), Literal::Int(20), Literal::Int(30)}) |
| ->Bind(*schema_, /*case_sensitive=*/true)); |
| auto bound_pred = AssertAndCastToBoundPredicate(in_pred); |
| |
| // Test values in the set |
| EXPECT_THAT(bound_pred->Test(Literal::Int(10)), HasValue(testing::Eq(true))); |
| EXPECT_THAT(bound_pred->Test(Literal::Int(20)), HasValue(testing::Eq(true))); |
| EXPECT_THAT(bound_pred->Test(Literal::Int(30)), HasValue(testing::Eq(true))); |
| |
| // Test values not in the set |
| EXPECT_THAT(bound_pred->Test(Literal::Int(15)), HasValue(testing::Eq(false))); |
| EXPECT_THAT(bound_pred->Test(Literal::Int(40)), HasValue(testing::Eq(false))); |
| EXPECT_THAT(bound_pred->Test(Literal::Int(0)), HasValue(testing::Eq(false))); |
| } |
| |
| TEST_F(PredicateTest, BoundSetPredicateTestNotIn) { |
| ICEBERG_UNWRAP_OR_FAIL( |
| auto not_in_pred, |
| Expressions::NotIn("age", {Literal::Int(10), Literal::Int(20), Literal::Int(30)}) |
| ->Bind(*schema_, /*case_sensitive=*/true)); |
| auto bound_pred = AssertAndCastToBoundPredicate(not_in_pred); |
| |
| // Test values not in the set |
| EXPECT_THAT(bound_pred->Test(Literal::Int(15)), HasValue(testing::Eq(true))); |
| EXPECT_THAT(bound_pred->Test(Literal::Int(40)), HasValue(testing::Eq(true))); |
| EXPECT_THAT(bound_pred->Test(Literal::Int(0)), HasValue(testing::Eq(true))); |
| |
| // Test values in the set |
| EXPECT_THAT(bound_pred->Test(Literal::Int(10)), HasValue(testing::Eq(false))); |
| EXPECT_THAT(bound_pred->Test(Literal::Int(20)), HasValue(testing::Eq(false))); |
| EXPECT_THAT(bound_pred->Test(Literal::Int(30)), HasValue(testing::Eq(false))); |
| } |
| |
| TEST_F(PredicateTest, BoundSetPredicateTestWithStrings) { |
| ICEBERG_UNWRAP_OR_FAIL( |
| auto in_pred, |
| Expressions::In("name", {Literal::String("Alice"), Literal::String("Bob"), |
| Literal::String("Charlie")}) |
| ->Bind(*schema_, /*case_sensitive=*/true)); |
| auto bound_pred = AssertAndCastToBoundPredicate(in_pred); |
| |
| // Test strings in the set |
| EXPECT_THAT(bound_pred->Test(Literal::String("Alice")), HasValue(testing::Eq(true))); |
| EXPECT_THAT(bound_pred->Test(Literal::String("Bob")), HasValue(testing::Eq(true))); |
| EXPECT_THAT(bound_pred->Test(Literal::String("Charlie")), HasValue(testing::Eq(true))); |
| |
| // Test strings not in the set |
| EXPECT_THAT(bound_pred->Test(Literal::String("David")), HasValue(testing::Eq(false))); |
| EXPECT_THAT(bound_pred->Test(Literal::String("alice")), |
| HasValue(testing::Eq(false))); // Case sensitive |
| EXPECT_THAT(bound_pred->Test(Literal::String("")), HasValue(testing::Eq(false))); |
| } |
| |
| TEST_F(PredicateTest, BoundSetPredicateTestWithLongs) { |
| ICEBERG_UNWRAP_OR_FAIL(auto in_pred, |
| Expressions::In("id", {Literal::Long(100L), Literal::Long(200L), |
| Literal::Long(300L)}) |
| ->Bind(*schema_, /*case_sensitive=*/true)); |
| auto bound_pred = AssertAndCastToBoundPredicate(in_pred); |
| |
| // Test longs in the set |
| EXPECT_THAT(bound_pred->Test(Literal::Long(100L)), HasValue(testing::Eq(true))); |
| EXPECT_THAT(bound_pred->Test(Literal::Long(200L)), HasValue(testing::Eq(true))); |
| EXPECT_THAT(bound_pred->Test(Literal::Long(300L)), HasValue(testing::Eq(true))); |
| |
| // Test longs not in the set |
| EXPECT_THAT(bound_pred->Test(Literal::Long(150L)), HasValue(testing::Eq(false))); |
| EXPECT_THAT(bound_pred->Test(Literal::Long(400L)), HasValue(testing::Eq(false))); |
| } |
| |
| TEST_F(PredicateTest, BoundSetPredicateTestSingleLiteral) { |
| ICEBERG_UNWRAP_OR_FAIL(auto in_pred, Expressions::In("age", {Literal::Int(42)}) |
| ->Bind(*schema_, /*case_sensitive=*/true)); |
| |
| // Single element IN becomes Equal |
| EXPECT_EQ(in_pred->op(), Expression::Operation::kEq); |
| auto bound_literal = AssertAndCastToBoundPredicate(in_pred); |
| EXPECT_THAT(bound_literal->Test(Literal::Int(42)), HasValue(testing::Eq(true))); |
| EXPECT_THAT(bound_literal->Test(Literal::Int(41)), HasValue(testing::Eq(false))); |
| } |
| |
| } // namespace iceberg |