[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.jphttp://www.digitune.org/




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