Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

PLSQL Packages

Download as pdf or txt
Download as pdf or txt
You are on page 1of 10

PL/SQL Packages

What is a Package?

A package is a schema object that groups logically related PL/SQL types, variables,
constants, subprograms, cursors, and exceptions. A package is compiled and stored
in the database, where many applications can share its contents. You can think of a
package as an application.

A package always has a specification, which declares the public items that can be
referenced from outside the package. You can think of the package specification as
the application programming interface (API).

If the public items include cursors or subprograms, then the package must also have
a body. The body must define queries for public cursors and code for public
subprograms. The body can also declare and define private items that cannot be
referenced from outside the package, but are necessary for the internal workings of
the package. Finally, the body can have an initialization part, whose statements
initialize variables and do other one-time setup steps, and an exception-handling
part. You can change the body without changing the specification or the references
to the public items; therefore, you can think of the package body as a black box.

The AUTHID clause of the package specification determines whether the subprograms
and cursors in the package run with the privileges of their definer (the default) or
invoker, and whether their unqualified references to schema objects are resolved in
the schema of the definer or invoker.

Package Specification

A package specification declares public items. The scope of a public item is the
schema of the package. A public item is visible everywhere in the schema.
To reference a public item that is in scope but not visible, qualify it with the package
name

Each public item declaration has all information that you need to use the item. For
example, suppose that a package specification declares the function factorial this
way:

FUNCTION factorial (n INTEGER) RETURN INTEGER; -- returns n!

The declaration shows that factorial needs one argument of type INTEGER and
returns a value of type INTEGER, which is all you must know to invokefactorial. You
need not know how factorial is implemented (for example, whether it is iterative or
recursive).

1
Creating Package Specifications

To create a package specification, use the "CREATE PACKAGE Statement".

In Example 10-1, the specification for the package trans_data declares two public
types and three public variables.

Example 10-1 Simple Package Specification

CREATE OR REPLACE PACKAGE trans_data AS


TYPE TimeRec IS RECORD (
minutes SMALLINT,
hours SMALLINT);
TYPE TransRec IS RECORD (
category VARCHAR2(10),
account INT,
amount REAL,
time_of TimeRec);
minimum_balance CONSTANT REAL := 10.00;
number_processed INT;
insufficient_funds EXCEPTION;
END trans_data;
/

In Example 10-2, the specification for the package aa_pkg declares an associative
array type, aa_type. Then, the standalone procedure print_aa declares a formal
parameter of type aa_type. Next, the anonymous block declares a variable of
type aa_type, populates it, and passes it to the procedure print_aa, which prints it.

2
Example 10-2 Passing Associative Array to Standalone Subprogram

CREATE OR REPLACE PACKAGE aa_pkg IS


TYPE aa_type IS TABLE OF INTEGER INDEX BY VARCHAR2(15);
END;
/
CREATE OR REPLACE PROCEDURE print_aa (
aa aa_pkg.aa_type
) IS
i VARCHAR2(15);
BEGIN
i := aa.FIRST;

WHILE i IS NOT NULL LOOP


DBMS_OUTPUT.PUT_LINE (aa(i) || ' ' || i);
i := aa.NEXT(i);
END LOOP;
END;
/
DECLARE
aa_var aa_pkg.aa_type;
BEGIN
aa_var('zero') := 0;
aa_var('one') := 1;
aa_var('two') := 2;
print_aa(aa_var);
END;
/

Result:

1 one
2 two
0 zero

Because the package specifications in Example 10-1 and Example 10-2 do not
declare cursors or subprograms, the packages trans_data and aa_pkg do not need
bodies.

Package Body

If a package specification declares cursors or subprograms, then a package body is


required; otherwise, it is optional. The package body and package specification must
be in the same schema.

Every cursor or subprogram declaration in the package specification must have a


corresponding definition in the package body. The headings of corresponding

3
subprogram declarations and definitions must match word for word, except for white
space.

To create a package body, use the "CREATE PACKAGE BODY Statement".

In Example 10-3, the headings of the corresponding subprogram declaration and


