Summary
Any query that pushes a filter on a TIMESTAMP WITH TIME ZONE (timestamptz) column into read_vortex panics and aborts the process (a Rust panic across the FFI boundary, not a catchable DuckDB error). Plain TIMESTAMP filters and unfiltered scans of the same column work fine.
This blocks Vortex-backed DuckLake in practice (every table with a timestamptz column becomes unqueryable once predicate pushdown kicks in), and blocks timestamptz predicate pushdown generally.
Minimal reproduction (stock extension, no DuckLake, no patches)
-- write a vortex file with a timestamptz column
COPY (SELECT i AS id, TIMESTAMPTZ '2024-01-01' + to_seconds(i) AS ts_tz
FROM range(100000) t(i))
TO 'tz.vortex' (FORMAT vortex);
-- read it back with a pushed timestamptz filter -> panic
SELECT count(*) FROM read_vortex('tz.vortex')
WHERE ts_tz >= TIMESTAMPTZ '2024-01-01 06:00:00';
Actual behavior
thread '<unnamed>' panicked at .../jiff-0.2.35/src/span.rs:830:35:
value for seconds is out of bounds: parameter 'seconds' is not in the
required range of -631107417600..=631107417600
fatal runtime error: failed to initiate panic, error 5, aborting
Controls (isolates it to the pushed timestamptz filter)
| case |
result |
TIMESTAMPTZ column with pushed filter |
panic |
same data as plain TIMESTAMP, with filter |
works |
TIMESTAMPTZ column, no filter (SELECT count(*)) |
works |
Likely root cause
In vortex-duckdb/src/convert/scalar.rs, the TemporalMetadata::Timestamp branch handles the tz case differently from the non-tz case:
if let Some(tz) = tz.as_ref() { // TIMESTAMP_TZ
...
return Ok(Value::new_timestamp_tz(value()?)); // <-- no TimeUnit passed
}
match unit { // plain TIMESTAMP
TimeUnit::Microseconds => Value::new_timestamp_us(value()?), // <-- unit-tagged
...
}
DuckDB's TIMESTAMP_TZ value is microseconds since epoch (and convert/dtype.rs correctly maps the type to Timestamp::new_with_tz(TimeUnit::Microseconds, "UTC", ...)). But the scalar branch calls new_timestamp_tz(value) without the microsecond unit, so the raw µs count (e.g. '2024-01-01 06:00:00' ~ 1.704e15) is later interpreted as seconds — overflowing jiff's +/-631_107_417_600s range and panicking. The non-tz path is correct because it tags the unit (new_timestamp_us).
Suggested fix: construct the tz value with the microsecond unit (mirror the non-tz branch), and add a filter-pushdown test over a timestamptz column.
Separately: scalar conversion for pushdown should ideally return a VortexError (→ fall back to no-pushdown) rather than panic, so a bad value can't abort the host process.
Environment
vortex rev 765489149270c9f2e82e742fe84d3f5575672f9d, jiff 0.2.35
duckdb-vortex extension, DuckDB v0.0.1 build, linux/amd64
- Reproduced with the stock prebuilt duckdb+vortex shell (no DuckLake, no local patches)
Summary
Any query that pushes a filter on a
TIMESTAMP WITH TIME ZONE(timestamptz) column intoread_vortexpanics and aborts the process (a Rust panic across the FFI boundary, not a catchable DuckDB error). PlainTIMESTAMPfilters and unfiltered scans of the same column work fine.This blocks Vortex-backed DuckLake in practice (every table with a
timestamptzcolumn becomes unqueryable once predicate pushdown kicks in), and blockstimestamptzpredicate pushdown generally.Minimal reproduction (stock extension, no DuckLake, no patches)
Actual behavior
Controls (isolates it to the pushed timestamptz filter)
TIMESTAMPTZcolumn with pushed filterTIMESTAMP, with filterTIMESTAMPTZcolumn, no filter (SELECT count(*))Likely root cause
In
vortex-duckdb/src/convert/scalar.rs, theTemporalMetadata::Timestampbranch handles the tz case differently from the non-tz case:DuckDB's
TIMESTAMP_TZvalue is microseconds since epoch (andconvert/dtype.rscorrectly maps the type toTimestamp::new_with_tz(TimeUnit::Microseconds, "UTC", ...)). But the scalar branch callsnew_timestamp_tz(value)without the microsecond unit, so the raw µs count (e.g.'2024-01-01 06:00:00'~1.704e15) is later interpreted as seconds — overflowing jiff's+/-631_107_417_600srange and panicking. The non-tz path is correct because it tags the unit (new_timestamp_us).Suggested fix: construct the tz value with the microsecond unit (mirror the non-tz branch), and add a filter-pushdown test over a
timestamptzcolumn.Separately: scalar conversion for pushdown should ideally return a
VortexError(→ fall back to no-pushdown) rather thanpanic, so a bad value can't abort the host process.Environment
vortexrev765489149270c9f2e82e742fe84d3f5575672f9d,jiff0.2.35duckdb-vortexextension, DuckDBv0.0.1build, linux/amd64