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]

   1  <?php
   2  /**
   3   * Library for basic 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  // security - hide paths
  23  if (!defined('ADODB_DIR')) die();
  24  
  25  
  26  class perf_oci8 extends ADODB_perf{
  27  
  28  	 var $noShowIxora = 15; // if the sql for suspicious sql is taking too long, then disable ixora
  29  
  30  	 var $tablesSQL = "select segment_name as \"tablename\", sum(bytes)/1024 as \"size_in_k\",tablespace_name as \"tablespace\",count(*) \"extents\" from sys.user_extents
  31  	    group by segment_name,tablespace_name";
  32  
  33  	 var $version;
  34  
  35  	 var $createTableSQL = "CREATE TABLE adodb_logsql (
  36  	 	   created date NOT NULL,
  37  	 	   sql0 varchar(250) NOT NULL,
  38  	 	   sql1 varchar(4000) NOT NULL,
  39  	 	   params varchar(4000),
  40  	 	   tracer varchar(4000),
  41  	 	   timer decimal(16,6) NOT NULL
  42  	 	 )";
  43  
  44  	 var $settings = array(
  45  	 'Ratios',
  46  	 	 'data cache hit ratio' => array('RATIOH',
  47  	 	 	 "select round((1-(phy.value / (cur.value + con.value)))*100,2)
  48  	 	 	 from v\$sysstat cur, v\$sysstat con, v\$sysstat phy
  49  	 	 	 where cur.name = 'db block gets' and
  50  	 	 	       con.name = 'consistent gets' and
  51  	 	 	       phy.name = 'physical reads'",
  52  	 	 	 '=WarnCacheRatio'),
  53  
  54  	 	 'sql cache hit ratio' => array( 'RATIOH',
  55  	 	 	 'select round(100*(sum(pins)-sum(reloads))/sum(pins),2)  from v$librarycache',
  56  	 	 	 'increase <i>shared_pool_size</i> if too ratio low'),
  57  
  58  	 	 'datadict cache hit ratio' => array('RATIOH',
  59  	 	 "select
  60             round((1 - (sum(getmisses) / (sum(gets) +
  61     	 	  sum(getmisses))))*100,2)
  62  	 	 from  v\$rowcache",
  63  	 	 'increase <i>shared_pool_size</i> if too ratio low'),
  64  
  65  	 	 'memory sort ratio' => array('RATIOH',
  66  	 	 "SELECT ROUND((100 * b.VALUE) /DECODE ((a.VALUE + b.VALUE),
  67         0,1,(a.VALUE + b.VALUE)),2)
  68  FROM   v\$sysstat a,
  69         v\$sysstat b
  70  WHERE  a.name = 'sorts (disk)'
  71  AND    b.name = 'sorts (memory)'",
  72  	 "% of memory sorts compared to disk sorts - should be over 95%"),
  73  
  74  	 'IO',
  75  	 	 'data reads' => array('IO',
  76  	 	 "select value from v\$sysstat where name='physical reads'"),
  77  
  78  	 'data writes' => array('IO',
  79  	 	 "select value from v\$sysstat where name='physical writes'"),
  80  
  81  	 'Data Cache',
  82  
  83  	 	 'data cache buffers' => array( 'DATAC',
  84  	 	 "select a.value/b.value  from v\$parameter a, v\$parameter b
  85  	 	 	 where a.name = 'db_cache_size' and b.name= 'db_block_size'",
  86  	 	 	 'Number of cache buffers. Tune <i>db_cache_size</i> if the <i>data cache hit ratio</i> is too low.'),
  87  	 	 'data cache blocksize' => array('DATAC',
  88  	 	 	 "select value from v\$parameter where name='db_block_size'",
  89  	 	 	 '' ),
  90  
  91  	 'Memory Pools',
  92  	 	 'Mem Max Target (11g+)' => array( 'DATAC',
  93  	 	 "select value from v\$parameter where name = 'memory_max_target'",
  94  	 	 	 'The memory_max_size is the maximum value to which memory_target can be set.' ),
  95  	 'Memory target (11g+)' => array( 'DATAC',
  96  	 	 "select value from v\$parameter where name = 'memory_target'",
  97  	 	 	 'If memory_target is defined then SGA and PGA targets are consolidated into one memory_target.' ),
  98  	 	 'SGA Max Size' => array( 'DATAC',
  99  	 	 "select nvl(value,0)/1024.0/1024 || 'M' from v\$parameter where name = 'sga_max_size'",
 100  	 	 	 'The sga_max_size is the maximum value to which sga_target can be set.' ),
 101  	 'SGA target' => array( 'DATAC',
 102  	 	 "select nvl(value,0)/1024.0/1024 || 'M'  from v\$parameter where name = 'sga_target'",
 103  	 	 	 'If sga_target is defined then data cache, shared, java and large pool size can be 0. This is because all these pools are consolidated into one sga_target.' ),
 104  	 'PGA aggr target' => array( 'DATAC',
 105  	 	 "select nvl(value,0)/1024.0/1024 || 'M' from v\$parameter where name = 'pga_aggregate_target'",
 106  	 	 	 'If pga_aggregate_target is defined then this is the maximum memory that can be allocated for cursor operations such as sorts, group by, joins, merges. When in doubt, set it to 20% of sga_target.' ),
 107  	 'data cache size' => array('DATAC',
 108  	 	 	 "select value from v\$parameter where name = 'db_cache_size'",
 109  	 	 	 'db_cache_size' ),
 110  	 	 'shared pool size' => array('DATAC',
 111  	 	 	 "select value from v\$parameter where name = 'shared_pool_size'",
 112  	 	 	 'shared_pool_size, which holds shared sql, stored procedures, dict cache and similar shared structs' ),
 113  	 	 'java pool size' => array('DATAJ',
 114  	 	 	 "select value from v\$parameter where name = 'java_pool_size'",
 115  	 	 	 'java_pool_size' ),
 116  	 	 'large pool buffer size' => array('CACHE',
 117  	 	 	 "select value from v\$parameter where name='large_pool_size'",
 118  	 	 	 'this pool is for large mem allocations (not because it is larger than shared pool), for MTS sessions, parallel queries, io buffers (large_pool_size) ' ),
 119  
 120  	 	 'dynamic memory usage' => array('CACHE', "select '-' from dual", '=DynMemoryUsage'),
 121  
 122  	 	 'Connections',
 123  	 	 'current connections' => array('SESS',
 124  	 	 	 'select count(*) from sys.v_$session where username is not null',
 125  	 	 	 ''),
 126  	 	 'max connections' => array( 'SESS',
 127  	 	 	 "select value from v\$parameter where name='sessions'",
 128  	 	 	 ''),
 129  
 130  	 'Memory Utilization',
 131  	 	 'data cache utilization ratio' => array('RATIOU',
 132  	 	 	 "select round((1-bytes/sgasize)*100, 2)
 133  	 	 	 from (select sum(bytes) sgasize from sys.v_\$sgastat) s, sys.v_\$sgastat f
 134  	 	 	 where name = 'free memory' and pool = 'shared pool'",
 135  	 	 'Percentage of data cache actually in use - should be over 85%'),
 136  
 137  	 	 'shared pool utilization ratio' => array('RATIOU',
 138  	 	 'select round((sga.bytes/case when p.value=0 then sga.bytes else to_number(p.value) end)*100,2)
 139  	 	 from v$sgastat sga, v$parameter p
 140  	 	 where sga.name = \'free memory\' and sga.pool = \'shared pool\'
 141  	 	 and p.name = \'shared_pool_size\'',
 142  	 	 'Percentage of shared pool actually used - too low is bad, too high is worse'),
 143  
 144  	 	 'large pool utilization ratio' => array('RATIOU',
 145  	 	 	 "select round((1-bytes/sgasize)*100, 2)
 146  	 	 	 from (select sum(bytes) sgasize from sys.v_\$sgastat) s, sys.v_\$sgastat f
 147  	 	 	 where name = 'free memory' and pool = 'large pool'",
 148  	 	 'Percentage of large_pool actually in use - too low is bad, too high is worse'),
 149  	 	 'sort buffer size' => array('CACHE',
 150  	 	 	 "select value from v\$parameter where name='sort_area_size'",
 151  	 	 	 'max in-mem sort_area_size (per query), uses memory in pga' ),
 152  
 153  	 	 /*'pga usage at peak' => array('RATIOU',
 154  	 	 '=PGA','Mb utilization at peak transactions (requires Oracle 9i+)'),*/
 155  	 'Transactions',
 156  	 	 'rollback segments' => array('ROLLBACK',
 157  	 	 	 "select count(*) from sys.v_\$rollstat",
 158  	 	 	 ''),
 159  
 160  	 	 'peak transactions' => array('ROLLBACK',
 161  	 	 	 "select max_utilization  tx_hwm
 162      	 	 from sys.v_\$resource_limit
 163      	 	 where resource_name = 'transactions'",
 164  	 	 	 'Taken from high-water-mark'),
 165  	 	 'max transactions' => array('ROLLBACK',
 166  	 	 	 "select value from v\$parameter where name = 'transactions'",
 167  	 	 	 'max transactions / rollback segments < 3.5 (or transactions_per_rollback_segment)'),
 168  	 'Parameters',
 169  	 	 'cursor sharing' => array('CURSOR',
 170  	 	 	 "select value from v\$parameter where name = 'cursor_sharing'",
 171  	 	 	 'Cursor reuse strategy. Recommended is FORCE (8i+) or SIMILAR (9i+). See <a href=http://www.praetoriate.com/oracle_tips_cursor_sharing.htm>cursor_sharing</a>.'),
 172  	 	 /*
 173  	 	 'cursor reuse' => array('CURSOR',
 174  	 	 	 "select count(*) from (select sql_text_wo_constants, count(*)
 175    from t1
 176   group by sql_text_wo_constants
 177  having count(*) > 100)",'These are sql statements that should be using bind variables'),*/
 178  	 	 'index cache cost' => array('COST',
 179  	 	 	 "select value from v\$parameter where name = 'optimizer_index_caching'",
 180  	 	 	 '=WarnIndexCost'),
 181  	 	 'random page cost' => array('COST',
 182  	 	 	 "select value from v\$parameter where name = 'optimizer_index_cost_adj'",
 183  	 	 	 '=WarnPageCost'),
 184  	 'Waits',
 185  	 	 'Recent wait events' => array('WAITS','select \'Top 5 events\' from dual','=TopRecentWaits'),
 186  //	 	 'Historical wait SQL' => array('WAITS','select \'Last 2 days\' from dual','=TopHistoricalWaits'), -- requires AWR license
 187  	 'Backup',
 188  	 	 'Achivelog Mode' => array('BACKUP', 'select log_mode from v$database', '=LogMode'),
 189  
 190  	 	 'DBID' => array('BACKUP','select dbid from v$database','Primary key of database, used for recovery with an RMAN Recovery Catalog'),
 191  	 	 'Archive Log Dest' => array('BACKUP', "SELECT NVL(v1.value,v2.value)
 192  FROM v\$parameter v1, v\$parameter v2 WHERE v1.name='log_archive_dest' AND v2.name='log_archive_dest_10'", ''),
 193  
 194  	 	 'Flashback Area' => array('BACKUP', "select nvl(value,'Flashback Area not used') from v\$parameter where name=lower('DB_RECOVERY_FILE_DEST')", 'Flashback area is a folder where all backup data and logs can be stored and managed by Oracle. If Error: message displayed, then it is not in use.'),
 195  
 196  	 	 'Flashback Usage' => array('BACKUP', "select nvl('-','Flashback Area not used') from v\$parameter where name=lower('DB_RECOVERY_FILE_DEST')", '=FlashUsage', 'Flashback area usage.'),
 197  
 198  	 	 'Control File Keep Time' => array('BACKUP', "select value from v\$parameter where name='control_file_record_keep_time'",'No of days to keep RMAN info in control file.  Recommended set to x2 or x3 times the frequency of your full backup.'),
 199  	 	 'Recent RMAN Jobs' => array('BACKUP', "select '-' from dual", "=RMAN"),
 200  
 201  	 	 //	 	 'Control File Keep Time' => array('BACKUP', "select value from v\$parameter where name='control_file_record_keep_time'",'No of days to keep RMAN info in control file. I recommend it be set to x2 or x3 times the frequency of your full backup.'),
 202        'Storage', 'Tablespaces' => array('TABLESPACE', "select '-' from dual", "=TableSpace"),
 203  	 	 false
 204  
 205  	 );
 206  
 207  
 208  	function __construct(&$conn)
 209  	 {
 210  	 global $gSQLBlockRows;
 211  
 212  	 	 $gSQLBlockRows = 1000;
 213  	 	 $savelog = $conn->LogSQL(false);
 214  	 	 $this->version = $conn->ServerInfo();
 215  	 	 $conn->LogSQL($savelog);
 216  	 	 $this->conn = $conn;
 217  	 }
 218  
 219  	function LogMode()
 220  	 {
 221  	 	 $mode = $this->conn->GetOne("select log_mode from v\$database");
 222  
 223  	 	 if ($mode == 'ARCHIVELOG') return 'To turn off archivelog:<br>
 224  	 <pre><font size=-2>
 225          SQLPLUS> connect sys as sysdba;
 226          SQLPLUS> shutdown immediate;
 227  
 228          SQLPLUS> startup mount exclusive;
 229          SQLPLUS> alter database noarchivelog;
 230          SQLPLUS> alter database open;
 231  </font></pre>';
 232  
 233  	 	 return 'To turn on archivelog:<br>
 234  	 <pre><font size=-2>
 235          SQLPLUS> connect sys as sysdba;
 236          SQLPLUS> shutdown immediate;
 237  
 238          SQLPLUS> startup mount exclusive;
 239          SQLPLUS> alter database archivelog;
 240          SQLPLUS> archive log start;
 241          SQLPLUS> alter database open;
 242  </font></pre>';
 243  	 }
 244  
 245  	function TopRecentWaits()
 246  	 {
 247  
 248  	 	 $rs = $this->conn->Execute("select * from (
 249  	 	 select event, round(100*time_waited/(select sum(time_waited) from v\$system_event where wait_class <> 'Idle'),1) \"% Wait\",
 250      total_waits,time_waited, average_wait,wait_class from v\$system_event where wait_class <> 'Idle' order by 2 desc
 251  	 ) where rownum <=5");
 252  
 253  	 	 $ret = rs2html($rs,false,false,false,false);
 254  	 	 return "&nbsp;<p>".$ret."&nbsp;</p>";
 255  
 256  	 }
 257  
 258  	function TopHistoricalWaits()
 259  	 {
 260  	 	 $days = 2;
 261  
 262  	 	 $rs = $this->conn->Execute("select * from (   SELECT
 263           b.wait_class,B.NAME,
 264          round(sum(wait_time+TIME_WAITED)/1000000) waitsecs,
 265          parsing_schema_name,
 266          C.SQL_TEXT, a.sql_id
 267  FROM    V\$ACTIVE_SESSION_HISTORY A
 268          join V\$EVENT_NAME B  on  A.EVENT# = B.EVENT#
 269         join V\$SQLAREA C  on  A.SQL_ID = C.SQL_ID
 270  WHERE   A.SAMPLE_TIME BETWEEN sysdate-$days and sysdate
 271         and parsing_schema_name not in ('SYS','SYSMAN','DBSNMP','SYSTEM')
 272  GROUP BY b.wait_class,parsing_schema_name,C.SQL_TEXT, B.NAME,A.sql_id
 273  order by 3 desc) where rownum <=10");
 274  
 275  	 	 $ret = rs2html($rs,false,false,false,false);
 276  	 	 return "&nbsp;<p>".$ret."&nbsp;</p>";
 277  
 278  	 }
 279  
 280  	function TableSpace()
 281  	 {
 282  
 283  	 	 $rs = $this->conn->Execute(
 284  	 "select tablespace_name,round(sum(bytes)/1024/1024) as Used_MB,round(sum(maxbytes)/1024/1024) as Max_MB, round(sum(bytes)/sum(maxbytes),4) * 100 as PCT
 285  	 from dba_data_files
 286     group by tablespace_name order by 2 desc");
 287  
 288  	 	 $ret = "<p><b>Tablespace</b>".rs2html($rs,false,false,false,false);
 289  
 290  	 	 $rs = $this->conn->Execute("select * from dba_data_files order by tablespace_name, 1");
 291  	 	 $ret .= "<p><b>Datafile</b>".rs2html($rs,false,false,false,false);
 292  
 293  	 	 return "&nbsp;<p>".$ret."&nbsp;</p>";
 294  	 }
 295  
 296  	function RMAN()
 297  	 {
 298  	 	 $rs = $this->conn->Execute("select * from (select start_time, end_time, operation, status, mbytes_processed, output_device_type
 299  	 	 	 from V\$RMAN_STATUS order by start_time desc) where rownum <=10");
 300  
 301  	 	 $ret = rs2html($rs,false,false,false,false);
 302  	 	 return "&nbsp;<p>".$ret."&nbsp;</p>";
 303  
 304  	 }
 305  
 306  	function DynMemoryUsage()
 307  	 {
 308  	 	 if (@$this->version['version'] >= 11) {
 309  	 	 	 $rs = $this->conn->Execute("select component, current_size/1024./1024 as \"CurrSize (M)\" from  V\$MEMORY_DYNAMIC_COMPONENTS");
 310  
 311  	 	 } else
 312  	 	 	 $rs = $this->conn->Execute("select name, round(bytes/1024./1024,2) as \"CurrSize (M)\" from  V\$sgainfo");
 313  
 314  
 315  	 	 $ret = rs2html($rs,false,false,false,false);
 316  	 	 return "&nbsp;<p>".$ret."&nbsp;</p>";
 317  	 }
 318  
 319  	function FlashUsage()
 320  	 {
 321          $rs = $this->conn->Execute("select * from  V\$FLASH_RECOVERY_AREA_USAGE");
 322  	 	 $ret = rs2html($rs,false,false,false,false);
 323  	 	 return "&nbsp;<p>".$ret."&nbsp;</p>";
 324  	 }
 325  
 326  	function WarnPageCost($val)
 327  	 {
 328  	 	 if ($val == 100 && $this->version['version'] < 10) $s = '<font color=red><b>Too High</b>. </font>';
 329  	 	 else $s = '';
 330  
 331  	 	 return $s.'Recommended is 20-50 for TP, and 50 for data warehouses. Default is 100. See <a href=http://www.dba-oracle.com/oracle_tips_cost_adj.htm>optimizer_index_cost_adj</a>. ';
 332  	 }
 333  
 334  	function WarnIndexCost($val)
 335  	 {
 336  	 	 if ($val == 0 && $this->version['version'] < 10) $s = '<font color=red><b>Too Low</b>. </font>';
 337  	 	 else $s = '';
 338  
 339  	 	 return $s.'Percentage of indexed data blocks expected in the cache.
 340  	 	 	 Recommended is 20 (fast disk array) to 30 (slower hard disks). Default is 0.
 341  	 	 	  See <a href=http://www.dba-oracle.com/oracle_tips_cbo_part1.htm>optimizer_index_caching</a>.';
 342  	 	 }
 343  
 344  	function PGA()
 345  	 {
 346  
 347  	 	 //if ($this->version['version'] < 9) return 'Oracle 9i or later required';
 348  	 }
 349  
 350  	function PGA_Advice()
 351  	 {
 352  	 	 $t = "<h3>PGA Advice Estimate</h3>";
 353  	 	 if ($this->version['version'] < 9) return $t.'Oracle 9i or later required';
 354  
 355  	 	 $rs = $this->conn->Execute('select a.MB,
 356  	 	 	 case when a.targ = 1 then \'<<= Current \'
 357  	 	 	 when a.targ < 1  or a.pct <= b.pct then null
 358  	 	 	 else
 359  	 	 	 \'- BETTER than Current by \'||round(a.pct/b.pct*100-100,2)||\'%\' end as "Percent Improved",
 360  	 a.targ as  "PGA Size Factor",a.pct "% Perf"
 361  	 from
 362         (select round(pga_target_for_estimate/1024.0/1024.0,0) MB,
 363                pga_target_factor targ,estd_pga_cache_hit_percentage pct,rownum as r
 364                from v$pga_target_advice) a left join
 365         (select round(pga_target_for_estimate/1024.0/1024.0,0) MB,
 366                pga_target_factor targ,estd_pga_cache_hit_percentage pct,rownum as r
 367                from v$pga_target_advice) b on
 368        a.r = b.r+1 where
 369            b.pct < 100');
 370  	 	 if (!$rs) return $t."Only in 9i or later";
 371  	 //	 $rs->Close();
 372  	 	 if ($rs->EOF) return $t."PGA could be too big";
 373  
 374  	 	 return $t.rs2html($rs,false,false,true,false);
 375  	 }
 376  
 377  	function Explain($sql,$partial=false)
 378  	 {
 379  	 	 $savelog = $this->conn->LogSQL(false);
 380  	 	 $rs = $this->conn->SelectLimit("select ID FROM PLAN_TABLE");
 381  	 	 if (!$rs) {
 382  	 	 	 echo "<p><b>Missing PLAN_TABLE</b></p>
 383  <pre>
 384  CREATE TABLE PLAN_TABLE (
 385    STATEMENT_ID                    VARCHAR2(30),
 386    TIMESTAMP                       DATE,
 387    REMARKS                         VARCHAR2(80),
 388    OPERATION                       VARCHAR2(30),
 389    OPTIONS                         VARCHAR2(30),
 390    OBJECT_NODE                     VARCHAR2(128),
 391    OBJECT_OWNER                    VARCHAR2(30),
 392    OBJECT_NAME                     VARCHAR2(30),
 393    OBJECT_INSTANCE                 NUMBER(38),
 394    OBJECT_TYPE                     VARCHAR2(30),
 395    OPTIMIZER                       VARCHAR2(255),
 396    SEARCH_COLUMNS                  NUMBER,
 397    ID                              NUMBER(38),
 398    PARENT_ID                       NUMBER(38),
 399    POSITION                        NUMBER(38),
 400    COST                            NUMBER(38),
 401    CARDINALITY                     NUMBER(38),
 402    BYTES                           NUMBER(38),
 403    OTHER_TAG                       VARCHAR2(255),
 404    PARTITION_START                 VARCHAR2(255),
 405    PARTITION_STOP                  VARCHAR2(255),
 406    PARTITION_ID                    NUMBER(38),
 407    OTHER                           LONG,
 408    DISTRIBUTION                    VARCHAR2(30)
 409  );
 410  </pre>";
 411  	 	 	 return false;
 412  	 	 }
 413  
 414  	 	 $rs->Close();
 415  	 //	 $this->conn->debug=1;
 416  
 417  	 	 if ($partial) {
 418  	 	 	 $sqlq = $this->conn->qstr($sql.'%');
 419  	 	 	 $arr = $this->conn->GetArray("select distinct sql1 from adodb_logsql where sql1 like $sqlq");
 420  	 	 	 if ($arr) {
 421  	 	 	 	 foreach($arr as $row) {
 422  	 	 	 	 	 $sql = reset($row);
 423  	 	 	 	 	 if (crc32($sql) == $partial) break;
 424  	 	 	 	 }
 425  	 	 	 }
 426  	 	 }
 427  
 428  	 	 $s = "<p><b>Explain</b>: ".htmlspecialchars($sql)."</p>";
 429  
 430  	 	 $this->conn->BeginTrans();
 431  	 	 $id = "ADODB ".microtime();
 432  
 433  	 	 $rs = $this->conn->Execute("EXPLAIN PLAN SET STATEMENT_ID='$id' FOR $sql");
 434  	 	 $m = $this->conn->ErrorMsg();
 435  	 	 if ($m) {
 436  	 	 	 $this->conn->RollbackTrans();
 437  	 	 	 $this->conn->LogSQL($savelog);
 438  	 	 	 $s .= "<p>$m</p>";
 439  	 	 	 return $s;
 440  	 	 }
 441  	 	 $rs = $this->conn->Execute("
 442  	 	 select
 443    '<pre>'||lpad('--', (level-1)*2,'-') || trim(operation) || ' ' || trim(options)||'</pre>'  as Operation,
 444    object_name,COST,CARDINALITY,bytes
 445  	 	 FROM plan_table
 446  START WITH id = 0  and STATEMENT_ID='$id'
 447  CONNECT BY prior id=parent_id and statement_id='$id'");
 448  
 449  	 	 $s .= rs2html($rs,false,false,false,false);
 450  	 	 $this->conn->RollbackTrans();
 451  	 	 $this->conn->LogSQL($savelog);
 452  	 	 $s .= $this->Tracer($sql,$partial);
 453  	 	 return $s;
 454  	 }
 455  
 456  	function CheckMemory()
 457  	 {
 458  	 	 if ($this->version['version'] < 9) return 'Oracle 9i or later required';
 459  
 460  	 	  $rs = $this->conn->Execute("
 461  select  a.name Buffer_Pool, b.size_for_estimate as cache_mb_estimate,
 462  	 case when b.size_factor=1 then
 463     	 	 '&lt;&lt;= Current'
 464  	  when a.estd_physical_read_factor-b.estd_physical_read_factor > 0.001 and b.estd_physical_read_factor<1 then
 465  	 	 '- BETTER than current by ' || round((1-b.estd_physical_read_factor)/b.estd_physical_read_factor*100,2) || '%'
 466  	 else ' ' end as RATING,
 467     b.estd_physical_read_factor \"Phys. Reads Factor\",
 468     round((a.estd_physical_read_factor-b.estd_physical_read_factor)/b.estd_physical_read_factor*100,2) as \"% Improve\"
 469     from (select size_for_estimate,size_factor,estd_physical_read_factor,rownum  r,name from v\$db_cache_advice order by name,1) a ,
 470     (select size_for_estimate,size_factor,estd_physical_read_factor,rownum r,name from v\$db_cache_advice order by name,1) b
 471     where a.r = b.r-1 and a.name = b.name
 472    ");
 473  	 	 if (!$rs) return false;
 474  
 475  	 	 /*
 476  	 	 The v$db_cache_advice utility show the marginal changes in physical data block reads for different sizes of db_cache_size
 477  	 	 */
 478  	 	 $s = "<h3>Data Cache Advice Estimate</h3>";
 479  	 	 if ($rs->EOF) {
 480  	 	 	 $s .= "<p>Cache that is 50% of current size is still too big</p>";
 481  	 	 } else {
 482  	 	 	 $s .= "Ideal size of Data Cache is when %BETTER gets close to zero.";
 483  	 	 	 $s .= rs2html($rs,false,false,false,false);
 484  	 	 }
 485  	 	 return $s.$this->PGA_Advice();
 486  	 }
 487  
 488  	 /*
 489  	 	 Generate html for suspicious/expensive sql
 490  	 */
 491  	function tohtml(&$rs,$type)
 492  	 {
 493  	 	 $o1 = $rs->FetchField(0);
 494  	 	 $o2 = $rs->FetchField(1);
 495  	 	 $o3 = $rs->FetchField(2);
 496  	 	 if ($rs->EOF) return '<p>None found</p>';
 497  	 	 $check = '';
 498  	 	 $sql = '';
 499  	 	 $s = "\n\n<table border=1 bgcolor=white><tr><td><b>".$o1->name.'</b></td><td><b>'.$o2->name.'</b></td><td><b>'.$o3->name.'</b></td></tr>';
 500  	 	 while (!$rs->EOF) {
 501  	 	 	 if ($check != $rs->fields[0].'::'.$rs->fields[1]) {
 502  	 	 	 	 if ($check) {
 503  	 	 	 	 	 $carr = explode('::',$check);
 504  	 	 	 	 	 $prefix = "<a href=\"?$type=1&sql=".rawurlencode($sql).'&x#explain">';
 505  	 	 	 	 	 $suffix = '</a>';
 506  	 	 	 	 	 if (strlen($prefix)>2000) {
 507  	 	 	 	 	 	 $prefix = '';
 508  	 	 	 	 	 	 $suffix = '';
 509  	 	 	 	 	 }
 510  
 511  	 	 	 	 	 $s .=  "\n<tr><td align=right>".$carr[0].'</td><td align=right>'.$carr[1].'</td><td>'.$prefix.$sql.$suffix.'</td></tr>';
 512  	 	 	 	 }
 513  	 	 	 	 $sql = $rs->fields[2];
 514  	 	 	 	 $check = $rs->fields[0].'::'.$rs->fields[1];
 515  	 	 	 } else
 516  	 	 	 	 $sql .= $rs->fields[2];
 517  	 	 	 if (substr($sql,strlen($sql)-1) == "\0") $sql = substr($sql,0,strlen($sql)-1);
 518  	 	 	 $rs->MoveNext();
 519  	 	 }
 520  	 	 $rs->Close();
 521  
 522  	 	 $carr = explode('::',$check);
 523  	 	 $prefix = "<a target=".rand()." href=\"?&hidem=1&$type=1&sql=".rawurlencode($sql).'&x#explain">';
 524  	 	 $suffix = '</a>';
 525  	 	 if (strlen($prefix)>2000) {
 526  	 	 	 $prefix = '';
 527  	 	 	 $suffix = '';
 528  	 	 }
 529  	 	 $s .=  "\n<tr><td align=right>".$carr[0].'</td><td align=right>'.$carr[1].'</td><td>'.$prefix.$sql.$suffix.'</td></tr>';
 530  
 531  	 	 return $s."</table>\n\n";
 532  	 }
 533  
 534  	 // code thanks to Ixora.
 535  	 // http://www.ixora.com.au/scripts/query_opt.htm
 536  	 // requires oracle 8.1.7 or later
 537  	function SuspiciousSQL($numsql=10)
 538  	 {
 539  	 	 $sql = "
 540  select
 541    substr(to_char(s.pct, '99.00'), 2) || '%'  load,
 542    s.executions  executes,
 543    p.sql_text
 544  from
 545    (
 546      select
 547        address,
 548        buffer_gets,
 549        executions,
 550        pct,
 551        rank() over (order by buffer_gets desc)  ranking
 552      from
 553        (
 554  	 select
 555  	   address,
 556  	   buffer_gets,
 557  	   executions,
 558  	   100 * ratio_to_report(buffer_gets) over ()  pct
 559  	 from
 560  	   sys.v_\$sql
 561  	 where
 562  	   command_type != 47 and module != 'T.O.A.D.'
 563        )
 564      where
 565        buffer_gets > 50 * executions
 566    )  s,
 567    sys.v_\$sqltext  p
 568  where
 569    s.ranking <= $numsql and
 570    p.address = s.address
 571  order by
 572    1 desc, s.address, p.piece";
 573  
 574    	 	 global $ADODB_CACHE_MODE;
 575    	 	 if (isset($_GET['expsixora']) && isset($_GET['sql'])) {
 576  	 	 	 	 $partial = empty($_GET['part']);
 577  	 	 	 	 echo "<a name=explain></a>".$this->Explain($_GET['sql'],$partial)."\n";
 578  	 	 }
 579  
 580  	 	 if (isset($_GET['sql'])) return $this->_SuspiciousSQL($numsql);
 581  
 582  	 	 $s = '';
 583  	 	 $timer = time();
 584  	 	 $s .= $this->_SuspiciousSQL($numsql);
 585  	 	 $timer = time() - $timer;
 586  
 587  	 	 if ($timer > $this->noShowIxora) return $s;
 588  	 	 $s .= '<p>';
 589  
 590  	 	 $save = $ADODB_CACHE_MODE;
 591  	 	 $ADODB_CACHE_MODE = ADODB_FETCH_NUM;
 592  	 	 if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
 593  
 594  	 	 $savelog = $this->conn->LogSQL(false);
 595  	 	 $rs = $this->conn->SelectLimit($sql);
 596  	 	 $this->conn->LogSQL($savelog);
 597  
 598  	 	 if (isset($savem)) $this->conn->SetFetchMode($savem);
 599  	 	 $ADODB_CACHE_MODE = $save;
 600  	 	 if ($rs) {
 601  	 	 	 $s .= "\n<h3>Ixora Suspicious SQL</h3>";
 602  	 	 	 $s .= $this->tohtml($rs,'expsixora');
 603  	 	 }
 604  
 605  	 	 return $s;
 606  	 }
 607  
 608  	 // code thanks to Ixora.
 609  	 // http://www.ixora.com.au/scripts/query_opt.htm
 610  	 // requires oracle 8.1.7 or later
 611  	function ExpensiveSQL($numsql = 10)
 612  	 {
 613  	 	 $sql = "
 614  select
 615    substr(to_char(s.pct, '99.00'), 2) || '%'  load,
 616    s.executions  executes,
 617    p.sql_text
 618  from
 619    (
 620      select
 621        address,
 622        disk_reads,
 623        executions,
 624        pct,
 625        rank() over (order by disk_reads desc)  ranking
 626      from
 627        (
 628  	 select
 629  	   address,
 630  	   disk_reads,
 631  	   executions,
 632  	   100 * ratio_to_report(disk_reads) over ()  pct
 633  	 from
 634  	   sys.v_\$sql
 635  	 where
 636  	   command_type != 47 and module != 'T.O.A.D.'
 637        )
 638      where
 639        disk_reads > 50 * executions
 640    )  s,
 641    sys.v_\$sqltext  p
 642  where
 643    s.ranking <= $numsql and
 644    p.address = s.address
 645  order by
 646    1 desc, s.address, p.piece
 647  ";
 648  	 	 global $ADODB_CACHE_MODE;
 649    	 	 if (isset($_GET['expeixora']) && isset($_GET['sql'])) {
 650  	 	 	 $partial = empty($_GET['part']);
 651  	 	 	 echo "<a name=explain></a>".$this->Explain($_GET['sql'],$partial)."\n";
 652  	 	 }
 653  	 	 if (isset($_GET['sql'])) {
 654  	 	 	  $var = $this->_ExpensiveSQL($numsql);
 655  	 	 	  return $var;
 656  	 	 }
 657  
 658  	 	 $s = '';
 659  	 	 $timer = time();
 660  	 	 $s .= $this->_ExpensiveSQL($numsql);
 661  	 	 $timer = time() - $timer;
 662  	 	 if ($timer > $this->noShowIxora) return $s;
 663  
 664  	 	 $s .= '<p>';
 665  	 	 $save = $ADODB_CACHE_MODE;
 666  	 	 $ADODB_CACHE_MODE = ADODB_FETCH_NUM;
 667  	 	 if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
 668  
 669  	 	 $savelog = $this->conn->LogSQL(false);
 670  	 	 $rs = $this->conn->Execute($sql);
 671  	 	 $this->conn->LogSQL($savelog);
 672  
 673  	 	 if (isset($savem)) $this->conn->SetFetchMode($savem);
 674  	 	 $ADODB_CACHE_MODE = $save;
 675  
 676  	 	 if ($rs) {
 677  	 	 	 $s .= "\n<h3>Ixora Expensive SQL</h3>";
 678  	 	 	 $s .= $this->tohtml($rs,'expeixora');
 679  	 	 }
 680  
 681  	 	 return $s;
 682  	 }
 683  
 684  	function clearsql()
 685  	 {
 686  	 	 $perf_table = adodb_perf::table();
 687  	 // using the naive "delete from $perf_table where created<".$this->conn->sysTimeStamp will cause the table to lock, possibly
 688  	 // for a long time
 689  	 	 $sql =
 690  "DECLARE cnt pls_integer;
 691  BEGIN
 692  	 cnt := 0;
 693  	 FOR rec IN (SELECT ROWID AS rr FROM $perf_table WHERE created<SYSDATE)
 694  	 LOOP
 695  	   cnt := cnt + 1;
 696  	   DELETE FROM $perf_table WHERE ROWID=rec.rr;
 697  	   IF cnt = 1000 THEN
 698  	   	 COMMIT;
 699  	 	 cnt := 0;
 700  	   END IF;
 701  	 END LOOP;
 702  	 commit;
 703  END;";
 704  
 705  	 	 $ok = $this->conn->Execute($sql);
 706  	 }
 707  
 708  }