触发器:允许更新 "no column except foo"
Trigger: allow updates to "no column except foo"
假设我们有一个 table 比如:
Table "someschema.todo"
Column | Type | Nullable | Default
--------------+--------------+----------+----------------------------------
id | integer | not null | nextval('todo_id_seq'::regclass)
contract_id | integer | not null |
title | text | |
description | text | |
status | todo_status | | 'INCOMPLETE'::todo_status
Foreign-key constraints:
"todo_contract_id_fkey" FOREIGN KEY (contract_id) REFERENCES contract(id)
行级别安全性已启用,我们假设已正确设置规则,但有一个例外:通常不允许 UPDATE
行的用户类型 table 需要能够将 status
列从一个 enum
值更改为另一个值。比如说,从 INCOMPLETE
到 PENDING
。该用户类型还需要能够在其他时间 UPDATE
table(取决于与 contract_id
fkey 相关的条件),因此我们不能只使用一揽子列授权。
可以说,这可能会使该列成为新 todo_status
table 的候选列,但我们暂时排除这种可能性。现在我们可以编写一个触发器来按名称检查每一列以查看它是否已被修改,并且只允许那些修改 status
而没有其他任何内容的查询......但这看起来很脆弱(如果我们稍后添加另一列怎么办? ) 和痛苦。
触发器中是否有允许修改 "no column except status
" 的方法?换句话说,"deny access unless the only column modified is status
".
补充:有没有我没有考虑过的使用 CREATE POLICY
内的 check_expression
或 using_expression
来完成此操作的方法?我一直在假设,因为我们没有 using_expression
中的 NEW
值或 check_expression
中的 OLD
值,我无法使用 RLS 来实现我们的目标需要。
触发器会相对稳健
CREATE OR REPLACE FUNCTION validate_update()
RETURNS trigger AS
$BODY$
DECLARE
v_key TEXT;
v_value TEXT;
valid_update boolean := true;
BEGIN
FOR v_key, v_value IN select key, value from each(hstore(NEW)) each LOOP
if (coalesce(v_value,'') != coalesce((hstore(OLD) -> v_key),'')) then
if (v_key != 'status') then
valid_update := false;
end if;
end if;
END LOOP;
if (valid_update) then
raise info 'good update';
return NEW;
else
raise info 'bad update';
return null;
end if;
END;
$BODY$
LANGUAGE plpgsql;
create trigger validate_update before update on someschema.todo
for each row execute procedure validate_update();
假设我们有一个 table 比如:
Table "someschema.todo"
Column | Type | Nullable | Default
--------------+--------------+----------+----------------------------------
id | integer | not null | nextval('todo_id_seq'::regclass)
contract_id | integer | not null |
title | text | |
description | text | |
status | todo_status | | 'INCOMPLETE'::todo_status
Foreign-key constraints:
"todo_contract_id_fkey" FOREIGN KEY (contract_id) REFERENCES contract(id)
行级别安全性已启用,我们假设已正确设置规则,但有一个例外:通常不允许 UPDATE
行的用户类型 table 需要能够将 status
列从一个 enum
值更改为另一个值。比如说,从 INCOMPLETE
到 PENDING
。该用户类型还需要能够在其他时间 UPDATE
table(取决于与 contract_id
fkey 相关的条件),因此我们不能只使用一揽子列授权。
可以说,这可能会使该列成为新 todo_status
table 的候选列,但我们暂时排除这种可能性。现在我们可以编写一个触发器来按名称检查每一列以查看它是否已被修改,并且只允许那些修改 status
而没有其他任何内容的查询......但这看起来很脆弱(如果我们稍后添加另一列怎么办? ) 和痛苦。
触发器中是否有允许修改 "no column except status
" 的方法?换句话说,"deny access unless the only column modified is status
".
补充:有没有我没有考虑过的使用 CREATE POLICY
内的 check_expression
或 using_expression
来完成此操作的方法?我一直在假设,因为我们没有 using_expression
中的 NEW
值或 check_expression
中的 OLD
值,我无法使用 RLS 来实现我们的目标需要。
触发器会相对稳健
CREATE OR REPLACE FUNCTION validate_update()
RETURNS trigger AS
$BODY$
DECLARE
v_key TEXT;
v_value TEXT;
valid_update boolean := true;
BEGIN
FOR v_key, v_value IN select key, value from each(hstore(NEW)) each LOOP
if (coalesce(v_value,'') != coalesce((hstore(OLD) -> v_key),'')) then
if (v_key != 'status') then
valid_update := false;
end if;
end if;
END LOOP;
if (valid_update) then
raise info 'good update';
return NEW;
else
raise info 'bad update';
return null;
end if;
END;
$BODY$
LANGUAGE plpgsql;
create trigger validate_update before update on someschema.todo
for each row execute procedure validate_update();