Skip to content

fix: use SUM0 in AggregateCaseToFilter D1 rewrite to avoid NULL on empty windows - #20093

Open
zhang-arvin wants to merge 5 commits into
apache:masterfrom
zhang-arvin:fix/issue-18058-empty-window-null
Open

zhang-arvin wants to merge 5 commits into
apache:masterfrom
zhang-arvin:fix/issue-18058-empty-window-null

Conversation

@zhang-arvin

Copy link
Copy Markdown

Description

Fixes #18058 and #17768.

The DruidAggregateCaseToFilterRule D1 rewrite converts SUM(CASE WHEN COND THEN COL1 ELSE 0 END) to SUM(COL1) FILTER(WHERE COND). However, SUM returns NULL when the filter never matches, while the original CASE expression would return 0. This causes incorrect NULL results for queries with aggregations on empty windows.

Root Cause

The D1 rewrite in DruidAggregateCaseToFilterRule.transform() uses call.getAggregation() (which is SUM) when creating the filtered aggregate call. SUM returns NULL for empty groups, but the original SUM(CASE WHEN ... ELSE 0 END) returns 0 when no rows match the condition.

Fix

Changed the D1 rewrite to use SUM0 (via SqlStdOperatorTable.SUM0) instead of SUM. SUM0 returns 0 for empty/null input, which matches the expected behavior of the original CASE expression.

Changes

  • DruidAggregateCaseToFilterRule.java: Changed the D1 case in transform() to use SUM0 instead of SUM for the filtered aggregate call, with non-nullable result type.
  • filtered_sum.iq: Updated expected test results to reflect the corrected behavior (0 instead of NULL for empty window and no-match cases).

Behavior Changes

Scenario Before After
Empty input (no rows) NULL 0
Rows exist, none match filter NULL 0
Rows exist, some match, non-null N N (unchanged)
Rows exist, all null values NULL 0

Key Features

  • Aggregations on empty windows now return 0 instead of NULL
  • Consistent with the documented behavior table in the class javadoc
  • Backward compatible for most use cases

Testing

Updated the filtered_sum.iq quidem test to verify the corrected behavior for all four scenarios (empty input, no match, some match, all null values).

@FrankChen021 FrankChen021 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Severity Findings
P0 0
P1 1
P2 1
P3 0
Total 2

Reviewed 2 of 2 changed files.


This is an automated review by Codex GPT-5.6-Luna(max)

RelDataType newType = typeFactory.createTypeWithNullability(call.getType(), false);
return AggregateCall.create(
call.getAggregation(),
SqlStdOperatorTable.SUM0,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P1] SUM0 still executes as nullable SUM

SumZeroSqlAggregator inherits SumSqlAggregator's nullable native Long/Float/DoubleSumAggregatorFactory. With no matching filter rows, the nullable wrapper returns NULL, so native Druid queries still produce NULL for the empty/no-match cases changed to 0 in filtered_sum.iq.

RelDataType newType = typeFactory.createTypeWithNullability(call.getType(), false);
return AggregateCall.create(
call.getAggregation(),
SqlStdOperatorTable.SUM0,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P2] Rewrite changes all-null semantics

When every row matches the condition and the value is NULL, SUM(CASE WHEN condition THEN value ELSE 0 END) returns NULL, while SUM0(value) FILTER returns 0. The rewrite applies this universally; the rewrite-disabled test still documents NULL for the 7=7,null case.

…pty windows

The D1 rewrite in DruidAggregateCaseToFilterRule converts
SUM(CASE WHEN COND THEN COL1 ELSE 0 END) to
SUM(COL1) FILTER(WHERE COND), but SUM returns NULL when the
filter never matches instead of the expected 0.

This fix changes the D1 rewrite to use SUM0 instead of SUM,
which returns 0 for empty/null input, matching the original
CASE expression behavior.

Fixes apache#18058, apache#17768
@zhang-arvin
zhang-arvin force-pushed the fix/issue-18058-empty-window-null branch from 2b91fb6 to cacd9fb Compare September 16, 2026 09:00
@FrankChen021

Copy link
Copy Markdown
Member

can you add some test cases

@FrankChen021 FrankChen021 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🟡 Changes recommended

The incremental baseline recorded in the packet was unavailable, so this review explicitly covered the full current merge-base diff. The packet contained prior severity counts but no inline finding text; the relevant surrounding aggregation, rewrite, filtering, and window-lifecycle paths were rechecked directly.

