Dandandan opened a new issue, #22193:
URL: https://github.com/apache/datafusion/issues/22193
### Describe the bug
`generate_series(start_date, end_date, INTERVAL ...)` panics with `attempt
to multiply with overflow` when either date is far enough from the Unix epoch
(roughly before year ~1670 or after year ~2270). The panic happens during
planning (when the table-valued function converts `Date32` days to timestamp
nanoseconds).
### To Reproduce
```rust
use datafusion::prelude::SessionContext;
#[tokio::main]
async fn main() {
let ctx = SessionContext::new();
let _ = ctx
.sql("SELECT * FROM generate_series(DATE '0001-01-01', DATE
'2000-01-01', INTERVAL '1' DAY) LIMIT 0")
.await
.unwrap()
.create_physical_plan()
.await;
}
```
Panic (debug-assertions on):
```
thread 'main' panicked at
datafusion/functions-table/src/generate_series.rs:714:24:
attempt to multiply with overflow
```
Also reproduces with:
- `EXPLAIN SELECT * FROM generate_series(DATE '0001-01-01', DATE
'2000-01-01', INTERVAL '1' DAY)`
- `EXPLAIN SELECT * FROM generate_series(DATE '0996-09-01', DATE
'1992-09-03', INTERVAL '1' DAY)` (original fuzzer find)
Without debug-assertions the multiplication would wrap silently and produce
a wildly wrong result, which is also bad.
### Expected behavior
Return a planner error explaining the requested range is out of
representable bounds (or use checked arithmetic and overflow → error). The
public SQL API should never panic on user-supplied SQL.
### Root cause
[`datafusion/functions-table/src/generate_series.rs`](https://github.com/apache/datafusion/blob/main/datafusion/functions-table/src/generate_series.rs),
around lines 712-715:
```rust
// Convert Date32 (days since epoch) to timestamp nanoseconds
const NANOS_PER_DAY: i64 = 24 * 60 * 60 * 1_000_000_000; //
86_400_000_000_000
let start_ts = start_date as i64 * NANOS_PER_DAY; // ← panics
let end_ts = end_date as i64 * NANOS_PER_DAY;
```
`Date32` is `i32` days since 1970-01-01, so `|start_date|` can be up to
~`2^31`. Multiplying by `86_400_000_000_000` overflows `i64` once `|days|`
exceeds `i64::MAX / 86_400_000_000_000 ≈ 106_751` days (~292 years). Any date
before ~year 1670 or after ~year 2270 triggers it.
### Suggested fix
Use `checked_mul`:
```rust
let start_ts = (start_date as i64).checked_mul(NANOS_PER_DAY)
.ok_or_else(|| plan_datafusion_err!(
"generate_series: start date {start_date} out of representable
timestamp range"
))?;
let end_ts = (end_date as i64).checked_mul(NANOS_PER_DAY)
.ok_or_else(|| plan_datafusion_err!(
"generate_series: end date {end_date} out of representable timestamp
range"
))?;
```
(Or convert via `Timestamp(Microsecond, _)` instead of nanoseconds — the
representable range there is ±290k years, well beyond `Date32`.)
### Additional context
Found by a `cargo fuzz` target (`fuzz/fuzz_targets/sql_physical_plan.rs`)
seeded with SQL from `datafusion/sqllogictest/test_files/`. The fuzzer mutated
a small year literal into `0996`, which was enough to overflow.
--
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]