PL/SQL v příkladech / Výjimky
Jak již bylo naznačeno v úvodu, pro zachytávání chyb běhu programu slouží část bloku uvozená klíčovým slovem EXCEPTION. Rozlišujeme chyby systémové, které jsou vyvolány automaticky při výskytu chyby, a uživatelsky definované, které je nutné vyvolat explicitně příkazem RAISE. Několik nejčastějších systémových popisují následující příklady.
Systémové
VALUE_ERROR
DECLARE
l_Cislo NUMBER;
BEGIN
l_Cislo:='Franta';
EXCEPTION
WHEN VALUE_ERROR THEN
dbms_output.put_line('Selhala implicitní konverze!');
END;
ZERO_DIVIDE
DECLARE
l_Cislo NUMBER;
BEGIN
l_Cislo:=20/0;
EXCEPTION
WHEN ZERO_DIVIDE THEN
dbms_output.put_line('Dělení nulou!');
END;
NO_DATA_FOUND
DECLARE
l_Cislo NUMBER;
BEGIN
SELECT employee_id
INTO l_Cislo
FROM employees
WHERE (last_name='Vomacka')
;
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line('Zaměstnanec Vomacka neexistuje!');
END;
TOO_MANY_ROWS
DECLARE
l_Cislo NUMBER;
BEGIN
SELECT employee_id
INTO l_Cislo
FROM employees
WHERE (salary>3000)
;
EXCEPTION
WHEN TOO_MANY_ROWS THEN
dbms_output.put_line('Víc než jeden řádek!');
END;
COLLECTION_IS_NULL
CREATE OR REPLACE TYPE Varchar_TT IS TABLE OF VARCHAR2(4000); /
DECLARE
lt_Pole Varchar_TT;
BEGIN
--lt_Pole:=Varchar_TT();
--lt_Pole.EXTEND(1);
lt_Pole(1):='test';
EXCEPTION
WHEN COLLECTION_IS_NULL THEN
dbms_output.put_line('Neinicializovaný objekt!');
END;
SUBSCRIPT_BEYOND_COUNT
DECLARE
lt_Pole Varchar_TT;
l_Cnt NUMBER;
BEGIN
lt_Pole:=Varchar_TT();
lt_Pole.EXTEND(1);
lt_Pole(2):='test';
EXCEPTION
WHEN SUBSCRIPT_BEYOND_COUNT THEN
dbms_output.put_line('Mimo index pole!');
END;
OTHERS
V jednom bloku je možné odchytit více typů chyb, nicméně platí, že pokud chyba nemá svůj handler, program skončí v obecné výjimce OTHERS. Zprávu o mimořádné události vrací funkce SQLERRM.
DECLARE
lt_Pole Varchar_TT;
BEGIN
lt_Pole:=Varchar_TT();
lt_Pole.EXTEND(1);
lt_Pole(2):='test';
EXCEPTION
WHEN COLLECTION_IS_NULL THEN
dbms_output.put_line('Neinicializovaný objekt!');
WHEN OTHERS THEN
dbms_output.put_line(SQLERRM); -- ORA-06533: Subscript beyond count
END;
V situacích, kdy dopředu víme, že bude operace potenciálně nebezpečná, je dobrou praxí chybám spíše předcházet. Ve velmi rozsáhlých systémech to zjednodušuje následné hledání problému.
DECLARE
l_NebezpecneCislo NUMBER:=0;
l_Pocet PLS_INTEGER;
l_Vysledek NUMBER;
BEGIN
-- ošetření NO_DATA_FOUND a TOO_MANY_ROWS
SELECT COUNT(1)
INTO l_Pocet
FROM employees
WHERE (last_name='Russell')
;
IF (l_Pocet>0)
AND (l_Pocet<2)
THEN
-- existuje právě jeden
SELECT CASE -- ošetření ZERO_DIVIDE a NULL hodnoty
WHEN (NVL(l_NebezpecneCislo, 0)=0)
THEN 0
ELSE (salary/l_NebezpecneCislo)
END
INTO l_Vysledek
FROM employees
WHERE (last_name='Russell')
;
dbms_output.put_line(l_Vysledek);
ELSE
-- neexistuje nebo jich je víc
dbms_output.put_line('Počet zaměstnanců: '||l_Pocet);
END IF;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('Neznámá chyba!');
END;
Uživatelské
Pokud nám předpřipravené Oracle výjimky nestačí, na řadě je definice vlastních v deklarativní části anonymního bloku nebo package. Pro vyvolání uživatelské výjimky slouží příkaz RAISE.
DECLARE
VYJIMKA EXCEPTION;
BEGIN
-- potenciálně
-- nebezpečné
-- operace
RAISE VYJIMKA;
EXCEPTION
WHEN VYJIMKA THEN
dbms_output.put_line('Moje výjimka');
END;
Propagace výjimek
Další příklad vysvětluje systém zachytávání a propagace výjimek skrze několik úrovní bloků kódu. V tomto případě jsou výjimky deklarované v samostatném balíku.
CREATE OR REPLACE PACKAGE BalikVyjimek AS VNEJSI_VYJIMKA EXCEPTION; VNITRNI_VYJIMKA EXCEPTION; END; /
BEGIN
/* ZAČÁTEK VNITŘNÍHO BLOKU */
BEGIN
-- potenciálně
-- nebezpečné
-- operace
RAISE BalikVyjimek.VNITRNI_VYJIMKA;
EXCEPTION
WHEN BalikVyjimek.VNITRNI_VYJIMKA THEN
dbms_output.put_line('Vnitřní výjimka');
END;
/* KONEC VNITŘNÍHO BLOKU */
-- vnitřní výjimka byla odchycena, pokračuji
-- další
-- nebezpečnou
-- operací
RAISE BalikVyjimek.VNEJSI_VYJIMKA;
EXCEPTION
-- VNEJSI_VYJIMKA nemá svůj handler, program skončí v obecné
WHEN OTHERS THEN
dbms_output.put_line('Jiná neočekávaná chyba');
END;