29.9. 錯誤處理

本節描速您如何在嵌入的 SQL 程序裡處理例外以及警告。有好幾種非排它的設施可用。

29.9.1. 設置回調

捕獲錯誤和警告的一個簡單的辦法是設置一個特定的動作, 當特定情況發生的時候執行這個動作。通常:

EXEC SQL WHENEVER condition action;

condition 可以是下列之一:

SQLERROR

聲明的動作將在執行一個 SQL 語句發生錯誤的時候執行。

SQLWARNING

聲明的動作將在執行一個 SQL 語句發生警告的時候執行。

NOT FOUND

聲明的動作將在一條 SQL 語句檢索了零行或者影響零行的情況下被 執行。(這個條件不是一個錯誤,但是您可能喜歡單獨處理這個情況。)

action 可以是下列之一:

CONTINUE

這個動作的意思是這個條件可以忽略。這是預設。

GOTO label
GO TO label

跳轉到指定的標籤(使用一個 C 的goto 語句)。

SQLPRINT

向標準錯誤打印一條訊息。這對簡單程序或者原型定義程序很有用。 消息的細節不能配置。

STOP

調用 exit(1),它將終止程序執行。

BREAK

執行 C 語句 break。這個應該只在循環或者 switch 語句中使用。

CALL name (args)
DO name (args)

用指定的參數調用指定的 C 函數。

SQL 標準只提供 CONTINUEGOTO (也是 GO TO)動作。

下面是一個例子,您可能可以用在簡單的程序裡。它在發生警告的時候打印一個簡單的訊息,在發生錯誤的時候退出程序。

EXEC SQL WHENEVER SQLWARNING SQLPRINT;
EXEC SQL WHENEVER SQLERROR STOP;

語句 EXEC SQL WHENEVER 是 SQL 預處理器的一個指示器, 而不是一個 C 語句。它設置的錯誤或者警告動作適用於下面出現的設置了句柄的所有 SQL 語句, 除非在第一個 EXEC SQL WHENEVER 和導致條件觸發的 SQL 語句之間為同一個條件設置了另外一個動作, 而不管 C 程序內部的流程控制是怎樣的。所以,下面的兩個 C 程序節選都不會有它們期望的效果。

/*
 * 錯誤
 */
int main(int argc, char *argv[])
{
    ...
    if (verbose) {
        EXEC SQL WHENEVER SQLWARNING SQLPRINT;
    }
    ...
    EXEC SQL SELECT ...;
    ...
}

/*
 * 錯誤
 */
int main(int argc, char *argv[])
{
    ...
    set_error_handler();
    ...
    EXEC SQL SELECT ...;
    ...
}

static void set_error_handler(void)
{
    EXEC SQL WHENEVER SQLERROR STOP;
}

29.9.2. sqlca

如果需要更強大的錯誤處理機制,那麼嵌入的 SQL 接口提供了一個叫 sqlca 的全局變量,它是一個有著下面定義的結構:

struct
{
    char sqlcaid[8];
    long sqlabc;
    long sqlcode;
    struct
    {
        int sqlerrml;
        char sqlerrmc[70];
    } sqlerrm;
    char sqlerrp[8];
    long sqlerrd[6];
    char sqlwarn[8];
    char sqlstate[5];
} sqlca;

(在一個多線程的程序裡,每個線程自動獲得自己的 sqlca 的拷貝。這個方式類似於處理標準 C 全局變量 errno。)

sqlca 包含警告和錯誤。如果在一個語句的執行中出現多個警告或者錯誤,那麼 sqlca 將只包含最後一個的訊息。

如果在最後的 SQL 語句執行過程中沒有發生錯誤, 那麼 sqlca.sqlcode 將是 0 並且 sqlca.sqlstate 將是"00000"。如果發生一個警告或者錯誤, 那麼 sqlca.sqlcode 將是負數並且 sqlca.sqlstate 將不是 "00000"。一個正數的 sqlca.sqlcode 標識一種無害的條件,比如最後的查詢返回零行。sqlcodesqlstate 是兩個不同的錯誤模式;下面詳細介紹。

