I need to restrict access to DDL with some table in oracle, forbid drop of the table. How I can do this? I just can create DDL trigger for database and schema
create table my_table(
id int primary key not null,
first_val int,
second_val int
);
create trigger delete_disabling_trigger
before drop on database
begin
if --some condition
dbms_output.put_line('delete_disabling_trigger');
RAISE_APPLICATION_ERROR(-175,'Cant delete this table');
end if;
end;
If you need table that someone won't be able to drop you can create the table in another schema. Give grants to select, insert, update, delete and create synonym for user that is using the table.
Scenario: User X needs table T which can't be dropped.
Create user persist identified by password.
Create table persist.T ...
GRANT SELECT, INSERT, UPDATE, DELETE on persist.T to X;
create synonym X.T for persist.T;
In such scenario X can manipulate data but can't change structure or drop table. Using trigger seems to be weird solution to block dropping table.
EDIT
create or replace trigger delete_disabling_trigger
before drop on database
begin
if(ORA_DICT_OBJ_NAME = 'MY_TABLE') then --and ORA_DICT_OBJ_OWNER = 'YOUR_SCHEMA'
dbms_output.put_line('delete_disabling_trigger');
RAISE_APPLICATION_ERROR(-20000,'Cant delete this table');
end if;
end;
/
I have just implemented a trigger to stop the drop of 5 tables which are being dropped from 2 development schemas by some as yet unknown process. We register the attempt in a table with an autonomous transaction, and stop the drop.
I hope to identify the process soon and get rid of the trigger.
CREATE OR REPLACE TRIGGER whos_dropping_my_table
BEFORE DROP
ON database
declare
PRAGMA AUTONOMOUS_TRANSACTION;
begin
insert into some_table_I_prepared_earlier
VALUES( SUBSTR(ora_sysevent,1,50),
SUBSTR(ora_dict_obj_owner,1,50),
SUBSTR(ora_dict_obj_name,1,50),
SUBSTR(ora_dict_obj_TYPE,1,50),
UPPER(sys_context('USERENV','TERMINAL')),
SYSDATE,
UPPER(sys_context('USERENV','OS_USER'))
);
commit;
if SUBSTR(ora_dict_obj_name,1,50) in
('TABLE_1','TABLE_2','TABLE_3','TABLE_4','Table_5')
then
begin
RAISE_APPLICATION_ERROR(num => -20998,
msg => 'Stop deleting my table, whoever you are');
end;
end if;
end;
Related
Is there a way to add insert/update/delete triggers in Clickhouse to keep track of changes in a specific table to write data that has been changed to another table.
Here is a similar script for Oracle:
CREATE OR REPLACE TRIGGER trigger_name
AFTER
INSERT OR UPDATE OR DELETE
ON table_name
FOR EACH ROW
DECLARE
action_type VARCHAR2(6);
BEGIN
action_type := CASE
WHEN INSERTING THEN 'insert'
WHEN UPDATING THEN 'update'
WHEN DELETING THEN 'delete'
END;
INSERT INTO ...
END;
I have a litte task. Firstly I added a column in my table with specific constraints. Then I added a trigger for other jobs.
But I need a rollbacksql and have no idea what to proceed. Can anybody help or give an advice about it? I am adding my sql snippet.
ALTER TABLE FCBSADM.GL_DEF ADD GL_TP NUMBER;
ALTER TABLE FCBSADM.GL_DEF ADD CONSTRAINTS CH_COL CHECK (GL_TP between 1 and 10);
CREATE OR REPLACE TRIGGER GL_DEF_GL_TP_TRG
BEFORE INSERT OR
DELETE OR
UPDATE OF CDATE, CMPNY_DEF_ID, CUSER, DESCR, GL_DEF_ID, MNY_TP_ID, ST, UDATE, UUSER, GL_TP
ON FCBSADM.GL_DEF
FOR EACH ROW
DECLARE
cnt number := 0;
BEGIN
IF INSERTING
THEN
IF :NEW.GL_TP = 2
THEN
SELECT 1 into cnt from dual where exists( select *
FROM LOOKUP_GLCODE_IND_CEZA lookup
WHERE lookup.indirim_glcode = :NEW.gl_Def_id);
IF (cnt = 1) THEN
raise_application_error
(-20101, 'Please insert record into LOOKUP_GLCODE_IND_CEZA before inserting GL_DEF');
END IF;
END IF;
END IF;
END;
What do you want to rollback? Adding a column and creating a trigger? If so, drop them, both.
alter table gl_def drop column gl_tp;
drop trigger gl_def_gl_tp_trg;
A trigger and newly added table can be rolled back only by using drop and alter statements. This is ok if its being done inside a script that executes only a few times. But is highly inefficient if both drop and alter are called frequently for n number of records.
I want LOCK TABLE for longer than to the next COMMIT / ROLLBACK.
For instance, I locked table, corrected its state and want subsequently update other tables but I don't want to keep locks for other tables for too long and expect to do a lot of COMMIT.
So I will do COMMIT / ROLLBACK for other tables but still want to prevent any modification to base table.
It looks like I need to open two sessions to DB. Keep LOCK TABLE in one and process tables in another. But I don't understand how can I propagate changes to base table without closing LOCK by COMMIT so they will be visible in another session.
Here is an example of using AUTONOMOUS_TRANSACTION
drop table EX_EMPLOYEE
/
drop table EX_EMPLOYEE1
/
create table EX_EMPLOYEE
(id number(4) null)
/
create table EX_EMPLOYEE1
(id number(4) null)
/
insert into EX_EMPLOYEE (id) values(1);
/
insert into EX_EMPLOYEE1 (id) values(1);
/
commit
/
create or replace procedure P_TEST
as
begin
DELETE FROM EX_EMPLOYEE WHERE ID =1;
P_TEST1();
end;
/
create or replace procedure P_TEST1
as
PRAGMA AUTONOMOUS_TRANSACTION;
begin
DELETE FROM EX_EMPLOYEE1 WHERE ID =1;
commit;
end;
/
then execute the procedure
begin
P_TEST();
end;
/
when finishes select on the 2 tables, youll notice the first table still contains the row, however the second the row is deleted.
select * from ex_employee where id =1;
select * from ex_employee1 where id =1;
You can also check from the database that the table is still locked:
SELECT * FROM V$LOCKED_OBJECT L
INNER JOIN USER_OBJECTS U ON(L.OBJECT_ID=U.OBJECT_ID);
Don't forget after this test to commit or rollback to release the lock.
I'm wondering if I will miss any data if I replace a trigger while my oracle database is in use. I created a toy example and it seems like I won't, but one of my coworkers claims otherwise.
create table test_trigger (id number);
create table test_trigger_h (id number);
create sequence test_trigger_seq;
--/
create or replace trigger test_trigger_t after insert on test_trigger for each row
begin
insert into test_trigger_h (id) values (:new.id);
end;
/
--/
begin
for i in 1..100000 loop
insert into test_trigger (id) values (test_trigger_seq.nextval);
end loop;
end;
/
--/
begin
for i in 1..10000 loop
execute immediate 'create or replace trigger test_trigger_t after insert on test_trigger for each row begin insert into test_trigger_h (id) values (:new.id); end;';
end loop;
end;
/
ran the two loops at the same time
select count(1) from test_trigger;
COUNT(1)
100000
select count(1) from test_trigger_h;
COUNT(1)
100000
create or replace is locking the table. So all the inserts will wait until it completes. Don't worry about missed inserts.
I think you might be going about testing this in the wrong way. Your insert statements won't take any time at all and so the replacement of the trigger can fit in through the gaps between inserts. As least this is what I infer due to the below.
If you change your test to ensure you have a long running SQL statement, e.g.
create table test_trigger (id number);
create table test_trigger_h (id number);
create sequence test_trigger_seq;
create or replace trigger test_trigger_t
after insert on test_trigger for each row
begin
insert into test_trigger_h (id) values (:new.id);
end;
/
insert into test_trigger
select level
from dual
connect by level <= 1000000;
If you then try to replace the trigger in a separate session it will not occur until after the insert has completed.
Unfortunately, I can't find anything in the documentation to back me up; this is just behavior that I'm aware of.
Following URL answers that trigger can be modified while application is running. its will a "library cache" lock and NOT a "data" lock. Oracle handles it internally without you worrying abt it.
Check out question raised by Ben- Can a trigger be locked; how would one determine that it is?
-- Run this from session 2:
select * from v$access where object = upper('test_trigger_t');
Am very new to Oracle triggers. Suppose I have a trigger on an insert in the table emp. Is there a way to find out what was the inserted record that fired the trigger. I wanted the trigger to have code that does something if the inserted record was a particular value.
Assuming you have a row-level trigger, you can simply use the :NEW pseudo-record
CREATE TRIGGER name_of_trigger
BEFORE INSERT ON emp
FOR EACH ROW
DECLARE
<<declare variables>>
BEGIN
IF( :new.ename = 'JUSTIN' )
THEN
<<do something if the newly inserted ENAME value is 'JUSTIN'>>
END IF;
END;
For a DDL trigger, the approach is completely different. In that case, the pseudofunctions ora_dict_obj_owner and ora_dict_obj_name will return the owner and name of the table that the DDL statement is operating on.
The row that is being insertes is available as NEW in the trigger
Check out the manual for more details.
http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/create_trigger.htm#BABEBAAB
http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/triggers.htm#LNPLS99955