I know whats the problem is (below) and have a workaround, but i would like to get rid of it.
I would like to implement soft delete for my_table.
The Problem is that if i run UPDATE "my_table" set delete_at = now() where id = ...
i get new row violates row-level security policy for table
.
The insert and delete (real delete) is working fine, so i skip this here.
-- create table
CREATE TABLE "my_table" (
"id" SERIAL PRIMARY KEY
"user_id" int not null references "user"(id) on delete cascade,
deleted_at timestamp with time zone
);
-- enable row level security
ALTER TABLE "my_table" ENABLE ROW LEVEL SECURITY;
-- allow read own data and everything for admin
CREATE POLICY my_table_read_policy ON "my_table" FOR SELECT
USING (
current_setting('role') = 'app_admin'
or
(
current_setting('role') = 'app_user'
and user_id = get_current_user_id()
and (
deleted_at is null
)
)
);
-- update, e.g. set delete_at date
CREATE POLICY my_table_update_policy ON "my_table" FOR UPDATE
USING (true)
WITH CHECK (
current_setting('role') = 'app_admin'
or
(
current_setting('role') = 'app_user'
and user_id = get_current_user_id()
)
);
I found that this happens because of my SELECT Policy which filters by deleted_at.
After changing the SELECT Policy to the following workaround (delete_at is null OR "within the last second")
, it works.
-- allow read own data and everything for admin
CREATE POLICY my_table_read_policy ON "my_table" FOR SELECT
USING (
current_setting('role') = 'app_admin'
or
(
current_setting('role') = 'app_user'
and user_id = get_current_user_id()
-- workaround: https://stackoverflow.com/a/74306008
and (
deleted_at is null OR ABS(EXTRACT(EPOCH FROM (now() - deleted_at))) < 1
)
)
);