すべての関数は変動性区分を持ちます。
取り得る区分は、VOLATILE
、STABLE
、もしくはIMMUTABLE
です。
CREATE FUNCTION
コマンドで分類の指定がなければデフォルトでVOLATILE
になります。
変動性に関する分類は、その関数の動作に関するオプティマイザへの約束事です。
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
関数内でデータベースのテーブルを検索(SELECT)することは勧められません。
テーブルの内容が変わってしまった場合にその不変性が壊れてしまうためです。
しかし、PostgreSQLでは強制的に検索(SELECT)できないようにはしていません。
よくあるエラーは、設定パラメータに依存する結果となる関数にIMMUTABLE
と付けることです。
例えば、タイムスタンプを操作する関数は、おそらくTimeZoneの設定に依存した結果になります。
こうした関数は、安全のため代わりにSTABLE
と付けてください。
PostgreSQLはデータの変更を防ぐためにSTABLE
関数とIMMUTABLE
関数がSELECT
以外のSQLコマンドを含まないことを要求します。
(こうした関数はまだデータベースを変更するVOLATILE
関数を呼び出すことができますので、これは防弾条件として完全ではありません。
これを行うと、STABLE
もしくはIMMUTABLE
関数は、そのスナップショットからそれらが隠されていることから、呼び出した関数によるデータベースの変更に気がつきません。)