Catching a constraint violation in psql - plsql

I am using sql developer, and have added a constraint to one of my tables.
constraint valid_gender check(gender in ('M','F','I','T'))
When I try to add an entry with say 'x' for gender using a plsql procedure, it fails with constraint violation (as it should).
I want to add a "Catch" to the plsql procedure so that if valid_gender is voilated I can raise_application_error specific to it. Is this possible?

Oracle will raise an exception that says:
ORA-02290: check constraint (yourschema.valid_gender) violated
You can catch that in an exception handler and raise your own exception instead using raise_application_error in a couple of ways.
1) You can specifically trap the ORA-02290 exception like this:
declare
e_check_violated exception
pragma exception_init (e_check_violated, -2290);
begin
insert ...
exception
when e_check_violated then
if sqlerrm like '%(yourschema.valid_gender)%' then
raise_application_error(-20001,'Invalid gender');
else
raise;
end if;
end;
2) You can trap all exceptions and inspect them:
begin
insert ...
exception
when others then
if sqlerrm like 'ORA-02290:%(yourschema.valid_gender)%' then
raise_application_error(-20001,'Invalid gender');
else
raise;
end if;
end;
In a large application it is quite common to have an exception handling procedure to generalise this and look up the constraint-specific message in a table.

use anonym block in your code...
BEGIN
INSERT or update...
EXCEPTION
WHEN dup_val_on_index THEN
RISE...
END;

You could just test it first:
if gender_value not in ('M','F','I','T') then
raise_application_error...
end if;

Related

How to remove extra information form error message after executing a package body?

I want to remove extra information form error message after executing a package body.
Example:
If I run a anonymous block then the error message come like -
ERROR at line 1:
ORA-20010: Object ID PE556092 is not Produced by the Template TP000036
ORA-06512: at "EXAMPLE.TESTPROC", line 5
ORA-06512: at line 1
But i want to show only the below error message:
ORA-20010: Object ID PE556092 is not Produced by the Template TP000036
You can use the below to get the Error Code and Message respectively:
dbms_output.put_line('Error code: '||sqlcode);
dbms_output.put_line('Error msg: '||sqlerrm);
Read more about SQLERRM.
You can do that by defining an exception and mapping it to the SQLCODE of the raised error. Then you handle the logic in the exception handler.
Example, here I map to error -20010 (the one you are using):
declare
err_obj_not_produced exception;
pragma exception_init (err_obj_not_produced, -20010);
l_object varchar2 (50);
l_template varchar2 (50);
begin
run_your_code ('With values');
exception
when err_obj_not_produced then
return 'Object ID ' || l_object || ' is not Produced by the Template ' || l_template;
end;
Another one way to hide information about previous errors. Suppose you have two procedures:
create or replace procedure proc1 is
begin
raise no_data_found;
end;
/
create or replace procedure proc2 is
begin
proc1;
exception
when no_data_found then
raise_application_error(-20010, 'There is no spoon, Neo.');
end;
/
when you run a block in SQL*Plus, you get:
SQL> begin
proc2;
end;
/
begin
*
ERROR at line 1:
ORA-20010: There is no spoon, Neo.
ORA-06512: at "DEV.PROC2", line 6
ORA-06512: at line 2
You can change proc2 like this:
create or replace procedure proc2 is
begin
proc1;
end;
/
And call it like this with desired result:
SQL> begin
begin
proc2;
exception
when no_data_found then
raise_application_error(-20010, 'There is no spoon, Neo.', false);
end;
exception
when others then
dbms_output.put_line(SQLERRM);
end;
/
ORA-20010: There is no spoon, Neo.
PL/SQL procedure successfully completed.
This example shows how third parameter of raise_application_error procedure works. If you pass false, procedure deletes information about previously raised exceptions from error stack. It also will be impossible to get this information using dbms_utility.format_error_backtrace.
As Jon Heller noticed in comments, hiding information about exceptions is a bad practice (and using when others then without raise too, as Jeffry Kemp noticed), so, I hope you know what you are doing. This way or another, it is a shooting yourself in the foot.

Exception handling - plsql

