IBM Books

Embedded SQL Programming Guide


Using Large Objects (LOBs)

The LONG VARCHAR and LONG VARGRAPHIC data types have a limit of 32K bytes of storage. While this may be sufficient for small to medium size text data, applications often need to store large text documents. They may also need to store a wide variety of additional data types such as audio, video, drawings, mixed text and graphics, and images. DB2 provides three data types to store these data objects as strings of up to two (2) gigabytes (GB) in size. The three data types are: Binary Large OBjects (BLOBs), single-byte Character Large OBjects (CLOBs), and Double-Byte Character Large OBjects (DBCLOBs).

Along with storing large objects (LOBs), a way is also needed to refer to, and to use and modify, each LOB in the database. Each DB2 table may have a large amount of associated LOB data. Although any single LOB value may not exceed 2 gigabytes, a single row may contain as much as 24 gigabytes of LOB data, and a table may contain as much as 4 terabytes of LOB data. The content of the LOB column of a particular row at any point in time has a large object value.

You can refer to and manipulate LOBs using host variables just as you would any other data type. However, host variables use the client memory buffer which may not be large enough to hold LOB values. Other means are necessary to manipulate these large values. Locators are useful to identify and manipulate a large object value at the database server and for extracting pieces of the LOB value. File reference variables are useful for physically moving a large object value (or a large part of it) to and from the client.

The subsections that follow discuss in more detail those topics introduced above.

Understanding Large Object Data Types (BLOB, CLOB, DBCLOB)

Large object data types store data ranging in size from zero bytes to two gigabytes - 1.

The three large object data types have the following definitions:

A separate database location stores all large object values outside their records in the table. There is a large object descriptor for each large object in each row in a table. The large object descriptor contains control information used to access the large object data stored elsewhere on disk. It is the storing of large object data outside their records that allows LOBs to be 2 GB in size. Accessing the large object descriptor causes a small amount of overhead when manipulating LOBs. (For storage and performance reasons you would likely not want to put small data items into LOBs.)

The maximum size for each large object column is part of the declaration of the large object type in the CREATE TABLE statement. The maximum size of a large object column determines the maximum size of any LOB descriptor in that column. As a result, it also determines how many columns of all data types can fit in a single row. The space used by the LOB descriptor in the row ranges from approximately 60 to 300 bytes, depending on the maximum size of the corresponding column. For specific sizes of the LOB descriptor, see the CREATE TABLE statement in the SQL Reference.

The lob-options-clause on CREATE TABLE gives you the choice to log (or not) the changes made to the LOB column(s). This clause also allows for a compact representation for the LOB descriptor (or not). This means you can allocate only enough space to store the LOB or you can allocate extra space for future append operations to the LOB. The tablespace-options-clause allows you to identify a LONG table space to store the column values of long field or LOB data types. For more information on the CREATE TABLE and ALTER TABLE statements, see the SQL Reference.

With their potentially very large size, LOBs can slow down the performance of your database system significantly when moved into or out of a database. Even though DB2 does not allow logging of a LOB value greater than 1 GB, LOB values with sizes near several hundred megabytes can quickly push the database log to near capacity. An error, SQLCODE -355 (SQLSTATE 42993), results from attempting to log a LOB greater than 1 GB in size. The lob-options-clause in the CREATE TABLE and ALTER TABLE statements allows users to turn off logging for a particular LOB column. Although setting the option to NOT LOGGED improves performance, changes to the LOB values after the most recent backup are lost during roll-forward recovery. For more information on these topics, see the Administration Guide.

Understanding Large Object Locators

Conceptually, LOB locators represent a simple idea that has been around for a while; use a small, easily managed value to refer to a much larger value. Specifically, a LOB locator is a 4-byte value stored in a host variable that a program can use to refer to a LOB value (or LOB expression) held in the database system. Using a LOB locator, a program can manipulate the LOB value as if the LOB value was stored in a regular host variable. The difference in using the LOB locator is that there is no need to transport the LOB value from the server to the application (and possibly back again).

The LOB locator is associated with a LOB value or LOB expression, not a row or physical storage location in the database. Therefore, after selecting a LOB value into a locator, there is no operation that you could perform on the original row(s) or tables(s) that would have any effect on the value referenced by the locator. The value associated with the locator is valid until the unit of work ends, or the locator is explicitly freed, whichever comes first. The FREE LOCATOR statement releases a locator from its associated value. In a similar way, a commit or roll-back operation frees all LOB locators associated with the transaction.

LOB locators can also be passed between DB2 and UDFs. There are special APIs available for UDFs to manipulate the LOB values using LOB locators. For more information on these APIs see "Using LOB Locators as UDF Parameters or Results".

When selecting a LOB value, you have three options:

