Oracle12--DDL触发器(五十七)

  • DDL触发器的作用
    • 定义:当创建,修改,删除数据库对象时,也会引发相应的触发器操作事件,此时可以利用触发器对这些数据库对象的DDL操作进行监控;
    • 语法:

CREATE [OR REPLACE] TRIGGER 触发器名称
[BEFORE | AFTER | INSTEAD OF] [DDL事件] ON [DATABASE | SCHEMA]
[WHEN 触发条件]
[DECLARE]
[程序声明部分;]
BEGIN
程序代码部分;
END [触发器名称];
/

DDL触发器的创建语法中,给出的操作对象有两种:
DATABASE:对数据库级的触发,需要相应的管理员权限(例如sys用户)才可以创建;
SCHEMA:对一个具体的模式(用户)的触发,每个用户都可以直接创建;
 

  • 触发器中支持的DDL事件:

    Oracle12--DDL触发器(五十七)

    • 在使用以上事件时,想取得一些系统的信息,如当前操作用户,删除的数据库对象等,需要在DBMS_STANDARD包中定义的一些事件属性函数,常用的事件属性函数如下:

      Oracle12--DDL触发器(五十七)

      Oracle12--DDL触发器(五十七)

    • 上表中出现的ORA_NAME_LIST_T是定义在DBMS_STANDARD包中的一个嵌套表类型,定义结构如下:

TYPE ora_name_list_t IS TABLE OF VARCHAR2(64);

  • 通过定义发现,该嵌套表中存放的是一个长度为64的字符数据,之后所有返回的数据信息都通过嵌套表取得
  • DDL触发器的使用
    • 示例1:禁止c##scott用户的所有DDL操作

CREATE OR REPLACE TRIGGER scott_forbid_trigger

BEFORE DDL

ON SCHEMA

BEGIN

RAISE_APPLICATION_ERROR(-20007,'scott用户禁止使用任何的DDL操作!') ;

END ;

/

该命令会让c##scott用户发出的任何DDL操作抛出异常,并禁止操作

删除此触发器:
DROP TRIGGER scott_forbid_trigger;

示例2:使用日志来记录数据库对象的操作

  • 数据库对象操作日志记录表创建脚本

DROP TABLE object_log PURGE ;

DROP SEQUENCE object_log_seq ;

CREATE SEQUENCE object_log_seq ;

CREATE TABLE object_log (

oid NUMBER CONSTRAINT pk_oid PRIMARY KEY,

username VARCHAR2(50) NOT NULL ,

operatedate DATE NOT NULL ,

objecttype VARCHAR2(50) NOT NULL ,

objectowner VARCHAR2(50) NOT NULL

) ;

此日志表,oid使用序列(object_log_seq)自动生成,操作日期通过SYSDATE取得,其他的字段使用事件属性取得

  • 编写触发器实现对数据库对象操作的日志记录

CREATE OR REPLACE TRIGGER object_trigger

AFTER CREATE OR DROP OR ALTER

ON DATABASE

DECLARE

BEGIN

INSERT INTO c##scott.object_log(oid,username,operatedate,objecttype,objectowner) VALUES

(c##scott.object_log_seq.nextval,ORA_LOGIN_USER,

SYSDATE,ORA_DICT_OBJ_TYPE,ORA_DICT_OBJ_OWNER) ;

END ;

/

在本触发器中,每当发生了对象的创建,删除,修改操作之后,都会自动向object_log数据表中添加相应的记录

  • 综合示例
    • 需求:禁止修改emp数据表的empno(主键)列和deptno(外键)列的定义结构;
    • 分析:
      • 如果要对修改表结构的操作进行触发,则需要使用ALTER事件操作,而且应该选择更新前触发;
      • 在每次修改表结构的操作中,需要取出一张数据表的所有列来判断其是否被修改,而要想取出一张表中的所有数据列,可以使用all_tab_columns数据字典来查询;
    • 步骤1:查询c##scott.emp表,验证该数据字典;

SELECT * FROM all_tab_columns

WHERE table_name='EMP' AND owner='C##SCOTT';

  • 分析:
    • 如果要把全部的列信息依次进行判断,则需要采用游标的方式来进行处理。使用取得指定查询的所有数据行(每一行是表中一个列的信息),而后使用ORA_IS_LATER_COLUMNORA_IS_DROP_COLUMN事件属性判断当前修改列或删除列的名字是否为empnodeptno,如果是,则抛出一个异常;
    • 为了保证此操作可以对当前用户使用,可以定义一个参数游标,通过ORA_DICT_OBJ_OWNER取得操作表的用户,通过ORA_DICT_OBJ_NAME取得操作数据表名称;
  • 步骤2:在c##scott用户中定义参数游标

CREATE OR REPLACE TRIGGER emp_alter_trigger

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

-- empno字段要被修改

IF empColumnRow.column_name = 'EMPNO' THEN

RAISE_APPLICATION_ERROR(-20007,'empno字段不允许修改!') ;

END IF ;

-- deptno字段要被修改

IF empColumnRow.column_name = 'DEPTNO' THEN

RAISE_APPLICATION_ERROR(-20008,'deptno字段不允许修改!') ;

END IF ;

END IF ;

IF ORA_IS_DROP_COLUMN(empColumnRow.column_name) THEN

-- empno字段要被删除

IF empColumnRow.column_name = 'EMPNO' THEN

RAISE_APPLICATION_ERROR(-20009,'empno字段不允许删除!') ;

END IF ;

-- deptno字段要被删除

IF empColumnRow.column_name = 'DEPTNO' THEN

RAISE_APPLICATION_ERROR(-20010,'deptno字段不允许删除!') ;

END IF ;

END IF ;

END LOOP ;

END IF ;

END ;

/

本程序创建了一个参数游标,分别接收对象的所有者,操作对象名称。之后在使用游标循坏找到操作时,通过两个事件属性将当前操作的用户及对象名称传递到参数游标之内。
如果现在用户修改或删除的字段是
empnodeptno,则程序将抛出异常,禁止操作;

  • 步骤3:创建完触发器之后,下面对emp表中的empno或deptno字段进行修改或删除操作
    • 修改empno字段

ALTER TABLE emp MODIFY (empno NUMBER(6)) ;

  • 删除deptno字段

ALTER TABLE emp DROP COLUMN deptno ;