Search code examples
oracle-databaseddl-trigger

Oracle DDL trigger: how to retrieve more details about the event occurred?


I want to make an history on an Oracle DB about all the DDL operations which are executed by the time.

I have created a TABLE and a DDL TRIGGER for do that, in this way:

CREATE TABLE AUDIT_DDL (
  D DATE,
  OSUSER VARCHAR2(255),
  CURRENT_USER VARCHAR2(255),
  HOST VARCHAR2(255),
  TERMINAL VARCHAR2(255),
  OWNER VARCHAR2(30),
  TYPE VARCHAR2(30),
  NAME VARCHAR2(30),
  SYSEVENT VARCHAR2(30));

--/   
CREATE OR REPLACE TRIGGER AUDIT_DDL_TRG AFTER DDL ON SCHEMA 
BEGIN
  IF (ORA_SYSEVENT='TRUNCATE')
  THEN
    NULL;
  ELSE
    INSERT INTO AUDIT_DDL(D, OSUSER,CURRENT_USER,HOST,TERMINAL,OWNER,TYPE,NAME,SYSEVENT)
    VALUES(
      SYSDATE,
      SYS_CONTEXT('USERENV','OS_USER') ,
      SYS_CONTEXT('USERENV','CURRENT_USER') ,
      SYS_CONTEXT('USERENV','HOST') , 
      SYS_CONTEXT('USERENV','TERMINAL') ,
      ORA_DICT_OBJ_OWNER,
      ORA_DICT_OBJ_TYPE,
      ORA_DICT_OBJ_NAME,
      ORA_SYSEVENT
    );
  END IF;
END;
/

This works perfectly: after each DDL statement, I have a new row in the AUDIT_DDL table.

But I still don't have any information about the exact kind of operation has been made.

For example, the following two statements will produce the same ALTER SYSEVENT in the AUDIT_DDL table:

ALTER TABLE MYTABLE RENAME COLUMN TEMP TO NEWTEMP;

ALTER TABLE MYTABLE DROP COLUMN NEWTEMP;

So, in this way, I can't know what operation has been made rather than a generic ALTER TABLE, and I can't even know if the column TEMP has been renamed or dropped from the table MYTABLE!

My question is: how can I retrieve some more information about the event has occurred after the DDL execution (object involved, details, etc...)?


Solution

  • check the below:

    drop the trigger AUDIT_DDL_TRG

    drop trigger AUDIT_DDL_TRG
    

    create a new column

     alter table AUDIT_DDL add statements varchar2(1000);
    

    run the trigger again

    CREATE OR REPLACE TRIGGER AUDIT_DDL_TRG
      AFTER DDL ON SCHEMA
    
    DECLARE
      sql_text ora_name_list_t;
      v_stmt   VARCHAR2(2000);
      n        PLS_INTEGER;
    BEGIN
      n := ora_sql_txt(sql_text);
      FOR i IN 1 .. n LOOP
        v_stmt := v_stmt || sql_text(i);
      END LOOP;
    
    
    v_stmt :=regexp_replace(v_stmt,
                                                       'rename[[:space:]]+.*[[:space:]]+to[[:space:]]+([a-z0-9_]+)',
                                                       '\1',
                                                       1,
                                                       1,
                                                       'i');
      IF (ORA_SYSEVENT = 'TRUNCATE') THEN
        NULL;
      ELSE
        INSERT INTO AUDIT_DDL
          (D,
           OSUSER,
           CURRENT_USER,
           HOST,
           TERMINAL,
           OWNER,
           TYPE,
           NAME,
           SYSEVENT,
                 statements)
        VALUES
          (SYSDATE,
           SYS_CONTEXT('USERENV', 'OS_USER'),
           SYS_CONTEXT('USERENV', 'CURRENT_USER'),
           SYS_CONTEXT('USERENV', 'HOST'),
           SYS_CONTEXT('USERENV', 'TERMINAL'),
           ORA_DICT_OBJ_OWNER,
           ORA_DICT_OBJ_TYPE,
           ORA_DICT_OBJ_NAME,
           ORA_SYSEVENT,
                 v_stmt);
      END IF;
    END;
    /
    

    do some alters, and youll see the statement