Oracle Trigger Only Fires After I do a explicit Commit - sql

I am new to triggers and just learning them. When I delete a row from the respective table I do not get a DBMS Output, but I know the row has been deleted. The only time I get a DBMS output for the trigger is when do a COMMIT or a RollBACK. Is this correct? Form what I have read the Trigger should automatically run when I delete a row but that doesn't seem to be the case for me. Additionally I ensured the trigger was enabled and set serveroutput was on.
create or replace Trigger tadr_Employee
After Delete ON employee
For Each Row
Begin
DBMS_OUTPUT.PUT_LINE('Tigger Fired After deleting anything from Lab10_Employees tadr');
End;
Delete FROM Employee
Where EmployeeID = 11111;

Related

Trigger that insert into a dblink table

I am trying to do a trigger, that select values into some tables, and then insert them into another table.
So, for now I got this. There is a lot of columns, so I don't copy them, it is only varchar2 values, and this part works, so I don't think it is useful :
create or replace
TRIGGER TRIGGER_FICHE
AFTER INSERT ON T_AG
BEGIN
declare
begin
INSERT INTO t_ag_hab#DBLINK_DEV
()
values
();
/*commit;*/
end;
END;
Stored procedure where trigger will be called(again a lot of parameters, not relevant to copy them :
INSERT INTO T_AG()
VALUES
();
commit work;
The thing is, we cannot do commit into a trigger, I read that, and understand it.
But, how can I see an update of my table, with the new value?
When the process is runnign, there is nor error, but I don't see the new line into t_ag_hab.
I know it's not very clear, but I don't know how to explain it other way.
How can I fix this?,
Because you're inserting into a remove table via a database link, you have a distributed transaction:
... distributed transaction processing is more complicated because the database must coordinate the committing or rolling back of the changes in a transaction as an atomic unit. The entire transaction must commit or roll back.
When you commit, you're committing both the local insert and the remote insert performed by your trigger, as an atomic unit. You cannot commit one without the other, and you don't have to do anything extra to commit the remote change:
The two-phase commit mechanism is transparent to users who issue distributed transactions. In fact, users need not even know the transaction is distributed. A COMMIT statement denoting the end of a transaction automatically triggers the two-phase commit mechanism. No coding or complex statement syntax is required to include distributed transactions within the body of a database application.
If you can't see the inserted data from the remote database afterwards then something else has deleted it after the commit, or more likely you're looking at the wrong database.
One slight downside (though also a feature) of a database link is that it hides the details of where the work is being done. You can drop and recreate a link to make your code update a different target database without having to modify the code itself. But that means your code doesn't know where the insert is actually going - you'd need to check the data dictionary to see where the link is pointing. And even then you might not know as the link can be using a TNS alias to identify the database, and changes to the tnsnames.ora aren't visible from within the database.
If you can see the data after committing by querying t_ag_ab#dblink_dev from the same database you ran your procedure, but you can't see if when queried locally from the database you expect that to be pointing to, then the link isn't pointing where you think it is. The insert is going to one database, and you are performing your query against a different one. Only you can decide which is the 'correct' database though; and either redefine the link (or TNS entry, if appropriate), or change where you're doing the query.
I am not able to understand you requirement clearly. For updating records in main table and insert the old records in audit table. we can use the below query as a trigger.(MS-SQL)
Create trigger trg_update ON T_AGENT
AFTER UPDATE AS
BEGIN
UPDATE Tab1
SET COL1 = I.COL1, COL2=I.COL2
FROM INSERTED I INNER JOIN Tab1 ON I.COL3=Tab1.Col3
INSERT Tab1_Audit(COL1,COL2,COL3)
SELECT Tab1 FROM DELETED
RETURN;
END;
So far what you presented is just for Inserting trigger. If you want to see the update action done try to add Update like this example.
SQL> CREATE OR REPLACE TRIGGER validate_update
2 AFTER INSERT OR UPDATE ON T_AGENT
3 FOR EACH ROW
4 BEGIN
5 IF UPDATING('ACCOUNT_ID') THEN -- do something like this when updating
6 DBMS_OUTPUT.put_line ('ERROR'); -- add your action here
7 ELSIF INSERTING THEN
8 INSERT INTO t_ag_hab#DBLINK_DEV() values();
9 END IF;
10 END;
11 /
Trigger created.

Row Not Being Deleted After ON DELETE Trigger Runs

I am working in PostgreSQL 8.4 and have written a trigger function like so:
CREATE OR REPLACE FUNCTION otherschema.order_delete_log_procedure() RETURNS TRIGGER AS
$$
BEGIN
DELETE FROM otherschema.order_log AS thelog WHERE thelog.log_order_id=OLD.order_id;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER order_deletion_log_trigger BEFORE DELETE ON public.order FOR EACH ROW EXECUTE PROCEDURE
otherschema.order_delete_log_procedure();
My problem is, the trigger fires perfectly (the trigger is deleting the log row), however the row that is supposed to be deleted from the table that is firing the trigger is not getting deleted (the order isn't getting deleted).
If I try to delete the order row manually, it just says that no rows were affected and it gives no error. Any thoughts?
Thanks!
Returning NULL from a BEFORE DELETE trigger aborts the delete operation. You should return old instead, or make it an AFTER DELETE trigger.

Simple IF EXISTS in TRIGGER fails

I've created a simple TRIGGER:
ALTER TRIGGER [dbo].[SprawdzZajetosc] ON [dbo].[Wypozyczenia]
FOR insert, update
AS
BEGIN
IF EXISTS (SELECT * FROM Wypozyczenia)
BEGIN
RAISERROR('Wybrany pojazd został już wypożyczony w wybranym przedziale czasu.', 16, 1)
ROLLBACK TRANSACTION
END
END
I can't understand why 'if' is returning me TRUE even if table 'Wypozyczenia' is empty? It doesn't matter what 'Wypozyczenia' contains - it always returns me TRUE.
I tried with count(*) it always returns me a value > 0.
Why is that?
I am not 100% sure of this, but it sounds logical to me - The trigger is an insert/update trigger. As soon as something is being inserted, the trigger is triggered and the condition is TRUE. Since there is a ROLLBACK TRANSACTION fired, the inserted row is then rolled back and hence you get an empty table. What are you actually trying to achieve here ?
Apart from the reasons why you're doing this, the cause for IF EXISTS() to be always TRUE in your case is very simple it's because you're using an AFTER or FOR trigger.
CREATE TRIGGER
AFTER specifies that the DML trigger is fired only when all operations
specified in the triggering SQL statement have executed successfully.
All referential cascade actions and constraint checks also must
succeed before this trigger fires.
Meaning the row(s) you're trying to insert are already in the table. It's just a transaction has not been committed yet.
Here is SQLFiddle demo
Your IF EXISTS() check might've worked only in INSTEAD OF INSERT trigger but then you should've take into consideration that triggers in SQL Server are statement based. Meaning it fires once per statement and you can insert more than one row in one statement.
Here is SQLFiddle demo
As far as FOR UPDATE clause goes in your trigger definition it doesn't make any sense at all. If you're updating something it should be in the table. Thus table is not empty.

Preventing certain rows from being deleted in Oracle

I want to prevent any row with VERSIONID=1 from being deleted in a certain table. I also want to log this in an audit table so we can see when this happens for logging purposes. I'm trying to do this with a trigger:
CREATE TRIGGER TPMDBO.PreventVersionDelete
BEFORE DELETE ON TPM_PROJECTVERSION
FOR EACH ROW
DECLARE
BEGIN
IF( :old.VERSIONID = 1 )
THEN
INSERT INTO TPM_AUDIT VALUES ('Query has attempted to delete root project version!', sysdate);
RAISE_APPLICATION_ERROR( -20001, 'Query has attempted to delete root project version!' );
END IF;
END;
I get the following results:
SQL> delete from TPM_PROJECTVERSION where PROJECTID=70 and VERSIONID=1;
delete from TPM_PROJECTVERSION where PROJECTID=70 and VERSIONID=1
*
ERROR at line 1:
ORA-20001: Query has attempted to delete root project version!
ORA-06512: at "TPMDBO.PREVENTVERSIONDELETE", line 6
ORA-04088: error during execution of trigger 'TPMDBO.PREVENTVERSIONDELETE'
However, the table TPM_AUDIT is empty. Am I doing something wrong?
If your trigger raises an error, the DELETE statement fails and the transaction is rolled back to the implicit savepoint that is created before the statement is run. That means that any changes made by the trigger are rolled back as well.
You can work around this by using autonomous transactions. Something like
CREATE PROCEDURE write_audit
AS
PRAGMA AUTOMOMOUS_TRANSACTION;
BEGIN
INSERT INTO tpm_audit
VALUES( 'Query has attempted to delete root project version!',
sysdate );
commit;
END;
CREATE TRIGGER TPMDBO.PreventVersionDelete
BEFORE DELETE ON TPM_PROJECTVERSION
FOR EACH ROW
DECLARE
BEGIN
IF( :old.VERSIONID = 1 )
THEN
write_audit;
RAISE_APPLICATION_ERROR( -20001, 'Query has attempted to delete root project version!' );
END IF;
END;
This will put the INSERT into TPM_AUDIT into a separate transaction that can be committed outside the context of the DELETE statement. Be very careful about using autonomous transactions, however
If you ever find yourself using autonomous transactions for anything other than writing to a log table, you're almost certainly doing something wrong.
Code in a PL/SQL block declared using autonomous transactions is truly autonomous so it cannot see uncommitted changes made by the current session.
Because of write consistency, it is entirely possible that Oracle will partially execute a DELETE statement, firing the row-level trigger a number of times, roll back that work, and then re-execute the DELETE. That silent rollback, however, will not roll back the changes made by the autonomous transaction. So it is entirely possible that a single DELETE of a single row would actually cause the trigger to be fired more than once and, therefore, create multiple rows in TPM_AUDIT.
If you can create a UNIQUE constraint on the TPM_PROJECTVERSION pk columns + the version column, then you can create a second table that would reference those rows.
Trying to delete a row in TPM_PROJECTVERSION would then fail because child rows are present. This would at least throw an error in your application and prevent the deletion.
The other table could be automatically populated through an insert trigger on TPM_PROJECTVERSION.
If you revoke the DELETE privilege on that helper table, it would never be possible to remove those rows.
I believe you need to COMMIT the INSERT operation before calling RAISE_APPLICATION_ERROR, which rolls back the transaction.

Solving the mutating table problem in Oracle SQL produces a deadlock

Hey, I'm trying to create a trigger in my Oracle database that changes all other records except the one that has just been changed and launched the trigger to 0. Because I am updating records in the same table as the one that launched the trigger I got the mutating table error. To solve this, I put the code as an anonymous transaction, however this causes a deadlock.
Trigger code:
CREATE OR REPLACE TRIGGER check_thumbnail AFTER INSERT OR UPDATE OF thumbnail ON photograph
FOR EACH ROW
BEGIN
IF :new.thumbnail = 1 THEN
check_thumbnail_set_others(:new.url);
END IF;
END;
Procedure code:
CREATE OR REPLACE PROCEDURE check_thumbnail_set_others(p_url IN VARCHAR2)
IS PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
UPDATE photograph SET thumbnail = 0 WHERE url <> p_url;
COMMIT;
END;
I assume I'm causing a deadlock because the trigger is launching itself within itself. Any ideas?
Using an autonomous transaction for this sort of thing is almost certainly a mistake. What happens if the transaction that inserted the new thumbnail needs to rollback? You've already committed the change to the other rows in the table.
If you want the data to be transactionally consistent, you would need multiple triggers and some way of storing state. The simplest option would be to create a package with a collection of thumbnail.url%type then create three triggers on the table. A before statement trigger would clear out the collection. A row-level trigger would insert the :new.url value into the collection. An after statement trigger would then read the values from the collection and call the check_thumbnail_set_others procedure (which would not be an autonomous transaction).