Skip to content

feat(files): read Excel, XML and ZIP inputs - #187

Merged
kvlonge merged 1 commit into
mainfrom
feat/xlsx-xml-zip
Nov 12, 2026
Merged

kvlonge merged 1 commit into
mainfrom
feat/xlsx-xml-zip

Conversation

@kvlonge

@kvlonge kvlonge commented Nov 12, 2026

Copy link
Copy Markdown
Contributor

Adds three file inputs to kind = "files" sources. SQLLoad reads all of them itself, on every destination.

  • Excel: format = "xlsx" with xlsx = { sheet, header, skip_rows }. Cells keep their stored types: int, float, bool, date, timestamp_ntz and time. Empty cells and error cells load as NULL. Cells are streamed with calamine's cell reader rather than a dense range, and a sheet is capped at 20 million values.
  • XML: format = "xml" with xml = { record_path, lists }.
    • Each element at record_path becomes a row. Its attributes and child elements become columns, matched by local name.
    • A repeated or listed child loads as a JSON array, and a nested element as a JSON object.
    • xsi:nil loads as NULL. Namespace declarations and xsi: attributes are not columns.
    • These fail with an error: a truncated document, a UTF-16 document, duplicate attribute names, and a record_path whose parent elements the document doesn't have.
  • ZIP: compression = "zip", with a members glob. Each matching member loads as a file of its own, keyed <archive>/<member> and versioned by its archive.
    • Only the archive's central directory and the members being read are fetched, using range requests. The archive is never downloaded whole.
    • Members are limited to 1 GiB each. An archive with duplicate member names fails.
    • CSV, xlsx and XML members are all supported, through one source per format over the same path.

Dependencies: this adds calamine 0.38, quick-xml 0.42 and zip 9 (with deflate). calamine turns on zip's deflate, which switches flate2 to the zlib-rs backend across the workspace, so gzip output bytes differ from 0.34. No test or feature depends on byte-identical gzip output; tests decompress before comparing. CsvOut::Gzip is boxed to satisfy clippy's large_enum_variant.

Docs: a new "Excel, XML and ZIP files" section in sources/files.mdx, marked "From 0.35.0 (not released yet)".

Testing:

  • make check and make test-unit pass.
  • The new test_read_containers cases cover typed xlsx cells, sheets and skip_rows, header clashes, XML shapes, namespaces and errors, ZIP members and globs, xlsx and XML inside a ZIP, and an archive larger than one 4 MiB range block.
  • A CLI end-to-end run on synthetic data loaded one ZIP holding CSV, xlsx and XML through three sources into DuckDB, and an append rerun loaded 0 rows.

Closes CHI-1238.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying sqlload with  Cloudflare Pages  Cloudflare Pages

Latest commit: da3ba1a
Status: ✅  Deploy successful!
Preview URL: https://2fe47224.sqlload.pages.dev
Branch Preview URL: https://feat-xlsx-xml-zip.sqlload.pages.dev

View logs

@kvlonge
kvlonge merged commit 2b496b6 into main Nov 12, 2026
13 checks passed
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.

1 participant