Skip to content

fix(duckdb,polars): fall back instead of silently changing temporal columns - #454

Merged
kevincostner17 merged 1 commit into
mainfrom
fix/duckdb-timedelta
Sep 16, 2026
Merged

kevincostner17 merged 1 commit into
mainfrom
fix/duckdb-timedelta

Conversation

@kevincostner17

Copy link
Copy Markdown
Contributor

Closes #443.

On the fully native path (strategy="conservative", fix_dtypes=False) native ingestion changed temporal values, with nothing recorded in fallback_events or backend_differences:

input before after
timedelta64[ns] DuckDB INTERVAL is microseconds: 1431904ns came back 1431000ns, 5ns came back 0 disclosed pandas fallback, values unchanged
tz-aware datetime64[ns, UTC] DuckDB returned it in the machine's session time zone at microsecond resolution (…00.000000123+00:00…05:30:00+05:30 on a machine set to Asia/Kolkata) disclosed pandas fallback, zone and value unchanged
period[M] DuckDB raised NotImplementedException: Data type 'period[M]' not recognized; Polars returned the raw int64 ordinal (2020-01600) disclosed pandas fallback on both
interval[int64, right] DuckDB raised the same error; Polars returned a {left, right} struct disclosed pandas fallback on both

pandas_ingest_fallback_reason() now takes the engine name, so the checks that belong to one backend's type system only apply there. Polars keeps its native path for nanosecond timedeltas and tz-aware datetimes, which it carries exactly — there is a test pinning that, so the DuckDB-only checks cannot quietly cost Polars its native path.

Because these are ingest-time reasons, fallback_policy="error" now refuses such a run before any pandas work, instead of returning silently altered data.

Default-output changes: none. The default strategy="balanced" already delegates the whole pipeline to pandas on every native engine.

docs/fallback-matrix.md had one row claiming datetime / timedelta / categorical / period / interval were all native on both engines. It is split into the three rows that are now true.

Verification — py3.12/pandas 2.3.3/duckdb 1.5.4/polars 1.42.1 and py3.9/pandas 1.5.3/duckdb 1.4.5/polars 1.36.1:

  • the #443 repro and the four new dtypes: exact values, fallback_events length 1 on both venvs; fail on main
  • tests/test_execution/: 377 passed, 10 skipped on both
  • full suite green on both; ruff and mypy clean

…olumns

On the fully native path (strategy="conservative", fix_dtypes=False) native
ingestion changed temporal values with nothing recorded in fallback_events or
backend_differences:

- DuckDB stores INTERVAL in microseconds, so a timedelta64[ns] column was
  truncated on the way back (1431904ns -> 1431000ns, 5ns -> 0).
- DuckDB returns TIMESTAMP WITH TIME ZONE in the session time zone at
  microsecond resolution, so a tz-aware datetime column came back in the
  machine's local zone with its sub-microsecond part dropped.
- Both engines mishandle period and interval dtypes: DuckDB raises
  NotImplementedException("Data type 'period[M]' not recognized") and Polars
  ingests a period as its raw int64 ordinal (2020-01 -> 600) and an interval
  as a {left, right} struct.

pandas_ingest_fallback_reason() now takes the engine name and rejects those
columns, so the run takes the disclosed pandas fallback and fallback_policy
="error" can refuse it. Polars keeps its native path for nanosecond timedeltas
and tz-aware datetimes, which it carries exactly.

docs/fallback-matrix.md splits the old datetime/timedelta/categorical/period/
interval row into the three rows that are now true.

Closes #443
@coderabbitai

coderabbitai Bot commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 38839c77-b15a-4038-9f36-cf1f5555ea23


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

FreshData benchmark report — performance

  • freshdata: ?
  • python: ?
  • platform: ?
fixture n_rows n_cols p50 s p95 s peak MB repair % false-repair % preserve % trust monotonic export %

Authored-code reduction (Metric 6)

@kevincostner17
kevincostner17 merged commit e704bc9 into main Sep 16, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

engine="duckdb" silently truncates timedelta64[ns] to microseconds

2 participants