この章では、C、C++およびJavaを使用してデータ・カートリッジのメソッドを実装する方法について説明します。メソッドとは、データ・カートリッジを使用して定義されたデータについて、許可される操作を定義するプロシージャおよびファンクションです。ここでは、外部プロシージャの開発とデバッグに関連する事項を重点的に取り扱います。
この章の内容は、次のとおりです。
PL/SQLは、強力なデータベース・プログラミング言語です。ただし一部のメソッドは、PL/SQLで最適なコーディングを行うには複雑すぎます。たとえば、数値の統合を実行するルーチンは、PL/SQLPL/SQLで実装するよりもCで実装する方が高速で実行されると思われます。
このように特化された処理をサポートするために、PL/SQLは他の言語で記述されたルーチンをコールするためのインタフェースを提供します。これにより、Cのような3GLのメリットと機能がデータベース・サーバーからのコールを介して使用可能になります。このような3GLルーチンは外部プロシージャと呼ばれ、共有ライブラリに格納され、PL/SQLに登録されてから、実行時にPL/SQLからコールされます。
外部プロシージャは、データ・カートリッジ開発者にとって重要なツールです。外部プロシージャを使用すると、カートリッジ・タイプ用の高速で効率的な計算集中型のルーチンを記述できるのみでなく、既存のコードをデータ・カートリッジとしてデータベースと統合できます。オーディオ・ファイルのフォーマット変換を実行するCのルーチンを含んだWindows NT DLLのように、その他の言語での既存の共有ライブラリは、オーディオ・カートリッジにより実装される型のメソッドから直接コールできます。同様に、外部プロシージャを使用して、信号の処理、デバイスの駆動、データ・ストリームの分析、グラフィックスのレンダリングまたは数値データの処理を行うことができます。
|
関連項目 外部プロシージャおよび使用方法の詳細は、『PL/SQLユーザーズ・ガイドおよびリファレンス』を参照してください。 |
共有ライブラリとは、Windows DLLやSolaris共有オブジェクトのように、外部プロシージャの実装コードを格納するオペレーティング・システム・ファイルです。Oracleから共有ライブラリへのアクセスには、別名ライブラリPL/SQL内でライブラリを表すスキーマ・オブジェクト)が使用されます。セキュリティ上の理由で、別名ライブラリを作成するにはDBA権限が必要です。
別名ライブラリを作成するには、オペレーティング・システム上でのライブラリの位置を決定し、データベース管理者またはCREATE LIBRARY権限を持つユーザーとしてログインし、例5-1で示す文を入力する必要があります。これでデータベースに別名ライブラリ・スキーマ・オブジェクトが作成されます。別名ライブラリの作成後は、共有ライブラリをPL/SQLから名前DS_Libで参照できます。
例5-1では、ライブラリの絶対パスを指定しています。ライブラリのコピーを複数のシステムで使用する場合は、指定(または専用)のエージェントによる外部プロシージャの分散実行をサポートするために、例5-2に示すように環境変数を使用してライブラリの相対位置を指定できます。この文では、${DS_LIB_HOME}環境変数を使用して、すべてのシステム上でライブラリを検出できる共通の参照ポイントまたはルート・ディレクトリを指定しています。AGENTキーワードに続く文字列は、ライブラリDS_Libに宣言される外部プロシージャの実行に使用するエージェント(実際にはデータベース・リンク)を指定します。
例5-2 環境変数を使用したライブラリの位置の指定方法
CREATE OR REPLACE LIBRARY DS_Lib AS
'${DS_LIB_HOME}/libdatastream.so' AGENT 'agent_link';
|
関連項目 専用の外部プロシージャ・エージェントの使用に関する詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください。 |
外部プロシージャをコールするには、外部プロシージャを定義する別名ライブラリのみでなく、プロシージャのコール方法や渡す引数も、PL/SQLに対して指示する必要があります。
例3-1ではDataStream型が定義されており、例3-2では例4-9で指定されているDS_PackageパッケージからのファンクションをコールすることでDataStreamのメソッドが定義されています。例5-3では、このパーケージの本体を定義します。
例5-3 パッケージ本体の定義方法
CREATE OR REPLACE PACKAGE BODY DS_Package AS
FUNCTION DS_Findmin(data CLOB) RETURN PLS_INTEGER IS EXTERNAL
NAME "c_findmin" LIBRARY DS_Lib LANGUAGE C WITH CONTEXT;
FUNCTION DS_Findmax(data CLOB) RETURN PLS_INTEGER IS EXTERNAL
NAME "c_findmax" LIBRARY DS_Lib LANGUAGE C WITH CONTEXT;
END;
PACKAGE BODY宣言句で、パッケージ・ファンクションが共有ライブラリ内の外部プロシージャに結合されることに注意してください。ファンクション宣言のEXTERNAL句では、名前(NAMEキーワードの後)、位置(必ず別名ライブラリ、LIBRARYキーワードの後)、外部プロシージャの記述言語(LANGUAGEキーワードの後)など、外部プロシージャに関する情報が登録されます。
この例では、EXTERNAL句の最後の部分はWITH CONTEXT指定です。これは、外部プロシージャにコンテキスト・ポインタが渡されることを意味します。コンテキスト・ポインタは外部プロシージャに対して不透明ですが、使用可能であるため、外部プロシージャはOracleサーバーにコールバックし、同じトランザクション・コンテキストでより多くのデータにアクセスできる可能性があります。
この例では、オブジェクト型のメソッドからの外部プロシージャ・コールについて説明していますが、データ・カートリッジではPL/SQLの他の様々な場所から外部プロシージャを使用できます。外部プロシージャ・コールは次の場所で使用できます。
無名ブロック
スタンドアロン・サブプログラムおよびパッケージ・サブプログラム
オブジェクト型のメソッド
データベース・トリガー
SQL文(パッケージ・ファンクションのコールのみ)
|
関連項目
|
外部プロシージャをコールするには、プロシージャの常駐するDLLまたは共有ライブラリがPL/SQLで認識される必要があります。PL/SQLは、外部プロシージャを登録したサブプログラムのEXTERNAL句にある別名ライブラリを参照します。データ・ディクショナリを使用して、オペレーティング・システムの共有ライブラリまたはDLLへの実際のパスが判別されます。
PL/SQLはリスナー・プロセスにアラートを送り、リスナー・プロセスはセッション固有のエージェントを起動します。他に特定のエージェントが指定されていないかぎり、プロシージャの特定のライブラリに対するCREATE LIBRARY文、またはCREATE PROCEDURE文のエージェント引数で、デフォルト・エージェントextprocが起動されます。リスナーは、接続をエージェントに渡します。PL/SQLは、DLL名、外部プロシージャ名およびコール元から渡されたパラメータをエージェントに渡します。この説明の残りの部分では、起動されるエージェントがデフォルト・エージェントextprocであるとします。
DLL名と外部プロシージャ名を受け取ったextprocは、DLLをロードして外部プロシージャを実行します。また、extprocはサービス・コール(例外の発生など)とOracleサーバーへのコールバックも処理します。最後に、extprocは外部プロシージャから戻された値をPL/SQLに渡します。図5-1に、制御フローを示します。
外部プロシージャが完了した後も、extproc はOracleセッション全体でアクティブになっています。そのため、extprocの起動コストが発生するのはコール回数に関係なく1回のみです。外部プロシージャは、計算上のメリットがコストを上回る場合にのみコールする必要があります。(ログオフ時にextprocが停止します。)
リスナーは、Oracleサーバーが稼働しているシステム上でextprocを起動する必要があることに注意してください。他のシステム上でのextprocの起動はサポートされていません。
リスナーが外部プロシージャをディスパッチできるように、構成ファイルlistener.oraおよびtnsnames.oraに適切なエントリが必要です。
リスナー構成ファイルlistener.oraには、例5-4で示すように外部プロシージャに関するSID_DESCエントリが必要です。
例5-4 リスナー構成ファイルでSID_DESCエントリを設定する方法
# Listener configuration file # This file is generated by stkconf.tsc CONNECT_TIMEOUT_LISTENER = 0 LISTENER = (ADDRESS_LIST= (ADDRESS=(PROTOCOL=ipc)(KEY=o8)) (ADDRESS=(PROTOCOL=tcp)(HOST=unix123)(PORT=1521)) ) SID_LIST_LISTENER = (SID_LIST= SID_DESC=(SID_NAME=o8)(ORACLE_HOME=/rdbms/u01/app/oracle/product/8.0.3)) (SID_DESC=(SID_NAME=extproc)(ORACLE_HOME=/rdbms/u01/app/oracle/product/8.0.3) (PROGRAM=extproc)) )
例5-4では、次のことを想定しています。
Oracleインスタンス名がo8であること。
Oracleサーバーが実行されるシステムまたはノードの名前がunix123であること。
Oracleサーバーのインストール・ディレクトリが/rdbms/u01であること。
Oracle TCP/IP通信のポート番号がデフォルトのリスナー・ポート1521であること。
ネットワーク基板構成ファイルのtnsnames.oraファイルも、例5-5で示すように、外部プロシージャを参照するように更新する必要があります。
例5-5 外部プロシージャを参照してネットワーク基板構成ファイルを更新する方法
o8 = (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=unix123)(PORT=1521)) (CONNECT_DATA=(SID=o8))) extproc_connection_data = (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=o8)) CONNECT_DATA=(SID=extproc)))
例5-5では外部プロシージャとの通信にIPCメカニズムが使用されることを想定しています。通信にはTCP/IPなども使用でき、その場合はPROTOCOLパラメータをtcpに設定する必要があります。
|
関連項目 listener.oraファイルおよびtnsnames.oraファイルの構成の詳細は、『Oracle Database管理者ガイド』を参照してください。 |
外部プロシージャへのパラメータの受渡しは、次のような状況により複雑化します。
PL/SQLのデータ型セットには、Cのデータ型セットとの1対1の対応関係がありません。
PL/SQLのパラメータにはnullを使用できますが、Cのパラメータには使用できません。Cとは異なり、PL/SQLには無効というRDBMSの概念が組み込まれています。
外部プロシージャは、CHAR、LONG RAW、RAWおよびVARCHAR2パラメータの現行の長さまたは最大長を必要とする場合があります。
外部プロシージャは、CHAR、VARCHAR2およびCLOBパラメータのキャラクタ・セット情報を必要とする場合があります。
PL/SQLは、外部プロシージャから戻される値の現行の長さ、最大長またはNULLステータスを必要とする場合があります。
以降の各項では、このような状況に対処するパラメータ・リストの指定方法について説明します。パラメータの受渡しの例は、例5-6を参照してください。この場合、パッケージ・ファンクションDS_Findmin(data CLOB)はCのルーチンc_findminをコールし、CLOB引数がCのルーチンにOCILobLocator()として渡されます。
外部プロシージャにパラメータを直接渡すことはありません。かわりに、外部プロシージャを登録したPL/SQLサブプログラムに渡します。そのため、パラメータのPL/SQLデータ型を指定する必要があります。表5-1では各PL/SQLデータ型はデフォルトの外部データ型に対応します。外部データ型はCのデータ型に対応します。
表5-1 パラメータのデータ型マッピング
| PL/SQL型 | サポートされている外部型 | デフォルトの外部型 |
|---|---|---|
BINARY_INTEGER, BOOLEAN, PLS_INTEGER |
CHAR, UNSIGNED CHAR, SHORT, UNSIGNED SHORT, INT, UNSIGNED INT, LONG, UNSIGNED LONG, SB1, UB1, SB2, UB2, SB4, UB4, SIZE_T |
INT |
NATURAL, NATURALN, POSITIVE, POSITIVEN, SIGNTYPE |
CHAR, UNSIGNED CHAR, SHORT, UNSIGNED SHORT, INT, UNSIGNED INT, LONG, UNSIGNED LONG, SB1, UB1, SB2 ,UB2, SB4, UB4, SIZE_T |
UNSIGNED INT |
FLOAT, REAL |
FLOAT |
FLOAT |
DOUBLE PRECISION |
DOUBLE |
DOUBLE |
CHAR, CHARACTER, LONG, ROWID, VARCHAR, VARCHAR2 |
STRING |
STRING |
LONG RAW, RAW |
RAW |
RAW |
BFILE, BLOB, CLOB |
OCILOBLOCATOR |
OCILOBLOCATOR |
PARAMETERS句を使用してデフォルトのデータ型マッピングをオーバーライドできる場合があります。たとえば、PL/SQLデータ型BOOLEANを外部データ型INTからCHAR外部データ型に再マップできます。
Cのプロトタイプ・パラメータを宣言するときのエラーを回避するために、表5-2を参照してください。表5-2は、特定の外部データ型とPL/SQLパラメータ・モードについて指定するCのデータ型を示しています。たとえば、OUTパラメータの外部データ型がCHARの場合、Cのプロトタイプではchar*データ型を指定します。
表5-2 外部データ型のマッピング
| 外部データ型 | IN、RETURN | 参照渡しのIN、参照渡しのRETURN | IN OUT、OUT |
|---|---|---|---|
CHAR |
char |
char * |
char * |
UNSIGNED CHAR |
unsigned char |
unsigned char * |
unsigned char * |
SHORT |
short |
short * |
short * |
UNSIGNED SHORT |
unsigned short |
unsigned short * |
unsigned short * |
INT |
int |
int * |
int * |
UNSIGNED INT |
unsigned int |
unsigned int * |
unsigned int * |
LONG |
long |
long * |
long * |
UNSIGNED LONG |
unsigned long |
unsigned long * |
unsigned long * |
SIZE_T |
size_t |
size_t * |
size_t * |
SB1 |
sb1 |
sb1 * |
sb1 * |
UB1 |
ub1 |
ub1 * |
ub1 * |
SB2 |
sb2 |
sb2 * |
sb2 * |
UB2 |
ub2 |
ub2 * |
ub2 * |
SB4 |
sb4 |
sb4 * |
sb4 * |
UB4 |
ub4 |
ub4 * |
ub4 * |
FLOAT |
float |
float * |
float * |
DOUBLE |
double |
double * |
double * |
STRING |
char * |
char * |
char * |
RAW |
unsigned char * |
unsigned char * |
unsigned char * |
OCILOBLOCATOR |
OCILobLocator * |
OCILobLocator * |
OCILobLocator ** |
オプションでPARAMETERS句を使用すると、PL/SQLの仮パラメータとファンクション戻り値に関する追加情報を外部プロシージャに渡すことができます。この句を使用してパラメータを再配置することもできます。
|
関連項目 『Oracle Database PL/SQL言語リファレンス』 |
起動された外部プロシージャは、データベースへのアクセスを必要とする場合があります。たとえば、DS_FindminがCLOBデータ全体をc_findminに上書きコピーすることはありません。これは、上書きコピーするとCのルーチンに必要なスタックの量が大幅に増大するためです。かわりに、PL/SQLファンクションは、データベースがCから再アクセスされて実際のLOBデータが読み取られるように、単にCのルーチンにLOBロケータを渡します。
Cのルーチンは、データの読取り時に、LOBに関連付けられているOCIのバッファリングおよびストリーム化インタフェースを使用できるため、スタック量は必要に応じて段階的に増えるのみです。このように外部プロシージャからデータベースに再アクセスすることをコールバックと呼びます。
データベースへのコールバックを可能にするには、WITH CONTEXT句を使用してデータベース環境、サービスおよびエラー・ハンドルへのアクセスを外部プロシージャに付与する必要があります。WITH CONTEXTを使用して外部プロシージャをコールすると、対応するCのルーチンは自動的に最初のパラメータとしてOCIExtProcContext*型の引数を取得します。パラメータの順序はPARAMETERS句を使用して変更できます。このコンテキスト・ポインタを使用し、OCIExtProcGetEnvコールを使用してハンドルをフェッチし、データベースをコールバックできます。このプロシージャについては、例5-6を参照してください。
|
関連項目 OCIコールバックの詳細は、『Oracle Call Interfaceプログラマーズ・ガイド』を参照してください。 |
Oracleサーバー上で実行される外部プロシージャは、アクセス・ファンクションOCIExtProcGetEnv()をコールしてOCI環境およびサービス・ハンドルを取得できます。OCIでは、コールバックを使用してSQL文とPL/SQLサブプログラムを実行し、データをフェッチしてLOBを操作できます。さらに、コールバックと外部プロシージャは同じユーザー・セッションおよびトランザクション・コンテキストで動作するため、両者のユーザー権限も同じです。
例5-6に、コールバックを示すために簡略化されたc_findminのバージョンを示します。
例5-6 コールバックの使用方法
Static OCIEnv *envhp;
Static OCISvcCtx *svchp;
Static OCIError *errhp;
Int c_findmin (OCIExtProcContext *ctx, OCILobLocator *lobl) {
sword retval;
retval = OCIExtProcGetEnv (ctx, &envhp, &svchp, &errhp);
if ((retval != OCI_SUCCESS) && (retval != OCI_SUCCESS_WITH_INFO))
exit(-1);
/* Use lobl to read the CLOB, compute the minimum, and store the value
in retval. */
return retval;
}
コールバックを使用する場合、次のSQL文とOCIルーチンはサポートされません。
トランザクション制御文(COMMITなど)
データ定義文(CREATEなど)
オブジェクト指向のOCIルーチン(OCIRefClearなど)
ポーリング・モードのOCIルーチン(OCIGetPieceInfoなど)
OCIルーチンは次のとおりです。
OCIEnvInit()
OCIInitialize()
OCIPasswordChange()
OCIServerAttach()
OCIServerDetach()
OCISessionBegin ()
OCISessionEnd ()
OCISvcCtxToLda()
OCITransCommit()
OCITransDetach()
OCITransRollback()
OCITransStart()
また、OCIルーチンOCIHandleAlloc()では、次のハンドル・タイプはサポートされません。
OCI_HTYPE_SERVER
OCI_HTYPE_SESSION
OCI_HTYPE_SVCCTX
OCI_HTYPE_TRANS
この項では、外部プロシージャの実行時に考えられる数種類のエラーについて説明します。
通常、外部プロシージャに失敗する場合は、Cのプロトタイプにエラーがあります。つまり、プロトタイプがPL/SQLにより内部的に生成されたプロトタイプと一致していません。互換性のないCデータ型を指定すると、このエラーが発生することがあります。たとえば、REAL型のOUTパラメータを渡すには、float *を指定する必要があります。float、double *または他のCデータ型を指定すると不一致となります。
このような場合は、「外部プロシージャ・エージェントへのRPC接続が失われました。」エラーが表示されます。これは、外部プロシージャによりコア・ダンプが発生したために、エージェントextprocが異常終了したことを意味します。Cプロトタイプ・パラメータの宣言時のエラーを回避するには、表5-2を参照してください。
外部プロシージャのデバッグに役立つように、PL/SQLにはユーティリティ・パッケージDEBUG_EXTPROCが用意されています。このパッケージをインストールするには、PL/SQLデモ・ディレクトリにあるスクリプトdbgextp.sqlを実行します。
このパッケージを使用するには、dbgextp.sql内の指示に従います。Oracleアカウントには、パッケージに対するEXECUTE権限とCREATE LIBRARY権限が必要です。
DEBUG_EXTPROCが動作するのは、実行中のプロセスに添付できるデバッガのあるプラットフォームのみであることに注意してください。
Windows NTシステムで開発している場合は、次の追加アクションを実行して外部プロシージャをデバッグできます。
Windows NT Task Managerを起動します([Ctrl]+[Alt]+[Del]を押して「タスク マネージャ」を選択します)。
「プロセス」タブで「ExtProc.exe」を選択します。
右クリックして「デバッグ」を選択します。
メッセージ・ボックスで「OK」を選択します。
この時点で、Microsoft Visual C++を使用してデバッグ・モードでDLLを作成した場合は、Visual C++がアクティブ化されます。
Visual C++ウィンドウで、「編集」→「ブレークポイント」を選択します。
PL/SQLデモ・ディレクトリにあるdbgextp.sqlで識別されているブレークポイントを使用します。
必ずスレッド・セーフな外部プロシージャを記述してください。特に、個別スレッドで実行中のルーチンにより共有される可能性があるため、静的変数は使用しないでください。
動的リンク・ライブラリ作成のヘルプは、RDBMSサブディレクトリ/publicにあります。このサブディレクトリにはテンプレートmakefileファイルがあります。
外部プロシージャのコール時には、INパラメータに書き込んだりOUTパラメータの容量をオーバーフローしないでください。PL/SQLでは、このようなエラー条件のランタイム・チェックは実行されません。同様に、OUTパラメータやファンクションの結果は読み取らないでください。また、値は必ずIN OUTおよびOUTパラメータとファンクションの結果に割り当てます。それ以外の場合、外部プロシージャで正常に値が戻されません。
WITH CONTEXTおよびPARAMETERS句を挿入する場合は、パラメータ・リスト内でコンテキスト・ポインタの位置を示すCONTEXTパラメータを指定する必要があります。PARAMETERS句を省略すると、コンテキスト・ポインタは外部プロシージャに渡される最初のパラメータになります。
PARAMETERS句を挿入する場合に、外部プロシージャがファンクションであれば、最後の位置に(RETURNプロパティではなく)RETURNパラメータを指定する必要があります。
仮パラメータごとに、対応するパラメータをPARAMETERS句に指定する必要があります。また、暗黙的変換は実行されないため、PARAMETERS句のパラメータのデータ型に、Cプロトタイプ内のデータ型との互換性があることを確認してください。
INDICATORまたはLENGTHを指定するパラメータのパラメータ・モードは、対応する仮パラメータと同じです。ただし、MAXLEN、CHARSETIDまたはCHARSETFORMを指定するパラメータは、BY REFERENCEをともに指定した場合も、常にINパラメータと同様に処理されます。
CHAR、LONG RAW、RAWまたはVARCHAR2型のパラメータには、プロパティLENGTHを使用する必要があります。また、そのパラメータがIN OUTまたはOUTでNULLの場合は、対応するCのパラメータの長さを0(ゼロ)に設定する必要があります。
|
関連項目 マルチスレッディングの詳細は、『Oracle Database Heterogeneous Connectivity管理者ガイド』を参照してください。 |
Javaデータ・カートリッジを利用するには、Javaクラス定義のロード方法、ストアド・プロシージャのコール方法およびコンテキスト管理の知識があることが重要です。ODCIクラスの詳細は、第18章「C、C++およびJavaを使用したカートリッジ・サービス」も参照してください。