外部函数

为了能够在创建和使用自定义DMSQL程序时,使用其他语言实现的接口,DM8提供了C、JAVA外部函数,这样即使外部函数在执行中出现了任何问题,都不会影响到服务器的正常执行。

无论是C外部函数还是JAVA外部函数,都需要将动态库或jar包上传到服务器端或在服务器端编译生成动态库或jar包,系统管理员应对动态库和jar包进行严格审查,以防止外部函数中包含病毒或恶意代码,引发安全问题。为了保证数据库的安全性和灵活性,DM提供了ini参数ENABLE_EXTERNAL_CALL来开关外部函数功能,默认情况下,数据库会关闭外部函数的创建和执行功能。

需要注意的是,DM不支持C或JAVA外部函数存放在ASM文件系统上的调用。

10.1 C外部函数

C外部函数是使用C、C++语言编写,在数据库外编译并保存在.dll、.so共享库文件中,被用户通过DMSQL程序调用的函数。

C外部函数的执行都通过代理dmap工具进行,为了执行C外部函数,需要先启动dmap服务。dmap执行程序在DM8安装目录的bin子目录下,直接执行即可启动dmap服务。

当用户调用C外部函数时,服务器操作步骤如下:首先,确定调用的(外部函数使用的)共享库及函数;然后,通知代理进程工作。代理进程装载指定的共享库,并在函数执行后将结果返回给服务器。

10.1.1 生成动态库

DM8提供两种方案编写C外部函数:

  • DM结构化参数

该方案中,用户必须使用DM8提供的编写C外部函数动态库的接口,严格按照如下格式书写外部函数的C代码。

C外部函数格式

de_data 函数名(de_args *args)
{
	C语言函数实现体;
}

参数

  1. <de_data> 返回值类型。de_data结构体类型如下:
struct de_data{
	int  null_flag;   /*参数是否为空,1表示非空,0表示空*/
	union	       /*只能为int、double或char类型*/
    {
		int    v_int;
		double  v_double;
		char   v_str[];
	}data;
};
  1. <de_args> 参数信类型。de_args结构体类型如下:
struct de_args
{
	int        n_args;      /*参数个数*/
    de_data*      args;        /*参数列表*/
};
  1. < C语言函数实现体> C语言函数对应的函数实现体。

使用说明

  1. C语言函数的参数可通过调用DM8提供的一系列get函数得到,同时可调用set函数重新设置这些参数的值;
  2. 根据返回值类型,调用不同的return函数接口;
  3. 必须根据参数类型、返回值类型,调用相同类型的get、set和return函数。当调用de_get_str和de_get_str_with_len得到字符串后,必须调用de_str_free释放空间;
  4. DM8提供的编写C外部函数动态库的接口如表10.1所示。
表10.1 DM8支持的编写C外部函数动态库的接口
函数类型 函数名 功能说明
get int de_get_int(de_args *args, int arg_id); 第arg_id 参数的数据类型为整型,从参数列表args 中取出第arg_id 参数的值,
double de_get_double(de_args *args, int arg_id); 第arg_id 参数的数据类型为double 类型,从参数列表args 中取出第arg_id 参数的值。
char* de_get_str(de_args *args, int arg_id); 第arg_id 参数的数据类型为字符串类型,从参数列表args 中取出第arg_id 参数的值。
char* de_get_str_with_len(de_args *args, int arg_id, int* len); 第arg_id 参数的数据类型为字符串类型,从参数列表args 中取出第arg_id 参数的值以及字符串长度。
set void de_set_int(de_args *args, int arg_id, int ret); 第arg_id 参数的数据类型为整型,设置参数列表args 的第arg_id 参数的值为 class="font7">ret 。
void de_set_double(de_args *args, int arg_id, double ret); 第arg_id 参数的数据类型为double 类型,设置参数列表args 的第arg_id 参数的值为 class="font7">ret 。
void de_set_str(de_args *args, int arg_id, char* ret); 第arg_id 参数的数据类型为字符串类型,设置第arg_id 参数的值为ret 。
void de_set_str_with_len(de_args *args, int arg_id, char* ret, int len); 第arg_id 参数的数据类型为字符串类型,将字符串ret 的前len 个字符赋值给参数列表 class="font7">args 的第arg_id 参数。
void>  de_set_null(de_args *args, int arg_id); 设置参数列表args 的第arg_id 个参数为空。
return de_data de_return_int(int ret); 返回值类型为整型。
de_data de_return_double(double ret); 返回值类型为double 型。
de_data>  de_return_str(char* ret); 返回值为字符串类型。
de_data de_return_str_with_len(char* ret, int len); 返回字符串ret 的前len 个字符。
de_data>  de_return_null(); 返回空值。
de_str_free void de_str_free(char* str); 调用de_get_str 函数后,需要调用此函数释放字符串空间。
de_is_null int de_is_null(de_args *args, int arg_id); 判断参数列表args 的第arg_id 个参数是否为空。

