31.6. 函數易失性範疇

每個函數都有一個易失性級別, 可能性有 VOLATILESTABLE,或者 IMMUTABLE。 如果 CREATE FUNCTION 命令沒有聲明範疇,那麼 VOLATILE 就是預設。 易失性範疇是給優化器的一個承諾,關於函數的行為的承諾:

對於更好的優化結果,您應該用可以用的最嚴格的易失性範疇標記您的函數。

任何有副作用的函數都必須標記為 VOLATILE, 這樣對其的調用就不會被優化。如果一個函數沒有副作用,但是它的數值可能在一個查詢裡改變, 那麼也必須標記為 VOLATILE; 這樣的函數的例子是 random()currval()timeofday()

在那些簡單的規劃後馬上執行的交互查詢上,STABLEIMMUTABLE 沒有什麼區別:函數是在規劃還是在執行開始的時候執行的差別並不大。 但是如果規劃被保存並且後來重用,那差別可就大了。 如果把一個函數標記為 IMMUTABLE 而它實際上又不是, 那麼就會導致在隨後使用其規劃的時候用上一個不完整的數值。 如果在使用準備好語句或者使用一種緩衝規劃的函數語言(比如 PL/pgSQL), 那後果可能很嚴重。

因為 MVCC 的快照行為(參閱 Chapter 12),一個只包含 SELECT 命令的函數可以安全地標記為 STABLE,即使它所選擇的資料表可能會被並發查詢修改也一樣。 PostgreSQL 將為一個 STABLE 函數使用調用它的查詢建立的快照, 因此它在該查詢的生存期內都會看到一個資料庫的固定視圖。 還要指出的是,current_timestamp 族函數都是穩定的, 因為它們的數值在一個交易裡是不改變的。

同樣的快照行為也用於 IMMUTABLE 函數里面的 SELECT 命令。 通常,在一個 IMMUTABLE 函數里選擇一個資料庫的資料表是不明智的行為, 因為如果資料表的內容改變,那麼這種不變性就將改變。 不過,PostgreSQL 並不強制您不能做這些事情。

一個常見的錯誤是把一個函數標記為 IMMUTABLE,而實際上這個函數的結果依賴某個配置參數。 比如,一個操作時間戳的函數可能有依賴於 timezone 設置的結果。 為了安全考慮,這樣的函數應該標記為 STABLE

注意: PostgreSQL 版本 8.0 之前, 要求 STABLEIMMUTABLE 函數不能修改資料庫這個約束並未由系統強制。 版本 8.0 透過要求這類 SQL 或者過程語言函數不能包含 SELECT 之外的 SQL 命令來強制這個約束。 (這麼做並不是完全防彈的升級,因為這樣的函數仍然可以調用那些可能修改資料庫的 VOLATILE 函數。 如果您這麼做了,您會發現 STABLEIMMUTABLE 並不會意識到被它調用的函數對資料庫做的修改。