Skip to content
Human Written
IMPORTANT

As you may notice, this page and pretty much the entire website were obviously created with the help of AI. I wonder how you could tell? Was it a big "Written With Claude" badge on every page? I moved it to the top now (with the help of AI of course) to make it even more obvious. There are a few blogposts that were written by me manually, the old-fashioned way, I hope there will be more in the future, and those have a similar "Human Written" badge. This project (not the website), on the other hand, is a very, very different story. It took me more than two years of painstaking and unpaid work in my own free time. A story that, hopefully, I will tell someday. But meanwhile, what would you like me to do? To create a complex documentation website with a bunch of highly technical articles with the help of AI and fake it, to give you an illusion that I also did that manually? Like the half of itnernet is doing at this point? How does that makes any sense? Is that even fair to you? Or maybe to create this website manually, the old-fashioned way, just for you? While working a paid job for a salary, most of you wouldn't even get up in the morning. Would you like me to sing you a song while we're at it? For your personal entertainment? Seriously, get a grip. Do you find this information less valuable because of the way this website was created? I give my best to fix it to keep the information as accurate as possible, and I think it is very accurate at this point. If you find some mistakes, inaccurancies or problems, there is a comment section at the bottom of every page, which I also made with the help of the AI. And I woould very much appreciate if you leave your feedback there. Look, I'm just a guy who likes SQL, that's all. If you don't approve of how this website was constructed and the use of AI tools, I suggest closing this page and never wever coming back. And good riddance. And I would ban your access if I could know how. Thank you for your attention to this matter.

PostgreSQL Optimization Labels 101

January 2026 · PostgreSQLOptimizationFunctionVOLATILESTABLEIMMUTABLE

Originally published on Medium

When creating a PostgreSQL function or procedure, you can declare many different labels that affect different aspects of your function. Some of them are optimization-oriented. Here is my 101 breakdown, because I keep forgetting them, and the best way to learn for me is to write.

VOLATILE / STABLE / IMMUTABLE

These are mutually exclusive.

  • VOLATILE (the default) — might change the database and return different results for the same parameters. PostgreSQL cannot optimize these at all, and this is the default behavior.

  • STABLE — can't change the database and will return the same results for the same parameters within the same query. Therefore, when called more than once in the same query, PostgreSQL will still execute this function only once, and hence the optimization.

  • IMMUTABLE — can't change the database and will always return the same results for the same parameters. Like a mathematical function, always the same. Most aggressive optimization will probably be called once per unique set of parameters and then cached. These types of functions can be used in indexing, where others cannot.

Important

If a function marked IMMUTABLE calls a STABLE function, it will revert to STABLE. If STABLE calls VOLATILE, it will revert to VOLATILE automatically, regardless of what is set during creation — so check the metadata.

PARALLEL UNSAFE / RESTRICTED / SAFE

Sometimes the query planner will opportunistically decide that some operation will run in parallel. This won't happen on a small set, only when the planner estimates a performance benefit. In that case you may see something like this in the planner:

Finalize Aggregate
    -> Gather
         Workers Planned: 2
         Workers Launched: 2
  • PARALLEL UNSAFE (the default) — forces planner to ditch parallel execution. These functions change databases and perform transactions.

  • PARALLEL RESTRICTED — restricts parallelism to the parallel group leader process. These functions can do temp tables, call cursors, and prepared statements.

  • PARALLEL SAFE — safe to run in parallel mode without restriction, enabling the planner to do parallelism when it chooses to do so.

COST / ROWS

COST (default: 100)

The planner needs to know how much operations cost in terms of execution performance, and functions are indeed operations. PostgreSQL uses its own arbitrary measurement unit.

  • With a lower cost (typically 1–10), the planner will not care about optimization when calling this function, and it will call it any time it damn well pleases.
  • With a higher cost (1000+), the planner will try to optimize calls for this function.

TIP

Tackling this setting should be done if the query is indeed slow.

ROWS (default: 1000)

The number of rows returned is another piece of information for the planner that can be used to optimize calls and plan execution better. Therefore, this can only be used in functions that return sets (SETOF or TABLE).

A planner cannot possibly know the number of rows returned, and it will assume that it is 1000. But you can, and if you do, you can set this parameter.

The practical implications of this optimization label depend on which joining strategy the planner chooses, based on the row estimate:

  • Nested loop — a sort of database internal N+1, suitable strategy for small datasets
  • Hashed join — when the planner decides it will make hashes first, and then use them to compare joining keys, much more efficient for larger sets than nested join

In any case, this parameter only makes sense if you tend to join a function that returns sets.

CALLED ON NULL INPUT / RETURNS NULL ON NULL INPUT / STRICT

If you have a function that is supposed to return NULL if one of the parameters is NULL, you can just say so by labeling it RETURNS NULL ON NULL INPUT or shorter STRICT. This tells the engine to avoid calling the function in that case — simply replace it with a NULL value.

Default is CALLED ON NULL INPUT, which is normal behavior, and it doesn't need to be stated explicitly.


This concludes my short series on PostgreSQL optimization labels (some might call them hints).

Comments

Released under the MIT License.