13 ラージ・オブジェクト(LOB)

この章では、埋込みSQL文により提供されるLOB (ラージ・オブジェクト)データ型のサポートについて記述します。4種類のLOB型を説明し、従来のLONGおよびLONG RAWデータ型と比較します。

Pro*COBOLの埋込みSQLインタフェースは、PL/SQL言語と同様の機能を提供します。

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

LOBインタフェースを使用したPro*COBOLによるプログラミング例を説明します。

内容は次のとおりです。

13.1 LOBの使用方法

LOB (ラージ・オブジェクト)とは、ASCIIテキスト、各国文字のテキスト、様々なグラフィック・フォーマットのファイルおよびサウンド波形などの大量のデータ(最大4GB)を格納するために使用されるデータベース型のことです。

13.1.1 内部LOB

内部LOB (BLOB、CLOB、NCLOB)はデータベースの表領域に格納されます。また、内部LOBでは、データベース・サーバーのトランザクション・サポートが有効です。(COMMIT、ROLLBACKなどを使用できます。)

BLOB(Binary LOB)には、ビデオ・クリップなどの非構造化バイナリ(「ロー」とも呼ばれます)・データが格納されます。

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

NCLOB(National Character LOB)には、各国語キャラクタ・セットの文字列データのラージ・ブロックが格納されます。

13.1.2 外部LOB

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

BFILE(Binary Files)では、データは外部バイナリ・ファイルの形式で格納されます。BFILEでは、GIF、JPEG、MPEG、MPEG2、テキストなどのフォーマットを扱うことができます。

13.1.3 BFILEのセキュリティ

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

BFILEで使用できるSQL文は、次の2種類です。

  • データ定義言語(DDL) SQL文のCREATE、REPLACE、ALTERおよびDROP。

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

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

     EXEC SQL CREATE OR REPLACE DIRECTORY "Mydir" AS '/usr/home/mydir' END-EXEC.

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

     EXEC SQL GRANT READ ON DIRECTORY "Mydir" TO scott END-EXEC.

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

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

13.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アップグレード・ガイド』を、LOBの詳細は『Oracle Database SecureFilesおよびラージ・オブジェクト開発者ガイド』を参照してください。

13.1.5 LOBロケータ

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

13.1.6 一時LOB

LOBデータベースを使用しやすくするために、一時LOBを作成できます。一時LOBはローカル変数に似ていますが、表には関連付けられていません。一時LOBを作成したユーザーのみがロケータを使用してアクセスでき、セッションが終了すると削除されます。

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

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

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

LBS (LOB Buffering Subsystem)は、クライアントのアドレス空間で1つまたは複数のLOBバッファとして使用するためのユーザー・メモリー領域です。

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

  • LOBに対する複数の読取り/書込みをバッファに蓄積し、FLUSHディレクティブが実行されたときにサーバーに書き込むため、LBSによってサーバーへのラウンドトリップが減少します。

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

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

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

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

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

13.2 LOBの使用方法

Pro*COBOLからLOBにアクセスするには、次の2つの方法があります。

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

  • 埋込みSQL文。

埋込みSQL文は、PL/SQLインタフェースと同じ機能を使用できるように設計されています。

次の表では、PL/SQLからのLOBアクセスおよびPro*COBOLの埋込みSQL文を比較します。ハイフンは、機能がないことを示します。

表13-1 LOBへのアクセス方法

PL/SQL(1) Pro*COBOLの埋込みSQL

COMPARE()

-

INSTR()

-

SUBSTR()

-

APPEND()

APPEND

:=

ASSIGN

CLOSE()

CLOSE

COPY()

COPY

CREATETEMPORARY()

CREATE TEMPORARY

-

DISABLE BUFFERING

-

ENABLE BUFFERING

ERASE()

ERASE

GETCHUNKSIZE()

DESCRIBE

ISOPEN()

DESCRIBE

FILECLOSE()

CLOSE

FILECLOSEALL()

FILE CLOSE ALL

FILEEXISTS()

DESCRIBE

FILEGETNAME()

DESCRIBE

FILEISOPEN()

DESCRIBE

FILEOPEN()

OPEN

BFILENAME()

FILE SET脚注2

-

FLUSH BUFFER

FREETEMPORARY()

FREE TEMPORARY

GETLENGTH()

DESCRIBE

=

-

ISTEMPORARY()

DESCRIBE

LOADFROMFILE()

LOAD FROM FILE

OPEN()

OPEN

READ()

READ

TRIM()

TRIM

WRITE()

WRITE

WRITEAPPEND()

WRITE

脚注1 dbmslob.sqlからコールします。BFILENAMEを除くルーチンの前には、すべて'DBMS_LOB'を付ける必要があります。

脚注2

BFILENAME()組込みSQLファンクションを使用することもできます。

