ヘッダーをスキップ

Oracle Database アプリケーション開発者ガイド-基礎編
10gリリース2(10.2)

B19248-02
目次
目次
索引
索引

戻る 次へ

14 外部プロシージャのコール

必要とする機能が提供されない言語を使用する場合、または別の言語で作成された既存コードを再利用する場合は、外部プロシージャをコールして、その他の言語で作成されたコードを使用できます。

この章の内容は次のとおりです。

複数言語プログラムの概要

Oracle Databaseは、次に示す複数の異なる言語での操作が可能です。

これらの実装言語の中から何をどのように選択すればよいのでしょうか?各言語には、それぞれ異なるメリットがあります。使用しやすさ、特定の専門知識を持つプログラマがいるかどうか、移植が必要かどうか、さらに既存コードの有無などが重要な決定要因になります。

ただし、アプリケーションでOracle Databaseをどのように使用するかによって、次のように選択範囲が狭くなる可能性があります。

最も重要なことは、パフォーマンスの観点では、サーバーのアドレス空間内で実行されるのは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操作を実行できます。

このようなプロシージャは必要なときにのみロードされるため、メモリーが節約されます。コール仕様が実装本体から切り離されているため、コール側プログラムに影響することなく、プロシージャを拡張できます。

外部プロシージャを使用すると、次のようなことができます。

外部プロシージャ用のコール仕様の概要

外部プロシージャの発行は、コール仕様を介して行いますが、このコール仕様には、AS LANGUAGE句を介したAS EXTERNAL機能のスーパーセットが含まれます。AS LANGUAGEコール仕様を使用すると、外部Cプロシージャを発行できる他に、Javaクラス・メソッドも発行できます。


注意

レガシー・アプリケーションをサポートするために、コール仕様では、AS EXTERNAL句を使用して発行することもできます。ただし、新しいアプリケーションを開発する場合は、AS LANGUAGE句を使用することをお薦めします。  


一般に、コール仕様を使用することによって、次のようなことができます。

既存プログラムを外部プロシージャとして使用するには、そのプログラムをロードして発行した後、コールします。

外部プロシージャのロード

外部CプロシージャまたはJavaメソッドをPL/SQLで使用できるようにするには、プロシージャまたはメソッドをロードする必要があります。ロード方法は、プロシージャがCで作成されているかJavaで作成されているかによって異なります。

Javaクラス・メソッドのロード

Javaプログラムのロード方法の1つとして、SQL*Plusから対話形式で実行できるCREATE JAVA文を使用する方法があります。CREATE JAVA文からJava Virtual Machine(JVM)ライブラリ・マネージャが暗黙的に起動され、そのときにこのライブラリ・マネージャがJavaバイナリ(.classファイル)およびリソースをローカルなBFILEまたはLOB列からRDBMSライブラリ・ユニットにロードします。

コンパイル済のJavaクラスが次のオペレーティング・システム・ファイル内に格納されているとします。

/home/java/bin/Agent.class

ファイルAgent.classから、スキーマscott内にクラス・ライブラリ・ユニットを作成するには、2つの手順を行う必要があります。まず、サーバーのファイル・システム上にディレクトリ・オブジェクトを作成します。ディレクトリ・オブジェクトの名前は、Agent.classのディレクトリ・パスの別名です。

ディレクトリ・オブジェクトを作成するには、次のように、ユーザーscottCREATE ANY DIRECTORY権限を付与してから、CREATE DIRECTORY文を実行する必要があります。

CONNECT System/Manager
GRANT CREATE ANY DIRECTORY TO Scott IDENTIFIED BY Tiger;
CONNECT Scott/Tiger
CREATE DIRECTORY Bfile_dir AS '/home/java/bin';

これで、次のようにクラス・ライブラリ・ユニットを作成する準備ができました。

CREATE JAVA CLASS USING BFILE (Bfile_dir, 'Agent.class');

ライブラリ・ユニットの名前は、クラスの名前から導出されます。

別の方法として、コマンドライン・ユーティリティLoadJavaを使用できます。このユーティリティは、Javaバイナリおよびリソースを、システムによって生成されたデータベース表にアップロードしてから、CREATE JAVA文を使用してJavaファイルをRDBMSライブラリ・ユニットにロードします。Javaファイルは、ファイル・システム、Java IDE、イントラネットまたはインターネットからアップロードできます。

外部Cプロシージャのロード

Cで作成された外部プロシージャまたはCアプリケーションからコール可能な外部プロシージャを使用できるようにデータベース構成を設定するには、開発者およびデータベース管理者が次の手順を実行します。


注意

  • この機能は、動的リンク・ライブラリ(DLL)をサポートするプラットフォーム、またはSolarisの.soライブラリなどの動的にロード可能な共有ライブラリをサポートするプラットフォームのみで使用できます。

  • 外部プロシージャ・エージェントは、使用されているコール規格に準拠するライブラリ内でプロシージャをコールできます。 サポートされているコール規格はCです。 PL/SQLの外部プロシージャで使用されるコール規格の副次句の詳細は、「CALLING STANDARD」を参照してください。

  • 全幅より小さい数値データ型(floatshortcharなど)を含まない、1)K&Rスタイル・プロトタイプまたは2)ISO/ANSIプロトタイプのいずれかを使用して、Cプロシージャを定義します。 他のデータ型は、デフォルトの引数プロモーションでサイズが変更されない場合、ISO/ANSIプロトタイプで機能します。

    /* Supported K & R */
    void C_findRoot(x)
     float x;
    ...
    
    /* Supported ISO/ANSI */
    void C_findRoot(double x)
    ...
    
    /* Not supported ISO/ANSI */
    void C_findRoot(float x)
    ...
    
 

