この章では、他のプログラミング言語で記述された外部プロシージャをコールするデータベース・アプリケーションの開発方法について説明します。
内容は次のとおりです。
Oracle Databaseは、次に示す複数の異なる言語での操作が可能です。
『Oracle Database PL/SQL言語リファレンス』で説明されるPL/SQL
『Oracle Call Interfaceプログラマーズ・ガイド』で説明される、Oracle Call Interface(OCI)を使用したC
『Pro*C/C++プログラマーズ・ガイド』で説明される、Pro*C/C++プリコンパイラを使用したCまたはC++
『Pro*COBOLプログラマーズ・ガイド』で説明される、Pro*COBOLプリコンパイラを使用したCOBOL
『Oracle Data Provider for .NET開発者ガイド』で説明される、Oracle Data Provider for .NET(ODP.NET)を使用した.NET
『Oracle Objects for OLE開発者ガイド for Microsoft Windows』、および『Oracle Provider for OLE DB開発者ガイド for Microsoft Windows』で説明される、Oracle Objects for OLE(OO4O)およびOracle Provider for OLE DBを使用したVisual Basic。
JDBCアプリケーション・プログラム・インタフェース(API)を使用したJava。『Oracle Database Java開発者ガイド』を参照してください。
これらの実装言語の中から何をどのように選択できるでしょうか。各言語には、それぞれ異なるメリットがあります。使用しやすさ、特定の専門知識を持つプログラマがいるかどうか、移植が必要かどうか、さらに既存コードの有無などが重要な決定要因になります。
ただし、アプリケーションでOracle Databaseをどのように使用するかによって、次のように選択範囲が狭くなる可能性があります。
PL/SQLは、SQLトランザクション処理に特化した強力な開発ツールです。
演算集中型のタスクは、Cなどの下位レベル言語で最も効率的に実行されます。
移植性とセキュリティの両方を求める場合は、Javaを選択できます。
パフォーマンスに関して最も重要なことは、サーバーのアドレス空間内で実行されるのはPL/SQLおよびJavaメソッドのみであるということです。C/C++メソッドは外部プロシージャとしてディスパッチされ、サーバー・システム上で実行されますが、これはデータベース・サーバーのアドレス空間外です。Pro*COBOLおよびPro*C/C++はプリコンパイラであり、Visual BasicはCで実装されているOCIを介してOracle Databaseにアクセスします。
以上のすべての要因を考慮すると、アプリケーションを複数の言語で実装する必要がある場合があることがわかります。たとえば、サーバーのアドレス空間内で実行されるJavaの登場によって、既存のJavaアプリケーションをデータベース内にインポートし、PL/SQLおよびSQLからJava関数をコールしてこのテクノロジを利用するという方法も可能になります。
PL/SQL外部プロシージャにより、Cプロシージャ・コールをPL/SQLの本体として作成できます。このようなCプロシージャは、PL/SQLからは直接、またSQLからはPL/SQLプロシージャ・コールを介してコールできます。データベースでは、コール仕様というインタフェースが提供されています。コール仕様を使用すると、他の言語から外部プロシージャをコールできます。このサービスは、SQL、PL/SQL、CおよびJava間の相互通信用に設計されていますが、このような言語をコールできる基本言語ならどの言語からでもアクセスできます。たとえば、JavaまたはC以外の言語で作成したプロシージャでも、Cからコール可能なプロシージャであれば、SQLまたはPL/SQLからでも使用できます。したがって、候補となるC++プロシージャがある場合は、そのプロシージャでC++のextern
"C"
文を使用してそのプロシージャをCからコールできるようにします。
したがって、複数の異なる言語の強みおよび機能をプログラム環境に関係なく利用できます。固有の制約を持つ1つの言語に制限する必要はありません。外部プロシージャの使用によって、特定の目的に特定の言語をデプロイできるため、再利用性およびモジュール性が向上します。
外部プロシージャとは、動的リンク・ライブラリ(DLL)に格納されているプロシージャ、またはJavaクラス・メソッドの場合はライブラリ・ユニットのことです。このプロシージャを基本言語に登録し、これをコールして特定の処理を実行できます。
たとえば、PL/SQLを使用する場合、PL/SQLが実行時にライブラリを動的にロードし、次に、プロシージャをPL/SQLプロシージャとみなしてコールします。このようなプロシージャは、現行のトランザクションに完全に組み込まれ、データベースをコールバックしてSQL操作を実行できます。
このようなプロシージャは必要なときにのみロードされるため、メモリーが節約されます。コール仕様が実装本体から切り離されているため、コール側プログラムに影響することなく、プロシージャを拡張できます。
外部プロシージャを使用すると、次のようなことができます。
クライアント・アプリケーションの実行とデータベース・インスタンスからのプロセスを分離し、クライアント側で生じた問題がデータベースに悪影響を与えないようにすることができます。
演算集中型プログラムをクライアントから実行速度の速いサーバーへ移動できます(ネットワーク通信でのラウンドトリップを回避するため)。
データベース・サーバーと外部システムおよびデータ・ソースとのインタフェースが実現します。
データベース・サーバー自体の機能を拡張できます。
注意: 外部ライブラリ(DLLファイル)は静的なリンクである必要があります。他の外部ライブラリ(DLLファイル)からの外部記号を参照すべきではありません。Oracle Databaseはこういった記号を解決するため、外部プロシージャの原因による失敗がありません。 |
外部プロシージャの発行は、コール仕様を介して行いますが、このコール仕様には、AS
LANGUAGE
句を介したAS
EXTERNAL
機能のスーパーセットが含まれます。AS
LANGUAGE
コール仕様を使用すると、外部Cプロシージャを発行できる他に、Javaクラス・メソッドも発行できます。
注意: レガシー・アプリケーションをサポートするために、コール仕様では、AS EXTERNAL 句を使用して発行することもできます。ただし、アプリケーションを開発する場合は、AS LANGUAGE 句を使用することをお薦めします。 |
一般に、コール仕様を使用することによって、次のようなことができます。
適切なCまたはJavaのターゲット・プロシージャのディスパッチ
データ型の変換
パラメータ・モードのマッピング
自動的なメモリー割当ておよびクリーンアップ
SQLからコールされるパッケージ・ファンクションに必要に応じて指定される純粋度制約。
データベース・トリガーからのJavaメソッドまたはCプロシージャのコール
位置の柔軟性: パフォーマンスを最適化し実装の詳細を隠すために、AS
LANGUAGE
コール仕様を、パッケージ仕様または型仕様、あるいはパッケージ(または型)本体に入れることができます。
既存プログラムを外部プロシージャとして使用するには、そのプログラムをロードして発行した後、コールします。
外部CプロシージャまたはJavaメソッドをPL/SQLで使用できるようにするには、プロシージャまたはメソッドをロードする必要があります。ロード方法は、プロシージャがCで作成されているかJavaで作成されているかによって異なります。
内容は次のとおりです。
Javaプログラムのロード方法の1つとして、SQL*Plusから対話形式で実行できるCREATE
JAVA
文を使用する方法があります。CREATE
JAVA
文からJava仮想マシン(JVM)ライブラリ・マネージャが暗黙的にコールされ、そのときにこのライブラリ・マネージャがJavaバイナリ(.class
ファイル)およびリソースをローカルなBFILE
またはLOB列からRDBMSライブラリ・ユニットにロードします。
コンパイル済のJavaクラスが次のオペレーティング・システム・ファイルhome
/java
/bin
/Agent
.class
に格納されているとします。
ファイルAgent
.classから、次のようにスキーマ
usernameに
libunit
を作成します。
SYSTEM
としてデータベースに接続し、ユーザーusername
にCREATE
ANY
DIRECTORY
権限を付与します。
username
としてデータベースにアクセスし、サーバーのファイル・システム上に次のようにディレクトリ・オブジェクトを作成します。
CREATE DIRECTORY Bfile_dir AS '/home/java/bin';
ディレクトリ・オブジェクトの名前は、Agent
.class
のディレクトリ・パスの別名です。
クラスlibunit
を次のように作成します。
CREATE JAVA CLASS USING BFILE (Bfile_dir, 'Agent.class');
libunit
の名前は、クラスの名前から導出されます。
別の方法として、コマンドライン・ユーティリティLoadJava
を使用できます。このユーティリティは、Javaバイナリおよびリソースを、システムによって生成されたデータベース表にアップロードしてから、CREATE
JAVA
文を使用してJavaファイルをRDBMSライブラリ・ユニットにロードします。Javaファイルは、ファイル・システム、Java IDE、イントラネットまたはインターネットからアップロードできます。
注意: 外部Cプロシージャは、DLLをサポートするプラットフォームまたはSolarisの.so ライブラリなどの動的にロード可能な共有ライブラリをサポートするプラットフォームのみで使用できます。 |
アプリケーションが外部Cプロシージャをコールすると、Oracle DatabaseまたはOracle Listenerが外部プロシージャ・エージェントextproc
を起動します。アプリケーションは、Oracle DatabaseまたはOracle Listenerが確立したネットワーク接続を使用し、次の情報をextproc
に渡します。
DLLまたは共有ライブラリの名前
外部プロシージャの名前
外部プロシージャのすべてのパラメータ
その後、extproc
はDLLまたは共有ライブラリをロードし、外部プロシージャを実行し、外部プロシージャによってアプリケーションに戻された値を渡します。アプリケーションとextproc
は、同じコンピュータ上に存在する必要があります。
extproc
は、使用されているコール規格に準拠するライブラリ内でプロシージャをコールできます。コール規格の詳細は、「CALLING STANDARD」を参照してください。
注意: 外部プロシージャのデフォルト構成では、Oracle Databaseおよびextproc と動作するネットワーク・リスナーは不要になりました。Oracle Databaseによってextproc が直接起動されるため、Oracle Listenerが誤ってextproc を起動する危険性が解消されました。セキュリティを最大限にするため、このデフォルト構成をお薦めします。
次のいずれかを使用する場合、Oracle Listenerが
デフォルト構成を変更すると、追加のネットワーク構成が必要になります。 |
Cで作成された外部プロシージャ、またはCアプリケーションからコール可能な外部プロシージャを使用できるようにデータベースを設定するには、開発者またはデータベース管理者が次の手順を実行します。
次のプロトタイプのいずれかを使用し、Cプロシージャを定義します。
Kernighan & Ritchieスタイル・プロトタイプの定義は次のとおりです。
void C_findRoot(x) float x; ...
全角ではない数値データ型(float
、short
、char
など)以外のISO/ANSIプロトタイプの定義は次のとおりです。
void C_findRoot(double x) ...
デフォルトの引数格上げによってサイズの変わらないその他のデータ型。
デフォルトの引数格上げによってサイズが変わる定義の例は次のとおりです。
void C_findRoot(float x) ...
外部プロシージャでデフォルト構成を使用する場合、Oracle Databaseはextproc
を直接起動します。listener
.ora
およびtnsnames
.ora
の構成を変更する必要はありません。次の構文を使用し、外部プロシージャが使用する環境変数をextproc
.ora
ファイルに定義します(ファイルは、UNIXオペレーティング・システムでは$ORACLE_HOME/hs/admin
に、WindowsではORACLE_HOME\hs\admin
にあります)。
SET name=value (environment_variable_name value)
extproc
がロードするDLLを制限するEXTPROC_DLLS
環境変数を次のいずれかの値に設定します。
NULL
の場合、次のとおり設定します。
SET EXTPROC_DLLS=
デフォルトのこの設定では、extproc
は、ディレクトリ$ORACLE_HOME
/bin
または$ORACLE_HOME
/lib
にあるDLLのみをロードします。
ONLY
の後にコロンで区切られた(Windowsシステムではセミコロンで区切られた)DLLをリストして指定する場合、次のとおり設定します。
SET EXTPROC_DLLS=ONLY:DLL1:DLL2
この設定では、extproc
はDLL1およびDLL2という名のDLLのみロードします。この設定で最大のセキュリティが得られます。
コロンで区切られた(Windowsシステムではセミコロンで区切られた)DLLのリストの場合、次のとおり設定します。
SET EXTPROC_DLLS=DLL1:DLL2
この設定では、extproc
は、DLL1およびDLL2という名のDLL、およびディレクトリ$ORACLE_HOME
/bin
または$ORACLE_HOME
/lib
に存在するDLLをロードします。
ANY
の場合、次のとおり設定します。
SET EXTPROC_DLLS=ANY
この設定では、extproc
はすべてのDLLをロードします。
外部プロシージャのデフォルト構成を変更し、extproc
エージェントをOracleリスナーから起動するには、Cで作成された外部プロシージャまたはCアプリケーションからコール可能な外部プロシージャを使用できるよう、データベース構成を次のように設定します。
エージェント用の構成パラメータ(デフォルト名extproc
)を構成ファイルtnsnames
.ora
およびlistener
.ora
内で設定します。この作業により、データベース起動時に外部プロシージャ・エージェントextproc
との接続が確立されます。
外部プロシージャ専用のリスナー・プロセスを開始します。
リスナーは、extproc
に必要な環境変数(ORACLE_HOME
、ORACLE_SID
およびLD_LIBRARY_PATH
など)をいくつか設定します。また、リスナーがlistener
.ora
エントリのENVS
セクションで固有の環境変数を定義すると、その環境変数はエージェント・プロセスに渡されます。それ以外は、エージェントに対してクリーンな環境を提供します。エージェント用に設定される環境変数は、クライアントおよびサーバー用に設定される環境変数から独立しています。したがって、エージェント・プロセス内で実行される外部プロシージャは、クライアントまたはサーバーのプロセス用に設定される環境変数を読み込むことはできません。
注意: 環境変数自体は標準Cプロシージャのsetenv およびgetenv を使用してそれぞれ設定し、読み込むことができます。この方法で設定された環境変数は、エージェント・プロセス専用です。そのプロセス内で実行されるすべての関数で読み込めますが、同一ホスト上で実行されている他のプロセスで読み込むことはできません。 |
外部プロシージャ用のエージェントを専用モード(デフォルト)で実行するか、マルチスレッド・モードで実行するかを決定します。専用モードでは、各セッションに対して専用エージェントが1つ起動されます。マルチスレッド・スレッドモードでは、単一のマルチスレッドextproc
エージェントが起動されます。マルチスレッドextproc
エージェントは、ユーザーごとに異なるスレッドを使用してコールを処理します。外部プロシージャをコールするユーザーが多いような構成では、マルチスレッドのextproc
エージェントを使用してシステム・リソースを節約することをお薦めします。
エージェントを専用モードで実行する場合、エージェント・プロセスの構成はこれ以上必要ありません。
エージェントをマルチスレッド・モードで実行する場合、データベース管理者はエージェントが(マルチスレッドextproc
エージェントとして)マルチスレッド・モードで起動されるようにデータベース・システムを構成する必要があります。この構成は、エージェント制御ユーティリティagtctl
を使用して行います。たとえば、次のコマンドを使用してextproc
を起動します。
agtctl startup extproc agent_sid
この例のagent_sid
は、このextproc
エージェントが処理するシステム識別子です。このシステム識別子のエントリは、通常、tnsnames
.ora
ファイルにエントリとして追加されています。extproc
管理でのagtctl
の使用の詳細は、「マルチスレッドextprocエージェントの管理」を参照してください。
注意:
|
図A-1に、マルチスレッドextproc
エージェントのアーキテクチャを示します。
ここでのDLLとは、外部プロシージャを格納する動的ロードが可能な任意のオペレーティング・システム・ファイルです。
セキュリティ上の理由から、DLLへのアクセスはDBAが制御します。DBAは、CREATE
LIBRARY
文を使用して、DLLを表す別名ライブラリというスキーマ・オブジェクトを作成します。次に、許可されているユーザーの場合は、DBAが別名
ライブラリに対するEXECUTE権限を付与します。このかわりに、DBAは、ユーザーにCREATE
ANY
LIBRARY
権限を付与することもあり、この場合、ユーザーは、次の構文を使用して自分の別名ライブラリを作成できます。
CREATE LIBRARY [schema_name.]library_name {IS | AS} 'file_path' [AGENT 'agent_link'];
注意: ANY 権限は強力なので、軽率に付与するべきではありません。詳細は次を参照してください。
|
DLLには、名前のみではなく、DLLへのフル・パスを指定することをお薦めします。次の例では、DLLであるutils
.so
を表す別名ライブラリc_utils
を作成します。
CREATE LIBRARY C_utils AS '/DLLs/utils.so';
DLLを柔軟に指定するために、表記規則${
VAR_NAME
}
を使用してパスのルート部分を環境変数として指定し、その変数をlistener
.ora
エントリのENVS
セクションに設定できます。
次の例では、agent_link
という名前で指定されるエージェントを使用して、ライブラリC_Utils
で外部プロシージャを実行します。
create or replace database link agent_link using 'agent_tns_alias'; create or replace library C_utils is '${EP_LIB_HOME}/utils.so' agent 'agent_link';
環境変数EP_LIB_HOME
は、エージェントによって/usr/bin/dll
などのそのインスタンス用の適切なパスに展開されます。変数EP_LIB_HOME
は、エージェントがアクセスできるように、listener
.ora
ファイル内に設定する必要があります。
セキュリティ上の理由から、デフォルト状態のextproc
は、ディレクトリ$ORACLE_HOME/bin
または$ORACLE_HOME/lib
に存在するDLLのみをロードするようになっています。また、extproc
に接続できるのは、同じシステムで実行されているOracle Databaseのクライアント・プロセスであるローカル・セッションのみです。
他のディレクトリからDLLをロードするには、環境変数EXTPROC_DLLS
を設定する必要があります。この環境変数の値には、完全パスで修飾したDLL名をコロンで区切った(Windowsシステムではセミコロンで区切った)リストを指定します。次に例を示します。
EXTPROC_DLLS=/private1/home/johndoe/dll/myDll.so:/private1/home/johndoe/dll/newDll.so
extproc
の環境変数はlistener
.ora
ファイルのENVS
パラメータから設定できますが、$ORACLE_HOME/hs/admin
ディレクトリにあるextproc
の初期化ファイルextproc
.ora
の環境変数も設定できます。extproc
.ora
およびlistener
.ora
のENVS
パラメータの両方が使用される場合、extproc
.ora
で定義された環境変数が優先されます。EXTPROC
機能の詳細は、Oracle Netのマニュアルを参照してください。
注意: Windowsシステム上のextproc.ora では、パスの各円記号に対してドライブ文字および二重円記号(\\)を使用して、パスを指定します。(各二重円記号内の最初の円記号は、エスケープ文字として機能します。) |
外部Cプロシージャを作成してから、これをDLLに追加します。プロシージャがDLL内にある場合、「外部プロシージャの公開」で説明されるコール仕様のメカニズムを使用して、プロシージャを公開します。
Oracle Databaseで使用できる外部プロシージャは、コール仕様で公開される外部プロシージャのみです。Javaクラス・メソッドまたは外部Cプロシージャの名前、パラメータ型および戻り型が、対応するSQLの要素にマップされます。これはPL/SQLストアド・プロシージャと同様に作成しますが、本体には宣言やBEGIN
END
ブロックではなく、AS
LANGUAGE
句を記述します。
AS
LANGUAGE
句では、次の指定を行います。
プロシージャの作成言語。
Javaメソッド
Javaメソッドのシグネチャ。
Cプロシージャ
DLLに対応するCプロシージャ用の別名ライブラリ。
DLL内のCプロシージャの名前。
パラメータの受渡し方法を指定するための様々なオプション。
異なるシステム上でプロシージャを実行するための、外部プロシージャ・エージェントextproc
の名前を保持するパラメータ(ある場合)
プロシージャ、ファンクション、パッケージ仕様部、パッケージ本体、型仕様または型本体用の通常のCREATE
OR
REPLACE
構文を使用して、宣言を開始します。
コール仕様は、名前宣言およびパラメータ宣言の後に指定します。構文は次のとおりです。
{IS | AS} LANGUAGE {C | JAVA}
これに続いて、次のいずれかを指定します。
NAME java_string_literal_name
java_string_literal_nameは、Javaメソッドのシグネチャです。
{ LIBRARY library_name [ NAME c_string_literal_name ] | [ NAME c_string_literal_name ] LIBRARY library_name } [ AGENT IN ( argument [, argument]... ) ] [ WITH CONTEXT ] [ PARAMETERS (external_parameter[, external_parameter]...) ];
library_name
は別名ライブラリの名前で、c_string_literal_name
は外部Cプロシージャの名前です。external_parameter
は次を意味します。
{ CONTEXT | SELF [{TDO | property}] | {parameter_name | RETURN} [property] [BY REFERENCE] [external_datatype]}
property
は次を意味します。
{INDICATOR [{STRUCT | TDO}] | LENGTH | DURATION | MAXLEN | CHARSETID | CHARSETFORM}
注意: Javaと異なり、CではSQL型は認識されません。このため、構文がより複雑になります。 |
内容は次のとおりです。
次の副次句は、PL/SQLに対して、外部Cプロシージャを検索する場所、そのプロシージャのコール方法、プロシージャに何を渡すかを指示します。
これらの副次句のうちLIBRARY
のみが必須です。
ローカルの別名ライブラリを指定します。(リモート・ライブラリの指定にデータベース・リンクは使用できません)。ライブラリ名はPL/SQL識別子です。このため、名前を二重引用符で囲むと、名前の大/小文字が区別されます。(デフォルトでは、名前は大文字で格納されます)。別名ライブラリにはEXECUTE
権限が必要です。
コール対象の外部Cプロシージャを指定します。プロシージャ名を二重引用符で囲むと、名前の大/小文字が区別されます。(デフォルトでは、名前は大文字で格納されます)。この副次句を省略すると、プロシージャ名はPL/SQLプロシージャ名を大文字で表したものがデフォルト設定されます。
注意: LANGUAGE およびCALLING STANDARD は、旧版のAS EXTERNAL 句のみに適用されます。 |
外部プロシージャが作成されている3GLを指定します。この副次句を省略すると、言語名はCにデフォルト設定されます。
外部プロシージャをコンパイルしたコール規格を指定します。サポートされているコール規格はCです。この副次句を省略すると、コール規格はCにデフォルト設定されます。
コンテキスト・ポインタが外部プロシージャに渡されることを指定します。コンテキスト・データ構造体は外部プロシージャに対して不透明ですが、外部プロシージャによってコールされるサービス・プロシージャでは使用できます。
外部プロシージャに渡されるパラメータの位置およびデータ型を指定します。現在の長さや最大長などのパラメータ・プロパティや、パラメータの受渡し方法(値によるか参照によるか)も指定できます。
このプロシージャを実行するエージェント・プロセスの名前を保持するパラメータを指定します。これは、外部プロシージャ・エージェントextproc
が、複数のエージェント・プロセスを使用して実行される場合を想定して行います。これによって、1つの外部プロシージャのエージェント・プロセスにエラーが発生した場合の信頼性が保証されます。エージェント・プロセスの名前(データベース・リンクの名前に対応)を渡すことができ、またtnsnames
.ora
およびlistener
.ora
が両方のインスタンス間で正しく設定されている場合、外部プロシージャが、もう一方のインスタンスでコールされます。両方のインスタンスは、同じホスト上に存在する必要があります。
この操作は、CREATE
LIBRARY
文のAGENT
句の場合と同様です。AGENT
IN
を使用して実行時の値を指定すると、柔軟性が向上します。
このようにエージェント名を指定すると、別名ライブラリで宣言されるエージェント名はいずれもオーバーライドされます。エージェント名が指定されていない場合は、extproc
エージェントがコール側プログラムと同じインスタンス上でデフォルト設定されます。
Javaクラスおよびそのメソッドは、RDBMSライブラリ・ユニットに格納されます。この中には、LOADJAVA
ユーティリティまたはSQL文CREATEJAVA
を使用して、Javaソース、バイナリおよびリソースをロードできます。ライブラリ・ユニットは、たとえばCで作成されたDLLに類似していますが、DLLには複数のプロシージャを入れられるのに対して、ライブラリ・ユニットはJavaクラスと1対1で対応します。
NAME
句文字列は、Javaメソッドを一意に識別します。PL/SQLのファンクションまたはプロシージャおよびJavaには対応するパラメータが必要です。Javaメソッドがパラメータをとらない場合は、空のパラメータ・リストを作成する必要があります。
JavaクラスをRDBMSにロードするとき、クラスはSQLに対して自動的には発行されません。これは、ほとんどのJavaクラスのメソッドは他のJavaクラスからのみコールされるか、該当するSQL型が存在しないパラメータをとるためです。
次に、引数の階乗を戻すJavaメソッド(J_calcFactorial
)を発行する場合を考えてみます。
package myRoutines.math; public class Factorial { public static int J_calcFactorial (int n) { if (n == 1) return 1; else return n * J_calcFactorial(n - 1); } }
次のコール仕様では、SQL*Plusを使用して、JavaメソッドJ_calcFactorial
をPL/SQLストアド・ファンクションplsToJavaFac_func
として発行します。
CREATE OR REPLACE FUNCTION Plstojavafac_func (N NUMBER) RETURN NUMBER AS LANGUAGE JAVA NAME 'myRoutines.math.Factorial.J_calcFactorial(int) return int';
次の例では、C関数Cdivisor_func
を外部関数として発行するplsCallsCdivisor_func
というPL/SQLスタンドアロン・ファンクションを作成します。
CREATE OR REPLACE FUNCTION Plscallscdivisor_func ( /* Find greatest common divisor of x and y: */ x PLS_INTEGER, y PLS_INTEGER) RETURN PLS_INTEGER AS LANGUAGE C LIBRARY C_utils NAME "Cdivisor_func"; /* Quotation marks preserve case. */
Javaクラス・メソッドおよび外部Cプロシージャはともに、次のいずれかの位置にコール仕様を指定できます。
スタンドアロンのPL/SQLプロシージャ
PL/SQLパッケージ仕様部
PL/SQLパッケージ本体
ADT仕様
ADT本体
注意: Oracle Databaseバージョン8.0では、AS EXTERNAL によりコール仕様をパッケージ本体または型本体に指定できませんでした。 |
参照:
|
例:
注意: 次のいくつかの例では、コール仕様の完全指定にAUTHID 句およびSQL_NAME_RESOLVE 句が必要な場合があります。 |
CREATE OR REPLACE PACKAGE Demo_pack AUTHID DEFINER AS PROCEDURE plsToC_demoExternal_proc (x PLS_INTEGER, y VARCHAR2, z DATE) AS LANGUAGE C NAME "C_demoExternal" LIBRARY SomeLib WITH CONTEXT PARAMETERS(CONTEXT, x INT, y STRING, z OCIDATE); END;
CREATE OR REPLACE PACKAGE Demo_pack AUTHID CURRENT_USER AS PROCEDURE plsToC_demoExternal_proc(x PLS_INTEGER, y VARCHAR2, z DATE); END; CREATE OR REPLACE PACKAGE BODY Demo_pack SQL_NAME_RESOLVE CURRENT_USER AS PROCEDURE plsToC_demoExternal_proc (x PLS_INTEGER, y VARCHAR2, z DATE) AS LANGUAGE JAVA NAME 'pkg1.class4.methodProc1(int,java.lang.String,java.sql.Date)'; END;
注意: この項の例が機能するには、次のデータ構造を設定する必要があります(これには、権限CREATE ANY LIBRARY が必要です)。
CREATE OR REPLACE LIBRARY SOMELIB AS '/tmp/lib.so'; |
CREATE OR REPLACE TYPE Demo_typ AUTHID DEFINER AS OBJECT (Attribute1 VARCHAR2(2000), SomeLib varchar2(20), MEMBER PROCEDURE plsToC_demoExternal_proc (x PLS_INTEGER, y VARCHAR2, z DATE) AS LANGUAGE C NAME "C_demoExternal" LIBRARY SomeLib WITH CONTEXT -- PARAMETERS(CONTEXT, x INT, y STRING, z OCIDATE) PARAMETERS(CONTEXT, x INT, y STRING, z OCIDATE, SELF) );
CREATE OR REPLACE TYPE Demo_typ AUTHID CURRENT_USER AS OBJECT (attribute1 NUMBER, MEMBER PROCEDURE plsToJ_demoExternal_proc (x PLS_INTEGER, y VARCHAR2, z DATE) ); CREATE OR REPLACE TYPE BODY Demo_typ AS MEMBER PROCEDURE plsToJ_demoExternal_proc (x PLS_INTEGER, y VARCHAR2, z DATE) AS LANGUAGE JAVA NAME 'pkg1.class4.J_demoExternal(int,java.lang.String,java.sql.Date)'; END;
スタンドアロンPL/SQLプロシージャ内にJavaクラス・メソッドを発行する例を示します。
CREATE OR REPLACE PROCEDURE plsToJ_demoExternal_proc (x PLS_INTEGER, y VARCHAR2, z DATE) AUTHID CURRENT_USER AS LANGUAGE JAVA NAME 'pkg1.class4.methodProc1(int,java.lang.String,java.sql.Date)';
スタンドアロンPL/SQLプログラム内にCプロシージャをAS
EXTERNAL
として発行する例を次に示します。AUTHID
句はオプションです。これによって、Oracle Databaseリリース8.0の外部プロシージャとの互換性が保たれます。
CREATE OR REPLACE PROCEDURE plsToC_demoExternal_proc (x PLS_INTEGER, y VARCHAR2, z DATE) AS EXTERNAL LANGUAGE C NAME "C_demoExternal" LIBRARY SomeLib WITH CONTEXT PARAMETERS(CONTEXT, x INT, y STRING, z OCIDATE);
CREATE OR REPLACE PACKAGE Demo_pack AUTHID DEFINER AS PROCEDURE plsToC_InBodyOld_proc (x PLS_INTEGER, y VARCHAR2, z DATE); PROCEDURE plsToC_demoExternal_proc (x PLS_INTEGER, y VARCHAR2, z DATE); PROCEDURE plsToC_InBody_proc (x PLS_INTEGER, y VARCHAR2, z DATE); PROCEDURE plsToJ_InBody_proc (x PLS_INTEGER, y VARCHAR2, z DATE); PROCEDURE plsToJ_InSpec_proc (x PLS_INTEGER, y VARCHAR2, z DATE) IS LANGUAGE JAVA NAME 'pkg1.class4.J_InSpec_meth(int,java.lang.String,java.sql.Date)'; PROCEDURE C_InSpec_proc (x PLS_INTEGER, y VARCHAR2, z DATE) AS LANGUAGE C NAME "C_demoExternal" LIBRARY SomeLib WITH CONTEXT PARAMETERS(CONTEXT, x INT, y STRING, z OCIDATE); END; CREATE OR REPLACE PACKAGE BODY Demo_pack AS PROCEDURE plsToC_InBodyOld_proc (x PLS_INTEGER, y VARCHAR2, z DATE) AS EXTERNAL LANGUAGE C NAME "C_InBodyOld" LIBRARY SomeLib WITH CONTEXT PARAMETERS(CONTEXT, x INT, y STRING, z OCIDATE); PROCEDURE plsToC_demoExternal_proc (x PLS_INTEGER, y VARCHAR2, z DATE) AS LANGUAGE C NAME "C_demoExternal" LIBRARY SomeLib WITH CONTEXT PARAMETERS(CONTEXT, x INT, y STRING, z OCIDATE); PROCEDURE plsToC_InBody_proc (x PLS_INTEGER, y VARCHAR2, z DATE) AS LANGUAGE C NAME "C_InBody" LIBRARY SomeLib WITH CONTEXT PARAMETERS(CONTEXT, x INT, y STRING, z OCIDATE); PROCEDURE plsToJ_InBody_proc (x PLS_INTEGER, y VARCHAR2, z DATE) IS LANGUAGE JAVA NAME 'pkg1.class4.J_InBody_meth(int,java.lang.String,java.sql.Date)'; END;
コール仕様によって、PL/SQLデータ型とCデータ型とのマッピングが可能になります。データ型マッピングの詳細は、「データ型の指定」を参照してください。
外部Cプロシージャへのパラメータの受渡しは、次のような理由で複雑になります。
使用可能なPL/SQLのセットが、Cデータ型のセットと1対1で対応していない。
Cとは異なり、PL/SQLにはNULLかどうかというRDBMS概念が含まれる。このため、PL/SQLパラメータはNULLにできるが、CパラメータはNULL
にできない。
外部プロシージャで、CHAR
、LONG
RAW
、RAW
およびVARCHAR2
パラメータの現在の長さまたは最大長が必要。
外部プロシージャで、CHAR
、VARCHAR2
およびCLOB
パラメータに関するキャラクタ・セット情報が必要。
PL/SQLで、外部プロシージャによって戻された値の現在の長さ、最大長またはNULL状態が必要。
注意: C外部プロシージャに渡すことができるパラメータの最大数は128です。ただし、FLOAT型またはDOUBLE型のパラメータを値渡しする場合は、最大数は127以下になります。どのくらい減るかは、パラメータの個数およびオペレーティング・システムによって異なります。目安としては、値渡しされるFLOAT型またはDOUBLE型のパラメータ1つを2つ分として数えます。 |
内容は次のとおりです。
パラメータは外部プロシージャに直接渡さないでください。かわりに、パラメータのPL/SQLデータ型を指定し、外部プロシージャを発行したPL/SQLプロシージャに渡します。PL/SQLデータ型は、デフォルトの外部データ型にマップされます(表14-1を参照)。
注意: PL/SQLデータ型のBINARY_INTEGER とPLS_INTEGER は同一です。わかりやすくするため、このドキュメントではBINARY_INTEGER とPLS_INTEGER の両方をPLS_INTEGER とします。 |
表14-1 パラメータ・データ型のマッピング
PL/SQLデータ型 | サポートされている外部データ型 | デフォルトの外部データ型 |
---|---|---|
BINARY_INTEGER BOOLEAN PLS_INTEGER |
[UNSIGNED] CHAR [UNSIGNED] SHORT [UNSIGNED] INT [UNSIGNED] LONG SB1, SB2, SB4 UB1, UB2, UB4 SIZE_T |
INT |
NATURALFoot 1 NATURALNFootref 1 POSITIVEFootref 1 POSITIVENFootref 1 SIGNTYPEFootref 1 |
[UNSIGNED] CHAR [UNSIGNED] SHORT [UNSIGNED] INT [UNSIGNED] LONG SB1, SB2, SB4 UB1, UB2, UB4 SIZE_T |
UNSIGNED INT |
FLOAT REAL |
FLOAT |
FLOAT |
DOUBLE PRECISION |
DOUBLE |
DOUBLE |
CHAR CHARACTER LONG NCHAR NVARCHAR2 ROWID VARCHAR VARCHAR2 |
STRING OCISTRING |
STRING |
LONG RAW RAW |
RAW OCIRAW |
RAW |
BFILE BLOB CLOB NCLOB |
OCILOBLOCATOR |
OCILOBLOCATOR |
NUMBER DECFootref 1 DECIMALFootref 1 INTFootref 1 INTEGERFootref 1 NUMERICFootref 1 SMALLINTFootref 1 |
OCINUMBER |
OCINUMBER |
DATE |
OCIDATE |
OCIDATE |
TIMESTAMP TIMESTAMP WITH TIME ZONE TIMESTAMP WITH LOCAL TIME ZONE |
|
|
INTERVAL DAY TO SECOND INTERVAL YEAR TO MONTH |
OCIInterval |
OCIInterval |
composite object types: ADTs |
dvoid |
dvoid |
composite object types: collections (varrays, nested tables) |
OCICOLL |
OCICOLL |
脚注1 このPL/SQLデータ型は、コール仕様でAS
EXTERNAL
を使用する場合にのみ準拠します。
外部データ型はそれぞれCデータ型にマップされ、データ型変換が暗黙的に実行されます。外部データ型はそれぞれCデータ型にマップされ、データ型変換が暗黙的に実行されます。Cプロトタイプ・パラメータの宣言時のエラーを回避するには、表14-2を参照してください。この表には、特定の外部データ型およびPL/SQLパラメータ・モードに指定するCデータ型が示されています。たとえば、OUT
パラメータの外部データ型がSTRING
型の場合は、Cプロトタイプにはデータ型char *を指定します。
表14-2 外部データ型のマッピング
PL/SL型に対応する外部データ型 | INモードまたはRETURNモードの場合、Cプロトタイプで指定 | 参照によるINモードまたは参照によるRETURNモードの場合、Cプロトタイプで指定 | IN OUTモードまたはOUTモードの場合、Cプロトタイプで指定 |
---|---|---|---|
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 * |
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 ** |
OCINUMBER |
OCINumber * |
OCINumber * |
OCINumber * |
OCISTRING |
OCIString * |
OCIString * |
OCIString * |
OCIRAW |
OCIRaw * |
OCIRaw * |
OCIRaw * |
OCIDATE |
OCIDate * |
OCIDate * |
OCIDate * |
OCICOLL |
OCIColl * or OCIArray * or OCITable * |
OCIColl ** or OCIArray ** or OCITable ** |
OCIColl ** or OCIArray ** or OCITable ** |
OCITYPE |
OCIType * |
OCIType * |
OCIType * |
TDO |
OCIType * |
OCIType * |
OCIType * |
ADT (final types) |
dvoid* |
dvoid* |
dvoid* |
ADT (nonfinal types) |
dvoid* |
dvoid* |
dvoid** |
コンポジット・データ型は、自己記述型ではありません。記述は、型記述子オブジェクト(TDO)に格納されています。オブジェクトおよびオブジェクトのインジケータ構造体に事前定義のOCIデータ型はありませんが、Oracle DatabaseのObject Type Translator(OTT)によって生成されるデータ型を使用する必要があります。INDICATOR
およびコンポジット・オブジェクトのオプションのTDO引数には、通常、Cデータ型のOCIType*があります。
REF
およびコレクション引数のOCICOLL
はオプションで、完全性を保つためにのみ存在しています。REF
またはコレクション型を別のデータ型にマップすること、および別のデータ型をREF
またはコレクション型にマップすることはできません。
BY
VALUE
を指定すると、スカラーIN
およびRETURN
引数が値渡しされます(デフォルト)。また、BY
REFERENCE
を指定すると、参照によって渡すことができます。
デフォルトまたはBY
REFERENCE
を指定した場合は、スカラーIN
OUT
およびOUT
引数が参照によって渡されます。IN
OUT
およびOUT
引数でのBY
VALUE
の指定はC用にはサポートされていません。BY
REFERENCE
/VALUE
句の使用は、デフォルトで値渡しされる外部データ型に制限されます。これは、次の外部データ型のIN
およびRETURN
引数に適用されます。
[UNSIGNED] CHAR [UNSIGNED] SHORT [UNSIGNED] INT [UNSIGNED] LONG SIZE_T SB1 SB2 SB4 UB1 UB2 UB4 FLOAT DOUBLE
このリストに記載されていない外部データ型のIN
およびRETURN
引数、IN
OUT
およびOUT
引数は、すべて参照によって渡されます。
一般に、外部プロシージャを発行するPL/SQLプロシージャは、次の例に示されているように、仮パラメータのリストを宣言します。
注意: この項の例には、次のようなデータ構造を設定しないと機能しないものもあります。CREATE LIBRARY MathLib AS '/tmp/math.so'; |
CREATE OR REPLACE FUNCTION Interp_func (
/* Find the value of y at x degrees using Lagrange interpolation: */
x IN FLOAT,
y IN FLOAT)
RETURN FLOAT AS
LANGUAGE C
NAME "Interp_func"
LIBRARY MathLib;
それぞれの仮パラメータ宣言では、名前、パラメータ・モードおよび(デフォルトの外部データ型にマップされる)PL/SQLデータ型が指定されます。これが、外部プロシージャが必要とするすべての情報である可能性があります。これがすべてではない場合は、PARAMETERS
句を使用して追加情報を指定できます。
デフォルト以外の外部データ型
パラメータの現在の長さまたは最大長
パラメータのNULL
/NOT
NULL
インジケータ
キャラクタ・セットのIDおよび形式
リスト内のパラメータの位置
IN
パラメータを渡す方法(値による方法または参照による方法)
PARAMETERS
句を使用する場合、次のことを認識しておく必要があります。
各仮パラメータには、PARAMETERS
句に対応するパラメータが必要です。
WITH
CONTEXT
句を含める場合は、パラメータ・リスト内でのコンテキスト・ポインタの位置を示すパラメータCONTEXT
を指定する必要があります。
外部プロシージャがファンクションの場合は、パラメータRETURN
を指定できますが、末尾に指定する必要があります。RETURN
を指定しないときは、デフォルトの外部型が使用されます。
PARAMETERS
句を使用して、デフォルトのデータ型マッピングをオーバーライドすることができる場合もあります。たとえば、外部データ型INT
から外部データ型CHAR
にPL/SQLデータ型のBOOLEAN
をマッピングしなおすことができます。
PARAMETERS
句は、PL/SQLの仮パラメータおよびファンクション結果に関する追加情報を外部プロシージャに渡すために使用することもできます。これは、次のプロパティを1つ以上指定して行います。
INDICATOR [{STRUCT | TDO}]LENGTH
DURATIONMAXLEN
CHARSETID
CHARSETFORM
SELF
表14-3に、使用可能外部データ型とデフォルト外部データ型、PL/SQLデータ型および特定のプロパティに使用できるPL/SQLパラメータ・モードを示します。(CからPL/SQLにデータを戻す場合に指定する)MAXLEN
は、IN
パラメータには適用できません。
表14-3 プロパティおよびデータ型
プロパティ | 使用可能外部データ型(C) | デフォルトの外部データ型(C) | 使用可能PL/SQLデータ型 | 使用可能PL/SQLモード | デフォルトのPL/SQL受渡し方法 |
---|---|---|---|---|---|
INDICATOR |
SHORT |
SHORT |
all scalars |
IN IN OUT OUT RETURN |
BY VALUE BY REFERENCE BY REFERENCE BY REFERENCE |
LENGTH |
[UNSIGNED] SHORT [UNSIGNED] INT [UNSIGNED] LONG |
INT |
CHAR LONG RAW RAW VARCHAR2 |
IN IN OUT OUT RETURN |
BY VALUE BY REFERENCE BY REFERENCE BY REFERENCE |
MAXLEN |
[UNSIGNED] SHORT [UNSIGNED] INT [UNSIGNED] LONG |
INT |
CHAR LONG RAW RAW VARCHAR2 |
IN OUT OUT RETURN |
BY REFERENCE BY REFERENCE BY REFERENCE |
CHARSETID CHARSETFORM |
UNSIGNED SHORT UNSIGNED INT UNSIGNED LONG |
UNSIGNED INT |
CHAR CLOB VARCHAR2 |
IN IN OUT OUT RETURN |
BY VALUE BY REFERENCE BY REFERENCE BY REFERENCE |
次の例では、PARAMETERS
句はPL/SQLの仮パラメータおよびファンクション結果のプロパティを指定します。
CREATE OR REPLACE FUNCTION plsToCparse_func ( x IN PLS_INTEGER, Y IN OUT CHAR) RETURN CHAR AS LANGUAGE C LIBRARY c_utils NAME "C_parse" PARAMETERS ( x, -- stores value of x x INDICATOR, -- stores null status of x y, -- stores value of y y LENGTH, -- stores current length of y y MAXLEN, -- stores maximum length of y RETURN INDICATOR, RETURN);
このPARAMETERS
句を使用すると、Cプロトタイプは次のようになります。
char *C_parse( int x, short x_ind, char *y, int *y_len, int *y_maxlen, short *retind );
Cプロトタイプ内の追加パラメータは、INDICATOR
(x
用)、LENGTH
(y
用)およびMAXLEN
(y
用)の他に、PARAMETERS
句のファンクション結果のINDICATOR
にも対応します。パラメータRETURN
は結果値を格納し、C関数識別子に対応します。
内容は次のとおりです。
INDICATOR
は、別のパラメータがNULL
かどうかを示す値を持つパラメータです。PL/SQLでは、RDBMSにおけるNULLかどうかという概念が言語に組み込まれているため、インジケータは必要ありません。ただし、外部プロシージャでは、パラメータまたはファンクション結果がNULL
かどうかを認識することが必要な場合があります。また、外部プロシージャでは、戻り値が実際にNULL
でありそれに応じた処理が必要であることをサーバーに指示する必要がある場合もあります。
このような場合は、プロパティINDICATOR
を使用して、インジケータを仮パラメータに関連付けることができます。PL/SQLプロシージャがファンクションの場合は、前述のようにインジケータをファンクション結果に関連付けることもできます。
インジケータの値を調べるには、定数OCI_IND_NULL
およびOCI_IND_NOTNULL
を使用できます。インジケータがOCI_IND_NULL
と等しい場合は、関連付けられているパラメータまたはファンクション結果はNULL
です。インジケータがOCI_IND_NOTNULL
と等しい場合は、関連付けられているパラメータまたはファンクション結果はNULL
ではありません。
IN
パラメータは読込み専用ですが、この場合、INDICATOR
は(BY
REFERENCE
を指定しないかぎり)値渡しされ、(BY
REFERENCE
を指定しても)読込み専用です。OUT
、IN
OUT
およびRETURN
パラメータの場合、INDICATOR
はデフォルトで参照によって渡されます。
INDICATOR
にはSTRUCT
オプションまたはTDOオプションを指定することもできます。INDICATOR
をオブジェクトのプロパティとして指定することはサポートされていないため、また、オブジェクトの引数にはINDICATOR
スカラーのかわりに完全なインジケータ構造体があるため、STRUCT
オプションを使用して指定する必要があります。コンポジット・オブジェクトおよびコレクションには、型記述子オブジェクト(TDO)を使用する必要があります。
PL/SQLでは、RAW
パラメータまたは文字列パラメータの長さを示す標準的な方法はありません。ただし、このようなパラメータの長さの受渡しを外部プロシージャとの間で行う必要がある場合が数多くあります。プロパティLENGTH
およびMAXLEN
を使用すると、仮パラメータの現在の長さおよび最大長を格納するパラメータを指定できます。
注意: 型がRAW またはLONG RAW のパラメータの場合は、プロパティLENGTH を使用してください。また、そのパラメータがIN OUT およびNULL か、またはOUT およびNULL の場合は、対応するCパラメータを長さ0(ゼロ)に設定してください。 |
IN
パラメータの場合は、LENGTH
は(BY
REFERENCE
を指定しないかぎり)値渡しされ、読込み専用です。OUT
、IN
OUT
およびRETURN
パラメータの場合は、LENGTH
はデフォルトで参照によって渡されます。
前述のように、MAXLEN
はIN
パラメータには適用されません。OUT
、IN
OUT
およびRETURN
パラメータの場合、MAXLEN
は参照によって渡され、読込み専用です。
Oracle Databaseではグローバリゼーション・サポートを提供していますが、これを使用すると、シングルバイトおよびマルチバイトの文字データを処理し、キャラクタ・セット間で変換を行うことができます。また、アプリケーションを異なる言語環境で実行することもできます。
デフォルトでは、サーバーおよびエージェントが同じ$ORACLE_HOME
値を使用する場合、エージェントはサーバー(ALTER
SESSION
文で指定した設定を含む)と同一のグローバリゼーション・サポート設定を使用します。
エージェントが別の$ORACLE_HOME
(2つの異なる別名およびシンボリック・リンクによって同じ場所が指定されている場合でも)で実行されている場合、キャラクタ・セット以外はサーバーと同じグローバリゼーション・サポート設定が使用されます。エージェント用のデフォルト・キャラクタ・セットはエージェントの環境設定NLS_LANG
およびNLS_NCHAR
によって定義されます。
プロパティCHARSETID
およびCHARSETFORM
では、渡された文字データの生成元である、デフォルト以外のキャラクタ・セットが識別されます。CHAR
、CLOB
およびVARCHAR2
型パラメータにより、CHARSETID
およびCHARSETFORM
を使用してキャラクタ・セットのIDおよび形式を外部プロシージャに渡すことができます。
IN
パラメータの場合、CHARSETID
およびCHARSETFORM
は(BY
REFERENCE
を指定しないかぎり)値渡しされ、(BY
REFERENCE
を指定した場合でも)読込み専用です。OUT
、IN
OUT
およびRETURN
パラメータの場合、CHARSETID
およびCHARSETFORM
は参照によって渡され、読込み専用です。
これらのプロパティのOCI属性名は、OCI_ATTR_CHARSET_ID
およびOCI_ATTR_CHARSET_FORM
です。
参照:
|
外部プロシージャの仮パラメータは、それぞれPARAMETERS
句の中に対応するパラメータが必要です。PL/SQLではパラメータを位置ではなく名前によって対応付けるため、対応するパラメータの位置は異なる可能性があります。ただし、PARAMETERS
句および外部プロシージャのためのCプロトタイプでは、同数のパラメータが同じ順序で必要です。
SELF
は、オブジェクト型のメンバーであるプロシージャに常に存在する引数で、オブジェクトのインスタンスそのものです。ほとんどの場合、この引数は暗黙的で、PL/SQLプロシージャの引数リストには含まれていません。ただし、SELF
は、PARAMETERS
句の引数として明示的に指定する必要があります。
たとえば、ユーザーが、個人の名前および生年月日で構成されるPerson
オブジェクトを作成し、さらに、このオブジェクト型の表を作成するとします。ユーザーは、後でこの表の各Person
の年令を判断する必要があるとします。
SQL*Plusでは、Person
オブジェクト型は次のように作成できます。
CREATE OR REPLACE TYPE Person1_typ AS OBJECT ( Name_ VARCHAR2(30), B_date DATE, MEMBER FUNCTION calcAge_func RETURN NUMBER ); /
通常、メンバー関数はPL/SQLで実装しますが、この例では外部プロシージャとなっています。このメンバー関数の本文を次のように宣言します。
CREATE OR REPLACE TYPE BODY Person1_typ AS MEMBER FUNCTION calcAge_func RETURN NUMBER AS LANGUAGE C NAME "age" LIBRARY agelib WITH CONTEXT PARAMETERS ( CONTEXT, SELF, SELF INDICATOR STRUCT, SELF TDO, RETURN INDICATOR ); END; /
calcAge_func
メンバー関数は引数をとらず、数値を戻すのみです。メンバー関数は、常に、対応付けられているオブジェクト型のインスタンスに対してコールされます。オブジェクト・インスタンス自体は、常にメンバー関数の暗黙的な引数になります。暗黙的な引数を参照するには、SELF
キーワードを使用します。これは、PARAMETERS句の中でSELF
への参照をサポートすることによって、外部プロシージャ構文内に組み込まれています。
対応表が作成され移入されます。
CREATE TABLE Person_tab OF Person1_typ; INSERT INTO Person_tab VALUES ('BOB', TO_DATE('14-MAY-85')); INSERT INTO Person_tab VALUES ('JOHN', TO_DATE('22-DEC-71'));
最後に、この表から対象の情報を取得します。
SELECT p.name, p.b_date, p.calcAge_func() FROM Person_tab p; NAME B_DATE P.CALCAGE_ ------------------------------ --------- ---------- BOB 14-MAY-85 0 JOHN 22-DEC-71 0
外部
メンバー関数と、Object-Type-Translator(OTT)によって生成される構造体
の定義を実装するCコードを次に示します。
#include <oci.h> struct PERSON { OCIString *NAME; OCIDate B_DATE; }; typedef struct PERSON PERSON; struct PERSON_ind { OCIInd _atomic; OCIInd NAME; OCIInd B_DATE; }; typedef struct PERSON_ind PERSON_ind; OCINumber *age (ctx, person_obj, person_obj_ind, tdo, ret_ind) OCIExtProcContext *ctx; PERSON *person_obj; PERSON_ind *person_obj_ind; OCIType *tdo; OCIInd *ret_ind; { sword err; text errbuf[512]; OCIEnv *envh; OCISvcCtx *svch; OCIError *errh; OCINumber *age; int inum = 0; sword status; /* get OCI Environment */ err = OCIExtProcGetEnv( ctx, &envh, &svch, &errh ); /* initialize return age to 0 */ age = (OCINumber *)OCIExtProcAllocCallMemory(ctx, sizeof(OCINumber)); status = OCINumberFromInt(errh, &inum, sizeof(inum), OCI_NUMBER_SIGNED, age); if (status != OCI_SUCCESS) { OCIExtProcRaiseExcp(ctx, (int)1476); return (age); } /* return NULL if the person object is null or the birthdate is null */ if ( person_obj_ind->_atomic == OCI_IND_NULL || person_obj_ind->B_DATE == OCI_IND_NULL ) { *ret_ind = OCI_IND_NULL; return (age); } /* The actual implementation to calculate the age is left to the reader, but an easy way of doing this is a callback of the form: select trunc(months_between(sysdate, person_obj->b_date) / 12) from DUAL; */ *ret_ind = OCI_IND_NOTNULL; return (age); }
Cでは、IN
スカラー・パラメータは値渡しされる(パラメータの値が渡される)か、参照によって渡す(値へのポインタが渡される)ことができます。スカラーを指すポインタが外部プロシージャで必要な場合は、BY
REFERENCE
句を指定し、参照によってパラメータを渡します。
CREATE OR REPLACE PROCEDURE findRoot_proc ( x IN DOUBLE PRECISION) AS LANGUAGE C LIBRARY c_utils NAME "C_findRoot" PARAMETERS ( x BY REFERENCE);
この場合、Cプロトタイプは次のように指定します。
void C_findRoot(double *x);
デフォルト(PARAMETERS
句がない場合に使用)は次のように指定します。
void C_findRoot(double x);
WITH
CONTEXT
句を含めることによって、外部プロシージャで、パラメータ、例外、メモリー割当ておよびユーザー環境に関する情報にアクセスできるようになります。WITH
CONTEXT
句は、コンテキスト・ポインタが外部プロシージャに渡されることを指定します。たとえば、次のPL/SQLファンクションを作成するとします。
CREATE OR REPLACE FUNCTION getNum_func ( x IN REAL) RETURN PLS_INTEGER AS LANGUAGE C LIBRARY c_utils NAME "C_getNum" WITH CONTEXT PARAMETERS ( CONTEXT, x BY REFERENCE, RETURN INDICATOR);
Cプロトタイプは次のとおりです。
int C_getNum( OCIExtProcContext *with_context, float *x, short *retind);
コンテキスト・データ構造体は外部プロシージャに対して不透明ですが、外部プロシージャによってコールされるサービス・プロシージャでは使用できます。
PARAMETERS
句も含める場合は、パラメータ・リスト内でのコンテキスト・ポインタの位置を示すパラメータCONTEXT
を指定する必要があります。PARAMETERS
句を省略すると、コンテキスト・ポインタは外部プロシージャに渡される最初のパラメータです。
PL/SQLは、IN
、IN
OUT
およびOUT
パラメータ・モード、および値を戻すプロシージャのRETURN
句をサポートします。
これで、Javaクラス・メソッドまたは外部Cプロシージャが発行されました。次は、これをコールします。
外部プロシージャは直接コールしないでください。かわりに、CALL
文を使用して、外部プロシージャを発行したPL/SQLプロシージャをコールしてください。「CALL文の構文」を参照してください。
このようなコールは、通常のPL/SQLプロシージャに対するコールと同じようにコーディングして、次のような場所で利用できます。
無名ブロック
スタンドアロンおよびパッケージ・プロシージャ
オブジェクト型のメソッド
データベース・トリガー
SQL文(パッケージ関数のみへのコール)。
サーバー側またはクライアント側(たとえば、Oracle Formsのようなツール内)で実行されるPL/SQLブロックまたはプロシージャは、外部プロシージャをコールできます。サーバー側では、外部プロシージャは別のプロセスのアドレス空間内で実行されるため、データベースが保護されます。図14-1に、Oracle Databaseと外部プロシージャ間の処理を示します。
内容は次のとおりです。
外部プロシージャをコールする前に、実行環境に存在する権限、許可およびシノニムを考慮する必要があります。
内容は次のとおりです。
外部プロシージャがCALL
仕様経由でコールされるとき、プロシージャは実行者権限ではなく定義者権限によって実行されます。
実行者権限で実行されるプログラムは、特定のスキーマには限定されません。コール側のサイトで実行され、コールした側の可視性と許可でデータベース項目(表やビューなど)にアクセスします。一方、定義者権限で実行されるプログラムでは、プログラムが定義されているスキーマに限定されます。このプログラムは定義を行う側で、サイトの定義者のスキーマ内の定義サイトで実行され、定義者の可視性および許可でデータベース項目にアクセスします。
外部プロシージャをコールするには、ユーザーはコール仕様に対するEXECUTE
権限が必要です。この権限を付与するには、SQL文GRANT
を使用します(『Oracle Database SQL言語リファレンス』を参照)。たとえば、この文により、ユーザーjohndoeはコール仕様がplsToJ_demoExternal_proc
である外部プロシージャをコールできます。
GRANT EXECUTE ON plsToJ_demoExternal_proc TO johndoe;
プロシージャをコールする必要があるユーザーにのみ、コール仕様に対するEXECUTE
権限を付与します。
開発者またはDBAは、便宜上、CREATE
PUBLIC
SYNONYM
文を使用して外部プロシージャのシノニムを作成できます。次の例では、すべてのユーザーがアクセス可能なパブリック・シノニムをDBAが作成します。PUBLIC
を指定しない場合、シノニムはプライベートになり、そのスキーマ内以外ではアクセスできません。
CREATE PUBLIC SYNONYM Rfac FOR johndoe.RecursiveFactorial;
SQLのCALL
文から外部プロシージャをコールします。CALL
文はSQL*Plusから対話形式で実行できます。構文は次のとおりです。
CALL [schema.][{object_type_name | package_name}]procedure_name[@dblink_name] [(parameter_list)] [INTO :host_variable][INDICATOR][:indicator_variable];
これは、SELECT
myproc(
...)
FROM
DUAL
という形式のSQL文を使用してプロシージャmyproc
を実行することと基本的には同じです。ただし、この場合は、SELECT
の実行に関連するオーバーヘッドが発生しません。
たとえば、この章で発行したplsToC_demoExternal_proc
を動的SQLを使用してコールする無名PL/SQLブロックを次に示します。PL/SQLは、外部CプロシージャC_demoExternal_proc
に3つのパラメータを渡します。
DECLARE xx NUMBER(4); yy VARCHAR2(10); zz DATE; BEGIN EXECUTE IMMEDIATE 'CALL plsToC_demoExternal_proc(:xxx, :yyy, :zzz)' USING xx,yy,zz; END;
CALL
文のセマンティクスは、同等のBEGIN
END
ブロックと同じです。
注意: CALL は、単独でPL/SQLのBEGIN END ブロックに入れることのできない唯一のSQL文です。これは、BEGIN END ブロック内でのEXECUTE IMMEDIATE 文の一部に使用できます。 |
以前に発行されたJ_calcFactorial
クラス・メソッドをコールする方法は次のとおりです。
次のように、SQL*Plusのホスト変数を2つ宣言して初期化します。
VARIABLE x NUMBER VARIABLE y NUMBER EXECUTE :x := 5;
J_calcFactorial
をコールします。
CALL J_calcFactorial(:x) INTO :y; PRINT y
結果:
Y ------ 120
外部Cプロシージャをコールするには、PL/SQLが適切なDLLパスを検出する必要があります。PL/SQLエンジンは、プロシージャ宣言のAS
LANGUAGE
句からのライブラリ別名に基づいて、パスをデータ・ディクショナリから取得します。
次に、PL/SQLはリスナー・プロセスに警告を出し、リスナー・プロセスがセッション固有のエージェントを起動します。デフォルトでは、このエージェントをextproc
といいますが、listener
.ora
ファイルで別の名前で指定できます。リスナーは接続をエージェントに渡し、PL/SQLはDLLの名前、外部プロシージャの名前およびパラメータをエージェントに渡します。
次に、エージェントはDLLをロードし、外部プロシージャを実行します。また、エージェントは(例外を呼び出すなどの)サービス・コールおよびOracle Databaseへのコールバックも処理します。最後に、エージェントは、外部プロシージャによって戻された値をPL/SQLに渡します。
注意: DLLのキャッシュはいくらか発生しますが、DLLはキャッシュ内に残らない可能性があります。したがって、グローバル変数はDLLには格納しないでください。 |
外部プロシージャが完了した後、エージェントはOracle Databaseセッションの終わりまでアクティブなまま残ります。ログオフするとエージェントが停止されます。この結果、何度コールしても、エージェントを起動するのは1回で済みますが、演算によるメリットがコールのコストを上回るときにのみ外部プロシージャをコールするようにします。
ここでは、この章で発行したPL/SQLファンクションplsCallsCdivisor_func
を無名ブロックからコールします。PL/SQLは整数パラメータを2つ外部関数Cdivisor_func
に渡し、この関数が最大公約数を戻します。
DECLARE g PLS_INTEGER; a PLS_INTEGER; b PLS_INTEGER; CALL plsCallsCdivisor_func(a, b); IF g IN (2,4,8) THEN ...
AS
EXTERNAL
コール仕様がTYPE
仕様部またはPACKAGE
仕様部で検出された場合、PL/SQLコンパイラはコンパイル時例外を呼び出します。
Cプログラムでは、OCIExtproc
関数を使用して例外を呼び出すことができます。
サービス・ルーチンが外部プロシージャからコールされると、サービス・ルーチンは例外を呼び出し、メモリーを割り当て、サーバーへのコールバック用のOCIハンドルをコールします。サービス・ルーチンを使用するには、WITH
CONTEXT
句を指定する必要があります。WITH CONTEXT句を使用すると、コンテキスト構造体を外部プロシージャに渡すことができます。コンテキスト構造体は、ヘッダー・ファイルociextp
.h
の中に次のように宣言されています。
typedef struct OCIExtProcContext OCIExtProcContext;
注意: ociextp.h は、LinuxおよびUNIXでは$ORACLE_HOME/plsql/public にあります。 |
サービス・プロシージャ:
このサービス・ルーチンは、外部プロシージャ・コールの間、nバイトのメモリーを割り当てます。この関数によって割り当てられるメモリーは、PL/SQLに制御が戻った直後に自動的に解放されます。
注意: 外部プロシージャでは、このサービス・ルーチンによって割り当てられたメモリーが自動的に処理されるため、C関数のfree をコールしないでください。 |
この関数のCプロトタイプは、次のとおりです。
dvoid *OCIExtProcAllocCallMemory( OCIExtProcContext *with_context, size_t amount);
パラメータwith_context
およびamount
は、それぞれ、コンテキスト・ポインタと割り当てられるバイト数です。この関数は、割り当てられたメモリーを指す型が未定のポインタを戻します。戻り値が0(ゼロ)の場合は、失敗を示します。
SQL*Plusで、外部関数plsToC_concat_func
を次のように発行するとします。
CREATE OR REPLACE FUNCTION plsToC_concat_func ( str1 IN VARCHAR2, str2 IN VARCHAR2) RETURN VARCHAR2 AS LANGUAGE C NAME "concat" LIBRARY stringlib WITH CONTEXT PARAMETERS ( CONTEXT, str1 STRING, str1 INDICATOR short, str2 STRING, str2 INDICATOR short, RETURN INDICATOR short, RETURN LENGTH short, RETURN STRING);
C_concat
は、コールされたときに2つの文字列を連結し、結果を戻します。
select plsToC_concat_func('hello ', 'world') from DUAL; PLSTOC_CONCAT_FUNC('HELLO','WORLD') ----------------------------------------------------------------------------- hello world
いずれの文字列もNULL
の場合は、結果もNULL
になります。次の例で示されるように、C_concat
はOCIExtProcAllocCallMemory
を使用して結果文字列のメモリーを割り当てます。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <oci.h> #include <ociextp.h> char *concat(ctx, str1, str1_i, str2, str2_i, ret_i, ret_l) OCIExtProcContext *ctx; char *str1; short str1_i; char *str2; short str2_i; short *ret_i; short *ret_l; { char *tmp; short len; /* Check for null inputs. */ if ((str1_i == OCI_IND_NULL) || (str2_i == OCI_IND_NULL)) { *ret_i = (short)OCI_IND_NULL; /* PL/SQL has no notion of a NULL ptr, so return a zero-byte string. */ tmp = OCIExtProcAllocCallMemory(ctx, 1); tmp[0] = '\0'; return(tmp); } /* Allocate memory for result string, including NULL terminator. */ len = strlen(str1) + strlen(str2); tmp = OCIExtProcAllocCallMemory(ctx, len + 1); strcpy(tmp, str1); strcat(tmp, str2); /* Set NULL indicator and length. */ *ret_i = (short)OCI_IND_NOTNULL; *ret_l = len; /* Return pointer, which PL/SQL frees later. */ return(tmp); } #ifdef LATER static void checkerr (/*_ OCIError *errhp, sword status _*/); void checkerr(errhp, status) OCIError *errhp; sword status; { text errbuf[512]; sb4 errcode = 0; switch (status) { case OCI_SUCCESS: break; case OCI_SUCCESS_WITH_INFO: (void) printf("Error - OCI_SUCCESS_WITH_INFO\n"); break; case OCI_NEED_DATA: (void) printf("Error - OCI_NEED_DATA\n"); break; case OCI_NO_DATA: (void) printf("Error - OCI_NODATA\n"); break; case OCI_ERROR: (void) OCIErrorGet((dvoid *)errhp, (ub4) 1, (text *) NULL, &errcode, errbuf, (ub4) sizeof(errbuf), OCI_HTYPE_ERROR); (void) printf("Error - %.*s\n", 512, errbuf); break; case OCI_INVALID_HANDLE: (void) printf("Error - OCI_INVALID_HANDLE\n"); break; case OCI_STILL_EXECUTING: (void) printf("Error - OCI_STILL_EXECUTE\n"); break; case OCI_CONTINUE: (void) printf("Error - OCI_CONTINUE\n"); break; default: break; } } char *concat(ctx, str1, str1_i, str2, str2_i, ret_i, ret_l) OCIExtProcContext *ctx; char *str1; short str1_i; char *str2; short str2_i; short *ret_i; short *ret_l; { char *tmp; short len; /* Check for null inputs. */ if ((str1_i == OCI_IND_NULL) || (str2_i == OCI_IND_NULL)) { *ret_i = (short)OCI_IND_NULL; /* PL/SQL has no notion of a NULL ptr, so return a zero-byte string. */ tmp = OCIExtProcAllocCallMemory(ctx, 1); tmp[0] = '\0'; return(tmp); } /* Allocate memory for result string, including NULL terminator. */ len = strlen(str1) + strlen(str2); tmp = OCIExtProcAllocCallMemory(ctx, len + 1); strcpy(tmp, str1); strcat(tmp, str2); /* Set NULL indicator and length. */ *ret_i = (short)OCI_IND_NOTNULL; *ret_l = len; /* Return pointer, which PL/SQL frees later. */ return(tmp); } /*======================================================================*/ int main(char *argv, int argc) { OCIExtProcContext *ctx; char *str1; short str1_i; char *str2; short str2_i; short *ret_i; short *ret_l; /* OCI Handles */ OCIEnv *envhp; OCIServer *srvhp; OCISvcCtx *svchp; OCIError *errhp; OCISession *authp; OCIStmt *stmthp; OCILobLocator *clob, *blob; OCILobLocator *Lob_loc; /* Initialize and Logon */ (void) OCIInitialize((ub4) OCI_DEFAULT, (dvoid *)0, (dvoid * (*)(dvoid *, size_t)) 0, (dvoid * (*)(dvoid *, dvoid *, size_t))0, (void (*)(dvoid *, dvoid *)) 0 ); (void) OCIEnvInit( (OCIEnv **) &envhp, OCI_DEFAULT, (size_t) 0, (dvoid **) 0 ); (void) OCIHandleAlloc( (dvoid *) envhp, (dvoid **) &errhp, OCI_HTYPE_ERROR, (size_t) 0, (dvoid **) 0); /* Server contexts */ (void) OCIHandleAlloc( (dvoid *) envhp, (dvoid **) &srvhp, OCI_HTYPE_SERVER, (size_t) 0, (dvoid **) 0); /* Service context */ (void) OCIHandleAlloc( (dvoid *) envhp, (dvoid **) &svchp, OCI_HTYPE_SVCCTX, (size_t) 0, (dvoid **) 0); /* Attach to Oracle Database */ (void) OCIServerAttach( srvhp, errhp, (text *)"", strlen(""), 0); /* Set attribute server context in the service context */ (void) OCIAttrSet ((dvoid *) svchp, OCI_HTYPE_SVCCTX, (dvoid *)srvhp, (ub4) 0, OCI_ATTR_SERVER, (OCIError *) errhp); (void) OCIHandleAlloc((dvoid *) envhp, (dvoid **)&authp, (ub4) OCI_HTYPE_SESSION, (size_t) 0, (dvoid **) 0); (void) OCIAttrSet((dvoid *) authp, (ub4) OCI_HTYPE_SESSION, (dvoid *) "samp", (ub4)4, (ub4) OCI_ATTR_USERNAME, errhp); (void) OCIAttrSet((dvoid *) authp, (ub4) OCI_HTYPE_SESSION, (dvoid *) "password", (ub4) 4, (ub4) OCI_ATTR_PASSWORD, errhp); /* Begin a User Session */ checkerr(errhp, OCISessionBegin ( svchp, errhp, authp, OCI_CRED_RDBMS, (ub4) OCI_DEFAULT)); (void) OCIAttrSet((dvoid *) svchp, (ub4) OCI_HTYPE_SVCCTX, (dvoid *) authp, (ub4) 0, (ub4) OCI_ATTR_SESSION, errhp); /* -----------------------User Logged In------------------------------*/ printf ("user logged in \n"); /* allocate a statement handle */ checkerr(errhp, OCIHandleAlloc( (dvoid *) envhp, (dvoid **) &stmthp, OCI_HTYPE_STMT, (size_t) 0, (dvoid **) 0)); checkerr(errhp, OCIDescriptorAlloc((dvoid *)envhp, (dvoid **) &Lob_loc, (ub4) OCI_DTYPE_LOB, (size_t) 0, (dvoid **) 0)); /* ------- subprogram called here-----------------------*/ printf ("calling concat...\n"); concat(ctx, str1, str1_i, str2, str2_i, ret_i, ret_l); return 0; } #endif
このサービス・ルーチンは、事前定義の例外を呼び出します。1から32,767の範囲の有効なOracle Databaseエラー番号を含む必要があります。この例外には、1から32,767の有効なOracle Databaseエラー番号が含まれている必要があります。クリーン・アップ処理後、外部プロシージャをすぐに戻す必要があります(OUT
またはIN
OUT
パラメータには値は何も割り当てられません)。この関数のCプロトタイプは、次のとおりです。
int OCIExtProcRaiseExcp( OCIExtProcContext *with_context, size_t errnum);
パラメータwith_context
およびerror_number
は、それぞれ、コンテキスト・ポインタおよびOracle Databaseエラー番号です。戻り値のOCIEXTPROC_SUCCESS
およびOCIEXTPROC_ERROR
は、正常終了および失敗を示します。
SQL*Plusで、外部プロシージャplsTo_divide_proc
を次のように発行するとします。
CREATE OR REPLACE PROCEDURE plsTo_divide_proc ( dividend IN PLS_INTEGER, divisor IN PLS_INTEGER, result OUT FLOAT) AS LANGUAGE C NAME "C_divide" LIBRARY MathLib WITH CONTEXT PARAMETERS ( CONTEXT, dividend INT, divisor INT, result FLOAT);
C_divide
は、コールされたときに2つの数値の比率を計算します。次の例で示すように、除数が0(ゼロ)の場合、C_divide
はOCIExtProcRaiseExcp
を使用して事前定義の例外ZERO_DIVIDE
を次のように呼び出します。
void C_divide (ctx, dividend, divisor, result) OCIExtProcContext *ctx; int dividend; int divisor; float *result; { /* Check for zero divisor. */ if (divisor == (int)0) { /* Raise exception ZERO_DIVIDE, which is Oracle Database error 1476. */ if (OCIExtProcRaiseExcp(ctx, (int)1476) == OCIEXTPROC_SUCCESS) { return; } else { /* Incorrect parameters were passed. */ assert(0); } } *result = (float)dividend / (float)divisor; }
このサービス・ルーチンはユーザー定義例外を呼び出し、ユーザー定義エラー・メッセージを戻します。この関数のCプロトタイプは、次のとおりです。
int OCIExtProcRaiseExcpWithMsg( OCIExtProcContext *with_context, size_t error_number, text *error_message, size_t len);
パラメータwith_context
、error_number
、error_message
は、それぞれ、コンテキスト・ポインタ、Oracle Databaseエラー番号、エラー・メッセージ・テキストです。パラメータlen
は、エラー・メッセージの長さを格納します。メッセージがNULLで終了する文字列の場合、len
は0(ゼロ)です。戻り値のOCIEXTPROC_SUCCESS
およびOCIEXTPROC_ERROR
は、正常終了および失敗を示します。
この前の例では、外部プロシージャplsTo_divide_proc
を発行しました。次の例では、別の実装方法を使用します。ここでは、除数が0(ゼロ)の場合、C_divide
はOCIExtProcRaiseExcpWithMsg
を使用してユーザー定義例外を呼び出します。
void C_divide (ctx, dividend, divisor, result) OCIExtProcContext *ctx; int dividend; int divisor; float *result; /* Check for zero divisor. */ if (divisor == (int)0) { /* Raise a user-defined exception, which is Oracle Database error 20100, and return a null-terminated error message. */ if (OCIExtProcRaiseExcpWithMsg(ctx, (int)20100, "divisor is zero", 0) == OCIEXTPROC_SUCCESS) { return; } else { /* Incorrect parameters were passed. */ assert(0); } } *result = dividend / divisor; }
コールバックを有効にするには、OCIExtProcGetEnv関数を使用します。
内容は次のとおりです。
このサービス・ルーチンを使用すると、外部プロシージャ・コール中にデータベースへのOCIコールバックが可能になります。この関数によって取得された環境ハンドルは、既存の接続を再利用してデータベースに戻ります。データベースとの接続を新しく確立する必要がある場合は、このハンドルを使用できません。自分でハンドルを作成する必要があります。
この関数のCプロトタイプは、次のとおりです。
sword OCIExtProcGetEnv ( OCIExtProcContext *with_context, OCIEnv envh, OCISvcCtx svch, OCIError errh )
パラメータwith_context
はコンテキスト・ポインタで、パラメータenvh
、svch
、errh
は、それぞれ、OCI環境、サービス、エラー・ハンドルです。戻り値のOCIEXTPROC_SUCCESS
およびOCIEXTPROC_ERROR
は、正常終了および失敗を示します。
外部CプロシージャおよびJavaクラス・メソッドでは、どちらもデータベースをコールバックしてSQL操作を実行できます。実際の例は、「例: 外部プロシージャのコール」を参照してください。
注意: コールバックは、必ずしも同一セッションで発生するとはかぎりません。OCIlogon を使用して、別セッション内でSQL文を実行することができます。 |
Oracle Database上で実行される外部Cプロシージャでは、サービス・ルーチンをコールしてOCI環境およびサービス・ハンドルを取得できます。OCIを使用すると、SQL文およびPL/SQLサブプログラムの実行、データのフェッチ、およびLOBの操作のためにコールバックを使用することができます。コールバックおよび外部プロシージャは同一のユーザー・セッションおよびトランザクション・コンテキストで動作するため、ユーザー権限も同一です。
SQL*Plusで、次のスクリプトを実行するとします。
CREATE TABLE Emp_tab (empno NUMBER(10)) CREATE PROCEDURE plsToC_insertIntoEmpTab_proc ( empno PLS_INTEGER) AS LANGUAGE C NAME "C_insertEmpTab" LIBRARY insert_lib WITH CONTEXT PARAMETERS ( CONTEXT, empno LONG);
後で、次のように外部プロシージャplsToC_insertIntoEmpTab_proc
からサービス・ルーチンOCIExtProcGetEnv
をコールできます。
#include <stdio.h> #include <stdlib.h> #include <oratypes.h> #include <oci.h> /* includes ociextp.h */ ... void C_insertIntoEmpTab (ctx, empno) OCIExtProcContext *ctx; long empno; { OCIEnv *envhp; OCISvcCtx *svchp; OCIError *errhp; int err; ... err = OCIExtProcGetEnv(ctx, &envhp, &svchp, &errhp); ... }
コールバックを使用しない場合は、oci
.h
を含める必要はなく、ociextp
.h
のみを含めます。
外部プロシージャからオブジェクト関連コールバックを実行するために、extproc
エージェント内のOCI環境がオブジェクト・モードで完全に初期化されています。この環境へのハンドルは、OCIExtProcGetEnv
プロシージャを使用して取得します。
オブジェクト・ランタイム環境を使用すると、OCIによって提供される静的および動的なオブジェクト・サポートが使用できます。静的サポートを使用するには、OTTを使用して該当するオブジェクト型用のC構造体を生成し、次に、従来のCコードを使用してオブジェクトの属性にアクセスします。
外部プロシージャの作成時に型がわからないオブジェクトについては、かわりの動的なオブジェクト・アクセス方法によって、OCIDescribeAny
がコールされてその型の属性およびメソッド情報が取得されます。その後、OCIObjectGetAttr
およびOCIObjectSetAttr
をコールして、属性値を取得して設定します。
現在の外部プロシージャ・モデルはステートレスのため、コールバックを実行する外部プロシージャごとにOCIExtProcGetEnv
をコールするか、または、OCIExtProc
サービス・ルーチンをコールします。外部プロシージャがコールされるたびに、コール後にコールバック・メカニズムがクリーン・アップされ、OCIハンドルが解放されます。
コールバックでは、次のSQL文およびOCIサブプログラムはサポートされていません。
COMMIT
などのトランザクション制御文
CREATE
などのデータ定義文
次のオブジェクト指向OCIサブプログラム
OCIObjectNew OCIObjectPin OCIObjectUnpin OCIObjectPinCountReset OCIObjectLock OCIObjectMarkUpdate OCIObjectUnmark OCIObjectUnmarkByRef OCIObjectAlwaysLatest OCIObjectNotAlwaysLatest OCIObjectMarkDeleteByRef OCIObjectMarkDelete OCIObjectFlush OCIObjectFlushRefresh OCIObjectGetTypeRef OCIObjectGetObjectRef OCIObjectExists OCIObjectIsLocked OCIObjectIsDirtied OCIObjectIsLoaded OCIObjectRefresh OCIObjectPinTable OCIObjectArrayPin OCICacheFlush, OCICacheFlushRefresh, OCICacheRefresh OCICacheUnpin OCICacheFree OCICacheUnmark OCICacheGetObjects OCICacheRegister
OCIGetPieceInfo
などのポーリング・モードOCIサブプログラム
次の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
外部プロシージャが失敗する場合は、通常、そのプロトタイプに不具合があります。プロトタイプが、PL/SQLによって内部的に生成されたプロトタイプと一致しないということです。これは、互換性のないCデータ型を指定した場合に発生する可能性があります。たとえば、型がREAL
のOUT
パラメータを渡すためにfloat *
を指定したとします。float
、double
*
または他のCデータ型を指定すると、結果が不一致になります。
このような場合は、次のエラーを受け取ることがあります。
lost RPC connection to external routine agent
このエラーは、外部プロシージャによってコア・ダンプが発生したため、extproc
が異常終了したことを表します。Cプロトタイプ・パラメータの宣言時にエラーを回避するには、前述の表を参照してください。
PL/SQLでは、外部プロシージャのデバッグを支援する目的でユーティリティ・パッケージDEBUG_EXTPROC
が提供されています。このパッケージをインストールするには、PL/SQLデモ・ディレクトリにあるスクリプトdbgextp
.sql
を実行します。(ディレクトリの場所については、ご使用のOracle Databaseのインストール・ガイドまたはユーザーズ・ガイドを参照してください。)
このパッケージを使用するには、dbgextp
.sql
の指示に従ってください。Oracle Databaseアカウントに、パッケージに対するEXECUTE
権限およびCREATE
LIBRARY
権限が必要です。
注意: DEBUG_EXTPROC は、実行中のプロセスにアタッチできるデバッガ付きのプラットフォームでのみ動作します。 |
PL/SQLデモ・ディレクトリには、外部プロシージャのコール方法を示すスクリプトextproc
.sql
もあります。付属ファイルextproc
.c
には、外部プロシージャ用のCソース・コードが含まれています。
デモを実行するには、extproc
.sql
にある指示に従ってください。SCOTT
アカウントを使用する必要があり、このアカウントにはCREATE
LIBRARY
権限が必要です。
グローバル変数は関数の外で宣言され、その値はプログラムのすべての関数によって共有されます。したがって、外部プロシージャの場合、DLL内のすべての関数がグローバル変数の値を共有します。グローバル変数は関数の存続期間を超えて存続するデータの格納にも使用されます。しかし、グローバル変数の使用は、次の2つの理由のため、お薦めしません。
スレッド
エージェント・プロセスは非スレッド構成になっているため、一度にアクティブになる関数は1つのみです。マルチスレッドのextproc
エージェントの場合は、一度に複数の関数をアクティブにできます。その場合は、それらが同時にグローバル変数にアクセスしようとして、正常な結果が得られなくなる可能性があります。
DLLのキャッシング
グローバル変数にデータを格納することで、関数func1
が関数func2
にデータを渡すとします。func1
の完了後DLLキャッシュはアンロードされ、この結果、グローバル変数のすべての値が失われる可能性があります。次に、func2
が実行されるときに、DLLは再ロードされ、グローバル変数はすべて0(ゼロ)に初期化されます。
静的変数には外部変数および内部変数の2種類があります。外部静的変数は、特殊なグローバル変数であり、使用をお薦めしません。内部静的変数は、特定の関数に対するローカルな変数ですが、関数がアクティブになるたびに生成、消滅するのではなく、存在したまま残ります。このため、この変数は1つの関数内にプライベートで永続的な記憶域を提供します。このような変数は、同一関数を後でコールするときにデータを渡すために使用します。ただし、DLLには前述のキャッシュ機能があるため、DLLがコールごとにアンロードおよび再ロードされる可能性があります。これは、内部静的変数がその値を失う可能性があることを表します。
参照: 動的リンク・ライブラリの作成方法は、RDBMSサブディレクトリ/public にあるテンプレートmakefile を参照してください。 |
外部プロシージャをコールする場合:
IN
パラメータには書込みを行わないでください。OUT
パラメータの容量をオーバーフローさせないようにしてください。(PL/SQLでは、実行時にこの2つのエラー状態はチェックしません。)
OUT
パラメータまたは関数結果を読み込まないでください。
IN
OUT
パラメータとOUT
パラメータおよび関数結果には、必ず値を代入してください。代入しないと、外部プロシージャが正常に戻りません。
WITH
CONTEXT
およびPARAMETERS
句を含める場合は、パラメータ・リスト内でのコンテキスト・ポインタの位置を示すパラメータCONTEXT
を指定する必要があります。
PARAMETERS
句を含めたときに、外部プロシージャが関数の場合は、パラメータRETURN
を最後の位置に指定する必要があります。
各仮パラメータには、PARAMETERS
句に対応するパラメータが必要です。また、PARAMETERS
句のパラメータのデータ型が、Cプロトタイプのデータ型と互換性があることも確認します。これは、暗黙的変換が実行されないためです。
型がRAW
またはLONG
RAW
のパラメータの場合は、プロパティLENGTH
を使用してください。また、そのパラメータがIN
OUT
またはOUT
およびNULLの場合は、対応するCパラメータの長さを0(ゼロ)に設定する必要があります。
外部プロシージャには次の制限が適用されます。
この機能は、DLLをサポートするプラットフォームのみで使用可能です。
CプロシージャまたはCからコール可能なプロシージャのみがサポートされます。
分散トランザクションと組み合せた外部プロシージャ・コールアウトはサポートされません。
PL/SQLカーソル変数またはレコードを外部プロシージャに渡すことはできません。レコードについては、かわりにオブジェクト型のインスタンスを使用できます。
LIBRARY
副次句には、リモート・ライブラリを指定するデータベース・リンクは使用できません。
外部プロシージャに渡すことができるパラメータの最大数は128です。ただし、FLOAT型またはDOUBLE型のパラメータを値渡しする場合は、最大数は127以下になります。どのくらい減るかは、パラメータの個数およびオペレーティング・システムによって異なります。目安としては、値渡しされるFLOAT型またはDOUBLE型のパラメータ1つを2つ分として数えます。