[ 
https://issues.apache.org/jira/browse/CALCITE-7574?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

leishp updated CALCITE-7574:
----------------------------
    Description: 
`RelDecorrelator.isFieldNotNullRecursive` throws `IndexOutOfBoundsException` 
when decorrelating a correlated scalar subquery that involves an Aggregate. The 
bug was introduced in CALCITE-6962 which added the 
`isFieldNotNull`/`isFieldNotNullRecursive` methods.

### Root Cause

In `RelDecorrelator.isFieldNotNullRecursive`, the Aggregate branch at line 3856:

 

```java
} else if (rel instanceof Aggregate) {
    Aggregate agg = (Aggregate) rel;
    ImmutableBitSet groupSet = agg.getGroupSet();
    if (index >= groupSet.size())

{  // BUG: should be agg.getGroupCount()         return false;     }

    return isFieldNotNullRecursive(agg.getInput(), 
groupSet.asList().get(index));
}
```

 
 - `ImmutableBitSet.size()` returns the bitset {*}{{*}}capacity{{*}}{*} 
(`words.length * 64`), typically 64 or more
 - `groupSet.asList().get(index)` internally calls `nth(index)`, which requires 
`index < cardinality()`
 - `Aggregate.getGroupCount()` returns `groupSet.cardinality()`, the actual 
number of group keys

When a field index corresponds to an aggregate result field (not a group 
field), the bounds check `index >= groupSet.size()` is too lenient (e.g., `1 >= 
64` is false), allowing execution to proceed to `nth(1)` which throws 
`IndexOutOfBoundsException` because the bitset has only 1 set bit.
 # 
 ## 
 ### Reproduction SQL

```sql
select t1.deptno, t1.total_sal,
  (select count(distinct t2.total_sal) + 1
   from (select deptno, sum(sal) as total_sal
         from emp where deptno is not null
         group by deptno) t2
   where t2.deptno = t1.deptno
     and t2.total_sal > t1.total_sal) as rank_sal
from (select deptno, sum(sal) as total_sal
      from emp where deptno is not null
      group by deptno) t1
order by t1.deptno, t1.total_sal desc
```

### Exception



```
java.lang.IndexOutOfBoundsException: index out of range: 1
    at org.apache.calcite.util.ImmutableBitSet.nth(ImmutableBitSet.java:903)
    at org.apache.calcite.util.ImmutableBitSet$2.get(ImmutableBitSet.java:681)
    at org.apache.calcite.util.ImmutableBitSet$2.get(ImmutableBitSet.java:679)
    at 
org.apache.calcite.sql2rel.RelDecorrelator.isFieldNotNullRecursive(RelDecorrelator.java:3859)
    at 
org.apache.calcite.sql2rel.RelDecorrelator.isFieldNotNullRecursive(RelDecorrelator.java:3851)
    at 
org.apache.calcite.sql2rel.RelDecorrelator.isFieldNotNullRecursive(RelDecorrelator.java:3859)
    at 
org.apache.calcite.sql2rel.RelDecorrelator.isFieldNotNullRecursive(RelDecorrelator.java:3872)
    at 
org.apache.calcite.sql2rel.RelDecorrelator.isFieldNotNullRecursive(RelDecorrelator.java:3851)
    at 
org.apache.calcite.sql2rel.RelDecorrelator.isFieldNotNull(RelDecorrelator.java:3840)
    at 
org.apache.calcite.sql2rel.RelDecorrelator.decorrelateRel(RelDecorrelator.java:1902)
    ...
```

##

  was:
`RelDecorrelator.isFieldNotNullRecursive` throws `IndexOutOfBoundsException` 
when decorrelating a correlated scalar subquery that involves an Aggregate. The 
bug was introduced in [CALCITE-6962] which added the 
`isFieldNotNull`/`isFieldNotNullRecursive` methods.

### Root Cause

In `RelDecorrelator.isFieldNotNullRecursive`, the Aggregate branch at line 3856:

```java
} else if (rel instanceof Aggregate) {
    Aggregate agg = (Aggregate) rel;
    ImmutableBitSet groupSet = agg.getGroupSet();
    if (index >= groupSet.size())

{  // BUG: should be agg.getGroupCount()         return false;     }

    return isFieldNotNullRecursive(agg.getInput(), 
groupSet.asList().get(index));
}
```
 - `ImmutableBitSet.size()` returns the bitset *{*}capacity{*}* (`words.length 
* 64`), typically 64 or more
 - `groupSet.asList().get(index)` internally calls `nth(index)`, which requires 
`index < cardinality()`
 - `Aggregate.getGroupCount()` returns `groupSet.cardinality()`, the actual 
number of group keys

When a field index corresponds to an aggregate result field (not a group 
field), the bounds check `index >= groupSet.size()` is too lenient (e.g., `1 >= 
64` is false), allowing execution to proceed to `nth(1)` which throws 
`IndexOutOfBoundsException` because the bitset has only 1 set bit.

### Reproduction SQL

```sql
select t1.deptno, t1.total_sal,
  (select count(distinct t2.total_sal) + 1
   from (select deptno, sum(sal) as total_sal
         from emp where deptno is not null
         group by deptno) t2
   where t2.deptno = t1.deptno
     and t2.total_sal > t1.total_sal) as rank_sal
from (select deptno, sum(sal) as total_sal
      from emp where deptno is not null
      group by deptno) t1
order by t1.deptno, t1.total_sal desc
```

### Exception

```
java.lang.IndexOutOfBoundsException: index out of range: 1
    at org.apache.calcite.util.ImmutableBitSet.nth(ImmutableBitSet.java:903)
    at org.apache.calcite.util.ImmutableBitSet$2.get(ImmutableBitSet.java:681)
    at org.apache.calcite.util.ImmutableBitSet$2.get(ImmutableBitSet.java:679)
    at 
org.apache.calcite.sql2rel.RelDecorrelator.isFieldNotNullRecursive(RelDecorrelator.java:3859)
    at 
org.apache.calcite.sql2rel.RelDecorrelator.isFieldNotNullRecursive(RelDecorrelator.java:3851)
    at 
org.apache.calcite.sql2rel.RelDecorrelator.isFieldNotNullRecursive(RelDecorrelator.java:3859)
    at 
org.apache.calcite.sql2rel.RelDecorrelator.isFieldNotNullRecursive(RelDecorrelator.java:3872)
    at 
org.apache.calcite.sql2rel.RelDecorrelator.isFieldNotNullRecursive(RelDecorrelator.java:3851)
    at 
org.apache.calcite.sql2rel.RelDecorrelator.isFieldNotNull(RelDecorrelator.java:3840)
    at 
org.apache.calcite.sql2rel.RelDecorrelator.decorrelateRel(RelDecorrelator.java:1902)
    ...
```

##


> RelDecorrelator.isFieldNotNullRecursive throws IndexOutOfBoundsException when 
> decorrelating correlated scalar subquery with Aggregate
> -------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: CALCITE-7574
>                 URL: https://issues.apache.org/jira/browse/CALCITE-7574
>             Project: Calcite
>          Issue Type: Bug
>          Components: core
>    Affects Versions: 1.41.0, 1.42.0
>            Reporter: leishp
>            Priority: Major
>
> `RelDecorrelator.isFieldNotNullRecursive` throws `IndexOutOfBoundsException` 
> when decorrelating a correlated scalar subquery that involves an Aggregate. 
> The bug was introduced in CALCITE-6962 which added the 
> `isFieldNotNull`/`isFieldNotNullRecursive` methods.
> ### Root Cause
> In `RelDecorrelator.isFieldNotNullRecursive`, the Aggregate branch at line 
> 3856:
>  
> ```java
> } else if (rel instanceof Aggregate) {
>     Aggregate agg = (Aggregate) rel;
>     ImmutableBitSet groupSet = agg.getGroupSet();
>     if (index >= groupSet.size())
> {  // BUG: should be agg.getGroupCount()         return false;     }
>     return isFieldNotNullRecursive(agg.getInput(), 
> groupSet.asList().get(index));
> }
> ```
>  
>  - `ImmutableBitSet.size()` returns the bitset {*}{{*}}capacity{{*}}{*} 
> (`words.length * 64`), typically 64 or more
>  - `groupSet.asList().get(index)` internally calls `nth(index)`, which 
> requires `index < cardinality()`
>  - `Aggregate.getGroupCount()` returns `groupSet.cardinality()`, the actual 
> number of group keys
> When a field index corresponds to an aggregate result field (not a group 
> field), the bounds check `index >= groupSet.size()` is too lenient (e.g., `1 
> >= 64` is false), allowing execution to proceed to `nth(1)` which throws 
> `IndexOutOfBoundsException` because the bitset has only 1 set bit.
>  # 
>  ## 
>  ### Reproduction SQL
> ```sql
> select t1.deptno, t1.total_sal,
>   (select count(distinct t2.total_sal) + 1
>    from (select deptno, sum(sal) as total_sal
>          from emp where deptno is not null
>          group by deptno) t2
>    where t2.deptno = t1.deptno
>      and t2.total_sal > t1.total_sal) as rank_sal
> from (select deptno, sum(sal) as total_sal
>       from emp where deptno is not null
>       group by deptno) t1
> order by t1.deptno, t1.total_sal desc
> ```
> ### Exception
> ```
> java.lang.IndexOutOfBoundsException: index out of range: 1
>     at org.apache.calcite.util.ImmutableBitSet.nth(ImmutableBitSet.java:903)
>     at org.apache.calcite.util.ImmutableBitSet$2.get(ImmutableBitSet.java:681)
>     at org.apache.calcite.util.ImmutableBitSet$2.get(ImmutableBitSet.java:679)
>     at 
> org.apache.calcite.sql2rel.RelDecorrelator.isFieldNotNullRecursive(RelDecorrelator.java:3859)
>     at 
> org.apache.calcite.sql2rel.RelDecorrelator.isFieldNotNullRecursive(RelDecorrelator.java:3851)
>     at 
> org.apache.calcite.sql2rel.RelDecorrelator.isFieldNotNullRecursive(RelDecorrelator.java:3859)
>     at 
> org.apache.calcite.sql2rel.RelDecorrelator.isFieldNotNullRecursive(RelDecorrelator.java:3872)
>     at 
> org.apache.calcite.sql2rel.RelDecorrelator.isFieldNotNullRecursive(RelDecorrelator.java:3851)
>     at 
> org.apache.calcite.sql2rel.RelDecorrelator.isFieldNotNull(RelDecorrelator.java:3840)
>     at 
> org.apache.calcite.sql2rel.RelDecorrelator.decorrelateRel(RelDecorrelator.java:1902)
>     ...
> ```
> ##



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to