1. 程式人生 > >Oracle 觸發器實現DDL語句監控

Oracle 觸發器實現DDL語句監控

前言

建立此觸發器的主要目的是為了控制資料庫的版本,雖然會將DDL語句保留但難免會出現遺漏,所以建立DDl觸發器記錄DDL操作,主要是用來核對資料庫變更的SQL語句

建立使用者並授權

#需要使用sys使用者授權

CREATE USER dbadmin IDENTIFIED BY dbadmin DEFAULT TABLESPACE DDL_AUD;

GRANT CONNECT TO dbadmin;
GRANT DBA TO dbadmin;
GRANT SELECT ON SYS.V_$OPEN_CURSOR TO dbadmin;

建立序列及表

DROP SEQUENCE SEQ_DDL_VERSION;
CREATE SEQUENCE SEQ_DDL_VERSION INCREMENT BY 1 START WITH 1 NOMAXVALUE NOMINVALUE NOCYCLE NOCACHE;

DROP TABLE TB_SYSTEM_DDL_LOGS CASCADE CONSTRAINTS;

/*==============================================================*/
/* TABLE: TB_SYSTEM_DDL_LOGS                                    */
/*==============================================================*/
CREATE TABLE TB_SYSTEM_DDL_LOGS 
(
   EVENT_ID             VARCHAR2(32)         DEFAULT SYS_GUID() NOT NULL,
   EVENT_NAME           VARCHAR2(20),
   TERMINAL             VARCHAR2(50),
   DB_NAME              VARCHAR2(50),
   OBJECT_NAME          VARCHAR2(30),
   OBJECT_NAME_LIST     VARCHAR(300),
   OBJECT_OWNER         VARCHAR2(30),
   OBJECT_TYPE          VARCHAR2(20),
   IS_ALTER_COLUMN      VARCHAR(10),
   IS_DROP_COLUMN       VARCHAR(10),
   SQL_ID               VARCHAR(13),
   SQL_TEXT             CLOB,
   CURRENT_USER         VARCHAR(30),
   CURRENT_USERID       NUMBER,
   SESSION_USER         VARCHAR(10),
   SESSION_USERID       NUMBER,
   PROXY_USER           VARCHAR(30),
   PROXY_USERID         NUMBER,
   CURRENT_SCHEMA       VARCHAR(30),
   HOST                 VARCHAR(100),
   OS_USER              VARCHAR(60),
   IP_ADDRESS           VARCHAR(32),
   DDL_TIME             DATE                 DEFAULT SYSDATE,
   SESSION_ID           VARCHAR(32),
   VERSION_NO           NUMBER,
   CONSTRAINT PK_TB_SYSTEM_DDL_LOGS PRIMARY KEY (EVENT_ID)
);

COMMENT ON TABLE TB_SYSTEM_DDL_LOGS IS
'【資料庫日誌】DDL日誌表';

COMMENT ON COLUMN TB_SYSTEM_DDL_LOGS.EVENT_ID IS
'事件ID自動生成';

COMMENT ON COLUMN TB_SYSTEM_DDL_LOGS.EVENT_NAME IS
'事件名稱';

COMMENT ON COLUMN TB_SYSTEM_DDL_LOGS.TERMINAL IS
'客戶端作業系統終端的名稱';

COMMENT ON COLUMN TB_SYSTEM_DDL_LOGS.DB_NAME IS
'資料庫名稱';

COMMENT ON COLUMN TB_SYSTEM_DDL_LOGS.OBJECT_NAME IS
'DDL發生的物件名稱';

COMMENT ON COLUMN TB_SYSTEM_DDL_LOGS.OBJECT_NAME_LIST IS
'物件列表';

COMMENT ON COLUMN TB_SYSTEM_DDL_LOGS.OBJECT_OWNER IS
'DDL發生物件的宿主';

COMMENT ON COLUMN TB_SYSTEM_DDL_LOGS.OBJECT_TYPE IS
'物件類別';

COMMENT ON COLUMN TB_SYSTEM_DDL_LOGS.IS_ALTER_COLUMN IS
'當列被修改的時候為真,否則為假 ';

COMMENT ON COLUMN TB_SYSTEM_DDL_LOGS.IS_DROP_COLUMN IS
'當列被DROP的時候為真,否則為假 ';

COMMENT ON COLUMN TB_SYSTEM_DDL_LOGS.SQL_ID IS
'SQL_ID';