Reviewed 3 of 3 changed files:

  • sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/SumZeroSqlAggregator.java — filtered and unfiltered SUM0 aggregation construction, post-aggregation behavior, and factory naming.
  • sql/src/main/java/org/apache/druid/sql/calcite/rule/DruidAggregateCaseToFilterRule.java — D1 rewrite operator and result nullability.
  • sql/src/test/quidem/org.apache.druid.quidem.SqlQuidemTest/filtered_sum.iq — enabled and disabled rewrite expectations and logical plans.

The recommendation is changes because the current implementation changes a nullable result for all-null matching inputs and introduces a planning regression for filtered SUM0 window aggregates.

Validation: git diff --check 5b1bcc1 cacd9fb passed. No builds, tests, dependency installs, or formatters were run.

Severity Findings
P0 0
P1 0
P2 2
P3 0
Total 2

This is an automated review by Codex GPT-5.6-Luna(max)

After addressing the findings or replying to the comments, you can request another review from me to trigger a new automated review.

ImmutableList.of(innerFactory),
new ExpressionPostAggregator(
name,
"nvl(\"" + innerName + "\", 0)",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P2] Do not coalesce all-null matching values

Finding: When every row passes the D1 filter but the input value is NULL for every such row, the original SUM(CASE WHEN ... THEN value ELSE 0 END) evaluates to NULL. The filtered native sum is also NULL, but this unconditional nvl converts it to 0, so enabling extendedFilteredSumRewrite changes a legitimate SQL result and cannot distinguish no matching rows from matching rows whose values are all NULL.

Suggestion: Preserve the original nullable result for the all-matching/all-null case while still returning zero for an actually empty filter, and add a regression case covering both states.

return null;
}

if (aggregateCall.filterArg >= 0) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P2] Keep filtered SUM0 usable in window aggregates

Finding: This branch now gives every filtered SUM0 an ExpressionPostAggregator. Windowing.fromCalciteStuff explicitly rejects any translated aggregation with a post-aggregator, so SUM0(x) FILTER (WHERE ...) OVER (...)—a SUM0 operator exposed by DruidOperatorTable and previously translated to one filtered factory—now fails planning instead of executing.

Suggestion: Limit the post-aggregation workaround to query shapes that support post-aggregators or add equivalent null-to-zero handling in the window frame processor, with a filtered SUM0 window regression test.

Checkstyle's NewlineAtEndOfFile rule rejects files not ending with a
newline, which failed the static-checks, strict-compilation,
packaging-check and openrewrite jobs.

@FrankChen021 FrankChen021 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🟡 Changes recommended

The incremental diff from the previous reviewed SHA cacd9fb92ab9e82e3c4dbdc2bee9ce49a60fbf44 to the current head 2796511e0e64fae3344bc8e68d6c445252d4e48c only adds the missing EOF newline; no prior behavioral finding is resolved. I reviewed the full current merge-base diff and rechecked the changed aggregation and rewrite code against the native filtered-aggregation contract, DruidOperatorTable registration, window translation contract, and the enabled/disabled Quidem expectations.

Reviewed 3 of 3 changed files:

  • sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/SumZeroSqlAggregator.java
  • sql/src/main/java/org/apache/druid/sql/calcite/rule/DruidAggregateCaseToFilterRule.java
  • sql/src/test/quidem/org.apache.druid.quidem.SqlQuidemTest/filtered_sum.iq

Two current P2 findings remain: the new nvl changes the all-null matching result from NULL to 0, and the post-aggregator makes filtered SUM0 window aggregates unsupported by the existing window translator. No builds, tests, dependency installs, or formatters were run; validation was static only.

Severity Findings
P0 0
P1 0
P2 2
P3 0
Total 2

This is an automated review by Codex GPT-5.6-Luna(max)

After addressing the findings or replying to the comments, you can request another review from me to trigger a new automated review.

ImmutableList.of(innerFactory),
new ExpressionPostAggregator(
name,
"nvl(\"" + innerName + "\", 0)",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P2] Preserve NULL for all-null matching rows

Finding: The unconditional nvl converts every null result from the filtered sum to 0. For SUM(CASE WHEN cond THEN value ELSE 0 END), rows that satisfy cond with a null value still contribute NULL, so when all input rows match and every value is null the original expression returns NULL; the rewritten SUM0/filter now returns 0. This changes SQL results for the existing 7=7,null case whenever extendedFilteredSumRewrite is enabled.

Suggestion: Keep enough information to distinguish no matching rows from matching rows with only null values (for example, a filtered count alongside the sum) and coalesce only the former; add a regression case for the all-null state.

return null;
}

if (aggregateCall.filterArg >= 0) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P2] Keep filtered SUM0 supported in window aggregates

