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);