feat: isolation level for roles/functions
This commit is contained in:
committed by
Steve Chavez
parent
4c555cbd5d
commit
aaf77902f6
+35
-1
@@ -8,7 +8,13 @@ ALTER ROLE :USER SET pgrst.db_anon_role = 'postgrest_test_anonymous';
|
||||
|
||||
CREATE ROLE postgrest_test_author;
|
||||
|
||||
GRANT postgrest_test_anonymous, postgrest_test_author TO :USER;
|
||||
CREATE ROLE postgrest_test_serializable;
|
||||
alter role postgrest_test_serializable set default_transaction_isolation = 'serializable';
|
||||
|
||||
CREATE ROLE postgrest_test_repeatable_read;
|
||||
alter role postgrest_test_repeatable_read set default_transaction_isolation = 'REPEATABLE READ';
|
||||
|
||||
GRANT postgrest_test_anonymous, postgrest_test_author, postgrest_test_serializable, postgrest_test_repeatable_read TO :USER;
|
||||
|
||||
CREATE SCHEMA v1;
|
||||
GRANT USAGE ON SCHEMA v1 TO postgrest_test_anonymous;
|
||||
@@ -108,3 +114,31 @@ begin
|
||||
alter role current_user set statement_timeout = %L;
|
||||
$$, timeout);
|
||||
end $_$ volatile language plpgsql ;
|
||||
|
||||
create table items as select x as id from generate_series(1,5) x;
|
||||
|
||||
create view items_w_isolation_level as
|
||||
select
|
||||
id,
|
||||
current_setting('transaction_isolation', true) as isolation_level
|
||||
from items;
|
||||
|
||||
grant all on items_w_isolation_level to postgrest_test_anonymous, postgrest_test_repeatable_read, postgrest_test_serializable;
|
||||
|
||||
create function default_isolation_level()
|
||||
returns text as $$
|
||||
select current_setting('transaction_isolation', true);
|
||||
$$
|
||||
language sql;
|
||||
|
||||
create function serializable_isolation_level()
|
||||
returns text as $$
|
||||
select current_setting('transaction_isolation', true);
|
||||
$$
|
||||
language sql set default_transaction_isolation = 'serializable';
|
||||
|
||||
create function repeatable_read_isolation_level()
|
||||
returns text as $$
|
||||
select current_setting('transaction_isolation', true);
|
||||
$$
|
||||
language sql set default_transaction_isolation = 'REPEATABLE READ';
|
||||
|
||||
@@ -873,6 +873,79 @@ def test_role_settings(defaultenv):
|
||||
assert response.text == '"10s"'
|
||||
|
||||
|
||||
def test_isolation_level(defaultenv):
|
||||
"isolation_level should be set per role and per function"
|
||||
|
||||
env = {
|
||||
**defaultenv,
|
||||
"PGRST_JWT_SECRET": SECRET,
|
||||
}
|
||||
|
||||
with run(env=env) as postgrest:
|
||||
# default isolation level for postgrest_test_anonymous
|
||||
response = postgrest.session.get(
|
||||
"/items_w_isolation_level?select=isolation_level&limit=1"
|
||||
)
|
||||
assert response.text == '[{"isolation_level":"read committed"}]'
|
||||
|
||||
# isolation level for postgrest_test_repeatable_read on GET
|
||||
headers = jwtauthheader({"role": "postgrest_test_repeatable_read"}, SECRET)
|
||||
response = postgrest.session.get(
|
||||
"/items_w_isolation_level?select=isolation_level&limit=1", headers=headers
|
||||
)
|
||||
assert response.text == '[{"isolation_level":"repeatable read"}]'
|
||||
|
||||
# isolation level for postgrest_test_serializable on POST
|
||||
headers = jwtauthheader({"role": "postgrest_test_serializable"}, SECRET)
|
||||
headers["Prefer"] = "return=representation"
|
||||
response = postgrest.session.post(
|
||||
"/items_w_isolation_level?select=isolation_level",
|
||||
json={"id": "666"},
|
||||
headers=headers,
|
||||
)
|
||||
assert response.text == '[{"isolation_level":"serializable"}]'
|
||||
|
||||
# isolation level for postgrest_test_serializable on PATCH
|
||||
headers = jwtauthheader({"role": "postgrest_test_serializable"}, SECRET)
|
||||
headers["Prefer"] = "return=representation"
|
||||
response = postgrest.session.patch(
|
||||
"/items_w_isolation_level?select=isolation_level&id=eq.666",
|
||||
json={"id": "666"},
|
||||
headers=headers,
|
||||
)
|
||||
assert response.text == '[{"isolation_level":"serializable"}]'
|
||||
|
||||
# isolation level for postgrest_test_serializable on DELETE
|
||||
headers = jwtauthheader({"role": "postgrest_test_serializable"}, SECRET)
|
||||
headers["Prefer"] = "return=representation"
|
||||
response = postgrest.session.delete(
|
||||
"/items_w_isolation_level?select=isolation_level&id=eq.666", headers=headers
|
||||
)
|
||||
assert response.text == '[{"isolation_level":"serializable"}]'
|
||||
|
||||
# default isolation level for function
|
||||
response = postgrest.session.get("/rpc/default_isolation_level")
|
||||
assert response.text == '"read committed"'
|
||||
|
||||
# changes with role isolation level
|
||||
headers = jwtauthheader({"role": "postgrest_test_repeatable_read"}, SECRET)
|
||||
response = postgrest.session.get(
|
||||
"/rpc/default_isolation_level", headers=headers
|
||||
)
|
||||
assert response.text == '"repeatable read"'
|
||||
|
||||
# isolation level can be set per function
|
||||
response = postgrest.session.get("/rpc/serializable_isolation_level")
|
||||
assert response.text == '"serializable"'
|
||||
response = postgrest.session.get("/rpc/repeatable_read_isolation_level")
|
||||
assert response.text == '"repeatable read"'
|
||||
|
||||
# isolation level for a function overrides the role isolation level
|
||||
headers = jwtauthheader({"role": "postgrest_test_repeatable_read"}, SECRET)
|
||||
response = postgrest.session.get("/rpc/serializable_isolation_level")
|
||||
assert response.text == '"serializable"'
|
||||
|
||||
|
||||
# TODO: This test fails now because of https://github.com/PostgREST/postgrest/pull/2122
|
||||
# The stack size of 1K(-with-rtsopts=-K1K) is not enough and this fails with "stack overflow"
|
||||
# A stack size of 200K seems to be enough for succeess
|
||||
|
||||
Reference in New Issue
Block a user