异常错误包含以下三种类型:

  • 系统内部异常

    系统隐式定义的异常错误,一般来说为 SQL 执行时的错误,例如死锁。 由 OceanBase 数据库的 PL 引擎自动抛出,但并没有异常名称的定义,程序通过 SQLCODE 和 SQLERRM 来获取细节。

  • 预定义异常

    OceanBase 数据库的 PL 引擎中定义的异常,PL 程序执行时的常见问题。这种异常情况由 OceanBase 数据库自动将其抛出,有明确的异常名称,程序中可以直接捕获。例如 SELECT… INTO… 可能触发 NO_DATA_FOUND 异常。

说明

系统内部异常和预定义异常都是系统自动抛出的。

  • 用户定义异常

    程序执行过程中,出现编程人员认为的异常情况。对这种异常情况的处理,需要用户在程序中定义,并且显式地在程序中将其引发。

系统内部异常

PL 程序遇到 OceanBase 数据库系统错误,会抛出系统内部异常。

这种异常有两种处理方式:

  • 通过 PRAGMA EXCEPTION_INIT 进行声明,然后在 EXCEPTION HANDLE 捕获其名称。

  • 用 EXCEPTION HANDLE 中的 OTHER 捕获,通过 SQLCODE 和 SQLERRM 来获取细节。

通过命名捕获

EXCEPTION_INIT 的语法如下:

  1. PRAGMA EXCEPTION_INIT(handle_name, sql_err_code);

handle_name 为异常的命名,可以通过这个名字在 EXCEPTION 中捕获,sql_err_code 为相应的数据库错误号。

通过命名捕获系统内部异常的示例如下:

  1. obclient> DECLARE
  2. -> DUPLICATED_DEPT_ID EXCEPTION;
  3. -> PRAGMA EXCEPTION_INIT(DUPLICATED_DEPT_ID, -5024);
  4. -> BEGIN
  5. -> UPDATE departments SET department_id=110
  6. -> where DEPARTMENT_ID=100;
  7. -> EXCEPTION
  8. -> WHEN DUPLICATED_DEPT_ID THEN
  9. -> DBMS_OUTPUT.PUT_LINE('Duplicated department id!');
  10. -> WHEN OTHERS THEN
  11. -> DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
  12. -> END;
  13. -> /
  14. Query OK, 0 rows affected (0.03 sec)
  15. Duplicated department id!

通过 OTHER 捕获

通过 OTHER 捕获系统内部异常的示例如下:

  1. obclient> DECLARE
  2. -> BEGIN
  3. -> UPDATE departments SET department_id=110
  4. -> where DEPARTMENT_ID=100;
  5. -> EXCEPTION
  6. -> WHEN OTHERS THEN
  7. -> DBMS_OUTPUT.PUT_LINE('Err Found: '||SQLCODE);
  8. -> END;
  9. -> /
  10. Query OK, 0 rows affected (0.02 sec)
  11. Err Found: -5024

预定义异常

对这种异常情况的处理,只需在 PL 块的异常处理部分,直接引用相应的异常情况名,并对其完成相应的异常错误处理即可。

示例如下:

  1. obclient> DECLARE
  2. -> v_empid employees.employee_id%TYPE;
  3. -> v_sal employees.salary%TYPE;
  4. -> BEGIN
  5. -> v_empid := 100;
  6. -> SELECT salary INTO v_sal FROM employees
  7. -> WHERE employee_id=v_empid;
  8. -> IF v_sal<=1500 THEN
  9. -> UPDATE employees SET salary=salary+100 WHERE employee_id=v_empid;
  10. -> DBMS_OUTPUT.PUT_LINE('Employee '||v_empid||' updated');
  11. -> ELSE
  12. -> DBMS_OUTPUT.PUT_LINE('Employee '||v_empid||' ignored');
  13. -> END IF;
  14. -> EXCEPTION
  15. -> WHEN NO_DATA_FOUND THEN
  16. -> -- 如果员工号v_empid不存在,触发NO_DATA_FOUND异常
  17. -> DBMS_OUTPUT.PUT_LINE('Employee id '||v_empid||' not found');
  18. -> WHEN TOO_MANY_ROWS THEN
  19. -> -- 如果员工号v_empid不止一条,触发TOO_MANY_ROWS异常
  20. -> DBMS_OUTPUT.PUT_LINE('Duplicated id: '||v_empid);
  21. -> WHEN OTHERS THEN
  22. -> DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
  23. -> END;
  24. -> /
  25. Query OK, 0 rows affected (0.06 sec)
  26. Employee id 100 not found

预定义异常的信息如下表:

预定义异常

错误码

ACCESS_INTO_NULL

-6530

CASE_NOT_FOUND

-6592

COLLECTION_IS_NULL

-6531

CURSOR_ALREADY_OPEN

-6511

DUP_VAL_ON_INDEX

-1

INVALID_CURSOR

-1001

INVALID_NUMBER

-1722

LOGIN_DENIED

-1017

NO_DATA_FOUND

+100

NO_DATA_NEEDED

-6548

NOT_LOGGED_ON

-1012

PROGRAM_ERROR

-6501

ROWTYPE_MISMATCH

-6504

SELF_IS_NULL

-30625

STORAGE_ERROR

-6500

SUBSCRIPT_BEYOND_COUNT

-6533

SUBSCRIPT_OUTSIDE_LIMIT

-6532

SYS_INVALID_ROWID

-1410

TIMEOUT_ON_RESOURCE

-51

TOO_MANY_ROWS

-1422

VALUE_ERROR

-6502

ZERO_DIVIDE

-1476

用户自定义异常

用户自定义异常可以显式调用 RAISE 语句来触发,通常用来处理应用逻辑的异常。基本步骤如下:

  1. 在 PL 的 DECLARE 部分定义异常名称,语法为:exception_name EXCEPTION

  2. 在 PL 程序体中显式触发异常,语法为:RAISE exception_name

  3. 在 PL程序的 EXCEPTION 部分对异常情况做出相应的处理,语法为:WHEN exception_name THEN

示例如下:

  1. obclient> DECLARE
  2. -> v_empid employees.employee_id%TYPE;
  3. -> v_sal employees.salary%TYPE;
  4. -> -- 1.定义异常名称 SALARY_NOT_SET
  5. -> SALARY_NOT_SET EXCEPTION;
  6. -> BEGIN
  7. -> v_empid := 103;
  8. -> SELECT salary INTO v_sal FROM employees
  9. -> WHERE employee_id=v_empid;
  10. -> IF v_sal<=1500 THEN
  11. -> UPDATE employees SET salary=salary+100 WHERE employee_id=v_empid;
  12. -> DBMS_OUTPUT.PUT_LINE('Employee '||v_empid||' updated');
  13. -> ELSIF v_sal is NULL THEN
  14. -> -- 2. v_sal为空时,触发这个异常
  15. -> RAISE SALARY_NOT_SET;
  16. -> ELSE
  17. -> DBMS_OUTPUT.PUT_LINE('Employee '||v_empid||' ignored');
  18. -> END IF;
  19. -> EXCEPTION
  20. -> WHEN NO_DATA_FOUND THEN
  21. -> DBMS_OUTPUT.PUT_LINE('Employee id '||v_empid||' not found');
  22. -> -- 3. SALARY_NOT_SET这个异常进行处理
  23. -> WHEN SALARY_NOT_SET THEN
  24. -> DBMS_OUTPUT.PUT_LINE('Salary not set: '||v_empid);
  25. -> WHEN OTHERS THEN
  26. -> DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
  27. -> END;
  28. -> /
  29. Query OK, 0 rows affected (0.07 sec)
  30. Salary not set: 103

自定义错误码

OceanBase 数据库提供了过程自定义错误代码 RAISE_APPLICATION_ERROR 和错误提示消息,让错误处理变得更加灵活, 比如函数中抛出自定义错误, 在调用函数的过程中捕获异常并处理。

语法如下:

  1. RAISE_APPLICATION_ERROR(error_number,error_message ) ;
  • error_number 为错误代码,范围从 -20000 到 -20999。

  • error_message 是相应的错误提示消息,最大长度为 2048 字节。

示例如下:

  1. obclient> DECLARE
  2. -> v_empid employees.employee_id%TYPE;
  3. -> v_sal employees.salary%TYPE;
  4. -> BEGIN
  5. -> v_empid := 103;
  6. -> SELECT salary INTO v_sal FROM employees
  7. -> WHERE employee_id=v_empid;
  8. -> IF v_sal is NULL THEN
  9. -> -- 抛出错误 20999
  10. -> RAISE_APPLICATION_ERROR(-20999, 'The salary of employee is not found');
  11. -> ELSIF v_sal<=1500 THEN
  12. -> UPDATE employees SET salary=salary+100 WHERE employee_id=v_empid;
  13. -> DBMS_OUTPUT.PUT_LINE('Employee '||v_empid||' updated');
  14. -> ELSE
  15. -> DBMS_OUTPUT.PUT_LINE('Employee '||v_empid||' ignored');
  16. -> END IF;
  17. -> END;
  18. -> /
  19. ORA-20999: The salary of employee is not found