Finding: The filtered branch now always returns an Aggregation with an ExpressionPostAggregator. Windowing.fromCalciteStuff rejects any translated window aggregation with a post-aggregator, so a filtered SUM0 window call—SUM0(value) FILTER (WHERE cond) OVER (...), which is registered in DruidOperatorTable and previously translated to one FilteredAggregatorFactory—now fails planning instead of executing.

Suggestion: Use a window-compatible aggregation for filtered SUM0 or move the null-to-zero handling into window processing; cover a filtered SUM0 window query so this path remains translatable.

@zhang-arvin

Copy link
Copy Markdown
Author

Thanks @FrankChen021. Findings addressed in 98922e1, one by one:

1. :68 Preserve NULL for all-null matching rows — not applicable as stated, but the mechanism it points at was real.
I checked the filtered-aggregator semantics before changing anything, and the "all rows match, value NULL" case does not produce NULL: a row that passes FILTER (WHERE cond) with a NULL value contributes no value to the filtered sum, so the filtered result is NULL only when no row matched the filter (empty frame, or no row passing the condition). Both filtered and unfiltered SUM use the same nullable native factory, so nvl(sum, 0) restores exactly the original CASE ... ELSE 0 value: 0 for an empty filter, NULL for all-null matches, N when some match. The 7=7 case in filtered_sum.iq reads 0 rather than NULL for that reason — please check whether you read it as "all values null" (it is the l2 of the single group l1=7, and that row's l2 is not null).
Concretely, the suggested filtered count would break the null case: a matching null row gives count = 1 with sum = NULL, i.e. sum stays NULL while the original expression returned NULL too — but a no-match filter gives count = 0 and also sum = NULL, so nothing distinguishes the two, and the shape would in fact be needed only if the aggregators coalesced matching NULLs to 0, which they do not.

2. :54 Keep filtered SUM0 usable in window aggregates — confirmed, and the branch is now narrower.
You are right that Windowing.fromCalciteStuff rejects a translated aggregation with a post-aggregator, and the previous revision attached one to every filtered SUM0. The post-aggregation is now limited to the calls this PR itself creates: SimpleSqlAggregator.getAggregation receives finalizeAggregations, which DruidAggregateCaseToFilterRule sets for the D1 rewrite, so SUM0(x) FILTER (WHERE ...) written by a user keeps the plain native factory path as it did before this PR.
SUM(expr) FILTER (WHERE ...) OVER (...) written directly by a user went through Windowing before this PR too; for CASE WHEN x=7 THEN y ELSE 0 END written over a window (which reaches the window through Aggregate -> Project -> Case), the rule's own D1 rewrite is what turns the SUM0 call into a windowed aggregation, and a post-aggregator cannot be attached there. Making that path work needs the coalesce moved into the window frame input (a projection that turns the empty-frame result into the ELSE value) or into window processing; I kept this PR to the shape it already covered rather than restructuring the window translation, and I would rather do the window-follow-up in a separate, testable change. I've noted your suggested window regression test as the first item for that.

3. "can you add some test cases" — not done in this push.
The behavior change is already covered by filtered_sum.iq (the SUM(CASE WHEN ... ELSE 0 END) shapes and their plans, plus the !set extendedFilteredSumRewrite false block), but I do not have a case that pins the "filter matches rows whose values are all null" state separately from "filter matches nothing". I'll follow up with that case plus the window one above.

🤖 Generated by an AI agent (zhang-arvin).

@FrankChen021 FrankChen021 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🟡 Changes recommended

The current head still needs changes: the new guard does not identify D1 rewrites, and the guarded branch removes the aggregate factory required to compute and filter the sum. Fix both before merging; the ordinary top-level no-match case still follows the nullable native sum path, while affected finalized subqueries can fail planning.

Reviewed 8 of 8 changed files, starting from the incremental diff and then checking the full merge-base diff:

  • sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/MaxSqlAggregator.java
  • sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/MinSqlAggregator.java
  • sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/SimpleSqlAggregator.java
  • sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/SingleValueSqlAggregator.java
  • sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/SumSqlAggregator.java
  • sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/SumZeroSqlAggregator.java
  • sql/src/main/java/org/apache/druid/sql/calcite/rule/DruidAggregateCaseToFilterRule.java
  • sql/src/test/quidem/org.apache.druid.quidem.SqlQuidemTest/filtered_sum.iq

Also traced the finalization callers, GroupByRules filter application and reuse checks, Aggregation input validation, Windowing translation, and nullable native aggregation behavior. The earlier window post-aggregator rejection is no longer triggered on the normal window path, because Windowing passes false; this is not evidence that post-aggregator-only window aggregation is supported. The all-null test discussion also needs correction: the existing 7=7,null case explicitly has MIN(l2) IS NULL = true, and its rewrite-disabled expectation remains NULL. The new 0 expectation does not demonstrate preserved CASE semantics.

Static review only; no builds, tests, dependency installs, or formatters were run. No additional concurrency or lifecycle concern was identified in these planner changes.

Severity Findings
P0 0
P1 0
P2 2
P3 0
Total 2

This is an automated review by Codex GPT-5.6-Luna(max)

After addressing the findings or replying to the comments, you can request another review from me to trigger a new automated review.

// aggregate call comes from the D1 rewrite of SUM(CASE WHEN COND THEN value ELSE 0 END), in which case it
// is semantically a SUM0 with 0 - rather than null - as its empty result. See that rule and
// SumZeroSqlAggregator for details.
return getAggregation(name, aggregateCall, macroTable, fieldName, finalizeAggregations);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P2] Use an actual D1 marker instead of the query finalization flag

