Fix stale values in ungrouped aggregate queries with no matching rows#5578
Open
vimalprakashts wants to merge 2 commits intotursodatabase:mainfrom
Open
Fix stale values in ungrouped aggregate queries with no matching rows#5578vimalprakashts wants to merge 2 commits intotursodatabase:mainfrom
vimalprakashts wants to merge 2 commits intotursodatabase:mainfrom
Conversation
When an ungrouped aggregate query (e.g. SELECT x, COUNT(*) FROM t WHERE 0) has no matching rows, non-aggregate columns should return NULL. Previously, btree cursors still pointed at the last scanned row, so Column and IdxRowId instructions returned stale values instead of NULL. Root cause: the NullRow fixup path only nulled coroutine output registers (for CTEs/subqueries) but did not issue NullRow on btree/index cursors. IdxRowId also lacked a null-flag check, so it returned the last rowid. Fix: - Emit NullRow for all btree table and index cursors in the no-rows path - Add null-flag check to IdxRowId so it returns NULL like Column does - Add Cursor::get_null_flag() accessor
a256165 to
b11a2b9
Compare
JavaScript's Number.isInteger(30.0) returns true, so the JS test runner formats SUM(real_col) = 30.0 as "30" instead of "30.0". Add an `expect @js` block to account for this known limitation.
93cb91d to
4bac7f0
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
NullRowfor all btree table and index cursors in the empty-result pathIdxRowIdso it returns NULL (matchingColumnbehavior)Bug
Expected (SQLite):
NULL|0Actual (Turso before fix):
2|0— leaks the last scanned row's valueThe same issue affects index-only scans (
IdxRowIdreturning a stale rowid).Root Cause
The ungrouped aggregation codegen has a "no rows" fallback path that evaluates non-aggregate columns when the scan loop never executed. This path nulled coroutine output registers (for CTEs/subqueries) but did not issue
NullRowon btree/index cursors. TheColumninstruction already checks the null flag, butIdxRowIddid not — so index-based plans also leaked stale rowids.Fix
core/translate/aggregation.rs: In the no-rows fallback, emitNullRowfor every btree table cursor and index cursor (looked up viaresolve_cursor_id_safeto handle covering-index cases where the table cursor isn't allocated).core/vdbe/execute.rs: Add a null-flag check at the top ofop_idx_row_id— if the cursor is in NullRow state, returnNULLimmediately instead of reading a stale rowid.core/types.rs: AddCursor::get_null_flag()accessor.Test Plan
testing/runner/tests/ungrouped-aggregate-nullrow.sqltest— covers table scans, index scans, multi-table joins, CTEs, WHERE-false and WHERE-no-match variantsmake -C testing/runner run-rustpassescargo testpassescargo clippy --workspace --all-features --all-targets -- --deny=warningspasses