Redrock Postgres 搜索 英文
版本: 9.3 / 9.4 / 9.5 / 9.6 / 10 / 11 / 12 / 13 / 14 / 15 / 16 / 17

36.17. 将相关对象打包到扩展中 #

36.17.1. 扩展文件
36.17.2. 扩展的可移植性
36.17.3. 扩展配置表
36.17.4. 扩展更新
36.17.5. 使用更新脚本安装扩展
36.17.6. 扩展的安全考虑因素
36.17.7. 扩展示例

对于 PostgreSQL 来说,一个有用的扩展通常包括多个 SQL 对象;例如,一个新数据类型将需要新的函数、新的运算符,并且可能需要新的索引运算符类。将所有这些对象收集到单个包中以简化数据库管理很有用。PostgreSQL 将这样的包称为扩展。要定义一个扩展,您至少需要一个脚本文件,其中包含SQL用于创建扩展对象的命令和一个控制文件,其中指定了扩展本身的一些基本属性。如果扩展包括 C 代码,通常还会有一个共享库文件,其中包含已构建的 C 代码。一旦拥有这些文件,一个简单的 CREATE EXTENSION 命令即可将对象加载到数据库中。

使用扩展而不是仅运行SQL脚本将一堆松散对象加载到数据库中的主要优势在于,PostgreSQL 随后便会理解扩展中的对象是放在一起的。您可以使用单个 DROP EXTENSION 命令删除所有对象(无需维护单独的卸载脚本)。更实用的是,pg_dump 会明白,它不应该转储扩展的各个成员对象 - 它只会将 CREATE EXTENSION 命令包含在转储文件中。这极大地简化了迁移到可能包含比旧版本更多或不同的对象的扩展的新版本。但请注意,在将此类转储加载到新数据库时,必须提供扩展的控制、脚本来和其它文件。

PostgreSQL 不会允许在单个扩展中删除单个对象,但可以删除整个扩展。同时,虽然您可以更改扩展成员对象的定义(例如,通过给函数使用 CREATE OR REPLACE FUNCTION),但请记住,修改后的定义不会被 pg_dump 转储。这样的更改通常只有在您同时在扩展的脚本文件中进行相同的更改时才有意义。(但是,对于包含配置数据的表有特殊规定;参见 第 36.17.3 节。)在实际使用中,通常最好创建扩展更新脚本以对扩展成员对象执行更改。

扩展脚本当中可以使用 GRANTREVOKE 语句,为该扩展中各部分的对象设置权限。每个对象的最终权限集(如果有的话)都将存储在 pg_init_privs 系统目录中。使用 pg_dump 时,CREATE EXTENSION 命令会包含在转储中,后面紧跟着 GRANTREVOKE 语句集,这些语句集是为对象设置权限(与转储时权限相同)所必需的。

PostgreSQL 目前不支持扩展脚本发出 CREATE POLICYSECURITY LABEL 语句。这些语句应设在扩展创建后。扩展对象上的所有 RLS 策略和安全标签都将包含在由 pg_dump 创建的转储。

扩展机制也为打包修改脚本提供了预设,这些脚本将调整扩展中包含的 SQL 对象的定义。例如,某个扩展的 1.1 版本与 1.0 相比新增了一个函数并更改了另一个函数的主体时,扩展作者可以提供一个仅执行这两个更改的更新脚本ALTER EXTENSION UPDATE 命令随后将用于应用这些更改并跟踪扩展的哪个版本实际上安装在给定的数据库中。

可以成为扩展的 SQL 对象类型显示在 ALTER EXTENSION 描述中。值得注意的是,数据库集群范围内的对象(例如数据库、角色和表空间)不能成为扩展成员,因为扩展仅在一个数据库中为人所知。(尽管扩展脚本并不禁止创建此类对象,但如果创建了此类对象,它们将不会被跟踪为扩展的一部分。)还要注意,虽然表可以成为扩展的成员,但其子级对象(例如索引)不会被直接视为扩展的成员。另一个重要的一点是,模式可以属于扩展,但反之则不行:扩展本身具有不合格名称,并且在任何模式中都不存在 within。不过,扩展的成员对象将在适用其对象类型的任何时候属于模式。一个扩展对其成员对象所在的模式拥有所有权也可能不拥有所有权。

