Skip to content

branch-4.1: [fix](aggregate) Normalize projected count slots before null safety checks #67732 - #67767

Closed
github-actions[bot] wants to merge 1 commit into
branch-4.1from
auto-pick-67732-branch-4.1
Closed

branch-4.1: [fix](aggregate) Normalize projected count slots before null safety checks #67732#67767
github-actions[bot] wants to merge 1 commit into
branch-4.1from
auto-pick-67732-branch-4.1

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

Cherry-picked from #67732

…hecks (#67732)

## Problem

Counting a projected alias over a nullable indexed column can return an
incorrect non-zero result when the filter retains only null rows. Mixing
the alias count with `COUNT(*)` or another count exposes the problem:

```sql
SELECT COUNT(x), COUNT(*)
FROM (SELECT k AS x FROM t WHERE k IS NULL) q;
```

For two matching null rows, the correct result is `(0, 2)`, but the
storage-layer index-count path can return `(2, 2)`.

## Root cause

The FE implementation rule validates `IS NULL` and OR predicates before
pushing count aggregation to the storage layer. In the Project variant,
this validation used the aggregate-side alias slot, while the filter
below the Project refers to the source slot. Their expression IDs
differ, so the null-safety guard did not recognize that the filter and
`COUNT` referenced the same nullable value.

The rule normalized the aggregate argument to the source slot only
later, after the safety decision had already been made.

## Reproduction

```sql
CREATE TABLE t (
    id INT NOT NULL,
    k INT NULL,
    INDEX idx_k (k) USING INVERTED
)
DUPLICATE KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS 1
PROPERTIES ("replication_num" = "1");

INSERT INTO t VALUES (1, NULL), (2, NULL), (3, 1);

SELECT COUNT(x), COUNT(*)
FROM (SELECT k AS x FROM t WHERE k IS NULL) q;

SELECT COUNT(x), COUNT(id)
FROM (SELECT k AS x, id FROM t WHERE k IS NULL) q;
```

Before this change, both queries return `(2, 2)` and the plan contains
`pushAggOp=COUNT_ON_INDEX`. Both queries should return `(0, 2)`.

## Fix

Normalize aggregate arguments through the Project before collecting the
slots used by the predicate safety checks. The count slots and filter
slots are now compared in the same source expression-ID domain. If `IS
NULL` targets a counted source slot, the FE rejects the index-count
pushdown and preserves the column's null values.

This change is limited to the FE planner.

## Tests

- Added a FE plan test for `COUNT(projected_alias) + COUNT(*)` above an
`IS NULL` filter, verifying that the count-on-index implementation rule
is rejected.
- Ran `PhysicalStorageLayerAggregateTest`: 7 tests passed.
- Deployed the FE to a local sandbox and reran both SQL reproductions.
They return `(0, 2)`, and the scan plan reports `pushAggOp=NONE`.
@github-actions
github-actions Bot requested a review from yiguolei as a code owner September 10, 2026 04:35
@hello-stephen

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Apache Doris.
Don't know what should be done next? See How to process your PR.

Please clearly describe your PR:

  1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
  2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
  3. What features were added. Why was this function added?
  4. Which code was refactored and why was this part of the code refactored?
  5. Which functions were optimized and what is the difference before and after the optimization?

@hello-stephen

Copy link
Copy Markdown
Contributor

run buildall

@morrySnow morrySnow closed this Sep 10, 2026
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.

2 participants