Skip to content

query: default Lz4 compression fails every query for readonly users when enable_http_compression is 0 (regression from #482) #486

Closed

Description

@polyglotAI-bot

Describe the bug

Since #482 (on main, not released yet), Compression::Lz4 (the default) sends the setting enable_http_compression=1 on every query. Before #482 it sent the HTTP parameter compress=1, which is not a setting.

A user with readonly = 1 can send a setting only if the value is equal to its current value. When enable_http_compression is 0 for that user, the server rejects every query with Code 164 (READONLY). The value is 0 for the user when:

Compression::Zstd has the same problem. It already sent enable_http_compression=1 in 0.15.2.

0.15.2 is not affected for Lz4. The regression ships with the next release if it is not fixed.

Steps to reproduce

  1. As admin: CREATE USER ro IDENTIFIED WITH no_password SETTINGS readonly = 1, enable_http_compression = 0 (on 25.9 and older, SETTINGS readonly = 1 is sufficient).
  2. With the default client (Lz4), run SELECT 1 as user ro.
  3. The query fails with Code 164.

Expected behaviour

The query returns 1, as it does with 0.15.2 on the same server and user.

The server accepts the old parameter from a readonly user and rejects the new setting (25.8.28.1, plain readonly = 1 user, POST SELECT 1):

?compress=1                    -> HTTP 200 (compressed body)
?enable_http_compression=0     -> HTTP 200 (value not changed)
?enable_http_compression=1     -> HTTP 500: Code: 164. DB::Exception: Cannot modify 'enable_http_compression' setting in readonly mode. (READONLY) (version 25.8.28.1 (official build))

Code example

use clickhouse::{Client, Compression};

// user `ro`: readonly = 1, enable_http_compression = 0
let client = Client::default()
    .with_url("http://localhost:8123")
    .with_user("ro");

let r = client.query("SELECT 1").fetch_one::<u8>().await;
// main:  Err(BadResponse("Code: 164. ... Cannot modify 'enable_http_compression' setting in readonly mode. (READONLY) ..."))
// with `.with_compression(Compression::None)`: Ok(1)

Results of fetch_one on server 26.3.28.5, user with readonly = 1, enable_http_compression = 0:

Compression 0.15.2 (fcb8b02) main (af76d45)
None Ok(1) Ok(1)
Lz4 (default) Ok(1) Code 164
Zstd(3) Code 164 Code 164

Same result for main on 26.9.4.3. On 26.3 and 26.9, a plain readonly = 1 user (no override) passes, because the server default is already 1.

Error log

BadResponse("Code: 164. DB::Exception: Cannot modify 'enable_http_compression' setting in readonly mode. (READONLY) (version 25.8.28.1 (official build))")

On server 25.8, the integration tests query_readonly::readonly_user (tests/it/query_readonly.rs:178) and query_syntax::query_with_readonly_user (tests/it/query_syntax.rs:213) fail with this error on main and pass on fcb8b02. They fail with cargo test and with cargo test --no-default-features (the clickhouse-ext-arrow dev-dependency enables the default lz4 feature). They pass with --all-features, because test-util makes Compression::default() return None.

CI does not see this: the test-local matrix is [ latest, head, 26.7 ], and all three default enable_http_compression to 1.

Root cause

src/query.rs, Query::do_execute (used by execute, fetch*, fetch_bytes, fetch_native):

  • lines 265-267: if self.client.compression.is_enabled() appends enable_http_compression=1.
  • lines 274-277: the Lz4 arm appends enable_http_compression=1 again, so the parameter is sent twice.
  • lines 279-284: the Zstd arm appends it again, plus http_zlib_compression_level.

The response side does not need the setting: Decompress::new (src/response.rs:290) selects the decoder from Content-Encoding, and uses the plain stream when the header is absent.

Suggested fix

One possible shape:

  • Do not send enable_http_compression for Lz4 / Zstd. Send only Accept-Encoding. When the effective value is 1 (server default since 25.10), the server compresses. When it is 0, the server sends a plain body, which Decompress::new already handles. This matches the plan in Verify compression settings used by the client after 25.10 #345. Trade-off: no response compression when the effective value is 0. Servers 25.9 and older are out of support per the README policy (SECURITY.md).
  • Do not go back to compress=1 for Lz4: that returns native-framed blocks in the server's default codec, which is Default (LZ4) compression breaks on ClickHouse 26.9+: Lz4Decoder hardcodes magic 0x82 but the server default codec is now ZSTD(3) #468.
  • Remove the duplicate append (lines 265-267 vs 275).
  • http_zlib_compression_level is also a setting. For a readonly user it fails the same way if the level is not equal to the user's current value (server default 3; Compression::zstd() uses level 3, so the default passes).
  • Add an integration test with a readonly = 1, enable_http_compression = 0 user, so CI catches this on every server version in the matrix.

Contrast case to keep: writable users on 25.10+ must still get compressed responses.

Configuration

Environment

ClickHouse server

  • ClickHouse Server version: 25.8.28.1, 26.3.28.5, 26.9.4.3
  • ClickHouse Server non-default settings, if any: user readonly = 1; on 25.10+ also enable_http_compression = 0 for the user or its profile.
  • CREATE TABLE statements for tables involved: none (SELECT 1).

Found by automated analysis (Polyglot AI) while running the test suite of #462 against server 25.8. Verified against live servers 25.8.28.1, 26.3.28.5 and 26.9.4.3, with the client built from main and from 0.15.2.

Related: #345, #468, #373 (an earlier READONLY Code 164 regression, for the readonly setting).

Activity

  1. added a commit that references this issue on Oct 8, 2026
    5e26e60
  2. abonander commented on Oct 13, 2026

    @abonander
    Contributor

    Fixed in 0.15.3 release PR

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions