メインコンテンツへスキップ

説明

pg_clickhouse は PostgreSQL の拡張機能で、[外部データラッパー] を含み、 ClickHouse データベースに対するリモートクエリの実行を可能にします。PostgreSQL 13 以降と ClickHouse 23 以降をサポートしています。

はじめに

pg_clickhouse を試す最も簡単な方法は Docker image で、これには pg_clickhouse と [re2][re2 拡張機能] 拡張機能を組み込んだ標準の PostgreSQL Docker image が含まれています:
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
ClickHouseテーブルのインポートやクエリのプッシュダウンを始めるには、チュートリアルを参照してください。

使用方法

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;

バージョニングポリシー

pg_clickhouse は、公開リリースにおいて Semantic Versioning に従います。
  • API に変更がある場合、メジャーバージョンが増分されます
  • 後方互換性のある SQL の変更がある場合、マイナーバージョンが増分されます
  • バイナリのみに関する変更がある場合、パッチバージョンが増分されます
インストールされると、PostgreSQL は 2 種類のバージョンを追跡します。
  • ライブラリバージョン (PostgreSQL 18 以降では PG_MODULE_MAGIC によって定義) は、完全なセマンティックバージョンを含み、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 を実行する必要があります。

DDL SQL リファレンス

以下の SQL DDL 構文では、pg_clickhouse を使用します。

CREATE EXTENSION

CREATE EXTENSION を使用して、データベースに pg_clickhouse を追加します。
CREATE EXTENSION pg_clickhouse;
特定のスキーマにインストールするには、WITH SCHEMA を使用します (推奨) :
CREATE SCHEMA ch;
CREATE EXTENSION pg_clickhouse WITH SCHEMA ch;

ALTER EXTENSION

ALTER EXTENSION を使用して、pg_clickhouse を変更します。例:
  • pg_clickhouse の新しいリリースをインストールした後は、UPDATE 句を使用します。
    ALTER EXTENSION pg_clickhouse UPDATE;
    
  • SET SCHEMA を使用して、拡張機能を新しいスキーマに移動します。
    CREATE SCHEMA ch;
    ALTER EXTENSION pg_clickhouse SET SCHEMA ch;
    

DROP EXTENSION

データベースから pg_clickhouse を削除するには、DROP EXTENSION を使用します:
DROP EXTENSION pg_clickhouse;
pg_clickhouse に依存するオブジェクトがある場合、このコマンドは失敗します。これらも削除するには、 CASCADE 句を使用します。
DROP EXTENSION pg_clickhouse CASCADE;

CREATE SERVER

CREATE SERVER を使用して、ClickHouse サーバーに接続する外部サーバーを作成できます。例:
CREATE SERVER taxi_srv FOREIGN DATA WRAPPER clickhouse_fdw
       OPTIONS(driver 'binary', host 'localhost', dbname 'taxi');
サポートされているオプションは次のとおりです。
  • driver: 使用する ClickHouse 接続ドライバーです。“binary” または “http” のいずれかを指定します。必須です。
  • dbname: 接続時に使用する ClickHouse データベースです。デフォルトは “default” です。
  • fetch_size: HTTPストリーミングのバッチサイズの目安 (バイト単位) です。バッチは 行境界で分割されます。デフォルトは 50000000 (50 MB) です。0 を指定すると ストリーミングは無効になり、レスポンス全体がバッファリングされます。外部テーブルではこの 値を上書きできます。
  • host: ClickHouseサーバーのホスト名です。デフォルトは “localhost” です。
  • port: ClickHouseサーバーへの接続に使用するポートです。デフォルトは 次のとおりです。
    • driver が “binary” で、host が ClickHouse Cloud ホストの場合は 9440
    • driver が “binary” で、host が ClickHouse Cloud ホストでない場合は 9004
    • driver が “http” で、host が ClickHouse Cloud ホストの場合は 8443
    • driver が “http” で、host が ClickHouse Cloud ホストでない場合は 8123

ALTER SERVER

外部サーバーを変更するには、ALTER SERVER を使用します。例:
ALTER SERVER taxi_srv OPTIONS (SET driver 'http');
オプションは CREATE SERVER と同じです。

DROP SERVER

