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

2008-06-27

PostgreSQLでの複文の制限

| 21:16 | PostgreSQLでの複文の制限 - iakioの日記 を含むブックマーク はてなブックマーク - PostgreSQLでの複文の制限 - iakioの日記

PostgreSQLでは、複文が利用可能かどうかはAPIによって(というかプロトコルによって)決まります。拡張問い合わせでは複文を使うことはできません。

PQexecと異なり、PQexecParamsは、文字列内に最大でも1つのSQLコマンドを入れることができます。 (セミコロンを入れることはできますが、空でないコマンドを1つ以上入れることはできません。) これは、プロトコル自体の制限ですが、SQL混入攻撃に対する追加の防御となる点より多少役に立ちます。

コマンド実行関数

すべてのケースを試してみたわけではないのですが、Javaで「更新系のSQLであれば複文に対応」となっているのは、Statement.executeUpdate()を使っているからで、PreparedStatement.executeUpdate()であれば実行できないのではないかと思います。

PHPだとこんな感じ

<?php
$con = pg_connect("");
$res = pg_query($con, "update tbl1 set t = '';update tbl1 set t = ''");

$res = pg_prepare($con, "", "update tbl1 set t = '';update tbl1 set t = ''");
$res = pg_execute($con, "", array());

実行

$ php test.php
PHP Warning:  pg_prepare(): Query failed: ERROR:  cannot insert multiple commands into a prepared statement in /home/iakio/test.php on line 5
PHP Warning:  pg_execute(): Query failed: ERROR:  unnamed prepared statement does not exist in /home/iakio/test.php on line 6

zsxxftclzsxxftcl2011/04/24 17:44cjchQp , [url=http://hjanipamgmnh.com/]hjanipamgmnh[/url], [link=http://ehhnmcxrnvtm.com/]ehhnmcxrnvtm[/link], http://tbmxkfsxlfwi.com/

TienTien2013/04/05 18:08I atcually found this more entertaining than James Joyce.

czbdxfczbdxf2013/04/07 06:103eG4ap <a href="http://cabwpqiayfyh.com/">cabwpqiayfyh</a>

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