obsługa wyjątków

Transkrypt

obsługa wyjątków
Wykład 7 – Obsługa wyjątków– dr Tadeusz Antczak
OBSŁUGA WYJĄTKÓW
W języku PL/SQL zaimplementowano obsługę błędów za pomocą:
 mechanizmu wyjątków
 programów obsługi wyjątków w tzw. sekcji obsługi wyjątków.
Wyjątki mogą być związane z:
błędami systemu Oracle
błędami zdefiniowanymi przez użytkownika.
Tak zaprojektowany, funkcjonujący w ramach danego programu mechanizm zapewnia, że wszelkie błędy
będą usuwane i naprawiane w optymalny sposób. Wyjątki w języku PL/SQL są podobne do wyjątków w
języku Java. Przykładowo, wyjątki Javy są zgłaszane i wykrywane w taki sam sposób jak w PL/SQL-u.
Jednak inaczej niż w Javie wyjątki w języku PL/SQL nie są obiektami i nie można dla nich definiować metod.
Należy sobie odpowiedzieć na pytanie, jakiego rodzaju błędy mogą wystąpić w programie PL/SQL. Błędy
można klasyfikować zgodnie z zasadami przyjętymi w poniższej tabeli.
Typ błędu
Zgłaszany przez
Obsługa
Występujący podczas
kompilacji programu
Kompilator PL/SQL
Interaktywna – kompilator zgłasza błędy, a programista
musi je poprawić
Występujący podczas
wykonywania programu
Mechanizm języka
PL/SQL
Programowa – wyjątki są zgłaszane i przechwytywane
przez programy obsługi wyjątków
Wyjątki:
 są przeznaczone do obsługi błędów powstających podczas wykonywania programu,
 nie obsługują tych pojawiających się podczas kompilacji, bowiem wówczas te błędy są wykrywane przez
mechanizm języka PL/SQL i zgłaszane użytkownikowi – program nie może ich obsłużyć, ponieważ nie
jest jeszcze uruchomiony.
Przykładowo poniższy blok, w którym nieprawidłowo podano identyfikator Ttowary w instrukcji SELECT
DECLARE
z_LiczbaProduktów NUMBER;
BEGIN
SELECT COUNT(*) INTO z_LiczbaProduktow
FROM ttowary;
END;
spowoduje powstanie błędu kompilacji:
PLS-201: identyfikator ‘TTOWARY’ musi być zadeklarowany
1
Wykład 7 – Obsługa wyjątków– dr Tadeusz Antczak
Wyjątki i programy obsługi wyjątków są metodami reakcji i sposobu postępowania programu w sytuacji
wystąpienia błędów podczas wykonywania programu. Do błędów występujących podczas wykonywania
programu zalicza się błędy SQL, takie jak:
ORA-1: naruszono więzy unikatowe
oraz błędy proceduralne
ORA-06502: PL/SQL: błąd wartości lub numeryczny
Po wystąpieniu błędu następuje zgłoszenie wyjątku. Wyjątek powoduje przekazanie sterowania do bloku
obsługi wyjątków, który jest oddzielną sekcją programu. Taki sposób organizacji kodu zapewnia oddzielenie
obsługi błędów od reszty programu, co ułatwia zrozumienie logiki programu i zapewnia, że wszystkie błędy
zostaną wykryte.
W przypadku języka, który nie stosuje modelu wyjątków do obsługi błędów (np. język C), w celu zapewnienia
prawidłowej obsługi błędów we wszystkich przypadkach jest konieczne wstawienie kodu obsługi błędów, co
ilustruje poniższy przykład:
int x, y, z
f(x); /* wywołanie funkcji, przekazanie x jako argumentu */
if <wystapil błąd>
obsluga błędu(...);
y=1/z;
if <wystapil błąd>
obsluga błędu(...);
z=x+y
if <wystapil błąd>
obsluga błędu(...);
Porównajmy poprzedni przykład z analogicznym fragmentem programu, tym razem zapisanym w języku
PL/SQL:
DECLARE
x
NUMBER;
y
NUMBER;
z
NUMBER;
BEGIN
f(x);
y:=1/z;
z:=x+y
EXCEPTION
WHEN OTHERS THEN
/* Program obsługi wykonywany dla wszystkich błędów */
program_obsługi_błedow(...)
END
2
Wykład 7 – Obsługa wyjątków– dr Tadeusz Antczak
Należy zwrócić uwagę, że sekcja obsługi błędów jest oddzielona od reszty programu. Takie rozwiązanie
zabezpiecza przed następującymi problemami, pojawiającymi się w programach utworzonych w języku C:
Łatwiej można zrozumieć założenia programu, ponieważ jego poszczególne elementy są w logiczny
sposób wyodrębnione.
Niezależnie od tego, której instrukcji wykonanie się nie powiedzie, program wykryje i obsłuży błąd.
Wynika to z faktu, iż program nie będzie kontynuował swego działania od instrukcji, która spowodowała
błąd. Sterowanie zostanie przekazane do programu obsługi wyjątków, a następnie do bloku zewnętrznego.
Deklarowanie wyjątków
Wyjątki są:
 deklarowane w sekcji deklaracji bloku,
 zgłaszane w sekcji wykonania
 obsługiwane w sekcji wyjątków.
