/** * Usually aborts on failure * If errors are explicitly ignored, returns success */ function query($sql, $fname = '', $tempIgnore = false) { global $wgProfiling; if ($wgProfiling) { # generalizeSQL will probably cut down the query to reasonable # logging size most of the time. The substr is really just a sanity check. # Who's been wasting my precious column space? -- TS #$profName = 'query: ' . $fname . ' ' . substr( Database::generalizeSQL( $sql ), 0, 255 ); if (is_null($this->getLBInfo('master'))) { $queryProf = 'query: ' . substr(Database::generalizeSQL($sql), 0, 255); $totalProf = 'Database::query'; } else { $queryProf = 'query-m: ' . substr(Database::generalizeSQL($sql), 0, 255); $totalProf = 'Database::query-master'; } wfProfileIn($totalProf); wfProfileIn($queryProf); } $this->mLastQuery = $sql; # Add a comment for easy SHOW PROCESSLIST interpretation if ($fname) { $commentedSql = preg_replace("/\\s/", " /* {$fname} */ ", $sql, 1); } else { $commentedSql = $sql; } # If DBO_TRX is set, start a transaction if ($this->mFlags & DBO_TRX && !$this->trxLevel() && $sql != 'BEGIN' && $sql != 'COMMIT' && $sql != 'ROLLBACK') { $this->begin(); } if ($this->debug()) { $sqlx = substr($commentedSql, 0, 500); $sqlx = strtr($sqlx, "\t\n", ' '); wfDebug("SQL: {$sqlx}\n"); } # Do the query and handle errors $ret = $this->doQuery($commentedSql); # Try reconnecting if the connection was lost if (false === $ret && ($this->lastErrno() == 2013 || $this->lastErrno() == 2006)) { # Transaction is gone, like it or not $this->mTrxLevel = 0; wfDebug("Connection lost, reconnecting...\n"); if ($this->ping()) { wfDebug("Reconnected\n"); $ret = $this->doQuery($commentedSql); } else { wfDebug("Failed\n"); } } if (false === $ret) { $this->reportQueryError($this->lastError(), $this->lastErrno(), $sql, $fname, $tempIgnore); } if ($wgProfiling) { wfProfileOut($queryProf); wfProfileOut($totalProf); } return $ret; }
/** * Usually aborts on failure. If errors are explicitly ignored, returns success. * * @param $sql String: SQL query * @param $fname String: Name of the calling function, for profiling/SHOW PROCESSLIST * comment (you can use __METHOD__ or add some extra info) * @param $tempIgnore Bool: Whether to avoid throwing an exception on errors... * maybe best to catch the exception instead? * @return true for a successful write query, ResultWrapper object for a successful read query, * or false on failure if $tempIgnore set * @throws DBQueryError Thrown when the database returns an error of any kind */ public function query($sql, $fname = '', $tempIgnore = false) { global $wgProfiling; // XXADDED by Reuben to log slower queries global $wgCommandLineMode; $LOG_SLOW = 30; $startTime = microtime(true); if ($wgProfiling) { # generalizeSQL will probably cut down the query to reasonable # logging size most of the time. The substr is really just a sanity check. # Who's been wasting my precious column space? -- TS #$profName = 'query: ' . $fname . ' ' . substr( Database::generalizeSQL( $sql ), 0, 255 ); if (is_null($this->getLBInfo('master'))) { $queryProf = 'query: ' . substr(Database::generalizeSQL($sql), 0, 255); $totalProf = 'Database::query'; } else { $queryProf = 'query-m: ' . substr(Database::generalizeSQL($sql), 0, 255); $totalProf = 'Database::query-master'; } wfProfileIn($totalProf); wfProfileIn($queryProf); } $this->mLastQuery = $sql; # Add a comment for easy SHOW PROCESSLIST interpretation #if ( $fname ) { global $wgUser; if (is_object($wgUser) && !$wgUser instanceof StubObject) { $userName = $wgUser->getName(); if (mb_strlen($userName) > 15) { $userName = mb_substr($userName, 0, 15) . '...'; } $userName = str_replace('/', '', $userName); } else { $userName = ''; } $commentedSql = preg_replace('/\\s/', " /* {$fname} {$userName} */ ", $sql, 1); #} else { # $commentedSql = $sql; #} # If DBO_TRX is set, start a transaction if ($this->mFlags & DBO_TRX && !$this->trxLevel() && $sql != 'BEGIN' && $sql != 'COMMIT' && $sql != 'ROLLBACK') { // avoid establishing transactions for SHOW and SET statements too - // that would delay transaction initializations to once connection // is really used by application $sqlstart = substr($sql, 0, 10); // very much worth it, benchmark certified(tm) if (strpos($sqlstart, "SHOW ") !== 0 and strpos($sqlstart, "SET ") !== 0) { $this->begin(); } } if ($this->debug()) { $sqlx = substr($commentedSql, 0, 500); $sqlx = strtr($sqlx, "\t\n", ' '); wfDebug("SQL: {$sqlx}\n"); } # Do the query and handle errors $ret = $this->doQuery($commentedSql); # Try reconnecting if the connection was lost if (false === $ret && ($this->lastErrno() == 2013 || $this->lastErrno() == 2006)) { # Transaction is gone, like it or not $this->mTrxLevel = 0; wfDebug("Connection lost, reconnecting...\n"); if ($this->ping()) { wfDebug("Reconnected\n"); $sqlx = substr($commentedSql, 0, 500); $sqlx = strtr($sqlx, "\t\n", ' '); global $wgRequestTime; $elapsed = round(microtime(true) - $wgRequestTime, 3); wfLogDBError("Connection lost and reconnected after {$elapsed}s, query: {$sqlx}\n"); $ret = $this->doQuery($commentedSql); } else { wfDebug("Failed\n"); } // XXCHANGED -- Reuben hack: we've found that our database drivers timeout // after 10 minutes of inactivity in a long-running script. We've found that // the mysql_ping() that's called right now doesn't work to reconnect, but // that closing and re-opening the connection usually works. if (false === $ret && $this->lastErrno() == 2006) { error_log('Encountered Reuben MySQL error 2006 DB hack. Re-connecting to server ' . $this->mServer); @$this->close(); $this->mConn = null; $this->open($this->mServer, $this->mUser, $this->mPassword, $this->mDBname); $this->ping(); $ret = $this->doQuery($commentedSql); } } if (false === $ret) { $this->reportQueryError($this->lastError(), $this->lastErrno(), $sql, $fname, $tempIgnore); } if (!defined('IS_PROD_EN_SITE')) { define('IS_PROD_EN_SITE', true); } if (!$wgCommandLineMode && IS_PROD_EN_SITE && $LOG_SLOW > 0) { $total = microtime(true) - $startTime; if ($total >= $LOG_SLOW) { $prof = substr($sql, 0, 1000); $totalStr = sprintf('%.2f', $total) . 's'; $backtrace = wfBacktrace(); $hostname = gethostname(); if (strpos($hostname, 'spare') === false) { $msg = "This query took {$totalStr}:<br>\n<br>\n{$prof}<br>\n<br>\n{$backtrace}<br>\nGenerated on {$hostname}<br>\n"; $headers = 'MIME-Version: 1.0' . "\r\n"; $headers .= 'Content-type: text/html; charset=iso-8859-1' . "\r\n"; mail('reuben@wikihow.com,jordan@wikihow.com', 'Slow query: ' . $totalStr, $msg, $headers); } } } if ($wgProfiling) { wfProfileOut($queryProf); wfProfileOut($totalProf); } return $this->resultObject($ret); }
/** * Usually aborts on failure * If errors are explicitly ignored, returns success */ function query($sql, $fname = '', $tempIgnore = false) { global $wgProfiling, $wgCommandLineMode; if ($wgProfiling) { # generalizeSQL will probably cut down the query to reasonable # logging size most of the time. The substr is really just a sanity check. $profName = 'query: ' . $fname . ' ' . substr(Database::generalizeSQL($sql), 0, 255); wfProfileIn('Database::query'); wfProfileIn($profName); } $this->mLastQuery = $sql; # Add a comment for easy SHOW PROCESSLIST interpretation if ($fname) { $commentedSql = "/* {$fname} */ {$sql}"; } else { $commentedSql = $sql; } # If DBO_TRX is set, start a transaction if ($this->mFlags & DBO_TRX && !$this->trxLevel() && $sql != 'BEGIN') { $this->begin(); } if ($this->debug()) { $sqlx = substr($commentedSql, 0, 500); $sqlx = strtr($sqlx, "\t\n", ' '); wfDebug("SQL: {$sqlx}\n"); } # Do the query and handle errors $ret = $this->doQuery($commentedSql); # Try reconnecting if the connection was lost if (false === $ret && ($this->lastErrno() == 2013 || $this->lastErrno() == 2006)) { # Transaction is gone, like it or not $this->mTrxLevel = 0; wfDebug("Connection lost, reconnecting...\n"); if ($this->ping()) { wfDebug("Reconnected\n"); $ret = $this->doQuery($commentedSql); } else { wfDebug("Failed\n"); } } if (false === $ret) { $this->reportQueryError($this->lastError(), $this->lastErrno(), $sql, $fname, $tempIgnore); } if ($wgProfiling) { wfProfileOut($profName); wfProfileOut('Database::query'); } return $ret; }
/** * Usually aborts on failure. If errors are explicitly ignored, returns success. * * @param $sql String: SQL query * @param $fname String: Name of the calling function, for profiling/SHOW PROCESSLIST * comment (you can use __METHOD__ or add some extra info) * @param $tempIgnore Bool: Whether to avoid throwing an exception on errors... * maybe best to catch the exception instead? * @return true for a successful write query, ResultWrapper object for a successful read query, * or false on failure if $tempIgnore set * @throws DBQueryError Thrown when the database returns an error of any kind */ public function query($sql, $fname = '', $tempIgnore = false) { global $wgProfiling; if ($wgProfiling) { # generalizeSQL will probably cut down the query to reasonable # logging size most of the time. The substr is really just a sanity check. # Who's been wasting my precious column space? -- TS #$profName = 'query: ' . $fname . ' ' . substr( Database::generalizeSQL( $sql ), 0, 255 ); if (is_null($this->getLBInfo('master'))) { $queryProf = 'query: ' . substr(Database::generalizeSQL($sql), 0, 255); $totalProf = 'Database::query'; } else { $queryProf = 'query-m: ' . substr(Database::generalizeSQL($sql), 0, 255); $totalProf = 'Database::query-master'; } wfProfileIn($totalProf); wfProfileIn($queryProf); } $this->mLastQuery = $sql; # Add a comment for easy SHOW PROCESSLIST interpretation #if ( $fname ) { global $wgUser; if (is_object($wgUser) && !$wgUser instanceof StubObject) { $userName = $wgUser->getName(); if (mb_strlen($userName) > 15) { $userName = mb_substr($userName, 0, 15) . '...'; } $userName = str_replace('/', '', $userName); } else { $userName = ''; } $commentedSql = preg_replace('/\\s/', " /* {$fname} {$userName} */ ", $sql, 1); #} else { # $commentedSql = $sql; #} # If DBO_TRX is set, start a transaction if ($this->mFlags & DBO_TRX && !$this->trxLevel() && $sql != 'BEGIN' && $sql != 'COMMIT' && $sql != 'ROLLBACK') { // avoid establishing transactions for SHOW and SET statements too - // that would delay transaction initializations to once connection // is really used by application $sqlstart = substr($sql, 0, 10); // very much worth it, benchmark certified(tm) if (strpos($sqlstart, "SHOW ") !== 0 and strpos($sqlstart, "SET ") !== 0) { $this->begin(); } } if ($this->debug()) { $sqlx = substr($commentedSql, 0, 500); $sqlx = strtr($sqlx, "\t\n", ' '); wfDebug("SQL: {$sqlx}\n"); } # Do the query and handle errors $ret = $this->doQuery($commentedSql); # Try reconnecting if the connection was lost if (false === $ret && ($this->lastErrno() == 2013 || $this->lastErrno() == 2006)) { # Transaction is gone, like it or not $this->mTrxLevel = 0; wfDebug("Connection lost, reconnecting...\n"); if ($this->ping()) { wfDebug("Reconnected\n"); $sqlx = substr($commentedSql, 0, 500); $sqlx = strtr($sqlx, "\t\n", ' '); global $wgRequestTime; $elapsed = round(microtime(true) - $wgRequestTime, 3); wfLogDBError("Connection lost and reconnected after {$elapsed}s, query: {$sqlx}\n"); $ret = $this->doQuery($commentedSql); } else { wfDebug("Failed\n"); } } if (false === $ret) { $this->reportQueryError($this->lastError(), $this->lastErrno(), $sql, $fname, $tempIgnore); } if ($wgProfiling) { wfProfileOut($queryProf); wfProfileOut($totalProf); } return $this->resultObject($ret); }
/** * Usually aborts on failure * If errors are explicitly ignored, returns success */ function query($sql, $fname = '', $tempIgnore = false) { global $wgProfiling, $wgCommandLineMode; if (wfReadOnly()) { # This is a quick check for the most common kinds of write query used # in MediaWiki, to provide extra safety in addition to UI-level checks. # It is not intended to prevent every conceivable write query, or even # to handle such queries gracefully. if (preg_match('/^(update|insert|replace|delete)/i', $sql)) { wfDebug("Write query from {$fname} blocked\n"); return false; } } if ($wgProfiling) { # generalizeSQL will probably cut down the query to reasonable # logging size most of the time. The substr is really just a sanity check. $profName = 'query: ' . $fname . ' ' . substr(Database::generalizeSQL($sql), 0, 255); wfProfileIn('Database::query'); wfProfileIn($profName); } $this->mLastQuery = $sql; # Add a comment for easy SHOW PROCESSLIST interpretation if ($fname) { $commentedSql = "/* {$fname} */ {$sql}"; } else { $commentedSql = $sql; } # If DBO_TRX is set, start a transaction if ($this->mFlags & DBO_TRX && !$this->trxLevel() && $sql != 'BEGIN') { $this->begin(); } if ($this->debug()) { $sqlx = substr($commentedSql, 0, 500); $sqlx = strtr($sqlx, "\t\n", ' '); wfDebug("SQL: {$sqlx}\n"); } # Do the query and handle errors $ret = $this->doQuery($commentedSql); # Try reconnecting if the connection was lost if (false === $ret && ($this->lastErrno() == 2013 || $this->lastErrno() == 2006)) { # Transaction is gone, like it or not $this->mTrxLevel = 0; wfDebug("Connection lost, reconnecting...\n"); if ($this->ping()) { wfDebug("Reconnected\n"); $ret = $this->doQuery($commentedSql); } else { wfDebug("Failed\n"); } } if (false === $ret) { $this->reportQueryError($this->lastError(), $this->lastErrno(), $sql, $fname, $tempIgnore); } if ($wgProfiling) { wfProfileOut($profName); wfProfileOut('Database::query'); } return $ret; }