约束定义DEFERRABLE INTITIALLY IMMEDIATE仍然是DEFERRED?

Erw*_*ter 14 postgresql constraints postgresql-9.1

关于这个答案,我偶然发现了一个我无法解释的现象.

版本:
x86_64-unknown-linux-gnu上的PostgreSQL 9.1.2,由gcc-4.4.real编译(Debian 4.4.5-8)4.4.5,64位

请考虑以下演示.测试平台:

CREATE TEMP TABLE t (
  id  integer
 ,txt text
 ,CONSTRAINT t_pkey PRIMARY KEY (id) DEFERRABLE INITIALLY IMMEDIATE
);

INSERT INTO t VALUES
 (1, 'one')
,(2, 'two');
Run Code Online (Sandbox Code Playgroud)

1)修改多行的UPDATE语句:

UPDATE t
SET    id = t_old.id
FROM   t t_old
WHERE (t.id, t_old.id) IN ((1,2), (2,1));
Run Code Online (Sandbox Code Playgroud)

目前的实施似乎有一个错误?上面的UPDATE虽然不应该有效.约束已定义INITIALLY IMMEDIATE,我没有使用SET CONSTRAINTS.

我错过了什么,或者这是一个(相当无害)的错误?


2)数据修改CTE

因此,修改CTE的数据也可以工作,尽管它有一个NOT DEFERREDpk 失败:

WITH x AS (
    UPDATE t SET id = 1 WHERE id = 2
    )
UPDATE t SET id = 2 WHERE id = 1;
Run Code Online (Sandbox Code Playgroud)

我引用了关于CTE手册:

WITH中的子语句彼此同时并与主查询一起执行.因此,在WITH中使用数据修改语句时,指定更新实际发生的顺序是不可预测的.所有语句都使用相同的快照执行(参见第13章),因此它们无法"看到"彼此对目标表的影响.


3)一个事务中的多个UPDATE语句

如果没有SET CONSTRAINTS,则会因UNIQUE违规而失败 - 正如所料:

BEGIN;
-- SET CONSTRAINTS t_pkey DEFERRED;
UPDATE t SET id = 2 WHERE txt = 'one';
UPDATE t SET id = 1 WHERE txt = 'two';
COMMIT;
Run Code Online (Sandbox Code Playgroud)

Dan*_*ité 30

我记得当PG9处于alpha状态时,我提出了一个几乎相同的观点.以下是Tom Lane(备受瞩目的PG核心开发人员)的回答:http: //archives.postgresql.org/pgsql-general/2010-01/msg00221.php

简而言之:不会修复.

不是说我同意你的建议,即目前的行为是一个错误.从相反的角度看待它:这NOT DEFERRABLE是不正确的行为.

实际上,在任何情况下都不应该发生此UPDATE中的约束违规,因为在UPDATE结束时满足约束.命令结束时的状态才是最重要的.执行单个语句期间的中间状态不应向用户公开.

似乎PostgreSQL通过在每行更新后检查重复项来实现非延迟约束,并在第一次重复时立即失败,这实质上是有缺陷的.但这是一个已知问题,可能与PostgreSQL一样古老.现在,解决方法恰好是使用DEFERRABLE约束.并且有一些讽刺意味着你认为它不足以因为它不会失败,而不知何故它应该是首先解决失败的方法!


PostgreSQL 9.1现状总结

  • NOT DEFERRABLE UNIQUE每行后PRIMARY KEY检查约束.

  • DEFERRABLE在每个语句之后检查设置为IMMEDIATE(INITIALLY IMMEDIATE或通过SET CONSTRAINTS)的约束.

  • DEFERRABLE在每次交易后检查设置为DEFERRED(INITIALLY DEFERRED或通过SET CONSTRAINTS)的约束.

注意UNIQUE/ PRIMARY KEY约束的特殊处理.引用手册页CREATE TABLE:

在每个命令之后立即检查不可延迟的约束.

虽然它在兼容性部分的下面进一步说明Non-deferred uniqueness constraints:

当一个UNIQUEPRIMARY KEY约束不可延迟时,PostgreSQL会 在插入或修改行时立即检查唯一性.SQL标准规定在语句结尾处强制执行唯一性 ; 例如,当单个命令更新多个键值时,这会有所不同.要获得符合标准的行为,请将约束声明为DEFERRABLE但不延迟(即INITIALLY IMMEDIATE).请注意,这可能比立即唯一性检查慢得多.

大胆强调我的.

如果您需要任何FOREIGN KEY约束来引用列,DEFERRABLE则不是一个选项,因为(每个文档):

引用的列必须是引用表中不可延迟的唯一或主键约束的列.


kgr*_*ttn 6

此处可能存在轻微的文档错误,但不适用于您所展示的情况。如果您开始一个事务并一次尝试更新,它们会失败,但如果单个语句使事情处于良好状态,它就不会抱怨。文档说:

如果约束是可延迟的,则该子句指定检查约束的默认时间。如果约束是 INITIALLY IMMEDIATE,则在每个语句之后检查它。这是默认设置。如果约束是 INITIALLY DEFERRED,则仅在事务结束时对其进行检查。

这正是似乎正在发生的事情。考虑到 的文档,我有什么惊喜DEFERRABLE,其中部分内容是:

不可延迟的约束将在每个命令后立即检查。

如果没有DEFERRABLE INITIALLY IMMEDIATE选项,即使UPDATE语句(大概构成“命令”)使事情处于良好状态,示例更新也会失败。也许应该修改文档以说明在每行都由语句修改时NOT DEFERRABLE强制执行约束?