【问题标题】:How to set trigger on column drop in oracle PL/SQL?如何在 oracle PL/SQL 中设置列​​删除触发器?
【发布时间】:2021-03-28 16:56:21
【问题描述】:

我尝试过这样做:

CREATE OR REPLACE TRIGGER DLL_No_Column_Del
BEFORE DROP ON SCHEMA
BEGIN
 IF ORA_DICT_OBJ_TYPE = 'COLUMN'
 THEN
    INSERT INTO column_del_attempt VALUES
    (
        user,
        SYSDATE,
        ora_dict_obj_name
    );
    RAISE_APPLICATION_ERROR(-20100, 'No column deletion allowed on table ' || ORA_DICT_OBJ_NAME);
 END IF;
END;

然后像这样删除列:

ALTER TABLE emp_copy
DROP COLUMN employee_id;

但它不起作用

【问题讨论】:

    标签: sql database oracle plsql


    【解决方案1】:

    创建一个自治程序来记录事件:

    CREATE PROCEDURE log_system_event(
      p_user_name   IN COLUMN_DEL_ATTEMPT.USER_NAME%TYPE,
      p_object_name IN COLUMN_DEL_ATTEMPT.OBJECT_NAME%TYPE,
      p_object_type IN COLUMN_DEL_ATTEMPT.OBJECT_TYPE%TYPE
    )
    AS
      PRAGMA AUTONOMOUS_TRANSACTION;
    BEGIN
      INSERT INTO column_del_attempt (
        user_name,
        datetime,
        object_name,
        object_type
      ) VALUES (
        p_user_name,
        SYSDATE,
        p_object_name,
        p_object_type
      );
      COMMIT;
    END log_system_event;
    /
    

    然后您可以从触发器中调用它(在 ALTER 事件上,而不是在 DROP 事件上):

    CREATE TRIGGER DLL_No_Column_Del
    BEFORE ALTER ON SCHEMA
    BEGIN
      IF ora_dict_obj_name = 'EMP_COPY' AND ORA_DICT_OBJ_TYPE = 'TABLE' THEN
        log_system_event( login_user, ora_dict_obj_name, ORA_DICT_OBJ_TYPE );
        
        RAISE_APPLICATION_ERROR(-20100, 'No column deletion allowed on table ' || ORA_DICT_OBJ_NAME);
      END IF;
    END;
    /
    

    那么,如果你这样做:

    ALTER TABLE emp_copy DROP COLUMN employee_id;
    

    它会引发异常:

    ORA-04088: error during execution of trigger 'FIDDLE_SPRKWOBAQONFKBBYMKLX.DLL_NO_COLUMN_DEL'
    ORA-00604: error occurred at recursive SQL level 1
    ORA-20100: No column deletion allowed on table EMP_COPY
    ORA-06512: at line 5
    

    然后:

    SELECT * FROM column_del_attempt;
    

    输出:

    用户名 |日期时间 |对象名 | OBJECT_TYPE :---------------------------- | :-------- | :------------ | :---------- FIDDLE_SPRKWOBAQONFKBBYMKLX | 20 年 12 月 18 日 | EMP_COPY |桌子

    db小提琴here


    更新

    要获取ALTER DDL 语句的文本according to AskTom,您可以使用V$OPEN_CURSOR 动态性能视图来获取DDL 语句的文本,然后您可以对其进行过滤:

    CREATE PROCEDURE log_system_event(
      p_user_name   IN COLUMN_DEL_ATTEMPT.USER_NAME%TYPE,
      p_object_name IN COLUMN_DEL_ATTEMPT.OBJECT_NAME%TYPE,
      p_object_type IN COLUMN_DEL_ATTEMPT.OBJECT_TYPE%TYPE,
      p_text        IN COLUMN_DEL_ATTEMPT.TEXT%TYPE
    )
    AS
      PRAGMA AUTONOMOUS_TRANSACTION;
    BEGIN
      INSERT INTO column_del_attempt (
        user_name,
        datetime,
        object_name,
        object_type,
        text
      ) VALUES (
        p_user_name,
        SYSDATE,
        p_object_name,
        p_object_type,
        p_text
      );
      COMMIT;
    END log_system_event;
    /
    

    然后:

    CREATE TRIGGER DLL_No_Column_Del
    BEFORE ALTER ON SCHEMA
    DECLARE
      v_text VARCHAR2(4000);
    BEGIN
      SELECT SQL_TEXT
      INTO   v_text
      FROM   V$OPEN_CURSOR;
      
      IF REGEXP_LIKE( v_text, '^ALTER\s+TABLE\s+("?)EMP_COPY\1\s+DROP\s+', 'i') THEN
        log_system_event( login_user, ora_dict_obj_name, ORA_DICT_OBJ_TYPE, v_text );
        
        RAISE_APPLICATION_ERROR(-20100, 'No column deletion allowed on table ' || ORA_DICT_OBJ_NAME);
      END IF;
    END;
    /
    

    (但是,db<>fiddle、SQLFiddle 和 Oracle 的 LiveSQL 都没有授予使用 V$OPEN_CURSOR dynamic performance view 的权限,所以我无法对此进行测试。)

    【讨论】:

    • 但它会在任何 ALTER TABLE 操作时触发。如何仅对 ALTER TABLE DROP COLUMN 起作用?
    【解决方案2】:

    最终解决方案:

    CREATE TABLE column_del_attempt (
      user_name   VARCHAR2(30),
      datetime    DATE,
      object_name VARCHAR2(30),
      object_type VARCHAR2(30)
    )
    /
    CREATE OR REPLACE PROCEDURE log_system_event(
      p_user_name   IN COLUMN_DEL_ATTEMPT.USER_NAME%TYPE,
      p_object_name IN COLUMN_DEL_ATTEMPT.OBJECT_NAME%TYPE,
      p_object_type IN COLUMN_DEL_ATTEMPT.OBJECT_TYPE%TYPE
    )
    AS
      PRAGMA AUTONOMOUS_TRANSACTION;
    BEGIN
      INSERT INTO column_del_attempt (
        user_name,
        datetime,
        object_name,
        object_type
      ) VALUES (
        p_user_name,
        SYSDATE,
        p_object_name,
        p_object_type
      );
      COMMIT;
    END log_system_event;
    /
    
    CREATE OR REPLACE TRIGGER DLL_No_Column_Del
    BEFORE ALTER ON SCHEMA
    DECLARE
        n NUMBER;
        sql_text_list ora_name_list_t;
        drop_exist BOOLEAN := FALSE;
    BEGIN
    n := ora_sql_txt(sql_text_list);
    FOR i in 1..n LOOP
        IF sql_text_list(i) LIKE '%DROP%' THEN 
            drop_exist := TRUE;
        END IF;
    END LOOP;
      IF drop_exist THEN
        log_system_event( login_user, ora_dict_obj_name, ORA_DICT_OBJ_TYPE );
        RAISE_APPLICATION_ERROR(-20100, 'No column deletion allowed on table ' || ORA_DICT_OBJ_NAME);
      END IF;
    END;
    /
    -- for tests:
    /*
    ALTER TABLE emp_copy ADD birth_date DATE NOT NULL;
    ALTER TABLE emp_copy DROP COLUMN birth_date;
    
    SELECT * FROM column_del_attempt;
    */
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-09-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-20
      • 2019-05-15
      • 2020-12-23
      相关资源
      最近更新 更多