この章では、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を使用したカートリッジ・サービス」も参照してください。