PostgreSQL中添加插件
背景
最近一个客户将SQLServer迁移到PostgreSQL中,性能得到了整体提升并省去每年接近百万的License费用,迁移中用户提出一个需求,需要将以前应用一些加解密的函数封装成PG的函数但是由于之前这些函数引入了C++第三方的动态库我们无法使用数据库中这种函数方式,所以我们通过PG中特有的插件功能来解决。
添加插件流程
文件结构
- 封装好的C++类库
- 对外接口文件
- 由于编译是发现直接调用C++类库与PG源码中宏定义会有重名问题,所以将C++类库在进行一次包装供C进行调用
1 2
| cs_cryptopp.h cs_cryptopp.cpp
|
- PG中插件实现的源文件
源码实现
对外结构文件
- 在cs_cryptopp.cpp文件中我们进行一次封装,调用C++类库中的函数
1 2 3 4 5 6 7 8 9 10 11 12
| int generateecdsakey_wrapper(char ** strPrivate, char ** strPublic) { CryptoPG cry; cry.GenerateECDSAKey(strPrivate, strPublic); return 0; }
#ifdef __cplusplus extern "C" int generateecdsakey_wrapper(char ** strPrivate, char ** strPublic); #endif
|
PG中插件实现文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| //cs_cryptopp_wrapper.c extern int generateecdsakey_wrapper(char ** strPrivate, char ** strPublic); //引入外部声明函数
PG_FUNCTION_INFO_V1(generateecdsakey); //注册函数,此函数是PG中我们需要使用的函数名
PG_MODULE_MAGIC; //必须要添加证明是PG的模块
/* * signmessage * * 生成电子签名信息 */ Datum signmessage(PG_FUNCTION_ARGS){
FuncCallContext *funcctx; TupleDesc tupdesc; CallCtx *myCtx;
if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
myCtx = (CallCtx *) palloc(sizeof(CallCtx)); myCtx->CallTime = 0; tupdesc = CreateTemplateTupleDesc(2, false); TupleDescInitEntry(tupdesc, 1, "Message", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, 2, "Signature", TEXTOID, -1, 0); funcctx->user_fctx = myCtx; funcctx->tuple_desc = BlessTupleDesc(tupdesc); MemoryContextSwitchTo(oldcontext); }
funcctx = SRF_PERCALL_SETUP(); myCtx = funcctx->user_fctx; tupdesc = funcctx->tuple_desc ;
if (myCtx->CallTime == 0) { char* strPGMessage = PG_GETARG_CSTRING(0); //获取函数传入的值 char* strSignature = NULL ; signmessage_wrapper(strPGMessage, &strSignature); //调用对应封装好的函数 HeapTuple tuple = NULL; Datum values[2]; bool isnull[2] = { 0,0 };
//放入返回值 values[0] = CStringGetTextDatum(strPGMessage); values[1] = CStringGetTextDatum(strSignature); free(strSignature); myCtx->CallTime++; tuple = heap_form_tuple(tupdesc, values, isnull); SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple)); //返回 //PostgreSQL中针对返回不同类型有专门宏定义 具体请参考源码src/include/fmgr.h文件 } else { SRF_RETURN_DONE(funcctx);
} }
|
SQL文件编写
编写完对应的代码之后我们还需要添加一个SQL文件,这样在PG中 CREATE EXTENSION会执行对应的SQL语句创建
1 2 3 4 5
| //创建一个函数对应cs_cryptopp_wrapper.c中函数名 CREATE FUNCTION signmessage(cstring) //接受类型定义为cstring RETURNS TABLE (Message text, signature text) //返回值我们定义为一个表因为会返回多列 AS 'MODULE_PATHNAME' LANGUAGE C STRICT;
|
编译安装与使用
编译安装
一般PG的插件我们都会放在源码目录下的contrib/项目名称/ 目录中,进入目录执行:
1 2 3 4 5 6 7
| make //编译 su - postgres //切换到postgres用户 pg_ctl stop //关闭数据库 exit //退回到root用户 make install //安装 su - postgres //切换到postgres用户 pg_ctl start //启动数据库
|
使用
登录pg数据库中执行:
1 2 3 4 5 6 7 8 9
| postgres= postgres= postgres= message | signature
2018-01-01 测试 | )\x06Ū + | \x0E\x05\x0C\x0E + | SBHLc?)!\x0EÒ:A\x03h\x1C\72n\x13={ (1 row)
|
参考链接