The use of the LOB value within the program can help the programmer determine which method is best. If the LOB value is very large and is needed only as an input value for one or more subsequent SQL statements, then it is best to keep the value in a locator. The use of a locator eliminates any client/server communication traffic needed to transfer the LOB value to the host variable and back to the server.

If the program needs the entire LOB value regardless of the size, then there is no choice but to transfer the LOB. Even in this case, there are still three options available to you. You can select the entire value into a regular or file host variable, but it may also work out better to select the LOB value into a locator and read it piecemeal from the locator into a regular host variable, as suggested in the following example.

Example: Using a Locator to Work With a CLOB Value

In this example, the application program retrieves a locator for a LOB value; then it uses the locator to extract the data from the LOB value. Using this method, the program allocates only enough storage for one piece of LOB data (the size is determined by the program) and it needs to issue only one fetch call using the cursor.

How the Sample LOBLOC Program Works

  1. Declare host variables. The BEGIN DECLARE SECTION and END DECLARE SECTION statements delimit the host variable declarations. Host variables are prefixed with a colon (:) when referenced in an SQL statement. CLOB LOCATOR host variables are declared.

  2. Fetch the LOB value into the host variable LOCATOR. A CURSOR and FETCH routine is used to obtain the location of a LOB field in the database to a host variable locator.

  3. Free the LOB LOCATORS. The LOB LOCATORS used in this example are freed, releasing the locators from their previously associated values.

The CHECKERR macro/function is an error checking utility which is external to the program. The location of this error checking utility depends upon the programming language used:

C
check_error is redefined as CHECKERR and is located in the util.c file.

COBOL
CHECKERR is an external program named checkerr.cbl.

FORTRAN
CHECKERR is a subroutine located in the util.f file.

See "Using GET ERROR MESSAGE in Example Programs" for the source code for this error checking utility.

C Sample: LOBLOC.SQC

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "util.h"
 
EXEC SQL INCLUDE SQLCA;
 
#define  CHECKERR(CE_STR)   if (check_error (CE_STR, &sqlca) != 0) return 1;
 
int main(int argc, char *argv[]) {
 
#ifdef DB2MAC
   char * bufptr;
#endif
 
   EXEC SQL BEGIN DECLARE SECTION; (1)
      char number[7];
      long deptInfoBeginLoc;
      long deptInfoEndLoc;
      SQL TYPE IS CLOB_LOCATOR resume;
      SQL TYPE IS CLOB_LOCATOR deptBuffer;
      short lobind;
      char buffer[1000]="";
      char userid[9];
      char passwd[19];
   EXEC SQL END DECLARE SECTION;
 
   printf( "Sample C program: LOBLOC\n" );
 
   if (argc == 1) {
      EXEC SQL CONNECT TO sample;
	  CHECKERR ("CONNECT TO SAMPLE");
   }
   else if (argc == 3) { 
      strcpy (userid, argv[1]);
      strcpy (passwd, argv[2]);
      EXEC SQL CONNECT TO sample USER :userid USING :passwd;
      CHECKERR ("CONNECT TO SAMPLE");
   }
   else {
      printf ("\nUSAGE: lobloc [userid passwd]\n\n");
      return 1;
   } /* endif */
   
   /* Employee A10030 is not included in the following select, because 
      the lobeval program manipulates the record for A10030 so that it is 
      not compatible with lobloc */
 
   EXEC SQL DECLARE c1 CURSOR FOR
            SELECT empno, resume FROM emp_resume WHERE resume_format='ascii' 
            AND empno <> 'A00130'; 
 
   EXEC SQL OPEN c1;
   CHECKERR ("OPEN CURSOR");
 
   do {
      EXEC SQL FETCH c1 INTO :number, :resume :lobind;  (2)
      if (SQLCODE != 0) break;
      if (lobind < 0) {
         printf ("NULL LOB indicated\n");
      } else {
         /* EVALUATE the LOB LOCATOR */
         /* Locate the beginning of "Department Information" section */
         EXEC SQL VALUES (POSSTR(:resume, 'Department Information'))
            INTO :deptInfoBeginLoc;
         CHECKERR ("VALUES1");
 
         /* Locate the beginning of "Education" section (end of "Dept.Info" */
         EXEC SQL VALUES (POSSTR(:resume, 'Education'))
            INTO :deptInfoEndLoc;
         CHECKERR ("VALUES2");
 
         /* Obtain ONLY the "Department Information" section by using SUBSTR */
         EXEC SQL VALUES(SUBSTR(:resume, :deptInfoBeginLoc,
            :deptInfoEndLoc - :deptInfoBeginLoc)) INTO :deptBuffer;
         CHECKERR ("VALUES3");
 
         /* Append the "Department Information" section to the :buffer var. */
         EXEC SQL VALUES(:buffer || :deptBuffer) INTO :buffer;
         CHECKERR ("VALUES4");
      } /* endif */
   } while ( 1 );
 
#ifdef DB2MAC     
   /* Need to convert the newline character for the Mac */
   bufptr = &(buffer[0]);
   while ( *bufptr != '\0' ) {
      if ( *bufptr == 0x0A ) *bufptr = 0x0D;
	  bufptr++;
   }
#endif
      
   printf ("%s\n",buffer);
 
   EXEC SQL FREE LOCATOR :resume, :deptBuffer; (3)
   CHECKERR ("FREE LOCATOR");
 
   EXEC SQL CLOSE c1;
   CHECKERR ("CLOSE CURSOR");
 
   EXEC SQL CONNECT RESET;
   CHECKERR ("CONNECT RESET");
   return 0;
}
/* end of program : LOBLOC.SQC */

