简体   繁体   English

如何在 oracle PL/SQL 中设置列删除触发器?

[英]How to set trigger on column drop in oracle PL/SQL?

I tried to do this:我试图这样做:

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;

Then drop column like that:然后像这样删除列:

ALTER TABLE emp_copy
DROP COLUMN employee_id;

But it doesn't work但它不起作用

Create an autonomous procedure to log the event:创建一个自治过程来记录事件:

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;
/

Then you can call it from the trigger (on the ALTER event, rather than the DROP 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;
/

Then, if you do:然后,如果你这样做:

ALTER TABLE emp_copy DROP COLUMN employee_id;

It will raise the exception:它将引发异常:

 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

Then:然后:

SELECT * FROM column_del_attempt;

Outputs:输出:

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

db<>fiddle here db<> 在这里摆弄


Update更新

To get the text of the ALTER DDL statement, according to AskTom , you can use the V$OPEN_CURSOR dynamic performance view to get the text of the DDL statement and then you can filter on that:要获取ALTER DDL 语句的文本, 根据 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;
/

Then:然后:

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;
/

(However, none of db<>fiddle , SQLFiddle nor Oracle's LiveSQL has granted permissions to use the V$OPEN_CURSOR dynamic performance view so I cannot test this.) (但是, db<>fiddle 、 SQLFiddle 和 Oracle 的 LiveSQL 都没有授予使用V$OPEN_CURSOR动态性能视图的权限,因此我无法对此进行测试。)

Final solution:最终解决方案:

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;
*/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM