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