注意:在第一次使用之后,一个动态装载的用户函数仍然停留在内存中,因而对该函数的更进一步的调用只是简单的符号表查找。
内建类型 | C 类型 | 定义在 |
---|---|---|
abstime | AbsoluteTime | utils/nabstime.h |
bool | bool | include/c.h |
box | (BOX *) | utils/geo-decls.h |
bytea | (bytea *) | include/postgres.h |
char | char | N/A |
cid | CID | include/postgres.h |
datetime | (DateTime *) | include/c.h or include/postgres.h |
int2 | int2 | include/postgres.h |
int2vector | (int2vector *) | include/postgres.h |
int4 | int4 | include/postgres.h |
float4 | float32 or (float4 *) | include/c.h or include/postgres.h |
float8 | float64 or (float8 *) | include/c.h or include/postgres.h |
lseg | (LSEG *) | include/geo-decls.h |
name | (Name) | include/postgres.h |
oid | oid | include/postgres.h |
oidvector | (oidvector *) | include/postgres.h |
path | (PATH *) | utils/geo-decls.h |
point | (POINT *) | utils/geo-decls.h |
regproc | regproc or REGPROC | include/postgres.h |
reltime | RelativeTime | utils/nabstime.h |
text | (text *) | include/postgres.h |
tid | ItemPointer | storage/itemptr.h |
timespan | (TimeSpan *) | include/c.h or include/postgres.h |
tinterval | TimeInterval | utils/nabstime.h |
uint2 | uint16 | include/c.h |
uint4 | uint32 | include/c.h |
xid | (XID *) | include/postgres.h |
传递引用,定长(pass by reference, fixed-length)
传递引用,变长(pass by reference, variable-length)
/* 4-byte integer, passed by value */ typedef int int4;另外,任何尺寸的定长类型都可以是传递引用型.例如,下面是一个 Postgres 类型的实现:
/* 16-byte structure, passed by reference */ typedef struct { double x, y; } Point;只能使用指向这些类型的指针来在 Postgres 函数里输入和输出.最后,所有变长类型同样也只能通过传递引用的方法来传递.所有变长类型必须以一个4字节长的长度域开始,并且所有存储在该类型的数据必须放在紧接着长度域的存储空间里.长度域是结构的全长(也就是说,包括长度域本身的长度).我们可以用下面方法定义一个 text 类型:
typedef struct { int4 length; char data[1]; } text;显然,上面的数据域不够存储任何可能的字串 -- 在 C 中定义这么个结构是不可能的.当处理变长类型时,我们必须仔细分配正确的存储器数量并初始化长度域.例如,如果我们想在一个 text 结构里存储 40 字节,我们可能会使用象下面的代码片段:
#include "postgres.h" ... char buffer[40]; /* our source data */ ... text *destination = (text *) palloc(VARHDRSZ + 40); destination->length = VARHDRSZ + 40; memmove(destination->data, buffer, 40); ...既然我们已经讨论了基本类型所有的可能结构,我们便可以用实际的函数举一些例子.假设 funcs.c 象下面一样:
#include <string.h> #include "postgres.h" /* By Value */ int add_one(int arg) { return(arg + 1); } /* By Reference, Fixed Length */ 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)); memset(new_t, 0, VARSIZE(t)); VARSIZE(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); memset((void *) new_text, 0, new_text_size); VARSIZE(new_text) = new_text_size; strncpy(VARDATA(new_text), VARDATA(arg1), VARSIZE(arg1)-VARHDRSZ); strncat(VARDATA(new_text), VARDATA(arg2), VARSIZE(arg2)-VARHDRSZ); return (new_text); }在 OSF/1 (平台上)我们要敲入:
CREATE FUNCTION add_one(int4) RETURNS int4 AS 'PGROOT/tutorial/funcs.so' LANGUAGE 'c'; CREATE FUNCTION makepoint(point, point) RETURNS point AS 'PGROOT/tutorial/funcs.so' LANGUAGE 'c'; CREATE FUNCTION concat_text(text, text) RETURNS text AS 'PGROOT/tutorial/funcs.so' LANGUAGE 'c'; CREATE FUNCTION copytext(text) RETURNS text AS 'PGROOT/tutorial/funcs.so' LANGUAGE 'c';On other systems, we might have to make the filename end in .sl (to indicate that it's a shared library).
* 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 instance of EMP */ int4 limit) { bool isnull = false; int4 salary; salary = (int4) GetAttributeByName(t, "salary", &isnull); if (isnull) return (false); return(salary > limit); }GetAttributeByName 是 Postgres 系统函数,用来返回当前记录的字段值.它有三个参数:类型为 TUPLE 的传入函数的参数,需要的字段名称,以及一个用以确定字段是否为空(null)的返回参数指针.GetAttributeByName 会把数据正确的对齐,这样你就可以把返回值转换成合适的类型.例如,如果你有一个字段的名称就是类型名,调用 GetAttributeByName 就会看起来象:
char *str; ... str = (char *) GetAttributeByName(t, "name", &isnull)下面的查询让 Postgres 知道 c_overpaid 函数:
* CREATE FUNCTION c_overpaid(EMP, int4) RETURNS bool AS 'PGROOT/tutorial/obj/funcs.so' LANGUAGE 'c';当然还有其他方法在 C 函数里构造新的记录或修改现有记录,这些方法都太复杂,不适合在本手册里讨论.
以基本类型为参数的 C 函数可以用直接的风格书写。内建的 Postgres 类型的 C 等效物可以通过把文件 PGROOT/src/backend/utils/builtins.h 做为头文件包含到 C 文件里访问。可以向 C 文件的头部增加
#include <utils/builtins.h>这一行实现这些。
制作 C 函数的基本规则如下:
-I$PGROOT/include包括在你的 cc 命令行里.有时候,你可能发现你需要的头文件在服务器源代码里(比如,你需要的文件被忘了装在 include 路径里).在这种情况下,你可能要增加象下面一条或几条
-I$PGROOT/src/backend -I$PGROOT/src/backend/include -I$PGROOT/src/backend/port/<PORTNAME> -I$PGROOT/src/backend/obj(这里 <PORTNAME> 是移植的名称,如 alpha 或 sparc).
当分配存储器时,用 Postgres 的函数 palloc 和 pfree 取代相应的 C 库函数 malloc 和 free.用 palloc 分配的存储器在事务结束时会自动释放,避免了内存泄露.
记得用 memset 或 bzero 对你的结构清零.有些函数(像 hash (散列)访问方法,hash (散列)联合和排序算法)计算包含在你的结构里的裸位(row bit).即使你初始化了你的结构的所有域,仍然有可能有几个对齐字节(结构中的洞)含有垃圾值.
大多数的 Postgres 内部类型定义在 postgres.h,所以始终包含该文件是一个好习惯.包含了 postgres.h 将自动包含 elog.h 和 palloc.h.
把你的目标码编译和装载成可以动态装入 Postgres 的库文件总是需要一些特殊的标记。参阅 链接动态链接库 获取如何在你的平台上做这件事的详细说明。