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. |