I want to create a function which returns BOOLEAN.
The function would check if a value exists (classic verification if a department already exists).
I implemented the NO_DATA_FOUND exception and I want, also, to add the WHEN OTHERS; but I don't have in mind any test case.
Here is the code of the function:
FUNCTION fnc_department_exists(p_dept_name hr.departments.department_name%TYPE) RETURN BOOLEAN IS
v_dummy PLS_INTEGER;
BEGIN
-- Verifica existenta unui anumit departament
pkg_logging.prc_log('HR', 'VERIFIC', 'Verifica daca exista departamentul "' || p_dept_name || '"');
SELECT 1
INTO v_dummy
FROM hr.departments
WHERE UPPER(department_name) = UPPER(p_dept_name);
pkg_logging.prc_log('HR', 'VERIFIC', 'Departamentul "' || p_dept_name || '" exista');
RETURN TRUE;
EXCEPTION
WHEN NO_DATA_FOUND THEN
pkg_logging.prc_log('HR', 'VERIFIC', 'Departamentul "' || p_dept_name || '" nu exista');
RETURN FALSE;
WHEN OTHERS THEN
pkg_logging.prc_log('HR', 'VERIFIC', 'A aparut o alta eroare: ' || SQLCODE || ' - ' || SQLERRM);
RETURN FALSE;
END fnc_department_exists;
Can someone suggest a test case for WHEN OTHERS branch, please?
Thank you,
If you have a department named e.g. 'PAYROLL', add another one named 'Payroll' and another one named 'payroll'. This will cause the SELECT to fail with an ORA-01422 (TOO_MANY_ROWS) exception.
I suggest that when your code gets into the WHEN OTHERS block, you should re-raise the exception rather than eating it and returning false. Thus:
WHEN OTHERS THEN
pkg_logging.prc_log('HR', 'VERIFIC', 'A aparut o alta eroare: ' ||
SQLCODE || ' - ' || SQLERRM);
RAISE;
Best of luck.
Related
OK so I have a cursor for loop that I want to print an error message when the select statement doesn't find a course that the user has inputted. But the problem is that cursor for loops automatically exits when the select statement fails, so my else statement never executes. How do I print a message saying that they course they are looking for doesn't exist. Note switching to a cursor fetch is not an option. For example id the user enters a course that exists it prints all relevant information. When the user inputs a course with no prerequisite it prints a proper message, but if the user inputs a course that doesn't exists, nothing gets printed.
DECLARE
course_name VARCHAR2(40) := '&course_input';
TYPE course_r IS RECORD(
course_description course.description%TYPE,
cost course.cost%TYPE,
prerequisite course.prerequisite%TYPE,
prerequisite_cost course.cost%TYPE
);
course_rec course_r;
CURSOR course_cursor IS
SELECT a.description, a.cost, a.prerequisite, b.cost AS preq_cost
FROM COURSE a
LEFT JOIN COURSE b ON a.prerequisite = b.course_no
WHERE UPPER(a.description) LIKE '%'||'&course_input'||'%';
BEGIN
FOR record IN course_cursor
LOOP
course_rec.course_description := record.description;
course_rec.cost := record.cost;
course_rec.prerequisite := record.prerequisite;
course_rec.prerequisite_cost := record.preq_cost;
IF course_rec.prerequisite IS NULL THEN
DBMS_OUTPUT.PUT_LINE('There is NO prerequisite course for any that starts on ' || course_name || '. Try again');
ELSIF course_rec.prerequisite IS NOT NULL THEN
DBMS_OUTPUT.PUT_LINE('Course: ' || course_rec.course_description);
DBMS_OUTPUT.PUT_LINE('Cost: ' || course_rec.cost);
DBMS_OUTPUT.PUT_LINE('Prerequisite: ' || course_rec.prerequisite);
DBMS_OUTPUT.PUT_LINE('Prerequisite Cost: ' || course_rec.prerequisite_cost);
DBMS_OUTPUT.PUT_LINE('=================================================');
ELSE
DBMS_OUTPUT.PUT_LINE('There is NO VALID course that starts on '||course_name||'. Try again.');
END IF;
END LOOP;
END;
/
You can do this by having a variable which only gets set inside the loop. Then you can check that variable after the loop has completed to see if it was set, and decide if you need to do additional work.
Something like:
DECLARE
course_name VARCHAR2(40) := '&course_input';
v_rows_present BOOLEAN := FALSE;
BEGIN
FOR course_rec IN (SELECT a.description,
a.cost,
a.prerequisite,
b.cost AS preq_cost
FROM course a
LEFT JOIN course b
ON a.prerequisite = b.course_no
WHERE upper(a.description) LIKE '%' || course_name || '%')
LOOP
v_rows_present := TRUE;
IF course_rec.prerequisite IS NULL
THEN
dbms_output.put_line('There is NO prerequisite course for any that starts on ' || course_name || '. Try again');
ELSE
dbms_output.put_line('Course: ' || course_rec.course_description);
dbms_output.put_line('Cost: ' || course_rec.cost);
dbms_output.put_line('Prerequisite: ' || course_rec.prerequisite);
dbms_output.put_line('Prerequisite Cost: ' || course_rec.prerequisite_cost);
dbms_output.put_line('=================================================');
END IF;
END LOOP;
IF NOT v_rows_present
THEN
dbms_output.put_line('There is NO VALID course that starts on ' || course_name || '. Try again.');
END IF;
END;
/
N.B. I've updated your code as you appear to have misapprehended how to use a cursor for loop.
Cursor-for-loops create their own record variable implicitly, so you don't need to declare one yourself.
You also don't need to declare a cursor explicitly either - that can be done as part of the cursor-for-loop statement.
You don't need to populate a new record with the same values from the cursor-for-loop record in order to use the values (as long as you're using them within the cursor-for-loop, of course!)
You could declare a counter, as say, PLS_INTEGER, initialize it to 0, then increment it inside the loop. After the loop, you can check the value and if it's 0, you know no rows were returned.
THE CURSOR FOR LOOP doesn't execute if there are no such courses in the courses table.So, check if a row exists before entering the loop.
Two other things to note:
LIKE '%'||'&course_input'||'%' is not required in the where clause
as the same variable is already passed from user input and assigned
in the declare section.Simply use LIKE '%' || course_name || '%'
RECORD is a PL/SQL reserved word and shouldn't be used as a loop
index variable, i've changed it to rec.
DECLARE
course_name VARCHAR2(40) := '&course_input';
TYPE course_r IS RECORD ( course_description course.description%TYPE,
cost course.cost%TYPE,
prerequisite course.prerequisite%TYPE,
prerequisite_cost course.cost%TYPE );
course_rec course_r;
cur_count NUMBER;
CURSOR course_cursor IS SELECT a.description,
a.cost,
a.prerequisite,
b.cost AS preq_cost
FROM course a
LEFT JOIN course b ON a.prerequisite = b.course_no
WHERE upper(a.description) LIKE '%' || course_name || '%';
BEGIN
SELECT COUNT(*)
INTO cur_count
FROM course a
WHERE upper(a.description) LIKE '%' || course_name || '%';
IF
cur_count > 0
THEN
FOR rec IN course_cursor LOOP
course_rec.course_description := rec.description;
course_rec.cost := rec.cost;
course_rec.prerequisite := rec.prerequisite;
course_rec.prerequisite_cost := rec.preq_cost;
IF
course_rec.prerequisite IS NULL
THEN
dbms_output.put_line('There is NO prerequisite course for any that starts on ' ||
course_name || '. Try again');
ELSE
dbms_output.put_line('Course: ' || course_rec.course_description);
dbms_output.put_line('Cost: ' || course_rec.cost);
dbms_output.put_line('Prerequisite: ' || course_rec.prerequisite);
dbms_output.put_line('Prerequisite Cost: ' || course_rec.prerequisite_cost);
dbms_output.put_line('=================================================');
END IF;
END LOOP;
ELSE
dbms_output.put_line('There is NO VALID course that starts on ' || course_name || '. Try again.'
);
END IF;
END;
/
I'm trying to compile a function in pl/sql in the 'body' section of a package. The intention is to return a number value either when successfully updating a table column or return the number associated with the error:
FUNCTION chkMissingData RETURN NUMBER
IS
BEGIN
UPDATE ERRORS
SET ERRORDESC =
CASE
WHEN ERRVAL IS NULL THEN 'No data'
ELSE 'OK'
RETURN 0; -- ORA-00905 missing keyword
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
dbms_output.put_line('INSERT: Constraint violated ' || ERR_NUM || ', Msg:' || ERR_MSG);
RETURN ERR_NUM;
WHEN OTHERS THEN
dbms_output.put_line('Error ' || ERR_NUM || ', Msg:' || ERR_MSG);
RETURN ERR_NUM;
END chkMissingData;
I've checked other answers to a similar issue but I don't see what I'm missing syntactically.
OK I forgot to place an 'end;' after the CASE statement!
You should use an IDE that includes an automatic formatter, such as SQL Developer. Then you can format and identify common syntax issues like that, before loading the example into StackOverflow.
You can also use livesql.oracle.com to test out your code, make sure it at least compiles, before posting here.
SQL Developer and LiveSQL are entirely free.
As mentioned above, the corrected snippet is below..
CREATE OR REPLACE
FUNCTION chkMissingData
RETURN NUMBER
IS
BEGIN
UPDATE ERRORS
SET ERRORDESC =
CASE
WHEN ERRVAL IS NULL
THEN 'No data'
ELSE 'OK'
END;
RETURN 0; -- ORA-00905 missing keyword
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
dbms_output.put_line('INSERT: Constraint violated ' || ERR_NUM || ', Msg:' || ERR_MSG);
RETURN ERR_NUM;
WHEN OTHERS THEN
dbms_output.put_line('Error ' || ERR_NUM || ', Msg:' || ERR_MSG);
RETURN ERR_NUM;
END chkMissingData;
err_num and err_msg are not declared variables.
I think you mean SQLCODE and SQLERRM
(And I'm guessing that ERRVAL is a column of your table ERRORS)
I have many procedures that do the same thing:
they refresh materialized view and check if the count is not 0, then push that data into production tables. this is the skeleton of what each one does, the only thing that changes is the name of the materialized view. I thought about creating one function that will take in the name of the MV and process it, but it is not working :(
create or replace
function REFRESH_MV (mv_to_refresh IN VARCHAR2)
RETURN VARCHAR2
AUTHID CURRENT_USER
AS
COUNTS INT;
begin
DBMS_MVIEW.REFRESH(mv_to_refresh,'C');
COMMIT;
SELECT COUNT(*) INTO COUNTS FROM 'SEMANTIC.' || mv_to_refresh;
IF COUNTS = 0 THEN
RETURN 'SEMANTIC.' || mv_to_refresh || ' is empty';
ELSE
'SEMANTIC_READ_ONLY.' || RELOAD_TABLE(mv_to_refresh);
RETURN 'SEMANTIC_READ_ONLY.' || mv_to_refresh || ' has been refreshed today';
END IF;
EXCEPTION WHEN OTHERS THEN NULL;
end;
You have to use EXECUTE IMMEDIATE or DBMS_SQL to do that; the first one should be easier to use in your case.
EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM 'SEMANTIC.' || mv_to_refresh INTO COUNTS;
should do the trick.
You should use dynamic SQL for this purpose:
CREATE OR REPLACE FUNCTION REFRESH_MV (mv_to_refresh IN VARCHAR2)
RETURN VARCHAR2
AUTHID CURRENT_USER
AS
COUNTS INT;
VSQL VARCHAR2(100);
begin
DBMS_MVIEW.REFRESH('SEMANTIC.' || mv_to_refresh, 'C');
COMMIT;
VSQL := 'SELECT COUNT(1) FROM SEMANTIC.' || mv_to_refresh;
EXECUTE IMMEDIATE VSQL INTO COUNTS;
IF COUNTS = 0 THEN
RETURN 'SEMANTIC.' || mv_to_refresh || ' is empty';
ELSE
SEMANTIC_READ_ONLY.RELOAD_TABLE(mv_to_refresh);
RETURN 'SEMANTIC_READ_ONLY.' || mv_to_refresh
|| ' has been refreshed today';
END IF;
EXCEPTION
WHEN OTHERS THEN
RETURN 'Error has occured: ' || SQLERRM;
END;
Please make sure you pass view name without schema prefix as input parameter.
You should also note that scince it function it should return value or raise exception. But in you example function will return nothing in case of exception.
I didn't quite get the semantics of RELOAD_TABLE() procedure. In example given it is supposed to be a some procedure in SEMANTIC_READ_ONLY schema. In case you really need the appropriate function to be evaluated dynamically, you again can use dynamic SQL to construct the valid string contaning the code and call it:
vsql := 'begin SCHEMA_NAME.' || GET_PROCEDURE_FOR(mv_to_refresh) || '; end;';
execute immediate vsql;
I'm working on a school work and I have an error in my code.
I have created a PLSQL package:
create or replace
PACKAGE GestionProjets AS
TYPE Signaletique IS TABLE OF EMPLOYES%ROWTYPE INDEX BY BINARY_INTEGER;
TYPE TableNomDep IS TABLE OF VARCHAR2(40);
PROCEDURE AjouterProjets(ProjetRecord IN PROJETS%ROWTYPE);
PROCEDURE SupprimerProjet(DelNumPro IN PROJETS.numpro%TYPE);
--PROCEDURE ModifierProjet(newRecord IN PROJETS%ROWTYPE, oldRecord IN PROJETS%ROWTYPE);
FUNCTION ListerEmployes(dep1 IN DEPARTEMENTS.Nomdep%TYPE, dep2 IN DEPARTEMENTS.Nomdep%TYPE) RETURN Signaletique;
END GestionProjets;
That's the body of the function where I have an error:
FUNCTION ListerEmployes(dep1 IN DEPARTEMENTS.Nomdep%TYPE, dep2 IN DEPARTEMENTS.Nomdep%TYPE) RETURN Signaletique AS
nomDeps TableNomDep;
SigTable Signaletique;
EXWrongDep1 EXCEPTION;
EXWrongDep2 EXCEPTION;
BEGIN
SELECT Nomdep
BULK COLLECT INTO nomDeps
FROM DEPARTEMENTS;
-- test if dep 1 est un parametre valide
IF NOT nomDeps.exists(dep1) THEN
RAISE EXWrongDep1;
END IF;
-- test if dep 2 est un parametre valide
IF NOT nomDeps.exists(dep2) THEN
RAISE EXWrongDep2;
END IF;
EXCEPTION
WHEN EXWrongDep1 THEN
RAISE_APPLICATION_ERROR(-20008, 'MAUVAIS PARAMETRE: ' || dep1 || ' N EXISTE PAS!');
WHEN EXWrongDep2 THEN
RAISE_APPLICATION_ERROR(-20008, 'MAUVAIS PARAMETRE: ' || dep2 || ' N EXISTE PAS!');
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR (-20007, 'ERREUR INCONNU: ' || SQLCODE || ' - ' || SQLERRM);
END ListerEmployes;
When I try to execute the function, i get this error message:
DECLARE
tabel gestionprojets.Signaletique;
BEGIN
tabel := gestionprojets.listeremployes('sdsd','sdsd');
END;
ORA-20007: ERREUR INCONNU: -6502 - ORA-06502: PL/SQL: numeric or value error: character to number conversion error
ORA-06512: at "EDGE.GESTIONPROJETS", line 108
ORA-06512: at line 4
I don't understand why I get this error message. The type of the Nomdep column is VARCHAR2(40).
I suspect the problem is that the table type is indexed by BINARY_INTEGER. When the .EXISTS calls are executed they're passed either dep1 or dep2 as the index for the table, and thus PL/SQL tries to convert the VARCHAR2(40) parameter to a BINARY_INTEGER. Since these input parameters are both non-numeric character strings ('sdsd') the conversion fails.
This code might run faster if, instead of reading the entire DEPARTEMENTS table into memory each time ListerEmployes is called, it was rewritten as
FUNCTION ListerEmployes(dep1 IN DEPARTEMENTS.Nomdep%TYPE, dep2 IN DEPARTEMENTS.Nomdep%TYPE) RETURN Signaletique
AS
strNomdep DEPARTEMENTS.Nomdp%TYPE;
BEGIN
-- test if dep 1 est un parametre valide
BEGIN
SELECT NOMDEP
INTO strNomdep
FROM DEPARTEMENTS
WHERE NOMDEP = dep1;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20008, 'MAUVAIS PARAMETRE: ' || dep1 || ' N EXISTE PAS!');
END;
-- test if dep 2 est un parametre valide
BEGIN
SELECT NOMDEP
INTO strNomdep
FROM DEPARTEMENTS
WHERE NOMDEP = dep2;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20008, 'MAUVAIS PARAMETRE: ' || dep2 || ' N EXISTE PAS!');
END;
EXCEPTION
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR (-20007, 'ERREUR INCONNU: ' || SQLCODE || ' - ' || SQLERRM);
END ListerEmployes;
Share and enjoy.
I don't know if thats right but for some reason my stored procedure is not rolling back after an exception occurs. So my insert statement is commited even when i get an exception
Did i forgot something?
PROCEDURE SP_USUARIO_INSERT
(
pUSU_IDUSUARIO IN OUT ENG.USU_USUARIO.USU_IDUSUARIO%TYPE,
pUSU_CDUSUARIO IN ENG.USU_USUARIO.USU_CDUSUARIO%TYPE,
pPES_IDPESSOA IN ENG.USU_USUARIO.PES_IDPESSOA%TYPE,
pUSU_DLSENHA IN ENG.USU_USUARIO.USU_CDUSUARIO%TYPE,
pUSU_DLOBSERVACAO IN ENG.USU_USUARIO.USU_DLOBSERVACAO%TYPE,
pUSU_NUIP IN ENG.USU_USUARIO.USU_NUIP%TYPE,
pUSU_DTCADASTRO IN ENG.USU_USUARIO.USU_DTCADASTRO%TYPE,
pUSU_DTDESATIVACAO IN ENG.USU_USUARIO.USU_DTDESATIVACAO%TYPE,
pUSU_DTULTIMOACESSO IN ENG.USU_USUARIO.USU_DTULTIMOACESSO%TYPE,
pUSU_DLMAQUINA IN ENG.USU_USUARIO.USU_DLMAQUINA%TYPE,
pUSU_STNOVO IN ENG.USU_USUARIO.USU_STNOVO%TYPE,
pUSU_STATIVO IN ENG.USU_USUARIO.USU_STATIVO%TYPE
)
IS
sCreateUser Varchar(200);
bUsuarioExiste Number;
eUsuarioExiste Exception;
BEGIN
SELECT
COUNT(usu_cdusuario)
INTO bUsuarioExiste
FROM ENG.USU_USUARIO
WHERE USU_CDUSUARIO = pUSU_CDUSUARIO;
IF(bUsuarioExiste > 0) THEN
RAISE eUsuarioExiste;
END IF;
SELECT usu_seq.nextval INTO pUSU_IDUSUARIO FROM DUAL;
INSERT INTO ENG.USU_USUARIO
(
USU_IDUSUARIO,
USU_CDUSUARIO,
PES_IDPESSOA,
USU_DLOBSERVACAO,
USU_NUIP,
USU_DTCADASTRO,
USU_DTDESATIVACAO,
USU_DTULTIMOACESSO,
USU_DLMAQUINA,
USU_STNOVO,
USU_STATIVO
)
VALUES
(
pUSU_IDUSUARIO,
pUSU_CDUSUARIO,
pPES_IDPESSOA,
pUSU_DLOBSERVACAO,
pUSU_NUIP,
sysdate,
pUSU_DTDESATIVACAO,
pUSU_DTULTIMOACESSO,
pUSU_DLMAQUINA,
pUSU_STNOVO,
pUSU_STATIVO
) ;
sCreateUser := 'CREATE USER ' || pUSU_CDUSUARIO || ' IDENTIFIED BY ' || pUSU_DLSENHA;
EXECUTE IMMEDIATE sCreateUser;
EXECUTE IMMEDIATE 'GRANT ENG_GERAL TO ' || pUSU_CDUSUARIO;
COMMIT;
EXCEPTION
WHEN eUsuarioExiste THEN
RAISE_APPLICATION_ERROR (-20001, 'Usuário já existe ou possui nome inválido.');
ROLLBACK;
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR (-20001, SQLCODE || ': ' || SQLERRM);
ROLLBACK;
END SP_USUARIO_INSERT;
The "EXECUTE IMMEDIATE sCreateUser;" is implicitly committing your insert.
I suspect you are 'handling' (or more accurately, ignoring) your exception.
create table temp (id number);
DECLARE
v_str VARCHAR2(2);
BEGIN
INSERT INTO temp VALUES (1);
v_str := '123';
EXCEPTION
WHEN VALUE_ERROR THEN DBMS_OUTPUT.PUT_LINE('Whoops');
END;
/
select * from temp;
Will show the row because, as far as the SQL layer is concerned, the procedure completed successfully (as the exception was caught and ignored).
There can be other reasons such as
The insert happens before the procedure execution and so isn't rolled back when the statement fails (and you don't explicitly rollback the transaction)
The insert is committed by a explicit commit before the exception is raised.
I think if you restructure your code, you can get the behavior you want.
PROCEDURE SP_USUARIO_INSERT
(
pUSU_IDUSUARIO IN OUT ENG.USU_USUARIO.USU_IDUSUARIO%TYPE,
pUSU_CDUSUARIO IN ENG.USU_USUARIO.USU_CDUSUARIO%TYPE,
pPES_IDPESSOA IN ENG.USU_USUARIO.PES_IDPESSOA%TYPE,
pUSU_DLSENHA IN ENG.USU_USUARIO.USU_CDUSUARIO%TYPE,
pUSU_DLOBSERVACAO IN ENG.USU_USUARIO.USU_DLOBSERVACAO%TYPE,
pUSU_NUIP IN ENG.USU_USUARIO.USU_NUIP%TYPE,
pUSU_DTCADASTRO IN ENG.USU_USUARIO.USU_DTCADASTRO%TYPE,
pUSU_DTDESATIVACAO IN ENG.USU_USUARIO.USU_DTDESATIVACAO%TYPE,
pUSU_DTULTIMOACESSO IN ENG.USU_USUARIO.USU_DTULTIMOACESSO%TYPE,
pUSU_DLMAQUINA IN ENG.USU_USUARIO.USU_DLMAQUINA%TYPE,
pUSU_STNOVO IN ENG.USU_USUARIO.USU_STNOVO%TYPE,
pUSU_STATIVO IN ENG.USU_USUARIO.USU_STATIVO%TYPE
)
IS
sCreateUser Varchar(200);
bUsuarioExiste Number;
eUsuarioExiste Exception;
l_sqlcode number;
BEGIN
SELECT
COUNT(usu_cdusuario)
INTO bUsuarioExiste
FROM ENG.USU_USUARIO
WHERE USU_CDUSUARIO = pUSU_CDUSUARIO;
IF(bUsuarioExiste > 0) THEN
RAISE eUsuarioExiste;
END IF;
sCreateUser := 'CREATE USER ' || pUSU_CDUSUARIO || ' IDENTIFIED BY ' ||
pUSU_DLSENHA;
EXECUTE IMMEDIATE sCreateUser;
begin
EXECUTE IMMEDIATE 'GRANT ENG_GERAL TO ' || pUSU_CDUSUARIO;
-- moved this into the insert
--SELECT usu_seq.nextval INTO pUSU_IDUSUARIO FROM DUAL;
INSERT INTO ENG.USU_USUARIO
(
USU_IDUSUARIO,
USU_CDUSUARIO,
PES_IDPESSOA,
USU_DLOBSERVACAO,
USU_NUIP,
USU_DTCADASTRO,
USU_DTDESATIVACAO,
USU_DTULTIMOACESSO,
USU_DLMAQUINA,
USU_STNOVO,
USU_STATIVO
)
VALUES
(
usu_seq.nextval, --pUSU_IDUSUARIO,
pUSU_CDUSUARIO,
pPES_IDPESSOA,
pUSU_DLOBSERVACAO,
pUSU_NUIP,
sysdate,
pUSU_DTDESATIVACAO,
pUSU_DTULTIMOACESSO,
pUSU_DLMAQUINA,
pUSU_STNOVO,
pUSU_STATIVO
)
returning ;
exception
when others then
-- save off the sqlcode, you will need it to reraise
l_sqlcode := SQLCODE;
-- rollback any insert that may have run,
-- depending on where the exception was raised
rollback;
-- drop the created user
execute immediate 'drop user ' || pUSU_CDUSUARIO ;
-- reraise the error
RAISE_APPLICATION_ERROR (-20001, l_sqlcode || ': ' ||
SQLERRM(-l_sqlcode));
end;
COMMIT;
EXCEPTION
WHEN eUsuarioExiste THEN
RAISE_APPLICATION_ERROR (-20001,
'Usuário já existe ou possui nome inválido.');
ROLLBACK;
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR (-20001,
SQLCODE || ': ' || SQLERRM);
ROLLBACK;
END SP_USUARIO_INSERT;
Doesn't the RAISE_APPLICATION_ERROR mean that the ROLLBACK is unreachable? Unless the caller is also issuing a ROLLBACK, I think those need to be the other way around. You may be getting in implicit commit from DDL as other have said, depending on what's actually erroring, so it might be irrelevant, but it doesn't look quite right.