Skip to content

Fix NDJSON and CSV export handling of existing file contents - #531

Open
officialasishkumar wants to merge 1 commit into
RubixML:masterfrom
officialasishkumar:fix-exporter-newline-handling
Open

officialasishkumar wants to merge 1 commit into
RubixML:masterfrom
officialasishkumar:fix-exporter-newline-handling

Conversation

@officialasishkumar

Copy link
Copy Markdown

Fixes #529

Exporting in append mode didn't take the existing file on disk into account. This caused three problems:

  • NDJSON: if the file didn't end with a newline, the first exported record was appended to the last line, which made the file unreadable (JSON Error on line N: Syntax error). NDJSON::export() now writes a line separator first in that case. This is the same fix 35864a4 made for CSV.
  • CSV, overwrite: the leading-separator check ignored $overwrite. With header: false, overwriting a file that had no trailing newline truncated it and then wrote a stray blank line before the first row. The check now only runs when appending.
  • CSV, empty file: the header was only written if the file didn't exist. An existing empty file, such as one from tempnam() or touch, got no header. Reading it back with header: true then used the first data row as the header and silently lost it. Empty files are now treated like missing files.

Both exporters now call clearstatcache() for the target path before checking it. Otherwise filesize() could return a stale value from before an earlier export in the same process.

Tests

  • CSVTest::exportOverwriteNoTrailingNewline: no leading blank line when overwriting.
  • CSVTest::exportAppendToEmptyFileWritesHeader: the header is written to an empty file and is not repeated on the next append.
  • NDJSONTest::exportAppendNoTrailingNewline: a separator is added when appending to a file without a trailing newline, and none is added when the file already ends with one.
  • NDJSONTest::exportOverwriteNoTrailingNewline: no separator is written when overwriting.

The first three fail on master. The full suite, PHPStan and the coding style check pass.

Exporting in append mode did not account for the state of the file
already on disk:

- NDJSON::export() wrote the first new record directly after the last
  byte of the file. If the existing file did not end with a newline,
  the first new record was glued onto the previous line, producing
  invalid NDJSON that the extractor could no longer read. It now
  inserts a line separator before the first record in that case,
  mirroring the fix applied to the CSV extractor in 35864a4.

- CSV::export() decided whether a leading separator was needed without
  looking at the overwrite flag. With header disabled and overwrite
  enabled, the file was truncated and then a stray blank line was
  written before the first row. The separator is now only considered
  when appending.

- CSV::export() only wrote the header when the file did not exist. An
  existing but empty file (e.g. one created by tempnam() or touch) got
  no header, so reading it back with the header enabled consumed the
  first data row as the header and silently dropped it. Empty files
  are now treated the same as missing files.

Both exporters clear the stat cache for the target path before
inspecting it so that file size checks reflect writes made by earlier
exports in the same process.

Fixes RubixML#529

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

NDJSON and CSV export mishandle existing files without a trailing newline or with no content

1 participant