天天看點

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 重寫,一定會比現在的處理方式好得多。