Oracle PL/SQL – Packages

Subject: Information Technology – Database Systems
Grade: 12th Grade – HTL Computer Science
Prerequisites: Procedures and Functions, Cursors, Exceptions
Author: HTL Pinkafeld – IF/IT



1. What is a Package?

1.1 Definition

A package is a named PL/SQL unit that groups logically related types, variables, constants, cursors, procedures, and functions. It consists of two parts:

Package Specification (Spec)    Package Body
─────────────────────────────   ─────────────────────────────
Public interface                 Implementation
  • Types                          • Procedure/function code
  • Variables/constants            • Private elements
  • Cursor declarations            • Initialization section
  • Procedure/function headers

1.2 Advantages of Packages

Advantage Description
Encapsulation Hide implementation details
Modularity Logically related code in one unit
Performance Entire package loaded into memory on first call
Session state Package variables persist for the whole session
Overloading Same name, different parameters
Dependency management Body can be recompiled without changing Spec

2. Package Specification

2.1 Syntax

CREATE [OR REPLACE] PACKAGE package_name IS
    -- Public types, constants, variables
    -- Cursor declarations (header only)
    -- Procedure/function headers (without body)
END [package_name];
/

2.2 Example: pkg_employee

CREATE OR REPLACE PACKAGE pkg_employee IS

    -- Constants
    c_min_salary CONSTANT NUMBER := 1700;
    c_max_salary CONSTANT NUMBER := 50000;

    -- Public type
    TYPE t_emp_info IS RECORD (
        empno  emp.empno%TYPE,
        ename  emp.ename%TYPE,
        sal    emp.sal%TYPE,
        dept   dept.dname%TYPE
    );

    -- Procedure headers (interface)
    PROCEDURE give_raise   (p_empno emp.empno%TYPE, p_percent NUMBER);
    PROCEDURE show_dept    (p_deptno dept.deptno%TYPE);

    -- Function headers
    FUNCTION annual_salary (p_empno emp.empno%TYPE) RETURN NUMBER;
    FUNCTION is_manager    (p_empno emp.empno%TYPE) RETURN BOOLEAN;

    -- Public cursor type
    TYPE t_emp_cursor IS REF CURSOR RETURN emp%ROWTYPE;

END pkg_employee;
/

3. Package Body

3.1 Syntax

CREATE [OR REPLACE] PACKAGE BODY package_name IS
    -- Private variables, types (not visible in Spec)
    -- Implementation of all Spec-declared subprograms
    -- Additional private subprograms
    [BEGIN
        -- Initialization section (runs once at first call)
    ]
END [package_name];
/

3.2 Implementation of pkg_employee

CREATE OR REPLACE PACKAGE BODY pkg_employee IS

    -- Private variable (only visible in body)
    v_call_count NUMBER := 0;

    PROCEDURE give_raise (p_empno emp.empno%TYPE, p_percent NUMBER) IS
        v_old emp.sal%TYPE;
        v_new emp.sal%TYPE;
    BEGIN
        IF p_percent <= 0 OR p_percent > 50 THEN
            RAISE_APPLICATION_ERROR(-20010, 'Invalid percent: ' || p_percent);
        END IF;

        SELECT sal INTO v_old FROM emp WHERE empno = p_empno;
        v_new := ROUND(v_old * (1 + p_percent / 100), 2);

        IF v_new > c_max_salary THEN
            RAISE_APPLICATION_ERROR(-20011, 'Salary would exceed maximum.');
        END IF;

        UPDATE emp SET sal = v_new WHERE empno = p_empno;
        COMMIT;
        DBMS_OUTPUT.PUT_LINE('Salary: ' || v_old || ' → ' || v_new);
    EXCEPTION
        WHEN NO_DATA_FOUND THEN
            RAISE_APPLICATION_ERROR(-20012, 'Employee ' || p_empno || ' not found.');
    END give_raise;

    PROCEDURE show_dept (p_deptno dept.deptno%TYPE) IS
    BEGIN
        v_call_count := v_call_count + 1;
        DBMS_OUTPUT.PUT_LINE('--- Department ' || p_deptno || ' ---');
        FOR r IN (
            SELECT e.ename, e.job, e.sal
            FROM   emp e
            WHERE  e.deptno = p_deptno
            ORDER  BY e.sal DESC
        ) LOOP
            DBMS_OUTPUT.PUT_LINE(RPAD(r.ename, 12) || RPAD(r.job, 10) || r.sal);
        END LOOP;
    END show_dept;

    FUNCTION annual_salary (p_empno emp.empno%TYPE) RETURN NUMBER IS
        v_sal  emp.sal%TYPE;
        v_comm emp.comm%TYPE;
    BEGIN
        SELECT sal, NVL(comm, 0) INTO v_sal, v_comm
        FROM emp WHERE empno = p_empno;
        RETURN (v_sal + v_comm) * 12;
    EXCEPTION
        WHEN NO_DATA_FOUND THEN RETURN NULL;
    END annual_salary;

    FUNCTION is_manager (p_empno emp.empno%TYPE) RETURN BOOLEAN IS
        v_cnt NUMBER;
    BEGIN
        SELECT COUNT(*) INTO v_cnt FROM emp WHERE mgr = p_empno;
        RETURN v_cnt > 0;
    END is_manager;

