プライマリ・コンテンツに移動
Oracle® Database開発ガイド
12c リリース1 (12.1)
B71295-06
  ドキュメント・ライブラリへ移動
ライブラリ
製品リストへ移動
製品
目次へ移動
目次
索引へ移動
索引

前
 
次
 

18 複数のプログラミング言語を使用したアプリケーションの開発

この章では、他のプログラミング言語で記述された外部プロシージャをコールするデータベース・アプリケーションの開発方法について説明します。

内容は次のとおりです。

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

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

  • 『Oracle Database PL/SQL言語リファレンス』で説明されるPL/SQL

  • 『Oracle Call Interfaceプログラマーズ・ガイド』で説明される、Oracle Call Interface(OCI)を使用したC

  • 『Oracle C++ Call Interfaceプログラマーズ・ガイド』で説明される、Oracle C++ Call Interface (OCCI)を使用したC++

  • 『Pro*C/C++プログラマーズ・ガイド』で説明される、Pro*C/C++プリコンパイラを使用したCまたはC++

  • 『Pro*COBOLプログラマーズ・ガイド』で説明される、Pro*COBOLプリコンパイラを使用したCOBOL

  • 『Oracle Provider for OLE DB開発者ガイドfor Microsoft Windows』で説明される、Oracle Provider for OLE DBを使用したVisual Basic。

  • 『Oracle Data Provider for .NET開発者ガイドfor Microsoft Windows』で説明される、Oracle Data Provider for .NETを使用した.NET

  • JDBCおよびSQLJクライアント側アプリケーション・プログラミング・インタフェース(API)を使用したJava。『Oracle Database JDBC開発者ガイド』および『Oracle Database SQLJ開発者ガイド』を参照してください。

  • 『Oracle Database Java開発者ガイド』で説明される、データベース内のJava。これには、『Oracle Database Java開発者ガイド』で説明される、Javaストアド・プロシージャ(SQLに発行されデータベースに格納されるJavaメソッド)の使用も含まれます。

    また、JPublisherユーティリティでは、SQLオブジェクトやPL/SQLパッケージなどのデータベース・エンティティを表すJavaクラスの生成、Javaクライアント・プログラムでのSQL、PL/SQL、およびサーバー側JavaからWebサービスへの発行、およびデータベース内からの外部Webサービスの起動などが可能です。『Oracle Database JPublisherユーザーズ・ガイド』を参照してください。

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

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

  • PL/SQLは、SQLトランザクション処理に特化した強力な開発ツールです。

  • 演算集中型のタスクは、Cなどの下位レベル言語で最も効率的に実行されます。

  • 移植性とセキュリティの両方を求める場合は、Javaを選択できます。

  • Microsoftプログラミング言語の知識がある場合は、.NETを選択することを推奨します。

パフォーマンスに関して最も重要なことは、サーバーのアドレス空間内で実行されるのはPL/SQLおよびJavaメソッドのみであるということです。C/C++および.NETメソッドは外部プロシージャとしてディスパッチされ、サーバー・システム上で実行されますが、これはデータベース・サーバーのアドレス空間外です。Pro*COBOLおよびPro*C/C++はプリコンパイラであり、Visual BasicはCで実装されているOracle Provider for OLE DBおよび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つの言語に制限する必要はありません。外部プロシージャの使用によって、特定の目的に特定の言語をデプロイできるため、再利用性およびモジュール性が向上します。

18.2 外部プロシージャの概要

外部プロシージャは、動的リンク・ライブラリ(DLL)に格納されているプロシージャです。このプロシージャを基本言語に登録し、これをコールして特定の処理を実行できます。

たとえば、PL/SQLを使用する場合、PL/SQLが実行時にライブラリを動的にロードし、次に、プロシージャをPL/SQLプロシージャとみなしてコールします。このようなプロシージャは、現行のトランザクションに完全に組み込まれ、データベースをコールバックしてSQL操作を実行できます。

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

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

  • クライアント・アプリケーションの実行とデータベース・インスタンスからのプロセスを分離し、クライアント側で生じた問題がデータベースに影響を与えないようにすることができます。

  • 演算集中型プログラムをクライアントから実行速度の速いサーバーへ移動できます(ネットワーク通信でのラウンド・トリップを回避するため)。

  • データベース・サーバーと外部システムおよびデータソースとのインタフェースが実現します。

  • データベース・サーバー自体の機能を拡張できます。


注意:

外部ライブラリ(DLLファイル)は静的なリンクである必要があります。他の外部ライブラリ(DLLファイル)からの外部記号を参照しないようにしてください。Oracle Databaseはこういった記号を解決するため、外部プロシージャの原因による失敗がありません。


参照:

外部プロシージャ保護の詳細は、『Oracle Databaseセキュリティ・ガイド』を参照してください

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

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


注意:

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

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

  • 適切なCまたはJavaのターゲット・プロシージャのディスパッチ

  • データ型の変換

  • パラメータ・モードのマッピング

  • 自動的なメモリー割当ておよびクリーンアップ

  • SQLからコールされるパッケージ・ファンクションに必要に応じて指定される純粋度制約

  • データベース・トリガーからのJavaメソッドまたはCプロシージャのコール

  • 位置の柔軟性: パフォーマンスを最適化し実装の詳細を隠すために、AS LANGUAGEコール仕様を、パッケージ仕様または型仕様、あるいはパッケージ(または型)本体に入れることができます。

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

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

外部CプロシージャまたはJavaメソッドをPL/SQLで使用できるようにするには、プロシージャまたはメソッドをロードする必要があります。


注意:

外部Cプロシージャは、DLLをサポートするプラットフォームまたはSolarisの.soライブラリなどの動的にロード可能な共有ライブラリをサポートするプラットフォームのみで使用できます。

アプリケーションが外部Cプロシージャをコールすると、Oracle DatabaseまたはOracle Listenerが外部プロシージャ・エージェントextprocを起動します。アプリケーションは、Oracle DatabaseまたはOracle Listenerが確立したネットワーク接続を使用し、次の情報をextprocに渡します。

  • DLLまたは共有ライブラリの名前

  • 外部プロシージャの名前

  • 外部プロシージャのすべてのパラメータ

その後、extprocはDLLまたは共有ライブラリをロードし、外部プロシージャを実行し、外部プロシージャによってアプリケーションに戻された値を渡します。アプリケーションとextprocは、同じコンピュータ上に存在する必要があります。

extprocは、使用されているコール規格に準拠するライブラリ内でプロシージャをコールできます。コール規格の詳細は、18.5.2.4項「CALLING STANDARD」を参照してください。


注意:

外部プロシージャのデフォルト構成では、Oracle Databaseおよびextprocと動作するネットワーク・リスナーは不要になりました。Oracle Databaseによってextprocが直接起動されるため、Oracle Listenerが誤ってextprocを起動する危険性が解消されました。セキュリティを最大限にするため、このデフォルト構成をお薦めします。

次のいずれかを使用する場合、Oracle Listenerがextprocを起動するようにデフォルトの構成を変更する必要があります。

  • マルチスレッドのextprocエージェント

  • Windows上の共有モードのOracle Database

  • 外部プロシージャを別のextprocエージェントにリダイレクトする、LIBRARY仕様のAGENT句またはPROCEDURE仕様のAGENT IN

デフォルト構成を変更すると、追加のネットワーク構成が必要になります。


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

  1. Cプロシージャの定義

  2. 環境の設定

  3. DLLを識別する

  4. 外部プロシージャを公開する

18.4.1 Cプロシージャの定義

次のプロトタイプのいずれかを使用し、Cプロシージャを定義します。

  • Kernighan & Ritchieスタイル・プロトタイプの定義は次のとおりです。

    void C_findRoot(x)
     float x;
    ...
    
  • 全角ではない数値データ型(floatshortcharなど)以外のISO/ANSIプロトタイプの定義は次のとおりです。

    void C_findRoot(double x)
    ...
    
  • デフォルトの引数格上げによってサイズの変わらないその他のデータ型。

    デフォルトの引数格上げによってサイズが変わる定義の例は次のとおりです。

    void C_findRoot(float x)
    ...
    

18.4.2 環境の設定

外部プロシージャでデフォルト構成を使用する場合、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プロセスの生成時に資格証明の使用を適用する、ENFORCE_CREDENTIAL環境変数を設定します。ENFORCE_CREDENTIAL値はTRUEまたはFALSE (デフォルト)のいずれかです。ENFORCE_CREDENTIAL、およびextprocプロセスで予期される動作で、考えられる認証と偽装の使用例に基づく議論については、『Oracle Databaseセキュリティ・ガイド』の外部プロシージャ保護に関する情報を参照してください。

外部プロシージャのデフォルト構成を変更し、extprocエージェントをOracle Listenerから起動するには、Cで作成された外部プロシージャまたはCアプリケーションからコール可能な外部プロシージャを使用できるよう、データベース構成を次のように設定します。


注意:

extprocで資格証明を使用する場合、Oracle Listenerはextprocエージェントの生成に使用できません。

  1. エージェント用の構成パラメータ(デフォルト名extproc)を構成ファイルtnsnames.oraおよびlistener.ora内で設定します。この作業により、データベース起動時に外部プロシージャ・エージェントextprocとの接続が確立されます。

  2. 外部プロシージャ専用のリスナー・プロセスを開始します。

    リスナーは、extprocに必要な環境変数(ORACLE_HOMEORACLE_SIDおよびLD_LIBRARY_PATHなど)をいくつか設定します。また、リスナーがlistener.oraエントリのENVSセクションで固有の環境変数を定義すると、その環境変数はエージェント・プロセスに渡されます。それ以外は、エージェントに対してクリーンな環境を提供します。エージェント用に設定される環境変数は、クライアントおよびサーバー用に設定される環境変数から独立しています。したがって、エージェント・プロセス内で実行される外部プロシージャは、クライアントまたはサーバーのプロセス用に設定される環境変数を読み込むことはできません。


    注意:

    環境変数自体は標準Cプロシージャのsetenvおよびgetenvを使用してそれぞれ設定し、読み込むことができます。この方法で設定された環境変数は、エージェント・プロセス専用です。そのプロセス内で実行されるすべての関数で読み込めますが、同一ホスト上で実行されている他のプロセスで読み込むことはできません。

  3. 外部プロシージャ用のエージェントを専用モード(デフォルト)で実行するか、マルチスレッド・モードで実行するかを決定します。

    専用モードでは、各セッションに対して専用エージェントが1つ起動されます。マルチスレッド・スレッドモードでは、単一のマルチスレッドextprocエージェントが起動されます。マルチスレッドextprocエージェントは、ユーザーごとに異なるスレッドを使用してコールを処理します。外部プロシージャをコールするユーザーが多いような構成では、マルチスレッドのextprocエージェントを使用してシステム・リソースを節約することをお薦めします。

    エージェントを専用モードで実行する場合、エージェント・プロセスの構成はこれ以上必要ありません。

    エージェントをマルチスレッド・モードで実行する場合、データベース管理者はエージェントが(マルチスレッドextprocエージェントとして)マルチスレッド・モードで起動されるようにデータベース・システムを構成する必要があります。この構成は、エージェント制御ユーティリティagtctlを使用して行います。たとえば、次のコマンドを使用してextprocを起動します。

    agtctl startup extproc agent_sid 
    

    この例のagent_sidは、このextprocエージェントが処理するシステム識別子です。このシステム識別子のエントリは、通常、tnsnames.oraファイルにエントリとして追加されています。extproc管理でのagtctlの使用の詳細は、『Oracle Call Interfaceプログラマーズ・ガイド』のマルチスレッドextprocエージェントの管理に関する項を参照してください。


注意:

  • マルチスレッドextprocエージェントを使用する場合、破損コール・スタックなどのエラーを避けるために、コールするライブラリはスレッド・セーフである必要があります。

  • エージェント・プロセスを起動する、データベース・サーバー、エージェント・プロセスおよびリスナー・プロセスは、すべて同じホスト上に存在する必要があります。

  • デフォルトでは、エージェント・プロセスはメイン・アプリケーションと同じデータベース・インスタンスで実行されます。信頼性が非常に重要な場合は、外部プロシージャ用のエージェント・プロセスを別のデータベース・インスタンス(同じホスト上)で実行すると、エージェントで問題が生じてもプライマリ・データベース・サーバーに影響を与えません。このようにするには、データベース・リンクを使用して別のデータベース・インスタンスを指定します。


