ソフトウェア開発者の日常

こだわりなく書きたいことを書いていきます。

PHP:ロールバックしているはずがロールバックされない

これまでに見たことがあるバグです。

あるシステムで、

  1. 入力内容をデータベースに登録処理
  2. 決済処理
  3. メール送信処理

という流れで処理をしていました。

決済処理でエラーが発生した場合とは、例えばクレジットカード番号が間違っていて決済できなかったときが該当します。
決済処理でエラーが発生しすると、データベースに登録のトランザクションロールバックして、登録した内容を破棄しています。

あるシステムで決済処理でエラーを起こしてデータベースを確認すると、破棄しているはずのデータがデータベースに登録されています。

pq_queryの使い方の違い

プログラムを見ると、以下のような構成になっていました。

$conn = pg_pconnect("dbname=publisher");

// トランザクション開始
pq_query("BEGIN");

// データベース登録処理
pq_query($conn, 'INSERT INTO table1 (email) VALUES ('example@com'));

// 決済処理
// 省略

if (決済処理の結果 == エラー) {
    // 決済処理でエラー
    pq_query("ROLLBACK");
} else {
    // 決済処理で正常
    pq_query("COMMIT");
}

これでどうしてデータベースにデータが登録されるのかと悩みだしました。

PHP: pg_query - Manual
のマニュアルを見ながら考えていると、トランザクション関係のpg_query()の第1引数が省略されているのが気になりました。
第1引数は、

connection
 PostgreSQL データベース接続リソース。connection が指定されていない場合はデフォルトの接続が使用されます。 デフォルトの接続は、直近の pg_connect() あるいは pg_pconnect() によって作成されたものです。 

と書かれているので、省略しても同じ接続リソースを使っているように見えます。

注意: connectionは省略可能ですが、それは推奨されません。 なぜならスクリプトのバグが発見しにくくなるためです。

とも書かれている点が気になった点です。


気になったら試してみるのがはっきりするので、トランザクション関係のpg_query()の第1引数を指定したところ、トランザクションロールバックできるようになりました。

f:id:AJYA:20180607124037p:plain
unsplash-logoArtem Sapegin


トランザクション関係のpg_query()の第1引数を省略していたことが原因でした。
省略していても、PHPの実行を止めるような致命的なエラーが発生していなかったので、気づくまで時間がかかりました。