DDL触发器
当创建、修改或者删除数据库对象时,也会引起相应的触发器操作事件,而此时就可以利用触发器来对这些数据库对象的 DDL 操作进行监控,DDL 触发器的创建语法如下所示。
CREATE [ OR REPLACE ] TRIGGER 触发器名称
[BEFORE | AFTER | INSTEAD OF] [DDL 事件] ON [DATABASE | SCHEMA]
[WHEN 触发条件]
[DECLARE]
[程序声明部分;]
BEGIN
程序代码部分;
END [触发器名称];
/
DDL触发器支持事件
DDL事件 | 触发时机 | 描述 |
---|---|---|
ALTER | BEFORE/AFTER | 修改对象的结构时触发 |
ANALYZE | BEFORE/AFTER | 分析数据库对象时触发 |
ASSOCIATE STATISTICS | BEFORE/AFTER | 启动统计数据库对象时触发 |
AUDIT | BEFORE/AFTER | 开启审核数据库对象时触发 |
COMMENT | BEFORE/AFTER | 为数据库对象设置注释信息时触发 |
CREATE | BEFORE/AFTER | 创建数据库对象时触发 |
DDL | BEFORE/AFTER | 针对于出现的所有DDL事件触发 |
DISASSOCIATE STATISTICS | BEFORE/AFTER | 关闭统计数据库对象时触发 |
DROP | BEFORE/AFTER | 删除数据库对象时触发 |
GRANT | BEFORE/AFTER | 用户授权时触发 |
NOAUDIT | BEFORE/AFTER | 禁用审核数据库对象时触发 |
RENAME | BEFORE/AFTER | 为数据库对象重命名时触发 |
REVOKE | BEFORE/AFTER | 用户撤销权限时触发 |
TRUNCATE | BEFORE/AFTER | 截断数据表时触发 |
常用的事件属性函数
事件属性函数 | 描述 |
---|---|
ORA_CLIENT_IP_ADDRESS | 取得客户端IP地址,如果是本地连接则返回NULL,返回数据类型为VARCHAR2 |
ORA_DATABASE_NAME | 取得数据库名称,返回的数据类型为VARCHAR2 |
ORA_DES_ENCRYPTED_PASSWORD | 取得加密后的口令内容,返回数据类型为VARCHAR2 |
ORA_DICT_OBJ_NAME | 取得对象名称,返回数据类型为VARCHAR2 |
ORA_DICT_OBJ_NAME_LIST(nameList OUT ORA_NAME_LIST_T) | 返回被修改的对象名称列表 |
ORA_DICT_OBJ_OWNER | 取得对象的拥有者,返回数据类型为VARCHAR2 |
ORA_DICT_OBJ_OWNER_LIST(objList OUT ORA_NAME_LIST_T) | 返回被修改对象的所有列表 |
ORA_DICT_OBJ_TYPE | 返回对象类型 |
ORA_GRANTEE(nameList OUT ORA_NAME_LIST_T) | 返回授予的权限或角色列表 |
ORA_INSTANCE_NUM | 取得当前数据库之中的实例编号 |
ORA_IS_ALTER_COLUMN(column Name VARCHAR2) | 判断列名称是否被修改,返回数据类型为BOOLEAN |
ORA_IS_CREATING_NESTED_TABLE | 如果创建一个嵌套表,返回数据类型为BOOLEAN |
ORA_IS_DROP_COLUMN(columnName VARCHAR2) | 判断一个列是否被删除,返回数据类型为BOOLEAN |
ORA_IS_SERVERERROR(errorCode Number) | 判断是否出现了指定的错误编号,返回数据类型为BOOLEAN |
ORA_LOGIN_USER | 取得当前模式名称,返回的数据类型为VARCHAR2 |
ORA_REVOKEE(nameList OUT ORA_NAME_LIST_T) | 返回撤销的权限或角色列表 |
ORA_SERVER_ERROR(point NUMBER) | 返回错误堆栈信息中的错误号,其中1为错误堆栈顶端,返回的数据类型为NUMBER |
ORA_SERVER_ERROR_MSG | 返回错误堆栈信息中的错误信息,其中1为错误堆栈顶端,返回数据类型为VARCHAR2 |
ORA_SYSEVENT | 返回触发器的操作事件,返回的数据类型为VARCHAR2 |
范例
范例 1 :使用 scott 用户创建 禁止 scott 用户的所有 DDL 操作
create or replace trigger scott_forbid_trigger
before DDL
ON SCHEMA
BEGIN
RAISE_APPLICATION_ERROR(-20007,'Scott is forbid any DDL');
END;
/
范例 2:创建一个记录对象日志信息表,并记录用户操作对象的日志
创建各个数据库对象的日志信息表
DROP TABLE object_log PURGE;
DROP SEQUENCE object_log_seq;
CREATE SEQUENCE object_log_seq;
CREATE TABLE object_log(
oid NUMBER,
username VARCHAR2(50) NOT NULL ,
operatedate DATE NOT NULL,
objecttype VARCHAR2(50) NOT NULL,
objectowner VARCHAR2(50) NOT NULL,
CONSTRAINT pk_oid PRIMARY KEY (oid)
);
触发器
create or replace trigger object_trigger
before CREATE OR DROP OR ALTER
ON DATABASE
BEGIN
INSERT INTO
object_log(oid,username,operatedate,objecttype,objectowner)
VALUES(object_log_seq.nextval,ora_login_user,SYSDATE,ora_dict_obj_type,ora_dict_obj_owner);
END;
/
范例 3:禁止修改 emp 数据表的 empno(主键) 列和 deptno(外键)列的定义结构
需要知道列的信息,可以使用 all_tab_columns 数据字典
CREATE OR REPLACE TRIGGER emp_alter_triger
BEFORE ALTER
ON SCHEMA
DECLARE
CURSOR emp_column_cur(p_tableOwner all_tab_columns.owner%TYPE,
p_tableName all_tab_columns.table_name%TYPE) IS
SELECT column_name from all_tab_columns
where owner=p_tableOwner AND table_name=p_tableName;
BEGIN
IF ora_dict_obj_type = 'TABLE' THEN
FOR empColumnRow IN emp_column_cur(ora_dict_obj_owner,
ora_dict_obj_name) LOOP
IF ora_is_alter_column(empColumnRow.column_name) THEN
IF empColumnRow.column_name = 'EMPNO' THEN
RAISE_APPLICATION_ERROR(-20007,'empno is can not update');
END IF;
IF empColumnRow.column_name = 'DEPTNO' THEN
RAISE_APPLICATION_ERROR(-20008,'deptno is can not update');
END IF;
END IF;
IF ora_is_drop_column(empColumnRow.column_name) THEN
IF empColumnRow.column_name = 'EMPNO' THEN
RAISE_APPLICATION_ERROR(-20009,'empno is can not delete');
END IF;
IF empColumnRow.column_name = 'DEPTNO' THEN
RAISE_APPLICATION_ERROR(-20010,'deptno is can not delete');
END IF;
END IF;
END LOOP;
END IF;
END;
/