# 38.17.

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

扩展文件38.17.2.

扩展可重定位性38.17.3.

扩展配置表38.17.4.

扩展更新38.17.5.

使用更新脚本安装扩展38.17.6.

扩展的安全注意事项38.17.7.

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

命令将对象加载到您的数据库中。使用扩展而不是仅仅运行 SQL 脚本将一堆“松散”的对象加载到数据库中的主要优点是 PostgreSQL 将理解扩展的对象是一起的。您可以使用单个删除扩展命令(无需维护单独的“卸载”脚本)。更有用,pg_dump 知道它不应该转储扩展的单个成员对象——它只会包含一个创建扩展转储中的命令,而不是。这极大地简化了向新版本扩展的迁移,新版本可能包含比旧版本更多或不同的对象。

但是请注意,在将此类转储加载到新数据库时,您必须拥有扩展的控件、脚本和其他文件。PostgreSQL 不会让您删除包含在扩展中的单个对象,除非删除整个扩展。此外,虽然您可以更改扩展成员对象的定义(例如,通过创建或替换函数对于函数),请记住修改后的定义不会被 pg 转储_倾倒。如果您同时在扩展的脚本文件中进行相同的更改,这样的更改通常是明智的。(但对于包含配置数据的表格有特殊规定;参见第 38.17.3 节.) 在生产环境中,通常最好创建一个扩展更新脚本来执行对扩展成员对象的更改。

扩展脚本可以对属于扩展的对象设置权限,使用授予撤销陈述。每个对象的最终权限集(如果已设置)将存储在pg_init_privs系统目录。当 pg_使用转储,创建扩展命令将包含在转储中,然后是一组授予撤销将对象的权限设置为进行转储时的权限所必需的语句。

PostgreSQL 目前不支持扩展脚本发布创建政策或者安全标签陈述。这些预计将在创建扩展后设置。扩展对象上的所有 RLS 策略和安全标签都将包含在 pg 创建的转储中_倾倒。

扩展机制还具有打包修改脚本的规定,这些脚本调整包含在扩展中的 SQL 对象的定义。例如,如果 1.1 版本的扩展添加了一个函数,并与 1.0 相比更改了另一个函数的主体,则扩展作者可以提供一个更新脚本这只是这两个变化。这更改扩展更新然后可以使用命令来应用这些更改并跟踪给定数据库中实际安装的扩展版本。

可以作为扩展成员的SQL对象的类型如的说明所示改变分机。值得注意的是,数据库、角色和表空间等数据库集群范围内的对象不能是扩展成员,因为扩展只在一个数据库中已知。(虽然不禁止扩展脚本创建此类对象,但如果它这样做,它们将不会作为扩展的一部分被跟踪。)还请注意,虽然表可以是扩展的成员,但它的附属对象(如索引)并不是扩展的直接成员。另一个重要的观点是,模式可以属于扩展,但反之亦然:扩展本身具有非限定名称,并且不存在于任何模式中。然而,扩展的成员对象只要适合其对象类型,就属于模式。扩展拥有其成员对象所在的架构可能合适,也可能不合适。

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

# 38.17.1.扩展文件

这个创建扩展命令依赖于每个扩展名的控制文件,该文件的名称必须与后缀为的扩展名相同控制,并且必须放置在装置的共享目录/扩展目录还必须至少有一个遵循命名模式的SQL脚本文件*扩大*--*版本*.sql(例如,foo——1.0.sql版本1延长期限)。默认情况下,脚本文件也放置在共享目录/扩展目录但是控制文件可以为脚本文件指定不同的目录。

扩展控制文件的文件格式与postgresql。形态文件,即*参数名称* = *价值*作业,每行一个。作者介绍的空行和注释#是允许的。确保引用任何不是单个单词或数字的值。

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

目录 (一串)

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

默认版本 (一串)

扩展的默认版本(如果中未指定版本,则将安装该扩展)创建扩展).虽然这可以省略,但会导致创建扩展如果没有,就失败版本选项出现,所以您通常不想这样做。