COBOL Sample: LOBLOC.SQB

 
 Identification Division.
 Program-ID. "lobloc".
 
 Data Division.
 Working-Storage Section.
     copy "sqlenv.cbl".
     copy "sql.cbl".
     copy "sqlca.cbl".
 
     EXEC SQL BEGIN DECLARE SECTION END-EXEC. (1)
 01 userid            pic x(8).
 01 passwd.
   49 passwd-length   pic s9(4) comp-5 value 0.
   49 passwd-name     pic x(18).
 01 empnum            pic x(6).
 01 di-begin-loc      pic s9(9) comp-5.
 01 di-end-loc        pic s9(9) comp-5.
 01 resume            USAGE IS SQL TYPE IS CLOB-LOCATOR.
 01 di-buffer         USAGE IS SQL TYPE IS CLOB-LOCATOR.
 01 lobind            pic s9(4) comp-5.
 01 buffer            USAGE IS SQL TYPE IS CLOB(1K).
     EXEC SQL END DECLARE SECTION END-EXEC.
 
 77 errloc          pic x(80).
 
 Procedure Division.
 Main Section.
     display "Sample COBOL program: LOBLOC".
 
* Get database connection information.
     display "Enter your user id (default none): " 
          with no advancing.
     accept userid.
 
     if userid = spaces
       EXEC SQL CONNECT TO sample END-EXEC
     else
       display "Enter your password : " with no advancing
       accept passwd-name.
 
* Passwords in a CONNECT statement must be entered in a VARCHAR
* format with the length of the input string.
     inspect passwd-name tallying passwd-length for characters
        before initial " ".
 
     EXEC SQL CONNECT TO sample USER :userid USING :passwd
         END-EXEC.
     move "CONNECT TO" to errloc.
     call "checkerr" using SQLCA errloc.
 
* Employee A10030 is not included in the following select, because
* the lobeval program manipulates the record for A10030 so that it is
* not compatible with lobloc
 
     EXEC SQL DECLARE c1 CURSOR FOR
              SELECT empno, resume FROM emp_resume
              WHERE resume_format = 'ascii'
              AND empno <> 'A00130' END-EXEC.
 
     EXEC SQL OPEN c1 END-EXEC.
     move "OPEN CURSOR" to errloc.
     call "checkerr" using SQLCA errloc.
 
     Move 0 to buffer-length. 
 
     perform Fetch-Loop thru End-Fetch-Loop
        until SQLCODE not equal 0.
 
* display contents of the buffer.
     display buffer-data(1:buffer-length).
 
     EXEC SQL FREE LOCATOR :resume, :di-buffer END-EXEC. (3)
     move "FREE LOCATOR" to errloc.
     call "checkerr" using SQLCA errloc.
 
     EXEC SQL CLOSE c1 END-EXEC.
     move "CLOSE CURSOR" to errloc.
     call "checkerr" using SQLCA errloc.
 
     EXEC SQL CONNECT RESET END-EXEC.
     move "CONNECT RESET" to errloc.
     call "checkerr" using SQLCA errloc.
 End-Main.
        go to End-Prog.
 
 Fetch-Loop Section.
     EXEC SQL FETCH c1 INTO :empnum, :resume :lobind (2)
        END-EXEC.
 
     if SQLCODE not equal 0
        go to End-Fetch-Loop.
 
* check to see if the host variable indicator returns NULL.
     if lobind less than 0 go to NULL-lob-indicated.
 
* Value exists.  Evaluate the LOB locator.
* Locate the beginning of "Department Information" section.
     EXEC SQL VALUES (POSSTR(:resume, 'Department Information'))
              INTO :di-begin-loc END-EXEC.
     move "VALUES1" to errloc.
     call "checkerr" using SQLCA errloc.
 
* Locate the beginning of "Education" section (end of Dept.Info)
     EXEC SQL VALUES (POSSTR(:resume, 'Education'))
               INTO :di-end-loc END-EXEC.
     move "VALUES2" to errloc.
     call "checkerr" using SQLCA errloc.
 
     subtract di-begin-loc from di-end-loc.
 
* Obtain ONLY the "Department Information" section by using SUBSTR
     EXEC SQL VALUES (SUBSTR(:resume, :di-begin-loc,
              :di-end-loc))
              INTO :di-buffer END-EXEC.
     move "VALUES3" to errloc.
     call "checkerr" using SQLCA errloc.
 
* Append the "Department Information" section to the :buffer var
     EXEC SQL VALUES (:buffer || :di-buffer) INTO :buffer
              END-EXEC.
     move "VALUES4" to errloc.
     call "checkerr" using SQLCA errloc.
 
     go to End-Fetch-Loop.
 
 NULL-lob-indicated.
     display "NULL LOB indicated".
 
 End-Fetch-Loop. exit.
 
 End-Prog.
            stop run.

FORTRAN Sample: LOBLOC.SQF

 
      program lobloc
      implicit none
 
      include 'sqlenv.f'
      include 'sql.f'
      EXEC SQL INCLUDE SQLCA
 
      EXEC SQL BEGIN DECLARE SECTION (1)
        character*8               userid
        character*18              passwd
        character*6               empnum
        integer*4                 di_begin_loc
        integer*4                 di_end_loc
        SQL TYPE IS CLOB_LOCATOR  resume
        SQL TYPE IS CLOB_LOCATOR  di_buffer
        integer*2                 lobind
        SQL TYPE IS CLOB(1K)      buffer
      EXEC SQL END DECLARE SECTION
 
      character*80     errloc
 
      print *, 'Sample Fortran Program: LOBLOC'
 
      print *, 'Enter your user id (default none):'
      read 101, userid
101   format (a8)      
 
      if( userid(1:1) .eq. ' ' ) then
	EXEC SQL CONNECT TO sample 
      else
	print *, 'Enter your password :'
	read 101, passwd
 
	EXEC SQL CONNECT TO sample USER :userid USING :passwd
      end if
      errloc = 'CONNECT'
      call checkerr (sqlca, errloc, *999)
 
*     Employee A10030 is not included in the following select, because
*     the lobeval program manipulates the record for A10030 so that it is
*     not compatible with lobloc 
 
      EXEC SQL DECLARE c1 CURSOR FOR
     +         SELECT empno, resume FROM emp_resume
     +         WHERE resume_format = 'ascii'
     +         AND empno <> 'A00130'
 
      EXEC SQL OPEN c1
      errloc = 'OPEN CURSOR'
      call checkerr (sqlca, errloc, *999)
 
 
   10 continue
         EXEC SQL FETCH c1 INTO :empnum, :resume :lobind (2)
         if (sqlcode .ne. 0) goto 100
         if (lobind .lt. 0) goto 50
 
*     The CLOB value exists.
*     Locate the beginning of the "Department Information" section
         EXEC SQL VALUES (POSSTR(:resume, 'Department Information'))
     +            INTO :di_begin_loc
         errloc = 'VALUES1'
         call checkerr (sqlca, errloc, *999)
 
*     Locate the beginning of the "Education" section (end of Dept.Info)
         EXEC SQL VALUES (POSSTR(:resume, 'Education'))
     +            INTO :di_end_loc
         errloc = 'VALUES2'
         call checkerr (sqlca, errloc, *999)
 
*     Obtain ONLY the "Department Information" section by using SUBSTR
         EXEC SQL VALUES(SUBSTR(:resume, :di_begin_loc,
     +            :di_end_loc - :di_begin_loc)) INTO :di_buffer
         errloc = 'VALUES3'
         call checkerr (sqlca, errloc, *999)
 
*     Append the "Department Information" section to the :buffer variable
         EXEC SQL VALUES(:buffer || :di_buffer) INTO :buffer
         errloc = 'VALUES4'
         call checkerr (sqlca, errloc, *999)
 
      goto 10
 
   50 print *,'NULL LOB indicated'
      goto 10
 
  100 print *, buffer
 
      EXEC SQL FREE LOCATOR :resume, :di_buffer (3)
      errloc = 'FREE LOCATOR'
      call checkerr (sqlca, errloc, *999)
 
      EXEC SQL CLOSE c1
      errloc = 'CLOSE CURSOR'
      call checkerr (sqlca, errloc, *999)
 
      EXEC SQL CONNECT RESET
      errloc = 'CONNECT RESET'
      call checkerr (sqlca, errloc, *999)
 
  999 stop
      end

Example: Deferring the Evaluation of a LOB Expression

There is no movement of the bytes of a LOB value until the assignment of a LOB expression to a target destination. This means that a LOB value locator used with string functions and operators can create an expression where the evaluation is postponed until the time of assignment. This is called deferring evaluation of a LOB expression.

In this example, a particular resume (empno = '000130') is sought within a table of resumes EMP_RESUME. The Department Information section of the resume is copied, cut, and then appended to the end of the resume. This new resume will then be inserted into the EMP_RESUME table. The original resume in this table remains unchanged.

