AndreaBozzo opened a new pull request, #10579:
URL: https://github.com/apache/arrow-rs/pull/10579
# Which issue does this PR close?
Closes #10577.
# Rationale for this change
`with_truncated_rows(true)` repairs a row that has fewer fields than the
schema by
padding it, and reports nothing about having done so. For a consumer that
reports on
data quality, a repaired parse and a clean parse are different outcomes, and
today
they are indistinguishable.
The count cannot be recovered after decoding. Padding fills offsets to
produce
zero-length fields, and `NullRegex` later turns those into nulls, so a
padded field
is byte-identical to a genuinely empty trailing field:
```
name,age,city
Alice,25,Rome -> ["Alice", "25", "Rome"]
Carol,35, -> ["Carol", "35", NULL] three fields, the last one empty
Bob,30 -> ["Bob", "30", NULL] two fields, padded
```
Disabling the null regex does not help, both cases become `""` and stay
identical.
Nothing on `ReaderBuilder`, `Format`, `Reader`, `BufReader` or `Decoder`
exposes the
information either.
This came out of dataprof, where the Arrow-backed CSV engine was the only
path that
reported a file of short rows with a perfect consistency score. The
workaround
shipped there is a pre-scan with the `csv` crate purely to recover the
count, which
costs a second full read of the file. On a 218 MB, 2M-row file that measured
at
roughly 3% of profiling wall time, because per-value analysis dominates the
parse.
So the workaround is viable, but it is a whole extra pass to recover a
number the
decoder already had and threw away.
# What changes are included in this PR?
A single counter, threaded up to the public types.
`arrow-csv/src/reader/records.rs`:
* `RecordDecoder` gains a `truncated_row_count` field, incremented in the
one branch
of `decode` that pads a short row.
* `RecordDecoder::truncated_row_count()` returns it.
* `flush` deliberately leaves the counter alone, so it accumulates across
batches.
`clear` resets it, because `clear` discards the buffered rows the count
refers to.
That is what keeps skipped rows out of the total, see below.
`arrow-csv/src/reader/mod.rs`:
* `Decoder::truncated_row_count()` and `BufReader::truncated_row_count()`
forward it.
`Reader<R>` is an alias for `BufReader<StdBufReader<R>>`, so the accessor
covers
both.
Two semantics worth calling out, both documented on the accessors:
* **The count is cumulative, not per batch.** `RecordDecoder` is reused
across
`flush` calls and the counter survives them. Reading it between batches
gives a
running total of the rows decoded so far, reading it after the input is
exhausted
gives the total for the whole input. It is meaningful at either point as
long as
that is understood, so it is stated rather than restricted.
* **Skipped rows do not contribute.** The header row and any rows before the
start
bound go through `RecordDecoder::decode` too, and would otherwise be
counted if
they were short. They are discarded via `clear`, which now resets the
counter with
them, so the total only ever covers rows that reached a batch.
# Are these changes tested?
Yes, at both levels, and each test was confirmed to fail against unpatched
code.
`records.rs`:
* `test_truncated_rows` extended to assert the count.
* `test_truncated_row_count_not_reset_by_flush`.
* `test_truncated_row_count_reset_by_clear`.
`mod.rs`:
* `test_truncated_row_count_counts_padded_rows`, one short row, count is 1.
* `test_truncated_row_count_ignores_empty_trailing_field`, a row with a
genuinely
empty trailing field produces the same null as a padded row but the count
stays 0.
This is the case that proves the count is not inferred from nulls.
* `test_truncated_row_count_clean_file`, count is 0.
* `test_truncated_row_count_without_truncated_rows`, the short row errors as
before
and the accessor still returns 0.
* `test_truncated_row_count_accumulates_across_batches`, `batch_size` 2 over
6 short
rows, asserting the running total is 2, 4, 6 rather than 2 each time.
* `test_truncated_row_count_excludes_skipped_rows`, a header shorter than
the schema
is padded while being skipped and does not count.
* `test_truncated_row_count_on_decoder`, the push-based `Decoder` path.
The two parts of the change, the increment and the `clear` reset, were
reverted
separately to confirm each is independently covered.
`cargo test -p arrow-csv`, `cargo fmt --all` and
`cargo clippy -p arrow-csv --all-targets -- -D warnings` are clean.
# Are there any user-facing changes?
Yes, one additive accessor, `truncated_row_count()`, on `BufReader` (and
therefore
`Reader`) and on `Decoder`. There are no breaking changes, no behaviour
changes to
parsing, and no new configuration.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]