CAPITOLUL 21

INTERACTIUNEA CU ORACLE

In acest unit se va trata modalitatea de access la baza de date si de controlarea tranzactiilor, prin instructiuni SQL in PL/SQL. De asemenea veti vedea cum erorile cauzate de SQL pot fi tratate de catre rutinele de tratare a exceptiilor.

Identificatori exceptie

Daca orice tip de exceptie este generat controlul este trecut sectiunii EXCEPTION a blocului in care exceptia apare. Daca exceptia nu e minuita aici sau daca nu exista nici o sectiune EXCEPTION atunci blocul se termina cu o exceptie `Unhandled` care poate avea repercursiuni in mediul de inchidere.


EXEMPLU:

        BEGIN

        INSERT INTO dept (deptno, dname) VALUES (50, `CLEANING`);

        INSERT INTO dept (deptno, dname) VALUES (50, `TRAINING`);

        -- Exception DUP_VAL_ON_INDEX raised here



        ...



        END;

          -- Block terminates which Unhandled Exception - ORA -00001

Pentru a prinde asemenea evenimente si a preveni propagarea exceptiilor catre mediile de inchidere sau blocuri se poate defini identificatorul exceptie in sectiunea EXCEPTION.


Sintaxa:

        WHEN exception-identifier THEN actions;

unde `actions` poate fi una sau mai multe declaratii PL/SQL sau SQL fiecare terminata cu ':'. Actiunile unui identificator exceptie sint delimitate fie de sfirsitul blocului (END) fie de inceputul unui alt identificator exceptie (WHEN).

Principalele exceptii care pot apare ca un rezultat al declaratiei SELECT sint NO_DATA_FOUND (fara linii returnate) si TOO_MANY_ROWS (mai mult decit o linie returnata).


Exemplu:

        DECLARE

        v_ename emp.ename%TYPE;

        v_job emp.job%TYPE;

        BEGIN

        SELECT ename, job

        INTO v_ename, v_job

        FROM emp

        WHERE hiredate BETWEEN '1-JAN-92' AND '31-DEC-92';

         ...

        EXCEPTION

        WHEN no_data_found THEN

                           INSERT INTO error_tab

                           VALUES ('Nobody in 92');

        WHEN too_many_rows THEN

                           INSERT INTO error_tab

                           VALUES ('More then one person in 92');

        END;

Notati ca, citiva identificatori exceptie pot fi definiti pentru bloc fiecare cu setul propriu de actiuni. Totusi cind o exceptie apare numai un identificator va fi procesat inainte de a parasi blocul.

Identificatorii exceptie 'WHEN OTHERS'

Desi sectiunea EXCEPTION din exemplul de sus va prinde cele doua exceptii specificate alte tipuri de exceptie se vor strecura in retea mai bine decit definirea unui identificator separat pentru fiecare tip de exceptie se poate folosi identificatorul exceptie 'WHEN OTHERS' care minuieste toate erorile neminuite inca in bloc. Daca e folosit 'WHWN OTHERS' trebuie sa fie codat dupa orice alt identificator exceptie din bloc.


Exemplu:

        BEGIN

        SAVEPOINT  so_far_so_good;

        INSERT INTO statistics_tab VALUES (18, 25,91);

        EXCEPTION

        WHEN dup_val_on_index THEN

                                  INSERT INTO error_tab VALUES (`Error

during block`);

END;

Functii pentru prinderea erorii

Cind o exceptie a aparut, veti dori evaluarea codului erorii asociate sau mesajul eroare.Aceasta poate fi important in minuirea exceptiilor folosind `WHEN OTHERS`, pina cind veti dori a decide ce actiune sa fie dupa eroare. PL/SQL furnizeaza 2 functii in acest scop :

SQLCODE
intoarce nr. erorii asociata cu exceptia care a aparut (in cazul lui NO_DATA_FOUND acesta va fi codul ANSI +100).Daca e folosit un singur identificator EXCEPTION, functia va intoarce 0.
SQLERRM
intoarce mesajul complet de eroare asociat cu exceptia (incluzind nr. erorii)

SQLCODE intoarce date numerice, si poate fi asignat unei variabile NUMBER cu precizie implicita.SQLERRM intoarce date caracter.Este recomandat a trunchia valoarea la o lungime cnoscuta inainte de a incerca sa o scriem intro variabila.


Exemplu:

DECLARE

 error-message CHAR (100);

 error-code  NUMBER;

BEGIN

...

EXCEPTION

WHEN OTHERS  THEN

              error_message :=SUBSTR(SQLERM,1,100);

              error_code :=SQLCODE;

              INSERT INTO errors VALUES (error_message, error_code);

              END;

SQLERRM are un parametru optional care accepta numarul erorii a carui mesaj e returnat.

NOTA: SQLERRM si SQLCODE nu pot fi folosite direct ca parti ale unei declaratii INSERT; valorile lor trebuie sa fie trecute prima data unei variabile.

