VARCHAR2 Becomes Numeric by passing as parameter to a function - oracle

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.

Related

Exception with text in Block -> Its Function -> Caller procedure way

A procedure calls a function. It works with several tables, so I devided it to blocks. All blocks has its own exception section where the specific error message can be sent via e-mail.
Now I want to make the code cleaner and pass the exception in this order:
Block -> Its Function -> Caller Procedure
I can do it with 'raise' but in this case the caller procedure doesn't know where the exception came from.
Another solution I think is that the first block would use raise_application_error(User-Error-Id, 'Specific error message'). But in this case the original SQLCODE is hidden by raise_application_error.
How can I solve this?
I have something like this:
procedure main
...
begin
v_pcs := calc_pcs(parameters);
exception
When others then
dbms_output.put_line(SCLCODE || ' - ' || SQLERRM);
-- Here I'd like to read the part of the code where the exception happened
end;
funcion calc_pcs(
parameters
) as
begin
-- first block
begin
v_return := 5 / 0; -- just an example of an error
exception
when others then
raise -- How to add text to predefined exception. For example 'FirstBlock || SqlErrM'
end;
...
rest of the code contains additional blocks with their exception handling and
there is other code without blocks
...
return v_return;
Exception
when others then
raise;
end;
Thanks Sayan! Sorry, but I may have missed something important in my previous post, let me clarify.
I want to concatenate a placeholder text to the original predefined exception string of the start block, for example "First Block" || "divisor is equal to zero". Further exception handling would pass this along with the SQLCODE unchanged.
You can do like this:
declare
e1 exception;
e2 exception;
e3 exception;
pragma exception_init(e1, -20201);
pragma exception_init(e2, -20202);
pragma exception_init(e3, -20203);
procedure func ( x int, txt varchar2 ) is
begin
case x
when 1 then raise_application_error(-20201, 'x = ['||x||'] error text 1: ' || txt);
when 2 then raise_application_error(-20202, 'x = ['||x||'] error text 2: ' || txt);
when 3 then raise_application_error(-20203, 'x = ['||x||'] error text 3: ' || txt);
else null;
end case;
end func;
begin -- main block:
func(2, 'test');
exception
when e1 then
dbms_output.put_line('e1');
dbms_output.put_line(SQLCODE || ' - ' || SQLERRM);
when e2 then
dbms_output.put_line('e2');
dbms_output.put_line(SQLCODE || ' - ' || SQLERRM);
when e3 then
dbms_output.put_line('e3');
dbms_output.put_line(SQLCODE || ' - ' || SQLERRM);
end;
/
DBFiddle: https://dbfiddle.uk/s7HeZpNB
dbms_output:
e2
-20202 - ORA-20202: x = [2] error text 2: test

Duplicate error message when error handling

