diff --git a/src/iceberg/expression/predicate.cc b/src/iceberg/expression/predicate.cc index 44c4a3a70..9af590f10 100644 --- a/src/iceberg/expression/predicate.cc +++ b/src/iceberg/expression/predicate.cc @@ -267,13 +267,18 @@ Result> UnboundPredicateImpl::BindUnaryOperation( if (!bound_term->MayProduceNull()) { return Expressions::AlwaysFalse(); } - // TODO(gangwu): deal with UnknownType + if (bound_term->type()->type_id() == TypeId::kUnknown) { + return Expressions::AlwaysTrue(); + } return BoundUnaryPredicate::Make(Expression::Operation::kIsNull, std::move(bound_term)); case Expression::Operation::kNotNull: if (!bound_term->MayProduceNull()) { return Expressions::AlwaysTrue(); } + if (bound_term->type()->type_id() == TypeId::kUnknown) { + return Expressions::AlwaysFalse(); + } return BoundUnaryPredicate::Make(Expression::Operation::kNotNull, std::move(bound_term)); case Expression::Operation::kIsNan: diff --git a/src/iceberg/test/predicate_test.cc b/src/iceberg/test/predicate_test.cc index 6b990462a..5d810e695 100644 --- a/src/iceberg/test/predicate_test.cc +++ b/src/iceberg/test/predicate_test.cc @@ -39,7 +39,8 @@ class PredicateTest : public ::testing::Test { SchemaField::MakeRequired(3, "age", int32()), SchemaField::MakeOptional(4, "salary", float64()), SchemaField::MakeRequired(5, "active", boolean()), - SchemaField::MakeRequired(6, "age2", int32())}, + SchemaField::MakeRequired(6, "age2", int32()), + SchemaField::MakeOptional(7, "always_null", unknown())}, /*schema_id=*/0); } @@ -365,6 +366,24 @@ TEST_F(PredicateTest, UnboundPredicateBindUnary) { EXPECT_EQ(bound_is_null->op(), Expression::Operation::kFalse); } +TEST_F(PredicateTest, UnboundPredicateBindUnaryWithUnknownType) { + // Test IS NULL on unknown type field - should return AlwaysTrue + auto is_null_unknown = Expressions::IsNull("always_null"); // always_null is unknown + auto bound_is_null_result = is_null_unknown->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::kTrue); + + // Test NOT NULL on unknown type field - should return AlwaysFalse + auto not_null_unknown = Expressions::NotNull("always_null"); + auto bound_not_null_result = not_null_unknown->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::kFalse); +} + TEST_F(PredicateTest, UnboundPredicateBindLiteral) { auto equal_pred = Expressions::Equal("age", Literal::Int(25)); auto bound_result = equal_pred->Bind(*schema_, /*case_sensitive=*/true);