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

   ### Describe the bug
   
   
   When an ordered aggregate's `ORDER BY` list mentions the same expression 
twice, DataFusion
   builds two internal descriptions of that ordering with different lengths, 
and every consumer
   that compares them fails. Depending on which consumer is reached, the query 
either **panics** a
   worker task or fails with an **internal Arrow error** that no user-facing 
mistake can explain.
   
   ```sql
   CREATE TABLE t AS SELECT i % 3 AS b, i AS id FROM (SELECT 
unnest(range(0,10)) AS i);
   SELECT b, first_value(id ORDER BY b, b) FROM t GROUP BY b;
   ```
   
   ```
   thread 'tokio-rt-worker' panicked at 
datafusion/functions-aggregate/src/first_last.rs:602:13:
   assertion `left == right` failed
     left: 2
    right: 1
   Error: Join Error
   caused by
   External error: task 131 panicked with message "assertion `left == right` 
failed\n  left: 2\n right: 1"
   ```
   
   Ten rows and one column; no configuration, no memory pressure, no particular 
partition count.
   
   A duplicated sort key is legal SQL — the repetition is simply redundant, 
since the second copy
   can never break a tie the first did not. PostgreSQL and DuckDB both accept 
it and answer
   correctly (transcripts under *Additional context*).
   
   **`DISTINCT ON` is the way this is most likely to be hit by accident**, 
because `DISTINCT ON`
   already requires its key to lead the `ORDER BY`; a user who then also names 
the key explicitly
   writes the duplicate without noticing:
   
   ```sql
   SELECT DISTINCT ON (b) b FROM t ORDER BY b, b;   -- same panic, same line
   ```
   
   #### Symptom matrix
   
   All rows are the same defect reaching a different consumer. Output is from 
`main`
   `6c27203c8e`, `datafusion-cli 55.1.0`, each statement in a fresh process.
   
   | Query | Result |
   |---|---|
   | `SELECT b, first_value(id ORDER BY b, b) FROM t GROUP BY b` | **panic** 
`first_last.rs:602`, `left: 2, right: 1` |
   | `SELECT DISTINCT ON (b) b FROM t ORDER BY b, b` | **panic**, same line 
(plans to `first_value`) |
   | `SELECT b, last_value(id ORDER BY b, b) FROM t GROUP BY b` | **panic**, 
same line |
   | `SELECT b, first_value(id ORDER BY b ASC, b DESC) FROM t GROUP BY b` | 
**panic**, same line |
   | `SELECT b, first_value(id ORDER BY b, id, b) FROM t GROUP BY b` | 
**panic**, `left: 3, right: 2` |
   | `SELECT b, first_value(id ORDER BY b, b, b) FROM t GROUP BY b` | 
**panic**, `left: 3, right: 1` |
   | `SELECT b, first_value(id ORDER BY b+0, b+0) FROM t GROUP BY b` | 
**panic** — it is not only column references |
   | `SELECT b, array_agg(id ORDER BY b, b) FROM t GROUP BY b` | `Arrow error: 
Invalid argument error: Incorrect number of arrays provided to RowConverter, 
expected 1 got 2` |
   | `SELECT b, string_agg(CAST(id AS VARCHAR), ',' ORDER BY b, b) FROM t GROUP 
BY b` | same `RowConverter` error |
   | `SELECT b, nth_value(id, 1 ORDER BY b, b) FROM t GROUP BY b` | `Arrow 
error: ... expected List(Struct("b@0": Int64, "b@0": Int64)) but found 
List(Struct("b@0": Int64)) at column index 2` |
   | `SELECT b, min(id ORDER BY b, b) FROM t GROUP BY b` | `Arrow error: ... 
number of columns(2) must match number of fields(4) in schema` |
   | `SELECT first_value(id ORDER BY b, b) FROM t` (ungrouped) | `Arrow error: 
... number of columns(3) must match number of fields(4) in schema` |
   | **any of the above with the duplicate removed** | **correct** |
   
   Two negative controls that bound the defect — both are fine, so this is 
