2. 扩展安装与依赖管理

PostgreSQL 的扩展机制把“数据库内核”与“可选能力”解耦:内核提供 SQL 解析、事务、存储、并发控制等基础能力,扩展则以一组 SQL 对象(函数、类型、操作符、索引访问方法等)加上可选的共享库文件,挂载到某个具体数据库中。理解扩展的安装与依赖管理,核心是理解三件事:扩展的元数据从哪来、对象装到哪个 schema、以及扩展之间如何互相依赖。本节围绕 CREATE EXTENSION、版本升级、依赖解析、schema 指定与权限控制,完成一个多扩展依赖环境的搭建。

一、扩展的元数据来源与控制文件

每个可安装扩展在文件系统上至少有一个控制文件,命名形如 扩展名.control,位于 SHAREDIR/extension(通常为 /usr/share/postgresql/<版本>/extension 或 $PGHOME/share/extension)。控制文件决定“这个扩展能不能装、装到哪、默认版本是多少”。一个典型的控制文件关键字段如下:

# myext.control
comment = '示例扩展:演示依赖与 schema 控制'
default_version = '1.2'
module_pathname = '$libdir/myext'
relocatable = false
schema = myext_schema
requires = 'plpgsql, hstore'
superuser = false
trusted = true

其中 default_version 指定不写版本时安装哪个版本;relocatable 决定安装后能否用 ALTER EXTENSION ... SET SCHEMA 迁移;schema 是固定 schema 约束;requires 声明前置依赖;superuser 与 trusted 共同决定普通用户能否安装。安装脚本则命名为 扩展名--版本.sql,升级脚本命名为 扩展名--旧版本--新版本.sql,PostgreSQL 正是靠这些脚本完成版本迁移。

二、CREATE EXTENSION 的安装与 schema 指定

安装扩展的基本语句是 CREATE EXTENSION 扩展名;。默认情况下,扩展对象会安装到当前 search_path 中第一个可写的 schema,通常是 public。更可控的做法是显式指定 schema:

CREATE SCHEMA IF NOT EXISTS ext_demo;
CREATE EXTENSION hstore WITH SCHEMA ext_demo;
CREATE EXTENSION pg_trgm WITH SCHEMA ext_demo VERSION '1.6';

这里有两个容易踩坑的点。第一,WITH SCHEMA 只对 relocatable = true 的扩展有效;若控制文件写死了 schema,指定其它 schema 会直接报错。第二,扩展对象一旦安装,其函数、操作符的可见性依赖 search_path。把扩展装进独立 schema 后,调用方要么在 search_path 中包含该 schema,要么用全限定名调用,例如 ext_demo.hstore(ARRAY['a','1'])。这也是生产环境推荐“扩展独立 schema”的原因:避免与业务对象重名,也便于权限收敛。

三、依赖解析:requires 与安装顺序

扩展依赖分两层。一层是控制文件里的 requires,声明扩展之间的依赖;另一层是扩展对象对其它 schema 中对象的引用,例如某个扩展函数内部调用了 hstore 类型。PostgreSQL 在 CREATE EXTENSION 时会读取 requires,若前置扩展未安装则报错,但不会自动帮你安装——依赖需要按顺序显式安装。因此多扩展环境的搭建顺序应当是:先装被依赖者,再装依赖者。

可以用系统视图核对依赖关系。pg_extension 记录已安装扩展及其 schema 与版本;pg_depend 记录对象级依赖;pg_available_extensions 列出可安装扩展及默认版本。下面这段查询可以快速看清当前库的扩展布局:

SELECT e.extname,
       n.nspname AS schema_name,
       e.extversion,
       ae.comment
FROM pg_extension e
JOIN pg_namespace n ON n.oid = e.extnamespace
LEFT JOIN pg_available_extensions ae ON ae.name = e.extname
ORDER BY e.extname;

若想查看某个扩展依赖了谁,可结合 pg_depend 与 pg_extension 做自连接,过滤 deptype = 'e'(extension 依赖)的行。实际排障时,ERROR: required extension "hstore" is not installed 这类报错,基本都能通过该查询定位缺失的前置扩展。

四、版本升级:ALTER EXTENSION UPDATE

扩展升级不是改控制文件就完事,而是执行升级脚本。命令为 ALTER EXTENSION 扩展名 UPDATE TO '目标版本';。若不写 TO,则升级到控制文件中的 default_version。升级路径必须存在:从 1.0 到 1.2,要么有 1.0--1.2.sql,要么有 1.0--1.1.sql 与 1.1--1.2.sql 形成链式路径,否则 PostgreSQL 会拒绝升级。

-- 查看可升级目标版本
SELECT * FROM pg_extension_update_paths('hstore');

-- 升级到指定版本
ALTER EXTENSION hstore UPDATE TO '1.7';

