日本語PDF

16 LOB

この章では、LOB(ラージ・オブジェクト)データ型の埋込みSQL文で提供されるサポートについて説明します。

4種類のLOB型を説明し、従来のLONGおよびLONG RAWデータ型と比較します。

Oracle Call Interface APIおよびPL/SQL言語と同様の機能を提供するPro*C/C++の埋込みSQLインタフェースを示します。

LOB文およびそのLOBオプションとホスト変数を説明します。

最後に、使用方法を簡単に示すLOBインタフェースを使用したPro*C/C++ プログラムの例をあげています。

この章のトピックは、次のとおりです:

16.1 LOB

LOB(ラージ・オブジェクト)列を使用するとASCIIテキスト、各国語キャラクタのテキスト、様々なグラフィック形式のファイルおよびサウンド波形などの大量のデータ(最大4GB)を格納できます。

16.1.1 内部LOB

内部LOB(BLOB、CLOB、NCLOB)はデータベース表領域に格納され、データベース・サーバーからトランザクション・サポート(コミット、ロールバックなどの処理)が有効です。

バイナリ・ラージ・オブジェクト(BLOB)には、ビデオ・クリップなどの非構造化バイナリ・データ(生データとも呼ばれます)が格納されます。

キャラクタ・ラージ・オブジェクト(CLOB)には、データベース・キャラクタ・セットの文字データの大きいブロックが格納されます。

各国語キャラクタ・ラージ・オブジェクト(NCLOB)には、各国語キャラクタ・セットの文字データの大きいブロックが格納されます。

16.1.2 外部LOB

外部LOBは、データベース表領域外のオペレーティング・システム・ファイルです。ただし、データベース・サーバーのトランザクション・サポートは無効です。

BFILE(Binary Files)では、データは外部バイナリ・ファイルの形式で格納されます。BFILEには、GIF、JPEG、MPEG、MPEG2、テキストなどの形式があります。

16.1.3 BFILEのセキュリティ

DIRECTORYオブジェクトは、BFILEにアクセスして操作するときに使用します。DIRECTORYは、ファイルが格納されているサーバー・ファイル・システムの実際の物理ディレクトリの(サーバーに格納されている)論理的な別名です。DIRECTORYオブジェクトに対するアクセス権限が割り当てられているユーザー以外は、ファイルにアクセスできません。

  • データ定義言語(DDL)のSQL文であるCREATE、REPLACE、ALTERおよびDROPは、DIRECTORYデータベース・オブジェクトに使用します。

  • DIRECTORYオブジェクト上のシステムおよびオブジェクトに対するREAD権限のGRANTおよびREVOKEを行うデータ操作言語(DML)SQL文。

CREATE DIRECTORYディレクティブの例を次に示します。

EXEC SQL CREATE OR REPLACE DIRECTORY "Mydir" AS '/usr/home/mydir' ;

ユーザーまたはロールは、GRANTなどのデータ操作言語(DML)文の権限が割り当てられている場合にのみ、ディレクトリを読み込むことができます。たとえば、ユーザーscottがディレクトリ/usr/home/mydirのBFILESを読み込めるようにするには次のようにします。

EXEC SQL GRANT READ ON DIRECTORY "Mydir" TO scott ;

1セッション内で、最大10個のBFILESを同時にオープンできます。SESSION_MAX_OPEN_FILESパラメータの設定を変更して、デフォルト値を変更できます。

16.1.4 LOBとLONGおよびLONG RAWの対比

LOBは、従来のLONGおよびLONG RAWデータ型と多くの点で異なります。

  • LOBの最大サイズは4GBです。LONGおよびLONG RAWの最大サイズは2GBです。

  • LOBでは、ランダム・アクセス方法および順次アクセス方法を使用できます。LONGおよびLONG RAWでは、順次アクセス方法のみです。

  • LOB (NCLOBは除きます)は、定義したオブジェクト型の属性になります。

  • 表では複数のLOB列を作成できますが、複数のLONGまたはLONG RAW列は作成できません。

既存のLONGおよびLONG RAW属性は、LOBに移行することをお薦めします。今後のリリースでは、LONGおよびLONG RAWはサポートしない予定です。移行の詳細は、『Oracle® Database移行ガイド』を参照してください。

16.1.5 LOBロケータ

LOBロケータは、LOBの実際の内容を指しています。ロケータはLOBの内容ではなくLOBを取り出したときに戻されます。LOBロケータは、特定のトランザクションまたはセッションには保存されずに、後続のトランザクションまたはセッションで再使用されます。

16.1.6 一時LOB

ローカル変数のように使用できるテンポラリLOBを作成すると、データベースLOBが使用しやすくなります。テンポラリLOBは、表には対応付けらません。アクセスできるのは作成者のみです。また、ロケータを持っており(ロケータを使用してアクセスします)、セッション終了時には削除されます。

一時BFILEはサポートされていません。INSERT、UPDATEまたはDELETE文のWHERE句でのみ、テンポラリLOBを入力変数(IN値)として使用できます。テンポラリLOBは、INSERT文で挿入される値またはUPDATE文のSET句の値としても使用できます。テンポラリLOBではデータベース・サーバーのトランザクション・サポートが無効なため、COMMITまたはROLLBACKを行うことはできません。

一時LOBロケータは、トランザクションをまたがって使用することができます。サーバーが異常終了したとき、およびデータベースSQL処理でエラーが発生したときは削除されます。

16.1.7 LOBバッファリング・サブシステム

LBS(LOBバッファリング・サブシステム)は、クライアント側のアドレス空間に、1つ以上のLOBのバッファとして提供されるユーザー・メモリーです。

バッファリングには次の利点があります。特に、LOBの特定領域に小規模な読取りおよび書込みを繰り返すクライアントのアプリケーションに有効です。

  • LBSを使用すると、LOBに対して読込み/書込みが複数回行われ、バッファがいっぱいになってから、FLUSHディレクティブが実行されるときにサーバーに書き込まれるため、サーバー・ラウンドトリップが減少します。

  • サーバー上でのLOBの合計更新数が減少します。この結果、LOBのパフォーマンスが向上し、ディスク領域が節約されます。

Oracleで提供しているバッファリングは、簡単なバッファ・サブシステムで、キャッシュではありません。バッファの内容が、サーバーのLOB値と常に同期しているとはかぎりません。サーバーのLOBに実際に更新を書き込むには、FLUSH文を使用します。

LOBのバッファへの読込み/書込みは、ロケータを使用して行われます。バッファリングが使用可能なロケータを使用すると、書込みを実行するまで、LOBを常に読み込むことができます。

ロケータは、WRITEのバッファに使用された後で更新され、バッファリング・サブシステムを介して表示できる最新のLOBに対するアクセス権限が割り当てられます。LOBに対するその後のWRITEは、この更新ロケータを介してのみバッファされます。バッファされたLOBの操作を行うトランザクションは、ユーザー・セッション間では移行できません。

LBSは、サーバーのLOB値をFLUSH文を使用して更新するユーザーが管理します。これは、シングル・ユーザーでシングル・スレッドです。サーバーLOBの妥当性を確保するには、ROLLBACKおよびSAVEPOINTアクションを使用します。バッファされたLOB操作のトランザクション・サポートは無効です。バッファされたLOB更新のトランザクション・セマンティクスを確保するには、エラー発生時にロールバックを行う論理セーブポイントをメンテナンスする必要があります。

16.2 プログラムでのLOBの使用方法

この項では、Pro*C/C++アプリケーションでのLOBの使用に関する、プログラミングの重要な問題をいくつか説明します。

16.2.1 LOBにアクセスする3種類の方法

Pro*C/C++のLOBにアクセスするには、次の3種類の方法があります。

  • PL/SQLブロックのDBMS_LOBパッケージ。

  • OCI(Oracle Call Interface)ファンクション・コール。

  • 埋込みSQL文。

SQL文は、PL/SQLインタフェースと同等の機能を提供するように設計されており、OCIインタフェースほど複雑ではありません。

次の表では、Pro*C/C++内でのOCIファンクション・コール、PL/SQLおよびPro*C/C++の埋込みSQL文によるLOBアクセスを比較しています。空欄は機能がないことを示します。

表16-1 LOBアクセス方法

OCI (1) PL/SQL(2) Pro*C/C++埋込みSQL

-

COMPARE()

-

-

INSTR()

-

-

SUBSTR()

-

OCILobAppend

APPEND()

APPEND

OCILobAssign

:=

ASSIGN

OCILobCharSetForm

-

-

OCICharSetId

-

-

OCILobClose

CLOSE()

CLOSE

OCILobCopy

COPY()

COPY

OCILobCreateTemporary

CREATETEMPORARY()

CREATE TEMPORARY

OCILobDisableBuffering

-

DISABLE BUFFERING

OCILobEnableBuffering

-

ENABLE BUFFERING

OCILobErase

ERASE()

ERASE

OCILobGetChunkSize

GETCHUNKSIZE()

DESCRIBE

OCILobIsOpen

ISOPEN()

DESCRIBE