手順 1    環境の設定

Cで作成された外部プロシージャまたはCアプリケーションからコール可能な外部プロシージャを使用できるようにデータベース構成を設定するには、データベース管理者が次の作業を実行します。

図14-1に、マルチスレッドの外部プロシージャ・エージェントのアーキテクチャを示します。ユーザー・セッション1および2から、いくつかのDLL内にあるファンクションへのコールアウトの要求が発行されます。これらの要求は、異機種間サービスを介してマルチスレッドのextprocエージェントに送られます。エージェントのディスパッチャ・スレッドがこれらの要求を処理し、タスク・スレッドに渡します。タスク・スレッドが実際に要求を処理し、DLLをそれぞれロードし、そこに含まれているファンクションをコールします。

手順 2    DLLを識別する

ここでの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'];

DLLには、名前のみではなく、フル・パスを指定することをお薦めします。次の例では、DLLであるutils.soを表す別名ライブラリc_utilsを作成します。

CREATE LIBRARY C_utils AS '/DLLs/utils.so';

DLLを柔軟に指定するために、表記規則${VAR_NAME}を使用してパスのルート部分を環境変数として指定し、その変数をlistener.oraエントリのENVSセクションに設定できます。

次の例では、agent_linkという名前で指定されるエージェントを使用して、ライブラリC_Utilsで外部プロシージャを実行します。環境変数EP_LIB_HOMEは、エージェントによって/usr/bin/dllなどのそのインスタンス用の適切なパスに展開されます。変数EP_LIB_HOMEは、エージェントがアクセスできるように、listener.oraファイル内に設定する必要があります。

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';

セキュリティ上の理由から、デフォルト状態のEXTPROCは、ディレクトリ$ORACLE_HOME/binまたは$ORACLE_HOME/libに存在するDLLのみをロードするようになっています。また、EXTPROCに接続できるのは、同じマシンで実行されているOracle Databaseのクライアント・プロセスであるローカル・セッションのみです。

他のディレクトリからDLLをロードするには、環境変数EXTPROC_DLLSを設定する必要があります。この環境変数の値には、完全パスで修飾したDLL名をコロン(:)で区切ったリストを指定します。次に例を示します。

EXTPROC_DLLS=/private1/home/scott/dll/myDll.so:/private1/home/scott/dll/newDll.so

この環境変数の設定には、listener.oraファイル内のENVSパラメータを使用することをお薦めします。EXTPROC機能の詳細は、Oracle Netのマニュアルを参照してください。

次の点に注意してください。

手順 3    外部プロシージャを公開する

新しい外部Cプロシージャを作成してから、これをDLLに追加します。プロシージャがDLL内にある場合、次の項で説明されるコール仕様のメカニズムを使用して、プロシージャを公開します。

外部プロシージャの公開

Oracle Databaseで使用できる外部プロシージャは、コール仕様で公開される外部プロシージャのみです。Javaクラス・メソッドまたはC外部プロシージャの名前、パラメータ型および戻り型が、対応するSQLの要素にマップされます。これはPL/SQLストアド・サブプログラムと同様に作成しますが、宣言やBEGIN ...ENDブロックではなく、AS LANGUAGE句を本文内に作成します。

AS LANGUAGE句では、次の指定を行います。

プロシージャ、ファンクション、パッケージ仕様部、パッケージ本体、型仕様または型本体用の通常のCREATE OR REPLACE構文を使用して、宣言を開始します。

コール仕様は、名前宣言およびパラメータ宣言の後に指定します。構文は次のとおりです。

{IS | AS} LANGUAGE {C | JAVA}


注意

Oracle Databaseでは、ANSI SQL92外部プロシージャをPL/SQL用に変更して使用しますが、ANSIキーワードのAS EXTERNALがコール仕様構文に置き換えられています。最初はJavaクラス・メソッド用として導入されたこの新しい構文が、Cプロシージャにまで拡張されています。 


これに続いて、次のいずれかを指定します。

NAME  java_string_literal_name

java_string_literal_nameは、Javaメソッドのシグネチャです。

