数据库功能
Postgres 内置支持 SQL 函数。这些函数存在于你的数据库中,你可以通过 rpc() 在你的应用中调用它们。
🌐 Postgres has built-in support for SQL functions.
These functions live inside your database, and you can call them from your app with rpc().
- 数据库函数与边缘函数 对两者进行了比较。如果你不确定哪个更适合,从这里开始吧。
- 创建一个数据库函数 有几个步骤,从一个单行函数到一个可以接受参数的函数。
- 安全数据库函数 涉及该函数以哪个用户身份运行以及谁可以调用它。
- 调试数据库功能 包括日志记录和错误处理。
快速演示 #
🌐 Quick demo
数据库函数 vs 边缘函数 #
🌐 Database functions vs Edge Functions
对于数据密集型操作,使用数据库函数。它们在你的数据库内部运行,你可以通过 REST 和 GraphQL API 远程调用它们。
🌐 For data-intensive operations, use database functions. They run inside your database, and you can call them remotely with the REST and GraphQL API.
对于需要低延迟的使用场景,请使用 Edge Functions。它们是全球分布的,而且你可以用 TypeScript 编写。
🌐 For use cases that need low latency, use Edge Functions. They're globally distributed and you write them in TypeScript.
创建一个数据库函数 #
🌐 Create a database function
入门 #
🌐 Getting started
可以从仪表板创建一个数据库函数,或者自己用 SQL 针对直接连接编写。
🌐 Create a database function from the Dashboard, or write the SQL yourself against a direct connection.
使用仪表板的方法:
🌐 To use the Dashboard:
- 去 SQL 编辑器 部分。
- 点击 新建查询。
- 输入用于创建或替换数据库函数的 SQL。
- 点击 运行。你也可以按
cmd+enter或ctrl+enter。
基本功能 #
🌐 Basic functions [#simple-functions]
创建一个基本的数据库函数,返回字符串“hello world”。
🌐 Create a basic database function that returns the string "hello world".
create or replace function hello_world() -- 1returns text -- 2language sql -- 3as $$ -- 4 select 'hello world'; -- 5$$; --6显示/隐藏详情
从最基本的角度来看,一个函数有以下几个部分:
🌐 At its most basic, a function has the following parts:
create or replace function hello_world():函数声明,其中hello_world是函数的名称。使用create创建新函数,使用replace针对已存在的函数,或者当函数可能还不存在时使用create or replace。returns text:函数返回的数据类型。对于不返回任何值的函数,写returns void。language sql:函数体内部使用的语言。这也可以是过程型语言:plpgsql、plpython等。as $$:函数封装器。$$符号里面的任何内容都是函数体的一部分。select 'hello world';:一个基本的函数体。函数返回它最后一个查询的结果,这个结果可以是带有returning子句的insert、update或delete。$$;:函数封装器的结束符号。
不支持重载函数。每个函数都要起一个独一无二的名字。
🌐 Overloaded functions aren't supported. Give every function a unique name.
创建函数后,你可以在数据库里用 SQL 运行它,或者用其中一个客户端库来运行。
🌐 After you create the function, you can run it inside the database with SQL, or with one of the client libraries.
select hello_world();返回数据集 #
🌐 Returning data sets
数据库函数也可以从表或视图返回数据集。
🌐 A database function can also return a data set from a table or a view.
例如,创建两个表格来存放一些星球大战的数据。数据标签显示它们最终的行。
🌐 For example, create two tables holding some Star Wars data. The Data tab shows the rows they end up with.
create table planets ( id serial primary key, name text);insert into planets (name)values ('Tatooine'), ('Alderaan'), ('Kashyyyk');create table people ( id serial primary key, name text, planet_id bigint references planets);insert into people (name, planet_id)values ('Anakin Skywalker', 1), ('Luke Skywalker', 1), ('Princess Leia', 2), ('Chewbacca', 3);以下函数返回所有行星:
🌐 The following function returns all the planets:
create or replace function get_planets()returns setof planetslanguage sqlas $$ select * from planets;$$;因为这个函数返回一个表集,你可以对它应用筛选器和选择器。要只获取第一个行星:
🌐 Because this function returns a table set, you can apply filters and selectors to it. To get the first planet only:
select *from get_planets()where id = 1;传递参数 #
🌐 Passing parameters
创建一个函数,将新行星插入到 planets 表中,并返回新 ID。这个函数使用 plpgsql 语言。
🌐 Create a function that inserts a new planet into the planets table and returns the new ID. This function uses the plpgsql language.
create or replace function add_planet(name text)returns bigintlanguage plpgsqlas $$declare new_row bigint;begin insert into planets(name) values (add_planet.name) returning id into new_row; return new_row;end;$$;你可以在数据库中用 select 查询运行这个函数,或者用客户端库来运行:
🌐 You can run this function inside your database with a select query, or with the client libraries:
select * from add_planet('Jakku');保护一个数据库功能 #
🌐 Secure a database function
证券 definer 对比 invoker#
🌐 Security definer vs invoker
Postgres 运行一个函数时,要么以调用它的用户 (invoker) 的身份运行,要么以其创建者 (definer) 的身份运行。例如:
🌐 Postgres runs a function either as the user calling it (invoker) or as its creator (definer). For example:
create or replace function hello_world()returns textlanguage plpgsqlsecurity definer set search_path = ''as $$begin return 'hello world';end;$$;推荐使用 security invoker,它也是默认设置。使用 security definer 时,你必须设置 search_path。
🌐 Prefer security invoker, which is also the default. When you use security definer, you must set the search_path.
在空的搜索路径下,search_path = '',为函数体中的每个关系命名模式,例如 from public.table。空的搜索路径可以在函数可以访问你不希望调用用户访问的模式时限制损害。
🌐 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.
security definer 函数可以返回调用者不允许读取的行。它以创建者的权限运行。在仪表板中创建或通过迁移创建的函数由 postgres 拥有,这会绕过行级安全性。默认情况下,任何角色都可以调用它,包括 anon,即处理没有用户会话请求的角色。
🌐 A security definer function can return rows the caller isn't allowed to read. It runs with its creator's privileges. A function created in the Dashboard or by a migration is owned by postgres, which bypasses Row Level Security. Any role can call it by default, including anon, the role that serves requests carrying no user session.
固定 search_path 并不会改变谁可以调用该函数。当一个 security definer 函数读取每个用户的数据时:
🌐 Pinning the search_path doesn't change who can call the function. When a security definer function reads per-user data:
- 在函数体中检查所有权。 执行权限控制哪些角色可以调用该函数。它不会限制被允许的角色返回的行,所以即使授予给
authenticated,每个已登录的调用者仍然可以看到任意用户的行。 - 也缩小执行权限。 从
public以及每个直接拥有授权的角色(如anon)收回权限。然后再授予需要它的角色。请参阅本页的 函数权限 部分。
功能权限 #
🌐 Function privileges
默认情况下,任何角色都可以运行数据库函数。你可以通过两种方式限制执行:
🌐 By default, any role can run a database function. You can restrict execution in two ways:
-
根据具体情况撤销。撤销你想要保护的函数的执行权限,既包括
public,也包括你要限制的角色:revoke execute on function public.hello_world from public;revoke execute on function public.hello_world from anon; -
默认限制执行,然后再给需要每个功能的角色授权。
要限制所有存在的功能,从
public和你想限制的角色中撤销执行权限:revoke execute on all functions in schema public from public;revoke execute on all functions in schema public from anon, authenticated;要限制之后创建的每个函数,请更改
public和你想要限制的角色的默认权限: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:
grant execute on function public.hello_world to authenticated;资源 #
🌐 Resources
- 官方客户端库:JavaScript 和 Flutter
- 社区客户端库:github.com/supabase-community
- Postgres 官方文档:第9章 函数和操作符
- Postgres 参考:CREATE FUNCTION
在仪表板中创建一个数据库功能 #
🌐 Create a database function in the Dashboard
从 JavaScript 调用数据库函数 #
🌐 Call a database function from JavaScript
从数据库函数调用外部 API #
🌐 Call an external API from a database function