> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-detect-table-modification.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> Полная справочная документация по pg_clickhouse

# Справочная документация pg_clickhouse

<div id="description">
  ## Описание
</div>

pg\_clickhouse — это расширение PostgreSQL, которое позволяет выполнять удалённые запросы
к базам данных ClickHouse, в том числе через \[обёртка сторонних данных]. Оно поддерживает
PostgreSQL 13 и выше, а также ClickHouse 23.3 и выше.

<div id="getting-started">
  ## Начало работы
</div>

Проще всего попробовать pg\_clickhouse с помощью \[Docker-образа], который представляет собой
стандартный Docker-образ PostgreSQL с расширениями pg\_clickhouse и [re2][re2
extension]:

```sh theme={null}
docker run --name pg_clickhouse -e POSTGRES_PASSWORD=my_pass \
       -d ghcr.io/clickhouse/pg_clickhouse:18
docker exec -it pg_clickhouse psql -U postgres
```

См. [руководство](/ru/products/managed-postgres/extensions/pg_clickhouse/tutorial),
чтобы узнать, как импортировать таблицы ClickHouse и делегировать выполнение запросов.

<div id="usage">
  ## Использование
</div>

```sql theme={null}
CREATE EXTENSION pg_clickhouse;
CREATE SERVER taxi_srv FOREIGN DATA WRAPPER clickhouse_fdw
       OPTIONS(driver 'binary', host 'localhost', dbname 'taxi');
CREATE USER MAPPING FOR CURRENT_USER SERVER taxi_srv
       OPTIONS (user 'default');
CREATE SCHEMA taxi;
IMPORT FOREIGN SCHEMA taxi FROM SERVER taxi_srv INTO taxi;
```

<div id="versioning-policy">
  ## Политика версионирования
</div>

pg\_clickhouse придерживается [Semantic Versioning] для своих публичных релизов.

* Основная версия увеличивается при изменениях API
* Дополнительная версия увеличивается при обратно совместимых изменениях SQL
* Номер патча увеличивается при изменениях только в бинарном файле

После установки PostgreSQL отслеживает два варианта версии:

* Версия библиотеки (определяемая `PG_MODULE_MAGIC` в PostgreSQL 18 и
  выше) включает полную семантическую версию, которая видна в выводе
  функции `pgch_version()` или функции Postgres [`pg_get_loaded_modules()`].
* Версия расширения (определяемая в control-файле) включает только основную
  и дополнительную версии, которые видны в таблице `pg_catalog.pg_extension`, в
  выводе функции `pg_available_extension_versions()` и в `\dx
  pg_clickhouse`.

На практике это означает, что релиз, в котором увеличивается номер патча, например
с `v0.1.0` до `v0.1.1`, приносит пользу всем базам данных, в которых загружена `v0.1`, и
не требует выполнения `ALTER EXTENSION`, чтобы воспользоваться обновлением.

С другой стороны, релиз, в котором увеличивается дополнительная или основная версия,
будет сопровождаться SQL-скриптами обновления, и все существующие базы данных, содержащие
расширение, должны выполнить `ALTER EXTENSION pg_clickhouse UPDATE`, чтобы воспользоваться
обновлением.

<div id="ddl-sql-reference">
  ## Справочник по SQL DDL
</div>

В следующих выражениях SQL [DDL] используется pg\_clickhouse.

<div id="create-extension">
  ### CREATE EXTENSION
</div>

Используйте [CREATE EXTENSION], чтобы добавить pg\_clickhouse в базу данных:

```sql theme={null}
CREATE EXTENSION pg_clickhouse;
```

Используйте `WITH SCHEMA`, чтобы установить расширение в определённую схему (рекомендуется):

```sql theme={null}
CREATE SCHEMA ch;
CREATE EXTENSION pg_clickhouse WITH SCHEMA ch;
```

<div id="alter-extension">
  ### ALTER EXTENSION
</div>

Используйте [ALTER EXTENSION], чтобы изменить pg\_clickhouse. Примеры:

* После установки нового релиза pg\_clickhouse используйте предложение `UPDATE`:

  ```sql theme={null}
  ALTER EXTENSION pg_clickhouse UPDATE;
  ```

* Используйте `SET SCHEMA`, чтобы переместить расширение в новую схему:

  ```sql theme={null}
  CREATE SCHEMA ch;
  ALTER EXTENSION pg_clickhouse SET SCHEMA ch;
  ```

<div id="drop-extension">
  ### DROP EXTENSION
</div>

Используйте [DROP EXTENSION], чтобы удалить pg\_clickhouse из базы данных:

```sql theme={null}
DROP EXTENSION pg_clickhouse;
```

Эта команда завершится ошибкой, если от pg\_clickhouse зависят какие-либо объекты. Используйте
предложение `CASCADE`, чтобы удалить и их:

```sql theme={null}
DROP EXTENSION pg_clickhouse CASCADE;
```

<div id="create-server">
  ### CREATE SERVER
</div>

Используйте [CREATE SERVER], чтобы создать внешний сервер, подключающийся к серверу ClickHouse. Пример:

```sql theme={null}
CREATE SERVER taxi_srv FOREIGN DATA WRAPPER clickhouse_fdw
       OPTIONS(driver 'binary', host 'localhost', dbname 'taxi');
```

Поддерживаются следующие параметры:

* `driver`: драйвер подключения к ClickHouse: "binary" или
  "http". **Обязательный параметр.**
* `compression`: сжатие native-протокола для драйвера "binary": одно из
  значений "none", "lz4" или "zstd". По умолчанию — "lz4". Для драйвера "http" не используется.
* `dbname`: база данных ClickHouse, используемая при подключении. По умолчанию —
  "default".
* `host`: имя хоста сервера ClickHouse. По умолчанию — "localhost";
* `port`: порт сервера ClickHouse, к которому нужно подключаться. Значения по умолчанию
  следующие:
  * 9440, если `driver` — "binary" и `host` — хост ClickHouse Cloud
  * 9004, если `driver` — "binary" и `host` не является хостом ClickHouse Cloud
  * 8443, если `driver` — "http" и `host` — хост ClickHouse Cloud
  * 8123, если `driver` — "http" и `host` не является хостом ClickHouse Cloud
* `min_tls_version`: минимальная версия протокола TLS, согласуемая при подключениях,
  использующих TLS. Одно из значений: `TLSv1`, `TLSv1.1`, `TLSv1.2` или `TLSv1.3`. По умолчанию —
  минимальная версия, заданная в самой библиотеке TLS. Применяется к обоим драйверам.
* `secure`: управляет использованием TLS для подключения. Возможные значения:
  * `auto` (по умолчанию): использовать TLS, если `host` — хост ClickHouse Cloud или
    `port` — защищенный порт; в противном случае — plaintext.
  * `on` (или `true`/`yes`/`1`): всегда использовать TLS. Значение `port` по умолчанию — 8443
    ("http") или 9440 ("binary").
  * `off` (или `false`/`no`/`0`): никогда не использовать TLS. Значение `port` по умолчанию — 8123
    ("http") или 9000 ("binary").

<div id="alter-server">
  ### ALTER SERVER
</div>

Используйте [ALTER SERVER], чтобы изменить определение внешнего сервера. Пример:

```sql theme={null}
ALTER SERVER taxi_srv OPTIONS (SET driver 'http');
```

Параметры те же, что и для [CREATE SERVER](#create-server).

<div id="drop-server">
  ### DROP SERVER
</div>

Чтобы удалить внешний сервер, используйте [DROP SERVER]:

```sql theme={null}
DROP SERVER taxi_srv;
```

Эта команда завершится ошибкой, если от сервера зависят какие-либо другие объекты. Используйте `CASCADE`, чтобы
также удалить эти зависимости:

```sql theme={null}
DROP SERVER taxi_srv CASCADE;
```

<div id="create-user-mapping">
  ### CREATE USER MAPPING
</div>

Используйте [CREATE USER MAPPING], чтобы сопоставить пользователя PostgreSQL с пользователем ClickHouse. Например,
чтобы сопоставить текущего пользователя PostgreSQL с удалённым пользователем ClickHouse при
подключении через внешний сервер `taxi_srv`:

```sql theme={null}
CREATE USER MAPPING FOR CURRENT_USER SERVER taxi_srv
       OPTIONS (user 'demo');
```

Поддерживаются следующие параметры:

* `user`: Имя пользователя ClickHouse. По умолчанию — "default".
* `password`: Пароль пользователя ClickHouse.

<div id="alter-user-mapping">
  ### ALTER USER MAPPING
</div>

Используйте [ALTER USER MAPPING], чтобы изменить определение пользовательского сопоставления:

```sql theme={null}
ALTER USER MAPPING FOR CURRENT_USER SERVER taxi_srv
       OPTIONS (SET user 'default');
```

Параметры такие же, как для [CREATE USER MAPPING](#create-user-mapping).

<div id="drop-user-mapping">
  ### DROP USER MAPPING
</div>

Используйте [DROP USER MAPPING], чтобы удалить пользовательское сопоставление:

```sql theme={null}
DROP USER MAPPING FOR CURRENT_USER SERVER taxi_srv;
```

<div id="import-foreign-schema">
  ### IMPORT FOREIGN SCHEMA
</div>

Используйте [IMPORT FOREIGN SCHEMA], чтобы импортировать все таблицы, определённые в базе данных ClickHouse, в схему PostgreSQL как внешние таблицы:

```sql theme={null}
CREATE SCHEMA taxi;
IMPORT FOREIGN SCHEMA demo FROM SERVER taxi_srv INTO taxi;
```

Используйте `LIMIT TO`, чтобы импортировать только указанные таблицы:

```sql theme={null}
IMPORT FOREIGN SCHEMA demo LIMIT TO (trips) FROM SERVER taxi_srv INTO taxi;
```

Используйте `EXCEPT`, чтобы исключить таблицы:

```sql theme={null}
IMPORT FOREIGN SCHEMA demo EXCEPT (users) FROM SERVER taxi_srv INTO taxi;
```

pg\_clickhouse получит список всех table в указанной базе данных
("demo" в примерах выше), получит определения столбцов для каждой из них
и выполнит команды [CREATE FOREIGN TABLE](#create-foreign-table), чтобы создать
внешние таблицы. Столбцы будут определены с использованием [поддерживаемых типов
данных](#data-types) и, если это удастся определить, параметров, поддерживаемых [CREATE
FOREIGN TABLE](#create-foreign-table).

<Tip>
  **Сохранение регистра импортированных идентификаторов**

  `IMPORT FOREIGN SCHEMA` применяет `quote_identifier()` к именам table и столбцов,
  которые импортирует, из-за чего идентификаторы с символами в верхнем регистре
  или пробелами заключаются в двойные кавычки. Поэтому такие имена table и столбцов
  в запросах PostgreSQL также должны быть заключены в двойные кавычки. Имена,
  состоящие только из строчных букв и не содержащие пробелов, заключать в кавычки
  не нужно.

  Например, для такой таблицы ClickHouse:

  ```sql theme={null}
  CREATE OR REPLACE TABLE test
  (
      id UInt64,
      Name TEXT,
      updatedAt DateTime DEFAULT now()
  )
  ENGINE = MergeTree
  ORDER BY id;
  ```

  `IMPORT FOREIGN SCHEMA` создаёт такую внешнюю таблицу:

  ```sql theme={null}
  CREATE TABLE test
  (
      id          BIGINT      NOT NULL,
      "Name"      TEXT        NOT NULL,
      "updatedAt" TIMESTAMPTZ NOT NULL
  );
  ```

  Поэтому в запросах нужно правильно использовать кавычки, например:

  ```sql theme={null}
  SELECT id, "Name", "updatedAt" FROM test;
  ```

  Чтобы создавать объекты с другими именами или именами только в нижнем регистре (и, следовательно,
  регистронезависимыми), используйте [CREATE FOREIGN TABLE](#create-foreign-table).
</Tip>

<div id="create-foreign-table">
  ### CREATE FOREIGN TABLE
</div>

Используйте [CREATE FOREIGN TABLE] для создания внешней таблицы, позволяющей запрашивать данные из базы данных ClickHouse:

```sql theme={null}
CREATE FOREIGN TABLE acts (
    user_id    bigint NOT NULL,
    page_views int,
    duration   smallint,
    sign       smallint
) SERVER taxi_srv OPTIONS(
    table_name 'acts'
    engine 'CollapsingMergeTree(sign)'
);
```

Поддерживаются следующие опции таблицы:

* `database`: Имя удалённой базы данных. По умолчанию используется база данных,
  заданная для внешнего сервера.
* `table_name`: Имя удалённой таблицы. По умолчанию используется имя, указанное
  для внешней таблицы.
* `engine`: \[движок таблицы], используемый таблицей ClickHouse. Для
  `CollapsingMergeTree()` и `AggregatingMergeTree()` pg\_clickhouse
  автоматически применяет параметры к функциональным выражениям, выполняемым для
  таблицы.

Используйте [тип данных](#data-types), соответствующий удалённому типу данных ClickHouse
для каждого столбца. Поддерживаются следующие опции столбцов:

* `column_name`: Имя столбца на стороне ClickHouse, которое используется
  вместо имени атрибута PostgreSQL при генерации запросов и операций
  вставки. Это полезно для сопоставления имён столбцов PostgreSQL в нижнем регистре без кавычек со
  столбцами ClickHouse, чувствительными к регистру, например:

  ```sql theme={null}
  CREATE FOREIGN TABLE hits (
      watchid    bigint   OPTIONS(column_name 'WatchID'),
      javaenable smallint OPTIONS(column_name 'JavaEnable'),
      title      text     OPTIONS(column_name 'Title')
  ) SERVER taxi_srv OPTIONS(table_name 'hits');
  ```

* `AggregateFunction`: Имя агрегатной функции, применяемой к
  столбцу типа [AggregateFunction Type]. Сопоставьте тип данных с типом ClickHouse,
  передаваемым в функцию, и укажите имя агрегатной функции через
  соответствующую опцию столбца; pg\_clickhouse автоматически добавит
  `Merge` к агрегатной функции, вычисляющей этот столбец.

  ```sql theme={null}
  CREATE FOREIGN TABLE test (
      column1 bigint  OPTIONS(AggregateFunction 'uniq'),
      column2 integer OPTIONS(AggregateFunction 'anyIf'),
      column3 bigint  OPTIONS(AggregateFunction 'quantiles(0.5, 0.9)')
  ) SERVER clickhouse_srv;
  ```

* `SimpleAggregateFunction`: Имя агрегатной функции, применяемой к
  столбцу типа [SimpleAggregateFunction Type]. Сопоставьте тип данных с
  типом ClickHouse, передаваемым в функцию, и укажите имя
  агрегатной функции через соответствующую опцию столбца.

<div id="alter-foreign-table">
  ### ALTER FOREIGN TABLE
</div>

Используйте [ALTER FOREIGN TABLE], чтобы изменить описание внешней таблицы:

```sql theme={null}
ALTER TABLE table ALTER COLUMN b OPTIONS (SET AggregateFunction 'count');
```

Поддерживаемые параметры таблицы и столбца совпадают с параметрами для [CREATE FOREIGN
TABLE].

<div id="drop-foreign-table">
  ### DROP FOREIGN TABLE
</div>

Чтобы удалить внешнюю таблицу, используйте [DROP FOREIGN TABLE]:

```sql theme={null}
DROP FOREIGN TABLE acts;
```

Эта команда завершится ошибкой, если существуют объекты, зависящие от внешней таблицы.
Чтобы удалить и их, используйте предложение `CASCADE`:

```sql theme={null}
DROP FOREIGN TABLE acts CASCADE;
```

<div id="dml-sql-reference">
  ## Справочник по DML SQL
</div>

Приведённые ниже SQL-выражения [DML] могут использовать pg\_clickhouse. В примерах ниже используются
следующие таблицы ClickHouse:

```sql theme={null}
CREATE TABLE logs (
    req_id    Int64 NOT NULL,
    start_at   DateTime64(6, 'UTC') NOT NULL,
    duration  Int32 NOT NULL,
    resource  Text  NOT NULL,
    method    Enum8('GET' = 1, 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT', 'OPTIONS', 'TRACE', 'PATCH', 'QUERY') NOT NULL,
    node_id   Int64 NOT NULL,
    response  Int32 NOT NULL
) ENGINE = MergeTree
  ORDER BY start_at;

CREATE TABLE nodes (
    node_id Int64 NOT NULL,
    name    Text  NOT NULL,
    region  Text  NOT NULL,
    arch    Text  NOT NULL,
    os      Text  NOT NULL
) ENGINE = MergeTree
  PRIMARY KEY node_id;
```

<div id="explain">
  ### EXPLAIN
</div>

Команда [EXPLAIN] работает как ожидается, но параметр `VERBOSE` приводит к выводу запроса ClickHouse "Remote SQL":

```pgsql theme={null}
try=# EXPLAIN (VERBOSE)
       SELECT resource, avg(duration) AS average_duration
         FROM logs
        GROUP BY resource;
                                     QUERY PLAN
------------------------------------------------------------------------------------
 Foreign Scan  (cost=1.00..5.10 rows=1000 width=64)
   Output: resource, (avg(duration))
   Relations: Aggregate on (logs)
   Remote SQL: SELECT resource, avg(duration) FROM "default".logs GROUP BY resource
(4 rows)
```

Этот запрос передаётся в ClickHouse через узел плана "Foreign Scan" как
удалённый SQL.

<div id="select">
  ### SELECT
</div>

Используйте оператор [SELECT] для выполнения запросов к таблицам pg\_clickhouse,
как и к любым другим таблицам:

```pgsql theme={null}
try=# SELECT start_at, duration, resource FROM logs WHERE req_id = 4117909262;
          start_at          | duration |    resource
----------------------------+----------+----------------
 2025-12-05 15:07:32.944188 |      175 | /widgets/totem
(1 row)
```

pg\_clickhouse старается по возможности максимально переносить выполнение запроса в ClickHouse,
включая агрегатные функции. Используйте [EXPLAIN](#explain), чтобы определить
степень использования pushdown. Например, для приведённого выше запроса всё выполнение
переносится в ClickHouse

```pgsql theme={null}
try=# EXPLAIN (VERBOSE, COSTS OFF)
       SELECT start_at, duration, resource FROM logs WHERE req_id = 4117909262;
                                             QUERY PLAN
-----------------------------------------------------------------------------------------------------
 Foreign Scan on public.logs
   Output: start_at, duration, resource
   Remote SQL: SELECT start_at, duration, resource FROM "default".logs WHERE ((req_id = 4117909262))
(3 rows)
```

pg\_clickhouse также выполняет pushdown JOIN для таблиц, находящихся на одном и том же удалённом сервере:

```pgsql theme={null}
try=# EXPLAIN (ANALYZE, VERBOSE)
       SELECT name, count(*), round(avg(duration))
         FROM logs
         LEFT JOIN nodes on logs.node_id = nodes.node_id
        GROUP BY name;
                                                                                  QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Foreign Scan  (cost=1.00..5.10 rows=1000 width=72) (actual time=3.201..3.221 rows=8.00 loops=1)
   Output: nodes.name, (count(*)), (round(avg(logs.duration), 0))
   Relations: Aggregate on ((logs) LEFT JOIN (nodes))
   Remote SQL: SELECT r2.name, count(*), round(avg(r1.duration), 0) FROM  "default".logs r1 ALL LEFT JOIN "default".nodes r2 ON (((r1.node_id = r2.node_id))) GROUP BY r2.name
   FDW Time: 0.086 ms
 Planning Time: 0.335 ms
 Execution Time: 3.261 ms
(7 rows)
```

JOIN с локальной таблицей без тщательной настройки приведёт к менее эффективным
запросам. В этом примере мы создаём локальную копию
таблицы `nodes` и выполняем JOIN с ней вместо удалённой таблицы:

```pgsql theme={null}
try=# CREATE TABLE local_nodes AS SELECT * FROM nodes;
SELECT 8

try=# EXPLAIN (ANALYZE, VERBOSE)
       SELECT name, count(*), round(avg(duration))
         FROM logs
         LEFT JOIN local_nodes on logs.node_id = local_nodes.node_id
        GROUP BY name;
                                                             QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------
 HashAggregate  (cost=147.65..150.65 rows=200 width=72) (actual time=6.215..6.235 rows=8.00 loops=1)
   Output: local_nodes.name, count(*), round(avg(logs.duration), 0)
   Group Key: local_nodes.name
   Batches: 1  Memory Usage: 32kB
   Buffers: shared hit=1
   ->  Hash Left Join  (cost=31.02..129.28 rows=2450 width=36) (actual time=2.202..5.125 rows=1000.00 loops=1)
         Output: local_nodes.name, logs.duration
         Hash Cond: (logs.node_id = local_nodes.node_id)
         Buffers: shared hit=1
         ->  Foreign Scan on public.logs  (cost=10.00..20.00 rows=1000 width=12) (actual time=2.089..3.779 rows=1000.00 loops=1)
               Output: logs.req_id, logs.start_at, logs.duration, logs.resource, logs.method, logs.node_id, logs.response
               Remote SQL: SELECT duration, node_id FROM "default".logs
               FDW Time: 1.447 ms
         ->  Hash  (cost=14.90..14.90 rows=490 width=40) (actual time=0.090..0.091 rows=8.00 loops=1)
               Output: local_nodes.name, local_nodes.node_id
               Buckets: 1024  Batches: 1  Memory Usage: 9kB
               Buffers: shared hit=1
               ->  Seq Scan on public.local_nodes  (cost=0.00..14.90 rows=490 width=40) (actual time=0.069..0.073 rows=8.00 loops=1)
                     Output: local_nodes.name, local_nodes.node_id
                     Buffers: shared hit=1
 Planning:
   Buffers: shared hit=14
 Planning Time: 0.551 ms
 Execution Time: 6.589 ms
```

В этом случае мы можем перенести большую часть агрегации в ClickHouse,
выполнив группировку по `node_id` вместо локального столбца, а затем выполнить JOIN
с таблицей lookup:

```sql theme={null}
try=# EXPLAIN (ANALYZE, VERBOSE)
       WITH remote AS (
           SELECT node_id, count(*), round(avg(duration))
             FROM logs
            GROUP BY node_id
       )
       SELECT name, remote.count, remote.round
         FROM remote
         JOIN local_nodes
           ON remote.node_id = local_nodes.node_id
        ORDER BY name;
                                                          QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------
 Sort  (cost=65.68..66.91 rows=490 width=72) (actual time=4.480..4.484 rows=8.00 loops=1)
   Output: local_nodes.name, remote.count, remote.round
   Sort Key: local_nodes.name
   Sort Method: quicksort  Memory: 25kB
   Buffers: shared hit=4
   ->  Hash Join  (cost=27.60..43.79 rows=490 width=72) (actual time=4.406..4.422 rows=8.00 loops=1)
         Output: local_nodes.name, remote.count, remote.round
         Inner Unique: true
         Hash Cond: (local_nodes.node_id = remote.node_id)
         Buffers: shared hit=1
         ->  Seq Scan on public.local_nodes  (cost=0.00..14.90 rows=490 width=40) (actual time=0.010..0.016 rows=8.00 loops=1)
               Output: local_nodes.node_id, local_nodes.name, local_nodes.region, local_nodes.arch, local_nodes.os
               Buffers: shared hit=1
         ->  Hash  (cost=15.10..15.10 rows=1000 width=48) (actual time=4.379..4.381 rows=8.00 loops=1)
               Output: remote.count, remote.round, remote.node_id
               Buckets: 1024  Batches: 1  Memory Usage: 9kB
               ->  Subquery Scan on remote  (cost=1.00..15.10 rows=1000 width=48) (actual time=4.337..4.360 rows=8.00 loops=1)
                     Output: remote.count, remote.round, remote.node_id
                     ->  Foreign Scan  (cost=1.00..5.10 rows=1000 width=48) (actual time=4.330..4.349 rows=8.00 loops=1)
                           Output: logs.node_id, (count(*)), (round(avg(logs.duration), 0))
                           Relations: Aggregate on (logs)
                           Remote SQL: SELECT node_id, count(*), round(avg(duration), 0) FROM "default".logs GROUP BY node_id
                           FDW Time: 0.055 ms
 Planning:
   Buffers: shared hit=5
 Planning Time: 0.319 ms
 Execution Time: 4.562 ms
```

Узел "Foreign Scan" теперь выполняет агрегацию по `node_id`, сокращая
число строк, которые нужно вернуть в Postgres, с 1000 (то есть
всех) до всего 8 — по одной на каждый узел.

<div id="partitioned-tables">
  ### Партиционированные таблицы
</div>

\[Партиционированная таблица] PostgreSQL может сочетать локальные партиции с внешними
партициями, размещёнными в ClickHouse. Распространённая структура предполагает перенос старых данных в
ClickHouse, тогда как новые данные остаются в PostgreSQL:

```sql theme={null}
CREATE TABLE events (id int, ts date, val int, amt float8)
    PARTITION BY RANGE (ts);

-- 2023 data lives on ClickHouse
CREATE FOREIGN TABLE events_2023 PARTITION OF events
    FOR VALUES FROM ('2023-01-01') TO ('2024-01-01')
    SERVER ch_svr OPTIONS (table_name 'events');

-- 2024 data stays local
CREATE TABLE events_2024 PARTITION OF events
    FOR VALUES FROM ('2024-01-01') TO ('2025-01-01');
```

Пример переноса данных из локальных партиций во внешние см. в файле
[offload-partition.sql].

Для агрегирования данных по локальным и внешним партициям требуется \[пораздельная
агрегация партиций], которая в PostgreSQL по умолчанию отключена:

```pgsql theme={null}
SET enable_partitionwise_aggregate = on;
```

При включённом `enable_partitionwise_aggregate` PostgreSQL вычисляет *частичный
агрегат* под `Append`, а затем *завершающий агрегат* над ним объединяет эти
частичные результаты. pg\_clickhouse передаёт частичный агрегат внешней партиции
в ClickHouse:

```pgsql theme={null}
try=# EXPLAIN (VERBOSE, COSTS OFF)
       SELECT count(*), sum(val), min(ts), max(ts) FROM events;
                                                       QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------
 Finalize Aggregate
   Output: count(*), sum(events.val), min(events.ts), max(events.ts)
   ->  Append
         ->  Foreign Scan
               Output: (PARTIAL count(*)), (PARTIAL sum(events.val)), (PARTIAL min(events.ts)), (PARTIAL max(events.ts))
               Relations: Aggregate on (events_2023 events)
               Remote SQL: SELECT count(*), sum(val), min(ts), max(ts) FROM "default".events
         ->  Partial Aggregate
               Output: PARTIAL count(*), PARTIAL sum(events_1.val), PARTIAL min(events_1.ts), PARTIAL max(events_1.ts)
               ->  Seq Scan on public.events_2024 events_1
                     Output: events_1.val, events_1.ts
```

<div id="when-partial-aggregates-push-down">
  #### Когда выполняется проталкивание частичных агрегатов
</div>

PostgreSQL представляет частичный агрегат в виде *переходного состояния*, которое
на этапе завершения объединяется по всем партициям. pg\_clickhouse может протолкнуть
частичное состояние партиции, только если его можно представить в виде значения ClickHouse:

* **Декомпозируемые агрегаты**, у которых переходное состояние уже является итоговым
  значением, проталкиваются напрямую: `count`, `sum`, `min`, `max`, `bool_and`/`every`,
  `bool_or`, `bit_and`, `bit_or` и `bit_xor`.
* **`avg` для целых чисел** проталкивает состояние `{count, sum}` в виде массива.
* **`avg`, `var_pop`, `var_samp`, `stddev_pop` и `stddev_samp` для чисел с
  плавающей запятой** проталкивают состояние `{N, sum, sum of squared deviations}` в виде
  массива.

`FILTER (WHERE …)` проталкивается вместе с этими агрегатными функциями.

<div id="when-they-fall-back">
  #### Когда используется локальная обработка
</div>

Агрегатные функции, состояние перехода которых имеет непрозрачный тип PostgreSQL `internal`,
не имеют переносимого представления, поэтому внешняя партиция вместо этого загружает свои строки
и выполняет агрегацию локально. Это относится ко всем агрегатам для `numeric`, а также к
`avg(bigint)` и `avg(interval)`. Агрегаты `DISTINCT`, упорядоченных множеств и вариативные
агрегаты также используют локальную обработку.

<div id="prepare-execute-deallocate">
  ### PREPARE, EXECUTE, DEALLOCATE
</div>

Начиная с версии v0.1.2, pg\_clickhouse поддерживает параметризованные запросы, которые в основном создаются
с помощью команды [PREPARE]:

```pgsql theme={null}
try=# PREPARE avg_durations_between_dates(date, date) AS
       SELECT date(start_at), round(avg(duration)) AS average_duration
         FROM logs
        WHERE date(start_at) BETWEEN $1 AND $2
        GROUP BY date(start_at)
        ORDER BY date(start_at);
PREPARE
```

Используйте [EXECUTE] как обычно для выполнения подготовленного оператора:

```pgsql theme={null}
try=# EXECUTE avg_durations_between_dates('2025-12-09', '2025-12-13');
    date    | average_duration
------------+------------------
 2025-12-09 |              190
 2025-12-10 |              194
 2025-12-11 |              197
 2025-12-12 |              190
 2025-12-13 |              195
(5 строк)
```

<Warning>
  Параметризованное выполнение не позволяет [HTTP-драйверу](#create-server)
  корректно преобразовывать часовые пояса DateTime в версиях ClickHouse до 25.8,
  пока \[эта ошибка] не была \[исправлена]. Обратите внимание, что иногда PostgreSQL
  использует параметризованный план запроса даже без `PREPARE`. Для запросов,
  требующих точного преобразования часового пояса, если обновление до 25.8 или
  более поздней версии невозможно, используйте [бинарный драйвер](#create-server).
</Warning>

pg\_clickhouse, как обычно, выполняет проталкивание агрегаций, что видно в
подробном выводе [EXPLAIN](#explain):

```pgsql theme={null}
try=# EXPLAIN (VERBOSE) EXECUTE avg_durations_between_dates('2025-12-09', '2025-12-13');
                                                                                                            QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Foreign Scan  (cost=1.00..5.10 rows=1000 width=36)
   Output: (date(start_at)), (round(avg(duration), 0))
   Relations: Aggregate on (logs)
   Remote SQL: SELECT date(start_at), round(avg(duration), 0) FROM "default".logs WHERE ((date(start_at) >= '2025-12-09')) AND ((date(start_at) <= '2025-12-13')) GROUP BY (date(start_at)) ORDER BY date(start_at) ASC NULLS LAST
(4 rows)
```

Обратите внимание, что были отправлены полные значения дат, а не плейсхолдеры параметров.
Так происходит для первых пяти запросов, как описано в
\[заметках о PREPARE]. При шестом выполнении отправляются ClickHouse
\[параметры запроса] в формате `{param:type}`:
параметры:

```pgsql theme={null}
                                                                                                         QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Foreign Scan  (cost=1.00..5.10 rows=1000 width=36)
   Output: (date(start_at)), (round(avg(duration), 0))
   Relations: Aggregate on (logs)
   Remote SQL: SELECT date(start_at), round(avg(duration), 0) FROM "default".logs WHERE ((date(start_at) >= {p1:Date})) AND ((date(start_at) <= {p2:Date})) GROUP BY (date(start_at)) ORDER BY date(start_at) ASC NULLS LAST
(4 rows)
```

Используйте [DEALLOCATE], чтобы освободить подготовленный оператор:

```pgsql theme={null}
try=# DEALLOCATE avg_durations_between_dates;
DEALLOCATE
```

<div id="insert">
  ### INSERT
</div>

Используйте команду [INSERT], чтобы вставить значения в удалённую таблицу ClickHouse:

```pgsql theme={null}
try=# INSERT INTO nodes(node_id, name, region, arch, os)
VALUES (9,  'Augustin Gamarra', 'us-west-2', 'amd64', 'Linux')
     , (10, 'Cerisier', 'us-east-2', 'amd64', 'Linux')
     , (11, 'Dewalt', 'use-central-1', 'arm64', 'macOS')
;
INSERT 0 3
```

<div id="copy">
  ### COPY
</div>

Используйте команду [COPY], чтобы выполнить вставку батча строк в удалённую таблицу
ClickHouse:

```pgsql theme={null}
try=# COPY logs FROM stdin CSV;
4285871863,2025-12-05 11:13:58.360760,206,/widgets,POST,8,401
4020882978,2025-12-05 11:33:48.248450,199,/users/1321945,HEAD,3,200
3231273177,2025-12-05 12:20:42.158575,220,/search,GET,2,201
\.
>> COPY 3
```

> **⚠️ Ограничения Batch API**
>
> В pg\_clickhouse пока не реализована поддержка Batch API вставки PostgreSQL FDW.
> Поэтому [COPY] сейчас использует операторы [INSERT](#insert) для
> вставки записей. Это будет исправлено в одном из будущих релизов.

<div id="load">
  ### LOAD
</div>

С помощью [LOAD] загрузите разделяемую библиотеку pg\_clickhouse:

```pgsql theme={null}
try=# LOAD 'pg_clickhouse';
LOAD
```

Обычно использовать [LOAD] не требуется, так как Postgres автоматически загрузит
pg\_clickhouse при первом использовании любой из его возможностей (функций, внешних
таблиц и т. д.).

Единственный случай, когда [LOAD] pg\_clickhouse может быть полезен, — это [SET](#set)
параметров pg\_clickhouse перед выполнением запросов, которые от них зависят.

<div id="set">
  ### SET
</div>

Используйте [SET], чтобы установить пользовательские параметры конфигурации pg\_clickhouse.

<div id="pg_clickhousesession_settings">
  #### `pg_clickhouse.session_settings`
</div>

Параметр `pg_clickhouse.session_settings` задаёт \[настройки ClickHouse],
которые будут применяться к последующим запросам. Пример:

```sql theme={null}
SET pg_clickhouse.session_settings = 'join_use_nulls 1, final 1';
```

По умолчанию:

```
join_use_nulls 1, group_by_use_nulls 1, final 1, transform_null_in 0
```

Установите это значение в пустую строку, чтобы использовать настройки сервера ClickHouse —
однако учтите, что корректность pushdown зависит от некоторых значений по умолчанию:
`join_use_nulls` для внешних JOIN и `transform_null_in` для семейства `IN`
(см. [IN и семантика NULL](#in-and-null-semantics)).

```sql theme={null}
SET pg_clickhouse.session_settings = '';
```

Синтаксис представляет собой список пар ключ/значение, разделённых запятыми и отделённых друг от друга одним или
несколькими пробелами. Ключи должны соответствовать \[настройкам ClickHouse]. Экранируйте пробелы,
запятые и символы обратной косой черты в значениях с помощью обратной косой черты:

```sql theme={null}
SET pg_clickhouse.session_settings = 'join_algorithm grace_hash\,hash';
```

Или используйте значения в одинарных кавычках, чтобы не экранировать пробелы и запятые; также можно
использовать [dollar quoting], чтобы не нужно было заключать их в двойные кавычки:

```sql theme={null}
SET pg_clickhouse.session_settings = $$join_algorithm 'grace_hash,hash'$$;
```

Если для вас важна читаемость и нужно задать много параметров, используйте несколько
строк, например:

```sql theme={null}
SET pg_clickhouse.session_settings TO $$
    connect_timeout 2,
    count_distinct_implementation uniq,
    final 1,
    group_by_use_nulls 1,
    join_algorithm 'prefer_partial_merge',
    join_use_nulls 1,
    log_queries_min_type QUERY_FINISH,
    max_block_size 32768,
    max_execution_time 45,
    max_result_rows 1024,
    metrics_perf_events_list 'this,that',
    network_compression_method ZSTD,
    poll_interval 5,
    totals_mode after_having_auto
$$;
```

Некоторые настройки будут игнорироваться в случаях, когда они могли бы помешать
работе самого pg\_clickhouse. К ним относятся:

* `date_time_output_format`: HTTP-драйвер требует, чтобы значение было "iso"
* `format_tsv_null_representation`: HTTP-драйвер требует значение по умолчанию
* `output_format_tsv_crlf_end_of_line` HTTP-драйвер требует значение по умолчанию

В остальных случаях pg\_clickhouse не проверяет настройки, а передаёт их в
ClickHouse для каждого запроса. Таким образом, он поддерживает все настройки каждой
версии ClickHouse.

Обратите внимание: pg\_clickhouse должен быть загружен до установки
`pg_clickhouse.session_settings`; для этого либо используйте \[предварительную загрузку разделяемой библиотеки], либо
просто воспользуйтесь одним из объектов в расширении, чтобы гарантировать его загрузку.

<div id="pg_clickhousepushdown_regex">
  #### `pg_clickhouse.pushdown_regex`
</div>

Параметр `pg_clickhouse.pushdown_regex` определяет, будет ли pg\_clickhouse
выполнять pushdown функций и операторов регулярных выражений. По умолчанию это включено;
установите для этого параметра значение false, чтобы отключить pushdown для них:

```sql theme={null}
SET pg_clickhouse.pushdown_regex = 'false';
```

См. [Регулярные выражения](#regular-expressions) для получения подробной информации.

<div id="alter-role">
  ### ALTER ROLE
</div>

Используйте команду `SET` оператора [ALTER ROLE] для [предварительной загрузки](#preloading) pg\_clickhouse
и/или для [установки](#set) его параметров для определённых ролей:

```pgsql theme={null}
try=# ALTER ROLE CURRENT_USER SET session_preload_libraries = pg_clickhouse;
ALTER ROLE

try=# ALTER ROLE CURRENT_USER SET pg_clickhouse.session_settings = 'final 1';
ALTER ROLE
```

Используйте команду `RESET` оператора [ALTER ROLE], чтобы сбросить предварительную загрузку pg\_clickhouse
и/или его параметры:

```pgsql theme={null}
try=# ALTER ROLE CURRENT_USER RESET session_preload_libraries;
ALTER ROLE

try=# ALTER ROLE CURRENT_USER RESET pg_clickhouse.session_settings;
ALTER ROLE
```

<div id="preloading">
  ## Предварительная загрузка
</div>

Если pg\_clickhouse нужен для всех или почти всех подключений Postgres,
рассмотрите \[предварительную загрузку разделяемой библиотеки], чтобы она загружалась автоматически:

<div id="session_preload_libraries">
  ### `session_preload_libraries`
</div>

Загружает разделяемую библиотеку при каждом новом подключении к PostgreSQL:

```ini theme={null}
session_preload_libraries = pg_clickhouse
```

Полезно, если нужно воспользоваться обновлениями без перезапуска сервера: просто
подключитесь заново. Также это можно задать для конкретных пользователей или ролей через [ALTER
ROLE](#alter-role).

<div id="shared_preload_libraries">
  ### `shared_preload_libraries`
</div>

Загружает разделяемую библиотеку в родительский процесс PostgreSQL при запуске:

```ini theme={null}
shared_preload_libraries = pg_clickhouse
```

Полезно для экономии памяти и снижения накладных расходов на загрузку для каждого сеанса, но при обновлении библиотеки
требуется перезапуск кластера.

<div id="data-types">
  ## Типы данных
</div>

pg\_clickhouse сопоставляет следующие типы данных ClickHouse с типами
данных PostgreSQL. [IMPORT FOREIGN SCHEMA](#import-foreign-schema) использует первый тип для
столбца PostgreSQL при импорте столбцов; дополнительные типы можно использовать в
операторах [CREATE FOREIGN TABLE](#create-foreign-table):

| ClickHouse | PostgreSQL       | Примечания                       |
| ---------- | ---------------- | -------------------------------- |
| Bool       | boolean          |                                  |
| Date       | date             |                                  |
| Date32     | date             |                                  |
| DateTime   | timestamptz      |                                  |
| Decimal    | numeric          |                                  |
| Float32    | real             |                                  |
| Float64    | double precision |                                  |
| IPv4       | inet             |                                  |
| IPv6       | inet             |                                  |
| Int16      | smallint         |                                  |
| Int32      | integer          |                                  |
| Int64      | bigint           |                                  |
| Int8       | smallint         |                                  |
| JSON       | jsonb, json      |                                  |
| String     | text, bytea      |                                  |
| UInt16     | integer          |                                  |
| UInt32     | bigint           |                                  |
| UInt64     | bigint           | Ошибка для значений > BIGINT max |
| UInt8      | smallint         |                                  |
| UUID       | uuid             |                                  |

Любой столбец также можно прочитать как `text`, `varchar` или другой строковый тип. Значение
сначала преобразуется в указанный выше тип PostgreSQL, а затем выводится с помощью функции
вывода этого типа. Для значений UInt64, превышающих максимум bigint, по-прежнему возникает ошибка, поэтому выводите их
с помощью функции ClickHouse `toString()`.

Ниже приведены дополнительные примечания и подробности.

<div id="bytea">
  ### BYTEA
</div>

ClickHouse не предоставляет эквивалента типа PostgreSQL [BYTEA], однако
позволяет хранить произвольные байты в типе [String]. В общем случае строки ClickHouse
следует сопоставлять с типом PostgreSQL [TEXT], но при работе с двоичными данными используйте
тип [BYTEA]. Пример:

```sql theme={null}
-- Create ClickHouse table with String columns.
CALL clickhouse_perform('ch_srv', $$
    CREATE TABLE bytes (
        c1 Int8, c2 String, c3 String
    ) ENGINE = MergeTree ORDER BY (c1);
$$);

-- Create foreign table with BYTEA columns.
CREATE FOREIGN TABLE bytes (
    c1 int,
    c2 BYTEA,
    c3 BYTEA
) SERVER ch_srv OPTIONS( table_name 'bytes' );

-- Insert binary data into the foreign table.
INSERT INTO bytes
SELECT n, sha224(bytea('val'||n)), decode(md5('int'||n), 'hex')
  FROM generate_series(1, 4) n;

-- View the results.
SELECT * FROM bytes;
```

Этот последний запрос `SELECT` выведет:

```pgsql theme={null}
 c1 |                             c2                             |                 c3
----+------------------------------------------------------------+------------------------------------
  1 | \x1bf7f0cc821d31178616a55a8e0c52677735397cdde6f4153a9fd3d7 | \xae3b28cde02542f81acce8783245430d
  2 | \x5f6e9e12cd8592712e638016f4b1a2e73230ee40db498c0f0b1dc841 | \x23e7c6cacb8383f878ad093b0027d72b
  3 | \x53ac2c1fa83c8f64603fe9568d883331007d6281de330a4b5e728f9e | \x7e969132fc656148b97b6a2ee8bc83c1
  4 | \x4e3c2e4cb7542a45173a8dac939ddc4bc75202e342ebc769b0f5da2f | \x8ef30f44c65480d12b650ab6b2b04245
(4 rows)
```

Обратите внимание: если в столбцах ClickHouse есть нулевые байты, внешняя
таблица, использующая столбцы [TEXT], не будет выводить корректные значения:

```sql theme={null}
-- Create foreign table with TEXT columns.
CREATE FOREIGN TABLE texts (
    c1 int,
    c2 TEXT,
    c3 TEXT
) SERVER ch_srv OPTIONS( table_name 'bytes' );

-- Encode binary data as hex.
SELECT c1, encode(c2::bytea, 'hex'), encode(c3::bytea, 'hex') FROM texts ORDER BY c1;
```

Вывод:

```pgsql theme={null}
 c1 |                          encode                          |              encode
----+----------------------------------------------------------+----------------------------------
  1 | 1bf7f0cc821d31178616a55a8e0c52677735397cdde6f4153a9fd3d7 | ae3b28cde02542f81acce8783245430d
  2 | 5f6e9e12cd8592712e638016f4b1a2e73230ee40db498c0f0b1dc841 | 23e7c6cacb8383f878ad093b
  3 | 53ac2c1fa83c8f64603fe9568d883331                         | 7e969132fc656148b97b6a2ee8bc83c1
  4 | 4e3c2e4cb7542a45173a8dac939ddc4bc75202e342ebc769b0f5da2f | 8ef30f44c65480d12b650ab6b2b04245
(4 rows)
```

Обратите внимание, что вторая и третья строки содержат усечённые значения. Это объясняется тем,
что PostgreSQL использует строки, завершающиеся нулевым байтом, и не поддерживает нулевые байты внутри
строк.

Попытка вставить бинарные значения в столбцы [TEXT] завершится успешно и будет работать как ожидается:

```sql theme={null}
-- Insert via text columns:
TRUNCATE texts;
INSERT INTO texts
SELECT n, sha224(bytea('val'||n)), decode(md5('int'||n), 'hex')
  FROM generate_series(1, 4) n;

-- View the data.
SELECT c1, encode(c2::bytea, 'hex'), encode(c3::bytea, 'hex') FROM texts ORDER BY c1;
```

Текстовые столбцы будут корректными:

```pgsql theme={null}

 c1 |                          encode                          |              encode
----+----------------------------------------------------------+----------------------------------
  1 | 1bf7f0cc821d31178616a55a8e0c52677735397cdde6f4153a9fd3d7 | ae3b28cde02542f81acce8783245430d
  2 | 5f6e9e12cd8592712e638016f4b1a2e73230ee40db498c0f0b1dc841 | 23e7c6cacb8383f878ad093b0027d72b
  3 | 53ac2c1fa83c8f64603fe9568d883331007d6281de330a4b5e728f9e | 7e969132fc656148b97b6a2ee8bc83c1
  4 | 4e3c2e4cb7542a45173a8dac939ddc4bc75202e342ebc769b0f5da2f | 8ef30f44c65480d12b650ab6b2b04245
(4 rows)
```

Но если читать их как [BYTEA], этого не произойдет:

```pgsql theme={null}
# SELECT * FROM bytes;
 c1 |                                                           c2                                                           |                                   c3
----+------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------
  1 | \x5c783162663766306363383231643331313738363136613535613865306335323637373733353339376364646536663431353361396664336437 | \x5c786165336232386364653032353432663831616363653837383332343534333064
  2 | \x5c783566366539653132636438353932373132653633383031366634623161326537333233306565343064623439386330663062316463383431 | \x5c783233653763366361636238333833663837386164303933623030323764373262
  3 | \x5c783533616332633166613833633866363436303366653935363864383833333331303037643632383164653333306134623565373238663965 | \x5c783765393639313332666336353631343862393762366132656538626338336331
  4 | \x5c783465336332653463623735343261343531373361386461633933396464633462633735323032653334326562633736396230663564613266 | \x5c783865663330663434633635343830643132623635306162366232623034323435
(4 rows)
```

<Tip>
  Как правило, столбцы [TEXT] следует использовать только для закодированных строк, а столбцы [BYTEA] —
  только для двоичных данных; никогда не чередуйте их.
</Tip>

<div id="function-and-operator-reference">
  ## Справочник по Function и операторам
</div>

<div id="functions">
  ### Функции
</div>

Эти функции служат интерфейсом для выполнения запросов к базе данных ClickHouse.

<div id="clickhouse_raw_query">
  #### `clickhouse_raw_query`
</div>

<Warning>
  Устарело: `clickhouse_raw_query()` выводит предупреждение об устаревании и будет
  удалена в следующем выпуске. Используйте [`clickhouse_query`](#clickhouse_query) для
  чтения строк и [`clickhouse_perform`](#clickhouse_perform) для выполнения операторов,
  не возвращающих значений. Обе функции повторно используют драйвер, учетные данные,
  базу данных и кэш подключений настроенного сервера вместо специальной строки подключения.
</Warning>

```sql theme={null}
SELECT clickhouse_raw_query(
    'CREATE TABLE t1 (x String) ENGINE = Memory',
    'host=localhost port=8123'
);
```

Подключается к сервису ClickHouse, выполняет один запрос и отключается. Необязательный второй аргумент задает строку подключения,
которая по умолчанию имеет вид `host=localhost port=8123`. Поддерживаются следующие параметры подключения:

* `driver`: Используемый драйвер подключения: "http" или "binary"; по умолчанию
  "http"
* `host`: Хост, к которому нужно подключиться; обязателен.
* `port`: Порт, к которому нужно подключиться. По умолчанию `8123` для драйвера "http" или
  `9000` для драйвера "binary"; при подключении к хосту ClickHouse Cloud используются соответственно
  `8443` или `9440`
* `dbname`: Имя базы данных, к которой нужно подключиться.
* `username`: Имя пользователя, от которого выполняется подключение; по умолчанию `default`
* `password`: Пароль, используемый для аутентификации; по умолчанию пароль отсутствует

Оба драйвера возвращают строки, разделенные табуляцией (значения null как `\N`), но представление
отдельных значений различается: драйвер "http" возвращает собственное TSV-форматирование ClickHouse
без изменений, тогда как драйвер "binary" преобразует каждое значение с помощью
функции вывода PostgreSQL.

По умолчанию ни одна роль не имеет доступа `EXECUTE` к этой функции; рассмотрите возможность
предоставления доступа [GRANT] только тем ролям, которым действительно требуется выполнять
произвольные запросы ClickHouse, например выделенной роли администратора ClickHouse:

Полезно для запросов, которые не возвращают записей, но запросы, которые все же возвращают значения,
будут возвращены как одно текстовое значение:

```sql theme={null}
SELECT clickhouse_raw_query(
    'SELECT schema_name, schema_owner from information_schema.schemata',
    'host=localhost port=8123'
);
```

```sql theme={null}
      clickhouse_raw_query
---------------------------------
 INFORMATION_SCHEMA      default+
 default default                +
 git     default                +
 information_schema      default+
 system  default                +

(1 row)
```

<div id="clickhouse_server_version">
  #### `clickhouse_server_version`
</div>

```sql theme={null}
SELECT clickhouse_server_version('taxi_srv');
```

Возвращает версию сервера ClickHouse в формате `major.minor.patch` для указанного
стороннего сервера, при необходимости подключаясь с использованием параметров сервера и
пользовательского сопоставления текущего пользователя:

```sql theme={null}
 clickhouse_server_version
---------------------------
 25.8.1
(1 row)
```

Получает версию из рукопожатия соединения по нативному протоколу или по HTTP
с помощью единственного запроса `SELECT version()` и кэширует её на весь срок
жизни соединения.

<div id="clickhouse_query">
  #### `clickhouse_query`
</div>

```sql theme={null}
SELECT * FROM clickhouse_query(
    'server',
    'SELECT id, name, salary FROM remote_table WHERE salary > 50000'
) AS ch(id int, name text, salary numeric);
```

Выполняет запрос к уже настроенному стороннему серверу и возвращает его
строки в виде отношения, сопоставляя каждый столбец результата ClickHouse с типом PostgreSQL,
указанным в списке определений столбцов. Повторно использует `driver` сервера,
учётные данные, базу данных и кэш подключений.

Первый аргумент — имя сервера, созданного с помощью [CREATE SERVER]. Требуется
список определений столбцов (`AS name(col type, ...)`): PostgreSQL необходимо
знать структуру результата до получения строк, и она должна соответствовать столбцам, которые возвращает запрос. Значения преобразуются из ClickHouse в объявленные типы так же,
как для столбца внешней таблицы. Операторам, не возвращающим результаты, например DDL,
нечего объявлять; вместо этого выполняйте их с помощью
[`clickhouse_perform`](#clickhouse_perform).

По умолчанию ни одна роль не имеет права `EXECUTE`; предоставьте его роли, чтобы разрешить ей использовать
функцию.

```sql theme={null}
GRANT EXECUTE ON FUNCTION clickhouse_query(text, text) TO ch_admin;
```

<div id="clickhouse_perform">
  #### `clickhouse_perform`
</div>

```sql theme={null}
CALL clickhouse_perform(
    'server',
    'CREATE TABLE remote_table (id Int32) ENGINE = MergeTree ORDER BY id'
);
```

Выполняет оператор на предварительно настроенном стороннем сервере и отбрасывает
любой результат. Используйте его для операторов, не возвращающих строк, например DDL, когда
[`clickhouse_query`](#clickhouse_query) не имеет формы результата, которую можно объявить. Он
разрешает сервер так же, как `clickhouse_query`, повторно используя его `driver`,
учётные данные, базу данных и кэш подключений.

Как процедуру его следует вызывать с помощью [CALL], а не `SELECT`; он не возвращает
строк. По умолчанию ни у одной роли нет права `EXECUTE`; предоставьте его роли с помощью `GRANT`, чтобы она могла
использовать процедуру.

```sql theme={null}
GRANT EXECUTE ON PROCEDURE clickhouse_perform(text, text) TO ch_admin;
```

<div id="pushdown-functions">
  ### Функции с pushdown
</div>

pg\_clickhouse выполняет pushdown для части встроенных функций PostgreSQL, используемых
в условных выражениях (в секциях `HAVING` и `WHERE`). Для них используются следующие
эквиваленты в ClickHouse:

* `abs`: [abs](/ru/reference/functions/regular-functions/arithmetic-functions#abs)
* `factorial`: [factorial](/ru/reference/functions/regular-functions/math-functions#factorial)
* `mod` (int2/int4/int8/numeric): [остаток от деления](/ru/reference/functions/regular-functions/arithmetic-functions#modulo)
* `pow` & `power` (float8/numeric): [pow](/ru/reference/functions/regular-functions/math-functions#pow)
* `round`: [round](/ru/reference/functions/regular-functions/rounding-functions#round)
* `sin`, `cos`, `tan`, `atan`, `atan2`, `sinh`, `cosh`, `tanh`, `asinh`, `degrees`, `radians`, `pi`: [математические функции ClickHouse](/ru/reference/functions/regular-functions/math-functions)
  с такими же именами. Для `asin`, `acos`, `atanh`, `acosh` pushdown не применяется: PG
  выдаёт ошибку для входных данных вне диапазона, тогда как CH возвращает `NaN`.
* `date_part`:
  * `date_part('day')`: [toDayOfMonth](/ru/reference/functions/regular-functions/date-time-functions#toDayOfMonth)
  * `date_part('doy')`: [toDayOfYear](/ru/reference/functions/regular-functions/date-time-functions#toDayOfYear)
  * `date_part('dow')`: [toDayOfWeek](/ru/reference/functions/regular-functions/date-time-functions#toDayOfWeek)
  * `date_part('year')`: [toYear](/ru/reference/functions/regular-functions/date-time-functions#toYear)
  * `date_part('month')`: [toMonth](/ru/reference/functions/regular-functions/date-time-functions#toMonth)
  * `date_part('hour')`: [toHour](/ru/reference/functions/regular-functions/date-time-functions#toHour)
  * `date_part('minute')`: [toMinute](/ru/reference/functions/regular-functions/date-time-functions#toMinute)
  * `date_part('second')`: [toSecond](/ru/reference/functions/regular-functions/date-time-functions#toSecond)
  * `date_part('quarter')`: [toQuarter](/ru/reference/functions/regular-functions/date-time-functions#toQuarter)
  * `date_part('isoyear')`: [toISOYear](/ru/reference/functions/regular-functions/date-time-functions#toISOYear)
  * `date_part('week')`: [toISOYear](/ru/reference/functions/regular-functions/date-time-functions#toISOWeek)
  * `date_part('epoch')`: [toISOYear](/ru/reference/functions/regular-functions/date-time-functions#toUnixTimestamp)
* `date_trunc`:
  * `date_trunc('week')`: [toMonday](/ru/reference/functions/regular-functions/date-time-functions#toMonday)
  * `date_trunc('second')`: [toStartOfSecond](/ru/reference/functions/regular-functions/date-time-functions#toStartOfSecond)
  * `date_trunc('minute')`: [toStartOfMinute](/ru/reference/functions/regular-functions/date-time-functions#toStartOfMinute)
  * `date_trunc('hour')`: [toStartOfHour](/ru/reference/functions/regular-functions/date-time-functions#toStartOfHour)
  * `date_trunc('day')`: [toStartOfDay](/ru/reference/functions/regular-functions/date-time-functions#toStartOfDay)
  * `date_trunc('month')`: [toStartOfMonth](/ru/reference/functions/regular-functions/date-time-functions#toStartOfMonth)
  * `date_trunc('quarter')`: [toStartOfQuarter](/ru/reference/functions/regular-functions/date-time-functions#toStartOfQuarter)
  * `date_trunc('year')`: [toStartOfYear](/ru/reference/functions/regular-functions/date-time-functions#toStartOfYear)
* `extract(field FROM source)`: такие же соответствия, как у `date_part`
* `date(timestamp)` & `date(timestamptz)`: [toDate](/ru/reference/functions/regular-functions/type-conversion-functions#toDate)
  (при обратном преобразовании как алиас CH `date`)
* `array_position`: [indexOf](/ru/reference/functions/regular-functions/array-functions#indexOf) с
  [nullIf](reference/functions/regular-functions/functions-for-nulls#nullif)
  для преобразования `0` в `NULL` и
  [arraySlice](reference/functions/regular-functions/array-functions#arraySlice)
  при наличии третьего аргумента, задающего начальный индекс поиска; обратите внимание, что `nan` пока [не сопоставляется](https://github.com/ClickHouse/ClickHouse/issues/113169)
* `array_cat`: [arrayConcat](/ru/reference/functions/regular-functions/array-functions#arrayConcat)
* `array_append`: [arrayPushBack](/ru/reference/functions/regular-functions/array-functions#arrayPushBack)
* `array_prepend`: [arrayPushFront](/ru/reference/functions/regular-functions/array-functions#arrayPushFront)
* `array_remove`: [arrayRemove](/ru/reference/functions/regular-functions/array-functions#arrayRemove)
* `cardinality`: [длина](reference/functions/regular-functions/array-functions#length)
* `array_length(array, 1)`: `nullIf(length(array), 0)`
* `array_length` & `cardinality`: [длина](/ru/reference/functions/regular-functions/array-functions#length)
* `array_to_string`: [arrayStringConcat](/ru/reference/functions/regular-functions/array-functions#arrayStringConcat)
* `string_to_array`: [splitByString](/ru/reference/functions/regular-functions/splitting-merging-functions#splitByString)
* `split_part`: [splitByString](/ru/reference/functions/regular-functions/splitting-merging-functions#splitByString) + индексация массива
* `trim_array`: [arrayResize](/ru/reference/functions/regular-functions/array-functions#arrayResize)
* `array_fill`: [arrayWithConstant](/ru/reference/functions/regular-functions/array-functions#arrayWithConstant)
* `array_reverse`: [arrayReverse](/ru/reference/functions/regular-functions/array-functions#arrayReverse)
* `array_shuffle`: [arrayShuffle](/ru/reference/functions/regular-functions/array-functions#arrayShuffle)
* `array_sample`: [arrayRandomSample](/ru/reference/functions/regular-functions/array-functions#arrayRandomSample)
* `array_sort`: [arraySort](/ru/reference/functions/regular-functions/array-functions#arraySort) / [arrayReverseSort](/ru/reference/functions/regular-functions/array-functions#arrayReverseSort)
* `btrim`: [trimBoth](/ru/reference/functions/regular-functions/string-functions#trimboth)
* `ltrim`: [trimLeft](/ru/reference/functions/regular-functions/string-functions#trimLeft)
* `rtrim`: [trimRight](/ru/reference/functions/regular-functions/string-functions#trimRight)
* `concat_ws`: [concatWithSeparator](/ru/reference/functions/regular-functions/string-functions#concatwithseparator)
* `lower(text)`: [lowerUTF8](/ru/reference/functions/regular-functions/string-functions#lowerutf8)
* `upper(text)`: [upperUTF8](/ru/reference/functions/regular-functions/string-functions#upperutf8)
* `substring(text, ...)` & `substr(text, ...)`: [substringUTF8](/ru/reference/functions/regular-functions/string-functions#substringutf8)
* `substring(bytea, ...)` & `substr(bytea, ...)`: [substring](/ru/reference/functions/regular-functions/string-functions#substring)
* `length(text)`: [lengthUTF8](/ru/reference/functions/regular-functions/string-functions#lengthutf8)
* `length(bytea)` & `octet_length`: [length](/ru/reference/functions/regular-functions/array-functions#length)
* `reverse(text)`: [reverseUTF8](/ru/reference/functions/regular-functions/string-functions#reverseutf8)
* `reverse(bytea)`: [reverse](/ru/reference/functions/regular-functions/string-functions#reverse)
* `strpos`: [positionUTF8](/ru/reference/functions/regular-functions/string-search-functions#positionutf8)
* `regexp_like`: [match](/ru/reference/functions/regular-functions/string-search-functions#match)
* `regexp_match`: [extractGroups](/ru/reference/functions/regular-functions/string-search-functions#extractGroups)
  если регулярное выражение содержит подвыражения в скобках; в противном случае
  [extractAll](/ru/reference/functions/regular-functions/string-search-functions#extractAll)
  со срезом через [arraySlice](/ru/reference/functions/regular-functions/array-functions#arraySlice).
* `regexp_replace`: [replaceRegexpOne](/ru/reference/functions/regular-functions/string-replace-functions#replaceRegexpOne) или [replaceRegexpOne](/ru/reference/functions/regular-functions/string-replace-functions#replaceRegexpAll) при наличии флага `g`
* `regexp_split_to_array`: [splitByRegexp](/ru/reference/functions/regular-functions/splitting-merging-functions#splitByRegexp)
* `md5`: [MD5](/ru/reference/functions/regular-functions/hash-functions#MD5)
* `encode(bytea, fmt)`, если `fmt` — строковая константа (регистронезависимая):
  * `encode(bytea, 'hex')`: [hex](/ru/reference/functions/regular-functions/encoding-functions#hex)
    обёрнутая в [lower](/ru/reference/functions/regular-functions/string-functions#lower),
    поскольку PostgreSQL выводит hex в нижнем регистре.
  * `encode(bytea, 'base64')`: [base64Encode](/ru/reference/functions/regular-functions/encoding-functions#base64encode)
    обёрнутая в [replaceRegexpAll](/ru/reference/functions/regular-functions/string-replace-functions#replaceRegexpAll)
    для воспроизведения разрыва строки MIME (RFC 2045), вставляемого PostgreSQL через каждые 76 символов.
  * `encode(bytea, 'base64url')` (PostgreSQL 19+): [base64URLEncode](/ru/reference/functions/regular-functions/encoding-functions#base64urlencode),
    которая соответствует URL-алфавиту RFC 4648 PostgreSQL без дополнения.
* `json_extract_path_text`: [синтаксис подстолбцов](/ru/reference/data-types/newjson#reading-json-paths-as-sub-columns)
* `json_extract_path`: [toJSONString](/ru/reference/functions/regular-functions/json-functions#toJSONString) + [синтаксис подстолбцов](/ru/reference/data-types/newjson#reading-json-paths-as-sub-columns)
* `jsonb_extract_path_text`: [синтаксис подстолбцов](/ru/reference/data-types/newjson#reading-json-paths-as-sub-columns)
* `jsonb_extract_path`: [toJSONString](/ru/reference/functions/regular-functions/json-functions#toJSONString) + [синтаксис обращения к подстолбцам](/ru/reference/data-types/newjson#reading-json-paths-as-sub-columns)
* `bit_count(bytea)`: [bitCount](/ru/reference/functions/regular-functions/bit-functions#bitcount)
* `to_timestamp(float8)`: [toDateTime64](/ru/reference/functions/regular-functions/type-conversion-functions#todatetime64)
* `to_char(timestamp[tz], fmt)`: [formatDateTime](/ru/reference/functions/regular-functions/date-time-functions#formatDateTime)
  если `fmt` — это строковая константа, для каждого ключевого слова которой есть
  точный эквивалент в ClickHouse. Поддерживаемые ключевые слова см. в [to\_char()](#to_char)
  в разделе «Примечания по совместимости». В противном случае функция выполняется
  локально в PostgreSQL.
* `statement_timestamp`, `transaction_timestamp`, & `clock_timestamp`:
  [nowInBlock64](/ru/reference/functions/regular-functions/date-time-functions#nowInBlock64)
  (`nowInBlock64(9, $session_timezone)`)
* `CURRENT_DATE`:
  [now](/ru/reference/functions/regular-functions/date-time-functions#now) и
  [toDate](/ru/reference/functions/regular-functions/type-conversion-functions#toDate)
  (`toDate(now($session_timezone))`)
* `now`, `CURRENT_TIMESTAMP`, & `LOCALTIMESTAMP`:
  [now64](/ru/reference/functions/regular-functions/date-time-functions#now64)
  (`now64(9, $session_timezone)`)
* `CURRENT_TIMESTAMP(n)` & `LOCALTIMESTAMP(n)`:
  [now64](/ru/reference/functions/regular-functions/date-time-functions#now64)
  (`now64(n, $session_timezone)`)
* `CURRENT_DATABASE`: Передаётся в качестве значения из функции PostgreSQL.
* `CURRENT_SCHEMA`: Передаётся как значение из функции PostgreSQL.
* `CURRENT_CATALOG`: Передаётся как значение из функции PostgreSQL.
* `CURRENT_USER`: Передаётся в качестве значения из функции PostgreSQL.
* `USER`: передаётся в качестве значения из функции PostgreSQL.
* `CURRENT_ROLE`: Передаётся как значение из функции PostgreSQL.
* `SESSION_USER`: Передаётся как значение, возвращаемое функцией PostgreSQL.

<div id="pushdown-operators">
  ### Операторы pushdown
</div>

* Срез массива (`arr[L:U]`): [arraySlice](/ru/reference/functions/regular-functions/array-functions#arraySlice)
* `@>` (массив содержит): [hasAll](/ru/reference/functions/regular-functions/array-functions#hasAll)
* `<@` (массив содержится в): [hasAll](/ru/reference/functions/regular-functions/array-functions#hasAll)
* `&&` (массивы пересекаются): [hasAny](/ru/reference/functions/regular-functions/array-functions#hasAny)
* `~` (совпадение с регулярным выражением): [match](/ru/reference/functions/regular-functions/string-search-functions#match)
* `!~` (нет совпадения с регулярным выражением): [match](/ru/reference/functions/regular-functions/string-search-functions#match)
* `~*` (регистронезависимое отсутствие совпадения с регулярным выражением): [match](/ru/reference/functions/regular-functions/string-search-functions#match)
* `!~*` (регистронезависимое отсутствие совпадения с регулярным выражением): [match](/ru/reference/functions/regular-functions/string-search-functions#match)
* `->>` (извлечение элемента JSON/JSONB как текста): [синтаксис подстолбцов](/ru/reference/data-types/newjson#reading-json-paths-as-sub-columns)
* `->` (извлечение JSON/JSONB): [toJSONString](/ru/reference/functions/regular-functions/json-functions#toJSONString) + [синтаксис подстолбцов](/ru/reference/data-types/newjson#reading-json-paths-as-sub-columns)

<div id="in-and-null-semantics">
  ### Семантика IN и NULL
</div>

ClickHouse вычисляет `IN` в рамках двузначной логики: если проверяемое значение не находит
совпадений, возвращается `0`, даже если участвует NULL, тогда как PostgreSQL возвращает
NULL. Чтобы сохранить семантику PostgreSQL, pg\_clickhouse безусловно выполняет pushdown семейства
`IN` для списка констант или массива (`IN`, `NOT IN`, `= ANY`, `= ALL`,
`<> ANY`, `<> ALL`): нативной или недорогой формы, когда можно
доказать, что ни проверяемое значение, ни элемент массива не могут быть NULL, либо защищённой
формы `CASE` в остальных случаях, которая вместо этого проверяет NULL во время выполнения,
вычисляя точный трёхзначный результат PostgreSQL (TRUE, FALSE, NULL) в
любом контексте, включая позиции значений, например в списке `SELECT` или
`GROUP BY`.

Фильтр `NOT IN (SELECT ...)` по столбцам с типом Nullable также поддерживает pushdown;
при обратном преобразовании добавляются компенсирующие проверки, сохраняющие поведение PostgreSQL:
множество, содержащее NULL, исключает каждую строку, а NULL в проверяемом значении проходит только
при сравнении с пустым множеством. Каждая проверка опускается, если объявление `NOT NULL`
доказывает её ненужность. В отличие от форм с массивами выше, эта проверка применяется только
в простом условии фильтрации (или под `NOT`); `IN (SELECT ...)` (в позиции значения) и тела
сгруппированных или агрегированных подзапросов по-прежнему не поддерживают pushdown.
Объявление столбцов как `NOT NULL` максимально расширяет возможности pushdown, позволяя вместо этого
отправлять более дешёвую форму без проверок; [IMPORT FOREIGN SCHEMA] делает это автоматически для
столбцов ClickHouse без типа `Nullable`. При доказательстве учитываются константы, отличные от NULL,
столбцы `NOT NULL` и базовая арифметика (`+`, `-`, `*`, унарный `-`) над ними.

Эти правила предполагают, что в ClickHouse используется значение по умолчанию `transform_null_in = 0`, которое
pg\_clickhouse задаёт для каждого запроса через значение по умолчанию
параметра [`pg_clickhouse.session_settings`](#pg_clickhousesession_settings),
чтобы профиль сервера ClickHouse не мог незаметно его изменить. Установка
`transform_null_in = 1` нарушает семантику каждого `IN`, для которого выполняется pushdown.

<div id="custom-functions">
  ### Пользовательские функции
</div>

Эти пользовательские функции, созданные pg\_clickhouse, обеспечивают
pushdown внешних запросов для некоторых функций ClickHouse, у которых нет
аналогов в PostgreSQL. Если какую-либо из этих функций не удастся
выполнить через pushdown, будет вызвано исключение.

* [dictGet](/ru/reference/functions/regular-functions/ext-dict-functions#dictget-dictgetordefault-dictgetornull)

<div id="extension-pushdown">
  ### Pushdown для расширений
</div>

pg\_clickhouse распознает функции некоторых основных и сторонних расширений и передает их на pushdown к их эквивалентам в ClickHouse.

<div id="re2">
  #### re2
</div>

Все операторы и функции \[расширения re2] проталкиваются в ClickHouse в соотношении 1:1:

* `@~` → [match](/ru/reference/functions/regular-functions/string-search-functions#match)
* `re2match` → [match](/ru/reference/functions/regular-functions/string-search-functions#match)
* `re2extract` → [extract](/ru/reference/functions/regular-functions/string-search-functions#extract)
* `re2extractall` → [extractAll](/ru/reference/functions/regular-functions/string-search-functions#extractAll)
* `re2regexpextract` → [regexpExtract](/ru/reference/functions/regular-functions/string-search-functions#regexpExtract)
* `re2extractgroups` → [extractGroups](/ru/reference/functions/regular-functions/string-search-functions#extractGroups)
* `re2replaceregexpone` → [replaceRegexpOne](/ru/reference/functions/regular-functions/string-replace-functions#replaceRegexpOne)
* `re2replaceregexpall` → [replaceRegexpAll](/ru/reference/functions/regular-functions/string-replace-functions#replaceRegexpAll)
* `re2countmatches` → [countMatches](/ru/reference/functions/regular-functions/string-search-functions#countMatches)
* `re2countmatchescaseinsensitive` → [countMatchesCaseInsensitive](/ru/reference/functions/regular-functions/string-search-functions#countMatchesCaseInsensitive)
* `re2multimatchany` → [multiMatchAny](/ru/reference/functions/regular-functions/string-search-functions#multiMatchAny)
* `re2multimatchanyindex` → [multiMatchAnyIndex](/ru/reference/functions/regular-functions/string-search-functions#multiMatchAnyIndex)
* `re2multimatchallindices` → [multiMatchAllIndices](/ru/reference/functions/regular-functions/string-search-functions#multiMatchAllIndices)

<div id="intarray">
  #### intarray
</div>

Одна функция [intarray] выполняется в ClickHouse:

* `idx` → [indexOf](/ru/reference/functions/regular-functions/array-functions#indexOf)

<div id="fuzzystrmatch">
  #### fuzzystrmatch
</div>

В ClickHouse проталкиваются две функции [fuzzystrmatch]:

* `soundex`: [soundex](/ru/reference/functions/regular-functions/string-functions#soundex)
* `levenshtein` (с двумя аргументами): [editDistanceUTF8](/ru/reference/functions/regular-functions/string-functions#editDistanceUTF8)

<div id="pushdown-casts">
  ### Приведения типов с pushdown
</div>

pg\_clickhouse выполняет pushdown для приведений типов, таких как `CAST(x AS bigint)`, если
типы данных совместимы. Для несовместимых типов pushdown завершится ошибкой; если `x` в этом
примере имеет тип ClickHouse `UInt64`, ClickHouse откажется приводить это значение.

Чтобы выполнять pushdown приведений к несовместимым типам данных, pg\_clickhouse предоставляет
следующие функции. Они вызывают исключение в PostgreSQL, если pushdown не выполняется.

* [toUInt8](/ru/reference/functions/regular-functions/type-conversion-functions#touint8)
* [toUInt16](/ru/reference/functions/regular-functions/type-conversion-functions#touint16)
* [toUInt32](/ru/reference/functions/regular-functions/type-conversion-functions#touint32)
* [toUInt64](/ru/reference/functions/regular-functions/type-conversion-functions#touint64)
* [toUInt128](/ru/reference/functions/regular-functions/type-conversion-functions#touint128)

<div id="pushdown-aggregates">
  ### Агрегатные функции с pushdown
</div>

Для этих агрегатных функций PostgreSQL поддерживается pushdown в ClickHouse.

* [any\_value](/ru/reference/functions/aggregate-functions/any)
* [array\_agg](/ru/reference/functions/aggregate-functions/groupArray)
* [avg](/ru/reference/functions/aggregate-functions/avg)
* [bit\_and](/ru/reference/functions/aggregate-functions/groupBitAnd)
* [bit\_or](/ru/reference/functions/aggregate-functions/groupBitOr)
* [bit\_xor](/ru/reference/functions/aggregate-functions/groupBitXor)
* [bool\_and / every](/ru/reference/functions/aggregate-functions/groupBitAnd)
* [bool\_or](/ru/reference/functions/aggregate-functions/groupBitOr)
* [count](/ru/reference/functions/aggregate-functions/count)
* [corr](/ru/reference/functions/aggregate-functions/corr)
* [covarpop](/ru/reference/functions/aggregate-functions/covarPop)
* [covarsamp](/ru/reference/functions/aggregate-functions/covarSamp)
* [min](/ru/reference/functions/aggregate-functions/min)
* [max](/ru/reference/functions/aggregate-functions/max)
* [stddev\_pop](/ru/reference/functions/aggregate-functions/stddevPop)
* [stddev\_samp / stddev](/ru/reference/functions/aggregate-functions/stddevSamp)
* [string\_agg](/ru/reference/functions/aggregate-functions/groupConcat)
* [sum](/ru/reference/functions/aggregate-functions/sum)
* [var\_op](/ru/reference/functions/aggregate-functions/varPop)
* [var\_samp /variance](/ru/reference/functions/aggregate-functions/varSamp)

<div id="custom-aggregates">
  ### Пользовательские агрегаты
</div>

Эти пользовательские агрегатные функции, созданные в pg\_clickhouse, обеспечивают pushdown внешних
запросов для некоторых агрегатных функций ClickHouse, не имеющих эквивалентов в PostgreSQL. Если
какую-либо из этих функций невозможно передать через pushdown, будет вызвано исключение.

* [argMax](/ru/reference/functions/aggregate-functions/argMax)
* [argMin](/ru/reference/functions/aggregate-functions/argMin)
* [uniq](/ru/reference/functions/aggregate-functions/uniq)
* [uniqCombined](/ru/reference/functions/aggregate-functions/uniqCombined)
* [uniqCombined64](/ru/reference/functions/aggregate-functions/uniqCombined64)
* [uniqExact](/ru/reference/functions/aggregate-functions/uniqExact)
* [uniqHLL12](/ru/reference/functions/aggregate-functions/uniqHLL12)
* [uniqTheta](/ru/reference/functions/aggregate-functions/uniqthetasketch)
* [quantile](/ru/reference/functions/aggregate-functions/quantile)
* [quantileExact](/ru/reference/functions/aggregate-functions/quantileExact)

<div id="pushdown-ordered-set-aggregates">
  ### Pushdown для агрегатных функций ordered set
</div>

Эти \[агрегатные функции ordered set] сопоставляются с \[параметрическими
агрегатными функциями] ClickHouse путём передачи их *непосредственного аргумента* в качестве параметра, а
выражений `ORDER BY` — в качестве аргументов. Например, следующий запрос PostgreSQL:

```sql theme={null}
SELECT percentile_cont(0.25) WITHIN GROUP (ORDER BY a) FROM t1;
```

Преобразуется в следующий запрос к ClickHouse:

```sql theme={null}
SELECT quantile(0.25)(a) FROM t1;
```

Обратите внимание, что нестандартные суффиксы `ORDER BY` — `DESC` и `NULLS FIRST` —
не поддерживаются и вызовут ошибку.

* `percentile_cont(double)`: [quantile](/ru/reference/functions/aggregate-functions/quantile)
* `percentile_cont(double[])`: [quantiles](https://clickhouse.com/docs/reference/functions/aggregate-functions/quantiles)
* `percentile_disc(double)`: [quantileExactLow](https://clickhouse.com/docs/reference/functions/aggregate-functions/quantileExactLow)
* `percentile_disc(double[])`: [quantilesExactLow](https://clickhouse.com/docs/reference/functions/aggregate-functions/quantilesExactLow)

<div id="custom-ordered-set-aggregates">
  ### Пользовательская агрегатная функция ordered set
</div>

Эти пользовательские \[агрегатные функции ordered set], созданные pg\_clickhouse,
обеспечивают pushdown внешних запросов для некоторых \[параметрических агрегатных
функций] ClickHouse. Если какую-либо из этих функций не удаётся выполнить с pushdown, будет вызвано
исключение.

* `quantile(double)`: [quantile](/ru/reference/functions/aggregate-functions/quantile)
* `quantileExact(double)`: [quantileExact](/ru/reference/functions/aggregate-functions/quantileExact)

### Пользовательские агрегатные функции ordered set

Эти пользовательские \[агрегатные функции ordered set], созданные pg\_clickhouse,
поддерживают pushdown внешних запросов для отдельных параметрических \[агрегатных
функций] ClickHouse. Если любую из этих функций нельзя выполнить с pushdown, будет вызвано
исключение.

* [quantile](https://clickhouse.com/docs/sql-reference/aggregate-functions/reference/quantile)
* [quantileExact](https://clickhouse.com/docs/sql-reference/aggregate-functions/reference/quantileexact)

<div id="pushdown-window-functions">
  ### Оконные функции с pushdown
</div>

Эти \[оконные функции] PostgreSQL проталкиваются в ClickHouse с секциями `OVER
(PARTITION BY ... ORDER BY ...)`, включая спецификации рамки окна, где это
применимо.

* [row\_number](https://clickhouse.com/docs/reference/functions/window-functions/row_number)
* [rank](https://clickhouse.com/docs/reference/functions/window-functions/rank)
* [dense\_rank](https://clickhouse.com/docs/reference/functions/window-functions/dense_rank)
* [ntile](https://clickhouse.com/docs/reference/functions/window-functions/ntile)
* [cume\_dist](https://clickhouse.com/docs/reference/functions/window-functions/cume_dist)
* [percent\_rank](https://clickhouse.com/docs/reference/functions/window-functions/percent_rank)
* [lead](https://clickhouse.com/docs/reference/functions/window-functions/lead)
* [lag](https://clickhouse.com/docs/reference/functions/window-functions/lag)
* [first\_value](https://clickhouse.com/docs/reference/functions/window-functions/first_value)
* [last\_value](https://clickhouse.com/docs/reference/functions/window-functions/last_value)
* [nth\_value](https://clickhouse.com/docs/reference/functions/window-functions/nth_value)
* `min` / `max` (с секцией `OVER`)

При pushdown функции ранжирования (`row_number`, `rank`, `dense_rank`, `ntile`, `cume_dist`,
`percent_rank`) не включают секцию рамки окна, поскольку ClickHouse
не поддерживает спецификации рамки окна для этих функций.

<div id="compatibility-notes">
  ## Примечания по совместимости
</div>

<div id="regular-expressions">
  ### Регулярные выражения
</div>

Хотя pg\_clickhouse выполняет pushdown регулярных выражений в эквиваленты ClickHouse,
когда [pg\_clickhouse.pushdown\_regex](#pg_clickhousepushdown_regex) имеет значение true (по
умолчанию), и старается обеспечить базовый уровень совместимости, важно учитывать
различия между ними и то, как pg\_clickhouse их обрабатывает.

* PostgreSQL поддерживает \[регулярные выражения POSIX], а ClickHouse —
  [регулярные выражения RE2][RE2]. Учитывайте различия в их поведении: используйте RE2,
  если регулярное выражение будет обрабатываться в ClickHouse (например, в
  предложении `WHERE`), и POSIX, если оно будет обрабатываться в Postgres (например, в
  предложении `SELECT`).

* pg\_clickhouse проталкивает \[флаги Postgres], добавляя их в начало
  регулярного выражения ClickHouse внутри `(?)`. Например:

  ```sql theme={null}
  regexp_like(val, '^VAL\d', 'i')
  ```

  Преобразуется в

  ```sql theme={null}
  match(val, concat('(?i)', '^VAL\\d'))
  ```

* Единственные флаги, которые поддерживаются в обоих вариантах и потому могут использоваться при обработке в
  ClickHouse:

  | Флаг | Как   | Примечания                                                               |
  | ---- | ----- | ------------------------------------------------------------------------ |
  | `i`  | `i`   | регистронезависимое сопоставление                                        |
  | `m`  | `m-s` | `^` и `$` соответствуют началу/концу строки, а также началу/концу текста |
  | `n`  | `m-s` | псевдоним Postgres для `m`                                               |
  | `p`  | `-s`  | не позволяет `.` и `[^x]` соответствовать `\n`                           |
  | `s`  | `s`   | позволяет `.` и `[^x]` соответствовать `\n`                              |
  | `t`  |       | строгий синтаксис, игнорируется                                          |
  | `w`  | `m`   | обратное частичное сопоставление с учётом переводов строки               |

  RE2 поддерживает только эти флаги; не используйте другие \[флаги Postgres].

* В этой таблице приведено краткое описание влияния различных флагов (а также отсутствия флага, что
  эквивалентно `s`) на сопоставление символов новой строки и концов строк. Обратите внимание, что в
  Postgres флаги `m` и `p` не позволяют отрицательным символьным классам (`[^xyz]`)
  сопоставляться с символом новой строки, тогда как их аналоги в ClickHouse это ограничение не вводят. В остальном
  поведение ClickHouse такое же, как в Postgres:

  | Шаблон регулярного выражения для `a\nb` | Postgres | ClickHouse | Совпадает? |
  | --------------------------------------- | :------: | :--------: | :--------: |
  | `a.b`                                   |   true   |    true    |     ✔︎     |
  | `a[^x]b`                                |   true   |    true    |     ✔︎     |
  | `a$`                                    |   false  |    false   |     ✔︎     |
  | **Флаг `s`**                            |          |            |            |
  | `(?s)a.b`                               |   true   |    true    |     ✔︎     |
  | `(?s)a[^x]b`                            |   true   |    true    |     ✔︎     |
  | `(?s)a$`                                |   false  |    false   |     ✔︎     |
  | **Флаг `m`**                            |          |            |            |
  | `(?m)a.b`                               |   false  |    false   |     ✔︎     |
  | `(?m)a[^x]b`                            |   true   |    false   |      ✘     |
  | `(?m)a$`                                |   true   |    true    |     ✔︎     |
  | **Флаг `p`**                            |          |            |            |
  | `(?p)a.b`                               |   false  |    false   |     ✔︎     |
  | `(?p)a[^x]b`                            |   true   |    false   |      ✘     |
  | `(?p)a$`                                |   false  |    false   |     ✔︎     |
  | **Флаг `w`**                            |          |            |            |
  | `(?w)a.b`                               |   true   |    true    |      ✔     |
  | `(?w)a[^x]b`                            |   true   |    true    |      ✔     |
  | `(?w)a$`                                |   true   |    true    |      ✔     |

* Любые другие флаги, передаваемые функциям регулярных выражений, будут препятствовать
  pushdown функции.

* Исключение — `regexp_replace()`, которая также поддерживает флаг `g`. Когда
  задан `g`, pg\_clickhouse использует `replaceRegexpAll()` вместо
  `replaceRegexpOne()` и удаляет этот флаг перед добавлением остальных флагов.

* Аргумент replacement в Postgres `regexp_replace()` поддерживает `\&` для
  ссылки на всё совпадение, тогда как в ClickHouse для ссылки на всё
  совпадение используется `\0`. Обязательно используйте `\0`, когда функция проталкивается в ClickHouse.

* Postgres `regexp_match` возвращает `NULL`, если совпадений нет, тогда как
  проталкиваемые выражения возвращают пустой массив. Используйте `COALESCE()`,
  чтобы вместо `NULL` возвращать пустой массив и тем самым сравнивать возвращаемые значения
  единообразно. Например:

  ```sql theme={null}
  SELECT * FROM events WHERE COALESCE(regexp_match(msg, '^ERR'), '{}');
  ```

Чтобы полностью избежать неоднозначности, рассмотрите возможность установки
[pg\_clickhouse.pushdown\_regex](#pg_clickhousepushdown_regex), чтобы предотвратить
передачу регулярных выражений Postgres через pushdown в ClickHouse, и используйте
[re2 extension], для которого pg\_clickhouse поддерживает [прямой pushdown](#re2)
совместимых с ClickHouse регулярных выражений [RE2].

<div id="to_char">
  ### `to_char()`
</div>

PostgreSQL [`to_char()`] для `timestamp` и `timestamp with time zone`
проталкивается в ClickHouse [formatDateTime] только в том случае, если аргумент format
— это строковая константа, не равная NULL, и каждому ключевому слову PostgreSQL в ней
соответствует побайтно идентичный эквивалент в ClickHouse. Если формат задаётся динамически
(не `Const`) или содержит неподдерживаемое ключевое слово либо модификатор,
вызов переключается на локальное вычисление в PostgreSQL — pushdown никогда
не применяется при частичном переводе, поэтому вывод остаётся совместимым с PG.

Формы `to_char()` с двумя аргументами для `numeric`, `interval` и других
нетемпоральных типов никогда не проталкиваются; ClickHouse [formatDateTime] форматирует только
значения даты и времени.

<div id="translated-keywords">
  #### Преобразованные ключевые слова
</div>

| PostgreSQL                 | ClickHouse | Значение                                         |
| -------------------------- | ---------- | ------------------------------------------------ |
| `YYYY`, `yyyy`             | `%Y`       | 4-значный год                                    |
| `YY`, `yy`                 | `%y`       | 2-значный год                                    |
| `MM`, `mm`                 | `%m`       | месяц с ведущим нулём (01–12)                    |
| `DD`, `dd`                 | `%d`       | день месяца с ведущим нулём (01–31)              |
| `DDD`, `ddd`               | `%j`       | день года с ведущим нулём (001–366)              |
| `HH24`, `hh24`             | `%H`       | час в 24-часовом формате с ведущим нулём (00–23) |
| `HH`, `hh`, `HH12`, `hh12` | `%I`       | час в 12-часовом формате с ведущим нулём (01–12) |
| `MI`, `mi`                 | `%i`       | минуты с ведущим нулём (00–59)                   |
| `SS`, `ss`                 | `%S`       | секунды с ведущим нулём (00–59)                  |
| `Q`, `q`                   | `%Q`       | квартал (1–4)                                    |
| `Mon`                      | `%b`       | сокращённое название месяца, например `Oct`      |
| `Dy`                       | `%a`       | сокращённое название дня недели, например `Mon`  |
| `AM`, `PM`                 | `%p`       | индикатор AM/PM, всегда в верхнем регистре       |

<div id="quoted-text-and-literals">
  #### Текст в кавычках и литералы
</div>

Текст, заключённый в `"..."`, передаётся как есть; при этом любой символ `%`
удваивается до `%%`, чтобы экранировать префикс спецификатора ClickHouse. Последовательность `\"` вне
кавычек также передаётся как литеральный `"`. Внутри `"..."` обратная косая черта
экранирует только `"`; другие последовательности с обратной косой чертой трактуются как литеральный текст.

<div id="authors">
  ## Авторы
</div>

[David E. Wheeler](https://justatheory.com/)

<div id="copyright">
  ## Авторские права
</div>

Авторские права (c) 2025-2026, ClickHouse

[foreign data wrapper]: https://www.postgresql.org/docs/current/fdwhandler.html "Документация PostgreSQL: написание foreign data wrapper"

[Docker image]: https://github.com/ClickHouse/pg_clickhouse/pkgs/container/pg_clickhouse "Последняя версия на Docker Hub"

[ClickHouse]: https://clickhouse.com/clickhouse

[Semantic Versioning]: https://semver.org/spec/v2.0.0.html "Семантическое версионирование 2.0.0"

[`pg_get_loaded_modules()`]: https://pgpedia.info/g/pg_get_loaded_modules.html "pgPedia: pg_get_loaded_modules()"

[DDL]: https://en.wikipedia.org/wiki/Data_definition_language "Википедия: язык определения данных"

[CREATE EXTENSION]: https://www.postgresql.org/docs/current/sql-createextension.html "Документация PostgreSQL: CREATE EXTENSION"

[ALTER EXTENSION]: https://www.postgresql.org/docs/current/sql-alterextension.html "Документация PostgreSQL: ALTER EXTENSION"

[DROP EXTENSION]: https://www.postgresql.org/docs/current/sql-dropextension.html "Документация PostgreSQL: DROP EXTENSION"

[CREATE SERVER]: https://www.postgresql.org/docs/current/sql-createserver.html "Документация PostgreSQL: CREATE SERVER"

[CALL]: https://www.postgresql.org/docs/current/sql-call.html "Документация PostgreSQL: CALL"

[ALTER SERVER]: https://www.postgresql.org/docs/current/sql-alterserver.html "Документация PostgreSQL: ALTER SERVER"

[DROP SERVER]: https://www.postgresql.org/docs/current/sql-dropserver.html "Документация PostgreSQL: DROP SERVER"

[CREATE USER MAPPING]: https://www.postgresql.org/docs/current/sql-createusermapping.html "Документация PostgreSQL: CREATE USER MAPPING"

[ALTER USER MAPPING]: https://www.postgresql.org/docs/current/sql-alterusermapping.html "Документация PostgreSQL: ALTER USER MAPPING"

[DROP USER MAPPING]: https://www.postgresql.org/docs/current/sql-dropusermapping.html "Документация PostgreSQL: DROP USER MAPPING"

[IMPORT FOREIGN SCHEMA]: https://www.postgresql.org/docs/current/sql-importforeignschema.html "Документация PostgreSQL: IMPORT FOREIGN SCHEMA"

[CREATE FOREIGN TABLE]: https://www.postgresql.org/docs/current/sql-createforeigntable.html "Документация PostgreSQL: CREATE FOREIGN TABLE"

[table engine]: /reference/engines/table-engines/index "Документация ClickHouse: движки таблиц"

[AggregateFunction Type]: /reference/data-types/aggregatefunction "Документация ClickHouse: тип AggregateFunction"

[SimpleAggregateFunction Type]: /reference/data-types/simpleaggregatefunction "Документация ClickHouse: тип SimpleAggregateFunction"

[ALTER FOREIGN TABLE]: https://www.postgresql.org/docs/current/sql-alterforeigntable.html "Документация PostgreSQL: ALTER FOREIGN TABLE"

[DROP FOREIGN TABLE]: https://www.postgresql.org/docs/current/sql-dropforeigntable.html "Документация PostgreSQL: DROP FOREIGN TABLE"

[DML]: https://en.wikipedia.org/wiki/Data_manipulation_language "Википедия: язык манипулирования данными"

[EXPLAIN]: https://www.postgresql.org/docs/current/sql-explain.html "Документация PostgreSQL: EXPLAIN"

[SELECT]: https://www.postgresql.org/docs/current/sql-select.html "Документация PostgreSQL: SELECT"

[partitioned table]: https://www.postgresql.org/docs/current/ddl-partitioning.html "Документация PostgreSQL: партиционирование таблиц"

[partitionwise aggregation]: https://www.postgresql.org/docs/current/runtime-config-query.html#GUC-ENABLE-PARTITIONWISE-AGGREGATE "Документация PostgreSQL: enable_partitionwise_aggregate"

[offload-partition.sql]: https://github.com/ClickHouse/pg_clickhouse/blob/main/doc/offload-partition.sql

[PREPARE]: https://www.postgresql.org/docs/current/sql-prepare.html "Документация PostgreSQL: PREPARE"

[EXECUTE]: https://www.postgresql.org/docs/current/sql-execute.html "Документация PostgreSQL: EXECUTE"

[DEALLOCATE]: https://www.postgresql.org/docs/current/sql-deallocate.html "Документация PostgreSQL: DEALLOCATE"

[PREPARE]: https://www.postgresql.org/docs/current/sql-prepare.html "Документация PostgreSQL: PREPARE"

[INSERT]: https://www.postgresql.org/docs/current/sql-insert.html "Документация PostgreSQL: INSERT"

[COPY]: https://www.postgresql.org/docs/current/sql-copy.html "Документация PostgreSQL: COPY"

[LOAD]: https://www.postgresql.org/docs/current/sql-load.html "Документация PostgreSQL: LOAD"

[SET]: https://www.postgresql.org/docs/current/sql-set.html "Документация PostgreSQL: SET"

[ALTER ROLE]: https://www.postgresql.org/docs/current/sql-alterrole.html "Документация PostgreSQL: ALTER ROLE"

[предварительная загрузка разделяемых библиотек]: https://www.postgresql.org/docs/current/runtime-config-client.html#RUNTIME-CONFIG-CLIENT-PRELOAD "Документация PostgreSQL: предварительная загрузка разделяемых библиотек"

[агрегатная функция ordered set]: https://www.postgresql.org/docs/current/functions-aggregate.html#FUNCTIONS-ORDEREDSET-TABLE

[параметрический агрегатная функция]: /reference/functions/aggregate-functions/parametric-functions

[ClickHouse settings]: /reference/settings/session-settings "Документация ClickHouse: настройки сеанса"

[dollar quoting]: https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-DOLLAR-QUOTING "Документация PostgreSQL: строковые константы с долларовым цитированием"

[PREPARE notes]: https://www.postgresql.org/docs/current/sql-prepare.html#SQL-PREPARE-NOTES "Документация PostgreSQL: примечания к PREPARE"

[query parameters]: /guides/clickhouse/data-modelling/stored-procedures-and-prepared-statements#alternatives-to-prepared-statements-in-clickhouse "Документация ClickHouse: альтернативы подготовленным операторам в ClickHouse"

[underlying bug]: https://github.com/ClickHouse/ClickHouse/issues/85847 "ClickHouse/ClickHouse#85847 Некоторые запросы в multipart-формах не читают настройки"

[fixed]: https://github.com/ClickHouse/ClickHouse/pull/85570 "ClickHouse/ClickHouse#85570 исправление для HTTP с multipart"

[BYTEA]: https://www.postgresql.org/docs/current/datatype-binary.html "Документация PostgreSQL: двоичные типы данных"

[GRANT]: https://www.postgresql.org/docs/current/sql-grant.html "Документация PostgreSQL: GRANT"

[String]: /reference/data-types/string "Документация ClickHouse: String"

[TEXT]: https://www.postgresql.org/docs/current/datatype-character.html "Документация PostgreSQL: символьные типы"

[оконная функция]: https://www.postgresql.org/docs/current/functions-window.html "Документация PostgreSQL: оконные функции"

[POSIX Regular Expressions]: https://www.postgresql.org/docs/current/functions-matching.html#FUNCTIONS-POSIX-REGEXP "Документация PostgreSQL: регулярные выражения POSIX"

[Postgres flags]: https://www.postgresql.org/docs/current/functions-matching.html#POSIX-EMBEDDED-OPTIONS-TABLE "Документация PostgreSQL: буквы встроенных опций ARE"

[RE2]: https://github.com/google/re2/wiki/Syntax "Синтаксис RE2"

[re2 extension]: https://github.com/ClickHouse/pg_re2 "pg_re2: совместимые с ClickHouse функции регулярных выражений на основе RE2"

[intarray]: https://www.postgresql.org/docs/current/intarray.html "Документация PostgreSQL: intarray"

[fuzzystrmatch]: https://www.postgresql.org/docs/current/fuzzystrmatch.html "Документация PostgreSQL: fuzzystrmatch"

[`to_char()`]: https://www.postgresql.org/docs/current/functions-formatting.html "Документация PostgreSQL: функции форматирования типов данных"

[formatDateTime]: /reference/functions/regular-functions/date-time-functions#formatDateTime "Документация ClickHouse: formatDateTime"
