使用 Dci 连接金仓数据库Kingbase

本文介绍如何使用 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 驱动连接并操作数据库的一个简单示例:

  1. 导入 Dci 头文件:

    #include "dci.h"
    

  2. 初始化 DCI 环境

    使用 DCIEnvCreate() 创建 Dci 环境句柄:

    DCIEnv *envhp = NULL;
    DCIEnvCreate(&envhp, DCI_DEFAULT, NULL, NULL, NULL, NULL, 0, NULL);
    

  3. 创建 DCI 错误句柄

    使用 DCIHandleAlloc() 创建错误消息句柄,对应句柄类型使用 DCI_HTYPE_ERROR:

    DCIError *errhp = NULL;
    DCIHandleAlloc(envhp, (void **)&errhp, DCI_HTYPE_ERROR, 0, NULL);
    

  4. 创建服务上下文,连接数据库

    使用 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 目录。

      注:

      1. 路径中 KingbaseES 文件夹需要用户自己建立;
      2. X 为系统盘符;
      3. 在 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));
      
  5. 创建 SQL 语句句柄

    使用 DCIHandleAlloc() 创建错误消息句柄,对应句柄类型使用 DCI_HTYPE_STMT:

    DCIStmt *stmthp = NULL;
    DCIHandleAlloc(envhp, (void **)&stmthp, DCI_HTYPE_STMT, 0, NULL);
    

  6. 创建测试表

    使用 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);
    

  7. 插入数据

    使用 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);
    

  8. 查询数据并打印结果集

    同理插入数据的流程,使用 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);
    

  9. 关闭连接并释放资源:

    // 清理资源
    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]

常见问题

  1. 驱动包 SSL 库与系统环境的 SSL 库冲突

    原因:系统环境的依赖库版本过高,或应用程序运行时错误加载了系统环境的SSL库。

    解决:通过ldd 应用程序名,如 ldd testDci 查看当前环境下的依赖关系,确保运行应用程序时加载驱动包提供的 SSL 库,若仍然有 SSL 相关报错,则确定是驱动包提供的 SSL 库无法在当前环境下使用,此时请联系技服获取 Dci 静态依赖 SSL 库的驱动包来解决 SSL 依赖冲突问题。

  2. Windows 环境使用 Visual studio 编译Debug版本的应用程序崩溃

    原因:当前默认提供的 Windows 驱动包是 Release 版本,使用 Debug 模式编译应用程序可能造成未知问题。

    解决:请使用 Release 模式编译应用程序,如有需求,请联系技服申请 Debug 版本的 Dci驱动。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值