OCILobFileClose

FILECLOSE()

CLOSE

OCILobFileCloseAll

FILECLOSEALL()

FILE CLOSE ALL

OCILobFileExists

FILEEXISTS()

DESCRIBE

OCILobFileGetName

FILEGETNAME()

DESCRIBE

OCILobFileIsOpen

FILEISOPEN()

DESCRIBE

OCILobFileOpen

FILEOPEN()

OPEN

OCILobFileSetName

BFILENAME()

FILE SET脚注3

OCILobFlushBuffer

-

FLUSH BUFFER

OCILobFreeTemporary

FREETEMPORARY()

FREE TEMPORARY

OCILobGetLength

GETLENGTH()

DESCRIBE

OCILobIsEqual

=

-

OCILobIsTemporary

ISTEMPORARY()

DESCRIBE

OCILobLoadFromFile

LOADFROMFILE()

LOAD FROM FILE

OCILobLocatorIsInit

-

-

OCILobOpen

OPEN()

OPEN

OCILobRead

READ()

READ

OCILobTrim

TRIM()

TRIM

OCILobWrite

WRITE()

WRITE

OCILobWriteAppend

WRITEAPPEND()

WRITE

脚注1 C/C++ユーザーのみ。これらの関数のプロトタイプは、ociap.hにあります。

脚注2 dbmslob.sqlを参照してください。BFILENAMEを除くルーチンの前には、すべて'DBMS_LOB'を付ける必要があります。

脚注3

組込みSQL関数BFILENAME()も使用できます。

注意:

新しい文を使用する前に、LOBに対して修正または変更する行を明示的にロックする必要があります。LOB値を修正する操作には、APPEND、COPY、ERASE、LOAD FROM FILE、TRIMおよびWRITEがあります。

16.2.2 アプリケーションでのLOBロケータ

LOBロケータをPro*C/C++アプリケーションで使用する場合は、oci.hヘッダー・ファイルをインクルードし、BLOBに対してOCIBlobLocator型のポインタ、CLOBおよびNCLOBに対してOCIClobLocator、またはBFILEに対してOCIBFileLocatorを宣言します。

NCLOBの場合は、次のいずれかの操作を行う必要があります。

  • C/C++宣言で'CHARACTER SET IS NCHAR_CS'句を使用します。

  • コマンドラインまたは構成ファイルで、NLS_CHARプリコンパイラ・オプションをあらかじめ指定し、NLS_NCHAR環境変数を設定する必要があります。

設定方法は次のとおりです。

/* In your precompiler program */
#include <oci.h>
...
OCIClobLocator CHARACTER SET IS NCHAR_CS *a_nclob ;

または、Pro*C/C++をコールするときに、プリコンパイラ・オプションNLS_CHARを

NLS_CHAR=(a_nclob)

と設定している場合は、コードから、CHARACTER SET句を削除できます。

#include <oci.h>
...
OCIClobLocator *a_nclob ;

他に次のように簡潔に宣言します。

/* In your precompiler program */
#include <oci.h>
...
OCIBlobLocator  *a_blob ;
OCIClobLocator  *a_clob ;
OCIBFileLocator *a_bfile ;

関連項目

16.2.3 LOBの初期化

LOBの初期化方法は、LOBの種類によって異なります。この項では、種類ごとに初期化方法を説明します。

16.2.3.1 内部LOB

BLOBを初期化して空にするには、EMPTY_BLOB()関数を使用するか、ALLOCATE SQL文を使用します。CLOBおよびNCLOBの場合は、EMPTY_CLOB()関数を使用しますEMPTY_BLOB()およびEMPTY_CLOB()の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

これらの関数は、INSERT文のVALUES句内またはUPDATE文のSET句のソースでのみ使用できます。

次に例を示します。

EXEC SQL INSERT INTO lob_table (a_blob, a_clob)
   VALUES (EMPTY_BLOB(), EMPTY_CLOB()) ;

ALLOCATE文を実行すると、LOBロケータが割り当てられ、初期化後に空になります。つまり、次のコードを実行しても、前の例と同じ結果が得られます。

#include <oci.h>
...
OCIBlobLocator *blob ;
OCIClobLocator *clob ;
EXEC SQL ALLOCATE :blob ;
EXEC SQL ALLOCATE :clob ;
EXEC SQL INSERT INTO lob_table (a_blob, a_clob)
   VALUES (:blob, :clob) ;
16.2.3.2 外部LOB

BFILEおよびFILENAMEのDIRECTORY別名を初期化するには、LOB FILE SET文を次のように使用します。

#include <oci.h>
...
char *alias = "lob_dir" ;
char *filename = "image.gif" ;
OCIBFileLocator *bfile ;
EXEC SQL ALLOCATE :bfile ;
EXEC SQL LOB FILE SET :bfile
   DIRECTORY = :alias, FILENAME = :filename ;
EXEC SQL INSERT INTO file_table (a_bfile) VALUES (:bfile) ;

DIRECTORYオブジェクトのネーミング規則およびDIRECTORYオブジェクト権限の詳細は、『Oracle Databaseアドバンスト・アプリケーション開発者ガイド』を参照してください。

また、INSERTまたはUPDATE文のBFILENAME('ディレクトリ', 'ファイル名')機能を使用して、BFILE列または特定行の属性を初期化し、実際の物理ディレクトリおよびファイル名を取得できます。

EXEC SQL INSERT INTO file_table (a_bfile)
   VALUES (BFILENAME('lob_dir', 'image.gif'))
      RETURNING a_bfile INTO :bfile ;

注意:

BFILENAME()では、ディレクトリまたはファイル名の権限、および物理ディレクトリの有無は確認されません。BFILEロケータを使用してファイルにアクセスしたときに、これらが確認され、ファイルにアクセスできない場合にはエラーが戻されます。

16.2.3.3 一時LOB

埋込みSQLのLOB CREATE TEMPORARY文を使用して最初に一時LOBを作成したときに、一時LOBは初期化され空になります。EMPTY_BLOB()およびEMPTY_CLOB()関数は、一時LOBでは使用できません。

16.2.3.4 LOBの解放

FREE文は、ALLOCATE文によって確保されたメモリーを解放するときに使用します。

EXEC SQL FREE :a_blob;

16.3 LOB文のルール

LOB文を使用するときのルールを説明します。

16.3.1 すべてのLOB文に適用されるルール

次の一般的な制限および制約は、SQL LOB文を使用してLOBを操作するときに適用されます。

  • EXEC SQL LOB文で使用できるLOBロケータは1つのみのため、EXEC SQL LOB文ではFOR句は使用できません。

  • 分散LOBはサポートされていません。新しい埋込みSQL LOB文ではATデータベース句を使用できますが、同一のSQL LOB文で、異なるデータベース接続を使用して作成またはALLOCATEされたLOBロケータを混在させることはできません。

  • LOB READおよびWRITE操作を行う場合、OCIでは、コールバック関数をクライアントから指定できるコールバック・メカニズムを提供しています。このコールバック関数は、LOB値ピースの読込みあるいは書込みが行われるたびに実行されます。埋込みSQL LOB文では、この機能はサポートされていません。

  • OCIでは、テンポラリLOBを作成するときに使用可能な期間を、独自に作成または指定できるメカニズムを提供しています。テンポラリLOBのREADおよびWRITE操作に使用されるバッファ・キャッシュを指定するメカニズムもあります。このインタフェースでは、これらの機能はサポートされていません。

16.3.2 LOBバッファリング・サブシステムに適用されるルール

LBSでは、次のルールに従う必要があります。

  • 読取りまたは書込みアクセス時のエラーは、次回のサーバーへのアクセス時にレポートされます。このため、エラー・リカバリのコードは、ユーザーが作成する必要があります。

  • バッファへの書込みによりLOBを更新するときは、必ずLOBバッファリング・サブシステムを経由して更新を行ってください。

  • バッファリングが可能な更新済LOBロケータは、INパラメータとしてPL/SQLプロシージャに渡せますが、IN OUTあるいはOUTパラメータとしては渡すことはできません。エラーが戻されます。更新されたロケータを戻そうとしたときも、エラーが戻されます。

  • バッファリング可能な更新済ロケータを、別のロケータにはASSIGNできません。

  • バッファへの書込みをLOB値に追加することができますが、その開始オフセットの1文字は、LOBの最後に続く必要があります。LBSでは、データベース・サーバーのLOBに、0バイトの充填文字または空白が入るAPPEND文は使用できません。

  • ホスト・ロケータのバインド変数のキャラクタ・セットとデータベース・サーバーのCLOBは、同じであることが必要です。

  • ASSIGN、READおよびWRITE文のみ、バッファリング可能なロケータとともに使用できます。

  • バッファリングが使用可能なロケータで、APPEND、COPY、ERASE、DESCRIBE(LENGTHのみ)およびTRIM文を実行するとエラーが発生します。また、これらの文をバッファリング不可能なロケータとともに使用した場合でも、そのロケータによってポイントされたLOBが他のロケータによってバッファ・モードでアクセスされた場合には、エラーが戻されます。

    注意:

    次の処理の前に、LOBバッファリング・サブシステムが使用可能なLOBに対して、FLUSH文を実行する必要があります。

  • トランザクションをコミットするとき。

  • カレント・トランザクションから他のトランザクションへの移行。

  • LOB上でのバッファ操作の使用禁止。

  • 外部プロシージャの実行からPL/SQLルーチンに戻るとき。

    注意:

    PL/SQLブロックからロケータ・パラメータを引数にして外部コールアウトがコールされた場合、ENABLE文を含むすべてのバッファリングは、外部プロシージャ内で行う必要があります。

