Skip to content

fix(proto): preserve CSV/JSON scan options on roundtrip - #24233

Open
buraksenn wants to merge 8 commits into
apache:mainfrom
buraksenn:24180-preserve-csv-json-scan-options
Open

fix(proto): preserve CSV/JSON scan options on roundtrip#24233
buraksenn wants to merge 8 commits into
apache:mainfrom
buraksenn:24180-preserve-csv-json-scan-options

Conversation

@buraksenn

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Rationale for this change

Physical-plan protobuf serialization does not preserve several CSV and JSON scan options. Custom CSV terminators, JSON newline-delimited mode, and file compression therefore revert to their defaults after a roundtrip, which can cause the
decoded plan to read the file incorrectly.

What changes are included in this PR?

  • Add terminator to CsvScanExecNode.
  • Add newline_delimited to JsonScanExecNode.
  • Add file_compression_type to the shared FileScanExecConf.
  • Serialize and restore these options in CSV and JSON scans.

Are these changes tested?

Yes. Extended the CSV and JSON physical-plan roundtrip tests to cover custom terminators, non-newline-delimited JSON, compression, and backward-compatible defaults.

Are there any user-facing changes?

CSV and JSON scans now preserve their format and compression options across protobuf roundtrips. The protobuf changes are additive and backward compatible. JsonSource also gains an is_newline_delimited getter.

@github-actions github-actions Bot added proto Related to proto crate datasource Changes to the datasource crate labels Aug 10, 2026
…json-scan-options

# Conflicts:
#	datafusion/proto/tests/cases/roundtrip_physical_plan.rs
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown

Thank you for opening this pull request!

Reviewer note: cargo-semver-checks reported the current version number is not SemVer-compatible with the changes in this pull request (compared against the base branch).

Details
     Cloning apache/main
    Building datafusion-datasource v55.0.0 (current)
       Built [  48.916s] (current)
     Parsing datafusion-datasource v55.0.0 (current)
      Parsed [   0.033s] (current)
    Building datafusion-datasource v55.0.0 (baseline)
       Built [  50.922s] (baseline)
     Parsing datafusion-datasource v55.0.0 (baseline)
      Parsed [   0.035s] (baseline)
    Checking datafusion-datasource v55.0.0 -> v55.0.0 (no change; assume patch)
     Checked [   0.249s] 223 checks: 223 pass, 31 skip
     Summary no semver update required
    Finished [ 101.577s] datafusion-datasource
    Building datafusion-datasource-csv v55.0.0 (current)
       Built [  48.778s] (current)
     Parsing datafusion-datasource-csv v55.0.0 (current)
      Parsed [   0.012s] (current)
    Building datafusion-datasource-csv v55.0.0 (baseline)
       Built [  49.912s] (baseline)
     Parsing datafusion-datasource-csv v55.0.0 (baseline)
      Parsed [   0.013s] (baseline)
    Checking datafusion-datasource-csv v55.0.0 -> v55.0.0 (no change; assume patch)
     Checked [   0.108s] 223 checks: 223 pass, 31 skip
     Summary no semver update required
    Finished [  99.945s] datafusion-datasource-csv
    Building datafusion-datasource-json v55.0.0 (current)
       Built [  50.156s] (current)
     Parsing datafusion-datasource-json v55.0.0 (current)
      Parsed [   0.013s] (current)
    Building datafusion-datasource-json v55.0.0 (baseline)
       Built [  48.911s] (baseline)
     Parsing datafusion-datasource-json v55.0.0 (baseline)
      Parsed [   0.014s] (baseline)
    Checking datafusion-datasource-json v55.0.0 -> v55.0.0 (no change; assume patch)
     Checked [   0.103s] 223 checks: 223 pass, 31 skip
     Summary no semver update required
    Finished [ 100.313s] datafusion-datasource-json
    Building datafusion-proto v55.0.0 (current)
       Built [  60.038s] (current)
     Parsing datafusion-proto v55.0.0 (current)
      Parsed [   0.019s] (current)
    Building datafusion-proto v55.0.0 (baseline)
       Built [  60.588s] (baseline)
     Parsing datafusion-proto v55.0.0 (baseline)
      Parsed [   0.020s] (baseline)
    Checking datafusion-proto v55.0.0 -> v55.0.0 (no change; assume patch)
     Checked [   0.117s] 223 checks: 223 pass, 31 skip
     Summary no semver update required
    Finished [ 122.026s] datafusion-proto
    Building datafusion-proto-models v55.0.0 (current)
       Built [  28.005s] (current)
     Parsing datafusion-proto-models v55.0.0 (current)
      Parsed [   0.138s] (current)
    Building datafusion-proto-models v55.0.0 (baseline)
       Built [  28.100s] (baseline)
     Parsing datafusion-proto-models v55.0.0 (baseline)
      Parsed [   0.136s] (baseline)
    Checking datafusion-proto-models v55.0.0 -> v55.0.0 (no change; assume patch)
     Checked [   1.824s] 223 checks: 222 pass, 1 fail, 0 warn, 31 skip

