事务
在 用户模拟 之后,对 API 资源 的每个请求都在事务中运行。事务的顺序如下
START TRANSACTION; -- <Access Mode> <Isolation Level>
-- <Transaction-scoped settings>
-- <Main Query>
END; -- <Transaction End>
访问模式
访问模式决定事务是否可以修改数据库。有两种可能的值:只读和读写。
在只读事务中修改数据库是不可能的。PostgREST 利用这一点来在 GET 和 HEAD 请求中强制执行 HTTP 语义。考虑以下情况
CREATE SEQUENCE callcounter_count START 1;
CREATE VIEW callcounter AS
SELECT nextval('callcounter_count');
由于 callcounter
视图修改了序列,因此使用 GET 或 HEAD 调用它会导致错误
curl "http://localhost:3000/callcounter"
HTTP/1.1 405 Method Not Allowed
{"code":"25006","details":null,"hint":null,"message":"cannot execute nextval() in a read-only transaction"}
表和视图上的访问模式
对表和视图的访问模式由 HTTP 方法决定。
HTTP 方法 |
访问模式 |
---|---|
GET, HEAD |
只读 |
POST, PATCH, PUT, DELETE |
读写 |
函数的访问模式
访问模式 |
|||
---|---|---|---|
HTTP 方法 |
易变 |
稳定 |
不可变 |
GET, HEAD |
只读 |
只读 |
只读 |
POST |
读写 |
只读 |
只读 |
注意
易变性标记是对函数行为的承诺。PostgreSQL 允许您将修改数据库的函数标记为
IMMUTABLE
或STABLE
,而不会出现错误。但是,由于函数处于只读事务中,因此它将在 PostgREST 下失败。OPTIONS 方法 不会启动事务,因此这里不相关。
隔离级别
每个事务都使用 PostgreSQL 的默认隔离级别:READ COMMITTED。除非您修改了模拟角色或函数的default_transaction_isolation。
ALTER ROLE webuser SET default_transaction_isolation TO 'repeatable read';
每个 webuser
的查询都将使用设置为 REPEATABLE READ 的 default_transaction_isolation
执行。
或者更改每个函数调用的隔离级别。
CREATE OR REPLACE FUNCTION myfunc()
RETURNS text as $$
SELECT 'hello';
$$
LANGUAGE SQL
SET default_transaction_isolation TO 'serializable';
事务范围设置
PostgREST 使用与事务生命周期绑定的设置。这些设置可用于获取有关 HTTP 请求的信息。或者修改 HTTP 响应。
您可以使用 current_setting
获取这些设置。
-- request settings use the ``request.`` prefix.
SELECT
current_setting('request.<setting>', true);
您可以使用 set_config
设置这些设置。
-- response settings use the ``response.`` prefix.
SELECT
set_config('response.<setting>', 'value1' ,true);
请求路径和方法
路径和方法存储为 text
。
SELECT current_setting('request.path', true);
SELECT current_setting('request.method', true);
请求角色和搜索路径
由于 用户模拟,PostgREST 设置了标准的 role
。您可以通过不同的方式获取它
SELECT current_role;
SELECT current_user;
SELECT current_setting('role', true);
此外,它还根据 db-schemas 和 db-extra-search-path 设置 search_path
。
响应头
您可以设置 response.headers
以将头添加到 HTTP 响应中。例如,此语句将向响应添加缓存头
-- tell client to cache response for two days
SELECT set_config('response.headers',
'[{"Cache-Control": "public"}, {"Cache-Control": "max-age=259200"}]', true);
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Cache-Control: no-cache, no-store, must-revalidate
请注意,response.headers
应该设置为一个包含单个键对象的数组,而不是单个包含多个键的对象。这是因为诸如 Cache-Control
或 Set-Cookie
之类的头在设置多个值时需要重复。对象不允许重复键。
注意
PostgREST 提供的头,例如 Content-Type
、Location
等,可以通过这种方式覆盖。请注意,无论覆盖的 Content-Type
响应头如何,内容仍将转换为 JSON,除非您使用 媒体类型处理程序。
响应状态码
您可以设置 response.status
来覆盖 PostgREST 提供的默认状态码。例如,以下函数将替换默认的 200
状态码。
create or replace function teapot() returns json as $$
begin
perform set_config('response.status', '418', true);
return json_build_object('message', 'The requested entity body is short and stout.',
'hint', 'Tip it over and pour it out.');
end;
$$ language plpgsql;
curl "http://localhost:3000/rpc/teapot" -i
HTTP/1.1 418 I'm a teapot
{
"message" : "The requested entity body is short and stout.",
"hint" : "Tip it over and pour it out."
}
如果状态码是标准的,PostgREST 将完成状态消息(本例中为 **I’m a teapot**)。
模拟角色设置
PostgreSQL 应用连接角色(身份验证器)设置。此外,PostgREST 将应用 模拟角色 设置作为事务范围的设置。这允许更细粒度的控制角色执行的操作。
例如,考虑 statement_timeout。它允许您中止任何超过指定时间执行的语句。默认情况下它被禁用。
ALTER ROLE authenticator SET statement_timeout TO '10s';
ALTER ROLE anonymous SET statement_timeout TO '1s';
使用上述设置,所有用户都将获得 10 秒的全局语句超时,而 匿名 用户将获得 1 秒的超时。
具有特权上下文的设置
默认情况下不会应用具有需要特权的上下文的设置。这是为了避免出现权限错误。有关更多详细信息,请参阅 理解 Postgres 参数上下文。
但是,从 PostgreSQL 15 开始,您可以使用以下方法授予这些设置的特权:
GRANT SET ON PARAMETER <setting> TO <authenticator>;
函数设置
除了 模拟角色设置 之外,PostgREST 还将应用函数设置作为事务范围的设置。这允许函数设置覆盖模拟和连接角色设置。
CREATE OR REPLACE FUNCTION myfunc()
RETURNS void as $$
SELECT pg_sleep(3); -- simulating some long-running process
$$
LANGUAGE SQL
SET statement_timeout TO '4s';
调用上述函数时(请参阅 函数作为 RPC),语句超时将为 4 秒。
注意
只有由配置 db-hoisted-tx-settings 提升的事务才会应用。
主查询
主查询是通过请求 表和视图 或 函数作为 RPC 生成的。所有生成的查询都使用预备语句(db-prepared-statements)。
事务结束
如果事务没有失败,它将始终以 COMMIT 结束。除非 db-tx-end 配置为在任何情况下都回滚,或者使用 事务结束偏好 有条件地回滚。这对于测试目的很有用。
中止事务
任何数据库故障(例如约束失败)都会导致事务回滚。您也可以在函数内部引发错误以导致回滚。
预请求
预请求是一个函数,它可以在设置事务范围设置后,并在主查询之前运行。它通过db-pre-request启用。
这提供了一个修改设置或引发异常以阻止请求完成的机会。
通过预请求设置标头
例如,让我们为来自 Internet Explorer(6 或 7)浏览器的所有请求添加一些缓存标头。
create or replace function custom_headers()
returns void as $$
declare
user_agent text := current_setting('request.headers', true)::json->>'user-agent';
begin
if user_agent similar to '%MSIE (6.0|7.0)%' then
perform set_config('response.headers',
'[{"Cache-Control": "no-cache, no-store, must-revalidate"}]', false);
end if;
end; $$ language plpgsql;
-- set this function on postgrest.conf
-- db-pre-request = custom_headers
现在,当您对表或视图发出 GET 请求时,您将获得缓存标头。
curl "http://localhost:3000/people" -i \
-H "User-Agent: Mozilla/4.01 (compatible; MSIE 6.0; Windows NT 5.1)"