學習SQLSTATE,sqlstate08001
1. 什麼是SQLSTATE
shell> SELECT * FROM no_such_table;
ERROR 1146 (42S02): Table 'test.no_such_table' doesn't exist
上面執行一條SQL語句出錯後的顯示。1146是MySQL自己定義的錯誤碼,42S02是ANSI SQL和ODBC定義的錯誤碼,“Table 'test.no_such_table' doesn't exist”是MySQL返回的錯誤原因。
其中,42S02就是本文要討論的SQLSTATE
2. 為什麼要有SQLSTATE
42S02是ANSI SQL和ODBC定義的錯誤碼,可以理解成是錯誤碼標準。假設沒有SQLSTATE,世界會是什麼樣子?你開發了一款資料庫驅動程式,希望相容MySQL、Oracle、SQLServer。對於鎖衝突,MySQL返回錯誤碼2011,Oracle返回9912,SQLServer返回3231(以上3個資料為杜撰),如果你希望檢查到鎖衝突後,立即執行do_something(),那你需要這樣寫代碼:
if (2011 == conn.errno || 9912 == conn.errno || 3231 == conn.errno) {
do_something();
}
如果還希望支援Postgre,則需要增加Postgre的錯誤碼處理。這是個悲傷地故事,不想再講。
可見,資料庫自訂錯誤碼是靠不住的,他們各自為政。也許你會想,為什麼這些資料庫廠商不能協調一下,統一一下錯誤碼呢?理想很豐滿,現實很骨感。因為在某個特定資料庫內部實現中,可能內部定義了四五個不同的錯誤碼來表示鎖衝突,用一個錯誤碼無法滿足內部邏輯的需求。所以,完美的解決方式是:
*. 內部,用資料庫自己的錯誤碼,愛怎麼用就怎麼用,當需要把這個錯誤碼輸出到外部的時候,先做一個轉換,將內部錯誤碼轉換成SQLSTATE。
*. 資料庫驅動程式只看SQLSTATE,忽略資料庫自訂錯誤碼。
3. SQLSTATE資料格式詳解
SQLSTATE包含5個字母,前兩位表示錯誤類別,後三位表示子類,均有0~9,A~Z(大寫)這些字元組成。00000表示沒有錯誤。
前兩個字母定義的錯誤類別:
00 = 沒有錯誤
01 = 有WARNING
02 = 遊標NOT FOUND
> 02 表示某種異常,MySQL的異常,詳細見http://dev.mysql.com/doc/refman/5.6/en/error-messages-server.html 這裡定義了MySQL內部800多個錯誤碼與SQLSTATE的映射
並不是每一個內部錯誤碼都能明確映射到一個有意義的SQLSTATE,對於這一類內部錯誤碼,統統都映射到HY000這個SQLSTATE上去,意思就是:我也不知道咱們這個錯誤碼對應哪個SQLSTATE好,就這麼湊合著吧。例如:Error: 1004 SQLSTATE: HY000 (ER_CANT_CREATE_FILE)
關於SQLSTATE的格式,還有很多講究,詳細參考這篇文檔,比較清晰:https://mariadb.com/kb/en/sql-99/sqlstate-codes/
4. 資料庫中如何?SQLSTATE
可以建立一個Map,將錯誤碼映射到SQLSTATE即可。如果錯誤碼的規劃設計正好是從0~N,或者0~-N,那麼可以直接用數組來實現這個映射,錯誤碼即為數組的下標;更通用的方式,還是用數組,只不過尋找方式是二分尋找,也很方便。
MySQL中的實現,詳見share/errmsg.txt和include/sql_state.h 。
5. OceanBase中如何?SQLSTATE
參見lib/ob_errno.cpp
可以看到,與MySQL相比,OB還多了一個負擔:把OceanBase內部錯誤碼儘可能映射成MySQL內部錯誤碼。啥時候別人寫資料庫的時候能把內部錯誤碼映射成OceanBase的啊?