外部サーバーを削除するには、DROP SERVER を使用します:
DROP SERVER taxi_srv;
このコマンドは、他のオブジェクトがそのサーバーに依存している場合は失敗します。CASCADE を使用すると、 それらの依存オブジェクトもあわせて削除できます:
DROP SERVER taxi_srv CASCADE;

CREATE USER MAPPING

CREATE USER MAPPING を使用して、PostgreSQL ユーザーを ClickHouse ユーザーに対応付けます。たとえば、taxi_srv 外部サーバー を使用して接続する際に、現在の PostgreSQL ユーザーをリモートの ClickHouse ユーザーに対応付けるには、次のようにします。
CREATE USER MAPPING FOR CURRENT_USER SERVER taxi_srv
       OPTIONS (user 'demo');
サポートされているオプションは次のとおりです。
  • user: ClickHouseユーザー名です。デフォルトは “default” です。
  • password: ClickHouseユーザーのパスワードです。

ALTER USER MAPPING

ALTER USER MAPPING を使用して、ユーザーマッピングの定義を変更します。
ALTER USER MAPPING FOR CURRENT_USER SERVER taxi_srv
       OPTIONS (SET user 'default');
CREATE USER MAPPING と同じオプションです。

DROP USER MAPPING

ユーザーマッピングを削除するには、DROP USER MAPPING コマンドを使用します。
DROP USER MAPPING FOR CURRENT_USER SERVER taxi_srv;

IMPORT FOREIGN SCHEMA

IMPORT FOREIGN SCHEMA を使用すると、ClickHouse database で定義されているすべての table を、外部テーブルとして PostgreSQL スキーマにインポートできます。
CREATE SCHEMA taxi;
IMPORT FOREIGN SCHEMA demo FROM SERVER taxi_srv INTO taxi;
LIMIT TO を使用して、インポート対象を特定のテーブルに限定します。:
IMPORT FOREIGN SCHEMA demo LIMIT TO (trips) FROM SERVER taxi_srv INTO taxi;
EXCEPT を使用してテーブルを除外します。
IMPORT FOREIGN SCHEMA demo EXCEPT (users) FROM SERVER taxi_srv INTO taxi;
pg_clickhouse は、指定された ClickHouse データベース (上記の例では “demo”) 内のすべてのテーブルの一覧を取得し、各テーブルのカラム定義を取得したうえで、外部テーブル を作成するための CREATE FOREIGN TABLE コマンドを実行します。カラムは、サポートされているデータ型 と、検出できる場合は CREATE FOREIGN TABLE でサポートされているオプションを使用して定義されます。
インポートされた識別子の大文字・小文字の保持IMPORT FOREIGN SCHEMA は、インポートするテーブル名とカラム名に対して quote_identifier() を実行します。これにより、大文字や空白を含む識別子は二重引用符で囲まれます。そのため、そのようなテーブル名やカラム名は PostgreSQL のクエリ内で二重引用符で囲む必要があります。すべて小文字で空白を含まない名前は、引用符で囲む必要はありません。たとえば、次の ClickHouse テーブルがあるとします。
CREATE OR REPLACE TABLE test
(
    id UInt64,
    Name TEXT,
    updatedAt DateTime DEFAULT now()
)
ENGINE = MergeTree
ORDER BY id;
IMPORT FOREIGN SCHEMA は次の 外部テーブル を作成します。
CREATE TABLE test
(
    id          BIGINT      NOT NULL,
    "Name"      TEXT        NOT NULL,
    "updatedAt" TIMESTAMPTZ NOT NULL
);
したがって、クエリでは適切に引用する必要があります。たとえば、次のようになります。
SELECT id, "Name", "updatedAt" FROM test;
異なる名前、またはすべて小文字の名前 (つまり 大文字・小文字を区別しない名前) でオブジェクトを作成するには、CREATE FOREIGN TABLE を使用します。

CREATE FOREIGN TABLE

