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

Force rollback in case of an implicit commit

SQL 或 PLSQL 中是否有一种方法可以确保没有提交任何内容?因为,有时会调用 function/procedure 并且结果未知。例如,它可以触发隐式提交。有什么办法可以避免吗?

我在 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

我将对上面的 ALTER SESSION DISABLE COMMIT IN PROCEDURE 答案投赞成票。但是,正如已经指出的那样——如果您已经在进行交易,那将不起作用。也就是说,您处于打算提交的情况,但不希望调用的过程在您准备好之前提交。

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

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

这里是 link 对我正在描述的方法的描述:

如果您想确保不会修改任何内容,"set transaction read only" 是正确的答案。

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

假设您的程序执行此操作:

 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; 

让我解释一下使它成为可能的想法:您所需要的只是一个包含延迟约束的 table。 仅当您发出 "commit" 时才检查延迟约束:就数据未提交而言,它可能违反约束,但如果您尝试提交,则整个事务将回滚并引发 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;

上述基本实现的进一步步骤是使 "myspecialtable" 成为全局临时值 table(种类:在提交时保留行)因此它将仅包含在生命周期内写入的临时值您的 oracle 会话,它不会永久存储在数据库中。此外,通过这种方式,其他会话将能够在这个特殊的 table 中写入自己的数据,而不会干扰您的 table.

完整的解决方案是这个:

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

希望这对您有所帮助