HPlogo ALLBASE/SQL C Application Programming Guide: HP 3000 MPE/iX Computer Systems > Chapter 6 Processing with Cursors

Examples

» 

Technical documentation

Complete book in PDF
» Feedback

 » Table of Contents

 » Index

This code is intended as a guide; you will want to customize it for your specific needs.

The code illustrates status checking techniques with emphasis on deadlock detection. Four generalized code segments are presented:

  • A status checking routine to be used in conjunction with the other code segments.

  • Using a single kept cursor with locks.

  • Using multiple cursors and cursor stability.

  • Avoiding locks on terminal reads.

Common StatusCheck Procedure

   SQLStatusCheck ()

   {

      /************************************************************/

      /*  Deadlock occurred:  Set DeadLockFree to FALSE           */

      /************************************************************/



      if (sqlca.sqlcode = -14024) {

         DeadLockFree = FALSE;



      /************************************************************/

      /* If your program monopolizes CPU time by repeatedly       */

      /* reapplying a transaction, you could include a call       */

      /* to the XL PAUSE intrinsic at this point.                 */

      /************************************************************/



      }



      /************************************************************/

      /* Fatal Error: Show Messages, Release DBE, and terminate   */

      /************************************************************/



      else if (sqlca.sqlcode < 0) {

         do {

            EXEC SQL SQLEXPLAIN :SQLMessage;

            printf(SQLMessage);

         } while (sqlca.sqlcode != 0);



         EXEC SQL RELEASE;

      }



      /************************************************************/

      /* On fatal errors, halt program to avoid an endless loop   */

      /************************************************************/



      halt ();



      /************************************************************/

      /* No More Rows Found:  Set EndofScan to TRUE               */

      /************************************************************/



      if (sqlca.sqlcode = 100)

         EndofScan = TRUE;

   }  /* SQLStatusCheck function */

Single Cursor WITH LOCKS

   SQLSingleCursor ()

   {

      /***************************************************************/

      /*           First, declare the cursor:                        */

      /***************************************************************/



      EXEC SQL DECLARE C1 CURSOR FOR SELECT PartName,

                  FROM PurchDB.Parts WHERE SalesPrice > 500.00;



      /***************************************************************/

      /*  Next, open the cursor using KEEP CURSOR WITH LOCKS option: */

      /***************************************************************/



      do {

         DeadLockFree = TRUE;

         EXEC SQL OPEN C1 KEEP CURSOR WITH LOCKS;

         if (sqlca.sqlcode != 0)

            SQLStatusCheck ();

      } while (!DeadLockFree);



      /***************************************************************/

      /*       COMMIT WORK in order to preserve the cursor position  */

      /***************************************************************/



      EXEC SQL COMMIT WORK;

      if (sqlca.sqlcode != 0)

         SQLStatusCheck ();



      /***************************************************************/

      /*    Execute BULK FETCH option until there is no deadlock     */

      /***************************************************************/



      EndofScan = FALSE;

      while (! EndofScan) {

         do {

            DeadlockFree = TRUE;

            EXEC SQL BULK FETCH C1 INTO :PriceList, 1, 20;

            if (sqlca.sqlcode != 0)

               SQLStatusCheck ();

            else {

               EXEC SQL COMMIT WORK;

               if (sqlca.sqlcode != 0)

                  SQLStatusCheck ();

            }

         } while (!DeadLockFree);
      /*********************************************************/

      /*  Display qualified rows.  SQLERRD[3] contains the     */

      /*  number of qualified rows.                            */

      /*********************************************************/



         printf ("\n Part Name               Sales Price\n");

         for (i = 0; i < sqlca.sqlerrd[2]; i)

            printf("%s       %10.2f",PriceList[i].PartName,PriceList[i].SalesPrice);



      } /* WHILE not EndofScan */

      /***************************************************************/

      /*      Execute the CLOSE command until no deadlock occurs     */

      /***************************************************************/



      do {

         EXEC SQL CLOSE C1;

         if (sqlca.sqlcode != 0)

            SQLStatusCheck();

      } while (!DeadLockFree);



      EXEC SQL COMMIT WORK;

      if (sqlca.sqlcode != 0)

         SQLStatusCheck();



   }  /* SQLSingleCursor function */