ClickHouse データベースのデータをクエリできる外部テーブルを作成するには、 CREATE FOREIGN TABLE を使用します:
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'
);
サポートされているテーブルオプションは次のとおりです。
  • database: リモート database の名前です。デフォルトでは、foreign server に定義された database が使用されます。
  • fetch_size: HTTP streaming のおおよその Batch サイズ (バイト単位) です。サーバーレベルの fetch_size を上書きします。デフォルトは 50000000 (50 MB) です。0 を指定すると streaming は無効になり、応答全体がバッファされます。
  • table_name: リモート table の名前です。デフォルトでは、外部テーブル に指定された名前が使用されます。
  • engine: ClickHouse テーブルで使用する table engine です。 CollapsingMergeTree() および AggregatingMergeTree() では、pg_clickhouse が テーブルに対して実行される関数式にパラメーターを自動的に適用します。
各カラムについて、リモートの ClickHouse データ型に適した data type を使用してください。 サポートされているカラムオプションは次のとおりです。
  • column_name: ClickHouse 側のカラム名です。クエリや insert のデパース時に、PostgreSQL の attribute 名より優先して使用されます。 引用符なしの小文字の PostgreSQL カラム名を、大文字と小文字を区別する ClickHouse カラムに 対応付ける場合に便利です。例:
    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 カラムに適用される aggregate function の名前です。データ型は、その関数に渡される ClickHouse の型に 対応付けてください。適切なカラムオプションで aggregate function の名前を指定すると、 pg_clickhouse はそのカラムを評価する aggregate function に Merge を 自動的に付加します。
    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 カラムに適用される aggregate function の名前です。データ型は、その関数に渡される ClickHouse の型に対応付け、適切なカラムオプションで aggregate function の名前を指定してください。

ALTER FOREIGN TABLE

外部テーブルの定義を変更するには、ALTER FOREIGN TABLE を使用します。
ALTER TABLE table ALTER COLUMN b OPTIONS (SET AggregateFunction 'count');
サポートされるテーブルオプションとカラムオプションは、CREATE FOREIGN TABLE と同じです。

DROP FOREIGN TABLE

外部テーブルを削除するには、DROP FOREIGN TABLE を使用します。
DROP FOREIGN TABLE acts;
外部テーブルに依存しているオブジェクトがある場合、このコマンドは失敗します。 それらも削除するには、CASCADE 句を使用します:
DROP FOREIGN TABLE acts CASCADE;

DML SQL リファレンス

以下の SQL DML 式では、pg_clickhouse を使用することがあります。例では、 以下の ClickHouse テーブルを使用します:
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;

EXPLAIN

EXPLAIN コマンドは期待どおりに動作しますが、VERBOSE オプションを指定すると ClickHouse の “Remote SQL” クエリが出力されます:
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)
このクエリは「Foreign Scan」プランノードを介して ClickHouse にプッシュダウンされ、 リモート SQL として実行されます。

SELECT

SELECTステートメントを使用すると、pg_clickhouse テーブルに対しても、他のあらゆるテーブルと同様にクエリを実行できます。
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 を使用します。 たとえば上記のクエリでは、すべての実行が ClickHouse にプッシュダウンされます
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 は、同じリモートサーバー上のテーブルに対する JOIN もプッシュダウンします:
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)
ローカルテーブルとの結合では、適切にチューニングしないと非効率なクエリが生成されることがあります。 この例では、リモートテーブルではなく nodes テーブルのローカルコピーを 作成し、それと結合します。
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
この場合、ローカルのカラムではなく node_id でグループ化することで、 集約処理のより多くを ClickHouse 側に押し下げ、 その後でルックアップテーブルと結合できます:
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 (そのすべて) から 各ノードにつき 1 行、わずか 8 行まで削減します。

PREPARE, EXECUTE, DEALLOCATE

v0.1.2 以降、pg_clickhouse は、主に PREPARE コマンドで作成される パラメータ付きクエリをサポートしています。
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 を使用します。
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 rows)
パラメータ化実行では、ClickHouse 25.8 より前のバージョンでは [根本的な不具合] が [修正] されていないため、http ドライバー で DateTime のタイムゾーンを 正しく変換できません。PostgreSQL では PREPARE を使用していなくても、 パラメータ化されたクエリプランが使われることがある点に注意してください。タイムゾーンの正確な変換が必要で、 25.8 以降にアップグレードできないクエリについては、代わりに binary ドライバー を使用してください。
pg_clickhouse は、通常どおり集計をプッシュダウンします。これは EXPLAIN の詳細出力で確認できます:
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)
ここでは、パラメータプレースホルダーではなく、完全な日付値が送信されている点に注意してください。 これは、PostgreSQL の [PREPARE に関する注記]で説明されているとおり、最初の 5 回のリクエストに当てはまります。6 回目の実行では、ClickHouse の {param:type} 形式の[クエリパラメータ]が送信されます。 parameters:
                                                                                                         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 を使用します。
