Skip to main content

概要

说明

chdb_hook 模块会挂接 PostgreSQL 的 COPY 命令, 使其能够借助 chDB,以 chDB 支持的任意数据格式将数据复制到 (TO) 或从 (FROM) 本地文件、AWS S3 桶、Google Cloud Storage 等位置读取。 该模块同样挂接 CREATE TABLE,使表能够从上述任意目标推导其列并加载其行。

加载

以 super user 身份,通过以下任一方式加载 chdb_hook。请选择最适合你的用例的方式:
  • 通过 LOAD 命令显式加载;作用范围为当前 session:
    ClickHouse Cloud 的 SQL 控制台尚不支持 LOAD 'chdb_hook' 命令,但可以通过 psql 或任何其他 database connection 运行该命令。 你也可以联系支持代表将其添加到你的 Postgres service 配置中,添加后即可在 SQL 控制台中使用。
  • 对所有 sessions 生效:在 postgresql.conf 中设置 [session_preload_libraries]:
    或通过 ALTER SYSTEM:
    该设置也可以通过 ALTER DATABASE 针对单个 database 设置:
    或通过 ALTER ROLE 针对特定用户和组设置:
  • 在 server 启动时通过 [shared_preload_libraries] 设置加载,使其始终对所有 sessions 和 databases 可用:
请注意,加载 chdb_hook 后,属于 pg_read_server_files 或 pg_write_server_files roles 的用户可以使用 COPY 读写 Postgres server 上的文件以及云存储中的数据。

COPY 重载

在加载时,chdb_hook 会挂接 Postgres 的 COPY 命令,从而以 chDB 支持的任意数据格式,将数据复制到 (TO) 或从 (FROM) 本地文件、AWS S3 桶、Google Cloud Storage 等位置读写。例如,若要从 S3 中的 CSV 文件加载数据到表中,请先创建表,然后使用 s3:// URL 调用 COPY:

特权

chdb_hook 的 COPY 所需的特权与它所替换的 COPY 相同:COPY TO 需要对该 relation 或每个被复制的列具有 SELECT 特权,COPY FROM 则需要 INSERT 特权。file:// URL 会读取或写入 server 上的文件,因此还需要属于 pg_read_server_files 或 pg_write_server_files 角色。COPY FROM 需要 read-write 事务。

URL 协议

chdb_hook 仅在 URL 形式的 COPY 目标使用以下协议之一时才会执行:

URL 格式

URL 的格式因目标 (target) 而异。

File 表引擎

必须是 Postgres 服务器上的绝对路径,使用相对路径会报错。Postgres 用户必须根据实际需要属于 pg_read_server_files 或 pg_write_server_files 角色,Postgres 系统用户也必须相应地具备该文件的读或写权限。对于 COPY TO,如果该 路径不存在,chdb_hook 会创建所有缺失的父目录,因此必须具备相应的文件系统权限。示例:

HTTP

任何常规 HTTP URL,包括位于公有云存储中的 URL。对于 COPY TO, chdb_hook 会尝试通过 POST 将数据发送到该 URL。示例:

S3

S3 URL 可以采用 S3 URI 的形式
或者采用对象 URL 的形式:

GCS

GCS 的 URL 采用公网 URL 的形式:
或者使用 Cloud Storage URI,chdb_hook 会将其转换为公网 URL:

Azure Blob 存储

使用以账户名作为子域名的 blob.windows.net URL:
或者使用其他主机名:

Azure ABFS

ABFS URL 必须使用以下格式:

HDFS URL

HDFS URL 可以采用典型的 HTTP 风格 URL,并可选地附带端口号:

路径通配符

在 COPY FROM 命令中,URL 路径可以包含通配符。文件必须匹配整个路径模式,而不仅仅是后缀或前缀。唯一的例外是:当路径指向一个已存在的目录且未使用通配符时,会隐式在路径末尾添加 *,以选中该目录下的所有文件。 支持的通配符:
  • *:匹配任意多个除 / 以外的字符,包括空字符串。
  • ?:匹配任意单个字符。
  • {groucho,harpo,chico}:替换为字符串 “groucho”、“harpo”、“chico” 中的任意一个。这些字符串可以包含 /。
  • {N..M}:匹配任意 >= N 且 <= M 的数字。
  • **:递归匹配目录下的所有文件。
例如,若要用一条命令加载以下文件中的数据: 可以使用 {some,another}_prefix 匹配这两个目录名,并使用 some_file_{1..3}.csv' 匹配这些文件,如下所示:

选项

chdb_hook 的 COPY 命令支持以下选项:

