プライマリ・コンテンツに移動
Pro*C/C++プログラマーズ・ガイド
12c リリース1(12.1)
B71397-03
目次へ移動
目次
索引へ移動
索引

前
次

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