Multiple Cursors and Cursor Stability

   SQLMultiCursor ()

   {

      /***************************************************************/

      /*           First, declare your cursors:                      */

      /***************************************************************/



      EXEC SQL DECLARE C1 CURSOR FOR SELECT BranchNo FROM Tellers

                 WHERE TellerNo > 15000 FOR UPDATE OF Status;



      EXEC SQL DECLARE C2 CURSOR FOR SELECT BranchNo FROM Branches

                 FOR UPDATE OF Credit;



      /***************************************************************/

      /* Next, Open cursor C1. Use explicit BEGIN WORK in do while loop. */

      /* loop to make sure ALLBASE/SQL will use the CS isolation level  */

      /* in case the program runs into a deadlock condition.            */

      /***************************************************************/



      do {

         DeadLockFree = TRUE;

         EXEC SQL BEGIN WORK CS;

         if (sqlca.sqlcode != 0)

            SQLStatusCheck();

         else {

            EXEC SQL OPEN C1 KEEP CURSOR WITH LOCKS;

            if (sqlca.sqlcode != 0)

               SQLStatusCheck();

         }

      } while (!DeadLockFree);



      /***************************************************************/

      /*   COMMIT WORK in order to preserve the cursor position      */

      /***************************************************************/



      EXEC SQL COMMIT WORK;

      if (sqlca.sqlcode != 0)

         SQLStatusCheck();



      /****************************************************************/

      /*  Initialize EndofScan to FALSE for the outer and inner loops */

      /****************************************************************/



      EndofScan = FALSE;

      while (! EndofScan) {


         /**********************************************************/

         /*   The following do while loop is executed once per     */

         /*   deadlock.  We FETCH again using Cursor C1, reopen    */

         /*   Cursor C2, then start to fetch rows using C2.        */

         /*   Note that there is a deadlock, and when the          */

         /*   transaction is aborted, Cursor                       */

         /*   C2 is closed and Cursor C1 returns to the beginning  */

         /*   of the transaction.  Any work done by the current    */

         /*   transaction on the database is undone. This provides */

         /*   a method of reapplying the transaction when a        */

         /*   deadlock at any point rolls it back.                 */

         /**********************************************************/



         do {

            DeadLockFree = TRUE;

            EXEC SQL FETCH C1 INTO :HostBranchNo1;

            if (sqlca.sqlcode != 0)

               SQLStatusCheck();

            else /*  1  */ {

               EXEC SQL OPEN C2;

               if (sqlca.sqlcode != 0)

                  SQLStatusCheck();

               else /*  2  */ {

                  while ((! EndofScan) && DeadLockFree) {

                     EXEC SQL FETCH C2 INTO :HostBranchNo2;

                     if (sqlca.sqlcode != 0)

                        SQLStatusCheck();

                     else /*  3  */ {

                     if (HostBranchNo1 == HostBranchNo2) {



      /**********************************************************/

      /*   Update the Branches table.  Note:  You might wish    */

      /*   to include a DateUpdated column in the Branches      */

      /*   table that can be updated with a timestamp every     */

      /*   time the Credit column is updated.  Then, in case    */

      /*   the program terminates abnormally, you will know     */

      /*   which values in the Credit column were updated       */

      /*   before termination.                                  */

      /**********************************************************/



                        EXEC SQL UPDATE Branches

                                 SET Credit = Credit*0.005

                                 WHERE CURRENT OF C2;

                        if (sqlca.sqlcode != 0)

                           SQLStatusCheck();

                     }  /* if HostBranchNo1 */

                  }  /* else 3 */

               }  /* inner while not EndofScan clause */
                   if (EndofScan) {

                      EndofScan = FALSE;

                      EXEC SQL CLOSE C2;

                      if (sqlca.sqlcode != 0)

                         SQLStatusCheck();

                         else  /*  4  */ {

                            EXEC SQL UPDATE TELLERS SET Status = :NewStatus

                                      WHERE CURRENT OF C1;

                            if (sqlca.sqlcode != 0)

                               SQLStatusCheck();

                            else  /*  5  */ {



       /**********************************************************/

       /*  Changes are committed and a new transaction begins.   */

       /*  Cursor C1 still open; locks associated with the page  */

       /*  pointed to by the cursor are kept.  All locks for C2  */

       /*  are released when the transaction is committed        */

       /**********************************************************/



                               EXEC SQL COMMIT WORK;

                               if (sqlca.sqlcode != 0)

                                 SQLStatusCheck();

                            }  /* else 5 */

                         }  /* else 4 */

                   }  /* if EndofScan */

                }  /* else 2 */

            }  /* else 1 */

         } while (!DeadLockFree);

      }  /* outer while not EndofScan clause */



      /***************************************************************/

      /*   Repeat the CLOSE command until no deadlock is found:      */

      /***************************************************************/



      do {

         EXEC SQL CLOSE C1;

         if (sqlca.sqlcode != 0)

            SQLStatusCheck();

      } while (!DeadLockFree);



      /****************************************************************/

      /*   Final COMMIT WORK:  current transaction ends, and no new   */

      /*   transaction begins.  THIS STEP IS ABSOLUTELY ESSENTIAL     */

      /*   TO END THE KEEP STATE!                                     */

      /****************************************************************/



      EXEC SQL COMMIT WORK;

      if (sqlca.sqlcode != 0)

         SQLStatusCheck();



   } /* SQLMultiCursor  function */

