除了在编译语言(包括用户定义过程语言中的函数和用 SQL 编写的函数)中编写的函数的调用之外,所有调用的函数都将通过该特定语言的调用处理程序函数执行。调用处理程序负责通过解析提供的源代码等方式,以有意义的方式执行该函数。本章概述了如何编写一个新的过程语言调用处理程序。
过程语言的调用处理程序是一个“正常的”函数,必须使用版本-1 接口在 C 等编译语言中编写,并且必须在 PostgreSQL 中注册,表示不接受参数,并返回类型language_handler
。这种特殊的伪类型将该函数标识为调用处理程序,并防止它在 SQL 命令中被直接调用。有关 C 语言调用约定和动态加载的更多详细信息,请参阅第 36.10 节。
调用处理程序的调用方式与其他任何函数相同:它接收对包含参数值和所调用函数信息的FunctionCallInfoBaseData
struct
的指针,并被期待返回一个Datum
结果(并且可能设置FunctionCallInfoBaseData
结构的isnull
字段,如果想要返回 SQL null 结果)。调用处理程序与普通被调用函数的区别是,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
本身具有相同的使用寿命。但处理程序还可以选择使用更长时间的内存上下文,以便跨查询缓存函数定义信息。
当程序语言函数作为触发器调用时,不会以通常的方式传递任何参数,但 FunctionCallInfoBaseData
的 context
字段指向 TriggerData
结构,而不是像 普通函数调用那样为 NULL
。语言处理程序应提供机制来使程序语言函数获取触发器信息。
用 C 扩展编写的程序语言处理程序的模板在 src/test/modules/plsample
中提供。这是一个工作示例,演示了创建程序语言处理程序、处理参数和返回值的一种方法。
虽然提供调用处理程序足以创建一个最小的程序语言,但也还有两项其他函数可以酌情提供以使该语言使用起来更方便。这两种函数为 验证器 和 内联处理程序。在 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 引用页还有一些有用的详细信息。