6 マルチメディア・データ型の使用

マルチメディア・データ型は、Oracleデータベースでラージ・オブジェクト(LOB)として表されます。データ・カートリッジではPL/SQLおよびOCIでマルチメディア・データ型を実装できます。

6.1 カートリッジとマルチメディア・データ型の概要

一部のデータ・カートリッジでは、グラフィック・イメージやサウンド波形などのRAWバイナリ・データ、またはテキストや数値ストリームなどの文字データを大量に処理する必要があります。Oracleは、この種のデータを処理するためのラージ・オブジェクト(LOB)をサポートしています。

  • 内部LOBは、領域の最適化と効率的なアクセスを実現する方法でデータベース表領域に格納されます。内部LOBはサーバーのトランザクション・モデルに関与します。

    内部LOBには、バイナリ・データ(BLOB)、シングルバイト文字データ(CLOB)、固定幅のシングルバイトまたはマルチバイト文字データ(NCLOB)を格納できます。NCLOBは、Oracleデータベースに対して定義済の各国語キャラクタ・セットに対応する文字データで構成されます。可変幅の文字データは、Oracleではサポートされていません。

  • 外部LOBは、データベース表領域外部のオペレーティング・システム・ファイルにBFILE(バイナリ・データ)として格納されます。これらは、トランザクションに関与できません。

内部LOBBFILEはともに、大量データの処理の柔軟性を大きく高めます。

LOBに格納されるデータは、LOBと呼ばれます。Oracleサーバーにとって、LOB値は構造化されておらず、問合せできません。LOB値を解凍し、カートリッジ固有の方法で解析する必要があります。

LOBは、Oracle Call Interface(OCI)またはPL/SQLのDBMS_LOBパッケージを使用して操作できます。LOBの各部分を操作するファンクション(LOBを格納できるオブジェクト型に対するメソッドなど)を記述できます。

関連項目:

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

6.2 LOBに対するDDLの使用

LOB定義には、CREATE TYPEおよびCREATE TABLE文を使用できます。例6-1では、lob_typeというデータ型の中でCLOBを指定します。

例6-2では、各行がlob_tableデータのインスタンスであるオブジェクト表lob_tableを作成します。

例6-3では、LOB例6-2のようにオブジェクト表に格納するのではなく、標準の表に格納します。

表にLOBを作成するときに、LOBの格納、バッファリングおよびキャッシュ・プロパティを設定できます。

関連項目:

CREATE TABLEALTER TABLECREATE TYPEALTER TYPEの各文でのLOBの使用方法については、Oracle Database SQL言語リファレンスおよびOracle Database SecureFilesおよびラージ・オブジェクト開発者ガイドを参照してください。

6.2.1 型のLOB属性の作成

例6-1 型のCLOB属性の作成

CREATE OR REPLACE TYPE lob_type AS OBJECT ( 
  id  INTEGER, 
  data CLOB );

6.2.2 LOBオブジェクト表の作成

例6-2 LOBオブジェクト表の作成

CREATE TABLE lob_table OF lob_type;

6.2.3 LOB列の作成

例6-3 表のLOB列の作成

CREATE TABLE lob_table1  (
  id  INTEGER,
  b_lob   BLOB,
  c_lob   CLOB,
  nc_lob  NCLOB,
  b_file  BFILE );

6.3 LOBロケータ

LOBは、他の行データとともに、または行データとは別個に格納できます。格納場所に関係なく、各LOBには実際の位置へのハンドルまたはポインタとして表示できるロケータがあります。LOBを選択すると、LOB値のかわりにLOBロケータが戻されます。例6-4では、b_lobLOBロケータを選択してPL/SQLローカル変数image1に入れます。

APIファンクションを使用してLOB値を操作する場合は、ロケータを使用してLOBを参照します。PL/SQLのDBMS_LOBパッケージには、例6-5に示すPUT_LINE()GETLENGTH()のようなLOBの操作に役立つルーチンが含まれています。

OCIでは、LOBロケータはOCILobLocator *などのLOBLocatorPointersにマップされます。