Locators permit the assembly and examination of the new resume without actually moving or copying any bytes from the original resume. The movement of bytes does not happen until the final assignment; that is, the INSERT statement -- and then only at the server.

Deferring evaluation gives DB2 an opportunity to increase LOB I/O performance. This occurs because the LOB function optimizer attempts to transform the LOB expressions into alternative expressions. These alternative expressions produce equivalent results but may also require fewer disk I/Os.

In summary, LOB locators are ideally suited for a number of programming scenarios:

  1. When moving only a small part of a much larger LOB to a client program.
  2. When the entire LOB cannot fit in the application's memory.
  3. When the program needs a temporary LOB value from a LOB expression but does not need to save the result.
  4. When performance is important (by deferring evaluation of LOB expressions).

How the Sample LOBEVAL Program Works

  1. Declare host variables. The BEGIN DECLARE SECTION and END DECLARE SECTION statements delimit the host variable declarations. Host variables are prefixed with a colon (:) when referenced in an SQL statement. CLOB LOCATOR host variables are declared.

  2. Fetch the LOB value into the host variable LOCATOR. A CURSOR and FETCH routine is used to obtain the location of a LOB field in the database to a host variable locator.

  3. LOB data is manipulated through the use of LOCATORS. The next five SQL statements manipulate the LOB data without moving the actual data contained in the LOB field. This is done through the use of the LOB LOCATORS.

  4. LOB data is moved to the target destination. The evaluation of the LOB assigned to the target destination is postponed until this SQL statement. The evaluation of this LOB statement has been deferred.

  5. Free the LOB LOCATORS. The LOB LOCATORS used in this example are freed, releasing the locators from their previously associated values.

The CHECKERR macro/function is an error checking utility which is external to the program. The location of this error checking utility depends upon the programming language used:

C
check_error is redefined as CHECKERR and is located in the util.c file.

COBOL
CHECKERR is an external program named checkerr.cbl.

See "Using GET ERROR MESSAGE in Example Programs" for the source code for this error checking utility.

C Sample: LOBEVAL.SQC

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "util.h"
 
EXEC SQL INCLUDE SQLCA;
 
#define  CHECKERR(CE_STR)   if (check_error (CE_STR, &sqlca) != 0) return 1;
 
int main(int argc, char *argv[]) {
 
   EXEC SQL BEGIN DECLARE SECTION; (1)
      char userid[9];
      char passwd[19];
      long            hv_start_deptinfo;
      long            hv_start_educ;
      long            hv_return_code;
      SQL TYPE IS CLOB(5K) hv_new_section_buffer;
      SQL TYPE IS CLOB_LOCATOR hv_doc_locator1;
      SQL TYPE IS CLOB_LOCATOR hv_doc_locator2;
      SQL TYPE IS CLOB_LOCATOR hv_doc_locator3;
   EXEC SQL END DECLARE SECTION;
 
   printf( "Sample C program: LOBEVAL\n" );
 
   if (argc == 1) {
      EXEC SQL CONNECT TO sample;
	  CHECKERR ("CONNECT TO SAMPLE");
   }
   else if (argc == 3) { 
      strcpy (userid, argv[1]);
      strcpy (passwd, argv[2]);
      EXEC SQL CONNECT TO sample USER :userid USING :passwd;
      CHECKERR ("CONNECT TO SAMPLE");
   }
   else {
      printf ("\nUSAGE: lobeval [userid passwd]\n\n");
      return 1;
   } /* endif */
   
   /* delete any instance of "A00130" from previous executions of this sample */
   EXEC SQL DELETE FROM emp_resume WHERE empno = 'A00130';
 
   /* Use a single row select to get the document */
   EXEC SQL SELECT resume INTO :hv_doc_locator1 FROM emp_resume
      WHERE empno = '000130' AND resume_format = 'ascii';  (2)
   CHECKERR ("SELECT");
 
   /* Use the POSSTR function to locate the start of
      sections "Department Information" & "Education" */
   EXEC SQL VALUES (POSSTR(:hv_doc_locator1, 'Department Information'))
      INTO :hv_start_deptinfo; (3)
   CHECKERR ("VALUES1");
 
   EXEC SQL VALUES (POSSTR(:hv_doc_locator1, 'Education'))
      INTO :hv_start_educ;
   CHECKERR ("VALUES2");
 
   /* Replace Department Information Section with nothing */
   EXEC SQL VALUES (SUBSTR(:hv_doc_locator1, 1, :hv_start_deptinfo -1)
      || SUBSTR (:hv_doc_locator1, :hv_start_educ))
      INTO :hv_doc_locator2;
   CHECKERR ("VALUES3");
 
   /* Move Department Information Section into the hv_new_section_buffer */
   EXEC SQL VALUES (SUBSTR(:hv_doc_locator1, :hv_start_deptinfo,
      :hv_start_educ -:hv_start_deptinfo)) INTO :hv_new_section_buffer;
   CHECKERR ("VALUES4");
 
   /* Append our new section to the end (assume it has been filled in)
      Effectively, this just moves the Department Information to the bottom
      of the resume. */
   EXEC SQL VALUES (:hv_doc_locator2 || :hv_new_section_buffer) INTO
      :hv_doc_locator3;
   CHECKERR ("VALUES5");
 
   /* Store this resume section in the table. This is where the LOB value
      bytes really move */
   EXEC SQL INSERT INTO emp_resume VALUES ('A00130', 'ascii',
      :hv_doc_locator3);  (4)
   CHECKERR ("INSERT");
 
   printf ("LOBEVAL completed\n");
 
   /* free the locators */  (5)
   EXEC SQL FREE LOCATOR :hv_doc_locator1, :hv_doc_locator2, : hv_doc_locator3;
   CHECKERR ("FREE LOCATOR");
 
   EXEC SQL CONNECT RESET;
   CHECKERR ("CONNECT RESET");
   return 0;
}
/* end of program : LOBEVAL.SQC */