LIBRARY library_name
[NAME c_string_literal_name]
[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型は認識されません。このため、構文がより複雑になります。 


Javaクラス・メソッド用のAS LANGUAGE句

AS LANGUAGE句は、PL/SQLとJavaクラス・メソッドの間のインタフェースです。

外部Cプロシージャ用のAS LANGUAGE句

次の副次句は、PL/SQLに対して、外部Cプロシージャを検索する場所、そのプロシージャのコール方法、プロシージャに何を渡すかを指示します。必要な副次句は、LIBRARY副次句のみです。

LIBRARY

ローカルの別名ライブラリを指定します(リモート・ライブラリの指定にデータベース・リンクは使用できません)。ライブラリ名はPL/SQL識別子です。このため、名前を二重引用符で囲むと、名前の大/小文字が区別されます(デフォルトでは、名前は大文字で格納されます)。別名ライブラリにはEXECUTE権限が必要です。

NAME

コール対象の外部Cプロシージャを指定します。プロシージャ名を二重引用符で囲むと、名前の大/小文字が区別されます(デフォルトでは、名前は大文字で格納されます)。この副次句を省略すると、プロシージャ名はPL/SQLサブプログラム名を大文字で表したものがデフォルト設定されます。


注意

LANGUAGEおよびCALLING STANDARDは、旧版のAS EXTERNAL句のみに適用されます。 


LANGUAGE

外部プロシージャが作成されている3GLを指定します。この副次句を省略すると、言語名はCにデフォルト設定されます。

CALLING STANDARD

外部プロシージャをコンパイルしたコール規格を指定します。サポートされているコール規格はCです。この副次句を省略すると、コール規格はCにデフォルト設定されます。

WITH CONTEXT

コンテキスト・ポインタが外部プロシージャに渡されることを指定します。コンテキスト・データ構造体は外部プロシージャに対して不透明ですが、外部プロシージャによってコールされるサービス・プロシージャでは使用できます。

PARAMETERS

外部プロシージャに渡されるパラメータの位置およびデータ型を指定します。現在の長さや最大長などのパラメータ・プロパティや、パラメータの受渡し方法(値によるか参照によるか)も指定できます。

AGENT IN

このプロシージャを実行するエージェント・プロセスの名前を保持するパラメータを指定します。これは、外部プロシージャ・エージェントが、複数のエージェント・プロセスを使用して実行される場合を想定して行います。これによって、1つの外部プロシージャのエージェント・プロセスにエラーが発生した場合の信頼性が保証されます。エージェント・プロセスの名前(データベース・リンクの名前に対応)を渡すことができ、またtnsnames.oraおよびlistener.oraが両方のインスタンス間で正しく設定されている場合、外部プロシージャが、もう一方のインスタンスで起動します。両方のインスタンスは、同じホスト上に存在する必要があります。

この操作は、CREATE LIBRARY文のAGENT句の場合と同様です。AGENT INを使用して実行時の値を指定すると、柔軟性が向上します。

このようにエージェント名を指定すると、別名ライブラリで宣言されるエージェント名はいずれもオーバーライドされます。エージェント名が指定されていない場合は、extprocエージェントがコール側プログラムと同じインスタンス上でデフォルト設定されます。

Javaクラス・メソッドの発行

Javaクラスおよびそのメソッドは、RDBMSライブラリ・ユニットに格納されます。この中には、LOADJAVAユーティリティまたはSQL文CREATE JAVAを使用して、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プロシージャの発行

次の例では、C関数Cdivisor_funcを外部関数として発行するplsCallsCdivisor_funcというPL/SQLスタンドアロン・ファンクションを作成します。

CREATE OR REPLACE FUNCTION Plscallscdivisor_func (
/* Find greatest common divisor of x and y: */
   x     BINARY_INTEGER, 
   y     BINARY_INTEGER) 
RETURN BINARY_INTEGER 
AS LANGUAGE C
   LIBRARY C_utils
   NAME "Cdivisor_func"; /* Quotation marks preserve case. */

コール仕様の位置

Javaクラス・メソッドおよび外部Cプロシージャはともに、次のいずれかの位置にコール仕様を指定できます。

スタンドアロンPL/SQLファンクションでのコール仕様の例はすでに説明しました。ここでは、その他の位置について例をいくつか示します。


注意

次のいくつかの例では、コール仕様の完全指定にAUTHID句およびSQL_NAME_RESOLVE句が必要な場合と必要でない場合があります。

詳細は、『PL/SQLユーザーズ・ガイドおよびリファレンス』および『Oracle Database SQLリファレンス』を参照してください。 


例: PL/SQLパッケージ内へのコール仕様の配置

CREATE OR REPLACE PACKAGE Demo_pack 
AUTHID DEFINER 
AS
   PROCEDURE plsToC_demoExternal_proc (x BINARY_INTEGER, y VARCHAR2, z DATE) 
   AS LANGUAGE C
      NAME "C_demoExternal"
      LIBRARY SomeLib
      WITH CONTEXT
      PARAMETERS(CONTEXT, x INT, y STRING, z OCIDATE);
END;

例: PL/SQLパッケージ本体内へのコール仕様の配置

CREATE OR REPLACE PACKAGE Demo_pack 
   AUTHID CURRENT_USER
AS 
   PROCEDURE plsToC_demoExternal_proc(x BINARY_INTEGER, y VARCHAR2, z DATE);
END;
 
CREATE OR REPLACE PACKAGE BODY Demo_pack 
   SQL_NAME_RESOLVE CURRENT_USER
AS
   PROCEDURE plsToC_demoExternal_proc (x BINARY_INTEGER, y VARCHAR2, z DATE)
   AS LANGUAGE JAVA
      NAME 'pkg1.class4.methodProc1(int,java.lang.String,java.sql.Date)';
END;

例: オブジェクト型仕様内へのコール仕様の配置


注意

次のようなデータ構造を設定しないと機能しない例もあります。

CONN SYS/CHANGE_ON_INSTALL AS SYSDBA;
GRANT CREATE ANY LIBRARY TO scott;
CONNECT scott/tiger
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 BINARY_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 BINARY_INTEGER, y VARCHAR2, z DATE)
);