注意:

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

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

Pro*COBOLアプリケーションでLOBロケータを使用するには、次の擬似タイプを使用します。

  • SQL-BLOB

  • SQL-CLOB

  • SQL-NCLOB

  • SQL-BFILE

MY-NCLOBと呼ばれるNCLOB変数を宣言するには、次のようにします。

 01  MY-NCLOB    SQL-NCLOB.

13.2.2 LOBの初期化

この項では、様々なLOBの初期化方法を説明します。

13.2.2.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()) END-EXEC.

ALLOCATE文は、LOBロケータを割り当て、初期化して空にします。次のコードは、前の例と同じです。

...
 01  A-BLOB      SQL-BLOB.
 01  A-CLOB      SQL-CLOB.
...
     EXEC SQL ALLOCATE :A-BLOB END-EXEC.
     EXEC SQL ALLOCATE :A-CLOB END-EXEC.
     EXEC SQL INSERT INTO lob_table (a_blob, a_clob)
        VALUES (:A-BLOB, :A-CLOB) END-EXEC.
13.2.2.2 外部LOB

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

...
 01  ALIAS        PIC X(14) VARYING.
 01  FILENAME     PIC X(14) VARYING.
 01  A-BFILE      SQL-BFILE.
 ...
     MOVE "lob_dir" TO ALIAS-ARR.
     MOVE 7 TO ALIAS-LEN.
     MOVE "image.gif" TO FILENAME-ARR
     MOVE 9 TO FILENAME-LEN..     
     EXEC SQL ALLOCATE :A-BFILE END-EXEC.
     EXEC SQL LOB FILE SET :A-BFILE
        DIRECTORY = :ALIAS, FILENAME = :FILENAME END-EXEC.
     EXEC SQL INSERT INTO file_table (a_bfile) VALUES (:A-BFILE) END-EXEC.

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 :A-BFILE END-EXEC.

注意:

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

13.2.2.3 一時LOB

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

13.2.2.4 LOBの解放

ALLOCATE文で使用されるメモリーを解放するには、FREE文を使用します。

EXEC SQL FREE :A-BLOB END-EXEC.

13.3 LOB文のルール

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

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

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

  • 埋込みSQLのLOB文では、FOR句は使用できません。これらの文は、単一のLOBロケータのみ使用できます。ただし、ALLOCATEおよびFREE文では、FOR句を使用できます。

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

13.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のみ)、SELECTおよびTRIMの文を使用すると、エラーになります。また、これらの文をバッファリング不可能なロケータとともに使用した場合でも、そのロケータによってポイントされたLOBが他のロケータによってバッファ・モードでアクセスされた場合には、エラーが戻されます。

注意:

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

  • トランザクションのコミット。

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

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

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

LOB文の場合、次のルールおよび注意を使用します。

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

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

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

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

    注意:

    変数BLOB、CLOBおよびNCLOBは、使用するプラットフォームの位置合せの要件に従って使用する必要があります。使用するプラットフォームの位置合せの制限の詳細は、そのプラットフォームのマニュアルを参照してください。

13.4 LOB文

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

13.4.1 APPEND

用途

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

構文

     EXEC SQL [AT [:]database] LOB APPEND :src TO :dst END-EXEC.

ホスト変数

src (IN)

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

dsc (IN OUT)

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

使用上の注意

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

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

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

13.4.2 ASSIGN

用途

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

構文

     EXEC SQL [AT [:]database] LOB ASSIGN :src to :dst END-EXEC.

ホスト変数

src (IN)

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

dsc (IN OUT)

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

使用上の注意

割当て後は、両方のロケータは同じLOB値を参照します。宛先LOBロケータは、有効な初期化された(割り当てられた)ロケータであることが必要です。

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

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

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

13.4.3 CLOSE

用途

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

構文

     EXEC SQL [AT [:]database] LOB CLOSE :src END-EXEC.

ホスト変数

src (IN OUT)

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

使用上の注意

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

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

13.4.4 COPY

用途

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

構文

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

ホスト変数

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バッファリングは、ロケータのどちらに対しても使用可能にしないでください。

一時LOBを永続LOBにするには、COPY文を使用して、一時LOBを永続LOBに明示的にCOPYする必要があります。amt変数はコピーする最大量を示します。指定した量がコピーされる前にソースLOBの最後に到達した場合、エラーを発行せずに操作が終了します。

13.4.5 CREATE TEMPORARY

用途

一時LOBを作成します。

構文

     EXEC SQL [AT [:]database] LOB CREATE TEMPORARY :src END-EXEC.

ホスト変数

src (IN OUT)

実行前でINになったときは、srcは事前に割り当てられたLOBロケータです。

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

使用上の注意

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

セッション終了時に、すべての一時LOBは解放されます。一時LOBへの読取りおよび書込みは、バッファ・キャッシュを経由しません。

