Skip to Main Content

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Frederic.

Asked: February 04, 2001 - 3:36 pm UTC

Last updated: June 29, 2011 - 5:35 pm UTC

Version: 8.1.7

Viewed 1000+ times

You Asked

Hello,

My question is related to DDL triggers, which have undergone quite significant improvements from 8.1.6 to 8.1.7.

Still, the available level of information about the DDL that fired the trigger is very poor.

Do you know of any trick that may allow us to see the complete DDL statement (yeah, with storage clauses and all...) ?

I thought about retrieving from v$SQLAREA, but only ALTER DDL can be seen here.

Any idea?

Thanks.

/Frederic.

and Tom said...

You can read whatever you want out of the data dictionary. It is not possible to read the CREATE statment out from anywhere. The DROP we do not need to read (you can reconstruct that). The alter we can get (but don't forget -- some alters are CREATES in disguise!!! alter table t add constraint, that is really a CREATE constraint)..

For example:

tkyte@TKYTE816> create table log
2 ( operation varchar2(25),
3 owner varchar2(25),
4 name varchar2(25),
5 extra varchar2(4000) );

Table created.

tkyte@TKYTE816>
tkyte@TKYTE816> create or replace trigger ddl_trigger
2 after create or alter or drop on SCHEMA
3 declare
4 l_sysevent varchar2(25);
5 l_extra varchar2(4000);
6 begin
7 select ora_sysevent into l_sysevent from dual;
8
9 if ( l_sysevent in ('DROP','CREATE') )
10 then
11 if l_sysevent = 'CREATE'
12 then
13 begin
14 select 'storage ( initial ' || initial_extent ||
15 ' next ' || next_extent || ' .... )'
into l_extra
16 from all_tables
where table_name = ora_dict_obj_name
17 and owner = user;
18 exception
19 when no_data_found then null;
20 end;
21 end if;
22
23 insert into log
24 select ora_sysevent, ora_dict_obj_owner,
25 ora_dict_obj_name, l_extra
26 from dual;
27 elsif ( l_sysevent = 'ALTER' )
28 then
29 insert into log
30 select ora_sysevent, ora_dict_obj_owner,
31 ora_dict_obj_name, sql_text
32 from v$open_cursor
33 where upper(sql_text) like 'ALTER%' ||
34 ora_dict_obj_name || '%'
35 and sid = ( select sid
36 from v$session
37 where audsid=userenv('sessionid') );
38 end if;
39 end;
40 /

Trigger created.

tkyte@TKYTE816> drop table t;
Table dropped.

tkyte@TKYTE816> create table t ( x int );
Table created.

tkyte@TKYTE816> alter table t add y date;
Table altered.

tkyte@TKYTE816> select * from log;

OPERATION OWNER NAME EXTRA
--------- ----- ---- -------------------------------------------
DROP TKYTE T
CREATE TKYTE T storage ( initial 131072 next 131072 .... )
ALTER TKYTE T alter table t add y date



Rating

  (24 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Track DDL statements

Gonela, December 04, 2001 - 2:40 pm UTC

Hi Tom,

Your answer was very helpful.
I've created "DDL_TRIGGER" as you referred and it is
working fine and inserting details into LOG table.
But If any other user having DDL privileges to my schema & executing DDL from remote then changes are not capturing by the trigger..I understand that this trigger is limited to schema level.But to avoid 'AUDIT' option, I am trying to use this trigger. Can you please suggest me, how to track DDLs issued by any privileged user on one particular schema?

Regards,

Tom Kyte
December 05, 2001 - 4:25 pm UTC

you would have to create a DATABASE level DDL trigger that looked at object's schema that was being affected. You would monitor ALL DLL and capture only that which you wanted to capture.

Alex, December 19, 2001 - 10:30 am UTC

Is it possibal to track a select on a table using triggers

like:
Create or replace trigger for select



Tom Kyte
December 19, 2001 - 4:05 pm UTC

No, not in 8i, in 8i you can AUDIT selects and in 9i, there is fine grained auditing that takes this functionality up a level (very similar to a before select trigger).




Some exception

Sue Hsieh, January 15, 2002 - 2:40 pm UTC

Hi Tom,

Thank you very much for this. I use your example to create my own database trigger to track DDL events, and it works very well.

However, there is some exceptions that can cause this trigger to fail. One exception I encounted was that someone inserted a row to a table to cause a invalid trigger to compile(an alter event occurred). However, it is not captured in v$open_cursor, and cause a no_data_found exception. Currently, I only use exception statement to avoid exceptions. Do you have better suggestion with it.

Also do you have suggestions to distinguish alter xxx compile and the real alter object events? When one object got changed, the dependent objects got recompiled, and the last_ddl_time column on dba_objects for those dependent objects got updated as well. So, how can we distinguish from data dictionary tables for this?

Thanks,

Shuhsin


Tom Kyte
January 15, 2002 - 9:47 pm UTC

upgrade to 9i and you'll be able to get the firing statement via a function call...

If its not in v$open_cursor -- you'll have to not audit it.

timestamp

an, January 16, 2002 - 11:05 am UTC

Hi Tom:

it's very hopeful, i ran it at our db, works very well.
can i also get timestamp from the trigger?
thank you very much,

Tom Kyte
January 16, 2002 - 3:13 pm UTC

just use sysdate

Where Can we get the Trigger_Code in Dictionary.

Qaisar, January 16, 2002 - 1:18 pm UTC

It's so helpful.

Where can we extract the Trigger Body or Code from data Dictionary like the Other trigger's code is in DBA_TRIGGERS.

Can you please through some light for extracting the code. Thanks

Tom Kyte
January 16, 2002 - 3:24 pm UTC

This works in many cases:

set echo off
set long 50000
set verify off
set feedback off
set termout off
set heading off
set pagesize 0
spool &1..sql

select
'create or replace trigger "' ||
trigger_name || '"' || chr(10)||
decode( substr( trigger_type, 1, 1 ),
'A', 'AFTER', 'B', 'BEFORE', 'I', 'INSTEAD OF' ) ||
chr(10) ||
triggering_event || chr(10) ||
'ON "' || table_owner || '"."' ||
table_name || '"' || chr(10) ||
decode( instr( trigger_type, 'EACH ROW' ), 0, null,
'FOR EACH ROW' ) || chr(10) ,
trigger_body
from user_triggers
where trigger_name = upper('&1')
/
prompt /

spool off
set verify on
set feedback on
set termout on
set heading on

excellent

zhu chao, January 17, 2002 - 7:13 am UTC

A little question:
How can i record the truncate statement? In my test, truncate command is not recorded.
I add it in
if ( l_sysevent in ('DROP','CREATE','TRUNCATE' ))
But it doesnot work.

And:
Which Oracle document talk about the usage of commands like:
select ora_sysevent into l_sysevent from dual;.
How can i findout all the possible ora_sysevent ? from which document?
Thanks.

add on

zhu chao, January 17, 2002 - 7:17 am UTC

Sorry, tom, i ask a foolish question, i find it out.
Just add or truncate in the after statement.
Thanks.

Sorry if I could not make it clear.

Qaisar, January 17, 2002 - 9:20 am UTC

My Question is to get the code about the Database Triggers/ Instance Triggers.

I knew that we can get the code from User_, All_ and DBA_Triggers, I am under the impression that this stores only Table Triggers, is it right? If yes, Where can I get the Info about Instance Triggers.

Thanks.

Tom Kyte
January 17, 2002 - 11:51 am UTC

tkyte@TKYTE816> select distinct triggering_event from dba_triggers;

TRIGGERING_EVENT
--------------------------------------------------------------------------

DELETE
DROP
INSERT
SHUTDOWN <<<<====
STARTUP <<<<====
UPDATE

6 rows selected.

they are in the same place...

how about pwk

A reader, March 08, 2002 - 10:07 am UTC

Hi Tom:

i was very happy when i ran ddl_triger script in our db, we only have one major db, but many users use and modify it, so i do need to set at database level to monitor the db. recently i found out the log table restored also pwd when a user changes his pwd. i just wonder it's possible if i get rid of the use pwd record?

thank you very much,
an

Tom Kyte
March 08, 2002 - 2:28 pm UTC

You own the trigger - you can make it do whatever you want!

Instead of

29 insert into log
30 select ora_sysevent, ora_dict_obj_owner,
31 ora_dict_obj_name, sql_text
32 from v$open_cursor
33 where upper(sql_text) like 'ALTER%' ||
34 ora_dict_obj_name || '%'
35 and sid = ( select sid
36 from v$session
37 where audsid=userenv('sessionid') );



select OUT the sql_text and if it is like 'ALTER%USER%IDENTIFIED%BY%' -- substring it and then put it into the log.


I should also point out that in 9i there is a function -- ora_sql_text -- that'll give you the text of the statement that fired the trigger, it'll be much easier to capture that stuff in 9i using that function.

Am, April 11, 2002 - 6:11 am UTC

I dont know what is wrong but I get error
(1):PL/SQL: ORA-00942: table or view does not exit
when I try to save it

but when I comment the "
elsif ( l_sysevent = 'ALTER' )
then
insert into log
select ora_sysevent, ora_dict_obj_owner,
ora_dict_obj_name, sql_text
from v$open_cursor
where upper(sql_text) like 'ALTER%' ||
ora_dict_obj_name || '%'
and sid = ( select sid
from v$session
where audsid=userenv('sessionid') ); " it works

Can you help me



Tom Kyte
April 11, 2002 - 7:36 am UTC

read

</code> http://asktom.oracle.com/Misc/RolesAndProcedures.html <code>

to understand why and then have sys or someone connected as sysdba:

grant select on v_$open_cursor to OWNER_OF_TRIGGER;
grant select on v_$session to OWNER_OF_TRIGGER;

and try again.

DDL triggers in 8.1.7", version 8.1.7

Ted, January 29, 2003 - 2:08 pm UTC

When searching for triggers with the ALL_TRIGGERS views for an ID with without triggers, no triggers are returned. The sys.all_triggers has "select" granted to public. Looks like a privilege issue. (System DBA's will not grant select on DBA_TRIGGERS to app id's). Any idea on how to find the triggers on all id's via the "all_triggers" view?
If it is a privilege issue, what privilege is needed.

Thanks for help.

Tom Kyte
January 29, 2003 - 2:19 pm UTC

what?

do you mean "when I query all_triggers -- i see no rows". that is the only conclusion I could come to.

All_triggers shows you the triggers you are allowed to see, no more, no less



You can read the text of all_triggers out of all_views. In there you will find things like:

(
trigobj.owner# = userenv('SCHEMAID') or
tabobj.owner# = userenv('SCHEMAID') or
exists (select null from sys.sysauth$
where grantee# in (select kzsrorol from x$kzsro)
and privilege# = -152 /* CREATE ANY TRIGGER */)))
)

which says

trigobj.owner# = userenv('SCHEMAID') or ==> you OWN the trigger
tabobj.owner# = userenv('SCHEMAID') or ==> you OWN the table the trigger is
on


exists (select null from sys.sysauth$ ==> you have the CREATE ANY TRIGGER
priv





So, by its very definition (and this follows the way all of the ALL_ views are designed to work)

o you have to own the trigger
o own the table the trigger is on
o have the CREATE ANY TRIGGER priv

DBA_TRIGGERS lets you see *all* triggers, regardless. That is "how to find the triggers on all id's" -- by its very definition you cannot use ALL_TRIGGERS (unless you have CREATE ANY TRIGGER -- but that would be less likely then getting a DBA to grant on DBA_TRIGGERS)

Now, that aside for a moment -- if your application truly needs this information, that is you have a BUSINESS REQUIREMENT for this data -- and the DBA's say "nope, no go", then you have a problem with your work environment. The DBA views are not mystical/magical, they just let you see the definition of everything. If you have a business need to see everything there is no reason they should not give you access to it.





DDL triggers in 8.1.7", version 8.1.7

Ted, January 29, 2003 - 2:21 pm UTC

Tom,

Just found the answer to my question in another question/answer on your web page "No data in All_Triggers table".

Thanks Again


DDL-Trigger-Attributes

Jens, January 29, 2003 - 3:48 pm UTC

hi tom,

i created two simple schema triggers - firing before and after DDL-events create, drop, alter, rename.

why do i get the same (old) object name in both triggers for attribute "ORA_DICT_OBJ_NAME" when I issue a rename?

is there any way to get the object-id (which stays the same in the event of a rename) from a trigger-attribute?

Tom Kyte
January 30, 2003 - 7:51 am UTC

you would take the name/owner/type and goto *_objects with that to find out anything else you wanted.

Dictionary shows OLD data in AFTER ddl trigger?

Yuliy, March 15, 2003 - 1:59 am UTC

Tom,

Excellent info.

I noticed, however, that even in an AFTER DDL (ON SCHEMA) trigger, some data dictionary views (USER_TAB_COLUMNS is what I am interested in) supply OLD information, that is, as it was BEFORE the ddl. I have the test code, but you'll see what I mean if you try 'alter table add...' and 'alter table modify...' to add columns/change columns' datatype and watch the result from within an AFTER DDL ON SCHEMA trigger. You'll find that USER_TAB_COLUMNS still shows the columns and types as they were *prior* to ddl.

On the other hand, the trigger/dictionary work as expected when you CREATE a new table or ALTER TABLE DROP COLUMN -- they show columns as if the ddl has completed, just as one would expect for an AFTER trigger.

In contrast, a BEFORE DDL trigger shows a very consistent view of the dictionary BEFORE any modifications.

Confused... I really hoped for the "new" dictionary information in my AFTER trigger. Help! (version 9.2.0.1.0)

Tom Kyte
March 15, 2003 - 9:09 am UTC

the information you are looking for is in the ora_ functions.

OK

Jim, March 08, 2004 - 10:31 am UTC

Dear Tom,
In the create trigger statement I found a clause like
" referencing parent as parent".What does parent denote and
how to make use of it?
Thanks in advance.
Bye!


Tom Kyte
March 08, 2004 - 2:02 pm UTC

<quote ref=sql reference>

The referencing_clause lets you specify correlation names. You can use correlation names in the PL/SQL block and WHEN condition of a row trigger to refer specifically to old and new values of the current row. The default correlation names are OLD and NEW. If your row trigger is associated with a table named OLD or NEW, use this clause to specify different correlation names to avoid confusion between the table name and the correlation name.

o If the trigger is defined on a nested table, then OLD and NEW refer to the row of the nested table, and PARENT refers to the current row of the parent table.

</quote>

OK

Rajan, March 24, 2004 - 5:31 am UTC

Dear Tom,
Do you have any simple examples for cascaded triggers?what
can be the maximum allowed levels for such triggers?Please
do reply.
Bye!


Tom Kyte
March 24, 2004 - 9:03 am UTC

no, no examples (i try to avoid triggers actually, only use them when absolutely necessary)

there are no limits other than the number of open cursors you can have (you control that).

OK

Catherine, April 08, 2004 - 6:26 am UTC

Hi Tom,
How to make use of the event attribute function "ora_sql_text"?Do you have a simple example for it?Please do write a followup.

Tom Kyte
April 08, 2004 - 10:22 am UTC

you use it in an event trigger to see what statement fired the trigger.

i mistyped, should be ora_sql_TXT -- if you search for that, you'll see examples

Is there any timestamp for newly added columns?

Thaha Hussain, August 25, 2004 - 3:24 am UTC

Dear Tom,

Is there any timestamp for newly added columns? Pls tell me how to get it.



Tom Kyte
August 25, 2004 - 7:39 am UTC

there is a last_ddl_time for the table, not at the column level.

just to clarify

Susan, August 26, 2004 - 2:54 pm UTC

there is no way to capture the create statements, you can only log the create? We're running 8.1.7 and I've been asked if there is a way to use an event trigger to prevent creation of pl/sql code that doesn't contain a versioning comment. Believe me I know this is not the way to ensure versioning/release compliance. Basically, they want to search the incoming statement if it doesn't contain certain text (relating to the versioning directory path) then throw an exception that doesn't allow the create. I'm pretty sure this can't be done, am I right? Thanks.

Tom Kyte
August 26, 2004 - 3:39 pm UTC

8i -- no, the function needed to get the triggering text in the DDL trigger did not exist.

9i, yes, you can -- the create/alter whatever statement is accessible via a builtin function.




OK

Kumar, March 14, 2005 - 10:23 am UTC

Dear Tom,
Are there any init.ora parameters which affect cascaded triggers?
People say about open_cursors.Is it true?



Tom Kyte
March 14, 2005 - 10:43 am UTC

well, open cursors will limit the depth -- as each trigger invocation will be a cursor and presumably the triggers themselves would use some as well

but if you hit it, i would say you have a design flaw! (triggers should be simple, concise, to the point and not have tons of cascading going on)

ora_sql_text function

Sasa, June 21, 2005 - 11:36 am UTC

Hi Tom,
Ora_sql_text function returns null in trigger in 10g while this was working fine in 9i.
Do you happen to know any problem related to this?
Regards,
Sasa

PS
Oracle Database 10g Enterprise Edition Release 10.1.0.2.0 - Prod
PL/SQL Release 10.1.0.2.0 - Production
CORE 10.1.0.2.0 Production
TNS for 32-bit Windows: Version 10.1.0.2.0 - Production
NLSRTL Version 10.1.0.2.0 - Production



How to track datafile modification with trigger ?

JLU, October 06, 2005 - 1:09 pm UTC

Hi tom,

Is it possibal to track an alter on datafile using triggers ?

I saw that it's possible to use :

Trigger after create,drop or alter on database : but this not works for datafile...

Any idea ?

Thanks.



Tom Kyte
October 06, 2005 - 1:43 pm UTC

Well, the alter's are recorded into the alert log and you can use AUDITING as well - but the DDL trigger doesn't fire for ALTER DATABASE (and since the database isn't a catalog object, not for the ALTER trigger either)

Is this getting better in 10g and 11g

Mark Brady, June 29, 2011 - 4:06 pm UTC

Is there any better way of determining WHAT table just tried or just completed the CREATE Process in the latest versions of the RDBMS?
Tom Kyte
June 29, 2011 - 5:35 pm UTC

I'm not sure what you mean?

Yes, Sorry for being vague

Mark Brady, June 30, 2011 - 1:32 pm UTC

I was looking for a method to determine when a CREATE failed. BEFORE DDL triggers don't seem to fire when a CREATE errors, so I'm assuming that the valid name, permission checks, existing object checks all occur prior to the Before Triggers being fired.

I didn't realize that ServerError would capture those, So between DDL and ServerError I'm covered.

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library