描述
pg_clickhouse 是一个 PostgreSQL 扩展,可在 ClickHouse 数据库上远程执行查询, 其中包括一个[外部数据包装器]。它支持 PostgreSQL 14 及以上版本,以及 ClickHouse 23.3 及以上版本。快速开始
试用 pg_clickhouse 最简单的方式是使用 [Docker 镜像],其中包含 标准的 PostgreSQL Docker 镜像,并集成了 pg_clickhouse 和 [re2][re2 扩展] 扩展:用法
版本策略
pg_clickhouse 的公开发布版本遵循[语义化版本控制]。- API 发生变更时,递增主版本号
- SQL 发生向后兼容的变更时,递增次版本号
- 仅有 binary 变更时,递增补丁版本号
- 库版本 (在 PostgreSQL 18 及以上版本中由
PG_MODULE_MAGIC定义) 包含完整的语义化版本号,可在pgch_version()函数的输出或 Postgrespg_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: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 修改 外部服务器。示例: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 修改用户映射的定义:DROP USER MAPPING
使用 DROP USER MAPPING 移除用户映射:IMPORT FOREIGN SCHEMA
使用 IMPORT FOREIGN SCHEMA 可将 ClickHouse 数据库中定义的所有表作为外部表导入到 PostgreSQL schema 中:LIMIT TO 将导入限制到特定表:
EXCEPT 排除这些表:
-
导入的列会保留 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')类型) 会触发错误。
CREATE FOREIGN TABLE
使用 CREATE FOREIGN TABLE 创建可查询 ClickHouse 数据库中数据的外部表:database:远程 数据库 的名称。默认使用为 外部服务器 定义的 数据库。table_name:远程表的名称。默认使用为 外部表 指定的名称。engine:ClickHouse 表使用的[表引擎]。对于CollapsingMergeTree()和AggregatingMergeTree(),pg_clickhouse 会自动将参数应用于在该表上执行的函数表达式。
-
column_name:ClickHouse 端的列名,在反解析查询和 插入语句时,会优先使用该名称而不是 PostgreSQL attribute 名称。 这对于将未加引号的小写 PostgreSQL 列名映射到 区分大小写的 ClickHouse 列很有用,例如: -
AggregateFunction:应用于 AggregateFunction Type 列的聚合函数名称。将数据类型映射为传递给该函数的 ClickHouse 类型,并通过相应的列选项指定 聚合函数名称,pg_clickhouse 会自动在计算该列的聚合函数后追加Merge。请将该列声明为 可为空 (nullable) ,以避免出现 状态列 andNOT 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 修改外部表的定义:DROP FOREIGN TABLE
使用 DROP FOREIGN TABLE 删除外部表:CASCADE 子句也会将它们一并删除:
DML SQL 参考
下面的 SQL DML 表达式可能会用到 pg_clickhouse。示例基于 以下 ClickHouse 表:EXPLAIN
EXPLAIN 命令可按预期运行,但VERBOSE 选项会触发输出
ClickHouse 的 “Remote SQL” 查询:
SELECT
使用 SELECT 语句在 pg_clickhouse 表上执行查询,与在任何其他表上执行查询无异:nodes 表的一个本地副本,并与它连接,而不是连接远程表:
node_id 而不是本地列分组,将更多聚合下推到 ClickHouse,然后再
与查找表进行 join:
node_id 下推聚合,将需要从 Postgres 拉回的行数从 1000 行 (全部行) 减少到仅 8 行,即每个节点 1 行。
分区表
PostgreSQL [分区表]可以同时包含本地分区和由 ClickHouse 支持的外部分区。常见的布局是将较早的数据迁移到 ClickHouse,而较新的数据保留在 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 命令创建:{param:type} 风格的[查询参数]:
INSERT
使用 INSERT 命令向远程 ClickHouse 表中插入值:COPY
使用 COPY 命令将一个批次的数据行插入远程 ClickHouse 表:⚠️ 批次 API 的限制 pg_clickhouse 尚未支持 PostgreSQL FDW 的批次 insert API。因此,COPY 目前通过 INSERT 语句 插入记录。后续版本将对此进行改进。
LOAD
使用 LOAD 加载 pg_clickhouse 共享库:SET
使用 SET 来设置 pg_clickhouse 的自定义配置参数。pg_clickhouse.session_settings
pg_clickhouse.session_settings 参数用于配置在后续查询中要设置的 [ClickHouse
设置]。示例:
join_use_nulls 以及 IN 家族的 transform_null_in
(请参阅 IN 和 NULL 语义) 。
date_time_output_format:http 驱动要求其为 “iso”format_tsv_null_representation:http 驱动要求使用默认值output_format_tsv_crlf_end_of_line:http 驱动要求使用默认值
pg_clickhouse.session_settings;可以使用共享库预加载,或者
直接使用该扩展中的某个对象以确保其被加载。
pg_clickhouse.pushdown_regex
pg_clickhouse.pushdown_regex 参数用于控制 pg_clickhouse
是否对正则表达式函数和操作符执行下推。默认情况下会执行下推;
将此参数设置为 false 可阻止下推:
ALTER ROLE
使用 ALTER ROLE 的SET 命令,可为特定角色预加载 pg_clickhouse,
和/或 SET 其参数:
RESET 命令重置 pg_clickhouse 的预加载
和/或相关参数:
预加载
如果每个或几乎每个 Postgres 连接都需要使用 pg_clickhouse, 请考虑使用共享库预加载来自动加载它:session_preload_libraries
在每次建立与 PostgreSQL 的新连接时加载共享库:
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 查询将输出:
FixedString(N) 列会用空字节填充较短的值。文本列会丢弃这些尾随填充,而 BYTEA 列则会保留所有字节。
复合类型
Array
ClickHouse [Array] 直接映射为 PostgreSQL 数组,二者语义等价。主要区别在于,Postgres 多维数组要求其中各数组表达式的维度一致。尝试读取维度不一致的 ClickHouse 数组 (例如[[1], [2,3]]) 时,
会触发错误。
数组索引访问会在适当情况下下推,多维
索引访问也同样适用。示例:
Map
PostgreSQL 中没有与 ClickHouse [Map] 类型相对应的类型。 因此,pg_clickhouse 会将 [Map] 列映射为text[][],其中每个键值对
各自作为一个独立的 array。IMPORT FOREIGN SCHEMA 也采用
这一映射。同样,也可以将 Map 以 array 的形式 INSERT。示例如下:
插入
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 这类带点号的名称。例如,
对于以下外部表:
WHERE 子句中使用常规的数组功能 (包括数组下标语法) 对值进行过滤:
flatten_nested=0 创建的 [Nested] 列会映射为一个 array,每个嵌套行对应其中一个元素。每个 array 元素包含该行的各个值:
c2 Nested(id Int64, name String) 为例,其形式如下:
text[][] 列无法下推:
手动类型映射
IMPORT FOREIGN SCHEMA 使用的是通用的 PostgreSQL 类型。例如,
假设某个 ClickHouse 表包含 [Enum]、[Tuple]、Map 以及未展平的
[Nested] (flatten_nested = 0) 列,如下所示:
key 和 value) 。Nested 和 Map 会映射为复合类型数组,而 Tuple
则映射为单个复合值:
SELECT
列表中:
WHERE 子句中的 [Nested] 类型,只要字段名
完全一致即可:
INSERT。
函数与 operator 参考
函数
这些函数提供了查询 ClickHouse 数据库的接口。clickhouse_server_version
major.minor.patch。如有必要,将使用该服务器的选项和当前用户的 user mapping 进行连接:
SELECT version() 查询获取版本信息,并在连接的整个生命周期内缓存该信息。
clickhouse_query
driver、
凭证、数据库和连接缓存。
第一个参数是通过 CREATE SERVER 创建的服务器名称。必须提供
列定义列表 (AS name(col type, ...)) :PostgreSQL 在拉取行之前需要知道
结果形态,且该列表必须与查询返回的列相匹配。值会以与外部表列相同的方式
从 ClickHouse 转换为声明的类型。不返回结果的语句 (如 DDL)
无需声明任何内容;请改用
clickhouse_perform 执行它们。
默认情况下,任何角色都没有 EXECUTE 权限;请向角色 GRANT 权限,以允许其使用
该函数。
clickhouse_perform
clickhouse_query 没有可声明的结果形态。它以与 clickhouse_query 相同的方式
解析 服务器,并复用其
driver、凭证、数据库 和 连接缓存。
作为存储过程,必须通过 CALL 而非 SELECT 调用,并且不返回
行。默认情况下,没有任何角色拥有 EXECUTE 权限;向角色授予 GRANT 权限即可允许其
使用该存储过程。
下推函数
pg_clickhouse 会将条件子句 (HAVING 和 WHERE 子句) 中使用的部分 PostgreSQL 内置函数下推到 ClickHouse 中执行。该子集与 ClickHouse 中的对应函数如下:
abs: absfactorial: factorialmod(int2/int4/int8/numeric): 取模pow&power(float8,不含 numeric): powround: roundsin,cos,tan,atan,atan2,sinh,cosh,tanh,asinh,degrees,radians,pi:ClickHouse 数学函数 中的同名函数。asin、acos、atanh、acosh不会下推:对于超出范围的输入,PG 会报错,而 CH 会返回NaN。date_part:date_part('day'): toDayOfMonthdate_part('doy'): toDayOfYeardate_part('dow'): toDayOfWeekdate_part('year'): toYeardate_part('month'): toMonthdate_part('hour'): toHourdate_part('minute'): toMinutedate_part('second'): toSeconddate_part('quarter'): toQuarterdate_part('isoyear'): toISOYeardate_part('week'): toISOYeardate_part('epoch'): toISOYear
date_trunc:date_trunc('week'): toMondaydate_trunc('second'): toStartOfSeconddate_trunc('minute'): toStartOfMinutedate_trunc('hour'): toStartOfHourdate_trunc('day'): toStartOfDaydate_trunc('month'): toStartOfMonthdate_trunc('quarter'): toStartOfQuarterdate_trunc('year'): toStartOfYear
extract(field FROM source):映射与date_part相同date(timestamp)&date(timestamptz): toDate (反解析后为 CH 别名date)array_position:indexOf,配合 nullIf 将0转换为NULL;如果存在用于指定搜索起始索引的第三个参数,则使用 arraySlice。 请注意,nan当前不会匹配array_cat: arrayConcatarray_append: arrayPushBackarray_prepend: arrayPushFrontarray_remove: arrayRemovecardinality:在 ClickHouse 26.9+ 中为 arrayFlattenedLength, 否则为 length, 后者仅统计最外层数组array_length(array, 1):nullIf(length(array), 0)array_length&cardinality:lengtharray_to_string: arrayStringConcatstring_to_array: splitByStringsplit_part: splitByString + 数组下标trim_array: arrayResizearray_fill: arrayWithConstantarray_reverse: arrayReversearray_shuffle:arrayShufflearray_sample: arrayRandomSamplearray_sort: arraySort / arrayReverseSortbtrim: trimBothltrim: trimLeftrtrim: trimRightconcat_ws: concatWithSeparatorlower(text): lowerUTF8upper(text): upperUTF8substring(text, ...)&substr(text, ...): substringUTF8substring(bytea, ...)&substr(bytea, ...): 子串length(text): lengthUTF8length(bytea)&octet_length:lengthreverse(text): reverseUTF8reverse(bytea): reversestrpos:positionUTF8regexp_like: matchregexp_match: extractGroups 如果正则表达式包含带括号的子表达式;否则为 extractAll 再使用 arraySlice 切片。regexp_replace:replaceRegexpOne 或 replaceRegexpOne,存在g标志时regexp_split_to_array: splitByRegexpmd5: MD5sha224、sha256、sha384和sha512:对应的 ClickHouse SHA 函数- 当
fmt为字符串常量 (不区分大小写) 时的encode(bytea, fmt):encode(bytea, 'hex'):hex 再套用 lower, 因为 PostgreSQL 会输出小写 hex。encode(bytea, 'base64'):base64Encode 再套用 replaceRegexpAll, 以复现 PostgreSQL’s MIME (RFC 2045) 每 76 个字符换行的规则。encode(bytea, 'base64url')(PostgreSQL 19+):base64URLEncode, 与 PostgreSQL’s 不带 padding 的 RFC 4648 URL 字母表一致。
json_extract_path_text:子列语法json_extract_path: toJSONString + 子列语法jsonb_extract_path_text: 子列语法jsonb_extract_path:toJSONString + 子列语法bit_count(bytea): bitCountto_timestamp(float8): toDateTime64to_char(timestamp[tz], fmt): formatDateTime 当fmt为字符串常量,且其中每个关键字在 ClickHouse 中都有完全对应的等价写法时。支持的关键字请参见兼容性说明中的 to_char()。 否则,该函数会在 PostgreSQL 中本地执行。statement_timestamp,transaction_timestamp, &clock_timestamp: nowInBlock64 (nowInBlock64(9, $session_timezone))CURRENT_DATE: now 以及 toDate (toDate(now($session_timezone)))now、CURRENT_TIMESTAMP和LOCALTIMESTAMP: now64 (now64(9, $session_timezone))CURRENT_TIMESTAMP(n)&LOCALTIMESTAMP(n): 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 函数传入。
下推操作符
- 数组切片 (
arr[L:U]): arraySlice @>(数组包含) : hasAll<@(数组被包含) : hasAll&&(数组重叠) : hasAny~(正则表达式匹配) : match!~(正则表达式不匹配) : match~*(不区分大小写的正则表达式不匹配) : match!~*(不区分大小写的正则表达式不匹配) : match->>(将 JSON/JSONB 元素提取为文本) : sub-column syntax->(提取 JSON/JSONB) : toJSONString + sub-column syntax
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:@~→ matchre2match→ matchre2extract→ extractre2extractall→ extractAllre2regexpextract→ regexpExtractre2extractgroups→ extractGroupsre2replaceregexpone→ replaceRegexpOnere2replaceregexpall→ replaceRegexpAllre2countmatches→ countMatchesre2countmatchescaseinsensitive→ countMatchesCaseInsensitivere2multimatchany→ multiMatchAnyre2multimatchanyindex→ multiMatchAnyIndexre2multimatchallindices→ multiMatchAllIndices
intarray
有一个 [intarray] 函数可下推至 ClickHouse:idx→ indexOf
fuzzystrmatch
两个 [fuzzystrmatch] 函数可下推至 ClickHouse:soundex:soundexlevenshtein(2 参数) :editDistanceUTF8
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。- any_value
- array_agg
- avg
- bit_and
- bit_or
- bit_xor
- bool_and / every
- bool_or
- count
- corr
- covarpop
- covarsamp
- min
- max
- stddev_pop
- stddev_samp / stddev
- string_agg
- sum
- var_op
- var_samp /variance
自定义聚合函数
这些由 pg_clickhouse 创建的自定义聚合函数,可为部分在 PostgreSQL 中没有对应函数的 ClickHouse 聚合函数提供外部查询下推。如果这些函数中的任何一个无法下推, 就会引发异常。下推有序集合聚合函数
这些有序集合聚合函数会映射为 ClickHouse 的[参数化 聚合函数]:将其直接参数作为 parameter 传递,并将其ORDER BY expression 作为 argument。例如,以下 PostgreSQL 查询:
ORDER BY 后缀 DESC 和 NULLS FIRST,
使用它们会报错。
percentile_cont(double): quantilepercentile_cont(double[]): quantilespercentile_disc(double): quantileExactLowpercentile_disc(double[]): quantilesExactLow
自定义有序集合聚合函数
这些由 pg_clickhouse 创建的自定义有序集合聚合函数为部分 ClickHouse [参数化聚合 函数]提供外部查询下推。若其中任何函数无法下推,将引发异常。quantile(double): quantilequantileExact(double): quantileExact
自定义有序集合聚合函数
这些由 pg_clickhouse 创建的自定义有序集合聚合函数可为特定的 ClickHouse 参数化聚合函数提供外部查询下推。如果其中任何函数无法下推,则会引发异常。下推窗口函数
以下 PostgreSQL [窗口函数]可通过包含OVER (PARTITION BY ... ORDER BY ...) 子句的查询下推到 ClickHouse;适用时还包括窗口帧规范。
- row_number
- rank
- dense_rank
- ntile
- cume_dist
- percent_rank
- lead
- lag
- first_value
- last_value
- nth_value
min/max(带OVER子句)
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,以便以兼容的方式比较返回值。 例如:
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_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”