COMMENT ON COLUMN TB_SYSTEM_DDL_LOGS.SQL_TEXT IS
'SQL語句';

COMMENT ON COLUMN TB_SYSTEM_DDL_LOGS.CURRENT_USER IS
'當前SESSION擁有許可權的使用者的名稱(比如說當前SESSION是SYS,但是正在執行system.myproc,那麼current_user就是system)';

COMMENT ON COLUMN TB_SYSTEM_DDL_LOGS.CURRENT_USERID IS
'當前SESSION擁有的許可權的使用者的ID';

COMMENT ON COLUMN TB_SYSTEM_DDL_LOGS.SESSION_USER IS
'session所屬的使用者名稱';

COMMENT ON COLUMN TB_SYSTEM_DDL_LOGS.SESSION_USERID IS
'當前SESSION所屬的使用者id';

COMMENT ON COLUMN TB_SYSTEM_DDL_LOGS.PROXY_USER IS
'開啟當前SESSION的使用者的名稱';

COMMENT ON COLUMN TB_SYSTEM_DDL_LOGS.PROXY_USERID IS
'開啟當前SESSION的使用者的ID';

COMMENT ON COLUMN TB_SYSTEM_DDL_LOGS.CURRENT_SCHEMA IS
'當前SESSION預設的SCHEMA名稱,可以用SESSION SET CURRENT_SCHEMA語句修改';

COMMENT ON COLUMN TB_SYSTEM_DDL_LOGS.HOST IS
'客戶端的主機名稱';

COMMENT ON COLUMN TB_SYSTEM_DDL_LOGS.OS_USER IS
'客戶端的作業系統使用者名稱';

COMMENT ON COLUMN TB_SYSTEM_DDL_LOGS.IP_ADDRESS IS
'客戶端的IP地址';

COMMENT ON COLUMN TB_SYSTEM_DDL_LOGS.DDL_TIME IS
'修改時間';

COMMENT ON COLUMN TB_SYSTEM_DDL_LOGS.SESSION_ID IS
'SESSION_ID';

COMMENT ON COLUMN TB_SYSTEM_DDL_LOGS.VERSION_NO IS
'版本號';

序列的作用主要是為了標識DDL操作版本,採用的是一種類似與SVN版本控制的方式,每發生一次變化版本號就加一

表是用來儲存變更記錄的

建立觸發器

CREATE OR REPLACE TRIGGER TRIG_MONITOR_SYSTEM_DDL
AFTER DDL ON DATABASE
/**
 * 建立時間:2014年7月1日09:49:02
 * 描述:監控DDL操作並將DDL操作及DDL語句記錄到日誌表中
 */
DECLARE
      PRAGMA AUTONOMOUS_TRANSACTION;
      TR_EVENT_ID VARCHAR2(32);
      TR_TERMINAL VARCHAR2(50);
      TR_IPADDR VARCHAR2(30);
      TR_CUR_USER VARCHAR2(30);
      TR_CUR_USERID NUMBER;
      TR_SE_USER VARCHAR2(30);
      TR_SE_USERID NUMBER;
      TR_PROXY_USER VARCHAR2(30);
      TR_PROXY_USERID NUMBER;
      TR_CUR_SC VARCHAR2(30);
      TR_HOST VARCHAR2(100);
      TR_OS_USER VARCHAR2(60);
      TR_SESSIONID VARCHAR2(32);
      TR_SQL_ID VARCHAR2(13);
      TR_SQL VARCHAR2(60);
      TR_VERSION_NO NUMBER;
      TR_N NUMBER;
      TR_STMT CLOB := NULL;
      TR_SQL_TEXT ORA_NAME_LIST_T;
