注意:在第一次使用之后,一个动态装载的用户函数仍然停留在内存中,因而对该函数的更进一步的调用只是简单的符号表查找。
| 内建类型 | 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 的库文件总是需要一些特殊的标记。参阅 链接动态链接库 获取如何在你的平台上做这件事的详细说明。