13.4.6 DISABLE BUFFERING

用途

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

構文

     EXEC SQL [AT [:]database] LOB DISABLE BUFFERING :src END-EXEC.

ホスト変数

src (IN OUT)

内部LOBロケータ。

使用上の注意

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

注意: 変更を永続的なものにするには、FLUSH BUFFERコマンドを使用してください。DISABLE BUFFERING文は、LOBバッファリング・サブシステムが作成した変更を、暗黙的にフラッシュすることはありません。

13.4.7 ENABLE BUFFERING

用途

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

構文

     EXEC SQL [AT [:]database] LOB ENABLE BUFFERING :src END-EXEC.

ホスト変数

src (IN OUT)

内部LOBロケータ。

使用上の注意

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

13.4.8 ERASE

用途

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

構文

     EXEC SQL [AT [:]database] LOB ERASE :amt 
        FROM :src [AT :src_offset] END-EXEC.

ホスト変数

amt (IN OUT)

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

src (IN OUT)

内部LOBロケータ。

src_offset (IN)

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

使用上の注意

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

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

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

13.4.9 FILE CLOSE ALL

用途

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

構文

     EXEC SQL [AT [:]database] LOB FILE CLOSE ALL END-EXEC.

使用上の注意

適切にクローズされなかったオープン・ファイルがセッションに存在する場合は、FILE CLOSE ALL文を使用して、セッションでオープンしているファイルをすべてクローズし、ファイル操作を始めから再開できます。

13.4.10 FILE SET

用途

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

構文

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

ホスト変数

file (IN OUT)

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

alias (IN)

設定するDIRECTORY別名。

filename (IN)

設定するFILENAME。

使用上の注意

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

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

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

DIRECTORY別名およびFILENAME属性がサポートされている外部データ型は、VARCHAR、VARCHAR2およびCHARFのみになります。

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

13.4.11 FLUSH BUFFER

用途

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

構文

     EXEC SQL [AT [:]database] LOB FLUSH BUFFER :src [FREE] END-EXEC.

ホスト変数

src (IN OUT)

内部LOBロケータ。

使用上の注意

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

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

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

13.4.12 FREE TEMPORARY

用途

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

構文

     EXEC SQL [AT [:]database] LOB FREE TEMPORARY :src END-EXEC.

ホスト変数

src (IN OUT)

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

使用上の注意

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

13.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] END-EXEC.

ホスト変数

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は、そのデータにあわせて拡張されます。4GBを超えてLOBが拡張されると、エラーになります。

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

amountパラメータは、ロードする最大量を示します。指定した量がロードされる前にソースBFILEの最後に到達した場合は、エラーを発行せずに操作が終了します。

13.4.14 OPEN

用途

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

構文

     EXEC SQL [AT [:]database] LOB OPEN :src 
        [ READ ONLY | READ WRITE ] END-EXEC.

ホスト変数

src (IN OUT)

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

使用上の注意

デフォルト・モードでは、LOBまたはBFILEはREAD ONLYアクセスでオープンされます。

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

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

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

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

13.4.15 READ

用途

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

構文

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

ホスト変数

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型ごとの最大長のリストです。

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

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

BFILE

BLOB

RAW

VARRAW

LONG RAW

LONG VARRAW

65535

65533

2147483647

2147483643

RAW

32767

-

CLOB

VARCHAR2

VARCHAR

LONG VARCHAR

65535

65533

2147483643

VARCHAR2

32767

-

NCLOB

NVARCHAR2

4000

NVARCHAR2

4000

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

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

buflen (IN)

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

使用上の注意

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

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

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

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

  • WITH LENGTH句を指定しなかった場合は、「文字データの処理」のルールに従ってバッファ・ホスト変数をOUTモードで処理すると長さが決定します。

13.4.16 TRIM

用途

LOB値を切り捨てます。

構文

     EXEC SQL [AT [:]database] LOB TRIM :src TO :newlen END-EXEC.

ホスト変数

src (IN OUT)

内部LOBのLOBロケータ。

newlen (IN)

LOB値の新しい長さ。

使用上の注意

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

13.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] END-EXEC.

ホスト変数

amt (IN OUT)

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

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

ポーリング・メソッドを使用して書込みをした場合は、WRITE LAST文の実行後に、WRITE文実行時に書き込まれた累積合計長がamtから戻されます。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オプションを指定したときに、宛先オフセットを指定するとエラーになります。

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

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

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

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

同じルールが、READ文のバッファ長の決定にも適用されます。「READ」を参照してください。

関連項目

13.4.18 DESCRIBE

用途

この文は、いくつかのOCIおよびPL/SQL文と同等です。LOB DESCRIBE SQL文を使用してLOBから属性を取り出します。LOB DESCRIBE文の書式は次のとおりです。

