DDL trigger not working when trying to drop table - sql

I am trying to create a simple DDL trigger that won't let me drop any table inside my database
The trigger creates but I get an error when I try to test the trigger if it is working or not:
`CREATE OR REPLACE TRIGGER Exercitiul12
BEFORE DROP ON SCHEMA
BEGIN
RAISE_APPLICATION_ERROR(-20001, 'Nu puteti sterge tabelul ' || ora_dict_obj_name);
END;
/
BEGIN
DROP TABLE Restaurant;
END;
/`
How can I solve the problem?

You can try something like this:
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;
/

Related

Drop partition trigger in Oracle

How can we create a drop partition DDL trigger in Oracle.
For eg
Suppose we create DDL trigger to capture or prevent if someone fires drop TABLE/INDEX or naything in our schema.
Create or replace trigger my_trg
After DROP on schema
Can we do same for drop partition .
Can we store the detail ..like this partition was dropped by this particular user.
Can we prevent user from dropping partition through trigger or capture which user dropped the partition
The trigger would be this one:
CREATE OR REPLACE TRIGGER LOG_ALTER
BEFORE ALTER ON SCHEMA
WHEN (ora_dict_obj_type = 'TABLE' AND ora_dict_obj_name = 'YOUR_TABLE')
DECLARE
sqlcmd T_USER_LOGGING.ULOG_SQL_CMD%TYPE;
sql_text ora_name_list_t;
n NUMBER;
BEGIN
n := ora_sql_txt(sql_text);
IF n IS NOT NULL THEN
FOR i IN 1..n LOOP
sqlcmd := sqlcmd || sql_text(i);
END LOOP;
END IF;
IF REGEXP_LIKE(sqlcmd, 'DROP\s+PARTITION', 'i') THEN
RAISE_APPLICATION_ERROR(-20010, 'User '||ora_login_user||' must not drop partition from table YOUR_TABLE');
END IF;
END;
/
But note, a user with ADMINISTER DATABASE TRIGGER system privilege can drop the partition anyway as he ignores the RAISE_APPLICATION_ERROR

Oracle procedure/function to create a trigger in table

I'm trying to create a procedure that given a table name, it will create a sequence and auto incrementing trigger, all using variables based on the table name.
Code :
CREATE OR REPLACE procedure CREATE_SEQUENTIAL_TR(table_name VARCHAR)
is -- Tried using declare but it wouldn't accept
coluna_cod varchar(100 char);
begin
--Finding the cod column name for this table first
--They start with "PK_CD"
select
COLUMN_NAME
into
coluna_cod
from
ALL_TAB_COLUMNS
where
TABLE_NAME=table_name
and COLUMN_NAME like "PK_CD%";
--Creating the sequence obj
drop sequence "cod" || table_name;
create sequence "cod" || table_name;
--Now creating the trigger
create or replace trigger "cod" || table_name || "tr"
before
UPDATE or INSERT on table_name
for each row
declare
cod number := coluna_cod;
tr_name varchar(100 char) := "cod" || table_name
begin
if UPDATING then
if :new.cod != :old.cod then
:new.cod := :old.cod;
end if;
else -- inserting
:new.cod := tr_name.nextval();
end if;
end;
end;
The complexity of this ended up quite out of the scope of my knowledge.
At the moment it is giving an error on drop sequence "cod" || table_name (Unexpected DROP symbol found) but I'm sure I have made other errors.
Can someone help me figure this logic out?
You can't put DDL statements (like drop or create or alter) directly inside a PL/SQL block. If you want to do DDL inside PL/SQL, you can do an execute immediate:
declare
begin
drop sequence X; -- error
execute immediate 'drop sequence X'; -- works fine
end;
/

EXECUTE IMMEDIATE PL/SQL?

CREATE OR REPLACE TRIGGER P88
AFTER INSERT ON reparation
FOR EACH ROW
DECLARE
vope number;
BEGIN
SELECT observation_reparation into vope from repartion;
if(vope IS NULL)THEN
EXECUTE IMMEDIATE 'ALTER TABLE ' || reparation.observations_Reparation || ' MODIFY libelle_piece NVARCHAR2(50)';
END IF;
END;
/
I get this:
error:table or view does not exist.
CREATE OR REPLACE TRIGGER P88
AFTER INSERT ON reparation
FOR EACH ROW
DECLARE
vope number;
BEGIN
SELECT observation_reparation into vope from repartion;
if(vope IS NULL)THEN
EXECUTE IMMEDIATE 'ALTER TABLE reparation RENAME COLUMN observations_Reparation TO libelle_piece';
END IF;
END;
if you also need to change the declaration of the column you will need another ALTER statement
alter table reparation modify (libelle_piece NVARCHAR2(50))

create a trigger before insert

