Rem
Rem atxdemo.sql
Rem
Rem  Copyright (c) Oracle Corporation 1998. All Rights Reserved.
Rem
Rem  NAME
Rem    atxdemo.sql - Demo program for Autonomous Transactions, RDBMS, 8.1
Rem
Rem
Rem  FUNCTION
Rem     
Rem     The Demo Program illustrates the use of Autonomous Transactions.
Rem
Rem     An Autonomous Transaction allows you to step out of the context of the
Rem     calling transaction (which is main transaction) , perform some SQL
Rem     operations (in new context which is autonomous transaction's context),
Rem     commit or rollback those operations and then return to the calling
Rem     transaction's (main transaction's) context without losing the work 
Rem     done earlier in the main transaction.
Rem    
Rem
Rem  NOTES ON EXECUTING THIS DEMO PROGRAM
Rem
Rem    1) Use svrmgrl for running this demo program
Rem  
Rem    2) Connect to oracle and run atxdemo.sql
Rem
Rem    3) The result is printed in file atxdemo.lis
Rem

/*
** ##########################################################################
**
**          DESCRIPTION OF DEMO EXAMPLE
**
** ##########################################################################
**
** The Demo Example illustrates the use of Autonomous Transactions introduced
** in 8.1 
**
** Assumptions made :
** ------------------
** 1) In a bank, a customer can initiate a withdrawal request.
**
** 2) Withdrawal is allowed, only if balance  > $500, after withdrawal.
**
** Scenario :
** ----------
** 1) A customer issues a transaction request(withdrawal). Before the request
**    is processed, the request gets a unique transaction_id.
**
** 2) The status of each transaction is recorded alongwith appropriate message.
**    The Status can be one of the following.
**       a) 'S' - Processing of transaction started
**       b) 'W' - Withdrawal Successful
**       c) 'R' - Request Rejected
**
** 3) Depending on the Balance conditions, the customer initiated transaction
**    gets completed successfully or aborted.
**
** Main Transaction :
** --------------------
** 1. Update Balance in account_info table .
**
** 2. Initiate the autonomous transaction C1. C1 in turn calls another  
**    autonomous transaction C2.
**
** 3. Check validity of the transaction request.
**
** 4. Initiate the autonomous transaction C3. 
**
** 5. Depending on the validity/correctness of request, commit or rollback the
**    main transaction.
**
** Procedure process_withdrawal is executed as main transaction.
**
** Autonomous Transactions :
** -------------------------
** C1.  Insert the request details like Account_no, Cheque_no, amount and 
**      Unique Transaction id into transaction_log table and commit.
**      Also set status of the transaction to 'S' which represents that 
**      processing of the request has started.
**      Procedure log_transaction is executed as Autonomous Transaction to 
**      perform above mentioned operations .
**
** C2. To Generate Unique Transaction Id, this Autonomous Transaction is 
**     called from C1. C2 uses trans_counter table for generating Unique 
**     Transaction Id. Function get_next_trans_id in Package generate_next_id
**     is used to generate next unique transaction id. This also demonstrates
**     calling Autonomous Blocks from SQL contexts.
**
** C3. Update status and message in transaction_log table and commit. The 
**     procedure update_transaction_status is invoked as an Autonomous 
**     Transaction to perform above task.
*/

/*
** #########################################################################
**
**               CODING  STYLE  USED
**
** #########################################################################
**
**    1. Uppercase is used for PL/SQL keywords and lower case for defined 
**       items.
**
**    2. Names for local variables are Prefixed with l_ 
**
*
*/


SET SERVEROUTPUT ON
SPOOL atxdemo.dbg

/*--------------------------------------------------------------------------*/
/*         Table for storing information about all accounts in a bank       */
/*--------------------------------------------------------------------------*/

CREATE TABLE account_info (
	 Customer_Id    Varchar2(10),
	 Account_no     Varchar2(10),
	 Balance        Number(10,2)
	    )
/
 
/*--------------------------------------------------------------------------*/
/*Table for storing information related to customer initiated transactions */
/*-------------------------------------------------------------------------*/