次の手順に従います。

  • 外部コールアウトを呼び出します。

  • バッファリングのロケータをENABLEします。

  • ロケータを使用してREADまたはWRITEします。

  • LOBに対してFLUSHします(LOBを暗黙的にフラッシュできません)。

  • バッファリングのロケータを使用禁止にします。

  • PL/SQLのファンクション/プロシージャ/メソッドに戻ります。

LOBは、明示的にFLUSHする必要があります。

16.3.3 ホスト変数に対するルール

LOB文では、次のルールおよび注意事項に従ってください。

  • srcおよびdstを使用して、内部または外部LOBロケータを参照できますが、fileでは、外部ロケータ以外は参照できません。

  • 数値のホスト値(amt、src_offset、dst_offsetなど)は、4バイトの符号なし整数の変数として宣言されます。値は、0から4GBに制限されています。

  • NULLは、LOBロケータで使用します。LOB文では標識変数は必要ありません。NULLは、数値変数(amt、src_offsetなど)には使用できません。エラーが発生します。

  • オフセット値src_offsetおよびdst_offsetのデフォルト値は1です。

16.4 LOB文

アルファベット順に文を説明します。databaseは、すべての文でデータベース接続を表しています。

16.4.1 APPEND

用途

この文はLOB値を別のLOBの最後に追加します。

構文

EXEC SQL [AT [:]database] LOB APPEND :src TO :dst ;

ホスト変数

src (IN):

一意にソースLOBを参照する内部LOBロケータです。

dst (IN OUT)

一意に宛先LOBを参照する内部LOBロケータです。

使用上の注意

ソースLOBのデータが宛先LOBの最後にコピーされると、宛先LOBが最大4GBまで拡張されます。LOBが4GBを超えて拡張される場合は、エラーが発生します。

ソースおよび宛先LOBは、すでに存在している必要があります。また、宛先LOBは初期化されている必要があります。

ソースおよび宛先LOBの両方が、同じ内部LOB型であることが必要です。どちらのロケータに対しても、LOBバッファリングを有効にすると、エラーとなります。

16.4.2 ASSIGN

用途

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

構文

EXEC SQL [AT [:]database] LOB ASSIGN :src to :dst ;

ホスト変数

src (IN):

コピー元のLOBまたはBFILEロケータ・ソース。

dst (IN OUT)

コピー先のLOBまたはBFILEロケータ。

使用上の注意

割当て後は、両方のロケータは同じLOB値を参照します。宛先LOBロケータは、初期化された有効な(ALLOCATEで割り当てられた)ロケータにしてください。

内部LOBでは、宛先ロケータが表に格納されている場合にのみ、ソース・ロケータのLOB値が宛先ロケータのLOB値にコピーされます。Pro*C/C++の場合は、宛先ロケータを含むオブジェクトに対してFLUSHを発行すると、LOB値がコピーされます。

BFILEロケータが内部LOBロケータに割り当てられている場合、またはその逆の場合には、エラーが戻されます。src LOBとdst LOBが同じ型でない場合にもエラーになります。

バッファリングが使用可能な内部LOBに対するソース・ロケータの場合、そのソース・ロケータがLOBバッファリング・サブシステム経由でLOB値を修正するために使用され、WRITE後にバッファに対してFLUSHしていないときは、ソース・ロケータを宛先ロケータに割り当てることはできません。これは、LOBバッファリング・サブシステムを介してLOB値を修正する場合、1つのLOBに対してロケータは1つしか使用できないためです。

16.4.3 CLOSE (LOBの場合)

用途

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

構文

EXEC SQL [AT [:]database] LOB CLOSE :src ;

ホスト変数

src (IN OUT)

クローズされるLOBまたはBFILEのロケータ。

使用上の注意

異なるロケータまたは同一ロケータを使用した場合でも、同一LOBを2回クローズするとエラーになります。外部LOBの場合は、BFILEが存在して一度もオープンされていない状態であれば、エラーは発生しません。

オープンしていたLOBをすべてクローズする前に、トランザクションをCOMMITするとエラーになります。トランザクションのROLLBACK時にオープンしているLOBは、クローズされず、すべて破棄されます。

16.4.4 COPY

用途

LOB値の全部または一部を別のLOBにコピーします。

構文

EXEC SQL [AT [:]database] LOB COPY :amt FROM :src [AT :src_offset]
   TO :dst [AT :dst_offset] ;

ホスト変数

amt (IN)

コピーするBLOBの最大バイト数またはCLOBおよびNCLOBの最大文字数。

src (IN):

ソースLOBのロケータ。

src_offset (IN)

CLOBまたはNCLOBの場合は、文字数。BLOBの場合は、バイト数。LOBの先頭で1から始まります。

dst (IN)

宛先LOBのロケータ。

dst_offset (IN)

宛先オフセット。src_offsetと同じルールが適用されます。

使用上の注意

データが宛先のオフセット以降にあらかじめ存在する場合は、ソース・データで上書きされます。宛先のオフセットがカレント・データの最後を超えている場合は、宛先LOBのカレント・データの最後から新しく書き込まれたソース・データの先頭まで、ゼロバイト充填文字(BLOB)または空白(CLOB)が書き込まれます。

新規に書き込むデータが宛先LOBの現行の長さよりも大きい場合、宛先LOBは、そのデータにあわせて拡張されます。4GBを超えてLOBが拡張されると、ランタイム・エラーが発生します。

初期化されていないLOBからコピーすると、エラーになります。

ソースLOBおよび宛先LOBは、同じ型である必要があります。LOBバッファリングは、ロケータのどちらに対しても使用可能にしないでください。

amt変数は、コピーの最大量です。指定した量がコピーされる前にソースLOBの最後に到達した場合、操作はORA-22993エラーで終了します。

テンポラリLOBを永続LOBにするには、COPY文を使用して、テンポラリLOBを永続LOBに明示的にCOPYする必要があります。

16.4.5 CREATE TEMPORARY

用途

一時LOBを作成します。

構文

EXEC SQL [AT [:]database] LOB CREATE TEMPORARY :src ;

ホスト変数

src (IN OUT)

実行前はINで、srcは、以前にALLOCATEされたLOBロケータです。

実行後でOUTになったときは、srcは新しい空の一時LOBをポイントするLOBロケータです。

使用上の注意

実行が正常に終了すると、ロケータはデータベース・サーバーに新しく作成された、表に依存しない一時LOBをポイントします。一時LOBは空で、長さゼロです。

セッション終了時に、すべての一時LOBは解放されます。テンポラリLOBに対するREADおよびWRITEでは、バッファ・キャッシュは経由されません。

16.4.6 DISABLE BUFFERING

用途

LOBロケータのLOBバッファリングを使用禁止にします。

構文

EXEC SQL [AT [:]database] LOB DISABLE BUFFERING :src ;

ホスト変数

src (IN OUT)

内部LOBロケータ。

使用上の注意

この文は、BFILEをサポートしていません。後続の読取りまたは書込みは、LBS経由では行われません。

注意:

この文では、LOBバッファリング・サブシステムの変更は暗黙的にフラッシュされないため、変更を有効にするにはFLUSH BUFFERコマンドを使用します。

16.4.7 ENABLE BUFFERING

用途

LOBロケータのLOBバッファリングを使用可能にします。

構文

EXEC SQL [AT [:]database] LOB ENABLE BUFFERING :src ;

ホスト変数

src (IN OUT)

内部LOBロケータ。

使用上の注意

この文は、BFILEをサポートしていません。後続の読取りおよび書込みは、LBSを経由して行われます。

16.4.8 ERASE

用途

指定されたオフセットから始まる、指定された量のLOBデータを消去します。

構文

EXEC SQL [AT [:]database] LOB ERASE :amt FROM :src [AT :src_offset] ;

ホスト変数

amt (IN OUT)

入力は、消去するバイト数または文字数です。戻される出力は、実際に消去されたバイト数または文字数です。

src (IN OUT)

内部LOBロケータ。

src_offset (IN)

LOBの先頭からのオフセット。1から始まります。

使用上の注意

この文は、BFILEをサポートしていません。

実行後に消去された実際の文字/バイト数がamtから戻されます。要求した文字数またはバイト数を消去する前にLOB値の最後に到達した場合は、実際の消去数と要求した消去数は異なります。LOBが空の場合は、amtには、0文字/バイトが消去されたことを示します。

BLOBの場合は、消去とはゼロバイト充填文字で既存のLOB値を上書きすることです。CLOBの場合は、空白で既存のLOB値を上書きすることです。

16.4.9 FILE CLOSE ALL

用途

カレント・セッションでオープンしているBFILESをすべてクローズします。

構文

EXEC SQL [AT [:]database] LOB FILE CLOSE ALL ;

使用上の注意

クローズ処理が正常に終了しなかったためにセッションにオープンしているファイルが存在する場合は、FILE CLOSE ALL文を使用して、セッション内でオープンしているファイルをすべてクローズし、最初からファイル操作を再開できます。

16.4.10 FILE SET

用途

BFILEロケータのDIRECTORY別名およびFILENAMEを設定します。

構文

EXEC SQL [AT [:]database] LOB FILE SET :file
    DIRECTORY = :alias, FILENAME = :filename ;

ホスト変数

file (IN OUT)

DIRECTORY別名およびFILENAMEが設定されているBFILEロケータ。

alias (IN)

設定するDIRECTORY別名。

filename (IN)

設定するFILENAME。

使用上の注意

指定したBFILEロケータは、この文で使用する前に、ALLOCATEされている必要があります。

DIRECTORY別名およびFILENAMEは必須項目です。

DIRECTORY別名の最大長は128バイトです。FILENAMEの最大長は255バイトです。

DIRECTORY別名およびFILENAME属性は、CHARZ、STRING、VARCHAR、VARCHAR2およびCHARF以外の外部データ型ではサポートされません。

この文を外部LOBロケータ以外で使用すると、エラーになります。

16.4.11 FLUSH BUFFER

用途

LOBのバッファをデータベース・サーバーに書き込みます。

構文

EXEC SQL [AT [:]database] LOB FLUSH BUFFER :src [FREE] ;

ホスト変数

src (IN OUT)

内部LOBロケータ。

使用上の注意

入力ロケータが参照するLOBからサーバーのデータベースLOBに、バッファ・データを書き込みます。

LOBバッファリングは、入力LOBロケータに対してすでに有効になっている必要があります。

デフォルトでのFLUSH操作では、バッファ・リソースの解放および別のバッファされたLOB操作への再割当ては行われません。ただし、バッファを明示的に解放する場合は、オプションのFREEキーワードを使用して指定できます。

16.4.12 FREE TEMPORARY

用途

LOBロケータ用に一時領域を解放します。

構文

EXEC SQL [AT [:]database] LOB FREE TEMPORARY :src ;

ホスト変数

src (IN OUT)

テンポラリLOBをポイントしているLOBロケータ。

使用上の注意

入力ロケータは、一時LOBをポイントしている必要があります。出力ロケータは、初期化されずに、後続のLOB文で使用されます。

16.4.13 LOAD FROM FILE

用途

BFILEの一部または全部を、内部LOBにコピーします。

構文

EXEC SQL [AT [:]database] LOB LOAD :amt FROM FILE :file [AT :src_offset] INTO
    :dst [AT :dst_offset] ;

ホスト変数

amt (IN)

ロードされる最大バイト数。

file (IN OUT)

BFILEロケータのソース。

src_offset (IN)

ファイルの先頭からのオフセットのバイト数。1から始まります。

dst (IN OUT)

宛先LOBロケータ。BLOB、CLOBまたはNCLOBになります。

dst_offset (IN)

書込みが開始される宛先LOBの先頭からのバイト数(BLOBの場合)または文字数(CLOBおよびNCLOBの場合)。1から始まります。

使用上の注意

データは、ソースBFILEから宛先内部LOBにコピーされます。BFILEデータをCLOBまたはNCLOBにコピーする場合、キャラクタ・セットは変換されません。このため、BFILEデータは、あらかじめデータベース内のCLOBまたはNCLOBと同じキャラクタ・セットになっている必要があります。

ソースおよび宛先LOBは、すでに存在している必要があります。宛先の開始位置にすでにデータがある場合は、ソース・データで上書きされます。宛先の開始位置が現行のデータの最後を超える場合は、ゼロバイトの充填文字(BLOB)または空白(CLOBおよびNCLOB)が宛先LOBに書き込まれます。充填文字は、宛先LOBのデータの最後から、ソースから新しく書き込まれたデータの始まりまでに書き込まれます。

新規に書き込むデータが宛先LOBの現行の長さよりも大きい場合、宛先LOBは、そのデータにあわせて拡張されます。4GBを超えてLOBが拡張されると、エラーになります。

また、初期化されていないBFILEからコピーをすると、エラーになります。

amountパラメータは、ロードする最大量を示します。指定した量がロードされる前にソースBFILEの最後に到達した場合は、処理はORA-22993エラーで終了します。

16.4.14 OPEN (LOBの場合)

用途

読取り、または読取り/書込みアクセスのために、LOBまたはBFILEをオープンします。

構文

EXEC SQL [AT [:]database] LOB OPEN :src [ READ ONLY | READ WRITE ] ;

ホスト変数

src (IN OUT)

LOBまたはBFILEのLOBロケータ。

使用上の注意

LOBまたはBFILEをOPENできるデフォルト・モードは、READ ONLYアクセスです。

内部LOBの場合は、OPENはロケータではなくLOBに対応付けられます。すでにOPENされているロケータを別のロケータに割り当てても、新しいLOBをOPENしたとはみなされません。両方のロケータから同じLOBが参照されます。BFILEの場合は、OPENはロケータに対応付けられます。

同時にOPENできるLOBの最大数は32個です。33個目のLOBをOPENするとエラーが戻されます。

書込み可能なBFILEは、サポートしていません。このため、BFILEをREAD WRITEモードでOPENすると、エラーが戻されます。

LOBをREAD ONLYモードでオープンした後にWRITEすると、エラーになります。

16.4.15 READ

用途

LOBまたはBFILEの一部または全部をバッファに読み込みます。

構文

EXEC SQL [AT [:]database] LOB READ :amt FROM :src [AT :src_offset]
   INTO :buffer [WITH LENGTH :buflen] ;

ホスト変数

amt (IN OUT)

入力は、読み込まれる文字数またはバイト数です。出力は、読み込まれた実際の文字数またはバイト数です。

読み込まれたバイト数がバッファ長より大きい場合は、LOBはポーリング・モードで読み込まれているとみなされます。入力時にこの値がゼロの場合は、データは入力オフセットからLOBの最後までポーリング・モードで読み込まれます。

実際に読み込まれるバイト数または文字数はamtで戻されます。データがピース単位で読み込まれる場合、amtには常に最後に読み込まれたピースが含まれます。

LOBの最後に到達した場合は、「ORA-01403: データが見つかりません。」というエラーが発生します。

ポーリング・モードで読み込むときは、アプリケーションからLOB READを繰り返しコールし、データがなくなるまでLOBのピースを読み込む必要があります。ORA-01403エラーを捕捉するには、WHENEVERディレクティブのNOT FOUND条件を使用してポーリング・モードの使用を制御します。

src (IN):

LOBまたはBFILEロケータ。

src_offset (IN)

読込みを開始する、LOB値の先頭からの絶対オフセットです。キャラクタLOBにおいては、これはLOBの先頭からの文字数を意味します。バイナリLOBまたはBFILEにおいては、これはバイト数を意味します。先頭位置は1です。

buffer (IN/OUT)

LOBデータが読み込まれるバッファ。バッファの外部データ型は、ソースLOBの型によって一定の型に制限されます。バッファの最大長は、LOB値を格納するために使用される外部データ型によって決まります。次の表では、有効な外部データ型および対応する最大長を、ソースLOB型単位に分類したものです。

表16-2 ソースLOBおよびプリコンパイラ・データ型

外部LOB(4) 内部LOB プリコンパイラ外部データ型 プリコンパイラ最大長(5) PL/SQLデータ型 PL/SQL最大長

BFILE

BLOB

RAW

VARRAW

LONG RAW

LONG VARRAW

65535

65533

2147483647

2147483643

RAW

32767

BFILE

CLOB

VARCHAR2

VARCHAR

LONG VARCHAR

65535

65533

2147483643

VARCHAR2

32767

BFILE

NCLOB

NVARCHAR2

4000

NVARCHAR2

4000

脚注 4 これらの外部データ型は、BFILEで使用できます。

脚注 5 長さは、文字単位ではなく、バイト単位で計算されています。

buflen (IN)

他の方法で指定できないときは、指定したバッファ長が指定されます。

使用上の注意

BFILEはデータベース・サーバーにあらかじめ存在し、入力ロケータを使用してオープンされている必要があります。データベースにはファイルを読み込む権限が、またユーザーにはディレクトリの読取り権限が必要です。

初期化されていないLOBまたはBFILEからの読込みは、エラーとなります。

バッファの長さは、次のように決定されます。

  • WITH LENGTH句を指定した場合の、buflenの値。

  • WITH LENGTH句がない場合は、OUTモードのバッファ・ホスト変数の指定によって決定されます。

16.4.16 TRIM

用途

LOB値を切り捨てます。

構文

EXEC SQL [AT [:]database] LOB TRIM :src TO :newlen ;

ホスト変数

src (IN OUT)

内部LOBのLOBロケータ。

newlen (IN)

LOB値の新しい長さ。

使用上の注意

この文はBFILEには使用できません。新規の長さは、現行の長さより大きくは設定できません。大きく設定した場合は、エラーが戻されます。

16.4.17 WRITE

用途

バッファの内容をLOBに書き込みます。

構文

EXEC SQL [AT [:]database] LOB WRITE [APPEND] [ FIRST | NEXT | LAST | ONE ]
    :amt FROM :buffer [WITH LENGTH :buflen] INTO :dst [AT :dst_offset] ;

ホスト変数

amt (IN OUT)

入力は、書き込まれる文字数またはバイト数です。

出力は、書き込まれた実際の文字数またはバイト数です。

ポーリング・メソッドを使用して書込みをした場合は、WRITE LAST文の実行後に、WRITE文実行時に書き込まれた累積合計長がamtから戻されます。WRITE文が中断された場合は、amtは定義されません。

buffer (IN)

LOBデータが書き込まれるバッファ。

dst (IN OUT)

LOBロケータ。

dst_offset (IN)

CLOBおよびNCLOBの場合は文字数単位、BLOBの場合はバイト数単位の、LOBの先頭からのオフセット(1から始まります)。

buflen (IN)

他の方法で計算できないときのバッファ長。

使用上の注意

LOBデータがすでに存在する場合は、バッファに格納されているデータで上書きされます。指定されたオフセットが、現在LOB内にあるデータの最後を超える場合は、ゼロバイト充填文字または空白がLOBに挿入されます。

WRITE文にキーワードAPPENDを指定すると、LOBの最後にデータが自動的に書き込まれます。APPENDを指定すると、宛先オフセットがLOBの最後とみなされます。WRITE文にAPPENDオプションを指定したときに、宛先オフセットを指定するとエラーになります。

バッファは、LOBに対して1ピースで書き込まれるか(デフォルトのONE方向変換を使用します)、標準のポーリング・メソッドを使用してピース単位で書き込むことができます。

FIRSTを使用してポーリングを開始し、NEXTで後続のピースを書き込みます。LASTキーワードは、書込みを終了する最後のピースの書込みに使用します。

このピース単位の書込みモードを使用すると、各ピースのサイズおよび場所が異なる場合に、バッファおよびバッファ長をコール単位に指定できます。

すべての書込みの終了後に渡される合計データ量が、amtパラメータで指定した量よりも少ない場合は、エラーになります。

同じルールが、READ文のバッファ長の決定にも適用されます。

16.4.18 DESCRIBE

用途

この文は複数のOCIおよびPL/SQL文に相当します(このため最後に保存します)。LOB DESCRIBE SQL文を使用してLOBから属性を取り出します。この機能は、OCIおよびPL/SQLプロシージャに似ています。LOB DESCRIBE文の書式は次のとおりです。

構文

EXEC SQL [AT [:]database] LOB DESCRIBE :src GET attribute1 [{, attributeN}]
   INTO :hv1 [[INDICATOR] :hv_ind1] [{, :hvN [[INDICATOR] :hv_indN] }] ;

次の属性を指定できます。

CHUNKSIZE | DIRECTORY | FILEEXISTS | FILENAME | ISOPEN | ISTEMPORARY | LENGTH

ホスト変数

src (IN):

内部または外部LOBのLOBロケータ。

hv1 ... hvN ... (OUT)

属性値を受け取るホスト変数。属性名リストで指定した順に指定します。

hv_ind1 ... hv_indN ... (OUT)

インジケータNULL状態を受け取るオプションのホスト変数。属性名リストで指定した順に指定します。

この表では、属性、対応付けられるLOBおよび読み込まれるC言語のデータ型を説明します。

表16-3 LOB属性

LOB属性 属性の説明 制限事項 C言語のデータ型

CHUNKSIZE

LOB値を格納するLOBチャンクに使用される領域の量(BLOBの場合はバイト数単位、CLOBまたはNCLOBの場合は文字数単位)。このチャンク・サイズの倍数でREADまたはWRITE要求を発行すると、パフォーマンスが向上します。すべてのWRITEを1つのチャンクで行うと、余分なバージョン化が実行されることも重複されることもありません。同一CHUNKに対してWRITEコールを複数回発行するかわりに、1つのチャンク内でWRITEコールを十分な数だけまとめることができます。

BLOB、CLOBおよびNCLOBのみ

unsigned int

DIRECTORY

BFILEの場合は、DIRECTORY別名。最大長は128バイトです。

FILE LOBのみ

char * 脚注6

FILEEXISTS

サーバーのオペレーティング・システムのファイル・システム上に、BFILEが存在するかどうかを決定します。ゼロ以外の場合は、FILEEXISTは真、ゼロの場合は、偽です。

FILE LOBのみ

signed int

FILENAME

BFILEの名前。最大長は255バイトです。

FILE LOBのみ

char*

ISOPEN

BFILEの場合は、入力BFILEロケータがOPEN文で使用されなかった場合は、このロケータからはOPENされていないものとみなされます。ただし、別のBFILEロケータによって、BFILEがOPENされていることもあります。複数のロケータを使用して、同一BFILE上で複数のOPENを実行することもできます。LOBの場合は、別のロケータによりLOBがOPENされた場合も、その入力ロケータによってOPENされているとみなされます。ゼロ以外の場合は、ISOPENは真、ゼロの場合は、偽です。

-

signed int

ISTEMPORARY

入力LOBロケータが一時LOBを参照するかどうかを決定します。ゼロ以外の場合は、ISTEMPORARYは真、ゼロの場合は、偽です。

BLOB、CLOBおよびNCLOBのみ

signed int

LENGTH

BLOBおよびBFILEの長さはバイト数単位、CLOBおよびNCLOBの長さは文字数単位で表されます。BFILEの場合、EOFが存在するときは、EOFも長さに含まれます。空の内部LOBは、ゼロ長になります。初期化されていないLOBおよびBFILEの長さは、定義されません。

-

unsigned int

脚注6

DIRECTORY属性およびFILENAME属性の場合は、CHARZ、STRING、VARCHAR、VARCHAR2およびCHARF以外の外部データ型はサポートされません。

使用上の注意

標識変数は、short型で宣言します。実行が完了すると、sqlca.sqlerrd[2]にはエラーなしで取り出された複数の属性が戻されます。実行エラーが発生した場合、エラーが発生した属性は、sqlca.sqlerrd[2]の内容より1つ多くなります。

DESCRIBEの例

ここで、任意のBFILEからDIRECTORYおよびFILENAME属性を抽出する、簡単なPro*C/C++の例を示します。

次のOCIBFileLocator宣言で型の解決およびコンパイルを正しく行うには、oci.hヘッダー・ファイルが必要です。

#include <oci.h>
...
OCIBFileLocator *bfile ;
char directory[31], filename[256] ;
short d_ind, f_ind ;

最後に、LOB表からBFILEロケータを選択し、DESCRIBEを実行します。

EXEC SQL ALLOCATE :bfile ;
EXEC SQL SELECT a_bfile INTO :bfile FROM lob_table WHERE ... ;
EXEC SQL LOB DESCRIBE :bfile
   GET DIRECTORY, FILENAME INTO :directory:d_ind, :filename:f_ind ;

標識変数は、DIRECTORYおよびFILENAME属性で使用するときにのみ有効です。属性値の保存に使用されるホスト変数バッファの大きさが不足している場合は、これらの属性値の文字列が切り捨てられることがあります。切捨てが発生した場合は、インジケータの値には属性の元の長さが設定されます。

16.5 LOBおよびナビゲーショナル・アクセス用インタフェース

ナビゲーショナル・アクセス用インタフェースは、LOBを属性として含むオブジェクト型の操作に使用することもできます。

16.5.1 一時オブジェクト

OBJECT CREATE文を使用して、LOB属性を持つ一時および永続オブジェクトを作成します。テンポラリLOBを一時オブジェクトのLOB属性にASSIGNし、永続LOBまたは永続オブジェクトのLOB属性に値をコピーしてデータを保存します。または、テンポラリLOBをLOB属性にASSIGNし、FLUSHを使用してデータベースに値を書き込みます。

BFILE属性の一時オブジェクトを作成して、ディスク上のBFILEからデータを読み込むことができます。テンポラリBFILEはサポートされていません。

16.5.2 永続オブジェクト

内部LOB属性が格納されたオブジェクト・キャッシュに永続オブジェクトを作成すると、LOB属性は暗黙的に空に設定されます。まず、OBJECT FLUSH文を使用してこのオブジェクトをフラッシュし、表に行を挿入して空のLOBを作成する必要があります。オブジェクト・キャッシュのオブジェクトを(VERSION=LATESTオプションを使用して)リフレッシュすると、実際のロケータが属性に読み込まれます。

BFILE属性のオブジェクトを作成すると、BFILEはNULLに設定されます。BFILEを読み込む前に、有効なディレクトリ別名およびファイル名で更新する必要があります。

テンポラリLOBは、永続オブジェクトのLOB属性にASSIGNされることがあります。オブジェクトがフラッシュされると、実際のLOB値がコピーされます。COPY文でテンポラリLOBロケータおよびLOB属性のロケータを使用して、テンポラリLOBの値を永続オブジェクトのLOB属性に明示的にコピーすることもできます。

16.5.3 ナビゲーショナル・アクセス用インタフェースの例

ナビゲーショナル・アクセス用インタフェースでLOBを処理する場合は、OBJECT GETおよびSET文を使用します。

オブジェクト型の属性のLOBロケータを取り出し、新しい埋込みSQL LOB文で使用できます。OBJECT SET文を使用して、LOBロケータをオブジェクト型の属性に戻します。

この場合、直接LOB ASSIGN操作を実行した場合と同じ結果になります。型の変更など、LOB ASSIGNが実行された場合に適用されるオブジェクト型に対して、LOB属性のOBJECT GETまたはSETを実行した場合にもこのルールが適用されます。

たとえば、次の簡単な型の定義を仮定します。

CREATE TYPE lob_type AS OBJECT (a_blob BLOB) ;

この例では、この型を有効な(初期化済の)BLOB属性を持つデータベースの列とみなします。

Pro*C/C++で使用できるOTT生成のC言語の構造体は、次のようになります。

struct lob_type
{
   OCIBlobLocator *a_blob ;
} ;
typedef struct lob_type lob_type ;

Pro*C/C++プログラムを作成して、DESCRIBE文でBLOB属性を抽出し、BLOBの現行の長さを取り出します。次に、TRIMでBLOBのサイズを半分に調整し、SET OBJECTで属性を元に戻してから、OBJECT FLUSHで変更を有効にします。

まず、oci.hをインクルードし、一部のローカル変数を宣言します。

#include <oci.h>
lob_type *lob_type_p ;
OCIBlobLocator *blob = (OCIBlobLocator *)0 ;
unsigned int length ;

オブジェクトからBLOB属性を選択し、OBJECT GETおよびDESCRIBEを行ってBLOBの現行の長さを取得します。

EXEC SQL ALLOCATE :blob ;
EXEC SQL SELECT a_column
   INTO :lob_type_p FROM a_table WHERE ... FOR UPDATE ;
EXEC SQL OBJECT GET a_blob FROM :lob_type_p INTO :blob ;
EXEC SQL LOB DESCRIBE :blob GET LENGTH INTO :length ;

長さを半分にし、BLOBを新しい長さにTRIMします。

length = (unsigned int)(length / 2) ;
EXEC SQL LOB TRIM :blob TO :length ;

BLOBが変更されると、BLOB属性をオブジェクトに戻し、変更をサーバーにFLUSHし、コミットします。

EXEC SQL OBJECT SET a_blob OF :lob_type_p TO :blob ;
EXEC SQL OBJECT FLUSH :lob_type_p ;
EXEC SQL FREE :blob ;
EXEC SQL COMMIT WORK ;

関連項目:

OTTのINTYPEファイルの作成方法とOTTの実行方法については、Object Type Translator(OTT)を参照してください。

16.6 LOBプログラムの例

BFILEおよびBLOBの読込みおよび書込みの方法について、2つの例をあげます。

16.6.1 BLOBのREADおよびファイル書込みの例

この例では、長さが不明な任意の長さのBLOBからデータをバッファに読み込み、バッファから外部ファイルにそのデータを書き込みます。バッファが小さいため、読み込むBLOBのサイズに応じて、1つのREAD文でBLOB値をバッファに読み込める場合もありますが、標準ポーリング・モードを使用する必要がある場合もあります。

まず、oci.hおよび簡単なローカル変数を宣言します。

#include <oci.h>
OCIBlobLocator *blob ;
FILE *fp ;
unsigned int amt, offset = 1 ;

BLOB値を格納し、ファイルに書き込むバッファが必要です。

#define MAXBUFLEN 5000
unsigned char buffer[MAXBUFLEN] ;
EXEC SQL VAR buffer IS RAW(MAXBUFLEN) ;

BLOBホスト変数を割り当て、READするBLOBを選択します。

EXEC SQL ALLOCATE :blob ;
EXEC SQL SELECT a_blob INTO :blob FROM lob_table WHERE ... ;

BLOB値を書き込む外部ファイルをオープンします。

fp = fopen((const char *)"image.gif", (const char *)"w") ;

1回のREADですべてのLOB値をバッファに読み込める場合は、シグナルLOB READの終了に対してNOT FOUND条件を取得する必要があります。

EXEC SQL WHENEVER NOT FOUND GOTO end_of_lob ;

最初のREADを行います。量パラメータは、最大値の4GBに設定します。バッファより大きいため、LOBを読み込めない場合は、ポーリング・モードを使用してREADします。

amt = 4294967295 ;
EXEC SQL LOB READ :amt FROM :blob AT :offset INTO :buffer ;

この例の場合、バッファの大きさが不足しているためLOB値をすべて格納できないので、読込み済のデータはバイナリI/Oを使用して書込みおよび読込みを続行します。

(void) fwrite((void *)buffer, (size_t)MAXBUFLEN, (size_t)1, fp) ;

標準ポーリング・モードを使用して、無限ループ内でLOB READによって読込みを続行します。ループを終了するには、NOT FOUND条件を設定します。

EXEC SQL WHENEVER NOT FOUND DO break ;
while (TRUE)
  {

ポーリング中はオフセットが使用されないため、後続のLOB READでは省略できます。ただし、最後のREADのコールでREADされた量が通知されるようにするため、量パラメータを指定します。

    EXEC SQL LOB READ :amt FROM :blob INTO :buffer ;
    (void) fwrite((void *)buffer, (size_t)MAXBUFLEN, (size_t)1, fp) ;
  }

LOB値の最後に到達しました。量パラメータには、READされた最後のピースの量が保存されます。ポーリング中は、中間の各ピースの量が、MAXBUFLEN、つまりバッファの最大サイズに設定されます。

end_of_lob:
(void) fwrite((void *)buffer, (size_t)amt, (size_t)1, fp) ;

この基本的な構造のコードでは、任意の長さの内部LOBがローカル・バッファにREADされ、外部ファイルに書き込まれます。OCIおよびPL/SQLをモデルにしています。

16.6.2 ファイルの読込みおよびBLOBのWRITEの例

この例では、長さが明確な任意の長さのファイルからデータをバッファに読み込み、バッファからデータを内部BLOBに書き込みます。バッファが小さいため、読み込むファイルのサイズに応じて、1つのWRITE文でファイル・データをLOBに書き込める場合もありますが、標準ポーリング・モードを使用する必要がある場合もあります。

まず、oci.hおよび簡単なローカル変数を宣言します。

#include <oci.h>
OCIBlobLocator *blob ;
FILE *fp ;
unsigned int amt, offset = 1 ;
unsigned filelen, remainder, nbytes ;
boolean last ;

ファイル・データを格納し、LOBに書き込むバッファが必要です。

#define MAXBUFLEN 5000
unsigned char buffer[MAXBUFLEN] ;
EXEC SQL VAR buffer IS RAW(MAXBUFLEN) ;

空の表の空のBLOBを初期化して、ALLOCATEされたロケータにそのBLOBを取り出し、ファイルからデータをコピーします。

EXEC SQL ALLOCATE :blob ;
EXEC SQL INSERT INTO lob_table (a_blob) VALUES (EMPTY_BLOB())
   RETURNING a_blob INTO :blob ;

バイナリ・ファイルをオープンして長さを決定します。BLOBに書き込む合計量が、バイナリ・ファイルの実際の長さになります。

fp = fopen((const char *)"image.gif", (const char *)"r") ;
(void) fseek(fp, 0L, SEEK_END) ;
filelen = (unsigned int)ftell(fp) ;
amt = filelen ;

バッファ・サイズに基づいて読み込むバイト数を決定し、ファイルの初期読込みを設定します。

if (filelen > MAXBUFLEN)
    nbytes = MAXBUFLEN ;
else
    nbytes = filelen ;

ファイルI/O操作を発行してnバイトのデータをファイルfpからバッファに読み込み、残りの読込み量を決定します。ファイルの先頭から読込みを開始します。

(void) fseek(fp, 0L, SEEK_SET) ;
(void) fread((void *)buffer, (size_t)nbytes, (size_t)1, fp) ;
remainder = filelen - nbytes ;

残りの読込み量に応じて、1ピースでバッファに書き込むか、ポーリングを開始し、複数の小さなピース単位でファイルのデータを書き込むポーリングを開始します。

     if (remainder == 0)
     {

この場合は、1ピースでデータを書き込みます。

        EXEC SQL LOB WRITE ONE :amt
           FROM :buffer INTO :blob AT :offset ;
      }
     else
      {

ポーリング・メソッドを開始し、ピース単位でデータをLOBに書き込みます。ポーリング・メソッドを開始するには、まず、最初のWRITEでFIRSTキーワードを使用します。

        EXEC SQL LOB WRITE FIRST :amt
           FROM :buffer INTO :blob AT :offset ;

簡単なループを設定し、ポーリング・モードを実装します。

        last = FALSE ;
        EXEC SQL WHENEVER SQLERROR DO break ;
        do
          {

ファイルから読み込み、宛先LOBにWRITEするバイト数を計算します。また、読み込んだピースがLASTピースかどうかを判断します。

            if (remainder > MAXBUFLEN)
                nbytes = MAXBUFLEN ;
            else
                {
                    nbytes = remainder ;
                    last = TRUE ;
                }

ファイル・システムのファイルから次のnbytesをバッファに読み込みます。ファイル読込み中にエラーが発生した場合は、自動的に次のWRITEがLASTになるように設定します。

            if  fread((void *)buffer, (size_t)nbytes, (size_t)1, fp) != 1)
               last = TRUE ;

ここで、LASTピースをWRITEするか、中間のNEXTピースをWRITEします。NEXTピースは、ファイルから読み込まれるデータが残っていることを示しています。

           if (last)
             {  
               EXEC SQL LOB WRITE LAST :amt
                  FROM :buffer INTO :blob  ;
             }
           
           else
             {
               EXEC SQL LOB WRITE NEXT :amt
                  FROM :buffer INTO :blob  ;
             }
           remainder -= nbytes ;
          } 
while (!last && !feof(fp)) ;

このコード例では、任意の長さのファイルをローカル・バッファに読み込み、LOBに書き込みます。これは、OCIの例をモデルにしています。

16.6.3 lobdemo1.pc

このプログラムlobdemo1.pcは、LOB埋込みSQL文の例です。ソース・コードは、demoディレクトリにあります。このアプリケーションでは、社会保障番号列、名前列および交通違反の集計が格納されているCLOB列で構成されるlicense_tableという表を使用します。標準的な自動車部門の簡単なSQL操作をモデルにしています。

次の操作を指定できます。

  • 新しいレコードを追加します。

  • 社会保障番号別のレコード・リストを出力します。

  • 特定の社会保障番号で指定して、レコード情報のリストを出力します。

  • 既存のCLOBの内容に新しい交通違反を追加します。

/***************************************************************************
  
  SCENARIO: 
  
  We consider the example of a database used to store driver's
  licenses. The licenses are stored as rows of a table containing
  three columns: the sss number of a person, his name in text and the 
  text summary of the info found in his license.

  The sss number is the driver's unique social security number.

  The name is the driver's given name as found on his ID card.

  The text summary is a summary of the information on the driver,
  including his driving record, which can be arbitrarily long and may
  contain comments and data regarding the person's driving ability. 

  APPLICATION OVERVIEW:

  This example demonstrate how a Pro*C client can handle the new LOB
  datatypes through PL/SQL routines. Demonstrated are mechanisms for
  accessing and storing lobs to tables and manipulating LOBs through
  the stored procedures available through the dbms_lob package.

****************************************************************************/

/***************************************************************************

   To run the demo:

   1. Execute the script, lobdemo1c.sql in SQL*Plus
   2. Precompile using Pro*C/C++
        proc lobdemo1 user=scott/tiger sqlcheck=full
   3. Compile/Link (This step is platform specific)

****************************************************************************/

/*** The following will be added to the creation script for this example ***
 *** This code can be found in lobdemo1c.sql				 ***

connect scott/tiger;

set serveroutput on;

Rem Make sure database has no license_table floating around

drop table license_table;

Rem ABSTRACTION:
Rem A license table reduces the notion of a driver's license into three 
Rem distinct components - a unique social security number (sss), 
Rem a name (name), and a text summary of miscellaneous information.

Rem IMPLEMENTATION:
Rem Our implementation follows this abstraction

create table license_table(
  sss char(9),
  name varchar2(50),
  txt_summary clob);

insert into license_table 
	values('971517006', 'Dennis Kernighan', 
	'Wearing a Bright Orange Shirt - 31 Oct 1996');

insert into license_table 
	values('555001212', 'Eight H. Number', 
	'Driving Under the Influence - 1 Jan 1997');

insert into license_table 
 	values('010101010', 'P. Doughboy', 
	'Impersonating An Oracle Employee - 10 Jan 1997');

insert into license_table
 	values('555377012', 'Calvin N. Hobbes', 
	'Driving Under the Influence - 30 Nov 1996');

select count(*) from license_table;

Rem Commit to save
commit;

****************************************************************************/

/**************************
 * Begin lobdemo1.pc code *
 **************************/

#define EX_SUCCESS       0
#define EX_FAILURE       1

#ifndef STDIO
# include <stdio.h>
#endif /* STDIO */

#ifndef SQLCA_ORACLE
# include <sqlca.h>
#endif /* SQLCA_ORACLE */

#ifndef OCI_ORACLE
# include <oci.h>
#endif /* OCI_ORACLE */

#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

#ifndef LOBDEMO1_ORACLE
# include "lobdemo1.h"
#endif /* LOBDEMO1_ORACLE */

/***********
 * Defines *
 ***********/
#define SSS_LENGTH 12
#define NAME_LENGTH 50 /* corresponds with max length of name in table */
#define BUFLEN 1024
#define MAXCRIME 5
#define DATELENGTH 12

/***********
 * Globals *
 ***********/

char *CrimeList[MAXCRIME]={ "Driving Under the Influence", 
			    "Grand Theft Auto", 
			    "Driving Without a License",
			    "Impersonating an Oracle Employee",
			    "Wearing a Bright Orange Shirt" };

char curdate[DATELENGTH];

/*********************** 
 * Function prototypes *
 ***********************/

#if defined(__STDC__)
  void GetDate( void );
  void PrintSQLError( void );
  void Driver( void );
  void ListRecords( void );
  void PrintCrime( OCIClobLocator *a_clob );
  void GetRecord( void );
  void NewRecord( void );
  char *NewCrime( void );
  void GetName( char *name_holder );
  void AppendToClob( OCIClobLocator *a_clob, char *charbuf );
  void AddCrime( void );
  void ReadClob( OCIClobLocator *a_clob );
  boolean GetSSS( char *suggested_sss );
#else
  void GetDate();
  void PrintSQLError( );
  void Driver( );
  void ListRecords( );
  void PrintCrime(/* OCIClobLocator *a_clob */);
  void GetRecord( );
  void NewRecord( );
  char *NewCrime( );
  void GetName(/* char *name_holder */);
  void AppendToClob(/* OCIClobLocator *a_clob, char *charbuf */);
  void AddCrime();
  boolean GetSSS(/* char *suggested_sss */);
#endif

/* 
 * NAME
 *   GetDate
 * DESCRIPTION
 *   Get date from user
 * LOB FEATURES
 *   none
 */ 

void GetDate()
{
  time_t now;

  now = time(NULL);
  strftime(curdate, 100, " - %d %b %Y", localtime(&now));
}

main()
{
  char * uid = "scott/tiger";

  EXEC SQL WHENEVER SQLERROR DO PrintSQLError();

  printf("Connecting to license database account: %s \n", uid);
  EXEC SQL CONNECT :uid;
  
  GetDate();

  printf("\t*******************************\n");
  printf("\t* Welcome to the DMV Database *\n");
  printf("\t*******************************\n\n");
  printf("Today's Date is%s\n", curdate);

  Driver();

  EXEC SQL COMMIT RELEASE;

  return (EX_SUCCESS);
}

/* 
 * NAME
 *   Driver
 * DESCRIPTION
 *   Command Dispatch Routine
 * LOB FEATURES
 *   none
 */ 

void Driver()
{
  char choice[20];
  boolean done = FALSE;

  while (!done)
  {
    printf("\nLicense Options:\n");
    printf("\t(L)ist available records by SSS number\n");
    printf("\t(G)et information on a particular record\n");
    printf("\t(A)dd crime to a record\n");
    printf("\t(I)nsert new record to database\n");
    printf("\t(Q)uit\n");
    printf("Enter your choice: ");

    fgets(choice, 20, stdin);
    switch(toupper(choice[0]))
    {
    case 'L':
      ListRecords();
      break;
    case 'G':
      GetRecord();
      break;
    case 'A':
      AddCrime();
      break;
    case 'I': 
      NewRecord();
      break;
    case 'Q':
      done = TRUE;
      break;
    default:
      break;
    }
  }
}

/* 
 * NAME
 *   ListRecords
 * DESCRIPTION
 *   List available records by sss number
 * LOB FEATURES
 *   none
 */ 

void ListRecords()
{
  char *select_sss = "SELECT SSS FROM LICENSE_TABLE";
  char sss[10];

  EXEC SQL PREPARE sss_exec FROM :select_sss;
  EXEC SQL DECLARE sss_cursor CURSOR FOR sss_exec;
  EXEC SQL OPEN sss_cursor;

  printf("Available records:\n");

  EXEC SQL WHENEVER NOT FOUND DO break;
  while (TRUE)
    {
      EXEC SQL FETCH sss_cursor INTO :sss;
      printf("\t%s\n", sss);
    }
  EXEC SQL WHENEVER NOT FOUND CONTINUE;

  EXEC SQL CLOSE sss_cursor;
}