format:

读取或写入所使用的格式。必须是 chDB 支持的 formats 之一,包括 TSV、CSV、Parquet、Iceberg、JSON 等。 若省略该参数或将其设为 auto,chDB 会根据 URL 末尾的文件扩展名自动判断格式。

structure

行的 chDB 数据结构。由列名、[ClickHouse 数据类型] 和修饰符组成的列表。若省略该选项,chdb_hook 会将 Postgres 数据类型映射为大体合适的 ClickHouse 类型,详见 Postgres 到 chDB。若设置为 auto,chDB 会尝试推断类型。 示例:

access_key 和 access_secret

AWS 账户用户用于对请求进行身份验证的长期凭据。

session_token

与 access_key 和 access_secret 搭配使用的 AWS 会话令牌,通常通过环境变量 AWS_SESSION_TOKEN 定义。仅适用于 S3 URL。

compression

文件压缩格式。当无法从文件名推断出压缩格式时使用。支持的值:
  • auto (默认)
  • none
  • gzip 或 gz
  • brotli 或 br
  • xz 或 LZMA
  • zstd 或 zst
  • lz4
  • bz2
  • snappy

timeout

请求超时时间 (毫秒) 。适用于 HTTP、S3、GCS 和 Azure URL。 默认值为 30000 (30 秒) 。

encoding_check

指定在转换 chDB String 和 JSON 值时,如何处理数据库编码下的无效字符。可选值如下:
  • fail (默认) :引发错误
  • remove:移除无效字节
  • replace:在 UTF-8 编码下,将无效字节替换为 Unicode 替换字符 (�) ;对于其他编码, 其行为与 remove 相同
  • truncate:在第一个无效字节处截断文本

调试

出错时,chdb_hook 的 COPY 命令会将其尝试执行的 chDB 查询包含在错误上下文中:
chdb_hook 对查询参数使用 {name:Type} 形式的占位符,以防范 SQL 注入漏洞,并尽量降低将凭据等敏感数据写入日志的风险。 不过,如果你为排查问题需要查看这些参数的内容,可临时将 Postgres 的 [log_min_messages] GUC 设置为 DEBUG1 或更高级别,让 chdb_hook 将查询和参数输出到 Postgres 日志 (绝不会发送给客户端) ,其显示形式如下:
请勿在单次调试会话之外持续将 [log_min_messages] 设为调试级别, 以免记录凭据等敏感信息;此外,PostgreSQL 自身也会输出 大量调试信息,很快就会把日志占满。

CREATE TABLE 重载

chdb_hook 同样对 CREATE TABLE 进行了挂接,使表能够从 URL 推导出自身的列,并加载其中的行。 若要创建结构由 URL 推导而来的表,请在 structure_from 选项中传入该 URL,并将列清单留空 (注意:该公开数据文件包含 4100 万行) :
使用 copy_from 同时加载行和列:
仅当语句本身未指定任何列时,copy_from 才会推断列。 列清单、INHERITS 子句、OF 类型或分区都会定义列, 此时 copy_from 仅会复制:
当 copy_from 推断列时,会使用 DESCRIBE 返回的源 schema。 因此,Map、Tuple 和 Nested 值会以文本 array 列的形式加载, 而这些列无法体现其原始的 ClickHouse 类型。 两种方式都支持与 COPY 相同的 URL 协议和选项:凭证、format、压缩、timeout,乃至显式指定的结构均同样适用。Postgres 会保留其余的存储参数:
structure_from 和 copy_from 均无法与 IF NOT EXISTS 搭配使用。请使用 COPY 来加载已有的 relation。

限制