CREATE TABLE transaction_log (
	 Transaction_Id      Varchar2(10),
	 Account_no          Varchar2(10),
	 Cheque_no           Varchar2(10),
	 Amount              Number(10,2),
	 Transaction_type    Char(1),
	 Transaction_Date    Date,
	 Transaction_Status  Char(1),
	 Message             Varchar2(50) 
		       )
/

/*-------------------------------------------------------------------------*/
/*Table storing number which is equal to latest transaction_id generated + 1*/ 
/*         Note that this table is one row, one column table                */
/*-------------------------------------------------------------------------*/

CREATE TABLE trans_counter (
	trans_id     NUMBER(8)
	    )
/

/*------------------------------------------------------------------------*/
/*    Create a package where function to get next transaction id resides   */
/*    This function namely, get_next_trans_id demonstrates calling         */
/*    Autonomous Transactions From SQL Context                             */
/*    Even though the Package specifies WNDS and RNDS restrictions on      */
/*    function get_next_txn_id, because the function is executed as        */
/*    Autonomous Transaction, the update and commit  is allowed within     */
/*    the function.                                                        */
/* ----------------------------------------------------------------------- */

CREATE OR REPLACE PACKAGE generate_next_id IS
    FUNCTION  get_next_txn_id   RETURN NUMBER;
      PRAGMA RESTRICT_REFERENCES(get_next_txn_id, WNDS, RNDS);
END generate_next_id;
/
SHOW ERRORS


/*------------------------------------------------------------------------*/
/*        Define Body Of the package                                     */
/*------------------------------------------------------------------------*/

CREATE OR REPLACE PACKAGE BODY generate_next_id IS

  FUNCTION get_next_txn_id
  RETURN NUMBER
  IS
         PRAGMA AUTONOMOUS_TRANSACTION;-- Specifying Autonomous Transaction
         l_trans_id NUMBER(8);
  BEGIN
         SELECT trans_id INTO l_trans_id FROM trans_counter;
	 UPDATE trans_counter SET trans_id = trans_id + 1;
	 COMMIT;
	 RETURN (l_trans_id);
  EXCEPTION
    /* If no data present in trans_counter then initialize counter */

    WHEN NO_DATA_FOUND THEN
	  INSERT INTO trans_counter VALUES(2);
	  COMMIT;
	  RETURN(1);
  END get_next_txn_id;

END generate_next_id;
/
SHOW ERRORS
   

/*--------------------------------------------------------------------------*/
/*   Procedure which logs details for each and every transaction into       */
/*   Transaction_log table. The parameters to this procedure are Account_no */
/*   Cheque_no, transaction amount and type of transaction.                 */
/*   The Transaction type can be 'W' - For Withdrawal.                      */
/*   The Procedure executes as an Autonomous Transaction and hence the      */
/*   Commit operation does not the commit the work done in Main             */
/*   Transaction. Also the autonomous transaction is totally independent of */
/*   main Transaction.                                                      */
/*--------------------------------------------------------------------------*/
CREATE OR REPLACE PROCEDURE log_transaction(Account_Id  Varchar2,
    Cheque_no  Varchar2, Amount Number,Trans_type Char)
IS
   pragma AUTONOMOUS_TRANSACTION;-- Specifying Autonomous Transaction
Begin
   INSERT INTO transaction_log VALUES(generate_next_id.get_next_txn_id,
       Account_id, Cheque_no, Amount, Trans_type,SYSDATE,'S',
	'Started Processing Transaction');
   COMMIT;
End log_transaction;
/
SHOW ERRORS


/*------------------------------------------------------------------------  */
/*   Procedure which updates the status and message details of the          */
/*   transaction. Parameters to this procedure are Account_no, Status       */
/*   of transaction and message to be updated.                              */
/*   Account_no is passed to uniquely identify the transaction log record.  */ 
/*    The Procedure executes as an Autonomous Transaction and hence the     */
/*   Commit operation doesnot the commit the work done in Main              */
/*   Transaction. Also the autonomous transaction is totally independent of */
/*   main Transaction.                                                      */
/*--------------------------------------------------------------------------*/
CREATE OR REPLACE PROCEDURE update_transaction_status(Account_Id  Varchar2,
    Status Char, Mesg VARCHAR2)