CREATE OR REPLACE TYPE BODY Demo_typ 
AS
   MEMBER PROCEDURE plsToJ_demoExternal_proc (x BINARY_INTEGER, y VARCHAR2, z DATE)
   AS LANGUAGE JAVA
      NAME 'pkg1.class4.J_demoExternal(int,java.lang.String,java.sql.Date)';
END;

例: AUTHIDを指定したJava

スタンドアロンPL/SQLサブプログラム内にJavaクラス・メソッドを発行する例を次に示します。

CREATE OR REPLACE PROCEDURE plsToJ_demoExternal_proc (x BINARY_INTEGER, y VARCHAR2, z 
DATE)
   AUTHID CURRENT_USER 
AS LANGUAGE JAVA
   NAME 'pkg1.class4.methodProc1(int,java.lang.String,java.sql.Date)';

例: オプションのAUTHIDを指定したC

スタンドアロンPL/SQLプログラム内にCプロシージャをAS EXTERNALとして発行する例を次に示します。AUTHID句はオプションです。これによって、Oracle Database8 R8.0の外部プロシージャとの互換性が保たれます。

CREATE OR REPLACE PROCEDURE plsToC_demoExternal_proc (x BINARY_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 BINARY_INTEGER, y VARCHAR2, z DATE);
   PROCEDURE plsToC_demoExternal_proc (x BINARY_INTEGER, y VARCHAR2, z DATE); 
   PROCEDURE plsToC_InBody_proc (x BINARY_INTEGER, y VARCHAR2, z DATE);
   PROCEDURE plsToJ_InBody_proc (x BINARY_INTEGER, y VARCHAR2, z DATE);

   PROCEDURE plsToJ_InSpec_proc (x BINARY_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 BINARY_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 BINARY_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 BINARY_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 BINARY_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 BINARY_INTEGER, y VARCHAR2, z DATE)
   IS LANGUAGE JAVA
      NAME 'pkg1.class4.J_InBody_meth(int,java.lang.String,java.sql.Date)';
END;

コール仕様による外部Cプロシージャへのパラメータの受渡し

コール仕様によって、PL/SQLデータ型とCデータ型とのマッピングが可能になります。データ型マッピングの詳細は、「データ型の指定」を参照してください。

外部Cプロシージャへのパラメータの受渡しは、次のような理由で複雑になります。

次の項では、前述の状況に対処するパラメータ・リストを指定する方法を説明します。


注意

C外部プロシージャに渡すことができるパラメータの最大数は128です。ただし、FLOAT型またはDOUBLE型のパラメータを値渡しする場合は、最大数は127以下になります。どのくらい減るかは、パラメータの個数およびオペレーティング・システムによって異なります。目安としては、値渡しされるFLOAT型またはDOUBLE型のパラメータ1つを2つ分として数えます。 


データ型の指定

パラメータは外部プロシージャに直接渡さないでください。かわりに、外部プロシージャを発行したPL/SQLサブプログラムに渡します。したがって、パラメータにはPL/SQLデータ型を指定する必要があります。PL/SQLデータ型は、デフォルトの外部データ型にマップされます(表14-1を参照)。

表 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
 
NATURAL1
NATURALN1
POSITIVE1
POSITIVEN1
SIGNTYPE1
 
