↑↓ 选择 ↵ 打开 ⌫ 改范围 完整检索页

pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。

受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
测试与开发版本: 19 / devel
不受支持的版本: 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
历史版本PostgreSQL 8.4 已于 2014 年 7 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本。

第 48 章 编写过程语言调用处理器

凡是不是使用当前针对编译型语言的“版本 1”接口编写的函数,在被调用时都会经过该语言专用的调用处理器函数。这包括用户定义过程语言中的函数、用 SQL 编写的函数,以及使用版本 0 编译语言接口的函数。调用处理器负责以恰当的方式执行该函数,例如解释所提供的源文本。本章概述如何编写新的过程语言调用处理器。

过程语言的调用处理器是一个“普通”函数,必须使用诸如 C 这样的编译型语言、按照版本 1 接口编写,并在 PostgreSQL 中注册为不接受参数且返回 language_handler 类型。这个特殊伪类型会将该函数标识为调用处理器,并阻止它在 SQL 命令中被直接调用。

调用处理器与其他函数的调用方式相同:它接收一个指向 FunctionCallInfoData struct 的指针,其中包含参数值和被调用函数的信息,并应返回一个 Datum 结果(如果要返回 SQL 空值,还需要设置 FunctionCallInfoData 结构体的 isnull 字段)。调用处理器与普通被调用函数的区别在于,FunctionCallInfoData 结构体的 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 本身具有相同的生命周期。不过,处理器也可以选择使用生命周期更长的内存上下文,以便跨查询缓存函数定义信息。

过程语言函数作为触发器调用时,不会按通常方式传入参数,但 FunctionCallInfoData 的 context 字段会指向一个 TriggerData 结构体,而不是像普通函数调用那样为 NULL。语言调用处理器应提供让过程语言函数获取触发器信息的机制。

下面是用 C 编写的过程语言处理器模板:

#include "postgres.h"
#include "executor/spi.h"
#include "commands/trigger.h"
#include "fmgr.h"
#include "access/heapam.h"
#include "utils/syscache.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"

PG_FUNCTION_INFO_V1(plsample_call_handler);

Datum
plsample_call_handler(PG_FUNCTION_ARGS)
{
    Datum          retval;

    if (CALLED_AS_TRIGGER(fcinfo))
    {
        /*
         * 作为触发器过程调用
         */
        TriggerData    *trigdata = (TriggerData *) fcinfo->context;

        retval = ...
    }
    else
    {
        /*
         * 作为函数调用
         */

        retval = ...
    }

    return retval;
}

只需在省略号处添加几千行代码,就能完成这个调用处理器。

将处理器函数编译为可加载模块后(参见 第 34.9.6 节),可用以下命令注册示例过程语言:

CREATE FUNCTION plsample_call_handler() RETURNS language_handler
    AS 'filename'
    LANGUAGE C;
CREATE LANGUAGE plsample
    HANDLER plsample_call_handler;

标准发行版中附带的各过程语言,在尝试编写自己的语言调用处理器时都是很好的参考资料。请查看源码树中的 src/pl 子目录。

提交更正

译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。