IS
   pragma AUTONOMOUS_TRANSACTION;-- Specify Autonomous Transaction
Begin
   UPDATE transaction_log SET Transaction_status = status, Message = mesg
   WHERE ( Account_no = Account_Id ) AND ( Transaction_Status = 'S');
   COMMIT;
End update_transaction_status;
/     
SHOW ERRORS

/*------------------------------------------------------------------------ */
/*    Procedure which processes a particular withdrawal.                   */
/*    Parameters to this procedure are Account Number, Cheque Number &     */
/*    amount of transaction.                                               */
/*    This procedure in turn calls two Autonomous Transactions to          */
/*    perform logging transaction details and status,message updating      */
/*    Main Transaction is rolled back if the initiated request             */
/*    can not be executed.This happens if Balance after withdrawal         */
/*    goes below $500. But the log record for this transaction gets        */
/*    commited into Transaction_log table with appropriate message         */
/*    This procedure assumes that account_id exists and amount is positive.*/
/* ---------------------------------------------------------------------- */

CREATE OR REPLACE PROCEDURE process_withdrawal(Account_Id Varchar2,
    Cheque_No Varchar2, Amount Number)
IS
    l_balance  NUMBER(10,2);
BEGIN

    /* ----------- Update Balance -------------------------------------*/
    UPDATE Account_Info SET BALANCE = BALANCE - Amount
    WHERE Account_no = Account_Id;

    /* Call Autonomous Transaction to record transaction details ------*/
    log_transaction(Account_Id, Cheque_no, Amount,'W');

    /* Check Validity of initiated Transaction ------------------------*/
    SELECT BALANCE INTO l_balance FROM Account_Info WHERE 
    Account_no = Account_Id;

    If l_Balance < 500 Then
	/* Not Valid Request. Call Autonomous Transaction to Update ----*/
	/* transaction log record And Rollback Main Transaction --------*/

	update_transaction_status(Account_Id,'R',
	   'Balance Goes Below $500.Txn Rejected');
        ROLLBACK;
    Else
	/* Valid Request. Call Autonomous Transaction to Update ----*/
	/* transaction log record And Commit Main Transaction ------*/

	update_transaction_status(Account_Id,'W','Withdrawal Successful');
	COMMIT;
    End If;
END process_withdrawal;
/
SHOW ERRORS

/*------------------------------------------------------------------------ */
/*       Display Cust Id,Account and Balance Information                   */
/*------------------------------------------------------------------------ */

CREATE OR REPLACE PROCEDURE print_acct_balance 
IS
    CURSOR l_c1 IS 
	 SELECT customer_id,account_no,balance 
	 FROM account_info;
    l_customer_id  VARCHAR2(10);
    l_account_no   VARCHAR2(10);
    l_balance      NUMBER(10,2);
BEGIN
     DBMS_OUTPUT.PUT('--------------------------------------------------');
     DBMS_OUTPUT.PUT_LINE('---------------------------');
     DBMS_OUTPUT.PUT_LINE('  Account No        Cust Id           Balance');
     DBMS_OUTPUT.PUT_LINE('                                    ( Dollars )');
     DBMS_OUTPUT.PUT('--------------------------------------------------');
     DBMS_OUTPUT.PUT_LINE('---------------------------');
     OPEN l_c1;
     LOOP
	 FETCH l_c1 INTO l_customer_id,l_account_no,l_balance;
	 If (l_c1%NOTFOUND) Then
	    EXIT;
         End if;
	 DBMS_OUTPUT.PUT_LINE('  '||l_account_no||'            '||
	    l_customer_id || '              ' || l_balance);
     END LOOP;
     CLOSE l_c1;
     DBMS_OUTPUT.PUT('--------------------------------------------------');
     DBMS_OUTPUT.PUT_LINE('---------------------------');
END print_acct_balance;
/
SHOW ERRORS

/*------------------------------------------------------------------------ */
/*     Display Transaction Log Records and Balance Information.            */
/*------------------------------------------------------------------------ */

