Unparser turns NULL list literals into `[]` and fails on NULL struct/map literals
Les mainteneurs répondent en général sous 1 jour
Personne n'a encore pris cette issue.
Évaluation
- Difficulté
- 2/5
- Temps estimé
- 1-3 heures
- Accessibilité débutants
- 72/100
Piste de recherche
Commence dans Unparser::scalar_to_sql dans datafusion/sql/src/unparser/expr.rs. Les branches List (environ lignes 1450-1454) rendent les valeurs enfants sans vérifier la présence de null, et les branches Struct, Map et Union (environ lignes 1579-1581) renvoient not_impl_err! même pour les null. Reproduis avec les deux requêtes CASE de l'issue, puis vérifie que les null typés comme Int32(None) sont déjà convertis en NULL. Terminé quand les littéraux NULL de types imbriqués sont convertis en NULL et que les tests existants de l'unparser datafusion-sql passent toujours.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Description
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.
- Langage dominant
- Rust
- Étoiles
- 9.5k
- Forks
- 2.5k
- Merge moyen
- 4 j 12 h
- PR mergées (30 j)
- 306
Préparer son environnement
Lance le conteneur de développement du projet dans votre navigateur, avec votre propre compte GitHub.
- Aucun Dockerfile ni fichier Docker Compose
- Propose un modèle de pull request
- Lire le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Autres issues de apache/datafusion
-
[Bug] atan2 does not normalize -0.0 the way Spark doesPeut-être pris @spicysharkco l’a pris il y a 4 jours. Ouvertebug spark
Difficulté 2/5 1-3 heures Accessibilité débutants 72/100
apache/datafusion#26333 ·
Les mainteneurs répondent en général sous 1 jour
-
Deduplicate bucket computation in width_bucket_interval_mdn_exactPeut-être pris @Shahzeb-DataEngineer l’a pris il y a 1 jour. Ouverteenhancement good first issue help wanted spark tech debt
Difficulté 2/5 1-3 heures Accessibilité débutants 85/100
apache/datafusion#26307 · 3 commentaires ·
Les mainteneurs répondent en général sous 1 jour
-
Use the shared aggr_test_schema in CSV scan tests instead of a local copyPeut-être pris @med9110 l’a pris il y a 5 jours. Ouvertecore enhancement good first issue help wanted tech debt
Difficulté 2/5 1-3 heures Accessibilité débutants 85/100
apache/datafusion#26306 · 2 commentaires ·
Les mainteneurs répondent en général sous 1 jour
-
Struct union type coercion takes field nullability from the wrong field when field order differsPeut-être pris @med9110 l’a pris il y a 5 jours. Ouvertebug help wanted logical-expr tech debt
Difficulté 2/5 1-3 heures Accessibilité débutants 68/100
apache/datafusion#26296 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
MIN/MAX don't unwrap nested encodings inside Dictionary columnsPeut-être pris @grevend l’a pris il y a 4 jours. Ouvertebug
Difficulté 2/5 1-3 heures Accessibilité débutants 72/100
apache/datafusion#26290 ·
Les mainteneurs répondent en général sous 1 jour
Toutes les issues de apache/datafusion
Issues similaires
-
Difficulté 2/5 1-3 heures Accessibilité débutants 64/100
Les mainteneurs répondent en général sous 2 jours
-
Difficulté 2/5 1-3 heures Accessibilité débutants 72/100
Les mainteneurs répondent en général sous 8 jours
-
Difficulté 2/5 1-3 heures Accessibilité débutants 82/100
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
swc-project/swc#12576 ·
Les mainteneurs répondent en général sous 1 jour
-
good first issue gpu serve
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
Les mainteneurs répondent en général sous 1 jour