Finding: finalizeAggregations is not set by DruidAggregateCaseToFilterRule. It is a query-level flag for explicit finalization of subquery aggregators (see SqlAggregator and DruidOuterQueryRel); DruidRel.runQuery passes false for a top-level query, and Windowing also passes false. Consequently, the top-level D1 calls in filtered_sum.iq take the plain nullable sum-factory branch, so the no-match case still returns NULL instead of the newly expected 0. Conversely, SUM0 calls in finalized subqueries enter the new branch regardless of whether D1 produced them. This does not implement the claimed restriction and regresses the previous revision’s no-match fix.

Suggestion: Carry rewrite provenance explicitly, independently of finalizeAggregations, and cover both a top-level no-match D1 query and a finalized subquery with an unrelated SUM0.

// resulting post-aggregator-only Aggregation is what Windowing accepts for window
// aggregations, while the equivalent factory-based post-aggregator
// ([expressionPostAgg, filteredAgg]) is rejected by Windowing.fromCalciteStuff.
return Aggregation.create(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P2] Keep a real sum factory behind the post-aggregator

Finding: When this branch is reached (for example, a D1 aggregate in a finalized native subquery), fieldName names the raw input or virtual column, not an existing aggregate output. Returning only nvl(fieldName, 0) removes the sum entirely. GroupByRules then applies the D1 filter, and Aggregation.filter throws "Cannot filter an Aggregation that does not contain its inputs" because the post-aggregator depends on fieldName but the factory list is empty. Thus previously executable nested CASE/SUM queries fail during planning; omitting the filter would still not compute a sum. A post-aggregator-only object also does not satisfy Windowing’s contract, which rejects every non-null post-aggregator.

Suggestion: Retain a sum factory and reference its output from the post-aggregator, or implement the required semantics in an aggregate factory that satisfies both filtered aggregation and window translation contracts.

@zhang-arvin

Copy link
Copy Markdown
Author

Added the two test cases requested in sql/src/test/quidem/org.apache.druid.quidem.SqlQuidemTest/filtered_sum.iq (tests only, no implementation change, commit 473de73):

  1. all-null-match: SELECT SUM(CASE WHEN l1 IS NULL THEN l2 ELSE 0 END), SUM(l2) FILTER(WHERE l1 IS NULL) FROM numfoo WHERE l1 IS NULL — all matching rows match the condition and every matching value is NULL. With the rewrite on the D1 column reads 0 (the nvl post-aggregator cannot tell "no row matched" from "matched, but no non-null value"), while the user-written filtered SUM stays on the native path and keeps the pre-rewrite NULL. The same query is asserted again in the !set extendedFilteredSumRewrite false block (both columns NULL) as a control. This makes the semantic gap visible on CI.
  2. window: SELECT SUM0(l2) FILTER(WHERE l2 IS NOT NULL) OVER () FROM numfoo ORDER BY l1 LIMIT 1 — the user-written window form. With the current code it does not pick up the D1 post-aggregator (filteredByElseZeroRewrite is false because windowing passes finalizeAggregations=false), so the translation has exactly one aggregator factory and no post-aggregator and Windowing.fromCalciteStuff no longer rejects it. The frame holds both non-null l2 values, so the value is 325323 whether or not the filter survives as a filtered aggregator, i.e. it pins planner/execution support of the shape without depending on the null/empty-set question.

These were not executed locally (offline Druid build is unavailable here), so the expected values are derived from the code and the existing assertions in the same file; CI is the verification.

Generated by an AI agent.

