Delphi - Kivételek kezelése PL/SQL-ben

Oracle adatbázis-kezelés 6. rész

forráskód letöltése
Adatbázis-kezelő alkalmazások fejlesztése során sok esetben van szükségünk arra, hogy a művelet végrehajtás során bekövetkező hibás állapotokat kezeljük, vagy egyszerűen csak input adatok helyességét ellenőrizzük. A hibakezelés történhet az alkalmazás kódjából is, de mivel a legtöbb műveletet több okból is tárolt eljárásokra, függvényekre bízzuk, így sokkal megfelelőbb, ha ezeket mindjárt ezekben az alprogramokban tehetnénk meg. Ennek a problémának a feloldására is van megoldás az Oracle rendszerben. Mai cikkünkben a PL/SQL kivételkezelő rendszerét vizsgáljuk meg, mely igen sokszínű és fontos része a rendszernek.
A példaprogram használata előtt létre kell hozni egy SOUSER sémát az Oracle adatbázis-rendszerünkben, és fel kell ruházni DBA jogosultsággal. A cikksorozat harmadik részében a sémát két adattáblával egészítettük ki, melyek a cikksorozat további részeinek tábláit képezik, így az Oracle06.sql script lefuttatása csak az említett táblák hiánya esetén szükséges. Ezek meglétéről meg kell győződni, ugyanis ezek hiányában a létrehozandó további objektumok létrejönnek ugyan, de fordítási hibával. A sémába bejelentkezve le kell viszont futtatni a mellékelt Oracle06ph.sql és Oracle06pb.sql script-eket a hivatkozott további objektumok létrehozásához.
Áttekintés
A kivételes állapotok a program futása közben bekövetkező kivételes állapotok. Ilyen kivételes állapotok előállhatnak akaratlanul (hibaként), de szándékosan is előidézhető a RAISE utasítás segítségével. Ha kivételes állapot keletkezik, akkor a főág megszakad, és egy előre megírt hibaágra (EXCEPTION) adódik a vezérlés. Ha az adott blokkban ilyen nincs, akkor vezérlés a hibajelzéssel együtt visszaadódik a hívó környezetnek.
A kivételes állapotok kezelésében nincs különbség a névtelen blokkok és az eljárások, illetve függvények között, csak a blokkok egymásba ágyazásának mechanizmusa más: a névtelen blokkok egymásba ágyazása statikus, és a programszövegtől függ, míg az eljárások és függvények egymásba ágyazása futási időben dől el a hívási lánc alapján (dinamikus). Ebben a részben tehát a blokk éppúgy jelenthet névtelen blokkot, mint eljárást vagy függvényt.
A kivételes állapotoknak két nagy köre van:
  • Előre definiált: az Oracle beépített kivételes állapotként ismeri őket. Ezek a következők:
Kivételes állapot Oracle hibakód
DUP_VAL_ON_INDEX -1
INVALID_CURSOR -1001
INVALID_NUMBER -1722
LOGIN_DENIED -1017
NO_DATA_FOUND -1403 (100)
NOT_LOGGED_ON -1012
PROGRAM_ERROR - 6501
STORAGE_ERROR - 6500
TIMEOUT_ON_RESOURCE - 51
TOO_MANY_ROWS - 1422
VALUE_ERROR - 6502
ZERO_DEVIDE - 1476
CURSOR_ALREADY_OPEN - 6511
TRANSACTION_BACKED_OUT - 61
  • A felhasználó által definiált: az ezekhez tartozó kivételes állapotot a felhasználó a saját blokkján belül deklarálja, és a RAISE utasítás segítségével explicite idézi elő a kivételes állapotot.
Ha a PL/SQL blokkon belül bármilyen kivételes állapot keletkezik, a vezérlés átadódik az aktuális blokk EXCEPTION részére. Ha nincs ilyen rész, vagy pedig a szóban forgó kivételre vonatkozóan nincs benne utasítás, akkor blokk végrehajtása az ’Unhandled Exception’ (le nem kezelt kivétel) üzenettel félbeszakad, és a vezérlés visszaadódik a hívó környezetnek.
A helyben kezelni kívánt kivételes állapotokat tehát a blokk EXCEPTION részében fel kell tüntetni a végrehajtandó teendőkkel együtt. A szintaxis a következő:
EXCEPTION
WHEN <kivétel neve> THEN
  <utasítás>;
A SELECT utasítás gyakran okoz kivételes állapotot. Ezek zöme NO_DATA_FOUND (a SELECT utasítás nem talál a feltételnek megfelelő rekordot), vagy pedig TOO_MANY_ROWS (amikor a SELECT utasítás egynél több rekordot talált).
Megjegyzendő, hogy a PL/SQL egy blokkon belül több blokk megadását is lehetővé teszi, de ezek közül legfeljebb egyet hajt végre, majd kilép a blokkból. A NO_DATA_FOUND és a TOO_MANY_ROWS kivételes állapot sohasem lép föl DML utasításokban.
A PL/SQL csak azokat a kivételeket kezeli le, melyek explicite szerepelnek az EXCEPTION részben. Bármely más kivétel esetén a blokk félbeszakad. Ez elkerülhető, ha WHEN OTHERS részt helyezünk el az EXCEPTION ágban, mely minden, egyébként nem kezelt kivétel esetén működésbe lép. A WHEN OTHERS kivételkezelőnek az EXCEPTION részben legutolsóként kell szerepelnie.
Ha egy kivétel előáll, szükség lehet a kivételhez kapcsolódó hibakódra, vagy hibaüzenetre is. Ez különösen fontos, ha a kivételt a WHEN OTHERS ágban akarjuk feldolgozni, és a kivételnek megfelelően el akarunk ágazni. A hibakód és a hibaüzenet meghatározására a PL/SQL–ben a SQLCODE és az SQLERRM függvényeket használhatjuk. Az SQLCODE numerikus értéket ad vissza, amit egy NUMBER típusú változóban tárolhatunk. Az SQLERRM karakteres adatot szolgáltat. Az SQLERRM úgy is használható, hogy bemenő paraméterkén megadjuk annak a hibának a kódját, melynek hibaüzenetére kíváncsiak vagyunk. A két utasítás nem használható közvetlenül SQL utasításokban, így a következő példa szerint érdemes használni:
DECLARE
  error_message VARCHAR(100);
  error_code NUMBER;
