PostgreSQL - ON CONFLICT UPDATE with view with columns子集

时间:2017-02-24 05:09:12

标签: postgresql

目前正在运行9.5.3版。当然是更新计划。

我有一个PostgreSQL数据库,其架构早于表行级安全性(即CREATE POLICY ...)。使用视图实现行级安全性。通过仅选择名称与CURRENT_USER匹配的行来在视图中完成安全性。

我试图使用这样的视图构建一个upsert查询。当我尝试命名conflict_target时出现问题。

使用ON CONFLICT UPDATE ...的问题来自于命名违反了哪些约束。

玩具示例

CREATE TABLE foo (id serial, num int, word text, data text, ownername varchar(64));

对于每个用户,wordnum的组合必须是唯一的。

CREATE UNIQUE INDEX foo_num_word_owner_idx ON foo (num, word, ownername);

使用基于当前用户名的视图实现行级安全性。为视图授予权限,为普通用户删除基础表。在v 9.5之后添加了security_barrier。请注意,用户看不到ownername

CREATE VIEW foo_user WITH (security_barrier = True) AS
    SELECT id, num, word, data FROM foo 
    WHERE foo.ownername = CURRENT_USER;    

现在自动设置所有者名称:

CREATE OR REPLACE FUNCTION trf_set_owner() RETURNS trigger AS
$$
BEGIN
    IF (TG_OP = 'INSERT') THEN
    NEW.ownername = CURRENT_USER::varchar(64);   
    END IF;
    IF (TG_OP = 'UPDATE') THEN
        NEW.ownername = CURRENT_USER::varchar(64);
    END IF;
    RETURN NEW;
END;
$$
LANGUAGE 'plpgsql';

CREATE TRIGGER foo_row_owner
    BEFORE INSERT OR UPDATE ON foo FOR EACH ROW
     EXECUTE PROCEDURE trf_set_owner();

请注意,ownername列不会显示在视图中;行安全性对用户是不可见的。

现在添加一些数据:

INSERT INTO foo_user (num, word, data) VALUES (1, 'asdf', 'cat'), (2, 'qwer', 'dog');


SELECT * FROM foo;
-- normally, this would give an error related to privileges,
-- because we don't allow users to query the underlying table.
-- bypassed here for demo purposes.

 id | num | word | data | ownername
----+-----+------+------+-----------
  1 |   1 | asdf | cat  | admin
  2 |   2 | qwer | dog  | admin
(2 rows)


SELECT * FROM foo_user;

 id | num | word | data
----+-----+------+------
  1 |   1 | asdf | cat
  2 |   2 | qwer | dog
(2 rows)

到目前为止,非常好。

我尝试过什么

如上所述,对于每个用户,numword必须是唯一的。不同的所有者拥有相同的numword(事实上,我们期望它)没有问题。

我试图利用ON CONFLICT中的INSERT子句创建一些后端UPSERT-ish功能。而且它正在倒塌。

错误的简单示例

首先,一个简单的失败插入:

INSERT INTO foo_user (num, word, data) VALUES (2, 'qwer', 'frog');
ERROR:  duplicate key value violates unique constraint "foo_num_word_owner_idx"
DETAIL:  Key (num, word, ownername)=(2, qwer, admin) already exists.

完全可以预料到。没有错。

ON CONFLICT,首次尝试

现在我们尝试让客户体验更顺畅:

INSERT INTO foo_user (num, word, data) VALUES (2, 'qwer', 'frog')
    ON CONFLICT DO UPDATE 
    SET data = 'frog'
    WHERE num = 2 AND word = 'qwer';

ERROR:  ON CONFLICT DO UPDATE requires inference specification or constraint name
LINE 2:     ON CONFLICT DO UPDATE
            ^
HINT:  For example, ON CONFLICT (column_name).

是的,就像文件说的那样。它需要知道它破坏了什么规则。没问题:

ON CONFLICT,第二次尝试

INSERT INTO foo_user (num, word, data) VALUES (2, 'qwer', 'frog')
    ON CONFLICT (num, word, ownername) DO UPDATE 
    SET data = 'frog'
    WHERE num = 2 AND word = 'qwer';

ERROR:  column "ownername" does not exist
LINE 2:     ON CONFLICT (num, word, ownername) DO UPDATE

真。视图中不存在所有者名称。我们无法从唯一索引中删除ownername,因为我们完全希望不同的所有者拥有相同的numword值。

ON CONFLICT,第三次尝试

所以我尝试将索引转换为约束,并命名约束:

ALTER TABLE foo 
    ADD CONSTRAINT foo_num_word_owner_crt UNIQUE 
    USING INDEX foo_num_word_owner_idx;

NOTICE:  ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index 
"foo_num_word_owner_idx" to "foo_num_word_owner_crt"

好的,现在来测试一下:

INSERT INTO foo_user (num, word, data) VALUES (2, 'qwer', 'frog')
    ON CONFLICT ON CONSTRAINT foo_num_word_owner_crt DO UPDATE 
    SET data = 'frog'
    WHERE num = 2 AND word = 'qwer';

ERROR:  constraint "foo_num_word_owner_crt" for table "foo_user" does not exist

这是有道理的:我们在查看视图但指定表约束。

结论

现在我没有想法。我们如何让ON CONFLICT与这样的观点一起玩得很好?或者是不可能的?

这个关闭(举起拇指和食指)建议我们从视图切换到具有行级安全性的表格,但这相当多的工作(不一定是API破坏者,但仍然。)

非常感谢任何见解。

0 个答案:

没有答案