BFILEの場合は、LOB列に独自の個別ロケータがあり、このロケータでサーバーのファイル・システムにある外部ファイルに格納されたLOB値を参照します。これは、BFILE列を含む表の2行で同じファイルまたは2つの個別ファイルを参照できることを意味します。PL/SQLまたはOCIプログラムにおけるBFILEロケータ変数の動作は、他の自動変数と同様です。ファイル操作の場合の動作は、ほとんどの従来型プログラム言語の標準I/Oライブラリの一部として使用可能なファイル・ディスクリプタと同様です。

6.3.1 LOBの選択とローカル変数への割当て

例6-4 LOBロケータの選択とローカル変数への割当て

DECLARE
       image1  BLOB;
       image_no  INTEGER := 101;
BEGIN
       SELECT b_lob  INTO image1 FROM lob_table
                  WHERE key_value = image_no;
             ...
END;

6.3.2 LOBの操作

例6-5 PUT_LINEとGETLENGTHによるLOBの操作

BEGIN
     DBMS_OUTPUT.PUT_LINE('Size of the Image is: ', 
                       DBMS_LOB.GETLENGTH(image1));
END;

6.4 LOBを空にする

特殊ファンクションEMPTY_BLOBEMPTY_CLOBをSQL DMLのINSERT文またはUPDATE文で使用すると、NULLまたはNULL以外の内部LOBを空に初期化できます。これらは、Oracle SQL DMLで特殊ファンクションとして使用可能であり、DBMS_LOBパッケージには含まれていません。

OCIまたはDBMS_LOBパッケージを使用して内部LOBにデータを書き込む前に、LOB列を非NULLにする必要があります。つまり、空または移入済のLOB値を指すロケータが含まれている必要があります。BLOB列の値を空に初期化するには、INSERT文のVALUES句でファンクションEMPTY_BLOBを使用します。同様に、CLOB列またはNCLOB列の値を初期化するには、EMPTY_CLOB()ファンクションを使用します。これらのファンクションの構文は次のとおりです。

FUNCTION EMPTY_BLOB() RETURN BLOB;
FUNCTION EMPTY_CLOB() RETURN CLOB;

EMPTY_BLOBBLOB型の空のロケータを戻し、EMPTY_CLOBCLOB型の空のロケータを戻します。これはNCLOBにも使用できます。これらのファンクションにプラグマは関連付けられていません。

例外が発生するのは、これらのファンクションをSQL INSERT文のVALUES句のどこかで使用するか、SQL UPDATE文のSET句のソースとして使用した場合です。

例6-6では、SQL DMLでEMPTY_BLOB()を使用します。

例6-7では、PL/SQLプログラムでのEMPTY_CLOB()の使用方法を示します。

6.4.1 SQLでのEMPTY_BLOB()の使用

例6-6 SQL DMLでのEMPTY_BLOB()の使用

INSERT INTO lob_table VALUES (1001, EMPTY_BLOB(), 'abcde', NULL);
UPDATE lob_table SET c_lob = EMPTY_CLOB() WHERE key_value = 1001;
INSERT INTO lob_table VALUES (1002, NULL, NULL, NULL);

6.4.2 PL/SQLでのEMPTY_CLOB()の使用

例6-7 PL/SQLプログラムでのEMPTY_CLOB()の使用

DECLARE 
  lobb         CLOB; 
  read_offset  INTEGER; 
  read_amount  INTEGER; 
  rawbuf       RAW(20); 
  charbuf      VARCHAR2(20);
BEGIN
  read_amount := 10; read_offset := 1;
  UPDATE lob_table SET c_lob = EMPTY_CLOB() 
  WHERE key_value = 1002 RETURNING c_lob INTO lobb;
  dbms_lob.read(lobb, read_amount, read_offset, charbuf); 
  dbms_output.put_line('lobb value: ' || charbuf);
END

6.5 OCIによるLOBの操作

OCIには、BLOBCLOBNCLOBおよびBFILEに格納されたデータへのアクセスに使用できるファンクションが組み込まれています。これらのファンクションは表6-1で説明しています。

表6-2では、LOBアクセスに関してOCIインタフェースとPL/SQL (DBMS_LOBパッケージ)インタフェースを比較します。