BEGIN
  ...
EXCEPTION
  WHEN OTHERS THEN
    error_code := SQLCODE;
    error_message := SUBSTR(SQLERRM,1,100);
    INSERT INTO ERRORS VALUES (error_code, error_message);
END;
Ha egy blokkban kivételes állapot keletkezik, akkor az kezelhető a blokkon belül, vagy továbbterjedhet külső blokkokba. A kivételes állapotok terjedésének szabályai a következők:
  • Ha egy létrejött kivételes állapotot egy blokk EXCEPTION része kezel, akkor a blokk végrehajtása normálisan ér véget, és a vezérlés visszatér a hívó blokkhoz.
  • Ha egy helyben keletkezett vagy belső blokktól kapott kivételes állapotot az adott blokk EXCEPTION része nem kezel, akkor a blokk végrehajtása hibaállapottal zárul, és a hibaállapotnak megfelelő kivételes helyzet megjelenik a hívó blokkban. A kivételes helyzet addig terjed a blokkstruktúrán kifelé, amíg egy olyan blokkba jut, amely tartalmaz egy megfelelő kezelőt. Ha egyik blokk sem kezeli a kivételt, akkor a program hibával véget ér, amely a hívó környezetnek adódik át.
  • Ha kivételes helyzet miatt az EXCEPTION ágra kerül a vezérlés, akkor onnan nem térhetünk vissza a normál ágra, és a hiba keletkezési helye valamint az EXCEPTION kulcsszó közé írt utasítások a blokk adott futásakor már nem hajthatók végre. A blokk törzsébe csak úgy térhetünk vissza, ha a hibaállapotot kezeljük, a hiba-okot megszüntetjük, a blokkból kilépünk, majd újra belépünk. Ebben az esetben egy kivétel általánosan kezelhető valamely külső blokkban, és ha szükséges, finomíthatjuk a kezelést belső blokkokban.
Kivételes helyzet kétféleképpen keletkezhet:
  • Olyan Oracle hibaállapotként, melyhez kivételes állapotot definiáltunk. Az ilyen kivételes állapotok automatikusan keletkeznek, ha a végrehajtás során Oracle hiba történik. Például az Oracle-0001 jelzésű hiba előállásakor automatikusan megjelenik a DUP_VAL_ON_INDEX kivételes helyzet.
  • Másik eset, amikor a RAISE utasítással mi magunk idézzük elő a kivételes helyzetet. Szintaxisa:
RAISE <kivételes állapot>;
Az utasítás argumentuma az előidézni kívánt kivételes állapot neve. Az ilyen módon előidézett kivételes állapot vonatkozhat Oracle hibára vagy lehet a felhasználó által definiált kivételes állapot.
Kivételes állapotokat a blokkok elején lehet deklarálni. Ha ezután a normál feldolgozást félbe akarjuk szakítani, akkor a deklarált kivételes állapotok a RAISE utasítással idézhetők elő. Szintaxisa:
DECLARE <kivétel neve> EXCEPTION;
...
RAISE <kivétel neve>;
A felhasználói kivételes állapotok segítségével a felhasználó elnevezheti, kiválthatja és kezelheti saját kivételes állapotait.
A felhasználónak módja van az Oracle hibák bármelyikét kivételes állapotként deklarálni. Ezzel elkerülhetővé válik az egyébként problémás WHEN OTHERS ág használata. Szintaxisa:
PRAGMA EXCEPTION_INIT(<kivétel neve>,<hibakód>);
Példa:
DECLARE
  fetch_failed EXCEPTION;
  PRAGMA EXCEPTION_INIT(FETCH_FAILED,-1002);
BEGIN
  ...
  EXCEPTION
  WHEN fetch_failed THEN
    ... 
END;
Példa alkalmazás
A mellékelt Oracle06ph.sql és Oracle06pb.sql script futtatásával egy GetDept és egy GetPrice nevű tárolt eljárás kerül be az adatbázisba egy ExceptionPack nevű csomagban. A tárolt eljárások egy-egy kivételre mutatnak példát. A GetDept eljárás visszaadja egy beírt azonosítóhoz tartozó székhely nevét a DEALERS táblából, amennyiben ez nem Budapest, kivétel keletkezik, melyet mi hoztunk létre és kezelünk. Ezt a funkciót a Form ’DEPARTMENT lekérdezés’ feliratú fülre kattintva érhetjük el, mely kivétel keletkezésekor ezt egy üzenetpanellel jelzi.
A GetPrice eljárás visszaad egy utazási útvonalhoz tartozó árat, melyhez az ’Eladások ára’ feliratú fülre kell kattintani és az ID mezőben meg kell adni egy létező azonosítót a TRIPS táblából. Nem létező azonosító megadásakor kivétel keletkezik, melyről a program tájékoztatást ad.
A Form-on található két vizuális kontrol, melyekben láthatók a táblák értékei.

Oracle adatbázis-kezelés cikksorozat