本文介紹了如何通過 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