pg_clickhouse 是一个 PostgreSQL 扩展,用于在 ClickHouse 数据库上远程执行查询,
并提供一个 foreign data wrapper。它支持 PostgreSQL 13 及更高版本,以及 ClickHouse 23 及更高版本。
尝试 pg_clickhouse 的最简单方式是使用 [Docker 镜像],该镜像基于标准 PostgreSQL Docker 镜像,并已预装 pg_clickhouse 扩展:
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 会跟踪两种版本号:
- 库版本(在 PostgreSQL 18 及更高版本中由
PG_MODULE_MAGIC 定义)包含完整的语义化版本,可在 pg_get_loaded_modules() 函数的输出中看到。
- 扩展版本(在控制文件中定义)只包含主版本号和次版本号,可在
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 将其安装到特定的 schema 中(推荐做法):
CREATE SCHEMA ch;
CREATE EXTENSION pg_clickhouse WITH SCHEMA ch;
ALTER EXTENSION
使用 ALTER EXTENSION 来修改 pg_clickhouse。示例:
DROP EXTENSION
使用 DROP EXTENSION 从数据库中删除 pg_clickhouse:
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"。
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 来删除外部服务器:
如果有其他对象依赖于该服务器对象,则此命令将失败。使用 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
数据库中定义的所有表作为外部表导入到 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
使用 CREATE FOREIGN TABLE 创建一个用于从 ClickHouse 数据库中查询数据的外部表:
CREATE FOREIGN TABLE uact (
user_id bigint NOT NULL,
page_views int,
duration smallint,
sign smallint
) SERVER taxi_srv OPTIONS(
table_name 'uact'
engine 'CollapsingMergeTree'
);
支持的表选项包括:
database:远程数据库的名称。默认为外部服务器定义的数据库。
table_name:远程表的名称。默认为为外部表指定的名称。
engine:ClickHouse 表使用的[表引擎]。对于 CollapsingMergeTree() 和
AggregatingMergeTree(),pg_clickhouse 会自动将参数应用到在该表上执行的函数表达式中。
为每一列使用与远程 ClickHouse 数据类型相匹配的数据类型。对于
AggregateFunction Type 和 SimpleAggregateFunction Type 列,将数据类型映射为传递给
函数的 ClickHouse 类型,并通过相应的列选项指定聚合函数的名称:
示例:
(aggregatefunction 'sum')
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;
对于使用 AggregateFunction 函数的列,pg_clickhouse 会自动在用于计算该列的聚合函数后追加 Merge。
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 来删除外部表:
如果存在任何依赖该外部表的对象,此命令会失败。
使用 CASCADE 子句可以将它们一并删除:
DROP FOREIGN TABLE uact CASCADE;
DML SQL 参考
下面的 SQL DML 表达式可能会使用 pg_clickhouse。示例基于由 make-logs.sql 创建的这些 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”的计划节点将远程 SQL 下推到 ClickHouse。
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/totam
(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 下推到 ClickHouse 执行:
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 表的本地副本,并连接该本地表,而不是远程表:
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 行(全部)减少到仅 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)
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 notes] 中所述。在第六次执行时,它会向 ClickHouse 发送
{param:type} 形式的[查询参数]:
参数:
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
使用 INSERT 命令将数据插入到远程 ClickHouse 表中:
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
使用 COPY 命令将一批行插入到远程 ClickHouse 表中:
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
⚠️ 批量 API 限制
pg_clickhouse 目前尚未实现对 PostgreSQL FDW 批量插入 API 的支持。因此,COPY 当前使用 INSERT 语句来插入记录。此行为将在未来版本中予以改进。
LOAD
使用 LOAD 语句加载 pg_clickhouse 共享库:
try=# LOAD 'pg_clickhouse';
LOAD
通常无需使用 LOAD,因为在首次使用其任一功能(函数、外部表等)时,Postgres 会自动加载 pg_clickhouse。
有一种情况使用 LOAD pg_clickhouse 会很有用:在执行依赖这些参数的查询之前,先通过 SET 设置 pg_clickhouse 参数。
SET
使用 SET 来设置运行时参数 pg_clickhouse.session_settings。
该参数用于配置后续查询中要应用的 ClickHouse settings。示例:
SET pg_clickhouse.session_settings = 'join_use_nulls 1, final 1';
默认值为 join_use_nulls 1。将其设为空字符串以退回到 ClickHouse 服务器端的设置。
SET pg_clickhouse.session_settings = '';
语法为由逗号分隔的键值对列表,各键值对之间以一个或多个空格分隔。键必须对应 ClickHouse settings。值中的空格、逗号和反斜杠需要使用反斜杠进行转义:
SET pg_clickhouse.session_settings = 'join_algorithm grace_hash\,hash';
或者使用用单引号包裹的值以避免对空格和逗号进行转义;也可以考虑使用 dollar quoting,从而无需使用双引号:
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 不会验证这些设置,而是在每次查询时将它们传递给 ClickHouse。因此,它支持该 ClickHouse 版本的所有设置。
请注意,必须先加载 pg_clickhouse,然后才能设置
pg_clickhouse.session_settings;可以使用 [shared library preloading],或者
直接使用扩展中的任一对象以确保其被加载。
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 ROLE 的 RESET 命令来重置 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 数据类型:
| ClickHouse | PostgreSQL | 说明 |
|---|
| Bool | boolean | |
| Date | date | |
| Date32 | date | |
| DateTime | timestamp | |
| Decimal | numeric | |
| Float32 | real | |
| Float64 | double precision | |
| IPv4 | inet | |
| IPv6 | inet | |
| Int16 | smallint | |
| Int32 | integer | |
| Int64 | bigint | |
| Int8 | smallint | |
| JSON | jsonb | 仅适用于 HTTP engine |
| String | text | |
| UInt16 | integer | |
| UInt32 | bigint | |
| UInt64 | bigint | 当值大于 BIGINT 最大值时会报错 |
| UInt8 | smallint | |
| UUID | uuid | |
这些函数为对 ClickHouse 数据库进行查询提供接口。
clickhouse_raw_query
SELECT clickhouse_raw_query(
'CREATE TABLE t1 (x String) ENGINE = Memory',
'host=localhost port=8123'
);
通过 ClickHouse 服务的 HTTP 接口连接、执行单个
查询,然后断开连接。可选的第二个参数指定连接字符串,
默认值为 host=localhost port=8123。支持的连接参数包括:
host:要连接的主机;必填。
port:要连接的 HTTP 端口;默认是 8123,除非 host 是
ClickHouse Cloud 主机,在这种情况下默认是 8443。
dbname:要连接的数据库名称。
username:用于连接的用户名;默认是 default。
password:用于认证的密码;默认为无密码。
适用于不返回记录的查询;对于有返回值的查询,
结果将作为单个文本值返回:
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)
下推函数
在用于查询 ClickHouse 外部表的条件(HAVING 和 WHERE 子句)中,所有 PostgreSQL 内置函数都会以相同的名称和签名自动下推到 ClickHouse。不过,其中有一些函数在 ClickHouse 中具有不同的名称或签名,需要映射到它们的等价函数。pg_clickhouse 会映射以下函数:
自定义函数
由 pg_clickhouse 创建的这些自定义函数,为部分在 PostgreSQL 中没有等价实现的 ClickHouse 函数提供外部查询下推功能。如果这些函数中的任意一个无法下推,则会抛出异常。
下推类型转换
pg_clickhouse 会对兼容的数据类型下推诸如 CAST(x AS bigint) 形式的类型转换。
对于不兼容的类型,下推会失败;如果此示例中的 x 是 ClickHouse 的 UInt64,
ClickHouse 将拒绝执行该类型转换。
为了在不兼容的数据类型上也能进行下推类型转换,pg_clickhouse 提供了以下函数。
如果这些函数未被下推,就会在 PostgreSQL 中抛出异常。
可下推的聚合函数
这些 PostgreSQL 聚合函数可以下推到 ClickHouse 执行。
自定义聚合
这些由 pg_clickhouse 创建的自定义聚合函数,为部分在 PostgreSQL 中没有等价实现的 ClickHouse 聚合函数提供外部查询下推(foreign query 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
不被支持,并会导致报错。
David E. Wheeler
版权声明
Copyright (c) 2025-2026, ClickHouse
"PostgreSQL 文档:共享库预加载
[PREPARE notes]: https://www.postgresql.org/docs/current/sql-prepare.html#SQL-PREPARE-NOTES
"PostgreSQL 文档:PREPARE 注意事项"
[query parameters]: https://clickhouse.com/docs/guides/developer/stored-procedures-and-prepared-statements#alternatives-to-prepared-statements-in-clickhouse
"ClickHouse 文档:ClickHouse 中预处理语句的替代方案"