Skip to content

[docs] Clarify FIRST_VALUE null handling in aggregation - #4946

Merged
platinumhamburg merged 3 commits into
apache:mainfrom
platinumhamburg:feature/fix-first-value-null
Oct 30, 2026
Merged

platinumhamburg merged 3 commits into
apache:mainfrom
platinumhamburg:feature/fix-first-value-null

Conversation

@platinumhamburg

@platinumhamburg platinumhamburg commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Purpose

Fixes #4945.

The aggregation documentation and AggFunctions.FIRST_VALUE() Javadoc promise that an initial NULL is retained, but the implementation treats NULL as unset and keeps the first non-null value. For example, NULL -> 10 -> 20 produces 10.

Document this existing behavior so that fields omitted from an initial partial update can still receive their first non-null value in a later update. An unset field and an explicitly written NULL share the same null accumulator.

Brief change log

  • Describe FIRST_VALUE as retaining the first non-null value in the user documentation and API Javadoc.
  • State that its behavior matches FIRST_VALUE_IGNORE_NULLS.
  • Explain the NULL sequence and partial-update behavior in the user documentation.

Tests

Passed on JDK 11:

./mvnw -pl fluss-server -am verify -Dtest=FieldAggregatorParameterizedTest,AggregateRowMergerTest,AggregationContextTest -Dsurefire.failIfNoSpecifiedTests=false

Includes the existing aggregation tests, Spotless, Checkstyle, and license checks. The complete repository test suite was not run.

API and Format

No runtime behavior, API signature, or storage format changes. The Javadoc now describes the existing NULL handling.

Documentation

Update the aggregation merge engine's first_value section and AggFunctions.FIRST_VALUE() Javadoc.

Keep the existing field value even when it is null. The row merger
already handles the first write before invoking field aggregation.

Cover null first values across full and partial updates.
@platinumhamburg
platinumhamburg marked this pull request as ready for review October 30, 2026 01:20
Start with no existing row and verify that 10 is retained across
subsequent inputs 20, null, and 30 for full and partial updates.
@luoyuxia
luoyuxia requested a balanced review from Copilot October 30, 2026 01:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

@polyzos

polyzos commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

@platinumhamburg, I think this breaks a more common case: with partial updates, a column that wasn't written yet also reaches agg() with a null accumulator. With this change it then stays null forever.

I used a test: a partial write of (id, other) followed by a partial write of (id, value=10) gives value = 10 on main and null with this PR.

Since "first value was null" and "not written yet" can't be told apart without extra state per field, should we keep the current behavior and update the docs to say FIRST_VALUE treats null as not set? WDYT?

@platinumhamburg

Copy link
Copy Markdown
Contributor Author

@polyzos Thanks for catching this. You’re right: I overlooked the distinction between the first write to a row and the first write to an individual field. The added partial-update tests always included the value column, so they missed the case you described.

Given that both “not written yet” and “explicitly written as NULL” are represented by a null accumulator, preserving the existing partial-update behavior is the better tradeoff. I agree we should keep the current implementation and document that FIRST_VALUE treats NULL as unset, effectively matching FIRST_VALUE_IGNORE_NULLS.

This means accepting a semantic difference from Paimon until we have a practical way to preserve that distinction across writes and recovery. I’ll update the issue and rework this PR as a documentation correction.

Document FIRST_VALUE as retaining the first non-null value, matching FIRST_VALUE_IGNORE_NULLS. Preserve partial-update initialization by keeping the existing aggregation implementation.
@platinumhamburg platinumhamburg changed the title [server] Preserve null first values in aggregate merge engine [docs] Clarify FIRST_VALUE null handling in aggregation Oct 30, 2026
@platinumhamburg
platinumhamburg merged commit 0010029 into apache:main Oct 30, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] FIRST_VALUE documentation incorrectly promises initial NULL retention

4 participants