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 de7c9ce0 fix(expression): treat COUNT(*) as referencing no fields in 
GetReferencedFieldIds (#981)
de7c9ce0 is described below

commit de7c9ce0715c5846ab5ce3cbcaa6d90c199a5ba1
Author: YangJie <[email protected]>
AuthorDate: Tue Oct 13 03:16:52 2026 -0400

    fix(expression): treat COUNT(*) as referencing no fields in 
GetReferencedFieldIds (#981)
    
    ReferenceVisitor::Aggregate unconditionally called
    aggregate->reference()->field_id(), but BoundAggregate::reference()
    returns nullptr for aggregates without a term such as COUNT(*), so
    GetReferencedFieldIds on a bound count(*) dereferenced null and
    crashed. Skip the insert when there is no reference; the aggregate
    then correctly contributes no field ids, matching the Java reference.
---
 src/iceberg/expression/binder.cc            |  5 ++++-
 src/iceberg/test/expression_visitor_test.cc | 19 +++++++++++++++++++
 2 files changed, 23 insertions(+), 1 deletion(-)

diff --git a/src/iceberg/expression/binder.cc b/src/iceberg/expression/binder.cc
index ecc1f1f2..52dede27 100644
--- a/src/iceberg/expression/binder.cc
+++ b/src/iceberg/expression/binder.cc
@@ -180,7 +180,10 @@ Result<FieldIdsSetRef> ReferenceVisitor::Predicate(
 
 Result<FieldIdsSetRef> ReferenceVisitor::Aggregate(
     const std::shared_ptr<BoundAggregate>& aggregate) {
-  referenced_field_ids_.insert(aggregate->reference()->field_id());
+  // Aggregates without a term (e.g. COUNT(*)) reference no field ids.
+  if (const auto& reference = aggregate->reference()) {
+    referenced_field_ids_.insert(reference->field_id());
+  }
   return referenced_field_ids_;
 }
 
diff --git a/src/iceberg/test/expression_visitor_test.cc 
b/src/iceberg/test/expression_visitor_test.cc
index e58902dc..d24142cf 100644
--- a/src/iceberg/test/expression_visitor_test.cc
+++ b/src/iceberg/test/expression_visitor_test.cc
@@ -839,6 +839,25 @@ TEST_F(ReferenceVisitorTest, Constants) {
   EXPECT_TRUE(refs_false.empty());
 }
 
+TEST_F(ReferenceVisitorTest, CountStar) {
+  // COUNT(*) has no referenced field; the aggregate's reference() is null, so
+  // GetReferencedFieldIds must return an empty set instead of dereferencing 
it.
+  ICEBERG_UNWRAP_OR_FAIL(auto bound_count_star, 
Bind(Expressions::CountStar()));
+
+  ICEBERG_UNWRAP_OR_FAIL(auto refs,
+                         
ReferenceVisitor::GetReferencedFieldIds(bound_count_star));
+  EXPECT_TRUE(refs.empty());
+}
+
+TEST_F(ReferenceVisitorTest, AggregateWithTerm) {
+  // An aggregate with a term (e.g. MAX(age)) still references its field, so 
the
+  // null guard must not drop the non-null case. age has field id 3.
+  ICEBERG_UNWRAP_OR_FAIL(auto bound_max, Bind(Expressions::Max("age")));
+
+  ICEBERG_UNWRAP_OR_FAIL(auto refs, 
ReferenceVisitor::GetReferencedFieldIds(bound_max));
+  EXPECT_THAT(refs, ::testing::UnorderedElementsAre(3));
+}
+
 TEST_F(ReferenceVisitorTest, UnboundPredicate) {
   auto unbound_pred = Expressions::Equal("name", Literal::String("Alice"));
   auto result = ReferenceVisitor::GetReferencedFieldIds(unbound_pred);

Reply via email to