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
10 changes: 8 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ All notable changes to this project will be documented in this file. It uses the
`pg_clickhouse.session_settings` parameter, so that a ClickHouse server
profile cannot silently change the `IN` semantics the pushdown rules rely
on ([#315]).
* Added or corrected pushdown for `IN`-family operations on arrays. Previous
behavior was missing or incorrect, either structurally or due to behavioral
differences in the ClickHouse implementation of the operation. Corrected
the structurally incorrect pushdown ([#315]) and handled ClickHouse
behavioral changes using special case statements where needed ([#317]).
* Added pushdown for more aggregate functions ([#290]):
* [corr]
* [covarpop]
Expand Down Expand Up @@ -92,8 +97,7 @@ All notable changes to this project will be documented in this file. It uses the
* Fixed wrong results from pushed-down `= ANY`/`<> ALL` expressions and `IN`
expressions over constant lists and when a `NULL` could reach the
comparison, because ClickHouse evaluates `IN` under two-valued logic while
PostgreSQL evaluates three-value `NULL` logic. These cases now ship only
where PostgreSQL's three-valued answer can be provably preserved ([#315]).
PostgreSQL evaluates three-value `NULL` logic ([#315], [#317]).
* Fixed `<> ANY(array)` deparsing to ClickHouse SQL that computes `<> ALL`,
which is wrong even with no `NULL`s involved: In Postgres,
`1 <> ANY('{1,5}')` is `TRUE`. For now, do not push down ([#315]).
Expand Down Expand Up @@ -149,6 +153,8 @@ All notable changes to this project will be documented in this file. It uses the
"ClickHouse/pg_clickhouse#316 Support inserting Array(Nullable(T)) via binary driver"
[#318]: https://github.com/ClickHouse/pg_clickhouse/pull/318
"ClickHouse/pg_clickhouse#318 Push down the re2 v0.4 `@~` operator"
[#317]: https://github.com/ClickHouse/pg_clickhouse/pull/317
"ClickHouse/pg_clickhouse#317 Push down the array IN family unconditionally"

## [v0.3.2] — 2026-06-16

Expand Down
31 changes: 19 additions & 12 deletions doc/pg_clickhouse.md
Original file line number Diff line number Diff line change
Expand Up @@ -1319,18 +1319,25 @@ equivalents as follows:
ClickHouse evaluates `IN` under two-valued logic: when the probe finds no
match it returns `0` even if a NULL is involved, where PostgreSQL computes
NULL. To preserve PostgreSQL semantics, pg_clickhouse pushes down the `IN`
family (`IN`, `NOT IN`, `= ANY`, `<> ALL`, and `IN (SELECT ...)`) only where
the two systems provably agree: in a plain filter condition, where a NULL
disqualifies a row exactly like `FALSE`, or when none of the probe, the list,
nor the subquery output can produce a NULL. A `NOT IN (SELECT ...)` filter
over nullable columns still pushes down, deparsed with compensating guards
that keep PostgreSQL's answer: a set containing a NULL disqualifies every
row, and a NULL probe passes only against an empty set. Each guard is
omitted when a `NOT NULL` declaration proves it unnecessary. The remaining
shapes (NULL-capable `IN` expressions in select lists, `GROUP BY`, or
`ORDER BY`, and `NOT IN` over arrays or grouped subqueries) are evaluated
locally. Declaring columns `NOT NULL` maximizes pushdown; [IMPORT FOREIGN
SCHEMA] does so automatically for non-`Nullable` ClickHouse columns.
family over a constant list or array (`IN`, `NOT IN`, `= ANY`, `= ALL`,
`<> ANY`, `<> ALL`) unconditionally: the native or cheap form where it can
prove neither the probe nor an array element can be NULL, or a guarded
`CASE` form otherwise that checks for NULL values at runtime instead,
computing PostgreSQL's exact three-valued answer (TRUE, FALSE, NULL) in
every context, including value positions like a `SELECT` list or
`GROUP BY`.

A `NOT IN (SELECT ...)` filter over nullable columns also pushes down,
deparsed with compensating guards that keep PostgreSQL's behavior: a set
containing a NULL disqualifies every row, and a NULL probe passes only
against an empty set. Each guard is omitted when a `NOT NULL` declaration
proves it unnecessary. Unlike the array forms above, this guard only
applies in a plain filter condition (or under `NOT`); we still do not push
down `IN (SELECT ...)` (in a value position) nor grouped/aggregated subquery
bodies. Declaring columns `NOT NULL` maximizes pushdown by letting the cheaper
unguarded form ship instead; [IMPORT FOREIGN SCHEMA] does so automatically for
non-`Nullable` ClickHouse columns. The proof follows non-NULL constants,
`NOT NULL` columns, and basic arithmetic (`+`, `-`, `*`, unary `-`) over them.

These rules assume ClickHouse's default `transform_null_in = 0`, which
pg_clickhouse sets on every query through the default value of the
Expand Down
Loading
Loading