Cum se propaga exceptiile?

Cind un subbloc minuieste o exceptie se termina normal si controlul va fi rezumat in blocul e inchidere imediat dupa END-ul subblocului.

Totusi cind o exceptie este generata si blocul curent nu are un identificator pentru el exceptia se propaga. Aceasta inseamna ca exceptia se reproduce ea insasi in blocuri de inchidere succesive pina cind un identificator e gasit. Daca nici unul dintre blocuri nu minuieste exceptia atunci o exceptie Unhandled este produsa in mediul gazda.

Aceasta saritura intre sectiunile EXCEPTION de la blocurile interne catre cele externe produce actiunile executabile ramase in aceste blocuri sa fie trecute (actiunile intre END-ul unui subbloc si keyword-ul EXCEPTION al blocului de inchidere nu sint procesate).

EXEMPLU:

        BEGIN

        ...

        BEGIN

        ...

        --Exception X raised at this point

        EXCEPTION

        --No handler that deals whith exception X

        END;

        ...

        EXCEPTION

        --Handler for X present here (i.e. The Buck stops here!)

        END;

Un avantaj al acestei comportari este ca anumite declaratii care cer minuirea propriilor erori poate fi inclus in propriul bloc lasind minuirea exceptiilor generale blocurilor de inchidere.


EXEMPLU:

        DECLARE

          e_mess CHAR(80)

        BEGIN

                DECLARE

                  v1 NUMBER(4);

                BEGIN

                  SELECT empno INTO v1 FROM emp WHERE job = 'PRESIDENT';

                EXCEPTION

                  WHEN too_many_rows THEN

                        INSERT INTO job_errors VALUES ('More than one

                                        President!');

                END;



                DECLARE

                  v1 NUMBER (4);

                BEGIN

                  SELECT empno INTO v1 FROM emp WHERE job = 'MANAGER';

                EXCEPTION

                  WHEN too_many_rows THEN

                        INSERT INTO job_errors VALUES ('More than one

                                                Manager');

                END;

                EXCEPTION

                        WHEN OTHERS THEN

                                e_mess := SUBSTR(SQLERRM, 1, 80);

                                INSERT INTO general VALUES (e_mess);

                END;

Exercitii

  • Alterati blocul dvs. produs la Cap. 20 Ex. 2.Redefiniti variabila PL/SQL ca NUMBER(1).Ce se intimpla daca 2 valori de intrare are valorile 4 si 2?

    Adaugati un Identificator Exceptie la blocul care inregistreaza un mesaj explicit in Messages pentru orice tip de exceptie care poate apare. Apoi lansati blocul din nou.

  • Scrieti un script PL/SQL care primeste la rulare un singur parammetru, unde este furnizt un tip de slujba.
    
    Ex.   @UNI3_FILE MANAGER
    
    

    Blocul PL/SQL din fisier trebuie sa SELECT-ezr rinduri din tabela EMP care are tipul job-ului in coloana JOB. (Va referiti la parametru ca '&1'). Trimiteti un mesaj in tabela MESSAGES, in functie de faptul ca se returneaza unul,niciunul sau mai multe rinduri.

    Ex.
    
    'Jobtype found once'
    
    'Jobtype not found'
    
    'Jobtype found more than once'
    
    
    Inregistrati Jobtype si in tabela MESSAGES, s executati tranzactia, indiferent de mesajul produs.

    Solutii.

  • O exceptie VALUE_ERROR este lansata daca valoarea este prea mare pt. variabila.
    
          DECLARE
    
                  V_RESULT NU MBER(9.2) ;
    
                  V_MESSAGE CHAR(60);
    
          BEGIN
    
                V_RESULT := &main_var ** &exponent ;
    
                -- or POWER (&main_var, &exponent);
    
                INSERT INTO messages (numcol1) VALUES (V_RESULT);
    
          EXCEPTION
    
                    WHEN OTHERS THEN V_MESSAGE := SUBSTR(SQLERRM,1,60);
    
                                     INSERT INTO messages (charcol1);
    
                                     VALUES (V_MESSAGE);
    
          END;
    
    
    
    
  • 
          DECLARE
    
                  JOBTYPE emp.job%TYPE;
    
          BEGIN
    
                SELECT job INTO JOBTYPE
    
                FROM emp WHERE job = '&1' ;
    
                INSERT INTO messages (charcol1, charcol2)
    
                     VALUES ('&1', 'Jobtype found once');
    
                COMMIT;
    
          EXCEPTION
    
                WHEN no_data_found THEN
    
                     INSERT INTO messages (charcol1, charcol2)
    
                     VALUES ('&1', 'Jobtype not found');
    
                COMMIT;
    
                WHEN too_many_rows THEN
    
                     INSERT INTO messages (charcol1, charcol2)
    
                     VALUES ('&1', 'Jobtype found more than once');
    
                COMMIT;
    
                END;