[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
DEC1
DECIMAL1
INT1
INTEGER1
NUMERIC1
SMALLINT1
 
OCINUMBER
 
OCINUMBER
 
DATE
 
OCIDATE
 
OCIDATE
 
TIMESTAMP
TIMESTAMP WITH TIME ZONE
TIMESTAMP WITH LOCAL TIME ZONE
 
OCIDateTime
 
OCIDateTime
 
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プロトタイプ・パラメータの宣言時のエラーを回避するには、表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 (non-final types)
 
dvoid*
 
dvoid*
 
dvoid**
 

コンポジット・オブジェクト型は、自己記述型でありません。記述は、型記述子オブジェクト(TDO)に格納されています。オブジェクトおよびオブジェクトのインジケータ構造体に事前定義のOCIデータ型はありませんが、Oracle Databaseのオブジェクト型トランスレータ(OTT)によって生成されるデータ型を使用する必要があります。INDICATORおよびコンポジット・オブジェクトのオプションのTDO引数には、通常、Cデータ型のOCIType*があります。

REFおよびコレクション引数のOCICOLLはオプションで、完全性を保つためにのみ存在しています。REFまたはコレクションを別のデータ型にマップすることは(その逆も)できません。

INおよびIN OUTパラメータ・モードのBY VALUE/BY REFERENCE

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引数は、すべて参照によって渡されます。

PARAMETERS句

一般に、外部プロシージャを発行する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句を使用して追加情報を指定できます。指定できる追加情報は次のとおりです。

PARAMETERS句を使用する場合、次のことを認識しておく必要があります。

デフォルトのデータ型マッピングのオーバーライド

PARAMETERS句を使用して、デフォルトのデータ型マッピングをオーバーライドすることができる場合もあります。たとえば、外部データ型INTから外部データ型CHARにPL/SQLデータ型のBOOLEANをマッピングしなおすことができます。

プロパティの指定

PARAMETERS句は、PL/SQLの仮パラメータおよびファンクション結果に関する追加情報を外部プロシージャに渡すために使用することもできます。これは、次のプロパティを1つ以上指定して行います。

INDICATOR [{STRUCT | TDO}]
LENGTH
DURATION
MAXLEN
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 BINARY_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(x, x_ind, y, y_len, y_maxlen, retind)
int    x;
short  x_ind;
char  *y;
int   *y_len;
int   *y_maxlen;
short *retind;

インジケータ変数x_indのデータ型はshortである必要があり、shortはISO/ANSIプロトタイプでは使用できないため、K&Rプロトタイプが必要です。

Cプロトタイプ内の追加パラメータは、INDICATORx用)、LENGTHy用)およびMAXLENy用)の他に、PARAMETERS句のファンクション結果のINDICATORにも対応します。パラメータRETURNは結果値を格納し、C関数識別子に対応します。

INDICATOR

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を指定しても)読込み専用です。OUTIN OUTおよびRETURNパラメータの場合、INDICATORはデフォルトで参照によって渡されます。

INDICATORにはSTRUCTオプションまたはTDOオプションを指定することもできます。INDICATORをオブジェクトのプロパティとして指定することはサポートされていないため、また、オブジェクトの引数にはINDICATORスカラーのかわりに完全なインジケータ構造体があるため、STRUCTオプションを使用して指定する必要があります。コンポジット・オブジェクトおよびコレクションには、型記述子オブジェクト(TDO)を使用する必要があります。

LENGTHおよびMAXLEN

PL/SQLでは、RAWパラメータまたは文字列パラメータの長さを示す標準的な方法はありません。ただし、このようなパラメータの長さの受渡しを外部プロシージャとの間で行う必要がある場合が数多くあります。プロパティLENGTHおよびMAXLENを使用すると、仮パラメータの現在の長さおよび最大長を格納するパラメータを指定できます。


注意

型がRAWまたはLONG RAWのパラメータの場合は、プロパティLENGTHを使用してください。また、そのパラメータがIN OUTおよびNULLか、またはOUTおよびNULLの場合は、対応するCパラメータを長さ0(ゼロ)に設定してください。 


INパラメータの場合は、LENGTHは(BY REFERENCEを指定しないかぎり)値渡しされ、読込み専用です。OUTIN OUTおよびRETURNパラメータの場合は、LENGTHはデフォルトで参照によって渡されます。

前述のように、MAXLENINパラメータには適用されません。OUTIN OUTおよびRETURNパラメータの場合、MAXLENは参照によって渡され、読込み専用です。

CHARSETIDおよびCHARSETFORM

Oracle Databaseではグローバリゼーション・サポートを提供していますが、これを使用すると、シングルバイトおよびマルチバイトの文字データを処理し、キャラクタ・セット間で変換を行うことができます。また、アプリケーションを異なる言語環境で実行することもできます。

デフォルトでは、サーバーおよびエージェントが同じ$ORACLE_HOME値を使用する場合、エージェントはサーバー(ALTER SESSIONコマンドで指定した設定を含む)と同一のグローバリゼーション・サポート設定を使用します。

エージェントが別の$ORACLE_HOME(2つの異なる別名およびシンボリック・リンクによって同じ場所が指定されている場合でも)で実行されている場合、キャラクタ・セット以外はサーバーと同じグローバリゼーション・サポート設定が使用されます。エージェント用のデフォルト・キャラクタ・セットはエージェントの環境設定NLS_LANGおよびNLS_NCHARによって定義されます。

プロパティCHARSETIDおよびCHARSETFORMでは、渡された文字データの生成元である、デフォルト以外のキャラクタ・セットが識別されます。CHARCLOBおよびVARCHAR2型パラメータにより、CHARSETIDおよびCHARSETFORMを使用してキャラクタ・セットのIDおよび形式を外部プロシージャに渡すことができます。

INパラメータの場合、CHARSETIDおよびCHARSETFORMは(BY REFERENCEを指定しないかぎり)値渡しされ、(BY REFERENCEを指定した場合でも)読込み専用です。OUTIN OUTおよびRETURNパラメータの場合、CHARSETIDおよびCHARSETFORMは参照によって渡され、読込み専用です。

これらのプロパティのOCI属性名は、OCI_ATTR_CHARSET_IDおよび
OCI_ATTR_CHARSET_FORMです。

関連項目

OCIで各国語データを使用する場合の詳細は、『Oracle Call Interfaceプログラマーズ・ガイド』および『Oracle Databaseグローバリゼーション・サポート・ガイド』を参照してください。 

パラメータの再配置

