Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/guides/batches.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ The violation's message matches the server's in its code, the constraint's name

### Too many partitions: a batch-shape 252

A schema whose `CREATE TABLE` declares a `PARTITION BY` answers which partition each stored row lands in (`partition_id` on each row) and how many partitions the batch spans (`partition_count`). There is no separate partition setter in 1.0: the key is part of the one statement you compile ([`reference/bindings-v1.md` §7](../reference/bindings-v1.md#7-what-v0-api-is-deleted-and-why)). A body that would split into more partitions than the call's `max_partitions_per_insert_block` allows — `100` unless a setting says otherwise, `0` meaning unlimited — is refused the way the server refuses it: the batch `outcome` is `rejected` with ClickHouse's own **code 252**, and the rows stay itemized, exactly the shape a `CHECK` violation has. Nothing in any row is wrong; the batch is. The remedy is to split the body by partition, and grouping an accepted batch's export spans by `partition_id` gives exactly those per-partition bodies.
A schema whose `CREATE TABLE` declares a `PARTITION BY` answers which partition each stored row lands in (`partition_id` on each row) and how many partitions the batch spans (`partition_count`). There is no separate partition setter in 1.0: the key is part of the one statement you compile ([`reference/bindings-v1.md` §7](../reference/bindings-v1.md#7-what-v0-api-is-deleted-and-why)). A body that would split into more partitions than the call's `max_partitions_per_insert_block` allows — `100` unless a setting says otherwise, `0` meaning unlimited — is refused the way the server refuses it: the batch `outcome` is `rejected` with ClickHouse's own **code 252**, and the rows stay itemized, exactly the shape a `CHECK` violation has. Nothing in any row is wrong; the batch is. The remedy is to split the body by partition, and grouping an accepted batch's export spans by `partition_id` gives exactly those per-partition bodies. On ABI v2 the limit, and `throw_on_max_partitions_per_insert_block`, come from the per-call settings, the server profile and the setup defaults only: one in the compile profile does not reach the INSERT, as a server's CREATE-time settings never reach its INSERT ([`settings.md`](settings.md#the-compile-profile-at-the-insert-abi-v2)).

**Branch on code 252, never on the message text.** The message is ClickHouse's own wording and moves between lines; the code is the stable part. And the name a code carries is the loaded build's own, so if you want it for a log line, ask the library rather than writing it down: its error-code table (`lib.ErrorCodes()` in Go, `lib.error_codes()` in Python and Rust, `lib.errorCodes()` in TypeScript) answers the name for 252 on that line ([`ErrorCodeTable`](../reference/bindings-v1.md#errorcodetable-from-the-error_code_table-document)).

Expand Down
2 changes: 1 addition & 1 deletion docs/guides/filters.md
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ The export mechanics you already know from the plain export channel carry over u

## A server's settings, and the ones a filter declines (ABI v2)

A filter's `WHERE` sees the settings layered under it: the build's own, the process defaults, the server profile, the schema's own settings and the filter's own, highest last, with `session_timezone` at every layer on the zone path above. Each one is honored (applied to the `WHERE`), passed through (accepted and ignored), or declined, and the library decides which, never a binding. Every layer known when the schema compiles (the process defaults as the compile captured them, the server profile and the schema's own settings) is judged then: a setting there that this build's filters do not honor is listed in the schema description's top-level `filter_declined_settings` (Go `SchemaDescription.FilterDeclinedSettings`, Python `filter_declined_settings`, TypeScript `filterDeclinedSettings`, Rust `filter_declined_settings`), once per name, with its tier from [the WHERE-settings lists](../../spec/abi-v2/where-settings/README.md) and its layer (`defaults`, `server` or `schema`, the highest that sets it), and compiling any filter on that schema raises the binding's `UnsupportedError` (`CHS_DECLINED`), naming each setting and its layer. A declined setting from the defaults or the schema's own settings used to be named only at evaluation, and now fails the compile. A declined setting from the filter's own settings is still named at evaluation: in the filter result's `unsupported_settings`, with every verdict `d`, and in the batch's own `unsupported_settings` when the filter rides along on `rows`, which makes that batch `unsupported`. An evaluation's own settings are the INSERT session's: they govern parsing the body and never reach the `WHERE`, so they are never named as WHERE settings. So a filter evaluated in the INSERT's session, a materialized view's `WHERE` for example, is compiled with that session's WHERE-tier settings in the filter's own settings. A consumer that streams rows instead of asking the server must refuse a tenant if `filter_declined_settings` is non-empty, or a `result-content` setting is changed.
A filter's `WHERE` sees the settings layered under it: the build's own, the process defaults, the server profile, the schema's own settings and the filter's own, highest last, with `session_timezone` at every layer on the zone path above. Each one is honored (applied to the `WHERE`), passed through (accepted and ignored), or declined, and the library decides which, never a binding. Every layer known when the schema compiles (the process defaults as the compile captured them, the server profile and the schema's own settings) is judged then: a setting there that this build's filters do not honor is listed in the schema description's top-level `filter_declined_settings` (Go `SchemaDescription.FilterDeclinedSettings`, Python `filter_declined_settings`, TypeScript `filterDeclinedSettings`, Rust `filter_declined_settings`), once per name, with its tier from [the WHERE-settings lists](../../spec/abi-v2/where-settings/README.md) and its layer (`defaults`, `server` or `schema`, the highest that sets it), and compiling any filter on that schema raises the binding's `UnsupportedError` (`CHS_DECLINED`), naming each setting and its layer. A declined setting from the defaults or the schema's own settings used to be named only at evaluation, and now fails the compile. A declined setting from the filter's own settings is still named at evaluation: in the filter result's `unsupported_settings`, with every verdict `d`, and in the batch's own `unsupported_settings` when the filter rides along on `rows`, which makes that batch `unsupported`. An evaluation's own settings are the INSERT session's: they govern parsing the body and the expressions the INSERT itself evaluates ([`settings.md`](settings.md#the-compile-profile-at-the-insert-abi-v2)), and never reach the `WHERE`, so they are never named as WHERE settings. So a filter evaluated in the INSERT's session, a materialized view's `WHERE` for example, is compiled with that session's WHERE-tier settings in the filter's own settings. A consumer that streams rows instead of asking the server must refuse a tenant if `filter_declined_settings` is non-empty, or a `result-content` setting is changed.

## Lifetimes

Expand Down
10 changes: 9 additions & 1 deletion docs/guides/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ A gate named in the **compile profile** binds at compile. A refusing value fails

This is the one place the precedence table above does not hold, and it holds the way it does because a table that exists is a table that exists: the gate settled at CREATE, and no later INSERT reopens the question.

## The compile profile at the INSERT (ABI v2)

On ABI v2 the order above has a second limit, and it is the server's own too: a server's CREATE-time settings never reach its INSERT. At the INSERT that a row, batch or filter-evaluation call models, the compile profile reaches only the parse. The settings that shape how a body is read (the ones ClickHouse's own `FormatFactory::getFormatSettings` reads, such as `input_format_*`, `input_format_allow_errors_*` and `date_time_input_format`) keep the order above, with the server profile's `settings` under the compile profile, except a type gate among them, which binds the compile only. A filter's `WHERE` is not the INSERT, and keeps its own layers ([`filters.md`](filters.md#a-servers-settings-and-the-ones-a-filter-declines-abi-v2)).

Everything else the INSERT does runs under the per-call settings, the server profile's `settings` and the setup defaults only, over ClickHouse's own defaults, with every type gate at ClickHouse's default unless one of those three sets it. That covers every expression the INSERT evaluates (a `DEFAULT` or `MATERIALIZED` fill, a `CHECK`, a TTL, a partition key, a projection, an expression in a `Values` body) and its limits, `max_partitions_per_insert_block` and `throw_on_max_partitions_per_insert_block` among them. The sorting key and the skip indices take the server profile and the setup defaults only.

So a gated `CAST` or a gated aggregate function whose gate is only in the compile profile is refused as the server refuses it (455, 44 or 63), and a partition limit only in the compile profile does not reach the INSERT. If your real INSERT session carries such a setting, pass it per call or in the server profile, as that session does. The full rule is the "Settings." paragraph of the [ABI v2 reference](../reference/abi-v2.md)'s preamble.

## Values cross as strings. Always

This has cost real bugs, so v1 makes it one rule in all four bindings: **a settings value is a string, and only a string.** The binding serializes the map into a JSON object of string values with the language's stock encoder and passes it verbatim, and it never rewrites a value: no boolean or integer spelling, no float. A non-string value is the language's own type error.
Expand Down Expand Up @@ -161,7 +169,7 @@ A zone name is valid exactly when ClickHouse's own `DateLUT` loads it, and a nam

The zone follows the settings precedence above: a per-call `session_timezone` beats the one in the compile profile, which beats the one in the setup defaults, which beats the image zone. A zone in the compile profile is a default for later calls on that schema; a compiled type always takes the image zone, never the profile's.

**A filter's zone is its own**, fixed when it is compiled: its `WHERE` runs in the zone of the settings the filter was compiled with, and an evaluation's settings decide only how the body is parsed. [`reference/bindings-v1.md` §2](../reference/bindings-v1.md#the-call-options) has the rule and the per-binding spellings.
**A filter's zone is its own**, fixed when it is compiled: its `WHERE` runs in the zone of the settings the filter was compiled with, and an evaluation's settings are the INSERT's: they decide how the body is parsed and the expressions the INSERT evaluates, never the `WHERE`. [`reference/bindings-v1.md` §2](../reference/bindings-v1.md#the-call-options) has the rule and the per-binding spellings.

0.x declined a per-call `session_timezone` that named a different zone than the process's, because zone was resolved once at `chs_init`. That decline is gone with the model it protected.

Expand Down
Loading
Loading