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