try=# DEALLOCATE avg_durations_between_dates;
DEALLOCATE

INSERT

リモートのClickHouseテーブルに値を挿入するには、INSERT コマンドを使用します。
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

COPY

リモートのClickHouseテーブルに複数の行を一括で挿入するには、COPY コマンドを使用します:
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 は、PostgreSQL FDW の Batch insert API のサポートをまだ実装していません。そのため、現在 COPY はレコードの 挿入に INSERT ステートメントを使用しています。これは今後のリリースで改善される予定です。

LOAD

pg_clickhouse の共有ライブラリを読み込むには、LOAD を使用します。
try=# LOAD 'pg_clickhouse';
LOAD
通常、LOAD を使う必要はありません。Postgres は、その機能 (関数、外部 テーブルなど) が初めて使用された時点で、自動的に pg_clickhouse を読み込みます。 LOAD pg_clickhouse が役立つ可能性があるのは、SET を使って それらに依存するクエリを実行する前に pg_clickhouse のパラメータを設定する場合です。

SET

SET を使用して、pg_clickhouse のカスタム設定パラメータを設定します。

pg_clickhouse.session_settings

pg_clickhouse.session_settings パラメータは、後続のクエリに適用する [ClickHouse 設定] を構成します。例:
SET pg_clickhouse.session_settings = 'join_use_nulls 1, final 1';
デフォルトは join_use_nulls 1, group_by_use_nulls 1, final 1 です。空文字列に設定すると、 ClickHouseサーバーの設定にフォールバックします。
SET pg_clickhouse.session_settings = '';
構文は、1 つ以上の空白で区切られたキーと値のペアを、カンマ区切りで並べたリストです。キーは [ClickHouse 設定] に対応している必要があります。値に含まれる空白、カンマ、バックスラッシュは、バックスラッシュでエスケープしてください。
SET pg_clickhouse.session_settings = 'join_algorithm grace_hash\,hash';
また、スペースやカンマをエスケープしなくて済むよう、値を単一引用符で囲むこともできます。二重引用符で囲む必要をなくすために、 [ドル引用]の使用も検討してください:
SET pg_clickhouse.session_settings = $$join_algorithm 'grace_hash,hash'$$;
可読性を重視し、多くの設定を指定する必要がある場合は、複数の 行に分けて記述してください。たとえば、次のようになります。
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.session_settings を設定する前に、 pg_clickhouse をロードしておく必要がある点に注意してください。[共有ライブラリのプリロード] を使用するか、 あるいは拡張機能内のいずれかのオブジェクトを使って、確実にロードされるようにしてください。

pg_clickhouse.pushdown_regex

pg_clickhouse.pushdown_regex パラメータは、pg_clickhouse が 正規表現関数と演算子をプッシュダウンするかどうかを制御します。デフォルトでは有効です。 プッシュダウンを無効にするには、このパラメータを false に設定してください:
SET pg_clickhouse.pushdown_regex = 'false';
詳細については、正規表現を参照してください。

ALTER ROLE

ALTER ROLE’のSETコマンドを使うと、pg_clickhouse をプリロードしたり、 特定のロールに対してそのパラメータをSETしたりできます。
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
ALTER ROLERESET コマンドを使用して、pg_clickhouse の事前読み込み および/またはパラメーターをリセットします:
try=# ALTER ROLE CURRENT_USER RESET session_preload_libraries;
ALTER ROLE

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

プリロード

すべてまたはほぼすべてのPostgres接続で pg_clickhouse を使用する必要がある場合は、自動的に読み込むために[共有ライブラリのプリロード]の使用を検討してください。

session_preload_libraries

PostgreSQL への接続が新しく作成されるたびに、共有ライブラリを読み込みます。
session_preload_libraries = pg_clickhouse
サーバーを再起動せずに更新を反映できるので便利です。必要なのは 再接続だけです。ALTER ROLE を使って、特定のユーザーまたはロールに対して設定することもできます。

