PL/SQL & T-SQL (Oracle and SQL Server) Module 9 – PL/SQL foundations: blocks, types, SQL and exceptions
PL/SQL exceptions and how they propagate
Handlers that catch PL/SQL errors by name, SQLCODE and SQLERRM, the path an unhandled exception takes from block to caller, and errors your code raises.
What you will learn
- Handle predefined exceptions by name and read SQLCODE and SQLERRM inside a handler
- Predict which block or caller catches an exception, or whether the client reports it
- Trace an exception raised in a declaration or inside a handler to the block that catches it
- Use RAISE_APPLICATION_ERROR and PRAGMA EXCEPTION_INIT to give callers errors they catch by name
- Find handlers that hide errors with compile-time warnings, and choose what a handler should do instead
Before you start
What you’ll learn · Before you start
What you will learn
- Handle predefined exceptions by name and read SQLCODE and SQLERRM inside a handler
- Predict which block or caller catches an exception, or whether the client reports it
- Trace an exception raised in a declaration or inside a handler to the block that catches it
- Use RAISE_APPLICATION_ERROR and PRAGMA EXCEPTION_INIT to give callers errors they catch by name
- Find handlers that hide errors with compile-time warnings, and choose what a handler should do instead
Before you start
On this page
Splitting order 3, worth 900.00, into 0 instalments stops a procedure with ORA-01476, and asking for order 7, which does not exist, stops it with ORA-01403. PL/SQL exceptions are the run-time errors behind such codes: a failing statement raises one, a handler can catch it by name, and an exception that a block does not catch leaves it for the enclosing block, then for the caller.
You will catch errors by name, read their codes and follow one exception to the client, so that you can predict how they propagate in your own code.
The EXCEPTION section catches an error by its name
The first block of handlers.sql divides the 900.00 of order 3, five bags of TEA-250 at 180.00, by 0 parts. The
division raises ZERO_DIVIDE, one of the predefined exception: An Oracle error that PL/SQL has given a name in its package STANDARD, such as NO_DATA_FOUND for ORA-01403 or ZERO_DIVIDE for ORA-01476, so that a handler can catch it by that name., the names that
PL/SQL gives common Oracle errors: NO_DATA_FOUND is ORA-01403, ZERO_DIVIDE ORA-01476, VALUE_ERROR ORA-06502 and
DUP_VAL_ON_INDEX ORA-00001. The block stops at the failing statement and turns to its EXCEPTION section, where
the first exception handler: A WHEN clause in the EXCEPTION section of a PL/SQL block that names one or more exceptions and the statements to run when one of them is raised, instead of letting the error end the block. More in “PL/SQL blocks, variables and data types” that names the exception runs; WHEN a OR b names two.
WHEN OTHERS catches whatever no earlier WHEN names, and it must come last, or the block does not compile
(PLS-00370). Inside a handler, SQLCODE returns the error’s code, −1476 here, and SQLERRM its message. Step
through the block:
The drawing shows the whole scene; its 7 steps are listed below.
Every step as text
- The block reads order 3. SELECT … INTO v_total reads order 3, five bags of TEA-250 at 180.00, so v_total holds 900.00 and the block prints Total: 900.00.
- The division raises ZERO_DIVIDE. v_total / v_parts divides by 0, and PL/SQL raises the predefined exception ZERO_DIVIDE, ORA-01476. The executable part stops at that statement.
- The rest of the block is skipped. The PUT_LINE after the division never runs. Control leaves the statements after BEGIN and moves to the EXCEPTION section of the same block.
- Handlers are read from the top. The first handler names NO_DATA_FOUND, which is a different exception, so it is passed over and the next WHEN is read.
- The first match runs. WHEN ZERO_DIVIDE names the exception, so its statements run: SQLCODE returns -1476 and SQLERRM returns ORA-01476: divisor is equal to zero.
- OTHERS is not needed. Only one handler runs for an exception. WHEN OTHERS would catch any exception that no earlier WHEN names, which is why Oracle accepts it only as the last handler.
- The block ends normally. After the handler, control goes to the end of the block, never back to the division. The block completes, so the client that sent it sees no error at all.
After a handler, control goes to the end of its block, never back to the failing statement, so the script’s loop gives each division a block of its own:
-- Engine: Oracle AI Database 26ai Free, Release 23.26.3.0.0; user LAB in FREEPDB1. Client: SQL*Plus or SQLcl.
-- Handlers in one block. Order 3 (900.00) split into 0 parts raises ZERO_DIVIDE: the rest of the block is skipped and
-- the ZERO_DIVIDE handler prints SQLCODE and SQLERRM. The second block puts the division in an inner block with its
-- own handler, so the loop goes on after the error. The third block shows the order Oracle demands. No data changes.
SET SERVEROUTPUT ON
DECLARE
v_total NUMBER(10, 2);
v_parts PLS_INTEGER := 0;
BEGIN
SELECT o.qty * s.unit_price INTO v_total
FROM orders o JOIN stock s ON s.sku = o.sku
WHERE o.order_id = 3;
DBMS_OUTPUT.PUT_LINE('Total: ' || TO_CHAR(v_total, 'FM999990.00'));
DBMS_OUTPUT.PUT_LINE('Each: ' || TO_CHAR(v_total / v_parts, 'FM999990.00')); -- raises ZERO_DIVIDE
DBMS_OUTPUT.PUT_LINE('never printed');
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('No such order');
WHEN ZERO_DIVIDE THEN
DBMS_OUTPUT.PUT_LINE('Handled: SQLCODE ' || SQLCODE || ', ' || SQLERRM);
WHEN OTHERS THEN -- every other exception; it must come last
DBMS_OUTPUT.PUT_LINE('Unexpected: ' || SQLERRM);
RAISE; -- report it, then raise it again for the client
END;
/
-- Expected (Predefined Exceptions page: ZERO_DIVIDE is ORA-01476 with code -1476; text from the ORA-01476 page):
-- Total: 900.00
-- Handled: SQLCODE -1476, ORA-01476: divisor is equal to zero
DECLARE
v_total NUMBER(10, 2) := 900;
BEGIN
FOR v_parts IN REVERSE 0 .. 3 LOOP
BEGIN -- inner block: its handler lets the loop go on
DBMS_OUTPUT.PUT_LINE('split in ' || v_parts || ': ' || TO_CHAR(v_total / v_parts, 'FM999990.00'));
EXCEPTION
WHEN ZERO_DIVIDE THEN
DBMS_OUTPUT.PUT_LINE('split in ' || v_parts || ': skipped');
END;
END LOOP;
DBMS_OUTPUT.PUT_LINE('loop finished');
END;
/
-- Expected (Continuing Execution After Handling Exceptions page: a handler never returns to its own block, so the
-- division gets a block of its own):
-- split in 3: 300.00
-- split in 2: 450.00
-- split in 1: 900.00
-- split in 0: skipped
-- loop finished
BEGIN
DBMS_OUTPUT.PUT_LINE(900 / 0);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('caught by OTHERS');
WHEN ZERO_DIVIDE THEN
DBMS_OUTPUT.PUT_LINE('caught by ZERO_DIVIDE');
END;
/
-- Expected (error help PLS-00370): the block does not compile, so nothing runs:
-- PLS-00370: OTHERS handler must be last among the exception handlers of a block """handlers.sql in the lab: SQL*Plus runs it as LAB on a freshly loaded practice schema."""
from oracle_session import Lab
with Lab() as lab:
lab.sqlplus("handlers/handlers.sql", errors=1) Output
$ sqlplus -s lab@//localhost/FREEPDB1 @handlers/handlers.sql Total: 900.00 Handled: SQLCODE -1476, ORA-01476: divisor is equal to zero PL/SQL procedure successfully completed. split in 3: 300.00 split in 2: 450.00 split in 1: 900.00 split in 0: skipped loop finished PL/SQL procedure successfully completed. WHEN OTHERS THEN * ERROR at line 4: ORA-06550: line 4, column 3: PLS-00370: OTHERS handler must be last among the exception handlers of a block ORA-06550: line 0, column 0: PL/SQL: Compilation unit analysis terminated Help: https://docs.oracle.com/error-help/db/ora-06550/
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 handlers_session.py
Expecting the block to resume after its handler
Wrong: one block holding the division and the lines that must run after it, trusting the handler to return. Right:
an inner BEGIN … EXCEPTION … END; block around the risky statement, whose handler ends only that block.
Checkpoint
Check: Handlers in the EXCEPTION section
An unhandled exception climbs to the enclosing block, then to the caller
In propagation.sql, print_instalment does its lookup and division in an inner block that handles only VALUE_ERROR.
The procedure body handles ZERO_DIVIDE, and the caller, an anonymous block: A PL/SQL block without a name, DECLARE ... BEGIN ... EXCEPTION ... END, sent to the database and run once rather than stored. More in “PL/SQL and T-SQL: what they are and where they run”,
handles NO_DATA_FOUND. An exception that no handler of its block names
exception propagation: What happens to an exception that no handler of its block names: it leaves the block and is raised again in the enclosing block, then in the caller, until a handler catches it or the client reports it.: it leaves the block and is raised again in the enclosing block,
then in the caller, at the call. If no level handles it, SQL*Plus or SQLcl prints the error with ORA-06512 lines
naming each place it passed. PL/SQL exceptions climb this way from wherever they start, so knowing how they propagate
tells you which handler runs:
The drawing shows the whole scene; its 9 steps are listed below.
Every step as text
- Zero parts raise ZERO_DIVIDE. print_instalment(3, 0) finds order 3, worth 900.00, and divides it by 0 inside the inner block. PL/SQL raises ZERO_DIVIDE, ORA-01476, and the inner block stops.
- The inner handler does not match. The inner block has one handler, WHEN VALUE_ERROR. It names a different exception, so the exception leaves the inner block and is raised again in the procedure body.
- The procedure catches it. WHEN ZERO_DIVIDE in print_instalment matches and prints SQLCODE -1476. The procedure's own 'done' line is skipped, the call returns normally, and the caller goes on to its next call.
- Order 7 raises NO_DATA_FOUND. print_instalment(7, 2) looks for order 7, which does not exist. The SELECT INTO in the inner block raises NO_DATA_FOUND, ORA-01403, before any division.
- Up to the procedure. WHEN VALUE_ERROR does not name NO_DATA_FOUND, so the exception is raised again in the procedure body, which skips the rest of its statements.
- Up to the caller. The procedure's only handler names ZERO_DIVIDE, so NO_DATA_FOUND leaves print_instalment as well and is raised again in the caller block, at the call itself.
- The caller catches it. WHEN NO_DATA_FOUND in the caller matches and prints SQLCODE 100. The caller's last line is skipped, and its block then ends normally, so the client sees no error.
- A caller with no handler. The second caller block of the script makes the same call with no handler. Nothing names NO_DATA_FOUND at any level, so PL/SQL returns the error to the client.
- The client reports the path. SQL*Plus or SQLcl prints ORA-01403 with ORA-06512 lines naming each place the error passed, the line of the SELECT INTO in PRINT_INSTALMENT first, then line 2 of the caller block.
The script holds the procedure and both caller blocks, with the lines each one prints:
-- Engine: Oracle AI Database 26ai Free, Release 23.26.3.0.0; user LAB in FREEPDB1. Client: SQL*Plus or SQLcl.
-- One exception climbing from an inner block to the procedure around it, then to the caller and the client.
-- print_instalment splits an order's total into equal parts: order 3 is 5 x TEA-250 at 180.00 = 900.00, and order 7
-- does not exist. examples/model/propagation_model.py prints the same path for every call. The script changes no
-- data and drops its procedure at the end.
SET SERVEROUTPUT ON
CREATE OR REPLACE PROCEDURE print_instalment (p_order_id IN NUMBER, p_parts IN NUMBER)
AUTHID DEFINER
IS
BEGIN
DECLARE -- the inner block: the lookup and the division
v_total NUMBER(10, 2);
BEGIN
SELECT o.qty * s.unit_price INTO v_total
FROM orders o JOIN stock s ON s.sku = o.sku
WHERE o.order_id = p_order_id; -- order 7: NO_DATA_FOUND
DBMS_OUTPUT.PUT_LINE('Order ' || p_order_id || ': ' || p_parts || ' parts of '
|| TO_CHAR(v_total / p_parts, 'FM999990.00')); -- 0 parts: ZERO_DIVIDE
EXCEPTION
WHEN VALUE_ERROR THEN
DBMS_OUTPUT.PUT_LINE('inner block: VALUE_ERROR');
END;
DBMS_OUTPUT.PUT_LINE('Order ' || p_order_id || ': done');
EXCEPTION
WHEN ZERO_DIVIDE THEN
DBMS_OUTPUT.PUT_LINE('print_instalment: ZERO_DIVIDE, SQLCODE ' || SQLCODE);
END print_instalment;
/
-- The first caller handles NO_DATA_FOUND.
BEGIN
print_instalment(3, 3);
print_instalment(3, 0);
print_instalment(7, 2);
DBMS_OUTPUT.PUT_LINE('caller: all three calls done'); -- skipped: the third call fails
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('caller: NO_DATA_FOUND, SQLCODE ' || SQLCODE);
END;
/
-- Expected (Overview of Exception Handling and Exception Propagation pages; codes from Predefined Exceptions):
-- Order 3: 3 parts of 300.00
-- Order 3: done
-- print_instalment: ZERO_DIVIDE, SQLCODE -1476
-- caller: NO_DATA_FOUND, SQLCODE 100
-- The second caller handles nothing.
BEGIN
print_instalment(7, 2);
END;
/
-- Expected: no level handles NO_DATA_FOUND, so the client prints the error stack, as the Exception Propagation
-- page shows for an unhandled exception (line 8 is the SELECT INTO of the stored procedure):
-- ORA-01403: no data found
-- ORA-06512: at "LAB.PRINT_INSTALMENT", line 8
-- ORA-06512: at line 2
DROP PROCEDURE print_instalment; """propagation.sql in the lab: SQL*Plus runs it as LAB on a freshly loaded practice schema."""
from oracle_session import Lab
with Lab() as lab:
lab.sqlplus("propagation/propagation.sql", errors=1) Output
$ sqlplus -s lab@//localhost/FREEPDB1 @propagation/propagation.sql Procedure created. Order 3: 3 parts of 300.00 Order 3: done print_instalment: ZERO_DIVIDE, SQLCODE -1476 caller: NO_DATA_FOUND, SQLCODE 100 PL/SQL procedure successfully completed. BEGIN * ERROR at line 1: ORA-01403: no data found ORA-06512: at "LAB.PRINT_INSTALMENT", line 8 ORA-06512: at line 2 Help: https://docs.oracle.com/error-help/db/ora-01403/ Procedure dropped.
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 propagation_session.py
The model prints the level that catches each call’s exception. Press Edit, add "NO_DATA_FOUND" to the inner block’s
handlers and run it: order 7 is then caught inside the procedure, and both callers finish every call.
"""Which handler catches an exception: Oracle's propagation rules, modelled.
The model replays the two caller blocks of propagation.sql on the practice
orders. print_instalment looks up an order and divides its total into parts
inside an inner block. The levels an exception can reach, innermost first:
the inner block, the body of print_instalment, the caller, then the client.
The rules (PL/SQL Language Reference 26ai, Overview of Exception Handling
and Exception Propagation):
1. A failing statement stops its block. The block's handlers are read from
the top; the first WHEN that names the exception, or OTHERS, runs, and
control then goes to the end of that block, never back.
2. With no such handler, the exception leaves the block and is raised
again in the enclosing block, then in the caller, at the call.
3. If no level handles it, the client (SQL*Plus or SQLcl) reports it.
Edit HANDLERS or the calls and run it again: give the inner block a
NO_DATA_FOUND handler, or take the caller's handler away.
"""
from decimal import Decimal
ORDER_TOTAL = {1: Decimal("360.00"), 2: Decimal("420.00"), 3: Decimal("900.00")}
ERRORS = { # name: (Oracle error, SQLCODE), from the Predefined Exceptions page
"NO_DATA_FOUND": ("ORA-01403", 100),
"ZERO_DIVIDE": ("ORA-01476", -1476),
"VALUE_ERROR": ("ORA-06502", -6502),
}
HANDLERS = {
"inner block": ["VALUE_ERROR"],
"print_instalment": ["ZERO_DIVIDE"],
"caller": ["NO_DATA_FOUND"],
}
FIRST_CALLER = [(3, 3), (3, 0), (7, 2)] # the first caller block
SECOND_CALLER = [(7, 2)] # the second one, no handler
def catcher(level, handlers, name):
"""The handler of one level that catches the exception, or None."""
for h in handlers.get(level, []):
if h in (name, "OTHERS"):
return h
return None
def instalment(order_id, parts):
"""The inner block: raises an exception name, or returns the text."""
if order_id not in ORDER_TOTAL:
raise LookupError("NO_DATA_FOUND")
if parts == 0:
raise LookupError("ZERO_DIVIDE")
each = (ORDER_TOTAL[order_id] / parts).quantize(Decimal("0.01"))
return f"Order {order_id}: {parts} parts of {each}"
def run_caller(title, calls, handlers):
"""Runs one caller block; returns False if the client saw an error."""
print(title)
for order_id, parts in calls:
print(f" print_instalment({order_id}, {parts})")
try:
print(" prints:", instalment(order_id, parts))
print(f" prints: Order {order_id}: done")
continue
except LookupError as exc:
name = str(exc)
code, sqlcode = ERRORS[name]
print(f" {name} ({code}) raised in the inner block")
for level in ("inner block", "print_instalment", "caller"):
hit = catcher(level, handlers, name)
if hit is None:
print(f" {level}: no handler names it")
continue
print(f" {level}: WHEN {hit} runs, SQLCODE {sqlcode}")
if level == "inner block":
print(f" prints: Order {order_id}: done")
elif level == "print_instalment":
print(f" skipped: Order {order_id}: done")
print(" the call returns normally")
else:
print(" skipped: the rest of the caller")
return True
break
else:
print(f" client: reports {code} with ORA-06512 lines")
return False
print(" the caller ran every call")
return True
run_caller("First caller block:", FIRST_CALLER, HANDLERS)
no_caller_handler = dict(HANDLERS, caller=[])
reached_end = run_caller("Second caller block (no handler):", SECOND_CALLER,
no_caller_handler) Output
First caller block:
print_instalment(3, 3)
prints: Order 3: 3 parts of 300.00
prints: Order 3: done
print_instalment(3, 0)
ZERO_DIVIDE (ORA-01476) raised in the inner block
inner block: no handler names it
print_instalment: WHEN ZERO_DIVIDE runs, SQLCODE -1476
skipped: Order 3: done
the call returns normally
print_instalment(7, 2)
NO_DATA_FOUND (ORA-01403) raised in the inner block
inner block: no handler names it
print_instalment: no handler names it
caller: WHEN NO_DATA_FOUND runs, SQLCODE 100
skipped: the rest of the caller
Second caller block (no handler):
print_instalment(7, 2)
NO_DATA_FOUND (ORA-01403) raised in the inner block
inner block: no handler names it
print_instalment: no handler names it
caller: no handler names it
client: reports ORA-01403 with ORA-06512 lines
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 propagation_model.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
In T-SQL an error travels outwards in a similar way, to the caller’s CATCH block, as
the TRY…CATCH lesson shows. Which changes an error leaves behind is
covered in SQL inside PL/SQL.
Key idea
An exception its block does not handle moves out one level at a time, to the enclosing block and then the caller, until a handler names it; with none, the client reports it.
Checkpoint
Check: How an unhandled exception travels
Code can raise errors of its own, and the compiler can flag handlers
Programs also report broken rules of their own. The table names four tools for that, where else an exception can start and the setting for compiler warnings:
| Name or place | What it does |
|---|---|
e_limit EXCEPTION; |
A user-defined exception: An exception name that a PL/SQL block, subprogram or package declares itself, such as e_limit EXCEPTION, for a rule of the program; only a RAISE in the program raises it. is a name you declare, raised only by your own RAISE e_limit;. |
| PRAGMA EXCEPTION_INIT: A PL/SQL declaration that ties a user-defined exception name to an error number, so that a handler written for the name catches the error with that number. | It ties such a name to an error number, so a handler can catch that error by name. |
RAISE_APPLICATION_ERROR |
Called from a stored procedure: A named unit of procedural code kept in the database and run with one call, with parameters and the permissions of the database, instead of being sent as text each time. More in “PL/SQL and T-SQL: what they are and where they run” or function, it raises an application error: An error that a stored procedure or function raises itself with RAISE_APPLICATION_ERROR, with a number from -20000 to -20999 and a message of its own; callers receive it like an Oracle error.: a number from −20000 to −20999 with your own message. |
RAISE; |
Inside a handler, it raises the exception being handled again. |
| A declaration or a handler | An exception can also start in a declaration’s initial value or inside a handler, which changes who can catch it. |
PLSQL_WARNINGS |
It switches on compile-time warning: A message from the PL/SQL compiler about a stored unit that compiles but may behave wrongly, run slowly or be hard to maintain, such as a handler that hides errors; the PLSQL_WARNINGS parameter switches them on. about stored code that may behave wrongly, such as a handler that hides errors. |
Checkpoint
Check: Errors your own code raises
Where an exception starts decides which handler catches it, reproduced Pro: opens with a Pro, Premium or Ultimate pass
Where an exception is raised changes which handlers get a chance to catch it. Two such places, a declaration and a
handler, run below on the practice data, with RAISE; and the codes that SQLCODE reports along the way.
In this part:
- Errors raised in a declaration, traced to the handler that catches them
- Errors raised inside a handler, and RAISE without a name
1 diagram · 5 runnable examples (Python, SQL) · about 8 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
Error numbers callers can catch by name, and handlers the compiler flags Pro: opens with a Pro, Premium or Ultimate pass
A procedure that refuses work should tell its caller why, with an error number the caller can catch by name. The compiler, for its part, can point at the handlers that hide errors. Both run below on the practice accounts.
In this part:
- Application errors that a caller catches by name
- Numbers outside the range, and what happens to the error stack
- Compile-time warnings that find a handler hiding errors
- Choosing what a handler does with the error
7 runnable examples (Python, SQL) · about 13 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
Practise: predict which handler runs, or whether the client sees the error
Most questions give a block and an error. Decide where the exception goes before you choose:
Practice that adapts to you
Practise this lesson
9 questions about 7 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Warm-up (fresher): which exceptions come with a name, and which do you declare yourself?
Predefined exceptions come with a name: PL/SQL declares them in package STANDARD for common Oracle errors, such as
NO_DATA_FOUND for ORA-01403, and the runtime raises them by itself. A user-defined exception is a name that your
own block, subprogram or package declares with EXCEPTION for a rule of yours, and only a RAISE in your code raises
it. A WHEN that names either kind catches it.
Three longer questions follow: a nightly load whose failures nobody saw, a default value that stopped a procedure, and the error numbers of a package that other teams call:
-
Mid to senior
A nightly job updates stock prices from a supplier file, row by row, and has reported success for weeks, yet 300 of 12,000 items still carry old prices. Each row's UPDATE sits in a block ending in WHEN OTHERS THEN NULL. Explain what happened, how you would fix the job, and how you would find similar handlers.
-
Mid to senior
A procedure declares v_limit NUMBER(5,2) := limit_for(p_card); and handles VALUE_ERROR by logging the card and using a limit of 500.00. Since limits of 1500.00 were added, calls for those cards fail with ORA-06502 and the log stays empty. Why does the handler never run, and how would you restructure the procedure?
-
Mid to staff
Six teams call your payments package. It raises errors with RAISE_APPLICATION_ERROR, but its numbers grew ad hoc: -20001 means three different things, and one procedure uses -19050. Design the error contract callers can rely on: the numbers, how callers catch them by name, and how the original Oracle error stays visible.
Flashcards · 8 cards
Recall it, then check
Say the answer to yourself before you look: recalling it is what makes it stick.
-
Which handler runs when a statement fails, and where does control go afterwards?
The block stops at the failing statement and the first handler that names the exception runs; WHEN a OR b names two, and WHEN OTHERS catches what no earlier WHEN names and must come last, or the block does not compile (PLS-00370). Control then goes to the end of that block, never back to the failing statement.
-
Which Oracle errors do the commonest predefined exceptions name?
NO_DATA_FOUND is ORA-01403, ZERO_DIVIDE is ORA-01476, VALUE_ERROR is ORA-06502 and DUP_VAL_ON_INDEX is ORA-00001.
-
What do SQLCODE and SQLERRM give you inside a handler?
SQLCODE returns the error's code, -1476 for a division by zero, and SQLERRM its message.
-
How do you keep the statements after a risky one running?
Put the risky statement in an inner BEGIN ... EXCEPTION ... END; block, whose handler ends only that block. A handler never returns to the failing statement, so one block holding both the risky statement and the work after it loses the rest.
-
Where does an exception go when its own block has no handler for it?
It leaves the block and is raised again in the enclosing block, then in the caller, at the call, one level at a time until a handler names it. With none, SQL*Plus or SQLcl prints the error with ORA-06512 lines naming each place it passed.
-
What is a user-defined exception, and how does a handler catch an Oracle error by name?
e_limit EXCEPTION; declares a name raised only by your own RAISE e_limit;. PRAGMA EXCEPTION_INIT ties such a name to an error number, so a handler can catch that numbered error by name.
-
What does RAISE_APPLICATION_ERROR raise, and what does a bare RAISE do?
Called from a stored procedure or function, RAISE_APPLICATION_ERROR raises an application error: a number from -20000 to -20999 with your own message. RAISE; inside a handler raises the exception being handled again.
-
What does PLSQL_WARNINGS do for exception handling?
It switches on compile-time warnings about stored code that may behave wrongly, such as a handler that hides errors. An exception can also start in a declaration's initial value or inside a handler, which changes who can catch it.
Key takeaways
- A failing statement stops its block, the first handler that names the exception runs, with
OTHERSlast, and control then goes to the end of that block. - Predefined exceptions name Oracle’s common errors, and inside a handler
SQLCODEandSQLERRMgive the code and the message. - An exception that no handler names leaves its block for the enclosing block, then the caller, and reaches the client when no level handles it.
- PL/SQL exceptions can also come from your own code, raised by name or with an error number, and the compiler can warn about handlers that hide them.
Terms in this lesson
- application error
- An error that a stored procedure or function raises itself with RAISE_APPLICATION_ERROR, with a number from -20000 to -20999 and a message of its own; callers receive it like an Oracle error.
- compile-time warning
- A message from the PL/SQL compiler about a stored unit that compiles but may behave wrongly, run slowly or be hard to maintain, such as a handler that hides errors; the PLSQL_WARNINGS parameter switches them on.
- exception propagation
- What happens to an exception that no handler of its block names: it leaves the block and is raised again in the enclosing block, then in the caller, until a handler catches it or the client reports it.
- PRAGMA EXCEPTION_INIT
- A PL/SQL declaration that ties a user-defined exception name to an error number, so that a handler written for the name catches the error with that number.
- predefined exception
- An Oracle error that PL/SQL has given a name in its package STANDARD, such as NO_DATA_FOUND for ORA-01403 or ZERO_DIVIDE for ORA-01476, so that a handler can catch it by that name.
- user-defined exception
- An exception name that a PL/SQL block, subprogram or package declares itself, such as e_limit EXCEPTION, for a rule of the program; only a RAISE in the program raises it.
References
- PL/SQL Language Reference 26ai: PL/SQL Error Handling (Oracle)
- PL/SQL Language Reference 26ai: Overview of Exception Handling (Oracle)
- PL/SQL Language Reference 26ai: Predefined Exceptions (Oracle)
- PL/SQL Language Reference 26ai: Exception Handler (Oracle)
- PL/SQL Language Reference 26ai: Continuing Execution After Handling Exceptions (Oracle)
- PL/SQL Language Reference 26ai: Exception Propagation (Oracle)
- PL/SQL Language Reference 26ai: User-Defined Exceptions (Oracle)
- PL/SQL Language Reference 26ai: EXCEPTION_INIT Pragma (Oracle)
- PL/SQL Language Reference 26ai: Raising Exceptions Explicitly (Oracle)
- PL/SQL Language Reference 26ai: SQLCODE Function (Oracle)
- PL/SQL Language Reference 26ai: SQLERRM Function (Oracle)
- PL/SQL User's Guide and Reference 10g Release 2: SQLERRM Function (Oracle)
- PL/SQL Language Reference 26ai: Retrieving Error Code and Error Message (Oracle)
- PL/SQL Language Reference 26ai: Compile-Time Warnings (Oracle)
- Database Reference 26ai: PLSQL_WARNINGS (Oracle)
- Database Error Messages: ORA-01476 (Oracle)
- Database Error Messages: ORA-06502 (Oracle)
- Database Error Messages: ORA-06512 (Oracle)
- Database Error Messages: ORA-06510 (Oracle)
- Database Error Messages: ORA-21000 (Oracle)
- Database Error Messages: PLS-00370 (Oracle)
- Database Error Messages: PLW-06009 (Oracle)
- Database Error Messages: PLW-05018 (Oracle)
- Database Error Messages: ORA-02290 (Oracle)
Report a problem with this lesson
Kept only in this browser. Your Learn progress