注:参数个数arg_id的起始值为0。

  • 标量类型参数

该方案中,用户不必引用DM提供的外部函数接口,可以按照标准的C风格编码,使用C标量类型作为参数类型。使用该方案编写的C函数,只能在使用X86 CPU的64位非Windows系统中,被数据库引用作为外部函数。

C外部函数格式

返回类型函数名(参数列表)

返回类型函数名(参数列表)
{
	C语言函数实现体;
}

使用说明

  1. 返回类型及参数列表中参数的数据类型只支持int、double以及char*类型;
  2. 参数列表中不支持out型参数;
  3. 如果参数列表中有char*的参数,不必在函数中对其进行释放;为了安全考虑,最好只对其进行只读操作;
  4. 如果返回char*类型,返回值必须使用malloc申请空间,且必须有结尾0,不允许直接返回常量或返回参数列表中传入的字符类型参数。

10.1.2 C外部函数创建

语法格式

CREATE OR REPLACE FUNCTION [<模式名>.]<函数名>[(<参数列表>)]
RETURN <返回值类型>
EXTERNAL '<动态库路径>' [<引用的函数名>] USING < C | CS >;

参数

  1. <函数名> 指明被创建的C外部函数的名字;
  2. <模式名> 指明被创建的C外部函数所属模式的名字,缺省为当前模式名;
  3. <参数列表> 指明C外部函数参数信息,如果是使用DM结构化参数编写的C函数,参数模式可设置为IN、OUT或IN OUT(OUT IN),缺省为IN类型;而使用标量类型参数编写的C函数则参数模式只能是IN类型。参数类型、个数都应和动态库里定义的一致;
  4. <返回值类型> 必须和动态库里定义的一致;
  5. <动态库路径> 用户按照DM规定的C语言函数格式编写的DLL文件生成的动态库所在的路径;动态库分为64位和32位两种,使用的时候要和操作系统一一对应。例如,64位的操作系统要用64位的动态库;
  6. <引用函数名> 指明<函数名>在<动态库路径>中对应的函数名;
  7. USING子句指明函数的类型,如果是DM结构化参数的C函数,类型为C;标量类型参数的C函数,类型为CS。

图例

C外部函数创建

C外部函数创建

语句功能

创建自定义C外部函数。

使用说明

  1. 仅允许DBA创建C外部函数;
  2. <引用函数名>如果为空,则默认与<函数名>相同;
  3. <动态库路径>分为.dll文件(windows)和.so文件(linux)两种;
  4. 外部函数定义的返回值类型与对应C语言函数的返回值类型必须一致,否则会导致返回值不可预测甚至系统异常。

权限

使用该语句的用户必须是DBA或该存储过程的拥有者且具有CREATE FUNCTION数据库权限的用户。

10.1.3 举例说明

例 编写(C语言)外部函数C_CONCAT,用于将两个字符串连接。

可以使用DM结构化参数方案来完成。

生成动态库。

第一步,使用Microsoft Visual Studio 2008创建新项目newp,位于d:\xx\tt文件夹中。在d:\xx\tt\newp\newp文件夹中,直接拷入dmde.lib动态库和de_pub.h头文件。dmde.lib和de_pub.h位于安装文件x:...\dmdbms\include中。

第二步,在newp项目中,添加头文件。将已有de_pub.h头文件添加进来,同时,添加新的tt.h头文件。tt.h文件内容如下:

#include "de_pub.h"
#include "string.h"
#include "stdlib.h"

第三步,在newp项目中,添加源文件。名为tt.c。tt.c内容如下:

#include "tt.h"
de_data C_CONCAT(de_args *args)
{
	de_data  de_ret;
	char* str1;
	char* str2;
	char* str3;
	int len1;
	int len2;

	str1 = (char*)de_get_str(args, 0);					 /*从参数列表中取第0个参数*/
	str2 = (char*)de_get_str_with_len(args, 1, (udint4*)&len2); 		 /*从参数列表中取第1个参数的值以及长度*/
	len1 = strlen(str1);
	str3 = (char*)malloc(len1 + len2);
	memcpy(str3, str1, len1);
	memcpy(str3 + len1, str2, len2);

	de_str_free((sdbyte*)str1); 	/*调用get函数得到字符串之后,需要调用此函数释放字符串空间*/
	de_str_free((sdbyte*)str2);

	de_ret = de_return_str_with_len((udbyte*)str3, len1 + len2);     /*返回字符串*/
	free(str3); 
	return de_ret;
}

第四步,在newp项目的源文件中,添加模块定义文件tt.def,内容如下:

LIBRARY	"tt.dll"
EXPORTS
	C_CONCAT

第五步,在Microsoft Visual Studio 2008界面上,单击项目,找到newp属性,点击打开。在“配置属性—链接器—输入”中添加附加依赖项dmde.lib,在“配置属性—常规”中调整配置类型为动态库(.dll)。

第六步,编译tt项目(设置项目输出文件路径:D:\xx\tt)。得到newp.dll文件。

至此,外部函数的使用环境准备完毕。

其次,创建并使用外部函数。

第一步,启动数据库服务器dmserver,启动disql。(dmserver、disql等工具位于安装目录x:...\dmdbms\bin中)

第二步,在disql中,创建外部函数MY_CONCAT,语句如下:

CREATE OR REPLACE FUNCTION MY_CONCAT(A VARCHAR, B VARCHAR) 
RETURN VARCHAR
EXTERNAL 'd:\xx\tt\newp.dll' C_CONCAT USING C;

第三步,调用C外部函数,语句如下:

select MY_CONCAT ('hello ', 'world!');

第四步,查看结果:

hello world!

也可以使用C标量类型参数方案完成,不过需要注意该方案仅支持使用X86 CPU的64位非Windows系统。

生成动态库,这里改用LINUX操作系统作为示例。

第一步,创建C文件test.c,编写C函数

#include <string.h>
#include "stdlib.h"
char*  C_CONCAT (char*	str1, char* str2)
{
    char* str3;
    int len1;
    int len2;
    len1 = strlen(str1);
    len2 = strlen(str2);
    str3 = (char*)malloc(len1 + len2 + 1);	//要多一个字节作为结尾0
    memcpy(str3, str1, len1);
    memcpy(str3 + len1, str2, len2);
    str3[len1 + len2] = 0;  //必须有结尾0
    return str3;
}

第二步,生成动态库

gcc -o /mnt/libtest.so -fPIC -shared test.c

获得libtest.so文件

至此,外部函数的使用环境准备完毕。

其次,创建并使用外部函数。

第一步,启动数据库服务器dmserver,启动disql。

第二步,在disql中,创建外部函数MY_CONCAT,语句如下:

CREATE OR REPLACE FUNCTION MY_CONCAT(A VARCHAR, B VARCHAR) 
RETURN VARCHAR
EXTERNAL '/mnt/libtest.so' C_CONCAT USING CS;

第三步,调用C外部函数,语句如下:

select MY_CONCAT ('hello ', 'world!');

第四步,查看结果:

hello world!

10.2 JAVA外部函数

JAVA外部函数是使用JAVA语言编写,在数据库外编译生成的jar包,被用户通过DMSQL程序调用的函数。

JAVA外部函数的执行都通过代理dmagent工具进行,为了执行JAVA外部函数,需要先启动dmagent服务。dmagent执行程序在DM8安装目录的tool/dmagent子目录下,其使用说明文档可参看该目录下的《readme》文档。

当用户调用JAVA外部函数时,服务器操作步骤如下:首先,确定调用(外部函数使用的)jar包及函数;然后,通知代理进程工作。代理进程装载指定的jar包,并在函数执行后将结果返回给服务器。

需要注意的是,进行JAVA外部函数调用应保证当前用户可以运行JAVA命令,否则会导致调用失败。

10.2.1 生成jar包

用户必须严格按照JAVA语言的格式书写代码,完成后生成jar包。

10.2.2 JAVA外部函数创建

语法格式

CREATE OR REPLACE FUNCTION [<模式名>.]<函数名>[(<参数列表>)] 
RETURN <返回值类型>
EXTERNAL '<jar包路径>' [<引用的函数名>] USING JAVA;

参数

  1. <函数名> 被创建的JAVA外部函数的名字;
  2. <模式名> 被创建的JAVA外部函数所属模式的名字,缺省为当前模式名;
  3. <参数列表> JAVA外部函数参数信息,参数模式可设置为IN、OUT或IN OUT (OUT IN),缺省为IN类型。参数类型、个数都应和jar包里的一致。目前支持的函数参数类型:Int、字符串(char、varchar、varchar2)、bigint、double。分别对应java类型:Int、string、 long、double;
  4. <返回值类型> 必须jar包里定义的一致;
  5. <jar包路径> 用户按照java语言格式编写的源码生成的jar包,及其所依赖的jar包所在的绝对路径。dmagent方式jar包要使用绝对路径;ap方式jar包名字是不起作用的;
  6. <引用函数名> 指明<函数名>在<jar包路径>中对应的函数名,jar包中的函数应为static类型。若<函数名>包含包名,则包名(或.包名)与类名的分隔符要用.或/。而类与方法的分隔符要用.。

图例

JAVA外部函数创建

JAVA外部函数创建

语句功能

创建自定义JAVA外部函数。

使用说明

  1. 仅允许DBA创建JAVA外部函数;
  2. <引用函数名>书写格式:包名、类名与方法名之间用.进行分隔。

权限

使用该语句的用户必须是DBA或该存储过程的拥有者且具有CREATE FUNCTION数据库权限的用户。

10.2.3 举例说明

例 编写(JAVA语言)外部函数:testAdd 用于求两个数之和,testStr用于在一个字符串后面加上hello。

首先,生成jar包。

第一步,使用eclipse创建新项目newp,位于F:\workspace文件夹中。

第二步,在newp项目中,添加包。右击newp,新建(new)一个package,命名(name)为com.test.package1。

第三步,在package1包中,添加类文件。右击src,新建(new)一个class,命名(name)为test。Modifiers选择public。class文件内容如下:

package com.test.package1;  
public class test {
	public static int testAdd(int a, int b) {
		return a + b;
	}
	public static String testStr(String str) {
		return str + " hello";
	}
}

第四步,生成jar包。在newp项目中,右击,选中EXPORT,选择Java\JAR file,取消.classpath和.project的勾选。目标路径JAR file设置为:E:\test.jar,然后finish。

第五步,查看E盘中test.jar。已经存在。

第六步,在安装目录的..\dmdbms\bin中创建一个external_jar文件夹,将test.jar拷入其中。

至此,外部函数的使用环境准备完毕。

其次,创建并使用外部函数。

第一步,启动数据库服务器dmserver,启动DM管理工具。

第二步,在DM管理工具中,创建外部函数MY_INT和MY_chr,语句如下:

CREATE OR REPLACE FUNCTION MY_INT(a int, b int) 
RETURN int
EXTERNAL '..\dmdbms\bin\external_jar\test.jar' "com.test.package1.test.testAdd" USING java;

CREATE OR REPLACE FUNCTION MY_chr(s varchar) 
RETURN varchar
EXTERNAL '..\dmdbms\bin\external_jar\test.jar' "com.test.package1.test.testStr" USING java;

第三步,调用JAVA外部函数,语句如下:

select MY_INT(1,2);
select MY_chr('abc');

第四步,查看结果,分别为:

3
abc hello

10.3 DMAP使用说明

DMAP(DM Assit progress)作为数据库管理系统的辅助进程,提供外部函数、备份还原等功能的执行。

10.3.1 启动DMAP

安装DM数据库以后,DMAP服务会自动启动。如果需要手动启动,有两种途径:一是启动DM服务查看器中的DmAPService;二是通过手动启动DMAP执行码实现,DMAP执行码位于DM安装目录的bin子目录下。除此之外,LINUX下,还可以调用bin目录下的DmAPService脚本启动DMAP服务。

默认不带参数启动DMAP时,DMAP监听端口号为4236,与DM服务器INI参数EXTERNAL_AP_PORT的默认值一致。

也可以使用参数dmap_ini指定dmap.ini配置文件启动DMAP,如下例所示:

dmap dmap_ini=d:\dmap.ini

其中,dmap.ini可配置dmap的端口号,如下所示:

AP_PORT=4236

需要注意的是,当使用dmap.ini对AP_PORT进行配置时,DM服务器INI参数EXTERNAL_AP_PORT的配置值应与AP_PORT一致。

10.3.2 使用DMAP执行外部函数

例:

CREATE OR REPLACE FUNCTION MY_CONCAT(A VARCHAR, B VARCHAR) 
RETURN VARCHAR
EXTERNAL 'D:\testroot\for_dmserver\smoketest_data\dameng\detest64.dll' C_CAT USING C; 
/
SQL> select MY_CONCAT ('hello ', 'a');

行号       MY_CONCAT('hello','a')
---------- ----------------------
1          hello a

可在log目录下查看dmap.log日志文件,其中记录了执行外部函数时消息收发的日志。

微信扫码
分享文档
扫一扫
联系客服