I have a problem. Am doing an insert function
case f1.RFD_CATEGORY_CODE when'O1' then 'C1GBC'
when 'O2' then 'C2GBC' else null end
the field is mandatory and thus instead of null i need to show an error message if the code is not taking C1GBC or C2GBC. and if the code is taking C1GBC or C2GBC then show successful as message.
i have create an exception below but am getting error
create or replace procedure CTP_CODE as
declare
--RFD_CAT_ERR varchar2;
RFD_CAT_ERR EXCEPTION;
begin
if RFD_CATEGORY_CODE is '01' then RFD_CATEGORY_CODE is 'C1GBC';
DBMS_OUTPUT.PUT_LINE ('No1. Successful Operation');
else
if RFD_CATEGORY_CODE is '02' then RFD_CATEGORY_CODE is 'C2GBC';
DBMS_OUTPUT.PUT_LINE ('No2. Successful Operation');
end if;
raise RFD_CAT_ERR;
end if;
EXCEPTION
when RFD_CAT_ERR then
DBMS_OUTPUT.PUT_LINE ('Error message!');
end;
/
Wrong Code also wrong syntax..
Whenever your code goes in else condition it will throw error as after executing 2nd if it will go to "raise RFD_CAT_ERR;" part and raise an exception.
so u should handle error in elsif after 2nd If condition.
You say the field is mandatory. If by that you mean the field in the table is constrained as not null then you don't have to worry about raising an exception. The insert statement will do that for you. All you have to do is catch it in your exception handler and use RAISE_APPLICATION_ERROR to return a meaningful message.
Do that rather than try to print an error message. If your procedure is called by a batch process, there will be no one there to see it.
This works for me :D
CASE f1.RFD_CATEGORY_CODE
WHEN 'O1' THEN 'C1GBC'
WHEN 'O2' THEN 'C2GBC'
ELSE 'error'
Then raise any errors for exception

Oracle PL/SQL - ORA-01403 “No data found” when using “SELECT INTO”

I have a pl sql code that execute three queries sequentially to determine a match level and do some logic
The issue is - when first query has no results (completely valid scenario) I get ORA-01403 No data found.
I understand that I need to incorporate [ Exception clause when NO_DATA_FOUND ]- but how to add it and continue to the next query?
PL/SQL Code
SELECT A into PARAM A FROM SAMPLE WHERE SOME CONDITION;
-- GOT ORA-01403 No data found HERE
MATCH_LEVEL =1;
if A is null then
do some logic;
end if
SELECT A INTO PARAM_B FROM SAMPLE WHERE SOME OTHER CONDITION
MATCH_LEVEL =2
if A is null then
do some logic 2;
end if
SELECT A INTO PARAM_B FROM SAMPLE WHERE SOME OTHER CONDITION
MATCH_LEVEL =3
if A is null then
do some logic 3;
end if
END PL/SQL Code
Declare
--your declarations
begin
SELECT A into PARAM A FROM SAMPLE WHERE SOME CONDITION;
-- GOT ORA-01403 No data found HERE
Begin
MATCH_LEVEL =1;
if A is null then
do some logic;
end if;
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line ('Error...');
END;
--- and son on for other blocks
end;
Just surround your SELECT INTO with begin-end;
begin
-- your faulty statement here
Exception
When NO_DATA_FOUND Then
-- Do what you want or nothing
WHEN TOO_MANY_ROWS THEN
-- what if you get more then one row? and need specific handler for this
When OTHERS Then
-- do something here or nothing (optional - may happen if you have more than your SELECT INTO between 'begin' and 'Exception')
end;
This is like try block of PL/Sql
With this technique you can log the reason your statement failed.
For a SELECT ... INTO ... statement, the PL/SQL engine assume there will be one, and only one row returned by your query. If there is no row, or more than one, an exception is raised.
FWIW, you can handle such cases without resorting on exception handling by using aggregate functions. That way, there will always be only one row in the result set.
Assuming A can't be NULL in your rows:
SELECT MAX(A) into PARAM A FROM SAMPLE WHERE SOME CONDITION;
-- A would be NULL if there was *no* row. Otherwise, it is *the* value for *the* row
MATCH_LEVEL =1;
if A is null then
do some logic;
end if
If the NULL value is a possible case, just add an extra COUNT(*) column:
SELECT MAX(A), COUNT(*) into A, HAS_FOUND_ROW FROM SAMPLE WHERE SOME CONDITION;
if HAS_FOUND_ROW > 0 then
...
end if;
Oracle will not allow you to open an implicit cursor (i.e. a select statement in the body of a code block) that returns no rows. You have two options here (3 really, counting #Sylvain's answer, but that is an unusual approach): use an explicit cursor or handle the error.
Explicit Cursor
An explicit cursor is one found in the DECLARE section it must be opened and fetched manually (or in a FOR loop). This has the added advantage that, if you parameterize the query properly, you can write it once and use it multiple times.
DECLARE
a sample.a%type;
MATCH_LEVEL number;
cursor cur_params (some_column_value number) is
SELECT A FROM SAMPLE WHERE some_column = some_column_value;
BEGIN
MATCH_LEVEL := 1;
open cur_params (match_level);
fetch cur_params into a;
close cur_params;
if A is null then
null; --some logic goes here
end if;
MATCH_LEVEL := 2;
open cur_params (match_level);
fetch cur_params into a;
close cur_params;
if A is null then
null; --some logic goes here
end if;
end;
Handle the error
If you choose to handle the error, you'll need to create a BEGIN...END block around the code that is going to throw the error. When disregarding an error, it's crucial that you ensure that you are only disregarding the specific error you want avoid, when generated from the specific statement you expect it from. If you simply add the EXCEPTION section to your existing BEGIN...END block, for instance, you couldn't know which statement generated it, or even if it was really the error you expected.
DECLARE
a sample.a%type;
MATCH_LEVEL number;
BEGIN
MATCH_LEVEL := 1;
BEGIN
SELECT A into A FROM SAMPLE WHERE some_column = MATCH_LEVEL;
EXCEPTION
WHEN NO_DATA_FOUND THEN
null; --Do nothing
END;
if A is null then
null; --some logic goes here
end if;
MATCH_LEVEL := 2;
BEGIN
SELECT A into A FROM SAMPLE WHERE some_column = MATCH_LEVEL;
EXCEPTION
WHEN NO_DATA_FOUND THEN
null; --Do nothing
END;
if A is null then
null; --some logic goes here
end if;
end;
While I'd discourage it, you can catch any other errors in the same exception blocks. However, by definition, those errors would be unexpected, so it would be a poor practice to discard them (you'll never know they even happened!). Generally speaking, if you use a WHEN OTHERS clause in your exception handling, that clause should always conclude with RAISE;, so that the error gets passed up to the next level and is not lost.

