Unparser turns NULL list literals into `[]` and fails on NULL struct/map literals
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 2/5
- Tempo stimato
- 1-3 ore
- Idoneità per principianti
- 72/100
Direzione di ricerca
Inizia da Unparser::scalar_to_sql in datafusion/sql/src/unparser/expr.rs. I rami List (circa righe 1450-1454) generano i valori figli senza controllare il null, e i rami Struct, Map e Union (circa righe 1579-1581) restituiscono not_impl_err! anche per i null. Riproduci con le due query CASE della issue, poi verifica che i null tipizzati come Int32(None) vengano già convertiti in NULL. Completato quando i letterali NULL di tipi annidati vengono convertiti in NULL e i test esistenti dell'unparser di datafusion-sql passano ancora.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Describe the bug
The SQL unparser mishandles NULL literals of nested types:
- A NULL list literal unparses as
[], so the generated SQL returns an empty array where the plan returns NULL, with no error. - A NULL struct, map, or union literal fails with
NotImplemented("Unsupported scalar: ...").
Optimized plans contain these literals whenever type coercion casts a NULL to a nested type and constant folding turns the cast into a literal, e.g. CASE WHEN ... THEN NULL ELSE list_col END or ... UNION ALL SELECT NULL.
The cause is in Unparser::scalar_to_sql. The list arms render the scalar's child values without checking whether the list itself is null (unparser/expr.rs#L1450-L1454), and the Struct/Map/Union arms return not_impl_err! even for nulls (unparser/expr.rs#L1579-L1581). Every other typed null, such as Int32(None) or DurationSecond(None), unparses as NULL.
To Reproduce
use datafusion::error::Result;
use datafusion::prelude::*;
use datafusion::sql::unparser::plan_to_sql;
#[tokio::main]
async fn main() -> Result<()> {
let ctx = SessionContext::new();
ctx.sql("CREATE TABLE t AS SELECT 1 AS a, [1, 2] AS l, named_struct('x', 1) AS s")
.await?;
for sql in [
"SELECT CASE WHEN a > 0 THEN NULL ELSE l END AS c FROM t",
"SELECT CASE WHEN a > 0 THEN NULL ELSE s END AS c FROM t",
] {
let plan = ctx.sql(sql).await?.into_optimized_plan()?;
println!("{:?}", plan_to_sql(&plan).map(|s| s.to_string()));
}
Ok(())
}
Output:
Ok("SELECT CASE WHEN (t.a > 0) THEN [] ELSE t.l END AS c FROM t")
Err(NotImplemented("Unsupported scalar: Struct({x:})"))
Running the first unparsed query returns [] where the original returns NULL. SELECT l FROM t UNION ALL SELECT NULL FROM t similarly unparses to ... UNION ALL SELECT [] AS l FROM t. Map columns fail the same way as struct columns.
Expected behavior
NULL nested-type literals unparse as NULL, like other typed nulls, e.g. CASE WHEN (t.a > 0) THEN NULL ELSE t.l END.
A guard arm at the top of scalar_to_sql is enough:
ScalarValue::List(_)
| ScalarValue::LargeList(_)
| ScalarValue::FixedSizeList(_)
| ScalarValue::ListView(_)
| ScalarValue::LargeListView(_)
| ScalarValue::Struct(_)
| ScalarValue::Map(_)
| ScalarValue::Union(..)
if v.is_null() =>
{
Ok(ast::Expr::value(ast::Value::Null))
}
With this arm, the queries above, the UNION ALL case, and the map case all unparse to SQL that returns the same results as the original query, and the existing datafusion-sql tests still pass.
Additional context
- Non-null struct and map literals, such as a constant-folded
named_struct('x', 1), also fail withUnsupported scalar. Rendering those needs dialect-specific syntax, so they are out of scope here. - Once #26311 merges,
CASE WHEN a > 0 THEN s ELSE NULL ENDfails the same way. Until then the unparser drops the failing ELSE, which happens to give the same result forELSE NULL. - Line references are pinned to commit 0d79252f61bd.
- Lingua principale
- Rust
- Stelle
- 9.4k
- Fork
- 2.5k
- Merge medio
- 4g 9h
- PR unite (30g)
- 329
Preparare l'ambiente
Avvia il container di sviluppo del progetto nel browser, con il tuo account GitHub.
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di apache/datafusion
-
Deduplicate bucket computation in width_bucket_interval_mdn_exactForse già presa @nightcityblade l’ha presa oggi. Apertaenhancement good first issue help wanted spark tech debt
Difficoltà 2/5 1-3 ore Idoneità per principianti 85/100
apache/datafusion#26307 · 3 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
Use the shared aggr_test_schema in CSV scan tests instead of a local copyForse già presa @med9110 l’ha presa oggi. Apertacore enhancement good first issue help wanted tech debt
Difficoltà 2/5 1-3 ore Idoneità per principianti 85/100
apache/datafusion#26306 · 2 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
gcd benchmarks pass number_rows: 0 for 100k-row inputsForse già presa @med9110 l’ha presa oggi. Apertabug functions good first issue help wanted tech debt
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 92/100
apache/datafusion#26297 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Struct union type coercion takes field nullability from the wrong field when field order differsForse già presa @med9110 l’ha presa oggi. Apertabug help wanted logical-expr tech debt
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
apache/datafusion#26296 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
apache/datafusion#26290 ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di apache/datafusion
Issue simili
-
RUSTSEC-2026-0348: Brotli certificate decompression can allocate up to 1 GiB before authenticationAperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 60/100
-
Difficoltà 1/5 1-3 ore Idoneità per principianti 62/100
-
core: Amount::from_str panics on values above 21M BTC instead of returning an errorForse già presa @bhuvan-somisetty l’ha presa oggi. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
I maintainer di solito rispondono entro 1 giorno
-
crate: spate-s3
Difficoltà 2/5 Mezza giornata Idoneità per principianti 62/100
I maintainer di solito rispondono entro 1 giorno
-
write_iceberg ignores the table's write.parquet.compression-codec propertyForse già presa @YuangGao l’ha presa oggi. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
Eventual-Inc/Daft#7606 ·
I maintainer di solito rispondono entro 1 giorno