COBOL Sample: LOBEVAL.SQB

 
 Identification Division.
 Program-ID. "lobeval".
 
 Data Division.
 Working-Storage Section.
     copy "sqlenv.cbl".
     copy "sql.cbl".
     copy "sqlca.cbl".
 
     EXEC SQL BEGIN DECLARE SECTION END-EXEC. (1)
 01 userid            pic x(8).
 01 passwd.
   49 passwd-length   pic s9(4) comp-5 value 0.
   49 passwd-name     pic x(18).
 01 hv-start-deptinfo pic s9(9) comp-5.
 01 hv-start-educ     pic s9(9) comp-5.
 01 hv-return-code    pic s9(9) comp-5.
 01 hv-new-section-buffer USAGE IS SQL TYPE IS CLOB(5K).
 01 hv-doc-locator1   USAGE IS SQL TYPE IS CLOB-LOCATOR.
 01 hv-doc-locator2   USAGE IS SQL TYPE IS CLOB-LOCATOR.
 01 hv-doc-locator3   USAGE IS SQL TYPE IS CLOB-LOCATOR.
     EXEC SQL END DECLARE SECTION END-EXEC.
 
 77 errloc          pic x(80).
 
 Procedure Division.
 Main Section.
     display "Sample COBOL program: LOBEVAL".
 
* Get database connection information.
     display "Enter your user id (default none): " 
          with no advancing.
     accept userid.
 
     if userid = spaces
       EXEC SQL CONNECT TO sample END-EXEC
     else
       display "Enter your password : " with no advancing
       accept passwd-name.
 
* Passwords in a CONNECT statement must be entered in a VARCHAR
* format with the length of the input string.
     inspect passwd-name tallying passwd-length for characters
        before initial " ".
 
     EXEC SQL CONNECT TO sample USER :userid USING :passwd
         END-EXEC.
     move "CONNECT TO" to errloc.
     call "checkerr" using SQLCA errloc.
 
* Delete any instance of "A00130" from previous executions
     EXEC SQL DELETE FROM emp_resume
              WHERE empno = 'A00130' END-EXEC.
 
* use a single row select to get the document
     EXEC SQL SELECT resume INTO :hv-doc-locator1 (2)
              FROM emp_resume
              WHERE empno = '000130'
              AND resume_format = 'ascii' END-EXEC.
     move "SELECT" to errloc.
     call "checkerr" using SQLCA errloc.
 
* use the POSSTR function to locate the start of sections
* "Department Information" & "Education"
     EXEC SQL VALUES (POSSTR(:hv-doc-locator1,
              'Department Information'))
              INTO :hv-start-deptinfo END-EXEC. (3)
     move "VALUES1" to errloc.
     call "checkerr" using SQLCA errloc.
 
     EXEC SQL VALUES (POSSTR(:hv-doc-locator1,
              'Education')) INTO :hv-start-educ END-EXEC.
     move "VALUES2" to errloc.
     call "checkerr" using SQLCA errloc.
 
* replace Department Information section with nothing
     EXEC SQL VALUES (SUBSTR(:hv-doc-locator1, 1,
              :hv-start-deptinfo - 1) ||
              SUBSTR(:hv-doc-locator1, :hv-start-educ))
              INTO :hv-doc-locator2 END-EXEC.
     move "VALUES3" to errloc.
     call "checkerr" using SQLCA errloc.
 
* move Department Information section into hv-new-section-buffer
     EXEC SQL VALUES (SUBSTR(:hv-doc-locator1,
              :hv-start-deptinfo,
              :hv-start-educ - :hv-start-deptinfo))
              INTO :hv-new-section-buffer END-EXEC.
     move "VALUES4" to errloc.
     call "checkerr" using SQLCA errloc.
 
