Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

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

Abierto Apto para principiantes
#26,316 1 comentario 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
2/5
Tiempo estimado
1-3 horas
Aptitud para principiantes
72/100
Tipo de issue
Error
Claridad
Bien especificado
Estado de actividad
Activo
Stack tecnológico
rust, sql
Área
databases

Línea de trabajo

Empieza en Unparser::scalar_to_sql en datafusion/sql/src/unparser/expr.rs. Las ramas de List (alrededor de las líneas 1450-1454) renderizan los valores hijos sin comprobar si son nulos, y las ramas Struct, Map y Union (alrededor de las líneas 1579-1581) devuelven not_impl_err! incluso para nulos. Reproduce el problema con las dos consultas CASE del issue y luego comprueba si los nulos tipados como Int32(None) ya se convierten a NULL en el unparser. Está terminado cuando los literales NULL de tipos anidados se convierten a NULL y las pruebas existentes del unparser de datafusion-sql siguen pasando.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

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.
Lenguaje dominante
Rust
Estrellas
9.4k
Forks
2.5k
Merge medio
4 d 9 h
PR fusionados (30 d)
329

Preparar el entorno

Abrir en Codespaces

Inicia el contenedor de desarrollo del proyecto en tu navegador, con tu propia cuenta de GitHub.

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de apache/datafusion

Todos los issues de apache/datafusion

Issues similares

Más issues de Rust

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.