我在表中有一个列A
(类型int
),在插入其他值时,该列的数据不可用。我不想拆分表格,因为没有其他真正的理由这样做。我正在数据库级别实现权限分离。
只有某些用户(属于 A 类)才能随时修改column A
。但是其他用户(在 B 类中,不一定与 A 类互斥)应该能够更新列,如果它的值尚未设置,即。如果是NULL
。
我正在使用 PostgreSQL 9.2.4。
我怎样才能做到这一点?触发器?规则?还有什么?
我在表中有一个列A
(类型int
),在插入其他值时,该列的数据不可用。我不想拆分表格,因为没有其他真正的理由这样做。我正在数据库级别实现权限分离。
只有某些用户(属于 A 类)才能随时修改column A
。但是其他用户(在 B 类中,不一定与 A 类互斥)应该能够更新列,如果它的值尚未设置,即。如果是NULL
。
我正在使用 PostgreSQL 9.2.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(...)
启用超级用户,而反向逻辑可能会禁止超级用户更新列。
更新触发器应该可以工作。它需要检查旧值,如果不是,则中止更新NULL
。
例子:
-- 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'
无法使用FOR EACH ROW
,否则我将无法检查OLD.value