I have this procedure:
CREATE OR REPLACE PROCEDURE procA (paramA VARCHAR2, paramB VARCHAR2, output_value OUT VARCHAR2)
IS
BEGIN
IF paramA IS NOT NULL THEN
BEGIN --1
SELECT columA
INTO output_value
FROM tableA
WHERE columnB = paramA;
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- 2
SELECT columB
INTO output_value
FROM tableB
WHERE columnC = paramB;
END;
ELSE
SELECT columB
INTO output_value
FROM tableB
WHERE columnC = paramB;
END IF;
EXCEPTION
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR (-20008, 'Error: ' || SQLCODE || ' ' || SUBSTR (SQLERRM, 1, 200));
END procA;
I run this call:
DECLARE
output VARCHAR2 (10);
BEGIN
procA ('valueA', 'valueB', output);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE (SQLERRM); --3
END;
With these parameters the first SELECT statement gets a NO_DATA_FOUND (1). And handled with the that EXCEPTION. Then, I run the second SELECT statement (2) and I get again, a NO_DATA_FOUND exception.
Why do I get a duplicate NO_DATA_FOUND message in (3), like this:
ORA-20008: Error: 100 ORA-01403: no data found
ORA-01403: no data found
The first exception is handled, I expected that the last error handling will return just one message.
You aren't seeing a single error being duplicated; you're seeing two errors, which happen to be the same.
If you add a format_error_stack call to your final exception hander you can see that both of the thrown exceptions are in that stack:
...
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(DBMS_UTILITY.FORMAT_ERROR_STACK);
RAISE_APPLICATION_ERROR (-20008, 'Error: ' || SQLCODE || ' ' || SUBSTR (SQLERRM, 1, 200));
END procA;
/
The call from your anonymous block then shows:
ORA-01403: no data found
ORA-06512: at "<schema>.PROCA", line 13
ORA-01403: no data found
ORA-06512: at "<schema>.PROCA", line 6
ORA-20008: Error: 100 ORA-01403: no data found
ORA-01403: no data found
db<>fiddle
It might be a bit clearer if you vary the errors, e.g.:
declare
l_dummy varchar2(1);
begin
begin
select 'x' into l_dummy from all_users;
exception when too_many_rows then
select 'x' into l_dummy from all_users where 1=0;
end;
exception when others then
dbms_output.put_line(dbms_utility.format_error_stack);
dbms_output.put_line('caught <' || sqlerrm || '>');
end;
/
which produces:
ORA-01403: no data found
ORA-06512: at line 7
ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at line 5
caught <ORA-01403: no data found
ORA-01422: exact fetch returns more than requested number of rows>
db<>fiddle
The SQLERRM call combines both error messages, though the documentation doesn't seem to say that will happen. Oracle support note 2438311.1 seems to suggest it shouldn't, but it does all of the db<>fiddle versions and in 12.2.0.1 and 19.11 at least.
But, what if I just want the last one?
If you're on Oracle 12c or later, you can use UTL_CALL_STACK.ERROR_MSG(1) to get the first error from the stack, instead of using SQLERRM.
...
EXCEPTION
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR (-20008,
'Error: ' || SQLCODE || ' ' || SUBSTR (UTL_CALL_STACK.ERROR_MSG(1), 1, 200));
END procA;
/
which will now just show:
ORA-20008: Error: 100 no data found
db<>fiddle (and another one for my earlier example.)
You can read more about that function in the documentation.
There is a difference between handling and catching an exception.
You're using RAISE_APPLICATION_ERROR, which throws the exception to the caller although you've caught it once and since you catch it two times - you have two errors.

ORACLE FUNCTION testcase

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.

PLS-00103: Encountered the symbol "EXCEPTION" error while checking for a value in the bind variable

I am getting the below error for the PL/SQL block I executed.
ORA-06550: line 16, column 1: PLS-00103: Encountered the symbol
"EXCEPTION" when expecting one of the following:
My anonymous procedure below was working fine until I included the user defined exception e_nonrented. Which checks if the value of bindvariable :g_movie_id=2.. If that is the case it throws an exception...
Below is the code:
VARIABLE g_movie_id NUMBER EXEC :g_movie_id := 2
DECLARE v_count NUMBER;
v_movieid NUMBER;
v_title mm_movie.movie_title%TYPE;
e_nonrented
EXCEPTION;
BEGIN
SELECT m.movie_title,
COUNT(r.rental_id),
r.movie_id
INTO v_title,
v_count,
v_movieid
FROM mm_movie m,
mm_rental r
WHERE m.movie_id = r.movie_id
AND m.movie_id = :g_movie_id
GROUP BY m.movie_title,
r.movie_id;
DBMS_OUTPUT.PUT_LINE(v_title || ': ' || v_count);
IF :g_movie_id = 2 THEN
RAISE e_nonrented;
EXCEPTION
WHEN no_data_found THEN
DBMS_OUTPUT.PUT_LINE('there is no movie id for: ' || :g_movie_id);
WHEN e_nonrented THEN
DBMS_OUTPUT.PUT_LINE(' Movie with Id ');
END;
You're simply missing an END IF; statement.
IF :g_movie_id = 2 THEN
RAISE e_nonrented;
END IF;
EXCEPTION

Rollback # Stored Procedure on Oracle

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.

Resources