fix: surface special-format conversion errors instead of swallowing them (#3988)#3989
Conversation
…hem (#3988) `Config::resolve_converted` previously swallowed polars-native special-format conversion failures (Avro, Parquet, Arrow IPC, JSON, JSONL, compressed CSV/TSV/SSV) and fell back to reading the original — often binary — bytes as delimited text. That emitted garbage with a *success* exit code; only `.zip` surfaced the error. A detected special format that fails to convert is now a hard error for ALL formats. The single escape hatch remains `QSV_SKIP_FORMAT_CHECK`, which opts into reading the raw bytes as-is. This surfaced pre-existing masked failures whose tests only passed because the asserted substring is embedded in the raw bytes: - `slice_from_avro`: the `NYC311-5.avro` fixture was unreadable by current polars (`OutOfSpec`). Regenerated it from the canonical CSV via the same polars AvroWriter; re-enabled the test with a full-row assertion. - `slice_from_jsonl`: strengthened to a full-object comparison (parsed as JSON, order-independent since polars does not preserve JSONL column order). - The two `slice` Decimal-pschema tests used an invalid pschema shape AND a format polars cannot apply a Decimal type to (JSONL). Reworked them onto the compressed-CSV reader — the path that genuinely applies a Decimal pschema and preserves 25-digit precision — and renamed to `slice_from_csvgz_*`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 0 |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
There was a problem hiding this comment.
Pull request overview
This PR makes special-format input conversion failures (e.g., Avro/Parquet/Arrow IPC/JSON/JSONL and compressed delimited inputs) fail fast instead of silently falling back to reading raw bytes as delimited text, preventing “successful” commands from emitting corrupt/garbage output. It also strengthens slice tests that previously passed for the wrong reason, and documents the behavior change.
Changes:
- Make
Config::resolve_convertedreturn a hard error on special-format conversion failure unlessQSV_SKIP_FORMAT_CHECKis set. - Strengthen
sliceintegration tests to validate full converted rows / decimal precision behavior rather than substring checks. - Add an
[Unreleased]changelog entry describing the user-visible behavior change and the test fixture adjustments.
Reviewed changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
src/config.rs |
Surfaces special-format conversion failures as InvalidInput errors unless explicitly opted out via QSV_SKIP_FORMAT_CHECK. |
tests/test_slice.rs |
Updates several slice tests to assert real conversion results (full-row / decimal precision) instead of substring checks that could pass on raw bytes. |
CHANGELOG.md |
Documents the behavior change under [Unreleased]. |
Comments suppressed due to low confidence (2)
tests/test_slice.rs:1325
- This block runs
slicetwice (assert_successthenstdout), butWorkdir::stdoutdoes not assert exit success. CaptureOutputonce and reuse its stdout to avoid masking failures.
wrk.assert_success(&mut cmd);
// Get the output with Decimal schema
let decimal_output: String = wrk.stdout(&mut cmd);
tests/test_slice.rs:1344
- Same issue as above:
assert_successruns the command once andstdoutruns it again without checking the status. Use a singleOutputto ensure this assertion can’t pass if the command exits non-zero.
wrk.assert_success(&mut cmd);
// Get the output with Float64 schema
let float_output: String = wrk.stdout(&mut cmd);
…-independent JSON compare Copilot flagged that `assert_success` followed by `stdout` runs the command twice and `Workdir::stdout` does not assert exit status. Added a `Workdir::stdout_on_success` helper (mirroring the existing `stderr_on_success`) that captures a single `Output`, asserts success, and returns its stdout — so the asserted run is the same run whose output is checked. Switched the four slice special-format tests to it. Left the `slice_from_jsonl` comparison as a parsed `serde_json::Value` equality: with the `preserve_order` feature, serde_json's `Map` is IndexMap-backed and IndexMap's `PartialEq` is order-independent (same length + all key/value pairs match, no order check — verified against indexmap docs), so the assertion is already order-independent despite polars not preserving JSONL column order. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
Closes part of #3988.
Config::resolve_convertedpreviously swallowed polars-native special-format conversion failures (Avro, Parquet, Arrow IPC, JSON, JSONL, compressed CSV/TSV/SSV) and fell back to reading the original — often binary — bytes as delimited text. The command then emitted garbage with a success exit code. Only.zipsurfaced the error.For a data-wrangling tool, silently producing corrupt output that looks successful is the worst failure mode — it can flow downstream into joins, DB loads, etc. unnoticed. This PR makes a detected special format that fails to convert a hard error for all formats, consistent with the existing
.zipbehavior. The single escape hatch remainsQSV_SKIP_FORMAT_CHECK, which opts into reading the raw bytes as-is.Behavior change
CHANGELOG.mdunder[Unreleased].Masked failures this surfaced (and fixed)
These tests only ever passed because the asserted substring is physically embedded in the raw file bytes — so the substring check passed whether or not conversion actually worked:
slice_from_avro— theNYC311-5.avrofixture is unreadable by current polars (avro-error: OutOfSpec). Regenerated it from the canonicalNYC311-5.csvvia the same polarsAvroWriter(qsv sqlp ... --format avro), verified it slices to the exact expected row, and strengthened the assertion to a full-row match.slice_from_jsonl— strengthened to a full-object comparison, parsed as JSON (order-independent, since polars does not preserve JSONL column order).slice_from_jsonl_with_decimal_precision{,_vs_float}— these used an invalid pschema shape (missing themetadatafield) and targeted a capability polars doesn't have: its JSONL reader cannot apply aDecimaltype (it refuses to coerce JSON numbers or strings into a Decimal column). Reworked both onto the compressed-CSV reader — the path that genuinely applies aDecimalpschema and preserves 25-digit precision vs f64's ~17 — and renamed toslice_from_csvgz_with_decimal_precision{,_vs_float}.No
#[ignore]s are introduced — all affected tests pass for the right reason.Verification
test_slice: 140 passed, 0 ignored.cargo clippy -F all_featuresclean;cargo +nightly fmtapplied.🤖 Generated with Claude Code