异常错误包含以下三种类型:
系统内部异常
系统隐式定义的异常错误,一般来说为 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 的语法如下:
PRAGMA EXCEPTION_INIT(handle_name, sql_err_code);
handle_name 为异常的命名,可以通过这个名字在 EXCEPTION 中捕获,sql_err_code 为相应的数据库错误号。
通过命名捕获系统内部异常的示例如下:
obclient> DECLARE
-> DUPLICATED_DEPT_ID EXCEPTION;
-> PRAGMA EXCEPTION_INIT(DUPLICATED_DEPT_ID, -5024);
-> BEGIN
-> UPDATE departments SET department_id=110
-> where DEPARTMENT_ID=100;
-> EXCEPTION
-> WHEN DUPLICATED_DEPT_ID THEN
-> DBMS_OUTPUT.PUT_LINE('Duplicated department id!');
-> WHEN OTHERS THEN
-> DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
-> END;
-> /
Query OK, 0 rows affected (0.03 sec)
Duplicated department id!
通过 OTHER 捕获
通过 OTHER 捕获系统内部异常的示例如下:
obclient> DECLARE
-> BEGIN
-> UPDATE departments SET department_id=110
-> where DEPARTMENT_ID=100;
-> EXCEPTION
-> WHEN OTHERS THEN
-> DBMS_OUTPUT.PUT_LINE('Err Found: '||SQLCODE);
-> END;
-> /
Query OK, 0 rows affected (0.02 sec)
Err Found: -5024
预定义异常
对这种异常情况的处理,只需在 PL 块的异常处理部分,直接引用相应的异常情况名,并对其完成相应的异常错误处理即可。
示例如下:
obclient> DECLARE
-> v_empid employees.employee_id%TYPE;
-> v_sal employees.salary%TYPE;
-> BEGIN
-> v_empid := 100;
-> SELECT salary INTO v_sal FROM employees
-> WHERE employee_id=v_empid;
-> IF v_sal<=1500 THEN
-> UPDATE employees SET salary=salary+100 WHERE employee_id=v_empid;
-> DBMS_OUTPUT.PUT_LINE('Employee '||v_empid||' updated');
-> ELSE
-> DBMS_OUTPUT.PUT_LINE('Employee '||v_empid||' ignored');
-> END IF;
-> EXCEPTION
-> WHEN NO_DATA_FOUND THEN
-> -- 如果员工号v_empid不存在,触发NO_DATA_FOUND异常
-> DBMS_OUTPUT.PUT_LINE('Employee id '||v_empid||' not found');
-> WHEN TOO_MANY_ROWS THEN
-> -- 如果员工号v_empid不止一条,触发TOO_MANY_ROWS异常
-> DBMS_OUTPUT.PUT_LINE('Duplicated id: '||v_empid);
-> WHEN OTHERS THEN
-> DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
-> END;
-> /
Query OK, 0 rows affected (0.06 sec)
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 语句来触发,通常用来处理应用逻辑的异常。基本步骤如下:
在 PL 的 DECLARE 部分定义异常名称,语法为:
exception_name EXCEPTION
在 PL 程序体中显式触发异常,语法为:
RAISE exception_name
在 PL程序的 EXCEPTION 部分对异常情况做出相应的处理,语法为:
WHEN exception_name THEN
示例如下:
obclient> DECLARE
-> v_empid employees.employee_id%TYPE;
-> v_sal employees.salary%TYPE;
-> -- 1.定义异常名称 SALARY_NOT_SET
-> SALARY_NOT_SET EXCEPTION;
-> BEGIN
-> v_empid := 103;
-> SELECT salary INTO v_sal FROM employees
-> WHERE employee_id=v_empid;
-> IF v_sal<=1500 THEN
-> UPDATE employees SET salary=salary+100 WHERE employee_id=v_empid;
-> DBMS_OUTPUT.PUT_LINE('Employee '||v_empid||' updated');
-> ELSIF v_sal is NULL THEN
-> -- 2. 当v_sal为空时,触发这个异常
-> RAISE SALARY_NOT_SET;
-> ELSE
-> DBMS_OUTPUT.PUT_LINE('Employee '||v_empid||' ignored');
-> END IF;
-> EXCEPTION
-> WHEN NO_DATA_FOUND THEN
-> DBMS_OUTPUT.PUT_LINE('Employee id '||v_empid||' not found');
-> -- 3. 对SALARY_NOT_SET这个异常进行处理
-> WHEN SALARY_NOT_SET THEN
-> DBMS_OUTPUT.PUT_LINE('Salary not set: '||v_empid);
-> WHEN OTHERS THEN
-> DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
-> END;
-> /
Query OK, 0 rows affected (0.07 sec)
Salary not set: 103
自定义错误码
OceanBase 数据库提供了过程自定义错误代码 RAISE_APPLICATION_ERROR 和错误提示消息,让错误处理变得更加灵活, 比如函数中抛出自定义错误, 在调用函数的过程中捕获异常并处理。
语法如下:
RAISE_APPLICATION_ERROR(error_number,error_message ) ;
error_number 为错误代码,范围从 -20000 到 -20999。
error_message 是相应的错误提示消息,最大长度为 2048 字节。
示例如下:
obclient> DECLARE
-> v_empid employees.employee_id%TYPE;
-> v_sal employees.salary%TYPE;
-> BEGIN
-> v_empid := 103;
-> SELECT salary INTO v_sal FROM employees
-> WHERE employee_id=v_empid;
-> IF v_sal is NULL THEN
-> -- 抛出错误 20999
-> RAISE_APPLICATION_ERROR(-20999, 'The salary of employee is not found');
-> ELSIF v_sal<=1500 THEN
-> UPDATE employees SET salary=salary+100 WHERE employee_id=v_empid;
-> DBMS_OUTPUT.PUT_LINE('Employee '||v_empid||' updated');
-> ELSE
-> DBMS_OUTPUT.PUT_LINE('Employee '||v_empid||' ignored');
-> END IF;
-> END;
-> /
ORA-20999: The salary of employee is not found