END pkg_employee;
/

3.3 Calling the Package

BEGIN
    -- Package elements called via dot notation:
    pkg_employee.give_raise(7369, 5);
    pkg_employee.show_dept(20);

    IF pkg_employee.is_manager(7839) THEN
        DBMS_OUTPUT.PUT_LINE('King is a manager.');
    END IF;

    DBMS_OUTPUT.PUT_LINE(
        'Annual salary King: ' || pkg_employee.annual_salary(7839)
    );
END;
/

4. Package Variables and Session State

4.1 Lifetime

Package variables live for the entire database connection (session). Each session has its own copy:

CREATE OR REPLACE PACKAGE pkg_counter IS
    v_total_calls NUMBER := 0;
    PROCEDURE count_call (p_action VARCHAR2);
    FUNCTION  get_count   RETURN NUMBER;
END pkg_counter;
/

CREATE OR REPLACE PACKAGE BODY pkg_counter IS
    PROCEDURE count_call (p_action VARCHAR2) IS
    BEGIN
        v_total_calls := v_total_calls + 1;
        DBMS_OUTPUT.PUT_LINE('[' || v_total_calls || '] ' || p_action);
    END;

    FUNCTION get_count RETURN NUMBER IS
    BEGIN
        RETURN v_total_calls;
    END;
END pkg_counter;
/

BEGIN
    pkg_counter.count_call('Login');
    pkg_counter.count_call('Query');
    pkg_counter.count_call('Update');
    DBMS_OUTPUT.PUT_LINE('Total: ' || pkg_counter.get_count());
END;
/

5. Overloading in Packages

Same name, different signatures:

CREATE OR REPLACE PACKAGE pkg_format IS
    FUNCTION format_val (p_num  NUMBER,  p_mask VARCHAR2 DEFAULT 'FM999,990.00') RETURN VARCHAR2;
    FUNCTION format_val (p_date DATE,    p_mask VARCHAR2 DEFAULT 'DD/MM/YYYY')   RETURN VARCHAR2;
    FUNCTION format_val (p_text VARCHAR2) RETURN VARCHAR2;
END pkg_format;
/

CREATE OR REPLACE PACKAGE BODY pkg_format IS
    FUNCTION format_val (p_num NUMBER, p_mask VARCHAR2 DEFAULT 'FM999,990.00') RETURN VARCHAR2 IS
    BEGIN RETURN TO_CHAR(p_num, p_mask); END;

    FUNCTION format_val (p_date DATE, p_mask VARCHAR2 DEFAULT 'DD/MM/YYYY') RETURN VARCHAR2 IS
    BEGIN RETURN TO_CHAR(p_date, p_mask); END;

    FUNCTION format_val (p_text VARCHAR2) RETURN VARCHAR2 IS
    BEGIN RETURN '"' || TRIM(p_text) || '"'; END;
END pkg_format;
/

6. Private and Public Elements

Element Defined In Visible To
Public Package Spec Everyone (callable outside the package)
Private Package Body Only within the Package Body
CREATE OR REPLACE PACKAGE pkg_demo IS
    FUNCTION public_function (p_x NUMBER) RETURN NUMBER;
END pkg_demo;
/

