Visitar URL original
Accessing tables as files, query construction and out-of-band modification in HTTP interface by alexey-milovidov · Pull Request #105249 · ClickHouse/ClickHouse · GitHub
Skip to content

Accessing tables as files, query construction and out-of-band modification in HTTP interface - #105249

Merged
alexey-milovidov merged 244 commits into
masterfrom
http-table-as-file
Aug 17, 2026
Merged

alexey-milovidov merged 244 commits into
masterfrom
http-table-as-file

Conversation

@alexey-milovidov

@alexey-milovidov alexey-milovidov commented May 18, 2026 •

Copy link
Copy Markdown
Member

Implements #46925 according to the updated spec.

Closes: #46925
Related: ClickHouse/clickhouse-docs#6398

What's in the box

  • Path → database/table/format/compression. New settings http_allow_database_as_path, http_allow_table_as_file, http_allow_filters_as_path, http_allow_filters_as_unrecognized_url_parameters let the HTTP interface interpret /database/table.format.compression (and hive-style /name=value/ partitions) in the URL path. When a path identifies a table, the request is processed as SELECT * FROM database.table.
  • Query construction settings. select, order, sort, filter, page wrap the base query as SELECT [select] FROM (...) [WHERE filter] [ORDER BY order]. page=N translates to offset = limit * (N - 1) (errors if limit is unset or offset is also set). Multiple filter URL parameters are combined with AND.
  • Format/compression overrides. default_format, format, input_format, output_format are now first-class settings; the explicit format / output_format overrides win over the FORMAT clause in the query and the file extension in the path, while default_format is only the fallback used when nothing else selects a format (so the path extension wins over it). A generic compression setting wraps the response body (independent of HTTP Content-Encoding). The URL path file extension is equivalent; specifying both with conflicting values throws.
  • Implicit table. When a path table coexists with a user query, the path table is exposed via implicit_table_at_top_level, so ?query=SELECT a, b against /hits.csv reads from hits with no FROM. If the user's query already has FROM, the path component only seeds the download filename.
  • Content-Disposition. Binary or compressed responses get Content-Disposition: attachment; filename=…, where the filename comes from the URL path component (or result.<format>.<compression> as fallback).
  • database and the format settings are now real settings. The database URL parameter and the X-ClickHouse-Database / X-ClickHouse-Format headers all flow through the regular settings pipeline (the header still overrides the URL parameter to preserve historical precedence). X-ClickHouse-Database is an alias for database; X-ClickHouse-Format is an alias for output_format (not default_format), i.e. an explicit override of the response format that also wins over a FORMAT clause in the query. It maps to output_format rather than to the bidirectional format because the header has always described the response only, so it must not reinterpret the body of an INSERT. For the same reason, --format in clickhouse-client maps to output_format (it has always been output-only there), while in clickhouse-local it keeps its historical bidirectional meaning and maps to format. They are marked changeable_in_readonly in the shipped default profile so they remain settable on HTTP GET (which forces readonly = 2) and for readonly = 1 users. Because database is a real setting, a profile that constrains it (e.g. marks it readonly or restricts its values) is enforced consistently on every way of choosing the current database: USE, SET database = ..., the HTTP database parameter/header, and the connect-time database of the native TCP, MySQL, and PostgreSQL protocols. In clickhouse-local, an explicitly configured database (--database or a config-file database key) is mirrored into the database setting so a profile-inherited value cannot override it.

HTTPHandler::processQuery is restructured so that authentication and the user profile are applied first, then the auth-related parameter (role), then the read-only enforcement, and only then the general settings.

Changelog category

  • New Feature

Changelog entry

Added a way to access tables and databases via URL paths in the HTTP interface (e.g. `/database/table.format.gz?filter=a>0`), plus new settings (`http_allow_database_as_path`, `http_allow_table_as_file`, `http_allow_filters_as_path`, `http_allow_filters_as_unrecognized_url_parameters`, `select`, `order`, `sort`, `filter`, `page`, `compression`, `format`, `input_format`, `output_format`, `default_format`, `database`) that compose with one another and with the existing `query` URL parameter.

