一个有用的 PostgreSQL 扩展通常包含多个 SQL 对象;例如,一种新的数据类型将需要新的函数、新的操作符,并且很可 能还需要新的索引操作符类。把所有这些对象收集到一个单独的包中,有助于 简化数据库管理。PostgreSQL 将这样的包称为 扩展。要定义一个扩展,至少需要一个 脚本文件,其中包含用于创建扩展对象的 SQL 命令,以及一个 控制文件, 用来指定扩展本身的一些基本属性。如果扩展包含 C 代码,通常还会有一个 共享库文件,C 代码已被构建到其中。一旦准备好这些文件,只需执行一个简 单的 CREATE EXTENSION 命令,就能把这些对象装入数据库。
使用扩展而不是仅仅运行 SQL 脚本把一堆“松散” 对象装入数据库,最主要的优点在于 PostgreSQL 能够理解这些对象是属于同一个扩展的。你可以用一条 DROP EXTENSION 命令删除全部对象(无需维护单独的“卸载”脚本)。更有用的是, pg_dump 知道自己不应转储扩展的各个成员对象 — 它只会在转储中包含一条 CREATE EXTENSION 命令。这极大地简化了迁移到扩展新版本的过程,即使新版本包含比旧版本更 多或不同的对象也是如此。不过要注意,在把这样的转储装载到新数据库时, 必须能够访问该扩展的控制文件、脚本文件以及其他相关文件。
PostgreSQL 不允许你删除扩展中包含的单个对象, 除非删除整个扩展。另外,虽然你可以修改扩展成员对象的定义(例如对函数 使用 CREATE OR REPLACE FUNCTION),但要记住,被修 改后的定义不会被 pg_dump 转储。这样的修改通 常只有在你同时在扩展脚本文件中做出同样修改时才有意义。(不过,对于包 含配置数据的表有特殊规定;见 Section 36.17.3。)在生产环境中,通 常更好的做法是创建一个扩展更新脚本,用它来完成对扩展成员对象的修改。
扩展脚本可以使用 GRANT 和 REVOKE 语句,为属于扩展的对象设置权限。每个对象的最终权限集合(如果设置了权 限)会存储在 pg_init_privs 系统目录中。使用 pg_dump 时,转储中会包含 CREATE EXTENSION 命令,后面再跟上一组必要的 GRANT 和 REVOKE 语句,以把对象 权限恢复到生成该转储时的状态。
PostgreSQL 目前不支持扩展脚本发出 CREATE POLICY 或 SECURITY LABEL 语句。它们应当在扩展创建完成之后再设置。扩展对象上的所有 RLS 策略和 安全标签都会包含在 pg_dump 创建的转储中。
扩展机制还提供了用于打包修改脚本的支持,以便调整扩展中所含 SQL 对象 的定义。例如,如果扩展 1.1 版相比 1.0 版增加了一个函数,并修改了另一 个函数的函数体,那么扩展作者可以提供一个 更新脚本 来只完成这两项修改。随后就可以使用 ALTER EXTENSION UPDATE 命令应用这些修改,并跟踪在 某个数据库中实际安装的是该扩展的哪个版本。
哪些 SQL 对象种类可以成为扩展成员,见 ALTER EXTENSION 的说明。特别是,数据库集簇范围内的对象,如数据库、角色和表空间,不能 成为扩展成员,因为扩展只在单个数据库内可见。(尽管扩展脚本并不禁止创 建这类对象,但如果这样做,它们不会作为扩展的一部分受到跟踪。)还要注意, 虽然表可以成为扩展成员,但其附属对象(如索引)并不直接被视为扩展成员。 另一个重要点是,模式可以属于扩展,但反过来不成立:扩展本身只有一个非 限定名,并不“位于”任何模式中。不过,扩展的成员对象会在其 对象类型适用的情况下属于某个模式。扩展是否应当拥有其成员对象所在的模 式,则要视具体情况而定。
如果扩展脚本创建了任何临时对象(如临时表),这些对象在当前会话的剩余 时间里会被视为扩展成员,但会像其他临时对象一样在会话结束时自动删除。 这是“扩展成员对象不能在不删除整个扩展的情况下被删除”这一规则的一个 例外。
CREATE EXTENSION 命令依赖于每个扩展都有一个控制文 件,该文件的名称必须与扩展同名并带有 .control 后缀, 且必须放在安装目录的 SHAREDIR/extension 目录中。此 外还必须至少有一个 SQL 脚本文件,其命名模式为 (例如,扩展 extension--version.sqlfoo 的 1.0 版脚本文 件为 foo--1.0.sql)。默认情况下,脚本文件也放在 SHAREDIR/extension 目录中;但控制文件可以为脚本文 件指定不同的目录。
扩展控制文件的其他查找位置可通过参数 extension_control_path 配置。
扩展控制文件的格式与 postgresql.conf 文件相同, 即由一组 parameter_name = value 赋值组成,每行一 条。允许空行和以 # 引入的注释。任何不是单个单词或 数字的值都要记得加引号。
控制文件可以设置下列参数:
directory (string) #包含扩展 SQL 脚本文件的目录。除非给出的是绝对路 径,否则该名称相对于找到控制文件的目录。默认情况下,会在找到控制 文件的同一目录中查找脚本文件。
default_version (string) #扩展的默认版本(即在 CREATE EXTENSION 中未指定版 本时将安装的版本)。虽然这个参数可以省略,但那样一来,如果没有给出 VERSION 选项,CREATE EXTENSION 就会失败,因此一般不希望这样做。
comment (string) #关于扩展的注释(任意字符串)。该注释会在初次创建扩展时应用,但不会 在扩展更新时应用(因为那样可能会覆盖用户后来添加的注释)。另外,也 可以在脚本文件中写一个 COMMENT 命令来设置扩 展注释。
encoding (string) #脚本文件所使用的字符集编码。如果脚本文件包含任何非 ASCII 字符,就 应指定这个参数。否则将假定这些文件使用数据库编码。
module_pathname (string) #该参数的值会替换脚本文件中每次出现的 MODULE_PATHNAME。如果未设置该参数,则不会进行替 换。通常会把它设置为简单的 , 然后在 C 语言函数的 shared_library_nameCREATE FUNCTION 命令中使用 MODULE_PATHNAME,这样脚本文件就无需把共享库的名 字硬编码进去。
requires (string) #本扩展所依赖的其他扩展名称列表,例如 requires = 'foo, bar'。这些被依赖的扩展必须先安 装好,本扩展才能安装。
no_relocate (string) #本扩展所依赖且应禁止通过 ALTER EXTENSION ... SET SCHEMA 更改其模式的扩展 名称列表。如果本扩展的脚本以无法跟踪重命名的方式引用了某个依赖扩展 的模式名(使用 @extschema: 语法), 就需要设置它。name@
superuser (boolean) #如果该参数为 true(默认值),则只有超级用户能够创 建该扩展或把它更新到新版本(但另见下面的 trusted)。 如果设置为 false,则只要求具备执行安装脚本或更新 脚本中命令所需的权限。如果脚本中的任何命令需要超级用户权限,通常都 应将此项设为 true。(这些命令反正也会失败,但提 前报错对用户更友好。)
trusted (boolean) #如果该参数被设为 true(默认并非如此),则允许某些 非超级用户安装 superuser 设为 true 的扩展。具体来说,任何在当前数据库上具有 CREATE 权限的用户都被允许安装。当执行 CREATE EXTENSION 的用户不是超级用户,但因该参数 而被允许安装时,安装或更新脚本会以引导超级用户的身份运行,而不是以 调用用户的身份运行。如果 superuser 为 false,这个参数就没有意义。一般来说,对于可能让 用户访问原本只有超级用户才能使用的能力(如文件系统访问)的扩展,不 应把它设为 true。此外,把一个扩展标记为 trusted,还需要付出大量额外努力来确保安装和更 新脚本写得足够安全;见 Section 36.17.6。
relocatable (boolean) #如果一个扩展在初次创建之后仍然可以把其包含的对象移动到不同的模式 中,那么它就是可重定位的。默认值为 false,也就是该扩展不可重定位。详见 Section 36.17.2。
schema (string) #该参数只能为不可重定位扩展设置。它强制扩展装载到指定名称的模式中, 而不能装载到其他模式。schema 参数只在初次创建扩展 时起作用,扩展更新时不会使用。详见 Section 36.17.2。
除主控制文件 外,扩展还 可以有按如下样式命名的次级控制文件: extension.control。 如果提供了这些文件,它们必须位于脚本文件目录中。次级控制文件遵循与主 控制文件相同的格式。在安装或更新到该扩展的相应版本时,次级控制文件中 设置的任何参数都会覆盖主控制文件中的设置。不过, extension--version.controldirectory 和 default_version 这两个参数不能在次级控制文件中设置。
扩展的 SQL 脚本文件可以包含任何 SQL 命令,但事务控 制命令(BEGIN、COMMIT 等)和无 法在事务块内执行的命令(例如 VACUUM)除外。这是因 为脚本文件会被隐式地放在事务块中执行。
扩展的 SQL 脚本文件也可以包含以 \echo 开头的行,扩展机制会忽略这些行(将其视为注 释)。这一约定通常用于在脚本文件被直接交给 psql 而不是通过 CREATE EXTENSION 装载时抛出错误(见 Section 36.17.7 中的示例脚本)。如果没有 这种机制,用户可能会意外地把扩展内容作为“松散”对象装载, 而不是作为一个扩展来装载,这种状态恢复起来会有些麻烦。
如果扩展脚本中包含字符串 @extowner@,该字符串会被 替换为调用 CREATE EXTENSION 或 ALTER EXTENSION 的用户名称(会作适当引用)。通常, 这个特性被标记为 trusted 的扩展用来把选定对象的 所有权赋给调用用户, 而不是赋给引导超级用户。(不过这样做时应当谨慎。例如,把一个 C 语言 函数的所有权赋给非超级用户,会为该用户创建一条权限提升路径。)
虽然脚本文件可以包含指定编码允许的任意字符,但控制文件应只包含纯 ASCII 字符,因为 PostgreSQL 无法知道控制文 件采用的是什么编码。在实践中,只有当你想在扩展注释中使用非 ASCII 字 符时这才会成为问题。对此推荐的做法是不要使用控制文件中的 comment 参数,而是在脚本文件中使用 COMMENT ON EXTENSION 来设置注释。
用户经常希望把扩展中的对象装载到与扩展作者原先设想不同的模式中。对 于这种可重定位性,支持三个级别:
完全可重定位的扩展可以在任何时候移动到另一个模式中,即使它已经被装 载到数据库之后也是如此。这通过 ALTER EXTENSION SET SCHEMA 命令完成,该命令会自 动把所有成员对象重命名到新模式中。通常,只有当扩展对其任何对象所在 模式都没有内部假设时,才有可能做到这一点。此外,扩展的对象一开始必 须全部位于同一个模式中(不属于任何模式的对象,如过程语言,不算在 内)。要把一个扩展标记为完全可重定位,只需在其控制文件中设置 relocatable = true。
扩展可能在安装期间可重定位,但安装之后不可重定位。如果扩展脚本文件 需要显式引用目标模式,例如为 SQL 函数设置 search_path 属性时,通常就是这种情况。对于这样的 扩展,应在控制文件中设置 relocatable = false,并在脚本文件中使用 @extschema@ 来引用目标模式。在脚本执行前,该字符 串的每次出现都会被替换为实际目标模式的名称(必要时加双引号)。用户 可以通过 CREATE EXTENSION 的 SCHEMA 选项设置目标模式。
如果扩展完全不支持重定位,应在控制文件中设置 relocatable = false,并把 schema 设置为预定目标模式的名称。这样将阻止使用 CREATE EXTENSION 的 SCHEMA 选项,除非它指定的正是控制文件中命名的那个模式。如果扩展对其模式名 称有无法通过 @extschema@ 替换解决的内部假设,通 常就需要采用这种方式。在这种情况下, @extschema@ 替换机制仍然可用,只是由于模式名由控 制文件决定,其用途比较有限。
在所有情况下,脚本文件执行时,其初始 search_path 都会指向目标模式;也就是说, CREATE EXTENSION 做的事情等价于:
SET LOCAL search_path TO @extschema@, pg_temp;
这使得脚本文件创建的对象能够进入目标模式。脚本文件当然也可以修改 search_path,但通常不建议这样做。 CREATE EXTENSION 完成后, search_path 会恢复为先前的设置。
目标模式由控制文件中的 schema 参数决定(如果给出了 该参数);否则由 CREATE EXTENSION 的 SCHEMA 选项决定(如果给出了该选项);否则由当前默 认的对象创建模式决定(也就是调用者的 search_path 中第一个模式)。当使用控制文件中的 schema 参数时, 如果目标模式尚不存在,则会自动创建;而在另外两种情况下,目标模式必须 已经存在。
如果控制文件的 requires 中列出了任何前置扩展,它们 的目标模式会追加到 search_path 的初始设置中,排在 新扩展的目标模式之后。这样新扩展的脚本文件就可以看到这些扩展的对象。
出于安全考虑,pg_temp 在所有情况下都会自动追加到 search_path 的末尾。
虽然不可重定位扩展可以包含分布在多个模式中的对象,但通常仍然希望把所 有供外部使用的对象放在单个模式中,这个模式会被视为该扩展的目标模式。 这种安排与创建依赖扩展时 search_path 的默认设置配 合起来会比较方便。
如果一个扩展引用了属于另一个扩展的对象,建议对这些引用加上模式限定。 做法是在扩展脚本文件中写 @extschema:,其中 name@name 是另一个扩展的名称(它必须列在本扩展的 requires 列表中)。该字符串会被替换为那个扩展目标 模式的名称(必要时加双引号)。虽然这种记法避免了在扩展脚本文件中对模 式名作硬编码假设,但它的使用也可能把另一个扩展的模式名嵌入到本扩展安 装后生成的对象中。(典型情况是 @extschema: 出现 在字符串字面量中,例如函数体或 name@search_path 设置中。 在其他情况下,对象引用会在解析时简化为 OID,不需要后续查找。)如果另 一个扩展的模式名以这种方式被嵌入,那么在本扩展安装完成后,你应当通过 把那个扩展的名称加入本扩展的 no_relocate 列表,阻 止它再被重定位。
有些扩展包含配置表,其中存放的数据可能会在扩展安装后被用户新增或修改。 通常,如果表属于某个扩展,那么 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', '');
可以用这种方式标记任意数量的表或序列。与 serial 或 bigserial 列关联的序列也可以这样标记。
当 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 的第二个参数没 有作用。
更复杂的情况,例如用户可能会修改最初提供的数据行,可以通过在配置表上 创建触发器来处理,以确保被修改的行被正确标记。
你可以再次调用 pg_extension_config_dump 来修改与 配置表关联的过滤条件。(这通常在扩展更新脚本中很有用。)要把某个表标 记为不再是配置表,唯一的方法是通过 ALTER EXTENSION ... DROP TABLE 将它与扩展解除关 联。
注意,这些表之间的外键关系会决定 pg_dump 转储它们的顺序。具体来说, pg_dump 会尝试先转储被引用表,再转储引用表。由于外键关系是在 CREATE EXTENSION 时建立的(那时数据尚未装入这些表中),因此不支持环 状依赖。若存在环状依赖,数据仍会被转储出来,但该转储将无法直接恢复, 需要用户介入处理。
与 serial 或 bigserial 列关联的序列需要被 直接标记,才能转储它们的状态。仅仅标记它们的父关系还不足以达到这一目 的。
扩展机制的一个优点是,它为管理定义扩展对象的 SQL 命令的更新提供了便 利方式。这是通过为每个已发布版本的扩展安装脚本关联一个版本名或版本号 来实现的。此外,如果你希望用户能够把数据库从一个版本动态更新到下一个 版本,就应提供 更新脚本,以完成从一个版本切换 到下一版本所需的修改。更新脚本的名称遵循如下模式: (例如,extension--old_version--target_version.sqlfoo--1.0--1.1.sql 包含把扩展 foo 的 1.0 版修改为 1.1 版所需的命令)。
在有合适更新脚本可用的前提下, ALTER EXTENSION UPDATE 命令可以把已安装的扩展更新 到指定的新版本。更新脚本运行在 CREATE EXTENSION 为安装脚本提供的同一环境中:尤其 是,search_path 的设置方式完全相同,而且脚本创建的 任何新对象都会自动加入扩展中。此外,如果脚本选择删除扩展成员对象,它 们也会自动与扩展解除关联。
如果扩展有次级控制文件,那么用于更新脚本的控制参数就是与该脚本目标 (新)版本相关联的那些参数。
ALTER EXTENSION 能够执行一系列更新脚本文件来完成 请求的更新。例如,如果只有 foo--1.0--1.1.sql 和 foo--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,'--')。
一个存在已久的扩展很可能会有多个版本,因此作者需要为其编写更新脚本。 例如,如果你发布过 foo 扩展的 1.0、1.1 和 1.2 版本,那么就应当有更新脚本 foo--1.0--1.1.sql 和 foo--1.1--1.2.sql。在 PostgreSQL 10 之前,还必须额外创建新的脚本 文件 foo--1.1.sql 和 foo--1.2.sql,用来直接构建更新后的扩展版本;否 则新版本无法被直接安装,只能先安装 1.0 再升级。那 样既繁琐又重复,但现在已经没有必要了,因为 CREATE EXTENSION 可以自动沿着更新链前进。例如,如 果只有 foo--1.0.sql、 foo--1.0--1.1.sql 和 foo--1.1--1.2.sql 这几个脚本文件可用,那么请求安 装 1.2 版时,就会按顺序运行这三个脚本。处理方式与 先安装 1.0 再更新到 1.2 完全相 同。(和 ALTER EXTENSION UPDATE 一样,如果有多条 路径可用,则优先选择最短的一条。)按这种风格组织扩展的脚本文件,可以 减少制作小型更新所需的维护工作量。
如果你对以这种风格维护的扩展使用次级(特定于版本的)控制文件,请记 住:即使某个版本没有独立的安装脚本,它也仍然需要一个控制文件,因为该 控制文件将决定如何执行到该版本的隐式更新。例如,如果 foo--1.0.control 指定了 requires = 'bar',而 foo 的其 他控制文件没有指定,那么当从 1.0 更新到其他版本时, 该扩展对 bar 的依赖就会被去掉。
广泛分发的扩展应尽量少假定其所处数据库的环境。因此,以一种不会被基于 搜索路径的攻击破坏的安全风格来编写扩展所提供的函数,是合适的做法。
将 superuser 属性设为 true 的 扩展,还必须考虑其安装脚本和更新脚本中操作所带来的安全风险。恶意用户并不难创建出特洛伊木 马对象,从而在后续粗心编写的扩展脚本执行时造成破坏,并借此获得超级用 户权限。
如果一个扩展被标记为 trusted,那么安装用户可以自己 选择安装模式,而该用户可能会有意使用不安全的模式,希望借此获得超级用 户权限。因此,从安全角度看,trusted 扩展暴露程度 极高,必须仔细审查其所有脚本命令,确保不存在任何可被利用之处。
关于如何安全地编写函数,建议见下面的 Section 36.17.6.1;关于如何安全地编 写安装脚本,建议见 Section 36.17.6.2。
扩展提供的 SQL 语言函数和 PL 语言函数在执行时会面临基于搜索路径的攻 击风险,因为这些函数是在执行时而不是创建时解析的。
CREATE FUNCTION 参考页中包含了关于如何安全编写 SECURITY DEFINER 函数的建议。对于扩展提供的任意函 数,都最好应用这些技术,因为该函数可能会被高权限用户调用。
如果你无法把 search_path 设置为只包含安全模式,那就 应假定每个非限定名都可能被解析为恶意用户定义的对象。要警惕那些会隐 式依赖 search_path 的构造;例如, IN 和 CASE 总是通过搜索路径来选择操作符。应改用 expression WHENOPERATOR( 和 schema.=) ANYCASE WHEN 。expression
通用扩展通常不应假定它被安装到了安全模式中,这意味着即使对其自身对象 使用了模式限定引用,也并非完全没有风险。例如,如果扩展定义了函数 myschema.myfunc(bigint),那么像 myschema.myfunc(42) 这样的调用,就可能被恶意函数 myschema.myfunc(integer) 截获。要注意函数和操作符 参数的数据类型必须与声明的实参类型精确匹配,必要时请使用显式类型转 换。
编写扩展安装脚本或更新脚本时,应防范脚本执行过程中发生基于搜索路径的 攻击。如果脚本中的对象引用可能被解析为脚本作者原本无意引用的其他对 象,那么破坏既可能立刻发生,也可能在稍后使用这个定义错误的扩展对象时 才发生。
像 CREATE FUNCTION 和 CREATE OPERATOR CLASS 这样的 DDL 命令通常是安全 的,但要警惕任何把通用表达式作为组成部分的命令。例如, CREATE VIEW 就需要仔细审查, CREATE FUNCTION 中的 DEFAULT 表达式也是如此。
有时扩展脚本可能需要执行通用 SQL,例如为完成 DDL 无法做到的系统目录 调整。执行这类命令时,要确保使用安全的 search_path;不要相信 CREATE/ALTER EXTENSION 提供的路径一定是安全的。 最佳做法是临时把 search_path 设为 pg_catalog, pg_temp,并在需要时显式插入对扩展安装 模式的引用。(这种做法对创建视图也可能有帮助。)在 PostgreSQL 源代码发行包的 contrib 模块中可以找到示例。
安全的跨扩展引用通常要求使用 @extschema: 语法为其 他扩展对象的名称加上模式限定,并且还要仔细匹配函数和操作符的参数类 型。name@
下面给出一个纯 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 FUNCTION pair(text, text)
RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::@extschema@.pair;';
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))::@extschema@.pair;'
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)::@extschema@.pair;';
控制文件 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,其说明见 Section 36.18。执行 make install 命令会把控制文件和脚本文件安装到 pg_config 报告的正确目录中。
文件安装好之后,就可以使用 CREATE EXTENSION 命令把 这些对象装入任何特定数据库中。
如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。