Unparser emits empty SELECT list for empty Projection nodes, ignoring supports_empty_select_list

Abierto
#27,332 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

@dreverri ya está trabajando en esto.

Desde el 11/11/2026.

  • #27333 de @dreverri — abierto

Evaluación

Dificultad
2/5
Tiempo estimado
1-3 horas
Aptitud para principiantes
20/100
Tipo de issue
Error
Claridad
Bien especificado
Estado de actividad
Estancado
Stack tecnológico
rust

Línea de trabajo

Read datafusion/sql/src/unparser/plan.rs, focusing on reconstruct_select_statement and empty_projection_fallback; the issue identifies the missing fallback for empty Projection expressions. Compare the generated SQL for a filtered count(*) query under the default and PostgreSQL dialects, and inspect the related tests. Done means unsupported empty select lists emit SELECT 1, while PostgreSQL retains SELECT FROM; a linked pull request is already open.

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

Descripción

Describe the bug

Unparsing the optimized plan of a simple count(*) query with a filter produces SELECT FROM ... (an empty select list), even for dialects whose supports_empty_select_list() is false. Engines that don't accept an empty select list reject the generated SQL. For example, BigQuery fails with:

SELECT list must not be empty

The column used in the filter isn't needed above the Filter, so optimize_projections prunes it and leaves a Projection with no expressions:

Projection: count(Int64(1)) AS count(*)
  Aggregate: groupBy=[[]], aggr=[[count(Int64(1))]]
    Projection:
      Filter: t.id > Int32(10)
        TableScan: t projection=[id]

#19221 (closing #18991) added Dialect::supports_empty_select_list and a SELECT 1 fallback, but only the TableScan path checks it (the case where the scan has projection=[], as in select count(*) from t with no filter). An empty Projection node goes through reconstruct_select_statement, which uses p.expr as is.

To Reproduce
use datafusion::prelude::SessionContext;
use datafusion::sql::unparser::plan_to_sql;

let ctx = SessionContext::new();
ctx.sql("create table t (id int)").await?.collect().await?;
let plan = ctx
    .sql("select count(*) from t where id > 10")
    .await?
    .into_optimized_plan()?;
println!("{}", plan_to_sql(&plan)?);

Output (default dialect):

SELECT count(1) AS "count(*)" FROM (SELECT FROM t WHERE (t.id > 10))

BigQueryDialect produces the same thing with backtick quoting.

Expected behavior

An empty Projection should get the same dialect-dependent fallback as an empty TableScan projection:

  • Dialects with supports_empty_select_list() == false (default, BigQuery, ...):
    SELECT count(1) AS "count(*)" FROM (SELECT 1 FROM t WHERE (t.id > 10))
    
  • Dialects that support it (PostgreSQL): unchanged, SELECT FROM ...
Additional context

The fix is small: in reconstruct_select_statement (datafusion/sql/src/unparser/plan.rs), use self.empty_projection_fallback() when p.expr is empty. I'll open a PR with the fix and tests.

Lenguaje dominante
Rust
Estrellas
9.6k
Forks
2.6k
Merge medio
3 d 4 h
PR fusionados (30 d)
345

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.