在隐式提交的情况下强制回滚

时间:2015-09-03 08:33:17

标签: sql oracle plsql commit rollback

在SQL或PLSQL中是否有办法确保没有提交任何内容?因为,有时调用函数/过程并且后果未知。例如,它可以触发隐式提交。有没有办法防止这种情况?

3 个答案:

答案 0 :(得分:2)

我在Oracle数据库中提供示例

这是针对SQL语句DML。

SQL> set transaction read only;

Transaction set.

SQL> update t set t=14;
update t set t=14
       *
ERROR at line 1:
ORA-01456: may not perform insert/delete/update operation inside a READ ONLY
transaction

这用于调用已提交的程序。

  SQL>alter session DISABLE COMMIT IN PROCEDURE ;
  SQL>exec procedureHavingCommit(10);
    BEGIN procedureHavingCommit(10); END;

    *
    ERROR at line 1:
    ORA-00034: cannot COMMIT in current PL/SQL session
    ORA-06512: at "ND210.DRMOP_UTIL", line 332
    ORA-06512: at "ND210.DRMOP_UTIL", line 1664
    ORA-00034: cannot COMMIT in current PL/SQL session
    ORA-06512: at line 1

答案 1 :(得分:1)

如果您想确保不会修改任何内容,那么"设置事务只读#34;是正确的答案。

否则:如果您正在编写一些调用其他程序员编写的其他程序的代码,并且您主要担心的是这些其他程序可能会发出不需要的提交(或者将来可能会被修改以发出不需要的提交),所以您要抓住在它们造成损害之前,我有一个你可以找到有用的解决方案,我目前在我的代码中用于这个目的:

让我们说你的程序这样做:

 procedure MyProcedureThatModifiesData is
 begin
      update mytables....
      SomeOthersProcedure;
      update myothertables ...;
      commit; -- having a commit in a stored procedure 
              -- is a bad idea: i wrote this only to mimick the global         
              -- application behaviour
 end; 

并且您希望确保,如果SomeOtherProcedure的作者修改了在其中插入提交的过程,则此提交将被阻止并回滚。

使用我的解决方案,代码变为:

 procedure MyProcedureThatModifiesData is
 begin
      pkg_block_unwanted_commits.DisableCommits; // <<!!

      update mytables....
      SomeOthersProcedure;

      update myothertables ...;
      pkg_block_unwanted_commits.ReenableCommits; // <<!!

      commit; -- having a commit in a stored procedure 
              -- is a bad idea: i wrote this only to mimick the global         
              -- application behaviour
end; 

让我解释使其成为可能的想法:您只需要一个包含延迟约束的表。   只有当您发出&#34; commit&#34;时才会检查延迟约束:如果数据未提交则可能违反约束,但如果您尝试提交,则会回滚整个事务并发生oracle错误提高。

现在重点是:如果您在程序开始时故意插入一些违反延迟约束的数据,您将获得您要求的确切内容:回滚而不是提交。 要重新启用提交,您所要做的就是删除违反约束的数据。

一个基本的实现可能是这样的:

  procedure MyProcedureThatModifiesData is
  begin
      -- this update "disables" the commits
      update myspecialtable set 
             myfield=unacceptable_value_that_violates_the_constraint;
      update mytables....
      SomeOthersProcedure;
      update myothertables ...;
      -- this update "re-enables" the commits  
      update myspecialtable set 
             myfield=valid_value;
      commit;
  end;

上述基本实现的另一个步骤是使&#34; myspecialtable&#34;一个全局临时表(种类:在提交时保留行),因此它只包含在oracle会话期间写入的临时值,并且不会永久存储在数据库中。此外,通过这种方式,其他会话将能够在此特殊表中编写自己的数据而不会干扰您的表。

完整的解决方案就是这个:

  create or replace package pkg_block_unwanted_commits is

      -- we will implement these two in order to allow nested calls:
      -- each DisableCommit must be paired with her EnableCommits.
      -- commits will be actually enabled only when each DisableCommit (including 
      -- nested calls) has been closed by her pairing EnableCommit
      procedure DisableCommits;
      procedure ReenableCommits;
  end;
  /
  -- this is the temporary table we will use for the above

  create global temporary table tbl_block_unwanted_commits
  (  
       -- this primary key, along with the "chk_only_one_row" constraint ensures that 
       -- this table can contain only one row
       only_one_row char(1) default 'X' primary key,

       -- it keeps track of the number of "opened" DisableCommits calls:
       -- we can commit only if nest_counter is zero 
       nest_counter number not null
  )
  on commit preserve rows
  /
  -- this one, considering that "only_one_row" is the primary key
  -- ensures we will have only one row in the table (just to be safe)
  alter table tbl_block_unwanted_commits add constraint
       chk_only_one_row check (only_one_row='X') 
  /
  -- this is just to reveal errors in our program if we call EnableCommits without having called DisableCommits 
  -- (mispaired calls)
  alter table tbl_block_unwanted_commits add constraint
       chk_unbalanced_enables check (nest_counter >=0)
  /
  -- this one is the constraint that actually does the trick of blocking commits
  -- we can commit only if whe have active calls to "disablecommits" that have not been paired with corresponding "ReenableCommits"
  alter table tbl_block_unwanted_commits add constraint     
        chk_blocked_commits check (nest_counter = 0) deferrable initially deferred
  /
  create or replace package body  pkg_block_unwanted_commits is

      procedure DisableCommits is 
      begin
          update tbl_block_unwanted_commits set nest_counter = nest_counter +1;
          if sql%notfound then
             insert into tbl_block_unwanted_commits(only_one_row,nest_counter)
             values ('X',1);
          end if;
      end;

      procedure ReenableCommits is
      begin
         update tbl_block_unwanted_commits set nest_counter = nest_counter -1;
      end;
  end;
  /

希望这会有所帮助

答案 2 :(得分:0)

我将在上面的程序中对ALTER SESSION DISABLE COMMIT进行投票。但是,正如已经指出的那样 - 如果你已经在交易中,这种方法并不奏效。也就是说,您处于打算提交的情况,但在准备好之前,您不希望调用过程提交。

在这种情况下我做的是写一些东西来检测COMMIT是否已经发生。它不会阻止提交,但它会导致我的程序出错 - 即使提交没有任何伤害。

这样,我100%肯定会在测试期间检测到COMMIT正在发生并且可以在设计中处理它。

以下是我所描述的方法说明的链接:https://stackoverflow.com/a/31752576/5174436