[pgsql-jp: 32134] Re: Rule と Lock
Tsunehisa Kazawa
kazawa @ ca2.so-net.ne.jp
2004年 1月 27日 (火) 22:46:09 JST
お返事遅くなってしまってすみません。加澤です。
対応策まで教えていただいて、本当にどうもありがとうございます。以下、まと
めも兼ねてその後の顛末をご報告します。
# また長くなっちゃうと思います…すみません。
Tatsuo Ishii wrote:
>>insert_daily_log02を削除してもロックされるところを見ると,そのようです
>>ね.daily_log とdaily_log01にRow Exclusive Lockがかかっていました(ちな
>>みに,Row Exclusive LockはINSERT/UPDATE/DELETEで必ず取得されます).
石井さんからのこの1通めのメールを見て、pg_locks テーブルでその時点での
ロックの状態を確認できることを後ればせながら思い出しました。下記の通り
daily_log テーブルへの insert 中のロック状態を確認してみると、全てのテー
ブルに対して RowExclusiveLock が取得されていることが確認できました。
testdb=# begin;
BEGIN
testdb=# insert into daily_log (log_memo) values ('hoge');
INSERT 0 0
testdb=# select c.relname,l.relation,l.mode from pg_class c,pg_locks l
testdb-# where c.relfilenode=l.relation;
relname | relation | mode
-------------+----------+------------------
daily_log01 | 17149 | RowExclusiveLock
daily_log02 | 17155 | RowExclusiveLock
pg_locks | 16759 | AccessShareLock
daily_log | 17143 | AccessShareLock
daily_log | 17143 | RowExclusiveLock
pg_class | 1259 | AccessShareLock
(6 rows)
> この件ですが,ruleの中で直接INSERTするのではなく,関数を呼んでその中で
> 間接的にINSERTすれば大丈夫のようですよ.すなわち,
>
> CREATE OR REPLACE FUNCTION f_insert_daily_log01(VARCHAR) RETURNS VOID
> AS '
> BEGIN
> INSERT INTO daily_log01(log_memo) VALUES ($1);
> RETURN;
> END;
> ' LANGUAGE PLPGSQL;
>
> のような関数を作り,
>
> CREATE RULE insert_daily_log01 AS ON INSERT TO daily_log
> WHERE 1=0
> DO INSTEAD SELECT f_insert_daily_log01(NEW.log_memo);
>
> のようなruleを定義します.なお,関数はPL/pgSQLである必要はないのですが,
> SQL関数だけは7.4ではインライン展開されてしまうのでたぶん駄目だと思いま
> す.
さらに上記のメールを読みまして (本当にありがとうございます!)、さっそく
自分なりにアレンジして function を作成してみました。
create function insert_daily_log(timestamp,varchar) returns void as '
declare
in_log_date alias for $1;
in_log_memo alias for $2;
begin
if 1=0 then
insert into daily_log01 (log_date,log_memo)
values (in_log_date,in_log_memo);
elsif 1=1 then
insert into daily_log02 (log_date,log_memo)
values (in_log_date,in_log_memo);
end if;
return;
end;
' language plpgsql;
# 最初、NEW を直接渡せないかといろいろやってみたんですが、どうして
# もうまくいきませんでした。
その後、daily_log テーブルのルールを次のように変更しました。
testdb=# create rule insert_daily_log_rule as on insert to daily_log
testdb-# do instead select insert_daily_log(NEW.log_date,NEW.log_memo);
CREATE RULE
この状態で daily_log テーブルに insert してみると、今度は daily_log テー
ブルと daily_log02 テーブルに対してだけ RowExclusiveLock が獲得されてい
ることが確認できました。
testdb=# begin;
BEGIN
testdb=# insert into daily_log (log_memo) values ('hoge');
insert_daily_log
------------------
(1 row) … ※1
testdb=# select c.relname,l.relation,l.mode from pg_class c,pg_locks l
testdb-# where c.relfilenode=l.relation;
relname | relation | mode
-------------+----------+------------------
daily_log02 | 17155 | AccessShareLock
daily_log02 | 17155 | RowExclusiveLock
pg_class | 1259 | AccessShareLock
daily_log | 17143 | AccessShareLock
daily_log | 17143 | RowExclusiveLock
pg_locks | 16759 | AccessShareLock
(6 rows)
***
ここでふと、insert の返りが通常と異なることに気がつきました (※1の点)。
rule で select 文に変換してしまったことにより、出力が select のそれに
なってしまっているわけです。
この段階で、試しに JDBC 経由で daily_log テーブルに insert しようとして
みたところ、「予期しない result が来た」というようなエラーを吐いてうまく
insert 出来なくなっていました。
そこで、rule を次のように変更してみました。
testdb=# create rule insert_daily_log_rule as on insert to daily_log
testdb-# do instead delete from daily_log where
testdb-# exists (select insert_daily_log(NEW.log_date,NEW.log_memo));
select を直接呼ぶ代わりに、delete 文の where 句で呼ぶようにしてみたわけ
です。もともと daily_log テーブルは常に空 (なはず) ですから、これでも問
題ないはず、ということで。うーん泥臭い。
この状態で daily_log テーブルに insert してみると、(oid や row count) は
常に 0 になっちゃいますが) とりあえず更新系コマンドを実行したかのような
返り値が帰ってきます。JDBC からの insert も問題なく動作しました。
以上です。
--
◇ 加澤恒央 Tsunehisa KAZAWA
◇ ◇ mailto:kazawa @ ca2.so-net.ne.jp
◇ http://www.digitune.org/
pgsql-jp メーリングリストの案内