如果最後一條 SQL 語句成功,如果適合該具體命令,那麼 sqlca.sqlerrd[1] 包含處理過的行的 OID,而 sqlca.sqlerrd[2] 包含 處理或返回的行數。

在發生錯誤或者警告的情況下,sqlca.sqlerrm.sqlerrmc 將包含一個描述該錯誤的字串。字串 sqlca.sqlerrm.sqlerrml 包含儲存在 sqlca.sqlerrm.sqlerrmc 裡的錯誤訊息的長度(strlen() 的結果, 對 C 程序員來說並非真正關心的)。請注意有些訊息太長,因而不能儲存在定長的 sqlerrmc 數組裡;它們會被截斷。

在發出警告的情況下,sqlca.sqlwarn[2] 被設置為 W。 (在所有其它的情況下,它都被設置為與 W 不同的東西。)如果sqlca.sqlwarn[1] 設置為 W,那麼就是一個數值在儲存到宿主變量的時候被截斷。 如果任何其它元素設置成資料表示一個警告,那麼 sqlca.sqlwarn[0] 被設置為 W

字串 sqlcaidsqlcabcsqlerrp 和剩下的 sqlerrd 以及 sqlwarn 元素目前沒有包含有用的訊息。

結構 sqlca 沒有在 SQL 標準裡定義,但是在好幾個其它 SQL 資料庫系統裡定義了。 定義的核心都類似,但是如果您想寫可以移植的應用,那麼您應該仔細研究不同的實現。

29.9.3. SQLSTATESQLCODE 之比較

字串 sqlca.sqlstatesqlca.sqlcode 是提供錯誤代碼的兩種不同的模式。兩種都在 SQL 裡聲明了,但是 SQLCODE 在 1992 年的標準版本裡標注為廢棄的,並且在 1999 年版本裡被刪除了。因此,對於新應用,我們強烈建議使用 SQLSTATE

SQLSTATE 是一個五字元的數組。五個字元包含數值或者大寫字母, 代資料表各種錯誤或者警告條件的代碼。SQLSTATE 有個層次化的模式:頭兩個字元標識條件的通常資料表示錯誤條件的類別, 後三個字元資料表示在該通用類中的子類。成功的狀態是由 00000 標識的。SQLSTATE 代碼在大多數地方都是定義在 SQL 標準裡的。PostgreSQL 伺服器本機支援 SQLSTATE 錯誤代碼;因此在應用裡使用這個錯誤代碼模式可以實現非常高的一致性。更多訊息請參閱 Appendix A

SQLCODE 是廢棄的錯誤代碼模式,是一個簡單的整數。 0 標識成功,正數標識帶著額外訊息的成功,負數資料表示一個錯誤。SQL 標準只定義了正數值 +100, 這個資料表示最後的命令返回零行或者影響了零行,並且沒有特定的負數值。 因此,這個模式只能實現很有限的一致性並且並沒有一個層次化的代碼機制。 歷史上,PostgreSQL 的嵌入的 SQL 處理器為自己使用賦予了一些 SQLCODE 的數值,這些都在下面列出,包括他們的數值和符號名。記住這些是不能移植到其它 SQL 實現中去的。 為了簡化應用向 SQLSTATE 模式的移植,我們還列出了 SQLSTATE。 不過,這兩個模式之間沒有一對一或者一對多的映射(包括多對多),所以在任何情況下您都應該參考在 Appendix A 裡列出的全局SQLSTATE

這些是已經賦值的 SQLCODE 數值:

-12 (ECPG_OUT_OF_MEMORY)

資料表明您的虛擬內存已經耗盡(SQLSTATE YE001)

-200 (ECPG_UNSUPPORTED)

資料表明預處理器生成了一些庫不知道的東西。可能您執行的預處理器版本和庫版本不同。(SQLSTATE YE002)

-201 (ECPG_TOO_MANY_ARGUMENTS)

