2

我在表中有一个列A(类型int),在插入其他值时,该列的数据不可用。我不想拆分表格,因为没有其他真正的理由这样做。我正在数据库级别实现权限分离。

只有某些用户(属于 A 类)才能随时修改column A。但是其他用户(在 B 类中,不一定与 A 类互斥)应该能够更新列,如果它的值尚未设置,即。如果是NULL
我正在使用 PostgreSQL 9.2.4。

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

4

4 回答 4

4

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

撤销UPDATE(和DELETE?!?)public不应该拥有特权的其他所有人。

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手册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 提供的,但使用另一个条件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
  LANGUAGE plpgsql AS
$func$
BEGIN
   -- To fail with exception, rolling back the whole transaction
   -- use this instead:
   -- 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$;

现在:

  • 公众无法完全更新表格。
  • some_users并被certain_users允许更新列col_a(仅此而已),
  • 只改变从certain_users经过一次col_a is NOT NULL

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

于 2013-04-12T21:47:06.853 回答
4

更新触发器应该可以工作。它需要检查旧值,如果不是,则中止更新NULL

于 2013-04-10T07:31:37.103 回答
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
于 2013-04-10T08:55:30.520 回答
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')
    )

访问权限功能

数据库角色

于 2013-04-10T12:22:18.910 回答