CREATE OR REPLACE PROCEDURE print_transaction_log 
IS
    CURSOR l_c1 IS 
	 SELECT account_no,amount,Transaction_status, 
	 message FROM transaction_log order by account_no asc;
    l_account_no   VARCHAR2(10);
    l_amount      NUMBER(10,2);
    l_trans_status CHAR(1);
    l_message    VARCHAR2(50);
    l_customer_id    VARCHAR2(10);
    l_balance    NUMBER(10,2);
BEGIN
     DBMS_OUTPUT.PUT('--------------------------------------------------');
     DBMS_OUTPUT.PUT_LINE('---------------------------');
     DBMS_OUTPUT.PUT_LINE(' Account  Cust  Requested  Status  Balance'||
     '        Message  '); 
     DBMS_OUTPUT.PUT_LINE('  No      Id    Amount     Of Txn (Dollars)');
     DBMS_OUTPUT.PUT_LINE('                (Dollars)  ');
     DBMS_OUTPUT.PUT('--------------------------------------------------');
     DBMS_OUTPUT.PUT_LINE('---------------------------');
     OPEN l_c1;
     LOOP
	 FETCH l_c1 INTO l_account_no,l_amount,l_trans_status,l_message;
	 If (l_c1%NOTFOUND) Then
	    EXIT;
         End if;
	 SELECT customer_id,balance INTO l_customer_id,l_balance  
	 FROM account_info WHERE account_no = l_account_no;
	 DBMS_OUTPUT.PUT_LINE(RTRIM(l_account_no)||'  '||
	    RTRIM(l_customer_id)||'   '||RTRIM(l_amount)||'       '|| 
	    RTRIM(l_trans_status) ||'     '|| l_balance ||'    '||
	    RTRIM(l_message));
     END LOOP;
     CLOSE l_c1;
     DBMS_OUTPUT.PUT('--------------------------------------------------');
     DBMS_OUTPUT.PUT_LINE('---------------------------');
END print_transaction_log;
/
SHOW ERRORS

/*------------------------------------------------------------------------ */
/*                            Create some accounts.                        */
/*------------------------------------------------------------------------ */

CREATE OR REPLACE PROCEDURE create_accounts
IS
BEGIN
      INSERT INTO account_info VALUES('CST001','ACT001',1200);
      INSERT INTO account_info VALUES('CST002','ACT002',5200);
      INSERT INTO account_info VALUES('CST003','ACT003',4750);
      INSERT INTO account_info VALUES('CST004','ACT004',1750);
      COMMIT;
END Create_accounts;   
/
SHOW ERRORS
SPOOL OFF

SPOOL atxdemo.lis

/* ------------- Main Execution Begins Here ------------------------------ */
BEGIN
  /* ------------------ Create Accounts ---------------------------------- */
  Create_accounts;

  /* ------ Display account information before withdrawal transactions ---*/
  DBMS_OUTPUT.NEW_LINE();
  DBMS_OUTPUT.PUT_LINE('Account Information Before WithDrawal Transactions :');
  print_acct_balance;

  /* ------ Process some withdrawal transactions ------------------------ */
  process_withdrawal('ACT001','CHQ001',1000);
  DBMS_OUTPUT.NEW_LINE();
  process_withdrawal('ACT003','CHQ002',3000);
  process_withdrawal('ACT004','CHQ003',1300);
  process_withdrawal('ACT002','CHQ004',1000);
  DBMS_OUTPUT.NEW_LINE();

  /* Display transaction log and Balance Information after withdrawal     */
  /* transactions                                                         */
  DBMS_OUTPUT.PUT_LINE('Transaction log and Balance Information after With'||
  'drawal Transactions :');

  print_transaction_log;
END;
/
SPOOL OFF

/*--- Clean Up ------- */
DROP TABLE account_info
/
DROP TABLE transaction_log 
/
DROP TABLE trans_counter 
/
DROP PACKAGE generate_next_id 
/
DROP PROCEDURE print_acct_balance
/
DROP PROCEDURE print_transaction_log
/
