開発環境で自動でSQLにExplainをかけるコンポーネント バージョンアップ(1.0)

CakePHP 1.2.1を使ってます。

昨日書いたAuto Explain Componentの記事ですが、反応はほとんどないと思って、ソースコードとか適当に書いた状態で公開してしまいました。反応があってうれしかったのですが、いくつか不具合を発見したので、いきなりバージョンアップです。


追記
すみません、この記事はMySQLのみ対応となります。PostgreSQLをご利用の方は、下記の記事を参照ください。
開発環境で自動でSQLにExplainをかけるコンポーネント バージョンアップ(1.1) PostgreSQL対応




バグとしては、モデルをnewを使って個別に呼び出してた場合や、insert, update , deleteなどが発行された場合はうまく動きませんでした。今回はそのバグの修正と、追加機能として、ある秒数以上かかったSelect文のみExplainするようにしました。デフォルトは0秒以上のクエリなので全てのSelect文がExplain対象です。

表示内容は下記の画像のように何も変化はありません。


基本的にはcomponentファイルを入れ替えるだけでOKです。

コンポーネントやReadmeなどのファイルはこのZIPをダウンロードするか、下記のコードをコピー&ペーストしてください。
app/controllers/components/explain_sql.php

<?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.0
 *
 */
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;
			}


			foreach( $db->_queriesLog as $key => $value ){

				if( preg_match( '/^SELECT /i', $value['query'] ) 
                  && $value['took'] >= $slowQueryThreshold ){

					$reesults = null;
					$results = $db->query( "Explain ". $value['query'] );

					$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."&nbsp";
				$html_out .= '</td>';
			}
			$html_out .= '</tr>';
		}

		$html_out .= '</table>';


		$this->_controller->output .= $html_out;
	}

}

?>


使い方は前と同じように、afterRenderとかafterFileterに入れてください。今回の追加機能で、ある指定秒数以上かかったクエリのみExplainするために、第一引数に数値をセットするようにしましたが、これがなくても動きます(全てのSelectがExplain対象)。
今回の例は、全ての画面に表示させるために、app/app_controller.phpに記載する例です。

<?php
class AppController extends Controller {

	var $components = array('ExplainSql');

	function afterFilter(){

		parent::afterFilter();

		$this->ExplainSql->showExplainSQL(  $slowQueryThreshold = 0  );

	}

}

?>


前のバージョンだと、発行されたクエリの取得を、むりやり適当なモデルを使って取得してたんですが、今回のバージョンから発行されたクエリの取得方法を変えました。さらにconfig/database.phpで$dafault以外のDBも使ってる場合でも動くようになってると思います(SelectだけスレーブDBを見ている場合とか)。
下記のmcurryさんソースコードを参考にしました。
http://github.com/mcurry/cakephp/tree/master/plugins/sql_log