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

wgtmac pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-cpp.git


The following commit(s) were added to refs/heads/main by this push:
     new a88f88a0 feat: fold IS NULL/NOT NULL predicates on unknown type (#983)
a88f88a0 is described below

commit a88f88a0ba5a3ce4d1b6cd1876483a668d8cf871
Author: Byeonggyu Park <[email protected]>
AuthorDate: Sat Oct 10 18:50:41 2026 +0900

    feat: fold IS NULL/NOT NULL predicates on unknown type (#983)
---
 src/iceberg/expression/predicate.cc |  7 ++++++-
 src/iceberg/test/predicate_test.cc  | 21 ++++++++++++++++++++-
 2 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/src/iceberg/expression/predicate.cc 
b/src/iceberg/expression/predicate.cc
index 44c4a3a7..9af590f1 100644
--- a/src/iceberg/expression/predicate.cc
+++ b/src/iceberg/expression/predicate.cc
@@ -267,13 +267,18 @@ Result<std::shared_ptr<Expression>> 
UnboundPredicateImpl<B>::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 6b990462..5d810e69 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);

Reply via email to