Suppose I have the following trigger :
create or replace trigger trigInsertSaloane before insert on saloane
for each row
declare
myExcp exception;
pragma exception_init (myExcp,-20005);
begin
for i in (select * from saloane) loop
if(:new.numar_salon=i.numar_salon) and (trim(upper(:new.nume_sectie))=trim(upper(i.nume_sectie))) then
raise myExcp;
end if;
end loop;
exception when myExcp then dbms_output.put_line('Record exists');
end;
/
All I want is to not insert the row if exception is raised, so something like rollback. In my case if exception is raised and caught, the line is also inserted. I don`t want that. Also I want to make that in a pretty way, by showing up a message and not getting any errors.How to make it?
ok.. a few points.
1) you need to raise an exception from a trigger for the insert will fail and not be inserted. so either do not catch your exception or re RAISE it again.
2) using dbms_output.put_line() will only display a message if the user/client has it turned on.
3) you do not need to loop over your cursor. adding a where clause is more efficient
4) your trigger will not work.. it will throw
ORA-04091: table SALOANE is mutating, trigger/function may not see it if you insert more then 1 row at a time. (try insert into saloane select * from saloane )
5) it may just be your example.. it looks like you could more simply use a unique constraint on the given columns to enforce this requirement.
Adding few more points as mentined in other answer.
Since you are handling the EXCEPTION (PRETTY WELL :p ) in the
Trigger so ideally Trigger event execution is successful so
INSERT/UPDATE/DELETE will happen.
What you need here is to RAISE some kind of exception which will
force the Trigger to fails with the Exception so
in this you need to have RAISE_APPLICATION_ERROR condition to
handle this.
As mentioned above UNIQUE Key constraint will be your best friend to
work in this case.
Related
I want to create a trigger that manages 2 magazines, 'Elle' and 'Time'.
So if the user tries to insert a new record in the magazine table that does not match these two, the latter should not be retained.
I already have all the tables and stuff ready there's no problem in that.
The problem is that I can't figure out a way to do this properly, a friend said that this code of mine interacts with lines only, and that I need a code for the whole table and recommended that I use a Cursor.
Here's my code:
Create or replace trigger TMag
After INSERT on Magazine
FOR EACH ROW
DECLARE
e EXCEPTION;
BEGIN
IF :new.mag_nom!= 'Elle' or :new.mag_nom!= 'Time' THEN
Delete from Magazine where ISBN=:new.ISBN;
raise e;
END IF;
exception
when e then dbms_output.put_line('nom mag incorrecte');
END;
Here's a look on my tables:
CLIENT(CIN, CL_NOM, CL_ADDR, CL_VILLE, EMAIL, CONTACT_NUM);
MAGAZINE(ISBN, MAG_NOM, PRIX_Mois);
ABONNEMENT(AB_ID, #ISBN, #IN_ID, Months);
INVOICE(IN_ID, #CIN, dateI, State) ;
Thanks in advance
I think you don't need a trigger, rather resolve the problem internally by creating a CHECK Constraint such as
ALTER TABLE Magazine
ADD CONSTRAINT correcte_mag_nom
CHECK (mag_nom IN ('Elle', 'Time'));
If the provided value is not eligible for mag_nom column, then it will hurl as
ORA-02290 check constrain (<schema>.CORRECTE_MAG_NUM) violated
You cannot use DML (your delete) on the same table that the trigger belongs to. Change your trigger to fire before the insert, and just raise the error to prevent the insert from happening in the first place.
Note that you must also declare a code number for the exception, and your logic should be if new.name!='Elle' AND new.name!='Time'. If you use "OR" then your trigger will not allow any inserts on the table at all...
CREATE OR REPLACE TRIGGER TMAG
BEFORE INSERT ON MAGAZINE
FOR EACH ROW
DECLARE
e_bad_mag_nom EXCEPTION;
PRAGMA EXCEPTION_INIT (e_bad_mag_nom, -20001)
BEGIN
IF :new.mag_nom!= 'Elle' AND :new.mag_nom!= 'Time' THEN
raise e_bad_mag_nom;
END IF;
EXCEPTION
when e_bad_mag_nom then dbms_output.put_line('nom mag incorrecte');
END;
See here for references:
https://www.oracletutorial.com/plsql-tutorial/plsql-raise/
How to raise an Exception inside a Trigger? Is there a way to do this?
I'm trying to understand the order in which PL/SQL exceptions get raised. Here's my code using the HR schema:
DECLARE
dept_to_delete departments.department_id%TYPE := 105;
dept_to_keep departments.department_id%TYPE := 115;
integrity_constraint_violated EXCEPTION;
PRAGMA EXCEPTION_INIT(integrity_constraint_violated, -2291);
no_department_to_delete EXCEPTION;
BEGIN
-- Will cause integrity_constraint_violated error
UPDATE employees
SET department_id = dept_to_keep
WHERE department_id = dept_to_delete;
DELETE FROM departments
WHERE department_id = dept_to_delete;
IF SQL%NOTFOUND THEN
RAISE no_department_to_delete;
END IF;
EXCEPTION
WHEN integrity_constraint_violated THEN
DBMS_OUTPUT.PUT_LINE('Cannot assign employees to non-existing department.');
WHEN no_department_to_delete THEN
DBMS_OUTPUT.PUT_LINE('No such department to delete.');
END;
Neither department id 105 nor 115 exist in the departments table. My expectation was that the integrity_contraint_violated exception would be thrown first, because it occurs first in the code, but the user-defined no_department_to_delete gets thrown first. The output is:
Cannot assign employees to non-existing department.
Is there logic to this or is it just impossible to know which exception will be raised first?
EDIT
Jon Heller is right. The UPDATE statement is not causing an exception because the WHERE clause filters out all records. I messed with that for way too long. Anyway, the takeaway is that PL/SQL exceptions are raised depending on the order they occur, which is intuitive. Good to know. Thanks, Jon.
PL/SQL exceptions are raised depending on the order they occur, not their order in the EXCEPTION clause.
The UPDATE statement must not be throwing an exception. (If you're sure it is, then we can resolve if you modify the question with a minimal, reproducible example.
Also, not directly related to the question, but I recommend removing your custom exception handling. It's not providing anything of value. If there was no exception handling, the block would automatically raise the error message, object name, and line number, which is plenty of information to debug the problem. With your custom exception handling, the object name and line number are not present. And if DBMS_OUTPUT is not enabled the exception will not be seen.
I have a stored proc that I call from an external program to upsert data. The way the proc is setup is that it inserts into the table and on exception 'dup_val_on_index' it does an update instead.
How could I get this error if I'm catching the 'dup_val_on_index' exception and doing an update inside of it? I assume a PK would catch this specific exception since it creates a duplicate?
Yes, the dup_val_on_index named error would be raised for a primary key constraint error.
Perhaps your update is the code that is causing the error to be raised.
Why don't you just use MERGE in the first place?
In case you really don't want to use merge, just build an inner pl/sql-block and catch the exception there:
create or replace procedure proc(param1 varchar2)
as
some_var varchar2(50);
begin
-- do some things
begin
insert on your table;
exception when dup_val_on_index then
update on your table;
end;
--proceed with some more things
end proc;
This is my code:
CREATE OR REPLACE TRIGGER unsuccessful_logins
AFTER INSERT OR UPDATE
ON temp_logins
for each row
DECLARE
CURSOR c_unsuccessful_attempts
IS
SELECT * from temp_attempts
and user_id= :new.user_id;
max_fails EXCEPTION;
BEGIN
FOR r_unsuccessful_attempts in c_unsuccessful_attempts
LOOP
if(:new.user_id = r_unsuccessful_attempts.user_id) then
if (r_unsuccessful_attempts.locked = 'Y') then
raise max_fails;
end if;
else
null;
end if;
END LOOP;
EXCEPTION
WHEN max_fails THEN
RAISE_APPLICATION_ERROR (-20300,'User '''||:new.login_name||''' has reached maximum failed logins. Please contact your system administrator ');
END unsuccessful_logins;
The error I am getting in my oracle apps screen is:
APP-01564: ORACLE error 20300 in fdssgn
cause: fdssgn failed due to ORA-20300: You have reached maximum failed logins. Please contact your system administrator.
ORA-06512: at "APPS.UNSUCCESSFUL_LOGINS",line 24
ORA-04088: error during excution of trigger 'APPS.UNSUCCESSFUL_LOGINS'.
The SQL statement being executed at the time of the error was:
INSERT INTO TEMP_LOGINS (USER_ID, ATTEMPT_TIME,TERMINAL_ID,LOGIN_NAME)
values(:user_id, sysdate,:erminal_id,:login_name)
I just want to display ORA-20300: You have reached maximum failed logins. Please contact your system administrator part.
And want to omit:
ORA-06512: at "APPS.UNSUCCESSFUL_LOGINS",line 24
ORA-04088: error during excution of trigger 'APPS.UNSUCCESSFUL_LOGINS'.
The SQL statement being executed at the time of the error was:
INSERT INTO TEMP_LOGINS (USER_ID, ATTEMPT_TIME,TERMINAL_ID,LOGIN_NAME)
values(:user_id, sysdate,:erminal_id,:login_name)
How can I get rid off these extra messages on the screen?
I'm not sure exactly what you mean by 'oracle apps', but this link may help; it has a section on exception handling.
As I suggested in an earlier comment, I suspect you need something in your application code to catch and gracefully handle the exception raised by the trigger, so some kind of wrapper around the insert statement. The link is specifically talking about handling an exception in Forms, which (as is probably quite clear) I'm not familiar with, but the principal is likely to be the same if you're using something related.
Adapting their example slightly, something like this might fit what I think you're trying to do:
DECLARE
too_many_attempts EXCEPTION;
PRAGMA EXCEPTION_INIT(too_many_attempts, -20300);
BEGIN
INSERT INTO TEMP_LOGINS (USER_ID, ATTEMPT_TIME,TERMINAL_ID,LOGIN_NAME)
values(:user_id, sysdate,:erminal_id,:login_name);
EXCEPTION
WHEN too_many_attempts THEN
fnd_message.set_string(SQLERRM);
fnd_message.error;
RAISE FORM_TRIGGER_FAILURE;
END;
The simple answer is, you can not omit output from RAISE_APPLICATION_ERROR. You could consider using dbms_output.put_line to provide a single message.
...
WHEN max_fails THEN
dbms_output.put_line('User Message');
I have a small doubt regarding BEFORE INSERT TRIGGER in oracle,
my trigger looks like this:
CREATE OR REPLACE TRIGGER some_trigger BEFORE INSERT
ON some_table REFERENCING NEW AS newRow
FOR EACH ROW
DECLARE
some_var number(25, 4);
BEGIN
-- do some stuff
:newRow.some_column :=some_var;
exception
when no_data_found then
NULL;
when others then
NULL;
END;
Here the update which I am doing on newRow.some_column is an optional thing, so my requirement is that even the trigger fails, the newRow should be inserted into the table and this is why I am eating up exceptions.
Is my assumption correct that if I eat up exception, the newRow will be inserted into the table in all scenarios ?
Thanks heaps.
Your exception "handling" will make sure that the insert succeeds, even if you have an exception in your trigger.
Some thoughts:
Your current code cannot cause a NO_DATA_FOUND-exception.
Do you really want your code to fail silently?
Why do you catch both NO_DATA_FOUND and OTHERS and ignore both? OTHERS will catch NO_DATA_FOUND too.
EDIT
I'd just catch the NO_DATA_FOUND and add a good comment about why you can silently ignore it in your case.
Make sure that your SELECT only returns a single row, otherwise TOO_MANY_ROWS needs to be handled too.
Ignoring OTHERS is generally considered bad practice. Your code could fail and you'd never notice. There is a new Compiler Warning for this, actually.