每个函数都有一个易失性级别, 可能性有 VOLATILE,STABLE,或者 IMMUTABLE。 如果 CREATE FUNCTION 命令没有声明范畴,那么 VOLATILE 就是缺省。 易失性范畴是给优化器的一个承诺,关于函数的行为的承诺:
VOLATILE 可以做任何事情,包括修改数据库。 它可以在同样参数成功调用是返回不同的结果。优化器对这样的函数不做任何假设。 一个使用易失函数的查询在需要数据值的时候每次都重新计算函数的值。
STABLE 函数不能修改数据库,并且保证在一个查询的环境里, 在给出同样的参数的情况下,它会给出同样的结果。这个范畴允许优化器在一个查询里把多个函数调用优化成一个。 特别是,在索引扫描条件的表达式里面包含这样的函数是安全的。 (因为索引扫描只计算一次比较值,而不是每行一次,在索引扫描条件里使用一个 VOLATILE 函数是不合法的。)
一个 IMMUTABLE 函数不能修改数据库并且保证在给出同样的参数的情况下, 永远返回同样的结果。这个范畴允许优化器在查询调用函数的时候预先吧函数计算成一个常量参数。 比如,类似 SELECT ... WHERE x = 2 + 2 的查询可以简化成 SELECT ... WHERE x = 4,因为在加法操作符下层的函数是标记为 IMMUTABLE 的。
对于更好的优化结果,你应该用可以用的最严格的易失性范畴标记你的函数。
任何有副作用的函数都必须标记为 VOLATILE, 这样对其的调用就不会被优化。如果一个函数没有副作用,但是它的数值可能在一个查询里改变, 那么也必须标记为 VOLATILE; 这样的函数的例子是 random(),currval(),timeofday()。
在那些简单的规划后马上执行的交互查询上,STABLE 和 IMMUTABLE 没有什么区别:函数是在规划还是在执行开始的时候执行的差别并不大。 但是如果规划被保存并且后来重用,那差别可就大了。 如果把一个函数标记为 IMMUTABLE 而它实际上又不是, 那么就会导致在随后使用其规划的时候用上一个不完整的数值。 如果在使用准备好语句或者使用一种缓冲规划的函数语言(比如 PL/pgSQL), 那后果可能很严重。
因为 MVCC 的快照行为(参阅 Chapter 12),一个只包含 SELECT 命令的函数可以安全地标记为 STABLE,即使它所选择的表可能会被并发查询修改也一样。 PostgreSQL 将为一个 STABLE 函数使用调用它的查询建立的快照, 因此它在该查询的生存期内都会看到一个数据库的固定视图。 还要指出的是,current_timestamp 族函数都是稳定的, 因为它们的数值在一个事务里是不改变的。
同样的快照行为也用于 IMMUTABLE 函数里面的 SELECT 命令。 通常,在一个 IMMUTABLE 函数里选择一个数据库的表是不明智的行为, 因为如果表的内容改变,那么这种不变性就将改变。 不过,PostgreSQL 并不强制你不能做这些事情。
一个常见的错误是把一个函数标记为 IMMUTABLE,而实际上这个函数的结果依赖某个配置参数。 比如,一个操作时间戳的函数可能有依赖于 timezone 设置的结果。 为了安全考虑,这样的函数应该标记为 STABLE。
注意: 在 PostgreSQL 版本 8.0 之前, 要求 STABLE 和 IMMUTABLE 函数不能修改数据库这个约束并未由系统强制。 版本 8.0 通过要求这类 SQL 或者过程语言函数不能包含 SELECT 之外的 SQL 命令来强制这个约束。 (这么做并不是完全防弹的升级,因为这样的函数仍然可以调用那些可能修改数据库的 VOLATILE 函数。 如果你这么做了,你会发现 STABLE 或 IMMUTABLE 并不会意识到被它调用的函数对数据库做的修改。