shared_preload_libraries

起動時に、共有ライブラリを PostgreSQL の親プロセスに読み込みます。
shared_preload_libraries = pg_clickhouse
メモリ使用量と各セッションの読み込みオーバーヘッドを抑えるのに役立ちますが、ライブラリの更新時には クラスターを再起動する必要があります。

データ型

pg_clickhouse は、以下の ClickHouse データ型を PostgreSQL のデータ型にマッピングします。IMPORT FOREIGN SCHEMA はカラムのインポート時に PostgreSQL カラムの先頭の型を使用し、追加の型は CREATE FOREIGN TABLE ステートメントで使用できます。
ClickHousePostgreSQL注記
Boolboolean
Datedate
Date32date
DateTimetimestamptz
Decimalnumeric
Float32real
Float64double precision
IPv4inet
IPv6inet
Int16smallint
Int32integer
Int64bigint
Int8smallint
JSONjsonb, json
Stringtext, bytea
UInt16integer
UInt32bigint
UInt64bigint値が BIGINT の最大値を超えるとエラー
UInt8smallint
UUIDuuid
追加の注記と詳細は以下のとおりです。

BYTEA

ClickHouse は PostgreSQL の BYTEA 型に相当する型を提供していませんが、String 型に任意のバイト列を格納できます。通常、ClickHouse の文字列は PostgreSQL の TEXT にマッピングしますが、バイナリデータを扱う場合は BYTEA にマッピングしてください。例:
-- Stringカラムを持つClickHouseテーブルを作成する。
SELECT clickhouse_raw_query($$
    CREATE TABLE bytes (
        c1 Int8, c2 String, c3 String
    ) ENGINE = MergeTree ORDER BY (c1);
$$);

-- BYTEAカラムを持つ外部テーブルを作成する。
CREATE FOREIGN TABLE bytes (
    c1 int,
    c2 BYTEA,
    c3 BYTEA
) SERVER ch_srv OPTIONS( table_name 'bytes' );

-- 外部テーブルにバイナリデータを挿入する。
INSERT INTO bytes
SELECT n, sha224(bytea('val'||n)), decode(md5('int'||n), 'hex')
  FROM generate_series(1, 4) n;

-- 結果を確認する。
SELECT * FROM bytes;
最後のSELECTクエリの出力は以下のとおりです:
c1 |                             c2                             |                 c3
----+------------------------------------------------------------+------------------------------------
  1 | \x1bf7f0cc821d31178616a55a8e0c52677735397cdde6f4153a9fd3d7 | \xae3b28cde02542f81acce8783245430d
  2 | \x5f6e9e12cd8592712e638016f4b1a2e73230ee40db498c0f0b1dc841 | \x23e7c6cacb8383f878ad093b0027d72b
  3 | \x53ac2c1fa83c8f64603fe9568d883331007d6281de330a4b5e728f9e | \x7e969132fc656148b97b6a2ee8bc83c1
  4 | \x4e3c2e4cb7542a45173a8dac939ddc4bc75202e342ebc769b0f5da2f | \x8ef30f44c65480d12b650ab6b2b04245
(4 rows)
ClickHouseのカラムにnulバイトが含まれている場合、TEXTカラムを使用する外部テーブルは正しい値を出力しないことに注意してください:
-- TEXTカラムを持つforeign tableを作成する。
CREATE FOREIGN TABLE texts (
    c1 int,
    c2 TEXT,
    c3 TEXT
) SERVER ch_srv OPTIONS( table_name 'bytes' );

-- バイナリデータを16進数にエンコードする。
SELECT c1, encode(c2::bytea, 'hex'), encode(c3::bytea, 'hex') FROM texts ORDER BY c1;
出力:
c1 |                          encode                          |              encode
----+----------------------------------------------------------+----------------------------------
  1 | 1bf7f0cc821d31178616a55a8e0c52677735397cdde6f4153a9fd3d7 | ae3b28cde02542f81acce8783245430d
  2 | 5f6e9e12cd8592712e638016f4b1a2e73230ee40db498c0f0b1dc841 | 23e7c6cacb8383f878ad093b
  3 | 53ac2c1fa83c8f64603fe9568d883331                         | 7e969132fc656148b97b6a2ee8bc83c1
  4 | 4e3c2e4cb7542a45173a8dac939ddc4bc75202e342ebc769b0f5da2f | 8ef30f44c65480d12b650ab6b2b04245
