Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

Unparser turns NULL list literals into `[]` and fails on NULL struct/map literals

Aperta Adatta ai principianti
#26,316 1 commento 0 reazioni 0 assegnatari Vedi su GitHub

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
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
rust, sql
Ambito
databases

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

bug help wanted unparser
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 with Unsupported 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 END fails the same way. Until then the unparser drops the failing ELSE, which happens to give the same result for ELSE 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

Apri in Codespaces

Avvia il container di sviluppo del progetto nel browser, con il tuo account GitHub.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di apache/datafusion

Tutte le issue di apache/datafusion

Issue simili

Altre issue su Rust

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.