Documentation entry for user-facing changes

🤖 Generated with Claude Code


Note

High Risk
High risk because it significantly changes HTTP request routing/parsing and introduces many new per-query settings that affect query construction, output formatting, compression, and readonly constraints, which could impact security and compatibility.

Overview
Enables “table-as-file” and query shaping via the HTTP interface. The server can now interpret URL paths like /db/table.format[.compression] (plus optional hive-style name=value path filters) to auto-generate SELECT * FROM ..., apply select/filter/order/sort wrappers, and support paging via new page setting (translated into limit/offset).

Promotes HTTP-specific knobs into first-class settings and aligns client behavior. Adds new settings for database, default_format, format, input_format, output_format, compression, and HTTP path feature gates; updates HTTP handler to route headers/URL params through the normal settings/constraints pipeline (including readonly carve-outs), adds generic response-body compression and Content-Disposition for binary/compressed outputs, and updates client/local tooling to mirror config/CLI format/database options into per-query settings.

Adjusts core settings/exec behavior for compatibility. Widens limit/offset to Double (supporting negative/fractional values), updates analyzers/interpreters/cluster proxy to handle/strip initiator-only settings in distributed queries, refines handler factory ordering/prefix mounting, and adds/updates stateless tests and configs for the new HTTP path semantics and hints.

Reviewed by Cursor Bugbot for commit 8efcce8. Bugbot is set up for automated code reviews on this repo. Configure here.

alexey-milovidov and others added 2 commits May 18, 2026 15:11
Implements the spec from
#46925 (comment).

Adds settings to interpret URL path components as a database, table, format,
compression, and filters (hive partitioning style), plus settings for query
construction (`select`, `order`, `sort`, `filter`, `page`) and format/compression
overrides (`format`, `input_format`, `output_format`, `default_format`,
`compression`).

When the URL path identifies a table, the request is processed as
`SELECT * FROM database.table`, and combined with the query-construction settings
the wrapped query becomes `SELECT [select] FROM (...) [WHERE filter] [ORDER BY order]`.
When both a path table and a `query` URL parameter are present, the table is
exposed via `implicit_table_at_top_level` so the query can omit `FROM`.

Binary or compressed responses set `Content-Disposition: attachment` so a
browser downloads them with a sensible filename.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds a `database` setting (alongside the existing `default_format` setting) so
that both can be set per-request via the HTTP interface without special handling.

The `database` and `default_format` URL parameters and `X-ClickHouse-Database`
and `X-ClickHouse-Format` headers now flow through the regular settings
pipeline. The header overrides the URL parameter, preserving the historical
precedence.

To remain settable on read-only HTTP methods (GET forces `readonly = 2`, and
some users have `readonly = 1` in their profile), both settings are marked
`changeable_in_readonly` in the shipped `users.xml` default profile.

The handler is restructured so that authentication and the user profile are
applied first, then the auth-related parameter (`role`), then the read-only
constraint, and only then the general settings — including `database` and
`default_format`. After settings are applied, the current database is set
based on the resolved value (combined with any database from the URL path).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@clickhouse-gh

clickhouse-gh Bot commented May 18, 2026 •

Copy link
Copy Markdown
Contributor

Workflow [PR], commit [2707bc5]

Summary: ✅


AI Review

Summary

This PR significantly expands the HTTP interface: path-style table access, query-construction settings, first-class database / format settings, and AST-level wrapping for SELECT / UNION. The current head is close, but I still see one unresolved native-protocol blocker and one deliberate-but-real backward-compatibility regression for attached stored views.

Findings