構文

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

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

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

ホスト変数

src (IN)

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

hv1 ... hvN (OUT)

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

hv_ind1 ... hv_indN (OUT)

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

次の表では、関連するLOBの属性および読込みが必要なCOBOL型を説明します。

表13-3 LOB属性

LOB属性 属性の説明 制限事項 COBOL型

CHUNKSIZE

LOB値の格納に使用される、指定された表領域ブロックに対する最適なスペース量(BLOBの場合はバイト、CLOBおよびNCLOBの場合は文字)です。チャンク・サイズは固定値ではなく、利用可能なチャンク・サイズに基づいた最大限の値が記憶域に利用されます。同一ページまたは同一ページ・セットで複数のWRITEリクエストを行った場合、データはコミット時のみディスクに書き込まれ、単一の変更として扱われます。これにより、より大きなチャンク・サイズでデータを書き込むことができます。

BLOB、CLOBおよびNCLOBのみ

PIC S9(9) COMP

DIRECTORY

BFILEの場合は、DIRECTORY別名。長さnは、1から128バイトです。実際の長さを使用します。

FILE LOBのみ

PIC X(n) [VARYING]

FILEEXISTS

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

FILE LOBのみ

PIC S9(9) COMP

FILENAME

BFILEの名前。長さnは、1から255バイトです。実際の長さを使用します。

FILE LOBのみ

PIC X(n) [VARYING]

ISOPEN

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

-

PIC S9(9) COMP

ISTEMPORARY

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

BLOB、CLOBおよびNCLOBのみ

PIC S9(9) COMP

LENGTH

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

-

PIC 9(9) COMP

使用上の注意

標識変数は、PIC S9(4) COMPとして宣言する必要があります。実行完了後、SQLERRD(3)には、エラーなしに取り出された属性の数が格納されます。実行エラーが発生した場合は、エラーが発生した属性の数はSQLERRD(3)の内容より1だけ多くなっています。

DESCRIBEの例

指定されたBFILEから、DIRECTORYおよびFILENAME属性を抽出する簡単なPro*COBOLの例です。

...
 01  A-BFILE      SQL-BFILE.
 01  DIRECTORY    PIC X(30) VARYING.
 01  FILENAME     PIC X(30) VARYING.
 01  D-IND        PIC S9(4) COMP.
 01  F-IND        PIC S9(4) COMP.
 01  FEXISTS      PIC S9(9) COMP.
 01  ISOPN        PIC S9(9) COMP.  
...

最後に、いくつかのLOB表からBFILEロケータを選択し、DESCRIBEします。

     EXEC SQL ALLOCATE :A-BFILE END-EXEC.
     EXEC SQL INSERT INTO lob_table (a_bfile) VALUES (BFILENAME ('lob.dir',
        'image.gif')) END-EXEC.
     EXEC SQL SELECT a_bfile INTO :A-BFILE FROM lob_table WHERE ... END-EXEC.
     EXEC SQL DESCRIBE :A-BFILE GET DIRECTORY, FILENAME, FILEEXISTS, ISOPEN
        INTO :DIRECTORY:D-IND, :FILENAME:F-IND, FEXISTS, ISOPN ND-EXEC.

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

13.4.19 ポーリング・メソッドを使用したREADおよびWRITE

ポーリング・メソッドでREADを行うときの例です。

最初のLOB READで量にゼロを設定(または読み込まれる全データのサイズ量を設定)し、読込みポーリングを開始します。次の例では、この量にゼロが初期設定されます(詳細は省略してあります)。

      EXEC SQL ALLOCATE :CLOB1 END-EXEC.

      EXEC SQL WHENEVER NOT FOUND GOTO END-OF-CLOB END-EXEC.

      EXEC SQL SELECT A_CLOB INTO :CLOB1 FROM LOB_TABLE WHERE ... END-EXEC.

      MOVE 0 TO AMT.
      EXEC SQL LOB READ :AMT FROM :VLOB1 AT :OFFSET INTO :BUFFER END-EXEC.

 READ-LOOP.
      EXEC SQL LOB READ :AMT FROM :CLOB1 INTO BUFFER $END-EXEC.
      GO TO READ-LOOP.

 END-OF-CLOB.     
      EXEC SQL WHENEVER NOT FOUND CONTINUE END-EXEC.

      EXEC SQL FREE :CLOB1 END-EXEC.

次のコードは、バッファから内部CLOBへのデータの書込みの例です。初期書込み文のAMT (16文字)値は、書き込むデータ全体の長さと同じである必要があります。バッファの長さは5文字です。

