§
In cazul sistemelor de
gestiune a bazelor de date erorile (dar nu numai) sunt numite si exceptii.
§
Un bloc PL/SQL poate
contine intre EXCEPTION si END instructiuni care sa trateze :
o erorile returnate de serverul Oracle sau uneltele
Oracle
o situatii definite de utilizator (exceptii definite de
utilizator).
§
Exceptiile pot avea
asociat un nume (identificator).
§
Unele dintre aceste nume
sunt predefinite (pentru o parte dintre erorile returnate de server sau
unelte).
§
Aparitia unei exceptii
este numita si ridicarea unei exceptii (exception raising)
§
Aparitia unei exceptii,
tratata sau nu, termina executia blocului. Toate instructiunile PL/SQL sau
cererile SQL care apar dupa cererea / instructiunea care a ridicat exceptia nu
mai sunt executate.
§
In cazul in care o
exceptie are asociat un tratament, la ridicarea ei se trece la executia
instructiunilor de tratare dupa care se iese din blocul curent fara eroare.
§
In cazul in care apare o
exceptie care nu este tratata explicit in portiunea dintre EXCEPTION si END,
blocul se termina cu eroare, exceptia putand fi tratata de blocul inconjurator
(daca exista).
§
In cazul exceptiilor
definite de utilizator acestea trebuiesc ridicate explicit in zona dintre BEGIN
si EXCEPTION prin instructiunea PL/SQL RAISE.
Un
exemplu de situatie in care serverul returneaza o eroare este urmatorul:
DECLARE
v_ename varchar2(10);
BEGIN
SELECT ENAME
INTO v_ename
FROM EMP
WHERE DEPTNO>20;
In
cazul in care cererea SQL returneaza mai mult decat o singura inregistrare,
valoarile rezultate nu pot fi stocate intr-o singura variabila scalara.
O
alta eroare pentru blocul de mai sus este si nereturnarea nici unei valori de
catre cererea SQL (in cazul in care nici un departament nu are codul mai mare
ca 20).
Sintaxa tratarii exceptiilor
este urmatoarea:
DECLARE
. . . .
BEGIN
. . . .
EXCEPTION
WHEN exceptie11 OR exceptie12 . . .THEN
instructiuni1
[WHEN exceptie21 OR exceptie22 . . . THEN
instructiuni2]
. . .
. . .
[WHEN OTHERS THEN
Instructiuni_others]
END;
unde:
§
exceptiexy este identificatorul unei exceptii (predefinit sau
definit de utilizator). OR se foloseste cand mai multe exceptii au aceleasi
instructiuni de tratare.
§
instructiunix reprezinta secventa de instructiuni PL/SQL sau cereri
SQL care se executa la aparitia exceptiilor respective
§
instructiuni_others reprezinta secventa de instructiuni PL/SQL sau cereri
SQL care se executa la aparitia unei exceptii netratate de clauzele WHEN
anterioare.
De
retinut:
§
WHEN OTHERS, daca exista,
este intotdeauna ultima.
§
Dupa tratatea exceptiei
aparute se iese din blocul curent. In consecinta este inutila prezenta
aceleiasi exceptii pe mai mult de un WHEN.
Exceptiile sunt de trei
feluri:
§
Exceptii Oracle
predefinite.
§
Exceptii Oracle care nu
sunt predefinite
§
Exceptii definite de
utilizator
Exceptiile predefinite au
deja un identificator asociat. Ele sunt ridicate automat la aparitie de catre
serverul Oracle sau uneltele Oracle.
Unele dintre exceptiile
predefinite ridicate de serverul Oracle:
Numele predefinit al
exceptiei |
Cand este ridicata automat
de serverul Oracle |
ACCESS_INTO_NULL |
Asignarea unei valori unui atribut al unui obiect neinitializat |
CASE_NOT_FOUND (*) |
Instructiune CASE fara ELSE pentru care nici una dintre etichete nu este egala cu valoarea cautata |
COLLECTION_IS_NULL |
Aplicarea unei alte metode decat EXISTS unui TABLE sau VARRAY neinitializat |
CURSOR_ALLREADY_OPEN |
Incercare de deschidere a unui cursor deja deschis |
DUP_VAL_ON_INDEX |
Inserarea unei valori duplicat pe o coloana unde acest lucru nu este permis (PRIMARY KEY, UNIQUE, etc) |
INVALID_CURSOR |
Operatie cursor invalida |
INVALID_NUMBER |
Eroare conversie de la sir de caractere la numar |
LOGIN_DENIED |
Autentificare Oracle cu un username/parola invalide |
NO_DATA_FOUND |
Cererea SELECT care ar fi trebuit sa intoarca o singura linie nu intoarce nici o linie |
NOT_LOGGED_ON |
Programul PL/SQL executa un acces la server fara sa fie realizata in prealabil conexiunea cu acesta |
PROGRAM_ERROR |
Eroare interna PL/SQL |
ROWTYPE_MISMATCH |
Diferenta de tip intre doua cursoare (unul gazda, altul PL/SQL) implicate intr-o asignare |
SELF_IS_NULL (*) |
Programul apeleaza o metoda MEMBER pe o instanta nula. In acest caz, parametrul implicit SELF - primul transmis unei metode MEMBER - e nul |
STORAGE_ERROR |
Depasire memorie pentru PL/SQL sau memoria este corupta |
SUBSCRIPT_BEYOND_COUNT |
Referinta la o tabele imbricata sau un obiect de tip VARRAY printr-un index mai mare decat numarul de elemente al colectiei |
SUBSCRIPT_BEYOND_LIMIT |
Referinta la o tabele imbricata sau un obiect de tip VARRAY printr-un index ilegal (de exemplu -1) |
SYS_INVALID_ROWID (*) |
Conversia unui sir de caractere la tipul UROWID a intors eroare deoarece sirul nu este un UROWID valid. |
TIMEOUT_ON_RESOURCE |
Time-out la incercarea de accesare a unei resurse de catre Oracle |
TOO_MANY_ROWS |
Cererea SELECT care ar fi trebuit sa intoarca o singura linie intoarce mai multe linii |
VALUE_ERROR |
Erori aritmetice, de conversie, de trunchiere sau de violare a unei constrangeri privind o valoare |
ZERO_DIVIDE |
Impartire la 0 |
(*) = Oracle9. Restul se
gasesc si in Oracle8.
Exemplu:
DECLARE
v_ename varchar2(10);
BEGIN
SELECT ENAME
INTO v_ename
FROM EMP
WHERE DEPTNO>20;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('Nu exista astfel de date');
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_LINE('Mai multe departamente');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('A aparut o alta exceptie');
Exceptii predefinite §
NU se declara in
zona DECLARE §
NU trebuiesc
ridicate explicit prin RAISE §
Se trateaza in zona
EXCEPTION
Reprezinta alte erori
returnate de Oracle, in afara celor predefinite. Aceste erori se pot trata in
doua moduri:
§
Prin folosirea lui WHEN
OTHERS
§
Prin declararea
exceptiei (i se da un nume) si asocierea unui cod de eroare Oracle in zona
DECLARE si tratarea ei in zona EXCEPTION.
In cazul celui de-al doilea
mod de tratare sablocul de tratare este urmatorul:
DECLARE
nume_exceptie EXCEPTION;
PRAGMA EXCEPTION_INIT(nume_exceptie, cod_eroare_Oracle);
BEGIN
. . . .
EXCEPTION
WHEN nume_exceptie THEN
instructiuni
. . . .
END;
Exemplu de tratare a erorii
cu cod -60 (detectare deadlock):
DECLARE
eroare_deadlock EXCEPTION;
PRAGMA EXCEPTION_INIT(eroare_deadlock, -60);
BEGIN
. . .
EXCEPTION
WHEN eroare_deadlock THEN
DBMS_OUTPUT.PUT_LINE('S-a
detectat deadlock');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('A
aparut o alta exceptie');
Exceptii Oracle non-predefinite §
Se declara in zona
DECLARE si se asociaza un cod de eroare §
NU trebuiesc
ridicate explicit prin RAISE §
Se trateaza in zona
EXCEPTION
Un program PL/SQL poate
contine exceptii definite de utilizator. In cazul cand programul detecteaza o
situatie anormala el poate trece in zona de exceptii prin instructiunea PL/SQL
RAISE nume_exceptie
Astfel de exceptii trebuiesc:
§
Declarate explicit
§
Ridicate explicit
Exemplu:
DECLARE
stoc_la_limita EXCEPTION;
v_stoc NUMBER;
BEGIN
SELECT CANTITATE
INTO v_stoc
FROM PRODUSE
WHERE CODPRODUS=2001;
IF (v_stoc < 5) THEN
RAISE
stoc_la_limita;
END IF;
EXCEPTION
WHEN stoc_la_limita THEN
DBMS_OUTPUT.PUT_LINE('Stocul este sub 5 buc.');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('A aparut o alta exceptie');
Exceptii definite de utilizator §
Se declara in zona
DECLARE (ca nume) §
Se ridica explicit
cu RAISE §
Se trateaza in zona
EXCEPTION
In
cazul in care dorim ca in zona EXCEPTION .. WHEN OTHERS sa identificam ce
eroare a aparut putem folosi doua functii care intorc codul numeric respectiv
mesajul text al erorii respective. In functie de valoarea lor putem sa tratam
diferentiat diferite erori netratate anterior in blocul respectiv.
Cele
doua functii sunt:
§
SQLCODE - intoarce codul
numeric al exceptiei (erorii returnate)
§
SQLERRM - intoaarce
mesajul text asociat cu acea exceptie
Codul de eroare returnat
poate fi:
Valoare SQLCODE |
Descriere |
0 |
Nu a aparut nici o exceptie |
1 |
A aparut o exceptie
definita de utilizator |
100 |
A aparut exceptia
NO_DATA_FOUND |
Numar negativ |
Cod eroare returnat de
serverul Oracle |
Exemplu:
DECLARE
V_cod NUMBER;
v_text VARCHAR2(255);
BEGIN
. . .
. .
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
v_cod := SQLCODE;
v_text := SQLERRM;
INSERT INTO ERORI VALUES(v_cod, v_text);
O
exceptie definita intr-un bloc este locala blocului respectiv si globala pentru
toate subblocurile acestuia (daca nu este redefinita), dar nu este cunoscuta in
afara blocului in care e declarata.
In
cazul in care un bloc trateaza o exceptie aparuta in cadrul sau el se termina
fara eroare si procesarea continua cu instructiunile care urmeaza END-ul
blocului respectiv.
In
cazul in care insa un bloc nu trateaza o anumita exceptie in propria sa zona
EXCEPTION aceasta se poate propaga la blocurile inconjuratoare pana gaseste o
rutina de tratare adecvata.
Exemplu: Acelasi nume de
exceptie este definita si in bloc si in subbloc. Ele sunt atunci considerate
exceptii diferite: prima a blocului fara subbloc iar a doua doar a subblocului.
In cazul de mai jos exceptia ridicata in subbloc nu este tratata de blocul
inconjurator.
DECLARE
exceptia1
EXCEPTION;
BEGIN
DECLARE
-- incepe un subbloc
exceptia1 EXCEPTION; -- redeclarare in subbloc
BEGIN
...
IF
... THEN
RAISE exceptia1; -- nu e tratata de bloc
-- fiind redefinita in subbloc
END
IF;
END;
-- sfarsit subbloc
EXCEPTION
WHEN exceptia1 THEN
-- nu trateaza exceptia din subbloc
...
END;
Pentru
a trata exceptia aparuta in subbloc se poate proceda in mai multe moduri:
§
Se elimina declaratia
exceptiei din subbloc in care caz ea va fi tratata de clauza WHEN existenta in
bloc
§
Se adauga o zona
EXCEPTIONS in subbloc in care sa se trateze aceasta exceptie, tratare care
poate fi diferita de cea existenta in bloc.
§
Fie se adauga WHEN
OTHERS la exceptiile tratate de bloc in care caz toate exceptiile netratate in
subbloc si bloc vor putea fi tratate aici.
Erorile
ad-hoc sunt similare erorilor Oracle non-predefinite dar trebuiesc ridicate
explicit in zona executabila sau in zona de exceptii folosind:
RAISE_APPLICATION_ERROR(cod_eroare,
text_eroare)
unde:
§
cod_eroare este un numar ales de utilizator intre -20000 si
-20999
§
text_eroare este un text ales de utilizator
Si
la aceste exceptii se poate folosi PRAGMA EXCEPTION_INIT pentru a le asigna un
nume.
Exemplu:
DECLARE
v_comm NUMBER;
comm_null EXCEPTION;
PRAGMA EXCEPTION_INIT(comm_null, -20021);
BEGIN
SELECT comm
INTO v_comm
FROM EMP
WHERE EMPNO=1098;
IF v_comm IS NULL THEN
RAISE_APPLICATION_ERROR(-20021,
'Nu are comision');
END IF;
EXCEPTION
WHEN comm_null THEN
-- tratare exceptie
In
cazul in care nu are asociat un nume, exceptia se poate trata in zona WHEN
OTHERS folosind SQLCODE:
DECLARE
v_comm NUMBER;
v_coderr NUMBER;
BEGIN
SELECT comm INTO v_comm FROM EMP
WHERE EMPNO=1098;
IF v_comm IS NULL THEN
RAISE_APPLICATION_ERROR(-20021, 'Nu are
comision')
END
IF;
EXCEPTION
WHEN OTHERS THEN
v_coderr := SQLCODE;
IF (v_coderr = -20021)
THEN
-- tratare exceptie
END IF;
In
cazul in care se doreste tratarea unei exceptii aparute intr-un subbloc atat in
acesta cat si in blocul inconjurator, se procedeaza in modul urmator:
§
Se prevede tratarea
exceptiei respective in zona EXCEPTIONS a subblocului.
§
La sfarsitul secventei
de instructiuni care trateaza exceptia se plaseaza instructiunea RAISE fara
parametri. Ea ridica exceptia curenta pentru blocul inconjurator.
§
Se prevede tratarea
exceptiei respective in zona EXCEPTIONS a blocului.
Exemplu:
DECLARE
exceptia1
EXCEPTION;
BEGIN
DECLARE
-- incepe un subbloc
BEGIN
...
IF ... THEN
RAISE exceptia1; -- nu e tratata de bloc
-- fiind redefinita in subbloc
END IF;
EXCEPTION --
tratare exceptii subbloc
WHEN exceptia1 THEN
-- instructiuni tratare pentru subbloc
.
. . .
RAISE;
END;
-- sfarsit subbloc
EXCEPTION
WHEN exceptia1 THEN
-- tratare exceptia1 si in bloc
...
END;
Nota: RAISE fara parametri poate apare doar in zona de
tratare exceptii.
A. Exceptii aparute in declaratii
Exceptiile
care apar in zona DECLARE determina terminarea executiei blocului curent si nu
pot fi tratate in zona EXCEPTIONS a acestuia. In schimb ele pot fi tratate in
blocul inconjurator (daca exista).
Un
exemplu de astfel de exceptie este initializarea necorespunzatoare a unei
variabile (ca mai jos unde intr-o constanta de 3 cifre se incearca stocarea
unui numar de 4 cifre)
sal_minim CONSTANT
NUMBER(3) := 1200;
B. Exceptii ridicare in zona de tratare a exceptiilor
In
zona de tratare a exceptiilor se poate ridica aceeasi exceptie (cum a fost
descris in paragraful 4.9) sau o alta exceptie.
In
toate cazurile exceptia respectiva poate fi tratata doar in blocul inconjurator
si nu in blocul curent
C. GOTO
Instructiunea
:
GOTO
<<eticheta>>
nu
poate fi folosita pentru a se trece din zona de tratare a exceptiilor in zona
executabila a blocului respectiv si nici invers.
Exemplu
de GOTO ilegal:
DECLARE
. . .
BEGIN
. . .
<<o_eticheta>>
SELECT
. .
.;
EXCEPTION
WHEN
. . . THEN
. . .
GOTO o_eticheta; -- GOTO ilegal
END;
Ea
poate fi folosita insa pentru salt din zona de tratare a exceptiilor in zona
executabila a unui bloc inconjurator.
D. Executia tuturor instructiunilor blocului
In
cazul in care apare o exceptie, restul instructiunilor blocului nu se mai
executa. Daca se doreste executia acestora si in cazul aparitiei unei exceptii,
instructiunile care cauzeaza astfel de eveniment trebuiesc transformate in
subblocuri.
Sa
consideram urmatorul bloc:
DECLARE
. . .
BEGIN
INSERT
. . . ;
UPDATE
. . . ;
SELECT
. . . ;
EXCEPTION
.
. .
END;
In
cazul aparitiei unei exceptii la INSERT urmatoarele doua cereri SQL nu se mai
executa. La fel, in cazul aparitiei unei exceptii la UPDATE nu se mai executa
SELECT-ul.
Solutia
este urmatoarea:
DECLARE
. . .
BEGIN
BEGIN -- subbloc pentru INSERT
INSERT
. . . ;
EXCEPTION -- exceptii
subbloc INSERT
WHEN OTHERS THEN
. . .
END; -- end subbloc INSERT
BEGIN -- subbloc pentru UPDATE
UPDATE
. . . ;
EXCEPTION -- exceptii
subbloc UPDATE
WHEN OTHERS THEN
. . .
END; -- end subbloc UPDATE
SELECT . . . ;
EXCEPTION --
exceptii pentru bloc
.
. .
END; -- end bloc
Nota: Subblocurile trebuie sa trateze exceptiile aparute,
in caz contrar exceptia se transfera automat blocului si instructiunile
urmatoare nu se mai executa.