You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 9f4b005
Browse filesBrowse the repository at this point in the historyBrowse files
fix: validate parquet.coerce_int96 when it is set (#25628)
## Which issue does this PR close?
- Part of #17498. This covers
`datafusion.execution.parquet.coerce_int96` only.
## Rationale for this change
The option is stored as a string, so a bad value is accepted and only
fails later:
```sql
-- Succeeds, and SHOW reports 'nanos'.
SET datafusion.execution.parquet.coerce_int96 = 'nanos';
-- Fails here, on the first table that touches Parquet:
-- Invalid or Unsupported Configuration: Unknown or unsupported parquet coerce_int96: nanos. Valid values are: ns, us, ms, and s.
CREATE EXTERNAL TABLE t STORED AS PARQUET LOCATION 'data.parquet';
```
The same thing happens through `CREATE EXTERNAL TABLE ... OPTIONS
('format.coerce_int96' 'nanos')`. If the table has an explicit schema
the string is parsed again in `ParquetSource` with `unwrap()`, so that
path panics instead.
#20253 fixed this the same way and was reviewed, but went stale and was
closed. This picks it up with the review applied.
## What changes are included in this PR?
- `DFTimeUnit` in `parquet_config.rs`: `ns`, `us`, `ms`, `s`, parsed
case-insensitively and displayed as those strings, so `SHOW`,
`information_schema` and the proto encoding are unchanged. The
`ConfigField` impls follow `DFParquetStatistics`, so a rejected value
leaves the previous one in place and an unset option stays unset.
- `ParquetOptions.coerce_int96` becomes `Option<DFTimeUnit>`, and
`ParquetFormat::coerce_int96()` / `with_coerce_int96()` use the typed
value.
- `parse_coerce_int96_string` in `datasource-parquet` is removed, along
with the `unwrap()` that called it. `From<DFTimeUnit> for
arrow::datatypes::TimeUnit` replaces it.
- Proto conversion parses on the way in, so a bad value in a serialized
plan is an error rather than a panic.
- The option's docstring said "If true, ..." as if it were a boolean. It
now says what the value is and lists the valid values. `configs.md` and
`information_schema.slt` carry that text.
I kept the `Option`. #20253 looked at dropping it, but `None` means "do
not coerce" and `coerce_int96_tz` only applies when `coerce_int96` is
set, so `None` and `Some(ns)` are not the same.
## What is the testing strategy for this PR?
- `set_variable.slt`: set, show, one rejected value with its full error
text, and that the setting is unchanged afterwards.
- `test_parquet_coerce_int96_validation` in `config.rs`: valid values, a
rejected value keeps the previous one, an unset value stays unset,
nested keys are rejected.
- `time_unit_parses_and_displays` in `parquet_config.rs`.
- `test_invalid_parquet_coerce_int96_rejected_from_proto` in
`proto-common`, and the two existing round-trip tests now use the typed
value.
- I also checked `CREATE EXTERNAL TABLE ... OPTIONS
('format.coerce_int96' 'nanos')` fails at CREATE time with the new
message. The existing `coerce_int96` data tests in `parquet.slt` and the
core Parquet tests pass unchanged.
## Are there any user-facing changes?
Yes. An invalid `datafusion.execution.parquet.coerce_int96` is rejected
by `SET` and by table options with a message, instead of being accepted
and failing (or panicking) at scan time.
This changes the public field `ParquetOptions::coerce_int96` from
`Option<String>` to `Option<DFTimeUnit>`, and the two `ParquetFormat`
accessors with it, so it needs the `api change` label. SQL and
`ConfigOptions::set` users are not affected.
I searched GitHub for code that assigns the field directly. These will
need a one-line change on upgrade, from `Some("us".to_string())` to
`Some(DFTimeUnit::Microsecond)` or `Some("us".parse()?)`:
- apache/datafusion-comet: `native/core/src/parquet/parquet_exec.rs`
hardcodes `"us"` for Spark, plus one test assertion.
- delta-io/delta-rs: `crates/core/src/datafile/properties.rs` uses
`get_or_insert_with(|| "us".to_string())`.
- lakehq/sail: `crates/sail-session/src/session_factory/mod.rs` assigns
`"us"`, and `crates/sail-data-source/src/formats/parquet/options.rs`
copies the field through its own `Option<String>`.
- DoumanAsh/mishka: `src/datafusion.rs` assigns an owned string.
Code that already uses `DFParquetMetadata::with_coerce_int96` or the
opener (apache/auron does) takes an arrow `TimeUnit` and is unaffected.
I can add a note to the 56.0.0 upgrade guide matching the
`ExecutionOptions::time_zone` entry from #25788 if that's wanted.
0 commit comments