(4 rows)
2行目と3行目には切り詰められた値が含まれていることに注意してください。これは、PostgreSQLがnul終端文字列に依存しており、文字列内のnulをサポートしていないためです。 バイナリ値を TEXT カラムに挿入しようとすると、成功し、期待どおりに動作します:
-- テキストカラム経由でinsertする:
TRUNCATE texts;
INSERT INTO texts
SELECT n, sha224(bytea('val'||n)), decode(md5('int'||n), 'hex')
  FROM generate_series(1, 4) n;

-- データを表示する。
SELECT c1, encode(c2::bytea, 'hex'), encode(c3::bytea, 'hex') FROM texts ORDER BY c1;
テキストカラムは正しく表示されます:

 c1 |                          encode                          |              encode
----+----------------------------------------------------------+----------------------------------
  1 | 1bf7f0cc821d31178616a55a8e0c52677735397cdde6f4153a9fd3d7 | ae3b28cde02542f81acce8783245430d
  2 | 5f6e9e12cd8592712e638016f4b1a2e73230ee40db498c0f0b1dc841 | 23e7c6cacb8383f878ad093b0027d72b
  3 | 53ac2c1fa83c8f64603fe9568d883331007d6281de330a4b5e728f9e | 7e969132fc656148b97b6a2ee8bc83c1
  4 | 4e3c2e4cb7542a45173a8dac939ddc4bc75202e342ebc769b0f5da2f | 8ef30f44c65480d12b650ab6b2b04245
(4 rows)
ただし、BYTEA として読み取ることはできません:
# SELECT * FROM bytes;
 c1 |                                                           c2                                                           |                                   c3
----+------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------
  1 | \x5c783162663766306363383231643331313738363136613535613865306335323637373733353339376364646536663431353361396664336437 | \x5c786165336232386364653032353432663831616363653837383332343534333064
  2 | \x5c783566366539653132636438353932373132653633383031366634623161326537333233306565343064623439386330663062316463383431 | \x5c783233653763366361636238333833663837386164303933623030323764373262
  3 | \x5c783533616332633166613833633866363436303366653935363864383833333331303037643632383164653333306134623565373238663965 | \x5c783765393639313332666336353631343862393762366132656538626338336331
  4 | \x5c783465336332653463623735343261343531373361386461633933396464633462633735323032653334326562633736396230663564613266 | \x5c783865663330663434633635343830643132623635306162366232623034323435
(4 行)
原則として、エンコード済みの文字列には TEXT カラムのみを、バイナリデータには BYTEA カラムのみを使用し、両者を混在させないでください。

関数と演算子のリファレンス

関数

これらの関数は、ClickHouse database にクエリを実行するためのインターフェイスを提供します。

clickhouse_raw_query

SELECT clickhouse_raw_query(
    'CREATE TABLE t1 (x String) ENGINE = Memory',
    'host=localhost port=8123'
);
ClickHouse service の HTTP インターフェイス経由で接続し、1 つの クエリを実行して切断します。省略可能な 2 番目の引数では connection string を指定します。デフォルトは host=localhost port=8123 です。使用可能な接続 パラメーターは次のとおりです。
  • host: 接続先のホスト。必須です。
  • port: 接続先の HTTP ポート。デフォルトは 8123 ですが、host が ClickHouse Cloud ホストの場合は 8443 になります
  • dbname: 接続先の database 名。
  • username: 接続時に使用するユーザー名。デフォルトは default です
  • password: 認証に使用するパスワード。デフォルトではパスワードは設定されていません
デフォルトでは、この関数に対する EXECUTE 権限はどのロールにもありません。アドホックな ClickHouse クエリを実行する正当な必要があるロールにのみ、アクセスを GRANT することを検討してください。 たとえば、専用の ClickHouse admin ロールなどです。 レコードを返さないクエリに便利ですが、値を返すクエリの場合は 単一のテキスト値として返されます。
SELECT clickhouse_raw_query(
    'SELECT schema_name, schema_owner from information_schema.schemata',
    'host=localhost port=8123'
);
      clickhouse_raw_query