如果扩展的脚本创建了任何临时对象(如临时表),这些对象在当前会话的剩余时间内将被视为扩展成员,但将在会话结束时自动删除,就像任何临时对象一样。这是扩展成员对象不能在不删除整个扩展的情况下被删除的规则的一个例外。

36.17.1. 扩展文件 #

对于每个扩展,CREATE EXTENSION 命令依赖于一个控制文件,该文件必须与扩展同名,并使用 .control 的后缀,并且必须放置在安装的 SHAREDIR/extension 目录中。也必须至少有一个SQL脚本文件,遵循命名模式 extension--version.sql(例如,foo--1.0.sql 表示扩展 foo 的版本 1.0)。默认情况下,脚本文件也会被放置在 SHAREDIR/extension 目录中;但控制文件可以为脚本文件指定一个不同的目录。

扩展控制文件的文件格式与 postgresql.conf 文件的格式相同,即每行一个 parameter_name = value 赋值的列表。允许有空白行和以 # 引入的注释。确保对不是单个单词或数字的任何值加引号。

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

directory (string) #

包含扩展的目录SQL脚本文件。除非给出了绝对路径,否则名称相对于安装的 SHAREDIR 目录。默认行为等效于指定 directory = 'extension'

default_version (string) #

扩展的默认版本(如果在 CREATE EXTENSION 中未指定版本,则将安装该版本)。虽然可以省略此版本,但这将导致 CREATE EXTENSION 失败,如果未出现 VERSION 选项,因此通常不需要这样做。

comment (string) #

有关扩展的注释(任何字符串)。该注释在最初创建扩展时应用,但不会在扩展更新期间应用(因为这可能会覆盖用户添加的注释)。或者,可以通过在脚本文件中编写 COMMENT 命令来设置扩展的注释。

编码 (字符串) #

脚本文件使用的字符集编码。如果脚本文件中包含任何非 ASCII 字符,则应指定此参数。否则,文件将被假定为数据库编码。

模块路径名 (字符串) #

此参数的值将替换脚本文件中 MODULE_PATHNAME 的每个出现。如果未设置,则不进行替换。通常,这被设置为 $libdir/共享库名称,然后在 C 语言函数的 CREATE FUNCTION 命令中使用 MODULE_PATHNAME,以便脚本文件不需要硬连线共享库的名称。

依赖项 (字符串) #

此扩展依赖的扩展名称列表,例如 requires = 'foo, bar'。在安装此扩展之前,必须安装这些扩展。

禁止重定位 (字符串) #

此扩展依赖的扩展名称列表,这些扩展应该被禁止通过 ALTER EXTENSION ... SET SCHEMA 更改其架构。如果此扩展的脚本以无法跟踪重命名的方式引用所需扩展的架构的名称(使用 @extschema:名称@ 语法),则需要这样做。

超级用户 (布尔值) #

如果此参数为 true(这是默认值),则只有超级用户可以创建该扩展或将其更新到新版本(但另请参见下文的 受信任)。如果将其设置为 false,则只需要执行安装或更新脚本中命令所需的权限。如果任何脚本命令需要超级用户权限,则通常应将其设置为 true。(此类命令无论如何都会失败,但最好预先给出错误。)

受信任的 (布尔值) #

如果将此参数设置为true(而非默认设置),则允许一些非超级用户安装superuser已设置为true的扩展程序。具体来说,拥有当前数据库上的CREATE权限的任何人都可以进行安装。当执行CREATE EXTENSION的用户并非超级用户,但被允许借用此参数进行安装时,安装或更新脚本会以引导超级用户的身份运行,而不会以调用用户的身份运行。如果superuserfalse,则此参数不相关。一般而言,对于允许访问仅限超级用户的能力(例如文件系统访问)的扩展程序,不应将其设置为true。此外,标记扩展程序为受信任需要付出大量额外工作来安全地编写扩展程序的安装和更新脚本;请参阅第 36.17.6 节

relocatable (boolean) #

如果一个扩展程序在首次创建后能够将其包含对象移至一个不同的架构中,则该扩展程序是可重定位的。默认设置为false,即扩展程序不可重定位。请参阅第 36.17.2 节以了解更多信息。

schema (string) #

此参数只能针对不可重定位扩展程序来设置。它强制将扩展程序加载到指定的架构,而不会加载到其他任何架构中。schema参数仅在首次创建扩展程序时咨询,而不在扩展程序更新过程中。请参阅第 36.17.2 节以了解更多信息。

