What is Error Handling?
Error Handling (also known as exception handling) is the systematic process of anticipating, detecting, and resolving runtime errors in PL/SQL programs.
Its core purpose is to gracefully manage unexpected situations that occur during program execution, preventing application crashes and ensuring data integrity.
Error handling solves the problem of unpredictable program behavior by providing structured mechanisms to capture, log, and respond to errors while maintaining control flow.
Exception Block
The exception block is a dedicated section in a PL/SQL program where you define how to handle errors that occur in the execution section.
DECLARE
v_employee_name employees.last_name%TYPE;
BEGIN
-- Execution section where errors might occur
SELECT last_name INTO v_employee_name
FROM employees WHERE employee_id = 999; -- This might fail
EXCEPTION -- Exception block starts here
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('Employee not found');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Unexpected error occurred');
END;
/
Predefined Exceptions
PL/SQL provides built-in exceptions for common database errors, such as constraint violations or data not found scenarios.
Predefined exceptions handle the most common error scenarios in Oracle Database.
DECLARE
v_salary employees.salary%TYPE;
BEGIN
SELECT salary INTO v_salary
FROM employees WHERE employee_id = 100;
-- Attempt invalid operation
v_salary := v_salary / 0;
EXCEPTION
WHEN ZERO_DIVIDE THEN -- Predefined exception
DBMS_OUTPUT.PUT_LINE('Division by zero attempted');
WHEN TOO_MANY_ROWS THEN -- Another predefined exception
DBMS_OUTPUT.PUT_LINE('Query returned multiple rows');
END;
/
User-Defined Exceptions
You can define custom exceptions to handle business logic errors specific to your application.
Use user-defined exceptions to make your code more expressive and self-documenting.
DECLARE
e_insufficient_funds EXCEPTION; -- Declare custom exception
PRAGMA EXCEPTION_INIT(e_insufficient_funds, -20001); -- Associate with error code
v_balance NUMBER := 500;
v_withdrawal NUMBER := 1000;
BEGIN
IF v_withdrawal > v_balance THEN
RAISE e_insufficient_funds; -- Raise custom exception
END IF;
EXCEPTION
WHEN e_insufficient_funds THEN
DBMS_OUTPUT.PUT_LINE('Insufficient funds for withdrawal');
END;
/
RAISE Statement
The RAISE statement explicitly triggers an exception, either predefined, user-defined, or re-raising the current exception.
DECLARE
e_invalid_age EXCEPTION;
v_age NUMBER := 15;
BEGIN
IF v_age < 18 THEN
RAISE e_invalid_age; -- Explicitly raise exception
END IF;
EXCEPTION
WHEN e_invalid_age THEN
DBMS_OUTPUT.PUT_LINE('Age must be 18 or older');
RAISE; -- Re-raise to propagate to calling environment
END;
/
RAISE_APPLICATION_ERROR
This procedure raises a user-defined error with a custom error message and error number (-20000 to -20999 range).
Always use error codes in the -20000 to -20999 range for application-defined errors.
CREATE OR REPLACE PROCEDURE process_order(p_order_id NUMBER) IS
v_status orders.status%TYPE;
BEGIN
SELECT status INTO v_status FROM orders WHERE order_id = p_order_id;
IF v_status != 'PENDING' THEN
RAISE_APPLICATION_ERROR(-20002,
'Order ' || p_order_id || ' is not in PENDING status');
END IF;
-- Process the order...
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20003, 'Order not found: ' || p_order_id);
END;
/
SQLCODE and SQLERRM
These functions provide error code and message information for the most recent exception.
DECLARE
v_employee_id NUMBER := 999;
BEGIN
DELETE FROM employees WHERE employee_id = v_employee_id;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Error code: ' || SQLCODE);
DBMS_OUTPUT.PUT_LINE('Error message: ' || SQLERRM);
-- Log detailed error information
INSERT INTO error_log (error_code, error_message, timestamp)
VALUES (SQLCODE, SQLERRM, SYSTIMESTAMP);
END;
/
Exception Propagation
Exceptions propagate through nested blocks until they find a handler or reach the calling environment.
Understanding exception propagation is key to building robust error handling strategies.
DECLARE
PROCEDURE inner_procedure IS
BEGIN
RAISE_APPLICATION_ERROR(-20004, 'Error from inner procedure');
END;
BEGIN
BEGIN -- Inner block
inner_procedure;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Inner block caught: ' || SQLERRM);
RAISE; -- Propagate to outer block
END;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Outer block caught: ' || SQLERRM);
END;
/
Logging Errors
Structured error logging captures exception details for debugging and auditing purposes.
Use autonomous transactions for error logging to ensure logs are preserved even if the main transaction rolls back.
CREATE TABLE error_log (
log_id NUMBER PRIMARY KEY,
error_code NUMBER,
error_message VARCHAR2(4000),
backtrace CLOB,
call_stack CLOB,
timestamp TIMESTAMP,
username VARCHAR2(30)
);
CREATE OR REPLACE PROCEDURE log_error IS
PRAGMA AUTONOMOUS_TRANSACTION; -- Ensures log commit even if main transaction rolls back
BEGIN
INSERT INTO error_log (
log_id, error_code, error_message, backtrace,
call_stack, timestamp, username
) VALUES (
error_seq.NEXTVAL, SQLCODE, SQLERRM,
DBMS_UTILITY.FORMAT_ERROR_BACKTRACE(),
DBMS_UTILITY.FORMAT_CALL_STACK(),
SYSTIMESTAMP, USER
);
COMMIT;
END;
/
CREATE OR REPLACE PROCEDURE risky_operation IS
BEGIN
-- Some operation that might fail
RAISE PROGRAM_ERROR;
EXCEPTION
WHEN OTHERS THEN
log_error; -- Call logging procedure
RAISE; -- Re-raise after logging
END;
/
Why is Error Handling Important?
Data Integrity (ACID Compliance)
Ensures database transactions maintain atomicity and consistency by properly handling errors that could leave data in an inconsistent state.
Maintainability (DRY Principle)
Centralized exception handling reduces code duplication and makes error management consistent across the application.
User Experience (Fail-Gracefully Pattern)
Provides meaningful error messages and alternative pathways instead of abrupt failures, enhancing application reliability.
Advanced Nuances
Exception Propagation in Nested Programs
When exceptions propagate through multiple nested procedures, the call stack and backtrace become crucial for debugging.
-- Advanced propagation example with detailed logging
CREATE OR REPLACE PROCEDURE level3 IS
BEGIN
RAISE VALUE_ERROR;
END;
CREATE OR REPLACE PROCEDURE level2 IS
BEGIN
level3;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Level2: ' || DBMS_UTILITY.FORMAT_ERROR_BACKTRACE());
RAISE;
END;
CREATE OR REPLACE PROCEDURE level1 IS
BEGIN
level2;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Level1: ' || DBMS_UTILITY.FORMAT_ERROR_STACK());
RAISE;
END;
Bulk Operation Exception Handling
For bulk operations, Oracle provides the SAVE EXCEPTIONS clause and SQL%BULK_EXCEPTIONS collection:
DECLARE
TYPE id_list IS TABLE OF NUMBER;
v_ids id_list := id_list(100, 200, 300);
e_bulk_errors EXCEPTION;
PRAGMA EXCEPTION_INIT(e_bulk_errors, -24381);
BEGIN
FORALL i IN 1..v_ids.COUNT SAVE EXCEPTIONS
UPDATE employees SET salary = salary * 1.1
WHERE employee_id = v_ids(i);
EXCEPTION
WHEN e_bulk_errors THEN
FOR j IN 1..SQL%BULK_EXCEPTIONS.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(
'Error ' || j || ': Index ' ||
SQL%BULK_EXCEPTIONS(j).ERROR_INDEX || ' - ' ||
SQLERRM(-SQL%BULK_EXCEPTIONS(j).ERROR_CODE)
);
END LOOP;
END;
/
How This Fits the Roadmap
Within the “PL/SQL Programming” section, Error Handling serves as a fundamental building block between basic syntax and advanced programming concepts.
Prerequisites:
- Basic PL/SQL block structure
- Variable declaration
- Control structures
Unlocks:
- Advanced package design and modular programming
- Robust transaction management patterns
- Performance optimization with bulk operations
- Sophisticated debugging and logging frameworks
- Enterprise-level application architecture
Error handling forms the foundation for writing production-grade PL/SQL code that is reliable, maintainable, and professional.