2024 年 9 月 26 日: PostgreSQL 17 发布!
支持版本:当前 (17) / 16 / 15 / 14 / 13 / 12
开发版本:开发版
不支持的版本:11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4

第 56 章。编写过程语言处理程序

所有对使用当前语言以外的语言编写的函数的调用(这包括用户定义的过程语言中的函数和用 SQL 编写的函数),都通过针对特定语言的调用处理程序函数进行。调用处理程序负责以有意义的方式执行函数,例如通过解释提供的源文本。本章概述了如何编写新的过程语言的调用处理程序。

过程语言的调用处理程序是一个正常函数,必须使用版本 1 接口用 C 等编译语言编写,并向PostgreSQL注册为不接受任何参数并返回类型language_handler。此特殊伪类型将函数标识为调用处理程序,并阻止它在 SQL 命令中直接调用。有关 C 语言调用约定和动态加载的更多详细信息,请参见第 36.10 节

调用处理程序的调用方式与任何其他函数相同:它接收指向包含参数值和有关被调用函数的信息的FunctionCallInfoBaseDatastruct的指针,并且它应该返回Datum结果(并且如果希望返回 SQL 空值结果,则可能还会设置FunctionCallInfoBaseData结构的isnull字段)。调用处理程序与普通被调用函数之间的区别在于,FunctionCallInfoBaseData结构的flinfo->fn_oid字段将包含要调用的实际函数的 OID,而不是调用处理程序本身的 OID。调用处理程序必须使用此字段来确定要执行哪个函数。此外,传递的参数列表已根据目标函数的声明设置,而不是调用处理程序的声明。

由调用处理程序负责从pg_proc系统目录中获取函数的条目,并分析被调用函数的参数和返回类型。CREATE FUNCTION命令中该函数的AS子句将位于pg_proc行的prosrc列中。这通常是过程语言中的源文本,但在理论上它可以是其他内容,例如文件路径名,或任何其他告诉调用处理程序详细执行操作的内容。

通常,同一个函数在每个 SQL 语句中被调用多次。调用处理程序可以通过使用flinfo->fn_extra字段来避免对有关被调用函数的信息进行重复查找。这最初将是NULL,但可以由调用处理程序设置为指向有关被调用函数的信息。在后续调用中,如果flinfo->fn_extra已非NULL,那么它就可以使用,并且可以跳过信息查找步骤。调用处理程序必须确保flinfo->fn_extra指向至少在当前查询结束之前有效的内存,因为可能保留了FmgrInfo数据结构。一种方法是在由flinfo->fn_mcxt指定的内存上下文中分配额外的内存;此类数据通常具有与FmgrInfo本身相同的生命周期。但处理程序也可以选择使用更长生命周期的内存上下文,以便跨查询缓存函数定义信息。

当过程语言函数被调用为触发器时,不会以通常的方式传递任何参数,但FunctionCallInfoBaseDatacontext字段指向TriggerData结构,而不是像普通函数调用一样是NULL。语言处理程序应该提供机制供过程语言函数获取触发器信息。

src/test/modules/plsample中提供了一个用作 C 扩展编写的过程语言处理程序模板。这是一个工作示例,演示了创建过程语言处理程序、处理参数和返回值的一种方法。

尽管提供调用处理程序足以创建最小的过程语言,但还可以选择提供另外两个函数,以使该语言更方便使用。它们分别是验证器内联处理程序。可以提供验证器以允许在CREATE FUNCTION期间进行语言特定的检查。可以提供内联处理程序以允许该语言支持通过DO命令执行的匿名代码块。

如果过程语言提供了验证器,则必须将其声明为接受一个类型为oid的单个参数的函数。验证器的结果被忽略,因此它通常被声明为返回void。验证器将在创建或更新以过程语言编写的函数的CREATE FUNCTION命令结束时被调用。传递的 OID 是该函数的pg_proc行的 OID。验证器必须以通常的方式获取此行,并执行适当的检查。首先,调用CheckFunctionValidatorAccess()来诊断用户无法通过CREATE FUNCTION实现的对验证器的显式调用。典型的检查包括验证函数的参数和结果类型是否受语言支持,以及函数主体在语言中是否语法正确。如果验证器发现该函数没有问题,它应该只返回。如果它发现错误,它应该通过正常的ereport()错误报告机制报告错误。抛出错误将强制事务回滚,从而防止错误的函数定义被提交。

验证器函数通常应该遵循check_function_bodies参数:如果将其关闭,则应跳过任何昂贵或与上下文相关的检查。如果该语言在编译时提供代码执行功能,则验证器必须抑制会导致此类执行的检查。特别是,此参数被pg_dump关闭,以便它可以加载过程语言函数,而无需担心函数主体对其他数据库对象的副作用或依赖关系。(由于此要求,调用处理程序应该避免假设验证器已完全检查了该函数。使用验证器的目的是不是让调用处理程序省略检查,而是立即通知用户CREATE FUNCTION命令中是否存在明显的错误。)虽然对要检查的确切内容的选择主要留给验证器函数的决定,但请注意,核心CREATE FUNCTION代码仅在check_function_bodies处于打开状态时才执行附加到函数的SET子句。因此,当check_function_bodies处于关闭状态时,应跳过结果可能受 GUC 参数影响的检查,以避免在还原转储时出现错误失败。

如果过程语言提供了内联处理程序,则必须将其声明为接受一个类型为internal的单个参数的函数。内联处理程序的结果被忽略,因此它通常被声明为返回void。当执行指定过程语言的DO语句时,将调用内联处理程序。实际传递的参数是指向InlineCodeBlock结构的指针,该结构包含有关DO语句的参数的信息,特别是将要执行的匿名代码块的文本。内联处理程序应该执行此代码并返回。

建议将所有这些函数声明以及 CREATE LANGUAGE 命令本身都封装到一个 扩展 中,这样只需一个简单的 CREATE EXTENSION 命令即可安装语言。有关编写扩展的信息,请参阅 第 36.17 节

标准发行版中包含的过程语言是编写自己的语言处理程序时的良好参考。请查看源代码树的 src/pl 子目录。 CREATE LANGUAGE 参考页面也有一些有用的详细信息。

提交更正

如果您在文档中发现任何不正确的内容,不符合您对特定功能的体验,或者需要进一步说明,请使用 此表格 报告文档问题。