Běhové chyby lze rozdělit na:
Některé z běžných vnitřních vyjímek mají i svá zástupná jména (např. výše zmíněným
odpovídají ZERO_DIVIDE a STORAGE_ERROR), případně je
lze dodatečně pojmenovat. Uživatelské vyjímky jsou pojmenovány vždy.
V případě, že v průběhu výpočtu programu nastane chyba, je vyvolána příslušná
vyjímka (automaticky při vnitřní vyjímce, či explicitně pomocí přikazu
RAISE). K ošetření vyvolané vyjímky se používá exception
handler, který zastaví právě vykonávaný blok a po svém skončení výpočet
pokračuje.
Příklad:
DECLARE
pe_ratio NUMBER(3,1);
BEGIN
SELECT price / earnings INTO pe_ratio FROM stocks
WHERE symbol = 'XYZ'; -- might cause division-by-zero error
INSERT INTO stats (symbol, ratio) VALUES ('XYZ', pe_ratio);
COMMIT;
EXCEPTION -- Exception handler
WHEN ZERO_DIVIDE THEN -- handles 'division by zero' error
INSERT INTO stats (symbol, ratio) VALUES ('XYZ', NULL);
COMMIT;
...
WHEN OTHERS THEN -- handles all other errors
ROLLBACK;
END; -- exception handlers and block end here
SELECT INTO nevrátí žádný řádek,
je vyvolána vyjímka s názvem NO_DATA_FOUND).
STANDARD.
WHEN <JMENO_VYJIMKY> THEN ...).
OTHERS (WHEN OTHERS THEN ...).
Dále je možné využít funkce:
SQLCODE - sloužící k navrácení čísla chyby
SQLERRM - sloužící k zobrazování chybové zprávy
EXCEPTION_INIT
(viz "Použití EXCEPTION_INIT").
Tabulku často používaných pojmenovaných vyjímek najdete v příloze.
RAISE.
EXCEPTION. Narozdíl od proměnné chápeme vyjímku jako chybový stav
a nikoliv jako datovou položku. Pole působnosti (rozsah platnosti) vyjímek i
proměnných je totožné.
DECLARE
past_due EXCEPTION;
Následující příklad ilustruje rozsah platnosti:
DECLARE
past_due EXCEPTION;
acct_num NUMBER;
BEGIN
...
DECLARE ---------- sub-block begins
past_due EXCEPTION; -- this declaration prevails
acct_num NUMBER;
BEGIN
...
IF ... THEN
RAISE past_due; -- this is not handled
END IF;
...
END; ------------- sub-block ends
EXCEPTION
WHEN past_due THEN -- does not handle RAISEd exception
...
END;
Poznámka:
Přilehlý blok neošetří vyvolanou vyjímku, protože deklarace vyjímky
past_due v podbloku převáží. Ačkoli tyto dvě vyjímky
sdílejí stejné jméno, jedná se o dvě různé vyjímky, stejně jako dvě proměnné
s názvem acct_num jsou dvě rozdílné proměnné.
Proto se tedy příkaz RAISE a klauzule WHEN odkazují
na rozdílné vyjímky. Aby přilehlý blok mohl ošetřit tuto vyjímku, musela by být
odstraněna její deklarace v podbloku, nebo přidán handler OTHERS.
OTHERS
EXCEPTION_INIT
EXCEPTION_INIT spojuje jméno vyjímky s příslušným číslem chyby v
Oraclu. Lze se pak odkazovat jménem na libovolnou vnitřní vyjímku a napsat pro
ni specifický handler.
Syntaxe:
PRAGMA EXCEPTION_INIT(exception_name, Oracle_error_number);
Kde exception_name je předem deklarovaná vyjímka.
raise_application_error (z balíčku
DBMS_STANDARD) lze oznámit vzniklou (neošetřenou) chybu aplikaci.
Syntax:
raise_application_error(error_number, message[, {TRUE | FALSE}]);
Kde error_number je záporné celé číslo
(integer) v rozsahu -20000 .. -20999 a message je řetězec
maximální délky 2048 bytů. Jestliže je třetí nepovinný parameter
TRUE, chyba je uložena na zásobník, pokud je FALSE
(default), chyba nahradí všechny dosud uložené chyby. Volání procedury ukončí
podprogram a vrátí chybu a zprávu aplikaci.
SQLCODE - číslo chyby (pro vnitřní chyby až na vyjímky číslo
záporné, pro uživatelské vrací 1)
SQLERRM - chybová zpráva (pro uživatelsky definované chyby
vrací text "User-Defined Exception")
Maximální délka chybové zprávy je 512 znaků včetně kódu chyby, vnořených zpráv
a vložených informací jako jsou jména tabulek a řádků.
Pokud nebyla vyvolána žádná vyjímka vrací SQLCODE nulu a
SQLERRM zprávu "ORA-0000: normal, successful completion".
Funkci SQLERRM lze také předat (záporné) číslo chyby. V takovém
případě vrací chybovou zprávu příslušící tomuto číslu.
STANDARD, takže je uživatel (programátor) nemusí deklarovat sám.
Předeklarování předdefinovaných vyjímek je náchylné k chybám, protože lokální
deklarace převáží deklaraci globální.
EXCEPTION_INIT) vyjímky jsou
vyvolávány implicitně - runtime systémem. Ostatní uživatelsky definované vyjímky
musí být vyvolány explicitně pomocí příkazu RAISE.
RAISE lze umístit kdekoli v rozsahu platnosti vyjímky.
Následující příklad ukazuje jak vyvolání uživatelské
(out_of_stock) tak vyjímky předdefinované
(INVALID_NUMBER):
DECLARE
out_of_stock EXCEPTION;
number_on_hand NUMBER(4);
acct_type INTEGER;
...
BEGIN
...
IF number_on_hand < 1 THEN
RAISE out_of_stock;
END IF;
...
IF acct_type NOT IN (1, 2, 3) THEN
RAISE INVALID_NUMBER; -- raise predefined exception
END IF;
...
EXCEPTION
WHEN INVALID_NUMBER THEN
ROLLBACK;
WHEN out_of_stock THEN
... -- handle the error
END;
Základní pravidla pro šíření:



