Skip to Main Content

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Abdullah.

Asked: February 11, 2001 - 6:21 am UTC

Last updated: July 31, 2012 - 12:01 pm UTC

Version: 7.3.4 & 8.0.5

Viewed 10K+ times! This question is

You Asked

Hi, Tom

1)I create this trigger to monitor create and drop statement,bu if the user was open two session from the same machine when doing create or drop statement the trigger will fail, but if i open one session it will successfull. So how can i get the sid,and serial# number for the current session, which i use to issue create statement -so the trigger will fire- the log table is already created.

create or replace trigger ddl_trigger
after create or alter or drop on SCHEMA
declare
l_machine varchar2(15);
l_terminal varchar2(15);
l_prog varchar2(15);
l_osuser varchar2(15);
begin
select machine,terminal,program,osuser into l_machine,l_terminal,l_prog,l_osuser
from v$session
where user = upper(osuser);
insert into log
select ora_sysevent,ora_dict_obj_owner,ora_dict_obj_name,ora_dict_obj_type,
to_char(sysdate,'fm dd/Mon/yyyy HH:MI:SS AM'),l_machine,l_terminal,l_prog,l_osuser
from dual;
end;


2) How can i write a trigger to write logs for any create or drop statement for all object in the database or in a spacefic schema in oracle8i and oracle7.3.4.

3) can i spool the trigger result to OS file or write to the os direct instead of insert into table or write to databse table.

Thanks
Abdullah

and Tom said...

1) the trigger would look like this:

create or replace trigger ddl_trigger
after create or alter or drop on SCHEMA
begin
insert into log
select ora_sysevent,ora_dict_obj_owner,
ora_dict_obj_name,ora_dict_obj_type,
to_char(sysdate,'fm dd/Mon/yyyy HH:MI:SS AM'),
machine,terminal,program,osuser
from v$session
where audsid = sys_context( 'userenv', 'sessionid' );

if ( sql%rowcount <> 1 )
then
raise_application_error
( -20001, 'Unable to id your session' );
end if;
end;
/

2) you cannot, event triggers are a new feature of Oracle8i release 8.1 and up only. You would have to enable AUDITING which frankly captures the same type of information you are above. You might consider just using auditing in 8i as well.

3) UTL_FILE allows PLSQL to write to an OS file.

Rating

  (22 ratings)

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

Comments

GRANT TO V$SESSION

neeti, March 07, 2002 - 10:35 am UTC

I am trying to use the same example on my schema but getting the following error...

Warning: Trigger created with compilation errors.

Elapsed: 00:00:00.17
Errors for TRIGGER DDL_TRIGGER:

LINE/COL ERROR
-------- -----------------------------------------------------------------
2/5 PL/SQL: SQL Statement ignored
7/12 PLS-00201: identifier 'SYS.V$SESSION' must be declared

Please help..


Tom Kyte
March 07, 2002 - 10:45 am UTC

As SYS or "as sysdba" or internal (8i and before) issue:


grant select on v_$session to YOUR_SCHEMA;



It worked....

neeti, March 07, 2002 - 10:50 am UTC

Thanks TOM

Another issue.....

Neeti, March 15, 2002 - 9:34 am UTC

Hi Tom,

Thanks for your quick response. Well the trigger works
fine as long as the user logs into sqlplus in the same schema where the trigger is, but when a diffrent user who
has create any table prev. create table in this schema from his login then the trigger does not get fired and I can not log those changes happening on the schema......

eg.

1) Trigger is on schema A.
2) User logs into schema B with create any table prev.
3) User run the statment from schema B

create table a.t(t number);

table created.

4) No changes are logged through the trigger in schema A.

Please help...

Thanks
Neeti


Tom Kyte
March 15, 2002 - 2:14 pm UTC

Yah -- I used an ON SCHEMA trigger.

Why in the world would you give your developers CREATE ANY TABLE in the first place? But anyway, what you are looking for then is an ON DATABASE trigger, one that is for the database, not just the schema.

UTL_FILE allows PLSQL to write to an OS file

mohammad taha, March 16, 2002 - 10:16 pm UTC

Mr.Tom,

Please give an example until reading that OS file again
to understand it clearly .

Tom Kyte
March 17, 2002 - 10:35 am UTC

this site is all about examples. If you search for

UTL_FILE

you might just be surprised at how many you find

Reader

A reader, June 14, 2002 - 10:34 am UTC

Tom,

