Commit 4798476
authored
fix: account for empty scalar subqueries in nullability (#24516)
## Which issue does this PR close?
- Closes #24513.
## Rationale for this change
A scalar subquery that returns zero rows evaluates to `NULL`, even when
its projected expression is non-nullable. DataFusion currently derives
`Expr::ScalarSubquery` nullability from the subquery's output field.
This can produce a non-nullable output schema containing `NULL`, and can
cause `SimplifyExpressions` to incorrectly fold predicates such as:
```sql
SELECT (SELECT 1 WHERE FALSE) IS NULL;
```
to `false`.
This change deliberately marks all scalar subqueries nullable, including
those guaranteed to return exactly one row (such as an ungrouped
aggregate like `(SELECT count(*) FROM t)`). This is a conservative
trade-off that gives up some nullability precision for correctness, and
is consistent with how PostgreSQL treats scalar subqueries.
A possible follow-up refinement is a `LogicalPlan::min_rows()` lower
bound (mirroring the existing `max_rows()`), which would let
uncorrelated scalar subqueries provably returning at least one row keep
their projected field's nullability.
## What changes are included in this PR?
Scalar subqueries are conservatively marked nullable in logical
expression schema derivation and physical expression planning. The
projected field's data type, name, and metadata are preserved.
## Are these changes tested?
Yes. Unit tests cover logical schema derivation and expression
simplification, and SQLLogicTests cover both zero-row execution and `IS
NULL` correctness.
The full workspace test suite and Clippy with warnings denied pass.
## Are there any user-facing changes?
Yes. Zero-row scalar subqueries with non-nullable projections now return
`NULL` without a schema validation error, and `IS NULL` predicates
produce the correct result.
In addition, output schemas containing scalar subqueries now always mark
those fields as nullable, even for subqueries that can never produce
`NULL`. Downstream consumers that inspect schema nullability can observe
this change. No public APIs change.
---
AI usage: Created with Claude Code and Opus 5. I have reviewed the code
and made modifications where it made sense.
---------
Signed-off-by: Fredrik Fornwall <fredrik@fornwall.net>1 parent 858d3bf commit 4798476
4 files changed
Lines changed: 80 additions & 9 deletions
File tree
- datafusion
- expr/src
- optimizer/src/simplify_expressions
- physical-expr/src
- sqllogictest/test_files
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
369 | 369 | | |
370 | 370 | | |
371 | 371 | | |
372 | | - | |
373 | | - | |
374 | | - | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
375 | 375 | | |
376 | 376 | | |
377 | 377 | | |
| |||
517 | 517 | | |
518 | 518 | | |
519 | 519 | | |
520 | | - | |
521 | | - | |
522 | | - | |
| 520 | + | |
| 521 | + | |
| 522 | + | |
| 523 | + | |
| 524 | + | |
| 525 | + | |
| 526 | + | |
| 527 | + | |
| 528 | + | |
523 | 529 | | |
524 | 530 | | |
525 | 531 | | |
| |||
800 | 806 | | |
801 | 807 | | |
802 | 808 | | |
803 | | - | |
| 809 | + | |
804 | 810 | | |
805 | 811 | | |
806 | 812 | | |
| |||
1268 | 1274 | | |
1269 | 1275 | | |
1270 | 1276 | | |
| 1277 | + | |
| 1278 | + | |
| 1279 | + | |
| 1280 | + | |
| 1281 | + | |
| 1282 | + | |
| 1283 | + | |
| 1284 | + | |
| 1285 | + | |
| 1286 | + | |
| 1287 | + | |
| 1288 | + | |
| 1289 | + | |
| 1290 | + | |
| 1291 | + | |
| 1292 | + | |
| 1293 | + | |
1271 | 1294 | | |
1272 | 1295 | | |
1273 | 1296 | | |
| |||
Lines changed: 15 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3788 | 3788 | | |
3789 | 3789 | | |
3790 | 3790 | | |
| 3791 | + | |
| 3792 | + | |
| 3793 | + | |
| 3794 | + | |
| 3795 | + | |
| 3796 | + | |
| 3797 | + | |
| 3798 | + | |
| 3799 | + | |
| 3800 | + | |
| 3801 | + | |
| 3802 | + | |
| 3803 | + | |
| 3804 | + | |
| 3805 | + | |
3791 | 3806 | | |
3792 | 3807 | | |
3793 | 3808 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
536 | 536 | | |
537 | 537 | | |
538 | 538 | | |
539 | | - | |
540 | 539 | | |
541 | 540 | | |
542 | | - | |
| 541 | + | |
| 542 | + | |
543 | 543 | | |
544 | 544 | | |
545 | 545 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2010 | 2010 | | |
2011 | 2011 | | |
2012 | 2012 | | |
| 2013 | + | |
| 2014 | + | |
| 2015 | + | |
| 2016 | + | |
| 2017 | + | |
| 2018 | + | |
| 2019 | + | |
| 2020 | + | |
| 2021 | + | |
| 2022 | + | |
| 2023 | + | |
| 2024 | + | |
| 2025 | + | |
| 2026 | + | |
| 2027 | + | |
| 2028 | + | |
| 2029 | + | |
| 2030 | + | |
2013 | 2031 | | |
2014 | 2032 | | |
2015 | 2033 | | |
| |||
2350 | 2368 | | |
2351 | 2369 | | |
2352 | 2370 | | |
| 2371 | + | |
| 2372 | + | |
| 2373 | + | |
| 2374 | + | |
| 2375 | + | |
| 2376 | + | |
| 2377 | + | |
| 2378 | + | |
| 2379 | + | |
| 2380 | + | |
| 2381 | + | |
| 2382 | + | |
| 2383 | + | |
| 2384 | + | |
| 2385 | + | |
2353 | 2386 | | |
2354 | 2387 | | |
2355 | 2388 | | |
| |||
0 commit comments