首页 > 数据库 > > 用户自定义函数-C语言函数
用户定义的函数可以使用C编写。这类函数被编译成动态库并且由服务器在需要的时候载入。在一个会话第一次调用一个特定的用户定义函数时,数据库进程会把动态库文件载入到内存中以便该函数被调用。用户在定义C函数的时候必须为该函数指定两块信息:动态库文件的名称,以及要在该动态库中调用的特定的C名称。
数据库支持数字,字符串,时间等多种类型。目前C函数支持的类型,以及各种类型的C类型和SQL类型的对应关系,类型的参数获取以及返回结果的接口,见下表:
SQL类型 | C类型 | 获取参数 | 返回结果 |
|---|---|---|---|
varchar | VarChar* | PG_GETARG_DATUM | PG_RETURN_VARCHAR_P |
text | text* | PG_GETARG_DATUM | PG_RETURN_TEXT_P |
char | BpChar* | PG_GETARG_DATUM | PG_RETURN_BPCHAR_P |
date | DateADT | PG_GETARG_DATUM | PG_RETURN_DATEADT |
timestamp | Timestamp | PG_GETARG_TIMESTAMP | PG_RETURN_TIMESTAMP |
smallint | int16 | PG_GETARG_INT16 | PG_RETURN_INT16 |
integer | int32 | PG_GETARG_INT32 | PG_RETURN_INT32 |
bigint | int64 | PG_GETARG_INT64 | PG_RETURN_INT64 |
对外提供的接口见下表:
函数名 | 功能 |
|---|---|
TextDatumGetCString | 传入text/varchar/bpchar的Datum类型,返回一个char*字符串 |
cstring_to_text | 转换char*字符串到text/varchar类型 |
date_pl_interval | date类型加一个时间间隔 |
timestamp_pl_interval | timestamp类型加一个时间间隔 |
int4_numeric | integer数据转换成numeric类型 |
palloc | 内存申请 |
pfree | 内存释放 |
PointerGetDatum | 指针类型强制转换成Datum类型 |
DirectFunctionCall1 | 调用1个参数的函数 |
DirectFunctionCall2 | 调用2个参数的函数 |
DirectFunctionCall3 | 调用3个参数的函数 |
PG_ARGISNULL(N) | 判断函数的第n个参数是否为NULL |
C语言函数的编写需要遵守基本的规则:
示例1:函数功能,返回两个时间中的较大的,文件名maxtimestamp.c,文件内容如下。
其中,PG_GETARG_TIMESTAMP(0)、PG_GETARG_TIMESTAMP(1)分别获取timestamp类型的第一个参数和第二个参数。PG_ARGISNULL(0)、PG_ARGISNULL(1)返回参数1、参数2是否为NULL。PG_RETURN_TIMESTAMP返回timestamp结果。
#include "postgres.h"
#include "fmgr.h"
#include "utils/timestamp.h"
PG_MODULE_MAGIC;
extern "C" Datum maxdate(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(maxdate);
Datum
maxdate(PG_FUNCTION_ARGS)
{
Timestamp para1 = PG_GETARG_TIMESTAMP();
Timestamp para2 = PG_GETARG_TIMESTAMP(1);
bool arg1_is_null = PG_ARGISNULL();
bool arg2_is_null = PG_ARGISNULL(1);
Timestamp result;
if (arg1_is_null == true && arg2_is_null == true)
{
PG_RETURN_NULL();
}
else if (arg1_is_null == true)
{
PG_RETURN_TIMESTAMP(para2);
}
else if (arg2_is_null == true)
{
PG_RETURN_TIMESTAMP(para1);
}
if (para1 < para2)
{
result = para2;
}
else
{
result = para1;
}
PG_RETURN_TIMESTAMP(result);
}
示例2:函数功能,返回next date,文件名nextdate.c,文件内容如下。
其中,函数timestamp_pl_interval在theDate的基础上增加一个时间间隔spanTime(1天)。
#include "postgres.h"
#include "fmgr.h"
#include "utils/timestamp.h"
PG_MODULE_MAGIC;
extern "C" Datum getLatterDate(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(getLatterDate);
Datum
getLatterDate(PG_FUNCTION_ARGS)
{
Timestamp theDate = PG_GETARG_TIMESTAMP(0);
Timestamp result;
Interval spanTime;
spanTime.time = 0;
spanTime.day = 1;
spanTime.month = 0;
result = DirectFunctionCall2(timestamp_pl_interval,
TimestampGetDatum(theDate),
PointerGetDatum(&spanTime));
PG_RETURN_TIMESTAMP(result);
}
示例3,函数功能,判断字符串中的内容是否全是数字,文件名isNumber.c,文件内容如下。其中,PG_GETARG_DATUM(0) 获取text类型的参数。TextDatumGetCString将参数转换为char*字符串。
#include "postgres.h"
#include "fmgr.h"
#include "utils/builtins.h"
PG_MODULE_MAGIC;
extern "C" Datum ISNUMBER(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(ISNUMBER);
Datum
ISNUMBER (PG_FUNCTION_ARGS)
{
Datum source = PG_GETARG_DATUM();
char *q = TextDatumGetCString(source);
int len = strlen(q);
for (int i = ; i < len; i++)
{
if (q[i] >= '0' && q[i] <= '9')
{
continue;
}
else
{
return ;
}
}
return 1;
}
在使用用户定义的C代码之前,必须编译链接生成一个能被服务器动态载入的文件。确切的说,需要生成一个共享库文件。
首先源文件被编译成一个目标文件,然后目标文件被连接起来。目标文件需要被创建成position-independent code (PIC),这意味着当它们被载入时,可以被放置在内存中的任意位置。
下面例子中,我们以文件isNumber.c为例,并且创建一个共享库isNumber.so。
Linux创建PIC的编译器标志是-fpic。在不同平台上的,需要参考GCC手册。创建一个共享库的编译器标志是-shared。一个完整的例子:
gcc -fpic -c isNumber.c -I include/postgresql/server/
gcc -shared -o isNumber.so isNumber.o
上述命令也可以连在一起:
gcc -shared -fpic -o isNumber.so isNumber.cpp -I include/postgresql/server/其中include/postgresql/server/ 为服务器对外发布的头文件路径,在安装目录下面。
gcc版本要求在5.4.0或者5.4.0之上。
以ISNUMBER为例:
create or replace function isnumber(text)
returns integer
as '...../isNumber.so', 'ISNUMBER'
language c strict fenced IMMUTABLE SHIIPABLE;
对于STABLE/VOLATILE类型的函数,仅当函数的属性是SHIPPABLE的时候,函数可以下推到DN执行。
函数属性在create function章节会有详细介绍。
说明:来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69959250/viewspace-3124784/ ,如需转载,请注明出处,否则将追究法律责任。