Does this DDL_TRIGGER has any potential to interact with
other before insert trigger on another table in the schema.
In the log table for DDL_TRIGGER, I added DATE field also

Recently I created a DDL_TRIGGER and the log was written to
my user schema. On the application schema there is a
before insert trigger on a table.

When I tried to disable the application trigger in the
application schema got errors
ORA-00604 error occurred at recursive SQL level 1
ORA-01401 inserted value too large for that column
ORA-06512

Once I dropped the DDL_TRIGGER , the errors went away

Thanks

Tom Kyte
June 14, 2002 - 10:47 am UTC

read
</code> http://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:3109217869403 <code>

You have a bug in your DDL_TRIGGER -- your "log" table has a column that is insufficient in width to hold the name of the table you are altering.

A reader, June 14, 2002 - 12:46 pm UTC

I am hesitant to ask, this must be pretty basic......

Could you please explain what are these columns??

"select ora_sysevent,ora_dict_obj_owner,
ora_dict_obj_name,ora_dict_obj_type.."

I dont see any ora_ columns in v$session. It returned an invalid column error on 8.1.5 but did not return any errors on 8.1.7.3. But the desc v$session does not show these columns on both versions. Nor does the ref manual for 8.1.7

Could you, Tom, kindly explain more about this...(link to doc would be enough). thanks.


Tom Kyte
June 14, 2002 - 2:02 pm UTC

</code> http://docs.oracle.com/docs/cd/A87860_01/doc/appdev.817/a76939/adg14evt.htm#1000872 <code>

they are event attribute functions -- many added in 816 and more in 817.

Thanks Very Much

A reader, June 14, 2002 - 12:55 pm UTC


A Reader

A reader, June 14, 2002 - 11:41 pm UTC

Thanx Tom.

errors occur during the execution of DDL

an, September 04, 2002 - 2:03 pm UTC

Hi Tom:

