to be more clear
I want to create a trigger that fires each time a new index is created in my schema
and then save that index name in a separate table let's call it index_log.
in index_log table, there is a creation_time column that tells the creation time of said index
Reinventing the wheel, are we? That information is stored in USER_OBJECTS, I suggest you use it.
Info:
SQL> alter session set nls_date_format = 'dd.mm.yyyy hh24:mi:ss';
Session altered.
SQL> select sysdate from dual;
SYSDATE
-------------------
28.12.2020 16:20:11
Table:
SQL> create table test (id number);
Table created.
Index:
SQL> create index i1t on test (id);
Index created.
When was it created?
SQL> select created from user_objects where object_name = 'I1T';
CREATED
-------------------
28.12.2020 16:20:20
SQL>
If you need to get only the index name and creation time, then you don't need any trigger as demonstrated by #Littlefoot.
Otherwise you can create a DDL-Trigger:
CREATE OR REPLACE TRIGGER audit_index
AFTER CREATE ON hr.SCHEMA
WHEN (ora_dict_obj_type = 'INDEX')
BEGIN
DBMS_OUTPUT.PUT_LINE('Index Name: ' || ora_dict_obj_name);
END;
/
You should have a look at Triggers for Publishing Events to get desired information from trigger event.
Related
I have a table temp_table with the following columns
Id number,
name varchar,
Password varchar,
pwd_change_date timestamp
I want to capture the timestamp in pwd_change_date column only when password column is changed.
So basically i want to use update statement inside the trigger to update timestamp value in pwd_change_date column for the same record.
Example
When a password is changed for one user, I want to capture the timestamp value in pwd_change_date for the same record.
I tried with before insert and after insert of password on temp_table, but getting mutation error. Is it allowed in Oracle to update the Same row/table on which trigger is fired?
You don't need to update the table again; you can modify the data before it is inserted, with a before-insert row level trigger, e.g.:
create trigger trig_pwd_date
before insert or update on temp_table
for each row
when (old.password is null and new.password is not null or new.password != old.password)
begin
:new.pwd_change_date := systimestamp;
end;
/
db<>fiddle demo
This used the new and old correlation names to decide if the password value has changed; and the new correlation name to assign the system time to the field in the pseudorecord, which becomes the column value when the insert completes.
Hopefully you aren't storing plain-text passwords in your table.
SQL> create table temp_table (password varchar2(50), pwd_change_date TIMESTAMP);
Table created.
SQL> create trigger trig_pwd_date
before insert or update on temp_table
for each row
when (old.password is null and new.password is not null or new.password != old.password)
begin
:new.pwd_change_date := systimestamp;
end; 2 3 4 5 6 7
8 /
Trigger created.
SQL> set time on
15:28:42 SQL> insert into temp_table values ('23456',sysdate);
1 row created.
15:29:01 SQL> commit;
Commit complete.
15:29:09 SQL> select * from temp_table;
PASSWORD
PWD_CHANGE_DATE
12345
21-SEP-20 03.28.02.370377 PM
23456
21-SEP-20 03.29.01.478017 PM
I've made a trigger in SQL and need him to write an output after inserting a new row in the table. Please see the example:
CREATE OR REPLACE TRIGGER GAS_CODES AFTER
INSERT ON blablatable
FOR EACH ROW
BEGIN
insert into blabla2table (...,...,...,...)
values (:new...,...,...,..);
---output:
dbms_output.put_line('New row has been added.');
END;
/
When I compile the trigger, it shows in the Script Output, but if I add a new row into the table, there's nothing.
You are missing SET SERVEROUTPUT ON. This command is understandable also by SQLDeveloper.
Let's do a quick test inside the SQLDeveloper.
CREATE USER "TEST_SCHEMA" IDENTIFIED BY "TEST";
User "TEST_SCHEMA" created.
GRANT UNLIMITED TABLESPACE TO "TEST_SCHEMA";
Grant succeeded.
CREATE TABLE "TEST_SCHEMA"."NAMES" ("ID" NUMBER, "NAME" VARCHAR2(25), PRIMARY KEY("ID"));
Table "TEST_SCHEMA"."NAMES" created.
CREATE OR REPLACE TRIGGER "TEST_SCHEMA"."NAMES_TRG_1" AFTER
INSERT ON "TEST_SCHEMA"."NAMES"
FOR EACH ROW
BEGIN
DBMS_OUTPUT.PUT_LINE('New row has been added.');
END;
/
Trigger NAMES_TRG_1 compiled
SET SERVEROUTPUT ON
This command won't print anything in SQL Developer. No worries here.
INSERT INTO "TEST_SCHEMA"."NAMES" VALUES (1, 'Mark Smith');
1 row inserted.
New row has been added.
As you can see, the output was there and it was inserted after the actual row was inserted into the table. Works fine.
To cleanup the testcase, run this:
DROP USER "TEST_SCHEMA" CASCADE;
EDIT 1:
When you are working with Table Data Editor, this is behaving differently. Table Data Editor has its own Oracle session and it has different way of capturing DBMS Output.
To open the DBMS capture window, you need to click on "VIEW" menu and select "DBMS Output" option.
Then click the green plus button and set the database, that will be captured.
Now you can see the output.
Beware as the output here is not "realtime", this window will show something only when there is a buffer flush, and the buffer flush cannot be invoked manually/directly.
Most likely the client (SQLDeveloper) doesn't read the output buffer.
To enable this you must choose from menu "view" -> "dbms output" and then click the green "+" in the dbms output window to read the output buffer for your connection ...
In sqlplus you can do it like this:
SQL> drop table tst purge;
Table dropped.
SQL> drop table tst2 purge;
Table dropped.
SQL> create table tst ( tst_no integer);
Table created.
SQL> create table tst2 ( tst_no integer);
Table created.
SQL> create or replace trigger tst_trg after insert on tst
for each row
begin
insert into tst2 (tst_no) values (:new.tst_no);
dbms_output.put_line('new row with tst_no='|| :new.tst_no);
end;
/ 2 3 4 5 6 7
Trigger created.
SQL> set serveroutput on;
exec dbms_output.enable;
insert into tst values (1); SQL>
PL/SQL procedure successfully completed.
SQL> SQL>
new row with tst_no=1
1 row created.
SQL> r
1* insert into tst values (1)
new row with tst_no=1
1 row created.
SQL> select * from tst2;
TST_NO
----------
1
1
SQL>
as you can see the output is read and printed in sqlplus, and rows are inserted into the target table tst2
hope it helps...
I have created a user (say DROP_PARTITION_USER) to drop partitions of a table. The table is owned by different user (say NORMAL_USER).
Currently, I have granted DROP ANY TABLE and ALTER ANY TABLE privileges to DROP_PARTITION_USER. When DROP_PARTITION_USER executes following statement, it gets executed successfully.
ALTER TABLE SCHEMANAME.TABLENAME DROP PARTITION <PARTITION_NAME> UPDATE GLOBAL INDEXES;
But, DROP ANY TABLE and ALTER ANY TABLE allows DROP_PARTITION_USER to drop and alter any table under any schema [https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_9013.htm ].
Is there any way in Oracle to restrict drop and alter table under specific schema?
The common way to solve this is to create a procedure owned by NORMAL_USER to drop one of the partitions of one of it's tables.
Then you GRANT EXECUTE on this procedure to DROP_PARTITION_USER.
You'll need no extra privileges.
CREATE OR REPLACE PROCEDURE my_drop_partition (p_table_name VARCHAR2, p_partition_name VARCHAR2)
IS
BEGIN
EXECUTE IMMEDIATE 'ALTER TABLE '||p_table_name||' DROP PARTITION '||p_partition_name;
END my_drop_partition;
/
GRANT EXECUTE ON my_drop_partition TO drop_partition_user;
You can use a DDL trigger to capture such attempts and take whatever action you like. For example
SQL> CREATE OR REPLACE TRIGGER STOP_THAT_STUFF
2 before create or alter or drop on database
3 begin
4 if ora_dict_obj_owner in ('SCOTT') and
5 ora_sysevent in ('DROP','ALTER') and
6 ora_dict_obj_name = 'MY_TABLE'
7 then
8 raise_application_error(-20000,'What the hell are you thinking!!!!');
9 end if;
10 end;
11 /
Trigger created.
SQL>
SQL> conn scott/tiger
Connected.
SQL> create table scott.my_table(x int );
Table created.
SQL> create table scott.my_other_table(x int);
Table created.
SQL> drop table my_other_table;
Table dropped.
SQL> drop table my_table;
drop table my_table
*
ERROR at line 1:
ORA-04088: error during execution of trigger 'SYS.STOP_THAT_STUFF'
ORA-00604: error occurred at recursive SQL level 1
ORA-20000: What the hell are you thinking!!!!
ORA-06512: at line 6
SQL> desc my_table
Name Null? Type
----------------------------------------------------------------------- -------- ----------------
X NUMBER(38)
Is it possible to parameterize schema and table names used in queries within stored procedures using dynamic synonyms?
What we have tried is
setting parameters in a name/value pair table
reading those parameters at run time to determine schema (and table names), as the targets change based on the mode of the
application
dropping any existing synonym and recreating for the schema and tables
referencing the synonyms in the queries
The reason we are attempting this approach is because the queries to be executed are merge statements that are hundreds of lines long, not suited for dynamic SQL.
Maybe you can cover your problem when you use Invoker Rights for your procedure. Have a look at this example:
CREATE USER SCOTT_1 IDENTIFIED BY "tiger";
GRANT UNLIMITED TABLESPACE TO SCOTT_1;
CREATE USER SCOTT_2 IDENTIFIED BY "tiger";
GRANT UNLIMITED TABLESPACE TO SCOTT_2;
CREATE TABLE SCOTT_1.EMP (EMP_NAME VARCHAR2(30));
CREATE TABLE SCOTT_2.EMP (EMP_NAME VARCHAR2(30));
INSERT INTO SCOTT_1.EMP VALUES ('Schema 1');
INSERT INTO SCOTT_2.EMP VALUES ('Schema 2');
COMMIT;
CREATE SYNONYM EMP FOR SCOTT_1.EMP; -- Just needed to compile the procedure
CREATE OR REPLACE FUNCTION GetSchema(p_schema IN VARCHAR2) RETURN VARCHAR2
AUTHID CURRENT_USER
AS
res VARCHAR2(30);
BEGIN
EXECUTE IMMEDIATE 'ALTER SESSION SET CURRENT_SCHEMA = ' || p_schema;
SELECT EMP_NAME
INTO res
FROM EMP;
-- Just switch back to own schema to avoid unexpected behaviors
EXECUTE IMMEDIATE 'ALTER SESSION SET CURRENT_SCHEMA = '||USER;
RETURN res;
END;
/
SELECT GetSchema('SCOTT_1') FROM dual;
GETSCHEMA('SCOTT_1')
---------------------------------
Schema 1
1 row selected.
SELECT GetSchema('SCOTT_2') FROM dual;
GETSCHEMA('SCOTT_2')
---------------------------------
Schema 2
1 row selected.
I need to create a trigger that updates another table whenever the trigger table has an insert into command, or an after insert trigger.
I need to pull the id that's being inserted into the table in order to update the other table, how do I go about doing so?
In case that's confusing, another attempt at my question:
Table 1 has an after insert trigger. Said trigger updates table 2 based on one of the id values being inserted into table 1. How do I pull said id value from table 1 in the trigger?
You can use :new in your trigger to reference the values being inserted, for example
create or replace trigger <trigger_name>
after insert on <table_name>
for each row
declare
l_id number;
begin
select :new.id into l_id from dual;
-- now l_id contains the id of the inserted row, do what you want with it
end;
Don't take the example to literally; you don't have to first select :new.id into a variable, you can use it directly in SQL inside the trigger. I did it here just for illustration.
Take a look at the Oracle docs: Coding Triggers
However, you might also want to take a look at some arguments why you should think twice if you really need to put your logic into triggers: The Trouble with Triggers
CREATE OR REPLACE TRIGGER testempdel
AFTER insert ON testemp
FOR EACH ROW
BEGIN
update testdelvalues set salary=:New.sal;
END;
/
Trigger created.
Initially the salary is null
SQL> select * from testdelvalues;
EMPNO EMPNAME SALARY
---------- ---------- ----------
7369 SMITH
After inserting the value in the other table is updated
SQL> insert into testemp (empno,ename,sal) values (1231,'TESTUSER',1000);
1 row created.
SQL> select * from testdelvalues;
EMPNO EMPNAME SALARY
---------- ---------- ----------
7369 SMITH 1000