除了主控制文件extension.control之外,一个扩展程序还可以有以extension--version.control作为样式命名的从属控制文件。如果提供,则这些文件必须位于脚本文件目录中。从属控制文件遵循与主控制文件相同的格式。在安装或更新到该扩展程序版本时,从属控制文件中设置的任何参数都会覆盖主控制文件。但是,无法在从属控制文件中设置directorydefault_version参数。

一个扩展程序的SQL脚本文件可以包含任何 SQL 命令,但不能包含事务控制命令(BEGINCOMMIT 等)以及无法在事务块内执行的命令(如 VACUUM)。这是因为脚本文件默认在事务块内执行。

一个扩展程序的SQL脚本文件还可以包含以 \echo 开头的行,这些行将被扩展机制忽略(视为注释)。通常使用此项,如果将脚本文件提供给 psql,而没有通过 CREATE EXTENSION 加载(见 第 36.17.7 节 中的示例脚本)。如果没有它,用户可能不小心将扩展的内容作为 松散 对象加载,而不是扩展加载,从这种状态恢复有点麻烦。

如果扩展脚本包含字符串 @extowner@,则该字符串将替换为调用 CREATE EXTENSIONALTER EXTENSION 的用户的名称(带引号)。通常,这个功能由标记为受信任的扩展使用,它将所选对象的权限分配给调用用户,而不是引导超级用户。(但是,在执行此操作时务必小心。例如,将 C 语言函数的权限分配给非超级用户会为该用户创建一个权限提升路径。)

当脚本文件可以包含指定编码允许的任何字符时,控制文件应仅包含纯 ASCII 码,因为 PostgreSQL 无从得知控制文件采用哪种编码。实际中,只有在扩展的注释中要使用非 ASCII 字符才存在这个问题。在这种情况下的推荐做法是,不要使用控制文件 comment 参数,而是在脚本文件中使用 COMMENT ON EXTENSION 来设置注释。

36.17.2. 扩展的可重定位性 #

用户通常希望将扩展中包含的对象加载到扩展作者所设想的方案中去。可重定位性支持三级

  • 完全可重定位的扩展可以在任何时候移动到另一个方案,甚至在被加载到数据库后。这是通过以下命令实现的:ALTER EXTENSION SET SCHEMA,该命令会自动将所有成员对象重命名为新的方案。通常,只有在扩展不包含任何其对象所处方案的内部假设时才可行。此外,扩展的对象最初必须全在一个方案中(忽略不属于任何方案的对象,例如过程语言)。通过在控制文件中设置 relocatable = true 来标记一个完全可重定位的扩展。

  • 安装期间可以重定位扩展,但之后不可以。如果扩展的脚本文件需要显式引用目标架构的情况通常如此,例如在 SQL 函数的 search_path 属性设置中。针对此类扩展,在控制文件中设置 relocatable = false,然后在脚本文件中使用 @extschema@ 来引用目标架构。在执行脚本之前,该字符串的所有出现项都将替换为实际目标架构的名称(必要时使用双引号括起来)。用户可以使用 CREATE EXTENSIONSCHEMA 选项设置目标架构。

  • 如果扩展根本不支持重定位,请在其控制文件中设置 relocatable = false,并将 schema 设置为预期目标架构的名称。这将阻止使用 CREATE EXTENSIONSCHEMA 选项,除非指定控制文件中命名的相同架构。如果扩展包含内部假设,而其架构名称无法通过使用 @extschema@ 替换,则通常需要进行此选择。在此情况下,@extschema@ 替换机制也适用,尽管由于架构名称是由控制文件确定的,因此其用处有限。

在所有情况下,脚本文件都将执行,而 search_path 最初设置为指向目标架构;也就是说,CREATE EXTENSION 执行的操作等效于以下操作

SET LOCAL search_path TO @extschema@, pg_temp;

这允许脚本文件创建的对象进入目标架构。如果需要,脚本文件可以更改 search_path,但通常不希望这样做。完成 CREATE EXTENSION 后,search_path 将还原为其先前的设置。

如果提供了控制文件中的 schema 参数,则目标架构由该参数决定;如果提供了 CREATE EXTENSIONSCHEMA 选项,则由该选项决定;否则,由当前默认对象创建架构(调用者的 search_path 中的第一个)决定。在使用控制文件 schema 参数时,如果目标架构尚不存在,系统会创建它,而在其他两种情况下,它必须已经存在。