the DDL tigger works fine until the database
is rebooted. Once the database is rebooted, errors occur during the execution
of DDL, such as the following:
The following error has occurred:
ORA-00604: error occurred at recursive SQL level 1
ORA-06550: line 123, column 22:
PLS-00103: Encountered the symbol "end-of-file" when expecting one of the
following:
. ( * @ % & - + ; / at for mod rem <an exponent (**)> and or
group having intersect minus order start union where connect
||

When recompile the trigger, it then works fine.
The database is currently rebooted nightly for backups.
now we have to disable the trigger, but we need the trigger stay alive, would you tell me why the errors occur?
Thank you very much.
an


Tom Kyte
September 04, 2002 - 3:37 pm UTC

Well, first of all -- let me get on my podium and shout really loud:

Don't shutdown for backups. Never. It is wasteful (i hesitate to say "stupid" cause peoples feelings get hurt).

Never never never shutdown unattended!!! Never (it is probably a major cause of pagers going off)


Dismount podium....

Now, obviously we have what appears to be a bug of some sort here -- not knowing versions, code, etc etc etc -- I can make these comments:

o contact support, open a tar

or

o shoot yourself for having more then 40/50 lines of code in a trigger. I have a rule that says the entire procedure must fit on a screen (you can run at whatever resolution you like). If the logic is so complex as to require lots of page up/down to read -- you need to modularize the code. At at least 123 lines of code -- I think you should take the body of the trigger -- put it in a package and modularize it. Your trigger will become a single line of code and perhaps whatever was causing the issue will disappear.

You should still do "contact support, open a tar" cause it does appear to be a bug but without an example -- I cannot tell.

"Schema" Triggers Are A Lie ...

Barry Johnson, March 13, 2003 - 9:57 am UTC

The "Another Issue ..." followup on this item is at best disingenuous. The fact is that Oracle's labelling of this feature as a "Schema Trigger" is a *gross* distortion ... *or* Oracle's needs to rewrite its definition of "schema", which would put it at odds with the standard industry definition.

If I log on as "Barry" and ALTER Scott.Emp (via an ALTER ANY TABLE privilege), a DDL trigger tagged to Barry.SCHEMA will fire, but a trigger tied to Scott.SCHEMA will NOT fire. Since Oracle define "schema" as the thing that owns an object (which would be "Scott", NOT "Barry" in this case), how can this feature possibly be honestly described as a "Schema Trigger"??

As for why you would want to do this "ANY" privilege thing ... I guess it is fundamentally because of deficiencies in the standard SQL security model, with Oracle's extensions not doing enough to help. And I plan to talk about it at the IOUG Live! 2003 Conference in Orlando on May 1, and I've spelled it out in the supporting paper which I'd be happy to share beforehand.

In the meantime, the workaround to get a properly implemented Schema trigger is to create a data base trigger that fires when a DDL occurs, and includes a

WHEN( Ora_Dict_Obj_Owner = "<schema of interest>" )

condition as part of its specification, tho' the privilege threshold to be able to create a data base trigger is a bit higher than the other trigger types.

Tom Kyte
March 14, 2003 - 5:16 pm UTC

whatever....

ok, here is the simplified definition:

the schema trigger fires in accordance to the schema you be logged in as.

I guess that is how it gets to be defined as a schema trigger, it is assocated with the schema you are logged into.

Schema is Associated With an Object ...

Barry Johnson, March 18, 2003 - 5:29 pm UTC

... and to say it is simply Session_User is to trivialise the issue. "Application Developers Guide - Fundamentals" defines "schema" as "a security domain that can contain data base objects".

If I log on as "Barry" and do an "ALTER Scott.Emp ...", there is *no* reason for a TRIGGER predicated on "BEFORE DDL ON Barry.SCHEMA" to fire (although this is what currently happens). There is *every* reason for a TRIGGER predicated on "BEFORE DDL ON Scott.SCHEMA" to fire because it is the "Scott" schema that owns the object being changed. There is *no* DDL action being performed "*ON* Barry.SCHEMA".

Let me put it another way: I want to protect "Scott" schema objects against DDL changes. Why would it occur to me - especially given the syntax of "BEFORE DDL ON <schema>.SCHEMA" - that I need to create a separate "schema" trigger against *each and every username* in the data base and, within each trigger, check to see if Ora_Dict_Obj_Owner = "Scott" to decide whether I need to do anything to protect "Scott". Since, in general, I can't know who is going to have what privileges when that may empower them to affect "Scott" so I have to be comprehensive.

(The workaround is actually to create a trigger predicated on "BEFORE DDL ON DATABASE' and add a "WHEN( Ora_Dict_Obj_Owner = 'Scott' )" but, alas, that requires access to an elevated data base privilege.)

No, Oracle need to change the syntax to "BEFORE DDL *BY* <UserName>.*USER*" to make it clear that this is the way it works. No wonder people are confused in the meantime.

Or Oracle could fix it to use Ora_Dict_Obj_Owner as the triggering schema criterium and turn this trigger type into something that would be wonderfully useful. As it is, I've yet to find a redeeming value for it.

Sincerely ... Barry Johnson



Re:DDL Trigger

Joe Bayer, February 18, 2004 - 11:14 am UTC

I am using 9.2.0.4. I found that in most cases, sql like "Alter table blabla drop parttion...." will not show up in v$open_curson, so I rewrite the trigger a little bit.

create or replace trigger ddl_monitor_trigger
after create or alter or drop on schema
declare
l_sysevent varchar2(25);
l_status varchar2(25);
l_obj_owner varchar2(25);
l_obj_name varchar2(25);
l_obj_type varchar2(25);
l_text varchar(1000);
l_sid varchar(10);
begin
select sql_text into l_text from v$session vs,v$sql vsql
where vs.audsid = (select SYS_CONTEXT('USERENV','SESSIONID') from dual)
and vs.SQL_ADDRESS = vsql.address
and vs.SQL_HASH_VALUE = vsql.hash_value;

select ora_sysevent into l_sysevent from dual;
select ora_dict_obj_owner into l_obj_owner from dual;
select ora_dict_obj_name into l_obj_name from dual;
select ora_dict_obj_type into l_obj_type from dual;
select status into l_status from user_objects where object_name = 'MY_PKG'
and object_type='PACKAGE' ;
if (l_status = 'INVALID' )
then
insert into system.my_table (insert_time,command,obj_owner,
obj_name,obj_type,sql_text)
values (sysdate,ora_sysevent,
l_obj_owner,l_obj_name,l_obj_type,l_text);
end if;
end;

Everything works fine except the sql_text, which did not catch the "alter table drop partition", which is the quilty sql during my test, it catch the sql from this trigger. Which is,
"select sql_text into l_text from v$session vs,v$sql vsql
where vs.audsid = (select SYS_CONTEXT('USERENV','SESSIONID') from dual)
and vs.SQL_ADDRESS = vsql.address
and vs.SQL_HASH_VALUE = vsql.hash_value;"

Could you give more help?

Thanks


Tom Kyte
February 18, 2004 - 9:18 pm UTC

why don't you use the builtin event function that gives you the sql text that fired the trigger?

</code> http://docs.oracle.com/docs/cd/B10501_01/appdev.920/a96590/adg14evt.htm#1005274 <code>

since you have 9ir2?

sql_text in 8.1.7

Hatem, May 28, 2004 - 10:15 am UTC

Hi tom
SYS@prod> desc scott.DDLOPERATIONS
Name
---------------------------------
USER_NAME
DDL_DATE
DDL_TYPE
OBJECT_TYPE
OWNER
OBJECT_NAME
IP_ADDRESS
HOST
OS_USER
TERMINAL
TEXT

CREATE OR REPLACE TRIGGER DDLTrigger
AFTER DDL ON DATABASE
declare
l_text varchar2(4000);
BEGIN
select sql_text into l_text from v$session vs,v$sql vsql
where vs.SQL_ADDRESS = vsql.address
and vs.SQL_HASH_VALUE = vsql.hash_value
and vs.audsid = (select SYS_CONTEXT('USERENV','SESSIONID') from dual);

insert into
scott.ddloperations
(
user_name,
ddl_date,
ddl_type,
object_type,
owner,
object_name,
IP_ADDRESS,
HOST,
OS_USER,
TERMINAL,
TEXT)
VALUES
(
ora_login_user,
sysdate,
ora_sysevent,
ora_dict_obj_type,
ora_dict_obj_owner,
ora_dict_obj_name,
sys_context('USERENV', 'IP_ADDRESS'),Sys_context('USERENV', 'HOST'),
sys_context('USERENV', 'OS_USER'), sys_context('USERENV', 'TERMINAL'),
l_text);
END;
/
APPS@prod> create table scott.emp2 as select * from scott.EMP_SNAPSHOT;

Table created.
APPS@prod> select text from scott.DDLOPERATIONS;

TEXT
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SELECT SQL_TEXT FROM V$SESSION VS,V$SQL VSQL WHERE VS.SQL_ADDRESS = VSQL.ADDRESS AND VS.SQL_HASH_VALUE = VSQL.HASH_VALUE AND VS.AUDSID = (SELECT SYS_CONTEXT('USERENV','SESSIONID') FROM DUAL )

my question how to get the DDL into the text?
i see on 92 just usee ora_sql_txt but how to do it in 8i?
Thanks In advance

Tom Kyte
May 28, 2004 - 11:46 am UTC

You cannot -- in 8i, not only is DDL not in the v$ tables, but the triggers have *no way* to access it at all.

A reader, December 19, 2005 - 12:47 pm UTC

Tom,

In Oracle 9206 if i isssue this command

"create user user1 identified by user1;
now this user will have "default" profile can he be assigned somethings else lets say "myprofile" rather than the default when the user is created.

Thanks.



Tom Kyte
December 19, 2005 - 1:01 pm UTC

A reader, December 19, 2005 - 1:42 pm UTC

Tom,

Maybe i wasnt clear enough as to what i am looking for, when the user is created i want his profile to be "MYPROFILE" not default as the user is created on the fly (strange setup, yes) can we use some trigger or something else?

Thanks.

Tom Kyte
December 19, 2005 - 1:46 pm UTC

you would just use the create user command - that was why I pointed you there

create user bob identified by pw profile myprofile;



A reader, December 19, 2005 - 2:02 pm UTC

Tom,

Thanks for the feedback I am aware of the syntax, its created on the fly thats the reason i mentioned strange setup (app server creates user after verifying credentials etc etc). I understand it should be fixed within the app server itself but can we acheive this thru some form of system trigger.

Thanks.

Tom Kyte
December 19, 2005 - 2:11 pm UTC

seems if you want everyone to have this "default" profile, you should, well - i don't know, modify the DEFAULT profile?


You could attempt to automate this by using dbms_job to schedule a job AFTER the create user takes place - but seems you really just want to

a) modify the default profile for all....

b) provide the java guys a stored procedure to call (they shouldn't have CREATE USER in their application code, that is a rather, well POWERFUL privilege - I wouldn't trust an application to run with it) and you can fix that procedure anytime you want easily.

