达梦数据库-学习-21-C 外部函数

目录

一、环境信息

二、说点什么

三、介绍

四、参数介绍

五、语法树

六、标量类型参数

[七、DM 结构化参数](#七、DM 结构化参数)

1、结构体

(1)de_data

(2)de_args

2、函数介绍

(1)de_get_int

(2)de_get_double

(3)de_get_str

(4)de_get_str_with_len

(5)de_set_int

(6)de_get_int

(7)de_set_double

(8)de_set_str

(9)de_set_str_with_len

(10)de_return_int

(11)de_return_double

(12)de_return_str

(13)de_return_str_with_len

(14)de_return_null

(15)de_str_free

(16)de_is_null

八、实验

1、测试数据

2、PLSQL自定义函数

3、C外部函数源码

4、C外部函数编译

5、C外部函数动态库拷贝

6、调整参数

7、重启数据库

8、C外部函数创建

9、验证正确性

10、效率对比


一、环境信息

|------|------------------------------------------------------------------------------------------------------------------------------|
| 名称 | 值 |
| 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]$ 
相关推荐
攻心的子乐6 分钟前
Flyweight(享元)设计模式 软考 享元 和 代理属于结构型设计模式
java·开发语言
阿图灵12 分钟前
文章记单词 | 第102篇(六级)
学习·学习方法
君的名字32 分钟前
怎么判断一个Android APP使用了Qt 这个跨端框架
android·开发语言·qt
酷道41 分钟前
CentOS停止维护了,解决yum不能安装软件的问题
linux·运维·centos
不秃的开发媛42 分钟前
JFace中MVC的表的单元格编辑功能的实现
java·开发语言·mvc
摆烂仙君1 小时前
怎样通过神经网络估计股票走向
linux·运维·服务器
努力学习的小廉1 小时前
我爱学算法之—— 二分查找(中)
开发语言·c++·算法
咸鱼2333号程序员1 小时前
Linux 输出输入重定向、tee命令详解
linux·运维·服务器·shell编程
只_只1 小时前
A1012 PAT甲级JAVA题解 The Best Bank
开发语言·python
petunsecn1 小时前
MySql添加非空字段时的“伪空”问题
数据库·mysql