Skip to content

Streaming aggregation implementation of ARRAY_AGG() - #4463

Open
robert-brunel wants to merge 1 commit into
mainfrom
apple/robert-brunel/array_agg-1
Open

Streaming aggregation implementation of ARRAY_AGG()#4463
robert-brunel wants to merge 1 commit into
mainfrom
apple/robert-brunel/array_agg-1

Conversation

@robert-brunel

@robert-brunel robert-brunel commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

This change implements the ArrayAggValue built-in to evaluate the ARRAY_AGG() aggregate function, as a StreamableAggregateValue (as opposed to an IndexableAggregateValue; indexing would require a new index type or maintainer and is not in scope).

The DISTINCT and ORDER BY clauses are not yet supported. The {RESPECT|IGNORE} NULLS clause is, in principle, supported, but subject to the limitation that nulls in arrays cannot currently be represented (Issue #3646).

The accumulator collects elements into a list and (since OneOfTypedState has no list slot) serializes that list for continuations through a wrapper record with a single repeated field.

@robert-brunel robert-brunel self-assigned this Aug 17, 2026
@robert-brunel robert-brunel added the enhancement New feature or request label Aug 17, 2026
@robert-brunel
robert-brunel force-pushed the apple/robert-brunel/array_agg-1 branch from c4bc382 to 6951560 Compare August 17, 2026 19:10
@robert-brunel
robert-brunel changed the base branch from main to apple/robert-brunel/nullable-arrays-tweaks August 17, 2026 19:14
@robert-brunel robert-brunel changed the title Streaming aggregation support for ARRAY_AGG() Streaming aggregation implementation of ARRAY_AGG() Aug 17, 2026
@robert-brunel
robert-brunel marked this pull request as ready for review August 17, 2026 19:14
@robert-brunel
robert-brunel force-pushed the apple/robert-brunel/array_agg-1 branch from 6951560 to 31f7c37 Compare August 18, 2026 10:14
Base automatically changed from apple/robert-brunel/nullable-arrays-tweaks to main August 18, 2026 18:33
This change implements the `ArrayAggValue` built-in to evaluate the `ARRAY_AGG()` aggregate function, as a `StreamableAggregateValue` (as opposed to an `IndexableAggregateValue`; indexing would require a new index type or maintainer and is not in scope).

The `DISTINCT` and `ORDER BY` clauses are not yet supported. The `{RESPECT|IGNORE} NULLS` clause is, in principle, supported, but subject to the limitation that nulls in arrays cannot currently be represented (Issue #3646).

The accumulator collects elements into a list and (since `OneOfTypedState` has no list slot) serializes that list for continuations through a wrapper record with a single `repeated` field.
@robert-brunel
robert-brunel force-pushed the apple/robert-brunel/array_agg-1 branch from 31f7c37 to 18da484 Compare August 18, 2026 18:33
@github-actions

Copy link
Copy Markdown

📊 Metrics Diff Analysis Report

Summary

  • New queries: 4
  • Dropped queries: 0
  • Plan changed + metrics changed: 0
  • Plan unchanged + metrics changed: 0
ℹ️ About this analysis

This automated analysis compares query planner metrics between the base branch and this PR. It categorizes changes into:

  • New queries: Queries added in this PR
  • Dropped queries: Queries removed in this PR. These should be reviewed to ensure we are not losing coverage.
  • Plan changed + metrics changed: The query plan has changed along with planner metrics.
  • Metrics only changed: Same plan but different metrics

The last category in particular may indicate planner regressions that should be investigated.

New Queries

Count of new queries by file:

  • yaml-tests/src/test/resources/array-agg-tests.metrics.yaml: 4

* the element type will be non-nullable, since {@code NULL} values are then skipped rather than collected.
*/
public ArrayAggValue(@Nonnull final Value child, final boolean ignoreNulls) {
this(child, ignoreNulls ? child.getResultType().notNullable() : child.getResultType(), ignoreNulls);

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.

if child.getResultType() == Type.NULL, notNullable() will throw an exception. is this expected? Or should the method verify child.getResultType() before this line?

this.elementType = elementType;
this.ignoreNulls = ignoreNulls;
this.wrapperDescriptorSupplier = Suppliers.memoize(() -> wrapperDescriptorFor(elementType));
this.resultTypeSupplier = Suppliers.memoize(() -> new Type.Array(true, elementType));

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.

so here the array's isNullable = true, while in line#145, it is constructed as Type.Array(false, elementType), is it intentional?

Type.Record.Field.of(new Type.Array(false, elementType),
Optional.of(NullableArrayTypeUtils.getRepeatedFieldName()))));
final TypeRepository localRepository = TypeRepository.newBuilder().addTypeIfNeeded(wrapperType).build();
return Verify.verifyNotNull(localRepository.getMessageDescriptor(wrapperType));

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.

are you trying to reuse some logics in TypeRepository here? is it possible to extract the logic as some static method and call the static method here instead?

this.valuesField = Verify.verifyNotNull(
wrapperDescriptor.findFieldByName(NullableArrayTypeUtils.getRepeatedFieldName()));
this.elements = new ArrayList<>();
this.ignoreNulls = ignoreNulls;

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.

this.seenAnyRow = false;

// Under IGNORE NULLS a NULL is simply dropped. Under RESPECT NULLS (the SQL default) it would have to
// be collected. However, nulls are not representable currently.
SemanticException.check(ignoreNulls, SemanticException.ErrorCode.UNSUPPORTED,
"An ARRAY value cannot have NULL elements");

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.

what happens if ignoreNulls = false?

- maxRows: 1
- result: [{10, [100, 200, 300]}]
- result: [{20, [400, 500]}]
- result: [{30, []}]

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.

if not ignore nulls, will this row be result: [{30, [null]}]?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants