函数作为 RPC

“单个资源可以等同于数据库函数,并具有抽象任何数量的存储项状态更改的能力”Roy T. Fielding

函数可以执行 PostgreSQL 允许的任何操作(读取数据、修改数据、引发错误,甚至 DDL 操作)。公开模式 中的每个函数,以及由 活动数据库角色 访问的每个函数,都可以在 /rpc 前缀下执行。

如果函数返回表类型,则可以

注意

为什么使用 /rpc 前缀?PostgreSQL 允许表或视图与函数具有相同的名称。该前缀可以避免路由冲突。

警告

存储过程 不受支持。

使用 POST 调用

要在 API 调用中提供参数,请在请求有效负载中包含一个 JSON 对象。对象的每个键/值对将成为一个参数。

例如,假设我们在数据库中创建了以下函数。

CREATE FUNCTION add_them(a integer, b integer)
RETURNS integer AS $$
 SELECT a + b;
$$ LANGUAGE SQL IMMUTABLE;

重要

每当创建或更改函数时,您都必须刷新 PostgREST 的模式缓存。请参阅 模式缓存重新加载 部分。

客户端可以通过发布类似于以下的对象来调用它

curl "http://localhost:3000/rpc/add_them" \
  -X POST -H "Content-Type: application/json" \
  -d '{ "a": 1, "b": 2 }'
3

注意

PostgreSQL 会将标识符名称转换为小写,除非您像这样用引号引起来

CREATE FUNCTION "someFunc"("someParam" text) ...

使用 GET 调用

如果函数不修改数据库,它也会在 GET 方法下运行(请参阅 访问模式)。

curl "http://localhost:3000/rpc/add_them?a=1&b=2"

函数参数名称与 POST 情况下的 JSON 对象键匹配,对于 GET 情况,它们与查询参数 ?a=1&b=2 匹配。

具有单个未命名 JSON 参数的函数

如果希望 JSON 请求主体作为单个参数发送,则可以创建一个具有单个未命名 jsonjsonb 参数的函数。为此,必须在请求中包含 Content-Type: application/json 标头。

CREATE FUNCTION mult_them(json) RETURNS int AS $$
  SELECT ($1->>'x')::int * ($1->>'y')::int
$$ LANGUAGE SQL;
curl "http://localhost:3000/rpc/mult_them" \
  -X POST -H "Content-Type: application/json" \
  -d '{ "x": 4, "y": 2 }'
8

注意

如果重载函数具有单个 jsonjsonb 未命名参数,则 PostgREST 将调用此函数作为回退,前提是未找到具有 POST 请求中发送的参数的其他重载函数。

警告

将 JSON 请求主体作为单个参数发送也可以使用 Prefer: params=single-object,但此方法已 **弃用**。

具有单个未命名参数的函数

您可以向具有单个未命名参数的函数发出 POST 请求以发送原始 byteatextxml 数据。

要发送原始 XML,参数类型必须为 xml,并且请求中必须包含标头 Content-Type: text/xml

要发送原始二进制数据,参数类型必须为 bytea,并且请求中必须包含标头 Content-Type: application/octet-stream

CREATE TABLE files(blob bytea);

CREATE FUNCTION upload_binary(bytea) RETURNS void AS $$
  INSERT INTO files(blob) VALUES ($1);
$$ LANGUAGE SQL;
curl "http://localhost:3000/rpc/upload_binary" \
  -X POST -H "Content-Type: application/octet-stream" \
  --data-binary "@file_name.ext"
HTTP/1.1 200 OK

[ ... ]

要发送原始文本,参数类型必须为 text,并且请求中必须包含标头 Content-Type: text/plain

具有数组参数的函数

您可以调用接受数组参数的函数

create function plus_one(arr int[]) returns int[] as $$
   SELECT array_agg(n + 1) FROM unnest($1) AS n;
$$ language sql;
curl "http://localhost:3000/rpc/plus_one" \
  -X POST -H "Content-Type: application/json" \
  -d '{"arr": [1,2,3,4]}'
[2,3,4,5]

