Function Volatility Classification Levels
PostgreSQL functions have three volatility levels by default. Proper use can significantly improve performance.
Core Differences
VOLATILE: Has side effects, cannot be optimized.STABLE: Executes database queries.IMMUTABLE: Pure function, execution results may be pre-evaluated and cached during planning.
When to Use?
VOLATILE: Any writes, any side effects, needs to see changes made by external commands, or calls anyVOLATILEfunctionSTABLE: Has database queries but no writes, or function results depend on configuration parameters (e.g., timezone)IMMUTABLE: Pure function.
Detailed Explanation
Each function carries a volatility level. Possible values include VOLATILE, STABLE, and IMMUTABLE. If no volatility level is specified when creating a function, it defaults to VOLATILE. Volatility is the function’s promise to the optimizer:
VOLATILEfunctions can do anything, including modifying database state. They may return different results on consecutive calls even with the same arguments. The optimizer won’t optimize away such functions; they are re-evaluated every time they’re called.STABLEfunctions cannot modify database state, and guarantee that given the same arguments within a single statement, they will return the same result. Therefore, the optimizer can optimize multiple calls with the same parameters into a single call.STABLEfunctions are allowed in index scan conditions, butVOLATILEfunctions are not. (In an index scan, comparison values are evaluated only once, not once per row, soVOLATILEfunctions cannot be used in index scan conditions).IMMUTABLEfunctions cannot modify database state and guarantee that given inputs will always return the same result at any time. This classification allows the optimizer to pre-compute the function when it’s called with constant parameters in a query. For example, a query likeSELECT ... WHERE x = 2 + 2can be simplified toSELECT ... WHERE x = 4because the underlying function of the integer addition operator is marked asIMMUTABLE.
Difference Between STABLE and IMMUTABLE
Call Count Optimization
Take this function as an example, which simply returns the constant 2:
When using the STABLE tag, it actually calls 10 times, but when using the IMMUTABLE tag, it’s optimized to a single call.
Here we change the function tag to IMMUTABLE:
Running the same query again, this time the function is called only once:
Execution Plan Caching
The second example concerns function calls in index conditions. Suppose we have a table containing integers from 1 to 1000:
Now create an IMMUTABLE function mymax:
We’ll find that when we use this function directly in index conditions, the index condition in the execution plan is directly evaluated, cached, and solidified as id=2:
But if we change it to a STABLE function, the result becomes runtime evaluation:
