12.5. C 语言函数 #
用户自定义函数可以用 C(或可与 C 兼容的语言,如 C++)编写。这样的函数被编译成动态可装载对象(也称共享库),由服务器按需装载。动态装载特性正是“C 语言”函数与“internal”函数的区别所在——两者的实际编码约定基本相同。(因此,标准的内部函数库是用户自定义 C 函数编码示例的丰富来源。)
目前 C
函数使用两种不同的调用约定。较新的“版本
1”调用约定通过为函数写一个
PG_FUNCTION_INFO_V1()
宏调用来表明,如下所示。没有这样的宏则表示是旧风格(“版本
0”)函数。无论哪种情况,CREATE FUNCTION
中指定的语言名都是
C。旧风格函数由于可移植性问题和功能欠缺现已弃用,但出于兼容性原因仍受支持。
12.5.1. 动态装载 #
在一个后端会话中,某个可装载对象文件里的用户自定义函数第一次被调用时,动态装载器会把该对象文件装载到内存中,以便能够调用该函数。因此,用户自定义
C
函数的
CREATE FUNCTION
必须为函数指定两项信息:可装载对象文件的名称,以及该对象文件内要调用的特定函数的
C
名称(链接符号)。如果没有显式指定
C
名称,则假定它与
SQL
函数名相同。
下面的算法被用来基于CREATE FUNCTION
命令中给定的名称来定位共享目标文件:
如果这一序列不成功,就会把平台特定的共享库文件名扩展(通常是
.so)追加到给定名称后并再试这一序列。如果仍然失败,装载就会失败。
注意
运行 PostgreSQL 服务器的用户 ID 必须能够遍历到你要装载的文件的路径。把文件或更高层目录变得对 “postgres” 用户不可读和/或不可执行是一个常见错误。
在任何情况下,CREATE FUNCTION命令中给定的文件名会被原封不动地记录在系统目录中,这样如果需要再次载入该文件则会应用同样的过程。
注意
PostgreSQL
不会自动编译
C
函数。对象文件必须在被
CREATE
FUNCTION 命令引用之前编译好。更多信息见第 12.5.7 节。
注意
动态装载的对象文件在第一次使用后会保留在内存中。同一会话中以后对该文件中函数的调用只会产生符号表查找这一很小的开销。如果你需要强制重新装载一个对象文件(例如重新编译之后),可以使用
LOAD
命令或开始一个新会话。
建议通过相对于 $libdir 的路径,或者通过动态库路径来定位共享库。这样一来,如果新安装位于不同的位置,版本升级会更简单。$libdir 实际代表的目录可以通过命令 pg_config --pkglibdir 查出。
注意
在
PostgreSQL
7.2
版之前,CREATE
FUNCTION 中只能指定对象文件的精确绝对路径。这种做法现已弃用,因为它使函数定义产生不必要的不可移植性。最好只指定不带路径和扩展名的共享库名,让搜索机制去提供那些信息。
12.5.2. C 语言函数中的基础类型
表 12.1给出了将要装载到
PostgreSQL
中的 C
函数的参数所需的
C
类型。“定义于”列给出为获得类型定义而需要包含的头文件。(实际定义可能在所列文件包含的另一个文件中。建议用户坚持使用已定义的接口。)注意,你应该始终包含
postgres.h
放在任何源文件的最前面,因为它声明了许多你反正需要的东西。
表 12.1. 内置 PostgreSQL 类型的等价 C 类型
| SQL 类型 | C 类型 | 定义于 |
|---|---|---|
abstime | AbsoluteTime | utils/nabstime.h |
boolean | bool | postgres.h(可能是编译器内置) |
box | BOX* | utils/geo_decls.h |
bytea | bytea* | postgres.h |
"char" | char | (编译器内置) |
character | BpChar* | postgres.h |
cid | CommandId | postgres.h |
date | DateADT | utils/date.h |
smallint (int2) | int2 或 int16 | postgres.h |
int2vector | int2vector* | postgres.h |
integer (int4) | int4 或 int32 | postgres.h |
real (float4) | float4* | postgres.h |
double precision (float8) | float8* | postgres.h |
interval | Interval* | utils/timestamp.h |
lseg | LSEG* | utils/geo_decls.h |
name | Name | postgres.h |
oid | Oid | postgres.h |
oidvector | oidvector* | postgres.h |
path | PATH* | utils/geo_decls.h |
point | POINT* | utils/geo_decls.h |
regproc | regproc | postgres.h |
reltime | RelativeTime | utils/nabstime.h |
text | text* | postgres.h |
tid | ItemPointer | storage/itemptr.h |
time | TimeADT | utils/date.h |
time with time zone | TimeTzADT | utils/date.h |
timestamp | Timestamp* | utils/timestamp.h |
tinterval | TimeInterval | utils/nabstime.h |
varchar | VarChar* | postgres.h |
xid | TransactionId | postgres.h |
在内部,PostgreSQL 把基本类型视为一个“内存块”。你针对某个类型定义的用户自定义函数转而定义了 PostgreSQL 能对它进行操作的方式。也就是说,PostgreSQL 只负责在磁盘上存储和检索数据,而使用你的用户自定义函数来输入、处理和输出数据。基本类型可以有以下三种内部格式之一:
传值,定长
传引用,定长
传引用,变长
传值类型的长度只能是
1、2 或
4
字节(如果你的机器上
sizeof(Datum)
为 8,也可以是
8
字节)。你应当小心地定义类型,使其在所有体系结构上具有相同的大小(以字节计)。例如,long
类型是危险的,因为它在某些机器上是
4
字节而在另一些机器上是
8
字节;而
int
类型在大多数
Unix
机器上是 4
字节。Unix
上 int4
类型的合理实现可以是:
/* 4-byte integer, passed by value */ typedef int int4;
PostgreSQL 会自动把事情安排好,使整数类型真正具有其宣称的大小。
另一方面,任意尺寸的定长类型都可以通过引用传递。例如,这里有一种 PostgreSQL类型的实现示例:
/* 16-byte structure, passed by reference */
typedef struct
{
double x, y;
} Point;
在
PostgreSQL
函数中传入和传出这类类型时只能使用指向它们的指针。要返回这种类型的值,用
palloc()
分配适量内存,填充所分配的内存,并返回指向它的指针。(或者,你也可以通过返回同类型输入值的指针来返回该输入值。但绝不要修改传引用输入值的内容。)
最后,所有变长类型也必须以传引用方式传递。所有变长类型都必须以一个恰好 4 字节的长度字段开始,而该类型中要存储的所有数据都位于紧随该长度字段之后的内存中。长度字段是结构的总长度(即它包含长度字段本身的大小)。我们可以这样定义 text 类型:
typedef struct {
int4 length;
char data[1];
} text;
显然,这里声明的 data 字段不足以容纳所有可能的字符串。由于在 C 中无法声明变大小的结构,我们依赖这样一个知识:C 编译器不会对数组下标做范围检查。我们只分配所需数量的空间,然后就像数组声明了正确长度一样去访问它。(如果你对这个技巧不熟悉,可能需要先花些时间读一本 C 编程入门教科书,再深入研究 PostgreSQL 服务器编程。)操作变长类型时,我们必须小心分配正确数量的内存并正确设置长度字段。例如,如果我们想在 text 结构中存储 40 字节,可以使用如下代码片段:
#include "postgres.h" ... char buffer[40]; /* our source data */ ... text *destination = (text *) palloc(VARHDRSZ + 40); destination->length = VARHDRSZ + 40; memcpy(destination->data, buffer, 40); ...
VARHDRSZ 和 sizeof(int4)
一样,但是用宏 VARHDRSZ 来引用变长类型的额外开销的尺寸被认为是比较好的风格。
了解了基础类型所有可能的结构后,就可以看一些实际函数示例。
12.5.3. C 语言函数的版本 0 调用约定
我们先介绍“旧风格”调用约定——虽然此方法现已弃用,但初学者更容易上手。在版本 0 方法中,C 函数的参数和结果就按普通 C 风格声明,只是要小心使用上所示的每种 SQL 数据类型的 C 表示。
下面是一些示例:
#include "postgres.h"
#include <string.h>
/* By Value */
int
add_one(int arg)
{
return arg + 1;
}
/* By Reference, Fixed Length */
float8 *
add_one_float8(float8 *arg)
{
float8 *result = (float8 *) palloc(sizeof(float8));
*result = *arg + 1.0;
return result;
}
Point *
makepoint(Point *pointx, Point *pointy)
{
Point *new_point = (Point *) palloc(sizeof(Point));
new_point->x = pointx->x;
new_point->y = pointy->y;
return new_point;
}
/* By Reference, Variable Length */
text *
copytext(text *t)
{
/*
* VARSIZE is the total size of the struct in bytes.
*/
text *new_t = (text *) palloc(VARSIZE(t));
VARATT_SIZEP(new_t) = VARSIZE(t);
/*
* VARDATA is a pointer to the data region of the struct.
*/
memcpy((void *) VARDATA(new_t), /* destination */
(void *) VARDATA(t), /* source */
VARSIZE(t)-VARHDRSZ); /* how many bytes */
return new_t;
}
text *
concat_text(text *arg1, text *arg2)
{
int32 new_text_size = VARSIZE(arg1) + VARSIZE(arg2) - VARHDRSZ;
text *new_text = (text *) palloc(new_text_size);
VARATT_SIZEP(new_text) = new_text_size;
memcpy(VARDATA(new_text), VARDATA(arg1), VARSIZE(arg1)-VARHDRSZ);
memcpy(VARDATA(new_text) + (VARSIZE(arg1)-VARHDRSZ),
VARDATA(arg2), VARSIZE(arg2)-VARHDRSZ);
return new_text;
}
假定上述代码已经写在文件funcs.c中并编译为共享对象,我们可以用类似下面的命令向
PostgreSQL定义这些函数:
CREATE FUNCTION add_one(int4) RETURNS int4
AS 'PGROOT/tutorial/funcs' LANGUAGE C
WITH (isStrict);
-- note overloading of SQL function name add_one()
CREATE FUNCTION add_one(float8) RETURNS float8
AS 'PGROOT/tutorial/funcs',
'add_one_float8'
LANGUAGE C WITH (isStrict);
CREATE FUNCTION makepoint(point, point) RETURNS point
AS 'PGROOT/tutorial/funcs' LANGUAGE C
WITH (isStrict);
CREATE FUNCTION copytext(text) RETURNS text
AS 'PGROOT/tutorial/funcs' LANGUAGE C
WITH (isStrict);
CREATE FUNCTION concat_text(text, text) RETURNS text
AS 'PGROOT/tutorial/funcs' LANGUAGE C
WITH (isStrict);
这里
PGROOT
表示
PostgreSQL
源码树的全路径。(更好的风格是在把
PGROOT/tutorial
加入搜索路径之后,在
AS
子句中只写
'funcs'。无论哪种情况,我们都可以省略共享库的系统特定扩展名,通常是
.so 或
.sl。)
注意我们把这些函数指定为“严格”的,意思是如果任何输入值为 NULL,系统就应自动假定结果为 NULL。这样做就避免了在函数代码中检查 NULL 输入。不这样做的话,我们就得显式检查 NULL,例如为每个传引用参数检查空指针。(对传值参数,我们甚至没有办法检查!)
虽然这个调用约定使用简单,但可移植性不好;在某些体系结构上,以这种方式传递小于 int 的数据类型存在问题。此外,也没有返回 NULL 结果的简单方法,除了把函数声明为严格之外也无法以任何方式处理 NULL 参数。接下来介绍的版本 1 约定克服了这些缺点。
12.5.4. C 语言函数的版本 1 调用约定
版本-1 的调用约定依赖于宏来屏蔽传递参数和结果的大部分复杂性。版本-1 函数的 C 声明总是:
Datum funcname(PG_FUNCTION_ARGS)
此外,宏调用:
PG_FUNCTION_INFO_V1(funcname);
必须出现在同一个源文件中(按惯例写在函数本身之前)。对
internal
语言的函数不需要此宏调用,因为
PostgreSQL
目前假定所有内部函数都是版本
1。但对动态装载的函数它是必需的。
在版本
1
函数中,每个实际参数用与该参数数据类型对应的
PG_GETARG_
宏取得,结果用与返回类型对应的
xxx()PG_RETURN_
宏返回。
xxx()
下面我们展示与上面相同的函数,以版本 1 风格编码:
#include "postgres.h"
#include <string.h>
#include "fmgr.h"
/* By Value */
PG_FUNCTION_INFO_V1(add_one);
Datum
add_one(PG_FUNCTION_ARGS)
{
int32 arg = PG_GETARG_INT32(0);
PG_RETURN_INT32(arg + 1);
}
/* By Reference, Fixed Length */
PG_FUNCTION_INFO_V1(add_one_float8);
Datum
add_one_float8(PG_FUNCTION_ARGS)
{
/* The macros for FLOAT8 hide its pass-by-reference nature */
float8 arg = PG_GETARG_FLOAT8(0);
PG_RETURN_FLOAT8(arg + 1.0);
}
PG_FUNCTION_INFO_V1(makepoint);
Datum
makepoint(PG_FUNCTION_ARGS)
{
/* Here, the pass-by-reference nature of Point is not hidden */
Point *pointx = PG_GETARG_POINT_P(0);
Point *pointy = PG_GETARG_POINT_P(1);
Point *new_point = (Point *) palloc(sizeof(Point));
new_point->x = pointx->x;
new_point->y = pointy->y;
PG_RETURN_POINT_P(new_point);
}
/* By Reference, Variable Length */
PG_FUNCTION_INFO_V1(copytext);
Datum
copytext(PG_FUNCTION_ARGS)
{
text *t = PG_GETARG_TEXT_P(0);
/*
* VARSIZE is the total size of the struct in bytes.
*/
text *new_t = (text *) palloc(VARSIZE(t));
VARATT_SIZEP(new_t) = VARSIZE(t);
/*
* VARDATA is a pointer to the data region of the struct.
*/
memcpy((void *) VARDATA(new_t), /* destination */
(void *) VARDATA(t), /* source */
VARSIZE(t)-VARHDRSZ); /* how many bytes */
PG_RETURN_TEXT_P(new_t);
}
PG_FUNCTION_INFO_V1(concat_text);
Datum
concat_text(PG_FUNCTION_ARGS)
{
text *arg1 = PG_GETARG_TEXT_P(0);
text *arg2 = PG_GETARG_TEXT_P(1);
int32 new_text_size = VARSIZE(arg1) + VARSIZE(arg2) - VARHDRSZ;
text *new_text = (text *) palloc(new_text_size);
VARATT_SIZEP(new_text) = new_text_size;
memcpy(VARDATA(new_text), VARDATA(arg1), VARSIZE(arg1)-VARHDRSZ);
memcpy(VARDATA(new_text) + (VARSIZE(arg1)-VARHDRSZ),
VARDATA(arg2), VARSIZE(arg2)-VARHDRSZ);
PG_RETURN_TEXT_P(new_text);
}
这些函数的CREATE FUNCTION命令与版本 0 的等价形式相同。
乍一看,版本
1
编码约定可能显得像无谓的故弄玄虚。但它们确实提供了不少改进,因为宏可以隐藏不必要的细节。例如,在编写
add_one_float8
时,我们不再需要知道
float8
是传引用类型。另一个例子是,变长类型的
GETARG
宏隐藏了处理获取“经
TOAST
处理”(压缩或行外存储)值的需要。上面所示的旧风格
copytext
和
concat_text
函数在存在经
TOAST
处理的值时实际上是错误的,因为它们没有对其输入调用
pg_detoast_datum()。(旧风格动态装载函数的处理器目前会处理这一细节,但效率低于版本
1
函数所能达到的水平。)
版本 1
函数的一大改进是对
NULL
输入和结果的更好处理。宏
PG_ARGISNULL(
允许函数测试每个输入是否为
NULL(当然,只有在未声明为“严格”的函数中才有必要这样做)。与
n)PG_GETARG_
宏一样,输入参数从零开始计数。注意,在确认参数不是
NULL
之前应避免执行
xxx()PG_GETARG_。要返回
NULL
结果,执行
xxx()PG_RETURN_NULL();它在严格和非严格函数中都可用。
版本 1
函数调用约定使得返回“集合”结果、实现触发器函数和过程语言调用处理器成为可能。版本
1
代码也比版本
0
更具可移植性,因为它不违反
ANSI C
对函数调用协议的限制。更多细节见源码发行包中的
src/backend/utils/fmgr/README。
12.5.5. C 语言函数中的复合类型
复合类型没有像
C
结构那样的固定布局。复合类型的实例可能包含空字段。此外,属于某个继承层次的复合类型可能具有与同一继承层次其他成员不同的字段。因此,PostgreSQL
提供了一个从
C
访问复合类型字段的过程式接口。当
PostgreSQL
处理一组行时,每一行都会作为一个
TUPLE
类型的不透明结构传入你的函数。假设我们想编写一个函数来回答查询
SELECT name, c_overpaid(emp, 1500) AS overpaid FROM emp WHERE name = 'Bill' OR name = 'Sam';
在上面的查询中,我们可以把 c_overpaid 定义为:
#include "postgres.h"
#include "executor/executor.h" /* for GetAttributeByName() */
bool
c_overpaid(TupleTableSlot *t, /* the current row of EMP */
int32 limit)
{
bool isnull;
int32 salary;
salary = DatumGetInt32(GetAttributeByName(t, "salary", &isnull));
if (isnull)
return (false);
return salary > limit;
}
/* In version-1 coding, the above would look like this: */
PG_FUNCTION_INFO_V1(c_overpaid);
Datum
c_overpaid(PG_FUNCTION_ARGS)
{
TupleTableSlot *t = (TupleTableSlot *) PG_GETARG_POINTER(0);
int32 limit = PG_GETARG_INT32(1);
bool isnull;
int32 salary;
salary = DatumGetInt32(GetAttributeByName(t, "salary", &isnull));
if (isnull)
PG_RETURN_BOOL(false);
/* Alternatively, we might prefer to do PG_RETURN_NULL() for null salary */
PG_RETURN_BOOL(salary > limit);
}
GetAttributeByName
是
PostgreSQL
的系统函数,返回当前行中的属性。它有三个参数:传给函数的
TupleTableSlot*
类型参数、所需属性的名称,以及一个返回参数(告知该属性是否为空)。GetAttributeByName
返回一个
Datum
值,你可以用适当的
DatumGet
宏把它转换为正确的数据类型。
XXX()
下面的命令让
PostgreSQL
知道
c_overpaid
函数:
CREATE FUNCTION c_overpaid(emp, int4)
RETURNS bool
AS 'PGROOT/tutorial/funcs'
LANGUAGE C;
虽然有办法在 C 函数内构造新行或修改现有行,但这些方法过于复杂,本手册不予讨论。示例请查阅后端源代码。
12.5.6. 编写代码
现在我们转向编写编程语言函数这一更困难的任务。请注意:本手册的这一部分不会使你成为程序员。在尝试为 PostgreSQL 编写 C 函数之前,你必须对 C(包括指针和 malloc 内存管理器的使用)有良好的理解。虽然也许可以把用 C 以外语言编写的函数装载到 PostgreSQL,但这通常很困难(即使在可能的时候),因为其他语言(如 FORTRAN 和 Pascal)往往不遵循与 C 相同的调用约定。也就是说,其他语言在函数之间传递参数和返回值的方式不同。因此,我们将假定你的编程语言函数是用 C 编写的。
构建 C 函数的基本规则如下:
使用
pg_config --includedir-server找出 PostgreSQL 服务器头文件安装在你的系统(或你的用户将要运行的系统)上的位置。此选项是 PostgreSQL 7.2 新增的。对 PostgreSQL 7.1,应使用选项--includedir。(如果遇到未知选项,pg_config会以非零状态退出。)对 7.1 之前的版本你只能猜测,但由于那是在当前调用约定引入之前,你不太可能想支持那些版本。分配内存时,使用 PostgreSQL 的例程
palloc和pfree,而不是相应的 C 库例程malloc和free。palloc分配的内存会在每个事务结束时自动释放,从而防止内存泄漏。总是使用
memset或bzero把结构体的字节清零。有几个例程(如哈希访问方法、哈希连接和排序算法)会计算结构体中原始位的函数。即使你初始化了结构体的所有字段,结构体中仍可能存在若干包含垃圾值的对齐填充字节(结构体中的空洞)。PostgreSQL 的大多数内部类型都在
postgres.h中声明,而函数管理器接口(PG_FUNCTION_ARGS等)位于fmgr.h中,因此至少需要包含这两个文件。出于可移植性考虑,最好把postgres.h放在最前面,先于任何其他系统或用户头文件。包含postgres.h时,也会顺带为你包含elog.h和palloc.h。对象文件中定义的符号名不得相互冲突,也不得与 PostgreSQL 服务器可执行文件中定义的符号冲突。如果收到此类错误消息,你必须重命名你的函数或变量。
编译和链接你的目标代码使其能被动态装载到 PostgreSQL 总是需要特殊的标志。关于如何针对你的特定操作系统进行操作,参见第 12.5.7 节的详细说明。
12.5.7. 编译和链接动态装载的函数 #
在你能够使用以 C 编写的 PostgreSQL 扩展函数之前,必须以特殊方式对它们进行编译和链接,以生成一个可由服务器动态装载的文件。更准确地说,需要创建一个共享库。
若想了解本节未涵盖的信息,你应阅读操作系统的文档,特别是 C 编译器
cc 和链接编辑器 ld 的手册页。此外,PostgreSQL 源代码在
contrib 目录中包含若干可用的示例。不过,如果你依赖这些示例,就会使你的模块依赖于 PostgreSQL
源代码是否可用。
创建共享库通常与链接可执行文件类似:先把源文件编译为目标文件,再把目标文件链接在一起。目标文件需要以位置无关代码(PIC)形式生成。从概念上讲,这意味着当它们被可执行文件装载时,可以放在内存中的任意位置。(面向可执行文件的目标文件通常不会这样编译。)链接共享库的命令中也包含一些特殊标志,用来把它与链接可执行文件的命令区分开来(至少理论上如此,某些系统上的实际做法要丑陋得多)。
在下面的示例中,我们假定你的源代码位于文件 foo.c 中,并将创建共享库 foo.so。除非另有说明,中间目标文件名为 foo.o。共享库可以包含多个目标文件,但这里我们只使用一个。
- BSD/OS
生成 PIC 的编译器选项是
-fpic。创建共享库时使用的链接器选项是-shared。gcc -fpic -c foo.c ld -shared -o foo.so foo.o
这从 BSD/OS 4.0 版起适用。
- FreeBSD
生成 PIC 的编译器选项是
-fpic。创建共享库时使用的编译器选项是-shared。gcc -fpic -c foo.c gcc -shared -o foo.so foo.o
这从 FreeBSD 3.0 版起适用。
- HP-UX
生成 PIC 的系统编译器选项是
+z。使用 GCC 时则是-fpic。用于共享库的链接器选项是-b。因此:cc +z -c foo.c
or:
gcc -fpic -c foo.c
and then:
ld -b -o foo.sl foo.o
与大多数其他系统不同,HP-UX 使用
.sl作为共享库扩展名。- IRIX
PIC 是默认行为,无须特殊的编译器选项。生成共享库时使用的链接器选项是
-shared。cc -c foo.c ld -shared -o foo.so foo.o
- Linux
生成 PIC 的编译器选项是
-fpic。在某些平台的某些情况下,如果-fpic不起作用,则必须使用-fPIC。更多信息请参阅 GCC 手册。创建共享库的编译器选项是-shared。完整示例如下:cc -fpic -c foo.c cc -shared -o foo.so foo.o
- NetBSD
生成 PIC 的编译器选项是
-fpic。对于 ELF 系统,使用带-shared选项的编译器来链接共享库。在较旧的非 ELF 系统上,则使用ld -Bshareable。gcc -fpic -c foo.c gcc -shared -o foo.so foo.o
- OpenBSD
生成 PIC 的编译器选项是
-fpic。链接共享库使用ld -Bshareable。gcc -fpic -c foo.c ld -Bshareable -o foo.so foo.o
- Solaris
使用 Sun 编译器时,生成 PIC 的编译器选项是
-KPIC;使用 GCC 时则是-fpic。要链接共享库,两种编译器都使用编译器选项-G,或者在使用 GCC 时改用-shared。cc -KPIC -c foo.c cc -G -o foo.so foo.o
or
gcc -fpic -c foo.c gcc -G -o foo.so foo.o
- Tru64 UNIX
PIC 是默认值,因此编译命令就是常规的那条。链接时需要使用带特殊选项的
ld。cc -c foo.c ld -shared -expect_unresolved '*' -o foo.so foo.o
使用 GCC 代替系统编译器时过程相同;不需要特殊选项。
- UnixWare
生成 PIC 的编译器选项,对于 SCO 编译器是
-K PIC,而-fpic则用于 GCC。链接共享库时,编译器选项对于 SCO 编译器是-G,对于 GCC 则是-shared。cc -K PIC -c foo.c cc -G -o foo.so foo.o
或者
gcc -fpic -c foo.c gcc -shared -o foo.so foo.o
提示
如果你想把扩展模块打包以便广泛分发,应该考虑使用 GNU Libtool 来构建共享库。它把平台差异封装到一个通用而强大的接口之后。严肃的打包还需要考虑库版本管理、符号解析方法以及其他问题。
生成的共享库文件随后就可以装载到 PostgreSQL 中。在向 CREATE FUNCTION 命令指定文件名时,必须给出共享库文件名,而不是中间目标文件名。请注意,系统标准的共享库扩展名(通常是 .so 或 .sl)可以在 CREATE FUNCTION 命令中省略,并且通常也应省略,以获得最佳可移植性。
关于服务器期望在何处找到共享库文件,请回头参见第 12.5.1 节。