Merge branch 'MDL-52763-30' of git://github.com/danpoltawski/moodle into MOODLE_30_STABLE
[moodle.git] / lib / adodb / adodb-perf.inc.php
blob6a9867882374bba04ace0c6a7bd30796b44f48a2
1 <?php
2 /*
3 @version v5.20.1 06-Dec-2015
4 @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
5 @copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
6 Released under both BSD license and Lesser GPL library license.
7 Whenever there is any discrepancy between the two licenses,
8 the BSD license will take precedence. See License.txt.
9 Set tabs to 4 for best viewing.
11 Latest version is available at http://adodb.sourceforge.net
13 Library for basic performance monitoring and tuning.
15 My apologies if you see code mixed with presentation. The presentation suits
16 my needs. If you want to separate code from presentation, be my guest. Patches
17 are welcome.
21 if (!defined('ADODB_DIR')) include_once(dirname(__FILE__).'/adodb.inc.php');
22 include_once(ADODB_DIR.'/tohtml.inc.php');
24 define( 'ADODB_OPT_HIGH', 2);
25 define( 'ADODB_OPT_LOW', 1);
27 global $ADODB_PERF_MIN;
28 $ADODB_PERF_MIN = 0.05; // log only if >= minimum number of secs to run
31 // returns in K the memory of current process, or 0 if not known
32 function adodb_getmem()
34 if (function_exists('memory_get_usage'))
35 return (integer) ((memory_get_usage()+512)/1024);
37 $pid = getmypid();
39 if ( strncmp(strtoupper(PHP_OS),'WIN',3)==0) {
40 $output = array();
42 exec('tasklist /FI "PID eq ' . $pid. '" /FO LIST', $output);
43 return substr($output[5], strpos($output[5], ':') + 1);
46 /* Hopefully UNIX */
47 exec("ps --pid $pid --no-headers -o%mem,size", $output);
48 if (sizeof($output) == 0) return 0;
50 $memarr = explode(' ',$output[0]);
51 if (sizeof($memarr)>=2) return (integer) $memarr[1];
53 return 0;
56 // avoids localization problems where , is used instead of .
57 function adodb_round($n,$prec)
59 return number_format($n, $prec, '.', '');
62 /* obsolete: return microtime value as a float. Retained for backward compat */
63 function adodb_microtime()
65 return microtime(true);
68 /* sql code timing */
69 function adodb_log_sql(&$connx,$sql,$inputarr)
71 $perf_table = adodb_perf::table();
72 $connx->fnExecute = false;
73 $a0 = microtime(true);
74 $rs = $connx->Execute($sql,$inputarr);
75 $a1 = microtime(true);
77 if (!empty($connx->_logsql) && (empty($connx->_logsqlErrors) || !$rs)) {
78 global $ADODB_LOG_CONN;
80 if (!empty($ADODB_LOG_CONN)) {
81 $conn = $ADODB_LOG_CONN;
82 if ($conn->databaseType != $connx->databaseType)
83 $prefix = '/*dbx='.$connx->databaseType .'*/ ';
84 else
85 $prefix = '';
86 } else {
87 $conn = $connx;
88 $prefix = '';
91 $conn->_logsql = false; // disable logsql error simulation
92 $dbT = $conn->databaseType;
94 $time = $a1 - $a0;
96 if (!$rs) {
97 $errM = $connx->ErrorMsg();
98 $errN = $connx->ErrorNo();
99 $conn->lastInsID = 0;
100 $tracer = substr('ERROR: '.htmlspecialchars($errM),0,250);
101 } else {
102 $tracer = '';
103 $errM = '';
104 $errN = 0;
105 $dbg = $conn->debug;
106 $conn->debug = false;
107 if (!is_object($rs) || $rs->dataProvider == 'empty')
108 $conn->_affected = $conn->affected_rows(true);
109 $conn->lastInsID = @$conn->Insert_ID();
110 $conn->debug = $dbg;
112 if (isset($_SERVER['HTTP_HOST'])) {
113 $tracer .= '<br>'.$_SERVER['HTTP_HOST'];
114 if (isset($_SERVER['PHP_SELF'])) $tracer .= htmlspecialchars($_SERVER['PHP_SELF']);
115 } else
116 if (isset($_SERVER['PHP_SELF'])) $tracer .= '<br>'.htmlspecialchars($_SERVER['PHP_SELF']);
117 //$tracer .= (string) adodb_backtrace(false);
119 $tracer = (string) substr($tracer,0,500);
121 if (is_array($inputarr)) {
122 if (is_array(reset($inputarr))) $params = 'Array sizeof='.sizeof($inputarr);
123 else {
124 // Quote string parameters so we can see them in the
125 // performance stats. This helps spot disabled indexes.
126 $xar_params = $inputarr;
127 foreach ($xar_params as $xar_param_key => $xar_param) {
128 if (gettype($xar_param) == 'string')
129 $xar_params[$xar_param_key] = '"' . $xar_param . '"';
131 $params = implode(', ', $xar_params);
132 if (strlen($params) >= 3000) $params = substr($params, 0, 3000);
134 } else {
135 $params = '';
138 if (is_array($sql)) $sql = $sql[0];
139 if ($prefix) $sql = $prefix.$sql;
140 $arr = array('b'=>strlen($sql).'.'.crc32($sql),
141 'c'=>substr($sql,0,3900), 'd'=>$params,'e'=>$tracer,'f'=>adodb_round($time,6));
142 //var_dump($arr);
143 $saved = $conn->debug;
144 $conn->debug = 0;
146 $d = $conn->sysTimeStamp;
147 if (empty($d)) $d = date("'Y-m-d H:i:s'");
148 if ($conn->dataProvider == 'oci8' && $dbT != 'oci8po') {
149 $isql = "insert into $perf_table values($d,:b,:c,:d,:e,:f)";
150 } else if ($dbT == 'odbc_mssql' || $dbT == 'informix' || strncmp($dbT,'odbtp',4)==0) {
151 $timer = $arr['f'];
152 if ($dbT == 'informix') $sql2 = substr($sql2,0,230);
154 $sql1 = $conn->qstr($arr['b']);
155 $sql2 = $conn->qstr($arr['c']);
156 $params = $conn->qstr($arr['d']);
157 $tracer = $conn->qstr($arr['e']);
159 $isql = "insert into $perf_table (created,sql0,sql1,params,tracer,timer) values($d,$sql1,$sql2,$params,$tracer,$timer)";
160 if ($dbT == 'informix') $isql = str_replace(chr(10),' ',$isql);
161 $arr = false;
162 } else {
163 if ($dbT == 'db2') $arr['f'] = (float) $arr['f'];
164 $isql = "insert into $perf_table (created,sql0,sql1,params,tracer,timer) values( $d,?,?,?,?,?)";
167 global $ADODB_PERF_MIN;
168 if ($errN != 0 || $time >= $ADODB_PERF_MIN) {
169 $ok = $conn->Execute($isql,$arr);
170 } else
171 $ok = true;
173 $conn->debug = $saved;
175 if ($ok) {
176 $conn->_logsql = true;
177 } else {
178 $err2 = $conn->ErrorMsg();
179 $conn->_logsql = true; // enable logsql error simulation
180 $perf = NewPerfMonitor($conn);
181 if ($perf) {
182 if ($perf->CreateLogTable()) $ok = $conn->Execute($isql,$arr);
183 } else {
184 $ok = $conn->Execute("create table $perf_table (
185 created varchar(50),
186 sql0 varchar(250),
187 sql1 varchar(4000),
188 params varchar(3000),
189 tracer varchar(500),
190 timer decimal(16,6))");
192 if (!$ok) {
193 ADOConnection::outp( "<p><b>LOGSQL Insert Failed</b>: $isql<br>$err2</p>");
194 $conn->_logsql = false;
197 $connx->_errorMsg = $errM;
198 $connx->_errorCode = $errN;
200 $connx->fnExecute = 'adodb_log_sql';
201 return $rs;
206 The settings data structure is an associative array that database parameter per element.
208 Each database parameter element in the array is itself an array consisting of:
210 0: category code, used to group related db parameters
211 1: either
212 a. sql string to retrieve value, eg. "select value from v\$parameter where name='db_block_size'",
213 b. array holding sql string and field to look for, e.g. array('show variables','table_cache'),
214 c. a string prefixed by =, then a PHP method of the class is invoked,
215 e.g. to invoke $this->GetIndexValue(), set this array element to '=GetIndexValue',
216 2: description of the database parameter
219 class adodb_perf {
220 var $conn;
221 var $color = '#F0F0F0';
222 var $table = '<table border=1 bgcolor=white>';
223 var $titles = '<tr><td><b>Parameter</b></td><td><b>Value</b></td><td><b>Description</b></td></tr>';
224 var $warnRatio = 90;
225 var $tablesSQL = false;
226 var $cliFormat = "%32s => %s \r\n";
227 var $sql1 = 'sql1'; // used for casting sql1 to text for mssql
228 var $explain = true;
229 var $helpurl = '<a href="http://adodb.sourceforge.net/docs-adodb.htm#logsql">LogSQL help</a>';
230 var $createTableSQL = false;
231 var $maxLength = 2000;
233 // Sets the tablename to be used
234 static function table($newtable = false)
236 static $_table;
238 if (!empty($newtable)) $_table = $newtable;
239 if (empty($_table)) $_table = 'adodb_logsql';
240 return $_table;
243 // returns array with info to calculate CPU Load
244 function _CPULoad()
248 cpu 524152 2662 2515228 336057010
249 cpu0 264339 1408 1257951 168025827
250 cpu1 259813 1254 1257277 168031181
251 page 622307 25475680
252 swap 24 1891
253 intr 890153570 868093576 6 0 4 4 0 6 1 2 0 0 0 124 0 8098760 2 13961053 0 0 0 0 0 0 0 0 0 0 0 0 0 16 16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
254 disk_io: (3,0):(3144904,54369,610378,3090535,50936192) (3,1):(3630212,54097,633016,3576115,50951320)
255 ctxt 66155838
256 btime 1062315585
257 processes 69293
260 // Algorithm is taken from
261 // http://social.technet.microsoft.com/Forums/en-US/winservergen/thread/414b0e1b-499c-411e-8a02-6a12e339c0f1/
262 if (strncmp(PHP_OS,'WIN',3)==0) {
263 if (PHP_VERSION == '5.0.0') return false;
264 if (PHP_VERSION == '5.0.1') return false;
265 if (PHP_VERSION == '5.0.2') return false;
266 if (PHP_VERSION == '5.0.3') return false;
267 if (PHP_VERSION == '4.3.10') return false; # see http://bugs.php.net/bug.php?id=31737
269 static $FAIL = false;
270 if ($FAIL) return false;
272 $objName = "winmgmts:{impersonationLevel=impersonate}!\\\\.\\root\\CIMV2";
273 $myQuery = "SELECT * FROM Win32_PerfFormattedData_PerfOS_Processor WHERE Name = '_Total'";
275 try {
276 @$objWMIService = new COM($objName);
277 if (!$objWMIService) {
278 $FAIL = true;
279 return false;
282 $info[0] = -1;
283 $info[1] = 0;
284 $info[2] = 0;
285 $info[3] = 0;
286 foreach($objWMIService->ExecQuery($myQuery) as $objItem) {
287 $info[0] = $objItem->PercentProcessorTime();
290 } catch(Exception $e) {
291 $FAIL = true;
292 echo $e->getMessage();
293 return false;
296 return $info;
299 // Algorithm - Steve Blinch (BlitzAffe Online, http://www.blitzaffe.com)
300 $statfile = '/proc/stat';
301 if (!file_exists($statfile)) return false;
303 $fd = fopen($statfile,"r");
304 if (!$fd) return false;
306 $statinfo = explode("\n",fgets($fd, 1024));
307 fclose($fd);
308 foreach($statinfo as $line) {
309 $info = explode(" ",$line);
310 if($info[0]=="cpu") {
311 array_shift($info); // pop off "cpu"
312 if(!$info[0]) array_shift($info); // pop off blank space (if any)
313 return $info;
317 return false;
321 /* NOT IMPLEMENTED */
322 function MemInfo()
326 total: used: free: shared: buffers: cached:
327 Mem: 1055289344 917299200 137990144 0 165437440 599773184
328 Swap: 2146775040 11055104 2135719936
329 MemTotal: 1030556 kB
330 MemFree: 134756 kB
331 MemShared: 0 kB
332 Buffers: 161560 kB
333 Cached: 581384 kB
334 SwapCached: 4332 kB
335 Active: 494468 kB
336 Inact_dirty: 322856 kB
337 Inact_clean: 24256 kB
338 Inact_target: 168316 kB
339 HighTotal: 131064 kB
340 HighFree: 1024 kB
341 LowTotal: 899492 kB
342 LowFree: 133732 kB
343 SwapTotal: 2096460 kB
344 SwapFree: 2085664 kB
345 Committed_AS: 348732 kB
351 Remember that this is client load, not db server load!
353 var $_lastLoad;
354 function CPULoad()
356 $info = $this->_CPULoad();
357 if (!$info) return false;
359 if (strncmp(PHP_OS,'WIN',3)==0) {
360 return (integer) $info[0];
361 }else {
362 if (empty($this->_lastLoad)) {
363 sleep(1);
364 $this->_lastLoad = $info;
365 $info = $this->_CPULoad();
368 $last = $this->_lastLoad;
369 $this->_lastLoad = $info;
371 $d_user = $info[0] - $last[0];
372 $d_nice = $info[1] - $last[1];
373 $d_system = $info[2] - $last[2];
374 $d_idle = $info[3] - $last[3];
376 //printf("Delta - User: %f Nice: %f System: %f Idle: %f<br>",$d_user,$d_nice,$d_system,$d_idle);
378 $total=$d_user+$d_nice+$d_system+$d_idle;
379 if ($total<1) $total=1;
380 return 100*($d_user+$d_nice+$d_system)/$total;
384 function Tracer($sql)
386 $perf_table = adodb_perf::table();
387 $saveE = $this->conn->fnExecute;
388 $this->conn->fnExecute = false;
390 global $ADODB_FETCH_MODE;
391 $save = $ADODB_FETCH_MODE;
392 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
393 if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
395 $sqlq = $this->conn->qstr($sql);
396 $arr = $this->conn->GetArray(
397 "select count(*),tracer
398 from $perf_table where sql1=$sqlq
399 group by tracer
400 order by 1 desc");
401 $s = '';
402 if ($arr) {
403 $s .= '<h3>Scripts Affected</h3>';
404 foreach($arr as $k) {
405 $s .= sprintf("%4d",$k[0]).' &nbsp; '.strip_tags($k[1]).'<br>';
409 if (isset($savem)) $this->conn->SetFetchMode($savem);
410 $ADODB_CACHE_MODE = $save;
411 $this->conn->fnExecute = $saveE;
412 return $s;
416 Explain Plan for $sql.
417 If only a snippet of the $sql is passed in, then $partial will hold the crc32 of the
418 actual sql.
420 function Explain($sql,$partial=false)
422 return false;
425 function InvalidSQL($numsql = 10)
428 if (isset($_GET['sql'])) return;
429 $s = '<h3>Invalid SQL</h3>';
430 $saveE = $this->conn->fnExecute;
431 $this->conn->fnExecute = false;
432 $perf_table = adodb_perf::table();
433 $rs = $this->conn->SelectLimit("select distinct count(*),sql1,tracer as error_msg from $perf_table where tracer like 'ERROR:%' group by sql1,tracer order by 1 desc",$numsql);//,$numsql);
434 $this->conn->fnExecute = $saveE;
435 if ($rs) {
436 $s .= rs2html($rs,false,false,false,false);
437 } else
438 return "<p>$this->helpurl. ".$this->conn->ErrorMsg()."</p>";
440 return $s;
445 This script identifies the longest running SQL
447 function _SuspiciousSQL($numsql = 10)
449 global $ADODB_FETCH_MODE;
451 $perf_table = adodb_perf::table();
452 $saveE = $this->conn->fnExecute;
453 $this->conn->fnExecute = false;
455 if (isset($_GET['exps']) && isset($_GET['sql'])) {
456 $partial = !empty($_GET['part']);
457 echo "<a name=explain></a>".$this->Explain($_GET['sql'],$partial)."\n";
460 if (isset($_GET['sql'])) return;
461 $sql1 = $this->sql1;
463 $save = $ADODB_FETCH_MODE;
464 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
465 if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
466 //$this->conn->debug=1;
467 $rs = $this->conn->SelectLimit(
468 "select avg(timer) as avg_timer,$sql1,count(*),max(timer) as max_timer,min(timer) as min_timer
469 from $perf_table
470 where {$this->conn->upperCase}({$this->conn->substr}(sql0,1,5)) not in ('DROP ','INSER','COMMI','CREAT')
471 and (tracer is null or tracer not like 'ERROR:%')
472 group by sql1
473 order by 1 desc",$numsql);
474 if (isset($savem)) $this->conn->SetFetchMode($savem);
475 $ADODB_FETCH_MODE = $save;
476 $this->conn->fnExecute = $saveE;
478 if (!$rs) return "<p>$this->helpurl. ".$this->conn->ErrorMsg()."</p>";
479 $s = "<h3>Suspicious SQL</h3>
480 <font size=1>The following SQL have high average execution times</font><br>
481 <table border=1 bgcolor=white><tr><td><b>Avg Time</b><td><b>Count</b><td><b>SQL</b><td><b>Max</b><td><b>Min</b></tr>\n";
482 $max = $this->maxLength;
483 while (!$rs->EOF) {
484 $sql = $rs->fields[1];
485 $raw = urlencode($sql);
486 if (strlen($raw)>$max-100) {
487 $sql2 = substr($sql,0,$max-500);
488 $raw = urlencode($sql2).'&part='.crc32($sql);
490 $prefix = "<a target=sql".rand()." href=\"?hidem=1&exps=1&sql=".$raw."&x#explain\">";
491 $suffix = "</a>";
492 if ($this->explain == false || strlen($prefix)>$max) {
493 $suffix = ' ... <i>String too long for GET parameter: '.strlen($prefix).'</i>';
494 $prefix = '';
496 $s .= "<tr><td>".adodb_round($rs->fields[0],6)."<td align=right>".$rs->fields[2]."<td><font size=-1>".$prefix.htmlspecialchars($sql).$suffix."</font>".
497 "<td>".$rs->fields[3]."<td>".$rs->fields[4]."</tr>";
498 $rs->MoveNext();
500 return $s."</table>";
504 function CheckMemory()
506 return '';
510 function SuspiciousSQL($numsql=10)
512 return adodb_perf::_SuspiciousSQL($numsql);
515 function ExpensiveSQL($numsql=10)
517 return adodb_perf::_ExpensiveSQL($numsql);
522 This reports the percentage of load on the instance due to the most
523 expensive few SQL statements. Tuning these statements can often
524 make huge improvements in overall system performance.
526 function _ExpensiveSQL($numsql = 10)
528 global $ADODB_FETCH_MODE;
530 $perf_table = adodb_perf::table();
531 $saveE = $this->conn->fnExecute;
532 $this->conn->fnExecute = false;
534 if (isset($_GET['expe']) && isset($_GET['sql'])) {
535 $partial = !empty($_GET['part']);
536 echo "<a name=explain></a>".$this->Explain($_GET['sql'],$partial)."\n";
539 if (isset($_GET['sql'])) return;
541 $sql1 = $this->sql1;
542 $save = $ADODB_FETCH_MODE;
543 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
544 if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
546 $rs = $this->conn->SelectLimit(
547 "select sum(timer) as total,$sql1,count(*),max(timer) as max_timer,min(timer) as min_timer
548 from $perf_table
549 where {$this->conn->upperCase}({$this->conn->substr}(sql0,1,5)) not in ('DROP ','INSER','COMMI','CREAT')
550 and (tracer is null or tracer not like 'ERROR:%')
551 group by sql1
552 having count(*)>1
553 order by 1 desc",$numsql);
554 if (isset($savem)) $this->conn->SetFetchMode($savem);
555 $this->conn->fnExecute = $saveE;
556 $ADODB_FETCH_MODE = $save;
557 if (!$rs) return "<p>$this->helpurl. ".$this->conn->ErrorMsg()."</p>";
558 $s = "<h3>Expensive SQL</h3>
559 <font size=1>Tuning the following SQL could reduce the server load substantially</font><br>
560 <table border=1 bgcolor=white><tr><td><b>Load</b><td><b>Count</b><td><b>SQL</b><td><b>Max</b><td><b>Min</b></tr>\n";
561 $max = $this->maxLength;
562 while (!$rs->EOF) {
563 $sql = $rs->fields[1];
564 $raw = urlencode($sql);
565 if (strlen($raw)>$max-100) {
566 $sql2 = substr($sql,0,$max-500);
567 $raw = urlencode($sql2).'&part='.crc32($sql);
569 $prefix = "<a target=sqle".rand()." href=\"?hidem=1&expe=1&sql=".$raw."&x#explain\">";
570 $suffix = "</a>";
571 if($this->explain == false || strlen($prefix>$max)) {
572 $prefix = '';
573 $suffix = '';
575 $s .= "<tr><td>".adodb_round($rs->fields[0],6)."<td align=right>".$rs->fields[2]."<td><font size=-1>".$prefix.htmlspecialchars($sql).$suffix."</font>".
576 "<td>".$rs->fields[3]."<td>".$rs->fields[4]."</tr>";
577 $rs->MoveNext();
579 return $s."</table>";
583 Raw function to return parameter value from $settings.
585 function DBParameter($param)
587 if (empty($this->settings[$param])) return false;
588 $sql = $this->settings[$param][1];
589 return $this->_DBParameter($sql);
593 Raw function returning array of poll paramters
595 function PollParameters()
597 $arr[0] = (float)$this->DBParameter('data cache hit ratio');
598 $arr[1] = (float)$this->DBParameter('data reads');
599 $arr[2] = (float)$this->DBParameter('data writes');
600 $arr[3] = (integer) $this->DBParameter('current connections');
601 return $arr;
605 Low-level Get Database Parameter
607 function _DBParameter($sql)
609 $savelog = $this->conn->LogSQL(false);
610 if (is_array($sql)) {
611 global $ADODB_FETCH_MODE;
613 $sql1 = $sql[0];
614 $key = $sql[1];
615 if (sizeof($sql)>2) $pos = $sql[2];
616 else $pos = 1;
617 if (sizeof($sql)>3) $coef = $sql[3];
618 else $coef = false;
619 $ret = false;
620 $save = $ADODB_FETCH_MODE;
621 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
622 if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
624 $rs = $this->conn->Execute($sql1);
626 if (isset($savem)) $this->conn->SetFetchMode($savem);
627 $ADODB_FETCH_MODE = $save;
628 if ($rs) {
629 while (!$rs->EOF) {
630 $keyf = reset($rs->fields);
631 if (trim($keyf) == $key) {
632 $ret = $rs->fields[$pos];
633 if ($coef) $ret *= $coef;
634 break;
636 $rs->MoveNext();
638 $rs->Close();
640 $this->conn->LogSQL($savelog);
641 return $ret;
642 } else {
643 if (strncmp($sql,'=',1) == 0) {
644 $fn = substr($sql,1);
645 return $this->$fn();
647 $sql = str_replace('$DATABASE',$this->conn->database,$sql);
648 $ret = $this->conn->GetOne($sql);
649 $this->conn->LogSQL($savelog);
651 return $ret;
656 Warn if cache ratio falls below threshold. Displayed in "Description" column.
658 function WarnCacheRatio($val)
660 if ($val < $this->warnRatio)
661 return '<font color=red><b>Cache ratio should be at least '.$this->warnRatio.'%</b></font>';
662 else return '';
665 function clearsql()
667 $perf_table = adodb_perf::table();
668 $this->conn->Execute("delete from $perf_table where created<".$this->conn->sysTimeStamp);
670 /***********************************************************************************************/
671 // HIGH LEVEL UI FUNCTIONS
672 /***********************************************************************************************/
675 function UI($pollsecs=5)
677 global $ADODB_LOG_CONN;
679 $perf_table = adodb_perf::table();
680 $conn = $this->conn;
682 $app = $conn->host;
683 if ($conn->host && $conn->database) $app .= ', db=';
684 $app .= $conn->database;
686 if ($app) $app .= ', ';
687 $savelog = $this->conn->LogSQL(false);
688 $info = $conn->ServerInfo();
689 if (isset($_GET['clearsql'])) {
690 $this->clearsql();
692 $this->conn->LogSQL($savelog);
694 // magic quotes
696 if (isset($_GET['sql']) && get_magic_quotes_gpc()) {
697 $_GET['sql'] = $_GET['sql'] = str_replace(array("\\'",'\"'),array("'",'"'),$_GET['sql']);
700 if (!isset($_SESSION['ADODB_PERF_SQL'])) $nsql = $_SESSION['ADODB_PERF_SQL'] = 10;
701 else $nsql = $_SESSION['ADODB_PERF_SQL'];
703 $app .= $info['description'];
706 if (isset($_GET['do'])) $do = $_GET['do'];
707 else if (isset($_POST['do'])) $do = $_POST['do'];
708 else if (isset($_GET['sql'])) $do = 'viewsql';
709 else $do = 'stats';
711 if (isset($_GET['nsql'])) {
712 if ($_GET['nsql'] > 0) $nsql = $_SESSION['ADODB_PERF_SQL'] = (integer) $_GET['nsql'];
714 echo "<title>ADOdb Performance Monitor on $app</title><body bgcolor=white>";
715 if ($do == 'viewsql') $form = "<td><form># SQL:<input type=hidden value=viewsql name=do> <input type=text size=4 name=nsql value=$nsql><input type=submit value=Go></td></form>";
716 else $form = "<td>&nbsp;</td>";
718 $allowsql = !defined('ADODB_PERF_NO_RUN_SQL');
719 global $ADODB_PERF_MIN;
720 $app .= " (Min sql timing \$ADODB_PERF_MIN=$ADODB_PERF_MIN secs)";
722 if (empty($_GET['hidem']))
723 echo "<table border=1 width=100% bgcolor=lightyellow><tr><td colspan=2>
724 <b><a href=http://adodb.sourceforge.net/?perf=1>ADOdb</a> Performance Monitor</b> <font size=1>for $app</font></tr><tr><td>
725 <a href=?do=stats><b>Performance Stats</b></a> &nbsp; <a href=?do=viewsql><b>View SQL</b></a>
726 &nbsp; <a href=?do=tables><b>View Tables</b></a> &nbsp; <a href=?do=poll><b>Poll Stats</b></a>",
727 $allowsql ? ' &nbsp; <a href=?do=dosql><b>Run SQL</b></a>' : '',
728 "$form",
729 "</tr></table>";
732 switch ($do) {
733 default:
734 case 'stats':
735 if (empty($ADODB_LOG_CONN))
736 echo "<p>&nbsp; <a href=\"?do=viewsql&clearsql=1\">Clear SQL Log</a><br>";
737 echo $this->HealthCheck();
738 //$this->conn->debug=1;
739 echo $this->CheckMemory();
740 break;
741 case 'poll':
742 $self = htmlspecialchars($_SERVER['PHP_SELF']);
743 echo "<iframe width=720 height=80%
744 src=\"{$self}?do=poll2&hidem=1\"></iframe>";
745 break;
746 case 'poll2':
747 echo "<pre>";
748 $this->Poll($pollsecs);
749 break;
751 case 'dosql':
752 if (!$allowsql) break;
754 $this->DoSQLForm();
755 break;
756 case 'viewsql':
757 if (empty($_GET['hidem']))
758 echo "&nbsp; <a href=\"?do=viewsql&clearsql=1\">Clear SQL Log</a><br>";
759 echo($this->SuspiciousSQL($nsql));
760 echo($this->ExpensiveSQL($nsql));
761 echo($this->InvalidSQL($nsql));
762 break;
763 case 'tables':
764 echo $this->Tables(); break;
766 global $ADODB_vers;
767 echo "<p><div align=center><font size=1>$ADODB_vers Sponsored by <a href=http://phplens.com/>phpLens</a></font></div>";
771 Runs in infinite loop, returning real-time statistics
773 function Poll($secs=5)
775 $this->conn->fnExecute = false;
776 //$this->conn->debug=1;
777 if ($secs <= 1) $secs = 1;
778 echo "Accumulating statistics, every $secs seconds...\n";flush();
779 $arro = $this->PollParameters();
780 $cnt = 0;
781 set_time_limit(0);
782 sleep($secs);
783 while (1) {
785 $arr = $this->PollParameters();
787 $hits = sprintf('%2.2f',$arr[0]);
788 $reads = sprintf('%12.4f',($arr[1]-$arro[1])/$secs);
789 $writes = sprintf('%12.4f',($arr[2]-$arro[2])/$secs);
790 $sess = sprintf('%5d',$arr[3]);
792 $load = $this->CPULoad();
793 if ($load !== false) {
794 $oslabel = 'WS-CPU%';
795 $osval = sprintf(" %2.1f ",(float) $load);
796 }else {
797 $oslabel = '';
798 $osval = '';
800 if ($cnt % 10 == 0) echo " Time ".$oslabel." Hit% Sess Reads/s Writes/s\n";
801 $cnt += 1;
802 echo date('H:i:s').' '.$osval."$hits $sess $reads $writes\n";
803 flush();
805 if (connection_aborted()) return;
807 sleep($secs);
808 $arro = $arr;
813 Returns basic health check in a command line interface
815 function HealthCheckCLI()
817 return $this->HealthCheck(true);
822 Returns basic health check as HTML
824 function HealthCheck($cli=false)
826 $saveE = $this->conn->fnExecute;
827 $this->conn->fnExecute = false;
828 if ($cli) $html = '';
829 else $html = $this->table.'<tr><td colspan=3><h3>'.$this->conn->databaseType.'</h3></td></tr>'.$this->titles;
831 $oldc = false;
832 $bgc = '';
833 foreach($this->settings as $name => $arr) {
834 if ($arr === false) break;
836 if (!is_string($name)) {
837 if ($cli) $html .= " -- $arr -- \n";
838 else $html .= "<tr bgcolor=$this->color><td colspan=3><i>$arr</i> &nbsp;</td></tr>";
839 continue;
842 if (!is_array($arr)) break;
843 $category = $arr[0];
844 $how = $arr[1];
845 if (sizeof($arr)>2) $desc = $arr[2];
846 else $desc = ' &nbsp; ';
849 if ($category == 'HIDE') continue;
851 $val = $this->_DBParameter($how);
853 if ($desc && strncmp($desc,"=",1) === 0) {
854 $fn = substr($desc,1);
855 $desc = $this->$fn($val);
858 if ($val === false) {
859 $m = $this->conn->ErrorMsg();
860 $val = "Error: $m";
861 } else {
862 if (is_numeric($val) && $val >= 256*1024) {
863 if ($val % (1024*1024) == 0) {
864 $val /= (1024*1024);
865 $val .= 'M';
866 } else if ($val % 1024 == 0) {
867 $val /= 1024;
868 $val .= 'K';
870 //$val = htmlspecialchars($val);
873 if ($category != $oldc) {
874 $oldc = $category;
875 //$bgc = ($bgc == ' bgcolor='.$this->color) ? ' bgcolor=white' : ' bgcolor='.$this->color;
877 if (strlen($desc)==0) $desc = '&nbsp;';
878 if (strlen($val)==0) $val = '&nbsp;';
879 if ($cli) {
880 $html .= str_replace('&nbsp;','',sprintf($this->cliFormat,strip_tags($name),strip_tags($val),strip_tags($desc)));
882 }else {
883 $html .= "<tr$bgc><td>".$name.'</td><td>'.$val.'</td><td>'.$desc."</td></tr>\n";
887 if (!$cli) $html .= "</table>\n";
888 $this->conn->fnExecute = $saveE;
890 return $html;
893 function Tables($orderby='1')
895 if (!$this->tablesSQL) return false;
897 $savelog = $this->conn->LogSQL(false);
898 $rs = $this->conn->Execute($this->tablesSQL.' order by '.$orderby);
899 $this->conn->LogSQL($savelog);
900 $html = rs2html($rs,false,false,false,false);
901 return $html;
905 function CreateLogTable()
907 if (!$this->createTableSQL) return false;
909 $table = $this->table();
910 $sql = str_replace('adodb_logsql',$table,$this->createTableSQL);
911 $savelog = $this->conn->LogSQL(false);
912 $ok = $this->conn->Execute($sql);
913 $this->conn->LogSQL($savelog);
914 return ($ok) ? true : false;
917 function DoSQLForm()
921 $PHP_SELF = htmlspecialchars($_SERVER['PHP_SELF']);
922 $sql = isset($_REQUEST['sql']) ? $_REQUEST['sql'] : '';
924 if (isset($_SESSION['phplens_sqlrows'])) $rows = $_SESSION['phplens_sqlrows'];
925 else $rows = 3;
927 if (isset($_REQUEST['SMALLER'])) {
928 $rows /= 2;
929 if ($rows < 3) $rows = 3;
930 $_SESSION['phplens_sqlrows'] = $rows;
932 if (isset($_REQUEST['BIGGER'])) {
933 $rows *= 2;
934 $_SESSION['phplens_sqlrows'] = $rows;
939 <form method="POST" action="<?php echo $PHP_SELF ?>">
940 <table><tr>
941 <td> Form size: <input type="submit" value=" &lt; " name="SMALLER"><input type="submit" value=" &gt; &gt; " name="BIGGER">
942 </td>
943 <td align=right>
944 <input type="submit" value=" Run SQL Below " name="RUN"><input type=hidden name=do value=dosql>
945 </td></tr>
946 <tr>
947 <td colspan=2><textarea rows=<?php print $rows; ?> name="sql" cols="80"><?php print htmlspecialchars($sql) ?></textarea>
948 </td>
949 </tr>
950 </table>
951 </form>
953 <?php
954 if (!isset($_REQUEST['sql'])) return;
956 $sql = $this->undomq(trim($sql));
957 if (substr($sql,strlen($sql)-1) === ';') {
958 $print = true;
959 $sqla = $this->SplitSQL($sql);
960 } else {
961 $print = false;
962 $sqla = array($sql);
964 foreach($sqla as $sqls) {
966 if (!$sqls) continue;
968 if ($print) {
969 print "<p>".htmlspecialchars($sqls)."</p>";
970 flush();
972 $savelog = $this->conn->LogSQL(false);
973 $rs = $this->conn->Execute($sqls);
974 $this->conn->LogSQL($savelog);
975 if ($rs && is_object($rs) && !$rs->EOF) {
976 rs2html($rs);
977 while ($rs->NextRecordSet()) {
978 print "<table width=98% bgcolor=#C0C0FF><tr><td>&nbsp;</td></tr></table>";
979 rs2html($rs);
981 } else {
982 $e1 = (integer) $this->conn->ErrorNo();
983 $e2 = $this->conn->ErrorMsg();
984 if (($e1) || ($e2)) {
985 if (empty($e1)) $e1 = '-1'; // postgresql fix
986 print ' &nbsp; '.$e1.': '.$e2;
987 } else {
988 print "<p>No Recordset returned<br></p>";
991 } // foreach
994 function SplitSQL($sql)
996 $arr = explode(';',$sql);
997 return $arr;
1000 function undomq($m)
1002 if (get_magic_quotes_gpc()) {
1003 // undo the damage
1004 $m = str_replace('\\\\','\\',$m);
1005 $m = str_replace('\"','"',$m);
1006 $m = str_replace('\\\'','\'',$m);
1008 return $m;
1012 /************************************************************************/
1015 * Reorganise multiple table-indices/statistics/..
1016 * OptimizeMode could be given by last Parameter
1018 * @example
1019 * <pre>
1020 * optimizeTables( 'tableA');
1021 * </pre>
1022 * <pre>
1023 * optimizeTables( 'tableA', 'tableB', 'tableC');
1024 * </pre>
1025 * <pre>
1026 * optimizeTables( 'tableA', 'tableB', ADODB_OPT_LOW);
1027 * </pre>
1029 * @param string table name of the table to optimize
1030 * @param int mode optimization-mode
1031 * <code>ADODB_OPT_HIGH</code> for full optimization
1032 * <code>ADODB_OPT_LOW</code> for CPU-less optimization
1033 * Default is LOW <code>ADODB_OPT_LOW</code>
1034 * @author Markus Staab
1035 * @return Returns <code>true</code> on success and <code>false</code> on error
1037 function OptimizeTables()
1039 $args = func_get_args();
1040 $numArgs = func_num_args();
1042 if ( $numArgs == 0) return false;
1044 $mode = ADODB_OPT_LOW;
1045 $lastArg = $args[ $numArgs - 1];
1046 if ( !is_string($lastArg)) {
1047 $mode = $lastArg;
1048 unset( $args[ $numArgs - 1]);
1051 foreach( $args as $table) {
1052 $this->optimizeTable( $table, $mode);
1057 * Reorganise the table-indices/statistics/.. depending on the given mode.
1058 * Default Implementation throws an error.
1060 * @param string table name of the table to optimize
1061 * @param int mode optimization-mode
1062 * <code>ADODB_OPT_HIGH</code> for full optimization
1063 * <code>ADODB_OPT_LOW</code> for CPU-less optimization
1064 * Default is LOW <code>ADODB_OPT_LOW</code>
1065 * @author Markus Staab
1066 * @return Returns <code>true</code> on success and <code>false</code> on error
1068 function OptimizeTable( $table, $mode = ADODB_OPT_LOW)
1070 ADOConnection::outp( sprintf( "<p>%s: '%s' not implemented for driver '%s'</p>", __CLASS__, __FUNCTION__, $this->conn->databaseType));
1071 return false;
1075 * Reorganise current database.
1076 * Default implementation loops over all <code>MetaTables()</code> and
1077 * optimize each using <code>optmizeTable()</code>
1079 * @author Markus Staab
1080 * @return Returns <code>true</code> on success and <code>false</code> on error
1082 function optimizeDatabase()
1084 $conn = $this->conn;
1085 if ( !$conn) return false;
1087 $tables = $conn->MetaTables( 'TABLES');
1088 if ( !$tables ) return false;
1090 foreach( $tables as $table) {
1091 if ( !$this->optimizeTable( $table)) {
1092 return false;
1096 return true;
1098 // end hack