/* 
 * NAME
 *   PrintCrime
 * DESCRIPTION
 *   Tests correctness of clob
 * LOB FEATURES
 *   OCIlobRead and OCILobGetLength
 */

void PrintCrime(a_clob)
  OCIClobLocator *a_clob; 
{ 
  ub4 lenp; 

  printf("\n");
  printf("=====================\n");
  printf(" CRIME SHEET SUMMARY \n");
  printf("=====================\n\n");

  EXEC SQL LOB DESCRIBE :a_clob GET LENGTH INTO :lenp;

  if(lenp == 0) /* No crime on file */
    {
      printf("Record is clean\n");
    }
  else
    {
      ub4 amt = lenp;
      varchar *the_string = (varchar *)malloc(2 + lenp);

      the_string->len = (ub2)lenp;      

      EXEC SQL WHENEVER NOT FOUND CONTINUE;
      EXEC SQL LOB READ :amt
        FROM :a_clob INTO :the_string WITH LENGTH :lenp;
         
      printf("%.*s\n", the_string->len, the_string->arr);
      free(the_string);
    }
}

/* 
 * NAME
 *   GetRecord
 * DESCRIPTION
 *   Get license of single individual
 * LOB FEATURES
 *   allocate and select of blob and clob
 */ 

void GetRecord()
{
  char sss[SSS_LENGTH];
  
  if(GetSSS(sss) == TRUE)
    {
      OCIClobLocator *license_txt;
      char name[NAME_LENGTH]={'\0'};
      
      EXEC SQL ALLOCATE :license_txt;

      EXEC SQL SELECT name, txt_summary INTO :name, :license_txt 
	FROM license_table WHERE sss = :sss;
      
      printf("========================================================\n\n");
      printf("NAME: %s\tSSS: %s\n", name, sss);
      PrintCrime(license_txt);
      printf("\n\n========================================================\n");

      EXEC SQL FREE :license_txt;
    }
  else
    {
      printf("SSS Number Not Found\n");
    }
}

/* 
 * NAME
 *   NewRecord
 * DESCRIPTION
 *   Create new record in database
 * LOB FEATURES
 *   EMPTY_CLOB() and OCILobWrite
 */ 

void NewRecord()
{
  char sss[SSS_LENGTH], name[NAME_LENGTH] = {'\0'};
  
  if(GetSSS(sss) == TRUE)
    {
      printf("Record with that sss number already exists.\n");
      return;
    }
  else
    {
      OCIClobLocator *license_txt;
      
      EXEC SQL ALLOCATE :license_txt;
      
      GetName(name);

      EXEC SQL INSERT INTO license_table 
	VALUES (:sss, :name, empty_clob());

      EXEC SQL SELECT TXT_SUMMARY INTO :license_txt FROM LICENSE_TABLE
	WHERE SSS = :sss;

      printf("========================================================\n\n");
      printf("NAME: %s\tSSS: %s\n", name, sss);
      PrintCrime(license_txt);
      printf("\n\n========================================================\n");

      EXEC SQL FREE :license_txt;
    }
}

/* 
 * NAME
 *   NewCrime
 * DESCRIPTION
 *   Query user for new crime
 * LOB FEATURES
 *   None
 */ 

char *NewCrime()
{
  int  SuggestedCrimeNo;
  int  i;
  char crime[10];

  printf("Select from the following:\n");
  for(i = 1; i <= MAXCRIME; i++)
    printf("(%d) %s\n", i, CrimeList[i-1]);

  printf("Crime (1-5): ");
  fgets(crime, 10, stdin);
  SuggestedCrimeNo = atoi(crime);

  while((SuggestedCrimeNo < 1) || (SuggestedCrimeNo > MAXCRIME))
    {
      printf("Invalid selection\n");
      printf("Crime (1-5): ");
      fgets(crime, 10, stdin);
      SuggestedCrimeNo = atoi(crime);
    }
  
  return CrimeList[SuggestedCrimeNo-1];
}

/* 
 * NAME
 *   AppendToClob
 * DESCRIPTION
 *   Append String charbuf to a Clob in the following way:
 *   if the contents of the clob a_clob were <foo> and the
 *   contents of charbuf were <bar>, after the append a_clob
 *   will contain: <foo>\n<bar> - <curdate>
 *   where <curdate> is today's date as obtained by the
 *   GetDate procedure.
 * LOB FEATURES
 *   OCILobWrite
 * NOTE
 *   Potentially, charbuf can be a very large string buffer.
 *   Furthermore, it should be noted that lobs and lob
 *   performance were designed for large data. Therefore, 
 *   users are encouraged to read and write large chunks of
 *   data to lobs. 
 */ 

void AppendToClob(a_clob, charbuf)
  OCIClobLocator *a_clob;
  char *charbuf;
{
  ub4 ClobLen, WriteAmt, Offset;
  int CharLen = strlen(charbuf);
  int NewCharbufLen = CharLen + DATELENGTH + 4; 
  varchar *NewCharbuf;
 
  NewCharbuf = (varchar *)malloc(2 + NewCharbufLen);

  NewCharbuf->arr[0] = '\n';
  NewCharbuf->arr[1] = '\0';
  strcat((char *)NewCharbuf->arr, charbuf);
  NewCharbuf->arr[CharLen + 1] = '\0';
  strcat((char *)NewCharbuf->arr, curdate);

  NewCharbuf->len = NewCharbufLen;

  EXEC SQL LOB DESCRIBE :a_clob GET LENGTH INTO :ClobLen;

  WriteAmt = NewCharbufLen;
  Offset = ClobLen + 1;

  EXEC SQL LOB WRITE ONE :WriteAmt FROM :NewCharbuf
    WITH LENGTH :NewCharbufLen INTO :a_clob AT :Offset;

  free(NewCharbuf);
}

/* 
 * NAME
 *   AddCrime
 * DESCRIPTION
 *   Add a crime to a citizen's crime file
 * LOB FEATURES
 *   OCILobWrite
 */ 

void AddCrime()
{
  char sss[SSS_LENGTH];

  if (GetSSS(sss) == TRUE)
    {
      OCIClobLocator *license_txt;
      char *crimebuf;
      char  name[NAME_LENGTH] = {'\0'};
      
      EXEC SQL ALLOCATE :license_txt;	
      
      EXEC SQL SELECT txt_summary INTO :license_txt FROM license_table
	WHERE sss = :sss FOR UPDATE; 

      crimebuf = NewCrime();

      printf("Added %s to CrimeList\n", crimebuf);
      AppendToClob(license_txt, crimebuf);

      EXEC SQL SELECT name INTO :name FROM license_table WHERE sss = :sss;

      printf("NAME: %s SSS: %s\n", name, sss);
      PrintCrime(license_txt);

      EXEC SQL COMMIT;
      EXEC SQL FREE :license_txt;
    }
  else
    {
      printf("SSS Number Not Found\n");
    }
}

/* 
 * NAME
 *   GetSSS
 * DESCRIPTION
 *   Fills the passed buffer with a client-supplied social security number
 *   Returns FALSE if sss does not correspond to any entry in the database,
 *   else returns TRUE
 * LOB FEATURES
 *   none
 */

boolean GetSSS(suggested_sss)
  char *suggested_sss;
{
  int count = 0;
  int i;

  printf("Social Security Number: ");
  fgets(suggested_sss, SSS_LENGTH, stdin);

  for(i = 0; ((suggested_sss[i] != '\0') && (i < SSS_LENGTH)); i++)
    {
      if(suggested_sss[i] == '\n') 
	suggested_sss[i]='\0';
    }

  EXEC SQL SELECT COUNT(*) INTO :count FROM license_table 
    WHERE sss = :suggested_sss;

  return (count != 0);
}

/* 
 * NAME
 *   GetName
 * DESCRIPTION
 *   Get name from user. 
 *   
 * LOB FEATURES
 *   none
 */

void GetName(name_holder)
  char *name_holder;
{
  int count=0;
  int i;

  printf("Enter Name: ");
  fgets(name_holder, NAME_LENGTH + 1, stdin);

  for(i = 0; name_holder[i] != '\0'; i++)
    {
      if(name_holder[i] == '\n') 
	name_holder[i]='\0';
    }

  return;
}

/* 
 * NAME
 *   PrintSQLError
 * DESCRIPTION
 *   Prints an error message using info in sqlca and calls exit.
 * COLLECTION FEATURES
 *   none
 */ 

void PrintSQLError()
{
  EXEC SQL WHENEVER SQLERROR CONTINUE;
  printf("SQL error occurred...\n");
  printf("%.*s\n", (int)sqlca.sqlerrm.sqlerrml,
         (CONST char *)sqlca.sqlerrm.sqlerrmc);
  EXEC SQL ROLLBACK RELEASE;
  exit(EX_FAILURE);
}