開発環境で自動でSQLにExplainをかけるコンポーネント バージョンアップ(1.1) PostgreSQL対応
何度もバージョンアップしてすみません。MASA-Pさんのコメントや、あつさんのトラックバックからPostgreSQLでは一部のExplain結果しか出力されない問題がわかりました。ありがとうございます。
PostgreSQLで検証してませんでした、手を抜いてすみません。。。
というわけで、PostgreSQL対応しました。あつさんのコードを参考にしました。
下記のような画面になります。
最新版はここからダウンロードしてください。
PostgreSQL対応の差分だけ解説します
CakePHP のおいしい食べ方で英語で紹介してもらえて、http://planetcakephp.org/にも反映されたので、ソースコードは全部載せておきます
<?php /** * ExplainSqlComponent - Auto execute SQL Explain and set results in the debug mode. * * Copyright (c) 2009 Yasushi Ichikawa * * Use this compnent in afterFilter or afterRender. * var $components = array('ExplainSql'); * $this->ExplainSql->showExplainSQL( $slowQueryThreshold = 0 ); * * @author Yasushi Ichikawa * @version 1.11 * * @lastmodified Date: 2009-03-10 22:00:00 (JST) */ class ExplainSqlComponent extends Object{ /** * * @var controller object */ var $_controller; /** * set conroller object */ function startup(& $controller) { $this->_controller = $controller; } /** * Get all SQL query and execute SQL Explain of them without DESCRIBE query. * * if set the $slowQueryThreshold, * execute SQL Explain only slow query which are spent over $slowQueryThreshold seconds. * * @param integer $slowQueryThreshold * @access public */ function showExplainSQL( $slowQueryThreshold = 0 ){ $explain_results = array(); $count = 1; if(Configure::read() < 2 ){ return ; } if (!class_exists('ConnectionManager')) { return ; } $dbConfigs = ConnectionManager::sourceList(); foreach ( $dbConfigs as $configName ) { $db =& ConnectionManager::getDataSource( $configName ); if( empty($db->_queriesLog[0]) ){ continue; } $driver = $db->config['driver']; foreach( $db->_queriesLog as $key => $value ){ if( preg_match( '/^SELECT /i', $value['query'] ) && $value['took'] >= $slowQueryThreshold ){ $reesults = null; $results = $db->query( "Explain ". $value['query'] ); if( $driver === 'postgres' ){ //merge QIERY PLAN value $query_plan = "<ul>"; foreach( $results as $postgre_value ){ $query_plan .= "<li>"; $query_plan .= $postgre_value[0]['QUERY PLAN'] ; $query_plan .= "</li>"; } $query_plan .= "</ul>"; $results[0][0]['QUERY PLAN'] = $query_plan; //change column order $results[0][0] = array_merge( array("id" => $count), $results[0][0] ); } $results[0][0]['query'] = $value['query']; $results[0][0]['id'] = $count; $explain_results[] = $results[0][0]; $count++; } } } if( !empty( $explain_results[0] ) ){ $this->_outputHtml( $explain_results ); } return; } /** * set SQL Explain results on the controller->output. * * @param array $explain_results */ function _outputHtml( $explain_results ){ $html_out = '<table>'; $html_out .= '<tr>'; //set table column name foreach( $explain_results[0] as $titlekey => $titleval ){ $html_out .= '<th>'; $html_out .= $titlekey; $html_out .= '</th>'; } $html_out .= '</tr>'; //set results foreach($explain_results as $recordnum => $val_arr){ $html_out .= '<tr>'; foreach( $val_arr as $key => $value ){ $html_out .= '<td style = "text-align: left">'; $html_out .= $value." "; $html_out .= '</td>'; } $html_out .= '</tr>'; } $html_out .= '</table>'; $this->_controller->output .= $html_out; } } ?>
$db->configの中に、DB接続情報が入っているので、そこから接続しているDBサーバの値を取得して、PostgreSQLの場合の処理を追加しました。
PostgreSQLのExplain結果の配列構造とMySQLの結果とが異なるので、無理やり合わせました。あつさんの記事を参考に、QUERY PLANのカラムの値をひとつにマージしてリスト表示させてます。
QUERY PLAN1, QUERY PLAN2, QUERY PLAN3のようにカラムを分けようと思いましたが、クエリによってQUERY PLANの数が異なるので、1カラムにまとめました。
それと、idという番号を振ってるだけのカラムの位置が、PostgreSQLの場合は連想配列の後半に来てたので、array_mergeで先頭に来るようにしました。
不具合や、要望があればどしどしお寄せください。よろしくお願いします。
追記:bakeryに投稿しました。そのうち承認されたら公開されると思います(たぶん。。。)