iakioの日記 RSSフィード

2009-08-21

明示的なトランザクション内でのエラーとDEALLOCATE

| 22:20 | 明示的なトランザクション内でのエラーとDEALLOCATE - iakioの日記 を含むブックマーク はてなブックマーク - 明示的なトランザクション内でのエラーとDEALLOCATE - iakioの日記

PerlのDBD::Pgで、明示的なトランザクション内の$dbh->prepareでエラーが発生すると、勝手にROLLBACKしてしまうそうです。

試してみたところ現象は再現しました。

元エントリのトレースログから、Query('Q')とParse('P')だけ抜き出してみると、

To backend> Msg Q
To backend> "begin"
To backend> Msg P
To backend> "dbdpg_27"
To backend> "INSERT INTO test (x) VALUES ($1)"
To backend> Msg Q
To backend> "DEALLOCATE dbdpg_27"
To backend> Msg P
To backend> "dbdpg_28"
To backend> "INSERT INTO test (x) VALUES ($1)"
To backend> Msg Q
To backend> "ROLLBACK"
To backend> Msg Q
To backend> "DEALLOCATE dbdpg_28"
To backend> Msg Q
To backend> "begin"
To backend> Msg P
To backend> "dbdpg_29"
To backend> "INSERT INTO test (x) VALUES ($1)"
To backend> Msg Q
To backend> "DEALLOCATE dbdpg_29"
To backend> Msg Q
To backend> "commit"

となっていて、実際にはParse、実行、DEALLOCATEを繰り返しています。このDEALLOCATEはプレースホルダが破棄された時に自動的に呼ばれるようなのですが、ABORTしたトランザクション内ではDEALLOCATEを実行できないので、勝手にROLLBACKしているようです。

なので、こんな風にプレースホルダがすぐには破棄されないようにすると、ROLLBACKしないようです。

$dbh->begin_work;
my $st1 = $dbh->prepare("insert into test(x) values (?)"); $st1->execute(10);
my $st2 = $dbh->prepare("insert into test(x) values (?)"); $st2->execute(1);
my $st3 = $dbh->prepare("insert into test(x) values (?)"); $st3->execute(20);
$dbh->commit;

DBD::Pgのソースコードの中にも、

dbdimp.c:3507
    /* If we are in a failed transaction, rollback before deallocating */
    if (PQTRANS_INERROR == tstatus) {

と書いてありました。

とはいえちょっと困った仕様のような気がします。DBD::Pgを直すとすれば、プレースホルダが破棄されてもトランザクションが終了するまでDEALLOCATEするのを待つ、くらいでしょうが。

Rubyruby-pgPHPのpg_*ではプレースホルダをオブジェクトとして扱っていないので問題無さそうです。また、PHPのPDOではプレースホルダが破棄された時に自動的にDEALLOCATEを発行しますが、ROLLBACKは発行しないため、DEALLOCATEがエラーとなるようです。

<?php
$dbh = new PDO("pgsql:dbname=...");
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
$dbh->beginTransaction();
$dbh->prepare("insert into test(x) values(?)")->execute(array(10));
$dbh->prepare("insert into test(x) values(?)")->execute(array(1));
$dbh->prepare("insert into test(x) values(?)")->execute(array(20));
$dbh->commit();

nabenabe2009/08/23 02:47TBありがとうございます。ROLLBACKしていいから、DEALLOCATE後に空のトランザクションを begin して、内部的に失敗させてくれればそれで十分な予感。

PravalikaPravalika2012/10/08 00:16Hot damn, looking pretty uesful buddy.

whtbwqbrmpwhtbwqbrmp2012/10/09 13:40k6vtTC , [url=http://tfokhqkgvmok.com/]tfokhqkgvmok[/url], [link=http://wmmsericzehp.com/]wmmsericzehp[/link], http://bsgooojuolnj.com/

fmzzeycwfmzzeycw2012/10/10 21:36LcWUKx <a href="http://rhsvoumrprdr.com/">rhsvoumrprdr</a>

zyvcewjagrzzyvcewjagrz2012/10/11 11:12XbNpR2 , [url=http://htefyotokjvk.com/]htefyotokjvk[/url], [link=http://obhxihwsfztv.com/]obhxihwsfztv[/link], http://nukeywqmlfke.com/

トラックバック - http://postgresql.g.hatena.ne.jp/iakio/20090821