4. 扩展SQL接口与控制文件

一个 PostgreSQL 扩展能否被 CREATE EXTENSION 正确安装、被 ALTER EXTENSION ... UPDATE 平滑升级,取决于三份核心文件的配合:.control 控制文件、安装 SQL 脚本、升级 SQL 脚本。控制文件是扩展的“身份证与元数据”,SQL 脚本是扩展的“安装说明书”,升级脚本则是“版本迁移路线图”。本节围绕这三者的编写规范展开,并给出一个可实际打包的完整示例。

一、.control 文件:扩展的元数据入口

控制文件必须与扩展同名,放在 SHAREDIR/extension/ 下,例如 pg_stat_kcache.control。它由若干 key = 'value' 形式的参数组成,PostgreSQL 在解析时对未知参数会直接报错,因此不能随意添加字段。常用参数如下:

参数是否必需作用
comment必需扩展的一句话描述,出现在 \dx 输出中
default_version必需未指定 VERSION 时安装的版本号
module_pathname可选替换 SQL 脚本中的 MODULE_PATHNAME 占位符
relocatable可选是否允许 ALTER EXTENSION ... SET SCHEMA,默认 false
schema可选强制安装到指定 schema,与 relocatable 互斥
requires可选依赖的其他扩展名,逗号分隔
superuser可选是否仅超级用户可安装,默认 true
trusted可选是否可由有 CREATE 权限的普通用户安装

一个典型控制文件示例:

# pg_stat_kcache.control
comment = 'Kernel statistics gathering for PostgreSQL'
default_version = '2.2.0'
module_pathname = '$libdir/pg_stat_kcache'
relocatable = false
schema = 'public'
requires = 'pg_stat_statements'
superuser = true
trusted = false

注意 module_pathname 通常写成 $libdir/扩展名,PostgreSQL 会将其展开为实际的库目录。SQL 脚本中所有引用共享库函数的地方都应使用 MODULE_PATHNAME 占位符,而不是硬编码路径,这样打包后在不同平台上都能正确加载。

二、安装 SQL 脚本:扩展名--版本.sql

安装脚本命名规则为 扩展名--版本.sql,例如 pg_stat_kcache--2.2.0.sql。当用户执行 CREATE EXTENSION pg_stat_kcache; 时,PostgreSQL 会查找 default_version 对应的安装脚本并整体执行。脚本中通常包含:

  • 创建 schema 对象(表、视图、函数、类型、操作符);
  • 用 CREATE FUNCTION ... AS 'MODULE_PATHNAME', 'c_symbol' LANGUAGE C 绑定 C 函数;
  • 用 GRANT 控制权限;
  • 用 COMMENT ON 补充对象说明。

安装脚本必须满足一个关键约束:脚本执行完毕后,扩展对象集合必须与 pg_extension 中记录的版本一致。因此脚本中不要出现 CREATE EXTENSION、DROP EXTENSION 或事务控制语句,PostgreSQL 会在一个事务中执行整个脚本,失败则整体回滚。

三、升级脚本:扩展名--旧版本--新版本.sql

升级脚本命名规则为 扩展名--源版本--目标版本.sql,例如 pg_stat_kcache--2.1.0--2.2.0.sql。当用户执行 ALTER EXTENSION pg_stat_kcache UPDATE TO '2.2.0'; 时,PostgreSQL 会从当前版本出发,寻找一条由若干升级脚本组成的路径,依次执行。升级脚本中只写“从旧版本到新版本需要做的增量变更”,例如:

-- pg_stat_kcache--2.1.0--2.2.0.sql
ALTER FUNCTION pg_stat_kcache() SET search_path = public;
CREATE FUNCTION pg_stat_kcache_reset() RETURNS void
  AS 'MODULE_PATHNAME', 'pg_stat_kcache_reset'
  LANGUAGE C STRICT;
COMMENT ON FUNCTION pg_stat_kcache_reset() IS 'Reset kernel statistics counters';

升级脚本同样不能包含事务控制语句,且必须保证执行后对象状态与目标版本一致。如果跨越多个版本,可以只提供相邻版本脚本,让 PostgreSQL 自动串联;也可以提供 2.0.0--2.2.0.sql 这样的跳跃脚本,但要注意路径唯一性,避免出现多条可达路径导致歧义。

四、版本迁移路径与打包结构

下图展示了一个扩展从 2.0.0 到 2.2.0 的版本迁移关系,以及控制文件、安装脚本、升级脚本在打包目录中的位置关系。图中同时标出了 CREATE EXTENSION 与 ALTER EXTENSION ... UPDATE 两条入口路径。

扩展打包结构与版本迁移路径 打包目录结构 extension/ pg_stat_kcache.control pg_stat_kcache--2.2.0.sql pg_stat_kcache--2.0.0--2.1.0.sql pg_stat_kcache--2.1.0--2.2.0.sql pg_stat_kcache.so 控制文件 + 安装脚本 + 升级脚本 + 共享库 共同构成一个可发布的扩展包 版本迁移路径 2.0.0 初始版本 2.1.0 中间版本 2.2.0 default_version 2.0.0→2.1.0 2.1.0→2.2.0 2.0.0→2.2.0(可选跳跃脚本) CREATE EXTENSION → 执行安装脚本 ALTER EXTENSION UPDATE → 依次执行升级脚本

五、打包与验证要点

完成三份文件后,打包通常遵循 PostgreSQL 的 PGXS 构建体系:Makefile 中通过 EXTENSION = pg_stat_kcache、DATA = pg_stat_kcache--2.2.0.sql pg_stat_kcache--2.0.0--2.1.0.sql pg_stat_kcache--2.1.0--2.2.0.sql 声明文件,MODULE_big 或 MODULES 声明共享库,PG_CONFIG 指向目标 PostgreSQL 安装。执行 make install 后,文件会被复制到 SHAREDIR/extension/ 与 PKGLIBDIR。

验证时建议按以下顺序检查:

  1. 用 pg_config --sharedir 确认扩展目录,检查三份文件是否齐全;
  2. 在测试库执行 CREATE EXTENSION pg_stat_kcache VERSION '2.0.0';,确认安装脚本可执行;
  3. 执行 ALTER EXTENSION pg_stat_kcache UPDATE TO '2.2.0';,观察是否按 2.0.0→2.1.0→2.2.0 顺序执行;
  4. 查询 SELECT extname, extversion FROM pg_extension WHERE extname='pg_stat_kcache';,确认版本已变为 2.2.0;
  5. 用 \dx+ pg_stat_kcache 查看对象清单,确认新增函数、视图均已注册。
升级脚本一旦发布就不应再修改,否则已升级的实例与全新安装的实例会出现对象差异。若发现升级脚本有误,正确做法是发布一个新的目标版本,并提供从旧目标版本到新目标版本的升级脚本,而不是回改历史脚本。
控制文件中的 default_version 必须与安装脚本文件名中的版本号严格一致,且该版本必须存在对应的 扩展名--版本.sql。若 default_version 指向一个不存在的脚本,CREATE EXTENSION 会直接报 “could not open extension control file” 或 “extension ... has no installation script” 错误。

把控制文件、安装脚本、升级脚本三者视为一个整体来设计,是扩展可维护性的基础。控制文件决定“能不能装、装到哪、依赖谁”,安装脚本决定“装完是什么样”,升级脚本决定“旧实例如何安全走到新版本”。三者版本号对齐、路径唯一、脚本幂等,扩展才能在不同 PostgreSQL 实例之间稳定分发与演进。