『Oracle Call Interfaceプログラマーズ・ガイド』の図F-1は、マルチスレッドextprocエージェントのアーキテクチャを示しています。

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

注意:

ANY権限は強力なので、軽率に付与しないでください。詳細は次を参照してください。
  • ANYを含む、システム権限の管理の詳細は、『Oracle Databaseセキュリティ・ガイド』を参照してください。

  • ユーザー・アカウントと権限のセキュリティのガイドラインは、『Oracle Databaseセキュリティ・ガイド』を参照してください。


DLL名だけではなく、ディレクトリ・オブジェクトを使用してDLLのパスを指定することをお薦めします。次の例では、DLLであるutils.soを表す別名ライブラリc_utilsを作成します。

CREATE LIBRARY C_utils AS 'utils.so' IN DLL_DIRECTORY;

DLL_DIRECTORYは、'/DLLs'を参照するディレクトリ・オブジェクトです。

または、次の例のようにDLLのフル・パスを指定することもできます。

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.oraENVSパラメータの両方が使用される場合、extproc.oraで定義された環境変数が優先されます。EXTPROC機能の詳細は、Oracle Netのマニュアルを参照してください。


注意:

Windowsシステム上のextproc.oraでは、パスの各円記号に対してドライブ文字および二重円記号(\\)を使用して、パスを指定します。(各二重円記号内の最初の円記号は、エスケープ文字として機能します。)

18.4.4 外部プロシージャの公開

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

18.5 外部プロシージャの公開

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

内容は次のとおりです。

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

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

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

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

これらの副次句のうちLIBRARYのみが必須です。

18.5.2.1 LIBRARY

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

18.5.2.2 NAME

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


注意:

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

18.5.2.3 LANGUAGE

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

18.5.2.4 CALLING STANDARD

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

18.5.2.5 WITH CONTEXT

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

18.5.2.6 PARAMETERS

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

18.5.2.7 AGENT IN

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

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

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

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

Javaクラスおよびそのメソッドは、RDBMSライブラリ・ユニットに格納されます。この中には、LOADJAVAユーティリティまたはSQL文CREATEJAVAを使用して、Javaソース、バイナリおよびリソースをロードできます。ライブラリ・ユニットは、たとえばCで作成されたDLLに類似していますが、DLLには複数のプロシージャを入れられるのに対して、ライブラリ・ユニットはJavaクラスと1対1で対応します。

NAME句文字列は、Javaメソッドを一意に識別します。PL/SQLのファンクションまたはプロシージャおよびJavaには対応するパラメータが必要です。Javaメソッドがパラメータをとらない場合は、空のパラメータ・リストを作成する必要があります。

JavaクラスをRDBMSにロードするとき、クラスはSQLに対して自動的には発行されません。選択されたパブリック静的メソッドしかSQLに明示的に発行できないためです。ただし、適切な権限がある場合には、データベースに存在する他のJavaクラスからすべてのメソッドを起動することができます。

次に、引数の階乗を戻す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';

18.7 外部Cプロシージャの発行

次の例では、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. */

18.8 コール仕様の位置

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

  • スタンドアロンのPL/SQLプロシージャ

  • PL/SQLパッケージ仕様部

  • PL/SQLパッケージ本体

  • ADT仕様

  • ADT本体

内容は次のとおりです。


注意:

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


参照:

  • PL/SQLからの外部プロシージャのコールの詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください。

  • SQLのCALL文の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。


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

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;

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

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;

18.8.3 例: ADT仕様内へのコール仕様の配置


注意:

この項の例が機能するには、次のデータ構造を設定する必要があります(これには、権限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)
);

18.8.4 例: ADT本体内へのコール仕様の配置

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;

18.8.5 例: AUTHIDを指定したJava

スタンドアロン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)';

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

スタンドアロン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);