I bet he meant..

Another reader, December 19, 2005 - 2:13 pm UTC

.. automatically. Perhaps he has an application which is issuing the create user command and he wants to automatically assing an alternate profile to the user the application create. My advice: change the default profile to YOUR profile name (i.e. the new one you just created for the application).

A reader, December 19, 2005 - 2:23 pm UTC

Tom,

>>You could attempt to automate this by using dbms_job to >>schedule a job AFTER the create user takes place - but seems >>you really just want to

Initially i was thinking a logon trigger if the user has default profile change it to "MYPROFILE" do you see any issues with that?

Another Reader thanks as well you are correct thats what i am looking for. No we dont want to touch the default profile.

Thanks.

Tom Kyte
December 19, 2005 - 3:34 pm UTC

so, you want to make every login take long - just to try and patch a java program?

I would not be in favor of that, no.


I would touch the default profile, OR fix the code - that java program would never have, should never have CREATE USER.

A reader, December 19, 2005 - 4:00 pm UTC

Thank You Tom point noted.

Reader

A reader, July 25, 2012 - 6:40 pm UTC

After DDL trigger on a database was created. This trigger is acted upon when on a achema, tables are created. I have used ora_dict-obj_owner and ora_dict_obj_type. It works as indended
DB veriosn 10g and 11g

