zelmario opened a new issue, #24388:
URL: https://github.com/apache/datafusion/issues/24388

   ### Describe the bug
   
   `WITH ORDINALITY` on a table function is parsed and then discarded. No 
ordinality column is added and, in most forms, nothing is reported: the query 
succeeds and returns one column fewer than PostgreSQL returns for the same SQL.
   
   An error appears only when the table alias names every column, and that 
error blames the alias, which is the one part of the query that is correct:
   
   ```
   Error during planning: Source table contains 1 columns but only 2 names 
given as column alias
   ```
   
   `TableFactor::UNNEST` already handles this correctly and returns 
`not_impl_err!`. The `Table` and `Function` arms do not check the flag at all.
   
   ### To Reproduce
   
   No postgres front end or external data needed, just a stock `SessionContext` 
on 54.1.0:
   
   ```rust
   use datafusion::prelude::SessionContext;
   
   #[tokio::main]
   async fn main() {
       let ctx = SessionContext::new();
       for sql in [
           "SELECT * FROM generate_series(1,3)",
           "SELECT * FROM generate_series(1,3) WITH ORDINALITY",
           "SELECT * FROM generate_series(1,3) WITH ORDINALITY AS t(v)",
           "SELECT * FROM generate_series(1,3) WITH ORDINALITY AS t(v, n)",
       ] {
           println!("\n$ {sql}");
           match ctx.sql(sql).await {
               Ok(df) => match df.collect().await {
                   Ok(b) => println!("ok, {} column(s)", b.first().map(|b| 
b.num_columns()).unwrap_or(0)),
                   Err(e) => println!("execution error: {e}"),
               },
               Err(e) => println!("planning error: {e}"),
           }
       }
   }
   ```
   
   Output:
   
   ```
   $ SELECT * FROM generate_series(1,3)
   ok, 1 column(s)
   
   $ SELECT * FROM generate_series(1,3) WITH ORDINALITY
   ok, 1 column(s)
   
   $ SELECT * FROM generate_series(1,3) WITH ORDINALITY AS t(v)
   ok, 1 column(s)
   
   $ SELECT * FROM generate_series(1,3) WITH ORDINALITY AS t(v, n)
   planning error: Error during planning: Source table contains 1 columns but 
only 2 names given as column alias
   ```
   
   ### Expected behavior
   
   Either the ordinality column, or a refusal. PostgreSQL 17.11 returns two 
columns for all three forms:
   
   | Query | PostgreSQL 17.11 | DataFusion 54.1.0 |
   |---|---|---|
   | `generate_series(1,3) WITH ORDINALITY` | 2 columns, `1\|1 2\|2 3\|3` | 1 
column, no error |
   | `generate_series(1,3) WITH ORDINALITY AS t(v)` | 2 columns, `1\|1 2\|2 
3\|3` | 1 column, no error |
   | `generate_series(1,3) WITH ORDINALITY AS t(v, n)` | 2 columns, `1\|1 2\|2 
3\|3` | planning error |
   
   PostgreSQL allows a column alias to name fewer columns than the relation 
has, so `AS t(v)` is valid there and still returns the ordinality column. That 
is why only the complete alias trips DataFusion's arity check: everything 
shorter loses the column silently.
   
   The silent case seems the more important half. A confusing error costs 
someone an hour; a column that quietly is not there can be wrong for much 
longer.
   
   ### Additional context
   
   The flag is available on the AST node and dropped in 
`datafusion-sql/src/relation/mod.rs`. `UNNEST` checks it (line 233 in 54.1.0):
   
   ```rust
   TableFactor::UNNEST {
       alias,
       array_exprs,
       with_offset: false,
       with_offset_alias: None,
       with_ordinality,
   } => {
       if with_ordinality {
           return not_impl_err!("UNNEST with ordinality is not supported yet");
       }
   ```
   
   The other two arms let it fall into the `..`:
   
   ```rust
   TableFactor::Table {
       name, alias, args, ..          // line 152
   TableFactor::Function {
       name, args, alias, ..          // line 267
   ```
   
   The arity error then comes from `apply_expr_alias` in 
`datafusion-sql/src/planner.rs:568`, which no longer has the context to explain 
why the column count is short.
   
   A minimal fix would mirror the `UNNEST` behaviour on both arms:
   
   ```rust
   if with_ordinality {
       return not_impl_err!("WITH ORDINALITY is not supported yet");
   }
   ```
   
   That converts the silent cases into a refusal and is independent of whether 
the clause is ever implemented. Implementing it properly would obviously be 
better, and the two are separable. Happy to put up a PR for the `not_impl_err!` 
version if that is a direction you would take.
   
   `WITH ORDINALITY` is mentioned in passing as unsupported in #11419, in the 
context of `unnest` ordering. This report is about the silent drop and the 
diagnosis rather than the feature itself.
   
   Found while comparing a PostgreSQL wire front end backed by DataFusion 
against real PostgreSQL: `psql`'s `\d` sends 
`pg_catalog.pg_partition_ancestors(...) WITH ORDINALITY AS a(relid, depth)` 
when listing a table's triggers, so ordinary client introspection reaches it.
   
   **Version:** DataFusion 54.1.0, sqlparser 0.62.0, Rust 1.97.1, Linux x86_64.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to