由于一些已知问题以及 Postgres 与 chDB 之间数据类型行为的差异,chdb_hook 存在以下限制:
  • 如果关系上存在适用于当前复制角色的 row-level security 策略,则无法对其执行 COPY。Postgres 会通过将 COPY TO 重写为查询来实施此类策略,而 chdb_hook 不支持这种方式。
  • 无法对带有 WHERE 子句的 URL 执行 COPY FROM。
  • chDB 没有 NULL array,因此 COPY TO 会将 NULL 存储为空 array ([]) 。
  • chDB 将与 lseg、path 或 polygon 等价的类型表示为 array;因此这些类型的 NULL 值经 COPY TO 后同样会输出为空 array ([]) 。
  • ClickHouse 没有 NULL array,因此 COPY TO 会将 NULL 存储为空 array ([]) 。
  • ClickHouse 将与 lseg、path 或 polygon 等价的类型表示为 array;因此这些类型的 NULL 值经 COPY TO 后同样会输出为空 array ([]) 。
  • 若指定的结构未将该列定义为 Nullable,则 NULL 值在输出时会变成其默认值。请始终在结构中显式定义可为空的列,以避免这种转换。
  • 若开放 path 的最后一个点与第一个点相同,则会输出为闭合路径。
  • Protobuf 的 repeated 字段中不存在 null,因此 array 中的 NULL 值会被省略。
  • chDB 的 JSON type 仅支持 JSON 对象;仅当所有值都是 JSON 对象时,才可将 json 和 jsonb 的默认 String 映射覆盖为 JSON。 (ClickHouse/ClickHouse#68428)
  • chDB 的 JSON type 会忽略 null;值为 NULL 的对象键在输出时会被省略。仅当对象值不为 null,或可以接受这些值丢失时,才将 json 和 jsonb 的默认 String 映射覆盖为 JSON。 (ClickHouse/ClickHouse#68428)
  • JSON、JSONCompact 和 JSONColumnsWithMetadata 格式始终会校验 UTF-8,因此输出的 bytea 值中会带有替换字符。
  • COPY FROM 会将包含空字符串或零的 Protobuf Nullable 字段读取为 NULL。 (chdb-io/chdb-core#152)
  • COPY TO 为 Parquet 时,会丢弃 Nullable Tuple 自身 null map 中的 NULL。 (ClickHouse/ClickHouse#112427)
  • Parquet、Arrow、ArrowStream、ORC、Avro、Protobuf、ProtobufList、MsgPack 和 BSONEachRow 格式没有与 Postgres time 或 chDB Time64 对应的类型。请在显式结构中将 time 列配置为 String,以保留其值。
  • Protobuf 输出会将 timestamp 值截断到秒。
  • Protobuf 输出不支持早于 1970-01-01 的日期。请在显式结构中将 time 列配置为 String,以保留其值。 (ClickHouse/ClickHouse#111860)

数据类型

COPY 会将 relation 的 Postgres 类型映射为 chDB 类型, 而 CREATE TABLE 则将 URL 的 chDB 类型映射为 Postgres 类型。

Postgres 到 chDB

在未显式指定 结构 选项时,chdb_hook 会将 Postgres 类型映射为合适的 chDB 等价类型。如果这些映射不符合您的使用场景,可指定 结构,用所需类型覆盖自动生成的类型。 此表中未列出的类型,例如 name、varchar、char、bit、timetz、money 以及枚举类型,均映射为 String。 array 类型映射为以映射后元素类型为元素的 Array。ClickHouse 按列约束可空性,而 Postgres 按 array 约束可空性,因此元素始终为 Nullable。 没有任何 Postgres 类型直接映射到 Map、Tuple 或 Nested,但可以通过 结构 或源文件指定这些类型。Tuple 可以读取到 text[] 中 (每个元组字段对应一个元素) ,也可以读取到匹配的 composite type 中以保留字段类型。Map 或 Nested 可以读取到 text[][] 中 (每个键值对或嵌套元组对应一个内层 array) ,也可以读取到匹配复合类型的 array 中。示例请参见手动类型映射。

时间戳转换

在纯文本格式 (TSV、CSV 等) 中,COPY hook 会以 ISO-8601 格式 YYYY-MM-DDThh:mm:ssZ 输出 DateTime 和 DateTime64 值,且不受当前 datestyle 设置的影响。这样可以保证 timestamptz 值始终保持一致,即使导入这些值的 source 使用了不同的时区。在 structure 输出中改用其他类型,例如 Datetime64(3, 'America/Los_Angeles'),并不会改变输出的偏移量,但会改变 precision。 Timestamp TZ 示例: COPY hook 还会将 timestamp 值从会话时区转换为 UTC,从而确保输出值以该时区为基准。 当这些值被加载到新系统中时,新系统应将其转换为自身的本地时区。因此,时区不同则数值不同, 但按时区差换算后是等价的。 timezone 设置对时间戳 2026-08-28T12:00:00 的影响示例:

chDB 到 Postgres

对于已有的表,值会被转换为所声明的列类型。 其他标量类型会尽可能使用 PostgreSQL 的显式类型转换, array 元素则会被转换为所声明的元素类型。不支持的 转换以及超出范围的值会引发错误。 将 chDB 的 Interval 类型映射到 smallint、integer 或 bigint,即可按其单位数量进行读取和 写入。例如,值为 3 的 IntervalDay 会映射 为整数 3。将 IntervalNanosecond 映射到 bigint 可保留 纳秒精度,而映射到 interval 则会截断至微秒。 chdb_hook 根据 DESCRIBE 返回的 ClickHouse 类型创建表时,会使用下表中的默认 Postgres 类型。额外的读取目标类型仅适用于 显式声明的列,并包含 PostgreSQL 显式类型转换之外的转换。空白单元格表示仍可使用上述类型转换。这些目标类型仅针对读取; 写入遵循另外的转换规则。 输入兼容的类型可以通过其 PostgreSQL input 函数读取字符串。 复合类型的字段必须一一匹配且顺序一致。若要将元组读取为 array,每个字段都必须能够转换为该 array 的元素类型,且任何字段本身都不能是 array。 以 array 形式读取时,Map 的每个键值对对应一行,Nested 的每个嵌套行对应一行。若要将元组读取为 box,需提供两个点;读取为 circle 时,需提供一个点和半径;读取为 line 时,需提供三个系数。 本表中未列出的所有 chDB 类型都会引发错误,其中包括 Variant、Dynamic 和 AggregateFunction。可使用将它们映射为 String 的结构,从而以文本形式读取。 其中有几种类型,Postgres 支持的取值范围比 chDB 更窄;因此,当 Time 或 Time64 超过 24 小时,或 Date32 超出 Postgres 的日期范围时,复制操作会引发错误。

手动类型映射

列推断会将枚举映射为 text,并将 Tuple、Map 和 Nested 值映射为文本 array。如需保留字段类型或限制枚举的取值,请先创建 PostgreSQL 枚举类型和复合类型,再使用这些类型声明列。 可通过 结构 指定 chDB 类型,也可使用 structure 'auto' 基于源数据 (而非 Postgres 表) 推断类型。例如, 可使用这些类型加载事件状态、坐标、标签以及 嵌套条目:
按照 chDB 中的字段顺序声明复合类型字段,并使用兼容的 PostgreSQL 类型。对于 Map 值,先声明一个键字段,再声明一个值字段。每个 Tuple 对应一个复合值;每个 Map 键值对或 Nested 行对应 复合 array 中的一个元素。 如果在 structure 中显式指定 Nested,chDB 会将其拆分为多个 Array 列,每个字段对应一列。可以像上文示例那样使用 Array(Tuple(...)),将嵌套行读取到单个复合 array 列中;如果 chDB 源格式本身包含类型信息,也可以 使用 structure 'auto'。

文本编码

chDB 以字节形式读取 String、FixedString、Enum 和 JSON,不保证其编码。将此类列复制到 text 或任何其他非二进制类型时,会按照数据库编码校验字节,并对无法表示的数据引发错误:
所有编码都会拒绝 NUL,因为 Postgres 无法将其存储在 text 中。 复制到 bytea 可以保留 chDB 写入时的原始字节。请显式为这些列命名,因为 CREATE TABLE 会为这些类型推导出 text:
FixedString(N) 会使用 NUL 字节填充长度不足的值。复制到 text 时会去掉尾随的 NUL,而 bytea 会保留全部 N 个字节。

设置

chdb_hook.max_memory

定义 chDB 查询可使用的最大内存量,用于设置 chDB 的 max_memory_usage。需要 超级用户特权。可使用整数 表示兆字节数,或使用以下内存单位之一:
  • B (字节)
  • kB (千字节)
  • MB (兆字节)
  • GB (吉字节)
  • TB (太字节)
默认为 0,即不限制内存。

chdb_hook.max_threads

chDB 查询的最大查询处理线程数,用于设置 chDB 的 max_threads 参数。需要 超级用户特权。默认值为 0,即由 chDB 自行决定该值。 强烈建议在执行大规模 COPY 之前设置 chdb_hook.max_threads,以免 chDB 占满 CPU 资源,从而影响 PostgreSQL。

chdb_hook.max_parsing_threads

chDB 在解析支持并行解析的输入格式数据时可使用的最大线程数,用于设置 chDB 的 max_parsing_threads 设置。需要超级用户特权。默认为 0,即由 chDB 自行决定该值。 建议在 COPY 大量数据之前先设置 chdb_hook.max_parsing_threads,以避免 chDB 占满 CPU 而影响 PostgreSQL。

版本策略

chdb_hook 的公开发行版遵循 Semantic Versioning。
  • 主版本号在 API 发生变更时递增
  • 次版本号在发生向后兼容的 SQL 变更时递增
  • 补丁版本号在仅涉及二进制文件的变更时递增
安装完成后,可通过 Postgres 18 的 pg_get_loaded_modules() 函数在 PostgreSQL 中查看版本。

作者

Copyright (c) 2026, ClickHouse