* Append the new section to the end (assume it has been filled)
* Effectively, this just moves the Dept Info to the bottom of
* the resume.
     EXEC SQL VALUES (:hv-doc-locator2 ||
              :hv-new-section-buffer)
              INTO :hv-doc-locator3 END-EXEC.
     move "VALUES5" to errloc.
     call "checkerr" using SQLCA errloc.
 
* Store this resume in the table.
* This is where the LOB value bytes really move.
     EXEC SQL INSERT INTO emp_resume                              (4)
              VALUES ('A00130', 'ascii', :hv-doc-locator3)
              END-EXEC.
     move "INSERT" to errloc.
     call "checkerr" using SQLCA errloc.
 
     display "LOBEVAL completed".
 
     EXEC SQL FREE LOCATOR :hv-doc-locator1, :hv-doc-locator2(5)
              :hv-doc-locator3 END-EXEC.
     move "FREE LOCATOR" to errloc.
     call "checkerr" using SQLCA errloc.
 
     EXEC SQL CONNECT RESET END-EXEC.
     move "CONNECT RESET" to errloc.
     call "checkerr" using SQLCA errloc.
 
 End-Prog.
     stop run.

Indicator Variables and LOB Locators

For normal host variables in an application program, when selecting a NULL value into a host variable, a negative value is assigned to the indicator variable signifying that the value is NULL. In the case of LOB locators, however, the meaning of indicator variables is slightly different. Since a locator host variable itself can never be NULL, a negative indicator variable value indicates that the LOB value represented by the LOB locator is NULL. The NULL information is kept local to the client using the indicator variable value -- the server does not track NULL values with valid locators.

LOB File Reference Variables

File reference variables are similar to host variables except they are used to transfer data to and from client files, and not to and from memory buffers. A file reference variable represents (rather than contains) the file, just as a LOB locator represents (rather than contains) the LOB value. Database queries, updates, and inserts may use file reference variables to store, or to retrieve, single LOB values.

For very large objects, files are natural containers. In fact, it is likely that most LOBs begin as data stored in files on the client before they are moved to the database on the server. The use of file reference variables assists in moving LOB data. Programs use file reference variables to transfer LOB data from the client file directly to the database engine. The client application does not have to write utility routines to read and write files using host variables (which have size restrictions) to carry out the movement of LOB data.
Note:The file referenced by the file reference variable must be accessible from (but not necessarily resident on) the system on which the program runs. For a stored procedure, this would be the server.

A file reference variable has a data type of BLOB, CLOB, or DBCLOB. It is used either as the source of data (input) or as the target of data (output). The file reference variable may have a relative file name or a complete path name of the file (the latter is advised). The file name length is specified within the application program. The data length portion of the file reference variable is unused during input. During output, the data length is set by the application requestor code to the length of the new data written to the file.

When using file reference variables there are different options on both input and output. You must choose an action for the file by setting the file_option field in the file reference variable structure. Choices for assignment to the field covering both input and output values are shown below.

Values (shown for C) and options when using input file reference variables are as follows:

Values and options when using output file reference variables are as follows:

Notes:

  1. In an Extended UNIX Code (EUC) environment, the files to which DBCLOB file reference variables point are assumed to contain valid EUC characters appropriate for storage in a graphic column, and to never contain UCS-2 characters. For more information on DBCLOB files in an EUC environment, see "Considerations for DBCLOB Files".

  2. If a LOB file reference variable is used in an OPEN statement, the file associated with the LOB file reference variable must not be deleted until the cursor is closed.

For more information on file reference variables, see the SQL Reference.

Example: Extracting a Document To a File

This program example shows how CLOB elements can be retrieved from a table into an external file.

How the Sample LOBFILE Program Works

  1. Declare host variables. The BEGIN DECLARE SECTION and END DECLARE SECTION statements delimit the host variable declarations. Host variables are prefixed with a colon (:) when referenced in an SQL statement. A CLOB FILE REFERENCE host variable is declared.

  2. CLOB FILE REFERENCE host variable is set up. The attributes of the FILE REFERENCE is set up. A file name without a fully declared path is, by default, placed in the current working directory.

  3. Select in to the CLOB FILE REFERENCE host variable. The data from the resume field is selected into the filename referenced by the host variable.

The CHECKERR macro/function is an error checking utility which is external to the program. The location of this error checking utility depends upon the programming language used:

C
check_error is redefined as CHECKERR and is located in the util.c file.

COBOL
CHECKERR is an external program named checkerr.cbl

See "Using GET ERROR MESSAGE in Example Programs" for the source code for this error checking utility.

C Sample: LOBFILE.SQC

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sql.h>
#include "util.h"
 