But it generates an recursive SQL if I create a USER on the database

Could you tell us if this normal side effects or if not how to resolve it

Thanks
Tom Kyte
July 30, 2012 - 9:37 am UTC

i don't know what you mean.

Alex, July 30, 2012 - 11:25 pm UTC

Hi Tom,

I have gotten to the same behavior, just different SQL but same behaviour. We have a client/server application were for some reason out of our control it drops a recreate some sequence in some of the options, we can't change the application, the application is deployed in one schema and is accessed from other schemas through synonyms. So when a sequences is recreated we want to give back the grants that it had before, and we are trying (but not succeeding) to do it through an schema trigger.

Leaving the explanation behind, this is the code that we are trying to execute:

CREATE USER GD1 IDENTIFIED BY GD1;
CREATE USER GD2 IDENTIFIED BY GD2;


-- Connected as GD1
CREATE OR REPLACE TRIGGER SEQ_CREATION_TRG AFTER CREATE ON GD1.SCHEMA 
DECLARE
 sql_text ora_name_list_t;
 stmt VARCHAR2(32000);
 n NUMBER;
BEGIN
  IF ora_dict_obj_type = 'SEQUENCE' THEN 
  n := ora_sql_txt(sql_text);
  FOR i IN 1..n LOOP
   stmt := stmt || sql_text(i);
  END LOOP;
  dbms_output.put_line('Running SQL:'||stmt);
  EXECUTE IMMEDIATE 'GRANT SELECT ON ' ||ora_dict_obj_name||' TO GF2'; 
 END IF;
END;
/


Trigger created.

SQL> CREATE SEQUENCE test_SEQ;
CREATE SEQUENCE test_SEQ
*
ERROR at line 1:
ORA-00604: error occurred at recursive SQL level 1
ORA-00942: table or view does not exist
ORA-06512: at line 4

--DBMS_OUTPUT
Running SQL:CREATE SEQUENCE test_SEQ
Running SQL:CREATE SEQUENCE test_SEQ


is there another way to do such grant, I were thinking about firing a dbms_job that gives the necessary permission 10 or 15 seconds later

This is the version of our database:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
PL/SQL Release 11.2.0.3.0 - Production
"CORE 11.2.0.3.0 Production"
TNS for HPUX: Version 11.2.0.3.0 - Production
NLSRTL Version 11.2.0.3.0 - Production

Tom Kyte
July 31, 2012 - 12:01 pm UTC

you have to use a job to do that. use dbms_job to submit the grant to be executed as soon AFTER the sequence is fully created as possible.


You cannot do this in a DDL trigger, the sequence is "created", but not fully there - not exposed to anything else - until after the entire set of events (and all triggers) are done.

The ddl that is the grant will not 'see' the sequence yet, just like any other transaction will not see it.

prevent DDL with DDL trigger

Michael, May 15, 2014 - 9:08 am UTC

hi,

we have a 3rd party application requiring the application owner to have "ALTER USER" and "EXP_FULL_DATABASE" (including "EXECUTE ANY PROCEDURE") privileges.

we are afraid the application owner could use this privs to become DBA by getting proxy login on a default DBA account or granting the DBA role to his users.

i'm wondering if it would be possible to prevent a "ALTER USER <any dba user> GRANT CONNECT THROUGH ..." or "EXECUTE ANY PROCEDURE" by any non-expdp client using a DDL trigger?

cheers, michael

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