Database Functions
Postgres has built-in support for SQL functions. These functions live inside your database, and they can be used with the API.
Quick demo#
Getting started#
Supra provides several options for creating database functions. You can use the Dashboard or create them directly using SQL. We provide a SQL editor within the Dashboard, or you can connect to your database and run the SQL queries yourself.
- Go to the "SQL editor" section.
- Click "New Query".
- Enter the SQL to create or replace your Database function.
- Click "Run" or cmd+enter (ctrl+enter).
Simple Functions#
Let's create a basic Database Function which returns a string "hello world".
_10create or replace function hello_world() -- 1_10returns text -- 2_10language sql -- 3_10as $$ -- 4_10 select 'hello world'; -- 5_10$$; --6
Show/Hide Details
At it's most basic a function has the following parts:
create or replace function hello_world(): The function declaration, wherehello_worldis the name of the function. You can use eithercreatewhen creating a new function orreplacewhen replacing an existing function. Or you can usecreate or replacetogether to handle either.returns text: The type of data that the function returns. If it returns nothing, you canreturns void.language sql: The language used inside the function body. This can also be a procedural language:plpgsql,plv8,plpython, etc.as $$: The function wrapper. Anything enclosed inside the$$symbols will be part of the function body.select 'hello world';: A simple function body. The finalselectstatement inside a function body will be returned if there are no statements following it.$$;: The closing symbols of the function wrapper.
After the Function is created, we have several ways of "executing" the function - either directly inside the database using SQL, or with one of the client libraries.
_10select hello_world();
Returning data sets#
Database Functions can also return data sets from Tables or Views.
For example, if we had a database with some Star Wars data inside:
Planets
| id | name |
|---|---|
| 1 | Tattoine |
| 2 | Alderaan |
| 3 | Kashyyyk |
People
| id | name | planet_id |
|---|---|---|
| 1 | Anakin Skywalker | 1 |
| 2 | Luke Skywalker | 1 |
| 3 | Princess Leia | 2 |
| 4 | Chewbacca | 3 |
We could create a function which returns all the planets:
_10create or replace function get_planets()_10returns setof planets_10language sql_10as $$_10 select * from planets;_10$$;
Because this function returns a table set, we can also apply filters and selectors. For example, if we only wanted the first planet:
_10select *_10from get_planets()_10where id = 1;
Passing parameters#
Let's create a Function to insert a new planet into the planets table and return the new ID. Note that this time we're using the plpgsql language.
_14create or replace function add_planet(name text)_14returns bigint_14language plpgsql_14as $$_14declare_14 new_row bigint;_14begin_14 insert into planets(name)_14 values (add_planet.name)_14 returning id into new_row;_14_14 return new_row;_14end;_14$$;
Once again, you can execute this function either inside your database using a select query, or with the client libraries:
_10select * from add_planet('Jakku');
Suggestions#
Database Functions vs Edge Functions#
For data-intensive operations, use Database Functions, which are executed within your database and can be called remotely using the REST and GraphQL API.
For use-cases which require low-latency, use Edge Functions, which are globally-distributed and can be written in Typescript.
Security definer vs invoker#
Postgres allows you to specify whether you want the function to be executed as the user calling the function (invoker), or as the creator of the function (definer). For example:
_10create function hello_world()_10returns text_10language plpgsql_10security definer set search_path = public_10as $$_10begin_10 select 'hello world';_10end;_10$$;
It is best practice to use security invoker (which is also the default). If you ever use security definer, you must set the search_path.
This limits the potential damage if you allow access to schemas which the user executing the function should not have.
Function privileges#
By default, database functions can be executed by any role. You can restrict this by altering the default privileges and then choosing which roles can execute functions.
_10ALTER DEFAULT PRIVILEGES REVOKE EXECUTE ON FUNCTIONS FROM PUBLIC;_10_10-- Choose which roles can execute functions_10GRANT EXECUTE ON FUNCTION hello_world TO authenticated;_10GRANT EXECUTE ON FUNCTION hello_world TO service_role;
Resources#
- Official Client libraries: JavaScript and Flutter
- Community client libraries: github.com/supra-community
- PostgreSQL Official Docs: Chapter 9. Functions and Operators
- PostgreSQL Reference: CREATE FUNCTION