ヘッダーをスキップ
Pro*C/C++プログラマーズ・ガイド
11gリリース2(11.2)
B61343-01
  ドキュメント・ライブラリへ
ライブラリ
製品リストへ
製品
目次
目次
索引
索引

戻る
戻る
 
次へ
次へ
 

16 LOB

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

4種類のLOBについて紹介し、従来のLONGおよびLONG RAWデータ型と比較します。

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

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

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

この章の項目は、次のとおりです。

LOB

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

内部LOB

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

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

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

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

外部LOB

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

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

BFILEのセキュリティ

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

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

  • データ操作言語(DML)のSQL文は、DIRECTORYオブジェクトのシステム読取り権限やオブジェクト読取り権限のGRANTおよびREVOKEに使用します。

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

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

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

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

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

DIRECTORYおよびBFILEのセキュリティの詳細は、『Oracle Databaseアドバンスト・アプリケーション開発者ガイド』を参照してください。GRANTコマンドの詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

LOBとLONGおよびLONG RAWの対比

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

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

  • LOBに対しては順次アクセスに加えてランダム・アクセスも可能です。LONGおよびLONG RAWに対しては順次アクセスのみが可能です。

  • LOB(NCLOBを除く)は、定義対象のオブジェクト型の属性です。

  • 表に複数のLOB列を含めることができますが、LONGおよびLONG RAWは1列しか設定できません。

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

LOBロケータ

LOBロケータは、LOBの実際の内容を指しています。LOBを取り出すと、LOBの内容ではなくロケータが戻されます。LOBロケータは、1つのトランザクションまたはセッションで保存し、別のトランザクションまたはセッションで使用することはできません。

テンポラリLOB

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

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

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

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

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

バッファリングには、LOBの特定の領域に対する少量データの読込みと書込みを何度も実行するクライアント上のアプリケーションの場合に、特に次のような利点があります。

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

  • また、バッファリングを使用すると、サーバー上でのLOB更新の合計回数も減少します。このため、LOBのパフォーマンスが向上し、ディスク領域を節約できます。

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

LOBのバッファへの読込み/書込みは、ロケータを使用して行われます。バッファリングで使用可能にしたロケータは、書込みを実行するまで一貫してLOBの読込み機能を提供します。

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

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

LBSの詳細は、『Oracle Databaseアドバンスト・アプリケーション開発者ガイド』を参照してください。

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

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

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です。

アプリケーションの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環境変数を設定する必要があります。


    関連項目

    「NLS_CHAR」

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

