全部產品
Search
文件中心

PolarDB:錯誤處理

更新時間:Aug 09, 2025

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)同樣支援該特性:

  1. 首先在DECLARE中聲明異常變數,給定一個變數名,例如my_exception EXCEPTION;

  2. 通過PRAGMA EXCEPTION_INIT 語句為該變數綁定一個 SQLCODE,例如PRAGMA EXCEPTION_INIT(my_exception, -1476);

    重要

    錯誤碼存在範圍限制,需為100(表示no_data_found異常)或在[-1000000, 0)範圍內,且只有[-65535, 0)這個範圍能夠綁定到Oracle的合法SQLCODE,其他範圍的錯誤碼沒有實際意義。

  3. 在 EXCEPTION 段中可以用WHEN my_exceptionWHEN 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

-

-

暫不支援當前情境