对于使用 GET 调用函数,您可以将数组作为 数组字面量 传递,例如 {1,2,3,4}。请注意,花括号必须进行 URL 编码({%7B,而 }%7D)。

curl "http://localhost:3000/rpc/plus_one?arr=%7B1,2,3,4%7D'"

注意

对于 PostgreSQL 10 之前的版本,要在 POST 负载中传递 PostgreSQL 本机数组,您需要对其进行引用并使用数组字面量

curl "http://localhost:3000/rpc/plus_one" \
  -X POST -H "Content-Type: application/json" \
  -d '{ "arr": "{1,2,3,4}" }'

在这些版本中,我们建议使用类型为 JSON 的函数参数来接受来自客户端的数组。

可变参数函数

您可以通过在 POST 请求中传递 JSON 数组来调用可变参数函数

create function plus_one(variadic v int[]) returns int[] as $$
   SELECT array_agg(n + 1) FROM unnest($1) AS n;
$$ language sql;
curl "http://localhost:3000/rpc/plus_one" \
  -X POST -H "Content-Type: application/json" \
  -d '{"v": [1,2,3,4]}'
[2,3,4,5]

在 GET 请求中,您可以重复相同的参数名称

curl "http://localhost:3000/rpc/plus_one?v=1&v=2&v=3&v=4"

重复操作也适用于使用 Content-Type: application/x-www-form-urlencoded 的 POST 请求。

curl "http://localhost:3000/rpc/plus_one" \
  -X POST -H "Content-Type: application/x-www-form-urlencoded" \
  -d 'v=1&v=2&v=3&v=4'

表值函数

返回表类型的函数可以使用与 表和视图 相同的过滤器进行过滤。它们也可以使用 资源嵌入

CREATE FUNCTION best_films_2017() RETURNS SETOF films ..
curl "http://localhost:3000/rpc/best_films_2017?select=title,director:directors(*)"
curl "http://localhost:3000/rpc/best_films_2017?rating=gt.8&order=title.desc"

函数内联

符合 内联规则 的函数也会内联 过滤器排序限制

例如,对于以下函数

create function getallprojects() returns setof projects
language sql stable
as $$
  select * from projects;
$$;

让我们在应用过滤器的情况下调用它并获取其 执行计划

curl "http://localhost:3000/rpc/getallprojects?id=eq.1" \
  -H "Accept: application/vnd.pgrst.plan"
Aggregate  (cost=8.18..8.20 rows=1 width=112)
  ->  Index Scan using projects_pkey on projects  (cost=0.15..8.17 rows=1 width=40)
        Index Cond: (id = 1)

注意计划中没有“函数扫描”节点,这表明它已被内联。

标量函数

PostgREST 会检测函数是标量函数还是表值函数,并相应地调整响应格式。

curl "http://localhost:3000/rpc/add_them?a=1&b=2"
3
curl "http://localhost:3000/rpc/best_films_2017"
[
  { "title": "Okja", "rating": 7.4},
  { "title": "Call me by your name", "rating": 8},
  { "title": "Blade Runner 2049", "rating": 8.1}
]

要手动选择返回格式(例如二进制),请参阅 媒体类型处理程序

无类型函数

支持返回 recordSETOF record 的函数。

create function projects_setof_record() returns setof record as $$
  select * from projects;
$$ language sql;
curl "http://localhost:3000/rpc/projects_setof_record"
[{"id":1,"name":"Windows 7","client_id":1},
 {"id":2,"name":"Windows 10","client_id":1},
 {"id":3,"name":"IOS","client_id":2}]

但是请注意,当尝试使用垂直过滤水平过滤时,它们会失败。

因此,虽然它们可以用于快速测试,但建议始终为函数选择严格的返回类型。

重载函数

您可以使用不同数量的参数调用重载函数。

CREATE FUNCTION rental_duration(customer_id integer) ..

CREATE FUNCTION rental_duration(customer_id integer, from_date date) ..
curl "http://localhost:3000/rpc/rental_duration?customer_id=232"
curl "http://localhost:3000/rpc/rental_duration?customer_id=232&from_date=2018-07-01"

重要

不支持具有相同参数名称但类型不同的重载函数。