初期読取りでEOFが読み取られた場合は、LOB WRITE ONEが行われます。EOFが読み込まれなかった場合は、バッファのLOB WRITE FIRSTによってポーリングが開始されます。データが読み込まれ、出力としてLOB WRITE NEXTが行われます。最後の書込みの後でデータが書き込まれるため、LOB WRITE NEXTにはオフセットは必要ありません。EOFが読み取られると、読取りループが終了し、LOB WRITE LASTが行われます。戻された量は、量の初期値(16)と等しくなっている必要があります。

     MOVE 16 TO AMT.
     PERFORM READ-NEXT-RECORD.
     MOVE INREC TO BUFFER-ARR.
     MOVE 5 TO BUFFER-LEN.
     IF (END-OF-FILE = "Y")
         EXEC SQL LOB WRITE ONE :AMT FROM :BUFFER INTO CLOB1 
            AT :OFFSET END-EXEC.
         PERFORM DISPLAY-CLOB
     ELSE
         EXEC SQL LOB WRITE FIRST :AMT FROM :BUFFER INTO :CLOB1
            AT :OFFSET END-EXEC.
     PERFORM READ-NEXT-RECORD.
     PERFORM WRITE-TO-CLOB 
        UNTIL END-OF-FILE = "Y".
     MOVE INREC TO BUFFER-ARR.
     MOVE 1 TO BUFFER-LEN.
     EXEC SQL LOB WRITE LAST :AMT FROM :BUFFER INTO :CLOB1 END-EXEC.
     PERFORM DISPLAY-CLOB.
     ...
 WRITE-TO-CLOB.
     MOVE INREC TO BUFFER-ARR.
     MOVE 5 TO BUFFER-LEN.
     EXEC SQL LOB WRITE NEXT :AMT FROM :BUFFER INTO :CLOB1 END-EXEC.
     PERFORM READ-NEXT RECORD.

 READ-NEXT-RECORD.
     MOVE SPACES TO INREC.
     READ INFILE NEXT RECORD
        AT END
        MOVE "Y" TO END-OF-FILE.
 ...

13.5 LOBサンプル・プログラム: LOBDEMO1.PCO

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

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

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

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

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

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

