Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 401 and 402] [Versions 401 and 403]

   1  <?php
   2  /**
   3   * performance monitoring and tuning.
   4   *
   5   * This file is part of ADOdb, a Database Abstraction Layer library for PHP.
   6   *
   7   * @package ADOdb
   8   * @link https://adodb.org Project's web site and documentation
   9   * @link https://github.com/ADOdb/ADOdb Source code and issue tracker
  10   *
  11   * The ADOdb Library is dual-licensed, released under both the BSD 3-Clause
  12   * and the GNU Lesser General Public Licence (LGPL) v2.1 or, at your option,
  13   * any later version. This means you can use it in proprietary products.
  14   * See the LICENSE.md file distributed with this source code for details.
  15   * @license BSD-3-Clause
  16   * @license LGPL-2.1-or-later
  17   *
  18   * @copyright 2000-2013 John Lim
  19   * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community
  20   */
  21  
  22  if (!defined('ADODB_DIR')) include_once(dirname(__FILE__).'/adodb.inc.php');
  23  include_once (ADODB_DIR.'/tohtml.inc.php');
  24  
  25  define( 'ADODB_OPT_HIGH', 2);
  26  define( 'ADODB_OPT_LOW', 1);
  27  
  28  global $ADODB_PERF_MIN;
  29  $ADODB_PERF_MIN = 0.05; // log only if >= minimum number of secs to run
  30  
  31  
  32  // returns in K the memory of current process, or 0 if not known
  33  function adodb_getmem()
  34  {
  35  	 if (function_exists('memory_get_usage'))
  36  	 	 return (integer) ((memory_get_usage()+512)/1024);
  37  
  38  	 $pid = getmypid();
  39  
  40  	 if ( strncmp(strtoupper(PHP_OS),'WIN',3)==0) {
  41  	 	 $output = array();
  42  
  43  	 	 exec('tasklist /FI "PID eq ' . $pid. '" /FO LIST', $output);
  44  	 	 return substr($output[5], strpos($output[5], ':') + 1);
  45  	 }
  46  
  47  	 /* Hopefully UNIX */
  48  	 exec("ps --pid $pid --no-headers -o%mem,size", $output);
  49  	 if (sizeof($output) == 0) return 0;
  50  
  51  	 $memarr = explode(' ',$output[0]);
  52  	 if (sizeof($memarr)>=2) return (integer) $memarr[1];
  53  
  54  	 return 0;
  55  }
  56  
  57  // avoids localization problems where , is used instead of .
  58  function adodb_round($n,$prec)
  59  {
  60  	 return number_format($n, $prec, '.', '');
  61  }
  62  
  63  /* obsolete: return microtime value as a float. Retained for backward compat */
  64  function adodb_microtime()
  65  {
  66  	 return microtime(true);
  67  }
  68  
  69  /* sql code timing */
  70  function adodb_log_sql(&$connx,$sql,$inputarr)
  71  {
  72      $perf_table = adodb_perf::table();
  73  	 $connx->fnExecute = false;
  74  	 $a0 = microtime(true);
  75  	 $rs = $connx->Execute($sql,$inputarr);
  76  	 $a1 = microtime(true);
  77  
  78  	 if (!empty($connx->_logsql) && (empty($connx->_logsqlErrors) || !$rs)) {
  79  	 global $ADODB_LOG_CONN;
  80  
  81  	 	 if (!empty($ADODB_LOG_CONN)) {
  82  	 	 	 $conn = $ADODB_LOG_CONN;
  83  	 	 	 if ($conn->databaseType != $connx->databaseType)
  84  	 	 	 	 $prefix = '/*dbx='.$connx->databaseType .'*/ ';
  85  	 	 	 else
  86  	 	 	 	 $prefix = '';
  87  	 	 } else {
  88  	 	 	 $conn = $connx;
  89  	 	 	 $prefix = '';
  90  	 	 }
  91  
  92  	 	 $conn->_logsql = false; // disable logsql error simulation
  93  	 	 $dbT = $conn->databaseType;
  94  
  95  	 	 $time = $a1 - $a0;
  96  
  97  	 	 if (!$rs) {
  98  	 	 	 $errM = $connx->ErrorMsg();
  99  	 	 	 $errN = $connx->ErrorNo();
 100  	 	 	 $conn->lastInsID = 0;
 101  	 	 	 $tracer = substr('ERROR: '.htmlspecialchars($errM),0,250);
 102  	 	 } else {
 103  	 	 	 $tracer = '';
 104  	 	 	 $errM = '';
 105  	 	 	 $errN = 0;
 106  	 	 	 $dbg = $conn->debug;
 107  	 	 	 $conn->debug = false;
 108  	 	 	 if (!is_object($rs) || $rs->dataProvider == 'empty')
 109  	 	 	 	 $conn->_affected = $conn->affected_rows(true);
 110  	 	 	 $conn->lastInsID = @$conn->Insert_ID();
 111  	 	 	 $conn->debug = $dbg;
 112  	 	 }
 113  	 	 if (isset($_SERVER['HTTP_HOST'])) {
 114  	 	 	 $tracer .= '<br>'.$_SERVER['HTTP_HOST'];
 115  	 	 	 if (isset($_SERVER['PHP_SELF'])) $tracer .= htmlspecialchars($_SERVER['PHP_SELF']);
 116  	 	 } else
 117  	 	 	 if (isset($_SERVER['PHP_SELF'])) $tracer .= '<br>'.htmlspecialchars($_SERVER['PHP_SELF']);
 118  	 	 //$tracer .= (string) adodb_backtrace(false);
 119  
 120  	 	 $tracer = (string) substr($tracer,0,500);
 121  
 122  	 	 if (is_array($inputarr)) {
 123  	 	 	 if (is_array(reset($inputarr))) $params = 'Array sizeof='.sizeof($inputarr);
 124  	 	 	 else {
 125  	 	 	 	 // Quote string parameters so we can see them in the
 126  	 	 	 	 // performance stats. This helps spot disabled indexes.
 127  	 	 	 	 $xar_params = $inputarr;
 128  	 	 	 	 foreach ($xar_params as $xar_param_key => $xar_param) {
 129  	 	 	 	 	 if (gettype($xar_param) == 'string')
 130  	 	 	 	 	 $xar_params[$xar_param_key] = '"' . $xar_param . '"';
 131  	 	 	 	 }
 132  	 	 	 	 $params = implode(', ', $xar_params);
 133  	 	 	 	 if (strlen($params) >= 3000) $params = substr($params, 0, 3000);
 134  	 	 	 }
 135  	 	 } else {
 136  	 	 	 $params = '';
 137  	 	 }
 138  
 139  	 	 if (is_array($sql)) $sql = $sql[0];
 140  	 	 if ($prefix) $sql = $prefix.$sql;
 141  	 	 $arr = array('b'=>strlen($sql).'.'.crc32($sql),
 142  	 	 	 	 	 'c'=>substr($sql,0,3900), 'd'=>$params,'e'=>$tracer,'f'=>adodb_round($time,6));
 143  	 	 //var_dump($arr);
 144  	 	 $saved = $conn->debug;
 145  	 	 $conn->debug = 0;
 146  
 147  	 	 $d = $conn->sysTimeStamp;
 148  	 	 if (empty($d)) $d = date("'Y-m-d H:i:s'");
 149  	 	 if ($conn->dataProvider == 'oci8' && $dbT != 'oci8po') {
 150  	 	 	 $isql = "insert into $perf_table values($d,:b,:c,:d,:e,:f)";
 151  	 	 } else if ($dbT == 'mssqlnative' || $dbT == 'odbc_mssql' || $dbT == 'informix' || strncmp($dbT,'odbtp',4)==0) {
 152  	 	 	 $timer = $arr['f'];
 153  	 	 	 if ($dbT == 'informix') $sql2 = substr($sql2,0,230);
 154  
 155  	 	 	 $sql1 = $conn->qstr($arr['b']);
 156  	 	 	 $sql2 = $conn->qstr($arr['c']);
 157  	 	 	 $params = $conn->qstr($arr['d']);
 158  	 	 	 $tracer = $conn->qstr($arr['e']);
 159  
 160  	 	 	 $isql = "insert into $perf_table (created,sql0,sql1,params,tracer,timer) values($d,$sql1,$sql2,$params,$tracer,$timer)";
 161  	 	 	 if ($dbT == 'informix') $isql = str_replace(chr(10),' ',$isql);
 162  	 	 	 $arr = false;
 163  	 	 } else {
 164  	 	 	 if ($dbT == 'db2') $arr['f'] = (float) $arr['f'];
 165  	 	 	 $isql = "insert into $perf_table (created,sql0,sql1,params,tracer,timer) values( $d,?,?,?,?,?)";
 166  	 	 }
 167  
 168  	 	 global $ADODB_PERF_MIN;
 169  	 	 if ($errN != 0 || $time >= $ADODB_PERF_MIN) {
 170  	 	 	 if($conn instanceof ADODB_mysqli && $conn->_queryID) {
 171  	 	 	 	 mysqli_free_result($conn->_queryID);
 172  	 	 	 }
 173  	 	 	 $ok = $conn->Execute($isql,$arr);
 174  	 	 	 if($conn instanceof ADODB_mysqli && $conn->_queryID){
 175  	 	 	 	 mysqli_free_result($conn->_queryID);
 176              	 	 }
 177  	 	 } else
 178  	 	 	 $ok = true;
 179  
 180  	 	 $conn->debug = $saved;
 181  
 182  	 	 if ($ok) {
 183  	 	 	 $conn->_logsql = true;
 184  	 	 } else {
 185  	 	 	 $err2 = $conn->ErrorMsg();
 186  	 	 	 $conn->_logsql = true; // enable logsql error simulation
 187  	 	 	 $perf = NewPerfMonitor($conn);
 188  	 	 	 if ($perf) {
 189  	 	 	 	 if ($perf->CreateLogTable()) $ok = $conn->Execute($isql,$arr);
 190  	 	 	 } else {
 191  	 	 	 	 $ok = $conn->Execute("create table $perf_table (
 192  	 	 	 	 created varchar(50),
 193  	 	 	 	 sql0 varchar(250),
 194  	 	 	 	 sql1 varchar(4000),
 195  	 	 	 	 params varchar(3000),
 196  	 	 	 	 tracer varchar(500),
 197  	 	 	 	 timer decimal(16,6))");
 198  	 	 	 }
 199  	 	 	 if (!$ok) {
 200  	 	 	 	 ADOConnection::outp( "<p><b>LOGSQL Insert Failed</b>: $isql<br>$err2</p>");
 201  	 	 	 	 $conn->_logsql = false;
 202  	 	 	 }
 203  	 	 }
 204  	 	 $connx->_errorMsg = $errM;
 205  	 	 $connx->_errorCode = $errN;
 206  	 }
 207  	 $connx->fnExecute = 'adodb_log_sql';
 208  	 return $rs;
 209  }
 210  
 211  
 212  /*
 213  The settings data structure is an associative array that database parameter per element.
 214  
 215  Each database parameter element in the array is itself an array consisting of:
 216  
 217  0: category code, used to group related db parameters
 218  1: either
 219  	 a. sql string to retrieve value, eg. "select value from v\$parameter where name='db_block_size'",
 220  	 b. array holding sql string and field to look for, e.g. array('show variables','table_cache'),
 221  	 c. a string prefixed by =, then a PHP method of the class is invoked,
 222  	 	 e.g. to invoke $this->GetIndexValue(), set this array element to '=GetIndexValue',
 223  2: description of the database parameter
 224  */
 225  
 226  class adodb_perf {
 227  	 var $conn;
 228  	 var $color = '#F0F0F0';
 229  	 var $table = '<table border=1 bgcolor=white>';
 230  	 var $titles = '<tr><td><b>Parameter</b></td><td><b>Value</b></td><td><b>Description</b></td></tr>';
 231  	 var $warnRatio = 90;
 232  	 var $tablesSQL = false;
 233  	 var $cliFormat = "%32s => %s \r\n";
 234  	 var $sql1 = 'sql1';  // used for casting sql1 to text for mssql
 235  	 var $explain = true;
 236  	 var $helpurl = '<a href="https://adodb.org/dokuwiki/doku.php?id=v5:performance:logsql">LogSQL help</a>';
 237  	 var $createTableSQL = false;
 238  	 var $maxLength = 2000;
 239  
 240      // Sets the tablename to be used
 241      static function table($newtable = false)
 242      {
 243          static $_table;
 244  
 245          if (!empty($newtable))  $_table = $newtable;
 246  	 	 if (empty($_table)) $_table = 'adodb_logsql';
 247          return $_table;
 248      }
 249  
 250  	 // returns array with info to calculate CPU Load
 251  	function _CPULoad()
 252  	 {
 253  /*
 254  
 255  cpu  524152 2662 2515228 336057010
 256  cpu0 264339 1408 1257951 168025827
 257  cpu1 259813 1254 1257277 168031181
 258  page 622307 25475680
 259  swap 24 1891
 260  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
 261  disk_io: (3,0):(3144904,54369,610378,3090535,50936192) (3,1):(3630212,54097,633016,3576115,50951320)
 262  ctxt 66155838
 263  btime 1062315585
 264  processes 69293
 265  
 266  */
 267  	 	 // Algorithm is taken from
 268  	 	 // http://social.technet.microsoft.com/Forums/en-US/winservergen/thread/414b0e1b-499c-411e-8a02-6a12e339c0f1/
 269  	 	 if (strncmp(PHP_OS,'WIN',3)==0) {
 270  	 	 	 static $FAIL = false;
 271  	 	 	 if ($FAIL) return false;
 272  
 273  	 	 	 $objName = "winmgmts:{impersonationLevel=impersonate}!\\\\.\\root\\CIMV2";
 274  	 	 	 $myQuery = "SELECT * FROM Win32_PerfFormattedData_PerfOS_Processor WHERE Name = '_Total'";
 275  
 276  	 	 	 try {
 277  	 	 	 	 @$objWMIService = new COM($objName);
 278  	 	 	 	 if (!$objWMIService) {
 279  	 	 	 	 	 $FAIL = true;
 280  	 	 	 	 	 return false;
 281  	 	 	 	 }
 282  
 283  	 	 	 	 $info[0] = -1;
 284  	 	 	 	 $info[1] = 0;
 285  	 	 	 	 $info[2] = 0;
 286  	 	 	 	 $info[3] = 0;
 287  	 	 	 	 foreach($objWMIService->ExecQuery($myQuery) as $objItem)  {
 288  	 	 	 	 	 	 $info[0] = $objItem->PercentProcessorTime();
 289  	 	 	 	 }
 290  
 291  	 	 	 } catch(Exception $e) {
 292  	 	 	 	 $FAIL = true;
 293  	 	 	 	 echo $e->getMessage();
 294  	 	 	 	 return false;
 295  	 	 	 }
 296  
 297  	 	 	 return $info;
 298  	 	 }
 299  
 300  	 	 // Algorithm - Steve Blinch (BlitzAffe Online, http://www.blitzaffe.com)
 301  	 	 $statfile = '/proc/stat';
 302  	 	 if (!file_exists($statfile)) return false;
 303  
 304  	 	 $fd = fopen($statfile,"r");
 305  	 	 if (!$fd) return false;
 306  
 307  	 	 $statinfo = explode("\n",fgets($fd, 1024));
 308  	 	 fclose($fd);
 309  	 	 foreach($statinfo as $line) {
 310  	 	 	 $info = explode(" ",$line);
 311  	 	 	 if($info[0]=="cpu") {
 312  	 	 	 	 array_shift($info);  // pop off "cpu"
 313  	 	 	 	 if(!$info[0]) array_shift($info); // pop off blank space (if any)
 314  	 	 	 	 return $info;
 315  	 	 	 }
 316  	 	 }
 317  
 318  	 	 return false;
 319  
 320  	 }
 321  
 322  	 /* NOT IMPLEMENTED */
 323  	function MemInfo()
 324  	 {
 325  	 	 /*
 326  
 327          total:    used:    free:  shared: buffers:  cached:
 328  Mem:  1055289344 917299200 137990144        0 165437440 599773184
 329  Swap: 2146775040 11055104 2135719936
 330  MemTotal:      1030556 kB
 331  MemFree:        134756 kB
 332  MemShared:           0 kB
 333  Buffers:        161560 kB
 334  Cached:         581384 kB
 335  SwapCached:       4332 kB
 336  Active:         494468 kB
 337  Inact_dirty:    322856 kB
 338  Inact_clean:     24256 kB
 339  Inact_target:   168316 kB
 340  HighTotal:      131064 kB
 341  HighFree:         1024 kB
 342  LowTotal:       899492 kB
 343  LowFree:        133732 kB
 344  SwapTotal:     2096460 kB
 345  SwapFree:      2085664 kB
 346  Committed_AS:   348732 kB
 347  	 	 */
 348  	 }
 349  
 350  
 351  	 /*
 352  	 	 Remember that this is client load, not db server load!
 353  	 */
 354  	 var $_lastLoad;
 355  	function CPULoad()
 356  	 {
 357  	 	 $info = $this->_CPULoad();
 358  	 	 if (!$info) return false;
 359  
 360  	 	 if (strncmp(PHP_OS,'WIN',3)==0) {
 361  	 	 	 return (integer) $info[0];
 362  	 	 }else {
 363  	 	 	 if (empty($this->_lastLoad)) {
 364  	 	 	 	 sleep(1);
 365  	 	 	 	 $this->_lastLoad = $info;
 366  	 	 	 	 $info = $this->_CPULoad();
 367  	 	 	 }
 368  
 369  	 	 	 $last = $this->_lastLoad;
 370  	 	 	 $this->_lastLoad = $info;
 371  
 372  	 	 	 $d_user = $info[0] - $last[0];
 373  	 	 	 $d_nice = $info[1] - $last[1];
 374  	 	 	 $d_system = $info[2] - $last[2];
 375  	 	 	 $d_idle = $info[3] - $last[3];
 376  
 377  	 	 	 //printf("Delta - User: %f  Nice: %f  System: %f  Idle: %f<br>",$d_user,$d_nice,$d_system,$d_idle);
 378  
 379  	 	 	 $total=$d_user+$d_nice+$d_system+$d_idle;
 380  	 	 	 if ($total<1) $total=1;
 381  	 	 	 return 100*($d_user+$d_nice+$d_system)/$total;
 382  	 	 }
 383  	 }
 384  
 385  	function Tracer($sql)
 386  	 {
 387          $perf_table = adodb_perf::table();
 388  	 	 $saveE = $this->conn->fnExecute;
 389  	 	 $this->conn->fnExecute = false;
 390  
 391  	 	 global $ADODB_FETCH_MODE;
 392  	 	 $save = $ADODB_FETCH_MODE;
 393  	 	 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
 394  	 	 if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
 395  
 396  	 	 $sqlq = $this->conn->qstr($sql);
 397  	 	 $arr = $this->conn->GetArray(
 398  "select count(*),tracer
 399  	 from $perf_table where sql1=$sqlq
 400  	 group by tracer
 401  	 order by 1 desc");
 402  	 	 $s = '';
 403  	 	 if ($arr) {
 404  	 	 	 $s .= '<h3>Scripts Affected</h3>';
 405  	 	 	 foreach($arr as $k) {
 406  	 	 	 	 $s .= sprintf("%4d",$k[0]).' &nbsp; '.strip_tags($k[1]).'<br>';
 407  	 	 	 }
 408  	 	 }
 409  
 410  	 	 if (isset($savem)) $this->conn->SetFetchMode($savem);
 411  	 	 $ADODB_CACHE_MODE = $save;
 412  	 	 $this->conn->fnExecute = $saveE;
 413  	 	 return $s;
 414  	 }
 415  
 416  	 /*
 417  	 	 Explain Plan for $sql.
 418  	 	 If only a snippet of the $sql is passed in, then $partial will hold the crc32 of the
 419  	 	 	 actual sql.
 420  	 */
 421  	function Explain($sql,$partial=false)
 422  	 {
 423  	 	 return false;
 424  	 }
 425  
 426  	function InvalidSQL($numsql = 10)
 427  	 {
 428  
 429  	 	 if (isset($_GET['sql'])) return;
 430  	 	 $s = '<h3>Invalid SQL</h3>';
 431  	 	 $saveE = $this->conn->fnExecute;
 432  	 	 $this->conn->fnExecute = false;
 433          $perf_table = adodb_perf::table();
 434  	 	 $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);
 435  	 	 $this->conn->fnExecute = $saveE;
 436  	 	 if ($rs) {
 437  	 	 	 $s .= rs2html($rs,false,false,false,false);
 438  	 	 } else
 439  	 	 	 return "<p>$this->helpurl. ".$this->conn->ErrorMsg()."</p>";
 440  
 441  	 	 return $s;
 442  	 }
 443  
 444  
 445  	 /*
 446  	 	 This script identifies the longest running SQL
 447  	 */
 448  	function _SuspiciousSQL($numsql = 10)
 449  	 {
 450  	 	 global $ADODB_FETCH_MODE;
 451  
 452              $perf_table = adodb_perf::table();
 453  	 	 	 $saveE = $this->conn->fnExecute;
 454  	 	 	 $this->conn->fnExecute = false;
 455  
 456  	 	 	 if (isset($_GET['exps']) && isset($_GET['sql'])) {
 457  	 	 	 	 $partial = !empty($_GET['part']);
 458  	 	 	 	 echo "<a name=explain></a>".$this->Explain($_GET['sql'],$partial)."\n";
 459  	 	 	 }
 460  
 461  	 	 	 if (isset($_GET['sql'])) return;
 462  	 	 	 $sql1 = $this->sql1;
 463  
 464  	 	 	 $save = $ADODB_FETCH_MODE;
 465  	 	 	 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
 466  	 	 	 if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
 467  	 	 	 //$this->conn->debug=1;
 468  	 	 	 $rs = $this->conn->SelectLimit(
 469  	 	 	 "select avg(timer) as avg_timer,$sql1,count(*),max(timer) as max_timer,min(timer) as min_timer
 470  	 	 	 	 from $perf_table
 471  	 	 	 	 where {$this->conn->upperCase}({$this->conn->substr}(sql0,1,5)) not in ('DROP ','INSER','COMMI','CREAT')
 472  	 	 	 	 and (tracer is null or tracer not like 'ERROR:%')
 473  	 	 	 	 group by sql1
 474  	 	 	 	 order by 1 desc",$numsql);
 475  	 	 	 if (isset($savem)) $this->conn->SetFetchMode($savem);
 476  	 	 	 $ADODB_FETCH_MODE = $save;
 477  	 	 	 $this->conn->fnExecute = $saveE;
 478  
 479  	 	 	 if (!$rs) return "<p>$this->helpurl. ".$this->conn->ErrorMsg()."</p>";
 480  	 	 	 $s = "<h3>Suspicious SQL</h3>
 481  <font size=1>The following SQL have high average execution times</font><br>
 482  <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";
 483  	 	 	 $max = $this->maxLength;
 484  	 	 	 while (!$rs->EOF) {
 485  	 	 	 	 $sql = $rs->fields[1];
 486  	 	 	 	 $raw = urlencode($sql);
 487  	 	 	 	 if (strlen($raw)>$max-100) {
 488  	 	 	 	 	 $sql2 = substr($sql,0,$max-500);
 489  	 	 	 	 	 $raw = urlencode($sql2).'&part='.crc32($sql);
 490  	 	 	 	 }
 491  	 	 	 	 $prefix = "<a target=sql".rand()." href=\"?hidem=1&exps=1&sql=".$raw."&x#explain\">";
 492  	 	 	 	 $suffix = "</a>";
 493  	 	 	 	 if ($this->explain == false || strlen($prefix)>$max) {
 494  	 	 	 	 	 $suffix = ' ... <i>String too long for GET parameter: '.strlen($prefix).'</i>';
 495  	 	 	 	 	 $prefix = '';
 496  	 	 	 	 }
 497  	 	 	 	 $s .= "<tr><td>".adodb_round($rs->fields[0],6)."<td align=right>".$rs->fields[2]."<td><font size=-1>".$prefix.htmlspecialchars($sql).$suffix."</font>".
 498  	 	 	 	 	 "<td>".$rs->fields[3]."<td>".$rs->fields[4]."</tr>";
 499  	 	 	 	 $rs->MoveNext();
 500  	 	 	 }
 501  	 	 	 return $s."</table>";
 502  
 503  	 }
 504  
 505  	function CheckMemory()
 506  	 {
 507  	 	 return '';
 508  	 }
 509  
 510  
 511  	function SuspiciousSQL($numsql=10)
 512  	 {
 513  	 	 return adodb_perf::_SuspiciousSQL($numsql);
 514  	 }
 515  
 516  	function ExpensiveSQL($numsql=10)
 517  	 {
 518  	 	 return adodb_perf::_ExpensiveSQL($numsql);
 519  	 }
 520  
 521  
 522  	 /*
 523  	 	 This reports the percentage of load on the instance due to the most
 524  	 	 expensive few SQL statements. Tuning these statements can often
 525  	 	 make huge improvements in overall system performance.
 526  	 */
 527  	function _ExpensiveSQL($numsql = 10)
 528  	 {
 529  	 	 global $ADODB_FETCH_MODE;
 530  
 531              $perf_table = adodb_perf::table();
 532  	 	 	 $saveE = $this->conn->fnExecute;
 533  	 	 	 $this->conn->fnExecute = false;
 534  
 535  	 	 	 if (isset($_GET['expe']) && isset($_GET['sql'])) {
 536  	 	 	 	 $partial = !empty($_GET['part']);
 537  	 	 	 	 echo "<a name=explain></a>".$this->Explain($_GET['sql'],$partial)."\n";
 538  	 	 	 }
 539  
 540  	 	 	 if (isset($_GET['sql'])) return;
 541  
 542  	 	 	 $sql1 = $this->sql1;
 543  	 	 	 $save = $ADODB_FETCH_MODE;
 544  	 	 	 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
 545  	 	 	 if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
 546  
 547  	 	 	 $rs = $this->conn->SelectLimit(
 548  	 	 	 "select sum(timer) as total,$sql1,count(*),max(timer) as max_timer,min(timer) as min_timer
 549  	 	 	 	 from $perf_table
 550  	 	 	 	 where {$this->conn->upperCase}({$this->conn->substr}(sql0,1,5))  not in ('DROP ','INSER','COMMI','CREAT')
 551  	 	 	 	 and (tracer is null or tracer not like 'ERROR:%')
 552  	 	 	 	 group by sql1
 553  	 	 	 	 having count(*)>1
 554  	 	 	 	 order by 1 desc",$numsql);
 555  	 	 	 if (isset($savem)) $this->conn->SetFetchMode($savem);
 556  	 	 	 $this->conn->fnExecute = $saveE;
 557  	 	 	 $ADODB_FETCH_MODE = $save;
 558  	 	 	 if (!$rs) return "<p>$this->helpurl. ".$this->conn->ErrorMsg()."</p>";
 559  	 	 	 $s = "<h3>Expensive SQL</h3>
 560  <font size=1>Tuning the following SQL could reduce the server load substantially</font><br>
 561  <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";
 562  	 	 	 $max = $this->maxLength;
 563  	 	 	 while (!$rs->EOF) {
 564  	 	 	 	 $sql = $rs->fields[1];
 565  	 	 	 	 $raw = urlencode($sql);
 566  	 	 	 	 if (strlen($raw)>$max-100) {
 567  	 	 	 	 	 $sql2 = substr($sql,0,$max-500);
 568  	 	 	 	 	 $raw = urlencode($sql2).'&part='.crc32($sql);
 569  	 	 	 	 }
 570  	 	 	 	 $prefix = "<a target=sqle".rand()." href=\"?hidem=1&expe=1&sql=".$raw."&x#explain\">";
 571  	 	 	 	 $suffix = "</a>";
 572  	 	 	 	 if($this->explain == false || strlen($prefix>$max)) {
 573  	 	 	 	 	 $prefix = '';
 574  	 	 	 	 	 $suffix = '';
 575  	 	 	 	 }
 576  	 	 	 	 $s .= "<tr><td>".adodb_round($rs->fields[0],6)."<td align=right>".$rs->fields[2]."<td><font size=-1>".$prefix.htmlspecialchars($sql).$suffix."</font>".
 577  	 	 	 	 	 "<td>".$rs->fields[3]."<td>".$rs->fields[4]."</tr>";
 578  	 	 	 	 $rs->MoveNext();
 579  	 	 	 }
 580  	 	 	 return $s."</table>";
 581  	 }
 582  
 583  	 /*
 584  	 	 Raw function to return parameter value from $settings.
 585  	 */
 586  	function DBParameter($param)
 587  	 {
 588  	 	 if (empty($this->settings[$param])) return false;
 589  	 	 $sql = $this->settings[$param][1];
 590  	 	 return $this->_DBParameter($sql);
 591  	 }
 592  
 593  	 /*
 594  	 	 Raw function returning array of poll parameters
 595  	 */
 596  	function PollParameters()
 597  	 {
 598  	 	 $arr[0] = (float)$this->DBParameter('data cache hit ratio');
 599  	 	 $arr[1] = (float)$this->DBParameter('data reads');
 600  	 	 $arr[2] = (float)$this->DBParameter('data writes');
 601  	 	 $arr[3] = (integer) $this->DBParameter('current connections');
 602  	 	 return $arr;
 603  	 }
 604  
 605  	 /*
 606  	 	 Low-level Get Database Parameter
 607  	 */
 608  	function _DBParameter($sql)
 609  	 {
 610  	 	 $savelog = $this->conn->LogSQL(false);
 611  	 	 if (is_array($sql)) {
 612  	 	 global $ADODB_FETCH_MODE;
 613  
 614  	 	 	 $sql1 = $sql[0];
 615  	 	 	 $key = $sql[1];
 616  	 	 	 if (sizeof($sql)>2) $pos = $sql[2];
 617  	 	 	 else $pos = 1;
 618  	 	 	 if (sizeof($sql)>3) $coef = $sql[3];
 619  	 	 	 else $coef = false;
 620  	 	 	 $ret = false;
 621  	 	 	 $save = $ADODB_FETCH_MODE;
 622  	 	 	 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
 623  	 	 	 if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
 624  
 625  	 	 	 $rs = $this->conn->Execute($sql1);
 626  
 627  	 	 	 if (isset($savem)) $this->conn->SetFetchMode($savem);
 628  	 	 	 $ADODB_FETCH_MODE = $save;
 629  	 	 	 if ($rs) {
 630  	 	 	 	 while (!$rs->EOF) {
 631  	 	 	 	 	 $keyf = reset($rs->fields);
 632  	 	 	 	 	 if (trim($keyf) == $key) {
 633  	 	 	 	 	 	 $ret = $rs->fields[$pos];
 634  	 	 	 	 	 	 if ($coef) $ret *= $coef;
 635  	 	 	 	 	 	 break;
 636  	 	 	 	 	 }
 637  	 	 	 	 	 $rs->MoveNext();
 638  	 	 	 	 }
 639  	 	 	 	 $rs->Close();
 640  	 	 	 }
 641  	 	 	 $this->conn->LogSQL($savelog);
 642  	 	 	 return $ret;
 643  	 	 } else {
 644  	 	 	 if (strncmp($sql,'=',1) == 0) {
 645  	 	 	 	 $fn = substr($sql,1);
 646  	 	 	 	 return $this->$fn();
 647  	 	 	 }
 648  	 	 	 $sql = str_replace('$DATABASE',$this->conn->database,$sql);
 649  	 	 	 $ret = $this->conn->GetOne($sql);
 650  	 	 	 $this->conn->LogSQL($savelog);
 651  
 652  	 	 	 return $ret;
 653  	 	 }
 654  	 }
 655  
 656  	 /*
 657  	 	 Warn if cache ratio falls below threshold. Displayed in "Description" column.
 658  	 */
 659  	function WarnCacheRatio($val)
 660  	 {
 661  	 	 if ($val < $this->warnRatio)
 662  	 	 	  return '<font color=red><b>Cache ratio should be at least '.$this->warnRatio.'%</b></font>';
 663  	 	 else return '';
 664  	 }
 665  
 666  	function clearsql()
 667  	 {
 668  	 	 $perf_table = adodb_perf::table();
 669  	 	 $this->conn->Execute("delete from $perf_table where created<".$this->conn->sysTimeStamp);
 670  	 }
 671  	 /***********************************************************************************************/
 672  	 //                                    HIGH LEVEL UI FUNCTIONS
 673  	 /***********************************************************************************************/
 674  
 675  
 676  	 function UI($pollsecs=5)
 677  	 {
 678  	 global $ADODB_LOG_CONN;
 679  
 680      $perf_table = adodb_perf::table();
 681  	 $conn = $this->conn;
 682  
 683  	 $app = $conn->host;
 684  	 if ($conn->host && $conn->database) $app .= ', db=';
 685  	 $app .= $conn->database;
 686  
 687  	 if ($app) $app .= ', ';
 688  	 $savelog = $this->conn->LogSQL(false);
 689  	 $info = $conn->ServerInfo();
 690  	 if (isset($_GET['clearsql'])) {
 691  	 	 $this->clearsql();
 692  	 }
 693  	 $this->conn->LogSQL($savelog);
 694  
 695  	 if (!isset($_SESSION['ADODB_PERF_SQL'])) $nsql = $_SESSION['ADODB_PERF_SQL'] = 10;
 696  	 else  $nsql = $_SESSION['ADODB_PERF_SQL'];
 697  
 698  	 $app .= $info['description'];
 699  
 700  
 701  	 if (isset($_GET['do'])) $do = $_GET['do'];
 702  	 else if (isset($_POST['do'])) $do = $_POST['do'];
 703  	  else if (isset($_GET['sql'])) $do = 'viewsql';
 704  	  else $do = 'stats';
 705  
 706  	 if (isset($_GET['nsql'])) {
 707  	 	 if ($_GET['nsql'] > 0) $nsql = $_SESSION['ADODB_PERF_SQL'] = (integer) $_GET['nsql'];
 708  	 }
 709  	 echo "<title>ADOdb Performance Monitor on $app</title><body bgcolor=white>";
 710  	 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>";
 711  	 else $form = "<td>&nbsp;</td>";
 712  
 713  	 $allowsql = !defined('ADODB_PERF_NO_RUN_SQL');
 714  	 global $ADODB_PERF_MIN;
 715  	 $app .= " (Min sql timing \$ADODB_PERF_MIN=$ADODB_PERF_MIN secs)";
 716  
 717  	 if  (empty($_GET['hidem']))
 718  	 echo "<table border=1 width=100% bgcolor=lightyellow><tr><td colspan=2>
 719  	 <b><a href=https://adodb.org/dokuwiki/doku.php?id=v5:performance:performance_index>ADOdb</a> Performance Monitor</b> <font size=1>for $app</font></tr><tr><td>
 720  	 <a href=?do=stats><b>Performance Stats</b></a> &nbsp; <a href=?do=viewsql><b>View SQL</b></a>
 721  	  &nbsp; <a href=?do=tables><b>View Tables</b></a> &nbsp; <a href=?do=poll><b>Poll Stats</b></a>",
 722  	  $allowsql ? ' &nbsp; <a href=?do=dosql><b>Run SQL</b></a>' : '',
 723  	  "$form",
 724  	  "</tr></table>";
 725  
 726  
 727  	  	 switch ($do) {
 728  	 	 default:
 729  	 	 case 'stats':
 730  	 	 	 if (empty($ADODB_LOG_CONN))
 731  	 	 	 	 echo "<p>&nbsp; <a href=\"?do=viewsql&clearsql=1\">Clear SQL Log</a><br>";
 732  	 	 	 echo $this->HealthCheck();
 733  	 	 	 //$this->conn->debug=1;
 734  	 	 	 echo $this->CheckMemory();
 735  	 	 	 break;
 736  	 	 case 'poll':
 737  	 	 	 $self = htmlspecialchars($_SERVER['PHP_SELF']);
 738  	 	 	 echo "<iframe width=720 height=80%
 739  	 	 	 	 src=\"{$self}?do=poll2&hidem=1\"></iframe>";
 740  	 	 	 break;
 741  	 	 case 'poll2':
 742  	 	 	 echo "<pre>";
 743  	 	 	 $this->Poll($pollsecs);
 744  	 	 	 break;
 745  
 746  	 	 case 'dosql':
 747  	 	 	 if (!$allowsql) break;
 748  
 749  	 	 	 $this->DoSQLForm();
 750  	 	 	 break;
 751  	 	 case 'viewsql':
 752  	 	 	 if (empty($_GET['hidem']))
 753  	 	 	 	 echo "&nbsp; <a href=\"?do=viewsql&clearsql=1\">Clear SQL Log</a><br>";
 754  	 	 	 echo($this->SuspiciousSQL($nsql));
 755  	 	 	 echo($this->ExpensiveSQL($nsql));
 756  	 	 	 echo($this->InvalidSQL($nsql));
 757  	 	 	 break;
 758  	 	 case 'tables':
 759  	 	 	 echo $this->Tables(); break;
 760  	 	 }
 761  	 	 global $ADODB_vers;
 762  	 }
 763  
 764  	 /*
 765  	 	 Runs in infinite loop, returning real-time statistics
 766  	 */
 767  	function Poll($secs=5)
 768  	 {
 769  	 	 $this->conn->fnExecute = false;
 770  	 	 //$this->conn->debug=1;
 771  	 	 if ($secs <= 1) $secs = 1;
 772  	 	 echo "Accumulating statistics, every $secs seconds...\n";flush();
 773  	 	 $arro = $this->PollParameters();
 774  	 	 $cnt = 0;
 775  	 	 set_time_limit(0);
 776  	 	 sleep($secs);
 777  	 	 while (1) {
 778  
 779  	 	 	 $arr = $this->PollParameters();
 780  
 781  	 	 	 $hits   = sprintf('%2.2f',$arr[0]);
 782  	 	 	 $reads  = sprintf('%12.4f',($arr[1]-$arro[1])/$secs);
 783  	 	 	 $writes = sprintf('%12.4f',($arr[2]-$arro[2])/$secs);
 784  	 	 	 $sess = sprintf('%5d',$arr[3]);
 785  
 786  	 	 	 $load = $this->CPULoad();
 787  	 	 	 if ($load !== false) {
 788  	 	 	 	 $oslabel = 'WS-CPU%';
 789  	 	 	 	 $osval = sprintf(" %2.1f  ",(float) $load);
 790  	 	 	 }else {
 791  	 	 	 	 $oslabel = '';
 792  	 	 	 	 $osval = '';
 793  	 	 	 }
 794  	 	 	 if ($cnt % 10 == 0) echo " Time   ".$oslabel."   Hit%   Sess           Reads/s          Writes/s\n";
 795  	 	 	 $cnt += 1;
 796  	 	 	 echo date('H:i:s').'  '.$osval."$hits  $sess $reads $writes\n";
 797  	 	 	 flush();
 798  
 799  	 	 	 if (connection_aborted()) return;
 800  
 801  	 	 	 sleep($secs);
 802  	 	 	 $arro = $arr;
 803  	 	 }
 804  	 }
 805  
 806  	 /*
 807  	 	 Returns basic health check in a command line interface
 808  	 */
 809  	function HealthCheckCLI()
 810  	 {
 811  	 	 return $this->HealthCheck(true);
 812  	 }
 813  
 814  
 815  	 /*
 816  	 	 Returns basic health check as HTML
 817  	 */
 818  	function HealthCheck($cli=false)
 819  	 {
 820  	 	 $saveE = $this->conn->fnExecute;
 821  	 	 $this->conn->fnExecute = false;
 822  	 	 if ($cli) $html = '';
 823  	 	 else $html = $this->table.'<tr><td colspan=3><h3>'.$this->conn->databaseType.'</h3></td></tr>'.$this->titles;
 824  
 825  	 	 $oldc = false;
 826  	 	 $bgc = '';
 827  	 	 foreach($this->settings as $name => $arr) {
 828  	 	 	 if ($arr === false) break;
 829  
 830  	 	 	 if (!is_string($name)) {
 831  	 	 	 	 if ($cli) $html .= " -- $arr -- \n";
 832  	 	 	 	 else $html .= "<tr bgcolor=$this->color><td colspan=3><i>$arr</i> &nbsp;</td></tr>";
 833  	 	 	 	 continue;
 834  	 	 	 }
 835  
 836  	 	 	 if (!is_array($arr)) break;
 837  	 	 	 $category = $arr[0];
 838  	 	 	 $how = $arr[1];
 839  	 	 	 if (sizeof($arr)>2) $desc = $arr[2];
 840  	 	 	 else $desc = ' &nbsp; ';
 841  
 842  
 843  	 	 	 if ($category == 'HIDE') continue;
 844  
 845  	 	 	 $val = $this->_DBParameter($how);
 846  
 847  	 	 	 if ($desc && strncmp($desc,"=",1) === 0) {
 848  	 	 	 	 $fn = substr($desc,1);
 849  	 	 	 	 $desc = $this->$fn($val);
 850  	 	 	 }
 851  
 852  	 	 	 if ($val === false) {
 853  	 	 	 	 $m = $this->conn->ErrorMsg();
 854  	 	 	 	 $val = "Error: $m";
 855  	 	 	 } else {
 856  	 	 	 	 if (is_numeric($val) && $val >= 256*1024) {
 857  	 	 	 	 	 if ($val % (1024*1024) == 0) {
 858  	 	 	 	 	 	 $val /= (1024*1024);
 859  	 	 	 	 	 	 $val .= 'M';
 860  	 	 	 	 	 } else if ($val % 1024 == 0) {
 861  	 	 	 	 	 	 $val /= 1024;
 862  	 	 	 	 	 	 $val .= 'K';
 863  	 	 	 	 	 }
 864  	 	 	 	 	 //$val = htmlspecialchars($val);
 865  	 	 	 	 }
 866  	 	 	 }
 867  	 	 	 if ($category != $oldc) {
 868  	 	 	 	 $oldc = $category;
 869  	 	 	 	 //$bgc = ($bgc == ' bgcolor='.$this->color) ? ' bgcolor=white' : ' bgcolor='.$this->color;
 870  	 	 	 }
 871  	 	 	 if (strlen($desc)==0) $desc = '&nbsp;';
 872  	 	 	 if (strlen($val)==0) $val = '&nbsp;';
 873  	 	 	 if ($cli) {
 874  	 	 	 	 $html  .= str_replace('&nbsp;','',sprintf($this->cliFormat,strip_tags($name),strip_tags($val),strip_tags($desc)));
 875  
 876  	 	 	 }else {
 877  	 	 	 	 $html .= "<tr$bgc><td>".$name.'</td><td>'.$val.'</td><td>'.$desc."</td></tr>\n";
 878  	 	 	 }
 879  	 	 }
 880  
 881  	 	 if (!$cli) $html .= "</table>\n";
 882  	 	 $this->conn->fnExecute = $saveE;
 883  
 884  	 	 return $html;
 885  	 }
 886  
 887  	function Tables($orderby='1')
 888  	 {
 889  	 	 if (!$this->tablesSQL) return false;
 890  
 891  	 	 $savelog = $this->conn->LogSQL(false);
 892  	 	 $rs = $this->conn->Execute($this->tablesSQL.' order by '.$orderby);
 893  	 	 $this->conn->LogSQL($savelog);
 894  	 	 $html = rs2html($rs,false,false,false,false);
 895  	 	 return $html;
 896  	 }
 897  
 898  
 899  	function CreateLogTable()
 900  	 {
 901  	 	 if (!$this->createTableSQL) return false;
 902  
 903  	 	 $table = $this->table();
 904  	 	 $sql = str_replace('adodb_logsql',$table,$this->createTableSQL);
 905  	 	 $savelog = $this->conn->LogSQL(false);
 906  	 	 $ok = $this->conn->Execute($sql);
 907  	 	 $this->conn->LogSQL($savelog);
 908  	 	 return ($ok) ? true : false;
 909  	 }
 910  
 911  	function DoSQLForm()
 912  	 {
 913  
 914  
 915  	 	 $PHP_SELF = htmlspecialchars($_SERVER['PHP_SELF']);
 916  	 	 $sql = isset($_REQUEST['sql']) ? $_REQUEST['sql'] : '';
 917  
 918  	 	 if (isset($_SESSION['phplens_sqlrows'])) $rows = $_SESSION['phplens_sqlrows'];
 919  	 	 else $rows = 3;
 920  
 921  	 	 if (isset($_REQUEST['SMALLER'])) {
 922  	 	 	 $rows /= 2;
 923  	 	 	 if ($rows < 3) $rows = 3;
 924  	 	 	 $_SESSION['phplens_sqlrows'] = $rows;
 925  	 	 }
 926  	 	 if (isset($_REQUEST['BIGGER'])) {
 927  	 	 	 $rows *= 2;
 928  	 	 	 $_SESSION['phplens_sqlrows'] = $rows;
 929  	 	 }
 930  
 931  ?>
 932  
 933  <form method="POST" action="<?php echo $PHP_SELF ?>">
 934  <table><tr>
 935  <td> Form size: <input type="submit" value=" &lt; " name="SMALLER"><input type="submit" value=" &gt; &gt; " name="BIGGER">
 936  </td>
 937  <td align=right>
 938  <input type="submit" value=" Run SQL Below " name="RUN"><input type=hidden name=do value=dosql>
 939  </td></tr>
 940    <tr>
 941    <td colspan=2><textarea rows=<?php print $rows; ?> name="sql" cols="80"><?php print htmlspecialchars($sql) ?></textarea>
 942    </td>
 943    </tr>
 944   </table>
 945  </form>
 946  
 947  <?php
 948  	 	 if (!isset($_REQUEST['sql'])) return;
 949  
 950  	 	 $sql = trim($sql);
 951  	 	 if (substr($sql,strlen($sql)-1) === ';') {
 952  	 	 	 $print = true;
 953  	 	 	 $sqla = $this->SplitSQL($sql);
 954  	 	 } else  {
 955  	 	 	 $print = false;
 956  	 	 	 $sqla = array($sql);
 957  	 	 }
 958  	 	 foreach($sqla as $sqls) {
 959  
 960  	 	 	 if (!$sqls) continue;
 961  
 962  	 	 	 if ($print) {
 963  	 	 	 	 print "<p>".htmlspecialchars($sqls)."</p>";
 964  	 	 	 	 flush();
 965  	 	 	 }
 966  	 	 	 $savelog = $this->conn->LogSQL(false);
 967  	 	 	 $rs = $this->conn->Execute($sqls);
 968  	 	 	 $this->conn->LogSQL($savelog);
 969  	 	 	 if ($rs && is_object($rs) && !$rs->EOF) {
 970  	 	 	 	 rs2html($rs);
 971  	 	 	 	 while ($rs->NextRecordSet()) {
 972  	 	 	 	 	 print "<table width=98% bgcolor=#C0C0FF><tr><td>&nbsp;</td></tr></table>";
 973  	 	 	 	 	 rs2html($rs);
 974  	 	 	 	 }
 975  	 	 	 } else {
 976  	 	 	 	 $e1 = (integer) $this->conn->ErrorNo();
 977  	 	 	 	 $e2 = $this->conn->ErrorMsg();
 978  	 	 	 	 if (($e1) || ($e2)) {
 979  	 	 	 	 	 if (empty($e1)) $e1 = '-1'; // postgresql fix
 980  	 	 	 	 	 print ' &nbsp; '.$e1.': '.$e2;
 981  	 	 	 	 } else {
 982  	 	 	 	 	 print "<p>No Recordset returned<br></p>";
 983  	 	 	 	 }
 984  	 	 	 }
 985  	 	 } // foreach
 986  	 }
 987  
 988  	function SplitSQL($sql)
 989  	 {
 990  	 	 $arr = explode(';',$sql);
 991  	 	 return $arr;
 992  	 }
 993  
 994     /************************************************************************/
 995  
 996      /**
 997       * Reorganise multiple table-indices/statistics/..
 998       * OptimizeMode could be given by last Parameter
 999       *
1000       * @example
1001       *      <pre>
1002       *          optimizeTables( 'tableA');
1003       *      </pre>
1004       *      <pre>
1005       *          optimizeTables( 'tableA', 'tableB', 'tableC');
1006       *      </pre>
1007       *      <pre>
1008       *          optimizeTables( 'tableA', 'tableB', ADODB_OPT_LOW);
1009       *      </pre>
1010       *
1011       * @param string table name of the table to optimize
1012       * @param int mode optimization-mode
1013       *      <code>ADODB_OPT_HIGH</code> for full optimization
1014       *      <code>ADODB_OPT_LOW</code> for CPU-less optimization
1015       *      Default is LOW <code>ADODB_OPT_LOW</code>
1016       * @author Markus Staab
1017       * @return Returns <code>true</code> on success and <code>false</code> on error
1018       */
1019      function OptimizeTables()
1020      {
1021          $args = func_get_args();
1022          $numArgs = func_num_args();
1023  
1024          if ( $numArgs == 0) return false;
1025  
1026          $mode = ADODB_OPT_LOW;
1027          $lastArg = $args[ $numArgs - 1];
1028          if ( !is_string($lastArg)) {
1029              $mode = $lastArg;
1030              unset( $args[ $numArgs - 1]);
1031          }
1032  
1033          foreach( $args as $table) {
1034              $this->optimizeTable( $table, $mode);
1035          }
1036  	 }
1037  
1038      /**
1039       * Reorganise the table-indices/statistics/.. depending on the given mode.
1040       * Default Implementation throws an error.
1041       *
1042       * @param string table name of the table to optimize
1043       * @param int mode optimization-mode
1044       *      <code>ADODB_OPT_HIGH</code> for full optimization
1045       *      <code>ADODB_OPT_LOW</code> for CPU-less optimization
1046       *      Default is LOW <code>ADODB_OPT_LOW</code>
1047       * @author Markus Staab
1048       * @return Returns <code>true</code> on success and <code>false</code> on error
1049       */
1050      function OptimizeTable( $table, $mode = ADODB_OPT_LOW)
1051      {
1052          ADOConnection::outp( sprintf( "<p>%s: '%s' not implemented for driver '%s'</p>", __CLASS__, __FUNCTION__, $this->conn->databaseType));
1053          return false;
1054      }
1055  
1056      /**
1057       * Reorganise current database.
1058       * Default implementation loops over all <code>MetaTables()</code> and
1059       * optimize each using <code>optmizeTable()</code>
1060       *
1061       * @author Markus Staab
1062       * @return Returns <code>true</code> on success and <code>false</code> on error
1063       */
1064      function optimizeDatabase()
1065      {
1066          $conn = $this->conn;
1067          if ( !$conn) return false;
1068  
1069          $tables = $conn->MetaTables( 'TABLES');
1070          if ( !$tables ) return false;
1071  
1072          foreach( $tables as $table) {
1073              if ( !$this->optimizeTable( $table)) {
1074                  return false;
1075              }
1076          }
1077  
1078          return true;
1079      }
1080      // end hack
1081  }