全ての関数は、変動性区分を持ちます。 取り得る区分は、VOLATILE、STABLE、もしくはIMMUTABLEです。 CREATE FUNCTIONコマンドで分類の指定がなければデフォルトでVOLATILEになります。 変動性に関する分類は、その関数に関するオプティマイザへの約束事です。
VOLATILE関数は、データベースの変更を含む、全てを行うことができます。 同一引数で続けて呼び出したとしても異なる結果を返すことができます。 オプティマイザはこうした関数の振舞いに対する前提を持ちません。 変動関数を使用した問い合わせは、その行の値を必要とする全ての行においてその関数を再評価します。
STABLE関数はデータベースを変更することができません。 また、単一の文内ですべての行に対して同一の引数を渡した場合に同一の結果を返すことが保証されています。 この区分により、オプティマイザは複数の関数の呼び出しを1つの呼び出しに最適化することができます。 特に、インデックススキャン条件内でこうした関数を含んだ式を使用することは安全です (インデックススキャンは行ごとに一度ではなく、一度だけ値の比較の評価を行いますので、インデックススキャン条件内でVOLATILE関数を使用することは意味がありません)。
IMMUTABLE関数はデータベースを変更することができません。 また、同一引数に対する呼び出しは常に同一の結果を返すことが保証されています。 問い合わせが定数の引数でこうした関数を呼び出した場合、オプティマイザはこの関数を事前に評価することができます。 例えば、SELECT ... WHERE x = 2 + 2 といった問い合わせは、SELECT ... WHERE x = 4のように単純化することができます。 これは、背後にある整数加算演算子がIMMUTABLEとして宣言されているためです。
最適化の結果を最善にするためには、関数に対して有効かつ最も厳密な変動性区分を付けなければなりません。
副作用を持つ関数は全て、VOLATILEと付けなければなりません。 こうした関数は最適化することができないためです。 関数が副作用を持たなかったとしても、単一問い合わせ内で値が変動する場合はVOLATILEと付けなければなりません。 例えば、random()、currval()、timeofday()などです。
その他の重要な例は、関数のcurrent_timestamp
系列はそれらの値がトランザクション内で変更しないことから、STABLEと見なされます。
計画作成を行い、すぐに実行されるような単一の対話式問い合わせを考えた場合、相対的にSTABLE区分とIMMUTABLE区分との違いはあまりありません。 このような場合、関数が計画作成中に一度実行されるか、問い合わせ実行中に一度実行されるかがあまり問題になりません。 しかし、計画が保存され、後で再利用される場合は大きな違いが現れます。 本来ならば関数が計画作成段階で早めに定数を保持することができない場合にIMMUTABLEを付けると、その後にこの計画を使用する時に古くて意味のない値が再利用されてしまうことになります。 これは、プリペアド文や計画をキャッシュする関数言語(PL/pgSQLなど)を使用する場合は危険です。
SQLもしくは標準手続き言語で書かれた関数に対し、揮発性と分類される範疇で決定される2番目に重要な性質があります。すなわち、その関数を呼び出すSQLコマンドにより作られてきている全てのデータ変更の可視性です。VOLATILE(揮発的)関数はそのような変更を捕らえますが、STABLE(安定的)またはIMMUTABLE(普遍的)関数はそうしません。この動作はMVCC(第13章を参照)のスナップショット行動を使用して実装されています。STABLEとIMMUTABLE関数は、呼び出す問い合わせの開始時点で成立したスナップショットを使用しますが、VOLATILE関数はそれぞれの問い合わせの実行開始時点の作りたてのスナップショットを取得します。
注意: C言語で書かれた関数は、どのように希望したとしてもスナップショットを管理しますが、通常C関数がこのようにも動作するようにすることは良い考えです。
このスナップショット取得の動作のため、同時実行の問い合わせによって別途変更されている可能性があるテーブルに対して選択していたとしても、SELECTコマンドのみを含む関数は、安全にSTABLEとすることができます。PostgreSQLは呼び出す問い合わせに対し、確立されたスナップショットを使用してSTABLE関数の全てのコマンドを実行します。従ってその問い合わせ間でデータベースデータベースに対して固定された視点で値を参照することになります。
IMMUTABLE関数内のSELECTコマンドも同様のスナップショット機能を使用します。 ただし、一般的に、IMMUTABLE内でデータベースのテーブルを選択することは勧められません。 テーブルの内容が変わってしまった場合にその普遍性が壊れてしまうためです。 しかし、PostgreSQLでは、強制的に選択できないようにはしていません。
よくあるエラーは、設定パラメータに依存する結果となる関数にIMMUTABLEと付けることです。 例えば、タイムスタンプを操作する関数は、おそらくtimezoneの設定に依存した結果になります。 安全のために、こうした関数は代わりにSTABLEと付けてください。
注意: PostgreSQLリリース8.0より前のリリースでは、STABLE関数とIMMUTABLE関数がデータベースを変更できないという必要条件がシステムによって禁止されていませんでした。 リリース8.0以降では、この区分のSQL関数や手続き言語関数に対してSELECT以外のSQLコマンドを含めないことを必要条件とすることにより、これを禁止しています (こうした関数はまだデータベースを変更するVOLATILE関数を呼び出すことができますので、これは完全な防弾用の条件ではありません。 これを行うと、STABLEもしくはIMMUTABLE関数は、そのスナップショットからそれらが隠されていることから、呼び出した関数によるデータベースの変更に気がつきません)。