---------------------------------
 INFORMATION_SCHEMA      default+
 default default                +
 git     default                +
 information_schema      default+
 system  default                +

(1 row)

プッシュダウン関数

pg_clickhouse は、条件式 (HAVING 句および WHERE 句) で使用される PostgreSQL の組み込み関数の一部をプッシュダウンします。これらに対応する ClickHouse の関数は次のとおりです。

プッシュダウン演算子

  • Array スライス (arr[L:U]): arraySlice
  • @> (配列が含む) : hasAll
  • <@ (配列に含まれる) : hasAll
  • && (配列の重なり) : hasAny
  • ~ (正規表現に一致) : match
  • !~ (正規表現に一致しない) : match
  • ~* (大文字と小文字を区別しない正規表現に一致しない) : match
  • !~* (大文字と小文字を区別しない正規表現に一致しない) : match
  • ->> (JSON/JSONB の要素をテキストとして抽出) : サブカラム構文
  • -> (JSON/JSONB を抽出) : toJSONString + サブカラム構文

カスタム関数

pg_clickhouse で作成されるこれらのカスタム関数は、PostgreSQL に同等の機能がない一部の ClickHouse 関数について、外部クエリのプッシュダウンを可能にします。これらの関数のいずれかをプッシュダウンできない場合は、例外が発生します。

拡張機能のプッシュダウン

pg_clickhouse は、一部のコア拡張機能およびサードパーティ拡張機能の関数を認識し、対応する ClickHouse の関数としてプッシュダウンします。

re2

[re2 拡張機能] のすべての関数は、ClickHouse に 1:1 でプッシュダウンされます。

intarray

ClickHouse にプッシュダウンされる intarray 関数は 1 つあります。

fuzzystrmatch

2つの fuzzystrmatch 関数は ClickHouse にプッシュダウンされます。

キャストのプッシュダウン

pg_clickhouse は、互換性のあるデータ型に対して CAST(x AS bigint) のようなキャストをプッシュダウンします。互換性のない型ではプッシュダウンは失敗します。たとえば、この例で x が ClickHouse の UInt64 である場合、ClickHouse はその値のキャストを拒否します。 互換性のないデータ型へのキャストをプッシュダウンするために、pg_clickhouse は以下の関数を提供しています。これらがプッシュダウンされなかった場合、PostgreSQL で例外を発生させます。

集約関数のプッシュダウン

以下の PostgreSQL 集約関数は ClickHouse にプッシュダウンされます。

カスタム集約関数

pg_clickhouse によって作成されたこれらのカスタム集約関数は、PostgreSQL に同等の機能がない一部の ClickHouse 集約関数に対する外部クエリのプッシュダウンを可能にします。これらの関数のいずれかをプッシュダウンできない場合は、例外がスローされます。

Pushdown 順序付き集合集約関数

これらの順序付き集合集約関数は、直接引数 をパラメータとして、 ORDER BY 式を引数として渡すことで、ClickHouse の[パラメトリック 集約関数]に対応します。たとえば、次の PostgreSQL クエリです。
SELECT percentile_cont(0.25) WITHIN GROUP (ORDER BY a) FROM t1;
次のClickHouseクエリに対応します:
SELECT quantile(0.25)(a) FROM t1;
デフォルト以外の ORDER BY 接尾辞である DESC および NULLS FIRST は サポートされておらず、使用するとエラーになります。

Pushdown ウィンドウ関数

これらの PostgreSQL の[ウィンドウ関数]は、該当する場合はフレーム指定も含めて、OVER (PARTITION BY ... ORDER BY ...) 句とともに ClickHouse にプッシュダウンされます。 ランキング関数 (row_numberrankdense_rankntilecume_distpercent_rank) では、プッシュダウン時にフレーム句は省略されます。これは、ClickHouse がこれらの関数でフレーム指定を受け付けないためです。

互換性に関する注意点

正規表現

