この章では、OCIを使用してプログラムを作成する際に必要となる概念および手順について説明します。この章を読み終えると、基本的なOCIアプリケーションの作成に必要なツールについてほとんど習得したことになります。
この章は、次の主要な項で構成されています。
新規ユーザーは、この章の情報に特に注意してください。この章は、以降の章を理解する基本となります。以降の章の内容は、この章の内容を補足しています。
LinuxおよびUNIXオペレーティング・システムでOCIおよびOCCIクライアント・アプリケーションの開発に必要なOCIおよびOCCIヘッダー・ファイルは、$ORACLE_HOME/rdbms/public
ディレクトリにあります。これらのファイルは、Oracle Databaseサーバー環境でも、Oracle Databaseクライアント管理およびカスタム環境でも使用できます。
すべてのデモ・プログラムとそれらの関連ヘッダー・ファイルは、引き続き$ORACLE_HOME/rdbms/demo
ディレクトリにあります。これらのデモ・ファイルは、Examplesメディアからのみインストールできます。これらのプログラムの名前とその目的の詳細は、付録Bを参照してください。
demo
ディレクトリに複数のMakefileが用意されています。各Makefileには、OCI実行可能ファイルを作成する際の指示のコメントが含まれています。コンパイル・エラーとリンク・エラーを回避するために、できるかぎりこれらのデモ用Makefileを使用することをお薦めします。
demo_rdbms.mk
ファイルは、demo
ディレクトリにあるサンプルMakefileです。コメントには、デモ用OCIプログラムのビルド方法が含まれています。demo_rdbms.mk
ファイルには、$ORACLE_HOME/rdbms/public
ディレクトリが含まれています。独自のカスタマイズMakefileで、INCLUDE
パスに$ORACLE_HOME/rdbms/public
ディレクトリがあることを確認してください。
ociucb.mk
ファイルは、demo
にあるコールバック共有ライブラリ作成用Makefileです。
OCIアプリケーションの全般的な目標は、マルチ・ユーザーのために稼働することです。N層構造では、マルチ・ユーザーがクライアント・アプリケーションにHTTP要求を送信します。クライアント・アプリケーションでは、データ交換やデータ処理など、いくつかのデータ操作を実行する必要があります。
OCIでは、次の基本的なプログラム・フローを使用しています。
OCIプログラミング環境およびスレッドの初期化により環境を作成します。
必要なハンドルの割当て、およびサーバー接続とユーザー・セッションの確立。
サーバー上でのSQL文の実行、データベース・サーバーとの間でのデータ交換、必要なアプリケーション・データ処理の実行。
プリペアド文の実行、または新規に実行する文の準備
ユーザー・セッションを終了し、サーバー接続から切断します。
ハンドルおよびデータ構造を解放します。
図2-1は、OCIアプリケーションのステップの流れを示しています。「OCIプログラミング・ステップ」では、各ステップをより詳細に説明しています。
図およびステップ・リストは、OCIプログラミングのステップを簡潔に表したものです。さらに、プログラムの機能性に応じて様々なバリエーションが考えられます。複数セッションおよび複数トランザクションの管理やオブジェクトの使用など、より高度な機能が組み込まれるOCIアプリケーションには、追加のステップが必要です。
すべてのOCI関数コールは、ある環境のコンテキスト内で実行されます。1つのOCIプロセス内に複数の環境が存在することが可能です。環境でプロセスレベルの初期化が必要になった場合には、自動的に実行されます。
注意: OCIアプリケーションには、複数のアクティブな接続および文を含めることができます。 |
ハンドルと記述子は、OCIアプリケーションで定義された不透明なデータ構造体です。ハンドルと記述子は、特定の割当てコールによって直接割り当てるか、あるいはOCI関数を使用して暗黙的に割り当てることができます。
7.xアップグレードの注意: リリース7.x OCIのアプリケーション作成の経験を持つプログラマは、大部分のOCIコールに使用されているこれらのデータ構造に慣れる必要があります。 |
ハンドルおよび記述子を使用して、データ、接続またはアプリケーション動作に関係する情報を格納します。ハンドルについては、次の項で詳しく定義します。記述子の詳細は、「OCI記述子」を参照してください。
ほとんどすべてのOCIコールには、パラメータ・リストに1つ以上のハンドルを含めることができます。ハンドルは、OCIライブラリによって割り当てられる記憶域への不透明なポインタです。ハンドルを使用して、コンテキストまたは接続情報を格納するか(たとえば、環境ハンドルまたはサービス・コンテキスト・ハンドル)、またはOCI関数やデータに関する情報を格納します(たとえば、エラー・ハンドルまたは記述ハンドル)。ハンドルを使用すると、アプリケーションではなくライブラリにこのデータが保持されるため、プログラミングが簡単になります。
ほとんどのOCIアプリケーションでは、ハンドルに格納されている情報へのアクセスが必要となります。この情報にアクセスして設定するには、属性の入手および設定のためのOCIコールであるOCIAttrGet()およびOCIAttrSet()を使用します。
表2-1は、OCI用に定義されているハンドルを示しています。それぞれのハンドル・タイプについて、OCIコールでハンドル・タイプを識別するために使用される、Cデータ型およびハンドル・タイプ定数をリストしています。
表2-1 OCIハンドル・タイプ
説明 | Cデータ型 | ハンドル・タイプ定数 |
---|---|---|
OCI環境ハンドル |
|
|
OCIエラー・ハンドル |
|
|
OCIサービス・コンテキスト・ハンドル |
|
|
OCI文ハンドル |
|
|
OCIバインド・ハンドル |
|
|
OCI定義ハンドル |
|
|
OCI記述ハンドル |
|
|
OCIサーバー・ハンドル |
|
|
OCIユーザー・セッション・ハンドル |
|
|
OCI認証情報ハンドル |
|
|
OCI接続プール・ハンドル |
|
|
OCIセッション・プール・ハンドル |
|
|
OCIトランザクション・ハンドル |
|
|
OCI複合オブジェクト検索(COR)ハンドル |
|
|
OCIスレッド・ハンドル |
|
該当なし |
OCIサブスクリプション・ハンドル |
|
|
OCIダイレクト・パス・コンテキスト・ハンドル |
|
|
OCIダイレクト・パス関数コンテキスト・ハンドル |
|
|
OCIダイレクト・パス列配列ハンドル |
|
|
OCIダイレクト・パス・ストリーム・ハンドル |
|
|
OCIプロセス・ハンドル |
|
|
OCI管理ハンドル |
|
|
OCI HAイベント・ハンドル |
|
該当なし |
アプリケーションでは、すべてのハンドル(バインド・ハンドル、定義ハンドルおよびスレッド・ハンドルは除く)が、特定の環境ハンドルに対して割り当てられます。環境ハンドルをパラメータの1つとして、ハンドル割当てコールに渡します。こうして、割り当てられたハンドルは、その特定の環境に固有のものとなります。
バインド・ハンドルおよび定義ハンドルは、文ハンドルを親として割り当てられ、そのハンドルによって表される文についての情報を含みます。
注意: バインド・ハンドルおよび定義ハンドルは、OCIライブラリによって暗黙的に割り当てられるため、ユーザーによる割当ては不要です。 |
環境ハンドルはOCIEnvCreate()またはOCIEnvNlsCreate()のコールによって割り当てられ、初期化され、このいずれかのコールがすべてのOCIアプリケーションで必要です。
ユーザー割当てハンドルは、すべてOCIハンドル割当てコールOCIHandleAlloc()によって初期化されます。
ハンドルのタイプには、セッション・プール・ハンドル、ダイレクト・パス・コンテキスト・ハンドル、スレッド・ハンドル、CORハンドル、サブスクリプション・ハンドル、記述ハンドル、文ハンドル、サービス・コンテキスト・ハンドル、エラー・ハンドル、サーバー・ハンドル、接続プール・ハンドル、イベント・ハンドルおよび管理ハンドルがあります。
スレッド・ハンドルは、OCIThreadHndInit()コールによって割り当てられます。
アプリケーションは、ハンドルが不要になった時点で全ハンドルを解放する必要があります。すべてのハンドルはOCIHandleFree()関数によって解放されます。
ハンドルにより、グローバル変数を使用する必要性が少なくなります。また、エラー・レポートが簡単になります。エラー・ハンドルは、エラーと診断情報を戻すために使用します。
環境ハンドルは、すべてのOCI関数を起動させるコンテキストを定義します。各環境ハンドルにはメモリー・キャッシュがあり、これによってメモリー・アクセスが高速になります。環境ハンドルに基づくメモリーの割当ては、このキャッシュで行われます。キャッシュへのアクセスは、複数のスレッドが同じ環境ハンドルに基づいてメモリーの割当てを試行する場合にシリアライズされます。複数のスレッドで単一の環境ハンドルを共有する場合、スレッドはキャッシュへのアクセスをブロックすることがあります。
環境ハンドルは、parentパラメータとしてOCIHandleAlloc()コールに渡され、その他のすべてのハンドル・タイプを割り当てます。バインド・ハンドルと定義ハンドルは、暗黙的に割り当てられます。
エラー・ハンドルは、大部分のOCIコールにパラメータとして渡されます。エラー・ハンドルは、OCI操作中に発生したエラーについての情報をメンテナンスします。コールでエラーが発生した場合、エラー・ハンドルをOCIErrorGet()
に渡して、発生したエラーに関する追加情報を取得することができます。
ほとんどのOCIコールではパラメータとしてエラー・ハンドルが必要であるため、エラー・ハンドルの割当ては、OCIアプリケーションの最初のステップの1つです。
サービス・コンテキスト・ハンドルは、サーバーに対するOCIコールの操作コンテキストを決定する属性を定義します。サービス・コンテキスト・ハンドルには、サーバー接続、ユーザー・セッションおよびトランザクションを表す属性として3つのハンドルが含まれています。これらの属性は、図2-2で説明されています。
サーバー・ハンドルは、データベースへの接続を識別します。これは、接続指向のトランスポート・メカニズムによって物理接続に変換されます。
ユーザー・セッション・ハンドルは、ユーザーのロールと権限(ユーザーのセキュリティ・ドメインとも呼ばれる)およびコールを実行するための操作コンテキストを定義します。
トランザクション・ハンドルは、SQL操作を実行するトランザクションを定義します。トランザクション・コンテキストには、フェッチ状態やパッケージのインスタンス化などの、ユーザー・セッション状態の情報が含まれています。
この方法によるサービス・コンテキスト・ハンドルの分析は、拡張性を提供し、プログラマは、洗練された複数層のアプリケーションとトランザクション処理(TP)モニターを作成して、複数のアプリケーション・サーバーと異なるトランザクション・コンテキスト上の複数のユーザーの要求を実行できます。
サービス・コンテキスト・ハンドルを使用する際は、その前にOCIHandleAlloc()、OCILogon()またはOCILogon2()によって、割当てと初期化を行う必要があります。サービス・コンテキスト・ハンドルは、OCIHandleAlloc()
によって明示的に割り当てられます。また、サーバー・ハンドル、セッション・ハンドルおよびトランザクション・ハンドルでは、OCIAttrSet()を使用して初期化できます。サービス・コンテキスト・ハンドルがOCILogon()
によって暗黙的に割り当てられている場合には、すでに初期化されています。
データベース接続ごとに常時シングル・ユーザー・セッションのみをメンテナンスするアプリケーションでは、OCILogon()
をコールして、初期化されたサービス・コンテキスト・ハンドルを取得できます。
より複雑なセッション管理を必要とするアプリケーションでは、サービス・コンテキスト・ハンドルを明示的に割り当てる必要があり、サーバー・ハンドルおよびユーザー・セッション・ハンドルは、サービス・コンテキスト・ハンドルに明示的に設定する必要があります。OCIServerAttach()コールとOCISessionBegin()コールは、サーバー・ハンドルとユーザー・セッション・ハンドルを初期化します。
グローバル・トランザクションの場合、あるいはセッションに対してアクティブなトランザクションが複数存在する場合、アプリケーションではトランザクションを明示的に定義するのみです。また、アプリケーションでは、データベースの内容を変更した際にOCIによって自動的に作成された暗黙的トランザクションを正しく処理できます。
文ハンドルは、図2-3に示すように、SQL文またはPL/SQL文と、それぞれに関連付けられた属性を識別するコンテキストです。
入力および出力バインド変数に関する情報は、バインド・ハンドルに格納されています。OCIライブラリでは、OCIBindByName()またはOCIBindByPos()関数でバインドされた各プレースホルダのバインド・ハンドルが割り当てられます。ユーザーはバインド・ハンドルを割り当てることはできません。バインド・ハンドルは、バインド・コールによって暗黙的に割り当てられます。
問合せ(select文)によって戻されたフェッチ済データは、定義ハンドルの仕様に従って変換され、取得されます。またOCIライブラリによって、OCIDefineByPos()で定義された各出力変数の定義ハンドルが割り当てられます。ユーザーは定義ハンドルを割り当てることはできません。定義ハンドルは、定義コールによって暗黙的に割り当てられます。
バインド・ハンドルおよび定義ハンドルはOCIライブラリによって暗黙的に割り当てられ、バインド操作または定義操作が繰り返されると、透過的に再利用されます。バインド・ハンドルまたは定義ハンドルの実際の値は、第5章で説明する拡張バインド操作または定義操作のためにアプリケーションで必要とされます。ハンドルが解放されるのは、文ハンドルが解放されたとき、または文ハンドルに新しい文が準備されたときです。バインド・ハンドルまたは定義ハンドルを明示的に割り当てると、メモリー・リークが発生する可能性があります。バインド・ハンドルまたは定義ハンドルを明示的に解放すると、プログラムが異常終了する可能性があります。
記述ハンドルは、OCI記述コールであるOCIDescribeAny()によって使用されます。このコールは、ファンクションやプロシージャなど、データベース内のスキーマ・オブジェクトに関する情報を取得します。このコールでは、記述されているオブジェクトに関する情報とともに、パラメータの1つとして記述ハンドルが使用されます。コールが完了すると、記述ハンドルには、そのオブジェクトに関する情報が含まれています。OCIアプリケーションでは、パラメータ記述子の属性を通して記述情報を取得します。
複合オブジェクト検索(COR)ハンドルは、Oracle Database内のオブジェクトを操作する一部のOCIアプリケーションによって使用されます。このハンドルにはCOR記述子が含まれており、これは別のオブジェクトによって参照されるオブジェクトを取得する手順を提供します。
サブスクリプション・ハンドルは、登録およびサブスクライブを行うOCIクライアント・アプリケーションによって使用され、データベース・イベントまたはAQネームスペース内のイベントの通知を受け取るために使用されます。サブスクリプション・ハンドルは、クライアントからの登録に関するすべての情報をカプセル化します。
ダイレクト・パス・ハンドルは、Oracle Database内でダイレクト・パス・ロード・エンジンを利用するOCIアプリケーションに必要です。ダイレクト・パス・ロード・インタフェースを使用して、アプリケーションからOracle Databaseのダイレクト・ブロック・フォーマッタにアクセスできます。図2-4では、各種ダイレクト・パス・ハンドルを示しています。
すべてのOCIハンドルには、そのハンドルに格納されているデータを表す属性があります。ハンドル属性は、属性取得コールOCIAttrGet()を使用して読み取ることができ、属性設定コールOCIAttrSet()を使用して変更することもできます。
たとえば、例2-1の文では、OCI_ATTR_USERNAME
属性に書き込むことで、セッション・ハンドルにユーザー名を設定しています。
例2-1 セッション・ハンドルでユーザー名を設定するためのOCI_ATTR_USERNAME属性の使用
text username[] = "hr"; err = OCIAttrSet ((void *) mysessp, OCI_HTYPE_SESSION, (void *)username, (ub4) strlen((char *)username), OCI_ATTR_USERNAME, (OCIError *) myerrhp);
コールの前に特定のハンドル属性を設定することが必要なOCI関数もあります。たとえば、ユーザーのログイン・セッションを確立するためにOCISessionBegin()をコールする場合は、コールの実行前にユーザー名とパスワードをユーザー・セッション・ハンドルに設定する必要があります。
完了後、ハンドル属性に有用なデータを戻すOCI関数もあります。たとえば、OCIStmtExecute()をコールしてSQL問合せを実行した場合は、例2-2で示すように、選択リスト項目に関する記述情報が文ハンドルに戻されます。
例2-2 選択リスト項目に関する記述情報の文ハンドルへの戻り
ub4 parmcnt; /* get the number of columns in the select list */ err = OCIAttrGet ((void *)stmhp, (ub4)OCI_HTYPE_STMT, (void *) &parmcnt, (ub4 *) 0, (ub4)OCI_ATTR_PARAM_COUNT, errhp);
OCI記述子およびロケータは、データ固有の情報を保持する不透明なデータ構造です。表2-2では、これらとそのCデータ型、およびその型の記述子をOCIDescriptorAlloc()のコール内で割り当てるOCI型定数を示しています。OCIDescriptorFree()関数は、記述子とロケータを解放します。「OCIArrayDescriptorAlloc()」関数および「OCIArrayDescriptorFree()」関数も参照してください。
表2-2 記述子タイプ
説明 | Cデータ型 | OCI型定数 |
---|---|---|
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
なし |
||
なし |
||
なし |
注意: OCILobLocator に対するC型は1つのみですが、このロケータは内部LOBおよび外部LOBに対しては異なるOCI型定数で割り当てられます。「LOBおよびBFILEロケータ」ではこの違いについて説明します。 |
次のリストでは、各記述子型の主な目的について説明しています。その後の各項では、各記述子型をより詳細に説明しています。
OCISnapshot
: 文の実行時に使用
OCILobLocator
: LOBコール(OCI_DTYPE_LOB
)またはBFILE
コール(OCI_DTYPE_FILE
)で使用
OCIParam
: 記述コールで使用
OCIRowid
: ROWID
値のバインドまたは定義で使用
OCIDateTime
およびOCIInterval
: 日時および時間隔データ型で使用
OCIComplexObjectComp
: 複合オブジェクト検索で使用
OCIAQEnqOptions
、OCIAQDeqOptions
、OCIAQMsgProperties
、OCIAQAgent
: アドバンスト・キューイングで使用
OCIAQNotify
: パブリッシュ・サブスクライブ通知で使用
OCIServerDNs
: LDAPベースのパブリッシュ・サブスクライブ通知で使用
スナップショット記述子は、実行コールOCIStmtExecute()のオプション・パラメータです。この記述子は、データベース・スナップショットに対する問合せが実行されることを示します。データベース・スナップショットは、ある特定の時点におけるデータベースの状態を表します。
スナップショット記述子は、OCIDescriptorAlloc()のコールで、OCI_DTYPE_SNAP
をtype
パラメータとして渡すことによって割り当てられます。
ラージ・オブジェクト(LOB)は、バイナリ・ラージ・オブジェクト(BLOB
)またはキャラクタ・ラージ・オブジェクト(CLOB
)データを保持できるOracleデータ型です。データベースでは、LOBロケータと呼ばれる不透明なデータ構造が、データベース行のLOB列、またはオブジェクトのLOB属性のかわりに格納されます。このロケータは、別の位置に格納されている実際のLOB値を指すポインタとして機能します。
注意: アプリケーションによって、LOBロケータを使用する場合と使用しない場合があります。LOBのデータ・インタフェースを使用できますが、このインタフェースはLOBロケータを必要としません。このインタフェースでは、CLOB 列の文字データ、またはBLOB 列のRAW データをバインドまたは定義できます。 |
OCI LOBロケータは、LOB (BLOB
またはCLOB
)またはFILE (BFILE
)に対するOCI操作の実行に使用されます。OCILobXXX
関数では、LOB値のかわりにLOBロケータがパラメータとして使用されます。OCI LOB関数では、パラメータとして実際のLOBデータは使用されません。この関数では、パラメータとしてLOBロケータを使用し、LOBロケータによって参照されるLOBデータを操作します。
LOBロケータは、OCIDescriptorAlloc()のコールで、BLOB
またはCLOB
のtype
パラメータとしてのOCI_DTYPE_LOB
、およびBFILE
のOCI_DTYPE_FILE
を渡すことによって割り当てられます。
注意: LOBロケータの2つのタイプは代替可能ではありません。BLOB かCLOB をバインドまたは定義するとき、アプリケーションでは、OCI_DTYPE_LOB を使用してロケータが適切に割り当てられるようにする必要があります。同様に、BFILE をバインドまたは定義するとき、アプリケーションでは、OCI_DTYPE_FILE を使用してロケータが割り当てられるようにする必要があります。 |
OCIアプリケーションでは、選択リストの要素としてLOB列または属性を含むSQL文を発行することによって、Oracle DatabaseからLOBロケータを取り出せます。その場合、アプリケーションは最初にLOBロケータを割り当て、次にそれを使用して出力変数を定義します。同様に、LOBロケータは、SQL文でLOBとプレースホルダとの間の関連付けを行うバインド操作の一部として使用できます。
OCIアプリケーションはパラメータ記述子を使用して、選択リスト列またはスキーマ・オブジェクトに関する情報を取得します。この情報は、記述操作を通して取得されます。
このパラメータ記述子は、OCIDescriptorAlloc()を使用して割り当てられることのない記述子タイプです。パラメータ記述子は、OCIParamGet()コールを使用してパラメータの位置を指定することによって、記述ハンドル、文ハンドル、または複合オブジェクト検索ハンドルの属性としてのみ取得できます。
ROWID
記述子(OCIRowid
)は、Oracle ROWIDを取り出して使用する必要があるアプリケーションで使用されます。ROWID
を操作するために、アプリケーションではSQL選択リスト内のROWIDの位置に対応するROWID
記述子を定義し、その記述子内のROWID
を検索できます。この同じ記述子を、後でINSERT
文またはWHERE
句の入力変数にバインドできます。
また、文の実行操作後に文ハンドルでOCIAttrGet()を使用し、ROWID
を記述子にリダイレクトできます。
日付、日時および時間隔の記述子は、日付、日時または時間隔データ型(OCIDate
、OCIDateTime
およびOCIInterval
)を使用するアプリケーションにより使用されます。これらの記述子は、バインドおよび定義で使用され、メモリーの割当ておよび解放を行う関数OCIDescriptorAlloc()およびOCIDescriptorFree()にパラメータとして渡されます。
OCIDescriptorAlloc()コールには、そのパラメータ・リストにxtramem_sz
パラメータが含まれています。このパラメータは、記述子またはロケータとともに割り当てるユーザー・メモリー量を指定するために使用します。
通常、アプリケーションは、このパラメータを使用して、記述子またはロケータと同じ存続期間を持つアプリケーション定義の構造体を割り当てます。この構造体は、アプリケーション・ブックキーピングまたはコンテキスト情報の格納に使用できます。
xtramem_sz
パラメータを使用すると、アプリケーションでは、各記述子またはロケータを割り当てたり、割当て解除するたびに、メモリーを明示的に割り当てたり、割当て解除する必要がありません。メモリーは記述子またはロケータとともに割り当てられるため、記述子またはロケータを解放すると(OCIDescriptorFree()により)、ユーザーのデータ構造も解放されます。
OCIHandleAlloc()コールには、ハンドルと同じ存続期間を持つユーザー・メモリーを割り当てるための同様のパラメータがあります。
OCIEnvCreate()コール(および非推奨のOCIEnvInit()コール)には、環境ハンドルと同じ存続期間を持つユーザー・メモリーを割り当てるための同様のパラメータがあります。
次の各項では、OCIアプリケーション開発の各ステップを詳細に説明します。いくつかのステップは、オプションです。たとえば、文が問合せでない場合には、選択リスト項目を記述または定義する必要はありません。
関連項目:
|
次の各項では、OCIのアプリケーションで必要なステップについて説明します。
また、OCI関数のいずれか、またはすべてのステップ間で、アプリケーション固有の処理も発生します。
この項では、OCI環境を初期化してサーバーとの接続を確立し、ユーザーがデータベースに対してアクションを実行できるように許可する方法を説明します。
次の3つの項では、OCI環境を初期化するための3つの主要なステップについて説明します。
各OCI関数コールは、OCIEnvCreate()コールで作成された環境のコンテキスト内で実行されます。このコールは、他のOCIコールの実行よりも先に行う必要があります。唯一の例外は、OCI共有モードのプロセスレベルの属性を設定する場合です。
OCIEnvCreate()のmode
パラメータは、OCIライブラリ関数をコールするアプリケーションが次のことを行えるどうか指定します。
スレッド化環境での実行(mode
= OCI_THREADED
)。
サブスクリプションの使用(mode
= OCI_EVENTS
)。
これらのモードは、環境ごとに設定できます。
アプリケーションがオブジェクトのバインドおよび定義を行う場合、またはOCIのオブジェクト・ナビゲーション・コールを使用する場合は、オブジェクト・モードでの初期化が必要です。また、アプリケーションでこれらの機能をすべて使用しないようにしたり(mode
= OCI_DEFAULT
)、縦線で区切って組み合せて使用することもできます。たとえば、mode
= (OCI_THREADED
| OCI_OBJECT
)を指定すると、アプリケーションはスレッド化環境で動作し、オブジェクトを使用します。
OCI環境ごとにユーザー定義のメモリー管理関数を指定できます。
Oracle Databaseは、ハンドルと記述子の割当ておよび割当て解除をするためのOCI関数を提供します。ハンドルは、OCIHandleAlloc()を使用して割り当てた後、OCIコールに渡す必要があります(OCIBindByPos()のように、OCIコールがユーザーにかわってハンドルを割り当てる場合は除きます)。
OCIHandleAlloc()
を使用して、表2-1にリストされた種類のハンドルを割り当てることができます。 アプリケーションの機能に応じて、これらのハンドルのいくつかまたはすべてを割り当ててください。
アプリケーションでは、OCIEnvNlsCreate()をコールしてOCI環境ハンドルを初期化する必要があります。既存のアプリケーションで、OCIEnvCreate()が使用されていた可能性があります。
アプリケーションで、このステップに従ってOracle Database接続を確立し、ユーザー・セッションを開始する場合、いくつかのオプションがあります。
これらの方法には次のものが含まれます。
注意: OCIInitialize() およびOCIEnvInit() コールのかわりに、OCIEnvCreate()またはOCIEnvNlsCreate()を使用する必要があります。OCIInitialize() およびOCIEnvInit() コールは、下位互換性を保つためにサポートされています。 |
シングル・ユーザー、単一接続オプションは、単純化されたログイン方法で、アプリケーションで常にデータベース接続ごとにシングル・ユーザー・セッションのみを維持する場合に使用できます。
アプリケーションからOCILogon2()またはOCILogon()をコールすると、OCIライブラリは、渡されたサービス・コンテキスト・ハンドルを初期化し、要求を行ったユーザーが指定したOracle Databaseへの接続を作成します。
例2-3では、シングル・ユーザー・セッション(ユーザー名hr
、パスワードhr
およびデータベースoracledb
)を開始するためのOCILogon2()コールを示しています。
例2-3 シングル・ユーザー・セッションのためのOCILogon2コールの使用
OCILogon2(envhp, errhp, &svchp, (text *)"hr", (ub4)strlen("hr"), (text *)"hr", (ub4)strlen("hr"), (text *)"oracledb", (ub4)strlen("oracledb"), OCI_DEFAULT);
このコールのパラメータには、接続を確立するために使用するサービス・コンテキスト・ハンドル(初期化済)、ユーザー名、ユーザーのパスワードおよびデータベース名を含めることができます。このコールでは、最後のパラメータmode
をOCI_DEFAULT
に設定することで、旧OCILogon()
コールと同じ結果が得られます。新しいアプリケーションでは、OCILogon2()を使用します。この関数によって、サーバー・ハンドルおよびユーザー・セッション・ハンドルが暗黙的に割り当てられます。
アプリケーションがこのログイン方法を使用する場合、サービス・コンテキスト、サーバーおよびユーザー・セッション・ハンドルはすべて読取り専用となります。つまり、OCIAttrSet()コールを使用してサービス・コンテキスト・ハンドルの該当する属性を変更することにより、アプリケーションがセッションまたはトランザクションを切り替えることはできません。
アプリケーションでOCILogon2()を使用してセッションおよび認可を初期化した場合は、OCILogoff()を使用してそれらを終了する必要があります。
注意: この機能を簡単に示すため、この例ではデプロイされたシステムが通常使用するパスワード管理手法は実行していません。本番環境では、Oracle Databaseパスワード管理ガイドラインに従い、サンプルのアカウントを無効にしてください。パスワード管理ガイドラインおよび他のセキュリティに関する推奨事項については、『Oracle Databaseセキュリティ・ガイド』を参照してください。 |
親プロセスによって作成された状態を子プロセスが再利用できるプロセス生成の機能を提供するオペレーティング・システムについては、「オペレーティング・システムの考慮事項」を参照してください。この項では、子プロセスが親によって作成されたものと同じデータベース接続を使用できない理由を説明します。
プロキシ認証は、ファイアウォールなどの中間層を伴う環境で一般的に使用されるプロセスで、エンド・ユーザーは中間層に対して認証を行い、中間層はエンド・ユーザーのプロキシとして、ユーザーのかわりにデータベースに対して認証を行います。中間層は、データベースにプロキシ・ユーザーとしてログインします。プロキシ・ユーザーは識別情報を切り替えることができ、データベースにログインするとエンド・ユーザーの識別情報に切り替わります。プロキシ・ユーザーは、特定のエンド・ユーザーに適した認可を使用して、そのエンド・ユーザーにかわって操作を実行できます。
注意: Oracle 11gのリリース1では、受入れ可能なパスワードの基準が大幅に上げられ、セキュリティが向上しました。この項で示すパスワードの例は誤りです。パスワードには8文字以上使用する必要があります。詳細は、『Oracle Databaseセキュリティ・ガイド』のパスワード保護のガイドラインを参照してください。 |
データベース・ユーザーのプロキシは、OCIと次のBNF構文を持つALTER
USER
文でサポートされます。
ALTER USER <targetuser> GRANT CONNECT THROUGH <proxy> [AUTHENTICATION REQUIRED];
ALTER
USER
文は、アプリケーションで一度のみ使用されます。以降は、何回でも接続できます。OCIでは、接続文字列または関数OCIAttrSet() (パラメータOCI_ATTR_PROXY_CLIENT
付き)を使用できます。
プロキシの切替えが行われると、現在接続しているユーザーがプロキシのターゲット・ユーザーになります。元のユーザーの識別情報は、権限の計算対象としては使用されません。元のユーザーはローカル・ユーザーまたは外部ユーザーになります。
例2-4から例2-11までは、OCILogon2() (mode
= OCI_DEFAULT
を設定)、OCILogon()
、OCIAttrSet() (セッション・ハンドルの属性OCI_ATTR_USERNAME
を渡す)を指定したOCISessionBegin()などの関数で使用できる接続文字列を示しています。
例2-4では、DilbertとJoeが2人のローカル・データベース・ユーザーです。DilbertがJoeの代理ユーザーの役割を果たせるようにするには、例2-4に示すSQL文を使用します。
ユーザー名dilbert
をjoe
の代理にするには、例2-5に示す接続文字列を使用します。(ユーザー名dilbert
にはパスワードtiger123
があります)。
左と右のカッコ([ と ]) は、接続文字列に入力します。
例2-6では、"Dilbert"と"Joe"が2人のローカル・データベース・ユーザーです。名前には大文字と小文字の区別があり、二重引用符で囲む必要があります。Dilbert"が"Joe"の代理ユーザーの役割を果たせるようにするには、例2-6に示すSQL文を使用します。
"Dilbert"を"Joe"の代理にするには、例2-7に示す接続文字列を使用します。必ず二重引用符(")を付けてください。
代理ユーザーが "dilbert[mybert]"として作成される場合、データベースに接続するには例2-8に示す接続文字列を使用します。(左と右のカッコ([ と ])は、接続文字列に入力します。)
例2-8 接続文字列での"dilbert[mybert]"の使用
"dilbert[mybert]"/tiger123 rem the user was already created this way: rem CREATE USER "dilbert[mybert]" IDENTIFIED BY tiger123;
例2-9では、dilbert[mybert]とjoe[myjoe]は、左右のカッコ([ と ])を含む2人のデータベース・ユーザーです。dilbert[mybert]がjoe[myjoe]の代理となるには、例2-9で示す接続文字列を使用します。
例2-10では、ALTER
USER
文を使用することによりターゲット・ユーザー名を設定できます。
例2-10 ターゲット・ユーザー名の設定
ALTER USER joe GRANT CONNECT THROUGH dilbert;
次に、例2-11で示すように、OCIプログラムで、OCIAttrSet()
コールを使用して属性OCI_ATTR_PROXY_CLIENT
とプロキシdilbert
を設定します。プログラム内で次の各文を使用して複数回接続を行います。
例2-11 OCIを使用したOCI_ATTR_PROXY_CLIENT属性および代理dilbertの設定
OCIAttrSet(session, OCI_HTYPE_SESSION, (void *)"dilbert", (ub4)strlen("dilbert"), OCI_ATTR_USERNAME, error_handle); OCIAttrSet(session, OCI_HTYPE_SESSION, (void *)"tiger123", (ub4)strlen("tiger123"), OCI_ATTR_PASSWORD, error_handle); OCIAttrSet(session, OCI_HTYPE_SESSION, (void *)"joe", (ub4)strlen("joe"), OCI_ATTR_PROXY_CLIENT, error_handle);
関連項目:
|
注意: プロキシを介したクライアント・アクセスには互換性の問題があります。この機能は、Oracle Databaseリリース10.2で導入されたため、10.2より前のクライアントはこの機能を備えていません。新しいクライアントが10.2より前のOracle Databaseでこの機能を使用すると、接続は失敗し、データベースのリリース・レベルのチェック後にクライアントがエラーを戻します。 |
プロキシを介さない複数のセッションまたは接続のオプションでは、明示的なアタッチ・コールおよびセッション開始コールを使用して、データベース接続で複数のユーザー・セッションおよび接続を維持します。Oracle Databaseにアタッチし、セッションを開始するための具体的なコールは、次のとおりです。
OCIServerAttach(): OCI操作用のOracle Databaseへのアクセス・パスを作成します。
OCISessionBegin(): 特定のOracle Databaseに対するユーザーのセッションを確立します。ユーザーがOracle Databaseに対する操作を実行するには、このコールが必須です。
異なるサービス・コンテキスト・ハンドルおよびセッション・コンテキスト・ハンドルを使用するOCISessionBegin()への後続コールは、前のユーザーをログオフするため、エラーを引き起こします。移行不可能な2つの同時セッションを実行するには、2番目のOCISessionBegin()コールに、同じサービス・コンテキスト・ハンドルおよび新しいセッション・コンテキスト・ハンドルを使用する必要があります。
これらのコールにより、データベースに対してSQL文およびPL/SQL文を実行できる操作環境が設定されます。
関連項目:
|
例2-12は、OCI環境の作成および初期化を示しています。
サーバー・コンテキストを作成し、それをサービス・ハンドルに設定します。
次に、ユーザー・セッション・ハンドルを作成し、データベース・ユーザー名とパスワードを使用してそれを初期化します。
簡単にするために、エラー・チェックは含まれていません。
#include <oci.h> ... main() { ... OCIEnv *myenvhp; /* the environment handle */ OCIServer *mysrvhp; /* the server handle */ OCIError *myerrhp; /* the error handle */ OCISession *myusrhp; /* user session handle */ OCISvcCtx *mysvchp; /* the service handle */ ... /* initialize the mode to be the threaded and object environment */ (void) OCIEnvCreate(&myenvhp, OCI_THREADED|OCI_OBJECT, (void *)0, 0, 0, 0, (size_t) 0, (void **)0); /* allocate a server handle */ (void) OCIHandleAlloc ((void *)myenvhp, (void **)&mysrvhp, OCI_HTYPE_SERVER, 0, (void **) 0); /* allocate an error handle */ (void) OCIHandleAlloc ((void *)myenvhp, (void **)&myerrhp, OCI_HTYPE_ERROR, 0, (void **) 0); /* create a server context */ (void) OCIServerAttach (mysrvhp, myerrhp, (text *)"inst1_alias", strlen ("inst1_alias"), OCI_DEFAULT); /* allocate a service handle */ (void) OCIHandleAlloc ((void *)myenvhp, (void **)&mysvchp, OCI_HTYPE_SVCCTX, 0, (void **) 0); /* set the server attribute in the service context handle*/ (void) OCIAttrSet ((void *)mysvchp, OCI_HTYPE_SVCCTX, (void *)mysrvhp, (ub4) 0, OCI_ATTR_SERVER, myerrhp); /* allocate a user session handle */ (void) OCIHandleAlloc ((void *)myenvhp, (void **)&myusrhp, OCI_HTYPE_SESSION, 0, (void **) 0); /* set user name attribute in user session handle */ (void) OCIAttrSet ((void *)myusrhp, OCI_HTYPE_SESSION, (void *)"hr", (ub4)strlen("hr"), OCI_ATTR_USERNAME, myerrhp); /* set password attribute in user session handle */ (void) OCIAttrSet ((void *)myusrhp, OCI_HTYPE_SESSION, (void *)"hr", (ub4)strlen("hr"), OCI_ATTR_PASSWORD, myerrhp); (void) OCISessionBegin ((void *) mysvchp, myerrhp, myusrhp, OCI_CRED_RDBMS, OCI_DEFAULT); /* set the user session attribute in the service context handle*/ (void) OCIAttrSet ((void *)mysvchp, OCI_HTYPE_SVCCTX, (void *)myusrhp, (ub4) 0, OCI_ATTR_SESSION, myerrhp); ... }
demo
ディレクトリ内のデモ・プログラムcdemo81.c
は、このプロセスとエラー・チェックを例示しています。
第4章では、OCIでのSQL文の処理に含まれる特定のステップについて説明します。
アプリケーションは、OCITransCommit()をコールすることにより、データベースへの変更をコミットします。このコールは、サービス・コンテキストをパラメータの1つとして使用します。トランザクションは、変更がコミットされるサービス・コンテキストに対応付けられています。このトランザクションは、アプリケーションで明示的に作成されたか、または、アプリケーションがデータベースを変更したときに暗黙的に作成された可能性があります。
注意: OCIStmtExecute()コールのOCI_COMMIT_ON_SUCCESS モードを使用すると、各文の実行後にトランザクションを実行するかどうかをアプリケーションで選択できるため、ラウンドトリップを節約できます。 |
トランザクションをロールバックするには、OCITransRollback()コールを使用します。
通常のログオフ以外のなんらかの方法でアプリケーションとOracle Databaseの接続が切断された場合(ネットワークとの接続が切れるなど)、OCITransCommit()をコールしていないと、アクティブ・トランザクションはすべて自動的にロールバックされます。
OCIアプリケーションは、終了する前に、次の3つのステップの実行が必要です。
各セッションに対してOCISessionEnd()をコールし、ユーザー・セッションを終了します。
各データ・ソースに対してOCIServerDetach()をコールし、データ・ソースへのアクセスを終了します。
各ハンドルに対してOCIHandleFree()をコールし、明示的に全ハンドルの割当てを解除します。
環境ハンドルを削除し、環境ハンドルに関連付けられたその他のハンドルの割当てをすべて解除します。
注意: OCIの親ハンドルが解放されると、それに対応付けられた子ハンドルもすべて自動的に解放されます。 |
OCIServerDetach()およびOCISessionEnd()のコールは、必須ではありませんが、お薦めします。アプリケーションがOCITransCommit() (トランザクション・コミット)をコールしないで終了すると、保留状態のトランザクションはすべて自動的にロールバックされます。
注意: アプリケーションでOCILogon2()の単純化されたログイン方法を使用する場合は、OCILogoff()をコールするとセッションが終了し、Oracle Databaseとの接続が切断され、サービス・コンテキスト・ハンドルとそれに関連付けられたハンドルが解放されます。ただし、アプリケーションにより割り当てた他のハンドルは、そのアプリケーションから解放してください。 |
OCI関数コールには、表2-3に記載された一連のリターン・コードが用意されており、これらにより、コールの成功または失敗(OCI_SUCCESS
、OCI_ERROR
など)や、アプリケーションが必要とするその他の情報(OCI_NEED_DATA
、OCI_STILL_EXECUTING
など)が示されます。大部分のOCIコールは、これらのコードの1つを戻します。
サーバーへの接続がOCI_ERROR
で終了しないように、アプリケーションでは、サーバー・ハンドル内の属性OCI_ATTR_SERVER_STATUS
の値をチェックできます。属性の値がOCI_SERVER_NOT_CONNECTED
の場合、サーバーへの接続およびユーザー・セッションを再確立する必要があります。
表2-3 OCIリターン・コード
OCIリターン・コード | 値 | 説明 |
---|---|---|
0 |
関数は正常に終了しました。 |
|
1 |
関数は正常に終了しました。OCIErrorGet()をコールすると、追加診断情報が戻されます。これには、警告が含まれる場合があります。 |
|
100 |
関数が終了しました。これ以上データはありません。 |
|
-1 |
関数が失敗しました。OCIErrorGet()をコールすると、エラーの追加情報が戻されます。 |
|
-2 |
無効なハンドルがパラメータとして渡されたか、ユーザー・コールバックで無効なハンドルまたは無効なコンテキストが渡されました。追加診断情報はありません。 |
|
99 |
アプリケーションで、ランタイム・データを提供する必要があります。 |
|
-3123 |
サービス・コンテキストが非ブロック化モードで確立されたため、現行の操作は即時完了できませんでした。この操作を完了するには、これを再度コールする必要があります。OCIErrorGet()がエラー・コードとして |
|
-24200 |
このコードはコールバック関数からのみ戻されます。これは、コールバック関数が、OCIライブラリの標準処理再開を示唆していることを示します。 |
|
-24201 |
このコードはコールバック関数からのみ戻されます。これは、コールバック関数がユーザー行のコールバックにより実行されていることを示します。 |
エラーが発生したことがリターン・コードに示されている場合、アプリケーションではOCIErrorGet()
をコールして、Oracle Database固有のエラー・コードおよびメッセージを取り出せます。OCIErrorGet()へのパラメータの1つは、エラーが発生したコールに渡されたエラー・ハンドルです。
注意: レコードがなくなる(OCI_NO_DATA が戻される)まで、繰り返しOCIErrorGet()をコールすると、複数の診断レコードを取り出すことができます。OCIErrorGet() は、最大1個の診断レコードを戻します。 |
表2-4に、フェッチされたデータが正常、NULL、または切捨ての場合の、OCIリターン・コード、エラー番号、標識変数および列リターン・コードを示します。
表2-4 リターン・コードとエラー・コード
データの状態 | リターン・コード | インジケータ - 戻さない場合 | インジケータ - 戻す場合 |
---|---|---|---|
NULL、切捨て以外 |
戻さない場合 |
エラー = 0 |
エラー = 0 インジケータ = 0 |
NULL、切捨て以外 |
戻す場合 |
エラー = 0 リターン・コード = 0 |
エラー = 0 インジケータ = 0 リターン・コード = 0 |
NULLデータ |
戻さない場合 |
エラー = 1405 |
エラー = 0 インジケータ = -1 |
NULLデータ |
戻す場合 |
エラー = 1405 リターン・コード = 1405 |
エラー = 0 インジケータ = -1 リターン・コード = 1405 |
切捨てデータ |
戻さない場合 |
エラー = 1406 |
エラー = 1406 インジケータ = data_len |
切捨てデータ |
戻す場合 |
エラー = 24345 リターン・コード = 1405 |
エラー = 24345 インジケータ = data_len リターン・コード = 1406 |
切捨てデータの場合、data_len
は、長さがSB2MAXVAL
以下であった場合に切り捨てられたデータの実際の長さです。この値を超えていれば、インジケータが-2に設定されます。
一部の関数では、表2-3にリストされているOCIエラー・コード以外の値を戻します。これらの関数を使用する場合、OUT
パラメータからではなく、関数コールから直接値が戻ります。それぞれの関数と戻り値の詳細は、該当する章を参照してください。
この項では、OCIアプリケーションをコーディングするときのその他の問題について説明します。
オペレーティング・システムでは、子プロセスで親プロセスによって作成された状態を再利用できるプロセス生成機能が提供されている場合があります。子プロセスの生成後、この子プロセスでは、親によって作成されたものと同じデータベース接続を使用できません。親と同じデータベース接続を子プロセスのために使用しようとすると、Oracle Netでは、データベースへの1つの接続を使用できるのは1つのユーザー・プロセスのみであるため、好ましくない接続障害が発生して、間欠的なORA-03137
エラーが生じる可能性があります。
複数の同時接続が必要な場合、使用プラットフォームでスレッド・パッケージがサポートされていれば、スレッドの使用を検討します。シングル・スレッドまたはマルチ・スレッドのいずれのアプリケーションでも、同時接続はサポートされます。
同時にオープンされる多数の接続によるパフォーマンス向上のため、それらのプーリングを検討してください。
OCI関数には整数、ハンドル、文字列など、多様なデータ型のパラメータを使用することができます。パラメータの型によっては注意事項があり、以降の項で説明します。
アドレス・パラメータは、変数のアドレスをOracle Databaseに渡すために使用されます。Cでは、通常スカラー・パラメータを値で渡すので、Cで開発する場合は注意してください。
データベース列にNULLを挿入する方法はいくつかあります。
1つは、INSERT
文またはUPDATE
文のテキストの中でリテラルNULL
を使用する方法です。たとえば、SQL文によりENAME
列はNULL
になります。
INSERT INTO emp1 (ename, empno, deptno) VALUES (NULL, 8010, 20)
OCIバインド・コールでは、標識変数を使用します。「標識変数」を参照してください。
バッファ長パラメータと最大長パラメータの両方をバインド・コールで0 (ゼロ)に設定するには、NULL
を挿入します。
注意: 対応する標識変数が定義コールで指定されていない変数に、NULL選択リスト項目をフェッチしようとすると、Oracle DatabaseはSQL標準要件に従いエラーを戻します。 |
各バインドと定義OCIコールには、標識変数または標識変数の配列をDML文、PL/SQL文または問合せに関連付けるパラメータがあります。
C言語にはNULL値という概念がないため、標識変数を入力変数に関連付けることにより、関連するプレースホルダがNULL
であるかどうかを指定します。データがOracle Databaseに渡されると、この標識変数の値により、データベース・フィールドにNULL
が割り当てられるかどうかが決定されます。
出力変数では、標識変数によって、Oracleから戻された値がNULL
または切り捨てられた値であるかどうかが判断されます。OCIStmtFetch2()コールでのNULL
フェッチ、またはOCIStmtExecute()コールでの切捨ての場合は、OCIコールによりOCI_SUCCESS_WITH_INFO
が戻されます。出力標識変数が設定されます。
標識変数のデータ型はsb2
です。標識変数の配列の場合、各配列要素の型はsb2
です。
入力ホスト変数については、OCIアプリケーションは次の値を標識変数に割り当てることができます。
入力インジケータ値 | Oracle Databaseからのアクション |
---|---|
-1 | Oracle DatabaseはNULL を列に割り当て、入力変数の値は無視します。 |
>=0 | 入力変数の値を列に割り当てます。 |
出力については、Oracle Databaseは次の値を標識変数に割り当てます。
出力インジケータ値 | 意味 |
---|---|
-2 | 項目の長さが出力変数の長さよりも長いため、項目を切り捨てます。さらに、元の長さが、sb2 標識変数で戻せる最大長より長くなっています。 |
-1 | 選択された値がNULLで、出力変数の値は変更されません。 |
0 | 完全な値をホスト変数に割り当てます。 |
>0 | 項目の長さが出力変数の長さよりも長いため、項目を切り捨てます。標識変数に戻された正の値は、切捨て前の実際の長さです。 |
リリース8.0より後に導入されたデータ型の標識変数は、前述の説明どおりに機能します。例外はSQLT_NTY (名前付きデータ型)です。SQLT_NTY型のデータの場合、標識変数はインジケータ構造体へのポインタである必要があります。SQLT_REF型のデータでは、他の変数型と同様に、標準のスカラー・インジケータを使用します。
Object Type Translator (OTT)を使用してデータベース型をC構造体に変換すると、各オブジェクト型についてNULLインジケータ構造体が生成されます。この構造体には、アトミックNULLインジケータと各オブジェクト属性のインジケータが含まれます。
関連項目:
|
ほとんどのオペレーティング・システムでは、キーボードからオペレーティング・システムの割込み文字(通常は[Ctrl] + [C]キー)を入力することで、長時間実行されたり繰り返されているOCIコールを取り消すことができます。
長時間実行されたり繰り返されているコールをオペレーティング・システムへの割込みにより取り消した場合は、エラー・コードORA-01013
(「ユーザーによって現行の操作の取消しが要求されました」)が戻されます。
特定のサービス・コンテキスト・ポインタまたはサーバー・コンテキスト・ポインタを指定すると、OCIBreak()
関数は、サーバーに関連付けられている現在実行中のOCI関数をすべて即時(非同期)に停止します。これは通常、サーバーで長時間実行されている処理中のOCIコールを停止するために使用します。OCIReset()関数は、OCIアプリケーションがOCIBreak()で機能を停止した後で、非ブロック化接続に対してプロトコル同期化を実行するために必要です。
長時間実行する可能性のあるコールは、非ブロック化コールを使用することによりステータスを監視できます。新しいアプリケーションでは、マルチスレッドを使用します。
SELECT
...FOR
UPDATE
OF
...文に関連付けられたROWID
を、後のUPDATE
文またはDELETE
文に使用できます。ROWID
は、文ハンドルに対してOCIAttrGet()をコールして取り出すことができ、これによりこのハンドルのOCI_ATTR_ROWID
属性を取り出せます。
たとえば、次のようなSQL文があるとします。
SELECT ename FROM emp1 WHERE empno = 7499 FOR UPDATE OF sal
この場合、フェッチが実行されると、ハンドルのROWID
属性には選択された行の行識別子が入ります。次のコードのようにOCIAttrGet()
をコールして、プログラムのバッファにROWIDを取り出すことができます。
OCIRowid *rowid; /* the rowid in opaque format */ /* allocate descriptor with OCIDescriptorAlloc() */ status = OCIDescriptorAlloc ((void *) envhp, (void **) &rowid, (ub4) OCI_DTYPE_ROWID, (size_t) 0, (void **) 0); status = OCIAttrGet ((void *) mystmtp, OCI_HTYPE_STMT, (void *) rowid, (ub4 *) 0, OCI_ATTR_ROWID, (OCIError *) myerrhp);
その後、保存されたROWID
をDELETE
文またはUPDATE
文で使用できます。たとえば、rowid
が行識別子が保存されているバッファである場合、新規給与をプレースホルダ:1
にバインドし、rowid
をプレースホルダ:2
にバインドすることで、次のようなSQL文を後から処理できます。
UPDATE emp1 SET sal = :1 WHERE rowid = :2
rowid
を:2
にバインドする場合は、必ずデータ型コード104 (ROWID
記述子、表3-2を参照)を使用してください。
プリフェッチを使用することで、後続のバッチ更新で使用するためにROWID
の配列を選択できます。
一部のワードはOracleによって確保されています。つまり、それらのワードはOracleにとって特別な意味を持っており、再定義できません。このため、それらのワードを使用して、列、表、索引などのデータベース・オブジェクトに名前を付けることはできません。
関連項目: SQLおよびPL/SQLのOracleキーワードまたは予約語のリストを参照するには、『Oracle Database SQL言語リファレンス』および『Oracle Database PL/SQL言語リファレンス』を参照してください。 |
表2-5は、Oracleによって予約されているネームスペースのリストです。Oracleライブラリ内の関数名の最初の文字列は、このリストの文字列に制限されています。名前が競合する可能性があるため、これらの文字列で始まる関数名を付けないでください。
表2-5 Oracle予約済ネームスペース
ネームスペース | ライブラリ |
---|---|
|
XAアプリケーション専用の外部関数 |
|
OracleのプリコンパイラおよびSQL*Moduleアプリケーションで使用される外部SQLLIB関数 |
|
外部OCI関数、内部OCI関数 |
|
Oracle UPIレイヤーの関数名 |
|
Oracle Netネイティブ・サービス・プロダクト Oracle Net RPCプロジェクト Oracle Netディレクトリ Oracle Netネットワーク・ライブラリ・レイヤー Oracle Net管理プロジェクト Oracle Netインターチェンジ Oracle Netトランスペアレント・ネットワーク・サービス Oracle Netドライバ Oracle Netセキュリティ・サービス Oracle Net V1 Oracle Net 2タスク |
|
コア・ライブラリ関数 |
|
Oracleグローバリゼーション・サポート・レイヤーの関数名 |
|
システム依存ライブラリの関数名 |
|
カーネル・オブジェクト |
特定のネームスペースのすべての関数リストは、該当するOracleライブラリに対応したドキュメントを参照してください。
OCIには、完了をポーリングするコールがあります。そのようなポーリング・コードのコールの例は、次のとおりです。
非ブロック化モードでのOCIコール
OCILobRead2()およびOCILobWrite2()などのピース単位のLOBデータで動作するOCIコール
OCIStmtSetPieceInfo()およびOCIStmtGetPieceInfo()で使用されるOCIStmtExecute()およびOCIStmtFetch2()
このような場合、OCIでは、アプリケーションで必ず同じOCIコールがその接続で繰り返され、その間にその接続でそれ以外のことが行われないようにする必要があります。そのような接続でその他のOCIコールを実行すると(OCIが制御をコール元に戻した場合)、予期しない動作が生じる可能性があります。
したがって、コール元は、そのようなポーリング・モードのOCIコールにより、同じコールがその接続で繰り返され、そのコールが完了するまで他のことが行われないようにする必要があります。
OCIBreak()およびOCIReset()は、このルールに対する例外です。これらのコールは、開始されたOCIコールをコール元が停止できるように使用されます。
注意: 非ブロック化モードでは、コール元はコールが完了するまで、同じコールを繰り返す必要があるため、CPUの使用率が増加します。かわりに、マルチスレッド・モードを使用します。 |
OCIでは、ブロック化モードまたは非ブロック化モードでサーバー接続を確立できます。接続がブロック化モードで行われた場合、結果が正常かエラーかにかかわらずコールが完了した場合にのみ、OCIコールはコントロールをOCIクライアント・アプリケーションに戻します。非ブロック化モードでは、コールが完了せずコールからOCI_STILL_EXECUTING
の値が戻されても、コントロールはただちにOCIプログラムに戻されます。
非ブロック化モードでは、アプリケーションで各OCI関数のリターン・コードをテストし、OCI_STILL_EXECUTING
が戻されるかどうかを確認する必要があります。この場合、OCIクライアントはこのOCIコールのサーバーでの再試行を待つ間、プログラム・ロジックの処理を継続できます。このモードは、グラフィカル・ユーザー・インタフェース(GUI)アプリケーション、リアルタイム・アプリケーションおよび分散環境の場合に特に有用です。
非ブロック化モードは非割込み駆動で、むしろポーリング・パラダイムに基づいています。これは、保留状態のコールがサーバーで終了したかどうかを、同一パラメータでそのコールを再び実行することにより、クライアント・アプリケーションでチェックする必要があることを意味します。
次の機能および関数は、非ブロック化モードではサポートされません。
ダイレクト・パス・ロード
LOBバッファリング
オブジェクト
問合せキャッシュ
スクロール・カーソル
透過的アプリケーション・フェイルオーバー(TAF)
OCIAQEnqArray()
OCIAQDeqArray()
OCIDescribeAny()
OCILobArrayRead()
OCILobArrayWrite()
OCITransStart()
OCITransDetach()
アプリケーションのブロック化状態は、attrtype
パラメータをOCI_ATTR_NONBLOCKING_MODE
に設定して、サーバー・コンテキスト・ハンドルでOCIAttrSet()をコールして状態を設定するか、OCIAttrGet()をコールして状態を読み取ることにより、変更またはチェックすることができます。この属性を設定できるのは、OCISessionBegin()またはOCILogon2()がコールされた後でのみです。それ以外の場合はエラーが戻されます。
注意: パラメータとしてサーバー・コンテキスト・ハンドルまたはサービス・コンテキスト・ハンドルを持つ関数のみが、OCI_STILL_EXECUTING を戻します。 |
OCIコールの実行中にOCIBreak()関数を使用することにより、長時間実行しているOCIコールを取り消すことができます。その後、OCIReset()コールを発行して、非同期操作およびプロトコルをリセットする必要があります。
PL/SQLは、Oracleが開発したSQL言語の手続き型拡張機能です。PL/SQLは、単純な問合せやSQLデータ操作言語(DML)文よりも複雑なタスクをサポートします。PL/SQLを使用すると、複数の構文を単一のブロックにグループ化し、それを1単位として実行できます。次のような構文があります。
1つまたは複数のSQL文
変数宣言
代入文
IF...THEN...ELSE
文やループなどのプロシージャ型制御文
例外処理
OCIプログラムでPL/SQLブロックを使用して、次の操作ができます。
Oracleストアド・プロシージャおよびストアド・ファンクションのコール
手続き型制御文を複数のSQL文と結合し、1つの単位として実行
表、CURSOR FOR
ループ、例外処理など、特殊なPL/SQL機能へのアクセス
カーソル変数の使用
サーバー内のオブジェクトの操作
注意:
|
注意: PL/SQLコードの作成時には、パーサーにより一対のハイフン「--」と改行文字との間のすべてがコメントとして処理されることを覚えておいてください。「--」を使用してコメントを各行に示した場合、Cコンパイラでは各行に改行「¥n」が挿入されず、すべての行がPL/SQLブロックで単一行として連結されます。この特定のケースでは、ある行がコメントで終わっている場合、パーサーは次の行のPL/SQLコード抽出に失敗してしまいます。この問題を回避するには、各「--」コメントの後に「\n」を入れ、そこでコメントが必ず終了するようにしてください。 |
関連項目: PL/SQLブロックのコーディングの詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください。 |
次の項では、ロケール情報の導出、文字列の操作、キャラクタ・セット変換、OCIメッセージなど、グローバリゼーションに使用されるOCI関数について説明します。これらの関数には複数の目的および機能があるため、詳細は、このマニュアルの他の章を参照してください。
OCIEnvNlsCreate()関数は、NLS_LANGおよびNLS_NCHARの設定とは関係なく、アプリケーションでキャラクタ・セット情報を設定できます。様々なクライアント側キャラクタ・セットIDおよび各国語キャラクタ・セットIDを使用して、同じシステム環境内で初期化した複数の環境ハンドルを1つのアプリケーションで使用できます。たとえば、次のようにします。
OCIEnvNlsCreate(OCIEnv **envhpp, ..., csid, ncsid);
この例では、csid
はキャラクタ・セットIDの値で、ncsid
は各国語キャラクタ・セットIDの値です。いずれの値も0 (ゼロ)またはOCI_UTF16ID
に設定できます。両方とも0 (ゼロ)の場合は、OCIEnvCreate()を使用するのと同じになります。その他の引数は、OCIEnvCreate()
コールの引数と同じです。
OCIEnvNlsCreate()は、OCI_UTF16ID
を検証するため、キャラクタ・セットをプログラムによって制御するための拡張機能です。
OCIEnvNlsCreate()
関数を使用してキャラクタ・セットIDを設定すると、NLS_LANGおよびNLS_NCHARの設定値が置換されます。OCIEnvNlsCreate()
関数では、各国語サポート・ランタイム・ライブラリ(NLSRTL)でサポートされるすべてのキャラクタ・セット以外に、OCI_UTF16ID
をキャラクタ・セットIDとして設定できます(このIDは、NLS_LANGまたはNLS_NCHARでは無効となります)。
AL16UTF16を除くすべてのOracleキャラクタ・セットIDは、OCIEnvNlsCreate()関数を使用して指定され、メタデータのエンコーディング、SQL CHAR
データおよびSQL NCHAR
データを指定できます。
NLS_LANGおよびNLS_NCHARのキャラクタ・セットは、関数のOCINlsEnvironmentVariableGet()を使用して取得できます。
OCINlsGetInfo()関数は、
OCI_UTF16ID
がOCIEnvNlsCreate()
で使用されている場合に、この値に関する情報を戻します。
OCIAttrGet()関数は、OCIEnvNlsCreate()に渡されたキャラクタ・セットIDおよび各国語キャラクタ・セットIDを戻します。これは、OCI_ATTR_ENV_CHARSET_ID
およびOCI_ATTR_ENV_NCHARSET_ID
を取得するために使用されます。これには、値OCI_UTF16ID
が含まれます。
OCIEnvNlsCreate()
を使用してcharset
およびncharset
パラメータにNULLが設定されている場合は、NLS_LANG
およびNLS_NCHAR
のキャラクタ・セットIDが戻されます。
この関数を使用してOCI_ATTR_CHARSET_FORM
がリセットされる場合、OCIAttrSet()関数は文字IDをデフォルトとして設定します。OCIEnvNlsCreate()がcharset
またはncharset
として渡される場合、有効なキャラクタ・セットIDにはOCI_UTF16ID
が含まれます。
OCIBindByName()およびOCIBindByPos()関数は、OCIEnvNlsCreate()コールのデフォルトのキャラクタ・セット(OCI_UTF16ID
を含む)を使用して変数をバインドします。OCIEnvNlsCreate()
を使用する場合、実際の長さと戻される長さは常にバイト単位で表されます。
OCIDefineByPos()関数は、OCIEnvNlsCreate()のcharset
の値(OCI_UTF16ID
を含む)を持つ変数をデフォルトとして定義します。OCIEnvNlsCreate()
を使用する場合、実際の長さと戻される長さは常にバイト単位で表されます。バインド・ハンドルおよび定義ハンドルのこの動作は、OCIEnvCreate()を使用する場合とは異なり、OCI_UTF16ID
はバインド・ハンドルおよび定義ハンドル用のキャラクタ・セットIDになります。
OCIは、サーバーとクライアント間のトランスレータとして機能し、制約チェックを行うための文字情報を渡します。
キャラクタ・セットには、可変幅と固定幅の2種類があります。(シングルバイト・キャラクタ・セットは、各バイトが1文字を表す固定幅キャラクタ・セットの特殊なケースです。)
固定幅キャラクタ・セットの場合、バイト数は文字数の倍数であるため、制約チェックが簡単です。したがって、固定幅キャラクタ・セットでは、文字数を調べるために文字列全体をスキャンする必要がありません。これに対して、可変幅キャラクタ・セットでは、文字列全体をスキャンして文字数を調べる必要があります。
OCIでのキャラクタ・セットのサポートの詳細は、「記述操作での文字長セマンティクスのサポート」および「OCIバインドおよび定義における文字変換」を参照してください。
グローバリゼーション・サポート関数は、環境ハンドルまたはユーザー・セッション・ハンドルを受け入れます。OCI環境ハンドルは、クライアントのNLS環境変数に関連付けられます。ALTER
SESSION
文がサーバーに発行された場合、この環境は変更されません。環境ハンドルに関連付けられたキャラクタ・セットは、クライアント・キャラクタ・セットです。OCIセッションハンドル(OCISessionBegin()によって戻されます)は、サーバー・セッション環境に関連付けられます。
ALTER
SESSION
文でセッション環境が変更された場合、NLS設定も変更されます。セッション・ハンドルに関連付けられたキャラクタ・セットは、データベース・キャラクタ・セットです。
セッションで最初のトランザクションが開始されるまで、OCIセッション・ハンドルには、関連付けられたNLS設定がないことに注意してください。SELECT
文では、トランザクションは開始されません。
Oracle Databaseロケールは、言語、地域およびキャラクタ・セット定義で構成されます。ロケールは、曜日と月の名前、および日付、時刻、数値、通貨の書式などの表記規則を決定します。グローバル化されたアプリケーションは、ユーザーのロケール設定および文化の規則に対応しています。たとえば、ロケールをドイツに設定すると、月日の名前がドイツ語で表示されます。
OCINlsGetInfo()関数を使用して、次の情報を取得できます。
曜日(翻訳済)
曜日の略称(翻訳済)
月名(翻訳済)
月名の略称(翻訳済)
はい/いいえ(翻訳済)
AM/PM (翻訳済)
AD/BC (翻訳済)
数値書式
借方/貸方
日付書式
通貨書式
デフォルトの言語
デフォルトの地域
デフォルト・キャラクタ・セット
デフォルトの言語ソート
デフォルト・カレンダ
例2-13のコードでは、ロケール情報を取得し、エラーをチェックします。
例2-13 OCIでのロケール情報取得
sword MyPrintLinguisticName(envhp, errhp) OCIEnv *envhp; OCIError *errhp; { OraText infoBuf[OCI_NLS_MAXBUFSZ]; sword ret; ret = OCINlsGetInfo(envhp, /* environment handle */ errhp, /* error handle */ infoBuf, /* destination buffer */ (size_t) OCI_NLS_MAXBUFSZ, /* buffer size */ (ub2) OCI_NLS_LINGUISTIC_NAME); /* item */ if (ret != OCI_SUCCESS) { checkerr(errhp, ret, OCI_HTYPE_ERROR); ret = OCI_ERROR; } else { printf("NLS linguistic: %s\n", infoBuf); } return(ret); }
文字列操作では、マルチバイト文字列およびワイド・キャラクタ文字列がサポートされています。
マルチバイト文字列は、システム固有のOracleキャラクタ・セットでエンコードされています。マルチバイト文字列で動作する関数は、バイト単位で計算された文字列の長さを使用して、文字列全体を1つの単位として使用します。ワイドキャラクタ(wchar
)文字列関数を使用すると、より柔軟に文字列を操作できます。これは、文字に基づいた操作および文字列に基づいた操作をサポートし、長さは文字列の長さを文字で計算した数値です。
ワイドキャラクタ・データ型OCIWchar
はOracle固有のもので、ANSI/ISO C標準で定義されているwchar_t
データ型と混同しないでください。Oracleのワイドキャラクタ・データ型はすべてのオペレーティング・システムで常に4バイトですが、wchar_t
のサイズは実装およびオペレーティング・システムによって異なります。Oracleワイド・キャラクタ・データ型は、マルチバイト文字を正規化するため、統一された固定幅になり、処理が簡単になります。これにより、Oracleのワイドキャラクタ・セットとシステム固有のキャラクタ・セット間でのラウンドトリップ変換の際、データが失われません。
文字列操作は、次のカテゴリに分類できます。
マルチバイト・キャラクタとワイド・キャラクタ間の文字列変換
文字の分類
大/小文字の変換
表示長の計算
比較、連結、検索などの一般的な文字列操作
例2-14では、単純な文字列操作の例を示します。
例2-14 OCIでの基本的な文字列操作
size_t MyConvertMultiByteToWideChar(envhp, dstBuf, dstSize, srcStr) OCIEnv *envhp; OCIWchar *dstBuf; size_t dstSize; OraText *srcStr; /* null terminated source string */ { sword ret; size_t dstLen = 0; size_t srcLen; /* get length of source string */ srcLen = OCIMultiByteStrlen(envhp, srcStr); ret = OCIMultiByteInSizeToWideChar(envhp, /* environment handle */ dstBuf, /* destination buffer */ dstSize, /* destination buffer size */ srcStr, /* source string */ srcLen, /* length of source string */ &dstLen); /* pointer to destination length */ if (ret != OCI_SUCCESS) { checkerr(envhp, ret, OCI_HTYPE_ENV); } return(dstLen); }
OCI文字分類関数については、「OCI文字分類関数」で詳しく説明します。
例2-15では、OCIでの文字の分類方法を示します。
例2-15 OCIでの文字列分類
boolean MyIsNumberWideCharString(envhp, srcStr) OCIEnv *envhp; OCIWchar *srcStr; /* wide char source string */ { OCIWchar *pstr = srcStr; /* define and init pointer */ boolean status = TRUE; /* define and initialize status variable */ /* Check input */ if (pstr == (OCIWchar*) NULL) return(FALSE); if (*pstr == (OCIWchar) NULL) return(FALSE); /* check each character for digit */ do { if (OCIWideCharIsDigit(envhp, *pstr) != TRUE) { status = FALSE; break; /* non-decimal digit character */ } } while ( *++pstr != (OCIWchar) NULL); return(status); }
Oracleキャラクタ・セットとUnicode (16ビット、固定幅のUnicodeエンコーディング)間の変換がサポートされています。UnicodeからOracleキャラクタ・セットへの文字のマッピングが存在しない場合、置換文字が使用されます。したがって、変換して元のキャラクタ・セットに戻す場合、データが失われる可能性があります。
Unicodeキャラクタ・セットに関連するキャラクタ・セット変換関数では、データのバインド・バッファおよび定義バッファをub2
アドレスで揃える必要があり、揃っていない場合、エラーが発生します。
例2-16では、Unicodeへの単純な変換を示します。
例2-16 OCIでのキャラクタ・セットの変換
/* Example of Converting Character Sets in OCI --------------------------------------------*/ size_t MyConvertMultiByteToUnicode(envhp, errhp, dstBuf, dstSize, srcStr) OCIEnv *envhp; OCIError *errhp; ub2 *dstBuf; size_t dstSize; OraText *srcStr; { size_t dstLen = 0; size_t srcLen = 0; OraText tb[OCI_NLS_MAXBUFSZ]; /* NLS info buffer */ ub2 cid; /* OCIEnv character set ID */ /* get OCIEnv character set */ checkerr(errhp, OCINlsGetInfo(envhp, errhp, tb, sizeof(tb), OCI_NLS_CHARACTER_SET)); cid = OCINlsCharSetNameToId(envhp, tb); if (cid == OCI_UTF16ID) { ub2 *srcStrUb2 = (ub2*)srcStr; while (*srcStrUb2++) ++srcLen; srcLen *= sizeof(ub2); } else srcLen = OCIMultiByteStrlen(envhp, srcStr); checkerr(errhp, OCINlsCharSetConvert( envhp, /* environment handle */ errhp, /* error handle */ OCI_UTF16ID, /* Unicode character set ID */ dstBuf, /* destination buffer */ dstSize, /* size of destination buffer */ cid, /* OCIEnv character set ID */ srcStr, /* source string */ srcLen, /* length of source string */ &dstLen)); /* pointer to destination length */ return dstLen/sizeof(ub2); }
ユーザー・メッセージAPIは、カートリッジ開発者が、独自のメッセージおよびOracle Databaseメッセージを取得するための単純なインタフェースを提供します。
例2-17では、メッセージ・ハンドルを作成し、impus.msg
からメッセージを取得するために初期化してメッセージ番号128を取得し、メッセージ・ハンドルをクローズします。この例では、OCI環境ハンドル、OCIセッション・ハンドル、製品、機能およびキャッシュ・サイズが正しく初期化されていることを想定しています。
例2-17 テキスト・メッセージ・ファイルからのメッセージ取得
OCIMsg msghnd; /* message handle */ /* initialize a message handle for retrieving messages from impus.msg*/ err = OCIMessageOpen(hndl,errhp, &msghnd, prod,fac,OCI_DURATION_SESSION); if (err != OCI_SUCCESS) /* error handling */ ... /* retrieve the message with message number = 128 */ msgptr = OCIMessageGet(msghnd, 128, msgbuf, sizeof(msgbuf)); /* do something with the message, such as display it */ ... /* close the message handle when there are no more messages to retrieve */ OCIMessageClose(hndl, errhp, msghnd);
lmsgen
ユーティリティは、テキストベースのメッセージ・ファイル(.msg
)をバイナリ形式(.msb
)に変換し、これにより、ユーザーが提供したOracleメッセージおよびOCIメッセージを、希望する言語でOCI関数に戻すことができます。
ImsgenユーティリティのBNF構文は、次のとおりです。
lmsgen
text_file
product
facility
[language
]
前述の構文の要素:
text_file
は、メッセージ・テキスト・ファイルです。
product
は、製品の名前です。
facility
は、機能の名前です。
language
は、NLS_LANG
パラメータで指定された言語に対応する、オプションのメッセージ言語です。メッセージ・ファイルに言語のタグが正しく付いていない場合は、言語パラメータが必要です。
テキスト・メッセージ・ファイルは、次のガイドラインに従う必要があります。
"/"
および"//
"で始まる行は、内部コメントとみなされるため、無視されます。
メッセージ・ファイルに特定の言語のタグを付けるには、次のような行を含めます。
# CHARACTER_SET_NAME= Japanese_Japan.JA16EUC
各メッセージには、3つのフィールドがあります。
message_number
,warning_level
,message_text
メッセージ番号は、メッセージ・ファイル内で一意であることが必要です。
警告レベルは、現在使用されていません。0に設定します。
メッセージ・テキストは、76バイトより長くできません。
Oracle Databaseメッセージ・テキスト・ファイルの例を次に示します。
/ Copyright (c) 2001 by the Oracle Corporation. All rights reserved. / This is a test us7ascii message file # CHARACTER_SET_NAME= american_america.us7ascii / 00000, 00000, "Export terminated unsuccessfully\n" 00003, 00000, "no storage definition found for segment(%lu, %lu)"
次の表は、lmsgen
パラメータのサンプル値を示します。
lmsgenパラメータ | 値 |
---|---|
product |
$HOME/myApplication |
facility |
imp |
language |
AMERICAN |
text_file |
impus.msg |
テキスト・メッセージ・ファイルの場所は、次のとおりです。
$HOME/myApp/mesg/impus.msg
テキスト・メッセージ・ファイル内の1行は、次のとおりです。
00128,2, "Duplicate entry %s found in %s"
lmsgen
ユーティリティは、テキスト・メッセージ・ファイル(impus.msg
)をバイナリ形式に変換します。これにより、impus.msb
というファイルが作成されます。
% lmsgen impus.msg $HOME/myApplication imp AMERICAN
出力結果は、次のようになります。
Generating message file impus.msg --> /home/scott/myApplication/mesg/impus.msb NLS Binary Message File Generation Utility: Version 9.2.0.0.0 -Production Copyright (c) Oracle Corporation 1979, 2001. All rights reserved. CORE 9.2.0.0.0 Production