PostgreSQL:Function
lastmod 2026-05-12created 2021-05-0220 edits
NULLIF
- Returns NULL if exp1 and exp2 are equal, otherwise returns exp1
-
CASE WHEN expr1 = expr2 THEN NULL ELSE expr1 END
Checking the current Role/User
DO
When developing/testing a Function, you sometimes want to check Function syntax/features. Creating a Function, testing it, and then deleting the Function is very inefficient; the DO feature/SQL command is what you use in this situation.
You can think of DO as a temporary Function with no Parameter that returns void.
COALESCE
Used for Null checks.
If the column is null, the substitute value is returned instead. (It can be substituted with another column.)
Example
Column
Specified string
null
json
Trigger Functions
Trigger function security summary
Basic principle: Trigger functions run as SECURITY INVOKER by default. Not every trigger function needs SECURITY DEFINER.
When SECURITY DEFINER is needed
When the function must query/modify other tables or bypass RLS:
| Function | Reason |
| | INSERT into the |
| | COUNT query on the |
| | COUNT query on the |
In this case you must also set SET search_path = '' (to prevent search_path injection).
When SECURITY DEFINER is not needed
When the function only manipulates the NEW/OLD records of its own table:
| Function | Reason |
| | Uses only NEW/OLD (immutable protection) |
| | Uses only NEW/OLD (immutable protection) |
| | Uses only NEW/OLD (immutable protection) |
These functions do not access external tables, so the default SECURITY INVOKER is sufficient.
Calling trigger functions via Supabase RPC
Trigger functions cannot be called via RPC.
Reason: The return type of a trigger function is TRIGGER.
-- 트리거 함수 시그니처
CREATE FUNCTION handle_new_org() RETURNS TRIGGER ...
-- Supabase RPC 호출 시
SELECT public.handle_new_org();
-- ERROR: trigger functions can only be called as triggers
Supabase RPC (supabase.rpc('function_name')) internally runs SELECT function_name(), but a RETURNS TRIGGER function can only run in a trigger context (NEW, OLD, TG_OP, etc.).
Functions callable via RPC: Only those with ordinary return types such as RETURNS void, RETURNS json, RETURNS setof record.
Therefore the trigger functions in the current project carry no risk of being called directly via RPC. Even functions with SECURITY DEFINER set run only through trigger events.
CREATE FUNCTION
The syntax alone looks rather complicated:
CREATE [ OR REPLACE ] FUNCTION
name ( [ [ argmode ] [ argname ] argtype [ { DEFAULT | = } default_expr ] [, ...] ] )
[ RETURNS rettype
| RETURNS TABLE ( column_name column_type [, ...] ) ]
{ LANGUAGE lang_name
| TRANSFORM { FOR TYPE type_name } [, ... ]
| WINDOW
| { IMMUTABLE | STABLE | VOLATILE }
| [ NOT ] LEAKPROOF
| { CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT }
| { [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER }
| PARALLEL { UNSAFE | RESTRICTED | SAFE }
| COST execution_cost
| ROWS result_rows
| SUPPORT support_function
| SET configuration_parameter { TO value | = value | FROM CURRENT }
| AS 'definition'
| AS 'obj_file', 'link_symbol'
| sql_body
} ...
Increment an integer, making use of an argument name, in PL/pgSQL:
CREATE OR REPLACE FUNCTION increment(i integer) RETURNS integer AS $$
BEGIN
RETURN i + 1;
END;
$$ LANGUAGE plpgsql;
Return a record containing multiple output parameters:
CREATE FUNCTION dup(in int, out f1 int, out f2 text)
AS $$ SELECT $1, CAST($1 AS text) || ' is text' $$
LANGUAGE SQL;
SELECT * FROM dup(42);
security option
The security options are as follows.
- Runs with the privileges of the caller, not the function creator - security invoker (default)
- Runs with the privileges of the function creator - security definer
Example:
-- 3. Create security definer function, which should be run as "postgres"
create function private.get_teams_for_authenticated_user()
returns setof bigint
language sql
security definer -- 실행 권한을 실행한 측이 아닌 함수 정의한 측(definer)로 지정.
set search_path = public -- 검색 경로 'public' schema 로 제한
stable -- 내용을 수정하지 않는다.
as $$
select team_id
from members
where user_id = auth.uid()
$$;
Function volatility classification
See PostgreSQL:Volatility for details.