Oracle 觸發器實現DDL語句監控
阿新 • • 發佈:2019-01-22
前言
建立此觸發器的主要目的是為了控制資料庫的版本,雖然會將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;