← На главную

pg_clickhouse v0.10.0 дожал pushdown до 16/22 в TPC-H, Q17 — 37 мс

11.08.2026 21:54 · hackernews

pg_clickhouse выпустил v0.10.0 и дожал pushdown до 16 из 22 запросов TPC-H. Три запроса — Q2, Q17 и Q22 — теперь уходят в ClickHouse целиком. Q17 — трофей: коррелированный подзапрос, который раньше считал среднее по 6 млн строк на каждой внешней строке, занимал 32.7 секунды, а теперь выполняется за 37 миллисекунд. Это в три порядка быстрее, чем у нативного PostgreSQL.

Осталось шесть запросов: Q13, Q15, Q16, Q18, Q20, Q21. Q16 и Q18 упираются в то, что PostgreSQL превращает их подзапросы в анти/semi-join, но с обеих сторон join — дерево, а депарсер пока не умеет его обходить. Это следующий шаг.

Главное нововведение релиза — пушинг SubPlan. Раньше коррелированный подзапрос выполнялся как nested loop с отдельным запросом в ClickHouse на каждую строку. Теперь весь подзапрос уезжает в один Remote SQL как полноценный подзапрос в ClickHouse. NOT IN аналогично превращается в LEFT ANTI JOIN. Всё это требует ClickHouse 25.8 и новее, на старых версиях pg_clickhouse откатывается к локальному выполнению.

Сложнее всего было сохранить семантику трехзначной логики PostgreSQL. ClickHouse использует двухзначную логику: x NOT IN (1, NULL) в PostgreSQL может быть FALSE или NULL, но никогда TRUE. Наивный pushdown молча инвертировал результаты. Решение — отслеживать, как используется результат выражения, и добавлять страховочные CASE WHEN, когда операнды могут быть NULL. Если pg_clickhouse доказывает, что NULL невозможен, guard'ы пропускаются, и запрос уходит как простой IN. Поведение обобщили на всё семейство IN, = ANY, = ALL, <> ANY, <> ALL. Чтобы серверный профиль ClickHouse не сломал логику, в pg_clickhouse.session_settings по умолчанию добавили transform_null_in 0.

Список функций и агрегатов, которые push down, заметно вырос: статистические агрегаты (corr, covar_pop, stddev_pop, var_pop), ordered-set агрегаты (percentile_cont → quantile), агрегация по партициям, where смешаны локальные и foreign партиции. Добавились encode/decode, ltrim/rtrim/btrim, интервальная арифметика. Плюс старые, но не упомянутые в прошлых постах JSON-операторы, window functions, to_char, split_part, soundex и levenshtein.

Архитектурный момент: pushdown встроенных функций теперь opt-in. Раньше любая функция с совпадающим именем уезжала в ClickHouse и могла вести себя иначе. Конкретный пример — тригонометрические функции: PostgreSQL кидает ошибку вне области определения, ClickHouse возвращает NaN. Теперь функция должна быть явно сопоставлена.

Драйвер полностью переписан: вместо clickhouse-cpp теперь используется ClickHouse/clickhouse-c. Старый клиент мешал C++ исключения с setjmp/longjmp обработкой ошибок PostgreSQL и буферизовал результаты целиком в памяти. Новый стримит блоками, сборка и размер упали на 75%. HTTP-драйвер тоже переведён на Native format, из-за чего fetch_size стал бесполезен и ушёл в deprecated. Бинарный драйвер теперь сбрасывает буфер INSERT/COPY после 64MiB, а не держит всё в памяти. Оба драйвера получили настройки compression (none/lz4/zstd) и TLS.

Починили и пару серьёзных багов: конкурентные foreign scans на бинарном драйвере могли упасть из-за общей коннекции — теперь каждый скан получает свою. Плюс исправлен use-after-free и потеря субсекундной точности при вставке timestamp через HTTP.

Из нового инструментария: clickhouse_query() теперь поддерживает и бинарный драйвер, а для операций без результата (CREATE TABLE и т.п.) добавили процедуру clickhouse_perform(). clickhouse_raw_query() объявлен устаревшим.

Осталось ещё много: шесть TPC-H запросов, лёгкие UPDATE/DELETE, UNION pushdown. Главный тормоз — обход join-деревьев с обеих сторон, и это естественная тема следующего поста.

Читать оригинал →