5.3 BFILE API

この項では、BFILEでサポートされる様々な操作について説明します。

BFILENAME関数または同等のAPIを使用するか、BFILE列に対してSELECT操作を使用してBFILE変数を初期化した後は、DBMS_LOBなどのAPIを使用してBFILEに対して読取り操作を実行できます。BFILEは読取り専用データ型であることに注意してください。そのため、BFILE APIを介してBFILEを使用してアクセスされるオペレーティング・システム・ファイルを更新または削除することはできません。

BFILEで実行される操作は、次のカテゴリに分かれています。

表5-1 BFILEに対する操作

分類 操作 DBMS_LOBパッケージの関数またはプロシージャの例
健全性チェック サーバーにBFILEが存在するかどうかをチェック FILEEXISITS
DIRECTORYオブジェクト名とファイル名を取得 FILEGETNAME
ディレクトリまたはファイルが存在するかどうかをチェックせずに、ロケータにBFILEの名前を設定 BFILENAME
オープンとクローズ ファイルをオープン OPEN
入力BFILEロケータを使用してファイルがオープンされたかどうかをチェック ISOPEN
ファイルをクローズ CLOSE
オープンされているすべてのFILEをクローズします。 FILECLOSEALL
読取り操作 BFILEの長さの取得 GETLENGTH
指定されたオフセットからBFILEのデータの読取り READ
SUBSTRを使用して、指定されたオフセット以降のBFILE値の一部を返す SUBSTR
INSTRを使用して、BFILE内のパターンの一致位置を返す INSTR
複数のロケータを含む操作 BFILEロケータsrcBFILEロケータdstへの割当て dst := src
BFILEデータのLOBへのロード LOADCLOBFROMFILE、LOADBLOBFROMFILE
2つのBFILEの値の全体または一部を比較 COMPARE

5.3.1 健全性チェック

BFILEに対して健全性チェック関数を使用すると、BFILEに関する情報を取得できます。

BFILENAME()およびOCILobFileSetName()関数では、指定したディレクトリおよびパス名が実際に存在するかどうかは検証されないことに注意してください。健全性チェック機能を使用して、BFILEが存在することを確認し、BFILEロケータからディレクトリおよびファイル名を抽出できます。

5.3.2 BFILEのオープンおよびクローズ

操作を実行する前にBFILEOPENし、プログラムを終了する前にCLOSEする必要があります。

BFILEロケータの動作は、ほとんどの従来のプログラミング言語の標準入出力ライブラリの一部として使用可能なファイル記述子と同様です。BFILEロケータを定義および初期化し、このロケータによって参照されるファイルをオープンした場合、ファイルをクローズするまでのすべての操作は、ロケータまたはファイルのローカル・コピーを使用して、同じプログラム・ブロック内から実行する必要があることを意味します。BFILEロケータ変数は、他のプロシージャ、メンバー・メソッドまたは外部関数コールアウトのパラメータとして使用できます。ただし、ファイルのオープンおよびクローズは、同じネスト・レベルにある、同じプログラム・ブロックから行うことをお薦めします。

アプリケーションに例外が発生した、または異常終了した場合も、オープンしているすべてのBFILEインスタンスをクローズする必要があります。このような場合にBFILEインスタンスをクローズしないと、データベースでオープン状態であるとみなされます。このような場合にBFILEインスタンスがオープン状態のままにならないような例外処理方法を使用していることを確認してください。

DBMS_LOB.FILECLOSEALLOCILobFileCloseAll()などのプロシージャを使用して、オープンしているすべてのBFILEをまとめてクローズできます。

5.3.3 BFILEからの読取り

BFILEデータの長さの読取り、データの一部の読取り、データ全体の読取りなど、様々な読取り操作を実行できます。

大量のBFILEから読み取る場合は、JDBCまたはOCIでストリーム読取りモードを使用できます。JDBCでは、getBinaryStream()メソッドを使用してこれを実現できます。OCIでは、次の項で説明される方法に従って実行できます。

OCIでのストリーム読取り

大量のBFILEデータを最も効率よく読み取るには、ストリーム・メカニズムを有効にしたOCILobRead2()関数をポーリングまたはコールバックを使用します。そのためには、次のようにoffsetパラメータを使用して読取り開始位置を指定します。

ub8  char_amt =  0;
ub8  byte_amt =  0;
ub4  offset = 1000;

OCILobRead2(svchp, errhp, locp, &byte_amt, &char_amt, offset, bufp, bufl,
            OCI_ONE_PIECE, 0, 0, 0, 0);

ポーリング・モードを使用した場合は、各OCILobRead2()コール後にbyte_amtパラメータの値を調べて、バッファに何バイト読み取られたかを確認してください。これは、バッファがいっぱいになっていない場合があるためです。