/* 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 ;

LOBの初期化

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

内部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) ;

外部LOB

次の方法で、LOB FILE SET文を使用して、BFILEのDIRECTORY別名およびFILENAMEを初期化します。

#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ロケータを使用してファイルにアクセスしたときに、これらが確認され、ファイルにアクセスできない場合にはエラーが戻されます。

テンポラリLOB

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

LOBの解放

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

EXEC SQL FREE :a_blob;

LOB文のルール

LOB文を使用するときのルールを次に示します。

すべての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操作に使用されるバッファ・キャッシュを指定するメカニズムもあります。このインタフェースでは、これらの機能はサポートされていません。

LOBバッファリング・サブシステムに対するルール

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

  • 読込みアクセスまたは書込みアクセスのエラーは、次のサーバーへのアクセス時にレポートされます。このため、エラー・リカバリのコーディングはユーザーが行う必要があります。

  • バッファに書き込まれたLOBを更新するときは、LOBバッファリング・サブシステムを経由せずに更新しないでください。

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

  • バッファリングが使用可能な更新されたロケータを、ASSIGN文で別のロケータに割り当てることはできません。

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

  • ホスト変数であるロケータ・バインド変数とデータベース・サーバー側の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する必要があります。

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

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

  • srcおよびdstから内部ロケータまたは外部LOBロケータを参照できますが、fileから参照できるのは外部ロケータのみです。

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

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

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

LOB文

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

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まで拡張できます。4GBを超えてLOBを拡張すると、エラーが発生します。

ソースLOBおよび宛先LOBはあらかじめ存在している必要があります。宛先LOBは初期化されている必要があります。

ソースLOBおよび宛先LOBは、同じ内部LOB型にする必要があります。ロケータのいずれかの型に対して、LOBバッファリングが使用可能になっている場合はエラーになります。

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値は、各LOBにつき1つのロケータに限られているためです。

CLOSE

用途

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

構文

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

ホスト変数

src (IN OUT)

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

使用上の注意

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

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

COPY

用途

LOB値の全部または一部を、2番目の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と同じルールです。

使用上の注意

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

新しく書き込まれたデータが現行の宛先LOBの長さを超える場合は、格納できるように宛先LOBが拡張されます。このLOBを4GBを超えて拡張すると、ランタイム・エラーになります。

初期化していないLOBからコピーする場合もエラーになります。

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

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

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

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は解放されます。テンポラリLOBに対するREADおよびWRITEでは、バッファ・キャッシュは経由されません。

DISABLE BUFFERING

用途

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

構文

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

ホスト変数

src (IN OUT)

内部LOBロケータ。

使用上の注意

この文ではBFILEはサポートされていません。この文以降の読込みまたは書込みでは、LBSは使用されません。


注意:

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

ENABLE BUFFERING

用途

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

構文

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

ホスト変数

src (IN OUT)

内部LOBロケータ。

使用上の注意

この文ではBFILEはサポートされていません。この文以降の読込みおよび書込みでは、LBSが使用されます。

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値を空白で上書きすることです。

FILE CLOSE ALL

用途

現行のセッションでオープンしているすべてのBFILEをクローズします。

構文

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

使用上の注意

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

FILE SET

用途

DIRECTORY別名を設定し、BFILEロケータに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別名の最大長は30バイトです。FILENAMEの最大長は255バイトです。

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

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

FLUSH BUFFER

用途

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

構文

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

ホスト変数

src (IN OUT)

内部LOBロケータ。

使用上の注意

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

LOBバッファリングは、入力LOBロケータに対してあらかじめ使用可能にする必要があります。

デフォルトでは、バッファ・リソースは、バッファされた別のLOB操作で再度割り当てられることがあるため、FLUSH操作では解放されません。ただし、バッファを明示的に解放する場合は、オプションのFREEキーワードを指定すると解放できます。

FREE TEMPORARY

用途

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

構文

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

ホスト変数

src (IN OUT)

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

使用上の注意

入力ロケータは、テンポラリLOBをポイントする必要があります。出力ロケータは、初期化されていないとマークされ、この文以降のLOB文で使用できます。

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

新しく書き込まれたデータが現行の宛先LOBの長さを超える場合は、格納できるように宛先LOBが拡張されます。このLOBを4GBを超えて拡張するとエラーになります。

初期化していないBFILEからコピーする場合もエラーになります。

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

OPEN

用途

読取り/書込みアクセスで使用する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すると、エラーが戻されます。

READ ONLYモードのLOBをオープンして、LOBにWRITEした場合もエラーになります。

READ

用途

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

構文

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

ホスト変数

amt (IN OUT)

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

読み込まれるバイト数がバッファ長よりも大きい場合は、LOBはポーリング・モードでREADされるとみなされます。入力時にこの値が0(ゼロ)の場合は、データはポーリング・モードで入力オフセットから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 1  内部LOB プリコンパイラ外部データ型 プリコンパイラ最大長  2  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


 1  BFILEで使用可能な外部データ型です。

 2  長さは文字数ではなくバイト数です。

buflen (IN)

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

使用上の注意

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

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

バッファ長は次の方法で決まります。

TRIM

用途

LOB値を切り捨てます。

構文

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

ホスト変数

src (IN OUT)

内部LOBのLOBロケータ。

newlen (IN)

LOB値の新しい長さ。

使用上の注意

この文はBFILEには使用できません。新しい長さを、現行の長さより大きくすることはできません。エラーが戻されます。

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された後のamtには、WRITE文の実行で書き込まれた累計の長さが戻されます。WRITE文が中断された場合、amtは未定義になります。

buffer (IN)

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


関連項目

データ型の長さは、「READ」を参照してください。

dst (IN OUT)

LOBロケータ。

dst_offset (IN)

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

buflen (IN)

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

使用上の注意

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

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

バッファは1ピース(デフォルトのONEキーワードを使用します)でLOBに書き込まれますが、標準ポーリング・モードを使用するとピース単位で書き込まれます。

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

このピース単位の書込みモードを使用したときは、各ピースのサイズが異なり、異なる場所から書き込まれる場合、各コールでバッファおよび長さが異なることがあります。

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

このルールは、READ文などでバッファ長を決定する場合にも適用されます。

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はすべてチャンク単位で行われます。チャンク単位以外のWRITEは行われません。重複してWRITEされることもありません。同一CHUNKに対して複数のWRITEコールを発行するかわりに、チャンクがいっぱいになるまでWRITEを蓄積できます。

BLOB、CLOBおよびNCLOBのみ

unsigned int

DIRECTORY

BFILEのDIRECTORY別名。最大長は30バイトです。

FILE LOBのみ

char *  1 

FILEEXISTS

サーバーのオペレーティング・システムのファイル・システム上に、BFILEが存在するかどうかを決定します。FILEEXISTSは0でないときは真で、0のときは偽です。

FILE LOBのみ

signed int

FILENAME

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

FILE LOBのみ

char*

ISOPEN

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

-

signed int

ISTEMPORARY

入力LOBロケータからテンポラリLOBを参照するかどうかを決定します。ISTEMPORARYは、0でないときは真で、0のときは偽です。

BLOB、CLOBおよびNCLOBのみ

signed int

LENGTH

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

-

unsigned int


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

使用上の注意

標識変数は、short型で宣言します。実行が完了すると、sqlca.sqlerrd[2]にはエラーなしで取り出された複数の属性が戻されます。実行エラーが発生した場合、エラーが発生したLOBの属性は、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属性で使用するときにのみ有効です。属性値の保存に使用されるホスト変数バッファの大きさが不足している場合は、これらの属性値の文字列が切り捨てられることがあります。切捨てが発生する場合、インジケータの値は属性の元の長さに設定されます。

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

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

一時オブジェクト

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

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

永続オブジェクト

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

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

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

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

ナビゲーショナル・アクセス用インタフェースで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言語の構造体は、次のようになります。


関連項目

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

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 ;

LOBプログラムの例

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

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をモデルにしています。詳細は、『Oracle Call Interfaceプログラマーズ・ガイド』の付録の例および『Oracle Databaseアドバンスト・アプリケーション開発者ガイド』の該当する章を参照してください。

ファイルの読込みおよび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の例をモデルにしています。詳細は、『Oracle Call Interfaceプログラマーズ・ガイド』の付録の例を参照してください。

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