[pgsql-jp: 38302] Re: SYNONYM 代わりのVIEW+RULEへの、更新時ロックの挙動について

松原 健二 matsubk @ alpha.co.jp
2007年 4月 11日 (水) 15:33:54 JST


松原です。

SAIMI Kohei 様<qs2007 @ aim.com> wrote:

> アイソレーションがread committedならば、transaction Aをコミットするまで、
> statusは0に見えますよね。で、レコードがアンロックされたあとに、前述のルー
> ルに従って更新しているということでは?

RULEによるVIEWへの更新実行は、実は内部で
 1) 更新対象VIEWデータの読み取り
 2) INSTEAD指定したテーブルへの、更新実行
の2手順に分かれていて、

read committed の場合、行の更新によって処理にWAITがかかるのは
2) の処理だけであるため、1)はアンロックされる前に実行されているため
OLD.* として、その時点での「id = 0, status = 0 なデータ」が読み取られる。
そしてアンロック後、OLD.id (=0) を条件とする更新が実行されてしまう、
という認識でよろしいでしょうか(※)。

> CREATE RULE upd_test
>   AS ON UPDATE TO test_view
>   DO INSTEAD UPDATE test_parent
>     SET status = NEW.status, id = NEW.id
>     WHERE id = OLD.id AND status = OLD.status;
> 
> としておけば、実際にUPDATEするときにはstatusの値が変更されているので、更
> 新されないのではという想像です。
> 
> #ここまで全く検証せずに書いてます。あしからず。
> 
> -- 
> SAIMI Kohei <qs2007 @ aim.com>
> 

上記(idとstatusを両方指定する)RULEを試してみたところ、
SAIMI様のご想定どおり、「status が別トランザクションによって更新された
データは、更新対象にしない」という挙動となりました。
ありがとうございます。

しかし、別な問題が起こります。
上記の(idとstatusを両方指定する)RULEにて、今度はid だけをWHERE句指定する
SQLを発行すると、

≪transaction A≫
1) DB=# BEGIN;
  BEGIN

3) DB=# UPDATE test_view SET status = 1
  DB-#   WHERE id = 0;
  UPDATE 1

5) DB=# COMMIT;
  COMMIT

≪transaction B≫
2) DB=# BEGIN;
  BEGIN

4) DB=# UPDATE test_view SET status = 2
  DB-#   WHERE id = 0;  -- <5)が終わるまでWAIT
  UPDATE 0  --<UPDATE 1 であることを期待

と、「idをWHERE句で指定していないのだから、statusが変更されているか
いないかに関わらず更新して欲しい」状況の場合に、更新してくれなく
なります。

(※)における私の認識が正しいのであれば、こうなってしまう理由は
理解できるのですが。。

目指している地点は、SYNONYMの完全な代替であり、
 ・id だけWHERE句指定すれば、status に関わらず更新してくれる
 ・id と status をWHERE句指定すれば、他トランザクションでstatusが更新された
  場合、更新されない
という挙動となって欲しいのですが、うまい手を思いつきません。
#前回記述では、要望が不明瞭でした。申し訳ありません。

何か良い手段はありますでしょうか。
以上、よろしくお願いいたします。


=======================
 (株)アルファシステムズ
 第二事業本部 第二事業部 第一技術部 四課一係
 松原 健二
=======================





pgsql-jp メーリングリストの案内