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
Related
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;
/
I have a table like having one column containing this data
Dummy Column
Alter PACKAGE ABC COMPILE;
Alter PACKAGE CDE COMPILE;
Alter PROCEDURE ABC COMPILE;
Alter TRIGGER ABC COMPILE;
I want to make a script such that when i run that it will execute the alter statements line by line and perform the DDL operations.
Something like this should work, obviously assuming you have real DDL statements stored in that dummy column.
for loop over the column when the value is not null
replace the ; by nothing in order to use execute immediate
Example
declare
vsql table.dummy_column%type;
begin
for h in ( select dummy_column from table where dummy_column is not null )
loop
vsql := replace(h.dummy_column,';','');
execute immediate vsql;
end loop;
end;
/
I try to create a trigger which automatically grants select on all new tables for a specific schema, whenever a new table in this schema is created.
Background for this is IBM InfoSphere Information Server's Exception Database. This tool creates new tables for exceptions that are created in DataStage Jobs and I want a group of developers to be able to query these tables without giving them permission to the owner of the schema.
So my idea was to create a trigger like this:
create or replace trigger set_permissions
after create on schema
DECLARE
obj_name VARCHAR2(30) := DICTIONARY_OBJ_NAME;
BEGIN
IF DICTIONARY_OBJ_TYPE = 'TABLE'
THEN
GRANT SELECT ON c##ESDB_USER.obj_name TO c##DATASTAGE_USER;
END IF;
END set_permissions;
But I get error "PLS-00103" after compiling the trigger. It says, that "GRANT" is not expected and it expects one of the following instead:
( begin case declare exit for goto if loop mod null pragma raise return select update while with <an identifier> <a double-quoted delimited-identifier> <a bind variable> << continue close current delete fetch lock insert open rollback savepoint set sql execute commit forall merge pipe purge json_exists json_value json_query json_object json_array
Sounds to me that GRANT is not allowed inside a trigger. If that's so, is there another way to automatically grant users select-permission to new tables inside a specific schema?
Error you got says that you can't execute DDL (yes, grant is a DDL) like that - it has to be done as dynamic SQL, using execute immediate.
However, it won't help in this case because DDL implicitly commits, and you can't commit within a trigger.
Now you'll say that you can create a trigger as an autonomous transaction. Well, yes - you can, but it wouldn't help in this case because the table is yet to be created (i.e. it doesn't exist yet).
Here's a workaround; see if it helps. In a few words:
create an auxiliary procedure (to make it simpler) which will, actually, perform grant operation
let trigger submit a job which will call that procedure
Here's how: I'm connected as Scott and will be granting privileges to user Mike (as I don't have your users):
SQL> show user
USER is "SCOTT"
SQL>
SQL> -- Auxiliary procedure
SQL> create or replace procedure p_grant (par_str in varchar2) is
2 begin
3 execute immediate par_str;
4 end;
5 /
Procedure created.
SQL> -- Trigger
SQL> create or replace trigger set_permissions
2 after create on schema
3 declare
4 l_job number;
5 l_str varchar2(200);
6 obj_name varchar2(30) := dictionary_obj_name;
7 begin
8 if dictionary_obj_type = 'TABLE'
9 then
10 l_str := 'GRANT SELECT ON ' ||obj_name || ' TO mike';
11 dbms_job.submit
12 (l_job,
13 'begin p_grant(' || chr(39) || l_str || chr(39) ||'); end;',
14 sysdate
15 );
16 end if;
17 end set_permissions;
18 /
Trigger created.
SQL>
Testing:
SQL> create table test (id number);
Table created.
SQL> insert into test values (222);
1 row created.
SQL> commit;
Commit complete.
Connect as Mike and check what it sees:
SQL> connect mike/lion
Connected.
SQL> select * from scott.test;
ID
----------
222
SQL>
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;
/
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.