スクウェア・エニックスノベルス『スタンプ・デッド』1~5巻&コミック版(完結)、
『太陽で台風』1・2巻(完結)発売中!
ガンガンノベルズ『魔法少女アーヤ☆アミー』発売中!
徳間デュアル文庫『魔王さんちの勇者さま』1~4巻(完結)発売中!
徳間文庫『欠陥妖怪住宅』発売中!
ぽにきゃんBOOKSライトノベルシリーズ『ブチ切れ勇者の世界征服』1~2巻発売中!
徳間文庫『パラレル家族計画』発売中!

2017年4月、第11回HJ文庫大賞様にて『銀賞』をいただきました!
2017年5月、ジャンプ小説新人賞’16 Winter様にて 小説フリー部門『銀賞』受賞をいただきました!

2017-08

«  | ホーム |  »

PDOでプリペアドステートメントを利用する場合にバインド済SQLを(無理矢理)ログに出力する

どうも、はむばねです。
ようやく情報系の話を書くだけの余裕ができたぜ(脳のリソース的な意味で)!
例によって誰得感漂う内容ではありますが、まぁ私の書くものが誰かの得になることの方がレアなので気にしないことにしよう。。
ていうか、ブログに書いとかな自分で調べた事とか考えた事とかを忘れれてしまうんや……。
ほぼ、自分用の備忘録ですね。
チラシの裏で良いという説も有力ですが、それも気にしない。

さて。
こないだ、とある(php+mySQLでの)ログ出力の処理を実装していたのですが。
プレースホルダが反映された後のSQLを確認する方法ってないんですかね?
え? mySQL側のログを見ろって?
はっはっは、閲覧権限がねーんだよファッキン!
ていうかそもそも、mySQL側のログにはプレースホルダ反映済みのSQLが記録されるんだっけ??
事前にSQLをコンパイル(実行だっけ?)してバインド時に結合するという動きであるならmySQL側でもログには記録されないような気がするのですが。
まぁ実行している以上、(それがログのためだけに手間になろうとも)記録する方法はあるとは思いますけれど。

mySQL側で試す環境もないではないのですが、めんどかったし今回の環境では(今のところ)どうせ使えないのでphp側で実装してみました。
全力の力技でな!
php側で元のSQL文とバインドする値を勝手に結合してログに出力してるだけです。
力技というか、正確なログですらないよね。
これで記録されるのは「実行されたSQL」ではなくて「実行されたはずのSQL」なのだから。
でも、一応書いてみる。

つっても、要はただの文字列操作しかしてません。
TMPテーブルの、TMP_IDとTMP_NAMEをselectする例

------------------------------------------------------------------------
//SQL ステートメントを準備
$stmt = $dbh->prepare("select * from TMP where TMP_ID like :TMP_ID and TMP_NAM like :TMP_NAM");

//検索条件を指定
$paramList = array(
':TMP_ID' => "000%",
':TMP_NAM' => "sample%",
);

//バインドしてSQL実行
$stmt->execute($paramList);

//prepareしたSQL文を取得
$query = $stmt->queryString;

//パラメータ格納配列のキー(SQL中の名前付きパラメータと同値)を取得
$keys = array_keys($paramList);

//名前付きパラメータをバインドする値に置換
for($i=0; $i $param = $paramList[$keys[$i]];
$query = str_replace($keys[$i], $param, $query);
}
------------------------------------------------------------------------

これで、$queryに実際に実行された(はずの)SQL文が格納されますね。
バインドするパラメータを(プレースホルダとして使用している名前付きパラメータを配列のキー名にして)連想配列に格納して、後で名前付きパラメータをキーに置換しているだけです。


以下、一応それっぽく動きそうなコード。
ついでに、errorInfo()でエラー情報も取ってきてます。

------------------------------------------------------------------------

$host = "host";
$user = "user";
$pass = "pass";
$db = "db";

// MySQLへ接続する
$dbh = new PDO("mysql:dbname=${db};host=${host}", $user, $pass);

//SQL ステートメントを準備
$stmtTMP = $dbh->prepare("select * from TMP where TMP_ID like :TMP_ID and TMP_NAM like :TMP_NAM");