definition do not match word for word; therefore, PL/SQL raises an exception, even
though employees.hire_date%TYPE is DATE.

Example 10-3 Matching Package Specification and Body

CREATE PACKAGE emp_bonus AS


PROCEDURE calc_bonus (date_hired employees.hire_date%TYPE);
END emp_bonus;
/
CREATE PACKAGE BODY emp_bonus AS
-- DATE does not match employees.hire_date%TYPE
PROCEDURE calc_bonus (date_hired DATE) IS
BEGIN
DBMS_OUTPUT.PUT_LINE
('Employees hired on ' || date_hired || ' get bonus.');
END;
END emp_bonus;
/

Result:

Warning: Package Body created with compilation errors.

Show errors (in SQL*Plus):

SHOW ERRORS

Result:

Errors for PACKAGE BODY EMP_BONUS:

LINE/COL ERROR
-------- -----------------------------------------------------------------
2/13 PLS-00323: subprogram or cursor 'CALC_BONUS' is declared in a
package specification and must be defined in the package body

Correct problem:

4
CREATE OR REPLACE PACKAGE BODY emp_bonus AS
PROCEDURE calc_bonus
(date_hired employees.hire_date%TYPE) IS
BEGIN
DBMS_OUTPUT.PUT_LINE
('Employees hired on ' || date_hired || ' get bonus.');
END;
END emp_bonus;
/

Result:

Package body created.

The cursors and subprograms declared in the package specification and defined in
the package body are public items that can be referenced from outside the package.
The package body can also declare and define private items that cannot be
referenced from outside the package, but are necessary for the internal workings of
the package.

You can change the package body without changing the specification or the
references to the public items.

Package Example

Example 10-8 creates a table, log, and a package, emp_admin, and then invokes
package subprograms from an anonymous block. The package has both specification
and body.

The specification declares a public type, cursor, and exception, and three public
subprograms. One public subprogram is overloaded (for information about
overloaded subprograms, see "Overloaded Subprograms").

The body declares a private variable, defines the public cursor and subprograms that
the specification declares, declares and defines a private function, and has an
initialization part.

The initialization part (which runs only the first time the anonymous block references
the package) inserts one row into the table log and initializes the private
variable number_hired to zero. Every time the package procedure hire_employee is
invoked, it updates the private variable number_hired.

Example 10-8 Creating emp_admin Package

-- Log to track changes (not part of package):

DROP TABLE log;


CREATE TABLE log (

5
date_of_action DATE,
user_id VARCHAR2(20),
package_name VARCHAR2(30)
);

-- Package specification:

CREATE OR REPLACE PACKAGE emp_admin AS


-- Declare public type, cursor, and exception:
TYPE EmpRecTyp IS RECORD (emp_id NUMBER, sal NUMBER);
CURSOR desc_salary RETURN EmpRecTyp;
invalid_salary EXCEPTION;

-- Declare public subprograms:

FUNCTION hire_employee (
last_name VARCHAR2,
first_name VARCHAR2,
email VARCHAR2,
phone_number VARCHAR2,
job_id VARCHAR2,
salary NUMBER,
commission_pct NUMBER,
manager_id NUMBER,
department_id NUMBER
) RETURN NUMBER;

-- Overload preceding public subprogram:


PROCEDURE fire_employee (emp_id NUMBER);
PROCEDURE fire_employee (emp_email VARCHAR2);

PROCEDURE raise_salary (emp_id NUMBER, amount NUMBER);


FUNCTION nth_highest_salary (n NUMBER) RETURN EmpRecTyp;
END emp_admin;
/
-- Package body:

CREATE OR REPLACE PACKAGE BODY emp_admin AS


number_hired NUMBER; -- private variable, visible only in this package

-- Define cursor declared in package specification:

CURSOR desc_salary RETURN EmpRecTyp IS


SELECT employee_id, salary
FROM employees
ORDER BY salary DESC;

6
-- Define subprograms declared in package specification:

FUNCTION hire_employee (
last_name VARCHAR2,
first_name VARCHAR2,
email VARCHAR2,
phone_number VARCHAR2,
job_id VARCHAR2,
salary NUMBER,
commission_pct NUMBER,
manager_id NUMBER,
department_id NUMBER
) RETURN NUMBER
IS
new_emp_id NUMBER;
BEGIN
new_emp_id := employees_seq.NEXTVAL;
INSERT INTO employees (
employee_id,
last_name,
first_name,
email,
phone_number,
hire_date,
job_id,
salary,
commission_pct,
manager_id,
department_id
)
VALUES (
new_emp_id,
hire_employee.last_name,
hire_employee.first_name,
hire_employee.email,
hire_employee.phone_number,
SYSDATE,
hire_employee.job_id,
hire_employee.salary,
hire_employee.commission_pct,
hire_employee.manager_id,
hire_employee.department_id
);
number_hired := number_hired + 1;
DBMS_OUTPUT.PUT_LINE('The number of employees hired is '
|| TO_CHAR(number_hired) );
RETURN new_emp_id;

7
END hire_employee;

PROCEDURE fire_employee (emp_id NUMBER) IS


BEGIN
DELETE FROM employees WHERE employee_id = emp_id;
END fire_employee;

PROCEDURE fire_employee (emp_email VARCHAR2) IS


BEGIN
DELETE FROM employees WHERE email = emp_email;
END fire_employee;

-- Define private function, available only inside package:

FUNCTION sal_ok (
jobid VARCHAR2,
sal NUMBER
) RETURN BOOLEAN
IS
min_sal NUMBER;
max_sal NUMBER;
BEGIN
SELECT MIN(salary), MAX(salary)
INTO min_sal, max_sal
FROM employees
WHERE job_id = jobid;

RETURN (sal >= min_sal) AND (sal <= max_sal);


END sal_ok;

PROCEDURE raise_salary (
emp_id NUMBER,
amount NUMBER
)
IS
sal NUMBER(8,2);
jobid VARCHAR2(10);
BEGIN
SELECT job_id, salary INTO jobid, sal
FROM employees
WHERE employee_id = emp_id;

IF sal_ok(jobid, sal + amount) THEN -- Invoke private function


UPDATE employees
SET salary = salary + amount
WHERE employee_id = emp_id;
ELSE

8
RAISE invalid_salary;
END IF;
EXCEPTION
WHEN invalid_salary THEN
DBMS_OUTPUT.PUT_LINE ('The salary is out of the specified range.');
END raise_salary;

FUNCTION nth_highest_salary (
n NUMBER
) RETURN EmpRecTyp
IS
emp_rec EmpRecTyp;
BEGIN
OPEN desc_salary;
FOR i IN 1..n LOOP
FETCH desc_salary INTO emp_rec;
END LOOP;
CLOSE desc_salary;
RETURN emp_rec;
END nth_highest_salary;

BEGIN -- initialization part of package body


INSERT INTO log (date_of_action, user_id, package_name)
VALUES (SYSDATE, USER, 'EMP_ADMIN');
number_hired := 0;
END emp_admin;
/
-- Invoke packages subprograms in anonymous block:

DECLARE
new_emp_id NUMBER(6);
BEGIN
new_emp_id := emp_admin.hire_employee (
'Belden',
'Enrique',
'EBELDEN',
'555.111.2222',
'ST_CLERK',
2500,
.1,
101,
110
);
DBMS_OUTPUT.PUT_LINE ('The employee id is ' || TO_CHAR(new_emp_id));
emp_admin.raise_salary (new_emp_id, 100);

DBMS_OUTPUT.PUT_LINE (

9
'The 10th highest salary is '||
TO_CHAR (emp_admin.nth_highest_salary(10).sal) ||
', belonging to employee: ' ||
TO_CHAR (emp_admin.nth_highest_salary(10).emp_id)
);

emp_admin.fire_employee(new_emp_id);
-- You can also delete the newly added employee as follows:
-- emp_admin.fire_employee('EBELDEN');
END;
/

Result is similar to:

The number of employees hired is 1


The employee id is 212
The 10th highest salary is 12075, belonging to employee: 168
There are now 107 employees.

10

You might also like