外部プロシージャの仮パラメータは、それぞれPARAMETERS句の中に対応するパラメータが必要です。PL/SQLではパラメータを位置ではなく名前によって対応付けるため、対応するパラメータの位置は異なる可能性があります。ただし、PARAMETERS句および外部プロシージャのためのCプロトタイプでは、同数のパラメータが同じ順序で必要です。

SELFの使用

SELFは、オブジェクト型のメンバーであるファンクションまたはプロシージャに常に存在する引数で、オブジェクトのインスタンスそのものです。ほとんどの場合、この引数は暗黙的で、PL/SQLプロシージャの引数リストには含まれていません。ただし、SELFは、PARAMETERS句の引数として明示的に指定する必要があります。

たとえば、ユーザーが、個人の名前および生年月日で構成されるPersonオブジェクトを作成し、さらに、このオブジェクト型の表を作成するとします。ユーザーは、後でこの表の各Personの年令を判断する必要があるとします。


注意

次のようなデータ構造を設定しないと機能しない例もあります。

CONNECT system/manager
GRANT CONNECT,RESOURCE,CREATE LIBRARY TO scott IDENTIFIED BY tiger;
CONNECT scott/tiger
CREATE OR REPLACE LIBRARY agelib UNTRUSTED IS
'/tmp/scott1.so';.

この例は、Solaris専用です。他のプラットフォームでは、他のライブラリやパスが必要になる場合があります。 


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
   ('SCOTT', TO_DATE('14-MAY-85'));

INSERT INTO Person_tab VALUES
   ('TIGER', TO_DATE('22-DEC-71'));

最後に、この表から対象の情報を取得します。

SELECT p.name, p.b_date, p.calcAge_func() FROM Person_tab p; 

NAME                           B_DATE    P.CALCAGE_ 
------------------------------ --------- ---------- 
SCOTT                          14-MAY-85          0 
TIGER                          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句を含めることによって、外部プロシージャで、パラメータ、例外、メモリー割当ておよびユーザー環境に関する情報にアクセスできるようになります。WITH CONTEXT句は、コンテキスト・ポインタが外部プロシージャに渡されることを指定します。たとえば、次のPL/SQLファンクションを作成するとします。

CREATE OR REPLACE FUNCTION getNum_func (
   x IN REAL) 
RETURN BINARY_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は、ININ OUTおよびOUTパラメータ・モードのみでなく、値を戻すプロシージャのRETURN句もサポートします。

CALL文による外部プロシージャの実行

これで、Javaクラス・メソッドまたは外部Cプロシージャが発行されました。次は、これを起動します。

外部プロシージャは直接コールしないでください。かわりに、CALL文を使用して、外部プロシージャを発行したPL/SQLサブプログラムをコールしてください。「CALL文の構文」を参照してください。

このようなコールは、通常のPL/SQLプロシージャまたはファンクションに対するコールと同じようにコーディングして、次のような場所に入れることができます。

サーバー側またはクライアント側(たとえば、Oracle Formsのようなツール内)で実行されるPL/SQLブロックまたはサブプログラムは、外部プロシージャをコールできます。サーバー側では、外部プロシージャは別のプロセスのアドレス空間内で実行されるため、データベースが保護されます。図14-2に、Oracle Databaseと外部プロシージャ間の処理を示します。

図 14-2    Oracle Databaseと外部プロシージャ


画像の説明

外部プロシージャの事前条件

外部プロシージャをコールする前に、実行環境に存在する権限、許可およびシノニムを考慮する必要があります。

外部プロシージャの権限

外部プロシージャがCALL仕様経由でコールされるとき、プロシージャは実行者権限ではなく定義者権限によって実行されます。

実行者権限で実行されるプログラムは、特定のスキーマには限定されません。コール側のサイトで実行され、コールした側の可視性と許可でデータベース項目(表やビューなど)にアクセスします。一方、定義者権限で実行されるプログラムでは、プログラムが定義されているスキーマに限定されます。このプログラムは定義を行う側で、サイトの定義者のスキーマ内の定義サイトで実行され、定義者の可視性および許可でデータベース項目にアクセスします。

許可の管理


注意

次のようなデータ構造を設定しないと機能しない例もあります。

CONNECT system/manager
GRANT CREATE ANY DIRECTORY to scott;
CONNECT scott/tiger
CREATE OR REPLACE DIRECTORY bfile_dir AS '/tmp';
CREATE OR REPLACE JAVA RESOURCE NAMED "appImages" USING BFILE (bfile_dir,'bfile_audio');
 

外部プロシージャをコールするには、ユーザーはコール仕様およびプロシージャによって使用されるリソースに対するEXECUTE権限が必要です。

SQL*Plusでは、GRANTおよびREVOKEデータ制御文を使用して許可を管理できます。次に例を示します。

GRANT EXECUTE ON plsToJ_demoExternal_proc TO Public;
REVOKE EXECUTE ON plsToJ_demoExternal_proc FROM Public;
GRANT EXECUTE ON JAVA RESOURCE "appImages" TO Public;
GRANT EXECUTE ON plsToJ_demoExternal_proc TO Scott;
REVOKE EXECUTE ON plsToJ_demoExternal_proc FROM Scott;

