35.15. 包装相关对象到一个扩展

PostgreSQL的一个有用扩展通常包括 多个SQL对象;例如,一个新的数据类型将需要新的函数,新的操作符,以及可能的新的索引操作类。 为了简化数据库管理有助于收集所有这些对象到一个单一的包。 PostgreSQL调用这样的包如extension。为了定义一个扩展, 你至少需要一个脚本文件包含SQL命令以创建扩展的对象,并且 控制文件指定扩展本身的几个基本性质。如果扩展包括C代码, 通常也是一个共享库文件的已经编译的C代码。 一旦你有了这些文件,一个简单的CREATE EXTENSION命令加载对象到你的数据库。

使用一个扩展的主要优点,不是运行 SQL脚本加载一组"loose"对象到 你的数据库,而是PostgreSQL将一起了解扩展的对象。 你可以删除使用单独DROP EXTENSION命令的 所有的对象(不需要维护一个单独的"卸载"脚本)。 更有用的,pg_dump知道 它不应该转储扩展的单个成员对象—; 它会只包括CREATE EXTENSION命令创建扩展。 这大大简化了迁移到一个新的扩展版本, 可能含有比旧版本更多的或不同的对象。 但是请注意,当装载转储到一个新的数据库的时候,你必须有扩展的控制,脚本,以及 其他可用的文件。

PostgreSQL不会让你删除 包含在扩展中的单独的对象,除了减少整个扩展。 同时,虽然你可以改变扩展成员对象的定义 (例如,通过CREATE OR REPLACE FUNCTION函数),记住修改后的定义将不会 通过pg_dump被转储。这种变化通常是唯一明智的,如果 你同时在扩展的脚本文件中有相同的变化。 (但对包含配置数据的表有特殊规定;见下文。)

扩展机制也为包装修改脚本制定规定,它调整包含扩展的SQL对象的定义。 例如,如果扩展的版本1.1增加了一个功能并且 改变相比较于1.0的另一个函数的主体,扩展可以提供一个更新脚本,只是那两个改变。 ALTER EXTENSION UPDATE命令可以用于应用这些变化,并且 跟踪扩展的版本,实际上是安装在一个给定的数据库中。

一些SQL对象是显示在ALTER EXTENSION的描述中的扩展对象。 值得注意的是,对象是数据库集群范围,如数据库,角色,和表空间, 无法扩展成员,因为扩展在一个数据库中是唯一已知的。 (虽然并不禁止扩展脚本创建这样的对象,如果它这样做, 他们将不会作为跟踪扩展。)也注意到,当一个表可以是扩展成员时, 其子对象如索引不直接考虑扩展成员。 另外重要的一点是,模式可以属于扩展,但非 反之亦然:扩展这样有一个不合格的名称并且不 存在任何模式"内部"。 扩展的成员对象,然而,将属于模式, 只要适合他们的对象类型。它可能或可能不适合拥有模式成员对象的一个扩展。

35.15.1. 扩展文件

CREATE EXTENSION命令依赖于每个扩展的控制文件, 它必须被命名为和带有.control后缀的扩展相同。并且被放置在安装的 SHAREDIR/extension目录中。必须至少有一个SQL脚本文件, 遵循命名模式extension--version.sql (比如,扩展foo的版本1.0foo--1.0.sql)。 缺省,脚本文件也被放置在SHAREDIR/extension目录中; 但是控制文件可以为脚本文件声明不同的目录。

扩展控制文件的文件格式与postgresql.conf文件相同, parameter_name = value 任务列表,每行一个。通过允许#引进空行和注释。 确保引用任何值,不是单词或者数字。

控制文件可以设置以下参数:

directory (string)

该目录包含扩展的SQL脚本文件。 除非给定绝对路径名,名字是相对于安装的SHAREDIR目录。 默认操作相当于指定directory = 'extension'

default_version (string)