议论 (一串)

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

编码 (一串)

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

模块路径名 (一串)

此参数的值将被替换为模块路径名在脚本文件中。如果未设置,则不会进行替换。通常,此设置为$libdir/*共享库名称*然后模块路径名用于创建函数用于C语言函数的命令,这样脚本文件就不需要硬连接共享库的名称。

要求 (一串)

例如,此扩展所依赖的扩展名列表requires='foo,bar'。必须先安装这些扩展,然后才能安装此扩展。

超级用户 (布尔值)

如果此参数为符合事实的(这是默认设置),只有超级用户才能创建扩展或将其更新为新版本(但也请参见可信的,见下文)。如果设置为错误的,只需要执行安装或更新脚本中的命令所需的权限。这通常应设置为符合事实的如果任何脚本命令需要超级用户权限。(这样的命令无论如何都会失败,但提前给出错误更方便用户。)

可信的 (布尔值)

此参数,如果设置为符合事实的(这不是默认设置),允许一些非超级用户安装具有超级用户开始符合事实的。具体而言,任何人只要创造当前数据库的权限。当用户执行创建扩展不是超级用户,但允许通过此参数进行安装,则安装或更新脚本将作为引导超级用户运行,而不是作为调用用户运行。如果超级用户错误的。通常,对于允许访问仅限超级用户的功能(如文件系统访问)的扩展,不应将此设置为真。此外,将扩展标记为受信任需要付出额外的努力来安全地编写扩展的安装和更新脚本;看见第38.17.6节.

可重新定位 (布尔值)

一个扩展是可重新定位如果可以在初始创建扩展后将其包含的对象移动到不同的模式中。默认值是错误的,即扩建部分不可重新安置。看见第38.17.2节了解更多信息。

模式 (一串)

只能为不可重新定位的扩展设置此参数。它强制将扩展加载到指定的模式中,而不是任何其他模式。这个模式参数仅在最初创建扩展时参考,而不是在扩展更新期间参考。看见第38.17.2节了解更多信息。

除了主控制文件*扩大*.控制,扩展名可以具有样式中命名的辅助控制文件*扩大*--*版本*.控制。如果提供,这些文件必须位于脚本文件目录中。辅助控制文件的格式与主控制文件相同。在安装或更新到该版本的扩展时,辅助控制文件中设置的任何参数都会覆盖主控制文件。然而,参数目录默认版本无法在辅助控制文件中设置。

扩展的SQL脚本文件可以包含任何SQL命令,但事务控制命令除外(开始, 犯罪等)以及无法在事务块内执行的命令(例如真空)。这是因为脚本文件在事务块内隐式执行。

扩展的SQL脚本文件也可以包含以开头的行\回音,将被扩展机制忽略(视为注释)。如果脚本文件被馈送到psql而不是通过创建扩展(参见中的脚本示例。)第38.17.7节)。否则,用户可能会意外地将扩展的内容作为“松散”对象加载,而不是作为扩展加载,这样的情况很难恢复。

如果扩展脚本包含字符串@敲诈者@,则该字符串将替换为调用创建扩展改变分机。通常,此功能由标记为受信任的扩展使用,以将选定对象的所有权分配给调用用户,而不是引导超级用户。(但是,在这样做时应该小心。例如,将C语言函数的所有权分配给非超级用户会为该用户创建权限提升路径。)

虽然脚本文件可以包含指定编码允许的任何字符,但控制文件应该只包含普通ASCII,因为PostgreSQL无法知道控制文件的编码方式。实际上,只有在扩展名的注释中使用非ASCII字符时,这才是一个问题。在这种情况下,建议不要使用控制文件议论参数,但使用关于延期的评论在脚本文件中设置注释。

# 38.17.2.扩展可重定位性

用户通常希望将扩展中包含的对象加载到不同于扩展作者设想的模式中。有三个受支持的可重新定位级别:

  • 完全可重新定位的扩展可以随时移动到另一个模式中,即使它已加载到数据库中。这是通过改变扩展集模式命令,自动将所有成员对象重命名为新架构。通常,只有当扩展不包含任何关于其对象所在模式的内部假设时,这才是可能的。此外,扩展的对象必须都在一个模式中(忽略不属于任何模式的对象,例如过程语言)。通过设置来标记完全可重新定位的扩展可重新定位=真在它的控制文件中。

  • 扩展可以在安装期间重新定位,但不能在安装之后重新定位。如果扩展的脚本文件需要显式引用目标模式(例如在设置中),通常会出现这种情况搜索路径SQL函数的属性。对于这样的扩展,设置可重新定位=错误在其控制文件中,并使用@extschema@以引用脚本文件中的目标架构。在执行脚本之前,此字符串的所有匹配项都将替换为实际目标架构的名称。用户可以使用模式选择创建扩展.

  • 如果扩展完全不支持重新定位,请设置可重新定位=错误在其控制文件中,并设置模式指向预期目标架构的名称。这将防止使用模式选择创建扩展,除非它在控制文件中指定了相同的模式。如果扩展包含关于模式名称的内部假设,而这些假设不能用@extschema@这个@extschema@在这种情况下,替换机制也可用,尽管它的用途有限,因为模式名称由控制文件确定。

    在所有情况下,脚本文件都将使用搜索_路径最初设置为指向目标模式;就是,创建扩展这是否等同于:

SET LOCAL search_path TO @extschema@, pg_temp;

这允许脚本文件创建的对象进入目标模式。脚本文件可以更改搜索路径如果它愿意,但这通常是不可取的。搜索路径完成后恢复到以前的设置创建扩展.

目标模式由模式参数,否则由模式选择创建扩展如果给定了,则为当前默认的对象创建模式(调用者的搜索路径).当控制文件模式参数时,如果目标模式不存在,则将创建它,但在其他两种情况下,它必须已经存在。

如果中列出了任何先决条件扩展要求在控制文件中,它们的目标模式被添加到搜索路径,遵循新扩展的目标架构。这使得它们的对象对新扩展的脚本文件可见。

为了安全起见,pg_温度将自动附加到搜索路径在所有情况下。

尽管不可重定位的扩展可以包含分布在多个模式中的对象,但通常需要将所有用于外部使用的对象放在一个模式中,该模式被视为扩展的目标模式。这样的安排在默认设置为搜索路径在创建依赖扩展时。

# 38.17.3.扩展配置表

一些扩展包括配置表,其中包含用户在安装扩展后可能添加或更改的数据。通常,如果表是扩展的一部分,pg不会转储表的定义或内容_倾倒但这种行为对于配置表来说是不可取的;用户所做的任何数据更改都需要包含在转储中,否则在转储和重新加载后,扩展将表现出不同的行为。

为了解决这个问题,扩展的脚本文件可以将它创建的表或序列标记为配置关系,这将导致_dump将表或序列的内容(而不是其定义)包含在转储中。为此,请调用函数pg_扩展_配置_转储(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', '');

任何数量的表或序列都可以用这种方式标记。与电视连续剧大系列列也可以被标记。

pg_扩展_配置_转储是空字符串,表的全部内容由pg转储_倾倒这通常仅在扩展脚本创建的表最初为空时才正确。如果表中混合了初始数据和用户提供的数据,则pg_扩展_配置_转储提供了一个哪里选择要转储的数据的条件。例如,你可能会这样做

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

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

然后确保标准输入仅在扩展脚本创建的行中为true。

对于序列,第二个参数pg_扩展_配置_转储没有效果。

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

您可以通过调用pg_扩展_配置_转储再一次(这在扩展更新脚本中通常很有用。)将表标记为不再是配置表的唯一方法是使用改变分机。。。升降台.

请注意,这些表之间的外键关系将决定pg转储表的顺序_倾倒具体来说,pg_dump将尝试在引用表之前转储被引用的表。由于外键关系是在创建扩展时(数据加载到表中之前)设置的,因此不支持循环依赖关系。当存在循环依赖关系时,数据仍将被转储,但转储将无法直接恢复,需要用户干预。

电视连续剧大系列列需要直接标记以转储其状态。仅仅标记他们的父系关系是不够的。

# 38.17.4.扩展更新

扩展机制的一个优点是,它提供了方便的方法来管理对定义扩展对象的SQL命令的更新。这是通过将版本名或版本号与扩展安装脚本的每个发布版本相关联来实现的。此外,如果希望用户能够从一个版本到下一个版本动态更新其数据库,则应提供更新脚本这使得从一个版本到下一个版本进行必要的更改。更新脚本的名称遵循以下模式*扩大*--*旧版本*--*目标版本*.sql(例如,foo--1.0--1.1.sql包含修改版本的命令1延长期限变成版本1.1).

如果有合适的更新脚本可用,命令更改扩展更新将已安装的扩展更新到指定的新版本。更新脚本在与之相同的环境中运行创建扩展提供安装脚本:特别是,搜索路径以相同的方式设置,脚本创建的任何新对象都会自动添加到扩展中。此外,如果脚本选择删除扩展成员对象,它们将自动与扩展分离。

如果扩展名具有辅助控制文件,则用于更新脚本的控制参数是与脚本的目标(新)版本关联的参数。

改变分机能够执行一系列更新脚本文件,以实现请求的更新。例如,如果foo--1.0--1.1.sqlfoo--1.1--2.0.sql有空的话,改变分机如果更新到版本,将按顺序应用它们21当前已安装。

PostgreSQL对版本名的属性没有任何假设:例如,它不知道1.1跟随1。它只是匹配可用的版本名,并遵循要求应用最少更新脚本的路径。(版本名实际上可以是任何不包含--或领先或落后-.)

例如,有时提供“降级”脚本很有用foo--1.1--1.0.sql允许还原与版本关联的更改1.1。如果这样做,请小心降级脚本可能会意外应用,因为它会产生较短的路径。危险的情况是,存在一个“快速路径”更新脚本,该脚本跳到几个版本之前,以及一个降级脚本到快速路径的起点。与一次推进一个版本相比,实施降级和快速路径所需的步骤可能更少。如果降级脚本删除任何不可替代的对象,这将产生不希望的结果。

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

SELECT * FROM pg_extension_update_paths('extension_name');

这将显示指定扩展的每对不同的已知版本名,以及从源版本到目标版本的更新路径序列,或无效的如果没有可用的更新路径。路径以文本形式显示,带有--分离器。你可以用regexp_分割_到_数组(路径“--”)如果你喜欢数组格式。

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

一个已经存在一段时间的扩展可能存在于多个版本中,作者需要为其编写更新脚本。例如,如果您发布了版本中的扩展1, 1.11.2,应该有更新脚本foo--1.0--1.1.sqlfoo--1.1--1.2.sql。在PostgreSQL 10之前,还需要创建新的脚本文件foo--1.1.sqlfoo——1.2.sql直接构建较新的扩展版本,否则无法直接安装较新的版本,只能通过安装1然后更新。这是乏味的,重复的,但现在没有必要了,因为创建扩展可以自动跟踪更新链。例如,如果只有脚本文件foo——1.0.sql, foo--1.0--1.1.sqlfoo--1.1--1.2.sql然后可以请求安装版本1.2通过按顺序运行这三个脚本而感到荣幸。处理过程与第一次安装时相同1然后更新到1.2(就像更改扩展更新,如果有多条路径可用,则首选最短路径。)以这种方式安排扩展的脚本文件可以减少生成小更新所需的维护工作量。

如果使用具有此样式维护的扩展名的辅助(特定于版本)控制文件,请记住,每个版本都需要一个控制文件,即使它没有独立的安装脚本,因为该控制文件将决定如何执行对该版本的隐式更新。例如,如果foo——1.0.控制指定requires='bar'但是的其他控制文件没有,扩展名依赖于酒吧从更新时将被删除1到另一个版本。

# 38.17.6.扩展的安全考虑

广泛分布的扩展应该很少考虑它们所占用的数据库。因此,以一种安全的方式编写扩展提供的函数是合适的,这种方式不能被基于搜索路径的攻击破坏。

具有超级用户属性设置为true还必须考虑安装和更新脚本中所采取的操作的安全隐患。对于恶意用户来说,创建特洛伊木马对象并不十分困难,这些对象会影响后来执行一个粗心编写的扩展脚本,从而允许该用户获得超级用户权限。

如果标记了扩展名可信的,则安装用户可以选择其安装模式,他们可能会故意使用不安全的模式,以期获得超级用户权限。因此,从安全角度来看,受信任的扩展是极其公开的,必须仔细检查它的所有脚本命令,以确保不可能有任何妥协。

中提供了有关安全编写函数的建议第38.17.6.1节中提供了有关安全编写安装脚本的建议第38.17.6.2节.

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

扩展提供的SQL语言和PL语言函数在执行时有遭受基于搜索路径的攻击的风险,因为这些函数的解析发生在执行时而不是创建时。

这个创建函数参考页包含关于写作的建议安全定义者功能安全。将这些技术应用于扩展提供的任何函数是一种很好的做法,因为该函数可能会被高权限用户调用。

如果你不能设置搜索路径为了只包含安全模式,假设每个非限定名称都可以解析为恶意用户定义的对象。小心依赖于搜索路径含蓄地例如在里面案件*表示*什么时候始终使用搜索路径选择操作员。在他们的位置上,使用接线员(*模式*有吗当*表示*.

通用扩展通常不应假定它已安装到安全模式中,这意味着即使是对其自身对象的模式限定引用也并非完全没有风险。例如,如果扩展定义了一个函数myschema。myfunc(bigint)然后一个电话,比如myschema。myfunc(42)可能会被敌对组织抓获myschema。myfunc(整数)。请注意,函数和运算符参数的数据类型与声明的参数类型完全匹配,必要时使用显式强制转换。

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

应编写扩展安装或更新脚本,以防止脚本执行时发生基于搜索路径的攻击。如果脚本中的对象引用可以解析为脚本作者想要解析的对象以外的其他对象,则可能会立即发生妥协,或者在使用错误定义的扩展对象时发生妥协。

DDL命令,例如创建函数创建操作符类通常是安全的,但要注意任何将通用表达式作为组件的命令。例如创建视图需要进行审查,一个违约表达创建函数.

有时,扩展脚本可能需要执行通用SQL,例如,进行无法通过DDL进行的目录调整。要小心地以安全的方式执行此类命令搜索路径; 做相信创建/更改扩展为了安全。最佳做法是临时设置搜索路径“pg_目录,pg_临时”并在需要时明确插入对扩展安装模式的引用。(这种做法也可能有助于创建视图。)例子可以在contribPostgreSQL源代码发行版中的模块。

交叉扩展引用很难完全安全,部分原因是不确定另一个扩展位于哪个模式。如果两个扩展都安装在同一个模式中,那么危险就会降低,因为这样一来,在安装时就不能在引用的扩展之前放置恶意对象搜索路径.然而,目前没有任何机制要求这样做。目前,最好的做法是,如果扩展依赖于另一个扩展,就不要将其标记为受信任,除非另一个扩展始终安装在pg_目录.

使用创建或替换函数,但在更新脚本中,该脚本必须更改已知为扩展成员的函数的定义。(其他情况也是如此。)或者替换选项。)使用或者替换不必要的是,不仅存在意外覆盖其他人的函数的风险,而且还会造成安全隐患,因为被覆盖的函数仍将由其原始所有者拥有,后者可以对其进行修改。

# 38.17.7.扩展示例

下面是一个纯SQL扩展的完整示例,这是一个两元素组合类型,可以在其名为“k”和“v”的插槽中存储任何类型的值。非文本值会自动强制为文本进行存储。

脚本文件配对——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 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)

此生成文件依赖于PGXS,如中所述第38.18节.命令制作安装将控件和脚本文件安装到pg报告的正确目录中_配置。

安装文件后,请使用创建扩展命令将对象加载到任何特定数据库中。