暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Oracle ddl触发器应用禁止drop&truncate分区

IT那活儿 2025-04-14
20

点击上方“IT那活儿”公众号--专注于企业全栈运维技术分享,不管IT什么活儿,干就完了!!!


问题背景

某历史库通过某复制工具复制生产库的ddl操作到历史库,保障历史库的表结构和生产一致,为定期迁移数据到历史库避免字段不一致造成导入失败,但是生产库ddl操作的删除分区和truncate分区操作不能复制。

历史库保留的数据时间较长,生产库保留数据时间较短会定期drop分区,这个操作部分同步到历史库,复制工具能够实现这个功能并使用了一段时间,某次生产做大量分区清理时drop分区的操作同步到历史库造成了历史库的数据丢失

后经带库恢复把丢失的数据找回,有梳理工具的功能避免同步drop分区的操作。为了避免再次出现误同步的情况,我们添加了ddl触发器限制drop&truncate分区的操作,保障数据的安全。

首先先介绍下ddl触发器,后面再展示案例和触发器内容。


DDL触发器

2.1 DDL触发器

Oracle DDL类型触发器主要是对于Oracle数据库的DDL操作触发的触发器,主要包括create、drop、alter等DDL事件,经常利用DDL类型触发器记录DDL操作记录或者限定对某个对象进行DDL操作。也可以根据对应DDL操作做对应的操作。

常用的DDL操作有:

  • grant(授权);

  • revoke(撤销授权);

  • create(创建);

  • drop(删除);

  • alter(修改);

  • comment(注释);

  • audit(审核);

  • rename(重命名)等。

2.2 DDL触发器语法

create [or replacetrigger 用户名.触发器名
       {before|after} {DDL事件} on {database|schema}
       [when 条件]
declare
  定义变量。
begin
  PL/SQL语句块。
end;

复制

参数说明:

  • {before|after}

    触发器是在DDL事件之前、之后触发。

  • {database|schema}

    作用在一个用户上,还是全部的用户。

  • [when 条件]

    只有满足when指定的条件,才会执行触发体中的代码,应用场景极少。


DDL案例

3.1 常见的ddl限制触发器要么是限制全部的ddl操作,要么是限制一个ddl事件的操作,但是我们需要限制只是drop或是truncate分区,这样需要对触发器的代码做细分判断来实现

例如:为了更好的保护重点表或是我们需要的表,添加了一个控制,控制那些表需要禁止ddl控制表结构,通过控制表限制那些表不允许drop,truncate,alter drop partition

-- Create table
createtable SYSTEM.PROTECTED_OBJECT
(
  OWNER VARCHAR2(30notnull,
  OBJECT_TYPE VARCHAR2(30notnull,
  OBJECT_NAME VARCHAR2(30notnull,
  ENABLED VARCHAR2(3notnull,
  DEFINE_TIME DATEdefaultsysdatenotnull,
  DEFINER VARCHAR2(32notnull
)
tablespaceUSERS;
 -- Create/Recreate primary, uniqueand foreign keyconstraints
altertable SYSTEM.PROTECTED_OBJECT
addconstraint PK_PROTECTED_OBJECT primary key (OWNER, OBJECT_TYPE, OBJECT_NAME)
usingindex
tablespaceUSERS;
-- Create/Recreate checkconstraints
altertable SYSTEM.PROTECTED_OBJECT
addcheck (enabled in ( 'YES''NO' ));

commentoncolumn SYSTEM.PROTECTED_OBJECT.ENABLED
is'是否限制ddl操作,YES标示限制,NO标示不限制';

复制

例如:

3.2 触发器内容,禁止,操作记录到alert日志里

create or replace trigger SYS.trg_stop_ddl
  before drop or truncate or alter on database
declare
  v_ObjectCount integer :0;
  v_SqlList ora_name_list_t;
  v_ActionSql clob :'';
  v_ListDepth integer :0;
  v_Session v$session%rowtype;
  v_Message varchar2(256);
  v_action_drop integer :0;
  v_action_truncate integer :0;
  v_sysevent varchar2(256);
begin
  -- Check the pre-defined table to see whether this object can be modified
  select count(*) into v_ObjectCount
  from system.protected_object o
  where o.enabled = 'YES'
    and upper( o.owner ) = ora_dict_obj_owner
    and upper( o.object_type ) = ora_dict_obj_type
    and upper( o.object_name ) = ora_dict_obj_name;
  --
if v_ObjectCount > 0then
    -- This object can not be modified
    select * into v_Session from v$session a
      where sid = ( select sid from v$mystat where rownum = 1 );

    -- Get the DDL sql text
    v_ListDepth := ora_sql_txt( v_SqlList );
    if v_ListDepth > 0then
      for i in1..v_ListDepth loop
        v_ActionSql := v_ActionSql || v_SqlList( i );
      end loop;
    endif;
    --
    v_action_drop:=instr(lower(v_ActionSql),'drop partition');
    v_action_truncate:=instr(lower(v_ActionSql),'truncate partition');
    v_sysevent:=ora_sysevent;
    --
    if (v_action_drop>0and v_sysevent='ALTER')then
       raise_application_error( -20001'DDL drop partition not allowed on this object:'||ora_dict_obj_name||': please contact DBA.');
    elsif (v_action_truncate>0and v_sysevent='ALTER'then
       raise_application_error( -20001'DDL truncate partition not allowed on this object:'||ora_dict_obj_name||', please contact DBA.'); 
    elsif (v_sysevent='DROP'or v_sysevent='TRUNCATE'then
      -- Write message to alert log
      sys.dbms_system.ksdwrt( 2'ORA-20001 DDL operation not allowed' );
      v_Message :'user='|| ora_login_user ||'; os_user='|| v_Session.Osuser
                   ||'; program='|| v_Session.Program ||'; machine='|| v_Session.Machine;
      sys.dbms_system.ksdwrt( 2'ORA-20002 '|| v_Message );
      sys.dbms_system.ksdwrt( 2'ORA-20003 DDL: '|| v_ActionSql );
      
      -- Raise error to user
      raise_application_error( -20001'DDL operation not allowed on this object :'||ora_dict_obj_name||', please contact DBA.');
    endif
endif;

end trg_stop_ddl;
/

复制

3.3 触发器生效效果

附图:

3.4 通过控制表放开限制或是打开限制,或是通过控制触发器放开限制

放开限制ddl限制方法1:

有对控制表修改权限的用户执行:

update system.protected_object t set t.enabled='NO';
commit;

复制

恢复限制:

update system.protected_object t set t.enabled='YES';

commit;

复制

放开限制ddl限制方法2:

触发器失效,需要dba权限:

alter trigger SYS.TRG_STOP_DDL disable;
--
alter trigger SYS.TRG_STOP_DDL enable;

复制

结 语:

通常ddl触发器可以限制所有的ddl操作或是某类ddl操作,这个触发器是限制alter类中的drop或是truncate分区操作是一个特例需求比较特殊,是ddl触发器的一个细分,希望对有同样需求的有帮助。


END


本文作者:王宏明(上海新炬中北团队)

本文来源:“IT那活儿”公众号

文章转载自IT那活儿,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论