関連項目

『Oracle Database SQLリファレンス』 

外部プロシージャのシノニムの作成

開発者またはDBAは、便宜上、CREATE [PUBLIC] SYNONYM文を使用して外部プロシージャのシノニムを作成できます。次の例では、すべてのユーザーがアクセス可能なパブリック・シノニムをDBAが作成します。PUBLICを指定しない場合、シノニムはプライベートになり、そのスキーマ内以外ではアクセスできません。

CREATE PUBLIC SYNONYM Rfac FOR Scott.RecursiveFactorial;

CALL文の構文

外部プロシージャは、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文の一部にすることはできます。 


Javaクラス・メソッドのコール

以前に発行された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プロシージャのコール方法

外部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    BINARY_INTEGER;
   a    BINARY_INTEGER;
   b    BINARY_INTEGER;
CALL plsCallsCdivisor_func(a, b); 
IF g IN (2,4,8) THEN ... 

複数言語のプログラム・エラーおよび例外処理

AS EXTERNALコール仕様がTYPE仕様部またはPACKAGE仕様部で検出された場合、PL/SQLコンパイラはコンパイル時エラーを呼び出します。

Cプログラムでは、OCIExtproc...関数を使用して例外を呼び出すことができます。

外部Cプロシージャでのサービス・プロシージャの使用

サービス・ルーチンが外部プロシージャからコールされると、サービス・ルーチンは例外を呼び出し、メモリーを割り当て、サーバーへのコールバック用のOCIハンドルを起動します。サービス・ルーチンを使用するには、WITH CONTEXT句を指定する必要があります。WITH CONTEXT句を使用すると、コンテキスト構造体を外部プロシージャに渡すことができます。コンテキスト構造体は、ヘッダー・ファイルociextp.hの中に次のように宣言されています。

typedef struct OCIExtProcContext OCIExtProcContext;


注意

ociextp.hは、UNIXでは$ORACLE_HOME/plsql/publicにあります。 


OCIExtProcAllocCallMemory()

このサービス・ルーチンは、外部プロシージャ・コールの間、nバイトのメモリーを割り当てます。この関数によって割り当てられるメモリーは、PL/SQLに制御が戻った直後に自動的に解放されます。


注意

外部プロシージャでは、このサービス・ルーチンによって割り当てられたメモリーが自動的に処理されるため、C関数のfree()をコールする必要はありません(コールしないでください)。 


この関数のCプロトタイプは、次のとおりです。

dvoid *OCIExtProcAllocCallMemory(
   OCIExtProcContext *with_context, 
   size_t amount);

パラメータwith_contextおよびamountは、それぞれ、コンテキスト・ポインタと割り当てられるバイト数です。この関数は、割り当てられたメモリーを指す型が未定のポインタを戻します。戻り値が0(ゼロ)の場合は、失敗を示します。

SQL*Plusで、外部関数plsToC_concat_funcを次のように発行するとします。


注意

次のようなデータ構造を設定しないと機能しない例もあります。

CONNECT system/manager
DROP USER y CASCADE;
GRANT CONNECT,RESOURCE,CREATE LIBRARY TO y IDENTIFIED BY y;
CONNECT y/y
CREATE LIBRARY stringlib AS '/private/varora/ilmswork/Cexamples/john2.so';
 

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_concatOCIExtProcAllocCallMemory()を使用して結果文字列のメモリーを割り当てます。

#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 *) "samp", (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)); 

  /* ------- subroutine called  here-----------------------*/ 
  printf ("calling concat...¥n");
  concat(ctx, str1, str1_i, str2, str2_i, ret_i, ret_l);

  return 0;
}

#endif

OCIExtProcRaiseExcp

このサービス・ルーチンは、事前定義の例外を呼び出します。この例外には、1〜32767の有効な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 BINARY_INTEGER, 
   divisor  IN BINARY_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_divideOCIExtProcRaiseExcp()を使用して事前定義の例外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 error 1476. */
    if (OCIExtProcRaiseExcp(ctx, (int)1476) == OCIEXTPROC_SUCCESS)
    {
      return;
    }
    else
    {
      /* Incorrect parameters were passed. */
      assert(0);
    }
  }
  *result = (float)dividend / (float)divisor;
}

OCIExtProcRaiseExcpWithMsg

このサービス・ルーチンはユーザー定義例外を呼び出し、ユーザー定義エラー・メッセージを戻します。この関数のCプロトタイプは、次のとおりです。

int OCIExtProcRaiseExcpWithMsg(
   OCIExtProcContext *with_context, 
   size_t  error_number,
   text   *error_message, 
   size_t  len);

パラメータwith_contexterror_numbererror_messageは、それぞれ、コンテキスト・ポインタ、Oracle Databaseエラー番号、エラー・メッセージ・テキストです。パラメータlenは、エラー・メッセージの長さを格納します。メッセージがNULLで終了する文字列の場合、lenは0(ゼロ)です。戻り値のOCIEXTPROC_SUCCESSおよびOCIEXTPROC_ERRORは、正常終了および失敗を示します。

