2

PostgreSQL9.1データベースに次のfooテーブルがあるとします

CREATE TABLE foo
(
  bar integer,
  flg_deleted boolean
);

このvwfooビューとともに:

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

また、 vwfooを使用して、1秒あたり2、3の短期間のトランザクションを実行するアプリケーションがあるとします。

ここで、列bazfooに追加し、 bazvwfooに含めたいと思います。しかし、もちろん、これらの変更のためにアプリケーションでエラーが発生することは望ましくありません。

次の手順を(単一のトランザクションで)実行して、目的の変更を実行する場合:

  1. vwfooを削除します。
  2. 列bazfooに追加します。
  3. vwfooを再度作成します(現在はbazを含みます)。

目的の動作が得られますか(アプリケーションにエラーはありません)?

トランザクション全体を通してvwfooに排他ロックが保持されますか(それが私が望むものです)?

トランザクションがステップ1と3の間のビューを使用しようとして失敗する可能性はありますか(単にブロックするのではなく、ロックを待つ)?

vwfooの「アイデンティティ」は、再作成されたときに変更されますか?言い換えると、トランザクションがステップ1と3の間でビューを使用しようとし、ブロックし、ステップ3の後に再開し、ビューが再作成されたために失敗する可能性はありますか?

ありがとう。

4

1 に答える 1

1

簡単なテストは、これがアプリケーションに問題を引き起こすことを示唆しています。再作成するには、2つの接続(AおよびB)を作成してから、次のコマンドを実行します。

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 に答える