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.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); }