//検索条件を指定
$paramList = array(
':TMP_ID' => "000%",
':TMP_NAM' => "sample%",
);


execSQL($stmtTMP, $paramList);



function execSQL($stmt, $paramList) {

//バインドしてSQL実行
$stmt->execute($paramList);

//パラメータ格納配列のキー(SQL中の名前付きパラメータと同値)を取得
$keys = array_keys($paramList);

//prepareしたSQL文を取得
$query = $stmt->queryString;

//メッセージ整形
$mes = "\t${query}\n";

//名前付きパラメータをバインドする値に置換
for($i=0; $i $param = $paramList[$keys[$i]];
$query = str_replace($keys[$i], $param, $query);
}

//タブ挿入
$query = str_replace("\n", "\n\t", $query);

//エラー情報取得
$errorInfo = $stmt->errorInfo();

//ログメッセージ生成
$mes .= date("Y/m/d H:i:s") . " SQL executed:\n";
$mes .= "\t${query}\n";
$mes .= "\tSQLSTATE: ${errorInfo[0]}\n";
$mes .= "\tError Code: ${errorInfo[1]}\n";
$mes .= "\tError Message: ${errorInfo[2]}\n";


//書き込みモードでファイルオープン
$fp = fopen('logs/online.log', 'a');

//ロック取得(排他ロック、既にロックされている場合は待機)
flock($fp, LOCK_EX);

//ログメッセージ出力
fwrite($fp, $mes);

//ロック解除
flock($fp, LOCK_UN);

//ファイルクローズ
fclose($fp);

return 0;
/* End -- execSQL */
}
?>
------------------------------------------------------------------------


きっと、以下のようなログが出力されるはず。
------------------------------------------------------------------------
2015/05/06 19:49:32 SQL executed:
select * from TMP where TMP_ID like 000% and TMP_NAM like sample%
SQLSTATE: 00000
Error Code:
Error Message:
------------------------------------------------------------------------


とまぁ、一応それっぽいログが出力されるようにはなるわけですけどね。
ぶっちゃけ、こんなもんが一般的な手法なわけないよね?
先述の通り、ログとしては全く正確ではないですからね。
あくまで、「こちらの意図した通りに動いてくれれば意図したログが出力される」という機構。
ログの意味合いを半分以上果たせてない。
例えば、mySQL側でバインド失敗とかしててもたぶんログには正常なSQL文が出力されるのではなかろうか。
元のSQL文も変数に格納しときゃえぇやんっていう中わざわざSQL文実行後にprepareされた文を取得してたり、errorInfo拾ってきたりもしてますが、十全とはとても言えませんよね。

世の中的には、この問題をどうやって解決してるんだぜ?
やっぱDB側からのアプローチになるのか??
しかし、DB2もそういや思い出してみたらログにはバインド前のSQL文が記録されてたような気もしてきたぞ……?
いや、それはdiagだったからか……?
あかん、そろそろ勤めてた時の記憶も曖昧になってきたぜ……。

まぁそんなわけなので、「いやバカじゃねーの? 普通こうするでそ」って方法があったら教えやがってください。
普通じゃなくてもいいです。
スポンサーサイト

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック

http://hamubane.blog.fc2.com/tb.php/4001-df36efd9
この記事にトラックバックする(FC2ブログユーザー)

«  | ホーム |  »

プロフィール

hamubane

Author:hamubane

たぶんライトノベル作家的なもの


Twitter

既刊情報

最新コメント

最新記事

カレンダー

07 | 2017/08 | 09
- - 1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31 - -

月別アーカイブ

カテゴリ

未分類 (0)
ブログ (4333)
スタンプ・デッド (73)
情報系 (23)
短ネタ (35)
太陽で台風 (2)
しゅーかつ (18)
レパートリー確認PJ (27)
自著を無理矢理褒める (7)
感想 (291)
天神周辺グルメ (43)

検索フォーム

RSSリンクの表示

リンク

このブログをリンクに追加する

ブロとも申請フォーム

この人とブロともになる

QRコード

QR