18.8.7 例: パッケージ内でのコール仕様の混合使用

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;

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

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

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

  • 使用可能なPL/SQLのセットが、Cデータ型のセットと1対1で対応していない。

  • Cとは異なり、PL/SQLにはNULLかどうかというRDBMS概念が含まれる。このため、PL/SQLパラメータはNULLにできるが、CパラメータはNULLにできない。

  • 外部プロシージャで、CHARLONG RAWRAWおよびVARCHAR2パラメータの現在の長さまたは最大長が必要。

  • 外部プロシージャで、CHARVARCHAR2およびCLOBパラメータに関するキャラクタ・セット情報が必要。

  • PL/SQLで、外部プロシージャによって戻された値の現在の長さ、最大長またはNULL状態が必要。


注意:

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

内容は次のとおりです。

18.9.1 データ型の指定

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


注意:

PL/SQLデータ型のBINARY_INTEGERPLS_INTEGERは同一です。わかりやすくするため、このマニュアルではBINARY_INTEGERPLS_INTEGERの両方をPLS_INTEGERとします。

表18-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
OCIDateTime
OCIDateTime
INTERVAL DAY TO SECOND
INTERVAL YEAR TO MONTH
OCIInterval
OCIInterval
composite object types: ADTs
dvoid
dvoid
composite object types: collections (associative arrays, varrays, nested tables)
OCICOLL
OCICOLL

脚注1 このPL/SQLデータ型は、コール仕様でAS EXTERNALを使用する場合にのみ準拠します。

18.9.2 外部データ型のマッピング

外部データ型はそれぞれCデータ型にマップされ、データ型変換が暗黙的に実行されます。外部データ型はそれぞれCデータ型にマップされ、データ型変換が暗黙的に実行されます。Cプロトタイプ・パラメータの宣言時のエラーを回避するには、表18-2を参照してください。この表には、特定の外部データ型およびPL/SQLパラメータ・モードに指定するCデータ型が示されています。たとえば、OUTパラメータの外部データ型がSTRING型の場合は、Cプロトタイプにはデータ型char *を指定します。

表18-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またはコレクション型にマップすることはできません。

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

18.9.4 仮パラメータの宣言

一般に、外部プロシージャを発行する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を指定しないときは、デフォルトの外部型が使用されます。

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

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

18.9.6 プロパティの指定

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

INDICATOR [{STRUCT | TDO}]
LENGTH
DURATION
MAXLEN
CHARSETID
CHARSETFORM
SELF

表18-3に、使用可能外部データ型とデフォルト外部データ型、PL/SQLデータ型および特定のプロパティに使用できるPL/SQLパラメータ・モードを示します。(CからPL/SQLにデータを戻す場合に指定する)MAXLENは、INパラメータには適用できません。

表18-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関数識別子に対応します。

内容は次のとおりです。

18.9.6.1 INDICATOR

INDICATORは、別のパラメータがNULLかどうかを示す値を持つパラメータです。PL/SQLでは、RDBMSにおけるNULLかどうかという概念が言語に組み込まれているため、インジケータは必要ありません。ただし、外部プロシージャは、パラメータまたはファンクションの結果がNULLであるかどうかを判断する必要がある場合があります。また、外部プロシージャは、戻り値がNULLでありそれに応じた処理が必要であることをサーバーに指示する必要がある場合もあります。

このような場合は、プロパティINDICATORを使用して、インジケータを仮パラメータに関連付けることができます。PL/SQLプロシージャがファンクションの場合、18.9.6項「プロパティの指定」に示すとおり、ファンクション結果にインジケータを関連付けられます。

インジケータの値を調べるには、定数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)を使用する必要があります。

18.9.6.2 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は参照によって渡され、読込み専用です。

18.9.6.3 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_ATTR_CHARSET_IDOCI_ATTR_CHARSET_FORMの詳細は、『Oracle Call Interfaceプログラマーズ・ガイド』を参照してください。

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


18.9.6.4 パラメータの再配置

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

