一个有用的 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 节。)在生产环境中,通常最好创建扩展更新脚本来执行对扩展成员对象的更改。
扩展脚本可以使用 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
目录中;但是,控制文件可以为脚本文件指定不同的目录。
扩展控制文件的文件格式与 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/
,然后在 C 语言函数的 shared_library_name
CREATE 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。此外,将扩展标记为可信需要付出大量的额外努力来安全地编写扩展的安装和更新脚本;请参阅第 36.17.6 节。
relocatable
( boolean
) #如果可以在初始创建扩展后将其包含的对象移动到不同的模式,则扩展是可重定位的。默认值为 false
,即扩展不可重定位。有关更多信息,请参见第 36.17.2 节。
schema
( string
) #此参数只能为不可重定位的扩展设置。它强制将扩展加载到指定的模式中,而不是其他模式中。仅在初始创建扩展时,而不是在扩展更新期间,才会查询 schema
参数。有关更多信息,请参见第 36.17.2 节。
除了主控制文件
之外,扩展还可以具有以 extension
.control
样式命名的辅助控制文件。如果提供,这些文件必须位于脚本文件目录中。辅助控制文件遵循与主控制文件相同的格式。当安装或更新到该版本的扩展时,在辅助控制文件中设置的任何参数都会覆盖主控制文件。但是,不能在辅助控制文件中设置参数 extension
--version
.controldirectory
和 default_version
。
扩展的SQL脚本文件可以包含任何 SQL 命令,但事务控制命令(BEGIN
、COMMIT
等)和不能在事务块内执行的命令(例如 VACUUM
)除外。这是因为脚本文件在事务块内隐式执行。
扩展的SQL脚本文件还可以包含以 \echo
开头的行,这些行将被扩展机制忽略(视为注释)。此规定通常用于在将脚本文件提供给 psql 而不是通过 CREATE EXTENSION
加载时抛出错误(请参阅第 36.17.7 节中的示例脚本)。如果没有该规定,用户可能会意外地将扩展的内容加载为 “松散” 对象,而不是作为扩展,这种情况恢复起来有点繁琐。
如果扩展脚本包含字符串 @extowner@
,则该字符串将替换为(适当引用的)调用 CREATE EXTENSION
或 ALTER EXTENSION
的用户的名称。通常,此功能由标记为可信的扩展使用,以将选定对象的所有权分配给调用用户而不是引导超级用户。(但是,应该小心地执行此操作。例如,将 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
仅在扩展脚本创建的行中为 true。
对于序列,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,'--')
。
存在一段时间的扩展可能存在多个版本,作者需要为这些版本编写更新脚本。例如,如果你以版本 1.0
、1.1
和 1.2
发布了 foo
扩展,则应该有更新脚本 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
的依赖关系将被删除。
广泛分发的扩展应该对它们所占用的数据库几乎不做任何假设。因此,以安全的方式编写扩展提供的函数是合适的,这种方式不会受到基于搜索路径的攻击的损害。
具有设置为 true 的 superuser
属性的扩展还必须考虑其安装和更新脚本中采取的操作的安全性风险。恶意用户创建特洛伊木马对象来危害稍后执行的粗心编写的扩展脚本并非难事,从而允许该用户获取超级用户权限。
如果扩展被标记为 trusted
,则其安装模式可以由安装用户选择,该用户可能会故意使用不安全的模式,希望获得超级用户权限。因此,受信任的扩展从安全角度来看非常脆弱,必须仔细检查其所有脚本命令,以确保不可能发生任何妥协。
有关安全编写函数的建议在下面的第 36.17.6.1 节中提供,有关安全编写安装脚本的建议在第 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
模块中可以找到示例。
跨扩展引用极难做到完全安全,部分原因是无法确定另一个扩展所在的模式。 如果两个扩展都安装在同一个模式中,那么危险会降低,因为这样恶意对象就无法在安装时的 search_path
中位于被引用的扩展之前。 但是,目前没有机制要求这样做。 目前,最佳实践是,如果一个扩展依赖于另一个扩展,除非另一个扩展始终安装在 pg_catalog
中,否则不要将该扩展标记为受信任。
这里是一个完整的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)::@[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
命令将对象加载到任何特定的数据库中。
如果您在文档中看到任何不正确、与您使用特定功能的经验不符或需要进一步澄清的内容,请使用此表单报告文档问题。