BEGIN
      TR_EVENT_ID := SYS_GUID();
      --獲取使用者資訊
      SELECT NVL(SYS_CONTEXT('USERENV','TERMINAL'),''),--客戶端作業系統終端的名稱
             NVL(SYS_CONTEXT('USERENV','IP_ADDRESS'),''),--客戶端作業系統終端的名稱
             NVL(SYS_CONTEXT('USERENV','CURRENT_USER'),''),--當前SESSION擁有許可權的使用者的名稱(比如說當前SESSION是SYS,但是正在執行SYSTEM.MYPROC,那麼CURRENT_USER就是SYSTEM)
             NVL(SYS_CONTEXT('USERENV','CURRENT_USERID'),''),--當前SESSION擁有的許可權的使用者的ID
             NVL(SYS_CONTEXT('USERENV','SESSION_USER'),''),--SESSION所屬的使用者名稱
             NVL(SYS_CONTEXT('USERENV','SESSION_USERID'),''),--當前SESSION所屬的使用者ID
             NVL(SYS_CONTEXT('USERENV','PROXY_USER'),''),--開啟當前SESSION的使用者的名稱
             NVL(SYS_CONTEXT('USERENV','PROXY_USERID'),''),--開啟當前SESSION的使用者的ID
             NVL(SYS_CONTEXT('USERENV','CURRENT_SCHEMA'),''),--當前SESSION預設的SCHEMA名稱
             NVL(SYS_CONTEXT('USERENV','HOST'),''),--客戶端的主機名稱
             NVL(SYS_CONTEXT('USERENV','OS_USER'),''),--客戶端的作業系統使用者名稱
             NVL(SYS_CONTEXT('USERENV','SESSIONID'),'')--SESSION的ID
      INTO TR_TERMINAL,TR_IPADDR,TR_CUR_USER,TR_CUR_USERID,TR_SE_USER,TR_SE_USERID,TR_PROXY_USER,TR_PROXY_USERID,
           TR_CUR_SC,TR_HOST,TR_OS_USER,TR_SESSIONID
      FROM DUAL;

      --獲取DDL SQL語句,如果語句過長無法全部獲得,可以根據SQL_ID查詢
      BEGIN
           SELECT SQL_TEXT,SQL_ID INTO TR_SQL,TR_SQL_ID
           FROM V$OPEN_CURSOR
           WHERE UPPER(SQL_TEXT) LIKE 'ALTER%'
                 OR UPPER(SQL_TEXT) LIKE 'CREATE%'
                 OR UPPER(SQL_TEXT) LIKE 'DROP%';

           TR_N := ORA_SQL_TXT(TR_SQL_TEXT);

           FOR I IN 1 .. TR_N LOOP
             TR_STMT := TR_STMT || TR_SQL_TEXT(I);
           END LOOP;

          EXCEPTION WHEN OTHERS THEN
           TR_SQL_ID := NULL;
           TR_STMT := NULL;
      END;

      --向TB_SYSTEM_DDL_LOGS日誌表中插入DDL操作記錄
      IF ORA_SYSEVENT <> 'TRUNCATE' AND ORA_DICT_OBJ_NAME NOT LIKE 'SYS_C%' THEN
        SELECT SEQ_DDL_VERSION.NEXTVAL INTO TR_VERSION_NO FROM DUAL;
        INSERT INTO TB_SYSTEM_DDL_LOGS
              (EVENT_ID,EVENT_NAME,TERMINAL,DB_NAME,OBJECT_NAME,OBJECT_OWNER,OBJECT_TYPE,
              IS_ALTER_COLUMN,IS_DROP_COLUMN,SQL_ID,SQL_TEXT,SESSION_ID,
              CURRENT_USER,CURRENT_USERID,SESSION_USER,SESSION_USERID,
              PROXY_USER,PROXY_USERID,CURRENT_SCHEMA,HOST,OS_USER,IP_ADDRESS,VERSION_NO)
        VALUES (TR_EVENT_ID,ORA_SYSEVENT,TR_TERMINAL,ORA_DATABASE_NAME,ORA_DICT_OBJ_NAME,ORA_DICT_OBJ_OWNER,ORA_DICT_OBJ_TYPE,
              NULL,NULL,TR_SQL_ID,TR_STMT,TR_SESSIONID,
              TR_CUR_USER,TR_CUR_USERID,TR_SE_USER,TR_SE_USERID,
              TR_PROXY_USER,TR_PROXY_USERID,TR_CUR_SC,TR_HOST,TR_OS_USER,TR_IPADDR,TR_VERSION_NO
        );
        COMMIT;
      END IF;
END;
/

這個觸發器中過濾了一些不必要操作,大家可以根據需求增加過濾內容

結果展示

SELECT EVENT_NAME,OBJECT_TYPE,OBJECT_NAME,OBJECT_OWNER,DDL_TIME,VERSION_NO
FROM DBADMIN.TB_SYSTEM_DDL_LOGS
ORDER BY VERSION_NO;