如果在控制文件的 requires 中列出了任何先决条件扩展,则在新的扩展目标架构之后,其目标架构会被添加到 search_path 的初始设置中。这使得新扩展的脚本文件可以查看这些对象。

出于安全考虑,在所有情况下,pg_temp 都会自动附加到 search_path 的末尾。

尽管不可重新定位的扩展可以包含跨多个模式分布的对象,但通常需要将面向外部使用的所有对象置于单个模式中,该模式被视为扩展的目标模式。在创建依赖扩展时,此类安排与 search_path 的默认设置协同工作,十分便捷。

如果扩展引用属于另一个扩展的对象,建议用模式限定那些引用。为此,在扩展的脚本文件中编写 @extschema:name@,其中 name 是另一个扩展的名称(该名称必须列在此扩展的 requires 列表中)。这个字符串将替换为该扩展的目标模式的名称(必要时加上双引号)。虽然此表示法无需在扩展的脚本文件中对模式名称做出硬连线假设,但它的使用可能会将其他扩展的模式名称嵌入此扩展的已安装对象中。(通常在字符串文本中使用 @extschema:name@ 时会发生这种情况,例如在函数体或 search_path 设置中。)在其他情况下,对象引用会在解析期间缩减为 OID,并且不需要后续查找。)如果另一个扩展的模式名称嵌入其中,你应该通过将另一个扩展的名称添加到此扩展的 no_relocate 列表中,禁止在安装此扩展之后重新定位另一个扩展。

36.17.3. 扩展配置表 #

一些扩展包括配置表,其中包含用户在安装扩展后可以添加或更改的数据。通常,如果一个表是扩展的一部分,则表定义及其内容都不会由 pg_dump 进行转储。但对于配置表来说,这种行为是不理想的;用户进行的任何数据更改都需要包含在转储中,否则扩展在转储和还原之后的行为会不同。

为了解决这个问题,扩展的脚本文件可以将它创建的表或序列标记为配置关联,这将导致 pg_dump 在转储中包含表或序列的内容(而不是其定义)。要执行此操作,请在创建表或序列后调用函数 pg_extension_config_dump(regclass, text),例如

CREATE TABLE my_config (key text, value text);
CREATE SEQUENCE my_config_seq;

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

用这种方法,可以标记任意数量的表或序列。与 serialbigserial 列关联的序列也可以被标记。

