PolarDB PostgreSQL版(相容Oracle)不僅支援原生PostgreSQL的錯誤處理系統,還相容Oracle文法中的常見錯誤處理機制。
主要包括以下幾種類型的錯誤處理機制:
預定義異常:既支援原生PostgreSQL的所有預定義異常,同時也支援相容Oracle文法中的常見預定義異常。
非預定義異常:相容Oracle文法中的異常變數,並支援通過
EXCEPTION_INIT為異常變數設定錯誤碼。自訂異常(RAISE_APPLICATION_ERROR):相容Oracle文法中的
RAISE_APPLICATION_ERROR函數,用於拋出自訂的錯誤碼和錯誤訊息。錯誤碼與錯誤資訊:既支援原生PostgreSQL的
SQLSTATE錯誤碼,也相容Oracle文法中常見的SQLCODE錯誤碼,此外還支援SQLERRM用於查看詳細錯誤資訊。
預定義異常
PolarDB PostgreSQL版(相容Oracle)既支援原生PostgreSQL的所有預定義異常,如division_by_zero。同時也支援相容Oracle文法中的常見預定義異常,如zero_divide。在SQL中既可以拋出這些異常,也可以捕獲這些異常。
拋出異常
異常的拋出分為兩種方式:執行PL/SQL語句拋出以及RAISE語句顯式拋出。樣本如下:
執行PL/SQL語句拋出異常
DECLARE
result INT;
BEGIN
result := 1 / 0;
END;RAISE語句顯式拋出異常
-- PostgreSQL 風格
BEGIN
RAISE division_by_zero;
END;
-- Oracle 風格
BEGIN
RAISE zero_divide;
END;捕獲異常
異常的捕獲通過EXCEPTION實現,既可以在WHEN子句中指定異常名以捕獲特定異常,也可以通過WHEN OTHERS捕獲所有異常。樣本如下:
WHEN子句中指定異常名
-- PostgreSQL 風格
DECLARE
result INT;
BEGIN
result := 1 / 0;
EXCEPTION
WHEN division_by_zero THEN
DBMS_OUTPUT.PUT_LINE('division by zero');
END;
-- Oracle 風格
DECLARE
result INT;
BEGIN
result := 1 / 0;
EXCEPTION
WHEN zero_divide THEN
DBMS_OUTPUT.PUT_LINE('zero divide');
END;WHEN OTHERS
DECLARE
result INT;
BEGIN
result := 1 / 0;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('division by zero');
END;非預定義異常
在Oracle文法中,存在大量的錯誤碼,其中只有少數錯誤碼被定義為系統預定義異常。例如,錯誤碼100對應於no_data_found異常,可以通過顯式拋出RAISE no_data_found來引發該異常,並通過WHEN no_data_found進行異常捕獲。
對於大量的非預定義異常,它們僅具備錯誤碼而沒有名稱,如何拋出和捕獲這些異常呢?為瞭解決這一問題,Oracle引入了異常變數。PolarDB PostgreSQL版(相容Oracle)同樣支援該特性:
首先在
DECLARE中聲明異常變數,給定一個變數名,例如my_exception EXCEPTION;。通過
PRAGMA EXCEPTION_INIT語句為該變數綁定一個SQLCODE,例如PRAGMA EXCEPTION_INIT(my_exception, -1476);。重要錯誤碼存在範圍限制,需為100(表示
no_data_found異常)或在[-1000000, 0)範圍內,且只有[-65535, 0)這個範圍能夠綁定到Oracle的合法SQLCODE,其他範圍的錯誤碼沒有實際意義。在
EXCEPTION段中可以用WHEN my_exception或WHEN OTHERS捕獲該異常。
樣本
DECLARE
result INT;
my_exception EXCEPTION;
PRAGMA EXCEPTION_INIT(my_exception, -66666);
BEGIN
result := 1 / 0;
EXCEPTION
WHEN my_exception THEN
DBMS_OUTPUT.PUT_LINE('zero divide');
END;自訂異常(RAISE_APPLICATION_ERROR)
預定義異常和非預定義異常的錯誤碼都屬於Oracle預留的錯誤碼範圍,通常為[-65535, 0)範圍的負數。除此以外,Oracle還提供了一個自訂異常錯誤碼區間[-20000, -20999],通過RAISE_APPLICATION_ERROR函數即可拋出自訂的錯誤碼和錯誤訊息。
樣本
DECLARE
salary NUMBER := 4000;
BEGIN
-- 校正商務規則,不符合條件時觸發錯誤
IF salary < 5000 THEN
RAISE_APPLICATION_ERROR(-20001, 'Salary is below the allowed minimum.');
END IF;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('SQLCODE: ' || SQLCODE);
DBMS_OUTPUT.PUT_LINE('Custom Error: ' || SQLERRM);
END;錯誤碼與錯誤資訊
在EXCEPTION段中通過WHEN子句捕獲到異常以後,如果希望擷取異常的各項相關資訊,可以通過如下方法:
SQLSTATE:原生PostgreSQL的錯誤碼,由五個字元的字串組成,前兩位表示類別(如語法錯誤、串連異常等),後三位表示具體錯誤子類。
SQLCODE:相容Oracle文法中的錯誤碼,分為三類:
100:表示
no_data_found異常,查詢語句無返回結果。[-65535, 0)範圍的負數:PolarDB PostgreSQL版(相容Oracle)叢集已相容Oracle文法中的各類錯誤情境。-99999:表示非法值,PolarDB PostgreSQL版(相容Oracle)叢集暫不支援當前情境。
SQLERRM:錯誤資訊,用於描述異常的詳細情況。
樣本
DECLARE
result INT;
BEGIN
result := 1 / 0;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('SQLCODE: ' || SQLCODE);
DBMS_OUTPUT.PUT_LINE('SQLSTATE: ' || SQLSTATE);
DBMS_OUTPUT.PUT_LINE('SQLERRM: ' || SQLERRM);
END;輸出結果為:
SQLCODE: -1476
SQLSTATE: 22012
SQLERRM: division by zero在PolarDB PostgreSQL版(相容Oracle)叢集中,部分Oracle風格的SQLCODE與PostgreSQL風格的SQLSTATE之間存在映射關係,而另一些則不具備直接映射關係。以下為您列舉了已支援的錯誤情境資訊:
SQLCODE | Oracle異常名 | SQLSTATE | PostgreSQL異常名 | 錯誤描述 |
100 | NO_DATA_FOUND | P0002 | no_data_found | 查詢語句無返回結果 |
-1 | DUP_VAL_ON_INDEX | 23505 | unique_violation | 插入重複鍵時違反唯一約束 |
-54 | 無 | 55P03 | lock_not_available | 加鎖失敗 |
-939 | 無 | 54023 | too_many_arguments | 函數參數過多 |
-957 | 無 | 42701 | duplicate_column | 列名重複 |
-960 | 無 | 42702 | ambiguous_column | 列名不明確 |
-1001 | INVALID_CURSOR | 34000 | invalid_cursor_name | 遊標未定義或遊標名非法 |
-1031 | 無 | 42501 | insufficient_privilege | 許可權不足 |
-1422 | TOO_MANY_ROWS | P0003 | too_many_rows | 查詢語句返回行數過多 |
-1428 | 無 | 2201E | invalid_argument_for_logarithm | log函數參數非法 |
-1428 | 無 | 2201F | invalid_argument_for_power_function | power函數參數非法 |
-1476 | ZERO_DIVIDE | 22012 | division_by_zero | 除數為0 |
-1578 | 無 | XX001 | data_corrupted | 資料損毀 |
-1821 | 無 | 22007 | invalid_datetime_format | 日期格式錯誤 |
-3125 | 無 | 08P01 | protocol_violation | 通訊協定錯誤 |
-4020 | 無 | 40P01 | deadlock_detected | 檢測到死結 |
-6035 | 無 | 53000 | insufficient_resources | 資源不足 |
-6113 | 無 | 53300 | too_many_connections | 串連過多 |
-6503 | 無 | 2F005 | function_executed_no_return_statement | 函數沒有RETURN語句 |
-6511 | CURSOR_ALREADY_OPEN | 42P03 | duplicate_cursor | 遊標重複定義或重複開啟 |
-6531 | COLLECTION_IS_NULL | 2203G | collection_is_null | 集合為空白 |
-6532 | SUBSCRIPT_OUTSIDE_LIMIT | 2202E | array_subscript_error | 數組下標越界 |
-6533 | SUBSCRIPT_BEYOND_COUNT | 2203H | subscript_beyond_count | 數組下標越界 |
-6592 | CASE_NOT_FOUND | 20000 | case_not_found | CASE語句沒有匹配的WHEN子句 |
-24381 | 無 | P0005 | forall_dml_error | FOR ALL語句報錯 |
-27102 | 無 | 53200 | out_of_memory | 記憶體不足 |
-30110 | 無 | 42601 | syntax_error | 語法錯誤 |
-30156 | 無 | 53100 | disk_full | 磁碟滿 |
-1722 | INVALID_NUMBER | - | - | TO_NUMBER函數參數非法 |
-1723 | 無 | - | - | 列的長度為0 |
-1724 | 無 | - | - | FLOAT類型精度非法 |
-1727 | 無 | - | - | NUMBER類型精度非法 |
-6502 | VALUE_ERROR | - | - | 數字類型格式非法或超過上限 |
-6508 | 無 | - | - | 包函數未定義 |
-99999 | 無 | - | - | 暫不支援當前情境 |