EXEC SQL INCLUDE SQLCA;
 
#define  CHECKERR(CE_STR)   if (check_error (CE_STR, &sqlca) != 0) return 1;
 
int main(int argc, char *argv[]) {
 
   EXEC SQL BEGIN DECLARE SECTION; (1)
      SQL TYPE IS CLOB_FILE resume;
      short lobind;
      char userid[9];
      char passwd[19];
   EXEC SQL END DECLARE SECTION;
 
   printf( "Sample C program: LOBFILE\n" );
 
   if (argc == 1) {
      EXEC SQL CONNECT TO sample;
	  CHECKERR ("CONNECT TO SAMPLE");
   }
   else if (argc == 3) { 
      strcpy (userid, argv[1]);
      strcpy (passwd, argv[2]);
      EXEC SQL CONNECT TO sample USER :userid USING :passwd;
      CHECKERR ("CONNECT TO SAMPLE");
   }
   else {
      printf ("\nUSAGE: lobfile [userid passwd]\n\n");
      return 1;
   } /* endif */
   
   strcpy (resume.name, "RESUME.TXT");  (2)
   resume.name_length = strlen("RESUME.TXT");
   resume.file_options = SQL_FILE_OVERWRITE;
 
   EXEC SQL SELECT resume INTO :resume :lobind FROM emp_resume  (3)
      WHERE resume_format='ascii' AND empno='000130';
 
   if (lobind < 0) {
      printf ("NULL LOB indicated \n");
   } else {
      printf ("Resume for EMPNO 000130 is in file : RESUME.TXT\n");
   } /* endif */
 
   EXEC SQL CONNECT RESET;
   CHECKERR ("CONNECT RESET");
   return 0;
}
/* end of program : LOBFILE.SQC */

COBOL Sample: LOBFILE.SQB

 
 Identification Division.
 Program-ID. "lobfile".
 
 Data Division.
 Working-Storage Section.
     copy "sqlenv.cbl".
     copy "sql.cbl".
     copy "sqlca.cbl".
 
     EXEC SQL BEGIN DECLARE SECTION END-EXEC. (1)
 01 userid            pic x(8).
 01 passwd.
   49 passwd-length   pic s9(4) comp-5 value 0.
   49 passwd-name     pic x(18).
 01 resume            USAGE IS SQL TYPE IS CLOB-FILE.
 01 lobind            pic s9(4) comp-5.
     EXEC SQL END DECLARE SECTION END-EXEC.
 
 77 errloc          pic x(80).
 
 Procedure Division.
 Main Section.
     display "Sample COBOL program: LOBFILE".
 
* Get database connection information.
     display "Enter your user id (default none): " 
          with no advancing.
     accept userid.
 
     if userid = spaces
       EXEC SQL CONNECT TO sample END-EXEC
     else
       display "Enter your password : " with no advancing
       accept passwd-name.
 
* Passwords in a CONNECT statement must be entered in a VARCHAR
* format with the length of the input string.
     inspect passwd-name tallying passwd-length for characters
        before initial " ".
 
     EXEC SQL CONNECT TO sample USER :userid USING :passwd
         END-EXEC.
     move "CONNECT TO" to errloc.
     call "checkerr" using SQLCA errloc.
 
     move "RESUME.TXT" to resume-NAME.                            (2)
     move 10 to resume-NAME-LENGTH.
     move SQL-FILE-OVERWRITE to resume-FILE-OPTIONS.
 
     EXEC SQL SELECT resume INTO :resume :lobind (3)
              FROM emp_resume
              WHERE resume_format = 'ascii'
              AND empno = '000130' END-EXEC.
     if lobind less than 0 go to NULL-LOB-indicated.
 
     display "Resume for EMPNO 000130 is in file : RESUME.TXT".
     go to End-Main.
 
 NULL-LOB-indicated.
     display "NULL LOB indicated".
 
 End-Main.
     EXEC SQL CONNECT RESET END-EXEC.
     move "CONNECT RESET" to errloc.
     call "checkerr" using SQLCA errloc.
 End-Prog.
            stop run.

Example: Inserting Data Into a CLOB Column

In the path description of the following C program segment:

The following example shows how to insert data from a regular file referenced by :hv_text_file into a CLOB column (note that the path names used in the example are for UNIX-based systems):

     strcpy(hv_text_file.name, "/u/userid/dirname/filnam.1"); 
     hv_text_file.name_length = strlen("/u/userid/dirname/filnam.1"); 
     hv_text_file.file_options = SQL_FILE_READ; /* this is a 'regular' file */ 
  
     EXEC SQL INSERT INTO CLOBTAB 
       VALUES(:hv_text_file);


[ Top of Page | Previous Page | Next Page | Table of Contents | Index ]

[ DB2 List of Books | Search the DB2 Books ]