❌ Blockers

  • [src/Client/ClientBase.cpp:1606], [src/Client/Connection.cpp:1099], [src/Core/BaseSettings.h:652] The new semantics-affecting settings are still serialized to old TCP peers without any per-setting revision/provenance gate. settingsWithoutCompatibilityDerived only strips compatibility-derived changes; when a new client explicitly sets database, select, filter, order, sort, page, format, input_format, output_format, or default_format, Connection::sendQuery still emits them and BaseSettings::read on an older server will silently skip the unknown names instead of failing closed. That means the same request can run against the wrong database, the wrong format, or the unwrapped query plan during a rolling upgrade. Suggested fix: add minimum-peer-revision metadata plus explicit-vs-compatibility provenance, then make the send sites reject explicitly requested unsupported settings while dropping compatibility-only defaults for old peers.
  • [dismissed by author -- https://github.com/ClickHouse/ClickHouse/pull/105249#discussion_r3463515204] [src/Analyzer/QueryTreeBuilder.cpp:292], [src/Storages/StorageView.cpp:390], [tests/queries/0_stateless/04821_attach_view_construction_settings.sh:28] The branch intentionally keeps loading old stored view metadata containing SETTINGS limit / offset, but it no longer preserves the stored cap. StorageView stores the attached definition verbatim, QueryTreeBuilder strips limit / offset from the inner SETTINGS without materializing them, and 04821_attach_view_construction_settings explicitly expects SELECT count() FROM db.v_old to return 10 after injecting SETTINGS limit = 3 into the metadata file. That is a silent upgrade-time semantic change for existing on-disk view definitions, not just a restriction on new CREATEs. Suggested fix: either keep executing legacy stored limit / offset semantics on attach (for example by normalizing legacy definitions on load) or fail the attach/upgrade path loudly instead of accepting metadata and changing query results.
Tests
  • ⚠️ Add a focused mixed-version TCP test where a 26.8 client explicitly sends one of the new semantics-affecting settings (for example SETTINGS database = 'db2' or SETTINGS page = 2) to a 25.4 server and assert the direct client→server path fails closed. The existing compatibility coverage only proves the compatibility-derived-default case.
Final Verdict

Changes requested.

LLVM Coverage Report

Metric Baseline Current Δ
Lines 86.70% 86.70% +0.00%
Functions 92.00% 92.00% +0.00%
Branches 79.10% 79.10% +0.00%

Changed lines: Changed C/C++ lines covered: 1719/1864 (92.22%) · Uncovered code

Full report · Diff report

@clickhouse-gh clickhouse-gh Bot added the pr-feature Pull request with new product feature label May 18, 2026
Comment thread src/Server/HTTPHandler.cpp Resolved
Comment thread src/Server/HTTPQueryConstructor.cpp Resolved
…nstraints

The dynamic-query handler now accepts an optional `url_prefix` element in its
`<http_handlers>` rule. When set, the factory adds a non-strict-path filter so
the handler matches any URL starting with the prefix, and the handler itself
strips the prefix from the URL path before parsing the remainder as
`database/table.format[.compression]` (or hive-partition filters).

Example config:

    <http_handlers>
        <rule>
            <url_prefix>/api/v1</url_prefix>
            <handler><type>dynamic_query_handler</type></handler>
        </rule>
        <defaults/>
    </http_handlers>

A request `GET /api/v1/default/hits.CSV` is then processed as if the path were
`/default/hits.CSV` — the `default` database, the `hits` table, and the `CSV`
output format.

Also extends the `<changeable_in_readonly/>` set in the shipped default profile
to all query-construction settings (`format`, `input_format`, `output_format`,
`compression`, `limit`, `offset`, `page`, `select`, `order`, `sort`, `filter`,
`implicit_table_at_top_level`) so they remain settable on HTTP GET and from
`readonly = 1` user profiles.

The stateless test grows a configured `/api/v1` handler (via
`tests/config/config.d/http_url_prefix.xml`) and a few smoke-test cases.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Comment thread src/Server/HTTPHandlerFactory.cpp Resolved
…rsing

Addresses issues called out by the automated review on
#105249:

* Register the Prometheus handler before the dynamic-query catch-all in
  `addDefaultHandlersFactory`, so `/metrics` is no longer shadowed when path
  features are enabled.

* `Settings::addToProgramOptions` now skips setting names that are already
  registered as command-line options (mirroring
  `MergeTreeSettings::addToProgramOptionsIfNotPresent`). Fixes the
  `option '--database' is ambiguous` error from the fast-test job after the
  new `database` setting was added.

* Move URL path parsing in `HTTPHandler::processQuery` to *after*
  `applySettingsChanges`, and gate on the per-context `http_allow_*` settings
  rather than the server-default settings. When all path features are off in
  the authenticated user's settings, the URL path is ignored entirely.

* `parseURLParameterAsFilter` now handles the case where HTMLForm splits on
  the operator's `=`. `?a!=2`, `?a>=2`, `?a<=2` are now reassembled to
  `a != 2`, `a >= 2`, `a <= 2` respectively. Operators without an `=` at all
  (`?a>2`, `?a<>2`, `?f(x)>3`) keep using the full-expression fallback.

* Add an "Ok" comment to the catch-all in `HTTPQueryConstructor.cpp`
  (silently falling back to "not binary" for unknown formats); the style
  check refuses bare `catch (...)` blocks.

* Test additions: per-user disable of path features ignores the URL path;
  `?a!=2`, `?a>=2`, `?a<>2` non-encoded operator forms.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Comment thread src/Server/HTTPHandlerFactory.cpp Resolved
Comment thread src/Server/HTTPHandler.cpp Resolved
@alexey-milovidov alexey-milovidov changed the title Accessing tables as files in HTTP interface Accessing tables as files, query construction and out-of-band modification in HTTP interface May 18, 2026
alexey-milovidov and others added 2 commits May 18, 2026 17:38
* Drop the global-defaults gate on the dynamic-query handler's catch-all
  filter and always route arbitrary paths to it (after strict handlers).
  Routing happens before authentication so we cannot read per-user settings
  here; instead, the per-user `http_allow_*_as_path` settings are honored
  inside `HTTPHandler::processQuery`, where path parsing is gated on the
  authenticated context.

* When path parsing is invoked but the path contains components that no
  enabled feature can claim (e.g. a non-filter, non-table component while
  `http_allow_database_as_path` is off), return an empty `HTTPPathInfo`
  instead of throwing — the URL path is effectively ignored and the
  request proceeds as if at the root URL. Without this, requests like
  `/hits` with `http_allow_table_as_file=0` would fail with a confusing
  "Database name in path is disabled" error.

* Path-derived `default_format` now defers to an explicit `default_format`
  setting (in addition to `format` and `output_format`). Matches the
  documented precedence: explicit overrides win over the file extension.

* Style: rephrase the `parseURLParameterAsFilter` doc comment to avoid
  `!",` which trips the `check_cpp` style rule banning that punctuation
  sequence ("looks dirty, unconfident").

* Add a regression test for the format-precedence rule.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`sort=1,-2` now produces `ORDER BY 1, 2 DESC`, ordering by the first column
ascending and the second column descending. This complements the existing
identifier-based form (`sort=a,-b`) and matches ClickHouse SQL where bare
integers in `ORDER BY` are interpreted positionally.

Items in `sort=` that consist entirely of decimal digits are emitted as bare
integer literals (not back-quoted); anything else continues to go through the
identifier validation path.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Comment thread src/Server/HTTPHandler.cpp Resolved
`limit` and `offset` settings are widened from `UInt64` to `Double`, so they
can hold negative or fractional values. The same applies to the new `page`
setting. The values now flow through to SQL `LIMIT` / `OFFSET` clauses,
where ClickHouse's existing native support for negative `LIMIT` ("last N
rows") and fractional `LIMIT` / `OFFSET` (a share of the result) takes over.

Page semantics:
- Positive page `N` with limit `L`: offset = `L * (N - 1)`; limit stays `L`.
- Negative page `N` with limit `L`: limit becomes `-L` and offset is
  `L * (N + 1)`, so `page=-1` is the last page, `page=-2` the second-to-last,
  and so on. This composes with ClickHouse's negative-`LIMIT` semantics:
  `LIMIT -L OFFSET 0` returns the last `|L|` rows; `LIMIT -L OFFSET -L`
  returns the rows before those, etc.

Call sites that consume `Setting::limit` / `Setting::offset` in
`InterpreterSelectWithUnionQuery`, `QueryTreeBuilder`, and
`ClusterProxy::executeQuery` are updated to use `Float64` arithmetic and to
emit `ASTLiteral(Field(double))` so the resulting `LIMIT` / `OFFSET`
expressions carry the user's value verbatim. The legacy `LimitStep` /
`OffsetStep` fallback (only reached for `WITH FILL` / `WITH TIES`) is
preserved for non-negative integer values.

Test additions: `?limit=-1`, `?limit=0.5`, `?limit=0.5&offset=0.5`,
`?limit=1&page=-1`, `?limit=1&page=-2`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ault_format always changeable in readonly

The previous fast-test failure was caused by `Settings::addToProgramOptionsAsMultitokens`
not having the duplicate-skip logic that `Settings::addToProgramOptions` already had.
With `--allow_repeated_settings` (which the CI passes), the client's `--database`
CLI option collided with the new `database` setting and boost::program_options reported
the option as ambiguous on every command. Applies the same skip logic to the
multitokens path. Also catches `boost::bad_any_cast` in `addToClientOptions`: the
collision means the value stored under the shared name belongs to the client option
(`String`), not the multitokens setting (`Strings`), and we should not try to read
it as the setting.

For backward compatibility, `database` and `default_format` are now always
changeable in `readonly` mode regardless of the user profile's `<constraints>`.
Operators upgrading from a version where these were special URL parameters did
not need a `<changeable_in_readonly/>` entry, and breaking those scenarios on
upgrade would be unfriendly. The setting-profile `<constraints>` block remains
the canonical way to widen *other* settings; this hardcoded list (`database`,
`default_format`) only ensures that the former special-parameter behavior is
preserved.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Comment thread src/Server/HTTPHandler.cpp Resolved
alexey-milovidov and others added 3 commits May 18, 2026 19:54
…ptions into matching settings

`--database` (and its `-d` short alias), `--format` (`-f`), `--input-format`,
and `--output-format` are now mirrored into the corresponding `database`,
`format`, `input_format`, and `output_format` settings on `cmd_settings`. This
means the value is sent to the server as a regular per-query setting in
addition to seeding the TCP connection's initial database / the client's
default-format selection. The user-facing effect: `clickhouse-client
--database=foo` behaves the same way as passing `?database=foo` over HTTP.

The settings registration still uses the duplicate-skip logic added earlier
(`--database` / `--format` collide with the settings' auto-registered names),
so `boost::program_options` keeps the client-side option as the canonical
parser for those names; we just copy the value across after parsing.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
… drop client-side slots

The previous commit kept dedicated `("database,d", ...)` and `("format,f", ...)`
client-side `boost::program_options` declarations and worked around the name
collision with the new `database` / `format` settings via a duplicate-skip in
`Settings::addToProgramOptions[AsMultitokens]`. The dedicated client slots
existed only so that `-d` and `-f` short aliases would parse.

This commit teaches the setting registration to attach those short aliases
directly: when emitting the `boost::program_options` `option_description` for
`database` or `format`, we splice the short character into the name (boost's
`"name,short"` convention) so a single registration handles both `--database`
and `-d` (and `--format` and `-f`). The client no longer needs to declare those
options or hand the value back into the setting after parsing — `cmd_settings`
gets the value directly through the option's notifier.

Removed:
  * `("database,d", ...)` and `("format,f", ...)` from `ClientBase::addCommonOptions`
  * The manual copy of `--database` / `--format` values from
    `boost::program_options::variables_map` to `cmd_settings` (no longer needed)
  * The duplicate-skip pass and `boost::bad_any_cast` fallback in
    `Settings::addToProgramOptions[AsMultitokens]` and `addToClientOptions`
    (no longer any duplicate to skip)

`addOptionsToTheClientConfiguration` still seeds the legacy
`config().setString("database", ...)` / `("format", ...)` keys from the
setting value so the TCP connection's `default_database` and the client's
default-format selection continue to work; it just reads from `cmd_settings`
now instead of from the parsed `options` map.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…to settings

Reverts the short-alias-in-settings approach. The dedicated client-side
declarations are restored so:

* short alias handling stays alongside the rest of the CLI in
  `ClientBase::addCommonOptions`;
* `--database` and `--format` continue to appear in `clickhouse-client --help`
  among the main options (alongside `-d` and `-f`), with the existing
  human-readable descriptions;
* `Settings::addToProgramOptions[AsMultitokens]` skips the duplicate (the
  setting still sees the value because `addOptionsToTheClientConfiguration`
  copies `options["database"]` / `options["format"]` into `cmd_settings`).

To make the configuration file and named-connection paths also work:

* In `clickhouse-client`, after `default_database = config().getString("database", "")`
  reads the value out of the (possibly file-sourced) config, propagate it into
  `cmd_settings->set("database", ...)` unless the CLI already set it.
* In `ClientBase::setDefaultFormatsAndCompressionFromConfiguration`, mirror the
  configured `format` / `output-format` / `input-format` into the corresponding
  settings.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Comment thread src/Access/SettingsConstraints.cpp Resolved
Comment thread src/Client/ClientBase.cpp Resolved
…outing

Three follow-ups to address the fast-test failures on the previous head:

1. `--param_database=...` was failing every TCP request with
   `Cannot parse quoted string: expected opening quote ''', got 'd'`.
   The client serializes query parameters by calling `Settings::set(name, value)`
   for each one; when the name happens to be a real declared setting (now
   including `database`), the value lands in a typed `SettingFieldString` whose
   `toString()` returns the raw value, whereas custom (undeclared) settings'
   `toString()` SQL-quotes it. The server's `Settings::toNameToNameMap()` then
   called `readQuoted` on the value, which choked on the unquoted form.
   Make `toNameToNameMap` accept both forms — if the string starts with a `'`,
   parse as a quoted SQL literal; otherwise pass it through verbatim.

2. `?profile=readonly` (and similar) was being treated as a SQL filter when
   `http_allow_filters_as_unrecognized_url_parameters` was on, because
   `profile` is not a `DECLARE`d setting (it's handled specially by
   `Context::setSetting`). Add `profile` to the early "known name" carve-out
   in `HTTPHandler::processQuery` so it flows through the normal settings
   pipeline and triggers profile loading.

3. The dynamic-query catch-all filter was too permissive and hijacked
   single-component-no-extension paths like `/sashboards`, breaking
   `02842_suggest_http_page_in_error_message` (which expects a "Maybe you
   meant /dashboard" hint from `NotFoundHandler`). Narrow the extended
   routing to paths that *look* like a database/table reference:
   non-empty query string, multiple `/` separators, a `.` in the last
   component, or a comparison operator (`= < > !`). Simple typo paths fall
   through to `NotFoundHandler` again.

Test config: drop `http_allow_filters_as_unrecognized_url_parameters` from
the global default profile (it would otherwise turn typos / `?profile=`
into filters and break tests like `02152_invalid_setting_with_hints_...`).
The 04245 test enables it per-request for the cases that exercise it.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Comment thread src/Server/HTTPHandlerFactory.cpp Resolved
Comment thread src/Client/ClientBase.cpp Resolved
…defaults; avoid rule-name collision

Three issues uncovered by CI:

1. Tests like `01399_http_request_headers` were hanging on
   `curl -H 'X-ClickHouse-Database: header_test' -d 'SHOW TABLES'`.
   The header now flows through the `database` setting; after
   `applySettingsChanges` we call `setCurrentDatabase` to apply it. That call
   used to happen *after* `query_scope = QueryScope::create(context)`, so an
   `UNKNOWN_DATABASE` exception from `setCurrentDatabase` would unwind through
   an in-flight query scope and leave the HTTP response buffers in a state
   that never finalized — the curl just timed out. Move the
   database-resolution block *before* `QueryScope::create`, so the exception
   unwinds cleanly and the client sees the expected `Code: 81`.

2. The test config installs a configured dynamic-query handler under
   `/api/v1` (used by `04245_http_table_as_file`'s `url_prefix` test). The
   factory iterates `config.keys("http_handlers")` which returns child
   elements in unspecified order, and on Poco's hash-backed map this often
   put `defaults` first. The built-in catch-all dynamic handler registered
   by `defaults` would then match `/api/v1/...` before the configured
   `rule_api_v1` ever ran. Defer the `defaults` block in
   `createHandlersFactoryFromConfig` until after every `rule*` child has
   been processed, so user-configured rules always take precedence over the
   catch-all.

3. The test config splits `<http_handlers>` across two `config.d/` files
   (`handlers.yaml` for the `/upyachka` redirect, `http_url_prefix.xml` for
   the `/api/v1` prefix). Both used the element name `<rule>`, and the
   ClickHouse XML config merger treats same-named siblings as merging-by-name,
   so one overwrote the other. Rename my XML's element to `rule_api_v1` —
   the factory accepts any element name that starts with `rule`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Comment thread src/Server/HTTPHandler.cpp Resolved
Comment thread src/Server/HTTPHandler.cpp Resolved
@alexey-milovidov
alexey-milovidov added this pull request to the merge queue Aug 17, 2026
Merged via the queue into master with commit 5ffb88c Aug 17, 2026
181 checks passed
@alexey-milovidov
alexey-milovidov deleted the http-table-as-file branch August 17, 2026 01:42
@robot-ch-test-poll4 robot-ch-test-poll4 added the pr-synced-to-cloud The PR is synced to the cloud repo label Aug 17, 2026
valerypetrov pushed a commit to valerypetrov/ClickHouse that referenced this pull request Aug 22, 2026
…teractive mode

`applyCmdOptions` seeds the `default_format` setting on the global context as
the fallback format of the sessions of the embedded protocol listeners
(`SYSTEM START LISTEN`), where it must match a real `clickhouse-server`
(`TabSeparated`). The client context is copied from the global context and
inherited the seed, and the per-query output-format resolution introduced in
ClickHouse#105249 treats a non-empty `default_format` setting as the fallback format, so
the seed overrode the interactive display default (`PrettyCompact`) with `TSV`.

Clear the seeded value on the client context: this client renders results from
its own `ClientBase::default_output_format`. An explicit `--default_format` on
the command line stays honored, as do `SET default_format = ...` and an
in-query `SETTINGS` clause; the sessions of the embedded listeners still get
the seed through the global context.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
bacek pushed a commit to bacek/ClickHouse that referenced this pull request Aug 24, 2026
Add per-column sorting to the built-in Web SQL UI. Two arrows in every column
header choose the sort; the query is then re-run with it as the `order`
query-construction setting (added by ClickHouse#105249), so the server returns the whole
result sorted instead of the page reordering the rows it happens to hold.

The arrow semantics: the arrow of the active direction deactivates it, the other
direction replaces it, a plain click makes the column the only sort key, and
Shift appends the column after the keys already in effect (or only flips the
direction of a key it already is, keeping its position). Every active arrow shows
its position in the `ORDER BY` as a superscript when there is more than one key.
The arrows follow the pin and colorize toggles: hidden until the header is
hovered or an arrow is focused on a hovering pointer, always visible on a coarse
pointer, and always visible while active.

The sort is sent as a fully back-quoted `ORDER BY` expression list, so a result
column whose name is an expression (`count()`) or carries a space is a usable
sort key. It is offered only where the setting actually shapes the result - a
`SELECT` / `UNION`, resolved by the same statement-kind walk that decides the
`Run all` classification - so a `SHOW` / `DESCRIBE` / `EXPLAIN` result never
carries a sort indicator the rows do not have. A multi-statement run and the
vertical single-row layout offer no sorting either.

Like the color modes and the pinned columns, the sort is per tab and travels with
the tab's result snapshot, the URL and the history entry; unlike them it shapes
the rows, so an auto-run `run=1` link reproduces the sorted result, and Download
re-runs with the same `order`. A sort stays bound to the statement it was made
on and is dropped by a run of anything else, so a stale sort cannot put an
`ORDER BY` on a column the new statement lacks.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
zeekay pushed a commit to hanzoai/datastore that referenced this pull request Sep 11, 2026
… start

Runners are reused across jobs, and a job that is cancelled or times out
can leave its `clickhouse-server` running or slowly shutting down. Such a
leftover keeps the server ports, so the next job's server fails every
listen with `Address already in use` and `wait_ready` times out with an
opaque `Connection refused`, failing the whole job at the `Start
ClickHouse Server` step.

Observed in the "Stateless tests (amd_debug, flaky check)" job:
https://s3.amazonaws.com/clickhouse-test-reports/json.html?PR=105249&sha=0fc7d64da40e5f01b107434f039acaede261af12&name_0=PR&name_1=Stateless%20tests%20%28amd_debug%2C%20flaky%20check%29
on ClickHouse#105249 - the first
server start of the job got `Address already in use` on every port
(9181, 9009, 8123, 9000, ...), so a full server instance from a previous
job was still alive on the runner.

The cleanup runs in `ClickHouseProc.start` for replica 0 only, next to
the analogous `dmesg --clear` cross-job hygiene. All server processes
are stopped before every replica-0 start (`stop_server` precedes the
mid-job restart in bugfix validation), so anything matching at that
point is stale by definition. The cleanup never runs locally, where the
machine may host a developer's own server.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
zeekay pushed a commit to hanzoai/datastore that referenced this pull request Sep 11, 2026
…rsing

Addresses issues called out by the automated review on
ClickHouse#105249:

* Register the Prometheus handler before the dynamic-query catch-all in
  `addDefaultHandlersFactory`, so `/metrics` is no longer shadowed when path
  features are enabled.

* `Settings::addToProgramOptions` now skips setting names that are already
  registered as command-line options (mirroring
  `MergeTreeSettings::addToProgramOptionsIfNotPresent`). Fixes the
  `option '--database' is ambiguous` error from the fast-test job after the
  new `database` setting was added.

* Move URL path parsing in `HTTPHandler::processQuery` to *after*
  `applySettingsChanges`, and gate on the per-context `http_allow_*` settings
  rather than the server-default settings. When all path features are off in
  the authenticated user's settings, the URL path is ignored entirely.

* `parseURLParameterAsFilter` now handles the case where HTMLForm splits on
  the operator's `=`. `?a!=2`, `?a>=2`, `?a<=2` are now reassembled to
  `a != 2`, `a >= 2`, `a <= 2` respectively. Operators without an `=` at all
  (`?a>2`, `?a<>2`, `?f(x)>3`) keep using the full-expression fallback.

* Add an "Ok" comment to the catch-all in `HTTPQueryConstructor.cpp`
  (silently falling back to "not binary" for unknown formats); the style
  check refuses bare `catch (...)` blocks.

* Test additions: per-user disable of path features ignores the URL path;
  `?a!=2`, `?a>=2`, `?a<>2` non-encoded operator forms.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
staging - docs — 2707bc58 Deployed Aug 17, 2026 by mintlify[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-feature Pull request with new product feature pr-synced-to-cloud The PR is synced to the cloud repo

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Accessing tables as files in HTTP interface

4 participants