[
https://issues.apache.org/jira/browse/CALCITE-7645?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Zhen Chen updated CALCITE-7645:
-------------------------------
Description:
AggregateUnionTransposeRule accepts aggregate calls with FILTER, but in the
child aggregate rebuild path it does not preserve the original
AggregateCall.filterArg.
The normal path copies aggRel.getAggCallList(), so filtered aggregate calls are
preserved. However, when the nullability of an aggregate argument differs
between the UNION output type and a specific UNION input type, the rule
recreates the child AggregateCall to re-evaluate nullability. That recreated
call currently passes filterArg = -1, which drops the aggregate FILTER
predicate.
Reproducer:
{code:java}
select min(v) filter (where p)
from (
select * from (values (10, false), (100, true)) as t(v, p)
union all
select * from (values (cast(null as integer), true), (200, true)) as t(v, p)
) {code}
Expected result:
100
After applying AggregateUnionTransposeRule, the first pushed-down aggregate
becomes MIN($0) instead of MIN($0) FILTER $1. That branch returns 10, and the
top MIN therefore returns 10, which is incorrect.
Observed rewritten plan:
{code:java}
LogicalAggregate(group=[{}], EXPR$0=[MIN($0)])
LogicalUnion(all=[true])
LogicalAggregate(group=[{}], EXPR$0=[MIN($0)])
LogicalProject(V=[$0], P=[$1])
LogicalValues(tuples=[[\{ 10, false }, \{ 100, true }]])
LogicalAggregate(group=[{}], EXPR$0=[MIN($0) FILTER $1])
LogicalProject(V=[$0], P=[$1])
LogicalValues(tuples=[[\{ null, true }, \{ 200, true }]]) {code}
was:
AggregateUnionTransposeRule accepts aggregate calls with FILTER, but in the
child aggregate rebuild path it does not preserve the original
AggregateCall.filterArg.
The normal path copies aggRel.getAggCallList(), so filtered aggregate calls are
preserved. However, when the nullability of an aggregate argument differs
between the UNION output type and a specific UNION input type, the rule
recreates the child AggregateCall to re-evaluate nullability. That recreated
call currently passes filterArg = -1, which drops the aggregate FILTER
predicate.
Reproducer:
select min(v) filter (where p)
from (
select * from (values (10, false), (100, true)) as t(v, p)
union all
select * from (values (cast(null as integer), true), (200, true)) as t(v, p)
)
Expected result:
100
After applying AggregateUnionTransposeRule, the first pushed-down aggregate
becomes MIN($0) instead of MIN($0) FILTER $1. That branch returns 10, and the
top MIN therefore returns 10, which is incorrect.
Observed rewritten plan:
LogicalAggregate(group=[{}], EXPR$0=[MIN($0)])
LogicalUnion(all=[true])
LogicalAggregate(group=[{}], EXPR$0=[MIN($0)])
LogicalProject(V=[$0], P=[$1])
LogicalValues(tuples=[[\{ 10, false }, \{ 100, true }]])
LogicalAggregate(group=[{}], EXPR$0=[MIN($0) FILTER $1])
LogicalProject(V=[$0], P=[$1])
LogicalValues(tuples=[[\{ null, true }, \{ 200, true }]])
> AggregateUnionTransposeRule drops aggregate FILTER when rebuilding child
> aggregate calls
> ----------------------------------------------------------------------------------------
>
> Key: CALCITE-7645
> URL: https://issues.apache.org/jira/browse/CALCITE-7645
> Project: Calcite
> Issue Type: Bug
> Components: core
> Affects Versions: 1.42.0
> Reporter: Zhen Chen
> Assignee: Zhen Chen
> Priority: Major
> Fix For: 1.43.0
>
>
> AggregateUnionTransposeRule accepts aggregate calls with FILTER, but in the
> child aggregate rebuild path it does not preserve the original
> AggregateCall.filterArg.
> The normal path copies aggRel.getAggCallList(), so filtered aggregate calls
> are preserved. However, when the nullability of an aggregate argument differs
> between the UNION output type and a specific UNION input type, the rule
> recreates the child AggregateCall to re-evaluate nullability. That recreated
> call currently passes filterArg = -1, which drops the aggregate FILTER
> predicate.
>
> Reproducer:
> {code:java}
> select min(v) filter (where p)
> from (
> select * from (values (10, false), (100, true)) as t(v, p)
> union all
> select * from (values (cast(null as integer), true), (200, true)) as t(v, p)
> ) {code}
> Expected result:
> 100
> After applying AggregateUnionTransposeRule, the first pushed-down aggregate
> becomes MIN($0) instead of MIN($0) FILTER $1. That branch returns 10, and the
> top MIN therefore returns 10, which is incorrect.
> Observed rewritten plan:
> {code:java}
> LogicalAggregate(group=[{}], EXPR$0=[MIN($0)])
> LogicalUnion(all=[true])
> LogicalAggregate(group=[{}], EXPR$0=[MIN($0)])
> LogicalProject(V=[$0], P=[$1])
> LogicalValues(tuples=[[\{ 10, false }, \{ 100, true }]])
> LogicalAggregate(group=[{}], EXPR$0=[MIN($0) FILTER $1])
> LogicalProject(V=[$0], P=[$1])
> LogicalValues(tuples=[[\{ null, true }, \{ 200, true }]]) {code}
--
This message was sent by Atlassian Jira
(v8.20.10#820010)