--- id: 'functions' title: 'Database Functions' description: 'Creating and using Postgres functions.' video: 'https://www.youtube.com/v/MJZCCpCYEqk' --- Postgres has built-in support for [SQL functions](https://www.postgresql.org/docs/current/sql-createfunction.html). These functions live inside your database, and you can call them from your app with [`rpc()`](../../reference/javascript/rpc). ## Quick demo ## Getting started Create a database function from the Dashboard, or write the SQL yourself against a [direct connection](../../guides/database/connecting-to-postgres). To use the Dashboard: 1. Go to the **SQL Editor** section. 2. Click **New Query**. 3. Enter the SQL that creates or replaces your database function. 4. Click **Run**. You can also press `cmd+enter` or `ctrl+enter`. ## Basic functions [#simple-functions] Create a basic database function that returns the string "hello world". ```sql create or replace function hello_world() -- 1 returns text -- 2 language sql -- 3 as $$ -- 4 select 'hello world'; -- 5 $$; --6 ```
Show/Hide Details At its most basic, a function has the following parts: 1. `create or replace function hello_world()`: The function declaration, where `hello_world` is the name of the function. Use `create` for a new function, `replace` for one that exists, or `create or replace` when the function might not exist yet. 2. `returns text`: The type of data the function returns. For a function that returns nothing, write `returns void`. 3. `language sql`: The language used inside the function body. This can also be a procedural language: `plpgsql`, `plpython`, etc. 4. `as $$`: The function wrapper. Anything inside the `$$` symbols is part of the function body. 5. `select 'hello world';`: A basic function body. The function returns the result of the last `select` statement in its body. 6. `$$;`: The closing symbols of the function wrapper.
Overloaded functions aren't supported. Give every function a unique name. After you create the function, you can run it inside the database with SQL, or with one of the client libraries. ```sql select hello_world(); ``` ```js const { data, error } = await supabase.rpc('hello_world') ``` Reference: [`rpc()`](../../reference/javascript/rpc) <$Show if="sdk:dart"> ```dart final data = await supabase .rpc('hello_world'); ``` Reference: [`rpc()`](../../reference/dart/rpc) <$Show if="sdk:swift"> ```swift try await supabase.rpc("hello_world").execute() ``` Reference: [`rpc()`](../../reference/swift/rpc) <$Show if="sdk:kotlin"> ```kotlin val data = supabase.postgrest.rpc("hello_world") ``` Reference: [`rpc()`](../../reference/kotlin/rpc) <$Show if="sdk:python"> ```python data = supabase.rpc('hello_world').execute() ``` Reference: [`rpc()`](../../reference/python/rpc) <$Show if="sdk:csharp"> ```c# await supabase.Rpc("hello_world", null); ``` Reference: [`Rpc()`](../../reference/csharp/rpc) ## Returning data sets A database function can also return a data set from a [table](../../guides/database/tables) or a view. For example, take a database holding some Star Wars data: ### Planets ``` | id | name | | --- | -------- | | 1 | Tatooine | | 2 | Alderaan | | 3 | Kashyyyk | ``` ### People ``` | id | name | planet_id | | --- | ---------------- | --------- | | 1 | Anakin Skywalker | 1 | | 2 | Luke Skywalker | 1 | | 3 | Princess Leia | 2 | | 4 | Chewbacca | 3 | ``` ```sql create table planets ( id serial primary key, name text ); insert into planets (id, name) values (1, 'Tattoine'), (2, 'Alderaan'), (3, 'Kashyyyk'); create table people ( id serial primary key, name text, planet_id bigint references planets ); insert into people (id, name, planet_id) values (1, 'Anakin Skywalker', 1), (2, 'Luke Skywalker', 1), (3, 'Princess Leia', 2), (4, 'Chewbacca', 3); ``` The following function returns all the planets: ```sql create or replace function get_planets() returns setof planets language sql as $$ select * from planets; $$; ``` Because this function returns a table set, you can apply filters and selectors to it. To get the first planet only: ```sql select * from get_planets() where id = 1; ``` ```js const { data, error } = supabase.rpc('get_planets').eq('id', 1) ``` <$Show if="sdk:dart"> ```dart final data = await supabase .rpc('get_planets') .eq('id', 1); ``` <$Show if="sdk:swift"> ```swift let response = try await supabase.rpc("get_planets").eq("id", value: 1).execute() ``` <$Show if="sdk:kotlin"> ```kotlin val data = supabase.postgrest.rpc("get_planets") { filter { eq("id", 1) } } ``` <$Show if="sdk:python"> ```python data = supabase.rpc('get_planets').eq('id', 1).execute() ``` ## Passing parameters Create a function that inserts a new planet into the `planets` table and returns the new ID. This function uses the `plpgsql` language. ```sql create or replace function add_planet(name text) returns bigint language plpgsql as $$ declare new_row bigint; begin insert into planets(name) values (add_planet.name) returning id into new_row; return new_row; end; $$; ``` You can run this function inside your database with a `select` query, or with the client libraries: ```sql select * from add_planet('Jakku'); ``` ```js const { data, error } = await supabase.rpc('add_planet', { name: 'Jakku' }) ``` <$Show if="sdk:dart"> ```dart final data = await supabase .rpc('add_planet', params: { 'name': 'Jakku' }); ``` <$Show if="sdk:swift"> Using `Encodable` type: ```swift struct Planet: Encodable { let name: String } try await supabase.rpc( "add_planet", params: Planet(name: "Jakku") ) .execute() ``` Using `AnyJSON` convenience` type: ```swift try await supabase.rpc( "add_planet", params: ["name": AnyJSON.string("Jakku")] ) .execute() ``` <$Show if="sdk:kotlin"> ```kotlin val data = supabase.postgrest.rpc( function = "add_planet", parameters = buildJsonObject { //You can put here any serializable object including your own classes put("name", "Jakku") } ) ``` <$Show if="sdk:python"> ```python data = supabase.rpc('add_planet', params={'name': 'Jakku'}).execute() ``` <$Show if="sdk:csharp"> ```c# await supabase.Rpc("add_planet", new Dictionary { { "name", "Jakku" } }); ``` ## Suggestions ### Database Functions vs Edge Functions For data-intensive operations, use database functions. They run inside your database, and you can call them remotely with the [REST and GraphQL API](../api). For use cases that need low latency, use [Edge Functions](../../guides/functions). They're globally distributed and you write them in TypeScript. ### Security `definer` vs `invoker` Postgres runs a function either as the user _calling_ it (`invoker`) or as its _creator_ (`definer`). For example: ```sql create function hello_world() returns text language plpgsql security definer set search_path = '' as $$ begin return 'hello world'; end; $$; ``` Prefer `security invoker`, which is also the default. When you use `security definer`, you must set the `search_path`. With an empty search path, `search_path = ''`, name the schema for every relation in the function body, such as `from public.table`. An empty search path limits the damage when the function can reach a schema you don't want the calling user to reach. ### Function privileges By default, any role can run a database function. You can restrict execution in two ways: 1. Revoke on a case-by-case basis. Revoke execute for the functions you want to protect, from both `public` and the role you're restricting: ```sql revoke execute on function public.hello_world from public; revoke execute on function public.hello_world from anon; ``` 1. Restrict execution by default, then grant access to the roles that need each function. To restrict every function that exists, revoke execute from both `public` and the role you want to restrict: ```sql revoke execute on all functions in schema public from public; revoke execute on all functions in schema public from anon, authenticated; ``` To restrict every function created later, change the default privileges for both `public` and the role you want to restrict: ```sql alter default privileges in schema public revoke execute on functions from public; alter default privileges in schema public revoke execute on functions from anon, authenticated; ``` You can then regrant permissions for a specific function to a specific role: ```sql grant execute on function public.hello_world to authenticated; ``` ### Debugging functions Add logs to help you debug a function. Logs matter most in a complex function. Good targets to log include: - Values of (non-sensitive) variables - Returned results from queries #### General logging Use the `raise` keyword to write custom logs to the [Postgres logs](/dashboard/project/_/logs/postgres-logs) in the Dashboard. Three severity levels appear by default: - `log` - `warning` - `exception` (error level) ```sql create function logging_example( log_message text, warning_message text, error_message text ) returns void language plpgsql as $$ begin raise log 'logging message: %', log_message; raise warning 'logging warning: %', warning_message; -- immediately ends function and reverts transaction raise exception 'logging error: %', error_message; end; $$; select logging_example('LOGGED MESSAGE', 'WARNING MESSAGE', 'ERROR MESSAGE'); ``` #### Error handling You can create custom errors with the `raise exception` keywords. A common pattern is to throw an error when a variable doesn't meet a condition: ```sql create or replace function error_if_null(some_val text) returns text language plpgsql as $$ begin -- error if some_val is null if some_val is null then raise exception 'some_val should not be NULL'; end if; -- return some_val if it is not null return some_val; end; $$; select error_if_null(null); ``` Value checking is common, so Postgres provides the `assert` keyword as a shorthand. It takes the following format: ```sql -- throw error when condition is false assert , 'message'; ``` For example: ```sql create function assert_example(name text) returns uuid language plpgsql as $$ declare student_id uuid; begin -- save a user's id into the user_id variable select id into student_id from attendance_table where student = name; -- throw an error if the student_id is null assert student_id is not null, 'assert_example() ERROR: student not found'; -- otherwise, return the user's id return student_id; end; $$; select assert_example('Harry Potter'); ``` You can also capture and modify an error message with the `exception` keyword: ```sql create function error_example() returns void language plpgsql as $$ begin -- fails: cannot read from nonexistent table select * from table_that_does_not_exist; exception when others then raise exception 'An error occurred in function : %', sqlerrm; end; $$; ``` #### Advanced logging For a more complex function, or for harder debugging, log the following: - Formatted variables - Individual rows - Start and end of function calls ```sql create or replace function advanced_example(num int default 10) returns text language plpgsql as $$ declare var1 int := 20; var2 text; begin -- Logging start of function raise log 'logging start of function call: (%)', (select now()); -- Logging a variable from a SELECT query select col_1 into var1 from some_table limit 1; raise log 'logging a variable (%)', var1; -- It is also possible to avoid using variables, by returning the values of your query to the log raise log 'logging a query with a single return value(%)', (select col_1 from some_table limit 1); -- If necessary, you can even log an entire row as JSON raise log 'logging an entire row as JSON (%)', (select to_jsonb(some_table.*) from some_table limit 1); -- When using INSERT or UPDATE, the new value(s) can be returned -- into a variable. -- When using DELETE, the deleted value(s) can be returned. -- All three operations use "RETURNING value(s) INTO variable(s)" syntax insert into some_table (col_2) values ('new val') returning col_2 into var2; raise log 'logging a value from an INSERT (%)', var2; return var1 || ',' || var2; exception -- Handle exceptions here if needed when others then raise exception 'An error occurred in function : %', sqlerrm; end; $$; select advanced_example(); ``` ## Resources - Official Client libraries: [JavaScript](../../reference/javascript/rpc) and [Flutter](../../reference/dart/rpc) - Community client libraries: [github.com/supabase-community](https://github.com/supabase-community) - Postgres Official Docs: [Chapter 9. Functions and Operators](https://www.postgresql.org/docs/current/functions.html) - Postgres Reference: [CREATE FUNCTION](https://www.postgresql.org/docs/9.1/sql-createfunction.html) ## Deep dive ### Create Database Functions ### Call Database Functions using JavaScript ### Using Database Functions to call an external API