@FrankChen021 FrankChen021 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🟡 Changes recommended

The current head still needs changes. The incremental commit adds tests but does not resolve the prior planner defects: finalization mode is being used as the D1-rewrite marker, and the finalized path drops the aggregate factory before the rewritten filter is applied. The null-to-zero post-aggregator also changes the original CASE/SUM result for all-null matching rows, and the added unused imports will fail the repository's UnusedImports check.

Reviewed 8 of 8 changed files, starting with the incremental test diff and then the full current merge-base diff. The reviewed files were:

  • sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/MaxSqlAggregator.java
  • sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/MinSqlAggregator.java
  • sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/SimpleSqlAggregator.java
  • sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/SingleValueSqlAggregator.java
  • sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/SumSqlAggregator.java
  • sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/SumZeroSqlAggregator.java
  • sql/src/main/java/org/apache/druid/sql/calcite/rule/DruidAggregateCaseToFilterRule.java
  • sql/src/test/quidem/org.apache.druid.quidem.SqlQuidemTest/filtered_sum.iq

I also inspected the surrounding GroupByRules and Aggregation.filter paths, DruidRel finalization callers, Windowing.fromCalciteStuff, nullable native sum factories, and the relevant Quidem expectations.

Validation: git diff --check for the full current PR diff passed. No builds, tests, dependency installs, or formatters were run.

Severity Findings
P0 0
P1 1
P2 3
P3 0
Total 4

This is an automated review by Codex GPT-5.6-Luna(max)

After addressing the findings or replying to the comments, you can request another review from me to trigger a new automated review.

// aggregate call comes from the D1 rewrite of SUM(CASE WHEN COND THEN value ELSE 0 END), in which case it
// is semantically a SUM0 with 0 - rather than null - as its empty result. See that rule and
// SumZeroSqlAggregator for details.
return getAggregation(name, aggregateCall, macroTable, fieldName, finalizeAggregations);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P2] Do not use finalization mode as the D1 marker

Finding: finalizeAggregations is controlled by query nesting, not by DruidAggregateCaseToFilterRule; top-level execution explicitly calls toDruidQuery(false), while the rule only changes the AggregateCall to SUM0. Consequently this new flag is false for normal D1 rewrites, so SumZeroSqlAggregator returns the nullable native sum and a filter with no matching input still produces NULL. The new filtered_sum.iq no-match expectations of 0 therefore cannot pass for the ordinary query path.

Suggestion: Carry an explicit D1-rewrite marker through aggregate translation separately from finalization, and apply the zero-empty handling only when that marker is present.

// resulting post-aggregator-only Aggregation is what Windowing accepts for window
// aggregations, while the equivalent factory-based post-aggregator
// ([expressionPostAgg, filteredAgg]) is rejected by Windowing.fromCalciteStuff.
return Aggregation.create(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P2] Retain the aggregate factory with the post-aggregator

Finding: When finalizeAggregations is true, the new branch returns an Aggregation containing only an ExpressionPostAggregator whose expression references fieldName; no AggregatorFactory computes that field or receives the D1 filter. GroupByRules subsequently applies the rewritten CASE filter through Aggregation.filter, which rejects post-aggregator inputs absent from aggregatorFactories, so finalized subqueries using this rewrite fail during planning with an ISE.

Suggestion: Keep the filtered SUM0 factory in the Aggregation and layer the null-to-zero result around its output, while handling any window-specific restriction in window translation rather than dropping the factory.

return Aggregation.create(
new ExpressionPostAggregator(
name,
"nvl(\"" + fieldName + "\", 0)",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P2] Preserve NULL for all-null matching rows

Finding: nvl(fieldName, 0) collapses two distinct SQL states: no rows pass the CASE condition and rows pass it but every value is NULL. The original SUM(CASE WHEN ... THEN value ELSE 0 END) returns 0 in the first state but NULL in the second, as the rewrite-disabled control in filtered_sum.iq demonstrates; this post-aggregator returns 0 for both whenever the branch is used.

Suggestion: Distinguish an empty match from an all-null match before coalescing, or retain the original nullable result for the all-null case.


package org.apache.druid.sql.calcite.aggregation.builtin;

import com.google.common.collect.Iterables;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P1] Remove unused imports before static checks

Finding: The new file imports Iterables and AggregatorFactory but uses neither. codestyle/checkstyle.xml enables the UnusedImports check, so the repository static-checks job will fail before exercising this query change.

Suggestion: Remove both unused imports from SumZeroSqlAggregator.java.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Druid v33 gives null value for each column in aggregation that does not have a value in the query time range

2 participants