oxdebugdb.php

Go to the documentation of this file.
00001 <?php
00002 
00006 class oxDebugDb
00007 {
00008 
00014     private static $_aSkipSqls = array();
00015 
00019     public function __construct()
00020     {
00021     }
00022 
00030     protected static function _skipWhiteSpace($sStr)
00031     {
00032         return str_replace(array(' ', "\t", "\r", "\n"), '', $sStr);
00033     }
00034 
00042     protected static function _isSkipped($sSql)
00043     {
00044         if (!count(self::$_aSkipSqls)) {
00045             $sFile = oxRegistry::getConfig()->getLogsDir() . 'oxdebugdb_skipped.sql';
00046             if (is_readable($sFile)) {
00047                 $aSkip = explode('-- -- ENTRY END', file_get_contents($sFile));
00048                 foreach ($aSkip as $sQ) {
00049                     if (($sQ = self::_skipWhiteSpace($sQ))) {
00050                         self::$_aSkipSqls[md5($sQ)] = true;
00051                     }
00052                 }
00053             }
00054         }
00055         $checkTpl = md5(self::_skipWhiteSpace(self::_getSqlTemplate($sSql)));
00056         $check = md5(self::_skipWhiteSpace($sSql));
00057 
00058         return self::$_aSkipSqls[$check] || self::$_aSkipSqls[$checkTpl];
00059     }
00060 
00066     public function getWarnings()
00067     {
00068         $aWarnings = array();
00069         $aHistory = array();
00070         $oDb = oxDb::getDb();
00071         if (method_exists($oDb, "logSQL")) {
00072             $iLastDbgState = $oDb->logSQL(false);
00073         }
00074         $rs = $oDb->select("select sql0, sql1, tracer from adodb_logsql order by created limit 5000");
00075         if ($rs != false && $rs->recordCount() > 0) {
00076             $aLastRecord = null;
00077             while (!$rs->EOF) {
00078                 $sId = $rs->fields[0];
00079                 $sSql = $rs->fields[1];
00080 
00081                 if (!self::_isSkipped($sSql)) {
00082                     if ($this->_checkMissingKeys($sSql)) {
00083                         $aWarnings['MissingKeys'][$sId] = true;
00084                         // debug: echo "<li> <pre>".self::_getSqlTemplate($sSql)." </pre><br>";
00085                     }
00086                 }
00087 
00088                 // multiple executed single statements
00089                 if ($aLastRecord && $this->_checkMess($sSql, $aLastRecord[1])) {
00090                     // sql0 matches, also, this is exactly following statement: MESS?
00091                     $aWarnings['MESS'][$sId] = true;
00092                     $aWarnings['MESS'][$aLastRecord[0]] = true;
00093                 }
00094 
00095                 foreach ($aHistory as $aHistItem) {
00096                     if ($this->_checkMess($sSql, $aHistItem[1])) {
00097                         // sql0 matches, also, this is exactly following statement: MESS?
00098                         $aWarnings['MESS_ALL'][$sId] = true;
00099                         $aWarnings['MESS_ALL'][$aHistItem[0]] = true;
00100                     }
00101                 }
00102 
00103                 $aHistory[] = $aLastRecord = $rs->fields;
00104                 /*
00105                 if (preg_match('/select[^\*]*(?<!(from))\*.*?(?<!(from))from/im', $sSql)) {
00106                     $aWarnings['Select fields not strict'][$sId] = true;
00107                 }*/
00108                 $rs->moveNext();
00109             }
00110         }
00111         $aWarnings = $this->_generateWarningsResult($aWarnings);
00112         $this->_logToFile($aWarnings);
00113         if (method_exists($oDb, "logSQL")) {
00114             $oDb->logSQL($iLastDbgState);
00115         }
00116 
00117         return $aWarnings;
00118     }
00119 
00127     protected function _generateWarningsResult($aInput)
00128     {
00129         $aOutput = array();
00130         $oDb = oxDb::getDb();
00131         foreach ($aInput as $fnc => $aWarnings) {
00132             $ids = implode(",", oxDb::getInstance()->quoteArray(array_keys($aWarnings)));
00133             $rs = $oDb->select("select sql1, timer, tracer from adodb_logsql where sql0 in ($ids)");
00134             if ($rs != false && $rs->recordCount() > 0) {
00135                 while (!$rs->EOF) {
00136                     $aOutputEntry = array();
00137                     $aOutputEntry['check'] = $fnc;
00138                     $aOutputEntry['sql'] = $rs->fields[0];
00139                     $aOutputEntry['time'] = $rs->fields[1];
00140                     $aOutputEntry['trace'] = $rs->fields[2];
00141                     $aOutput[] = $aOutputEntry;
00142                     $rs->moveNext();
00143                 }
00144             }
00145         }
00146 
00147         return $aOutput;
00148     }
00149 
00158     protected function _checkMissingKeys($sSql)
00159     {
00160         if (strpos(strtolower(trim($sSql)), 'select ') !== 0) {
00161             return false;
00162         }
00163 
00164         $rs = oxDb::getDb(oxDb::FETCH_MODE_ASSOC)->execute("explain $sSql");
00165         if ($rs != false && $rs->recordCount() > 0) {
00166             while (!$rs->EOF) {
00167                 if ($this->_missingKeysChecker($rs->fields)) {
00168                     return true;
00169                 }
00170                 $rs->moveNext();
00171             }
00172         }
00173 
00174         return false;
00175     }
00176 
00185     private function _missingKeysChecker($aExplain)
00186     {
00187         if ($aExplain['type'] == 'system') {
00188             return false;
00189         }
00190 
00191         if (strstr($aExplain['Extra'], 'Impossible WHERE') !== false) {
00192             return false;
00193         }
00194 
00195         if ($aExplain['key'] === null) {
00196             return true;
00197         }
00198 
00199         if (strpos($aExplain['type'], 'range')) {
00200             return true;
00201         }
00202 
00203         if (strpos($aExplain['type'], 'index')) {
00204             return true;
00205         }
00206 
00207         if (strpos($aExplain['type'], 'ALL')) {
00208             return true;
00209         }
00210 
00211         if (strpos($aExplain['Extra'], 'filesort')) {
00212             if (strpos($aExplain['ref'], 'const') === false) {
00213                 return true;
00214             }
00215         }
00216 
00217         if (strpos($aExplain['Extra'], 'temporary')) {
00218             return true;
00219         }
00220 
00221         return false;
00222     }
00223 
00232     protected function _checkMess($s1, $s2)
00233     {
00234         if (strpos(strtolower(trim($s1)), 'select ') !== 0) {
00235             return false;
00236         }
00237 
00238         if (strpos(strtolower(trim($s2)), 'select ') !== 0) {
00239             return false;
00240         }
00241 
00242         // strip from values
00243         $s1 = self::_getSqlTemplate($s1);
00244         $s2 = self::_getSqlTemplate($s2);
00245 
00246         if (!strcmp($s1, $s2)) {
00247             return true;
00248         }
00249 
00250         return false;
00251     }
00252 
00260     protected static function _getSqlTemplate($sSql)
00261     {
00262         $sSql = preg_replace("/'.*?(?<!\\\\)'/", "'#VALUE#'", $sSql);
00263         $sSql = preg_replace('/".*?(?<!\\\\)"/', '"#VALUE#"', $sSql);
00264         $sSql = preg_replace('/[0-9]/', '#NUMVALUE#', $sSql);
00265 
00266         return $sSql;
00267     }
00268 
00274     protected function _logToFile($aWarnings)
00275     {
00276         $oStr = getStr();
00277         $sLogMsg = "\n\n\n\n\n\n-- " . date("m-d  H:i:s") . " --\n\n";
00278         foreach ($aWarnings as $w) {
00279             $sLogMsg .= "{$w['check']}: {$w['time']} - " . $oStr->htmlentities($w['sql']) . "\n\n";
00280             $sLogMsg .= $w['trace'] . "\n\n\n\n";
00281         }
00282         oxRegistry::getUtils()->writeToLog($sLogMsg, 'oxdebugdb.txt');
00283     }
00284 }