Skip to content

fix: allow explicit COMPRESSION = NONE for ParquetFormat - #75

Open
simozzy wants to merge 2 commits into
databendlabs:mainfrom
PlaidCloud:upstream-pr/compression-none-sentinel
Open

simozzy wants to merge 2 commits into
databendlabs:mainfrom
PlaidCloud:upstream-pr/compression-none-sentinel

Conversation

@simozzy

@simozzy simozzy commented Jun 18, 2026 •

Copy link
Copy Markdown
Contributor

Problem

ParquetFormat(compression=Compression.NONE) raises
TypeError: Compression should be None, ZStd, or Snappy., so there is no way to
request uncompressed Parquet output. Databend accepts COMPRESSION = NONE for
Parquet and writes UNCOMPRESSED; omitting the option gives the server default,
which is ZSTD.

Fix

ParquetFormat accepts Compression.NONE alongside ZSTD and SNAPPY and emits
it as COMPRESSION = NONE. None still omits the option. CSV/TSV/NDJSON are
unchanged.

The four format classes also set inherit_cache = False (matching
CopyIntoTable/CopyIntoLocation). Without it, compiling a format raises
SQLAlchemy's "will not make use of SQL compilation caching" SAWarning, which the
test suite treats as an error.

Tests

tests/test_copy_format.py:

  • Compile tests: None omits COMPRESSION; Compression.NONE emits
    COMPRESSION = NONE for all four formats; explicit codecs are emitted;
    ParquetFormat rejects GZIP/BZ2/ZIP.
  • Unload tests: COPY INTO a stage, then read the file's row-group sizes with
    inspect_parquet. With Compression.NONE the compressed and uncompressed sizes
    are equal; with the option omitted, compressed is smaller.

Run against Databend v1.2.881: the unload test fails on this PR's previous
revision (66 != 249, i.e. ZSTD was written) and on main (the TypeError), and
passes with this change.

🤖 Generated with Claude Code

…mats

Compression.NONE is a truthy Enum member, so the `if compression:` guard
in CSVFormat/TSVFormat/NDJSONFormat/ParquetFormat treated the
no-compression sentinel as 'a codec was specified'. For ParquetFormat this
raised TypeError('Compression should be None, ZStd, or Snappy.') outright;
for the others it emitted a spurious COMPRESSION = NONE option.

Guard now skips both None and Compression.NONE, so the uncompressed case
renders no COMPRESSION option. Explicit codecs are unchanged, and
ParquetFormat still rejects codecs its writer can't use (gzip/bz2/zip).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e9239985e4

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread databend_sqlalchemy/dml.py Outdated
)
self.options["MISSING_FIELD_AS"] = f"{missing_field_as}"
if compression:
if compression and compression is not Compression.NONE:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject NONE for Parquet instead of using defaults

When callers pass ParquetFormat(compression=Compression.NONE), this condition now skips validation and emits no COMPRESSION option. For Parquet, Databend only supports ZSTD/SNAPPY for this option and the omitted-option default is ZSTD, so a user asking for no compression silently gets ZSTD-compressed output instead of the previous clear rejection. Keep Compression.NONE rejected for Parquet rather than treating it like None.

Useful? React with 👍 / 👎.

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.

Addressed in c83bc03. NONE is no longer dropped; it's emitted as
COMPRESSION = NONE, which current Databend honours for Parquet (verified with
inspect_parquet).

@simozzy

simozzy commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

CI failure is unrelated to this PR — it's a Databend nightly server flake.

This PR only changes COPY INTO compression handling (dml.py) and its copy-format tests;
neither goes near the sequence code path.

The run was 707 passed, 677 skipped, 2 errors — the 2 errors are both at teardown of the
compliance suite's SequenceTest, on server-side errors:

[3101]SequenceAlreadyExists: 'SequenceMeta 'noret_sch_id_seq' already exists: create_sequence'
[1126]unknown sequence "noret_sch_id_seq"   (dropping it at teardown)

The server both refuses to create a sequence (already exists) and then can't find it to drop it.
It's non-deterministic: PR #76, run against the same nightly, hit the same SequenceTest from the
opposite side (nextval reporting a just-created sequence as unknown).

Root cause: CI is pinned to databend:nightly. Last green main was 2026-02-28; these runs are on
nightly v1.2.920 / driver v0.34.0, which made sequence create/get/drop racy. Every open PR inherits it.

Suggest a re-run (may pass on retry) and/or pinning CI to a known-good Databend tag. Happy to file
an upstream issue for the sequence flake.

@hantmac hantmac left a comment

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.

Thanks for adding regression coverage and making the compilation-cache policy explicit. I cannot merge this as-is: None (use the server default) and Compression.NONE (explicitly disable compression) are not equivalent for Parquet. I reproduced the generated SQL locally and checked the Databend option parser/defaults. Please preserve the explicit no-compression request and update the tests rather than asserting that it disappears. The existing inline finding about the Parquet default remains unresolved.

This is a correctness issue independent of the historical CI failure; those old logs have expired, so I could not independently verify the author's sequence-flake diagnosis.

Comment thread databend_sqlalchemy/dml.py Outdated
)
self.options["MISSING_FIELD_AS"] = f"{missing_field_as}"
if compression:
if compression and compression is not Compression.NONE:

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.

[P2] Do not silently turn explicit NONE into the Parquet default.

With this change, ParquetFormat(compression=Compression.NONE) compiles to FILE_FORMAT = (TYPE = PARQUET). Databend fills in ZSTD when the option is omitted, so a request for uncompressed output silently produces compressed output. None should mean 'leave the server default', not 'uncompressed'.

Source: Parquet option parsing defaults to Zstd. Current Databend also has a Parquet NONE regression test, so NONE is not inherently an invalid SQL option on current servers.

Please distinguish omission from explicit NONE: emit COMPRESSION = NONE when supported, or keep an explicit rejection for any supported server versions that cannot honor it. Do not silently omit it. Adjust test_no_compression_emits_no_option accordingly, and preferably verify the resulting Parquet codec in an integration test. The same sentinel-erasing guards for the text formats are unnecessary: their existing COMPRESSION = NONE is a legitimate explicit option.

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.

You're right: omission and explicit NONE aren't the same for Parquet, and I'd
conflated them. Fixed in c83bc03:

  • ParquetFormat(compression=Compression.NONE) now emits COMPRESSION = NONE;
    None still omits it and gets the server's ZSTD default.
  • The CSV/TSV/NDJSON guards are reverted, so they emit COMPRESSION = NONE again,
    as before.
  • test_no_compression_emits_no_option is split into
    test_omitted_compression_emits_no_option and test_explicit_none_is_emitted.
  • Added the integration test you suggested: unload to a stage and compare
    inspect_parquet's compressed vs uncompressed row-group sizes. Against v1.2.881:
    NONE 247/247, ZSTD 63/246, SNAPPY 61/246, omitted 63/246.

On older servers: the Parquet writer's NONE arm arrived with
databendlabs/databend#17793. Before that, from reading the source rather than
running an old server, NONE ends in an error, so the request should still fail
loudly rather than being silently changed. That seemed better than a client-side
version check, but I can add one if you'd prefer.

Omitting COMPRESSION means the server default, and for Parquet that is
ZSTD, so dropping an explicit Compression.NONE silently produced
compressed output. CSV/TSV/NDJSON go back to emitting it as before, and
ParquetFormat now accepts NONE alongside ZSTD and SNAPPY.

The unload test checks the written file's codec via inspect_parquet:
compressed and uncompressed row-group sizes match only for NONE.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@simozzy simozzy changed the title fix: treat Compression.NONE sentinel as uncompressed in COPY INTO formats fix: allow explicit COMPRESSION = NONE for ParquetFormat Oct 19, 2026
@simozzy

simozzy commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the careful review. Fixed in c83bc03, details on the inline thread.

On CI: I re-checked locally against a Databend v1.2.881 release binary with
pytest -n4. On unmodified main (ff06513), SequenceTest failed in 5 of 6 runs
(test_nextval_direct, test_insert_lastrowid,
test_insert_roundtrip_translate[True]). This branch shows the same and nothing
else: 718 passed, 1 SequenceTest failure.

@simozzy

simozzy commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

@hantmac Could you possibly check why the CI runs are failing?

@simozzy
simozzy requested a review from hantmac October 22, 2026 15:11
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.

3 participants