例6-8では、データベースからロケータへのLOBの選択方法を示します。lob_type型にはINTEGER型のidCLOB型のdataの2つの属性があり、lob_type型のlob_table表が存在しているとみなします。

サンプル・プログラムpopulate.cでは、OCIを使用してCLOBにファイルの内容が移入されます。

関連項目:

パラメータ、パラメータの型、戻り値、コード例などの詳細は、Oracle Call Interfaceプログラマーズ・ガイドを参照してください。

6.5.1 OCIのLOB操作ファンクション

表6-1 OCIのLOB操作ファンクションの概要

ファンクション 説明
OCILobAppend()

LOB値を別のLOBに追加します。

OCILobArrayRead()

1回のラウンドトリップで複数のロケータのLOBデータを読み取ります。

OCILobArrayWrite()

1回のラウンドトリップで複数のロケータのLOBデータを書き込みます。

OCILobAssign()

LOBロケータを別のLOBロケータに割り当てます。

OCILobCharSetForm()

LOBのキャラクタ・セット形式を戻します。

OCILobCharSetId()

LOBのキャラクタ・セットIDを戻します。

OCILobClose()

オープンしているLOBまたはBFILEをクローズします。

OCILobCopy2()

LOBの全体または一部をコピーします(非推奨のOCILobCopy()に代わるメソッド)。

OCILobCreateTemporary()

一時LOBを作成します。

OCILobDisableBuffering()

バッファリング・サブシステムを使用禁止にします。

OCILobEnableBuffering()

LOBデータの以降の読取り操作と書込み操作にLOBバッファリング・サブシステムを使用します。

OCILobErase2()

LOBの全体または指定のオフセット以降の部分を消去します(非推奨のOCILobErase()に代わるメソッド)。

OCILobFileClose()

オープンしているBFILEをクローズします。

OCILobFileCloseAll()

オープンBFILEをすべてクローズします。

OCILobFileExists()

BFILEが存在するかどうかを確認します。

OCILobFileGetName()

BFILEの名前を戻します。

OCILobFileIsOpen()

BFILEがオープンしているかどうかを確認します。

OCILobFileOpen()

BFILEをオープンします。

OCILobFileSetName()

BFILEの名前をロケータに設定します。

OCILobFlushBuffer()

LOBバッファリング・サブシステムへの変更を、データベース(サーバー)へフラッシュします。

OCILobFreeTemporary()

一時LOBを解放します。

OCILobGetChunkSize()

LOBのチャンク・サイズを取得します。

OCILobGetContentType()

SecureFile内のデータのユーザー指定コンテンツ・タイプ文字列を取得します(設定されている場合)。

OCILobGetLength2()

LOBの長さを取得します(非推奨のOCILobGetLength()に代わるメソッド)。

OCILobGetOptions()

指定されたSecureFile LOBの指定された入力オプション・タイプに対応する有効な設定を取得します。

OCILobGetStorageLimit()

LOB (BLOBCLOBまたはNCLOB)の最大長(バイト単位)を取得します。

OCILobIsEqual()

2つのLOBロケータが同じLOBを参照しているかどうかを確認します。

OCILobIsOpen()

LOBまたはBFILEがオープンしているかどうかを確認します。

OCILobIsTemporary()

ロケータが一時LOBを指し示しているかどうかを確認します。

OCILobLoadFromFile2()

BFILEデータを内部LOBにロードします(非推奨のOCILobLoadFromFile()に代わるメソッド)。

OCILobLocatorAssign()

LOBまたはBFILEロケータを別のロケータに割り当てます。

OCILobLocatorIsInit()

LOBロケータが初期化されているかどうかをテストして確認します。

OCILobOpen()

LOBを指定のモードでオープンします。

OCILobRead2()

NULLでないLOBまたはBFILEの指定の部分をバッファに読み取ります(非推奨のOCILobRead()に代わるメソッド)。

OCILobSetContentType()

SecureFile LOB内のデータのコンテンツ・タイプ文字列を設定します。

OCILobSetOptions()

SecureFile LOBのオプション設定を有効化します。

OCILobTrim2()

LOBを切り捨てます(非推奨のOCILobTrim()に代わるメソッド)。

OCILobWrite2()