Avoiding Locks on Terminal Reads

   SQLNoTermLock ()

   {

      /***************************************************************/

      /*           First, declare the cursor:                        */

      /***************************************************************/

      EXEC SQL DECLARE C1 Cursor FOR

                SELECT PartNumber, SalesPrice FROM PurchDB.Parts;



      /***************************************************************/

      /* Execute the OPEN command until there is no deadlock:        */

      /***************************************************************/

      do {

         DeadLockFree = TRUE;

         EXEC SQL OPEN C1 KEEP CURSOR WITH NOLOCKS;

         if (sqlca.sqlcode != 0)

            SQLStatusCheck;

      } while (!DeadLockFree);



      /***************************************************************/

      /* COMMIT WORK to preserve the cursor position:                */

      /***************************************************************/

      EXEC SQL COMMIT WORK;

      if (sqlca.sqlcode != 0)

         SQLStatusCheck;



      /***************************************************************/

      /* Now FETCH a row from the active set until EndofScan         */

      /***************************************************************/

      while (! EndofScan) {

         do {

            DeadLockFree = TRUE;

            EXEC SQL FETCH C1 INTO :PartNumber, :PresentSalesPrice;

            if (sqlca.sqlcode != 0)

               SQLStatusCheck();

            else {

               EXEC SQL COMMIT WORK;

               if (sqlca.sqlcode != 0)

                  SQLStatusCheck();

            }

         } while (!DeadLockFree);



         /*********************************************************/

         /* Display the present price and prompt for a new one:   */

         /*********************************************************/



         printf("\n Part Number:         %d", PartNumber);

         printf("\n Current Sales Price: %10.2f", PresentSalesPrice);

         scanf ("\n Enter New Sales Price: %10.2f", NewSalesPrice);
         /*********************************************************/

         /* Verify that the value of PresentSalesPrice has not    */

         /* changed.  If not, update with NewSalesPrice           */

         /*********************************************************/



         do {

            DeadLockFree = TRUE;

            EXEC SQL SELECT SalesPrice INTO :SalesPrice FROM PurchDB.Parts

            WHERE PartNumber = :PartNumber;

            if (sqlca.sqlcode < 0)

               SQLStatusCheck();

            if (sqlca.sqlcode = 100)

               printf("\n Part Number no longer in database. Not updated.")

            else {

               if (SalesPrice == PresentSalesPrice) {

                  EXEC SQL UPDATE PurchDB.Parts

                  SET SalesPrice = :NewSalesPrice

                  WHERE PartNumber = :PartNumber;

                  if (sqlca.sqlcode != 0)

                     SQLStatusCheck();

               }

               else printf("\n Current price has changed. Not updated.");

            }

         } while (!DeadLockFree);



      }  /* while not EndofScan  */



      /***************************************************************/

      /*  Execute the CLOSE command until there is no deadlock:      */

      /***************************************************************/



      do {

         EXEC SQL CLOSE C1;

         if (sqlca.sqlcode != 0)

            SQLStatusCheck();

      } while (!DeadLockFree);



      /****************************************************************/

      /*   Final COMMIT WORK:  current transaction ends, and no new   */

      /*   transaction begins.  THIS STEP IS ABSOLUTELY ESSENTIAL     */

      /*   TO END THE KEEP STATE!                                     */

      /****************************************************************/



      EXEC SQL COMMIT WORK;

      if (sqlca.sqlcode != 0)

         SQLStatusCheck();



   }  /* SQLNoTermLock function */
Feedback to webmaster