扩展的默认版本(如果在CREATE EXTENSION中没有声明版本,则一个将被安装)。 虽然这可以被省略,如果没有VERSION选项,这将导致CREATE EXTENSION 失败,所以你通常不想这样做。

comment (string)

关于扩展的注释(任何字符串)。另外,注释可以通过脚本文件中的COMMENT 命令进行设置。

encoding (string)

通过脚本文件使用字符集编码。如果脚本文件包含任何非-ASCII字符,则被声明。 否则这些文件被认为数据库编码。

module_pathname (string)

这个参数的值将 为了每个发生在脚本文件中的MODULE_PATHNAME被替换。如果它不被设置, 则没有替代。通常情况下,这是设置为 $libdir/shared_library_name并且 MODULE_PATHNAMECREATE FUNCTION命令中为C语言函数被使用, 因此脚本文件不需要硬线共享库的名字。

requires (string)

这个扩展取决于扩展名列表,比如requires = 'foo, bar'。 这些扩展必须在可以被安装前被安装。

superuser (boolean)

如果这个参数是true(缺省),只有超级用户可以创建扩展或者更新它到一个新版本。 如果它被设置为false,仅仅需要安装过程中执行命令所需的权限或者更新脚本。

relocatable (boolean)

如果扩展初始化创建之后可能移动所包含的对象到不同的模式中, 则扩展是浮动的。缺省是false等,这个扩展是不浮动的。 参见下文获取更多信息。

schema (string)

这个参数只能设置为非-浮动的扩展。它强制扩展被加载到精确的命名模式中,并且 没有任何其他的。参见下文获取更多信息。

除了初步控制文件extension.control, 扩展有在形式extension--version.control 中命名的二级控制文件。如果被提供,这些必须位于脚本文件目录中。 二级控制文件遵循同样格式作为初步控制文件。 当安装或者更新扩展版本的时候,在二级控制文件中设置的任何参数覆盖初步控制文件, 然而,不能在二级控制文件中设置参数directorydefault_version

扩展的SQL脚本文件可以包含任何SQL命令,除了事务控制命令 (BEGINCOMMIT等)以及不能在一个事务块中执行的命令(比如VACUUM)。 这是因为脚本文件在事务块中是隐式执行的。

扩展的SQL脚本文件也可以包含以\echo开头的行, 这被扩展机制忽略(作为注释)。如果脚本文件给psql而不是通过CREATE EXTENSION (参见下文例子脚本)被加载,则这个规定往往抛出错误。 没有那些,用户可能无意中加载扩展内容作为"loose"对象而不是作为扩展, 从中恢复的事态有点繁琐。

当脚本文件可以包含指定编码允许的任何字符时,则控制文件应该包含纯ASCII, 因为PostgreSQL不知道控制文件中的编码方式。 实践中如果你想在扩展注释中使用非-ASCII字符,这个是一个问题。 在这种情况下推荐做法是不使用控制文件comment参数,但是代替使用 脚本文件中的COMMENT ON EXTENSION设置comment。

35.15.2. 扩展浮动

用户通常希望加载包含在扩展中的对象到一个扩展者考虑到的不同的模式中。 有浮动的三种支持级别。

在所有情况下,脚本文件与search_path初始设置指向 目标模式一起被执行;也就是说,CREATE EXTENSION相当于:

SET LOCAL search_path TO @extschema@;

这允许通过脚本文件创建的对象到目标模式。如果它希望,则脚本文件可以改变search_path。 但是这通常是不可取的,search_path被存储到CREATE EXTENSION的 先前设置完成。

如果它被给定,目标模式是由控制文件中的schema参数决定的。 否则由CREATE EXTENSIONSCHEMA选项决定。 否则当前的默认对象创建模式(调用者search_path的第一个)。 当使用控制文件schema,如果它不存在,则创建目标模式, 但是在其他两种情况下,它必须已经存在。

如果任何先决条件扩展列在控制文件的requires中,目标模式附加到 search_path的初始设置中。这允许对象对于新的扩展脚本文件时可见的。

