32.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 并不会意识到被它调用的函数对数据库做的修改。