目录
[七、DM 结构化参数](#七、DM 结构化参数)
一、环境信息
|------|------------------------------------------------------------------------------------------------------------------------------|
| 名称 | 值 |
| CPU | 12th Gen Intel(R) Core(TM) i7-12700H |
| 操作系统 | CentOS Linux release 7.9.2009 (Core) |
| 内存 | 4G |
| 逻辑核数 | 4 |
| DM版本 | 1 DM Database Server 64 V8 2 DB Version: 0x7000c 3 03134284194-20240703-234060-20108 4 Msg Version: 12 5 Gsu level(5) cnt: 0 |
二、说点什么
工作中我们有时会遇到一个简单的查询,上面嵌套了一个用户自定义的PLSQL编写的函数,去掉此函数非常快,加上就效率堪忧,需要我们去优化,这时C外部函数就是一个好的选择,或者做ORACLE到信创数据库的迁移,有些函数用户是用C外部函数实现的,需要我们去改写成信创数据库的C外部函数,今天我们就来介绍一下达梦C外部函数如何编写。
三、介绍
C 外部函数是使用 C、C++语言编写,在数据库外编译并保存在.dll、.so 共享库文件 中,被用户通过 DMSQL 程序调用的函数。
C 外部函数的执行一般通过代理 dmap 工具进行,此时为了执行 C 外部函数,需要先 启动 dmap 服务。dmap 执行程序在 DM8 安装目录的 bin 子目录下,直接执行即可启动 dmap 服务。
同时,结构化的 C 外部函数支持结合 INI 参数 EFC_USE_AP 进行性能优化,当指定参 数值为 0 时,结构化的 C 外部函数会在 dmserver 内部调用,不再和 dmap 进行通信,可以提高函数的执行效率。
当用户调用 C 外部函数时,服务器操作步骤如下:首先,确定调用的(外部函数使用的)共享库及函数;然后,通知代理进程工作。代理进程装载指定的共享库,并在函数执行后将结果返回给服务器。
C外部函数分为标量类型参数和DM 结构化参数两种。
四、参数介绍
|----------------------|------------------------------------------------------------------------------------------|
| 参数名 | 描述 |
| ENABLE_EXTERNAL_CALL | 是否允许创建或执行外部函数。 0:不允许; 1:允许; |
| EFC_USE_AP | 是否允许结构化的C外部函数在DMSERVER内部执行。 1:表示使用常规DMAP方式通信; 0:表示不使用DMAP,直接在DMSERVER内部调用执行,可以提高函数的执行效率; |
五、语法树
sql
CREATE [OR REPLACE] FUNCTION [IF NOT EXISTS] [<模式名>.]<函数名>[(<参数列表>)]
RETURN <返回值类型>
EXTERNAL '<动态库路径>' [<引用的函数名>] USING < C | CS >;
|-------|---------------------------------------------------------------------------------------------------------------------------------------|
| 参数名 | 描述 |
| 函数名 | 指明被创建的 C 外部函数的名字; |
| 模式名 | 指明被创建的 C 外部函数所属模式的名字,缺省为当前模式名; |
| 参数列表 | 指明 C 外部函数参数信息,如果是使用 DM 结构化参数编写的 C 函 数,参数模式可设置为 IN、OUT 或 IN OUT(OUT IN),缺省为 IN 类型; 使用标量类型参数编写的 C 函数则参数模式只能是 IN 类型。参数类型、个数都应和动态库里定义的一致; |
| 返回值类型 | 必须和动态库里定义的一致; |
| 动态库路径 | 用户按照 DM 规定的 C 语言函数格式编写的 DLL 文件生成的动态库所在的路径; 动态库分为 64 位和 32 位两种,使用的时候要和操作系统一一对应。例如, 64 位的操作系统要用 64 位的动态库; |
| 引用函数名 | 指明函数名>在动态库路径>中对应的函数名; |
| USING | USING 子句指明函数的类型,如果是 DM 结构化参数的 C 函数,类型为 C; 标量 类型参数的 C 函数,类型为 CS; |
六、标量类型参数
该方案中,用户不必引用 DM 提供的外部函数接口,可以按照标准的 C 风格编码,使 用 C 标量类型作为参数类型。使用该方案编写的 C 函数,只能在使用 X86 CPU 的 64 位非 Windows 系统中,被数据库引用作为外部函数。
例子可以参考之前写的博客《达梦数据库-学习-07-C外部函数创建,及与Plsql自定义函数效率对比》。
七、DM 结构化参数
该方案中,C 用户必须使用 DM8 提供的编写 C 外部函数动态库的接口,严格按照要求格式书写外部函数的 C 代码。
优点在于效率高且适用于各个平台。
1、结构体
(1)de_data
返回值结构体。
cpp
typedef struct de_data_struct de_data;
struct de_data_struct
{
udint4 null_flag; /*whether is null, 1:not null 0: null*/
union
{
sdint4 v_int;
ddouble v_double;
de_str v_str;
}data;
};
//只支持 int、double、float、binary_float、char 类型。其中 float 类型在系统内部被转化为 double 类型执行,相关接口请使用 double 类型的接口
(2)de_args
传入参数结构体。
cpp
typedef struct de_args_struct de_args;
struct de_args_struct
{
udint4 n_args;
de_data* args;
};
2、函数介绍
(1)de_get_int
cpp
/**
* <p>Function: return the arg_id para as int</p>
* <p>Arthimetic: none</p>
* <p>arg list: <br><ul>
<li> args: not null</li>
<li> arg_id: get the id para value, start with 0, not null</li>
* </ul></p>
* <p>return val : the arg_id para as int</p>
*/
sdint4
de_get_int(
de_args* args,
udint4 arg_id
);
第 arg_id 参数的数据类型为整型,从参数列表args中取出第arg_id参数的值。
(2)de_get_double
cpp
/**
* <p>Function: return the arg_id para as double</p>
* <p>Arthimetic: none</p>
* <p>arg list: <br><ul>
<li> args: not null</li>
<li> arg_id: get the id para value,start with 0,not null</li>
* </ul></p>
* <p>return val : the arg_id para as double</p>
*/
ddouble
de_get_double(
de_args* args,
udint4 arg_id
);
第 arg_id 参数的数据类型为 double 类型,从参数列表 args 中取出第 arg_id 参数的值。
(3)de_get_str
cpp
/**
* <p>Function: return the arg_id para as string</p>
* <p>Arthimetic: none</p>
* <p>arg list: <br><ul>
<li> args: not null</li>
<li> arg_id: get the id para value,start with 0,not null</li>
* </ul></p>
* <p>return val : the arg_id para as string</p>
*/
udbyte*
de_get_str(
de_args* args,
udint4 arg_id
);
第 arg_id 参数的数据类型为字符串类型,从参数列表 args 中取出第 arg_id 参数的值。
(4)de_get_str_with_len
cpp
/**
* <p>Function: return the arg_id para as string</p>
* <p>Arthimetic: none</p>
* <p>arg list: <br><ul>
<li> args: not null</li>
<li> arg_id: get the id para value,start with 0,not null</li>
<li> alen: return the length of string</li>
* </ul></p>
* <p>return val : the arg_id para as string</p>
*/
udbyte*
de_get_str_with_len(
de_args* args,
udint4 arg_id,
udint4* alen
);
第 arg_id 参数的数据类型为字符串类型,从参数列表 args 中取出第 arg_id 参数的值以及字符串长度。
(5)de_set_int
cpp
/**
* <p>Function: set the arg_id para as int</p>
* <p>Arthimetic: none</p>
* <p>arg list: <br><ul>
<li> args: not null</li>
<li> arg_id: set the id para value,start with 0,not null</li>
* </ul></p>
* <p>return val: the arg_id para as int</p>
*/
void
de_set_int(
de_args* args,
udint4 arg_id,
sdint4 ret
);
第 arg_id 参数的数据类型为整型,设置参数列表args的第arg_id参数的值 为 ret。
(6)de_get_int
cpp
/**
* <p>Function: return the arg_id para as int</p>
* <p>Arthimetic: none</p>
* <p>arg list: <br><ul>
<li> args: not null</li>
<li> arg_id: get the id para value, start with 0, not null</li>
* </ul></p>
* <p>return val : the arg_id para as int</p>
*/
sdint4
de_get_int(
de_args* args,
udint4 arg_id
);
第 arg_id 参数的数据类型为整型,从参数列表args中取出第arg_id参数的值。
(7)de_set_double
cpp
/**
* <p>Function: set the arg_id para as double</p>
* <p>Arthimetic: none</p>
* <p>arg list: <br><ul>
<li> args: not null</li>
<li> arg_id: set the id para value,start with 0,not null</li>
* </ul></p>
* <p>return val: the arg_id para as double</p>
*/
void
de_set_double(
de_args* args,
udint4 arg_id,
ddouble ret
);
第 arg_id 参数的数据类型为 double 类型,设置参数列表 args 的第 arg_id 参数的值为 ret。
(8)de_set_str
cpp
/**
* <p>Function: set the arg_id para as string</p>
* <p>Arthimetic: none</p>
* <p>arg list: <br><ul>
<li> args: not null</li>
<li> arg_id: set the id para value,start with 0,not null</li>
<li> ret: the new arg_id para as string,end with '\0'</li>
* </ul></p>
* <p>return val: nothing</p>
*/
void de_set_str(
de_args* args,
udint4 arg_id,
udbyte* ret
);
第 arg_id 参数的数据类型为字符串类 型,设置第 arg_id 参数的值为 ret。
(9)de_set_str_with_len
cpp
/**
* <p>Function: set the arg_id para as string</p>
* <p>Arthimetic: none</p>
* <p>arg list: <br><ul>
<li> args: not null</li>
<li> arg_id: set the id para value,start with 0,not null</li>
<li> alen: length of the new string</li>
<li> ret: the new arg_id para as string,not end with '\0'</li>
* </ul></p>
* <p>return val: nothing</p>
*/
void
de_set_str_with_len(
de_args* args,
udint4 arg_id,
udbyte* ret,
udint4 alen
);
第 arg_id 参数的数据类型为字符串类 型,将字符串 ret 的前 len 个字符赋值给参数列表 args 的第 arg_id 参数。
(10)de_return_int
cpp
/**
* <p>Function: return the arg_id para as int</p>
* <p>Arthimetic: none</p>
* <p>arg list: <br><ul>
<li> ret: return the int value</li>
* </ul></p>
* <p>return val: int</p>
*/
de_data
de_return_int(
sdint4 ret
);
返回值类型为整型。
(11)de_return_double
cpp
/**
* <p>Function: return the arg_id para as double</p>
* <p>Arthimetic: none</p>
* <p>arg list: <br><ul>
<li> ret: return the double value</li>
* </ul></p>
* <p>return val: double</p>
*/
de_data
de_return_double(
ddouble ret
);
返回值类型为 double 型。
(12)de_return_str
cpp
/**
* <p>Function: return the arg_id para as string</p>
* <p>Arthimetic: none</p>
* <p>arg list: <br><ul>
<li> ret: return the string value, end with '\0'</li>
* </ul></p>
* <p>return val: string</p>
*/
de_data
de_return_str(
udbyte* ret
);
返回值为字符串类型。
(13)de_return_str_with_len
cpp
/**
* <p>Function: return the arg_id para as string</p>
* <p>Arthimetic: none</p>
* <p>arg list: <br><ul>
<li> ret: return the string value, not end with '\0'</li>
<li> alen: the length of string</li>
* </ul></p>
* <p>return val: string</p>
*/
de_data
de_return_str_with_len(
udbyte* ret,
udint4 alen
);
返回字符串 ret 的前 len 个字符。
(14)de_return_null
cpp
/**
* <p>Function: return null</p>
* <p>Arthimetic: none</p>
* <p>arg list: none<br><ul>
* </ul></p>
* <p>return val: none</p>
*/
de_data
de_return_null();
返回空值。
(15)de_str_free
cpp
/**
* <p>Function: free space</p>
* <p>Arthimetic: none</p>
* <p>arg list: <br><ul>
<li>str: return the point to the space of the string need free</li>
* </ul></p>
* <p>return val: nothing</p>
*/
void
de_str_free(
sdbyte* str
);
调用 de_get_str 函数后,需要调用此 函数释放字符串空间。
(16)de_is_null
cpp
/**
* <p>Function: whether the args are null</p>
* <p>Arthimetic: none</p>
* <p>arg list: <br><ul>
<li> args: not null</li>
<li> arg_id: set the id para value,start with 0,not null</li>
* </ul></p>
* <p>return val: bool</p>
*/
udint4
de_is_null(
de_args* args,
udint4 arg_id
);
判断参数列表args的第arg_id个参数 是否为空。
八、实验
我们使用PLSQL自定义函数和C外部函数分别实现16进制字符串转换成ASCII可见字符。
1、测试数据
sql
CREATE USER LZL IDENTIFIED BY qwer1234S;
GRANT DBA TO LZL;
DROP TABLE LZL.TEST_HEX_TO_ASCII;
CREATE TABLE LZL.TEST_HEX_TO_ASCII(HEX VARCHAR2(100));
INSERT INTO LZL.TEST_HEX_TO_ASCII VALUES('4142'),('48656C6C6F'),('434445'),('41427E'),('53554e'),('434445'),('464748');
INSERT INTO LZL.TEST_HEX_TO_ASCII SELECT * FROM LZL.TEST_HEX_TO_ASCII;
INSERT INTO LZL.TEST_HEX_TO_ASCII SELECT * FROM LZL.TEST_HEX_TO_ASCII;
INSERT INTO LZL.TEST_HEX_TO_ASCII SELECT * FROM LZL.TEST_HEX_TO_ASCII;
INSERT INTO LZL.TEST_HEX_TO_ASCII SELECT * FROM LZL.TEST_HEX_TO_ASCII;
INSERT INTO LZL.TEST_HEX_TO_ASCII SELECT * FROM LZL.TEST_HEX_TO_ASCII;
INSERT INTO LZL.TEST_HEX_TO_ASCII SELECT * FROM LZL.TEST_HEX_TO_ASCII;
INSERT INTO LZL.TEST_HEX_TO_ASCII SELECT * FROM LZL.TEST_HEX_TO_ASCII;
INSERT INTO LZL.TEST_HEX_TO_ASCII SELECT * FROM LZL.TEST_HEX_TO_ASCII;
INSERT INTO LZL.TEST_HEX_TO_ASCII SELECT * FROM LZL.TEST_HEX_TO_ASCII;
INSERT INTO LZL.TEST_HEX_TO_ASCII SELECT * FROM LZL.TEST_HEX_TO_ASCII;
INSERT INTO LZL.TEST_HEX_TO_ASCII SELECT * FROM LZL.TEST_HEX_TO_ASCII;
INSERT INTO LZL.TEST_HEX_TO_ASCII SELECT * FROM LZL.TEST_HEX_TO_ASCII;
INSERT INTO LZL.TEST_HEX_TO_ASCII SELECT * FROM LZL.TEST_HEX_TO_ASCII;
INSERT INTO LZL.TEST_HEX_TO_ASCII SELECT * FROM LZL.TEST_HEX_TO_ASCII;
INSERT INTO LZL.TEST_HEX_TO_ASCII SELECT * FROM LZL.TEST_HEX_TO_ASCII;
COMMIT;
SELECT COUNT(*) FROM LZL.TEST_HEX_TO_ASCII; -- 229376
2、PLSQL自定义函数
sql
CREATE OR REPLACE FUNCTION LZL.Hex2AsciiPlSql(Hex in varchar2)
return varchar2
as
DECLARE
HexLen int;
TmpStr char(2);
i int;
j int := -1;
Res varchar2(4000);
StartIdx int := 1;
BEGIN
HexLen := lengthb(Hex);
-- 数据长度为0
if HexLen == 0 then
return NULL;
end if;
-- 因为十六进制数由两个字符组成,判断是否能整除2
if HexLen % 2 != 0 then
return NULL;
end if;
Hex := upper(Hex);
HexLen := HexLen / 2;
-- 跳过头部的\X
SELECT substr(Hex,1,2) INTO TmpStr;
if TmpStr = '\X' then
StartIdx := 2;
j := 1;
end if;
for i in StartIdx..HexLen loop
j := j + 2;
SELECT substr(Hex,j,2) INTO TmpStr;
case
when TmpStr = '20' then Res := Res || ' ';
when TmpStr = '21' then Res := Res || '!';
when TmpStr = '22' then Res := Res || '"';
when TmpStr = '23' then Res := Res || '#';
when TmpStr = '24' then Res := Res || '$';
when TmpStr = '25' then Res := Res || '%';
when TmpStr = '26' then Res := Res || '&';
when TmpStr = '27' then Res := Res || '''';
when TmpStr = '28' then Res := Res || '(';
when TmpStr = '29' then Res := Res || ')';
when TmpStr = '2A' then Res := Res || '*';
when TmpStr = '2B' then Res := Res || '+';
when TmpStr = '2C' then Res := Res || ',';
when TmpStr = '2D' then Res := Res || '-';
when TmpStr = '2E' then Res := Res || '.';
when TmpStr = '2F' then Res := Res || '/';
when TmpStr = '30' then Res := Res || '0';
when TmpStr = '31' then Res := Res || '1';
when TmpStr = '32' then Res := Res || '2';
when TmpStr = '33' then Res := Res || '3';
when TmpStr = '34' then Res := Res || '4';
when TmpStr = '35' then Res := Res || '5';
when TmpStr = '36' then Res := Res || '6';
when TmpStr = '37' then Res := Res || '7';
when TmpStr = '38' then Res := Res || '8';
when TmpStr = '39' then Res := Res || '9';
when TmpStr = '3A' then Res := Res || ':';
when TmpStr = '3B' then Res := Res || ';';
when TmpStr = '3C' then Res := Res || '<';
when TmpStr = '3D' then Res := Res || '=';
when TmpStr = '3E' then Res := Res || '>';
when TmpStr = '3F' then Res := Res || '?';
when TmpStr = '40' then Res := Res || '@';
when TmpStr = '41' then Res := Res || 'A';
when TmpStr = '42' then Res := Res || 'B';
when TmpStr = '43' then Res := Res || 'C';
when TmpStr = '44' then Res := Res || 'D';
when TmpStr = '45' then Res := Res || 'E';
when TmpStr = '46' then Res := Res || 'F';
when TmpStr = '47' then Res := Res || 'G';
when TmpStr = '48' then Res := Res || 'H';
when TmpStr = '49' then Res := Res || 'I';
when TmpStr = '4A' then Res := Res || 'J';
when TmpStr = '4B' then Res := Res || 'K';
when TmpStr = '4C' then Res := Res || 'L';
when TmpStr = '4D' then Res := Res || 'M';
when TmpStr = '4E' then Res := Res || 'N';
when TmpStr = '4F' then Res := Res || 'O';
when TmpStr = '50' then Res := Res || 'P';
when TmpStr = '51' then Res := Res || 'Q';
when TmpStr = '52' then Res := Res || 'R';
when TmpStr = '53' then Res := Res || 'S';
when TmpStr = '54' then Res := Res || 'T';
when TmpStr = '55' then Res := Res || 'U';
when TmpStr = '56' then Res := Res || 'V';
when TmpStr = '57' then Res := Res || 'W';
when TmpStr = '58' then Res := Res || 'X';
when TmpStr = '59' then Res := Res || 'Y';
when TmpStr = '5A' then Res := Res || 'Z';
when TmpStr = '5B' then Res := Res || '[';
when TmpStr = '5C' then Res := Res || '\\';
when TmpStr = '5D' then Res := Res || ']';
when TmpStr = '5E' then Res := Res || '^';
when TmpStr = '5F' then Res := Res || '_';
when TmpStr = '60' then Res := Res || '`';
when TmpStr = '61' then Res := Res || 'a';
when TmpStr = '62' then Res := Res || 'b';
when TmpStr = '63' then Res := Res || 'c';
when TmpStr = '64' then Res := Res || 'd';
when TmpStr = '65' then Res := Res || 'e';
when TmpStr = '66' then Res := Res || 'f';
when TmpStr = '67' then Res := Res || 'g';
when TmpStr = '68' then Res := Res || 'h';
when TmpStr = '69' then Res := Res || 'i';
when TmpStr = '6A' then Res := Res || 'j';
when TmpStr = '6B' then Res := Res || 'k';
when TmpStr = '6C' then Res := Res || 'l';
when TmpStr = '6D' then Res := Res || 'm';
when TmpStr = '6E' then Res := Res || 'n';
when TmpStr = '6F' then Res := Res || 'o';
when TmpStr = '70' then Res := Res || 'p';
when TmpStr = '71' then Res := Res || 'q';
when TmpStr = '72' then Res := Res || 'r';
when TmpStr = '73' then Res := Res || 's';
when TmpStr = '74' then Res := Res || 't';
when TmpStr = '75' then Res := Res || 'u';
when TmpStr = '76' then Res := Res || 'v';
when TmpStr = '77' then Res := Res || 'w';
when TmpStr = '78' then Res := Res || 'x';
when TmpStr = '79' then Res := Res || 'y';
when TmpStr = '7A' then Res := Res || 'z';
when TmpStr = '7B' then Res := Res || '{';
when TmpStr = '7C' then Res := Res || '|';
when TmpStr = '7D' then Res := Res || '}';
when TmpStr = '7E' then Res := Res || '~';
else return NULL;
end case;
end loop;
return Res;
END;
3、C外部函数源码
sql
#include <stdio.h>
#include "de_pub.h"
#include "stdlib.h"
de_data Hex2Ascii(de_args *args)
{
de_data Ret;
char *Hex = NULL;
udint4 HexLen = 0;
char *Ascii = NULL;
udint4 AsciiLen = 0;
udint4 Val = 0;
size_t HexIdx = 0;
size_t AsciiIdx = 0;
int RetVal = 0;
if(!de_is_null(args, 0))
{
/*从参数列表中取第 0 个参数的值以及长度*/
Hex = (char*)de_get_str_with_len(args, 0, (udint4*)&HexLen);
}
/*设置返回值的参数为空,为错误情况做准备。*/
Ret.null_flag = 0;
/*16进制字符串长度为0的情况*/
if (HexLen == 0 || Hex == NULL)
{
goto END_PHASE;
}
/*因为十六进制数由两个字符组成,判断是否能整除2*/
if (HexLen % 2 != 0)
{
goto END_PHASE;
}
/*如果包含\x或0x跳过*/
if ((Hex[0] == '\\' || Hex[0] == '0') && (Hex[1] == 'x' || Hex[1] == 'X'))
{
HexIdx = 1;
HexLen -= 2;
/*Ascii只需要存储Hex的一半,因为十六进制数由两个字符组成,对应一个字符。*/
HexLen /= 2;
AsciiLen = HexLen;
}
else
{
HexIdx = 0;
HexLen /= 2;
AsciiLen = HexLen;
HexLen--;
}
Ascii = (char*)malloc(sizeof(char) * AsciiLen);
/*内存是否分配成功*/
if (Ascii == NULL)
{
goto END_PHASE;
}
for (; HexIdx <= HexLen; HexIdx++,AsciiIdx++)
{
/*将两个十六进制字符转换为对应的整数值*/
Val = 0;
/*需要检查一下是否为合格的十六进制数,两位,第一位0-7、第二位0-9 A-F a-f*/
if(Hex[HexIdx * 2] >= '0' && Hex[HexIdx * 2] <= '7')
{
if((Hex[HexIdx * 2 + 1] >= '0' && Hex[HexIdx * 2 + 1] <= '9') ||
(Hex[HexIdx * 2 + 1] >= 'A' && Hex[HexIdx * 2 + 1] <= 'F') ||
(Hex[HexIdx * 2 + 1] >= 'a' && Hex[HexIdx * 2 + 1] <= 'f'))
{
}
else
{
goto FREE_MEM_PHASE;
}
}
else
{
goto FREE_MEM_PHASE;
}
RetVal = sscanf(&(Hex[HexIdx * 2]), "%2x", &Val);
if (RetVal == 0 || RetVal == EOF)
{
goto FREE_MEM_PHASE;
}
/*将整数值转换为 ASCII 字符并存储在 Ascii 字符串中*/
Ascii[AsciiIdx] = (char)Val;
}
/*返回字符串*/
Ret = de_return_str_with_len((udbyte*)Ascii, AsciiLen);
FREE_MEM_PHASE:
free(Ascii);
Ascii = NULL;
END_PHASE:
/*调用 get 函数得到字符串之后,需要调用此函数释放字符串空间*/
if(!de_is_null(args, 0))
{
de_str_free((sdbyte*)Hex);
}
return Ret;
}
4、C外部函数编译
sql
[root@localhost DmExternalFuncC]# gcc -O3 -Wall -Wextra -Werror -o /opt/Developer/DmExternalFuncC/Libs/libHex2Ascii.so -fPIC -shared /opt/Developer/DmExternalFuncC/Src/Hex2Ascii.c -I /opt/Developer/DmExternalFuncC/Include
5、C外部函数动态库拷贝
bash
[root@localhost DmExternalFuncC]# cp /opt/Developer/DmExternalFuncC/Libs/libHex2Ascii.so /opt/Dm8/ExternalFuncLibs/
[root@localhost DmExternalFuncC]# chown -R dmdba:dmdba /opt/Dm8/ExternalFuncLibs/libHex2Ascii.so
6、调整参数
sql
SP_SET_PARA_VALUE(2,'ENABLE_EXTERNAL_CALL',1);
SP_SET_PARA_VALUE(2,'EFC_USE_AP',0);
7、重启数据库
bash
[root@localhost DmExternalFuncC]# systemctl restart DmServiceSingleDb.service
8、C外部函数创建
sql
CREATE OR REPLACE FUNCTION LZL.Hex2Ascii(Hex VARCHAR)
RETURN VARCHAR
EXTERNAL '/opt/Dm8/ExternalFuncLibs/libHex2Ascii.so' "Hex2Ascii" USING C;
9、验证正确性
|--------------------------|-------------|
| 名称 | 描述 |
| UTL_RAW.CAST_TO_VARCHAR2 | 达梦自带的转换函数。 |
| Hex2AsciiPlSql | PLSQL自定义函数。 |
| Hex2Ascii | C外部函数。 |
sql
SQL> SELECT HEX,UTL_RAW.CAST_TO_VARCHAR2(HEX),LZL.Hex2AsciiPlSql(HEX),LZL.Hex2Ascii(HEX) FROM LZL.TEST_HEX_TO_ASCII LIMIT 7;
行号 HEX UTL_RAW.CAST_TO_VARCHAR2(HEX) LZL.HEX2ASCIIPLSQL(HEX) LZL.HEX2ASCII(HEX)
---------- ---------- ----------------------------- ----------------------- ------------------
1 4142 AB AB AB
2 48656C6C6F Hello Hello Hello
3 434445 CDE CDE CDE
4 41427E AB~ AB~ AB~
5 53554e SUN SUN SUN
6 434445 CDE CDE CDE
7 464748 FGH FGH FGH
7 rows got
10、效率对比
bash
[dmdba@localhost Dm8]$ time disql SYSDBA/qwer1234S@localhost:5236 -e "SELECT LZL.Hex2Ascii(HEX) FROM LZL.TEST_HEX_TO_ASCII;" > /opt/Hex2Ascii.txt
real 0m0.235s
user 0m0.074s
sys 0m0.009s
[dmdba@localhost Dm8]$ time disql SYSDBA/qwer1234S@localhost:5236 -e "SELECT UTL_RAW.CAST_TO_VARCHAR2(HEX) FROM LZL.TEST_HEX_TO_ASCII;" > /opt/Hex2Ascii.txt
real 0m0.575s
user 0m0.087s
sys 0m0.018s
[dmdba@localhost Dm8]$ time disql SYSDBA/qwer1234S@localhost:5236 -e "SELECT LZL.Hex2AsciiPlSql(HEX) FROM LZL.TEST_HEX_TO_ASCII;" > /opt/Hex2Ascii.txt
real 0m6.345s
user 0m0.078s
sys 0m0.015s
[dmdba@localhost Dm8]$