Skip to content

GH-51275: [C++] Allow the CSV Reader to accept multi-character delimiters - #51286

Open
HuaHuaY wants to merge 3 commits into
apache:mainfrom
HuaHuaY:strengthen_csv
Open

HuaHuaY wants to merge 3 commits into
apache:mainfrom
HuaHuaY:strengthen_csv

Conversation

@HuaHuaY

@HuaHuaY HuaHuaY commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

Rationale for this change

Now the type of delimiter in ParseOptions is char, and we can only read csv files whose delimiter is one character. I think we can lift this restriction.

What changes are included in this PR?

Added a higher-priority delimiter_string to ParseOptions, and added support for multi-character delimiters in both the chunker and the parser.

Are these changes tested?

Yes.

Are there any user-facing changes?

Yes. A new option delimiter_string is added to ParseOptions.

Copilot AI lite review requested due to automatic review settings September 10, 2026 13:14
@HuaHuaY
HuaHuaY requested a review from pitrou as a code owner September 10, 2026 13:14
@HuaHuaY
HuaHuaY requested a review from wgtmac September 10, 2026 13:15
@HuaHuaY

HuaHuaY commented Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

@ursabot please benchmark lang=C++

@rok

rok commented Sep 10, 2026

Copy link
Copy Markdown
Member

Benchmark runs are scheduled for commit 31adcd2. Watch https://buildkite.com/apache-arrow and https://conbench.arrow-dev.org for updates. A comment will be posted here when the runs are complete.

Copilot AI 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.

🟡 Changes recommended

CsvFileFormat::Equals() currently compares delimiter even when delimiter_string overrides it, which can produce incorrect inequality for semantically identical parse options.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR extends the Arrow C++ CSV reader to support multi-character field delimiters by introducing a new ParseOptions::delimiter_string that overrides the existing single-character delimiter, and updating the parser/chunker accordingly.

Changes:

  • Add ParseOptions::delimiter_string (higher priority than delimiter) and validate delimiter interactions with quoting/escaping/line endings.
  • Implement multi-character delimiter matching in both the CSV parser and the chunker (lexing boundary finder).
  • Add unit tests covering multi-character delimiters across reader, parser, and chunker.
File summaries
File Description
cpp/src/arrow/dataset/file_csv.cc Include delimiter_string in format equality; reject CSV writing when delimiter_string is set.
cpp/src/arrow/csv/options.h Add ParseOptions::delimiter_string with override semantics.
cpp/src/arrow/csv/options.cc Extend ParseOptions::Validate() for delimiter-string constraints (escaping/quoting/CRLF).
cpp/src/arrow/csv/lexing_internal.h Add delimiter helpers and a streaming delimiter matcher; adjust bulk filter delimiter byte selection.
cpp/src/arrow/csv/chunker.cc Support multi-character delimiter detection (for quote recognition) during chunking when needed.
cpp/src/arrow/csv/parser.cc Add multi-character delimiter matching in the field parser state machine.
cpp/src/arrow/csv/reader_test.cc Add end-to-end reader test for multi-character delimiters.
cpp/src/arrow/csv/parser_test.cc Add parser tests for multi-character delimiters, delimiter-prefix-at-block-end, and single-char delimiter_string.
cpp/src/arrow/csv/chunker_test.cc Add chunker test for multi-character delimiter behavior with partial buffers.
Review details
  • Files reviewed: 9/9 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread cpp/src/arrow/dataset/file_csv.cc Outdated
Copilot AI review requested due to automatic review settings September 10, 2026 13:49
@github-actions github-actions Bot added awaiting committer review Awaiting committer review and removed awaiting review Awaiting review labels Sep 10, 2026

Copilot AI 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.

🔵 Needs a closer look

It changes core CSV parsing/chunking state machines and delimiter handling, which warrants final human review for subtle correctness/performance edge cases.

Review details
  • Files reviewed: 9/9 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread cpp/src/arrow/csv/options.cc
@HuaHuaY

HuaHuaY commented Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

@ursabot please benchmark lang=C++

@rok

rok commented Sep 10, 2026

Copy link
Copy Markdown
Member

Benchmark runs are scheduled for commit a161eff. Watch https://buildkite.com/apache-arrow and https://conbench.arrow-dev.org for updates. A comment will be posted here when the runs are complete.

Comment thread cpp/src/arrow/csv/options.cc
Comment thread cpp/src/arrow/dataset/file_csv.cc
Status ParseOptions::Validate() const {
if (ARROW_PREDICT_FALSE(delimiter == '\n' || delimiter == '\r')) {
return Status::Invalid("ParseOptions: delimiter cannot be \\r or \\n");
// The chunker handles escapes before delimiter matching, so allowing the escape

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is it cleaner to consolidate the two cases like below?

  std::string invalid_chars = "\r\n";
  if (escaping) {
    invalid_chars.push_back(escape_char);
  }

  const bool use_delimiter_string = !delimiter_string.empty();
  const auto invalid_pos =
      use_delimiter_string ? delimiter_string.find_first_of(invalid_chars)
                           : invalid_chars.find(delimiter);
  if (invalid_pos != std::string::npos) {
    return Status::Invalid("ParseOptions: delimiter contains an invalid character");
  }

  const char delimiter_first_byte =
      use_delimiter_string ? delimiter_string.front() : delimiter;
  if (quoting && delimiter_first_byte == quote_char) {
    return Status::Invalid(
        "ParseOptions: delimiter cannot start with the quote character");
  }

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.

However, by doing this, we lose the specific reason for the error.

Comment thread cpp/src/arrow/csv/parser.cc Outdated
Comment thread cpp/src/arrow/csv/parser_test.cc
Comment thread cpp/src/arrow/dataset/file_csv.cc Outdated
}
if (!parse_options.delimiter_string.empty()) {
return Status::NotImplemented(
"Writing CSV files with delimiter_string is not supported");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Perhaps letting users know that it is non spec-compliant?

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.

Currently, even if delimiter_string is a compliant single-character comma, it will be rejected.

Comment thread cpp/src/arrow/csv/chunker_test.cc
Copilot AI lite review requested due to automatic review settings October 9, 2026 15:57
@HuaHuaY
HuaHuaY requested a review from wgtmac October 9, 2026 15:58

Copilot AI 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.

🟡 Changes recommended

Writer-option compatibility can silently produce comma-delimited output for an effectively equal delimiter configuration.

1 open finding
2 resolved since last review

🧠 Review effort: Lite

Comment on lines +507 to +510
if (!parse_options.delimiter_string.empty()) {
return Status::NotImplemented(
"Writing CSV files with delimiter_string is not supported");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants