ECOB(Embedded SQL C Preprocessor for OceanBase)は、OceanBaseデータベースのアーキテクチャ環境において、Oracle ProCと互換性のあるOceanBaseのプリコンパイラであり、ProcCと互換性のある完全な機能を提供します。
ProCは、Oracleデータベースエコシステムが提供するアプリケーション開発ツールです。C言語でアプリケーションを記述する際、プログラムのソースコードに直接SQL文を埋め込むことができます。これらの埋め込みSQL文は、ホストCプログラム内のC言語変数を入出力として利用できます。その後、ProCのプリコンパイラであるprocは、ソースコードに対して完全な構文解析を行うためのプリプロセッシングを実行し、埋め込みSQL文と指示をランタイムライブラリsqllibへの関数呼び出しに変換します。最終的に、C言語のプログラムソースコードファイルを出力します。このファイルをC言語コンパイラでコンパイル・リンクすることで、実行可能プログラムを生成できます。したがって、1つのprocプロジェクトは多数の.pcソースファイルで構成され、Makefileなどを用いてビルドされます。OceanBaseデータベースはPro*Cへのサポートを提供するため、同様にプリコンパイラecobとランタイムライブラリecoblibを含むプリコンパイラECOBを開発しました。また、移行コストを最小限に抑えるため、当社のプリコンパイラecobはprocプログラムと同じコマンドラインオプションと機能も提供しています。
関連する概念
.pc:組み込みSQLを含むC言語プログラムファイルの拡張子は
.pcです。ECOB:OceanBaseデータベースが提供する、組み込みSQLを含むC言語プログラムをコンパイルするためのプリコンパイラです。
ecoblib:OceanBaseデータベースがECOB用に提供するランタイムライブラリであり、ECOBが必要とする各種関数インターフェースを提供します。
Pro*C:Oracleが提供する、組み込みSQLを含むC言語プログラムをコンパイルするためのプリコンパイラです。
sqllib:OracleがProC用に提供するランタイムライブラリであり、ProCが必要とする各種関数インターフェースを提供します。
機能と特徴
OceanBaseデータベースのOracleモードと完全に互換性のあるSQL構文をサポートします。
COMMIT、CONNECT、DELETE、EXECUTE、EXECUTE IMMEDIATE、INSERT、SELECT、UPDATE、WHENEVER、CALL、PREPARE、ROLLBACKなどの基本的な組み込みSQLステートメントをサポートします。カーソル(Cursor)関連の
DECLARE、OPEN、CLOSE、FETCH(NEXT、ABSOLUTE、WITH HOLD、CURRENT OFを含む)などのステートメントをサポートします。ANSI標準の動的SQLに必要な
DESCRIPTORおよび関連するALLOCATE、DEALLOCATE、GET、SET、DESCRIBE、FETCHなどのステートメントをサポートします。ホスト変数は
BEGIN DECLARE SECTIONとEND DECLARE SECTIONの間で宣言しなくても直接使用できます。Cプリプロセッサのマクロ #ifdef、#ifndef、#else、#endif、#define の認識と処理をサポートします。
ecoblibライブラリは上記すべてのステートメントのランタイム動作をサポートしており、CHAR_MAPなどのprocプログラム互換動作を提供するだけでなく、Tuxedo環境でのプログラム実行もサポートします。
ecobプログラムはすべてのprocプログラムオプションと互換セマンティクス(例:PARSE=FULL)を認識し、procプログラムとシームレスに置き換えて使用できます。
データ型と変数
ECOBは、Oracleの内部データ型とC言語の外部データ型をサポートしており、それら間のデータ型変換も可能です。また、ECOBでは現在、インデクサー、VARCHAR、構造体、文字列ポインタなどの変数をサポートしています。
内部データ型
ECOBにおける内部データ型とは、OceanBaseデータベースで使用可能なデータ型を指します。現在ECOBがサポートする内部データ型は以下の表のとおりです。
内部データ型 |
説明 |
|---|---|
| CHAR | 固定長文字列。最大長は2000です。 |
| VARCHAR2 | 可変長文字列。最大長は32767です。 |
| NUMBER | 数値型。精度の範囲は [1,38]、桁数の範囲は [-84,127] です。 |
| INT | 整数型。最大値は38ビットです。 |
| BINARY_FLOAT | 32ビット浮動小数点数。最小値は1.17549E-38F、最大値は3.40282E+38Fです。 |
| BINARY_DOUBLE | 64ビット浮動小数点数。最小値は2.22507485850720E-308、最大値は1.79769313486231E+308です。 |
| DATE | 日付型。形式は YYYY-MM-DD HH:MI:SS です。 |
| TIMESTAMP | 時刻型。形式は YYYY-MM-DD HH:MI:SS [.FFFFFFFFF] です。 |
外部データ型
ECOBにおける外部データ型は、ホスト変数が格納するデータの型(C言語のデータ型)を指定するために使用されます。データをデータベースに入力する際、ECOBは入力されたホスト変数の外部データ型と内部データ型を変換します。データを外部プログラムに出力する際、ECOBはデータベース内のテーブルの内部データ型と出力先のホスト外部データ型を変換します。
以下の表は、ECOBがサポートする外部データ型と、それらが対応するホスト変数のデータ型を示しています。
外部データ型 |
説明 |
ホスト変数データ型 |
|---|---|---|
| ECOBt_char | 定長文字列を格納するために使用されます。 | char、char *、char [] |
| ECOBt_unsigned_char | 定長文字列を格納するために使用されます。 | unsigned char |
| ECOBt_short | 符号付き短整数を格納するために使用されます。 | int、int *、 int[] |
| ECOBt_unsigned_short | 無符号短整数を格納するために使用されます。 | unsigned int |
| ECOBt_int | 符号付き整数を格納するために使用されます。 | short 、short * 、short[] |
| ECOBt_unsigned_int | 無符号整数を格納するために使用されます。 | unsigned short |
| ECOBt_long | 符号付き長整数を格納するために使用されます。 | long、long * 、long[] |
| ECOBt_unsigned_long | 無符号長整数を格納するために使用されます。 | unsigned long |
| ECOBt_long_long | 符号付き長長整数を格納するために使用されます。 | long long、long long * 、long long[] |
| ECOBt_unsigned_long_long | 無符号長長整数を格納するために使用されます。 | unsigned long long |
| ECOBt_float | 32ビット浮動小数点数を格納するために使用されます。 | float 、float * 、float[] |
| ECOBt_double | 64ビット浮動小数点数を格納するために使用されます。 | double、double *、double[] |
| ECOBt_varchar | 可変長文字列を格納するために使用されます。 | varchar varchar * varchar[] |
| ECOBt_struct | 構造体を格納するために使用されます。 | struct 、struct *、struct[] |
データ型の変換
ecobプログラムでは、ホスト変数(Host Variables)に外部データ型が使用されます。データベースからC言語の変数にデータを読み込んだり、C言語のデータをデータベースに書き込んだりする際、外部データ型と内部データ型の間で変換が発生します。
以下の表は、データ型間の変換関係を示しています。ここで、IN はC言語からデータベース型への変換(データベースへのデータ書き込み)をサポートすることを示し、OUT はデータベース型からC言語への変換(データ読み取り)をサポートすることを示します。
varchar2/char |
int |
NUMBER |
FLOAT |
BINARY_FLOAT |
BINARY_DOUBLE |
Date |
TIMESTAMP |
|
|---|---|---|---|---|---|---|---|---|
| char/unsigned char | IN/OUT | IN/OUT | IN/OUT | IN/OUT | IN/OUT | IN/OUT | - | - |
| int/unsigned int | IN/OUT | IN/OUT | IN/OUT | IN/OUT | IN/OUT | IN/OUT | - | - |
| long/unsigned long | IN/OUT | IN/OUT | IN/OUT | IN/OUT | IN/OUT | IN/OUT | - | - |
| long long/unsigned long long | IN/OUT | IN/OUT | IN/OUT | IN/OUT | IN/OUT | IN/OUT | - | - |
| float | IN/OUT | IN/OUT | IN/OUT | IN/OUT | IN/OUT | IN/OUT | - | - |
| double | IN/OUT | IN/OUT | IN/OUT | IN/OUT | IN/OUT | IN/OUT | - | - |
| char[n]/varchar[n]/char* | IN/OUT | IN/OUT | IN/OUT | IN/OUT | IN/OUT | IN/OUT | IN/OUT | IN |
インディケーター変数
インディケーター変数は、データベースの NULL 値を処理するために使用される変数です。SELECT または FETCH ステートメントを実行する際、インディケーター変数を使用せずに列が NULL 値を返した場合、エラーメッセージが表示されます。インディケーター変数は short 型で定義する必要があり、ホスト変数の後に続けて記述する必要があります。
インディケーター変数の構文は以下のとおりです:
:host_variable [INDICATOR] :indicator_variable
インディケーター変数を使用すると、以下のルールに基づいて返された列が NULL かどうかを検出できます:
インディケーター変数が -1 を返す場合、データベースが
NULL値を返したことを意味します。インディケーター変数が 0 を返す場合、列の値が出力ホスト変数に代入されたことを意味します。
インディケーター変数が 0 より大きい値を返す場合、切り捨てられた列の値が出力ホスト変数に代入され、インディケーター変数にはデータ列の実際の長さが格納されていることを意味します。
インディケーター変数が -2 を返す場合、切り捨てられた列の値が出力ホスト変数に代入されたが、実際の長さが特定できないことを意味します。
VARCHAR変数
VARCHAR 変数は、VARCHAR型で定義された可変長文字列です。VARCHAR 型はホスト変数の定義にのみ使用でき、通常のC変数の定義には使用できません。そのため、VARCHAR 変数を定義する際には、長さを指定する必要があります。例文は以下のとおりです:
VARCHAR name[20];
上記のVARCHAR変数はプリコンパイルされると、以下のようなC構造体が生成されます:
struct{
unsigned short len;
unsigned char arr[20];
}name;
構造体の中で、arr は文字列に対応し、len は文字列の実際の長さを表します。
構造体と文字列ポインタ
ecobプログラムで構造体と文字列ポインタをホスト変数(Host Variables)として使用する際、注意が必要な点がいくつかあります:
構造体(Struct)をホスト変数として使用する場合、全体として渡すことができます。プリコンパイル時に、構造体は自動的に各データベース列に分割されます。
ポインタを結果として出力する場合、NULL以外に初期化する必要があります。例文は以下のとおりです:
char * p1 ; char * p2; p1=(char *) malloc(11); p2=(char*) malloc(11); strcpy(p1," "); strcpy(p2,"00000"); EXEC SQL SELECT c1,c2 into :p1,:p2 from t1 where rownum < 2;
組み込みSQL
ECOBがサポートする組み込みSQL文には、すべての標準SQL文が含まれており、ホストプログラムとOBServerサービス間でデータを受け渡すための拡張SQL文もいくつか含まれています。ECOBで組み込みSQL文を使用する際の構文は以下のとおりで、文の前にEXEC SQLを追加する必要があります。
EXEC SQL <standard SQL statement>|<extension SQL statement>
説明
この章では、ECOBがサポートする拡張SQL文とECOB固有のSQL文について説明します。標準SQL文の構文については、「OceanBaseデータベースSQLリファレンス(Oracleモード)」マニュアルを参照してください。
変数宣言
ECOBでは、DECLARE SECTIONステートメントはホスト変数を宣言するために使用されます。これはECOB独自のSQLステートメントです。構文は以下のとおりです:
EXEC SQL BEGIN DECLARE SECTION;
...
EXEC SQL END DECLARE SECTION;
例文:
EXEC SQL BEGIN DECLARE SECTION;
int a;
char * b;
EXEC SQL END DECLARE SECTION;
宣言ブロック内では、以下の要素のみを使用できます:
ホスト変数またはインジケータ変数
C言語のコメント
EXEC SQL INCLUDEステートメント
キーワードtypedef
注意点として、parse=fullオプションを指定した場合(デフォルト値はfull)、ほとんどのケースでホスト変数はDECLARE SECTIONステートメントで宣言する必要はありません。ただし、構造体にVARCHAR型の変数が含まれる場合は、宣言ブロック内で宣言する必要があります。parse=NONEまたはparse=partialオプションを指定した場合は、ホスト変数はDECLARE SECTIONステートメントで宣言する必要があります。
CONNECTデータベース
ECOBが現在サポートしている拡張SQLステートメントのうち、CONNECTステートメントはOceanBaseデータベースとの接続を確立するために使用されます。構文は以下のとおりです:
構文1:EXEC SQL CONNECT <:username> identified by <:password> (using <:dbstring>)
構文2:EXEC SQL CONNECT <:user_password> (using <:dbstring>)
構文3:EXEC SQL CONNECT <:username> identified by <:password> (AT <:dbname>) (using <:dbstring>)
各変数の意味は以下のとおりです:
<:username>:
OceanBaseデータベースのOracleテナントに接続するためのユーザー名。形式はユーザー名@テナント名で、例えば
test@oracleのようになります。OBProxyを使用して接続する場合は、クラスタ名を追加し、形式はユーザー名@テナント名#クラスタ名となります。例えばtest@oracle#cluster1のようになります。<:password>:
OceanBaseデータベースのOracleテナントに接続するためのパスワード。
<:dbstring>:
OceanBaseデータベースのOracleテナントに接続するためのサービス名。このサービス名は、
TNS_ADMIN環境変数で指定されたtnsnames.oraファイルの接続文字列の名前です。もしtnsnames.oraファイル内の接続文字列が以下のようになっている場合:demo= (DESCRIPTION= (ADDRESS=(PROTOCOL = TCP)(HOST = 10.10.10.10)(PORT = 30035)) (CONNECT_DATA= (SERVICE_NAME=TEST)) )その場合、
<:dbstring>の値はdemoとなります。IP+Port方式の接続文字列を使用する場合、<:dbstring>の値は 'ip:port/dbname' のように記述することもできます。例えば、'10.10.10.10:30035/test' のようになります。<:user_password>:
OceanBaseデータベースのOracleテナントに接続するためのユーザー名とパスワードです。例:testUser@oracle/******。ユーザー名とパスワードはスラッシュ(/)で区切られます。
使用例:
//connect method 1 char * username ="test***@oracle"; char * password = "******"; char * servicename = "obdb"; EXEC SQL CONNECT :username identified by :password using :servicename; //connect method 2 char * userpass = "test***@oracle/welcome1"; char * servicename = "obdb"; EXEC SQL CONNECT :userpass using :servicename; //connect method 3 char * username ="test***@oracle"; char * password = "******"; char * servicename = "obdb"; char * connname = "democonn"; EXEC SQL CONNECT :username identified by :password AT :connname using :servicename;
基本SQL文
ECOBが現在サポートしている拡張SQL文の中で、基本SQL文には SELECT、INSERT、UPDATE、DELETE、COMMIT、ROLLBACK の各文があります。
SELECT文は、クエリを実行し、その結果を外部ホスト変数に出力します。INSERT文は、挿入文を実行し、外部ホスト変数の値をデータベースのテーブル列に挿入します。UPDATE文は、更新文を実行し、外部データ変数の値をデータベースのテーブル列に更新します。また、現在のカーソル列のみを更新することも選択できます。DELETE文は、削除文を実行し、データベースの1行または複数行を削除します。現在のカーソル列のみを削除することも選択できます。COMMIT文は、トランザクションをコミットします。リソースの解放とデータベース接続の閉じ方を選択できます。ROLLBACK文は、トランザクションをロールバックします。リソースの解放とデータベース接続の閉じ方を選択できます。
プリペアドステートメント
ECOBが現在サポートしている拡張SQL文の中で、プリペアドステートメント(Prepared Statement)に関連する文には PREPARE 文と EXECUTE 文があります。これらは動的SQL文であり、固定数の入力および出力ホスト変数を含むことができます。
PREPARE文は、PSモードでSQL文をプリペアするために使用されます。EXECUTE文は、コンパイル済みのSQL文を実行するために使用されます。
ストアドプロシージャ
ECOBが現在サポートしている拡張SQL文の中で、CALL 文はストアドプロシージャを実行するために使用されます。現在のバージョンでは、パラメータ OUT を持たないストアドプロシージャのみがサポートされています。
カーソル
ECOBが現在サポートしている拡張SQL文の中で、カーソル(Cursor)に関連する文には DECLARE CURSOR、OPEN、FETCH、CLOSE などの文があります。
DECLARE CURSOR文は、カーソル変数を定義するために使用されます。現在のカーソル変数はクエリ文のみをサポートします。OPEN文は、カーソル変数を開くために使用されます。FETCH文は、カーソル変数が保持する結果セットを取得するために使用されます。スクロール可能なカーソルの場合、FETCHの位置を指定できます。CLOSE文は、カーソル変数を閉じるために使用されます。
簡単な動的SQL
ECOBが現在サポートしている拡張SQLステートメントの中で、EXECUTE IMMEDIATE ステートメントは動的なSQL文字列を実行するために使用されます。この種のステートメントでは、SQL文は実行時に生成できますが、ホスト変数は使用できません。
ANSI動的SQL
ECOBが現在サポートしている拡張SQLステートメントには、ANSI標準に準拠した動的SQLステートメントが含まれています。現在サポートされているのは ALLOCATE DESCRIPTOR、DEALLOCATE DESCRIPTOR、DESCRIBE INPUT DESCRIPTOR、DESCRIBE OUTPUT DESCRIPTOR、GET DESCRIPTOR、SET DESCRIPTOR、OPEN USING DESCRIPTOR、および FETCH INTO DESCRIPTOR などのステートメントです。SQL文の入力または出力に使用されるホスト変数の数がプリコンパイル時に不明な場合、ANSI動的SQLステートメントを使用できます。
ALLOCATE DESCRIPTORステートメントはSQL記述領域を割り当てるために使用されます。DEALLOCATE DESCRIPTORステートメントはSQL記述領域を解放するために使用されます。DESCRIBE INPUT DESCRIPTORステートメントは変数情報をバインドするために使用されます。DESCRIBE OUTPUT DESCRIPTORステートメントは出力列の情報を取得するために使用されます。GET DESCRIPTORステートメントはSQL記述領域のItem情報を取得するために使用されます。SET DESCRIPTORステートメントはSQL記述領域のItem情報を設定するために使用されます。OPEN USING DESCRIPTORステートメントはANSI動的SQLでカーソル変数を開くために使用されます。FETCH INTO DESCRIPTORステートメントは動的ステートメント記述子を使用して、カーソル変数に保存された結果セットを取得します。
エラー処理
SQLCA構造
現在、ECOBはThe SQL Communications Area(SQLCA)データ構造を実装しています。現在の実装では、#include<sqlca.h> または EXEC SQL INCLUDE SQLCA ステートメントを明示的に使用する必要はありません。SQLCA構造体には、sqlcode、sqlerrm、sqlerrdなどのメンバーがあります。
sqlcodeの取り得る値の範囲は以下のとおりです:
値が0の場合、成功を意味します。
値が0未満の場合、ステートメントがデータベースで実行失敗したことを意味します。具体的なエラーコードはOracleと異なる場合があります。アプリケーションでは、
sqlca.sqlcode<0の判断を直接行うことを推奨します。値が0より大きい場合、データベースで適切なデータが見つからなかったことを意味します。現在、0より大きい値としては1403のみがサポートされており、対応するメッセージは
DATA NOT FOUNDです。
sqlerrm構造体には、sqlerrmlとsqlerrmcという2つのメンバーがあります。sqlerrmlはエラーメッセージの長さを格納し、sqlerrmcはエラー情報を保存します。
sqlerrdは長さ6のlong型配列です。インデックス0、1、3、4、5の要素の値は空です。インデックス2の要素には、INSERT、UPDATE、DELETE などのステートメントで処理された行数が保存されます。
これらの関数として、sqlglsは最後に実行されたSQLステートメントを取得するために、sqlglmは最後に実行された操作のエラー情報を取得するために提供されています。
ここで、sqlgls が返すのはSQLステートメント、stmlen が返すのはSQLステートメントの長さ、sqlfc が返すのはSQL機能コードです。sqlglm が返すのはエラー情報で、buffer_size はバッファの最大長、message_length はエラー情報の実際の長さです。 WHENEVERステートメント
WHENEVER ステートメントは、エラーおよび警告条件の処理方法を指定するために使用されます。
構文は以下のとおりです:
EXEC SQL WHENEVER (SQLERROR | NOT FOUND) ( DO (routine | BREAK | CONTINUE) | CONTINUE | GOTO <label> | STOP )
ここで、SQLERROR は現在実行中のステートメントでエラーが発生した場合に、ステートメント内で定義されたActionを実行してエラー処理を行うことを意味し、NOT FOUND は現在実行中のステートメントでデータが見つからなかった場合に、ステートメント内で定義されたActionを実行して例外処理を行うことを意味します。
サポートされているActionは以下のとおりです:
CONTINUE:実行を続けます。これがデフォルトの動作であり、何の例外も処理しない場合と同じです。DO:エラー処理関数を実行します。DO BREAK:プログラム内にBREAKステートメントを追加したもので、LOOPループ内で使用されます。DO CONTINUE:プログラム内にCONTINUEステートメントを追加したもので、LOOPループ内で使用されます。GOTO label_name:プログラムのlabel_nameにジャンプします。STOP:プログラムを終了します。コミットされているトランザクションはすべてロールバックされ、EXIT()関数を実行した場合と同様になります。
注意点として、WHENEVER ステートメントは、その後に続くすべての組み込みステートメントに対して有効になります。特にAction DO BREAKとDO CONTINUEについては、これらのActionはループ体の内部にある必要があり、そうでない場合はエラーが発生します。そのため、Action DO BREAKまたはDO CONTINUEの後にループ体がないDMLステートメントについては、そのステートメントの前で例外処理の方法をリセットする必要があります。
{
EXEC SQL WHENEVER SQLERROR DO sqlerror();
EXEC SQL INSERT INTO t1 VALUES(1,'ABC');
...
}
void sqlerror(){
...
}
また例を挙げると
int c1val;
EXEC SQL DECLARE cur CURSOR FOR select c1 from t1;
EXEC SQL WHENEVER NOT FOUND DO BREAK;
for(;;){
EXEC SQL FETCH cur INTO :c1val;
}