コールバックを使用する場合、lenpパラメータはコールバックへの入力で、バッファ内で格納されたバイト数を示します。バッファの空き領域を確認するため、コールバック処理中にlenpパラメータを必ずチェックしてください。

量パラメータ

  • DBMS_LOB.READ APIをコールする場合、amountパラメータのサイズはデータのサイズより大きくできます。ただし、このパラメータはバッファのサイズ以下にする必要があります。PL/SQLで、バッファサイズの上限は32Kです。
  • OCILobRead2()関数をコールする場合は、byte_amtパラメータに値UB8MAXVALを渡して、BFILEの終わりまで読み取ることができます。

5.3.4 複数のBFILEロケータの操作

BFILE操作は、2つのロケータのうちの少なくとも1つがBFILEロケータである2つのロケータを受け入れます。BFILEを含む割当ておよび比較操作では、両方のロケータがBFILE型である必要があります。

LOBへのBFILEデータのロードには、次の各項で説明する特別な考慮事項が含まれます。

LOBへのBFILEデータのロード

PLSQLでは、DBMS_LOB.LOADBLOBFROMFILEおよびDBMS_LOB.LOADCLOBFROMFILEが設定されている場合、DBMS_LOB.LOADFROMFILEプロシージャは非推奨になります。具体的には、DBMS_LOB.LOADCLOBFROMFILEプロシージャを使用してCLOBまたはNCLOBインスタンスをロードすると、文字セット変換が実行されます。

BFILEデータのロード量の指定

次の表に示す関数に対する量パラメータでは、次のいずれかの値を渡す必要があります。

  • ロードするBFILEの実際のサイズ(バイト単位)以下の量。
  • 最大許容LOBサイズ(バイトで表されます)。この値を渡すと、BFILE全体がロードされます。この方法を使用すると、ロード前にBFILEのサイズを確認せずにBFILE全体をロードできます。最大許容LOBサイズを取得するには、次の表で説明する方法を使用します。

表5-2 ファイルからのロード操作用の最大LOBサイズ

環境 関数 最大LOBサイズを渡すために取得する値
DBMS_LOB DBMS_LOB.LOADBLOBFROMFILE DBMS_LOB.LOBMAXSIZE
DBMS_LOB DBMS_LOB.LOADCLOBFROMFILE DBMS_LOB.LOBMAXSIZE
OCI OCILobLoadFromFile2() UB8MAXVAL
OCI OCILobLoadFromFile()(LOBのサイズが4GB未満の場合) UB4MAXVAL

BFILEデータを使用したBLOBのロード

DBMS_LOB.LOADBLOBFROMFILEプロシージャは、BLOBBFILEのデータをロードします。これを使用して、任意の永続または一時BLOBインスタンスにデータをロードできます。このプロシージャは、BLOBの新しいソースおよび宛先オフセットを戻し、ループで使用するときに後続のコールに渡すことができます。

CLOBへのBFILEデータのロード

DBMS_LOB.LOADCLOBFROMFILEプロシージャは、CLOBまたはNCLOBBFILEの文字データをロードします。これを使用すると、永続または一時的なCLOBまたはNCLOBインスタンスにデータをロードできます。このプロシージャのコール時にBFILE文字セットIDを指定し、キャラクタ・セットがBFILEデータの文字セットから宛先のCLOBまたはNCLOB文字セットに正しく変換されていることを確認できます。このプロシージャは、CLOBまたはNCLOBの新しいソースおよび宛先オフセットを戻し、ループで使用する場合は後続のコールに渡せます。

次の項目の例を示します。
  • default csid(0)の使用方法。
  • BFILEに対してgetlengthをコールせずに、ファイル全体をロードする方法。
  • 戻されたオフセット値を使用して、実際にロードしたデータ量を計算する方法。

この例では、ad_sourceUTF8文字セット・フォーマットのBFILEで、データベースの文字セットがUTF8であることを想定しています。

CREATE OR REPLACE PROCEDURE loadCLOB1_proc (dst_loc IN OUT CLOB) IS
  src_loc     BFILE := BFILENAME('MEDIA_DIR','monitor_3060.txt') ;
  amt         NUMBER := DBMS_LOB.LOBMAXSIZE;
  src_offset  NUMBER := 1 ;
  dst_offset  NUMBER := 1 ;
  lang_ctx    NUMBER := DBMS_LOB.DEFAULT_LANG_CTX;
  warning     NUMBER;
