Oracle7 Server Concepts

Contents Index Home Previous Next

Packages

Packages provide a method of encapsulating related procedures, functions, and associated cursors and variables together as a unit in the database.

For example, the following two statements create the specification and body for a package that contains several procedures and functions that process banking transactions.

CREATE PACKAGE bank_transactions  AS 
  minimum_balance  CONSTANT NUMBER := 100.00; 
  PROCEDURE apply_transactions; 
  PROCEDURE enter_transaction (acct   NUMBER, 
				kind   CHAR, 
				amount NUMBER); 
END bank_transactions; 
 
CREATE PACKAGE BODY bank_transactions AS 
 
/*  Package to input bank transactions */ 
 
   new_status  CHAR(20);  /* Global variable to record status 
				of transaction being applied. Used 
				for update in APPLY_TRANSACTIONS. */ 
 
   PROCEDURE do_journal_entry (acct NUMBER, 
				kind CHAR) IS 
 
/*  Records a journal entry for each bank transaction applied 
    by the APPLY_TRANSACTIONS procedure. */ 
 
  BEGIN 
	INSERT INTO journal 
	  VALUES (acct, kind, sysdate); 
	IF kind = 'D' THEN 
	  new_status := 'Debit applied'; 
	ELSIF kind = 'C' THEN 
	  new_status := 'Credit applied'; 
	ELSE 
	  new_status := 'New account'; 
	END IF; 
  END do_journal_entry; 
 
 
  PROCEDURE credit_account (acct NUMBER, credit NUMBER) IS 
 
/* Credits a bank account the specified amount. If the account 
   does not exist, the procedure creates a new account first. */ 
 
	old_balance  NUMBER; 
	new_balance  NUMBER; 
 
  BEGIN 
	SELECT balance INTO old_balance FROM accounts 
	  WHERE acct_id = acct 
	  FOR UPDATE OF balance; /* Locks account for credit update */ 
	new_balance := old_balance + credit; 
	UPDATE accounts SET balance = new_balance 
	  WHERE acct_id = acct; 
	do_journal_entry(acct, 'C'); 
 
  EXCEPTION 
	WHEN NO_DATA_FOUND THEN  /* Create new account if not found */ 
	  INSERT INTO accounts (acct_id, balance) 
	    VALUES(acct, credit); 
	  do_journal_entry(acct, 'N'); 
	WHEN OTHERS THEN  /* Return other errors to application */ 
	  new_status := 'Error: ' || SQLERRM(SQLCODE); 
  END credit_account; 
 
  PROCEDURE debit_account (acct  NUMBER, debit NUMBER) IS 
 
/* Debits an existing account if result is greater than the 
   allowed minimum balance. */ 
 
	old_balance         NUMBER; 
	new_balance         NUMBER; 
	insufficient_funds  EXCEPTION; 
 
  BEGIN 
	SELECT balance INTO old_balance FROM accounts 
	  WHERE acct_id = acct 
	  FOR UPDATE OF balance; 
	new_balance := old_balance - debit; 
	IF new_balance >= minimum_balance THEN 
	  UPDATE accounts SET balance = new_balance 
	    WHERE acct_id = acct; 
	do_journal_entry(acct, 'D'); 
	ELSE 
	  RAISE insufficient_funds; 
	END IF; 
  EXCEPTION 
	WHEN NO_DATA_FOUND THEN 
	  new_status := 'Nonexistent account'; 
	WHEN insufficient_funds THEN 
	  new_status := 'Insufficient funds'; 
	WHEN OTHERS THEN  /* Returns other errors to application */ 
	  new_status := 'Error: ' || SQLERRM(SQLCODE); 
  END debit_account; 
 
  PROCEDURE apply_transactions IS 
 
/* Applies pending transactions in the table TRANSACTIONS to the 
   ACCOUNTS table. Used at regular intervals to update bank 
   accounts without interfering with input of new transactions. */ 
 
/* Cursor fetches and locks all rows from the TRANSACTIONS 
   table with a status of 'Pending'. Locks released after all         pending transactions have been applied. */ 
 
	CURSOR trans_cursor IS 
	  SELECT acct_id, kind, amount FROM transactions 
	    WHERE status = 'Pending' 
	    ORDER BY time_tag 
	    FOR UPDATE OF status; 
 
  BEGIN 
	FOR trans IN trans_cursor LOOP   /* implicit open and fetch */ 
	  IF trans.kind = 'D' THEN 
	    debit_account(trans.acct_id, trans.amount); 
	  ELSIF trans.kind = 'C' THEN 
	    credit_account(trans.acct_id, trans.amount); 
	  ELSE 
	    new_status := 'Rejected'; 
	  END IF; 
	  /* Update TRANSACTIONS table to return result of applying 
	     this transaction. */ 
	  UPDATE transactions SET status = new_status 
	    WHERE CURRENT OF trans_cursor; 
	END LOOP; 
	COMMIT;  /* Release row locks in TRANSACTIONS table. */ 
  END apply_transactions; 
 
 
  PROCEDURE enter_transaction (acct   NUMBER, 
				kind   CHAR, 
				amount NUMBER) IS 
 
/* Enters a bank transaction into the TRANSACTIONS table. A new 
   transaction is always input into this 'queue' before being 
   applied to the specified account by the APPLY_TRANSACTIONS 
   procedure. Therefore, many transactions can be simultaneously 
   input without interference. */ 
 
  BEGIN 
	INSERT INTO transactions 
	  VALUES (acct, kind, amount, 'Pending', sysdate); 
	COMMIT; 
  END enter_transaction; 
 
END bank_transactions; 

While packages allow the database administrator or application developer to organize similar routines, they also offer increased functionality and database performance.

Applications for Packages

Packages are used to define related procedures, variables, and cursors and are often implemented to provide advantages in the following areas:

Encapsulation

Stored packages allow you to encapsulate, or group, related stored procedures, variables, datatypes, etc. in a single named, stored unit in the database. This provides for better organization during the development process.

Encapsulation of procedural constructs in a package also makes privilege management easier. Granting the privilege to use a package makes all constructs of the package accessible to the grantee.

Public and Private Data and Procedures

The methods of package definition allow you to specify which variables, cursors, and procedures are

public Directly accessible to the user of a package.
private Hidden from the user of a package.
For example, a package might contain ten procedures. However, the package can be defined so that only three procedures are public and therefore available for execution by a user of the package; the remainder of the procedures are private and can only be accessed by the procedures within the package.

Do not confuse public and private package variables with grants to PUBLIC, which are described in Chapter 17, "Database Access".

Separate Package Specification and Package Body

You create a package in two parts: the specification and the body. A package's specification declares all public constructs of the package and the body defines all constructs (public and private) of the package. This separation of the two parts provides the following advantages:

Performance Improvement

Using packages rather than stand-alone stored procedures results in the following improvements:

Dependency Tracking for Packages

A package is dependent on the objects referenced by the procedures and functions defined in its body. Oracle automatically tracks and manages such dependencies. See Chapter 16, "Dependencies Among Schema Objects", for more information about dependency tracking.


Contents Index Home Previous Next