バッファからLOBにデータを書き込んで既存のデータを上書きします(非推奨のOCILobWrite()に代わるメソッド)。

OCILobWriteAppend2()

LOBの現在の終了位置からデータを書き込みます(非推奨のOCILobWriteAppend()に代わるメソッド)。

6.5.2 OCIおよびPL/SQLインタフェースの比較

表6-2 OCIおよびPL/SQL(DBMS_LOB)インタフェースの比較

OCI (ociap.h) PL/SQL DBMS_LOB(dbmslob.sql)

該当なし

DBMS_LOB.COMPARE() 

該当なし

DBMS_LOB.INSTR() 

該当なし

DBMS_LOB.SUBSTR() 
OCILobAppend()
DBMS_LOB.APPEND() 
OCILobAssign()

該当なし[PL/SQL代入演算子を使用]

OCILobCharSetForm()

該当なし

OCILobCharSetId()

該当なし

OCILobCopy()
DBMS_LOB.COPY() 
OCILobDisableBuffering()

該当なし

OCILobEnableBuffering()

該当なし

OCILobErase()
DBMS_LOB.ERASE()
OCILobFileClose()
DBMS_LOB.FILECLOSE()
OCILobFileCloseAll()
DBMS_LOB.FILECLOSEALL()
OCILobFileExists()
DBMS_LOB.FILEEXISTS()
OCILobFileGetName()
DBMS_LOB.FILEGETNAME()
OCILobFileIsOpen()
DBMS_LOB.FILEISOPEN()
OCILobFileOpen()
DBMS_LOB.FILEOPEN()
OCILobFileSetName()

該当なし(BFILENAME演算子を使用)

OCILobFlushBuffer()

該当なし

OCILobGetLength()
DBMS_LOB.GETLENGTH()
OCILobIsEqual()

該当なし

OCILobLocatorIsInit()

該当なし[常に初期化]

OCILobRead()
DBMS_LOB.READ() 
OCILobTrim()
DBMS_LOB.TRIM() 
OCILobWrite() 
DBMS_LOB.WRITE() 

6.5.3 ロケータに入れる格納済LOBの選択

例6-8 データベースからロケータへのLOBの選択

/*-----------------------------------------------------------------------*/ 
/* Select lob locators from a CLOB column                                */ 
/* Use the 'FOR UPDATE' clause for writing to the LOBs.                  */ 
/*-----------------------------------------------------------------------*/ 
static OCIEnv        *envhp;
static OCIServer     *srvhp;
static OCISvcCtx     *svchp;
static OCIError      *errhp;
static OCISession    *authp;
static OCIStmt       *stmthp;
static OCIDefine     *defnp1;
static OCIBind       *bndhp;
 
