C 驱动连接 OceanBase 数据库
本文介绍了如何通过 OBCI 驱动连接并使用 OceanBase 数据库。
前提条件
确保设置了基本的数据库开发环境。
确保满足如下硬件环境:
硬件平台:x86_64。
操作系统:CentOS/Redhat 系 Linux 发行版 7.2。
编译器:GCC 4.8。
联系技术支持人员获取 OBCI 和 LibOBClient 的 RPM 安装包。
C 驱动连接 OceanBase 数据库
步骤一:获取数据库连接参数
参考 获取连接参数 文档,获取相应的租户连接参数,例如:
obclient -hxxx.xxx.xxx.xxx -P1521 -u a**** -p******
数据库连接参数包含了访问数据库所需的参数信息,在验证示例代码前,可通过数据库连接参数验证登录数据库,保证信息正确。
参数说明:
-h:OceanBase 数据库连接的域名。
-P:OceanBase 数据库连接端口,Oracle 模式租户默认是 1521。
-u:连接租户的账号。
-p:账号密码。
步骤二:安装 C 相关驱动
当您获取 RPM 包后,在命令行工具中以 root 用户权限执行如下命令进行 OBCI 驱动的安装。
由于 OBCI 依赖于 LibOBClient,所以需要先安装 LibOBClient。
$ rpm -ivh libobclient-<version>.x86_64.rpm
再安装 OBCI。
$ rpm -ivh obci-<version>.x86_64.rpm
在默认情况下,软件包中包含的程序与文件将安装在如下路径中:
头文件被安装在
/u01/obclient/include
路径下。库文件被安装在
/u01/obclient/lib
路径下。
在高版本 OBCI 安装包中,由于版权限制,连接过程需要使用的数据库文件需要安装 Oracle Instant Client 获取,详细下载请参考 Oracle Instant Client Downloads。
常见需要的安装包为 basic 和 SDK 包。
安装 basic 包。
$ rpm -ivh oracle-instantclient-basic-<version>.x86_64.rpm
安装 SDK 包
$ rpm -ivh oracle-instantclient-devel-<version>.x86_64.rpm
步骤三:编写示例代码
本文通过具体实例介绍在 OceanBase 数据库 Oracle 模式下,C 语言通过 OBCI 与数据库服务器 OBServer 节点交互的基本方式。
初始化 OBCI 环境和线程。
初始化 OBCI 程序环境
OCIInitialize(OCI_DEFAULT, NULL, NULL, NULL, NULL)
初始化环境句柄
OCIEnvInit(&envhp, OCI_DEFAULT, 0, 0)
分配必要的句柄与数据结构。
分配句柄
OCIHandleAlloc(envhp, (dvoid **)&svchp, OCI_HTYPE_SVCCTX, 0, 0)
服务器环境句柄
OCIHandleAlloc(envhp, (dvoid **)&srvhp, OCI_HTYPE_SERVER, 0, 0)
服务器句柄
OCIHandleAlloc(envhp, (dvoid **)&authp, OCI_HTYPE_SESSION, 0, 0)
会话句柄
OCIHandleAlloc(envhp, (dvoid **)&errhp, OCI_HTYPE_ERROR, 0, 0)
错误句柄
OCIHandleAlloc(envhp, (dvoid **)&dschp, OCI_HTYPE_DESCRIBE, 0, 0)
建立与数据库的连接以及创建用户会话。
设置用户名
OCIAttrSet(authp, OCI_HTYPE_SESSION, (text *)strUserName,(ub4)strlen(strUserName), OCI_ATTR_USERNAME, errhp)
设置密码
OCIAttrSet(authp, OCI_HTYPE_SESSION, (text *)strPassword,(ub4)strlen(strPassword), OCI_ATTR_PASSWORD, errhp)
设置服务器环境句柄属性
OCIAttrSet((dvoid *)svchp, (ub4)OCI_HTYPE_SVCCTX,(dvoid *)srvhp, (ub4)0, OCI_ATTR_SERVER, errhp) OCIAttrSet(svchp, OCI_HTYPE_SVCCTX, (dvoid *)authp,0, OCI_ATTR_SESSION, errhp)
创建会话
OCISessionBegin(svchp, errhp, authp, OCI_CRED_RDBMS, OCI_DEFAULT)
开始会话
OCIHandleAlloc(envhp, (dvoid **)&stmthp, OCI_HTYPE_STMT, 0, 0)
通过 SQL 与 OceanBase 服务器交换数据,而后再做数据处理。一条 SQL 语句在 OBCI 应用程序中的执行步骤如下:
通过调用函数
OCIStmtPrepare()
或者OCIStmtPrepare2()
准备 SQL 语句。OCIStmtPrepare(stmthp, errhp, (text *)sql, strlen(sql), OCI_NTV_SYNTAX,OCI_DEFAULT)
通过调用一个或者多个函数,如
OCIBindByPos()
把输入变量的地址绑定在 DML 语句中的占位符中。OCIBindByPos(stmthp, &bidhp[0], errhp, 1, &szpersonid, sizeof(szpersonid), SQLT_INT, NULL, NULL, NULL, 0, NULL, 0)
或者通过
OCIBindByName()
函数进行。OCIBindByName(stmthp, &bidhp[0], errhp, (const OraText*)":personid", 9, &szpersonid, sizeof(szpersonid), SQLT_INT, NULL, NULL, NULL, 0, NULL, 0)
调用
OCIStmrExecute()
函数执行 SQL 语句。OCIStmtExecute(svchp, stmthp, errhp, (ub4)1, (ub4)0, (CONST OCISnapshot *)0, (OCISnapshot *)0, (ub4)OCI_DEFAULT)
调用
OCIDefineByPos()
函数为 SQL 语句中的数据输出项定义输出变量。OCIDefineByPos(stmthp, &defhp[0], errhp, 1, &szpersonid, sizeof(szpersonid), SQLT_INT, &ind[0], 0, 0, OCI_DEFAULT)
调用
OCIStmtFetch()
函数来获取查询的结果集。OCIStmtFetch(stmthp, errhp, 1, OCI_FETCH_NEXT, OCI_DEFAULT)
结束用户会话与断开与数据库的连接。
结束会话
OCISessionEnd(svchp, errhp, authp, (ub4)0)
断开与数据库的连接
OCIServerDetach(srvhp, errhp, OCI_DEFAULT)
释放在程序中所分配的句柄。
OCIHandleFree((dvoid *)dschp, OCI_HTYPE_DESCRIBE) OCIHandleFree((dvoid *)stmthp, OCI_HTYPE_STMT) OCIHandleFree((dvoid *)errhp, OCI_HTYPE_ERROR) OCIHandleFree((dvoid *)authp, OCI_HTYPE_SESSION) OCIHandleFree((dvoid *)svchp, OCI_HTYPE_SVCCTX) OCIHandleFree((dvoid *)srvhp, OCI_HTYPE_SERVER)
示例代码
示例文件 test.c
代码内容如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <malloc.h>
#include "oci.h"
/*声明句柄*/
OCIEnv *envhp; /*环境句柄*/
OCISvcCtx *svchp; /*服务环境句柄*/
OCIServer *srvhp; /*服务器句柄*/
OCISession *authp; /*会话句柄*/
OCIStmt *stmthp; /*语句句柄*/
OCIDescribe *dschp; /*描述句柄*/
OCIError *errhp; /*错误句柄*/
OCIDefine *defhp[3]; /*定义句柄*/
OCIBind *bidhp[4]; /*绑定句柄*/
sb2 ind[3]; /*指示符变量*/
/*绑定 select 结果集的参数*/
int szpersonid; /*存储 personid 列*/
text szname[51]; /*存储 name 列*/
text szemail[51]; /*存储 mail 列*/
char sql[256]; /*存储执行的 sql 语句*/
int main(int argc, char *argv[])
{
char strServerName[50];
char strUserName[50];
char strPassword[50];
/*设置服务器,用户名和密码*/
strcpy(strServerName, "xxx.xxx.xxx.xxx:1521");
strcpy(strUserName, "a****");
strcpy(strPassword, "******");
/*初始化 OCI 应用环境*/
OCIInitialize(OCI_DEFAULT, NULL, NULL, NULL, NULL);
/*初始化环境句柄*/
OCIEnvInit(&envhp, OCI_DEFAULT, 0, 0);
/*分配句柄*/
OCIHandleAlloc(envhp, (dvoid **)&svchp, OCI_HTYPE_SVCCTX, 0, 0);
/*服务器环境句柄*/
OCIHandleAlloc(envhp, (dvoid **)&srvhp, OCI_HTYPE_SERVER, 0, 0);
/*服务器句柄*/
OCIHandleAlloc(envhp, (dvoid **)&authp, OCI_HTYPE_SESSION, 0, 0);
/*会话句柄*/
OCIHandleAlloc(envhp, (dvoid **)&errhp, OCI_HTYPE_ERROR, 0, 0);
/*错误句柄*/
OCIHandleAlloc(envhp, (dvoid **)&dschp, OCI_HTYPE_DESCRIBE, 0, 0);
/*描述符句柄*/
/*连接服务器*/
OCIServerAttach(srvhp, errhp, (text *)strServerName, (sb4)strlen(strServerName), OCI_DEFAULT);
/*设置用户名和密码*/
OCIAttrSet(authp, OCI_HTYPE_SESSION, (text *)strUserName, (ub4)strlen(strUserName), OCI_ATTR_USERNAME, errhp);
OCIAttrSet(authp, OCI_HTYPE_SESSION, (text *)strPassword, (ub4)strlen(strPassword), OCI_ATTR_PASSWORD, errhp);
/*设置服务器环境句柄属性*/
OCIAttrSet((dvoid *)svchp, (ub4)OCI_HTYPE_SVCCTX, (dvoid *)srvhp, (ub4)0, OCI_ATTR_SERVER, errhp);
OCIAttrSet(svchp, OCI_HTYPE_SVCCTX, (dvoid *)authp, 0, OCI_ATTR_SESSION, errhp);
/*创建并开始一个用户会话*/
OCISessionBegin(svchp, errhp, authp, OCI_CRED_RDBMS, OCI_DEFAULT);
OCIHandleAlloc(envhp, (dvoid **)&stmthp, OCI_HTYPE_STMT, 0, 0);
/*语句句柄*/
/************************************************************************/
/*创建 person 表*/
/************************************************************************/
static text* SQL_CREATE_TB = (text*)"create table person(personid number, name varchar(256), email varchar(256))";
/*准备 SQL 语句*/
OCIStmtPrepare(stmthp, errhp, SQL_CREATE_TB, strlen((char *)SQL_CREATE_TB),OCI_NTV_SYNTAX, OCI_DEFAULT);
/*执行 SQL 语句*/
OCIStmtExecute(svchp, stmthp, errhp, 1, 0, 0, 0, OCI_DEFAULT);
/*提交到数据库*/
OCITransCommit(svchp, errhp, OCI_DEFAULT);
/************************************************************************/
/*插入数据*/
/************************************************************************/
memset(sql, 0, sizeof(sql));
strcpy(sql, "insert into person values(:personid,:name,:email)");
/*准备 SQL 语句*/
OCIStmtPrepare(stmthp, errhp, (text *)sql, strlen(sql),OCI_NTV_SYNTAX, OCI_DEFAULT);
/*绑定输入列*/
OCIBindByName(stmthp, &bidhp[0], errhp, (const OraText*)":personid", 9, &szpersonid, sizeof(szpersonid), SQLT_INT, NULL, NULL, NULL, 0, NULL, 0);
OCIBindByName(stmthp, &bidhp[2], errhp, (const OraText*)":name", 5, szname, sizeof(szname), SQLT_STR, NULL, NULL, NULL, 0, NULL, 0);
OCIBindByName(stmthp, &bidhp[3], errhp, (const OraText*)":email", 6, szemail, sizeof(szemail), SQLT_STR, NULL, NULL, NULL, 0, NULL, 0);
/*设置输入参数*/
szpersonid = 1;
memset(szname, 0, sizeof(szname));
strcpy((char*)szname, "obtest");
memset(szemail, 0, sizeof(szemail));
strcpy((char*)szemail, "t@ob.com");
/*执行 SQL 语句*/
OCIStmtExecute(svchp, stmthp, errhp, (ub4)1, (ub4)0, (CONST OCISnapshot *)0, (OCISnapshot *)0, (ub4)OCI_DEFAULT);
/*提交到数据库*/
OCITransCommit(svchp, errhp, OCI_DEFAULT);
/************************************************************************/
/*查询 person 表*/
/************************************************************************/
strcpy(sql, "select personid ,name,email from person;");
/*准备 SQL 语句*/
OCIStmtPrepare(stmthp, errhp, (text *)sql, strlen(sql), OCI_NTV_SYNTAX, OCI_DEFAULT);
/*绑定输出列*/
OCIDefineByPos(stmthp, &defhp[0], errhp, 1, &szpersonid, sizeof(szpersonid), SQLT_STR, &ind[0], 0, 0, OCI_DEFAULT);
OCIDefineByPos(stmthp, &defhp[1], errhp, 2, (ub1 *)szname, sizeof(szname), SQLT_STR, &ind[1], 0, 0, OCI_DEFAULT);
OCIDefineByPos(stmthp, &defhp[2], errhp, 3, (ub1 *)szemail, sizeof(szemail), SQLT_STR, &ind[2], 0, 0, OCI_DEFAULT);
/*执行 SQL 语句*/
OCIStmtExecute(svchp, stmthp, errhp, (ub4)0, 0, NULL, NULL,
OCI_DEFAULT);
printf("%-10s%-10s%-10s\n", "PERSONID", "NAME", "email");
while ((OCIStmtFetch(stmthp, errhp, 1, OCI_FETCH_NEXT, OCI_DEFAULT)) != OCI_NO_DATA)
{
printf("%-10d", szpersonid);
printf("%-10s", szname);
printf("%-10s\n", szemail);
break;
}
/*提交到数据库*/
OCITransCommit(svchp, errhp, OCI_DEFAULT);
/************************************************************************/
/*删除 person 表*/
/************************************************************************/
static text* SQL_DROP_TB = (text*)"drop table person";
/*准备 SQL 语句*/
OCIStmtPrepare(stmthp, errhp, (text*)SQL_DROP_TB, strlen((char *)SQL_DROP_TB), OCI_NTV_SYNTAX, OCI_DEFAULT);
/*执行 SQL 语句*/
OCIStmtExecute(svchp, stmthp, errhp, 1, 0, 0, 0, OCI_COMMIT_ON_SUCCESS);
/*提交到数据库*/
OCITransCommit(svchp, errhp, OCI_DEFAULT);
//结束会话
OCISessionEnd(svchp, errhp, authp, (ub4)0);
//断开与数据库的连接
OCIServerDetach(srvhp, errhp, OCI_DEFAULT);
//释放OCI句柄
OCIHandleFree((dvoid *)dschp, OCI_HTYPE_DESCRIBE);
OCIHandleFree((dvoid *)stmthp, OCI_HTYPE_STMT);
OCIHandleFree((dvoid *)errhp, OCI_HTYPE_ERROR);
OCIHandleFree((dvoid *)authp, OCI_HTYPE_SESSION);
OCIHandleFree((dvoid *)svchp, OCI_HTYPE_SVCCTX);
OCIHandleFree((dvoid *)srvhp, OCI_HTYPE_SERVER);
return 0;
}
修改代码中的数据库连接参数。参考如下字段,对应的值,则取自步骤一获取的数据库连接参数。
strcpy(strServerName, "xxx.xxx.xxx.xxx:1521");
strcpy(strUserName, "a****");
strcpy(strPassword, "******");
strServerName:取自
-h
和-P
参数,OceanBase 数据库连接的域名和端口。Oracle 模式租户默认端口是 1521。strUserName:取自
-u
参数,连接租户的账号。strPassword:取自
-p
参数,账号密码 。
步骤四:执行示例
代码编辑完成后,可以通过如下命令进行编译。
$ gcc test.c -I/u01/obclient/include /u01/obclient/lib/libobci.a -L/usr/local/lib64 -lstdc++ -lpthread -ldl -lm -g -o test
如果使用了高版本 obci,安装 Oracle Instant Client,-I 需要使用 Oracle 目录,如下:
$ gcc test.c -I/usr/include/oracle/21/client64/ -L/u01/obclient/lib/ -L/usr/local/lib64 -lobci -lobclnt -g -o test
编译完成后,指定运行路径。
$ export LD_LIBRARY_PATH=/u01/obclient/lib
运行代码示例。
$ ./test
运行得到如下结果,说明数据库连接成功且语句执行正常。
PERSONID NAME email 49 obtest t@ob.com