24 $this->_logsEnabled =
true;
25 $this->_logsLevels = CLogger::LEVEL_PROFILE;
26 $this->_logsCategories =
'system.db.CDbCommand.*';
40 foreach ($timings as $timing) {
44 return $this->
render(dirname(__FILE__) .
'/../views/panels/db_bar.php', array(
46 'time' => number_format($time * 1000) .
' ms',
52 return $this->
render(dirname(__FILE__) .
'/../views/panels/db.php', array(
57 'connectionsCount' => count($this->data[
'connections']),
70 'time' => date(
'H:i:s.', $timing[3]) . sprintf(
'%03d', (
int)(($timing[3] - (
int)$timing[3]) * 1000)),
71 'duration' => sprintf(
'%.1f ms', $timing[4] * 1000),
72 'procedure' => $this->
formatSql($timing[1], $this->insertParamValues),
86 'procedure' => $item[0],
88 'total' => sprintf(
'%.1f ms', $item[2] * 1000),
89 'avg' => sprintf(
'%.1f ms', $item[2] * 1000 / $item[1]),
90 'min' => sprintf(
'%.1f ms', $item[3] * 1000),
91 'max' => sprintf(
'%.1f ms', $item[4] * 1000),
102 $connections = array();
103 foreach ($this->data[
'connections'] as $id => $connection) {
104 if ($connection[
'driver'] ==
'mysql' && isset($connection[
'info'])) {
105 foreach (explode(
' ', $connection[
'info']) as $line) {
106 list($key, $value) = explode(
': ', $line, 2);
107 $connection[$key] = $value;
109 unset($connection[
'info']);
111 $connections[$id] = $connection;
124 if ($this->_timings !== null) {
127 $messages = $this->data[
'messages'];
130 foreach ($messages as
$i => $log) {
131 list($token, , $category, $timestamp) = $log;
133 if (strpos($token,
'begin:') === 0) {
134 $log[0] = $token = substr($token, 6);
136 } elseif (strpos($token,
'end:') === 0) {
137 $log[0] = $token = substr($token, 4);
138 if (($last = array_pop($stack)) !== null && $last[0] === $token) {
139 $timings[$last[4]] = array(count($stack), $token, $category, $last[3], $timestamp - $last[3]);
143 $now = microtime(
true);
144 while (($last = array_pop($stack)) !== null) {
145 $delta = $now - $last[3];
146 $timings[$last[4]] = array(count($stack), $last[0], $last[2], $last[3], $delta);
149 return $this->_timings = $timings;
160 if ($this->_resume !== null) {
165 $duration = $timing[4];
166 $query = $this->
formatSql($timing[1], $this->insertParamValues);
168 if (!isset($resume[$key])) {
169 $resume[$key] = array($query, 1, $duration, $duration, $duration);
172 $resume[$key][2] += $duration;
173 if ($resume[$key][3] > $duration) $resume[$key][3] = $duration;
174 if ($resume[$key][4] < $duration) $resume[$key][4] = $duration;
177 usort($resume, array($this,
'compareResume'));
178 return $this->_resume = $resume;
183 if ($a[2] == $b[2])
return 0;
184 return $a[2] < $b[2] ? 1 : -1;
195 $sqlStart = strpos($message,
'(') + 1;
196 $sqlEnd = strrpos($message ,
')');
197 $sql = substr($message, $sqlStart, $sqlEnd - $sqlStart);
198 if (strpos($sql,
'. Bound with ') !==
false) {
199 list($query, $params) = explode(
'. Bound with ', $sql);
200 if (!$insertParams)
return $query;
215 while (preg_match(
'/((?:\:[a-z0-9\.\_\-]+)|\d+)\s*\=\s*/i', $params, $m, PREG_OFFSET_CAPTURE, $pos)) {
216 $start = $m[0][1] + strlen($m[0][0]);
218 if (($params{$start} ==
'"') || ($params{$start} ==
"'")) {
219 $quote = $params{$start};
221 while (($pos = strpos($params, $quote, $pos + 1)) !==
false) {
223 while ($params{$pos - $slashes - 1} ==
'\\') $slashes++;
224 if ($slashes % 2 == 0) {
225 $binds[$key] = substr($params, $start, $pos - $start + 1);
230 } elseif (($end = strpos($params,
',', $start + 1)) !==
false) {
231 $binds[$key] = substr($params, $start, $end - $start);
234 $binds[$key] = substr($params, $start, strlen($params) - $start);
257 if (preg_match(
'/[`"\']/', $query, $m, PREG_OFFSET_CAPTURE, $pos)) {
263 if (($qend = strpos($query, $qchar, $qend + 1)) !==
false) {
264 while ($query{$qend - $sls - 1} ==
'\\') $sls++;
266 $qend = strlen($query) - 1;
269 $quote = substr($query, $qbegin, $qend - $qbegin + 1);
270 $token = substr($query, $pos, $qbegin - $pos);
273 $token = substr($query, $pos);
279 while (preg_match(
'/\:[a-z0-9\.\_\-]+|\?/i', $token, $m, PREG_OFFSET_CAPTURE, $tpos)) {
281 if ($key ==
'?') $key = $pind++;
282 if (isset($params[$key])) {
283 $value = $params[$key];
287 $subsql .= substr($token, $tpos, $m[0][1] - $tpos) . $value;
288 $tpos = $m[0][1] + strlen($m[0][0]);
290 $subsql .= substr($token, $tpos);
292 $sql .= $subsql . $quote;
293 }
while ($quote !==
'');
309 if ($this->_hl === null) {
310 $this->_hl = Yii::createComponent(array(
311 'class' =>
'CTextHighlighter',
313 'showLineNumbers' =>
false,
316 $html = $this->_hl->highlight($sql);
317 return strip_tags($html,
'<div>,<span>');
322 $connections = array();
323 foreach (Yii::app()->getComponents() as $id => $component) {
324 if ($component instanceof CDbConnection) {
326 $connections[$id] = array(
327 'class' => get_class($component),
328 'driver' => $component->getDriverName(),
331 $connections[$id][
'server'] = $component->getServerVersion();
332 $connections[$id][
'info'] = $component->getServerInfo();
333 }
catch (Exception $e) {}
338 'messages' => $this->
getLogs(),
339 'connections' => $connections,
351 if (preg_match(
'/^\s*SELECT/', $query)) {
353 case 'mysql':
return 'EXPLAIN ' . $query;
354 case 'pgsql':
return 'EXPLAIN ' . $query;
355 case 'sqlite':
return 'EXPLAIN QUERY PLAN ' . $query;
356 case 'oci':
return 'EXPLAIN PLAN FOR ' . $query;
368 public static function explain($query, $connection)
371 if ($procedure === null) {
372 throw new Exception(
'Explain not available');
374 switch ($connection->driverName) {
376 $connection->createCommand($procedure)->execute();
377 return $connection->createCommand(
'SELECT * FROM table(dbms_xplan.display)')->queryAll();
379 return $connection->createCommand($procedure)->queryAll();
390 $connections = array();
391 foreach ($this->data[
'connections'] as $name => $connection) {
392 if (self::getExplainQuery($query, $connection[
'driver']) !== null) {
393 $connections[$name] = $connection;