プライマリ・コンテンツに移動
Oracle® Call Interfaceプログラマーズ・ガイド
12c リリース1 (12.1)
B72465-07
目次へ移動
目次
索引へ移動
索引

前
次

7 LOBおよびBFILEの操作

この章では、LOBおよびBFILEの操作について説明します。

この章は、次の項目で構成されています。

LOBでのOCI関数の使用について

OCIには、データベース内のラージ・オブジェクト(Large Object: LOB)で操作を実行するための一連の関数があります。

永続LOB (BLOBCLOBNCLOB)はデータベースの表領域に格納されており、領域の最適化とアクセスの効率化を実現します。これらのLOBは、Oracle Databaseのトランザクションを完全にサポートしています。BFILEとは、データベース表領域外のサーバーのオペレーティング・システム・ファイルに格納された大型のデータ・オブジェクトのことです。

また、OCIは一時LOBもサポートしており、これは、LOBデータ上で操作するローカル変数のように使用することができます。

BFILEは読取り専用です。バイナリのBFILEのみをサポートしています。

関連項目:

  • LOBの使用方法を示したコード例は、「OCIデモ・プログラム」を参照してください

  • 特定のLOBコードのサンプルは、$ORACLE_HOME/rdbms/demo/lobs/oci/を参照してください。

  • DBMS_LOBパッケージは、『Oracle Database PL/SQLパッケージおよびタイプ・リファレンス』を参照してください

  • Oracle Database SecureFiles and Large Objects開発者ガイド

永続LOBの作成と変更について

LOBインスタンスは永続的(データベースに格納)または一時的(アプリケーションの有効範囲内のみに存在)のいずれかである可能性があります。

永続LOBと永続オブジェクトの概念を混同しないでください。

永続LOBの作成および変更には、次の2つの方法があります。

  • データ・インタフェースの使用

    文字データをCLOB列に挿入、またはRAWデータをBLOB列に直接挿入すると、LOBを作成できます。SQLのUPDATE文を使用するか、文字データをCLOB列にバインドするか、またはRAWデータをBLOB列にバインドすると、LOBを変更することもできます。

    リモート・サーバーとローカル・サーバーがいずれもOracle Database 10gリリース2以上であるので、リモートLOBの(dblinkを介した)挿入、更新および選択がサポートされます。データ・インタフェースでは、最大2GB -1、つまりsb4データ型の最大サイズまでのデータ・サイズのみサポートされます。

  • LOBロケータの使用

    内部LOBを新規作成するには、OCIDescriptorAlloc()を使用して新規のLOBロケータを初期化してから、OCIAttrSet()をコールして(OCI_ATTR_LOBEMPTY属性を使用して)空に設定し、次に、そのロケータをINSERT文でプレースホルダにバインドします。この処理により、LOB列または属性がある表に空のロケータを挿入します。次に、SELECT...FOR UPDATE操作をこの行で実行してロケータを取得し、OCI LOB関数の1つを使用してロケータに書き込みます。

    注意:

    LOB列または属性を変更(書込み、コピー、切捨てなど)するには、そのLOBが含まれている行をロックする必要があります。そのためには、操作を実行する前にSELECT...FOR UPDATE文を使用してロケータを選択します。

LOB書込みコマンドを正常終了するには、トランザクションをオープンしておく必要があります。データの書込み前にトランザクションをコミットする場合は、コミットによってトランザクションがクローズされるため、(SELECT...FOR UPDATE文の再発行などにより)行を再ロックする必要があります。

関連項目:

表内のBFILEとオペレーティング・システム・ファイルの関連付けについて

INSERT文でBFILENAME関数を使用すると、外部サーバー側(オペレーティング・システム)ファイルと表内のBFILE列または属性を関連付けることができます。

UPDATE文でBFILENAMEを使用して、BFILEの列または属性を別のオペレーティング・システム・ファイルに関連付けます。OCILobFileSetName()を使用して、表内のBFILEをオペレーティング・システム・ファイルに関連付けることもできます。通常、BFILENAMEINSERTまたはUPDATE文内でバインド変数なしで使用され、OCILobFileSetName()がバインド変数に使用されます。

関連項目:

  • OCILobFileSetName()

  • BFILENAME関数の詳細は、『Oracle Database SecureFilesおよびラージ・オブジェクト開発者ガイド』 を参照してください

オブジェクトのLOB属性

OCIアプリケーションでは、OCIObjectNew()関数を使用して、LOB属性を持つ永続オブジェクトまたは一時オブジェクトを作成することができます。

関連項目:

OCIObjectNew()

オブジェクトのLOB属性への書込み

OCIを使用して、LOB属性を持つ新規の永続オブジェクトを作成したり、そのLOB属性への書込みができます。

LOBロケータを使用するときは、アプリケーションでは次のステップに従います。

  1. OCIObjectNew()をコールして、LOB属性を持つ永続オブジェクトを作成します。
  2. オブジェクトを「使用済」(変更済)とマークします。
  3. 表に行を挿入して、オブジェクトをフラッシュします。
  4. オブジェクトをデータベースから取り出し、LOBに有効なロケータを取得して、オブジェクトの最新バージョンを再確保します(またはオブジェクトをリフレッシュします)。
  5. オブジェクト内のLOBロケータを使用してOCIObjectWrite2()をコールし、データを書き込みます。

LOB属性に書き込むには、もう1つ方法があります。データ・インタフェースを使用すると、CLOB属性の文字データ、またはBLOB属性のRAWデータのバインドまたは定義ができます。

関連項目:

LOB属性を持つ一時オブジェクト

アプリケーションでは、OCIObjectNew()をコールして、内部LOB(BLOBCLOBNCLOB)属性を持つ一時オブジェクトを作成できます。

ただし、LOB属性を持つ一時オブジェクトは現在サポートされていないため、読取りや書込みなどの、LOB属性での操作は実行できません。一時内部LOB型を作成するためのOCIObjectNew()コールは失敗しませんが、アプリケーションでは、この一時LOBでLOB操作を使用することはできません。

ただし、アプリケーションでは、BFILE属性を持つ一時オブジェクトを作成し、BFILE属性を使用して、サーバーのファイル・システムに格納されているファイルからデータを読み取ることができます。アプリケーションでは、OCIObjectNew()をコールして、一時BFILEを作成できます。

関連項目:

OCIObjectNew()

LOBの配列インタフェース

OCI配列インタフェースは、他のデータ型と同様に、LOBに対しても使用できます。

