00001 <?php
00002
00003
00008 class oxDebugDb
00009 {
00010
00011 private static $_aSkipSqls = array();
00012
00013 protected static function _skipWhiteSpace($str){
00014 return str_replace(array(' ', "\t", "\r", "\n"), '', $str);
00015 }
00016
00017 protected static function _isSkipped($sSql){
00018 if (!count(self::$_aSkipSqls)) {
00019 $file = file_get_contents(realpath(dirname(__FILE__).'/..').'/oxdebugdb_skipped.sql');
00020 $m = explode('-- -- ENTRY END', $file);
00021 foreach ($m as $n) {
00022 $n = self::_skipWhiteSpace($n);
00023 if ($n)
00024 self::$_aSkipSqls[md5($n)] = true;
00025 }
00026 }
00027 $checkTpl = md5(self::_skipWhiteSpace(self::getSqlTemplate($sSql)));
00028 $check = md5(self::_skipWhiteSpace($sSql));
00029 return self::$_aSkipSqls[$check] || self::$_aSkipSqls[$checkTpl];
00030 }
00031
00037 public function getWarnings()
00038 {
00039 $aWarnings = array();
00040 $aHistory = array();
00041 $oDb = oxDb::getDb();
00042 $_lastDbgState = $oDb->LogSQL( false );
00043 $rs = $oDb->Execute("select sql0, sql1, tracer from adodb_logsql order by created limit 5000");
00044 if ($rs != false && $rs->recordCount() > 0) {
00045 $_lastRecord = null;
00046 while (!$rs->EOF) {
00047 $sId = $rs->fields[0];
00048 $sSql = $rs->fields[1];
00049
00050 if (!self::_isSkipped($sSql)) {
00051 if ($this->_checkMissingKeys($sSql)) {
00052 $aWarnings['MissingKeys'][$sId] = true;
00053
00054 }
00055 }
00056
00057
00058 if ($_lastRecord && $this->_checkMess($sSql,$_lastRecord[1])) {
00059
00060 $aWarnings['MESS'][$sId] = true;
00061 $aWarnings['MESS'][$_lastRecord[0]] = true;
00062 }
00063
00064 foreach ($aHistory as $aHistItem) {
00065 if ($this->_checkMess($sSql,$aHistItem[1])) {
00066
00067 $aWarnings['MESS_ALL'][$sId] = true;
00068 $aWarnings['MESS_ALL'][$aHistItem[0]] = true;
00069 }
00070 }
00071
00072 $aHistory[] = $_lastRecord = $rs->fields;
00073
00074
00075
00076
00077 $rs->moveNext();
00078 }
00079 }
00080 $aWarnings = $this->_generateWarningsResult($aWarnings);
00081 $this->_logToFile($aWarnings);
00082 $oDb->LogSQL( $_lastDbgState );
00083 return $aWarnings;
00084 }
00085
00091 protected function _generateWarningsResult($aInput)
00092 {
00093 $aOutput = array();
00094 $oDb = oxDb::getDb();
00095 foreach ($aInput as $fnc => $aWarnings) {
00096 $ids = implode("','", array_keys($aWarnings));
00097 $rs = $oDb->Execute("select sql1, timer, tracer from adodb_logsql where sql0 in ('$ids')");
00098 if ($rs != false && $rs->recordCount() > 0) {
00099 while (!$rs->EOF) {
00100 $aOutputEntry = array();
00101 $aOutputEntry['check'] = $fnc;
00102 $aOutputEntry['sql'] = $rs->fields[0];
00103 $aOutputEntry['time'] = $rs->fields[1];
00104 $aOutputEntry['trace'] = $rs->fields[2];
00105 $aOutput[] = $aOutputEntry;
00106 $rs->moveNext();
00107 }
00108 }
00109 }
00110 return $aOutput;
00111 }
00112
00119 protected function _checkMissingKeys($sSql)
00120 {
00121 if (strpos(strtolower(trim($sSql)), 'select ') !== 0)
00122 return false;
00123
00124 $oDb = oxDb::getDb(true);
00125 $rs = $oDb->Execute("explain $sSql");
00126 if ($rs != false && $rs->recordCount() > 0) {
00127 while (!$rs->EOF) {
00128 if ($this->_missingKeysChecker($rs->fields))
00129 return true;
00130 $rs->moveNext();
00131 }
00132 }
00133 return false;
00134 }
00135
00142 private function _missingKeysChecker($aExplain)
00143 {
00144
00145 if ($aExplain['type'] == 'system') {
00146 return false;
00147 }
00148
00149 if ($aExplain['key'] === null)
00150 return true;
00151
00152 if (strpos($aExplain['type'], 'range'))
00153 return true;
00154 if (strpos($aExplain['type'], 'index'))
00155 return true;
00156 if (strpos($aExplain['type'], 'ALL'))
00157 return true;
00158
00159 if (strpos($aExplain['Extra'], 'filesort')) {
00160 if (strpos($aExplain['ref'], 'const') === false)
00161 return true;
00162 }
00163 if (strpos($aExplain['Extra'], 'temporary'))
00164 return true;
00165
00166 return false;
00167 }
00168
00176 protected function _checkMess($s1, $s2)
00177 {
00178 if (strpos(strtolower(trim($s1)), 'select ') !== 0)
00179 return false;
00180 if (strpos(strtolower(trim($s2)), 'select ') !== 0)
00181 return false;
00182
00183
00184 $s1 = self::getSqlTemplate($s1);
00185 $s2 = self::getSqlTemplate($s2);
00186
00187 if (!strcmp($s1, $s2)) {
00188 return true;
00189 }
00190
00191 return false;
00192 }
00193
00200 protected static function getSqlTemplate($sSql) {
00201 $sSql = preg_replace("/'.*?(?<!\\\\)'/", "'#VALUE#'", $sSql);
00202 $sSql = preg_replace('/".*?(?<!\\\\)"/', '"#VALUE#"', $sSql);
00203 $sSql = preg_replace('/[0-9]/', '#NUMVALUE#', $sSql);
00204 return $sSql;
00205 }
00206
00207
00208
00212 protected function _logToFile($aWarnings)
00213 {
00214 $s = "\n\n\n\n\n\n-- ".date("m-d H:i:s")." --\n\n";
00215 foreach ($aWarnings as $w)
00216 {
00217 $s .= "{$w['check']}: {$w['time']} - ".htmlentities($w['sql'])."\n\n";
00218 $s .= $w['trace']."\n\n\n\n";
00219 }
00220 $f = fopen(realpath(dirname(__FILE__).'/..').'/oxdebugdb.txt', "a+");
00221 if (!$f)
00222 return;
00223 fwrite($f, $s);
00224 fclose($f);
00225 }
00226 }