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;