User-Defined Functions
COALESCE or LOWER. PostgreSQL functions are created with CREATE FUNCTION and most commonly written in PL/pgSQL.Scalar functions
CREATE FUNCTION discounted_price(price numeric, discount_pct numeric) RETURNS numeric LANGUAGE plpgsql IMMUTABLE AS $$ BEGIN RETURN round(price - (price * discount_pct / 100), 2); END; $$; SELECT discounted_price(199.99, 20);
discounted_price
------------------
159.99Table-valued functions
RETURNS TABLE or RETURNS SETOF. It can be queried from the FROM clause just like a real table:CREATE FUNCTION products_below_price(max_price numeric)
RETURNS TABLE (id integer, name text, price numeric)
LANGUAGE plpgsql
STABLE
AS $$
BEGIN
RETURN QUERY
SELECT p.id, p.name, p.price
FROM products p
WHERE p.price <= max_price
ORDER BY p.price;
END;
$$;
SELECT * FROM products_below_price(25.00);id | name | price ---+---------------+------- 4 | USB Cable | 8.99 7 | Notebook | 4.50 12 | Wireless Mouse| 19.99
Volatility: IMMUTABLE, STABLE, VOLATILE
Category | Meaning | Planner behavior |
|---|---|---|
| Given the same arguments, always returns the same result, with no dependency on the database state | Can be evaluated once and the result reused/cached, even folded into an index |
| Given the same arguments, returns the same result within a single statement/scan, but may depend on database state (e.g. reading a table) | Can be evaluated once per statement rather than once per row |
| May return a different result on every call, even with identical arguments (e.g. | Must be re-evaluated for every single row — no caching or reordering |
VOLATILE is the default if you don't specify a category, and it's always safe — it just gives up optimization opportunities. Declaring a function IMMUTABLE or STABLE when it genuinely qualifies can meaningfully speed up queries that call it repeatedly, but declaring it incorrectly (e.g. marking a function IMMUTABLE when it actually reads a table that changes) can lead to PostgreSQL caching a stale result and returning wrong answers. Only use IMMUTABLE/STABLE when the function genuinely behaves that way.CREATE FUNCTIONdefines a scalar function (single value) or a table-valued function (RETURNS TABLE/RETURNS SETOF).Table-valued functions can be queried directly in a
FROMclause.Volatility (
IMMUTABLE/STABLE/VOLATILE) tells the planner how aggressively it can optimize calls to the function.Getting volatility wrong in the "too optimistic" direction can produce incorrect query results, not just missed optimizations.