Vyjímka se šíří dokonce i za svůj rozsah platnosti, tj. i za blok, v němž byla
nadeklarována.
Mimo svůj rozsah platnosti však smí být zachycena pouze pomocí handleru
OTHERS.
RAISE do
handleru. V případě, že hadler ošetřuje pouze jedinou vyjímku, je možné vynechat
jméno vyjímky.
WHEN, která specifikuje vyjímku,
pro níž je určen (případně více vyjímek oddělených pomocí OR),
následovanou sérií příkazů, jež mají být v případě vyjímky vykonány. Jejich
provedení ukončí blok či podprogram a výpočet pokračuje za celým blokem.OTHERS, který je (pokud je použit)
posledním handlerem v bloku a zachytává libovolnou vyjímku.
Poznámka:
Podle platnosti běžných pravidel rozsahu PL/SQL proměnných, lze i v handlerech
používat jak lokální tak globální proměnné. Avšak pokud je vyjímka vyvolána
uvnitř kurzorového FOR cyklu, je nejdřív kurzor uzavřen.
Také nelze provádět skoky do a z handleru (příkazem GOTO). Nicméně
příkaz GOTO lze použít ke skoku z handleru do přilehlého bloku.
Příklad (do limit nelze uložit číslo větší než 999):
DECLARE
limit CONSTANT NUMBER(3) := 5000; -- raises an exception
BEGIN
...
EXCEPTION
WHEN OTHERS THEN ... -- cannot catch the exception
OTHERS
do nejvyšší úrovně každého PL/SQL bloku a podprogramu a provedením případných
opravných operací.
Příklad:
DECLARE
pe_ratio NUMBER(3,1);
BEGIN
DELETE FROM stats WHERE symbol = 'XYZ';
BEGIN ---------- sub-block begins
SELECT price / NVL(earnings, 0) INTO pe_ratio FROM stocks
WHERE symbol = 'XYZ';
EXCEPTION
WHEN ZERO_DIVIDE THEN
pe_ratio := 0;
END; ---------- sub-block ends
INSERT INTO stats (symbol, ratio) VALUES ('XYZ', pe_ratio);
EXCEPTION
...
savepoint a
rollback.
Příklad:
DECLARE
name CHAR(20);
ans1 CHAR(3);
ans2 CHAR(3);
ans3 CHAR(3);
suffix NUMBER := 1;
BEGIN
...
LOOP -- could be FOR i IN 1..10 LOOP to allow ten tries
BEGIN -- sub-block begins
SAVEPOINT start_transaction; -- mark a savepoint
/* Remove rows from a table of survey results. */
DELETE FROM results WHERE answer1 = 'NO';
/* Add a survey respondent's name and answers. */
INSERT INTO results VALUES (name, ans1, ans2, ans3);
-- raises DUP_VAL_ON_INDEX if two respondents
-- have the same name (because there is a unique
-- index on the name column)
COMMIT;
EXIT;
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
ROLLBACK TO start_transaction; -- undo changes
suffix := suffix + 1; -- try to fix
name := name || TO_CHAR(suffix); -- problem
...
END; -- sub-block ends
END LOOP;
END;
Příklad:
DECLARE
stmt INTEGER := 1; -- designates 1st SELECT statement
BEGIN
SELECT ...
stmt := 2; -- designates 2nd SELECT statement
SELECT ...
stmt := 3; -- designates 3rd SELECT statement
SELECT ...
...
EXCEPTION
WHEN NO_DATA_FOUND THEN
INSERT INTO errors VALUES ('Error in statement ' || stmt);
...
END;
SQLCODE:
| Vyjímka | Oracle chyba | Hodnota SQLCODE |
|---|---|---|
ACCESS_INTO_NULL |
ORA-06530 | -6530 |
COLLECTION_IS_NULL |
ORA-06531 | -6531 |
CURSOR_ALREADY_OPEN |
ORA-06511 | -6511 |
DUP_VAL_ON_INDEX |
ORA-00001 | -1 |
INVALID_CURSOR |
ORA-01001 | -1001 |
INVALID_NUMBER |
ORA-01722 | -1722 |
LOGIN_DENIED |
ORA-01017 | -1017 |
NO_DATA_FOUND |
ORA-01403 | +100 |
NOT_LOGGED_ON |
ORA-01012 | -1012 |
PROGRAM_ERROR |
ORA-06501 | -6501 |
ROWTYPE_MISMATCH |
ORA-06504 | -6504 |
STORAGE_ERROR |
ORA-06500 | -6500 |
SUBSCRIPT_BEYOND_COUNT |
ORA-06533 | -6533 |
SUBSCRIPT_OUTSIDE_LIMIT |
ORA-06532 | -6532 |
TIMEOUT_ON_RESOURCE |
ORA-00051 | -51 |
TOO_MANY_ROWS |
ORA-01422 | -1422 |
VALUE_ERROR |
ORA-06502 | -6502 |
ZERO_DIVIDE |
ORA-01476 | -1476 |
Stručný popis výše uvedených vyjímek:
| Vyjímka | Popis |
|---|---|
ACCESS_INTO_NULL |
Při přiřazování hodnoty do atributu neinicializovaného (null) objektu. |
COLLECTION_IS_NULL |
Při používání jiné sběrné metody (collection method) než EXISTS
na neinicializovanou (atomicky null) vnořenou tabulku nebo pole (varray).Při přiřazování hodnoty do položek neinicializované vnořené tabulky nebo pole |
CURSOR_ALREADY_OPEN |
Při otevření již jednou otevřeného kurzoru (cursor). |
DUP_VAL_ON_INDEX |
Při ukládání totožné (již existující) hodnoty do databázového sloupce, který je označen jako unique. |
INVALID_CURSOR |
Při provádění nepovolené operace s kurzorem (cursor) jakou je například zavření neotevřeného kurzoru. |
INVALID_NUMBER |
Při selhání převodu řetězce na číslo z toho důvodu, že řetězec
neobsahuje platné číslo (v SQL). V procedurálním příkazu je vyvolána vyjímka VALUE_ERROR. |
LOGIN_DENIED |
Při přihlašování k Oracle s neplatným jménem (username) a/nebo heslem (password). |
NO_DATA_FOUND |
V situaci, kdy příkaz SELECT INTO nevrátí žádné řádky, při
odkazování na smazaný prvek vnořené tabulky nebo neinicializovaný prvek
index-by tabulky.Od příkazu FETCH se případně dá očekávat navrácení prázdného
řádku (no rows) a v tomto případě vyjímka není vyvolána.Agregační funkce SQL jako AVG či SUM vracejí
vždy hodnotu nebo null a proto také příkaz SELECT INTO
s agregační funkcí nevyvolává vyjímku NO_DATA_FOUND. |
NOT_LOGGED_ON |
Při pokusu provést databázou operaci bez předchozího připojení k Oracle. |
PROGRAM_ERROR |
Při vnitřní chybě (internal problem) PL/SQL. |
ROWTYPE_MISMATCH |
Při nekompatibilitě hlavní proměnné (host) a PL/SQL kurzorové proměnné. |
STORAGE_ERROR |
Při vyčerpání nebo poškození paměti. |
SUBSCRIPT_BEYOND_COUNT |
Při odkazování na prvek vnořené tabulky (nested table) či pole (varray) s číslem indexu větším než je počet dostupných prvků. |
SUBSCRIPT_OUTSIDE_LIMIT |
Při odkazování na prvek vnořené tabulky (nested table) či pole (varray) s číslem indexu, který je mimo povolený rozsah (např. -1). |
TIMEOUT_ON_RESOURCE |
Při vypršení času, během kterého Oracle čeká na prostředky (resource). |
TOO_MANY_ROWS |
V situaci, kdy příkaz SELECT INTO vrátí více než jednu řádku. |
VALUE_ERROR |
Při chybě aritmetickě, převodní; při zkrácení nebo omezení velikosti. Například při dosazení hodnoty ze sloupce do znakové proměnné s menší délkou. V procedurálních příkazech je vyjímka vyvolána, když selže převod mezi řetězcem a číslem (v SQL je to vyjímka INVALID_NUMBER). |
ZERO_DIVIDE |
Při pokusu o dělení nulou. |