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
00085 }
00086 }
00087
00088
00089 if ($aLastRecord && $this->_checkMess($sSql, $aLastRecord[1])) {
00090
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
00098 $aWarnings['MESS_ALL'][$sId] = true;
00099 $aWarnings['MESS_ALL'][$aHistItem[0]] = true;
00100 }
00101 }
00102
00103 $aHistory[] = $aLastRecord = $rs->fields;
00104
00105
00106
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
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 }