35.6. 函数易失性范畴

每个函数都有一个易失性级别VOLATILESTABLE或者 IMMUTABLE。 如果CREATE FUNCTION命令没有明确声明范畴的话, VOLATILE就是缺省。易失性范畴是给优化器的一个关于函数行为的承诺:

为了最佳的优化结果,应该尽可能使用最严格的易失性范畴标记你的函数。

任何有副作用的函数都必须标记为VOLATILE, 这样对它的调用就不会被优化。即使一个函数没有副作用, 但它的数值可能在一个查询里改变,那么也必须标记为VOLATILE; 例如random()currval()timeofday()函数。

另一个重要例子是current_timestamp函数簇描述为STABLE, 因为他们的值在一个事务中没有改变。

在那些简单的规划后马上执行的交互查询上,STABLEIMMUTABLE没有什么区别: 函数是在规划开始时执行还是在查询开始时执行的差别并不大。 但是如果规划被保存并且后来被重用,那差别可就大了。 如果把一个函数标记为IMMUTABLE而它实际上又不是, 那么就会导致在随后使用其规划的时候用上一个不完整的数值。 如果在使用预先准备好语句或者使用一种缓冲规划的函数语言(比如PL/pgSQL), 那么后果可能很严重。

为了编写SQL或在任何标准的程序语言的函数,还有一个通过波动性范畴决定的重要属性, 即由SQL命令决定的任何数据变化能见度正在调用函数。 一个VOLATILE函数将看到这样的变化,STABLE 或者IMMUTABLE函数不这样。这种行为 使用MVCC快照行为实现(参阅第 13 章): STABLEIMMUTABLE函数使用快照 确立为调用查询的开始, 而VOLATILE函数每个查询执行开始时获得一个新的快照。

注意: 用C写的函数管理快照然而是他们想要的,但是使C函数这样进行工作往往是一个好主意。

因为快照行为, 一个只包含SELECT命令的函数可以安全地标记为STABLE, 即使它所选择的表可能会被其它并发查询修改也一样。 PostgreSQL将会在执行STABLE 函数时为调用它的查询建立快照, 因此它在该查询的生存期内都会看到一致的数据库视图。

同样的快照行为也用于IMMUTABLE函数里面的SELECT命令。 通常,在一个IMMUTABLE函数里选择一个数据库的表是不明智的, 因为如果表的内容改变,那么这种不变性就将改变。不过, PostgreSQL并不禁止你这样做。

一个常见的错误是把一个函数标记为IMMUTABLE, 而实际上这个函数的结果依赖某个配置参数。比如, 一个操作时间戳的函数可能有依赖于TimeZone设置的结果。 为了安全考虑,这样的函数应该标记为STABLE

注意: PostgreSQL要求STABLEIMMUTABLE 函数不包含除SELECT之外的SQL命令,以防止数据修改。 不过,这么做并不是完全防弹的升级, 因为这样的函数仍然可以调用那些可能修改数据库的VOLATILE函数。 如果你这么做的话将会发现STABLE或者 IMMUTABLE 并不会觉察到被它调用的函数对数据库所做的修改。