本文介绍如何使用 Dci 连接 Kingbase 数据库,并提供连接代码示例。
前提条件
-
确保已设置基本的应用开发环境。
-
确保已安装与驱动对应版本的数据库,且数据库连接可用。
-
确保 Dci 支持开发环境:
-
系统架构:
Linux:x86_64、arm、loongarch、mips、sw
Windows:V9 版本提供64位支持
-
编译器:
Linux:仅支持依赖 glibc 的编译器
Windows:依赖 msvc120 的运行时库
-
满足以上条件后,可通过金仓官网(电科金仓-成为世界卓越的数据库产品与服务提供商)获取对应版本架构的 Dci 驱动。
Dci 和 Oci
Kingbase 提供 Dci 和 Oci 接口,Oci 兼容 Oracle 的 Oci 接口,可复用 Oralce 的 Oci 头文件。
- OCI 是 Oracle 提供的一种应用程序编程接口(API),允许您创建使用函数调用来访问 Oracle 数据库、并控制SQL 语句执行和数据访问的所有阶段的应用程序。
- DCI 是 KingbaseES 提供的应用程序编程接口,该接口兼容 OCI 的功能,允许应用程序使用函数调用来访问 KingbaseES 数据库,并控制 SQL 语句执行和数据访问的所有阶段。
操作实例
基于 Dci 编译开发应用程序,下文以 Linux 环境使用 Dci 连接数据库并执行简单的插入查询操作做示例说明,主要步骤如下:
创建测试程序目录
下载并解压驱动包,驱动包内容如下所示:
Dci
├── include
│ ├── dciapi.h
│ ├── dcidef.h
│ ├── dci.h
│ └── dcitypes.h
└── lib
├── libcrypto.so.1.1
├── libdcikdb.so
├── libkci.so.5
└── libssl.so.1.1
创建测试程序文件夹 test_dci,将 Dci 驱动包的库文件放置在创建的文件夹路径下,并创建源文件 test_dci.c 及 Dci驱动使用的配置文 sys_service.conf,示例目录结构如下:
test_dci
├── include
│ ├── dciapi.h
│ ├── dcidef.h
│ ├── dci.h
│ └── dcitypes.h
├── lib
│ ├── libcrypto.so.1.1
│ ├── libdcikdb.so
│ ├── libkci.so.5
│ └── libssl.so.1.1
├── sys_service.conf
└── test_dci.c
其中, sys_service.conf 配置文件示例如下:
# DSN 数据源配置名
[KingbaseES]
# 数据库名
dbname=test
# 数据库端口
port=54321
# 数据库IP
host=127.0.0.1
# 驱动日志生成路径及日志文件名
#DCILog=oci.log
# 驱动日志级别
#DCILogLevel=DEBUG3
# 是否自动提交
AutoCommit=1
# 客户端字符集编码,需要和数据库服务端一致
ClientEncoding=utf8
开发源程序
开发源程序 test_dci.c,以下是使用 Dci 驱动连接并操作数据库的一个简单示例:
-
导入 Dci 头文件:
#include "dci.h"
-
初始化 DCI 环境
使用
DCIEnvCreate()
创建 Dci 环境句柄:DCIEnv *envhp = NULL; DCIEnvCreate(&envhp, DCI_DEFAULT, NULL, NULL, NULL, NULL, 0, NULL);
-
创建 DCI 错误句柄
使用
DCIHandleAlloc()
创建错误消息句柄,对应句柄类型使用 DCI_HTYPE_ERROR:DCIError *errhp = NULL; DCIHandleAlloc(envhp, (void **)&errhp, DCI_HTYPE_ERROR, 0, NULL);
-
创建服务上下文,连接数据库
使用
DCILogon()
创建错误消息句柄:text *dsnName = (text *)"KingbaseES"; /* 配置文件 sys_service.conf 中配置的数据源名 */ text *userName = (text *)"SYSTEM"; /* 用户名 */ text *password = (text *)"123456"; /* 密码 */ DCISvcCtx *svchp = NULL; DCILogon(envhp, errhp, &svchp, (text *)userName, strlen(userName), (text *)password, strlen(password), (text*)dsnName, strlen(dsnName));
备注
Dci提供两种数据库连接串形式:
-
DSN
推荐使用 DSN 形式的字符串。使用该形式的连接方式,会在当前运行环境查找对应的环境变量 KINGBASE_CONFDIR,若没有对应环境变量则查找默认路径是否存在 sys_service.conf 配置文件,并加载配置文件的驱动配置。
sys_service.conf 可通过指定系统环境变量 KINGBASE_CONFDIR 来让应用程序显示加载对应配置文件的 DSN 的配置。
若不配置环境变量,可通过在默认路径放置 sys_service.conf 来加载对应 DSN 配置,默认搜索路径如下:
-
Windows XP 默认路径为:X:\Documents and Settings\All Users\Application Data\KingbaseES 目录。
-
Windows 7 及以上版本默认路径为:X:\ProgramData\KingbaseES 目录。
-
Linux 指定路径为:/etc 目录。
注:
- 路径中 KingbaseES 文件夹需要用户自己建立;
- X 为系统盘符;
- 在 Windows 平台,需注意 Application Data 与 ProgramData 为系统隐藏文件夹。
使用 DSN 形式的连接方式的示例如下:
text *dsnName = (text *)"KingbaseES"; /* 配置文件 sys_service.conf 中配置的数据源名 */ text *userName = (text *)"SYSTEM"; /* 用户名 */ text *password = (text *)"123456"; /* 密码 */ DCISvcCtx *svchp = NULL; DCILogon(envhp, errhp, &svchp, (text *)userName, strlen(userName), (text *)password, strlen(password), (text*)dsnName, strlen(dsnName));
-
-
连接字符串
不推荐直接使用连接字符串的形式连接数据库。
使用该形式的连接方式,Dci会先尝试搜索 sys_service.conf 文件,并加载名为 [Default] 的 DSN 配置;
若无对应配置文件且无对应 DSN,则加载默认配置连接数据库,同时无法修改 Dci连接配置,导致 Dci使用使用受限,以下是使用连接字符串形式的 Dci默认连接配置:
# 非自动提交
AutoCommit=0
# DDL语句自动提交
AutoCommitDDL=1
# 启用批量拓展协议,优化批量操作性能
BatchInsertSize_Ext=1
# 开启语句级自动回滚,该配置会极大影响性能
Protocol=7.4-2
使用连接字符串的示例如下:
text *dsnName = (text *)"127.0.0.1:54321/test"; /* ip:port/dbname */ text *userName = (text *)"SYSTEM"; /* 用户名 */ text *password = (text *)"123456"; /* 密码 */ DCISvcCtx *svchp = NULL; DCILogon(envhp, errhp, &svchp, (text *)userName, strlen(userName), (text *)password, strlen(password), (text*)dsnName, strlen(dsnName));
-
-
创建 SQL 语句句柄
使用
DCIHandleAlloc()
创建错误消息句柄,对应句柄类型使用 DCI_HTYPE_STMT:DCIStmt *stmthp = NULL; DCIHandleAlloc(envhp, (void **)&stmthp, DCI_HTYPE_STMT, 0, NULL);
-
创建测试表
使用
DCIStmtPrepare()
生成预备语句,并使用DCIStmtExecute()
执行 SQL:text *sqlCreateTb = (text *)"create table test_dci(id int, name varchar(20))"; text *sqlDropTb = (text *)"drop table if exists test_dci"; // 删表 DCIStmtPrepare(stmthp, errhp, sqlDropTb, strlen((char *)sqlDropTb), DCI_NTV_SYNTAX, DCI_DEFAULT); DCIStmtExecute(svchp, stmthp, errhp, 0, 0, NULL, NULL, DCI_DEFAULT); // 建表 DCIStmtPrepare(stmthp, errhp, sqlCreateTb, strlen((char *)sqlCreateTb), DCI_NTV_SYNTAX, DCI_DEFAULT); DCIStmtExecute(svchp, stmthp, errhp, 0, 0, NULL, NULL, DCI_DEFAULT);
-
插入数据
使用
DCIStmtPrepare()
生成预备语句,可使用DCIBindByPos()
依次绑定输入参数,绑定参数后,使用DCIStmtExecute()
执行 SQL:text *sqlInsertTb = (text *)"insert into test_dci values(:1, :2)"; // 插入 DCIStmtPrepare(stmthp, errhp, sqlInsertTb, strlen((char *)sqlInsertTb), DCI_NTV_SYNTAX, DCI_DEFAULT); // 绑定输入参数 int valueIn1[MAX_ROWS] = {1, 2}; char valueIn2[MAX_ROWS][MAX_DATABUF_LEN] = {"Kingbase1", "kikk2"}; DCIBind *bindIn1p, *bindIn2p; DCIBindByPos(stmthp, &bindIn1p, errhp, 1, &valueIn1[0], sizeof(valueIn1[0]), SQLT_INT, NULL, NULL, NULL, 0, NULL, DCI_DEFAULT); DCIBindByPos(stmthp, &bindIn2p, errhp, 2, valueIn2[0], sizeof(valueIn2[0]), SQLT_STR, NULL, NULL, NULL, 0, NULL, DCI_DEFAULT); DCIStmtExecute(svchp, stmthp, errhp, MAX_ROWS, 0, NULL, NULL, DCI_DEFAULT);
-
查询数据并打印结果集
同理插入数据的流程,使用
DCIStmtPrepare()
生成预备语句,可使用DCIBindByPos()
依次绑定输入参数,绑定参数后,使用DCIStmtExecute()
执行 SQL:text *sqlSelectTb = (text *)"select id,name from test_dci"; // 查询 DCIStmtPrepare(stmthp, errhp, (text *)sqlSelectTb, strlen(sqlSelectTb), DCI_NTV_SYNTAX, DCI_DEFAULT); // 绑定输出参数 DCIDefine *defnp = NULL; int valueOut1 = 0; char valueOut2[MAX_DATABUF_LEN]={0}; DCIDefineByPos(stmthp, &defnp, errhp, 1, &valueOut1, sizeof(valueOut1), SQLT_INT, NULL, NULL, NULL, DCI_DEFAULT); DCIDefineByPos(stmthp, &defnp, errhp, 2, valueOut2, sizeof(valueOut2), SQLT_STR, NULL, NULL, NULL, DCI_DEFAULT); DCIStmtExecute(svchp, stmthp, errhp, 0, 0, NULL, NULL, DCI_DEFAULT);
执行查询后,可通过
DCIStmtFetch()
获取结果集,以下是遍历结果集一次获取一行结果的示例:sword status = DCI_SUCCESS; do { status = DCIStmtFetch(stmthp, errhp, 1, DCI_FETCH_NEXT, DCI_DEFAULT); // 剩余结果集为空则退出 if (DCI_NO_DATA == status) { break; } printf("col1=[%d], col2=[%s]\n", valueOut1, valueOut2); } while (DCI_SUCCESS == status);
-
关闭连接并释放资源:
// 清理资源 DCIHandleFree(stmthp, DCI_HTYPE_STMT); DCIHandleFree(errhp, DCI_HTYPE_ERROR); DCILogoff(svchp, errhp); DCIHandleFree(svchp, DCI_HTYPE_SVCCTX); DCIHandleFree(errhp, DCI_HTYPE_ERROR); DCIHandleFree(envhp, DCI_HTYPE_ENV);
编译程序
使用 gcc 编译,需要显示指定 Dci库文件安装路径,以下是示例使用的 gcc 编译参数说明:
描述 | 参数 |
---|---|
编译指定库文件路径 | -L directory (directory 指定 Dci库文件路径) |
编译指定链接库 | -ldcikdb |
运行时优先搜索运行库路径 | -Wl,-rpath directory (directory 指定 Dci库文件路径) |
完整的编译命令如下,以示例的目录结构为例:
gcc -I ./include -L ./lib test_dci.c -o testDci -ldcikdb -Wl,-rpath,./lib
运行程序
编译后执行程序,示例使用环境变量 KINGBASE_CONFDIR 的 sys_service.conf 的驱动配置操作数据库:
# 当前 sys_service.conf 路径,配置环境变量后执行程序
export KINGBASE_CONFDIR=.
./testDci
命令行窗口会打印刚刚插入的数据,示例期望输出如下所示:
insert ok.
col1=[1], col2=[Kingbase1]
col1=[2], col2=[kikk2]
常见问题
-
驱动包 SSL 库与系统环境的 SSL 库冲突
原因:系统环境的依赖库版本过高,或应用程序运行时错误加载了系统环境的SSL库。
解决:通过ldd 应用程序名,如
ldd testDci
查看当前环境下的依赖关系,确保运行应用程序时加载驱动包提供的 SSL 库,若仍然有 SSL 相关报错,则确定是驱动包提供的 SSL 库无法在当前环境下使用,此时请联系技服获取 Dci 静态依赖 SSL 库的驱动包来解决 SSL 依赖冲突问题。 -
Windows 环境使用 Visual studio 编译Debug版本的应用程序崩溃
原因:当前默认提供的 Windows 驱动包是 Release 版本,使用 Debug 模式编译应用程序可能造成未知问题。
解决:请使用 Release 模式编译应用程序,如有需求,请联系技服申请 Debug 版本的 Dci驱动。