`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 才为 true。

对于序列,` pg_extension_config_dump` 的第二个参数没有作用。

更复杂的情况,例如用户可以修改初始提供的行,可以通过在配置表中创建触发器来处理,以确保正确标记修改后的行。

你可以通过再次调用 ` pg_extension_config_dump` 来更改与某个配置表关联的筛选条件。(这通常在扩展更新脚本中有用。)标记表不再是配置表的方法只有一个,即使用 ` ALTER EXTENSION ... DROP TABLE` 将其与扩展分离。

请注意,这些表之间的外键关系将决定 pg_dump 转储表的顺序。具体来说,pg_dump 会尝试在外键相关表之前转储被引用表。由于外键关系是在 `CREATE EXTENSION` 时(在数据加载到表之前)设置的,因此不支持循环依赖关系。当存在循环依赖关系时,数据仍然会被转储,但转储无法直接还原,需要用户干预。

serialbigserial 列关联的序列需要直接标记以转储其状态。标记它们的父关系不足以用于此目的。

36.17.4. 扩展更新 #

扩展机制的一个优点是它提供了管理定义扩展对象的 SQL 命令更新的便捷方法。其方法是将版本名称或号码与扩展安装脚本的每个已发布版本相关联。此外,如果希望用户可以动态地将他们的数据库从一个版本更新到下一个版本,你应该提供 更新脚本,用于进行必要的更改以从一个版本过渡到下一个版本。更新脚本的名称遵循模式 extension--old_version--target_version.sql(例如,foo--1.0--1.1.sql 包含将扩展 foo 的版本 1.0 修改为版本 1.1 的命令)。

给定一个可用的更新脚本,命令 ALTER EXTENSION UPDATE 会将已安装的扩展更新到指定的新版本。更新脚本在与 CREATE EXTENSION 向安装脚本提供的相同环境中运行:尤其是,search_path 以相同的方式建立,并且脚本创建的任何新对象都会自动添加到扩展中。此外,如果该脚本选择取消扩展成员对象,它们会自动从扩展中分离。

如果扩展有二级控制文件,则为更新脚本使用的控制参数是与脚本的目标(新)版本相关联的那些参数。

ALTER EXTENSION 能够执行更新脚本文件的序列以实现请求的更新。例如,如果只有 foo--1.0--1.1.sqlfoo--1.1--2.0.sql 可用,则在当 1.0 已安装时请求将版本更新至 2.0 的情况下,ALTER EXTENSION 会按顺序应用它们。

PostgreSQL 不对版本名称的属性作任何假设:例如,它并不知道 1.1 是否在 1.0 之后。它只是匹配可用的版本名称,然后沿着需要应用尽可能少的更新脚本的路径前进。(版本名称实际上可以是任何不包含 -- 或前导或后缀 - 的字符串。)

有时提供 降级 脚本很有用,例如 foo--1.1--1.0.sql 可以让用户还原与版本 1.1 关联的更改。如果你这样做,小心降级脚本可能会意外地应用,因为这会产生更短的路径。有风险的情况是存在一个 快速路径 更新脚本,它会跳到几个版本和之前,以及一个降级脚本,其降级至快速路径的起点。应用降级脚本然后应用快速路径所需的时间可能短于一次向前移动一个版本所需的时间。如果降级脚本取消了任何不可替换的对象,这会产生不良后果。

要检查意外的更新路径,使用以下命令

SELECT * FROM pg_extension_update_paths('extension_name');

这将针对指定的扩展显示每对不同的已知版本名称,以及从源版本迁移至目标版本所需的更新路径顺序,如果没有可用的更新路径则显示 NULL。路径采用文本形式显示,以 -- 分隔符分隔。如果你更喜欢数组格式,可以使用 regexp_split_to_array(path,'--')

36.17.5. 使用更新脚本安装扩展 #

已经存在一段时间的扩展可能会出几个版本,而作者需要为这些版本编写更新脚本。例如,如果您发布了一个 foo 扩展,包含版本 1.01.11.2,那么应有用于更新脚本的 foo--1.0--1.1.sqlfoo--1.1--1.2.sql。在 PostgreSQL 10 之前,还有必要创建新的脚本文件 foo--1.1.sqlfoo--1.2.sql,以便直接构建较新的扩展版本,否则将无法直接安装较新版本,只能通过安装 1.0 然后更新来进行。这种方法既繁琐又有重复性,但现在无需执行了,因为 CREATE EXTENSION 可以自动遵循更新链。例如,如果只有脚本文件 foo--1.0.sqlfoo--1.0--1.1.sqlfoo--1.1--1.2.sql 可用,则安装版本 1.2 的请求将通过按顺序运行这三个脚本来执行。该处理流程与您先安装 1.0 然后更新到 1.2 的流程相同。(与 ALTER EXTENSION UPDATE 一样,如果有多条路径可用,则会首选最短的路径。)按照这种风格排列扩展的脚本文件可以减少生成小更新所需维护工作量。

如果您将辅助(版本特定的)控制文件与此风格维护的扩展一起使用,请记住,即使每个版本无独立安装脚本,也需要一个控制文件,因为该控制文件将确定如何对隐式更新到该版本的执行进行执行。例如,如果 foo--1.0.control 指定 requires = 'bar',而 foo 的其他控制文件没有指定该参数,则从 1.0 更新到另一个版本时,将删除扩展对 bar 的依赖关系。

36.17.6. 扩展的安全注意事项 #

广泛发布的扩展应假设不了解它们所在的数据库。因此,适当以安全的风格编写由扩展提供的函数,而不会因为基于搜寻路径的攻击而陷入困境。

superuser 属性设置为 true 的扩展还必须考虑其安装和更新脚本中执行的动作所引发的安全风险。恶意用户不会花费太多力气便能创建木马对象,这些对象会损害随后执行的书写粗糙的扩展脚本,从而允许该用户获取超级用户权限。

如果扩展被标记为 可信,则其安装架构可由正在安装的用户选择,该用户可能有意使用不安全的架构,以期获得超级用户权限。因此,从安全的角度来看,可信扩展极易受到攻击,且必须仔细检查其所有脚本命令,以确保不可能发生任何妥协。

有关安全编写函数的建议,请参阅下面的 第 36.17.6.1 节,有关安全编写安装脚本的建议,请参阅 第 36.17.6.2 节

36.17.6.1. 扩展函数的安全注意事项 #

由于在执行时而不是在创建时解析这些函数,因此扩展提供的 SQL 语言和 PL 语言函数在执行时面临基于搜索路径的攻击风险。

CREATE FUNCTION 参考页面包含有关安全地编写 SECURITY DEFINER 函数的建议。将这些技术应用于扩展提供的任何函数都是一种良好的做法,因为该函数可能由高权限用户调用。

如果你无法将 search_path 设置为仅包含安全架构,则假设每个不合格名称都可能解析为恶意用户定义的对象。注意依赖于 search_path 的结构;例如,INCASE expression WHEN 始终使用搜索路径选择一个操作符。请改用 OPERATOR(schema.=) ANYCASE WHEN expression

通用的扩展通常不应假设已安装到安全架构中,这意味着即使是对其自身对象的架构限定引用也并非完全没有风险。例如,如果该扩展已定义了一个函数 myschema.myfunc(bigint),则诸如 myschema.myfunc(42) 之类的调用可能会被敌对函数 myschema.myfunc(integer) 捕获。请小心,函数和操作符参数的数据类型与声明的参数类型完全一致,在必要时使用显式转换。

36.17.6.2. 扩展脚本的安全注意事项 #

安装或更新扩展的脚本应该编写入防止脚本执行期间发生基于查找路径的攻击。如果脚本中的对象引用可以解析为脚本编写者预期的其他对象,则可能立即发生入侵,或在使用定义错误的扩展对象时发生入侵。

尽管 CREATE FUNCTIONCREATE OPERATOR CLASS 等 DDL 命令通常是安全的,但请注意任何将其通用表达式作为组件的命令。例如, CREATE VIEW 需要进行审核, CREATE FUNCTION 中的 DEFAULT 表达式亦是如此。

有时扩展脚本可能需要执行通用 SQL,例如进行无法通过 DDL 完成的目录调整。请务必使用安全的 search_path 执行此类命令;不要信任 CREATE/ALTER EXTENSION 提供的路径是安全的。最佳做法是将 search_path 临时设置为 'pg_catalog, pg_temp' 并在需要时显式插入对扩展安装架构的引用。(这种做法也有助于创建视图。)示例可在 PostgreSQL 源代码分发中的 contrib 模块中找到。

很难使跨扩展引用完全安全,部分原因是不确定另一个扩展位于哪个架构中。如果两个扩展安装在同一架构中,则这种危险会降低,因为恶意对象无法在安装时的 search_path 中位于所引用的扩展之前。但是,目前还没有强制要求的机制。目前,最佳做法是,除非另一个扩展始终安装在 pg_catalog 中,否则不要将其标记为受信任的扩展(如果某个扩展依赖于另一个扩展)。

36.17.7. 扩展示例 #

以下是以下内容的完整示例:SQL- 仅扩展,它是一种两元素的混合类型,可以将其任意类型的值存储在名为 kv 的槽中。非文本值会自动强制转换为文本,以便进行存储。

脚本文件 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 FUNCTION pair(text, text)
RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::@[email protected];';

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

-- "SET search_path" is easy to get right, but qualified names perform better.
CREATE FUNCTION lower(pair)
RETURNS pair LANGUAGE SQL
AS 'SELECT ROW(lower($1.k), lower($1.v))::@[email protected];'
SET search_path = pg_temp;

CREATE FUNCTION pair_concat(pair, pair)
RETURNS pair LANGUAGE SQL
AS 'SELECT ROW($1.k OPERATOR(pg_catalog.||) $2.k,
               $1.v OPERATOR(pg_catalog.||) $2.v)::@[email protected];';

控制文件 pair.control 如下所示:

# pair extension
comment = 'A key/value pair data type'
default_version = '1.0'
# cannot be relocatable because of use of @extschema@
relocatable = false

虽然几乎无需 makefile 将这两个文件安装到正确的目录中,但可以使用包含此内容的 Makefile

EXTENSION = pair
DATA = pair--1.0.sql

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

此 makefile 依赖于以下内容:PGXS,该命令在 36.18 节 中有说明。命令 make install 将把控件和脚本文件安装到 pg_config 报告的正确目录中。

安装文件后,使用 CREATE EXTENSION 命令将对象加载到任何特定数据库中。