处理和记录 ORA-06512 错误代码

Handling and logging ORA-06512 error code

我刚刚创建了一个 error_log table 来记录 procedure/package 可能 运行 的任何错误。错误日志table如下

CREATE TABLE APMS.ERROR_LOG
(
ORA_ERR_TMSP     TIMESTAMP(6)                 NOT NULL,
ORA_ERR_NUMBER   NUMBER(5)                    NOT NULL,
ORA_ERR_MSG      VARCHAR2(200 CHAR)           NOT NULL,
ORA_ERR_TXT      VARCHAR2(500 CHAR)           NOT NULL,
ORA_ERROR_OPTYP  CHAR(1 CHAR)                 NOT NULL,
PROGRAM_NAME     VARCHAR2(50 CHAR)            NOT NULL,
ORA_IN_OUT       VARCHAR2(500 CHAR)           NOT NULL
)

我创建了一个模拟 table,它通过使用过程在时间戳字段中插入字符串来故意引发 ORA-06512 错误。这是将虚拟数据插入我的模拟 table 的过程,目的是引发错误并将其记录到我的 error_log table.

create or replace procedure test_procedure as 

begin 
  insert into mockdata values ('data1','mockname','mockcity');
  commit;
exception
  when others then
    insert into error_log 
    values   
    (ora_err_tmsp,ora_err_number,ora_err_msg,ora_err_txt,ora_err_optyp,program_name,ora_in_out);
    values
    (current_timestamp,sqlcode,'sqlerrm', 'detailed information','i','test_procedure','i');
    commit;
end;
/

当我尝试 run/compile 时,出现以下错误。

[Error] PLS-00103 (9: 1): PLS-00103: Encountered the symbol "VALUES" when expecting one of the following:

   ( begin case declare end exit for goto if loop mod null
   pragma raise return select update when while with
   <an

我是 pl/sql 的完全初学者,非常感谢您的帮助。

试试这个:

CREATE OR REPLACE PROCEDURE test_procedure
AS
BEGIN
   INSERT INTO mockdata
       VALUES ('data1', 'mockname', 'mockcity');

   COMMIT;
EXCEPTION
   WHEN OTHERS
   THEN
      INSERT INTO error_log
           (ora_err_tmsp,
                  ora_err_number,
                  ora_err_msg,
                  ora_err_txt,
                  ora_err_optyp,
                  program_name,
                  ora_in_out)
      values (CURRENT_TIMESTAMP,
              SQLCODE,
              sqlerrm,
              'detailed information',
              'i',
              'test_procedure',
              'i');
      COMMIT;
END;
/

在进入你的代码之前:如果你打算在你写的每个程序中写那种日志记录,也许你应该简单地避免这样做并保持你的手触及这个触发器并在你需要时启用它它:这个触发器在测试环境中可能很方便,或者如果你遇到了很大的麻烦并且你需要收集在给定时间内发生的所有可能的错误:

     CREATE OR REPLACE TRIGGER log_all_errors  AFTER SERVERERROR ON DATABASE
     declare
        procedure log_error is
        pragma autonomous_transaction;
        begin                  
            INSERT INTO error_log
            VALUES (SYSDATE, SYS.LOGIN_USER, SYS.INSTANCE_NUM, SYS.DATABASE_NAME, DBMS_UTILITY.FORMAT_ERROR_STACK);
            commit;
        end;

     BEGIN
         log_error;
     END log_all_errors ;

此触发器将记录系统中发生的所有错误。永久启用它不是一个好主意:您可以在紧急情况下使用它进行一些故障排除,并且您可能希望调整其代码以仅记录某种错误,但这是一个起点。 如果您进行一些研究,您会发现您甚至可以记录导致错误的语句的 SQL 文本。 请记住,有一些错误未被此触发器捕获(我指的是 NO_DATA_FOUND 和 TOO_MANY_ROWS 错误):在其正常生命周期中通常使用这些异常的代码太多了,所以 oracle 的人决定不捕获这些错误。

现在让我们回到您的代码:正如其他人指出的那样,您的方法对程序的正常执行不是中立的:

  1. 您正在记录错误,对吧,但是您将错误隐藏到调用您的过程的程序中。这不是一个好主意:调用程序肯定想知道发生了什么错误,它想发出 "rollback" 以取消所有以前的工作,而不是像没有发生任何错误一样继续。

  2. 不仅您隐藏了错误,而且每当发生错误时您也会发出提交:这很可能与您的调用程序在发生错误时所做的相反:当然调用者将发出回滚。相反,您将永久完成所有部分工作,直到错误发生。

  3. 作为旁注:即使在您的过程中没有发生错误时,上述不想要的提交问题:当没有错误发生时,您也在提交。这意味着如果调用程序在调用您的过程后遇到一些问题,它将无法回滚它在调用您的代码之前所做的任何事情。

    在过程中提交或回滚通常是一种不好的做法,除非您非常确定这样的过程永远不会作为更大事务的一部分被调用(例如示例:如果您的过程是数据库作业的主体,则可以提交)

那么:如何在不干扰调用程序的情况下编写错误日志?你通过在一个专用的自治事务中写入日志来做到这一点”过程:无论你在标记为 "authonomous transaction" 的过程中做什么,都在它自己的单独事务中运行:你提交或回滚 only what发生在里面。

(如您所见,我在上面的触发器中也使用了自治事务)

如果这样写,你的代码对调用程序来说会多"kind":

     CREATE OR REPLACE PROCEDURE test_procedure AS

        procedure write_error_log (errcode number, errstr varchar2) is
        pragma autonomous_transaction; 
        -- whatever we do in this procedure stays in its own new private transaction
        begin                         
              INSERT INTO error_log
                   (ora_err_tmsp,
                          ora_err_number,
                          ora_err_msg,
                          ora_err_txt,
                          ora_err_optyp,
                          program_name,
                          ora_in_out)
              values (CURRENT_TIMESTAMP,
                      errcode,
                      errstr,
                      'detailed information',
                      'i',
                      'test_procedure',
                      'i');
              COMMIT;  -- this commit does not interfere with the caller's transaction.
        end write_error_log;

     BEGIN
        INSERT INTO mockdata
            VALUES ('data1', 'mockname', 'mockcity');
        --here you were committing: you'd better not do it:
        --  you are making impossible for the calling program to roll back 
        -- COMMIT; 
      exception when others then             
        write_error_log(sqlcode,sqlerrm);
        raise; -- you should NOT hide the exception to the caller program, so you'd better re-raise it!
     END test_procedure;