Skip to main content

描述

pg_clickhouse 是一个 PostgreSQL 扩展,可在 ClickHouse 数据库上远程执行查询, 其中包括一个[外部数据包装器]。它支持 PostgreSQL 14 及以上版本,以及 ClickHouse 23.3 及以上版本。

快速开始

试用 pg_clickhouse 最简单的方式是使用 [Docker 镜像],其中包含 标准的 PostgreSQL Docker 镜像,并集成了 pg_clickhouse 和 [re2][re2 扩展] 扩展:
请参阅教程,开始了解如何导入 ClickHouse 表并下推查询。

用法

版本策略

pg_clickhouse 的公开发布版本遵循[语义化版本控制]。
  • API 发生变更时,递增主版本号
  • SQL 发生向后兼容的变更时,递增次版本号
  • 仅有 binary 变更时,递增补丁版本号
安装后,PostgreSQL 会跟踪两种版本形式:
  • 库版本 (在 PostgreSQL 18 及以上版本中由 PG_MODULE_MAGIC 定义) 包含完整的语义化版本号,可在 pgch_version() 函数的输出或 Postgres 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 扩展添加到数据库中:
使用 WITH SCHEMA 将其安装到指定的 schema 中 (推荐) :

ALTER EXTENSION

使用 ALTER EXTENSION 修改 pg_clickhouse。示例:
  • 安装 pg_clickhouse 的新 release 后,使用 UPDATE 子句:
  • 使用 SET SCHEMA 将该扩展移动到新的 schema:

DROP EXTENSION

使用 DROP EXTENSION 从数据库中删除 pg_clickhouse:
如果存在任何依赖于 pg_clickhouse 的对象,此命令会失败。请使用 CASCADE 子句将它们一并删除:

CREATE SERVER

使用 CREATE SERVER 创建连接到 ClickHouse 服务器的外部服务器。示例:
支持的选项有:
  • driver:要使用的 ClickHouse 连接驱动程序,可以是 “binary” 或 “http”。必填。
  • compression:binary driver 的 native 协议压缩,可选值为 “none”、“lz4” 或 “zstd”。默认为 “lz4”。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
  • min_tls_version:对使用 TLS 的连接协商的最低 TLS 协议版本。 可选值为 TLSv1、TLSv1.1、TLSv1.2 或 TLSv1.3。默认值 为 TLS 库自身的最低版本。适用于两种驱动程序。
  • secure:控制连接是否使用 TLS。可选值为:
    • auto (默认) :当 host 是 ClickHouse Cloud 主机或 port 是安全端口时使用 TLS;否则使用 plaintext。
    • on (或 true/yes/1) :始终使用 TLS。默认 port 为 8443 (“http”) 或 9440 (“binary”) 。
    • off (或 false/no/0) :从不使用 TLS。默认 port 为 8123 (“http”) 或 9000 (“binary”) 。
  • encoding_check:定义在转换 ClickHouse String 和 JSON 值时,如何处理 database encoding 下的无效字符。可选值为:
    • fail (默认) :引发错误
    • remove:删除无效字节
    • replace:在 UTF-8 编码下,将无效字节替换为 Unicode 替换字符 (�) ;对于其他编码,与 remove 相同
    • truncate:在第一个无效字节处截断文本

ALTER SERVER

使用 ALTER SERVER 修改 外部服务器。示例:
这些选项与 CREATE SERVER 中的选项相同。

DROP SERVER

使用 DROP SERVER 删除 外部服务器:
如果有任何其他对象依赖于该服务器,此命令将失败。使用 CASCADE 还会 一并删除这些依赖对象:

CREATE USER MAPPING

使用 CREATE USER MAPPING 将 PostgreSQL 用户映射为 ClickHouse 用户。例如,使用 taxi_srv 外部服务器 连接时,可将当前 PostgreSQL 用户映射为远程 ClickHouse 用户:
支持的选项如下:
  • user:ClickHouse 用户名。默认为 “default”。
  • password:ClickHouse 用户的密码。

ALTER USER MAPPING

使用 ALTER USER MAPPING 修改用户映射的定义:
这些选项与 CREATE USER MAPPING 中的选项相同。

DROP USER MAPPING

使用 DROP USER MAPPING 移除用户映射:

IMPORT FOREIGN SCHEMA