LOBDEMO1.PCOの内容は次のとおりです。

      *********************************************************************
      * LOB Demo 1: DMV Database                                          *
      *                                                                   *
      * 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/her name and the   *
      * text summary of the info found in his license.                    *
      *                                                                   *
      * The sss number and the name are the unique social security number *
      * and name of an individual.  The text summary is a summary of the  *
      * information on the individual, 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*COBOL client can handle the    *
      * new LOB datatypes.  Demonstrated are the mechanisms for accessing *
      * and storing lobs to/from tables.                                  *
      *                                                                   *
      *  To run the demo:                                                 *
      *                                                                   *
      *  1. Execute the script, lobdemo1.sql in Server Manager            *
      *  2. Precompile using Pro*COBOL                                    *
      *       procob lobdemo1                                             *
      *  3. Compile/Link (This step is platform specific)                 *
      *                                                                   *
      * lobdemo1.sql contains the following SQL statements:               *
      *                                                                   *
      * connect scott/tiger;                                              *
      *                                                                   *
      * drop table license_table;                                         *
      *                                                                   *
      * 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');                                *
      *                                                                   *
      * insert into license_table                                         *
      *  values('555001212', 'Eight H. Number',                           *
      *  'Driving Under the Influence');                                  *
      *                                                                   *
      * insert into license_table                                         *
      *  values('010101010', 'P. Doughboy',                               *
      *  'Impersonating An Oracle Employee');                             *
      *                                                                   *
      * insert into license_table                                         *
      *  values('555377012', 'Calvin N. Hobbes',                          *
      *  'Driving Under the Influence');                                  *
      *                                                                   *
      * The main program provides the menu of actions that can be         *
      * performed.  The program stops when the number 5 (Quit) option     *
      * is entered.  Depending on the input, this main program calls      *
      * the appropriate nested program to execute the chosen action.      *
      *                                                                   *
      *********************************************************************
       IDENTIFICATION DIVISION.
       PROGRAM-ID.  LOBDEMO1.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  USERNAME       PIC X(5).
       01  PASSWD         PIC X(5).
       01  CHOICE         PIC 9 VALUE 0.
       01  SSS            PIC X(9).
       01  SSSEXISTS      PIC 9 VALUE ZERO.
       01  LICENSE-TXT    SQL-CLOB .
       01  NEWCRIME       PIC X(35) VARYING.
       01  SSSCOUNT       PIC S9(4) COMP.
       01  THE-STRING    PIC X(200) VARYING.
       01  TXT-LENGTH    PIC S9(9) COMP.
       01  CRIMES.
           05 FILLER PIC X(35) VALUE "Driving Under the Influence".
           05 FILLER PIC X(35) VALUE "Grand Theft Auto".
           05 FILLER PIC X(35) VALUE "Driving Without a License".
           05 FILLER PIC X(35) VALUE 
                  "Impersonating an Oracle Employee".
           05 FILLER PIC X(35) VALUE "Wearing a Bright Orange Shirt".
       01  CRIMELIST REDEFINES CRIMES.
           05 CRIME  PIC X(35) OCCURS 5 TIMES.
       01  CRIME-INDEX   PIC 9.
       01  TXT-LEN       PIC S9(9) COMP.
       01  CRIME-LEN     PIC S9(9) COMP.
       01  NAME1         PIC X(50) VARYING.
       01  NEWNAME         PIC X(50).
      *********************************************************************

           EXEC SQL INCLUDE SQLCA END-EXEC.

       PROCEDURE DIVISION.
       A000-CONTROL SECTION.
      *********************************************************************
      *  A000-CONTROL
      *     Overall control section 
      *********************************************************************
       A000-CNTRL.
              EXEC SQL 
		WHENEVER SQLERROR DO PERFORM Z900-SQLERROR 
	      END-EXEC.
              PERFORM B000-LOGON.
	      PERFORM C000-MAIN UNTIL CHOICE = 5.
	      PERFORM D000-LOGOFF.
       A000-EXIT.
	      STOP RUN.

       B000-LOGON SECTION.
      *********************************************************************
      *  B000-LOGON
      *    Log on to database.
      *********************************************************************
       B000-LGN.
           DISPLAY '**************************************************'.
           DISPLAY '*            Welcome to the DMV Database         *'.
           DISPLAY '**************************************************'.
           MOVE "scott" TO USERNAME.
           MOVE "tiger" TO PASSWD.
           EXEC SQL
              CONNECT :USERNAME IDENTIFIED BY :PASSWD
           END-EXEC.
           DISPLAY " ".
           DISPLAY "Connecting to license database account:  ", 
              USERNAME, "/", PASSWD.
           DISPLAY " ".
       B000-EXIT.
           EXIT.
       C000-MAIN SECTION.
      *********************************************************************
      *  C000-MAIN
      *    Display the main menu and action requests   
      *********************************************************************
       C000-MN.

           DISPLAY " ".
           DISPLAY "License Options:".
           DISPLAY "1. List available records by SSS number".
           DISPLAY "2. Get information on a particular record".
           DISPLAY "3. Add crime to a record".
           DISPLAY "4. Insert new record to database".
           DISPLAY "5. Quit".
           DISPLAY " ".
 
           MOVE ZERO TO CHOICE.
           PERFORM Z300-ACCEPT-CHOICE UNTIL CHOICE < 6 
                                   AND CHOICE > 0.
           IF (CHOICE = 1)
              PERFORM C100-LIST-RECORDS.
           IF (CHOICE = 2)
               PERFORM C200-GET-RECORD.
           IF (CHOICE = 3)
               PERFORM C300-ADD-CRIME.
           IF (CHOICE = 4)
               PERFORM C400-NEW-RECORD.
        C000-EXIT.
	   EXIT.
       
        C100-LIST-RECORDS SECTION.
      *********************************************************************
      *  C100-LIST-RECORDS
      *   Select  Social Security Numbers from LICENCSE_TABLE
      *   and display the list
      *********************************************************************
        C100-LST.

           EXEC SQL DECLARE SSS_CURSOR CURSOR FOR 
			SELECT SSS FROM LICENSE_TABLE
           END-EXEC.
 
           EXEC SQL OPEN SSS_CURSOR END-EXEC.
 
           DISPLAY "Available records:".

           PERFORM C110-DISPLAY-RECORDS UNTIL SQLCODE = 1403.
           EXEC SQL CLOSE SSS_CURSOR END-EXEC.
       C100-EXIT.
           EXIT. 
       C110-DISPLAY-RECORDS SECTION.
      *********************************************************************
      *  C110-DISPLAY-RECORDS
      *    Fetch the next record from the cursor and display it.
      *********************************************************************
       C110-DSPLY.
            EXEC SQL FETCH SSS_CURSOR INTO :SSS END-EXEC.
            IF SQLCODE = 0 THEN
                DISPLAY SSS.
       C110-EXIT.
            EXIT. 

       C200-GET-RECORD SECTION.
      *******************************************************************
      *  C200-GET-RECORD
      *    Allocates the global clob LICENSE-TXT then selects 
      *    the name and text which corresponds to the client-supplied
      *    sss.  It then calls Z200-PRINTCRIME to print the information and
      *    frees the clob.
      *******************************************************************
       C200-GTRECRD.
           PERFORM Z100-GET-SSS.
           IF (SSSEXISTS = 1)
             EXEC SQL ALLOCATE :LICENSE-TXT END-EXEC
             EXEC SQL SELECT NAME, TXT_SUMMARY 
                  INTO :NAME1, :LICENSE-TXT FROM LICENSE_TABLE 
                  WHERE SSS = :SSS END-EXEC
             DISPLAY "==================================================
      -         "========================"
             DISPLAY " "
             DISPLAY "NAME:  ", NAME1-ARR, "SSS:  ", SSS
             DISPLAY " "
             PERFORM Z200-PRINTCRIME       
             DISPLAY " "
             DISPLAY "==================================================
      -         "========================"
             EXEC SQL FREE :LICENSE-TXT END-EXEC
           ELSE
              DISPLAY "SSS Number Not Found".
       C200-EXIT.
           EXIT.
       C310-GETNEWCRIME SECTION.
      *******************************************************************
      *  C310-GETNEWCRIME
      *    Provides a list of the possible crimes to the user and
      *    stores the user's correct response in the variable
      *    NEWCRIME.
      *******************************************************************
       C310-GTNWCRM.

            EXEC SQL WHENEVER SQLERROR CONTINUE END-EXEC.
           
            DISPLAY " ".
            DISPLAY "Select from the following:".
            PERFORM C311-DISPLAY-CRIME
              VARYING CRIME-INDEX FROM 1 BY 1
              UNTIL CRIME-INDEX > 5.
            MOVE ZERO TO CHOICE.
            PERFORM Z300-ACCEPT-CHOICE UNTIL CHOICE < 6
                                  AND CHOICE > 0.
            MOVE CRIME(CHOICE) TO NEWCRIME-ARR.
            MOVE 35 TO NEWCRIME-LEN.
            MOVE ZERO TO CHOICE.
       C310-EXIT.
            EXIT.
       C311-DISPLAY-CRIME SECTION.
      *******************************************************************
      *   C311-DISPLAY-CRIME
      *      Display an element of the crime table
      *******************************************************************
       C311-DSPLYCRM.
           DISPLAY "(", CRIME-INDEX, ") ", CRIME(CRIME-INDEX).
       C311-EXIT.
            EXIT.
       C320-APPENDTOCLOB SECTION.
      *******************************************************************
      * C320-APPENDTOCLOB
      *    Obtains the length of the global clob LICENSE-TXT and
      *    uses that in the LOB WRITE statement to append the NEWCRIME
      *    character buffer to the global clob LICENSE-TXT.
      *    The name corresponding the global SSS is then selected
      *    and displayed to the screen along with value of LICENSE-TXT.
      *    The caller to this function must allocate, select and later
      *    free the global clob LICENSE-TXT.
      *******************************************************************
       C320-PPNDTCLB.

           EXEC SQL 
		WHENEVER SQLERROR DO PERFORM Z900-SQLERROR 
	   END-EXEC.

           EXEC SQL LOB DESCRIBE :LICENSE-TXT GET LENGTH
               INTO :TXT-LEN END-EXEC.

           MOVE NEWCRIME-LEN TO CRIME-LEN.
           IF (TXT-LEN NOT = 0)
             ADD 3 TO TXT-LEN
           ELSE
             ADD 1 TO TXT-LEN.
           EXEC SQL LOB WRITE :CRIME-LEN FROM :NEWCRIME
                 INTO :LICENSE-TXT AT :TXT-LEN END-EXEC.

           EXEC SQL SELECT NAME INTO :NAME1 FROM LICENSE_TABLE 
               WHERE SSS = :SSS END-EXEC.
           DISPLAY " ".
           DISPLAY "NAME:  ", NAME1-ARR, "SSS:  ", SSS.
           DISPLAY " ".
           PERFORM Z200-PRINTCRIME.       
           DISPLAY " ".          

       C320-EXIT.
          EXIT.
 
       C300-ADD-CRIME SECTION.
      *******************************************************************
      * ADD-CRIME
      *    Obtains a sss and crime from the user and appends
      *    the crime to the list of crimes of the corresponding sss.
      *******************************************************************
       C300-DDCRM.

           EXEC SQL 
              WHENEVER SQLERROR DO PERFORM Z900-SQLERROR 
	   END-EXEC.

           PERFORM Z100-GET-SSS.
           IF (SSSEXISTS = 1)
             EXEC SQL ALLOCATE :LICENSE-TXT END-EXEC
             PERFORM C310-GETNEWCRIME       
             EXEC SQL SELECT TXT_SUMMARY INTO :LICENSE-TXT
                  FROM LICENSE_TABLE WHERE SSS = :SSS 
                  FOR UPDATE END-EXEC
             PERFORM C320-APPENDTOCLOB
             EXEC SQL FREE :LICENSE-TXT END-EXEC
           ELSE
              DISPLAY "SSS Number Not Found".
       C300-EXIT.
           EXIT.

       C400-NEW-RECORD SECTION.
      *******************************************************************
      * C400-NEW-RECORD
      *    Obtains the sss and name of a new record and inserts them
      *    along with an empty_clob() for the clob in the table.
      *******************************************************************
       C400-NWRCRD.

           PERFORM Z100-GET-SSS.
           IF (SSSEXISTS = 1)
             DISPLAY "Record with that sss number already exists"
           ELSE
             DISPLAY "Name? " WITH NO ADVANCING
             ACCEPT NEWNAME
             DISPLAY " ".
             EXEC SQL ALLOCATE :LICENSE-TXT END-EXEC
             EXEC SQL INSERT INTO LICENSE_TABLE
                  VALUES (:SSS, :NEWNAME, EMPTY_CLOB()) END-EXEC
             EXEC SQL SELECT TXT_SUMMARY INTO :LICENSE-TXT
                  FROM LICENSE_TABLE WHERE SSS = :SSS END-EXEC
             DISPLAY "==================================================
      -         "========================"
             DISPLAY "NAME: ", NEWNAME,"SSS: ", SSS
             PERFORM Z200-PRINTCRIME
             DISPLAY "==================================================
      -         "========================"
             EXEC SQL FREE :LICENSE-TXT END-EXEC.
       C400-EXIT.
          EXIT.
       D000-LOGOFF SECTION.
      *******************************************************************
      *  D000-LOGOFF
      *    Commit the work done to the database and log off
      *******************************************************************
       D000-LGFF.
           EXEC SQL COMMIT WORK RELEASE END-EXEC.
           DISPLAY " ".
           DISPLAY "HAVE A GOOD DAY!".
           DISPLAY " ".
       D000-EXIT.
           STOP RUN.
       Z100-GET-SSS SECTION.
      *******************************************************************
      *  Z100-GET-SSS
      *    Fills the global variable SSS with the client-supplied sss.
      *    Sets the global variable SSSEXISTS to 0 if the sss does not 
      *    correspond to any entry in the database, else sets it to 1.
      *******************************************************************
       Z100-GTSSS.
           DISPLAY "Social Security Number? " WITH NO ADVANCING.
           ACCEPT SSS.
           DISPLAY " ".
          
           EXEC SQL SELECT COUNT(*) INTO :SSSCOUNT FROM LICENSE_TABLE
               WHERE SSS = :SSS END-EXEC.
           
           IF (SSSCOUNT = 0)
              MOVE 0 TO SSSEXISTS
           ELSE 
              MOVE 1 TO SSSEXISTS.
       Z100-EXIT.
          EXIT.
       Z200-PRINTCRIME SECTION.
      *******************************************************************
      *  Z200-PRINTCRIME
      *    Obtains the length of the global clob LICENSE-TXT and
      *    uses that in the LOB READ statement to read the clob
      *    into a character buffer to display the contents of the clob.
      *    The caller to this function must allocate, select and later
      *    free the global clob LICENSE-TXT.
      *******************************************************************
       Z200-PRNTCRM.
           DISPLAY "=====================".
           DISPLAY " CRIME SHEET SUMMARY ".
           DISPLAY "=====================".

           MOVE SPACE TO THE-STRING-ARR.
           EXEC SQL LOB DESCRIBE :LICENSE-TXT GET LENGTH
               INTO :TXT-LENGTH END-EXEC.

           IF (TXT-LENGTH = 0)
              DISPLAY "Record is clean"
            ELSE
              EXEC SQL LOB READ :TXT-LENGTH FROM :LICENSE-TXT
                 INTO :THE-STRING END-EXEC
              DISPLAY THE-STRING-ARR.

       Z200-EXIT.
           EXIT.
       Z300-ACCEPT-CHOICE SECTION.
      *******************************************************************
      *   Z300-ACCEPT-CHOICE
      *      Accept a choice between 1 and 5
      *******************************************************************
       Z300-CCPT.
           DISPLAY "Your Selection (1-5)? " WITH NO ADVANCING.
           ACCEPT CHOICE.
           DISPLAY " ".
           IF CHOICE >5 OR CHOICE < 1 THEN
              DISPLAY "Invalid Selection"
              DISPLAY "Please Choose from the indicated list".
       Z300-EXIT.
          EXIT.
       
       Z900-SQLERROR SECTION.
      *******************************************************************
      * Z900-SQLERROR
      *    Called whenever a SQLERROR occurs.
      *    Display the Error, Roll Back any work done and Log Off
      *******************************************************************
       Z900-SQLRRR.
           EXEC SQL WHENEVER SQLERROR CONTINUE END-EXEC.
           DISPLAY " ".
           DISPLAY "ORACLE ERROR DETECTED:".
           DISPLAY " ".
           DISPLAY SQLERRMC.
           EXEC SQL ROLLBACK WORK RELEASE END-EXEC.
       Z900-EXIT.
           STOP RUN.