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については、これら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;
}