升级前应确认没有长事务或活跃连接持有扩展对象的锁,否则 ALTER EXTENSION 可能阻塞。升级脚本通常包含 CREATE OR REPLACE FUNCTION、ALTER TYPE ... ADD VALUE 等语句,属于 DDL,需在业务低峰执行。

五、权限控制:谁能装、谁能用

安装扩展涉及创建函数、类型甚至加载共享库,因此默认需要超级用户。PostgreSQL 13 起引入 trusted 扩展概念:控制文件标记 trusted = true 且 superuser = false 的扩展,可由拥有 CREATE 权限的普通用户在自有 schema 中安装。权限控制的关键点包括:

  • 安装权限:普通用户需对目标 schema 有 CREATE 权限,且扩展本身是 trusted。
  • 使用权限:扩展函数默认 EXECUTE 授予 PUBLIC,若需收紧,应 REVOKE 后按角色授予。
  • schema 隔离:把扩展装入独立 schema,并对业务角色只授予 USAGE,不授予 CREATE。
  • 升级权限:ALTER EXTENSION UPDATE 通常仍需扩展所有者或超级用户。

六、多扩展依赖环境搭建示例

下面给出一个可复现的搭建流程,目标是在独立 schema 中安装 hstore、pg_trgm,并让业务角色只读使用。示例参数与结果均为教学假设,实际执行请按环境调整。

-- 1. 创建扩展专用 schema 与业务角色
CREATE SCHEMA IF NOT EXISTS ext_demo;
CREATE ROLE app_user LOGIN PASSWORD 'ChangeMe_2024';

-- 2. 按依赖顺序安装扩展
CREATE EXTENSION IF NOT EXISTS hstore WITH SCHEMA ext_demo;
CREATE EXTENSION IF NOT EXISTS pg_trgm WITH SCHEMA ext_demo;

-- 3. 收紧权限:只允许使用,不允许创建
REVOKE CREATE ON SCHEMA ext_demo FROM PUBLIC;
GRANT USAGE ON SCHEMA ext_demo TO app_user;
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA ext_demo TO app_user;

-- 4. 验证安装结果
SELECT extname, extversion, nspname
FROM pg_extension e JOIN pg_namespace n ON n.oid = e.extnamespace
WHERE extname IN ('hstore','pg_trgm');

预期结果:两行记录,nspname 均为 ext_demo,版本分别为 1.8 与 1.6(示例值,随 PostgreSQL 版本不同而变化)。若 pg_trgm 安装时报缺少依赖,应回到第三节的依赖查询,先补齐前置扩展。若业务角色调用时报 permission denied for schema ext_demo,则检查 USAGE 是否授予、search_path 是否包含该 schema。

实践建议:把扩展安装脚本纳入版本管理,用 CREATE EXTENSION IF NOT EXISTS ... VERSION 'x.y' 固定版本,避免不同环境因默认版本差异导致行为不一致。升级时先在小库验证 pg_extension_update_paths 返回的路径,再上生产。
多扩展依赖环境:安装顺序、schema 归属与权限边界 控制文件 .control default_version = '1.2' requires = 'plpgsql, hstore' relocatable / schema / trusted 安装脚本:myext--1.2.sql 安装 / 升级脚本 myext--1.0.sql myext--1.0--1.1.sql myext--1.1--1.2.sql 链式路径决定可升级目标 目标数据库 pg_extension 记录已装扩展 pg_depend 记录对象依赖 pg_available_extensions 可装清单 extnamespace 决定 schema 归属 依赖解析顺序(requires 声明,需显式按序安装) plpgsql 内置过程语言 hstore 键值类型 pg_trgm 三元组相似度 myext 业务扩展 schema 归属:ext_demo CREATE EXTENSION hstore WITH SCHEMA ext_demo; CREATE EXTENSION pg_trgm WITH SCHEMA ext_demo; 对象全限定名:ext_demo.hstore(...) search_path 需包含 ext_demo 才可裸调用 relocatable=false 时不可 SET SCHEMA 迁移 权限边界 安装:trusted 扩展 + schema CREATE 权限 使用:GRANT USAGE ON SCHEMA ext_demo 调用:GRANT EXECUTE ON ALL FUNCTIONS 收紧:REVOKE CREATE ON SCHEMA FROM PUBLIC 升级:ALTER EXTENSION UPDATE 需所有者

把上述流程串起来看,扩展管理的本质是“元数据驱动 + 显式依赖 + 权限收敛”。控制文件决定可安装性与默认版本,安装脚本决定对象落地,requires 决定安装顺序,schema 决定命名空间与可见性,权限决定谁能装、谁能用、谁能升级。掌握这五条主线,多扩展依赖环境就能稳定复现,而不是靠试错堆出来。