Skip to content

Commit a88f88a

Browse files
authored
feat: fold IS NULL/NOT NULL predicates on unknown type (#983)
1 parent 84fb526 commit a88f88a

2 files changed

Lines changed: 26 additions & 2 deletions

File tree

‎src/iceberg/expression/predicate.cc‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,13 +267,18 @@ Result<std::shared_ptr<Expression>> UnboundPredicateImpl<B>::BindUnaryOperation(
267267
if (!bound_term->MayProduceNull()) {
268268
return Expressions::AlwaysFalse();
269269
}
270-
// TODO(gangwu): deal with UnknownType
270+
if (bound_term->type()->type_id() == TypeId::kUnknown) {
271+
return Expressions::AlwaysTrue();
272+
}
271273
return BoundUnaryPredicate::Make(Expression::Operation::kIsNull,
272274
std::move(bound_term));
273275
case Expression::Operation::kNotNull:
274276
if (!bound_term->MayProduceNull()) {
275277
return Expressions::AlwaysTrue();
276278
}
279+
if (bound_term->type()->type_id() == TypeId::kUnknown) {
280+
return Expressions::AlwaysFalse();
281+
}
277282
return BoundUnaryPredicate::Make(Expression::Operation::kNotNull,
278283
std::move(bound_term));
279284
case Expression::Operation::kIsNan:

‎src/iceberg/test/predicate_test.cc‎

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ class PredicateTest : public ::testing::Test {
3939
SchemaField::MakeRequired(3, "age", int32()),
4040
SchemaField::MakeOptional(4, "salary", float64()),
4141
SchemaField::MakeRequired(5, "active", boolean()),
42-
SchemaField::MakeRequired(6, "age2", int32())},
42+
SchemaField::MakeRequired(6, "age2", int32()),
43+
SchemaField::MakeOptional(7, "always_null", unknown())},
4344
/*schema_id=*/0);
4445
}
4546

@@ -365,6 +366,24 @@ TEST_F(PredicateTest, UnboundPredicateBindUnary) {
365366
EXPECT_EQ(bound_is_null->op(), Expression::Operation::kFalse);
366367
}
367368

369+
TEST_F(PredicateTest, UnboundPredicateBindUnaryWithUnknownType) {
370+
// Test IS NULL on unknown type field - should return AlwaysTrue
371+
auto is_null_unknown = Expressions::IsNull("always_null"); // always_null is unknown
372+
auto bound_is_null_result = is_null_unknown->Bind(*schema_, /*case_sensitive=*/true);
373+
ASSERT_THAT(bound_is_null_result, IsOk());
374+
375+
auto bound_is_null = bound_is_null_result.value();
376+
EXPECT_EQ(bound_is_null->op(), Expression::Operation::kTrue);
377+
378+
// Test NOT NULL on unknown type field - should return AlwaysFalse
379+
auto not_null_unknown = Expressions::NotNull("always_null");
380+
auto bound_not_null_result = not_null_unknown->Bind(*schema_, /*case_sensitive=*/true);
381+
ASSERT_THAT(bound_not_null_result, IsOk());
382+
383+
auto bound_not_null = bound_not_null_result.value();
384+
EXPECT_EQ(bound_not_null->op(), Expression::Operation::kFalse);
385+
}
386+
368387
TEST_F(PredicateTest, UnboundPredicateBindLiteral) {
369388
auto equal_pred = Expressions::Equal("age", Literal::Int(25));
370389
auto bound_result = equal_pred->Bind(*schema_, /*case_sensitive=*/true);

0 commit comments

Comments
 (0)