触发器:允许更新 "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 值更改为另一个值。比如说,从 INCOMPLETEPENDING。该用户类型还需要能够在其他时间 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_expressionusing_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();