尽管非可重定位扩展可以通过多个模式包含对象,为了外部使用把所有的对象放到一个单独模式中是可取的, 这被认为是扩展的目标模式。在相关扩展创建过程中,这样的安排方便search_path的缺省设置。

35.15.3. 扩展配置表

一些扩展包含配置表, 其中包含的数据可能安装扩展之后被用户添加或更改。 通常,如果一个表是扩展部分,既不是表的定义, 也不是被pg_dump备份的内容, 但这样的行为对配置表是不需要的; 用户修改的任何数据需要包含到备份中, 或备份和重载之后扩展会有不同的表现。

为了解决这个问题,扩展的脚本文件可以标记表,它已经作为配置表被创建,其中 将导致pg_dump包含转储中表的内容(不是定义)。要做到这一点, 在创建表之后调用pg_extension_config_dump(regclass, text),比如:

CREATE TABLE my_config (key text, value text);

SELECT pg_catalog.pg_extension_config_dump('my_config', '');

这种方式可以标记任何数量表。

pg_extension_config_dump的第二个参数是空字符串时, 该表的所有内容都被pg_dump备份。如果表 最初扩展脚本创建为空,通常是唯一正确的。如果有 一个初始数据和用户表中提供的数据的混合,则pg_extension_config_dump的 第二个参数提供了WHERE条件选择被备份的数据。比如,你可能做

CREATE TABLE my_config (key text, value text, standard_entry boolean);

SELECT pg_catalog.pg_extension_config_dump('my_config', 'WHERE NOT standard_entry');

并且确保通过扩展脚本创建的行standard_entry为真。

更加复杂的情况,比如初始化提供的行可能通过用户被修改,通过在配置表上创建触发器被处理 以确保正确标记修改的行。

你可以通过再次调用pg_extension_config_dump修改与配置表相关的过滤条件。 这在扩展更新脚本中通常是有用的。 标记表不再为配置表的唯一方法是从带有ALTER EXTENSION ... DROP TABLE 的扩展中分离出来。

请注意,这些表之间的外键关系将检测通过pg_dump转储出来的表的顺序。 特别的,pg_dump将尝试在转储引用表之前转储被引用的表。 因为外键关系是在CREATE EXTENSION时设置的(在数据被加载到表中之前), 所以不支持循环依赖。当存在循环依赖时,数据将仍然被转储出去, 但是转储将不能直接恢复,并且需要用户介入。

35.15.4. 扩展更新

扩展机制的一个优点是,它提供了方便管理更新定义一个扩展对象的SQL命令的方式。 这是通过将版本的名称或号链接扩展的安装脚本的每个发布版本做到的。 此外,如果您希望用户可以动态的从一个版本到下一个更新他们的数据库 ,你应该提供update scripts执行一个版本到下一个做出必要的改变。 以下模式extension--oldversion--newversion.sql 更新脚本的名字。(比如,foo--1.0--1.1.sql使用命令修改扩展foo 的版本1.0到版本1.1)。

给定一个可用的合适更新脚本, 命令ALTER EXTENSION UPDATE将更新已安装扩展到指定的新版本。 运行在相同环境中的更新脚本,CREATE EXTENSION 提供了安装环境脚本:特别是,search_path以相同 方式进行设置,并通过脚本创建任何新的对象被自动添加到扩展中。

如果扩展有二次控制文件,控制参数用于与脚本目标(新)版本联系的更新脚本。