18.9.6.5 SELF

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

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

  1. SQL*Plusでは、Personオブジェクト型は次のように作成できます。

    CREATE OR REPLACE TYPE Person1_typ AS OBJECT (
      Name_     VARCHAR2(30),
      B_date    DATE,
      MEMBER FUNCTION calcAge_func RETURN NUMBER
    );
    /
    
  2. このメンバー・ファンクションの本文を次のように宣言します。

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

    (通常、メンバー・ファンクションはPL/SQLで実装しますが、この例では外部プロシージャとなっています。)

    calcAge_funcメンバー関数は引数をとらず、数値を戻します。メンバー関数は、常に、対応付けられているオブジェクト型のインスタンスに対してコールされます。オブジェクト・インスタンス自体は、常にメンバー関数の暗黙的な引数になります。暗黙的な引数を参照するには、SELFキーワードを使用します。これは、PARAMETERS句の中でSELFへの参照をサポートすることによって、外部プロシージャ構文内に組み込まれています。

  3. 一致する表を作成し、移入します。

    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'));
    
  4. この表から対象の情報を取得します。

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

18.9.6.6 BY REFERENCE

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

18.9.6.7 WITH CONTEXT

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句を省略すると、コンテキスト・ポインタは外部プロシージャに渡される最初のパラメータです。

18.9.6.8 言語間のパラメータ・モードのマッピング

PL/SQLは、ININ OUTおよびOUTパラメータ・モード、および値を戻すプロシージャのRETURN句をサポートします。

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

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

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

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

  • 無名ブロック

  • スタンドアロンおよびパッケージ・プロシージャ

  • オブジェクト型のメソッド

  • データベース・トリガー

  • SQL文(パッケージ・ファンクションのみへのコール)

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

図18-1 Oracle Databaseと外部プロシージャ

図18-1の説明は次にあります。
「図18-1 Oracle Databaseと外部プロシージャ」の説明

内容は次のとおりです。

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

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

内容は次のとおりです。

18.10.1.1 外部プロシージャの権限

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

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

18.10.1.2 許可の管理

外部プロシージャをコールするには、ユーザーはコール仕様に対するEXECUTE権限が必要です。この権限を付与するには、SQL文GRANTを使用します(『Oracle Database SQL言語リファレンス』を参照)。たとえば、この文により、ユーザーjohndoeはコール仕様がplsToJ_demoExternal_procである外部プロシージャをコールできます。

GRANT EXECUTE ON plsToJ_demoExternal_proc TO johndoe;

プロシージャをコールする必要があるユーザーにのみ、コール仕様に対するEXECUTE権限を付与します。

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

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

CREATE PUBLIC SYNONYM Rfac FOR johndoe.RecursiveFactorial;

18.10.2 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 文の一部に使用できます。

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

18.6項「Javaクラス・メソッドのパブリッシュ」で発行するJ_calcFactorialクラス・メソッドのコール手順:

  1. 次のように、SQL*Plusのホスト変数を2つ宣言して初期化します。

    VARIABLE x NUMBER
    VARIABLE y NUMBER
    EXECUTE :x := 5;
    
  2. J_calcFactorialをコールします。

    CALL J_calcFactorial(:x) INTO :y;
    PRINT y
    

結果:

Y
------
   120

18.10.4 外部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回で済みますが、演算によるメリットがコールのコストを上回るときにのみ外部プロシージャをコールするようにします。

ここでは、18.7項「外部Cプロシージャの発行」で発行した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 ... 

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

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

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

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

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

typedef struct OCIExtProcContext OCIExtProcContext;

注意:

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

サービス・プロシージャ:

18.12.1 OCIExtProcAllocCallMemory

OCIExtProcAllocCallMemoryサービス・ルーチンは、外部プロシージャ・コールの間、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_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 *) "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

18.12.2 OCIExtProcRaiseExcp

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

18.12.3 OCIExtProcRaiseExcpWithMsg

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

}

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

コールバックを有効にするには、OCIExtProcGetEnv関数を使用します。

内容は次のとおりです。

18.13.1 OCIExtProcGetEnv

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操作を実行できます。実際の例については、18.13.5項を参照してください。


注意:

コールバックは、必ずしも同一セッションで発生するとはかぎりません。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を含めます。

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

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

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

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

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

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

コールバックでは、次の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

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

外部プロシージャが失敗する場合は、通常、そのプロトタイプに不具合があります。プロトタイプが、PL/SQLによって内部的に生成されたプロトタイプと一致しないということです。これは、互換性のないCデータ型を指定した場合に発生する可能性があります。たとえば、型がREALOUTパラメータを渡すためにfloat *を指定したとします。floatdouble *または他の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は、実行中のプロセスにアタッチできるデバッガ付きのプラットフォームでのみ動作します。

18.13.5 例: 外部Cプロシージャのコール

PL/SQLデモ・ディレクトリには、外部プロシージャのコール方法を示すスクリプトextproc.sqlもあります。付属ファイルextproc.cには、外部プロシージャ用のCソース・コードが含まれています。

デモを実行するには、extproc.sqlにある指示に従ってください。SCOTTアカウントを使用する必要があり、このアカウントにはCREATE LIBRARY権限が必要です。

18.13.6 外部Cプロシージャのグローバル変数

グローバル変数は関数の外で宣言され、その値はプログラムのすべての関数によって共有されます。したがって、外部プロシージャの場合、DLL内のすべての関数がグローバル変数の値を共有します。グローバル変数は関数の存続期間を超えて存続するデータの格納にも使用されます。しかし、グローバル変数の使用は、次の2つの理由のため、お薦めしません。

  • スレッド

    エージェント・プロセスは非スレッド構成になっているため、一度にアクティブになる関数は1つのみです。マルチスレッドのextprocエージェントの場合は、一度に複数の関数をアクティブにできます。その場合は、それらが同時にグローバル変数にアクセスしようとして、正常な結果が得られなくなる可能性があります。

  • DLLのキャッシング

    グローバル変数にデータを格納することで、関数func1が関数func2にデータを渡すとします。func1の完了後DLLキャッシュはアンロードされ、この結果、グローバル変数のすべての値が失われる可能性があります。次に、func2が実行されるときに、DLLは再ロードされ、グローバル変数はすべて0(ゼロ)に初期化されます。

18.13.7 外部Cプロシージャの静的変数

静的変数には外部変数および内部変数の2種類があります。外部静的変数は、特殊なグローバル変数であり、使用をお薦めしません。内部静的変数は、特定の関数に対するローカルな変数ですが、関数がアクティブになるたびに生成、消滅するのではなく、存在したまま残ります。このため、この変数は1つの関数内にプライベートで永続的な記憶域を提供します。このような変数は、同一関数を後でコールするときにデータを渡すために使用します。ただし、18.13.6項「外部Cプロシージャのグローバル変数」で説明するDLLキャッシュ機能によって、すべてのDLLはコール間でアンロードおよびリロードできるため、内部静的変数の値が失われます。


参照:

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

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

  • INパラメータには書込みを行わないでください。OUTパラメータの容量をオーバーフローさせないようにしてください。(PL/SQLでは、このようなエラー条件のランタイム・チェックは実行されません。)

  • OUTパラメータまたは関数結果を読み込まないでください。

  • IN OUTパラメータとOUTパラメータおよび関数結果には、必ず値を代入してください。代入しないと、外部プロシージャが正常に戻りません。

  • WITH CONTEXTおよびPARAMETERS句を含める場合は、パラメータ・リスト内でのコンテキスト・ポインタの位置を示すパラメータCONTEXTを指定する必要があります。

  • PARAMETERS句を含めたときに、外部プロシージャが関数の場合は、パラメータRETURNを最後の位置に指定する必要があります。

  • 各仮パラメータには、PARAMETERS句に対応するパラメータが必要です。また、PARAMETERS句のパラメータのデータ型が、Cプロトタイプのデータ型と互換性があることも確認します。これは、暗黙的変換が実行されないためです。

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

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

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

  • この機能は、DLLをサポートするプラットフォームのみで使用可能です。

  • CプロシージャまたはCからコール可能なプロシージャのみがサポートされます。

  • 分散トランザクションと組み合せた外部プロシージャ・コールアウトはサポートされません。

  • PL/SQLカーソル変数またはレコードを外部プロシージャに渡すことはできません。レコードについては、かわりにオブジェクト型のインスタンスを使用できます。

  • LIBRARY副次句には、リモート・ライブラリを指定するデータベース・リンクは使用できません。

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