--- failure constructible_struct_adds_field: struct exhaustively constructible through public API adds field ---

Description:
A pub struct that could be exhaustively constructed with a literal using only public API has a new pub field, breaking existing exhaustive literals.
        ref: https://doc.rust-lang.org/reference/expressions/struct-expr.html
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.50.0/src/lints/constructible_struct_adds_field.ron

Failed in:
  field CsvScanExecNode.terminator in /home/runner/work/datafusion/datafusion/datafusion/proto-models/src/generated/prost.rs:1986
  field CsvScanExecNode.terminator in /home/runner/work/datafusion/datafusion/datafusion/proto-models/src/generated/prost.rs:1986
  field JsonScanExecNode.newline_delimited in /home/runner/work/datafusion/datafusion/datafusion/proto-models/src/generated/prost.rs:2011
  field JsonScanExecNode.newline_delimited in /home/runner/work/datafusion/datafusion/datafusion/proto-models/src/generated/prost.rs:2011
  field FileScanExecConf.file_compression_type in /home/runner/work/datafusion/datafusion/datafusion/proto-models/src/generated/prost.rs:1957
  field FileScanExecConf.file_compression_type in /home/runner/work/datafusion/datafusion/datafusion/proto-models/src/generated/prost.rs:1957

     Summary semver requires new major version: 1 major and 0 minor checks failed
    Finished [  59.791s] datafusion-proto-models

@github-actions github-actions Bot added the auto detected api change Auto detected API change label Aug 10, 2026
@codecov-commenter

codecov-commenter commented Aug 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 57.14286% with 45 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.34%. Comparing base (e2bdc2c) to head (ee0769f).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
datafusion/proto-models/src/generated/pbjson.rs 0.00% 42 Missing ⚠️
datafusion/proto/src/physical_plan/mod.rs 88.88% 0 Missing and 2 partials ⚠️
datafusion/datasource-csv/src/source.rs 93.33% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #24233      +/-   ##
==========================================
- Coverage   81.35%   81.34%   -0.01%     
==========================================
  Files        1117     1117              
  Lines      397457   397554      +97     
  Branches   397457   397554      +97     
==========================================
+ Hits       323337   323385      +48     
- Misses      55208    55253      +45     
- Partials    18912    18916       +4     

☔ 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.

@kosiew kosiew left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@buraksenn,

Thanks for working on this. The overall approach looks good, and preserving these scan options through physical-plan protobuf serialization makes sense.

I found one issue with the CSV terminator representation that I think needs to be addressed before merging. I also left one non-blocking suggestion to strengthen the roundtrip coverage.

bool newlines_in_values = 7;
bool truncate_rows = 8;
// Custom line terminator. Absent means the default newline terminator.
optional string terminator = 9;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CsvOptions::terminator is an unrestricted Option<u8>, but using a protobuf string here only works for bytes that can be represented as single-byte UTF-8 values. For example, a valid terminator such as 0xFF will fail in CsvSource::try_to_proto when proto_byte_to_string tries to convert it to a string.

Could we use an optional bytes field instead, validate that it contains exactly one byte when decoding, and add a roundtrip test with a non-UTF-8 terminator such as 0xFF?

Since this is a new field, changing its type should not affect existing payloads. Both protobuf string and bytes are length-delimited on the wire.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review and suggestion @kosiew. I've applied it with the additional test case below

}

#[test]
fn roundtrip_csv_scan_preserves_format_options() -> Result<()> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The roundtrip tests do a good job of checking that the reconstructed configuration is preserved. It might also be useful to add a small end-to-end compressed CSV or JSON scan after serialization and deserialization. That would verify that the preserved compression and format options actually make it through to the reader path.

This is just a suggestion and not blocking.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added the test thanks

@buraksenn
buraksenn force-pushed the 24180-preserve-csv-json-scan-options branch from 02ede80 to f67bca2 Compare August 21, 2026 13:39

@kosiew kosiew left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@buraksenn,

Thanks for the follow-up. The issues from the previous review look addressed.

The CSV terminator is now represented as bytes, so arbitrary one-byte values such as 0xFF can roundtrip correctly. The decoder also rejects zero-byte and multi-byte terminators, and the new tests cover both the valid and invalid cases.

The end-to-end compressed scan suggestion is addressed as well. The new gzip JSON-array test serializes and deserializes the physical plan, then executes it successfully, which verifies that the compression and JSON format options make it through to the reader path.

I did not find any new correctness issues in the follow-up changes.

Thanks for addressing the feedback. Looks good to me.

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

Labels

auto detected api change Auto detected API change datasource Changes to the datasource crate proto Related to proto crate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Physical plan proto silently drops CSV/JSON scan options (newline_delimited, terminator, compression)

3 participants