本記事では、CアプリケーションからOBCIを使用してOceanBaseデータベースに接続し、使用する方法について説明します。
適用対象
この内容はOceanBaseデータベースEnterprise Editionにのみ適用されます。OceanBaseデータベースCommunity Editionは、MySQLモードのみご利用いただけます。
前提条件
基本的なデータベース開発環境をセットアップしておいてください。
以下のハードウェア環境を満たしていることを確認します:
ハードウェアプラットフォーム:x86_64
OS:CentOS/Redhat系Linuxディストリビューション7.2
コンパイラ:GCC 4.8
OBCIとLibOBClientのRPMパッケージの取得については、テクニカルサポートにお問い合わせください。
注意
前提条件として、以下の点にもご注意ください。
- OBCI V2.0.4以降、RPMパッケージの
includeフォルダにoci.hやociap.hなどのヘッダーファイルは含まれなくなりました。そのため、アプリケーションをコンパイルする際は、まずOracleの clntsh関連RPMをインストールし、そこに含まれるヘッダーファイルを直接利用する必要があります。 - OceanBase:V2.2.76以降(現行バージョンの全機能をご利用いただくには、OceanBase データベース V4.0 以降の使用を推奨します。)
- ODP (OBProxy):V4.0以上のバージョンが推奨されます。
- libobclient:V2.1.1以上のバージョン
- Oracleに関連するドライバーファイル
oracle-instantclientをインストールされていること (OBCI V2.0.4以降、Oracleヘッダーファイルが必要となるためです。) - OBCIがインストール済みであり、GCCコンパイラが事前にデプロイされていること。
ステップ1:データベース接続文字列の取得
OceanBase データベースのデプロイ担当者または管理者から、該当するデータベース接続文字列を取得します。例:
obclient -h100.88.xx.xx -usys@oracle -p****** -P2881
データベース接続文字列には、データベースへのアクセスに必要なパラメータ情報が含まれています。アプリケーションを作成する前に、データベース接続文字列を用いてデータベースへのログインを検証し、接続文字列のパラメータが正確であるか確認することができます。
パラメータの説明:
-h:OceanBaseデータベースへの接続IPアドレス。ODP (OceanBase Database Proxy)のアドレスである場合もあります。
-u:テナントの接続ユーザー名。書式はユーザー@テナント#クラスタ名となります。Oracleモードにおける管理者ユーザー名は、デフォルトで
sysです。データベースに直接接続する場合、クラスタ名は不要です。ODP を介して接続する場合は、クラスタ名を入力する必要があります。-p:ユーザーのパスワードです。
-P:OceanBaseデータベースの接続ポート番号であり、ODPのリスニングポートでもあります。
ステップ2:C関連ドライバーのインストール
RPMパッケージを入手したら、コマンドラインツールでrootユーザー権限を使い、以下のコマンドを実行してOBCIドライバをインストールします。
rpm -ivh obci-<version>.x86_64.rpm
rpm -ivh libobclient-<version>.x86_64.rpm
デフォルトでは、ソフトウェアパッケージに含まれるプログラムとファイルは、以下のパスにインストールされます:
ヘッダーファイルは、
/u01/obclient/includeディレクトリにインストールされます。ライブラリファイルは、
/u01/obclient/libディレクトリにインストールされています。
ステップ3:アプリケーションの作成
本記事では、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サーバーとデータをやり取りし、その後のデータ処理を行います。OBCIアプリケーションにおける、単一のSQL文の実行手順は以下の通りです:
a.
OCIStmtPrepare()またはOCIStmtPrepare2()関数を呼び出してSQL文を準備します。OCIStmtPrepare(stmthp, errhp, (text *)sql, strlen(sql), OCI_NTV_SYNTAX,OCI_DEFAULT)b.
OCIBindByPos()やOCIBindByName()など関数を1つまたは複数呼び出し、入力変数のアドレスをDML文のプレースホルダにバインド(紐つけ)します。OCIBindByPos(stmthp, &bidhp[0], errhp, 1, &szpersonid, sizeof(szpersonid), SQLT_INT, NULL, NULL, NULL, 0, NULL, 0) OCIBindByName(stmthp, &bidhp[0], errhp, (const OraText*)":personid", 9, &szpersonid, sizeof(szpersonid), SQLT_INT, NULL, NULL, NULL, 0, NULL, 0)c.
OCIStmrExecute()関数を呼び出して準備されたSQL文を実行します。OCIStmtExecute(svchp, stmthp, errhp, (ub4)1, (ub4)0, (CONST OCISnapshot *)0, (OCISnapshot *)0, (ub4)OCI_DEFAULT)d.
OCIDefineByPos()関数を呼び出して、SQL文のデータ出力項目を受け取るための出力変数を定義します。OCIDefineByPos(stmthp, &defhp[0], errhp, 1, &szpersonid, sizeof(szpersonid), SQLT_INT, &ind[0], 0, 0, OCI_DEFAULT)e.
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のコード内容は次のとおりです:
/**********************************************************
* Copyright(C) 2014 - 2020 Alibaba Inc. All Rights Reserved.
*
* Filename: ob_oci_test.c
* Description: ----
* Create: 2020-07-07 10:14:59
* Last Modified: 2020-07-07 10:14:59
***********************************************************/
#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, "172.30.xx.xx:2881");
strcpy(strUserName, "s**@oracle");
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;
}
コード内のデータベース接続パラメータ、ご自身の環境に合わせて修正してください。以下の項目を参考に、「ステップ1」で取得したデータベース接続文字列の対応する値を設定します。
strcpy(strServerName, "172.30.xx.xx:2881");
strcpy(strUserName, "s**@oracle");
strcpy(strPassword, "******");
strServerName:
-hと-Pの値から構成します。書式はIP:portとなります。これは、OceanBaseデータベースの接続IPアドレス(通常はODPアドレス)と、アクセスに使用するポート番号です。strUserName:
-uの値を使用します。テナントの接続ユーザー名で、書式はユーザー@テナント#クラスタ名となります。Oracleモードの管理者ユーザー名はデフォルトでsysです。データベースに直接接続する場合は、クラスタ名の部分は不要ですが、ODP を介して接続する場合は、ODP経由で接続する場合は指定する必要があります。strPassword:
-pの値を使用します。ユーザーのパスワードです。
ステップ 4:アプリケーションの実行
コードの編集が完了したら、以下のコマンドでコンパイルします:
//コンパイル
gcc test.c -I/u01/obclient/include /u01/obclient/lib/libobci.a -L/usr/local/lib64 -lstdc++ -lpthread -ldl -lm -g -o test
コンパイル完了後、次のコマンドでプログラムを実行します。以下のような結果が表示されれば、データベースへの接続に成功し、サンプルステートメントが正しく実行されたことを示します。
./test
PERSONID NAME email
1 obtest t***@ob.com
詳細情報
OBCIのインストール方法および使用方法の詳細については、公式リファレンス『OceanBase C言語呼び出しインターフェース』を参照してください。