sb4 select_locator(int rowind) 
{ 
  sword retval; 
  boolean flag; 
  int colc = rowind; 
  OCILobLocator *clob;
  text  *sqlstmt = (text *)"SELECT DATA FROM LOB_TABLE WHERE ID = :1 FOR UPDATE"; 

  if (OCIStmtPrepare(stmthp, errhp, sqlstmt, (ub4) strlen((char *)sqlstmt), 
      (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT)) 
  { 
    (void) printf("FAILED: OCIStmtPrepare() sqlstmt\n"); 
    return OCI_ERROR; 
  } 

  if (OCIStmtBindByPos(stmthp, bndhp, errhp, (ub4) 1, (dvoid *) &colc, 
      (sb4) sizeof(colc), SQLT_INT, (dvoid *) 0, (ub2 *)0, (ub2 *)0, (ub4) 0, 
      (ub4 *) 0, (ub4) OCI_DEFAULT)) 
  {
    (void) printf("FAILED: OCIStmtBindByPos()\n"); 
    return OCI_ERROR; 
  }
  
  if (OCIDefineByPos(stmthp, &defnp1, errhp, (ub4) 1, (dvoid *) &clob, (sb4) -1,
      (ub2) SQLT_CLOB, (dvoid *) 0, (ub2 *) 0, (ub2 *) 0, (ub4) OCI_DEFAULT)) 
  { 
    (void) printf("FAILED: 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"); 
    report_error(); 
    return OCI_ERROR; 
  } 
 
  /* Now test to see if the LOB locator is initialized */ 
  retval = OCILobLocatorIsInit(envhp, errhp, clob, &flag); 
  if ((retval != OCI_SUCCESS) && (retval != OCI_SUCCESS_WITH_INFO)) 
  { 
    (void) printf("Select_Locator --ERROR: OCILobLocatorIsInit(), 
        retval = %d\n", retval); 
    report_error(); 
    checkerr(errhp, retval); 
    return OCI_ERROR; 
  } 

  if (!flag) 
  { 
    (void) printf("Select_Locator --ERROR: LOB Locator is not initialized.\n"); 
    return OCI_ERROR;  
  } 

  return OCI_SUCCESS; 
}

6.6 DBMS_LOBパッケージを使用したLOBの操作

DBMS_LOBパッケージを使用して、PL/SQLからLOBを操作できます。表6-3でこのルーチンを説明します。

例6-9では、TRIMプロシージャをコールしてCLOB値を短い長さにトリミングします。lob_type型にはINTEGER型のidCLOB型のdataの2つの属性があり、lob_type型のlob_table表が存在しているとみなします。この例ではCLOBデータを処理するため、DBMS_LOB.TRIMの第2引数(834004)では文字数を指定します。この例でBLOBデータを処理する場合、この引数はバイト数として解析されます。

関連項目:

DBMS_LOBパッケージのルーチンの使用に関する詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。

6.6.1 DBMS_LOBパッケージのルーチン

表6-3 DBMS_LOBパッケージ・ルーチンの概要

ルーチン 説明
APPEND()

ソースLOBのコンテンツを宛先LOBに追加します。

COPY()

ソースLOBの全体または一部を宛先LOBにコピーします。

ERASE()

LOB全体または一部を消去します。

LOADBLOBFROMFILE()

BFILEデータを内部BLOBにロードします。

LOADCLOBFROMFILE()

BFILEデータを内部CLOBにロードします。

TRIM()

LOB値を指定された長さに切り捨てます。

WRITE()

指定されたオフセットからLOBにデータを書き込みます。

GETLENGTH

LOB値の長さを取得します。

INSTR()

LOBにあるパターンのn番目の一致のマッチング位置を戻します。

READ()

指定されたオフセット以降のLOBデータを読み込みます。

SUBSTR()

指定されたオフセット以降のLOB値の一部を戻します。

FILECLOSE()

ファイルをクローズします。

FILECLOSEALL()

前にオープンされたファイルをすべてクローズします。

FILEEXISTS()

サーバー上でファイルの有無をテストして確認します。

FILEGETNAME()

ディレクトリ別名とファイル名を取得します。

FILEISOPEN()

ファイルが入力のBFILEロケータを使用してオープンされたかどうかをテストして確認します。

FILEOPEN()

ファイルをオープンします。

6.6.2 CLOBのトリミング

例6-9 CLOBのトリミング

PROCEDURE Trim_Clob IS
        clob_loc  CLOB;
BEGIN
 -- get the LOB Locator
       SELECT data into clob_loc  FROM lob_table
       WHERE id  =  179 FOR UPDATE;
   -- call the TRIM Routine
       DBMS_LOB.TRIM(clob_loc, 834004);
       COMMIT;
END;

6.7 外部プロシージャ内のLOB

LOBロケータは、例6-1に示すように、外部プロシージャに引数として渡すことができます。

対応するCのファンクションは、OCILobLocator *型の引数を取得します。例6-10に定義するファンクションがコールされると、このファンクションからシグネチャint c_findmin(OCILobLocator*)を使用してcルーチンc_findmin()が起動されます。

このルーチン(c_findmin)は、DS_Libに関連した共有ライブラリにあります。ポインタOCILobLocator *を使用してLOBからデータを取得するには、コールバックを実行してデータベースに再接続する必要があります。

6.7.1 外部プロシージャの定義(PL/SQL)

例6-10 PL/SQL外部プロシージャの定義

FUNCTION DS_Findmin(data CLOB) RETURN PLS_INTEGER IS EXTERNAL 
                   NAME "c_findmin" LIBRARY DS_Lib LANGUAGE C;

6.8 LOBとトリガー

どの種類のトリガーでもLOB (:oldまたは:newの値)には書き込むことができません。

標準的なトリガーの場合、:old値は読み取れますが、:new値は読み取れません。INSTEAD OFトリガーの場合は、:old値と:new値を読み取ることができます。

トリガーが定義されている基礎となる表を更新しなくてもBFILE型を更新できるため、OF句にはLOB型の列を指定できません。

OCIファンクションまたはDBMS_LOBパッケージを使用してオブジェクト列のLOB値またはLOB属性を更新する場合、その列または属性を含む表に定義されているトリガーは起動されません。

6.9 パフォーマンス向上のためのカッコとしてのOpen/Closeの使用

Open/Closeファンクションでは、一連のLOB操作の開始と終了を示すことができます。これにより、索引の更新などの大規模な操作をCloseファンクションのコール時に実行できます。つまり、Openがコールされた後は、LOBが変更されるたびに索引が更新されることはなく、Closeがコールされるまでこの更新は再開されません。

Open/Close操作内ですべてのLOB操作をラップする必要はありませんが、コード・ブロックは次の理由から非常に役立ちます。

  • Open/Closeコール内でLOB操作をラップしないと、LOBが変更されるたびにLOBのオープンとクローズが暗黙的に実行されて、すべてのトリガーが起動されます。Open/Close操作のペア内でLOB操作をラップすると、LOBが変更されるたびにトリガーが起動されることはありません。かわりに、Closeがコールされるときにトリガーが1回起動されます。同様に、拡張可能索引はCloseがコールされるまで更新されません。これは、Open/Closeのコールの間は、LOBの拡張可能索引が有効でないことを意味します。

  • Open操作とClose操作の間はLOBに対する変更を表す状態が保存されないため、この技術は注意して適用する必要があります。Openをコールすると、LOB値の変更された部分や、変更前後のLOBの値が追跡されなくなります。跡されません。その間もLOB値はOCILob*操作またはDBMS_LOB操作のたびに直接更新され、通常の読取り一貫性メカニズムが適用されます。LOBが変更されるときには、LOBの拡張可能索引の更新も必要です。拡張可能なLOB索引は常に有効であり、いつでも使用できるためです。

  • APIにより、LOBがオープンされているかどうかを判別できます。オープンは、常にロケータではなくLOBに関連付けられています。ロケータでは状態の情報は保存されません。

6.9.1 Open/Close操作に関するエラーと制限

オープンしていたLOBをすべてクローズする前にトランザクションをコミットするとエラーになります。トランザクションのロールバック時には、オープンされているLOBもすべて破棄されます。つまり、クローズされないためにトリガーが起動されます。

異なるロケータまたは同じロケータを持つ同じLOBに対して2回Open/Closeを実行するとエラーになります。オープンされていないLOBをクローズするとエラーになります。

例6-11では、loc1がオープンされているLOBを参照し、loc2に割り当てられているとします。これ以降にloc2を使用してLOB値を変更しようとすると、その変更がloc1による変更とグループ化されます。つまり、LOBマネージャの状態のエントリは1つのみであり、ロケータごとに1つではありません。LOBloc1またはloc2を介してクローズされると、トリガーが起動されて、いずれかのロケータから行われたLOBの更新がすべてコミットされます。LOBのクローズ後に、ユーザーがいずれかのロケータを使用してLOBを変更しようとすると、その操作で暗黙的なOpen()およびClose()Open() ... operation ... Close()として実行されます。読取り一貫性は引き続きロケータごとに維持されることに注意してください。単にロケータではなくLOBがオープンされクローズされることを示しています。作成されるロケータのコピーの数に関係なく、LOBのトリガーは最初のCloseのコールで1回のみ起動されます。

6.9.1.1 Open()およびClose()コード・ブロックの使用

例6-11 Open()およびClose()コード・ブロックの使用

open (loc1);
loc2 := loc1;
write (loc1);
write (loc2); 
open (loc2);  /* error because the LOB is open */
close (loc1); /* triggers are fired and all LOB updates made before this 
                 statement by any locator are incorporated in the extensible
                 index */
write (loc2); /* implicit open, write, implicit close */