pg_clickhouse は pg_clickhouse.pushdown_regex が true (デフォルト) の場合、正規表現を ClickHouse の同等の表現にプッシュダウンし、 基本的なレベルでの互換性を確保しようとしますが、両者の違いと、それらを pg_clickhouse がどのように扱うかを理解しておく必要があります。
  • PostgreSQL は POSIX Regular Expressions をサポートし、ClickHouse は RE2 Regular Expressions をサポートします。動作の違いに注意してください。 正規表現が ClickHouse によって評価される場合 (たとえば WHERE 句内) は RE2 で記述し、Postgres によって評価される場合 (たとえば SELECT 句内) は POSIX で記述してください。
  • pg_clickhouse は、Postgres の [Regex flags] を (?) 内に付加して、 ClickHouse の正規表現にプッシュダウンします。たとえば、次のような式は
    regexp_like(val, '^VAL\d', 'i')
    
    次のようになります。
    match(val, concat('(?i-s)', '^VAL\\d'))
    
    -s が含まれている点に注意してください。これは、ClickHouse でデフォルトで 有効になっている s を無効にし、Postgres の正規表現の動作に合わせるためです。 Postgres の関数呼び出しのフラグに s が含まれている場合、pg_clickhouse は -s を付加しません。残念ながら、この動作により Postgres 24 以前では 一部の正規表現との互換性が損なわれます。
  • 両方でサポートされており、したがって ClickHouse で評価される場合に使用 できるフラグは、次のものだけです。
    • i: 大文字小文字を区別しない
    • m: 複数行モード:
    • s: .\n に一致する
    • p: 改行に部分的に依存するマッチング (s と同様に扱われる)
    • t: 厳密な構文 (デフォルト。pg_clickhouse によって削除される)
    RE2 がサポートするフラグはこれらだけです。Postgres flags のその他のフラグは使用しないでください
  • 正規表現関数にこれ以外のフラグが渡されると、その関数はプッシュダウンされません。
  • 例外は regexp_replace() で、これのみ g フラグもサポートします。g が 指定されている場合、pg_clickhouse は replaceRegexpOne() の代わりに replaceRegexpAll() を使用し、他のフラグを付加する前に g を削除します。
  • Postgres の regexp_replace() の置換引数では、マッチ全体を参照するために \& を使用できます。一方、ClickHouse ではマッチ全体を表すのに \0 を 使用します。関数が ClickHouse にプッシュダウンされる場合は、必ず \0 を 使用してください。
曖昧さを完全に避けるには、 pg_clickhouse.pushdown_regex を設定して Postgres の正規表現が ClickHouse にプッシュダウンされないようにし、さらに pg_clickhouse が ClickHouse 互換の RE2 正規表現の 直接プッシュダウン をサポートしている [re2 拡張機能] の利用を 検討してください。

to_char()

timestamp および timestamp with time zone に対する PostgreSQL の to_char() は、フォーマット引数が NULL ではない文字列定数であり、かつその中の PostgreSQL の各キーワードにバイト単位で完全一致する ClickHouse の対応語がある場合にのみ、ClickHouse の formatDateTime にプッシュダウンされます。フォーマットが動的な場合 (Const ではない場合) や、未対応のキーワードまたは modifier を含む場合、この呼び出しは PostgreSQL 側でローカル評価にフォールバックします。部分的な変換で プッシュダウン を試みることはないため、出力は PostgreSQL 互換のまま保たれます。 numericinterval、その他の非 timestamp 型に対する 2 引数の to_char() 形式は、プッシュダウンされません。ClickHouse の formatDateTime がフォーマットできるのは、日付時刻の値だけです。

変換されるキーワード

PostgreSQLClickHouse意味
YYYY, yyyy%Y4桁の年
YY, yy%y2桁の年
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午前/午後の指示子 (常に大文字)

引用されたテキストとリテラル

"..." で囲まれたテキストは、そのまま出力されます。このとき、ClickHouse の指定子プレフィックスをエスケープするため、リテラルの %%% に変換されます。引用符の外側にある \" も、リテラルの " としてそのまま出力されます。"..." 内では、バックスラッシュでエスケープできるのは " のみで、それ以外のバックスラッシュシーケンスはリテラルテキストとして扱われます。

著者

David E. Wheeler Copyright (c) 2025-2026, ClickHouse
最終更新日 2026年6月10日