配列インタフェースを使用するには、次の2つの方法があります。

  • データ・インタフェースの使用

    文字データの配列をCLOB列に対して、またはRAWデータの配列をBLOB列に対してバインドまたは定義できます。配列のバインドおよび定義インタフェースを使用すると、LOBを含む複数の行をサーバーへの1回のラウンドトリップで挿入および選択できます。

  • LOBロケータの使用

    LOBロケータを使用する場合は、次のコード例に示すように、記述子を割り当てる必要があります。

    LOBロケータの使用と記述子の割当て

    /* First create an array of OCILobLocator pointers: */
    OCILobLocator *lobp[10];
    
    for (i=0; i < 10; i++)
    { OCIDescriptorAlloc (...,&lobp[i],...);
    
    /* Then bind the descriptor as follows */
      OCIBindByPos(... &lobp[i], ...);
    

関連項目:

4GBより大きいサイズのLOBの使用について

OCIのOracle Database 10gリリース1からは、4GBより大きいサイズのLOBをサポートする関数が導入されました。この新機能は、4GBより小さいLOBの新規アプリケーションでも使用できます。

Oracle Databaseを使用すると、データベースのブロック・サイズとは異なるブロック・サイズを持つ表領域を作成できます。LOBの最大サイズは、表領域のブロック・サイズに応じて異なります。LOBが格納される表領域のブロック・サイズにより、LOB記憶域のパラメータであるCHUNKの値が制御されます。LOB列を作成する場合、CHUNKの値を指定できます。これは、LOBの操作用に割り当てるバイト数です。この値は、表領域ブロック・サイズの倍数である必要があります。そうでない場合、Oracle Databaseによって次の倍数に切り上げられます。(表領域のブロック・サイズがデータベースのブロック・サイズと同じ場合、CHUNKは、データベースのブロック・サイズの倍数でもあります。)CHUNKのデフォルト・サイズは、1つの表領域ブロックのサイズと同じで、最大値は32KBです。

このマニュアルでは、4GBは4GB–1、または4,294,967,295バイトと定義します。LOB(永続または一時)の最大サイズは、(4GB–1)×(CHUNK)です。LOBの最大サイズは、8TBから128TBの範囲です。

たとえば、データベースのブロック・サイズが32KBで、標準外の8KBのブロック・サイズで表領域を作成するとします。さらに、LOB列を持つ表を作成し、CHUNKサイズを16KB (表領域のブロック・サイズである8KBの倍数)に指定するとします。この場合、この列のLOBの最大サイズは(4GB-1)×16KBです。

BFILEの最大サイズは、ご使用のオペレーティング・システムで許容される最大ファイル・サイズとUB8MAXVALのどちらか小さい方となります。

以前のLOB関数ではub4を一部のパラメータのデータ型として使用し、ub4データ型では最大4GBしか保持できません。新しい関数では、8バイト長のパラメータ、oraub8を使用し、これはoratypes.hで定義されるデータ型です。コンパイラとオペレーティング・システムに応じて、データ型oraub8およびorasb8は該当する64ビット固有のデータ型にマップされます。厳密なANSIオプションを使用して32ビット・モードでコンパイルする場合、マクロを使用してoraub8およびorasb8を定義することはできません。

OCILobGetChunkSize()は、利用可能なチャンク・サイズをBLOBCLOBおよびNCLOBに対してバイト数で戻します。チャンクに格納されるバイト数は、内部記憶域のオーバヘッドにより、実際にはCHUNKパラメータのサイズより小さくなります。関数OCILobGetStorageLimit()により、現行インストールにおける内部LOBの最大サイズがバイト数で戻されます。

注意:

Oracle Databaseでは、どのようなプログラム環境においても、4GBを超えるBFILEはサポートされません。ご使用のオペレーティング・システムのその他のファイル・サイズ制限もBFILEに適用されます。

増加したLOBサイズに対して使用する関数

名前が「2」で終わり、データ型ub4のかわりにデータ型oraub8を使用する8つの関数が、Oracle Database 10gリリース1で導入されました。

その他、いくつかの問題を解決するために、読取りおよび書込みの関数(OCILobRead2()OCILobWrite2()およびOCILobWriteAppend2())で変更が行われました。

問題点: Oracle Database 10g リリース1より前では、パラメータamtpにより、LOBがバイト長または文字長のどちらであるかが、ロケータ・タイプおよびキャラクタ・セットに基づいて判断されていました。これは複雑で、ユーザーは要件に応じてバイト長または文字長を柔軟に使用することができませんでした。

解決策: 読取り/書込みコールでは、amtpパラメータのかわりに、byte_amtpおよびchar_amtpの両方のパラメータが必要です。char_amtpパラメータはCLOBNCLOBでは基本設定となり、byte_amtpパラメータは、char_amtpが0 (ゼロ)の場合のみ、入力とみなされます。CLOBおよびNCLOBの出力時には、byte_amtpchar_amtpの両方のパラメータに値が入ります。BLOBおよびBFILEの場合、入力時も出力時もchar_amptパラメータは無視されます。

問題点: OCILobRead2()には、ポーリング・モードを示すためのフラグがありません。このため、ユーザーは「100バイトのバッファがあります。できるかぎり使用してください。」と指示するのが困難になります。以前は、総量に対して指定する文字数を見積もる必要がありました。あまり多く見積もりすぎると、意図せずポーリング・モードに移行されていました。このように、ユーザー・コードによりポーリング・モードでトラップされる場合があり、以降のOCIコールがすべてブロックされてしまいます。

解決策: このコールでは、pieceを入力パラメータとして取り、OCI_ONE_PIECEが渡された場合は、byte_amtpパラメータまたはchar_amtpパラメータで指定した値がバッファ長より大きくても、バッファを可能なかぎり埋めて出る必要があります。buflの値を使用して、読み取るバイトの最大量を指定します。

問題点: ポーリング・モードでLOB書込みをコールした後は、ポーリングの終了までに実際にフェッチされる文字数またはバイト数をユーザーは認識できません。

解決策: ポーリング・モードでの各コール後に、byte_amtpおよびchar_amtpの両パラメータを更新してください。

問題点: コールバックを使用した、ストリーム・モードでの読取り時または書込み時には、ユーザーはデータの各ピースに対して同じバッファを使用する必要があります。

解決策: コールバック関数には、バッファおよびバッファ長を指定する2つの新規パラメータを含めてください。コールバック関数で、バッファ・パラメータをNULLに設定すると、以前の動作に従います(すべてのピースに対する最初のコールで渡されるデフォルト・バッファを使用)。

互換性および移行

既存のOCIプログラムを拡張して、4GBより大きい大容量のLOBデータを処理できます。

表7-1に、互換性の問題点をまとめます(「旧」とは、Oracle Database 10gリリース1より前のリリースを指します)。

表7-1 LOB関数の互換性および移行

LOB関数 旧クライアント/新規または旧サーバー(1) 新規クライアント/旧サーバー 新規クライアント/新規サーバー

OCILobArrayRead()

脚注 2NA

ピース・サイズおよびオフセットが4GB未満であればOK。

OK

OCILobArrayWrite()

NA

ピース・サイズおよびオフセットが4GB未満であればOK。

OK

OCILobCopy2()

NA

LOBサイズ、ピース・サイズ(量)およびオフセットが4GB未満であればOK。

OK

OCILobCopy()

OK (制限は4GB)。

OK

OK (制限は4GB)。

OCILobErase2()

NA

ピース・サイズおよびオフセットが4GB未満であればOK。

OK

OCILobErase()

OK (制限は4GB)。

OK

OK (制限は4GB)。

OCILobGetLength2()

NA

OK

OK

OCILobGetLength()

OK (制限は4GB)。

OK

OK (LOBサイズが > 4GBの場合はOCI_ERRORとなります)。

OCILobLoadFromFile2()

NA

LOBサイズ、ピース・サイズ(量)およびオフセットが4GB未満であればOK。

OK

OCILobLoadFromFile()

OK (制限は4GB)。

OK

OK (制限は4GB)。

OCILobRead2()

NA

LOBサイズ、ピース・サイズ(量)およびオフセットが4GB未満であればOK。

OK

OCILobRead()

OK (制限は4GB)。

新規サーバーを使用した場合: 4GB以上の量を4GB未満のオフセットから読み取ろうとすると、OCI_ERRORが戻されます。これは、ユーザーが4GB以上の量を読み取ると、*amtpの戻り値がオーバーフローするためで、エラーとしてフラグが立てられます。

注意:

  • オフセットから4GB – 1までを読み取る場合は、エラーとしてフラグは立てられません。

  • ポーリング・モードとともにストリーム・モードを使用すると、4GBを超えるピース・サイズを使用しない場合(この場合、4GBを超えるデータの読取りが可能)、エラーは戻されません。

OK

OK。

4GB以上の量を4GB未満のオフセットから読み取ろうとすると、OCI_ERRORが戻されます。これは、ユーザーが4GB以上の量を読み取ると、*amtpの戻り値がオーバーフローするためで、エラーとしてフラグが立てられます。

注意:

  • オフセットから4GB – 1までを読み取る場合は、エラーとしてフラグは立てられません。

  • ポーリング・モードとともにストリーム・モードを使用すると、4GBを超えるピース・サイズを使用しない場合はエラーは戻されません。

OCILobTrim2()

NA

OK

OK

OCILobTrim()

OK (制限は4GB)。

OK

OK (制限は4GB)。

OCILobWrite2()

NA

LOBサイズ、ピース・サイズ(量)およびオフセットが4GB未満であればOK。

OK

OCILobWrite()

OK (制限は4GB)。

新規サーバーを使用した場合:

4GB以上の量を(4GB未満のオフセットから)書き込むと、*amtpの戻り値がオーバーフローするため、OCI_ERRORが戻されます。

注意: 4GB –1までの量のデータにより、10GBのLOBを4GB –1までのオフセットから更新しても、エラーとしてフラグは立てられません。

OK

OK。

4GB以上の量を(4GB未満のオフセットから)書き込むと、*amtpの戻り値がオーバーフローするため、OCI_ERRORが戻されます。

注意: 4GB –1までの量のデータにより、10GBのLOBを4GB –1までのオフセットから更新しても、エラーとしてフラグは立てられません。

OCILobWriteAppend2()

NA

LOBサイズおよびピース・サイズが4GB未満であればOK。

OK

OCILobWriteAppend()

OK (制限は4GB)。

新規サーバーを使用した場合: 4GB以上のデータを追加すると、*amtpの戻り値がオーバーフローするため、OCI_ERRORが戻されます。

OK

OK (制限は4GB)。

4GB以上のデータを追加すると、*amtpの戻り値がオーバーフローするため、OCI_ERRORが戻されます。

OCILobGetStorageLimit()

NA

エラー

OK

脚注 1 「旧」とは、Oracle Database 10gリリース1より前のリリースを指します。

脚注 2

NAは適用されないことを意味します。

現行サーバーと現行クライアントを使用する場合は、「2」で終わる関数を使用してください。非推奨の関数を「2」で終わる関数と併用すると、OCILobWrite2()を使用して書き込まれたデータが4GBより大きいときに、アプリケーションでOCILobRead()を使用してそれが読み取られると、データの一部しか取得されない(コールバック関数が使用されていない場合)など、予期せぬ状況になる可能性があります。サイズが4GBを超えていて、非推奨の関数が使用されると、ほとんどの場合、アプリケーションでエラー・メッセージを受け取ります。ただし、サイズが4GBより小さいLOBに対して非推奨の関数を使用する場合、問題は発生しません。

OCIのLOB関数およびBFILE関数

データへのオフセットを伴うすべてのLOB操作では、オフセットは1から始まります。OCILobCopy2()OCILobErase2()OCILobLoadFromFile2()およびOCILobTrim2()などのLOB操作の場合、amountパラメータは、クライアント側のキャラクタ・セットにかかわらず、CLOBNCLOBの文字数です。

これらのLOB操作は、サーバー上のLOBデータの量を参照します。クライアント側のキャラクタ・セットが可変幅の場合は、次の一般的な規則がLOBコールのamountパラメータとoffsetパラメータに適用されます。

  • amount-amountパラメータがサーバー側のLOBを参照する場合、amountは文字数です。amountパラメータがクライアント側のバッファを参照する場合、amountはバイト数です。

  • offset - クライアント側のキャラクタ・セットが可変幅かどうかにかかわらず、offsetパラメータは常にCLOBまたはNCLOBの場合は文字数、BLOBまたはBFILEの場合はバイト数です。

これらの一般的な規則の例外は、特定のLOBコールの説明で示してあります。

LOBの読取り/書込みパフォーマンスの向上について

LOBの読取り/書込みパフォーマンスを向上する方法について説明します。

LOBに対するデータ・インタフェースの使用について

CLOB列の文字データまたはBLOB列のRAWデータをバインドまたは定義できます。

ここでは、複数回のラウンドトリップを必要とする従来のLOBインタフェースとは対照的に、LOBを挿入または選択する際に必要なラウンドトリップは1回のみです。

関連項目:

OCILobGetChunkSize()の使用について

OCILobGetChunkSize()は、利用可能なチャンク・サイズをBLOBCLOBおよびNCLOBに対してバイト数で戻します。

OCILobGetChunkSize()コールを使用すれば、BasicFile LOBに関するLOBの読取りおよび書込み操作のパフォーマンスを改善できます。サイズが利用可能なチャンク・サイズの倍数で、操作がチャンクの境界から始まるBasicFile LOBデータで読取りまたは書込みが行われると、パフォーマンスが向上します。OCILobGetChunkSize()の位置合せを使用して、SecureFile LOBを読取りまたは書込みする必要はありません。

OCILobGetChunkSize()のコールにより、LOBの利用可能なチャンク・サイズが戻されるため、アプリケーションは、同じチャンクに対する複数のLOB書込みコールを発行するかわりに、チャンク全体に対する一連の書込み操作をバッチ処理できます。

OCILobWriteAppend2()の使用について

OCIでは、LOBの末尾へのデータの書込みをより効率的にするためのショートカットが提供されています。

OCILobWriteAppend2()コールにより、最初にOCILobGetLength2()をコールしてOCILobWrite2()操作の開始点を決定しなくても、データをLOBの末尾に追加できます。OCILobWriteAppend2()では、両方のステップが行われます。

OCILobArrayRead()およびOCILobArrayWrite()の使用について

OCILobArrayRead()を使用して、複数のLOBロケータに対してLOBデータを読み取り、OCILobArrayWrite()を使用して、複数のLOBロケータに対してLOBデータを書き込むことにより、パフォーマンスを改善できます。

これら関数は、Oracle Database 10gリリース2で導入され、これらの操作のラウンドトリップ数を減らします。

関連項目:

  • これらの関数をコールバック関数とともにピース単位モードで使用する方法の詳細とコード例は、『Oracle Database SecureFilesおよびラージ・オブジェクト開発者ガイド』の「LOB配列読取り」および「LOB配列書込み」の項を参照してください

  • OCILobArrayRead()

  • OCILobArrayWrite()

LOBバッファリング関数

LOBバッファリング用に提供されている関数について説明します。

OCIには、内部LOB値の小さな読取りと書込みのために、LOBのバッファリングを制御する次のコールがあります。

  • OCILobEnableBuffering()

  • OCILobDisableBuffering()

  • OCILobFlushBuffer()

これらの関数により、内部LOB (BLOBCLOBNCLOB)を使用するアプリケーションでは、クライアント側のバッファで小さな読取りと書込みをバッファできます。これにより、ネットワーク・ラウンドトリップの回数とLOBのバージョンを削減でき、LOBのパフォーマンスが大幅に向上します。

関連項目:

LOBのオープンおよびクローズのための関数

OCIでは、LOBを明示的にオープンするための関数(OCILobOpen())およびLOBをクローズするための関数(OCILobClose())と、LOBがオープンされているかどうかをテストするための関数(OCILobIsOpen())が提供されています。

これらの関数により、一連のLOB操作の開始および終了がマークされるため、LOBのクローズ時に索引の更新など特定の処理が実行できます。

内部LOBでは、オープンされているかどうかの概念は、ロケータではなくLOBに関連しています。ロケータには、LOBの状態に関する情報は格納されません。オープンされている同じLOBを、複数のロケータが指し示すことが可能です。ただし、BFILEについては、オープンは特定のロケータに関連付けられます。したがって、別のロケータを使用して、同じBFILEに対して複数のオープン・コールが実行されることがあります。

アプリケーションで、LOB操作をOCILobOpen()コールとOCILobClose()コールのセットで囲んでいない場合は、LOBが変更されるたびにLOBが暗黙的にオープンおよびクローズされ、それによってLOBの変更に関連付けられているトリガーが起動します。

LOB操作がオープン・コールとクローズ・コールで囲まれていない場合は、LOBの変更時に、LOBの拡張可能な索引がすべて更新されるため、索引は常に有効で、いつでも使用できます。OCILobOpen()コールとOCILobClose()コールで囲まれているLOBが変更された場合、個々のLOBの変更に対してトリガーは起動されません。トリガーはOCILobClose()コールの後にのみ起動されるため、索引が更新されるのはクローズ・コールの後であり、索引はオープン・コールからクローズ・コールまでの間は無効です。OCILobIsOpen()は、内部LOBおよびBFILEsとともに使用できます。

トランザクションでオープンしたすべてのLOBをクローズする前に、そのトランザクションをコミットしようとすると、エラーが発生します。エラーが戻された場合、それ以降LOBはオープンとしてマークは付けられませんが、トランザクションのコミットは正常に行われます。このため、トランザクション内でLOBおよび非LOBデータに対して行われた変更はすべてコミットされますが、ドメイン索引およびファンクション索引は更新されません。これが起こった場合は、LOB列のファンクション索引とドメイン索引を再作成します。

トランザクションがない場合にオープンしているLOBは、セッションの終了前にクローズする必要があります。セッションの終了時にオープンしているLOBがある場合、そのLOBはオープンとしてマークされなくなり、ドメイン索引とファンクション索引は更新されません。これが起こった場合は、LOB列のファンクション索引とドメイン索引を再作成します。

この項には次のトピックが含まれます。「LOBのオープンおよびクローズに関する制限事項」

LOBのオープンおよびクローズに関する制限事項

LOBのオープンおよびクローズに関する制限事項について説明します。

LOBのオープンとクローズに関するメカニズムには、次の制限があります。

  • アプリケーションでは、トランザクションをコミットする前に、以前にオープンしたLOBをすべてクローズする必要があります。これを行わないと、エラーが発生します。トランザクションをロールバックする場合、すべてのオープンLOBは、行われた変更とともに廃棄されます。LOBがクローズされないために、関連付けられたトリガーは起動されません。

  • 内部LOBのオープン数に制限はありませんが、SESSION_MAX_OPEN_FILESパラメータによって決定されるファイルのオープン数には制限があります。すでにオープンしているロケータを別のロケータに割り当てても、新規LOBのオープンとしてはカウントされません。

  • 同じトランザクション内で、別のロケータまたは同じロケータを使用して同じ内部LOBを2度オープンまたはクローズすると、エラーになります。

  • オープンしていないLOBをクローズするとエラーになります。

    注意:

    オープンLOB値をクローズする必要があるトランザクションの定義は、次のいずれかとなります。

    • SET TRANSACTIONCOMMITの間

    • DATA MODIFYING DMLまたはSELECT ... FOR UPDATECOMMITとの間。

    • 自律型トランザクション・ブロック内

関連項目:

LOB読取りおよび書込みコールバック

OCIでは、読取りおよび書込み用コールバック関数をサポートします。

ストリーム転送のコールバック・インタフェース

データの挿入または取出しに使用するユーザー定義の読取りおよび書込み用コールバック関数により、LOBをストリーム転送するためのポーリング・メソッドにかわる方法が提供されます。これらの関数はプログラマが実装し、OCILobRead2()コール、OCILobWriteAppend2()コールおよびOCILobWrite2()コールを通じてOCIに登録します。これらのコールバック関数は、必要に応じてOCIでコールします。

コールバックを使用したLOBの読取り

ユーザー定義の読取りコールバック関数は、OCILobRead2()関数を通じて登録されます。コールバック関数には次のプロトタイプが必要です。

CallbackFunctionName ( void *ctxp, CONST void *bufp, oraub8 len, ub1 piece,
                       void **changed_bufpp, oraub8 *changed_lenp);

最初のパラメータのctxpはコールバックのコンテキストで、OCILobRead2()関数コールでOCIに渡されます。コールバック関数がコールされると、ctxpで提供した情報が戻されます(OCIがINの途中でこの情報を使用することはありません)。OCILobRead2()内のbufpパラメータは、LOBデータが戻される格納場所へのポインタで、buflはこのバッファの長さを表します。これにより、指定したバッファに読み取られたデータの量がわかります。

元のOCILobRead2()コールで提供したバッファの長さが、サーバーから戻されるデータをすべて格納するのに十分でない場合は、ユーザー定義コールバックがコールされます。この場合、pieceパラメータにより、バッファに戻された情報が最初のピース、次のピースまたは最後のピースのどれであるかが示されます。

changed_bufppパラメータとchanged_lenpパラメータは、コールバック関数内で使用すると、バッファを動的に変更できます。changed_bufppパラメータは変更されたバッファのアドレスを指し、changed_lenpパラメータは変更されたバッファの長さを指します。changed_bufppパラメータとchanged_lenpパラメータは、アプリケーションでバッファが動的に変更されない場合、コールバック関数内で使用する必要はありません。

例7-1では、OCILobRead2()関数を使用して、読取りコールバック関数を実装するコード・フラグメントを示しています。loblは前もって選択してある有効なロケータ、svchpは有効なサービス・ハンドル、errhpは有効なエラー・ハンドルであると想定します。この例では、すべてのLOBデータが読み取られるまで、ユーザー定義関数cbk_read_lob()が繰り返しコールされます。

例7-1 OCILobRead2()を使用した読取りコールバック関数の実装

...
oraub8   offset = 1;
oraub8   loblen = 0; 
oraub8   byte_amt = 0;
oraub8   char_amt = 0
ub1      bufp[MAXBUFLEN];

sword retval; 
byte_amtp = 4294967297;     /* 4 gigabytes plus 1 */ 

if (retval = OCILobRead2(svchp, errhp, lobl, &byte_amt, &char_amt, offset,
  (void *) bufp, (oraub8) MAXBUFLEN, (void *) 0, OCI_FIRST_PIECE, 
  cbk_read_lob, (ub2) 0, (ub1) SQLCS_IMPLICIT))
{
     (void) printf("ERROR: OCILobRead2() LOB.\n");
     report_error();
}
...
sb4 cbk_read_lob(ctxp, bufxp, len, piece, changed_bufpp, changed_lenp)
void       *ctxp;
CONST void *bufxp;
oraub8      len;
ub1         piece;
void       **changed_bufpp;
oraub8      *changed_lenp; 
{ 
    static ub4 piece_count = 0; 
    piece_count++; 

    switch (piece)
   {
      case OCI_LAST_PIECE:     /*--- buffer processing code goes here ---*/ 
          (void) printf("callback read the %d th piece\n\n", piece_count);
          piece_count = 0;
          break;
      case OCI_FIRST_PIECE:   /*--- buffer processing code goes here ---*/ 
          (void) printf("callback read the %d th piece\n", piece_count);
          /* --Optional code to set changed_bufpp and changed_lenp if the
              buffer must be changed dynamically --*/
          break;
      case OCI_NEXT_PIECE:   /*--- buffer processing code goes here ---*/
          (void) printf("callback read the %d th piece\n", piece_count);
         /* --Optional code to set changed_bufpp and changed_lenp if the
             buffer must be changed dynamically --*/
          break;
      default:
          (void) printf("callback read error: unknown piece = %d.\n", piece);
           return OCI_ERROR;
     } 
    return OCI_CONTINUE;
}

コールバックを使用したLOBの書込み

読取りコールバックと同様に、ユーザー定義の書込みコールバック関数が、OCILobWrite2()関数を通じて登録されます。コールバック関数には次のプロトタイプが必要です。

CallbackFunctionName ( void *ctxp, void *bufp, oraub8 *lenp, ub1 *piecep,
                        void **changed_bufpp, oraub8 *changed_lenp);

最初のパラメータのctxpはコールバックのコンテキストで、OCILobWrite2()関数コールでOCIに渡されます。OCIでコールバック関数がコールされると、ctxpで提供した情報が戻されます(OCIがINの途中でこの情報を使用することはありません)。bufpパラメータは記憶領域へのポインタで、OCILobWrite2()へのコールで指定されます。

コールで指定するデータをOCILobWrite2()に挿入した後も、残りのデータがユーザー定義コールバックによって挿入されます。コールバックでは、bufpによって示される記憶域に挿入するデータを指定し、またその長さをlenpに指定します。さらに、piecepパラメータを使用して、次のピース(OCI_NEXT_PIECE)か最後のピース(OCI_LAST_PIECE)か示します。アプリケーションで提供される記憶域ポインタにより、記憶域の割当てサイズより大きいデータが書き込まれないことを確認する必要があります。

changed_bufppパラメータとchanged_lenpパラメータは、コールバック関数内で使用すると、バッファを動的に変更できます。changed_bufppパラメータは変更されたバッファのアドレスを指し、changed_lenpパラメータは変更されたバッファの長さを指します。changed_bufppパラメータとchanged_lenpパラメータは、アプリケーションでバッファが動的に変更されない場合、コールバック関数内で使用する必要はありません。

例7-2では、OCILobWrite2()関数を使用して、書込みコールバック関数を実装するコード・フラグメントを示しています。loblは更新用にロックされている有効なロケータ、svchpは有効なサービス・ハンドル、errhpは有効なエラー・ハンドルであると想定しています。ユーザー定義関数cbk_write_lob()は、アプリケーションが最後のピースを提供していることをpiecepパラメータが示すまで繰り返しコールされます。

例7-2 OCILobWrite2()を使用した書込みコールバック関数の実装

...
 
ub1      bufp[MAXBUFLEN];
oraub8   byte_amt = MAXBUFLEN * 20;
oraub8   char_amt = 0;
oraub8   offset = 1;
oraub8   nbytes = MAXBUFLEN; 

/*-- code to fill bufp with data goes here. nbytes should reflect the size and
 should be less than or equal to MAXBUFLEN --*/
if (retval = OCILobWrite2(svchp, errhp, lobl, &byte_amt, &char_amt, offset,
    (void*)bufp, (ub4)nbytes, OCI_FIRST_PIECE, (void *)0, cbk_write_lob, 
    (ub2) 0, (ub1) SQLCS_IMPLICIT)) 
{
   (void) printf("ERROR: OCILobWrite2().\n");
   report_error();
   return;
}
... 
sb4 cbk_write_lob(ctxp, bufxp, lenp,  piecep, changed_bufpp, changed_lenp)
void    *ctxp; 
void    *bufxp; 
oraub8  *lenp; 
ub1       *piecep;
void  **changed_bufpp;
oraub8  *changed_lenp; 
{ 
    /*-- code to fill bufxp with data goes here. *lenp should reflect the
        size and should be less than or equal to MAXBUFLEN -- */
    /* --Optional code to set changed_bufpp and changed_lenp if the
        buffer must be changed dynamically --*/
    if (this is the last data buffer) 
        *piecep = OCI_LAST_PIECE; 
    else
        *piecep = OCI_NEXT_PIECE; 
    return OCI_CONTINUE;
}

一時LOBのサポート

OCIでは、一時LOBの作成および解放のための関数OCILobCreateTemporary()およびOCILobFreeTemporary()と、LOBが一時LOBであるかどうかを判断する関数OCILobIsTemporary()が提供されています。

一時LOBは、データベース内に永続的に格納されることはありませんが、LOBデータを操作するために、ローカル変数のように動作します。標準(永続) LOB上で動作するOCI関数は、一時LOB上でも使用できます。

永続LOBと同様に、すべての関数は一時LOBのロケータ上で動作し、実際のLOBデータにはロケータを介してアクセスされます。

一時LOBロケータは、次の型のSQL文への引数として使用できます。

  • UPDATE: 一時LOBロケータは、NULLかどうかのテスト時にWHERE句内の値として使用するか、あるいは関数へのパラメータとして使用できます。ロケータは、SET句内で使用することもできます。

  • DELETE: 一時LOBロケータは、NULLかどうかのテスト時にWHERE句内で使用するか、あるいは関数へのパラメータとして使用できます。

  • SELECT: 一時LOBロケータは、NULLかどうかのテスト時にWHERE句内で使用するか、あるいは関数へのパラメータとして使用できます。また、一時LOBは、関数の戻り値を選択する場合、SELECT...INTO文で戻される変数として使用することもできます。

注意:

永続ロケータを一時ロケータに選択した場合、一時ロケータは永続ロケータによって上書きされます。この場合、一時LOBは暗黙的には解放されません。ユーザーはSELECT...INTO操作の前に一時LOBを明示的に解放する必要があります。明示的に解放しなければ、一時LOBは指定したdurationの終了時まで解放されません。同じLOBを指し示している別の一時ロケータがなければ、元のロケータはSELECT...INTOによって上書きされるため、その一時LOBを指し示すロケータはなくなります。

一時LOBの作成および解放

OCILobCreateTemporary()関数を使用して一時LOBを作成します。この関数に渡されるパラメータには、LOBの継続時間を示す値が含まれています。デフォルトの継続時間は、カレント・セッションの長さです。継続時間の終了時に、すべての一時LOBが削除されます。ユーザーは、OCILobFreeTemporary()関数で一時LOBを明示的に解放することによって、一時LOBの領域を再生できます。一時LOBは、作成されたときは空の状態です。

一時LOBを作成するとき、その一時LOBがサーバーのバッファ・キャッシュに読み取られるかどうかも指定できます。

一時LOBを永続的にするには、OCILobCopy2()を使用して、データを一時LOBから永続LOBにコピーします。また、一時LOBをINSERT文のVALUES句で使用したり、UPDATE文内の割当てのソースとして使用したり、あるいは一時LOBを通常の永続LOB属性に割り当てて、オブジェクトをフラッシュできます。一時LOBは、標準LOBに使用する関数と同じ関数を使用して変更できます。

注意:

空のLOBを挿入する最も効率的な方法は、値が割り当てられていない一時LOBのバインドです。これは、次のメソッドより使用するリソースが少なくてすみます。

INSERT INTO tab1 VALUES(EMPTY_CLOB())

一時LOBの継続時間

OCIでは、いくつかの一時LOB用の事前定義済継続時間、およびアプリケーションでアプリケーション固有の継続時間を定義するために使用できる関数のセットが用意されています。事前定義済の継続時間とその関連属性は、次のとおりです。

  • コール(OCI_DURATION_CALL)、サーバー側のみ

  • セッション(OCI_DURATION_SESSION)

セッション継続時間は、セッションまたは接続が終了した時点で期限切れになります。コール継続時間は、現行のOCIコールが終了した時点で期限切れになります。

オブジェクト・モードで実行している場合は、アプリケーション固有の継続時間も定義できます。アプリケーション固有の継続時間はユーザー期間とも呼ばれ、OCIDurationBegin()を使用して継続開始時間を指定し、OCIDurationEnd()を使用して継続終了時間を指定することによって定義されます。

注意:

ユーザー定義の継続時間は、アプリケーションがオブジェクト・モードで初期化されている場合にのみ利用できます。

アプリケーション固有の継続時間にはそれぞれ、OCIDurationBegin()で戻される継続時間識別子があり、OCIDurationEnd()がコールされるまで一意であることが保証されます。アプリケーション固有の継続時間は、セッションの継続時間と同じに設定できます。

継続時間の終了時には、その継続時間に関連付けられたすべての一時LOBが解放されます。一時LOBに関連付けられた記述子は、OCIDescriptorFree()コールを使用して明示的に解放する必要があります。

ユーザー継続時間はネストできるため、1つの継続時間を別のユーザー継続時間の子継続時間として定義できます。親の継続時間が子の継続時間を持ち、その子が自分の子の継続時間を持つことも可能です。

注意:

継続時間がOCIDurationBegin()で開始されると、パラメータの1つは親の継続時間の識別子となります。親の継続時間が終了すると、子の継続時間もすべて終了します。

一時LOBの解放について

OCIプログラムがSQLまたはPL/SQLからLOBロケータを取得するたびに、OCILobIsTemporary()関数を使用して、一時ロケータかどうかをチェックします。そうである場合は、ロケータによってアプリケーションが終了される際、OCILobFreeTemporary()コールを使用してロケータを解放します。ロケータは、SELECTまたはOUT BIND中にDEFINEから取得できます。一時LOBの継続期間は、クライアント側に受け渡されるときに、常にセッション継続期間にアップグレードされます。アプリケーションでは、ロケータが次行のロケータで上書きされる前に次の関数を実行する必要があります。

OCILobIsTemporary(env, err, locator, is_temporary);
if(is_temporary)
    OCILobFreeTemporary(svc, err, locator);

ポインタを割り当てる際の注意事項

OCILobLocatorポインタを割り当てるときは、特に注意が必要です。

ポインタ割当てにより、LOBの簡単なコピーが作成されます。ポインタの割当て後は、ソースLOBおよびターゲットLOBはデータの同じコピーを指します。これは、割当てを実行するOCILobAssign()またはOCILobLocatorAssign()などのLOB APIを使用するのとは異なります。APIを使用すると、ロケータは割当て後にデータの個別のコピーを論理的に指します。

一時LOBの場合、ポインタ割当て前に、ターゲットとなるLOBロケータの一時LOBが、OCILobFreeTemporary()によって解放されることを確認する必要があります。OCILobLocatorAssign()を使用すると、割当てが発生する前に、ターゲットLOBロケータ変数の元の一時LOB(ある場合)が解放されます。

SQL文の実行において、OUTバインド変数を再利用する前に、OCILobFreeTemporary()コールを使用して、既存のOUTバインドLOBロケータ・バッファ内の一時LOBを解放する必要があります。

関連項目:

  • 詳細は、『Oracle Database SecureFilesおよびラージ・オブジェクト開発者ガイド』の一時LOBパフォーマンスのガイドラインに関する項を参照してください

  • 一時LOBの最適なパフォーマンスの詳細は、『Oracle Database SecureFilesおよびラージ・オブジェクト開発者ガイド』を参照してください

  • OCILobAssign()

  • OCILobLocatorAssign()

  • OCILobFreeTemporary()

  • OCILobLocatorAssign()

一時LOBの例

一時LOBの使用方法を示します。

例7-3は、一時LOBをどのように使用できるかを示しています。

例7-3 一時LOBの使用方法

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <oci.h>

/* Function Prototype */
static void checkerr (/*_ OCIError *errhp, sword status _*/);
sb4 select_and_createtemp (OCILobLocator *lob_loc,
                           OCIError      *errhp,
                           OCISvcCtx     *svchp,
                           OCIStmt       *stmthp,
                           OCIEnv        *envhp);
/* This function reads in a single video frame from the print_media table.
Then it creates a temporary LOB. The temporary LOB that is created is read
through the CACHE, and is automatically cleaned up at the end of the user's
session, if it is not explicitly freed sooner. This function returns OCI_SUCCESS
if it completes successfully or OCI_ERROR if it fails. */

sb4 select_and_createtemp (OCILobLocator *lob_loc,
                           OCIError      *errhp,
                           OCISvcCtx     *svchp,
                           OCIStmt       *stmthp,
                           OCIEnv        *envhp)
{
  OCIDefine     *defnp1;
  OCIBind       *bndhp;
  text          *sqlstmt;
  int rowind =1;
  ub4 loblen = 0;
  OCILobLocator *tblob;
  printf ("in select_and_createtemp \n");
    if(OCIDescriptorAlloc((void*)envhp, (void **)&tblob,
                          (ub4)OCI_DTYPE_LOB, (size_t)0, (void**)0))
  {
    printf("failed in OCIDescriptor Alloc in select_and_createtemp \n");
    return OCI_ERROR;
 }
  /* arbitrarily select where Clip_ID =1 */
  sqlstmt=(text *)"SELECT Frame FROM print_media WHERE product_ID = 1 FOR UPDATE";
  if (OCIStmtPrepare2(svchp, stmthp, errhp, sqlstmt, (ub4) strlen((char *)sqlstmt),
                     NULL, 0, (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT))
  {
      (void) printf("FAILED: OCIStmtPrepare() sqlstmt\n");
      return OCI_ERROR;
  }
  /* Define for BLOB */
  if (OCIDefineByPos(stmthp, &defnp1, errhp, (ub4)1, (void *) &lob_loc, (sb4)0,
             (ub2) SQLT_BLOB, (void *)0, (ub2 *)0, (ub2 *)0, (ub4) OCI_DEFAULT))
  {
    (void) printf("FAILED: Select locator: OCIDefineByPos()\n");
    return OCI_ERROR;
  }
  /* Execute the select and fetch one row */
  if (OCIStmtExecute(svchp, stmthp, errhp, (ub4) 1, (ub4) 0,
                   (CONST OCISnapshot*) 0, (OCISnapshot*) 0, (ub4) OCI_DEFAULT))
  {
    (void) printf("FAILED: OCIStmtExecute() sqlstmt\n");
    return OCI_ERROR;
  }
  if(OCILobCreateTemporary(svchp, errhp, tblob, (ub2)0, SQLCS_IMPLICIT,
                         OCI_TEMP_BLOB, OCI_ATTR_NOCACHE, OCI_DURATION_SESSION))
  {
    (void) printf("FAILED: CreateTemporary() \n");
    return OCI_ERROR;
  }
  if (OCILobGetLength(svchp, errhp, lob_loc, &loblen) != OCI_SUCCESS)
  {
    printf("OCILobGetLength FAILED\n");
    return OCI_ERROR;
  }
  if (OCILobCopy(svchp, errhp, tblob,lob_loc,(ub4)loblen, (ub4) 1, (ub4) 1))
  {
    printf( "OCILobCopy FAILED \n");
  }
  if(OCILobFreeTemporary(svchp,errhp,tblob))
  {
    printf ("FAILED: OCILobFreeTemporary call \n");
    return OCI_ERROR;
  }
     return OCI_SUCCESS;
}
int main(char *argv, int argc)
{
  /* OCI Handles */
  OCIEnv        *envhp;
  OCIServer     *srvhp;
  OCISvcCtx     *svchp;
  OCIError      *errhp;
  OCISession    *authp;
  OCIStmt       *stmthp;
  OCILobLocator *clob, *blob;
  OCILobLocator *lob_loc;
  int type =1;
  /* Initialize and Log on */
  OCIEnvCreate(&envhp, OCI_DEFAULT, (void *)0, 0, 0, 0,
        (size_t)0, (void *)0);
  (void) OCIHandleAlloc( (void *) envhp, (void **) &errhp, OCI_HTYPE_ERROR,
                          (size_t) 0, (void **) 0);
  /* server contexts */
  (void) OCIHandleAlloc( (void *) envhp, (void **) &srvhp, OCI_HTYPE_SERVER,
                         (size_t) 0, (void **) 0);
   /* service context */
  (void) OCIHandleAlloc( (void *) envhp, (void **) &svchp, OCI_HTYPE_SVCCTX,
                         (size_t) 0, (void **) 0);
    /* attach to Oracle Database */
  (void) OCIServerAttach( srvhp, errhp, (text *)"", strlen(""), 0);
    /* set attribute server context in the service context */
  (void) OCIAttrSet ((void *) svchp, OCI_HTYPE_SVCCTX,
                     (void *)srvhp, (ub4) 0,
                     OCI_ATTR_SERVER, (OCIError *) errhp);
   (void) OCIHandleAlloc((void *) envhp,
                        (void **)&authp, (ub4) OCI_HTYPE_SESSION,
                        (size_t) 0, (void **) 0);
   (void) OCIAttrSet((void *) authp, (ub4) OCI_HTYPE_SESSION,
                    (void *) "scott", (ub4)5,
                    (ub4) OCI_ATTR_USERNAME, errhp);
  (void) OCIAttrSet((void *) authp, (ub4) OCI_HTYPE_SESSION,
                    (void *) "password", (ub4) 5,
                    (ub4) OCI_ATTR_PASSWORD, errhp);
  /* Begin a User Session */
  checkerr(errhp, OCISessionBegin ( svchp,  errhp, authp, OCI_CRED_RDBMS,
                                    (ub4) OCI_DEFAULT));
  (void) OCIAttrSet((void *) svchp, (ub4) OCI_HTYPE_SVCCTX,
                    (void *) authp, (ub4) 0,
                    (ub4) OCI_ATTR_SESSION, errhp);
  /* ------- Done logging in ----------------------------------*/
   /* allocate a statement handle */
  checkerr(errhp, OCIHandleAlloc( (void *) envhp, (void **) &stmthp,
                             OCI_HTYPE_STMT, (size_t) 0, (void **) 0));
  checkerr(errhp, OCIDescriptorAlloc((void *)envhp, (void **)&lob_loc,
                   (ub4) OCI_DTYPE_LOB, (size_t) 0, (void **) 0));
  /* Subroutine calls begin here */
  printf("calling select_and_createtemp\n");
  select_and_createtemp (lob_loc, errhp, svchp,stmthp,envhp);
  return 0;
}
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((void *)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;
  }
}

LOBデータ、長さおよびチャンク・サイズのプリフェッチ

小さいLOBのOCIアクセスを改善するために、LOBデータをプリフェッチしてキャッシュする間にロケータもフェッチできます。

これは内部LOB、一時LOBおよびBFILEに適用されます。アプリケーションを準備するには、次のステップに従います。

  1. セッション・ハンドルのOCI_ATTR_DEFAULT_LOBPREFETCH_SIZE属性を設定します。この属性の値は、LOBロケータのデフォルトのプリフェッチ・データ・サイズを示します。この属性値により、セッション中にフェッチされる全LOBロケータのプリフェッチが可能になります。この属性のデフォルト値は0 (LOBデータのプリフェッチなし)です。このオプションにより、アプリケーション開発者は定義ハンドルごとにプリフェッチLOBサイズを設定する必要がなくなります。この属性を設定するか、または(ステップ3で)OCI_ATTR_LOBPREFETCH_SIZEを設定することができます。
  2. 実行する文の準備ステップと定義ステップを実行します。
  3. 必要な場合は、定義ハンドルのOCI_ATTR_LOBPREFETCH_SIZE属性を設定して、フェッチするLOBロケータのデフォルト・プリフェッチ・サイズをオーバーライドできます。このオプション属性では、特定の列からフェッチされるロケータのプリフェッチ・サイズを制御できます。
  4. OCI_ATTR_LOBPREFETCH_LENGTH属性を、プリフェッチするLOBの長さおよびチャンク・サイズに設定します。これは、ステップ1で説明されている属性(OCI_ATTR_LOBPREFETCH_SIZE)が機能するために、必須としてTRUEに設定されます。
  5. 文を実行します。
  6. 個別のLOBロケータを指定してOCILobRead2()またはOCILobArrayRead()をコールします。OCIによりプリフェッチ・バッファからデータが取得され、必要な文字変換が実行され、データがLOB読取りバッファにコピーされます(LOBセマンティクスは変更されません)。要求されたデータがプリフェッチ・バッファより大きい場合は、追加のラウンドトリップが必要になります。
  7. OCILobGetLength2()およびOCILobGetChunkSize()をコールして、サーバーへのラウンドトリップなしで長さとチャンク・サイズを取得します。

例7-4 LOBデータ、長さおよびチャンク・サイズのプリフェッチ

...
ub4 default_lobprefetch_size = 2000;                  /* Set default size to 2K */
... 
/* set LOB prefetch attribute to session */
OCIAttrSet (sesshp, (ub4) OCI_HTYPE_SESSION,
            (void *)&default_lobprefetch_size,               /* attribute value */
            0,                      /* attribute size; not required to specify; */
            (ub4) OCI_ATTR_DEFAULT_LOBPREFETCH_SIZE,
            errhp);
...
/* select statement */
char *stmt = "SELECT lob1 FROM lob_table";
...
/* declare and allocate LOB locator */
OCILobLocator * lob_locator;
lob_locator = OCIDescriptorAlloc(..., OCI_DTYPE_LOB, ...);
 
OCIDefineByPos(..., 1, (void *) &lob_locator, ..., SQLT_CLOB, ...);
...
/* Override the default prefetch size to 4KB */
ub4 prefetch_size = 4000;
OCIAttrSet (defhp,  OCI_HTYPE_DEFINE,
            (void *) &prefetch_size                             /* attr value */,
            0                  /* restricting prefetch size to be ub4 max val */,
            OCI_ATTR_LOBPREFETCH_SIZE                            /* attr type */,
            errhp);
...
/* Set prefetch length attribute */
boolean prefetch_length = TRUE;
OCIAttrSet( defhp,  OCI_HTYPE_DEFINE,
            (dvoid *) &prefetch_length  /* attr value */,
            0,
            OCI_ATTR_LOBPREFETCH_LENGTH  /* attr type */,
            errhp );
...
/* execute the statement. 4KB of data for the LOB is read and
 * cached in descriptor cache buffer.
 */
OCIStmtExecute (svchp, stmthp, errhp,
                1,               /* iters */
                0,               /* row offset */
                NULL,            /* snapshot IN */
                NULL,            /* snapshot out */
                OCI_DEFAULT);    /* mode */
...
oraub8 char_amtp = 4000;
oraub8 lob_len;
ub4 chunk_size;
 
/* LOB chunk size, length, and data are read from cache. No round-trip. */
 
OCILobGetChunkSize (svchp, errhp, lob_locator, &chunk_size);
 
OCILobGetLength2(svchp, errhp, lob_locator, &lob_len );
 
OCILobRead2(svchp, errhp, lob_locator, NULL, &char_amtp, ...);
...

プリフェッチ・サイズは、BLOBおよびBFILEの場合はバイト数、CLOBの場合は文字数であることに注意してください。

例7-4では、これらのステップを説明するコード・フラグメントを示しています。

プリフェッチ・キャッシュ割当て: 記述子用のプリフェッチ・キャッシュ・バッファは、LOBロケータをフェッチするときに割り当てられます。割り当てられるバッファ・サイズは、定義ハンドルのOCI_ATTR_LOBPREFETCH_SIZE属性によって決定され、この属性のデフォルト値は、セッション・ハンドルのOCI_ATTR_DEFAULT_LOBPREFETCH_SIZE属性値によって示されます。キャッシュ・バッファがすでに割り当てられている場合は、必要に応じてサイズ変更します。

次の2つのLOB APIの場合、ソース・ロケータにキャッシュされたデータがあると、宛先ロケータのキャッシュが割り当てられるかサイズ変更され、キャッシュ・データがソースから宛先にコピーされます。

記述子用に割り当てられたキャッシュ・バッファ・メモリーは、記述子自体が解放されるときに解放されます。

プリフェッチ・キャッシュの無効化: 記述子用のキャッシュは、ロケータを使用してLOBデータが更新されるときに無効化されます。これは、そのキャッシュがデータの読取りに使用されなくなり、ロケータでの次のOCILobRead2()コールでラウンドトリップが発生することを意味します。

次のLOB APIにより、使用された記述子用のプリフェッチ・キャッシュが無効化されます。

次のLOB APIにより、宛先LOBロケータ用のキャッシュが無効化されます。

パフォーマンス・チューニング: プリフェッチ・バッファのサイズは、平均LOBサイズとクライアント側のメモリーに基づいて決定する必要があります。大量のデータがプリフェッチされる場合は、メモリーの可用性を確認する必要があります。データ・フェッチに伴うコストの方がサーバーへのラウンドトリップに伴うコストよりも大きいため、大きいLOBをプリフェッチする場合には、大幅なパフォーマンス向上は期待できない可能性があります。

このLOBプリフェッチ機能を活用できるように、LOBデータ・サイズを適正に考慮する必要があります。パラメータはアプリケーション設計の一部であるため、パラメータ値の変更が必要になれば、アプリケーションを再作成する必要があります。

アップグレード: リリース11.1より前のサーバー、リリース11.1以降のサーバーに対するリリース11.1より前のクライアントには、LOBプリフェッチを使用できません。リリース11.1より前のサーバーをリリース11.1以降のクライアントに対して使用している場合、OCIAttrSet()からエラー、または、この機能がサーバーでサポートされていないという旨のエラー・メッセージが戻されます。

SecureFiles LOBのオプション

SecureFiles (Oracle Database 11gリリース1で導入されたSTORE AS SECUREFILEオプションを使用するLOB)の場合、CREATE TABLEおよびALTER TABLE文にSQLパラメータDEDUPLICATEを指定できます。

このパラメータにより、LOB列の複数行で同一のLOBデータが同じデータ・ブロックを共有するように指定して、ディスク領域を節約できます。この機能をオフにするには、KEEP_DUPLICATESを使用します。次の各オプションも、SECUREFILEとともに使用されます。

パラメータCOMPRESSでは、LOB圧縮をオンにします。NOCOMPRESSでは、LOB圧縮をオフにします。

パラメータENCRYPTでは、LOB暗号化をオンにして、オプションで暗号化アルゴリズムを選択します。NOENCRYPTでは、LOB暗号化をオフにします。各LOB列には、他のLOB列または非LOB列の暗号化に関係なく、独自の暗号化仕様を設定できます。有効なアルゴリズムは、3DES168AES128AES192およびAES256です。

リリース11.1より前に使用されていたLOBパラタイムがデフォルトです。このデフォルトのLOBパラダイムは、オプションSTORE AS BASICFILEでも明示的に設定されます。

次のOCI関数が、SECUREFILE機能で使用されます。

  • OCILobGetOptions()

  • OCILobSetOptions()

  • OCILobGetContentType()

  • OCILobSetContentType()

関連項目: