2

假设我在 PostgreSQL 9.1 数据库中有这个foo表:

CREATE TABLE foo
(
  bar integer,
  flg_deleted boolean
);

除了这个vwfoo视图:

CREATE VIEW vwfoo AS
  SELECT bar
  FROM foo
  WHERE flg_deleted = false;

还假设我有一个应用程序每秒运行几个短期事务,使用vwfoo

现在,我想在foo中添加一个baz列,并且我希望baz也可以在vwfoo中。但是,当然,我不希望我的应用程序因为这些更改而出现任何错误。

如果我执行以下步骤(在单个事务中)以执行所需的更改:

  1. 删除vwfoo
  2. 将列baz添加到foo
  3. 再次创建vwfoo(现在包括baz)。

我是否获得了所需的行为(应用程序中没有错误)?

在整个事务期间是否会在vwfoo上持有排他锁(这就是我想要的)?

是否有可能任何事务都会尝试在步骤 1 和 3 之间使用视图然后失败(而不仅仅是阻塞,等待锁定)?

重新创建时vwfoo的“身份”是否会改变?换句话说:任何事务是否有可能在第 1 步和第 3 步之间尝试使用视图、阻塞、在第 3 步之后恢复,然后因为重新创建视图而失败?

谢谢。

4

1 回答 1

1

快速测试表明这会导致您的应用程序出现问题。要重新创建,请创建两个连接(AB),然后运行:

A: BEGIN;
A: DROP VIEW vwfoo;
B: SELECT * FROM vwfoo;
(B blocks… )
A: CREATE VIEW vwfoo AS SELECT * FROM foo;
A: COMMIT;
(B yields:
    ERROR:  could not open relation with OID 326418
    LINE 1: SELECT * FROM vwfoo
)

相反,您应该通过重命名视图来进行这种原子交换:

A: CREATE VIEW vwfoo_new AS SELECT * FROM foo;
A: BEGIN;
A: ALTER VIEW vwfoo RENAME TO vwfoo_old;
B: SELECT * FROM vwfoo;
(B blocks…)
A: ALTER VIEW vwfoo_new RENAME TO vwfoo;
A: COMMIT;
(B completes as expected)
A: DROP TABLE vwfoo_old;

这将按预期工作(并且您不需要DROP TABLE在事务中运行(相对)昂贵的!)

编辑:您也可以使用相同的策略来解决您的“真正”问题:

ALTER TABLE foo ADD COLUMN bar_new TEXT;
UPDATE foo SET bar_new = bar;
CREATE VIEW vwfoo_new AS SELECT bar_new AS bar FROM foo;
… do the view switcheroo …
DROP VIEW vwfoo_old;
BEGIN;
ALTER TABLE foo RENAME bar TO bar_old;
ALTER TABLE foo RENAME bar_new TO bar;
COMMIT;
ALTER TABLE foo DROP COLUMN bar_old;

视图引用bar_new也将正确更新:

# \d vwfoo
View definition:
 SELECT foo.a
   FROM foo
  WHERE foo.b = false;
# ALTER foo RENAME a TO new_a;
ALTER TABLE
# \d vwfoo
View definition:
 SELECT foo.newa AS a
   FROM foo
  WHERE foo.b = false;
于 2013-01-08T06:31:59.743 回答