I want to create a trigger before insert and check inside the trigger function the summary_id values of the new row to be inserted, to see if the summary_id already exists in the table. if it exists then the trigger should return null as I don't want to insert duplicate values. I have written this function, but, when I tried to add a new duplicate row, it was inserted successfully!
CREATE OR REPLACE FUNCTION trigger_ack_bi()
RETURNS trigger AS $$
--DECLARE
-- max_points INTEGER;
BEGIN
IF(TG_OP = 'INSERT') THEN
IF NEW.ack_summary_id = (SELECT ack_summary_id FROM scm_main.tbl_ack WHERE scm_main.tbl_ack.ack_summary_id = NEW.ack_summary_id LIMIT 1) THEN
RETURN NULL;
ELSE
RETURN NEW;
END IF;
END IF;
END;
$$LANGUAGE plpgsql;
CREATE TRIGGER trigger_ack_bi BEFORE INSERT ON
scm_main.tbl_ack
FOR EACH ROW
EXECUTE PROCEDURE trigger_ack_bi()
ALTER FUNCTION trigger_ack_bi() OWNER TO postgres;
I'd recommend setting column to be NOT NULL and creating a PK on it...
alter table scm_main.tbl_ack alter column ack_summary_id set NOT NULL;
alter table scm_main.tbl_ack add primary key (ack_summary_id);
it seems the trigger function is correct. But, it is running in silent mode. raising an exception was what I needed to make sure that duplication had been prevented. Thank you for your other solutions.
CREATE OR REPLACE FUNCTION trigger_ack_bi()
RETURNS trigger AS $$
--DECLARE
-- max_points INTEGER;
BEGIN
IF(TG_OP = 'INSERT') THEN
IF NEW.ack_summary_id IS NULL THEN
RAISE EXCEPTION 'summaryID cannot be null';
END IF;
IF NEW.ack_summary_id = (SELECT ack_summary_id FROM scm_main.tbl_ack WHERE scm_main.tbl_ack.ack_summary_id = NEW.ack_summary_id LIMIT 1) THEN
RAISE EXCEPTION 'duplication prevented..';
RETURN NULL;
ELSE
RETURN NEW;
END IF;
END IF;
END;
$$LANGUAGE plpgsql;
CREATE TRIGGER trigger_ack_bi BEFORE INSERT ON
scm_main.tbl_ack
FOR EACH ROW
EXECUTE PROCEDURE trigger_ack_bi()

Ora-04072: INVALID TRIGGER TYPE

I'm trying to execute the following SQL statement on Oracle 11g. I'm not experienced when it comes to Oracle and I'm not sure why this is failing. This query was provided to me by our developer.
I was attempting to execute this through the SQL worksheet in OEM.
CREATE OR REPLACE TRIGGER TBL_ADMINCOMMAND_TRG BEFORE
INSERT OR UPDATE ON tbl_AdminCommands FOR EACH ROW
BEGIN
IF inserting
AND :new.ADMINCOMMANDID IS NULL THEN
SELECT TBL_ADMINCOMMANDS_SEQ.nextval INTO :new.ADMINCOMMANDID FROM DUAL;
END IF;
END;
ALTER TRIGGER TBL_ADMINCOMMAND_TRG ENABLE;
The code you show works for me, but only as two separate commands:
1)
CREATE OR REPLACE TRIGGER TBL_ADMINCOMMAND_TRG BEFORE
INSERT OR UPDATE ON tbl_AdminCommands FOR EACH ROW
BEGIN
IF inserting
AND :new.ADMINCOMMANDID IS NULL THEN
SELECT TBL_ADMINCOMMANDS_SEQ.nextval INTO :new.ADMINCOMMANDID FROM DUAL;
END IF;
END;
2)
ALTER TRIGGER TBL_ADMINCOMMAND_TRG ENABLE;
Try doing them one at a time.
As an aside, this line:
SELECT TBL_ADMINCOMMANDS_SEQ.nextval INTO :new.ADMINCOMMANDID FROM DUAL;
can be simplified to this in 11G:
:new.ADMINCOMMANDID := TBL_ADMINCOMMANDS_SEQ.nextval;
In fact, the whole trigger can be simplified to:
CREATE OR REPLACE TRIGGER TBL_ADMINCOMMAND_TRG
BEFORE INSERT ON tbl_AdminCommands
FOR EACH ROW
WHEN (NEW.ADMINCOMMANDID IS NULL)
BEGIN
:new.ADMINCOMMANDID := TBL_ADMINCOMMANDS_SEQ.nextval;
END;
If you are using SQL*Plus, you should end your PL/SQL commands with a single forward slash on a line by itself:
CREATE OR REPLACE TRIGGER TBL_ADMINCOMMAND_TRG
BEFORE INSERT OR UPDATE ON tbl_AdminCommands
FOR EACH ROW
BEGIN
IF inserting AND :new.ADMINCOMMANDID IS NULL
THEN
SELECT TBL_ADMINCOMMANDS_SEQ.nextval
INTO :new.ADMINCOMMANDID
FROM DUAL;
END IF;
END;
/
ALTER TRIGGER TBL_ADMINCOMMAND_TRG ENABLE;
Also note that if your trigger uses IF inserting you could do only a trigger BEFORE INSERT.