這意味著您的命令聲明了比命令預期要多的宿主變量(SQLSTATE 07001 或者 07002)

-202 (ECPG_TOO_FEW_ARGUMENTS)

這意味著您的命令聲明了比命令預期要少的宿主變量(SQLSTATE 07001 或者 07002)

-203 (ECPG_TOO_MANY_MATCHES)

這意味著查詢返回了多行,但是語句只準備儲存一行結果(比如,因為聲明的變量不是數組)。(SQLSTATE 21000)

-204 (ECPG_INT_FORMAT)

宿主變量是 int 類型,而資料庫裡的資料是其它類型, 並且包含無法解釋成 int 類型的數值。庫使用 strtol() 做這種轉換。(SQLSTATE 42804)

-205 (ECPG_UINT_FORMAT)

宿主變量是 unsigned int 而資料庫裡的資料是其它類型, 並且包含無法解釋成 unsigned int 類型的數值。 庫使用 strtoul() 做這種轉換。(SQLSTATE 42804)

-206 (ECPG_FLOAT_FORMAT)

宿主變量是 float 而資料庫裡的資料是其它類型, 並且包含無法解釋成 float 類型的數值。 庫使用 strtod() 做這種轉換。(SQLSTATE 42804)

-207 (ECPG_CONVERT_BOOL)

這意味著宿主變量是類型為 bool 的而資料庫中的資料既不是't' 也不是 'f'。(SQLSTATE 42804)

-208 (ECPG_EMPTY)

發送給 PostgreSQL 伺服器的語句是空的。 (這個通常不能在嵌入 SQL 程序裡出現,因此它可能是一個內部錯誤。) (SQLSTATE YE002)

-209 (ECPG_MISSING_INDICATOR)

返回了一個空值,但是沒有提供空值指示器變量。(SQLSTATE 22002)

-210 (ECPG_NO_ARRAY)

在一個需要數組的地方使用了一個普通變量。(SQLSTATE 42804)

-211 (ECPG_DATA_NOT_ARRAY)

在一個需要數組的地方資料庫返回了一個普通變量。 (SQLSTATE 42804)

-220 (ECPG_NO_CONN)

程序視圖訪問一個不存在的連接。(SQLSTATE 08003)

-221 (ECPG_NOT_CONN)

程序視圖訪問一個存在但未打開的連接(這是一個內部錯誤。)(SQLSTATE YE002)

-230 (ECPG_INVALID_STMT)

您正在嘗試使用的語句還沒準備好。(SQLSTATE 26000)

-240 (ECPG_UNKNOWN_DESCRIPTOR)

聲明的描述符沒有找到。您在試圖使用的語句沒有準備好(SQLSTATE 33000)

-241 (ECPG_INVALID_DESCRIPTOR_INDEX)

聲明的描述符索引超出範圍。(SQLSTATE 07009)

-242 (ECPG_UNKNOWN_DESCRIPTOR_ITEM)

請求了一個非法的描述符項。(這是一個內部錯誤。)(SQLSTATE YE002)

-243 (ECPG_VAR_NOT_NUMERIC)

在一個動態語句的執行的過程中,資料庫返回了一個數字只,而宿主變量不是數值型(SQLSTATE 07006)。

-244 (ECPG_VAR_NOT_CHAR)

在執行一個動態語句的過程中,資料庫返回了一個非數字數值,但是宿主變量是數值型。(SQLSTATE 07006)

-400 (ECPG_PGSQL)

一些 PostgreSQL 伺服器導致的錯誤。 消息包含來自 PostgreSQL 伺服器的錯誤消息。

-401 (ECPG_TRANS)

PostgreSQL 告訴我們不能啟動,提交, 或者回滾這個交易。(SQLSTATE 08007)

-402 (ECPG_CONNECT)

與資料庫的連接企圖沒有成功(SQLSTATE 08001)。

100 (ECPG_NOT_FOUND)

這是個無害的條件,資料表示最後一條命令檢索或者處理了零行,或者您在游標的結尾。(SQLSTATE 02000)