plsql propagating/raising exception from a subprogram/unit

Lets say i am calling a plsql procedure from another procedure (all defined in the same package)
I am trying to raise an application error from proc1 to be displayed on a c# program(proc1 will be the entry call) when something goes wrong. Its straightforwrd when an exception happens within proc1. But how to propagate the same error that gets raised in proc2?
Do i have to declare the same user_exception EXCEPTION in proc1 as well? Or
should i have a global exception variable at the package level ? Whats the standard practice?
(pls ignore any code errors such an exception_init etc..) I had only included the code for the concept..
create or replace procedure proc1 is
begin
--some plsqlcode
proc2();
exception
raise_application_error( ???? );
end proc1;
create or replace procedure proc2 is
user_exception EXCEPTION;
begin
--do something
if (somefalse condition) then
raise user_exception
exception
when user_exception then
--do some error handling
raise;
end proc2;
I hope i was clear in framing the question. Thanks in advance for suggestions/tips.
If you have a package, standard practice is to declare exception in a package.
create or replace package pkg is
user_exception exception;
procedure proc1;
procedure proc2;
end pkg;
create or replace package body pkg is
procedure proc1 is
begin
proc2;
exception
when user_exception then
raise;
end;
procedure proc2 is
begin
raise user_exception;
end;
end pkg;
One solution is to declare the exception so it's visible in both procedures:
user_exception EXCEPTION;
create or replace procedure proc1 is
begin
--some plsqlcode
proc2();
exception
when user_exception then -- added
raise_application_error( ???? ); -- added
when others then
raise_application_error( ???? );
end proc1;
create or replace procedure proc2 is
begin
--do something
if (somefalse condition) then
raise user_exception
exception
when user_exception then
--do some error handling
raise;
end proc2;
Share and enjoy.

Display custom message when constraint is violated PL/SQL

I'm writing a pl/sql procedure. I've got a constraint on a column which doesn't allow values below 0 and above 500. I need to display a custom message if this constraint is violated (eg. "ID out of range"). Currently this is the exception and also the output in getting. There is another procedure that is outputting the error, hence the use raise_applcation_error.
Exception
when VALUE_ERROR then
raise_application_error(-20002, 'Customer ID out of range');
Error Message
"ORA-20000: ORA-02290: check constraint (s5849497.CK_ID_RANGE) violated"
What I would like
"ORA-20000: Customer ID out or range"
Here is the whole block if it helps
set serveroutput on;
---------------------------------------------------------------
alter table customer
add constraint ck_id_range
check (custid > 0 and custid < 500);
---------------------------------------------------------------
create or replace procedure ADD_CUSTOMER_TO_DB(pcustid number, pcustname varchar2) as
begin
insert into customer
values (pcustid,pcustname,0,'OK');
exception
when DUP_VAL_ON_INDEX then
raise_application_error(-20001, 'Duplicate customer ID');
when VALUE_ERROR then
raise_application_error(-20002, 'Customer ID out of range');
when others then
raise_application_error(-20000, SQLERRM);
end;
Thank you
I'm guessing that you're getting an ORA-02290 error. The way to catch this in an exception handler is to declare and initialize an exception for the particular error code (in this case, -2290) and then use your custom exception in the handler:
create or replace procedure ADD_CUSTOMER_TO_DB(pcustid number,
pcustname varchar2)
as
eCheck_constraint_violated EXCEPTION;
PRAGMA EXCEPTION_INIT(eCheck_constraint_violated, -2290);
begin
insert into customer
values (pcustid,pcustname,0,'OK');
exception
when DUP_VAL_ON_INDEX then
raise_application_error(-20001, 'Duplicate customer ID');
when eCheck_constraint_violated then
raise_application_error(-20002, 'Customer ID out of range');
when others then
raise_application_error(-20000, SQLERRM);
end;
As you'll see above, I just replaced VALUE_ERROR with the newly-defined exception.
If by chance the error you're getting is not -2290, just put the error you're seeing in PRAGMA EXCEPTION_INIT invocation above instead of -2290.
Share and enjoy.

Resources