Istnieją dwa typy wyjątków:
definiowane przez użytkownika
predefiniowane.
Wyjątki definiowane przez użytkownika
Wyjątek zdefiniowany przez użytkownika jest błędem określanym przez programistę podczas tworzenia
programu. Błąd, który jest związany z takim wyjątkiem, niekoniecznie jest błędem systemu Oracle, np. może
być błędem związanym z danymi.
Wyjątki predefiniowane są związane z powszechnie występującymi błędami języków SQL i PL/SQL.
Wyjątki zdefiniowane przez użytkownika są deklarowane w sekcji deklaracji bloku PL/SQL. Podobnie jak
zmienne wyjątki te posiadają swój typ (EXCEPTION) oraz zakres, np.:
DECLARE
w_ZbytDuzoProduktowWycofanych
EXCEPTION;
gdzie w_ZbytDuzoProduktowWycofanych jest identyfikatorem widocznym aż do końca danego bloku. Należy
zwrócić uwagę, że zakres wyjątku jest taki sam jak zakres każdej innej zmiennej lub kursora w tej samej sekcji
deklaracji.
Wyjątki predefiniowane
W języku PL/SQL istnieje pewna liczba predefiniowanych wyjątków, które odpowiadają najczęściej
występującym błędom systemu Oracle. Ponieważ identyfikatory dla tych wyjątków są zdefiniowane w
pakiecie STANDARD, dlatego wyjątki predefiniowane są dostępne bezpośrednio do wykorzystania w
programie – nie jest konieczne ich deklarowanie w sekcji deklaracji, jak w przypadku wyjątków
zdefiniowanych przez użytkownika.
3
Wykład 7 – Obsługa wyjątków– dr Tadeusz Antczak
W poniższej tabeli opisano niektóre najpopularniejsze błędy obsługiwane w systemie Oracle.
Wyjątki predefiniowane w języku PL/SQL
Błąd Oracle
Nazwa wyjątku
Objaśnienie
ORA-0061
Naruszono ograniczenie unikalności. Ten wyjątek pojawia się w wyniku
próby umieszczenia rekordu w tabeli z określonym kluczem głównym, gdy
wstawiany rekord będzie powodował zduplikowanie wartości klucza już
istniejącego w tabeli
Podczas oczekiwania na zwolnienie zasobów nastąpiło przekroczenie limitu
TIMEOUT_ON_RESOURCE
czasu
TRANSACTION_BACKED_OUT Transakcja wycofana z powodu zakleszczenia
ORA-1001
INVALID_CURSOR
Nieprawidłowa operacja kursora
ORA-1012
NOT_LOGGED_ON
Brak połączenia z bazą Oracle
ORA-1017
LOGIN_DENIED
Nieprawidłowa nazwa użytkownika lub nieprawidłowe hasło.
ORA-1403
NO_DATA_FOUND
ORA-1410
SYS_INVALID_ROWID
ORA-1422
TOO_MANY_ROWS
ORA-1476
ZERO_DIVIDE
Wyjątek pojawia się w przypadku, gdy instrukcja SELECT … INTO która
powinna zwracać jeden wiersz, zwraca ich więcej.
Dzielenie przez zero.
ORA-1722
INVALID_NUMBER
Konwersja do wartości liczbowej nie powiodła się.
ORA-6500
STORAGE_ERROR
ORA-6501
PROGRAM_ERROR
Brak wystarczającej ilości pamięci podczas wykonywania operacji PL/SQL
powoduje wystąpienie wewnętrznego błędu PL/SQL.
Błąd wewnętrzny PL/SQL.
ORA-6504
ROWTYPE_MISMATCH
Zmienna kursora hosta oraz zmienna kursora PL/SQL mają niezgodne typy.
ORA-6502
VALUE_ERROR
ORA-65-11
CURSOR_ALREADY_OPEN
ORA-6530
ACCESS_INTO_NULL
ORA-6531
COLLECTION_IS_NULL
ORA-6532
SUBSCRIPT_OUTSIDE_LIMIT
ORA-6533
SUBSCRITP_BEYOND_COUNT
ORA_65-92
CASE_NOT_FOUND
Próba przypisania wartości atrybutom niezainicjowanego (o wartości NULL)
obiektu.
Próba zastosowania metod kolekcji innych niż EXISTS do niezainicjowanej
(o wartości NULL) tabeli PL/SQL lub tablicy VARRAY.
Odwołanie do indeksu tabeli zagnieżdżonej lub indeksu tablicy VARRAY na
zewnątrz deklarowanego zakresu (np.-1)
Odwołanie do do indeksu tabeli zagnieżdżonej lub indeksu tablicy VARRAY
większego niż liczba elementów w kolekcji.
W instrukcji CASE nie znaleziono klauzuli WHEN spełniającej warunek.
ORA-30625
SELF_IS_NULL
Próba wywołania metody dla egzemplarza obiektu o wartości NULL.
ORA-0001
ORA-0051
DUP_VAL_ON_INDEX
Nie odnaleziono danych. Jeśli instrukcja SELECT nie zwróci żadnego
wiersza, wystąpi ten błąd. Zazwyczaj pojawia się w przypadku wybrania
kursora niejawnego i wykonania instrukcji SELECT INTO.
Konwersja do uniwersalnego identyfikatora rowid nie powiodła się.
Błąd arytmetyczny (obcięcia lub konwersji). Ten błąd powstaje w wyniku
podjęcia próby umieszczenia wartości zmiennej, gdy zmienna jest
niezgodnego typu (np. próba umieszczenia tekstu w polu liczbowym) albo
wartość jest zbyt duża (np. umieszczenie wartości tekstowej o długości
przekraczającej długość zadeklarowanej zmiennej)
Próba otwarcia już otwartego kursora.
4
Wykład 7 – Obsługa wyjątków– dr Tadeusz Antczak
Zgłaszanie wyjątków.
W każdym przypadku, gdy podczas wykonywania programu w języku PL/SQL występuje błąd, program
sprawdza, czy użytkownik przygotował odpowiednie zabezpieczenia na taką ewentualność. Sprawdzając
sekcję wyjątków program weryfikuje, czy wystąpienie problemu było przewidywane. System Oracle zawiera
wbudowane pewne mechanizmy obsługi wyjątków. W przypadku konieczności obsługi błędu, którego jeszcze
nie uwzględniono w danym systemie Oracle, można utworzyć wyjątek zdefiniowany przez użytkownika.
Po wystąpieniu błędu związanego z pewnym wyjątkiem następuje zgłoszenie tego wyjątku Wyjątki
zdefiniowane przez użytkownika są zgłaszane jawnie przez instrukcję RAISE, podczas gdy wyjątki
predefiniowane (lub zdefiniowane przez użytkownika za pomocą dyrektywy pragma EXCEPTION_INIT,
związane z błędami systemu Oracle) są zgłaszane niejawnie, w razie wystąpienia skojarzonych z nimi błędów
systemu Oracle. Jeżeli wystąpi błąd, który nie został skojarzony z żadnym z wyjątków, to także zgłaszany jest
wyjątek. Może on być przechwycony przez program obsługi wyjątku OTHERS.
Błąd Oracle
Nazwa wyjątku
Nieokreślony OTHERS
Objaśnienie
Ten wyjątek służy do przechwytywania wszelkich błędów nieobsługiwanych przez programy
obsługi określonych wyjątków. Warto zwrócić uwagę, iż ten program obsługi błędów musi
być zawsze umieszczony na końcu, ponieważ system Oracle nie obsługuje żadnych
wyjątków następujących po tym programie.
DECLARE
-- Wyjątek do wskazania błędu
w_ZbytDuzoProduktowWycofanych EXCEPTION;
-- Bieżąca liczba produktów wycofanych
w_Biez_L_Produktow NUMBER(3);
-- Maksymalna liczba produktów wycofanych
z_Maks_L_Produktow NUMBER(3);
BEGIN
/* Odszukanie bieżącej liczby produktów wycofanych oraz maksymalnej liczby produktów wycofanych */
SELECT biez_L_Produktow, maks_L_Produktow
INTO z_Biez_L_Produktow, z_Maks_L_Produktow
FROM towary
WHERE wycofany = ‘Tak’;
/* Sprawdzenie liczby produktów wycofanych, czy nie przekracza ustalonej maksymalnej liczby
produktów wycofanych */
IF z_Biez_L_Produktow
z_Maks_L_Produktow THEN
-- Za dużo produktów wycofanych – zgłoszenie wyjątku
RAISE w_ZbytDuzoProduktowWycofanych;
END IF;
END;
5
Wykład 7 – Obsługa wyjątków– dr Tadeusz Antczak
Po zgłoszeniu wyjątku sterowanie jest natychmiast przekazywane do sekcji obsługi wyjątków danego bloku.
Jeżeli sekcja wyjątków w danym bloku nie istnieje, wyjątek jest propagowany do bloku obejmującego. Po
przekazaniu sterowania do programu obsługi wyjątków nie ma możliwości powrotu do sekcji wykonywania
bloku. Sytuację tę ilustruje poniższy rysunek.
DECLARE
Tutaj jest deklarowany wyjątek A.
A EXCEPTION;
BEGIN
...
Tutaj jest wywoływany wyjątek A.
RAISE A;
…
EXCEPTION
Każdy kod, który tutaj występuje, nie
jest wykonywany.
WHEN A THEN
…
END;
Sterowanie jest przekazywane do programu
obsługi wyjątków. Kod, który tutaj występuje,
będzie wykonany.
Rys 1. Przekazanie sterowania do programu obsługi wyjątków
Wyjątki predefiniowane są zgłaszane automatycznie w razie wystąpienia związanego z nimi błędu systemu
Oracle, np. poniższy blok PL/SQL wywołuje wyjątek DUP_VAL_ON_INDEX:
BEGIN
INSERT INTO Towary(nr, nazwa, cena_jedn)
VALUES (1002, ‘Czekolada mleczna’, 2,70);
INSERT INTO Towary(nr, nazwa, cena_jedn)
VALUES (1002, ‘Czekolada deserowa’, 2,10);
END;
Zgłoszenie wyjątku nastąpiło ze względu na to, iż kolumna nr tabeli towary stanowi klucz główny i dlatego
zdefiniowano na niej ograniczenie unikalności. W razie próby wstawienia nowej wartości 1002 do kolumny nr
przez kolejną instrukcję INSERT następuje wywołanie następującego błędu:
ORA-0001: naruszono więzy unikatowe
Powyższy błąd odpowiada wyjątkowi DUP_VAL_ON_INDEX.
Obsługa wyjątków
Po zgłoszeniu wyjątku sterowanie jest przekazywane do sekcji obsługi wyjątków bloku, którą to sytuację
przedstawia powyższy rysunek. Sekcja obsługi wyjątków składa się z programu obsługi niektórych lub
6
Wykład 7 – Obsługa wyjątków– dr Tadeusz Antczak
wszystkich wyjątków. Program ten zawiera kod, który jest wykonywany w razie wystąpienia błędu
skojarzonego z danym wyjątkiem i jeśli dany wyjątek zostanie zgłoszony. Składnię sekcji obsługi wyjątków
pokazano poniżej:
EXCEPTION
WHEN nazwa_wyjątku THEN
1_sekwencja_instrukcji
WHEN nazwa_wyjątku THEN
2_sekwencja_instrukcji
WHEN OTHERS THEN
3_sekwencja_instrukcji
END;
Każdy program obsługi wyjątków składa się z klauzuli WHEN oraz instrukcji, które będą wykonywane po
zgłoszeniu wyjątku. Klauzula WHEN służy do określenia, który wyjątek ma być obsługiwany przez dany
program obsługi. Poniżej przedstawiona zostanie kontynuacja poprzedniego przykładu:
DECLARE
-- Wyjątek do wskazania błędu
w_ZbytDuzoProduktowWycofanych EXCEPTION;
-- Bieżąca liczba produktów wycofanych
w_Biez_L_Produktow NUMBER(3);
-- Maksymalna liczba produktów wycofanych
z_Maks_L_Produktow NUMBER(3);
BEGIN
/* Odszukanie bieżącej liczby produktów wycofanych oraz maksymalnej liczby produktów wycofanych */
SELECT biez_L_Produktow, maks_L_Produktow
INTO z_Biez_L_Produktow, z_Maks_L_Produktow
FROM stan_produkty
WHERE wycofany = ‘Tak’;
/* Sprawdzenie liczby produktów wycofanych, czy nie przekracza ustalonej maksymalnej liczby
produktów wycofanych */
IF z_Biez_L_Produktow
z_Maks_L_Produktow THEN
-- Za dużo produktów wycofanych – zgłoszenie wyjątku
RAISE w_ZbytDuzoProduktowWycofanych;
END IF;
EXCEPTION
WHEN w_ZbytDuzoProduktowWycofanych THEN
7
Wykład 7 – Obsługa wyjątków– dr Tadeusz Antczak
/*Program obsługi jest wykonywany, jeśli bieżąca liczba produktów wycofanych przekracza ustaloną
maksymalną liczbę tych produktów. Wprowadzona zapis do dziennika, który zapisuje ten fakt */
INSERT INTO dziennik_błędów(informacja, data_informacji) VALUES (‘Bieżąca liczba produktów
wycofanych ‘||z_Biez_L_Produktow||’ przekracza ustalona maksymalną liczbę tych produktów
‘||z_Maks_L_Produktow, SYSDATE);
END;
Jeden program obsługi może być również wykonywany dla więcej niż jednego wyjątku. W tym celu należy po
prostu wykazać w klauzuli WHEN nazwy wyjątków oddzielone od siebie słowem kluczowym OR, jak w
poniższym przykładzie:
EXCEPTION
WHEN NO_DATA_FOUND OR TOO_MANY_ROWS THEN
INSERT INTO dziennik_błędów(informacja) VALUES (‘Wystąpił błąd instrukcji SELECT’);
END;
Inaczej niż w języku Java sekcja wyjątków bloku może zawierać programy obsługi dla wyjątków, które nie
zostały zgłoszone w sekcji wykonania oraz może nie obsługiwać wyjątków zgłoszonych w sekcji wykonania.
Kompilator języka PL/SQL pod tym względem nie sprawdza poprawności sekcji wyjątków.
Określony wyjątek może być obsłużony przez co najwyżej jeden program obsługi wyjątków w sekcji
wykonania. Jeśli istnieje więcej programów obsługi dla wyjątku, kompilator języka PL/SQL zgłosi
błąd PLS-483, co ilustruje poniższa sesja programu SQL*Plus:
DECLARE
-- Zadeklarowanie 2 wyjątków definiowanych przez użytkownika:
w_Wyjatek1 EXCEPTION;
w_Wyjatek2 EXCEPTION;
BEGIN
-- Zgłoszenie wyjątku w_Wyjatek1
RAISE w_Wyjatek1
EXCEPTION
WHEN w_Wyjatek2 THEN
INSERT INTO dziennik_bledow(informacja)
VALUES (‘wykonano pierwszy program obsługi wyjątków’);
WHEN w_Wyjatek1 THEN
INSERT INTO dziennik_bledow (informacja)
VALUES (‘wykonano drugi program obsługi wyjątków’);
WHEN w_Wyjatek1 OR w_Wyjatek2 THEN
INSERT INTO dziennik_bledow (informacja)
VALUES (‘wykonano trzeci program obsługi wyjątków’);
END;
8
Wykład 7 – Obsługa wyjątków– dr Tadeusz Antczak
WHEN w_Wyjatek1 OR w_Wyjatek2 THEN
*
BŁĄD w linii 15
ORA-06550: linia 15, kolumna 7
PLS-00483: wyjątek ‘W_WYJATEK2’ może się znaleźć w co najwyżej jednej obsłudze wyjątków w tym
bloku.
ORA-06550: linia 0, kolumna 0
PL/SQL: Przerwano proces analizy modułów w czasie kompilacji
Klauzula obsługi wyjątku OTHERS
W języku PL/SQL występuje specjalna klauzula obsługi wyjątków WHEN OTHERS. Klauzula ta będzie
wykonywana dla wszystkich zgłaszanych wyjątków, których nie obsłużono w pozostałych klauzulach WHEN w
bieżącej sekcji wyjątków. Powinna ona być zawsze ostatnią klauzulą obsługi błędów w bloku tak, aby wszystkie
poprzednie (bardziej szczegółowe) klauzule obsługi błędów były wcześniej sprawdzone. Klauzula WHEN
OTHERS obsłuży wszystkie rodzaje wyjątków, niezależnie od tego czy są one predefiniowane, czy też
zdefiniowane przez użytkownika. Do dobrej praktyki programistycznej zalicza się uwzględnianie klauzuli obsługi
wyjątków OTHERS na najwyższym poziomie programu (blok najbardziej zewnętrzny) w celu zapewnienia
wykrywania wszystkich błędów. W innym przypadku błędy będą propagowane do środowiska wywołania. Może to
spowodować niepożądane efekty, jak np. wycofanie bieżącej transakcji. Poniżej przedstawiono kontynuację
rozpoczętego wcześniej przykładu.
DECLARE
-- Wyjątek do wskazania błędu
w_ZbytDuzoProduktowWycofanych EXCEPTION;
-- Bieżąca liczba produktów wycofanych
w_Biez_L_Produktow NUMBER(3);
-- Maksymalna liczba produktów wycofanych
z_Maks_L_Produktow NUMBER(3);
BEGIN
-- Odszukanie bieżącej liczby produktów wycofanych oraz maksymalnej liczby produktów wycofanych
SELECT biez_L_Produktow, maks_L_Produktow
INTO z_Biez_L_Produktow, z_Maks_L_Produktow
FROM stan_produkty
WHERE wycofany = ‘Tak’;
/* Sprawdzenie liczby produktów wycofanych, czy nie przekracza ustalonej maksymalnej liczby
produktów wycofanych */
IF z_Biez_L_Produktow
z_Maks_L_Produktow THEN
-- Za dużo produktów wycofanych – zgłoszenie wyjątku
RAISE w_ZbytDuzoProduktowWycofanych;
END IF;
EXCEPTION
WHEN w_ZbytDuzoProduktowWycofanych THEN
9
Wykład 7 – Obsługa wyjątków– dr Tadeusz Antczak
/*Program obsługi jest wykonywany, jeśli bieżąca liczba produktów wycofanych przekracza ustaloną
maksymalną liczbę tych produktów. Wprowadzona zapis do dziennika, który zapisuje ten fakt*/
INSERT INTO dziennik_bledow(informacja, data_informacji) VALUES (‘Bieżąca liczba produktów
wycofanych ‘||z_Biez_L_Produktow||’ przekracza ustalona maksymalną liczbę tych produktów
‘||z_Maks_L_Produktow, SYSDATE);
WHEN OTHERS THEN
-- Klauzula obsługi wykonywana dla wszystkich innych błędów
INSERT INTO dziennik_bledow(informacja, data_informacji)
VALUES (‘Wystąpił inny błąd’, SYSDATE);
END;
Klauzula obsługi błędów OTHERS, przedstawiona w powyższym przykładzie, jedynie zapisuje fakt
wystąpienia błędu, jednak bez wskazania jego rodzaju. Dzięki predefiniowanym funkcjom SQLCODE oraz
SQLERRM, można określić rodzaj błędu wywołującego wyjątek.
Nie należy definiować klauzuli obsługi wyjątków w postaci WHEN OTHERS THEN NULL; ponieważ spowoduje to
przechwytywanie nieoczekiwanych błędów bez ich rejestrowania. Dobry program obsługi wyjątków OTHERS
zarejestruje błąd, a także dodatkowe informacje, pozwalające na późniejszą analizę sytuacji.
Funkcje SQLCODE oraz SQLERRM
Wewnątrz klauzuli obsługi OTHERS warto uwzględnić zapewnienie dostępu do informacji, który błąd Oracle
spowodował zgłoszenie określonego wyjątku, niezależnie od tego, czy z błędem jest związany wyjątek
predefiniowany, czy też nie. Jednym z powodów jest zarejestrowanie rodzaju błędu, a nie tylko faktu jego
zaistnienia. Znajomość rodzaju zaistniałego błędu sprzyja określeniu odpowiednich operacji, które powinny
zostać wykonane. Język PL/SQL umożliwia uzyskiwanie takich informacji za pomocą dwóch wbudowanych
funkcji: SQLCODE oraz SQLERRM. Funkcja SQLCODE zwraca bieżący kod błędu, a funkcja SQLERRM
zwraca tekst komunikatu o bieżącym błędzie. Dla wyjątków zdefiniowanych przez użytkownika funkcja
SQLCODE zwraca wartość1, natomiast funkcja SQLERRM komunikat „User-defined Exception” (wyjątek
zdefiniowany przez użytkownika). Poniżej podano zapis całości bloku PL/SQL, który dotąd był analizowany.
Warto przeanalizować budowę kompletnej klauzuli obsługi wyjątków OTHERS.
DECLARE
-- Wyjątek do wskazania błędu
w_ZbytDuzoProduktowWycofanych EXCEPTION
-- Bieżąca liczba produktów wycofanych
w_Biez_L_Produktow NUMBER(3);
-- Maksymalna liczba produktów wycofanych
z_Maks_L_Produktow NUMBER(3);
-- Kod oraz tekst innych błędów wykonania
z_KodBledu dziennik_błędów.kod%TYPE;
z_KomunikatBledu dziennik_błędów.komunikat%TYPE;
10
Wykład 7 – Obsługa wyjątków– dr Tadeusz Antczak
BEGIN
/* Odszukanie bieżącej liczby produktów wycofanych oraz maksymalnej liczby produktów wycofanych */
SELECT biez_L_Produktow, maks_L_Produktow
INTO z_Biez_L_Produktow, z_Maks_L_Produktow
FROM stan_produkty
WHERE wycofany = ‘Tak’;
/* Sprawdzenie liczby produktów wycofanych, czy nie przekracza ustalonej maksymalnej liczby
produktów wycofanych */
IF z_Biez_L_Produktow
z_Maks_L_Produktow THEN
-- Za dużo produktów wycofanych – zgłoszenie wyjątku
RAISE w_ZbytDuzoProduktowWycofanych;
END IF;
EXCEPTION
WHEN w_ZbytDuzoProduktowWycofanych THEN
/*Program obsługi jest wykonywany, jeśli bieżąca liczba produktów wycofanych przekracza ustaloną
maksymalną liczbę tych produktów. Wprowadzona zapis do dziennika, który zapisuje ten fakt*/
INSERT INTO dziennik_błędów(informacja, data_informacji) VALUES (‘Bieżąca liczba produktów
wycofanych ‘||z_Biez_L_Produktow||’ przekracza ustalona maksymalną liczbę tych produktów
‘||z_Maks_L_Produktow, SYSDATE);
WHEN OTHERS THEN
-- Klauzula obsługi wykonywana dla wszystkich innych błędów
z_KodBledu:=SQLCODE;
z_KomunikatBledu:=SUBSTR(SQLERRM,1,200);
-- Należy zwrócić uwagę na zastosowanie w tym miejscu funkcji SUBSTR
INSERT INTO dziennik_bledow(kod, komunikat, informacja, data_informacji)
VALUES (z_KodBledu, z_KomunikatBledu,‘Wystąpił błąd Oracle’,SYSDATE);
END;
Maksymalna długość komunikatu o błędzie wynosi 512 znaków. W powyższym przykładzie zmienna
z_KomunikaBledu ma długość tylko 200 znaków (w celu dopasowania do pola kod tabeli dziennik_bledow).
Jeżeli tekst komunikatu o błędzie jest dłuższy niż 200 znaków, to samo przypisanie:
z_KomunikatBledu:= SQLERRM;
spowoduje zgłoszenie wyjątku predefiniowanego VALUE_ERROR. Aby tego uniknąć, należy użyć
wbudowanej funkcji SUBSTR w celu zapewnienia, że zmiennej z_KomunikatBledu zostanie przypisanych
najwyżej 200 znaków tekstu komunikatu o błędzie.
Należy zwrócić uwagę, że funkcje SQLCODE i SQLERRM są najpierw przypisywane do zmiennych
lokalnych. Zmienne te są następnie stosowane w instrukcji SQL. Funkcje te są proceduralne, a zatem nie
można ich wykorzystać bezpośrednio w instrukcji SQL.
11
Wykład 7 – Obsługa wyjątków– dr Tadeusz Antczak
Funkcja SQLERRM może być również wywołana z pojedynczym argumentem liczbowym. W takim
przypadku funkcja zwraca tekst skojarzony z liczbą. Powyższy argument powinien być zawsze liczbą ujemną.
Jeżeli funkcja SQLERRM jest wywoływana z wartością równą zeru, to następuje zwrócenie następującego
komunikatu:
ORA-0000: normal, successful completion
(normalne, udane zakończenie operacji)
Jeżeli funkcja SQLERRM jest wywoływana z dodatnią wartością różną od +100, następuje zwrócenie
komunikatu:
non-ORACLE Exception
( wyjątek nie jest wyjątkiem systemowym Oracle)
Funkcja SQLERRM(100) zwraca:
ORA-1403: no data found
(nie znaleziono danych)
Funkcja SQLCODE wywołana z klauzuli obsługi wyjątków zwraca wartość ujemną oznaczającą błąd systemu
Oracle. Odstępstwem od tej reguły jest błąd ORA-1403: no data found (nie znaleziona danych), kiedy funkcja
SQLCODE zwraca wartość równą +100 (100 odpowiada kodowi ANSI błędu NO DATA FOUND – BRAK
DANYCH).
Funkcja SQLERRM (bez żadnych argumentów) wywołana z sekcji wykonania bloku zwraca zawsze:
ORA-0000: normalne, udane zakończenie
Natomiast funkcja SQLCODE zwraca 0. Wszystkie opisywane sytuacje są przedstawione w poniższej
przykładowej sesji programu SQL*Plus.
SQL>BEGIN
2.
DBMS_OUTPUT.PUT_LINE(‘SQLERRM(0): ‘ || SQLERRM(0));
3.
DBMS_OUTPUT.PUT_LINE(‘SQLERRM(100): ‘ || SQLERRM(100));
4.
DBMS_OUTPUT.PUT_LINE(‘SQLERRM(10): ‘ || SQLERRM(10));
5.
DBMS_OUTPUT.PUT_LINE(‘SQLERRM: ‘ || SQLERRM);
6.
DBMS_OUTPUT.PUT_LINE(‘SQLERRM(-1): ‘ || SQLERRM(-1);
7.
8.
9.
DBMS_OUTPUT.PUT_LINE(‘SQLERRM(-54): ‘ || SQLERRM(54);
END;
/
SQLERRM(0): ORA-000: normal, successful completion
SQLERRM(100): ORA-01403: no data found
SQLERRM(10): -10: non-ORACLE exception
SQLERRM: ORA-000: normal, successful completion
SQLERRM(-1): ORA-00001: unique constraint(.) violated
SQLERRM(-54): ORA-00054: resource busy and acquire with NOWAIT specified
PL/SQL procedure successfully completed
12
Wykład 7 – Obsługa wyjątków– dr Tadeusz Antczak
Zazwyczaj bardziej użytecznym sposobem zastosowania funkcji SQLERRM niż z przekazywanym
parametrem (jak np. za pomocą funkcji SQLCODE) jest zastosowanie jej bez parametrów. Taka wersja
zwraca pełny komunikat o błędzie z zastąpionymi wszystkimi ciągami znaków, np. nazwę ograniczenia w
przypadku błędu ORA-1 w poprzednim przykładzie.
Zastosowanie funkcji RAISE_APPLICATION_ERROR
W celu tworzenia własnych komunikatów o błędzie można stosować wbudowaną funkcję
RAISE_APPLICATION_ERROR. Własne komunikaty mogą być bardziej opisowe niż nazwane wyjątki.
Błędy zdefiniowane przez użytkownika są przekazywane do środowiska wywołującego w ten sam sposób, w
jaki przekazywane są błędy Oracle. Składnia funkcji RAISE_APPLICATION_ERROR jest następująca:
RAISE_APPLICATION_ERROR(numer_bledu, komunikat_o_bledzie, [zachowanie_bledow]);
gdzie
numer_bledu jest parametrem o wartości z przedziału od –20 000 do –20 999,
komunikat_o_bledzie jest tekstem skojarzonym z tym błędem – długość parametru komunikat_o_bledzie
nie może przekraczać 512 znaków.
zachowanie_bledow jest parametrem logicznym (wartość bolowska) – parametr logiczny
zachowanie_bledow jest opcjonalny. Jeżeli parametr zachowanie_bledow przyjmuje wartość TRUE,
wtedy do listy już zgłoszonych błędów jest dodawany nowy błąd (o ile taka lista już istnieje). Jeżeli
parametr zachowanie_bledow przyjmuje wartość FALSE, nowy błąd zastąpi bieżącą listę błędów
(ustawienie domyślne).
Kolejnym istotnym faktem, o którym należy pamiętać, jest to, iż błędy są zgłaszane z wewnątrz programu.
Aby zgłosić błąd z wewnątrz programu w języku PL/SQL, należy użyć wbudowanej funkcji o nazwie
RAISE_APPLICATION_ERROR. Wymaga ona podania dwóch argumentów. Jeden to numer błędu, który
musi się mieścić się w przedziale od -20000 do -20999. Drugi argument to komunikat o błędzie, który ma
zobaczyć użytkownik. Poniżej został podany przykład umieszczonej w programie linii kodu, która zapewnia
kierowanie informacji zwrotnych do użytkownika:
RAISE_APPLICATION_ERROR (-20123, ‘To jest błąd. Zrobiłeś coś nie tak’);
Poniższy fragment kodu przedstawia sytuacje, jak te powyżej wymienione mechanizmy obsługi błędów
współpracują w jednym programie:
DECLARE
l_liczba_pracownikow
NUMBER;
i
NUMBER; -- to będzie licznik
l_wiersz
pracownicy%ROWTYPE
BEGIN
SELECT *
INTO l_wiersz
FROM pracownicy
ORDER BY nazwisko_pracownika;
EXCEPTION
13
Wykład 7 – Obsługa wyjątków– dr Tadeusz Antczak
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR (-20052, ‘Brak danych w tej tabeli. SPRÓBUJ PONOWNIE!’);
WHEN others THEN
RAISE_APPLICATION_ERROR (-20999, ‘Coś poszło źle. Musisz sam wywnioskować co!’);
END;
Przykładowy efekt uruchomienia tego programu może być następujący:
declare
*
BŁĄD w wierszu 2:
ORA-20052: Brak danych w tej tabeli. SPRÓBUJ PONOWNIE!
ORA-06512: w wierszu 13
Jak wynika z konstrukcji obsługi wyjątków, ten program będzie obsługiwał błędy związane z brakiem danych,
a także inne. Znając zasadę działania można zapewnić obsługę różnorodnych problemów pojawiających się w
programach.
Zaawansowane funkcje obsługi błędów w programach w języku PL/SQL
Omówione zostaną teraz sposoby tworzenia własnych wyjątków przez użytkownika. Zostaną również
omówione pewne wartości specjalne dostępne podczas wykonywania programu w języku PL/SQL, które
mogą być przydatne w przypadku wystąpienia błędów w kodzie programu. Wartości te pomagają usuwać
błędy programu, jak również przekazują użytkownikom informacje zwrotne o zaistniałych błędach.
Wyjątki definiowane przez użytkownika
Gdy program w języku PL/SQL napotka błąd lub niepożądaną sytuację, zgłaszany jest wyjątek. Jak wiemy,
system Oracle jest wyposażony w mechanizmy obsługi wyjątków. Jego możliwości można rozszerzyć poprzez
zdefiniowanie własnych wyjątków, zwanych wyjątkami definiowanymi przez użytkownika.
Wyjątki definiowane przez użytkownika mogą być wywołane w celu obsługi błędu lub podjęcia działań w
sytuacji, której system Oracle nie interpretuje jako błąd.
Aby wyjątek zdefiniowany przez użytkownika był prawidłowy i właściwie stosowany należy w kodzie
programu zdefiniować trzy składniki:
deklaracja wyjątku,
wywoływanie wyjątku podczas wykonywania programu,
program obsługi samego wyjątku.
Jest to składnia nieco odmienna niż w przypadku wyjątków dostarczanych przez Oracle, które mogą być
używane w programie bez konieczności ich deklarowania czy nawet w przypadkach, gdy nie istniał błąd.
Poniżej w przykładowym programie zaprezentowano sposób łączenia się tych trzech składników w programie:
DECLARE
l_licznik
NUMER:=0;
l_nazwa
pracownik.nazwisko_pracownika%type;
14
Wykład 7 – Obsługa wyjątków– dr Tadeusz Antczak
CURSOR pobierz_nazwisko_pracownika IS
SELECT nazwisko_pracownika
FROM pracownicy;
wyj_stary_przyjaciel
EXCEPTION;
wyj_nie_znam_czlowieka
EXCEPTION;
BEGIN
OPEN pobierz_nazwisko_pracownika;
FETCH pobierz_nazwisko_pracownika INTO l_nazwisko;
IF l_nazwisko=’Jan Kowalski’ THEN
RAISE wyj_stary_przyjaciel;
ELSE
RAISE wyj_nie_znam_czlowieka;
CLOSE pobierz_nazwisko_pracownika;
EXCEPTION
WHEN wyj_stary_przyjaciel THEN
DBMS_OUTPUT.PUT_LINE(‘Znam ta osobę’);
WHEN wyj_nie_znam_czlowieka THEN
DBMS_OUTPUT.PUT_LINE(‘Nie znam tej osoby’);
END;
/
Jak widać na podstawie tego programu, definicja i zastosowanie wyjątku definiowanego przez użytkownika
zależy od jego konkretnych potrzeb. Dla celów omawianego przykładu wybrano dane z tabeli pracownicy i
utworzono wyjątek dotyczący interesującego w danej sytuacji zagadnienia.
Aby korzystanie z tego wyjątku było możliwe, należy zrealizować trzy zadania.
1. Pierwsze z nich to zadeklarowanie wyjątku. W tym celu należy umieścić jego nazwę w sekcji deklaracji
programu:
wyj_stary_przyjaciel
EXCEPTION;
wyj_nie_znam_czlowieka
EXCEPTION;
2. Drugie – wyjątek musi zostać wywołany w kodzie programu w momencie, gdy wystąpią określone
warunki. W omawianym przypadku jest to pobranie nazwiska znajomego lub obcej osoby. Aby wywołać
wyjątek w kodzie programu, należy po prostu użyć komendy RAISE wraz z nazwą wyjątku:
RAISE wyj_stary_przyjaciel;
Jak dotąd zdefiniowano, iż użytkownik zamierza zastosować wyjątek (sekcja deklaracji) i chce go użyć w
konkretnym momencie (sekcja wykonania).
3. Trzeba jeszcze określić, jakie działanie wyjątek będzie wykonywał. Służy do tego sekcja obsługi
wyjątków w programie. W tym przypadku działanie będzie polegało na zasygnalizowaniu, że osoba jest
znana użytkownikowi:
15
Wykład 7 – Obsługa wyjątków– dr Tadeusz Antczak
EXCEPTION
WHEN wyj_stary_przyjaciel THEN
DBMS_OUTPUT.PUT_LINE(‘Znam ta osobę’);
W powyższym przykładzie pokazano, jak tworzyć wyjątki definiowane przez użytkownika i jak z nich
korzystać. Zakres i kontekst stosowania wyjątków zależy wyłącznie od potrzeb użytkownika.
Zmienne przeznaczone do obsługi błędów dostępne w oprogramowaniu Oracle
Oprócz możliwości definiowania własnych wyjątków w programie PL/SQL użytkownik może również
korzystać z pewnych istotnych zmiennych systemu Oracle przeznaczonych do obsługi błędów. Zmienne te,
zwane pseudokolumnami, są dostępne w różnych formach, zależnie od miejsca w programie. Pseudokolumna
może być użyta w instrukcji SELECT lub podczas przetwarzania danych. Poniżej zostanie zaprezentowanych
klika przykładów pseudokolumn:
dane systemowe (SYSDATA);
numer wiersza (ROWNUM);
numer błędu Oracle (SQLCODE);
komunikat o błędzie (SQLERRM).
Ostatnie dwie wartości (są one najczęściej wykorzystywane w obsłudze błędów) zapewniają dostęp do
numerów błędów i komunikatów o błędach systemu Oracle, umożliwiając w ten sposób tworzenie programów
kończących się zawsze pomyślnie, nawet w przypadku wystąpienia błędu. Do poprzednio omawianego
przykładu programu tym razem zostanie dodany kolejny mechanizm obsługi wyjątków, który będzie
uaktywniany w przypadku wystąpienia określonych błędów. Posłuży do tego wyjątek WHEN OTHERS:
DECLARE
l_licznik
NUMER:=0;
l_nazwa
pracownik.nazwisko_pracownika%type;
CURSOR pobierz_nazwisko_pracownika IS
SELECT nazwisko_pracownika
FROM pracownicy;
wyj_stary_przyjaciel
EXCEPTION;
wyj_nie_znam_czlowieka
EXCEPTION;
BEGIN
OPEN pobierz_nazwisko_pracownika;
FETCH pobierz_nazwisko_pracownika INTO l_nazwisko;
IF l_nazwisko=’Jan Kowalski’ THEN
RAISE wyj_stary_przyjaciel;
ELSE
RAISE wyj_nie_znam_czlowieka;
CLOSE pobierz_nazwisko_pracownika;
EXCEPTION
16
Wykład 7 – Obsługa wyjątków– dr Tadeusz Antczak
WHEN wyj_stary_przyjaciel THEN
DBMS_OUTPUT.PUT_LINE(‘Znam ta osobę’);
WHEN wyj_nie_znam_czlowieka THEN
DBMS_OUTPUT.PUT_LINE(‘Nie znam tej osoby’);
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(‘Mamy problem…Numer ‘ || SQLCODE);
DBMS_OUTPUT.PUT_LINE(‘Treść komunikatu: ‘|| SQLERRM);
END;
Wyjątek WHEN OTHERS może być wykorzystywany do obsługi wszelkich pojawiających się błędów, dla
których nie zdefiniowano innych wyjątków. Musi to być również ostatni wyjątek umieszczony w sekcji
obsługi wyjątków, ponieważ system Oracle kończy przetwarzanie danych po napotkaniu wyjątku
spełniającego kryteria określone w tym wyjątku. Jeśli więc wyjątek WHEN OTHERS zostanie umieszczony
na pierwszym miejscu, dalsze wyjątki w ogóle nie będą przetwarzane.
W omawianym powyżej przykładzie użyto pseudokolumn SQLCODE i SQLERRM. Ich umieszczenie w
programach tworzonych w języku PL/SQL zapewnia, że podczas wykonywania kodu nie nastąpi błędne
zakończenie programu. Inna stosowana metoda może polegać na umieszczeniu tych wartości w zmiennych, są
zwracane do programu wywołującego.
Propagacja wyjątków
Wyjątki mogą występować w sekcji deklaracji, wykonania lub wyjątków bloku PL/SQL. Do tej pory na
wykładzie zostały przeanalizowane zdarzenia, jakie zachodzą w chwili zgłaszania wyjątków w sekcji
wykonania bloku. Teraz zajmiemy się przypadkiem, gdy nie został zaprojektowany program obsługi
wyjątków lub gdy wyjątek jest wywoływany z innej sekcji bloku niż sekcja wykonania bloku. Proces
zachodzący w takich przypadkach jest nazywany propagacją wyjątków.
Wyjątki wywoływane w sekcji wykonania.
W przypadku zgłaszania wyjątku w sekcji wykonania bloku mechanizm PL/SQL wykorzystuje poniższy
algorytm w celu określenia potrzebnego programu obsługi wyjątków.
1. Jeżeli bieżący blok posiada program obsługi wyjątku, należy go wykonać i zakończyć pomyślnie proces
wykonywania bloku. Następnie sterowanie jest przekazywane do bloku obejmującego.
2. Jeżeli nie istnieje program obsługi dla bieżącego wyjątku, to należy propagować wyjątek przez wywołanie
go w bloku obejmującym. Następnie dla bloku obejmującego należy wykonać czynności opisane w
punkcie 1. Gdy nie ma bloku obejmującego, wówczas sterowanie zostanie przekazane do środowiska
wywołującego, np. program SQL*Plus.
Przed dokładnym wykonaniem powyższego algorytmu, który zapewni nam obsługę wyjątku zgłaszanego w
sekcji wykonania, konieczne jest zdefiniowanie bloku obejmującego. Blok może być osadzony wewnątrz
17
Wykład 7 – Obsługa wyjątków– dr Tadeusz Antczak
drugiego i w takim przypadku blok zewnętrzny obejmuje blok wewnętrzny. Poniższy przykład ilustruje tego
typu sytuację:
DECLARE
-- Rozpoczęcie wykonywania bloku zewnętrznego
...
BEGIN
...
DECLARE
-- Rozpoczęcie wykonywania wewnętrznego bloku 1. Jest to blok osadzony w bloku zewnętrznym
...
BEGIN
...
END;
...
BEGIN
-- Rozpoczęcie wykonywania wewnętrznego bloku 2. Jest to również blok osadzony w bloku
-- zewnętrznym. Należy zwrócić uwagę, że blok ten nie posiada sekcji deklaracji
...
END;
...
-- Zakończenie bloku zewnętrznego
END;
W powyższym przykładzie oba wewnętrzne bloki 1 i 2 są objęte przez blok zewnętrzny. Każdy nieobsłużony
wyjątek wywołany w bloku 1 i 2 będzie propagowany do bloku zewnętrznego.
Wywołanie procedury również tworzy blok obejmujący. Tego typu sytuację ilustruje poniższy przykład:
BEGIN
-- Rozpoczęcie wykonywania bloku zewnętrznego
-- Wywołanie procedury. Procedura będzie objęta przez ten blok zewnętrzny
F(...);
EXCEPTION
WHEN OTHERS THEN
Wyjątki zgłaszane przez procedurę F będą przechwycone w tym miejscu
END;
Jeżeli procedura F zgłasza nieobsługiwany wyjątek, będzie on propagowany do bloku zewnętrznego,
ponieważ blok ten obejmuje procedurę.
Zajmiemy się obecnie omówieniem poszczególnych przypadków algorytmu propagacji wyjątków, co zostanie
zilustrowane za pomocą trzech poniższych przykładów.
18
Wykład 7 – Obsługa wyjątków– dr Tadeusz Antczak
Pierwszy przykład propagacji
Przykład ten ilustruje zastosowanie reguły 1. z algorytmu propagacji wyjątków. Wyjątek A jest zgłaszany i
obsługiwany w podbloku, następnie sterowanie powraca do bloku zewnętrznego.
Wyjątek A jest wywołany
DECLARE
w podbloku.
A EXCEPTION
BEGIN
BEGIN
RAISE A;
Wyjątek A jest również
EXCEPTION
obsługiwany w podbloku.
WHEN A THEN
…
END;
Tutaj jest wznawiane
sterowanie
END;
19
Wykład 7 – Obsługa wyjątków– dr Tadeusz Antczak
Drugi przykład propagacji
Dla podbloku zastosowano 2. regułę z algorytmu obsługi wyjątków. Wyjątek jest propagowany do bloku obejmującego,
gdzie jest stosowana 1. reguła. W ten sposób wykonywanie bloku obejmującego kończy się pomyślnie.
DECLARE
Wyjątek B jest wywołany
A EXCEPTION;
w podbloku.
B EXCEPTION;
BEGIN
BEGIN
RAISE B;
EXCEPTION
WHEN A THEN
…
END;
W podbloku nie ma żadnego
programu obsługi dla wyjątku B.
Wyjątek B jest propagowany do
bloku obejmującego i tam jest
EXCEPTION
obsługiwany.
WHEN B THEN
…
Następne sterowanie jest przekazywane
na zewnątrz bloku obejmującego, którego
END;
wywołanie kończy się pomyślnie.
Trzeci przykład propagacji
Dla podbloku stosuję się 2. reguły z algorytmu obsługi wyjątków. Wyjątek jest propagowany do bloku
obejmującego, gdzie w dalszym ciągu nie istnieje program obsługi odpowiedni dla tego wyjątku. Ponownie
jest stosowana 2. reguła z algorytmu obsługi wyjątków. W ten spo-sób wykonywanie bloku obejmującego
kończy się niepomyślnie, z nieobsłużonym wyjątkiem.
20
Wykład 7 – Obsługa wyjątków– dr Tadeusz Antczak
DECLARE
A EXCEPTION;
Wyjątek C jest wywołany
B EXCEPTION;
w podbloku.
C EXCEPTION;
BEGIN
W podbloku nie ma
żadnego programu
BEGIN
RAISE C;
EXCEPTION
WHEN A THEN
…
END;
obsługi dla wyjątku C.
Wyjątek C jest propagowany
do bloku obejmującego, ale
tam również nie ma żadnego
programu
obsługi Ctego
Następne wyjątek
jestwyjątku.
EXCEPTION
propagowany na zewnątrz
WHEN B THEN
środowiska wywołującego.
…
Wykonanie bloku obejmującego
END;
kończy się z nieobsłużonym
wyjątkiem.
Wyjątki zgłaszane w sekcji deklaracji
Po zgłoszeniu wyjątku w sekcji deklaracji jest on natychmiast propagowany do bloku obejmującego.
Następnie zachodzi jego dalsza propagacja, na zasadach podanych poprzednio. Nawet jeżeli w bieżącym
bloku istnieje program obsługi wyjątków, nie jest on wykonywany, co ilustrują poniżej załączone przykłady.
Czwarty przykład propagacji
W omawianym przykładzie zgłaszany jest wyjątek VALUE_ERROR przez zapisanie następującej deklaracji:
z_Liczba
NUMBER(3):= ‘ABC’;
Wyjątek ten jest natychmiast propagowany do bloku obejmującego. Nawet jeżeli występuje program obsługi
wyjątków OTHERS, nie jest on wykonywany. Jeżeli dany blok jest objęty blokiem zewnętrznym, to blok
zewnętrzny może przechwycić ten wyjątek (taki właśnie przypadek ilustruje poniższy przykład).
21
Wykład 7 – Obsługa wyjątków– dr Tadeusz Antczak
Nieprawidłowe przypisanie powoduje
DECLARE
wywołanie wyjątku VALUE_ERROR
v_Number NUMBER(3):=’ABC’;
BEGIN
Nawet jeżeli występuje program
…
obsługi WHEN OTHERS, nie jest on
wykonywany.
EXCEPTION
WHEN OTHERS THEN
Wykonanie bloku kończy się
...
niepowodzeniem, z nieobsłużonym
wyjątkiem VALUE_ERROR.
END;
Piąty przykład propagacji
Rozważymy sytuację, gdy podobnie jak w poprzednim przykładzie, wyjątek VALUE_ERROR jest zgłaszany
w sekcji deklaracji bloku wewnętrznego i natychmiast propagowany do bloku zewnętrznego. W bloku
zewnętrznym istnieje program obsługi wyjątków OTHERS, a zatem wyjątek jest obsłużony i wykonanie bloku
zewnętrznego kończy się pomyślnie.
22
Wykład 7 – Obsługa wyjątków– dr Tadeusz Antczak
BEGIN
Nieprawidłowe przypisanie powoduje
DECLARE
wywołanie wyjątku VALUE_ERROR
v_Number NUMBER(3):=’ABC’;
Nawet jeżeli występuje program
BEGIN
obsługi WHEN OTHERS,
…
nie jest on wykonywany.
EXCEPTION
WHEN OTHERS THEN
...
Program obsługi wyjątków w bloku
END;
zewnętrznym obsługuje wyjątek.
EXCEPTION
WHEN OTHERS THEN
Następne sterowanie jest przekazywane
…
na zewnątrz bloku obejmującego,
którego wykonanie kończy się
END;
pomyślnie.
Wyjątki zgłaszane w sekcji wyjątków.
Wyjątki mogą być również zgłaszane podczas wykonywania programu obsługi wyjątków. Może to zachodzić
jawnie przez instrukcję RAISE albo niejawnie przez błąd powstający podczas wykonywania programu obsługi
wyjątków. W każdym przypadku wyjątek jest propagowany natychmiast do bloku obejmującego, podobnie
jak wyjątki zgłaszane w sekcji deklaracji. Jest to spowodowane tym, iż w sekcji wyjątków w jednym czasie
tylko jeden wyjątek może być „aktywny”. Po obsłużeniu jednego wyjątku może być zgłoszony następny.
Jednak nie można zgłosić więcej niż jednego wyjątku w jednym czasie. Sytuację tą zilustrowano w
poniższych przykładach.
Szósty przykład propagacji wyjątków
W rozważanym przykładzie zachodzi zgłoszenie i obsłużenie wyjątku A. Jednak w programie obsługi wyjątku
A zgłaszany jest wyjątek B. Wyjątek ten jest natychmiast propagowany do bloku zewnętrznego z pominięciem
programu obsługi wyjątku B. Podobnie jak w poprzednim przykładzie, jeżeli ten blok jest objęty blokiem
zewnętrznym, to właśnie ten blok zewnętrzny może przechwycić wyjątek B, co ilustruje poniższy przykład.
23
Wykład 7 – Obsługa wyjątków– dr Tadeusz Antczak
Wyjątek A jest wywoływany
DECLARE
A EXCEPTION;
Wyjątek A jest obsługiwany, a wyjątek
B EXCEPTION;
B jest wywoływany, w programie
BEGIN
obsługi wyjątku A.
RAISE A;
Nawet jeśli występuje program
EXCEPTION
obsługi wyjątku B, nie jest on
WHEN A THEN
wykonywany. Wyjątek jest
RAISE B;
propagowany na zewnątrz bloku
WHEN B THEN
Wykonanie bloku kończy się
...
niepowodzeniem, z nieobsłużonym
END;
wyjątkiem B.
Siódmy przykład propagacji
Rozważymy analogiczna sytuację jak w poprzednim przykładzie, gdzie wyjątek B jest zgłaszany w programie
obsługi wyjątku A. Wyjątek ten jest natychmiast propagowany do bloku obejmującego z pominięciem
wewnętrznego programu obsługi wyjątku B. Jednak w tej sytuacji istnieje blok zewnętrzny, który obsługuje
wyjątek B i którego wykonanie kończy się pomyślnie. Sytuację tę ilustruje poniższy rysunek.
24
Wykład 7 – Obsługa wyjątków– dr Tadeusz Antczak
BEGIN
Wyjątek A jest wywołany.
DECLARE
Wyjątek A jest obsługiwany,
A EXCEPTION;
a wyjątek B jest wywoływany,
B EXCEPTION
w programie obsługi wyjątku A.
BEGIN
Nawet jeśli występuje program
RAISE A;
obsługi wyjątku B, nie jest on
EXCEPTION
wykonywany. Wyjątek jest
WHEN A THEN
propagowany na zewnątrz bloku
RAISE B;
Wyjątek B jest obsługiwany
WHEN B THEN
w bloku zewnętrznym.
...
Wykonanie bloku kończy się
END;
pomyślnie i sterowanie jest
EXCEPTION
przekazywane do środowiska
wywołującego.
WHEN B THEN
…
END;
25
Wykład 7 – Obsługa wyjątków– dr Tadeusz Antczak
Ósmy przykład propagacji
Z przykładów 6 i 7 wynika, że instrukcja RAISE, która jest stosowana w celu zgłoszenia innego wyjątku
wewnątrz programu obsługi, może być także użyta w programie bez argumentu. Jeżeli instrukcja RAISE nie
posiada argumentu, bieżący wyjątek jest propagowany do bloku obejmującego. Taka technika jest przydatna
w rejestrowaniu błędu i (lub) wykonaniu koniecznego z tego właśnie powodu porządkowania. W ten sposób
następuje także przekazanie do bloku informującego o wystąpieniu błędu. Sytuację tę zilustrowano w
poniższym przykładzie.
DECLARE
Wyjątek A jest wywoływany
A EXCEPTION;
Wyjątek A jest obsługiwany,
BEGIN
i błąd jest zarejestrowany.
RAISE A;
EXCEPTION
Ten sam wyjątek jest
wywoływany ponownie.
WHEN A THEN
INSERT INTO log_table(info)
Wyjątek jest propagowany na
VALUES(‘Wystąpił wyjątek A’);
zewnątrz bloku, którego
RAISE;
wykonanie kończy się
niepowodzeniem,
END;
z nieobsłużonym wyjątkiem B.
Należy zwrócić uwagę na fakt, iż w przykładzie 8 po instrukcji INSERT powinna znajdować się instrukcja
COMMIT. Dzięki temu zyskuje się pewność, że instrukcja INSERT zostanie zatwierdzona w przypadku
wycofania transakcji.
Wskazówki dotyczące wyjątków
Omówimy teraz rady oraz wskazówki dotyczące optymalnego wykorzystania mechanizmu wyjątków w
tworzonych programach PL/SQL. Poniższe wskazówki obejmują następujące zagadnienia: zakres wyjątków,
unikanie wyjątków nieobsługiwanych oraz rozpoznawanie instrukcji zgłaszającej określony wyjątek.
26
Wykład 7 – Obsługa wyjątków– dr Tadeusz Antczak
Wskazówki te mają na celu podanie sposobów efektywniejszego wykorzystania mechanizmu wyjątków w
tworzonych programach oraz pokazanie sposobu uniknięcia niektórych, związanych z nimi trudności.
Zakres wyjątków
Wyjątki, podobnie jak zmienne, posiadają zakres. Gdy wyjątek zdefiniowany przez użytkownika jest
propagowany poza swój zakres, wówczas nie można dłużej odwoływać się do niego przez nazwę. Sytuację
tego typu ilustruje poniższy skrypt.
SQL>DECLARE
2. w_WyjatekZdePrzezUzytk EXCEPTION;
3. BEGIN
4.
RAISE w_WyjatekZdePrzezUzytk;
5. END;
6. EXCEPTION
/* Wyjątek w_WyjatekZdePrzezUzytk jest tutaj poza zakresem – może być
7.
obsłużony jedynie przez program obsługi wyjątków OTHERS */
8.
9.
WHEN OTHERS THEN
10.
/* Ponowne zgłoszenie wyjątku, który zostanie propagowany
do środowiska wywołującego */
11.
12.
RAISE
13. END;
14. /
BEGIN
*
BŁĄD w linii 1
ORA-06510: PL/SQL: nieobsłużony wyjątek zdefiniowany przez użytkownika
ORA-06512: w linii 13
Jeżeli błąd zdefiniowany przez użytkownika ma być propagowany na zewnątrz bloku, najlepiej zdefiniować
wyjątek w pakiecie tak, aby był w dalszym ciągu widoczny na zewnątrz bloku. Można również zastosować
funkcję RAISE_APPLICATION_ERROR. Przykładowo, jeżeli został utworzony pakiet o nazwie Globalne
oraz zdefiniowany został w nim wyjątek w_WyjatekZdePrzezUzytk, to będzie on w dalszym ciągu widoczny w
bloku zewnętrznym.
Przeanalizujmy następujący przykład:
CREATE OR REPLACE PACKAGE Globalne AS
/* Ten pakiet zawiera deklaracje globalne. Obiekty tutaj zadeklarowane będą widoczne przez
kwalifikowane odwołania dla każdego innego bloku lub procedury. Należy zwrócić uwagę, że ten
pakiet nie zawiera treści. */
27
Wykład 7 – Obsługa wyjątków– dr Tadeusz Antczak
/* Wyjątek zdefiniowany przez użytkownika */
w_WyjatekZdePrzezUzytk EXCEPTION;
END Globalne;
Uwzględniając pakiet Globalne, można zmodyfikować poprzedni przykład, jak w poniższym fragmencie
kodu:
BEGIN
BEGIN
RAISE Globalne. w_WyjatekZdePrzezUzytk;
END;
EXCEPTION
/* Ponieważ wyjątek w_WyjatekZdePrzezUzytk jest w dalszym ciągu
widoczny, można go obsłużyć jawnie. */
WHEN Globalne. w_WyjatekZdePrzezUzytk THEN
/* Ponowne zgłoszenie wyjątku, który zostanie propagowany
do środowiska wywołującego */
RAISE
END;
/
UWAGA: Poza obsługą wyjątków pakiet Globalne może być również wykorzystywany do wspólnych tabel
PL/SQL, zmiennych oraz typów.
Unikanie nieobsługiwanych wyjątków
Dobrą praktyką programistyczną jest unikanie nieobsługiwanych wyjątków. Można to osiągnąć przez
uwzględnienie klauzuli obsługi wyjątków OTHERS znajdującej się na najwyższym poziomie programu. W
ten sposób utworzony program obsługi błędów może po prostu tylko rejestrować błąd i miejsce jego
wystąpienia. W ten sposób można zapewnić wykrywanie wszystkich błędów. Poniższy program ilustruje ten
sposób wykrywania błędów:
DECLARE
z_NumerBledu
NUMBER;
-- Zmienna do przechowywania numeru błędu.
z_TekstBledu
VARCHAR2(200);
-- Zmienna do przechowywania komunikatu o błędzie.
BEGIN
/* Normalne przetwarzanie PL/SQL */
...
EXCEPTION
WHEN OTHERS THEN
/* Rejestruj wszystkie wyjątki I zakończ pomyślnie wykonywanie bloku */
z_NumerBledu:= SQLCODE;
28
Wykład 7 – Obsługa wyjątków– dr Tadeusz Antczak
z_TekstBledu:= SUBSTR(SQLERRM, 1,200);
INSERT INTO dziennik_bledow (kod, komunikat, informacja) VALUES
(z_NumerBledu, z_TekstBledu, ‘Wystąpił błąd systemu Oracle ‘||
TO_CHAR(SYSDATE,’DD/MM/YY HH24:MI:SS’));
END;
Maskowanie lokalizacji błędu
Ponieważ dla całego bloku programu jest sprawdzana ta sama sekcja wyjątków, ustalenie instrukcji SQL
powodującej występowanie błędu może być trudne.
Rozważmy następujący przykład fragmentu kodu programu:
BEGIN
SELECT ...
SELECT ...
SELECT ...
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- Która instrukcja SELECT spowodowała zgłoszenie wyjątku!
END.
Istnieją dwie metody rozwiązania tego problemu.
Pierwsza metoda polega na zwiększaniu wartości licznika identyfikującego każdą instrukcję SQL:
DECLARE
-- Zmienna przechowująca numer instrukcji SELECT
z_LicznikSelect
NUMBER:=1;
BEGIN
SELECT …
z_LicznikSelect:=2;
SELECT …
z_LicznikSelect:=3;
SELECT …
EXCEPTION
WHEN DATA_NO_FOUND THEN
INSERT INTO dziennik_bledow (informacja)
VALUES(‘Nie znaleziono danych w instrukcji SELECT nr ‘||z_LicznikSelect);
END;
/
29
Wykład 7 – Obsługa wyjątków– dr Tadeusz Antczak
Druga metoda polega na umieszczaniu każdej instrukcji SELECT w jej własnym podbloku:
BEGIN
BEGIN
SELECT ...;
EXCEPTION
WHEN DATA_NO_FOUND THEN
INSERT INTO dziennik_bledow (informacja)
VALUES(‘Nie znaleziono danych w instrukcji SELECT nr 1‘);
END;
BEGIN
SELECT ...;
EXCEPTION
WHEN DATA_NO_FOUND THEN
INSERT INTO dziennik_bledow (informacja)
VALUES(‘Nie znaleziono danych w instrukcji SELECT nr 2‘);
END;
BEGIN
SELECT ...;
EXCEPTION
WHEN DATA_NO_FOUND THEN
INSERT INTO dziennik_bledow(informacja)
VALUES(‘Nie znaleziono danych w instrukcji SELECT nr 3‘);
END;
END;
/
30

Podobne dokumenty