specific to *ordered
   aggregates*, not to duplicated sort keys in general:
   
   | Query | Result |
   |---|---|
   | `SELECT * FROM t ORDER BY b, b LIMIT 3` | correct |
   | `SELECT first_value(id) OVER (ORDER BY b, b) FROM t LIMIT 3` | correct 
(window path keeps both keys) |
   
   Note the `ORDER BY b ASC, b DESC` row: the deduplication is on the 
*expression*, ignoring the
   sort options, so two entries that are not even semantically identical 
collapse to one.
   
   
   ### To Reproduce
   
   
   ```console
   $ datafusion-cli --version
   datafusion-cli 55.1.0
   
   $ datafusion-cli -q \
       -c "CREATE TABLE t AS SELECT i % 3 AS b, i AS id FROM (SELECT 
unnest(range(0,10)) AS i);" \
       -c "SELECT b, first_value(id ORDER BY b, b) FROM t GROUP BY b;"
   thread 'tokio-rt-worker' panicked at 
datafusion/functions-aggregate/src/first_last.rs:602:13:
   assertion `left == right` failed
     left: 2
    right: 1
   ```
   
   Backtrace of the panicking frame:
   
   ```
     4: 
FirstLastGroupsAccumulator<PrimitiveValueState<Int64Type>>::get_filtered_extreme_of_each_group
     5: <FirstLastGroupsAccumulator<...> as GroupsAccumulator>::update_batch
     6: 
aggregates::aggregate_hash_table::common::HashAggregateAccumulator::update_batch
     7: 
aggregates::group_values::metrics::GroupByMetrics::time_aggregation::<...>
     8: aggregates::hash_stream::PartialHashAggregateStream::handle_input_batch
   ```
   
   ### Expected behavior
   
   
   `first_value(id ORDER BY b, b)` answers as `first_value(id ORDER BY b)` does 
— the duplicate key
   is redundant and can be dropped — instead of panicking; and the `array_agg` 
/ `nth_value` /
   `min` / ungrouped forms return a result instead of an internal Arrow error.
   
   For comparison, on the same data:
   
   ```console
   $ psql -U postgres -c "CREATE TABLE tdup AS SELECT i%3 AS b, i AS id FROM 
generate_series(0,9) i;" \
                      -c "SELECT b, array_agg(id ORDER BY b, b) FROM tdup GROUP 
BY b ORDER BY b;" \
                      -c "SELECT DISTINCT ON (b) b FROM tdup ORDER BY b, b;" \
                      -c "SELECT b, min(id ORDER BY b ASC, b DESC) FROM tdup 
GROUP BY b ORDER BY b;"
   SELECT 10
    b | array_agg
   ---+-----------
    0 | {0,3,6,9}
    1 | {4,1,7}
    2 | {5,8,2}
   (3 rows)
   
    b
   ---
    0
    1
    2
   (3 rows)
   
    b | min
   ---+-----
    0 |   0
    1 |   1
    2 |   2
   (3 rows)
   ```
   
   (PostgreSQL 18.6.)
   
   ```console
   $ python3 -c "import duckdb; con=duckdb.connect()
   con.execute('CREATE TABLE t AS SELECT i%3 AS b, i AS id FROM range(0,10) 
tt(i)')
   print(con.execute('SELECT b, first(id ORDER BY b, b), list(id ORDER BY b, b) 
FROM t GROUP BY b ORDER BY b').fetchall())"
   [(0, 0, [0, 3, 6, 9]), (1, 1, [1, 4, 7]), (2, 2, [2, 5, 8])]
   ```
   
   (DuckDB 1.6.0.dev214; it also accepts `ORDER BY b ASC, b DESC` and the 
`DISTINCT ON` form.)
   
   ### Additional context
   
   _No response_


-- 
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