PostgreSQL SPI中的錯誤處理整理
PostgreSQL SPI 用於在 C 或是其他程式語言編寫的擴充套件函式(儲存過程)中呼叫資料庫本身的解析器、規劃器和執行器的功能,以及對 SQL 語句進行執行。
在最重要的一個函式 SPI_execute 的文件中,說明了發生錯誤時,將會返回下列負值之一:
SPI_ERROR_ARGUMENT
如果command為NULL或者count小於 0
SPI_ERROR_COPY
如果嘗試COPY TO stdout或者COPY FROM stdin
SPI_ERROR_TRANSACTION
如果嘗試了一個事務操縱命令( BEGIN、 COMMIT、 ROLLBACK、 SAVEPOINT、 PREPARE TRANSACTION、 COMMIT PREPARED、 ROLLBACK PREPARED或者其他變體)
SPI_ERROR_OPUNKNOWN
如果命令型別位置(不應該會發生)
SPI_ERROR_UNCONNECTED
如果呼叫過程未連線
你一定會奇怪,為什麼只有這麼幾個呢?還有其他的很多情況呢?比如傳進去的 SQL 有語法錯誤,或是實際執行時報錯,這些情況下會返回什麼呢?
然後文件中又說:注意如果一個通過 SPI 呼叫的命令失敗,那麼控制將不會返回到你的過程中。當然啦,你的過程所在的事務或者子事務將被回滾(這可能看起來令人驚訝,因為據文件所說 SPI 函式大多數都有錯誤返回約定。但是那些約定只適用於在 SPI 函式本身內部檢測到的錯誤)。通過在可能失敗的 SPI 呼叫周圍建立自己的子事務可以在錯誤之後恢復控制。當前文件中並未記載這些,因為所需的機制仍然在變化中。
原來檢查 SPI_execute 的原始碼可知,只有發生了上面幾種情況的錯誤時,SPI 會返回給你錯誤程式碼;而其他更內部的地方發生的所有錯誤,程式都是直接呼叫的 ereport 方法,如果錯誤級別達到 ERROR 及以上時,會中斷程式的執行,將事務回滾,並將錯誤資訊:1、記到日誌中;2、返回給客戶端。
因此,其他情況的錯誤,你根本就不必處理,PG 也不給你機會處理。你只有在客戶端才能看到具體的報錯資訊。
如果你是在一個很大的邏輯裡,不想整個事務被回滾掉,想出錯後控制還返回給程式,可以用 PG_TRY、PG_CATCH、PG_END_TRY 幾個巨集來通知 ereport 將控制返回給程式,同時用一個子事務把對 SPI 的呼叫包起來,參考 PL/Python 原始碼 plpy_spi.c 中,PLy_spi_subtransaction_<begin/commit/abort> 等方法的處理。
這也是因為 PostgreSQL 是用 C 語言開發的,一個不夠強的地方。假如將來用 Rust 重寫,一定會比現在的處理方式好得多。