From PgsqlWiki
C语言函数
用户定义的函数可以用 C 写(或者是那些可以与 C 兼容的语言,比如 C++)。这样的函数是编译成可动态装载的对象的(也叫做共享库),并且是由服务器根据需要装载的。动态装载的特性是"C 语言"函数和"内部"函数之间相互区别的地方 — 而实际上的两者的编码习惯是一样的。(因此,标准的内部函数库为写用户定义 C 函数提供了大量最好的样例。)
目前对 C 函数有两种调用传统。新的"版本 1"的调用风格是通过为该函数书写一个 PG_FUNCTION_INFO_V1() 宏来标识的,象下面演示的那样。缺少这个宏标识一个老风格的("版本 0")函数。两种情况下里在CREATE FUNCTION里声明的都是语言名字 C。现在老风格的函数已经废弃了,主要是因为移植性原因和缺乏功能,不过出于兼容性原因,系统仍然支持它。
动态装载
当某个可装载对象文件里的用户定义的函数第一次被某个服务器会话调用时,动态装载器把函数的目标文件装载入内存以便调用该函数。因此,给用户定义的 C 函数用的 CREATE FUNCTION 语句必须为函数声明两部分信息:可装载的对象文件名字,和所声明的在那个目标文件里调用的函数的 C 名字(联接符号)。如果没有明确声明C名字,那么就假设它与SQL函数名相同。
基于在 CREATE FUNCTION 命令中给出的名字, 下面的算法用于定位共享对象文件:
- 如果名字是一个绝对路径名,则装载给出的文件。
- 如果名字以字串 $libdir 开头, 那么该部分将被PostgreSQL库目录名代替, 该目录是在制作的时候判定的。
- 如果名字不包含目录部分,那么在配置变量 dynamic_library_path 里声明的路径里查找。
- 否则(没有在路径里找到该文件,或者它包含一个非绝对目录部分),那么动态装载器就会试图拿这个名字来装载,这样几乎可以肯定是要失败的。(依靠当前工作目录是不可靠的。)
如果这个顺序不管用,那么就给这个给出的名字附加上平台相关的共享库文件名扩展(通常是 .so),然后再重新按照上面的过程来一便。如果还是失败,那么装载失败。
我们建议用相对于 $libdir 或者通过动态库路径的方法来定位共享库。这样,在升级的时候,如果新版本在另外一个目录,就可以简化一些工作。$libdir 表示的实际路径可以用命令 pg_config --pkglibdir 找出来。
PostgreSQL 服务器运行时的用户 ID 必须可以遍历路径到达你想装载的文件。一个常见的错误就是把该文件设置为 postgresql 不可读和/或不可执行或者把一个高层目录的权限设置为 postgres 用户不能读和/或执行。
在任何情况下,在 CREATE FUNCTION 命令里给出的文件名在系统表里是以文本值记录的,因此, 如果需要再次装载,那么会再次运行同一个过程。
- 注意:PostgreSQL 不会自动编译一个函数; 在使用 CREATE FUNCTION 命令之前你必须编译它。 参阅 Section 32.9.6 获取更多信息。
为了避免一个动态装载对象装载到不兼容的服务器里,PostgreSQL 检查该文件是否包含一个有合适内容的“magic block”(标识块)。这么做让服务器可以判断明显的不兼容性,比如从不同的主 PostgreSQL 编译来的代码。从 PostgreSQL 8.2 开始就要求“magic block”(标识块)了。要包含一个标识块,在某个(只要一个就行了)模块的源代码文件里,在包含了头文件 fmgr.h 之后,写下面的东西:
#ifdef PG_MODULE_MAGIC PG_MODULE_MAGIC; #endif
如果代码不必在 PostgreSQL 8.2 之前的版本里编译,那么可以忽略 #ifdef 测试。
在第一次使用之后,那么一个动态装载的目标文件就会保持在内存里。在同一次会话中的后继的函数调用将只会产生很小的符号表查询的过热。 如果你需要强制对象文件的重载,比如你重新编译了该文件,那么可以使用LOAD命令或者开始一次新的会话。
另外,一个动态装载的文件可以包含初始化函数和终止函数。如果该文件包含一个叫做 _PG_init 的函数,那么该函数将在装载该文件后立即调用。这个函数不接受任何参数,并且应该返回空。如果这个文件包含一个叫做 _PG_fini 的函数,那么就会在卸载该文件之前运行该函数。这个函数也是不接受任何参数并且应该返回空。请注意 _PG_fini 只是在卸载文件的时候调用,而不是在进程结束的时候。(目前,卸载只是发生在用 LOAD 命令明确重载该文件的时候才会发生。)
基本类型的 C 语言函数
要知道如何写 C 语言的函数,你需要知道 PostgreSQL 在内部是如何表现基本数据类型的,以及它们是如何传入函数以及传出函数的。 PostgreSQL 内部把基本类型当作"一片内存"看待。定义在某种类型上的用户定义函数实际上定义了 PostgreSQL 对(该数据类型)可能的操作。 也就是说,PostgreSQL 只是从磁盘读取和存储该数据类型,而使用你定义的函数来输入,处理和输出数据。基本类型可以有下面三种内部形态(格式)之一:
- 传递数值,定长
- 传递引用,定长
- 传递引用,变长
传递数值的类型的长度只能是1,2 或 4 字节。(还有 8 字节,如果 sizeof(Datum) 在你的机器上是 8 的话)。你要仔细定义你的类型,确保它们在任何体系平台上都是相同尺寸(字节)。例如,long 型是一个危险的类型,因为在一些机器上它是 4 字节而在另外一些机器上是 8 字节,而 int型在大多数 Unix 机器上都是4字节的。在一个 Unix 机器上的 int4 合理的实现可能是:
/* 4-字节整数,传值 */ typedef int integer;
另外,任何尺寸的定长类型都可以是传递引用型。例如,下面是一个 PostgreSQL 类型的实现:
/* 16-字节结构,传递引用 */
typedef struct
{
double x, y;
} Point;
只能使用指向这些类型的指针来在 PostgreSQL 函数里传入和传出数据。要返回这样的类型的值,用 palloc() 分配正确数量的存储器,填充这些存储器,然后返回一个指向它的指针。(另外,如果你只是想返回一个和某个输入参数类型相同、数值相同的一个数值,你可以忽略额外的 palloc,只要返回指向输入数值的指针就行。)
最后,所有变长类型同样也只能通过传递引用的方法来传递。所有变长类型必须以一个正好 4 字节长的长度域开始,并且所有存储在该类型的数据必须放在紧接着长度域的存储空间里。长度域是结构的全长,也就是说,包括长度域本身的长度。
- 警告:绝对不要修改一个传递引用的输入值的内容。 如果你这么干,那么你很可能破坏磁盘上的数据,因为你给拿到指针很可能直接指向一个磁盘缓冲区。 这条规则的唯一例外在 Section 32.10 里。
下一篇:没有了