BEGIN
  DBMS_OUTPUT.PUT_LINE('------------ LOB LOADCLOBFORMFILE EXAMPLE ------------');
  DBMS_LOB.FILEOPEN(src_loc, DBMS_LOB.FILE_READONLY);

  /* The default_csid can be used when the BFILE encoding is in the same charset
   * as the destination CLOB/NCLOB charset
   */
   DBMS_LOB.LOADCLOBFROMFILE(dst_loc,src_loc, amt, dst_offset, src_offset,       
       DBMS_LOB.DEFAULT_CSID, lang_ctx,warning) ;
  DBMS_OUTPUT.PUT_LINE(' Amount specified ' || amt ) ;
  DBMS_OUTPUT.PUT_LINE(' Number of bytes read from source: ' || (src_offset-1));
  DBMS_OUTPUT.PUT_LINE(' Number of characters written to destination: ' ||(dst_offset-1) );
  IF (warning = DBMS_LOB.WARN_INCONVERTIBLE_CHAR)
  THEN
    DBMS_OUTPUT.PUT_LINE('Warning: Inconvertible character');
  END IF;
  DBMS_LOB.FILECLOSEALL() ;
END;
/
次の項目の例を示します。
  • NLS_CHARSET_IDファンクションを使用して、文字セット名から文字セットIDを取得する方法。
  • 戻されたオフセット値および言語コンテキストlang_ctxを使用して、1つのBFILEから複数のLOBにストリームのデータをロードする方法。
  • 警告メッセージの読取り方法

この例では、ad_file_ext_01JA16TSTSETフォーマットのBFILEで、データベースの各国語文字セットがAL16UTF16であることを想定しています。

CREATE OR REPLACE PROCEDURE loadCLOB2_proc (dst_loc1 IN OUT NCLOB,dst_loc2 IN OUT NCLOB) IS
  src_loc     BFILE := BFILENAME('MEDIA_DIR','monitor_3060.txt');
  amt         NUMBER := 100;
  src_offset  NUMBER := 1;
  dst_offset  NUMBER := 1;
  src_osin    NUMBER;
  cs_id       NUMBER := NLS_CHARSET_ID('JA16TSTSET'); /* 998 */
  lang_ctx    NUMBER := dbms_lob.default_lang_ctx;
  warning     NUMBER;
BEGIN
  DBMS_OUTPUT.PUT_LINE('------------ LOB LOADCLOBFORMFILE EXAMPLE ------------');
  DBMS_LOB.FILEOPEN(src_loc, DBMS_LOB.FILE_READONLY);
  DBMS_OUTPUT.PUT_LINE(' BFILE csid is ' || cs_id) ;

  /* Load the first 1KB of the BFILE into dst_loc1 */

  DBMS_OUTPUT.PUT_LINE(' ----------------------------' ) ;
  DBMS_OUTPUT.PUT_LINE('   First load  ' ) ;
  DBMS_OUTPUT.PUT_LINE(' ----------------------------' ) ;

  DBMS_LOB.LOADCLOBFROMFILE(dst_loc1, src_loc, amt, dst_offset, src_offset,
      cs_id, lang_ctx, warning);

  /* the number bytes read may or may not be 1k */

  DBMS_OUTPUT.PUT_LINE(' Amount specified ' || amt ) ;
  DBMS_OUTPUT.PUT_LINE(' Number of bytes read from source: ' ||
      (src_offset-1));
  DBMS_OUTPUT.PUT_LINE(' Number of characters written to destination: ' ||
      (dst_offset-1) );
  if (warning = dbms_lob.warn_inconvertible_char)
  then
    DBMS_OUTPUT.PUT_LINE('Warning: Inconvertible character');
  end if;

  /* load the next 1KB of the BFILE into the dst_loc2 */

  DBMS_OUTPUT.PUT_LINE(' ----------------------------' ) ;
  DBMS_OUTPUT.PUT_LINE('   Second load  ' ) ;
  DBMS_OUTPUT.PUT_LINE(' ----------------------------' ) ;

  /* Notice we are using the src_offset and lang_ctx returned from the previous
   * load. We do not use value 1001 as the src_offset here because sometimes the
   * actual amount read may not be the same as the amount specified.
   */

  src_osin := src_offset;
  dst_offset := 1;
  DBMS_LOB.LOADCLOBFROMFILE(dst_loc2, src_loc, amt, dst_offset, src_offset,
      cs_id, lang_ctx, warning);
  DBMS_OUTPUT.PUT_LINE(' Number of bytes read from source: ' ||
      (src_offset-src_osin) );
  DBMS_OUTPUT.PUT_LINE(' Number of characters written to destination: ' ||
      (dst_offset-1) );
  if (warning = DBMS_LOB.WARN_INCONVERTIBLE_CHAR)
  then
    DBMS_OUTPUT.PUT_LINE('Warning: Inconvertible character');
  end if;
  DBMS_LOB.FILECLOSEALL() ;

END;
/