ありがとう。また会おう。

ゆるいかんじで。かたのちからぬいて。やってます。

特殊な条件下でAffectedRowsが取得できない

嵌りました。。。orz
かなり特殊な状況ではあるのですが、同じ落とし穴に他の人が嵌らないよう、メモ。


使用しているバージョンは
PHP:5.2.6
PEARMDB2:現時点で最新
PostgreSQL:7.4系


で、問題は、PEARマニュアル
http://pear.php.net/manual/ja/package.database.mdb2.intro-execute.php
にある、「例 39-2execute() に配列を渡す」の要領で

<?php
//なんか適当なINSERT文をprepareしてるとして
$affectedRows = $sth->execute($data);
var_dump($affectedRows);

としても、出力が

int(0)

・・・なぜ!?データベースの中身はちゃんと更新されてるのに!!


で、いろいろ調べてたら、なんか奇妙な事実が次々とでてきた。

  1. DML文はどれを実行しても(INSERT/UPDATE/DELETE)、0が返る。エラーにはならない。データベースもちゃんと更新されている。
  2. プリペアードステートメントを使わず、直接 $sth->exec() した場合はちゃんと取得できる


これはもう挙動を追うしかないなぁ。。。と思い、MDB2のソースを探索。
う〜ん・・・execute()もexec()も最終的に行き着くところ同じなんだ。。。
だとすると、余計この2つで挙動が違うのか理解できない。。。


PostgreSQLがPREPAREをサポートしてないバージョン?
・・・いや、7.4からサポートだから、それは大丈夫なはず。。。
PHPのpg_prepare()関数も、5.1以降だから条件は満たしてるはず。。。


でもなんか妙に気になったので、試しにこのコードを実行してみた:

<?php
var_dump(function_exists('pg_prepare'));

//出力結果
//bool(false)

・・・・はぁ!?.。ooO(゚ぺ/)/


なぜ?なぜ?


PHPPostgreSQLもバージョン条件は満たしてるのに。。。


更に気になったので。。。

<?php
var_dump(function_exists('pg_execute'));
var_dump(function_exists('pg_query_param'));

//出力結果
//bool(true)
//bool(true)

はへっ!?


なんじゃこりゃ。。。わけわからん。。。(x_x)


どうやら、pg_prepare()だけが使えない模様。
う〜ん、これはどうもPHPのコンパイルオプションとか絡んでそうだな。。。
あと、PostgreSQLのライブラリのインクルードがどうとか。。。


ちょっとそっちを追いかけるのは後にしてと。
この、pg_prepare()が使えない、という前提で、もう一度MDB2をコードリーディング。


なるほどね。わかった。
どうやら、MDB2内部ではこんな風になってるらしい。

  1. pg_prepare()関数があれば、それを使う
  2. pg_prepare()がなくて、PostgreSQLがPREPARE構文をサポートするなら、pg_query()でPREPARE構文を使用する
  3. それもだめなら、MDB2でエミュレート

で、問題はこの2番目。
pg_query()関数を使って、PREPARE/EXECUTE構文をSQLとして発行する場合、pg_affected_rows()関数が影響行数を返さないんじゃないか疑惑浮上。
MDB2のexecute()メソッドの返値は、追っかけると最終的にpg_affected_rows()関数を呼び出しているので)


そこで実験。

<?php
$conn = pg_connect(DB_CONN_STRING);//DB_CONN_STRING は正しいDB接続文字列を格納した定数とする。
$sql_prepare = 'PREPARE test_stmt(text) AS INSERT INTO hoge VALUES ($1) ';
pg_query($conn, $sql_prepare);
$sql_execute = "EXECUTE test_stmt('fuga')";
$res = pg_query($conn, $sql_execute);
$affectedRows = pg_affected_rows($res);
var_dump($affectedRows);

出力結果:

int(0)

ビンゴだ。。。


わかったことを整理すると。

  • pg_affected_rows()関数は、PREPARE/EXECUTE構文をpg_query()関数で直接呼び出した場合には、影響行数を返さず、常に0を返す
  • MDB2は、pg_prepare()が使えないと、PREPARE/EXECUTE構文を使おうとする。

これが原因で、AffectedRowsが取得できなかったのか。。。


しかし、「(PHP/PostgreSQLのバージョン条件は満たしてるのに)なぜ pg_prepare()関数が未定義か? 」については未解決。。。
これはもう少し深いところ追いかけないとわからなそうだ。。。orz