4.    Exceptii

 

4.1.          Ce este o exceptie

 

§        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;

END;

 

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).

 

 

4.2.         Tratarea exceptiilor

 

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

 

 

4.3.         Exceptii Oracle predefinite

 

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');

END;

 

 

Exceptii predefinite

§        NU se declara in zona DECLARE

§        NU trebuiesc ridicate explicit prin RAISE

§        Se trateaza in zona EXCEPTION

 

 
 

 

 

 

 

 

 

 


4.4.         Exceptii Oracle non-predefinite

 

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');

END;

 

 

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

 

 
 

 

 

 

 

 

 

 

 


4.5.         Exceptii definite de utilizator

 

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');

END;

 

 

 

Exceptii definite de utilizator

§        Se declara in zona DECLARE (ca nume)

§        Se ridica explicit cu RAISE

§        Se trateaza in zona EXCEPTION

 

 
 

 

 

 

 

 

 

 


4.6.         Identificarea unei exceptii

 

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);

END;

 

 

 

4.7.         Domeniul de valabilitate pentru exceptii

 

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.

 

 

 

4.8.         Definirea unor erori ad-hoc

 

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

END;

 

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;

END;

 

4.9.         Retratarea exceptiilor

 

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.

 

 

4.10.     Alte consideratii privind exceptiile

 

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.