使用VC开发Windows平台PostgreSQL数据库扩展
2010-01-25纪洪波崔立业
纪洪波,崔立业
(通化师范学院 计算机科学系,吉林 通化 134002;2.吉电股份四平热电分公司)
PostgreSQL[1]数据库是非常流行的一款企业级开源数据库,其历史悠久、功能丰富,并且几乎可以做到与Oracle数据库的无缝兼容.PostgreSQL数据库有很多为人称道的特点,其中之一就是优越的可编程性.PostgreSQL可以使用类似于Oracle PL/SQL的PL/pgSQL语言进行存储过程、触发器的开发,还提供了具有更强流程控制能力的PL/Perl、PL/Python、PL/Ruby等内置脚本语言进行开发,甚至可以使用C、C++或者Java语言进行开发.
这其中SQL语言开发是扩展数据库的最基本工具,几乎所有的数据库都包含.SQL语言的优点是面向集合、功能丰富、使用简单,非常适合在数据库中作数据处理.但是和其他高级语言相比SQL语言的缺点也是显而易见的,交互性差、控制逻辑支持弱、灵活性不够,特别是和操作系统的交互几乎就是不可能的.所以PostgreSQL提供了对流程控制能力更强编程语言的支持,例如:Perl、PL/Python、PL/Ruby等.但是这些语言无论是运行效率还是与操作系统底层通讯的能力都还是有限的.不过PostgreSQL也想到了这一点,那就是使用C和C++语言来进行功能扩展.PostgreSQL数据库提供了一套非常方便高效的使用C/C++扩展开发方法,很多优秀的扩展都是使用C/C++编写的,例如:PostGIS(地理信息数据扩展)、TSearch或OpenFTS(全文检索扩展)、Slony-I(异步主/从复制方案)和XPath扩展等.
1 开发难点
PostgreSQL官方文档对扩展开发描述非常详细,不过唯独没有给出Windows平台MSVC环境下的开发过程.按照其它平台的描述和C语言标准推断,使用MSVC开发扩展需要严格按照标准C语言进行开发,包含头文件(postgres.h、fmgr.h)和postgres.lib库,使用特定的扩展宏PG_MODULE_MAGIC和PG_FUNCTION_INFO_V1进行开发.然后将C源代码编译成动态链接库(DLL),并将该库放到PostgreSQL的搜索目录中(也可以在安装扩展库时指定全路径),然后使用psql或者pgAdmin等工具运行CREATE FUNCTION SQL命令添加这个函数到数据库中,授权的用户就可以使用这些扩展了.
但是在实施开发过程的时候会发现,C源程序根本无法编译,即使编译也不能被PostgreSQL数据库识别.这是由于PostgreSQL最初并不是为Windows开发,也不是使用MSVC编译的数据库系统.本文讨论如何通过修改PostgreSQL的源代码头文件解决编译问题,以及具体开发步骤和编程方法.
2 开发数据库扩展
(1)设置开发环境.PostgreSQL数据库最初是为Unix操作系统设计的,所以编程风格和符号定义都不是非常符合Windows习惯.在Windows平台上,可以使用GCC的一个移植版本MinGW编译PostgreSQL扩展,虽然这样做比较容易,但编译出来的程序体积一般比较大.本文选择的9.0.1版本的PostgreSQL数据库是由VC2008编译完成的,所以本文讨论的是使用VC2008编译扩展,这样可以达到最好的兼容性.
首先修改Visual Studio的变量设置文件vsvars32.bat.添加PostgreSQL开发使用的头文件和库文件路径到环境变量INCLUDE和LIB中,这样VC编译的时候才可以找到需要的文件.
(2)修改PostgreSQL头文件.PostgreSQL自带开发库及头文件,不过其自带头文件符号定义不是兼容MSVC的动态共享库(动态链接库)的接口标准.编译生成的动态共享库PostgreSQL不能识别其中的功能函数,在阅读其大量源代码之后,笔者发现解决这个问题的方法是对头文件fmgr.h中的两个宏定义进行修改.
修改后这两个宏PG_MODULE_MAGIC[2]和PG_FUNCTION_INFO_V1的原定义程序段如下:
#define PG_MODULE_MAGIC
extern PGDLLEXPORT const Pg_magic_struct *PG_MAGIC_FUNCTION_NAME(void);
PGDLLEXPORT const Pg_magic_struct *
PG_MAGIC_FUNCTION_NAME(void)
{
static const Pg_magic_struct Pg_magic_data = PG_MODULE_MAGIC_DATA;
return &Pg_magic_data;
}
extern int no_such_variable
#define PG_FUNCTION_INFO_V1(funcname)
extern PGDLLEXPORT const Pg_finfo_record * CppConcat(pg_finfo_,funcname)(void);
PGDLLEXPORT const Pg_finfo_record *
CppConcat(pg_finfo_,funcname) (void)
{
static const Pg_finfo_record my_finfo = { 1 };
return &my_finfo;
}
extern int no_such_variable
主要的修改是:在这两个宏定义中添加了PGDLLEXPORT宏(粗斜体)修饰,这个宏的定义如下:
#define PGDLLEXPORT __declspec (dllexport)
其主要作用是将动态共享库中定义的方法暴露给外界调用代码.这样准备工作就做完了.
(3)编写扩展C源文件.PostgreSQL手册中包含了详细的共享库开发介绍,本文提供的程序就出自其中并作少许修改.源文件为c:→pgcex.c,源代码如下:
#include
#include
#include
PG_MODULE_MAGIC;
/* 传递数值 */
PG_FUNCTION_INFO_V1(add_one);
PGDLLEXPORT Datum add_one(PG_FUNCTION_ARGS)
{
int32 arg = PG_GETARG_INT32(0);
PG_RETURN_INT32(arg + 1);
}
/* 传递引用,定长 */
PG_FUNCTION_INFO_V1(add_one_float8);
PGDLLEXPORT Datum add_one_float8(
PG_FUNCTION_ARGS)
{
/* 用于 FLOAT8 的宏,隐藏其传递引用的本质 */
float8 arg = PG_GETARG_FLOAT8(0);
PG_RETURN_FLOAT8(arg + 1.0);
}
PG_FUNCTION_INFO_V1(concat_text);
PGDLLEXPORT 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);
SET_VARSIZE(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);
}
(4)编译C源代码为扩展库.使用如下命令行编译:
C:》cl pgcex.c -Fepgcex.dll -MD -LD /D “WIN32” /DLL postgres.lib
或
C:》cl /c pgcex.c /D “WIN32”
C:》link /DLL pgcex.obj postgres.lib
上述两种方法都是正确的,第一种方法是直接将源代码编译成动态共享库,并且动态共享库的名字为pgcex.dll.-MD是使用MSVCRT.lib创建多线程DLL,-LD指定本次编译任务是创建动态共享库,postgres.lib是本扩展开发必须链接的一个共享库.
3 使用扩展库
(1)安装共享库.打开pgAdmin3,执行如下SQL代码:
CREATE FUNCTION add_one(integer) RETURNS integer
AS 'c:/pgcex', 'add_one'
LANGUAGE C STRICT;
注意:重载了名字为 add_one()的SQL函数
CREATE FUNCTION add_one(double precision) RETURNS double precision
AS 'c:/pgcex', 'add_one_float8'
LANGUAGE C STRICT;
CREATE FUNCTION concat(text, text) RETURNS text
AS 'c:/pgcex', 'concat_text'
LANGUAGE C STRICT ;
在该SQL代码中使用了全路径'c:/pgcex'指定编译的扩展库pgcex.dll的位置,这样PostgreSQL可以找到并加载扩展库.当然也可以将动态共享库放到PostgreSQL的共享库目录中(通常为PostgreSQL安装目录中的lib子目录),则在SQL语句中就可以直接使用pgcex而不需要指定路径.
(2)执行扩展函数.执行扩展函数使用如下SQL语句:
select add_one(3);
select add_one(5.0);
select concat('hello','world');
执行结果如图1.
(3)删除共享函数.在PostgreSQL数据库中卸载扩展函数使用如下
SQL语句
DROP FUNCTION add_one(integer);
DROP FUNCTION add_one(double precision);
DROP FUNCTION concat(text, text);
图1 扩展函数执行结果图
以上详细讨论了在Windows平台上使用MSVC开发原生PostgreSQL扩展库的方法.实际工作当中PostgreSQL通常会部署到Linux服务器上,扩展的开发也相对容易.不过要是使其在Windows平台也可用,那本文的内容则是不能忽视的,希望能为各位同行提供一点儿帮助.
参考文献:
[1]Barry Stinson.PostgreSQL必备参考手册[M].人民邮电出版社,2002.
[2]Dynamic C Language Functions-Section 35.9 of the postgreSQL 9.0.1 manual [R/OL].http://www.postgresql.org/docs/9.0/interactive/xfunc-c.html.