更新机制可以用来解决一个重要的特殊情况: 将转变"松散"对象的集合到一个扩展。 在扩展机制被添加到PostgreSQL(9.1中)之前, 许多人写的扩展模块简化了已创建的各式各样的未包装的对象。 给定包含这样对象的现有数据库,我们怎么能转换 对象到适当成套扩展?删除它们然后执行纯CREATE EXTENSION是一种方式, 但它不是可取的,如果对象有依赖关系(例如,如果有扩展创建的数据类型的表列)。 修复这种情况的方式是创建一个空的扩展,然后使用ALTER EXTENSION ADD 把每个预先存在的对象附属在扩展中, 最后在当前扩展版本中创建任何新的对象, 但不在未包装发布中。CREATE EXTENSION 支持带有FROM old_version选项的情况。 这导致它不运行目标版本的正常安装脚本,而是更新脚本命名 extension--old_version--target_version.sql。 虚拟版本名称选择使用old_version胜任扩展发起者, 尽管未包装是一种常见的公约。如果你有多个以前的版本,你需要能够更新扩展风格, 使用多个虚拟版本名称来识别它们。

ALTER EXTENSION能够执行更新 脚本文件序列以实现请求更新。例如,如果只有 foo--1.0--1.1.sqlfoo--1.1--2.0.sql可用, 当目前安装的是1.0时, 如果需要更新到版本2.0ALTER EXTENSION将在序列中应用它们。

PostgreSQL不假定任何有关版本名称的属性: 例如,它不知道1.1遵循1.0。 它只匹配可用的版本名称并且 遵循路径要求应用最新的更新脚本。 (一个版本的名称可以是不包含 --或前导或尾随-的任意字符串。)

有时提供"downgrade"脚本是非常有用的, 例如foo--1.1--1.0.sql允许恢复与版本1.1相关的变化。 如果你这样做了,小心downgrade脚本可能会意外地 因它产生一个较短的路径而得以应用的可能性。 风险情况下有一个"快速路径"更新脚本,向前跳几个版本 以及降级脚本到快速路径的起点。 这可能需要较少的步骤应用降级,然后快速路径向前一次移动一个版本。 如果降级脚本删除任何不可替代的对象,这将产生不良的结果。

为了检查意外的更新路径,使用这个命令:

SELECT * FROM pg_extension_update_paths('extension_name');

这显示了已指定扩展的每对不同已知的版本名称, 以及更新路径序列将采取从源版本到目标版本, 或者如果没有可用的更新路径,则为NULL。 路径以带有--分隔符的文本形式显示。 如果你喜欢数组形式,则可以使用regexp_split_to_array(path,'--')

35.15.5. 扩展实例

这是一个SQL扩展的完整实例,二元复合类型可以存储插槽中的任何类型的值, 被命名为"k""v"。非-文本值自动强制转换为文本存储。

脚本文件pair--1.0.sql看起来像:

-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION pair" to load this file. \quit

CREATE TYPE pair AS ( k text, v text );

CREATE OR REPLACE FUNCTION pair(anyelement, text)
RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';

CREATE OR REPLACE FUNCTION pair(text, anyelement)
RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';

CREATE OR REPLACE FUNCTION pair(anyelement, anyelement)
RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';

CREATE OR REPLACE FUNCTION pair(text, text)
RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair;';

CREATE OPERATOR ~> (LEFTARG = text, RIGHTARG = anyelement, PROCEDURE = pair);
CREATE OPERATOR ~> (LEFTARG = anyelement, RIGHTARG = text, PROCEDURE = pair);
CREATE OPERATOR ~> (LEFTARG = anyelement, RIGHTARG = anyelement, PROCEDURE = pair);
CREATE OPERATOR ~> (LEFTARG = text, RIGHTARG = text, PROCEDURE = pair);

控制文件pair.control看起来像:

# pair extension
comment = 'A key/value pair data type'
default_version = '1.0'
relocatable = true

当你几乎不需要makefile安装这两个文件到正确目录时,你可以使用 包含下面内容的Makefile

EXTENSION = pair
DATA = pair--1.0.sql

PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)

这个makefile依赖于PGXS,在第 35.16 节中描述。 命令make install将安装控制和脚本文件到正确目录,通过pg_config 报告。

一旦安装了这些文件,使用CREATE EXTENSION命令加载对象到 任何特定数据库。