この前の例では、外部プロシージャplsTo_divide_procを発行しました。次の例では、別の実装方法を使用します。 ここでは、除数が0(ゼロ)の場合、C_divideOCIExtProcRaiseExcpWithMsg()を使用してユーザー定義例外を呼び出します。

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 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;

}

外部Cプロシージャを使用したコールバックの実行

次に、コールバックを使用可能にするために使用される関数を示します。

OCIExtProcGetEnv()

このサービス・ルーチンを使用すると、外部プロシージャ・コール中にデータベースへのOCIコールバックが可能になります。この関数によって取得された環境ハンドルは、既存の接続を再利用してデータベースに戻ります。データベースとの接続を新しく確立する必要がある場合は、このハンドルを使用できません。自分でハンドルを作成する必要があります。

この関数のCプロトタイプは、次のとおりです。

sword OCIExtProcGetEnv ( OCIExtProcContext *with_context,
   OCIEnv envh,
   OCISvcCtx svch,
   OCIError errh )

パラメータwith_contextはコンテキスト・ポインタで、パラメータenvhsvcherrhは、それぞれ、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 BINARY_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のみを含めます。

OCIコールバック用のオブジェクト・サポート

外部プロシージャからオブジェクト関連コールバックを実行するために、extprocエージェント内のOCI環境がオブジェクト・モードで完全に初期化されています。この環境へのハンドルは、OCIExtProcGetEnv()プロシージャを使用して取得します。

オブジェクト・ランタイム環境を使用すると、静的サポートのみでなく、OCIによって提供されている動的なオブジェクト・サポートも使用できます。静的サポートを利用するには、OTTを使用して該当するオブジェクト型用のC構造体を生成し、次に、従来のCコードを使用してオブジェクトの属性にアクセスします。

外部プロシージャの作成時に型がわからないオブジェクトについては、かわりの動的なオブジェクト・アクセス方法によって、OCIDescribeAny()が起動されてその型の属性およびメソッド情報が取得されます。その後、OCIObjectGetAttr()およびOCIObjectSetAttr()をコールして、属性値を取得して設定します。

現在の外部プロシージャ・モデルはステートレスのため、コールバックを実行する外部プロシージャごとにOCIExtProcGetEnv()をコールするか、または、OCIExtProc...()サービス・ルーチンを起動する必要があります。外部プロシージャが起動されるたびに、起動後にコールバック・メカニズムがクリーンアップされ、OCIハンドルが解放されます。

コールバックに関する制限事項

コールバックでは、次のSQLコマンドおよびOCIプロシージャはサポートされていません。

また、OCIプロシージャOCIHandleAllocでは、次のハンドル・タイプはサポートされていません。

OCI_HTYPE_SERVER 
OCI_HTYPE_SESSION 
OCI_HTYPE_SVCCTX 
OCI_HTYPE_TRANS

外部プロシージャのデバッグ

外部プロシージャが失敗する場合は、通常、そのプロトタイプに不具合があります。プロトタイプが、PL/SQLによって内部的に生成されたプロトタイプと一致しないということです。これは、互換性のないCデータ型を指定した場合に発生する可能性があります。たとえば、型がREALOUTパラメータを渡すためにfloat *を指定したとします。floatdouble *または他のCデータ型を指定すると、結果が不一致になります。

このような場合は、次のエラーを受け取ることがあります。

lost RPC connection to external routine agent 

このエラーは、外部プロシージャによってコア・ダンプが発生したため、エージェントextprocが異常終了したことを表します。Cプロトタイプ・パラメータの宣言時にエラーを回避するには、前述の表を参照してください。

パッケージDEBUG_EXTPROCの使用

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/TIGERアカウントを使用する必要があり、このアカウントにはCREATE LIBRARY権限が必要です。

外部Cプロシージャのためのガイドライン

グローバル変数の操作

グローバル変数は関数の外で宣言され、その値はプログラムのすべての関数によって共有されます。外部プロシージャの場合、これは、DLL内のすべての関数がグローバル変数の値を共有するということです。グローバル変数の使用は、次の2つの理由のため、お薦めしません。

静的変数の操作

静的変数には外部変数および内部変数の2種類があります。外部静的変数は、特殊なグローバル変数であり、使用をお薦めしません。内部静的変数は、特定の関数に対するローカルな変数ですが、関数がアクティブになるたびに生成、消滅するのではなく、存在したまま残ります。このため、この変数は1つの関数内にプライベートで永続的な記憶域を提供します。このような変数は、同一関数を後で起動するときにデータを渡すために使用します。ただし、DLLには前述のキャッシュ機能があるため、DLLが起動ごとにアンロードおよび再ロードされる可能性があります。これは、内部静的変数がその値を失う可能性があることを表します。

関連項目

動的リンク・ライブラリの作成方法は、RDBMSサブディレクトリ/publicにあるテンプレートmakefileを参照してください。 

コール仕様およびCALL文のガイドライン

外部プロシージャをコールする場合:

外部Cプロシージャに関する制限事項

外部プロシージャには次の制限が適用されます。


戻る 次へ
Oracle
Copyright © 2006 Oracle Corporation.

All Rights Reserved.
目次
目次
索引
索引