一个有用的 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
中未指定版本,则将安装此版本)。尽管可以省略此参数,但这会导致在 CREATE EXTENSION
中未出现 VERSION
选项时失败,因此您通常不希望这样做。
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
的用户的名称(适当引用)。通常,此功能由标记为受信任的扩展使用,以便将所选对象的 ownership 分配给调用用户而不是引导超级用户。(但是,应该谨慎这样做。例如,将 C 语言函数的 ownership 分配给非超级用户将为该用户创建权限提升路径。)
虽然脚本文件可以包含指定编码允许的任何字符,但控制文件应仅包含纯 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
,但这通常是不 desirable 的。CREATE EXTENSION
完成后,search_path
将恢复到其先前的设置。
目标模式由控制文件中的 schema
参数确定(如果给出),否则由 CREATE EXTENSION
的 SCHEMA
选项确定(如果给出),否则由当前默认的对象创建模式确定(调用者的 search_path
中的第一个)。当使用控制文件 schema
参数时,如果目标模式不存在,则会创建它,但在其他两种情况下,它必须已经存在。
如果在控制文件的 requires
中列出了任何先决条件扩展,则它们的目标模式将添加到 search_path
的初始设置中,位于新扩展的目标模式之后。这允许新扩展的脚本文件看到它们的对象。
出于安全考虑,在所有情况下,pg_temp
都会自动附加到 search_path
的末尾。
虽然不可重定位的扩展可以包含分布在多个模式中的对象,但通常 desirable 将所有供外部使用的对象放在单个模式中,该模式被视为扩展的目标模式。这种安排在创建依赖扩展期间可以方便地与 search_path
的默认设置配合使用。
如果扩展引用属于另一个扩展的对象,则建议对这些引用进行模式限定。为此,请在扩展的脚本文件中编写 @extschema:
,其中 name
@name
是另一个扩展的名称(必须在此扩展的 requires
列表中列出)。此字符串将替换为该扩展的目标模式的名称(如果需要,则加双引号)。尽管此表示法避免了需要在扩展的脚本文件中对模式名称进行硬编码假设,但它的使用可能会将另一个扩展的模式名称嵌入到此扩展的已安装对象中。(通常,当 @extschema:
用于字符串文字(例如函数体或 name
@search_path
设置)中时,就会发生这种情况。在其他情况下,对象引用在解析期间会简化为 OID,并且不需要后续查找。)如果另一个扩展的模式名称是这样嵌入的,则应通过将另一个扩展的名称添加到此扩展的 no_relocate
列表中,来防止在安装此扩展后重定位另一个扩展。
一些扩展包含配置表,其中包含用户在安装扩展后可能会添加或更改的数据。通常,如果表是扩展的一部分,则 pg_dump 不会转储表的定义或其内容。但这种行为对于配置表来说是不 desirable 的;用户所做的任何数据更改都需要包含在转储中,否则扩展在转储和还原后将表现不同。
为了解决此问题,扩展的脚本文件可以将其创建的表或序列标记为配置关系,这将导致 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,'--')
。
一个已经存在一段时间的扩展可能会存在多个版本,作者需要为其编写更新脚本。例如,如果你已经发布了版本为 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
的其他控制文件没有指定,则扩展对 bar
的依赖将在从 1.0
更新到其他版本时删除。
广泛分布的扩展应该对其所占据的数据库做很少的假设。因此,以安全的方式编写扩展提供的函数是合适的,这种方式不会受到基于搜索路径的攻击的损害。
将 superuser
属性设置为 true 的扩展还必须考虑其安装和更新脚本中执行的操作的安全隐患。恶意用户创建特洛伊木马对象并不是非常困难,这些对象会损害稍后执行的粗心编写的扩展脚本,从而允许该用户获取超级用户权限。
如果扩展被标记为 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-only 扩展示例,它是一个包含两个元素的复合类型,可以在其名为 “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
命令将对象加载到任何特定数据库中。
如果您在文档中发现任何不正确的内容,与您对特定功能的体验不符或需要进一步说明,请使用此表单报告文档问题。