确保只有一个列为空且只能由某些用户修改

时间:2013-04-10 07:28:49

标签: postgresql database-design triggers privileges postgresql-9.2

我在表中有column A(类型 int ),其数据在其他值的插入时间不可用。我不想分开桌子,因为没有其他真正的理由这样做。我正在数据库级别实现权限分离。并且只有某些用户(属于A类)应该能够随时修改column A。但是其他用户(在类别B中,mot必须与A类互斥)应该能够更新列,如果其值尚未设置,即。如果是NULL
我正在使用PostgreSQL 9.2.4。

我怎样才能做到这一点?触发器?规则是什么?还有别的吗?

4 个答案:

答案 0 :(得分:3)

更新触发器应该有效。如果不是NULL,则需要检查旧值并中止更新。

答案 1 :(得分:2)

示例:

-- setup
CREATE TABLE tbl (tbl_id serial PRIMARY KEY, value int);

-- trigger function that always fails
CREATE FUNCTION always_fail() RETURNS trigger as $$ begin raise exception 'value is not null'; end; $$ LANGUAGE plpgsql;
-- create the trigger
CREATE TRIGGER tbl_value_trigger BEFORE UPDATE ON tbl FOR EACH ROW WHEN (old.value is not null) EXECUTE PROCEDURE always_fail();

-- test it
INSERT INTO tbl(value) VALUES (NULL);
INSERT INTO tbl(value) VALUES (NULL);
UPDATE tbl SET value=2 WHERE tbl_id=1;
UPDATE tbl SET value=3 WHERE tbl_id=1; -- fails
UPDATE tbl SET value=2 WHERE tbl_id=2;
UPDATE tbl SET value=3 WHERE tbl_id=2; -- fails

-- cleanup
DROP TABLE tbl; 
DROP FUNCTION always_fail();

注意:

  • 我必须创建一个触发器函数(CREATE FUNCTION),因为CREATE TRIGGER需要一个触发器函数并且不接受正常函数,因此无法使用CREATE TRIGGER ... EXECUTE PROCEDURE raise exception 'value not null'
  • 在每次更新之前评估触发器,即
    • 未检查INSERTS,因此您可以插入NULL
    • 检查在更新之前发生,因此可以中止
  • 我必须使用FOR EACH ROW,否则我将无法查看OLD.value

答案 2 :(得分:1)

实现 实际要求的

  

只有某些用户才能修改A列   ...
  其他用户应该能够更新列,如果......它是NULL。

从公众和其他应有此特权的人撤销UPDATE(以及DELETE?!?)。

REVOKE UPDATE ON TABLE tbl FROM public;
REVOKE UPDATE ON TABLE tbl FROM ...

创建一个(组)角色some_users,允许更新col_a(以及其他任何内容):

CREATE ROLE some_users;
GRANT SELECT, UPDATE (col_a) ON TABLE tbl TO some_users;

为什么SELECT?请考虑the manual ON GRANT中的这句话:

  

实际上,任何重要的UPDATE命令都需要SELECT   特权,因为它必须引用表列来确定   要更新哪些行,和/或计算列的新值。

通过这种方式,您可以使用单点来执行列的权限 创建另一个(群组)角色certain_users,可以执行some_users可以执行的所有操作(以及更多内容):

CREATE ROLE certain_users;
GRANT some_users TO certain_users;

根据需要将这些角色的成员资格授予用户角色:

GRANT some_users TO lowly_user;
GRANT certain_users TO chief_user;

创建条件触发器,类似于@Daniel provided,但使用pg_has_role()的另一个条件:

CREATE TRIGGER tbl_value_trigger
BEFORE UPDATE ON tbl
FOR EACH ROW
WHEN (OLD.col_a IS NOT NULL AND NOT pg_has_role('some_users', 'member'))
EXECUTE PROCEDURE always_fail();

使用触发功能:

CREATE FUNCTION always_fail()
  RETURNS trigger AS
$func$
BEGIN

-- Fail with an exception, rolling back the whole transaction.
-- RAISE EXCEPTION 'value is not null';

-- Do nothing instead, letting the rest of the transaction commit.
-- Requires BEFORE trigger.
RAISE WARNING 'col_a IS NOT NULL. User % is too ambitious!', current_user;
RETURN NULL;  -- effectively aborts UPDATE

END
$func$ LANGUAGE plpgsql;

现在:

  • 公众无法完全更新表格。
  • some_userscertain_users可以更新列col_a(没有其他内容),
  • certain_users的更改通过col_a is NOT NULL

注意 超级用户自动成为任何群组的成员。因此,NOT pg_has_role(...) 形式启用超级用户,而反向逻辑可能会禁止超级用户更新列。

答案 3 :(得分:0)

要向某些用户授予无条件更新权限,请使其成为具有该权限的角色的成员。

create role the_role;
grant update on table t to the_role;
grant the_role to the_user;

现在,此更新查询仅在当前用户是the_role的成员或值为null时才有效:

update t
set A = 1
where
    id = 1
    and (
        A is null
        or
        pg_has_role('the_role', 'usage')
    )

Access privileges functions

Database Roles