CREATE OR REPLACE PACKAGE BODY pkg_demo IS
    -- PRIVATE (only for internal use):
    FUNCTION private_helper (p_x NUMBER) RETURN NUMBER IS
    BEGIN RETURN p_x * p_x; END;

    -- Public function uses private one:
    FUNCTION public_function (p_x NUMBER) RETURN NUMBER IS
    BEGIN RETURN private_helper(p_x) + 1; END;
END pkg_demo;
/

7. Initialization Section

The BEGIN section at the end of the Package Body runs exactly once per session – on the first call to any package element:

CREATE OR REPLACE PACKAGE pkg_config IS
    v_server_name  VARCHAR2(50);
    v_db_version   VARCHAR2(20);
    v_start_time   DATE;
END pkg_config;
/

CREATE OR REPLACE PACKAGE BODY pkg_config IS
BEGIN
    -- One-time initialization:
    SELECT host_name INTO v_server_name FROM v$instance;
    SELECT version   INTO v_db_version  FROM v$instance;
    v_start_time := SYSDATE;
    DBMS_OUTPUT.PUT_LINE('Package initialized: ' || TO_CHAR(v_start_time, 'HH24:MI:SS'));
EXCEPTION
    WHEN OTHERS THEN
        v_server_name := 'Unknown';
        v_db_version  := 'Unknown';
END pkg_config;
/

8. Important Oracle Standard Packages

8.1 DBMS_OUTPUT

BEGIN
    DBMS_OUTPUT.ENABLE(1000000);
    DBMS_OUTPUT.PUT_LINE('Line 1');
    DBMS_OUTPUT.PUT('Partial text ');
    DBMS_OUTPUT.NEW_LINE;
END;
/

8.2 DBMS_UTILITY

DECLARE
    v_start NUMBER;
    v_end   NUMBER;
BEGIN
    v_start := DBMS_UTILITY.GET_TIME();
    FOR i IN 1..100000 LOOP NULL; END LOOP;
    v_end := DBMS_UTILITY.GET_TIME();
    DBMS_OUTPUT.PUT_LINE('Duration: ' || (v_end - v_start) || ' cs');
END;
/

8.3 Commonly Used Standard Packages

Package Usage
DBMS_OUTPUT Text output (debugging)
DBMS_UTILITY Utility functions (timing, DDL)
DBMS_SQL Dynamic SQL
DBMS_LOB LOB processing
DBMS_SCHEDULER Job scheduling
DBMS_STATS Optimizer statistics
UTL_FILE File access (server side)
UTL_MAIL Send email
UTL_HTTP HTTP requests
DBMS_CRYPTO Cryptography
DBMS_RANDOM Random numbers

9. Managing Packages

-- Compile package spec and body:
ALTER PACKAGE pkg_employee COMPILE;
ALTER PACKAGE pkg_employee COMPILE BODY;

-- Check status of all packages:
SELECT object_name, object_type, status, last_ddl_time
FROM   user_objects
WHERE  object_type IN ('PACKAGE', 'PACKAGE BODY')
ORDER  BY object_name, object_type;

-- Show compile errors:
SELECT line, position, text
FROM   user_errors
WHERE  name = 'PKG_EMPLOYEE'
ORDER  BY type, sequence;

-- Drop body only (spec remains):
DROP PACKAGE BODY pkg_employee;

-- Drop spec and body:
DROP PACKAGE pkg_employee;

10. Summary and Outlook

10.1 Package Structure at a Glance

pkg_employee (Package)
├── Specification (public interface)
│   ├── Constants: c_min_salary, c_max_salary
│   ├── Types: t_emp_info, t_emp_cursor
│   ├── Procedures: give_raise, show_dept
│   └── Functions: annual_salary, is_manager
└── Body (implementation)
    ├── Private variable: v_call_count
    ├── Implementation of all Spec subprograms
    └── Private helper procedures/functions

10.2 When to Use a Package?

Situation Recommendation
Single independent procedure/function Standalone subprogram
Multiple related subprograms Package
Session-wide variables needed Package
Overloading desired Package
API for other users/schemas Package with clean Spec

10.3 Outlook

The final chapter covers Triggers – PL/SQL code that executes automatically when certain database events occur:

Previous TopicProcedures & Functions Next TopicTriggers