使用 IMPORT FOREIGN SCHEMA 可将 ClickHouse 数据库中定义的所有表作为外部表导入到 PostgreSQL schema 中:
使用 LIMIT TO 将导入限制到特定表:
使用 EXCEPT 排除这些表:
pg_clickhouse 将获取指定 ClickHouse 数据库 (上述示例中的 “demo”) 中所有表的列表,获取每个表的列定义, 并执行 CREATE FOREIGN TABLE 命令来创建 外部表。列将使用支持的数据 类型进行定义,并在可检测的情况下使用 CREATE FOREIGN TABLE 支持的选项。 需要注意以下几点:
  • 导入的列会保留 ClickHouse 类型的类型修饰符,Array 内部的类型 也不例外。例如,Array(Decimal(12,6)) 会被导入为 numeric(12,6)[]。
  • Nullable 列在导入时不带 NOT NULL;而 Array 内部的 Nullable 则无此影响,因为 PostgreSQL 数组始终允许 NULL 元素。
  • 无论 ClickHouse 中如何声明,AggregateFunction 和 SimpleAggregateFunction 列在导入时 均不带 NOT NULL,参见状态列与 NOT NULL。
  • Tuple 列会被导入为 text[]。Map 列以及使用 flatten_nested=0 创建的 Nested 列 会被导入为 text[][]。每次转换都会发出 一条 NOTICE。由于 PostgreSQL 的 record 伪类型无法用于定义表列, 因此每个 tuple 会转换为一个值数组,每个 map 会转换为一个由键值对组成的 二维数组,每个 Nested 值会转换为一个由行组成的 二维数组。若要以记录形式读取字段,请将该列修改为 相应复合类型的数组,示例参见手动类型 映射。
  • 精度大于 6 (微秒) 的 DateTime64 和 Time64 列 同样会触发 NOTICE,因为 PostgreSQL 的精度上限为 timestamp(6) 和 time(6)。
  • 在 PostgreSQL 中没有对应类型的列类型 (包括早于 ClickHouse JSON 出现的旧版 Object('json') 类型) 会触发错误。
导入标识符的大小写保留IMPORT FOREIGN SCHEMA 会对导入的表名和列名运行 quote_identifier(), 为包含大写字母或空格的标识符加上双引号。 因此,这类表名和列名在 PostgreSQL 查询中必须用双引号括起来。 如果名称全部为小写且不包含空格字符,则无需加引号。例如,给定以下 ClickHouse 表:
IMPORT FOREIGN SCHEMA 会创建这个外部表:
因此,查询中也必须正确加引号,例如:
若要创建名称不同或全部为小写 (因此 不区分大小写) 的对象,请使用 CREATE FOREIGN TABLE。

CREATE FOREIGN TABLE

使用 CREATE FOREIGN TABLE 创建可查询 ClickHouse 数据库中数据的外部表:
支持的表选项如下:
  • database:远程 数据库 的名称。默认使用为 外部服务器 定义的 数据库。
  • table_name:远程表的名称。默认使用为 外部表 指定的名称。
  • engine:ClickHouse 表使用的[表引擎]。对于 CollapsingMergeTree() 和 AggregatingMergeTree(),pg_clickhouse 会自动将参数应用于在该表上执行的函数表达式。
对于每一列,请使用与远程 ClickHouse 数据 类型相对应的数据类型。支持的列选项如下:
  • column_name:ClickHouse 端的列名,在反解析查询和 插入语句时,会优先使用该名称而不是 PostgreSQL attribute 名称。 这对于将未加引号的小写 PostgreSQL 列名映射到 区分大小写的 ClickHouse 列很有用,例如:
  • AggregateFunction:应用于 AggregateFunction Type 列的聚合函数名称。将数据类型映射为传递给该函数的 ClickHouse 类型,并通过相应的列选项指定 聚合函数名称,pg_clickhouse 会自动在计算该列的聚合函数后追加 Merge。请将该列声明为 可为空 (nullable) ,以避免出现 状态列 and NOT NULL 中所述的问题。
  • SimpleAggregateFunction:应用于 SimpleAggregateFunction Type 列的聚合函数名称。将数据类型映射为 传递给该函数的 ClickHouse 类型,并通过相应的 列选项指定聚合函数名称。

状态列与 NOT NULL

请保持 AggregateFunction 和 SimpleAggregateFunction 列为可空 (Nullable) 。 对于 NOT NULL 状态列,PostgreSQL 19 会将 count(column) 改写为 count(*), 导致统计的是行数,而非合并状态。 以一个 ClickHouse 表为例,该表为每个用户存储一个 count 状态,每个状态均由 1,000 个事件构建而成:
使用 NOT NULL 时,PostgreSQL 会对状态进行计数,导致计数结果无效:
如果不使用 NOT NULL,pg_clickhouse 会使用 countMerge() 合并状态,从而得到正确的计数 1000:
使用 ALTER FOREIGN TABLE 删除现有外部表上的该约束:
NOT NULL 仅对 PostgreSQL 生效。因此,IMPORT FOREIGN SCHEMA 会将状态列导入为可为空 (Nullable) 的列。

ALTER FOREIGN TABLE

使用 ALTER FOREIGN TABLE 修改外部表的定义:
支持的表和列选项与 CREATE FOREIGN TABLE 中的相同。

DROP FOREIGN TABLE

使用 DROP FOREIGN TABLE 删除外部表:
如果有任何对象依赖于该外部表,此命令将失败。 使用 CASCADE 子句也会将它们一并删除:

DML SQL 参考

下面的 SQL DML 表达式可能会用到 pg_clickhouse。示例基于 以下 ClickHouse 表:

EXPLAIN

EXPLAIN 命令可按预期运行,但 VERBOSE 选项会触发输出 ClickHouse 的 “Remote SQL” 查询:
此查询会通过 “Foreign Scan” 计划节点下推到 ClickHouse,作为远程 SQL 执行。

SELECT

使用 SELECT 语句在 pg_clickhouse 表上执行查询,与在任何其他表上执行查询无异:
pg_clickhouse 会尽可能将查询执行下推到 ClickHouse,包括聚合函数。使用 EXPLAIN 可确定 下推的范围。例如,对于上述查询,所有执行都会下推到 ClickHouse
pg_clickhouse 还会将同一远程服务器上的表之间的 JOIN 下推:
如果不经过仔细调优,与本地表连接会生成效率较低的查询。在这个示例中,我们创建了 nodes 表的一个本地副本,并与它连接,而不是连接远程表:
在这种情况下,我们可以通过 按 node_id 而不是本地列分组,将更多聚合下推到 ClickHouse,然后再 与查找表进行 join:
“Foreign Scan”节点现在会按 node_id 下推聚合,将需要从 Postgres 拉回的行数从 1000 行 (全部行) 减少到仅 8 行,即每个节点 1 行。

分区表

PostgreSQL [分区表]可以同时包含本地分区和由 ClickHouse 支持的外部分区。常见的布局是将较早的数据迁移到 ClickHouse,而较新的数据保留在 PostgreSQL 中:
有关如何将数据从本地分区迁移到外部分区的示例,请参阅 offload-partition.sql。 跨本地分区和外部分区的聚合需要启用 partitionwise aggregation,而 PostgreSQL 默认将其禁用:
启用 enable_partitionwise_aggregate 后,PostgreSQL 会在 Append 下方计算部分聚合,再由上方的最终聚合将这些部分结果合并为最终结果。pg_clickhouse 会将外部分区的部分聚合下推至 ClickHouse:

部分聚合何时可以下推

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, squared deviations 的总和} 状态作为数组下推。
FILTER (WHERE …) 也可以与这些聚合函数一起下推。

何时回退

转换状态为 PostgreSQL 不透明 internal 类型的聚合没有可移植的表示形式,因此外部分区会转而拉取其行,并在本地进行聚合。这包括所有基于 numeric 的聚合,以及 avg(bigint) 和 avg(interval)。DISTINCT、有序集聚合和可变参数聚合也会回退。

PREPARE, EXECUTE, DEALLOCATE

自 v0.1.2 起,pg_clickhouse 支持参数化查询,主要通过 PREPARE 命令创建:
像平常一样使用 EXECUTE 执行预处理语句:
与往常一样,pg_clickhouse 会下推聚合操作,如 EXPLAIN 的详细输出所示:
请注意,它发送的是完整的日期值,而不是参数占位符。 前五次请求都是如此,如 PostgreSQL [PREPARE 说明] 中所述。到第六次执行时,它会发送 ClickHouse {param:type} 风格的[查询参数]:
使用 DEALLOCATE 释放预处理语句:

INSERT

使用 INSERT 命令向远程 ClickHouse 表中插入值:

COPY

使用 COPY 命令将一个批次的数据行插入远程 ClickHouse 表:
⚠️ 批次 API 的限制 pg_clickhouse 尚未支持 PostgreSQL FDW 的批次 insert API。因此,COPY 目前通过 INSERT 语句 插入记录。后续版本将对此进行改进。

LOAD

使用 LOAD 加载 pg_clickhouse 共享库:
通常不需要使用 LOAD,因为 Postgres 会在首次使用 pg_clickhouse 的任一功能 (函数、外部表等) 时自动加载它。 而 LOAD pg_clickhouse 可能有用的一种情况是:在执行依赖这些参数的查询之前,先通过 SET 设置 pg_clickhouse 参数。

SET

使用 SET 来设置 pg_clickhouse 的自定义配置参数。

pg_clickhouse.session_settings

pg_clickhouse.session_settings 参数用于配置在后续查询中要设置的 [ClickHouse 设置]。示例:
默认值为
将其设为空字符串即可恢复为 ClickHouse server 的设置 — 但请注意,下推的正确性取决于其中某些默认值: OUTER JOIN 的 join_use_nulls 以及 IN 家族的 transform_null_in (请参阅 IN 和 NULL 语义) 。
其语法为由逗号分隔的键/值对列表,各项之间以一个或 多个空格分隔。键必须与 [ClickHouse 设置] 对应。在值中,空格、 逗号和反斜杠都要用反斜杠转义:
或者使用单引号包裹值,以避免转义空格和逗号;也可以考虑 使用[美元引号表示法],这样就无需再使用双引号:
如果你更注重可读性,并且需要设置很多项,可以分成多 行,例如:
当这些设置会干扰 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;可以使用共享库预加载,或者 直接使用该扩展中的某个对象以确保其被加载。

pg_clickhouse.pushdown_regex

pg_clickhouse.pushdown_regex 参数用于控制 pg_clickhouse 是否对正则表达式函数和操作符执行下推。默认情况下会执行下推; 将此参数设置为 false 可阻止下推:
详见正则表达式。

ALTER ROLE

使用 ALTER ROLE 的 SET 命令,可为特定角色预加载 pg_clickhouse, 和/或 SET 其参数:
使用 ALTER ROLE’s RESET 命令重置 pg_clickhouse 的预加载 和/或相关参数:

预加载

如果每个或几乎每个 Postgres 连接都需要使用 pg_clickhouse, 请考虑使用共享库预加载来自动加载它:

session_preload_libraries

在每次建立与 PostgreSQL 的新连接时加载共享库:
有助于在不重启服务器的情况下应用更新:只需 重新连接即可。也可以通过 ALTER ROLE 为特定用户或角色设置。

shared_preload_libraries

在启动时将共享库加载到 PostgreSQL 主进程中:
有助于减少每个会话的内存占用和加载开销,但在库更新时需要 重启集群。

数据类型

下表列出了 ClickHouse 数据类型到 PostgreSQL 数据类型的推荐映射关系。IMPORT FOREIGN SCHEMA 会使用这些映射,并推导出相应的类型修饰符和数组维度。如需声明其他 PostgreSQL 类型,请使用 CREATE FOREIGN TABLE。 “附加读取目标”列出了 binary 驱动程序在 PostgreSQL 显式类型转换之外额外支持的转换。单元格为空时,仍可使用这些显式类型转换。值必须在目标类型的取值范围内。这些目标仅适用于读取操作;写入操作遵循单独的转换规则。 任何列也都可以读取为 text、varchar 或其他字符串类型。此时值 会先转换为上文所述的 PostgreSQL 类型,再经由该类型的输出 函数进行渲染。读取为文本类型的 ClickHouse 字符串会依据 数据库编码进行校验,因此 PostgreSQL 无法识别的字节会引发错误。若要按 ClickHouse 写入时的原样读取字节,请将 String、FixedString、Enum 或 JSON 列声明为 BYTEA。 支持输入转换的类型可以通过其 PostgreSQL 输入函数读取字符串。 复合类型的字段必须一一匹配,且顺序一致。要将 tuple 读取 为 array,每个字段都必须能转换为该 array 的元素类型,且任何字段 本身都不能是 array。 读取为 array 时,Map 的每个键值对对应一行,Nested 的 每条嵌套记录对应一行。要将 tuple 读取为 box,需提供两个点;读取为 circle 时,需提供一个点和半径;读取为 line 时,需提供三个系数。 更多说明和细节见下文。

类型强制转换

使用 binary 驱动程序时,其他标量类型会尽可能使用 PostgreSQL 的显式 类型转换,array 元素则会转换为声明的 元素类型。不支持的转换以及超出范围的值会引发错误。 无论使用哪种驱动程序,都可以将 ClickHouse Interval 类型映射为 smallint、integer 或 bigint,以按其单位数量进行读写。例如, 值为 3 的 IntervalDay 会映射为整数 3。将 IntervalNanosecond 映射为 bigint 可保留纳秒精度,而映射为 interval 则会截断至微秒。

BYTEA

ClickHouse 不提供与 PostgreSQL BYTEA 类型等效的类型,但允许将任意字节存储在 [String] 类型中。一般情况下,ClickHouse 字符串应映射到 PostgreSQL 的 [TEXT] 类型;若需使用二进制数据,则应映射到 BYTEA。示例:
最终的 SELECT 查询将输出:
使用 [TEXT] 列的外部表通常无法将这些值读取为 文本,因为 pg_clickhouse 会依据数据库编码 校验 ClickHouse 字节:
输出结果:
摘要 (digest) 包含任意字节,这些字节很少能构成有效文本。此外,PostgreSQL 还保留了 nul 字符,因此若 ClickHouse 字符串中包含 nul,[TEXT] 列将永远无法读取该字符串。 尝试向 [TEXT] 列插入二进制值将会成功,并按预期运行:
此时文本列中的数据将是正确的:
但如果按 BYTEA 读取,则不会:
FixedString(N) 列会用空字节填充较短的值。文本列会丢弃这些尾随填充,而 BYTEA 列则会保留所有字节。
一般来说,[TEXT] 列只用于编码字符串,BYTEA 列只用于二进制数据, 并且绝不要在两者之间切换。

复合类型

Array

ClickHouse [Array] 直接映射为 PostgreSQL 数组,二者语义等价。主要区别在于,Postgres 多维数组要求其中各数组表达式的维度一致。尝试读取维度不一致的 ClickHouse 数组 (例如 [[1], [2,3]]) 时, 会触发错误。 数组索引访问会在适当情况下下推,多维 索引访问也同样适用。示例:
数组切片同样可以通过 [arraySlice] 函数下推,但目前尚不支持多维切片访问:

Map

PostgreSQL 中没有与 ClickHouse [Map] 类型相对应的类型。 因此,pg_clickhouse 会将 [Map] 列映射为 text[][],其中每个键值对 各自作为一个独立的 array。IMPORT FOREIGN SCHEMA 也采用 这一映射。同样,也可以将 Map 以 array 的形式 INSERT。示例如下:
每个嵌套数组必须恰好包含两个文本值。如果值与 ClickHouse 中对应的键类型或值类型 不兼容,将会报错。
插入 Map 需要使用 binary 驱动程序,该驱动程序会从 ClickHouse 服务器获取列类型。 http 驱动程序不具备这一能力,因此缺少对相应值进行格式化和插入所需的信息。

Tuple

同样,ClickHouse 的 [Tuple] 列会映射为 text[],并支持通过 binary driver 执行 INSERT。IMPORT FOREIGN SCHEMA 在执行此类映射时会发出 NOTICE。

Nested

默认情况下,ClickHouse 会将 Nested 列按字段拆分为多个 Array 列,每个字段对应一列 (flatten_nested=1) 。IMPORT FOREIGN SCHEMA 会从 ClickHouse 的 system.columns 目录中读取这些列,并分别创建对应的 PostgreSQL array 列,同时保留 items.a、items.b 这类带点号的名称。例如, 对于以下外部表:
其导入后的 schema 包含三列,而非两列:
查询 Nested 列时,需用双引号将列名括起来,例如:
还可以在 WHERE 子句中使用常规的数组功能 (包括数组下标语法) 对值进行过滤:
使用 flatten_nested=0 创建的 [Nested] 列会映射为一个 array,每个嵌套行对应其中一个元素。每个 array 元素包含该行的各个值:
此时得到的 schema 如下:
将嵌套记录组织为一个二维数组,其中的文本值需按照 ClickHouse 列中定义的各个类型进行格式化。 以本示例中的 c2 Nested(id Int64, name String) 为例,其形式如下:
不过,基于 Nested 类型的 text[][] 列无法下推:
这是因为 pg_clickhouse 无法区分映射到 ClickHouse array 列的 text array 列与映射到 Nested 列的 text array 列,因此无法将其重写为 ClickHouse 的 Nested 语法。 不过,也可以使用与之匹配的复合类型数组来映射这些项 (详见 手动类型映射) 。在这种情况下, 只要复合类型的字段名与 Nested 字段名完全一致,下推即可正常生效:

手动类型映射

IMPORT FOREIGN SCHEMA 使用的是通用的 PostgreSQL 类型。例如, 假设某个 ClickHouse 表包含 [Enum]、[Tuple]、Map 以及未展平的 [Nested] (flatten_nested = 0) 列,如下所示:
如需保留结构或约束取值,可定义 PostgreSQL 复合类型或枚举 类型,并手动创建外部表:
务必使枚举标签和复合类型字段名与 ClickHouse 枚举标签以及 Nested 或 Tuple 字段名完全一致,以确保 下推时使用正确的名称。
复合类型的字段顺序和类型须与 ClickHouse 中的声明保持一致。[Map] 的键 和值分别对应第一个和第二个字段 (本例中为 key 和 value) 。Nested 和 Map 会映射为复合类型数组,而 Tuple 则映射为单个复合值:
当然,你也可以使用复合类型的字段名,例如在 SELECT 列表中:
此外,对于 WHERE 子句中的 [Nested] 类型,只要字段名 完全一致即可:
目前尚不支持使用此类复合类型执行 INSERT。

函数与 operator 参考

函数

这些函数提供了查询 ClickHouse 数据库的接口。

clickhouse_server_version

报告指定 外部服务器 的 ClickHouse 服务器版本,格式为 major.minor.patch。如有必要,将使用该服务器的选项和当前用户的 user mapping 进行连接:
从原生协议连接的握手过程中读取版本信息,或通过 HTTP 执行单个 SELECT version() 查询获取版本信息,并在连接的整个生命周期内缓存该信息。

clickhouse_query

针对已配置的外部服务器执行查询,并将返回的 行作为关系返回,同时将每个 ClickHouse 结果列映射为列定义列表中 指定的 PostgreSQL 类型。它会复用该服务器的 driver、 凭证、数据库和连接缓存。 第一个参数是通过 CREATE SERVER 创建的服务器名称。必须提供 列定义列表 (AS name(col type, ...)) :PostgreSQL 在拉取行之前需要知道 结果形态,且该列表必须与查询返回的列相匹配。值会以与外部表列相同的方式 从 ClickHouse 转换为声明的类型。不返回结果的语句 (如 DDL) 无需声明任何内容;请改用 clickhouse_perform 执行它们。 默认情况下,任何角色都没有 EXECUTE 权限;请向角色 GRANT 权限,以允许其使用 该函数。

clickhouse_perform

对已配置的 外部服务器 执行一条语句,并丢弃 所有结果。适用于不返回行的语句,例如 DDL;此时 clickhouse_query 没有可声明的结果形态。它以与 clickhouse_query 相同的方式 解析 服务器,并复用其 driver、凭证、数据库 和 连接缓存。 作为存储过程,必须通过 CALL 而非 SELECT 调用,并且不返回 行。默认情况下,没有任何角色拥有 EXECUTE 权限;向角色授予 GRANT 权限即可允许其 使用该存储过程。

下推函数

pg_clickhouse 会将条件子句 (HAVING 和 WHERE 子句) 中使用的部分 PostgreSQL 内置函数下推到 ClickHouse 中执行。该子集与 ClickHouse 中的对应函数如下:

下推操作符

IN 和 NULL 语义

ClickHouse 对 IN 采用二值逻辑:当探测值未找到 匹配项时,即使涉及 NULL,也会返回 0,而 PostgreSQL 则会计算为 NULL。为保留 PostgreSQL 语义,pg_clickhouse 会无条件下推针对常量列表或数组的 IN 系列操作 (IN、NOT IN、= ANY、= ALL、<> ANY、<> ALL) :若能够证明探测值和数组元素均不可能为 NULL,则使用原生或开销较低的形式; 否则使用带保护条件的 CASE 形式,在运行时检查 NULL 值, 从而在所有上下文中准确计算 PostgreSQL 的三值结果 (TRUE、FALSE、NULL) , 包括 SELECT 列表或 GROUP BY 等值位置。 针对可空列的 NOT IN (SELECT ...) 过滤器也会下推, 并在反解析时加入补偿性保护条件,以保持 PostgreSQL 的行为:包含 NULL 的集合会排除 所有行,而 NULL 探测值仅会在集合为空时通过。当 NOT NULL 声明 证明某个保护条件并非必需时,该条件会被省略。与上述数组形式不同,此保护条件仅 适用于普通过滤条件 (或位于 NOT 之下) ;我们仍不会下推 值位置中的 IN (SELECT ...),也不会下推已分组或聚合的子查询 主体。将列声明为 NOT NULL 可使系统采用开销更低的无保护形式,从而最大化 下推;对于非 Nullable 的 ClickHouse 列,IMPORT FOREIGN SCHEMA 会自动执行此操作。 该推断基于非 NULL 常量、NOT NULL 列,以及对它们执行的基本算术运算 (+、-、*、一元 -) 。 这些规则假定 ClickHouse 的默认设置为 transform_null_in = 0。pg_clickhouse 会通过 pg_clickhouse.session_settings 参数的默认值, 在每个查询中设置该值, 以防止 ClickHouse 服务器 profile 对其进行静默更改。设置 transform_null_in = 1 会破坏所有已下推 IN 的语义。

自定义函数

这些由 pg_clickhouse 创建的自定义函数,可为部分在 PostgreSQL 中没有对应实现的 ClickHouse 函数提供外部查询下推。如果其中任何函数无法下推,则会引发异常。

扩展下推

pg_clickhouse 可识别部分核心扩展和第三方扩展中的函数,并将其下推为 ClickHouse 中对应的等效函数。

re2

所有 [re2 extension] 运算符和函数都可 1:1 下推到 ClickHouse:

intarray

有一个 [intarray] 函数可下推至 ClickHouse:

fuzzystrmatch

两个 [fuzzystrmatch] 函数可下推至 ClickHouse:

pgcyrpto

  • digest(text, text) 和 digest(bytea, text):当算法为常量 md5、sha1、sha224、 sha256、sha384 或 sha512 (匹配时不区分大小写) 时,下推为对应的 ClickHouse 哈希函数。

下推类型转换

对于兼容的 数据类型,pg_clickhouse 会下推 CAST(x AS bigint) 这类类型转换。 对于不兼容的类型,下推会失败;例如,如果此示例中的 x 是 ClickHouse 的 UInt64, ClickHouse 会拒绝执行该类型转换。 为了将类型转换下推到不兼容的 数据类型,pg_clickhouse 提供了 以下函数。如果这些函数没有被下推,就会在 PostgreSQL 中引发异常。

聚合函数下推

以下 PostgreSQL 聚合函数可下推到 ClickHouse。

自定义聚合函数

这些由 pg_clickhouse 创建的自定义聚合函数,可为部分在 PostgreSQL 中没有对应函数的 ClickHouse 聚合函数提供外部查询下推。如果这些函数中的任何一个无法下推, 就会引发异常。

下推有序集合聚合函数

这些有序集合聚合函数会映射为 ClickHouse 的[参数化 聚合函数]:将其直接参数作为 parameter 传递,并将其 ORDER BY expression 作为 argument。例如,以下 PostgreSQL 查询:
对应的 ClickHouse 查询如下:
请注意,不支持非默认的 ORDER BY 后缀 DESC 和 NULLS FIRST, 使用它们会报错。

自定义有序集合聚合函数

这些由 pg_clickhouse 创建的自定义有序集合聚合函数为部分 ClickHouse [参数化聚合 函数]提供外部查询下推。若其中任何函数无法下推,将引发异常。

自定义有序集合聚合函数

这些由 pg_clickhouse 创建的自定义有序集合聚合函数可为特定的 ClickHouse 参数化聚合函数提供外部查询下推。如果其中任何函数无法下推,则会引发异常。

下推窗口函数

以下 PostgreSQL [窗口函数]可通过包含 OVER (PARTITION BY ... ORDER BY ...) 子句的查询下推到 ClickHouse;适用时还包括窗口帧规范。 排名函数 (row_number、rank、dense_rank、ntile、cume_dist、 percent_rank) 在下推时会省略窗口帧子句,因为 ClickHouse 不支持为这些函数指定窗口帧。

兼容性说明

正则表达式

当 pg_clickhouse.pushdown_regex 为 true (默认值) 时, pg_clickhouse 会将正则表达式下推为 ClickHouse 中的等效形式, 并尽力保证基本兼容性,但你仍需了解二者之间的差异,以及 pg_clickhouse 如何处理这些差异。
  • PostgreSQL 支持 [POSIX 正则表达式],而 ClickHouse 支持 [RE2 正则表达式][RE2]。请注意两者的行为差异:当正则表达式由 ClickHouse 求值时 (例如,在 WHERE 子句中) ,请使用 RE2;当它由 Postgres 求值时 (例如,在 SELECT 子句中) ,请使用 POSIX。
  • pg_clickhouse 通过将 [Postgres 标志] 置于 ClickHouse 正则表达式前面并放入 (?) 中,来实现下推。例如:
    变成
  • 两者唯一同时支持、因此可在由 ClickHouse 求值时使用的标志有: RE2 仅支持这些标志;请勿使用任何其他 [Postgres 标志]。
  • 下表概述了各种标志 (以及不加标志的情况,此时 与 s 相同) 在匹配换行符和行尾时的影响。请注意,在 Postgres 中,m 和 p 会阻止否定字符类 ([^xyz]) 匹配 换行符,而 ClickHouse 中的对应标志则不会。除此之外, ClickHouse 的行为与 Postgres 相同:
  • 传递给正则表达式函数的任何其他标志都会阻止 该函数被下推。
  • 例外是 regexp_replace(),它还支持 g 标志。当 设置 g 时,pg_clickhouse 会使用 replaceRegexpAll() 而不是 replaceRegexpOne(),并会先移除该标志,再添加其他标志。
  • Postgres 的 regexp_replace() 的 replacement 参数支持使用 \& 来 引用整个匹配;而在 ClickHouse 中,则使用 \0 表示整个 匹配。该函数下推到 ClickHouse 时,务必使用 \0。
  • 当没有匹配项时,Postgres regexp_match 会返回 NULL,而 它下推执行的表达式则会返回空数组。请使用 COALESCE() 返回空数组而不是 NULL,以便以兼容的方式比较返回值。 例如:
为避免任何歧义,建议设置 pg_clickhouse.pushdown_regex 以阻止 Postgres 正则表达式下推到 ClickHouse,并使用 [re2 extension];对于该扩展,pg_clickhouse 支持将 与 ClickHouse 兼容的 [RE2] 正则表达式进行直接下推。

to_char()

PostgreSQL 中用于 timestamp 和 timestamp with time zone 的 [to_char()] 仅在 format 参数为非 NULL 的 string constant,且其中每个 PostgreSQL 关键字 在 ClickHouse 中都有一个逐字节完全一致的对应项时,才会下推到 ClickHouse [formatDateTime]。 如果 format 是动态的,或者包含任何不支持的关键字或 modifier, 则该调用会回退到 PostgreSQL 本地求值。pg_clickhouse 绝不会下推部分转换的结果, 因此输出会保持兼容。 用于 numeric、interval 及其他非 timestamp 类型的双参数 to_char() 形式永远不会下推;ClickHouse [formatDateTime] 仅用于格式化日期时间值。

已转换的关键字

带引号的文本和字面量

用 "..." 包裹的文本会原样传递,其中任何字面的 % 都会写成 %%,以转义 ClickHouse 的格式说明符前缀。引号外的 \" 也会 作为字面量 " 原样传递。在 "..." 内部,反斜杠只会转义 "; 其他反斜杠序列都会被视为字面文本。

作者

David E. Wheeler 版权所有 (c) 2025-2026,ClickHouse [Array] https://clickhouse.com/docs/reference/data-types/array “ClickHouse 文档:Array(T)” [Map]: https://clickhouse.com/docs/sql-reference/data-types/map “ClickHouse 文档:Map” [Tuple]: https://clickhouse.com/docs/reference/data-types/tuple “ClickHouse 文档:Tuple(T1, T2, …)” [Nested]: https://clickhouse.com/docs/sql-reference/data-types/nested-data-structures/nested “ClickHouse 文档:Nested” [Enum]: https://clickhouse.com/docs/reference/data-types/enum “ClickHouse 文档:Enum” [String]: /reference/data-types/string “ClickHouse 文档:String” [TEXT]: https://www.postgresql.org/docs/current/datatype-character.html “PostgreSQL 文档:字符类型” [window functions]: 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&#95;re2 “pg_re2:使用 RE2 的 ClickHouse 兼容正则函数” [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”
最后修改于 2026年8月26日