Skip to content
Database

数据库功能

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:

  1. 去 SQL 编辑器 部分。
  2. 点击 新建查询。
  3. 输入用于创建或替换数据库函数的 SQL。
  4. 点击 运行。你也可以按 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() -- 1
returns text -- 2
language sql -- 3
as $$ -- 4
select 'hello world'; -- 5
$$; --6
显示/隐藏详情

从最基本的角度来看,一个函数有以下几个部分:

🌐 At its most basic, a function has the following parts:

  1. create or replace function hello_world():函数声明,其中 hello_world 是函数的名称。使用 create 创建新函数,使用 replace 针对已存在的函数,或者当函数可能还不存在时使用 create or replace。
  2. returns text:函数返回的数据类型。对于不返回任何值的函数,写 returns void。
  3. language sql:函数体内部使用的语言。这也可以是过程型语言:plpgsql、plpython 等。
  4. as $$:函数封装器。$$ 符号里面的任何内容都是函数体的一部分。
  5. select 'hello world';:一个基本的函数体。函数返回它最后一个查询的结果,这个结果可以是带有 returning 子句的 insert、update 或 delete。
  6. $$;:函数封装器的结束符号。

创建函数后,你可以在数据库里用 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 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:

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 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;
$$;

你可以在数据库中用 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 text
language plpgsql
security 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.

功能权限 #

🌐 Function privileges

默认情况下,任何角色都可以运行数据库函数。你可以通过两种方式限制执行:

🌐 By default, any role can run a database function. You can restrict execution in two ways:

  1. 根据具体情况撤销。撤销你想要保护的函数的执行权限,既包括 public,也包括你要限制的角色:

    revoke execute on function public.hello_world from public;
    revoke execute on function public.hello_world from anon;
  2. 默认限制执行,然后再给需要每个功能的角色授权。

    要限制所有存在的功能,从 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

在仪表板中创建一个数据库功能 #

🌐 Create a database function in the Dashboard

从 JavaScript 调用数据库函数 #

🌐 Call a database function from JavaScript

从数据库函数调用外部 API #

🌐 Call an external API from a database function