Skip to content

fix: handle NULL string and format arguments in to_date, to_timestamp, and to_unixtime#23669

Open
U0001F3A2 wants to merge 2 commits into
apache:mainfrom
U0001F3A2:fix/23640-null-format-args
Open

fix: handle NULL string and format arguments in to_date, to_timestamp, and to_unixtime#23669
U0001F3A2 wants to merge 2 commits into
apache:mainfrom
U0001F3A2:fix/23640-null-format-args

Conversation

@U0001F3A2

@U0001F3A2 U0001F3A2 commented Jul 17, 2026

Copy link
Copy Markdown

Which issue does this PR close?

Rationale for this change

Three NULL-handling bugs in the shared helpers in datetime/common.rs:

SELECT to_date(NULL::VARCHAR, '%Y-%m-%d');                   -- Internal error: a should not be None.
SELECT to_date('2020-09-08', NULL::VARCHAR, NULL::VARCHAR);  -- Internal error: ret should not be None.

plus a NULL format in an array argument being parsed instead of skipped.

The third one is a correctness bug rather than only a spurious error. handle_array_op read the format with .value(pos) without checking validity, and an Arrow null slot may legally retain its bytes, so a NULL format can parse successfully and return a wrong value (repro).

What changes are included in this PR?

  • handle_multiple returns NULL for a NULL scalar input, and when every format is NULL.
  • handle_array_op skips null format slots, matching the scalar arm at the same site.
  • Docs: format_n now states the NULL rule, since skipping NULLs is not ordinary NULL propagation (to_date('2020-09-08', NULL::VARCHAR, '%Y-%m-%d') returns a date, not NULL).

Parse errors are unchanged when a non-NULL format is present and fails. to_time does not share these helpers and is untouched.

Are these changes tested?

Yes. sqllogictests cover every case in the issue across to_date, to_timestamp, and to_unixtime, confirmed to fail on main with the reported messages. Unit tests in to_date.rs pin the retained-bytes case, which SQL cannot express, plus guards that a failing non-NULL format still errors and that to_date(NULL::VARCHAR, 12345) still rejects the format type.

Are there any user-facing changes?

Yes, this is a SQL-semantics change (the point of the fix).

  • Previous: to_date(NULL::VARCHAR, '%Y-%m-%d') and an all-NULL format list raised Internal error; a NULL format inside an array argument was parsed (could silently return a wrong value).
  • New: a NULL input returns NULL, NULL formats are skipped, and an all-NULL format list returns NULL. A non-NULL format that fails to parse still errors.

No Rust API change; a committer may want the api-change label for the release notes.

U0001F3A2 and others added 2 commits July 17, 2026 19:25
…, and to_unixtime

The formatted variants share `handle_multiple` and `handle_array_op` in
`datetime/common.rs`, which mishandled NULL arguments three ways:

- a NULL scalar input hit `unwrap_or_internal_err!`, raising
  "Internal error: a should not be None."
- an all-NULL scalar format list left `ret` unset, raising
  "Internal error: ret should not be None."
- a NULL format slot in an array argument was read with `.value(pos)`,
  which ignores the validity buffer.

The third is a correctness bug rather than only a spurious error. Arrow
permits a null slot to retain its backing bytes, so a NULL format could
parse successfully and silently return a wrong value. The '' in the
reported error is only what a null slot happens to hold when the array
is built the usual way.

A NULL input and an all-NULL format list now yield NULL, and null format
slots are skipped, which matches what the scalar path already did.
Existing parse errors are preserved when a non-NULL format is present
and fails.

Closes apache#23640

Co-Authored-By: Claude <noreply@anthropic.com>
…_unixtime

The `format_n` docs promised an error when no format parses the
expression, and said nothing about NULL formats. Skipping NULL formats
is not ambient SQL NULL propagation, since a NULL alongside a usable
format still yields a value rather than NULL, so the rule is worth
stating.

Regenerated scalar_functions.md via dev/update_function_docs.sh.

`to_time` is left alone. It does not share these helpers and still
errors when every format is NULL, so its wording remains accurate.

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added documentation Improvements or additions to documentation sqllogictest SQL Logic Tests (.slt) functions Changes to functions implementation labels Jul 17, 2026
@U0001F3A2

U0001F3A2 commented Jul 17, 2026

Copy link
Copy Markdown
Author

Hi @alamb, first-time contributor here, so CI needs a committer to trigger it. Could you kick it off when you have a moment? Thanks!

@alamb

alamb commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Hi @alamb, first-time contributor here, so CI needs a committer to trigger it. Could you kick it off when you have a moment? Thanks!

Thanks @U0001F3A2 -- looks good I started the CI

your branch appears to have conflicts so we'll have to resolve that before merge

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.36170% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.67%. Comparing base (f151c10) to head (a1828b5).
⚠️ Report is 16 commits behind head on main.

Files with missing lines Patch % Lines
datafusion/functions/src/datetime/to_date.rs 92.68% 1 Missing and 2 partials ⚠️
datafusion/functions/src/datetime/common.rs 66.66% 0 Missing and 2 partials ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##             main   #23669    +/-   ##
========================================
  Coverage   80.66%   80.67%            
========================================
  Files        1086     1086            
  Lines      366673   366844   +171     
  Branches   366673   366844   +171     
========================================
+ Hits       295786   295946   +160     
+ Misses      53263    53262     -1     
- Partials    17624    17636    +12     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@U0001F3A2

U0001F3A2 commented Jul 18, 2026

Copy link
Copy Markdown
Author

Hi @alamb, looks like #23641 landed first and closes the same issue (#23640). The common.rs fix here is now redundant (functionally identical to what merged), so this PR isn't really a bug fix anymore.

Two things here aren't covered by #23641 though:

  • Rust unit tests for the NULL cases. One of them, test_to_date_null_format_slot_retaining_bytes, builds an array whose NULL format slot still physically holds parseable bytes, so a missing validity check surfaces as a wrong value rather than an error. fix: handle null date and timestamp format arguments #23641 added only SLTs, so that path isn't guarded.
  • Doc updates for to_date / to_timestamp* / to_unixtime noting that NULL formats are skipped and an all-NULL format list yields NULL.

Happy to rebase and trim this down to just the tests + docs as a follow-up to #23641, or close it if you'd rather not carry them. Which do you prefer?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation functions Changes to functions implementation sqllogictest SQL Logic Tests (.slt)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

to_date, to_timestamp, and to_unixtime fail on NULL string and format arguments

3 participants