Application Development Guide

Updating and Deleting Retrieved Data

It is possible to update and delete the row referenced by a cursor. For a row to be updatable, the query corresponding to the cursor must not be read-only. For a description of what makes a query updatable or deletable, refer to the SQL Reference.

Updating Retrieved Data

To update with a cursor, use the WHERE CURRENT OF clause in an UPDATE statement. Use the FOR UPDATE clause to tell the system that you want to update some columns of the result table. You can specify a column in the FOR UPDATE without it being in the fullselect; therefore, you can update columns that are not explicitly retrieved by the cursor. If the FOR UPDATE clause is specified without column names, all columns of the table or view identified in the first FROM clause of the outer fullselect are considered to be updatable. Do not name more columns than you need in the FOR UPDATE clause. In some cases, naming extra columns in the FOR UPDATE clause can cause DB2 to be less efficient in accessing the data.

Deleting Retrieved Data

Deletion with a cursor is done using the WHERE CURRENT OF clause in a DELETE statement. In general, the FOR UPDATE clause is not required for deletion of the current row of a cursor. The only exception occurs when using dynamic SQL (see Writing Dynamic SQL Programs for information on dynamic SQL) for either the SELECT statement or the DELETE statement in an application which has been precompiled with LANGLEVEL set to SAA1, and bound with BLOCKING ALL. In this case, a FOR UPDATE clause is necessary in the SELECT statement. Refer to the Command Reference for information on the precompiler options.

The DELETE statement causes the row being referenced by the cursor to be deleted. This leaves the cursor positioned before the next row and a FETCH statement must be issued before additional WHERE CURRENT OF operations may be performed against the cursor.

Types of Cursors

Cursors fall into three categories:

Read only
The rows in the cursor can only be read, not updated. Read-only cursors are used when an application will only read data, not modify it. A cursor is considered read only if it is based on a read-only select-statement. See the rules in Updating Retrieved Data for select-statements which define non-updatable result tables.

There can be performance advantages for read-only cursors. For more information on read-only cursors, refer to the Administration Guide: Implementation.

Updatable
The rows in the cursor can be updated. Updatable cursors are used when an application modifies data as the rows in the cursor are fetched. The specified query can only refer to one table or view. The query must also include the FOR UPDATE clause, naming each column that will be updated (unless the LANGLEVEL MIA precompile option is used).

Ambiguous
The cursor cannot be determined to be updatable or read only from its definition or context. This can happen when a dynamic SQL statement is encountered that could be used to change a cursor that would otherwise be considered read-only.

An ambiguous cursor is treated as read only if the BLOCKING ALL option is specified when precompiling or binding. Otherwise, it is considered updatable.
Note:Cursors processed dynamically are always ambiguous.

For a complete list of criteria used to determine whether a cursor is read-only, updatable, or ambiguous, refer to the SQL Reference.

Example: OPENFTCH Program

This example selects from a table using a cursor, opens the cursor, and fetches rows from the table. For each row fetched, it decides if the row should be deleted or updated (based on a simple criteria). The sample is available in the following programming languages:

C
openftch.sqc

Java
Openftch.sqlj and OpF_Curs.sqlj

COBOL
openftch.sqb

The REXX language does not support static SQL, so a sample is not provided.

How the OPENFTCH Program Works

  1. Declare the cursor. The DECLARE CURSOR statement associates the cursor c1 to a query. The query identifies the rows that the application retrieves using the FETCH statement. The job field of staff is defined to be updatable, even though it is not specified in the result table.
  2. Open the cursor. The cursor c1 is opened, causing the database manager to perform the query and build a result table. The cursor is positioned before the first row.
  3. Retrieve a row. The FETCH statement positions the cursor at the next row and moves the contents of the row into the host variables. This row becomes the current row.
  4. Update OR Delete the current row. The current row is either updated or deleted, depending upon the value of dept returned with the FETCH statement.

    If an UPDATE is performed, the position of the cursor remains on this row because the UPDATE statement does not change the position of the current row.

    If a DELETE statement is performed, a different situation arises, because the current row is deleted. This is equivalent to being positioned before the next row, and a FETCH statement must be issued before additional WHERE CURRENT OF operations are performed.

  5. Close the cursor. The CLOSE statement is issued, releasing the resources associated with the cursor. The cursor can be opened again, however.

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
For C programs that call DB2 APIs, the sqlInfoPrint function in utilapi.c is redefined as API_SQL_CHECK in utilapi.h. For C embedded SQL programs, the sqlInfoPrint function in utilemb.sqc is redefined as EMB_SQL_CHECK in utilemb.h.

Java
Any SQL error is thrown as an SQLException and handled in the catch block of the application.

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 Example: OPENFTCH.SQC

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "utilemb.h"
 
EXEC SQL INCLUDE SQLCA;
 
int main(int argc, char *argv[]) 
{
 
   EXEC SQL BEGIN DECLARE SECTION;
      char   pname[10];
      short  dept;
      char userid[9];
      char passwd[19];
   EXEC SQL END DECLARE SECTION;
 
   printf( "Sample C program: OPENFTCH\n" );
 
   if (argc == 1) 
   {
      EXEC SQL CONNECT TO sample;
      EMB_SQL_CHECK("CONNECT TO SAMPLE");
   }
   else if (argc == 3) 
   { 
      strcpy (userid, argv[1]);
      strcpy (passwd, argv[2]);
      EXEC SQL CONNECT TO sample USER :userid USING :passwd;
      EMB_SQL_CHECK("CONNECT TO SAMPLE");
   }
   else 
   {
      printf ("\nUSAGE: openftch [userid passwd]\n\n");
      return 1;
   } /* endif */
   
   EXEC SQL DECLARE c1 CURSOR FOR  (1)
            SELECT name, dept FROM staff WHERE job='Mgr'
            FOR UPDATE OF job;
 
   EXEC SQL OPEN c1;  (2)
   EMB_SQL_CHECK("OPEN CURSOR");
 
   do 
   {
      EXEC SQL FETCH c1 INTO :pname, :dept;  (3)
      if (SQLCODE != 0) break;
 
      if (dept > 40) 
      {
         printf( "%-10.10s in dept. %2d will be demoted to Clerk\n",
            pname, dept );
         EXEC SQL UPDATE staff SET job = 'Clerk'  (4)
            WHERE CURRENT OF c1;
         EMB_SQL_CHECK("UPDATE STAFF");
      } 
      else 
      {
         printf ("%-10.10s in dept. %2d will be DELETED!\n",
            pname, dept);
         EXEC SQL DELETE FROM staff WHERE CURRENT OF c1;
         EMB_SQL_CHECK("DELETE");
      } /* endif */
   } while ( 1 );
 
   EXEC SQL CLOSE c1;  (5)
   EMB_SQL_CHECK("CLOSE CURSOR");
 
   EXEC SQL ROLLBACK;
   EMB_SQL_CHECK("ROLLBACK");
   printf( "\nOn second thought -- changes rolled back.\n" );
 
   EXEC SQL CONNECT RESET;
   EMB_SQL_CHECK("CONNECT RESET");
   return 0;
}
/* end of program : OPENFTCH.SQC */

Java Example: Openftch.sqlj

OpF_Curs.sqlj

//  PURPOSE : This file, named OpF_Curs.sqlj, contains the definition 
//            of the class OpF_Curs used in the sample program Openftch. 
 
import sqlj.runtime.ForUpdate;
#sql public iterator OpF_Curs implements ForUpdate (String, short);

Openftch.sqlj

import java.sql.*;
import sqlj.runtime.*;
import sqlj.runtime.ref.*;
 
class Openftch 
{   static
  {   try
    {   Class.forName ("COM.ibm.db2.jdbc.app.DB2Driver").newInstance ();
    } 
    catch (Exception e)
    {   System.out.println ("\n  Error loading DB2 Driver...\n");
      System.out.println (e);
      System.exit(1);
    }
  }
 
  public static void main(String argv[])
  {   try 
    {   System.out.println ("  Java Openftch Sample");
 
      String url = "jdbc:db2:sample";       // URL is jdbc:db2:dbname
      Connection con = null;          
 
      // Set the connection
      if (argv.length == 0) 
      {   // connect with default id/password
        con = DriverManager.getConnection(url);  
      }
      else if (argv.length == 2)
      {   String userid = argv[0];
        String passwd = argv[1];
 
        // connect with user-provided username and password
        con = DriverManager.getConnection(url, userid, passwd); 
      }
      else 
      {   throw new Exception(
                 "\nUsage: java Openftch [username password]\n");
      } // if - else if - else 
 
      // Set the default context
      DefaultContext ctx = new DefaultContext(con);            
      DefaultContext.setDefaultContext(ctx);
 
      // Enable transactions
      con.setAutoCommit(false);
 
      // Executing SQLJ positioned update/delete statements.
      try
      {   OpF_Curs forUpdateCursor;
 
        String name = null;
        short  dept=0;
 
 
        #sql forUpdateCursor = 
        {   SELECT name, dept 
                FROM staff
                WHERE job='Mgr' 
        }; // #sql            (1)(2)
 
        while (true) 
        {   #sql 
          {   FETCH :forUpdateCursor
                  INTO :name, :dept
          }; // #sql                        (3)
          if (forUpdateCursor.endFetch()) break;
 
          if (dept > 40)
          {   System.out.println (
                  name + " in dept. " 
                  + dept + " will be demoted to Clerk");
            #sql
            {   UPDATE staff SET job = 'Clerk'
                WHERE CURRENT OF :forUpdateCursor
            }; // #sql                    (4)
          }
          else
          {   System.out.println (
                  name + " in dept. " + dept
                  + " will be DELETED!");
            #sql
            {   DELETE FROM staff
                WHERE CURRENT OF :forUpdateCursor
            }; // #sql
          } // if - else
        }
        forUpdateCursor.close();  (5)
      }
      catch( Exception e )
      {   throw e; 
      } 
      finally
      {   // Rollback the transaction
        System.out.println("\nRollback the transaction...");
        #sql { ROLLBACK };
        System.out.println("Rollback done.");
      } // try - catch - finally
    }
    catch( Exception e )
    {   System.out.println (e);
    } // try - catch
  } // main
} // class Openftch

COBOL Example: OPENFTCH.SQB

       Identification Division.
       Program-ID. "openftch".
 
       Data Division.
       Working-Storage Section.
 
           copy "sqlca.cbl".
 
           EXEC SQL BEGIN DECLARE SECTION END-EXEC.
       01 pname             pic x(10).
       01 dept              pic s9(4) comp-5.
       01 userid            pic x(8).
       01 passwd.
         49 passwd-length   pic s9(4) comp-5 value 0.
         49 passwd-name     pic x(18).
           EXEC SQL END DECLARE SECTION END-EXEC.
 
       77 errloc          pic x(80).
 
       Procedure Division.
       Main Section.
           display "Sample COBOL program: OPENFTCH".
 
      * 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.
 
           EXEC SQL DECLARE c1 CURSOR FOR                               (1)
                    SELECT name, dept FROM staff
                    WHERE job='Mgr'
                    FOR UPDATE OF job END-EXEC.
 
           EXEC SQL OPEN c1 END-EXEC                                    (2)
           move "OPEN" to errloc.
           call "checkerr" using SQLCA errloc.
 
      * call the FETCH and UPDATE/DELETE loop.
           perform Fetch-Loop thru End-Fetch-Loop
              until SQLCODE not equal 0.
 
           EXEC SQL CLOSE c1 END-EXEC.                                  (5)
           move "CLOSE" to errloc.
           call "checkerr" using SQLCA errloc.
 
           EXEC SQL ROLLBACK END-EXEC.
           move "ROLLBACK" to errloc.
           call "checkerr" using SQLCA errloc.
           display "On second thought -- changes rolled back.".
 
           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 :pname, :dept END-EXEC.               (3)
           if SQLCODE not equal 0
              go to End-Fetch-Loop.
 
           if dept greater than 40
              go to Update-Staff.
 
       Delete-Staff.
           display pname, " in dept. ", dept,
              " will be DELETED!".
 
           EXEC SQL DELETE FROM staff WHERE CURRENT OF c1 END-EXEC.
           move "DELETE" to errloc.
           call "checkerr" using SQLCA errloc.
 
           go to End-Fetch-Loop.
 
       Update-Staff.
           display pname, " in dept. ", dept,
              " will be demoted to Clerk".
 
           EXEC SQL UPDATE staff SET job = 'Clerk'                      (4)
                    WHERE CURRENT OF c1 END-EXEC.
           move "UPDATE" to errloc.
           call "checkerr" using SQLCA errloc.
 
       End-Fetch-Loop. exit.
 
       End-Prog.
           stop run.


[ Top of Page | Previous Page | Next Page ]