35.6. 函数易失性范畴
每个函数都有一个易失性级别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()
函数。
另一个重要例子是current_timestamp
函数簇描述为STABLE
, 因为他们的值在一个事务中没有改变。
在那些简单的规划后马上执行的交互查询上,STABLE
和 IMMUTABLE
没有什么区别: 函数是在规划开始时执行还是在查询开始时执行的差别并不大。 但是如果规划被保存并且后来被重用,那差别可就大了。 如果把一个函数标记为IMMUTABLE
而它实际上又不是, 那么就会导致在随后使用其规划的时候用上一个不完整的数值。 如果在使用预先准备好语句或者使用一种缓冲规划的函数语言(比如PL/pgSQL), 那么后果可能很严重。
为了编写SQL或在任何标准的程序语言的函数,还有一个通过波动性范畴决定的重要属性, 即由SQL命令决定的任何数据变化能见度正在调用函数。 一个VOLATILE
函数将看到这样的变化,STABLE
或者IMMUTABLE
函数不这样。这种行为 使用MVCC快照行为实现(参阅Chapter 13): STABLE
和IMMUTABLE
函数使用快照 确立为调用查询的开始, 而VOLATILE
函数每个查询执行开始时获得一个新的快照。
Note: 用C写的函数管理快照然而是他们想要的,但是使C函数这样进行工作往往是一个好主意。
因为快照行为, 一个只包含SELECT
命令的函数可以安全地标记为STABLE
, 即使它所选择的表可能会被其它并发查询修改也一样。 PostgreSQL将会在执行STABLE
函数时为调用它的查询建立快照, 因此它在该查询的生存期内都会看到一致的数据库视图。
同样的快照行为也用于IMMUTABLE
函数里面的SELECT
命令。 通常,在一个IMMUTABLE
函数里选择一个数据库的表是不明智的, 因为如果表的内容改变,那么这种不变性就将改变。不过, PostgreSQL并不禁止你这样做。
一个常见的错误是把一个函数标记为IMMUTABLE
, 而实际上这个函数的结果依赖某个配置参数。比如, 一个操作时间戳的函数可能有依赖于TimeZone设置的结果。 为了安全考虑,这样的函数应该标记为STABLE
。
Note: 在PostgreSQL之前,要求
STABLE
和IMMUTABLE
函数不能修改数据库这个约束并未由系统强制。版本8.0 通过要求这类函数不能包含SELECT
之外的 SQL 命令来强制这个约束。 不过,这么做并不是完全防弹的升级, 因为这样的函数仍然可以调用那些可能修改数据库的VOLATILE
函数。 如果你这么做的话将会发现STABLE
或者IMMUTABLE
并不会觉察到被它调用的函数对数据库所做的修改。