この章では、ラージ・オブジェクトとしてのOracle Databaseに示されているマルチメディア・データ型の動作方法を説明します。LOB型の簡潔な理論的概論を説明し、データ・カートリッジのPL/SQおよびOCI実装による実用的な使用の説明に焦点を絞っています。
この章の内容は、次のとおりです。
一部のデータ・カートリッジでは、グラフィック・イメージやサウンド波形などのRAWバイナリ・データ、またはテキストや数値ストリームなどの文字データを大量に処理する必要があります。Oracleは、この種のデータを処理するためのラージ・オブジェクト(LOB)をサポートしています。
内部LOBは、領域を最適化して効率的なアクセスを提供するようにデータベース表領域に格納されます。内部LOBはサーバーのトランザクション・モデルに関与します。
内部LOBは、バイナリ・データ(BLOB)、シングルバイト文字データ(CLOB)、固定幅のシングルバイトまたはマルチバイト文字データ(NCLOB)を格納できます。NCLOBは、Oracleデータベースに対して定義済の各国語キャラクタ・セットに対応する文字データで構成されます。可変幅の文字データは、Oracleではサポートされていません。
外部LOBは、データベース表領域外部のオペレーティング・システム・ファイルにBFILE(バイナリ・データ)として格納されます。これらは、トランザクションに関与できません。
LOB内部とBFILEはともに、大量データの処理に大幅な柔軟性を提供します。
LOBに格納されているデータは、LOB値と呼ばれます。Oracleサーバーにとって、LOB値は構造化されておらず、問合せできません。LOB値を解凍し、カートリッジ固有の方法で解析する必要があります。
LOBは、Oracle Call Interface(OCI)またはPL/SQLのDBMS_LOBパッケージを使用して操作できます。LOBの各部を操作するファンクション(LOBを格納できるオブジェクト型に対するメソッドなど)を記述できます。
|
関連項目 LOBの詳細は、『Oracle Database SecureFilesおよびラージ・オブジェクト開発者ガイド』を参照してください。 |
LOB定義には、CREATE TYPEおよびCREATE TABLE文を使用できます。例6-1では、データ型lob_type内のCLOBが指定されます。
例6-2では、各行がlob_tableデータのインスタンスであるオブジェクト表lob_tableを作成します。
例6-3では、LOBを例6-2のようにオブジェクト表に格納するのではなく、標準の表に格納します。
例6-3 表のLOBオブジェクト列の作成方法
CREATE TABLE lob_table1 ( id INTEGER, b_lob BLOB, c_lob CLOB, nc_lob NCLOB, b_file BFILE );
表にLOBを作成するときに、LOBの格納、バッファリングおよびキャッシュ・プロパティを設定できます。
|
関連項目 CREATE TABLE、ALTER TABLE、CREATE TYPEおよびALTER TYPE文にLOBを使用する方法の詳細は、『Oracle Database SQL言語リファレンス』および『Oracle Database SecureFilesおよびラージ・オブジェクト開発者ガイド』を参照してください。 |
LOBは、他の行データとともに、または行データとは別個に格納できます。格納場所に関係なく、各LOBには実際の位置へのハンドルまたはポインタとして表示できるロケータがあります。LOBを選択すると、LOB値のかわりにLOBロケータが戻されます。
例6-4では、b_lobのLOBロケータを選択してPL/SQLローカル変数image1に入れます。
例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;
APIファンクションを使用してLOB値を操作する場合は、ロケータを使用してLOBを参照します。例6-5のように、PL/SQLのDBMS_LOBパッケージには、PUT_LINEやGETLENGTHなど、LOBの操作に役立つルーチンが含まれています。
例6-5 PUT_LINE and GETLENGTHでのLOBの操作方法
BEGIN
DBMS_OUTPUT.PUT_LINE('Size of the Image is: ',
DBMS_LOB.GETLENGTH(image1));
END;
OCIでは、LOBロケータはOCILobLocator *などのLOBLocatorPointersにマップされます。
この章では、OCIのLOBインタフェースとPL/SQLのDBMS_LOBパッケージの概要について説明します。
BFILEの場合、LOB列には独自の個別ロケータがあり、サーバーのファイル・システムにある外部ファイルに格納されているLOB値を参照します。これは、BFILE列を含む表の2行で同じファイルまたは2つの個別ファイルを参照できることを意味します。PL/SQLまたはOCIプログラムにおけるBFILEロケータ変数の動作は、他の自動変数と同様です。ファイル操作の場合の動作は、ほとんどの従来型プログラム言語の標準I/Oライブラリの一部として使用可能なファイル・ディスクリプタと同様です。
|
関連項目
|
特殊ファンクションEMPTY_BLOBおよびEMPTY_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を使用して初期化できます。ファンクションの構文は次に示します。
例6-6 EMPTY_CLOB()の構文およびEMPTY_CLOB()ファンクション
FUNCTION EMPTY_BLOB() RETURN BLOB; FUNCTION EMPTY_CLOB() RETURN CLOB;
EMPTY_BLOBはBLOB型の空のロケータを戻し、EMPTY_CLOBはCLOB型の空のロケータを戻します。これはNCLOBにも使用できます。ファンクションには関連付けられているプログラマはありません。
例外が発生するのは、これらのファンクションをSQL INSERT文のVALUES句のどこかで使用するか、SQL UPDATE文のSET句のソースとして使用した場合です。
例6-7では、SQL DMLを使用したEMPTY_BLOBを示します。
例6-7 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-8では、PL/SQLプログラムでのEMPTY_CLOB()の使用方法を示します。
例6-8 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
OCIには、BLOB、CLOB、NCLOBおよびBFILEに格納されたデータへのアクセスに使用できるファンクションが組み込まれています。これらのファンクションは表6-1で説明しています。
|
関連項目 パラメータ、パラメータの型、戻り値およびコード例を含むドキュメントの詳細は、『Oracle Call Interfaceプログラマーズ・ガイド』を参照してください。 |
表6-1 OCIのLOB操作ファンクションの概要
| 関数 | 説明 |
|---|---|
OCILobAppend() |
|
OCILobAssign() |
ある |
OCILobCharSetForm() |
|
OCILobCharSetId() |
|
OCILobCopy() |
|
OCILobDisableBuffering() |
バッファリング・サブシステムの使用を無効化します。 |
OCILobEnableBuffering() |
|
OCILobErase() |
|
OCILobFileClose() |
オープン |
OCILobFileCloseAll() |
オープン |
OCILobFileExists() |
|
OCILobFileGetName() |
|
OCILobFileIsOpen() |
|
OCILobFileOpen() |
|
OCILobFileSetName() |
ロケータ内で |
OCILobFlushBuffer() |
|
OCILobGetLength() |
|
OCILobIsEqual() |
2つの |
OCILobLoadFromFile() |
|
OCILobLocatorIsInit() |
|
OCILobLocatorSize() |
|
OCILobRead() |
NULLでない |
OCILobTrim() |
|
OCILobWrite() |
バッファから |
表6-2に、LOBアクセスに関するOCIおよびPL/SQL(DBMS_LOBパッケージ)のインタフェースの比較を示します。
表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() |
該当なし( |
OCILobFlushBuffer() |
該当なし |
OCILobGetLength() |
DBMS_LOB.GETLENGTH() |
OCILobIsEqual() |
該当なし[PL/SQL等価演算子を使用] |
OCILobLoadFromFile |
DBMS_LOB.LOADFROMFILE() |
OCILobLocatorIsInit |
該当なし[常に初期化] |
OCILobRead |
DBMS_LOB.READ() |
OCILobTrim |
DBMS_LOB.TRIM() |
OCILobWrite |
DBMS_LOB.WRITE() |
例6-9では、データベースからロケータへのLOBの選択方法を示します。lob_type型には2つの属性、INTEGER型のidおよびCLOB型のdataがあり、lob_type型のlob_table表がすでに存在しているとみなします。
例6-9 データベースからロケータへのLOBの選択方法
/*-----------------------------------------------------------------------*/
/* Select lob locators from a CLOB column */
/* We need the 'FOR UPDATE' clause because we need to write 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;
}
サンプル・プログラムpopulate.cでは、OCIを使用してCLOBにディスク上のファイルの内容が移入されます。
DBMS_LOBパッケージを使用すると、PL/SQLからLOBを操作できます。表6-3でこのルーチンを説明します。
|
関連項目 DBMS_LOBパッケージのルーチンの使用に関する詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。 |
表6-3 DBMS_LOBパッケージ・ルーチンの概要
| ルーチン | 説明 |
|---|---|
APPEND() |
ソース |
COPY() |
ソース |
ERASE() |
すべてまたは一部の |
LOADFROMFILE() |
|
TRIM() |
|
WRITE() |
指定されたオフセットから |
GETLENGTH |
|
INSTR() |
|
READ() |
|
SUBSTR() |
|
FILECLOSE() |
ファイルI/Oをクローズします。 |
FILECLOSEALL() |
前にオープンされたファイルをすべてクローズします。 |
FILEEXISTS() |
サーバー上でファイルの有無をテストして確認します。 |
FILEGETNAME() |
ディレクトリ別名とファイル名を取得します。 |
FILEISOPEN() |
ファイルが入力の |
FILEOPEN() |
ファイルをオープンします。 |
例6-10では、TRIMプロシージャをコールしてCLOB値を短い長さにトリミングします。lob_type型には2つの属性、INTEGER型のidおよびCLOB型のdataがあり、lob_type型のlob_table表がすでに存在しているとみなします。この例ではCLOBデータを処理するため、DBMS_LOB.TRIMの第2引数(834004)では文字数を指定します。この例でBLOBデータを処理する場合、この引数はバイト数として解析されます。
LOBロケータは、外部プロシージャに引数として渡すことができます。対応するCのファンクションは、OCILobLocator *型の引数を取得します。表6-3に定義されたファンクションは、コール時に次のシグネチャを使用してcルーチン(c_findmin())を起動します。
int c_findmin(OCILobLocator *);
例6-11 PL/SQL外部プロシージャの定義方法
FUNCTION DS_Findmin(data CLOB) RETURN PLS_INTEGER IS EXTERNAL
NAME "c_findmin" LIBRARY DS_Lib LANGUAGE C;
このルーチン(c_findmin)は、DS_Libに関連した共有ライブラリにあります。ポインタOCILobLocator *を使用してLOBからデータを取得するには、コールバックを実行してデータベースに再接続する必要があります。
種類に関係なくトリガー内のLOB(:oldまたは:new値)には書き込めません。
標準的なトリガーの場合、:old値は読み取れますが、:new値は読み取れません。INSTEAD OFトリガーの場合は、:old値と:new値を読み取ることができます。
トリガーが定義されている基礎となる表を更新しなくてもBFILE型を更新できるため、OF句にはLOB型の列を指定できません。
OCIファンクションまたはDBMS_LOBパッケージを使用してオブジェクト列のLOB値またはLOB属性を更新する場合、その列または属性を含む表に定義されているトリガーは起動されません。
Open/Closeファンクションを使用すると、一連のLOB操作の開始と終了を示すことができます。これにより、Closeファンクションのコール後に索引の更新のような大規模な操作を実行できます。これは、Openがコールされた後は、LOBが変更されるたびに索引が更新されることはなく、この種の更新はCloseコールまで再開されないことを意味します。
すべてのLOB操作をOpen/Close操作でラップする必要はありませんが、コード・ブロックは次の理由から非常に役立ちます。
LOB操作をOpen/Closeコールでラップしなければ、LOBは変更のたびに暗黙的にオープンおよびクローズされ、トリガーが起動されます。LOB操作をOpen/Close操作のペアでラップすると、LOBが変更されるたびにトリガーが起動されることはありません。かわりに、Closeがコールされるときにトリガーが1つ起動されます。同様に、拡張可能索引は、Closeをコールするまで更新されません。これは、LOBの拡張可能索引がOpen/Closeコールの間は無効であることを意味します。
Open操作とClose操作の間はLOBの変更を反映する状態が保存されないため、この方法は慎重に適用する必要があります。Openをコールすると、LOB値のうち変更された部分も変更結果である新旧の値も追跡されなくなります。LOB値は引き続きOCILob*またはDBMS_LOB操作のたびに直接更新され、通常の読取り一貫性メカニズムが有効です。さらに、LOBの拡張可能索引はLOBの変更時に更新する必要があります。拡張可能LOB索引は常に有効であり、いつでも使用できるためです。
APIにより、LOBがオープンされているかどうかを判別できます。オープンは、常にロケータではなくLOBに関連付けられています。ロケータでは状態の情報は保存されません。
前にオープンしたLOBをすべてクローズする前にトランザクションをコミットするとエラーになります。トランザクションのロールバック時には、まだオープンされているLOBがすべて破棄されます。つまり、クローズされてトリガーが起動されることはありません。
異なるロケータまたは同じロケータを持つ同じLOBに対して2回Open/Closeを実行するとエラーになります。オープンされていないLOBをクローズするとエラーになります。
例6-12では、loc1がオープンされているLOBを参照し、loc2に割り当てられているとします。以降にLOB値を変更するためにloc2が使用されると、変更はloc1による変更とともにグループ化されます。つまり、LOBマネージャの状態のエントリは1つのみであり、ロケータごとに1つではありません。LOBが(loc1またはloc2を介して)クローズされると、トリガーが起動され、いずれかのロケータを介したLOBの更新がすべてコミットされます。LOBのクローズ後、ユーザーがいずれかのロケータを使用してLOBを変更しようとすると、その操作は暗黙的なOpen()およびClose()でOpen() ... operation ... Close()として実行されます。読取り一貫性は引き続きロケータごとに維持されることに注意してください。単にロケータではなくLOBがオープンされクローズされることを示しています。作成されるロケータのコピー数に関係なく、LOBのトリガーは最初のCloseコール時に1回のみ起動されます。
例6-12 コード・ブロックのオープンおよびクローズ方法
open (loc1);
loc2 := loc1;
write (loc1);
write (loc2);
open (loc2); /* error because the LOB is already open */
close (loc1); /* triggers are fired and all LOB updates made prior to this
statement by any locator are incorporated in the extensible
index */
write (loc2); /* implicit open, write, implicit close */