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がサポートする外部型と、それらとホスト変数データ型との対応関係を示しています。
| 外部データ型 | 説明 | ホスト変数のデータ型 |
|---|---|---|
| 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)をホスト変数として使用する場合、全体として渡すことができます。プリコンパイル時に、構造体は自動的に各データベース列に分割されます。
ポインタを結果として出力する場合、空でないよう初期化する必要があります。例文は以下のとおりです:
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アドレスとポート番号を使用した接続文字列の場合、<: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記述領域のアイテム情報を取得するために使用されます。SET DESCRIPTOR文はSQL記述領域のアイテム情報を設定するために使用されます。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 FUNCTION codeを返します。sqlglm はエラー情報を返し、buffer_size はbufferの最大長、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については、これら2つの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;
}