function reportConnectionError(&$conn) { wfProfileIn(__METHOD__); if (!is_object($conn)) { // No last connection, probably due to all servers being too busy wfLogDBError("LB failure with no last connection\n"); $conn = new Database(); if ($this->mFailFunction) { $conn->failFunction($this->mFailFunction); $conn->reportConnectionError($this->mLastError); } else { // If all servers were busy, mLastError will contain something sensible throw new DBConnectionError($conn, $this->mLastError); } } else { if ($this->mFailFunction) { $conn->failFunction($this->mFailFunction); } else { $conn->failFunction(false); } $server = $conn->getProperty('mServer'); wfLogDBError("Connection error: {$this->mLastError} ({$server})\n"); $conn->reportConnectionError("{$this->mLastError} ({$server})"); } wfProfileOut(__METHOD__); }
/** * Begin a transaction. If a transaction is already in progress, that transaction will be committed before the * new transaction is started. * * Note that when the DBO_TRX flag is set (which is usually the case for web requests, but not for maintenance scripts), * any previous database query will have started a transaction automatically. * * Nesting of transactions is not supported. Attempts to nest transactions will cause a warning, unless the current * transaction was started automatically because of the DBO_TRX flag. * * @param $fname string */ final public function begin( $fname = __METHOD__ ) { global $wgDebugDBTransactions; if ( $this->mTrxLevel ) { // implicit commit if ( !$this->mTrxAutomatic ) { // We want to warn about inadvertently nested begin/commit pairs, but not about // auto-committing implicit transactions that were started by query() via DBO_TRX $msg = "$fname: Transaction already in progress (from {$this->mTrxFname}), " . " performing implicit commit!"; wfWarn( $msg ); wfLogDBError( $msg ); } else { // if the transaction was automatic and has done write operations, // log it if $wgDebugDBTransactions is enabled. if ( $this->mTrxDoneWrites && $wgDebugDBTransactions ) { wfDebug( "$fname: Automatic transaction with writes in progress" . " (from {$this->mTrxFname}), performing implicit commit!\n" ); } } $this->runOnTransactionPreCommitCallbacks(); $this->doCommit( $fname ); if ( $this->mTrxDoneWrites ) { Profiler::instance()->transactionWritingOut( $this->mServer, $this->mDBname ); } $this->runOnTransactionIdleCallbacks(); } $this->doBegin( $fname ); $this->mTrxFname = $fname; $this->mTrxDoneWrites = false; $this->mTrxAutomatic = false; }
/** Open an MSSQL database and return a resource handle to it * NOTE: only $dbName is used, the other parameters are irrelevant for MSSQL databases */ function open($server, $user, $password, $dbName) { wfProfileIn(__METHOD__); # Test for missing mysql.so # First try to load it if (!@extension_loaded('mssql')) { @dl('mssql.so'); } # Fail now # Otherwise we get a suppressed fatal error, which is very hard to track down if (!function_exists('mssql_connect')) { throw new DBConnectionError($this, "MSSQL functions missing, have you compiled PHP with the --with-mssql option?\n"); } $this->close(); $this->mServer = $server; $this->mUser = $user; $this->mPassword = $password; $this->mDBname = $dbName; wfProfileIn("dbconnect-{$server}"); # Try to connect up to three times # The kernel's default SYN retransmission period is far too slow for us, # so we use a short timeout plus a manual retry. $this->mConn = false; $max = 3; for ($i = 0; $i < $max && !$this->mConn; $i++) { if ($i > 1) { usleep(1000); } if ($this->mFlags & DBO_PERSISTENT) { @($this->mConn = mssql_pconnect($server, $user, $password)); } else { # Create a new connection... @($this->mConn = mssql_connect($server, $user, $password, true)); } } wfProfileOut("dbconnect-{$server}"); if ($dbName != '') { if ($this->mConn !== false) { $success = @mssql_select_db($dbName, $this->mConn); if (!$success) { $error = "Error selecting database {$dbName} on server {$this->mServer} " . "from client host " . wfHostname() . "\n"; wfLogDBError(" Error selecting database {$dbName} on server {$this->mServer} \n"); wfDebug($error); } } else { wfDebug("DB connection error\n"); wfDebug("Server: {$server}, User: {$user}, Password: "******"...\n"); $success = false; } } else { # Delay USE query $success = (bool) $this->mConn; } if (!$success) { $this->reportConnectionError(); } $this->mOpened = $success; wfProfileOut(__METHOD__); return $success; }
/** * Begin a transaction. If a transaction is already in progress, * that transaction will be committed before the new transaction is started. * * Note that when the DBO_TRX flag is set (which is usually the case for web * requests, but not for maintenance scripts), any previous database query * will have started a transaction automatically. * * Nesting of transactions is not supported. Attempts to nest transactions * will cause a warning, unless the current transaction was started * automatically because of the DBO_TRX flag. * * @param string $fname * @throws DBError */ public final function begin($fname = __METHOD__) { if ($this->mTrxLevel) { // implicit commit if ($this->mTrxAtomicLevels) { // If the current transaction was an automatic atomic one, then we definitely have // a problem. Same if there is any unclosed atomic level. $levels = implode(', ', $this->mTrxAtomicLevels); throw new DBUnexpectedError($this, "Got explicit BEGIN while atomic sections {$levels} are still open."); } elseif (!$this->mTrxAutomatic) { // We want to warn about inadvertently nested begin/commit pairs, but not about // auto-committing implicit transactions that were started by query() via DBO_TRX $msg = "{$fname}: Transaction already in progress (from {$this->mTrxFname}), " . " performing implicit commit!"; wfWarn($msg); wfLogDBError($msg, $this->getLogContext(array('method' => __METHOD__, 'fname' => $fname))); } else { // if the transaction was automatic and has done write operations if ($this->mTrxDoneWrites) { wfDebug("{$fname}: Automatic transaction with writes in progress" . " (from {$this->mTrxFname}), performing implicit commit!\n"); } } $this->runOnTransactionPreCommitCallbacks(); $writeTime = $this->pendingWriteQueryDuration(); $this->doCommit($fname); if ($this->mTrxDoneWrites) { $this->mDoneWrites = microtime(true); $this->getTransactionProfiler()->transactionWritingOut($this->mServer, $this->mDBname, $this->mTrxShortId, $writeTime); } $this->runOnTransactionIdleCallbacks(); } # Avoid fatals if close() was called $this->assertOpen(); $this->doBegin($fname); $this->mTrxTimestamp = microtime(true); $this->mTrxFname = $fname; $this->mTrxDoneWrites = false; $this->mTrxAutomatic = false; $this->mTrxAutomaticAtomic = false; $this->mTrxAtomicLevels = array(); $this->mTrxIdleCallbacks = array(); $this->mTrxPreCommitCallbacks = array(); $this->mTrxShortId = wfRandomString(12); $this->mTrxWriteDuration = 0.0; // First SELECT after BEGIN will establish the snapshot in REPEATABLE-READ. // Get an estimate of the slave lag before then, treating estimate staleness // as lag itself just to be safe $status = $this->getApproximateLagStatus(); $this->mTrxSlaveLag = $status['lag'] + (microtime(true) - $status['since']); }
public function reportQueryError($error, $errno, $sql, $fname, $tempIgnore = false) { if ($this->ignoreErrors() || $tempIgnore) { wfDebug("SQL ERROR (ignored): {$error}\n"); } else { $sql1line = mb_substr(str_replace("\n", "\\n", $sql), 0, 5 * 1024); wfLogDBError("{fname}\t{db_server}\t{errno}\t{error}\t{sql1line}", $this->getLogContext(['method' => __METHOD__, 'errno' => $errno, 'error' => $error, 'sql1line' => $sql1line, 'fname' => $fname])); wfDebug("SQL ERROR: " . $error . "\n"); throw new DBQueryError($this, $error, $errno, $sql, $fname); } }
/** * @param $server string * @param $user string * @param $password string * @param $dbName string * @return bool * @throws DBConnectionError */ function open( $server, $user, $password, $dbName ) { global $wgAllDBsAreLocalhost, $wgDBmysql5, $wgSQLMode; wfProfileIn( __METHOD__ ); # Debugging hack -- fake cluster if ( $wgAllDBsAreLocalhost ) { $realServer = 'localhost'; } else { $realServer = $server; } $this->close(); $this->mServer = $server; $this->mUser = $user; $this->mPassword = $password; $this->mDBname = $dbName; wfProfileIn( "dbconnect-$server" ); # The kernel's default SYN retransmission period is far too slow for us, # so we use a short timeout plus a manual retry. Retrying means that a small # but finite rate of SYN packet loss won't cause user-visible errors. $this->mConn = false; $this->installErrorHandler(); try { $this->mConn = $this->mysqlConnect( $realServer ); } catch ( Exception $ex ) { wfProfileOut( "dbconnect-$server" ); wfProfileOut( __METHOD__ ); throw $ex; } $error = $this->restoreErrorHandler(); wfProfileOut( "dbconnect-$server" ); # Always log connection errors if ( !$this->mConn ) { if ( !$error ) { $error = $this->lastError(); } wfLogDBError( "Error connecting to {$this->mServer}: $error\n" ); wfDebug( "DB connection error\n" . "Server: $server, User: $user, Password: "******"..., error: " . $error . "\n" ); wfProfileOut( __METHOD__ ); return $this->reportConnectionError( $error ); } if ( $dbName != '' ) { wfSuppressWarnings(); $success = $this->selectDB( $dbName ); wfRestoreWarnings(); if ( !$success ) { wfLogDBError( "Error selecting database $dbName on server {$this->mServer}\n" ); wfDebug( "Error selecting database $dbName on server {$this->mServer} " . "from client host " . wfHostname() . "\n" ); wfProfileOut( __METHOD__ ); return $this->reportConnectionError( "Error selecting database $dbName" ); } } // Tell the server we're communicating with it in UTF-8. // This may engage various charset conversions. if ( $wgDBmysql5 ) { $this->query( 'SET NAMES utf8', __METHOD__ ); } else { $this->query( 'SET NAMES binary', __METHOD__ ); } // Set SQL mode, default is turning them all off, can be overridden or skipped with null if ( is_string( $wgSQLMode ) ) { $mode = $this->addQuotes( $wgSQLMode ); $this->query( "SET sql_mode = $mode", __METHOD__ ); } $this->mOpened = true; wfProfileOut( __METHOD__ ); return true; }
/** * Begin a transaction. If a transaction is already in progress, * that transaction will be committed before the new transaction is started. * * Note that when the DBO_TRX flag is set (which is usually the case for web * requests, but not for maintenance scripts), any previous database query * will have started a transaction automatically. * * Nesting of transactions is not supported. Attempts to nest transactions * will cause a warning, unless the current transaction was started * automatically because of the DBO_TRX flag. * * @param string $fname * @throws DBError */ public final function begin($fname = __METHOD__) { global $wgDebugDBTransactions; if ($this->mTrxLevel) { // implicit commit if (!$this->mTrxAtomicLevels->isEmpty()) { // If the current transaction was an automatic atomic one, then we definitely have // a problem. Same if there is any unclosed atomic level. throw new DBUnexpectedError($this, "Attempted to start explicit transaction when atomic levels are still open."); } elseif (!$this->mTrxAutomatic) { // We want to warn about inadvertently nested begin/commit pairs, but not about // auto-committing implicit transactions that were started by query() via DBO_TRX $msg = "{$fname}: Transaction already in progress (from {$this->mTrxFname}), " . " performing implicit commit!"; wfWarn($msg); wfLogDBError($msg, $this->getLogContext(array('method' => __METHOD__, 'fname' => $fname))); } else { // if the transaction was automatic and has done write operations, // log it if $wgDebugDBTransactions is enabled. if ($this->mTrxDoneWrites && $wgDebugDBTransactions) { wfDebug("{$fname}: Automatic transaction with writes in progress" . " (from {$this->mTrxFname}), performing implicit commit!\n"); } } $this->runOnTransactionPreCommitCallbacks(); $writeTime = $this->pendingWriteQueryDuration(); $this->doCommit($fname); if ($this->mTrxDoneWrites) { $this->mDoneWrites = microtime(true); $this->getTransactionProfiler()->transactionWritingOut($this->mServer, $this->mDBname, $this->mTrxShortId, $writeTime); } $this->runOnTransactionIdleCallbacks(); } # Avoid fatals if close() was called $this->assertOpen(); $this->doBegin($fname); $this->mTrxTimestamp = microtime(true); $this->mTrxFname = $fname; $this->mTrxDoneWrites = false; $this->mTrxAutomatic = false; $this->mTrxAutomaticAtomic = false; $this->mTrxAtomicLevels = new SplStack(); $this->mTrxIdleCallbacks = array(); $this->mTrxPreCommitCallbacks = array(); $this->mTrxShortId = wfRandomString(12); $this->mTrxWriteDuration = 0.0; }
/** * @throws DBConnectionError * @return bool */ private function reportConnectionError() { $conn = $this->mErrorConnection; // The connection which caused the error $context = array('method' => __METHOD__, 'last_error' => $this->mLastError); if (!is_object($conn)) { // No last connection, probably due to all servers being too busy wfLogDBError("LB failure with no last connection. Connection error: {last_error}", $context); // If all servers were busy, mLastError will contain something sensible throw new DBConnectionError(null, $this->mLastError); } else { $context['db_server'] = $conn->getProperty('mServer'); wfLogDBError("Connection error: {last_error} ({db_server})", $context); $conn->reportConnectionError("{$this->mLastError} ({$context['db_server']})"); // throws DBConnectionError } return false; /* not reached */ }
/** * Begin a transaction. If a transaction is already in progress, * that transaction will be committed before the new transaction is started. * * Note that when the DBO_TRX flag is set (which is usually the case for web * requests, but not for maintenance scripts), any previous database query * will have started a transaction automatically. * * Nesting of transactions is not supported. Attempts to nest transactions * will cause a warning, unless the current transaction was started * automatically because of the DBO_TRX flag. * * @param string $fname * @throws DBError */ public final function begin($fname = __METHOD__) { global $wgDebugDBTransactions; if ($this->mTrxLevel) { // implicit commit if (!$this->mTrxAtomicLevels->isEmpty()) { // If the current transaction was an automatic atomic one, then we definitely have // a problem. Same if there is any unclosed atomic level. throw new DBUnexpectedError($this, "Attempted to start explicit transaction when atomic levels are still open."); } elseif (!$this->mTrxAutomatic) { // We want to warn about inadvertently nested begin/commit pairs, but not about // auto-committing implicit transactions that were started by query() via DBO_TRX $msg = "{$fname}: Transaction already in progress (from {$this->mTrxFname}), " . " performing implicit commit!"; wfWarn($msg); wfLogDBError($msg); } else { // if the transaction was automatic and has done write operations, // log it if $wgDebugDBTransactions is enabled. if ($this->mTrxDoneWrites && $wgDebugDBTransactions) { wfDebug("{$fname}: Automatic transaction with writes in progress" . " (from {$this->mTrxFname}), performing implicit commit!\n"); } } $this->runOnTransactionPreCommitCallbacks(); $this->doCommit($fname); if ($this->mTrxDoneWrites) { Profiler::instance()->transactionWritingOut($this->mServer, $this->mDBname); } $this->runOnTransactionIdleCallbacks(); } $this->doBegin($fname); $this->mTrxFname = $fname; $this->mTrxDoneWrites = false; $this->mTrxAutomatic = false; $this->mTrxAutomaticAtomic = false; $this->mTrxAtomicLevels = new SplStack(); $this->mTrxIdleCallbacks = array(); $this->mTrxPreCommitCallbacks = array(); }
/** * @param $server string * @param $user string * @param $password string * @param $dbName string * @return bool * @throws DBConnectionError */ function open($server, $user, $password, $dbName) { global $wgAllDBsAreLocalhost, $wgDBmysql5, $wgSQLMode; wfProfileIn(__METHOD__); # Load mysql.so if we don't have it wfDl('mysql'); # Fail now # Otherwise we get a suppressed fatal error, which is very hard to track down if (!function_exists('mysql_connect')) { throw new DBConnectionError($this, "MySQL functions missing, have you compiled PHP with the --with-mysql option?\n"); } # Debugging hack -- fake cluster if ($wgAllDBsAreLocalhost) { $realServer = 'localhost'; } else { $realServer = $server; } $this->close(); $this->mServer = $server; $this->mUser = $user; $this->mPassword = $password; $this->mDBname = $dbName; $connFlags = 0; if ($this->mFlags & DBO_SSL) { $connFlags |= MYSQL_CLIENT_SSL; } if ($this->mFlags & DBO_COMPRESS) { $connFlags |= MYSQL_CLIENT_COMPRESS; } wfProfileIn("dbconnect-{$server}"); # The kernel's default SYN retransmission period is far too slow for us, # so we use a short timeout plus a manual retry. Retrying means that a small # but finite rate of SYN packet loss won't cause user-visible errors. $this->mConn = false; if (ini_get('mysql.connect_timeout') <= 3) { $numAttempts = 2; } else { $numAttempts = 1; } $this->installErrorHandler(); for ($i = 0; $i < $numAttempts && !$this->mConn; $i++) { if ($i > 1) { usleep(1000); } if ($this->mFlags & DBO_PERSISTENT) { $this->mConn = mysql_pconnect($realServer, $user, $password, $connFlags); } else { # Create a new connection... $this->mConn = mysql_connect($realServer, $user, $password, true, $connFlags); } #if ( $this->mConn === false ) { #$iplus = $i + 1; #wfLogDBError("Connect loop error $iplus of $max ($server): " . mysql_errno() . " - " . mysql_error()."\n"); #} } $error = $this->restoreErrorHandler(); wfProfileOut("dbconnect-{$server}"); # Always log connection errors if (!$this->mConn) { if (!$error) { $error = $this->lastError(); } wfLogDBError("Error connecting to {$this->mServer}: {$error}\n"); wfDebug("DB connection error\n" . "Server: {$server}, User: {$user}, Password: "******"..., error: " . $error . "\n"); wfProfileOut(__METHOD__); $this->reportConnectionError($error); } if ($dbName != '') { wfSuppressWarnings(); $success = mysql_select_db($dbName, $this->mConn); wfRestoreWarnings(); if (!$success) { wfLogDBError("Error selecting database {$dbName} on server {$this->mServer}\n"); wfDebug("Error selecting database {$dbName} on server {$this->mServer} " . "from client host " . wfHostname() . "\n"); wfProfileOut(__METHOD__); $this->reportConnectionError("Error selecting database {$dbName}"); } } // Tell the server we're communicating with it in UTF-8. // This may engage various charset conversions. if ($wgDBmysql5) { $this->query('SET NAMES utf8', __METHOD__); } else { $this->query('SET NAMES binary', __METHOD__); } // Set SQL mode, default is turning them all off, can be overridden or skipped with null if (is_string($wgSQLMode)) { $mode = $this->addQuotes($wgSQLMode); $this->query("SET sql_mode = {$mode}", __METHOD__); } $this->mOpened = true; wfProfileOut(__METHOD__); return true; }
/** * @param $error * @param $errno * @param $sql * @param string $fname * @param bool $tempIgnore */ function reportQueryError($error, $errno, $sql, $fname, $tempIgnore = false) { global $wgCommandLineMode, $wgFullyInitialised; # Ignore errors during error handling to avoid infinite recursion $ignore = $this->ignoreErrors(true); $this->mErrorCount++; if ($ignore || $tempIgnore) { wfDebug("SQL ERROR (ignored): " . $error . "\n"); } else { $sql1line = str_replace("\n", "\\n", $sql); wfLogDBError("{$fname}\t{$this->mServer}\t{$errno}\t{$error}\t{$sql1line}\n"); wfDebug("SQL ERROR: " . $error . "\n"); if ($wgCommandLineMode || !$this->mOut || empty($wgFullyInitialised)) { $message = "A database error has occurred\n" . "Query: {$sql}\n" . "Function: {$fname}\n" . "Error: {$errno} {$error}\n"; if (!$wgCommandLineMode) { $message = nl2br($message); } wfDebugDieBacktrace($message); } else { // this calls wfAbruptExit() $this->mOut->databaseError($fname, $sql, $error, $errno); } } $this->ignoreErrors($ignore); }
/** * @param string $server * @param string $user * @param string $password * @param string $dbName * @throws Exception|DBConnectionError * @return bool */ function open($server, $user, $password, $dbName) { global $wgAllDBsAreLocalhost, $wgSQLMode; # Debugging hack -- fake cluster if ($wgAllDBsAreLocalhost) { $realServer = 'localhost'; } else { $realServer = $server; } $this->close(); $this->mServer = $server; $this->mUser = $user; $this->mPassword = $password; $this->mDBname = $dbName; # The kernel's default SYN retransmission period is far too slow for us, # so we use a short timeout plus a manual retry. Retrying means that a small # but finite rate of SYN packet loss won't cause user-visible errors. $this->mConn = false; $this->installErrorHandler(); try { $this->mConn = $this->mysqlConnect($realServer); } catch (Exception $ex) { $this->restoreErrorHandler(); throw $ex; } $error = $this->restoreErrorHandler(); # Always log connection errors if (!$this->mConn) { if (!$error) { $error = $this->lastError(); } wfLogDBError("Error connecting to {db_server}: {error}", $this->getLogContext(array('method' => __METHOD__, 'error' => $error))); wfDebug("DB connection error\n" . "Server: {$server}, User: {$user}, Password: "******"..., error: " . $error . "\n"); $this->reportConnectionError($error); } if ($dbName != '') { wfSuppressWarnings(); $success = $this->selectDB($dbName); wfRestoreWarnings(); if (!$success) { wfLogDBError("Error selecting database {db_name} on server {db_server}", $this->getLogContext(array('method' => __METHOD__))); wfDebug("Error selecting database {$dbName} on server {$this->mServer} " . "from client host " . wfHostname() . "\n"); $this->reportConnectionError("Error selecting database {$dbName}"); } } // Tell the server what we're communicating with if (!$this->connectInitCharset()) { $this->reportConnectionError("Error setting character set"); } // Abstract over any insane MySQL defaults $set = array('group_concat_max_len = 262144'); // Set SQL mode, default is turning them all off, can be overridden or skipped with null if (is_string($wgSQLMode)) { $set[] = 'sql_mode = ' . $this->addQuotes($wgSQLMode); } if ($set) { // Use doQuery() to avoid opening implicit transactions (DBO_TRX) $success = $this->doQuery('SET ' . implode(', ', $set), __METHOD__); if (!$success) { wfLogDBError('Error setting MySQL variables on server {db_server} (check $wgSQLMode)', $this->getLogContext(array('method' => __METHOD__))); $this->reportConnectionError('Error setting MySQL variables on server {db_server} (check $wgSQLMode)'); } } $this->mOpened = true; return true; }
/** * @param $conn * @throws DBConnectionError */ function reportConnectionError(&$conn) { if (!is_object($conn)) { // No last connection, probably due to all servers being too busy wfLogDBError("LB failure with no last connection. Connection error: {$this->mLastError}\n"); $conn = new Database(); // If all servers were busy, mLastError will contain something sensible throw new DBConnectionError($conn, $this->mLastError); } else { $server = $conn->getProperty('mServer'); wfLogDBError("Connection error: {$this->mLastError} ({$server})\n"); $conn->reportConnectionError("{$this->mLastError} ({$server})"); } }
/** * @param $conn DatabaseBase * @param $threshold * @return int */ function postConnectionBackoff($conn, $threshold) { if (!$threshold) { return 0; } $status = $conn->getMysqlStatus("Thread%"); if ($status['Threads_running'] > $threshold) { $server = $conn->getProperty('mServer'); wfLogDBError("LB backoff from {$server} - Threads_running = {$status['Threads_running']}\n"); return $status['Threads_connected']; } else { return 0; } }
function open($server, $user, $password, $dbName) { global $wgAllDBsAreLocalhost; wfProfileIn(__METHOD__); # Test for missing mysql.so # First try to load it if (!@extension_loaded('mysql')) { @dl('mysql.so'); } # Fail now # Otherwise we get a suppressed fatal error, which is very hard to track down if (!function_exists('mysql_connect')) { throw new DBConnectionError($this, "MySQL functions missing, have you compiled PHP with the --with-mysql option?\n"); } # Debugging hack -- fake cluster if ($wgAllDBsAreLocalhost) { $realServer = 'localhost'; } else { $realServer = $server; } $this->close(); $this->mServer = $server; $this->mUser = $user; $this->mPassword = $password; $this->mDBname = $dbName; $success = false; wfProfileIn("dbconnect-{$server}"); # The kernel's default SYN retransmission period is far too slow for us, # so we use a short timeout plus a manual retry. Retrying means that a small # but finite rate of SYN packet loss won't cause user-visible errors. $this->mConn = false; if (ini_get('mysql.connect_timeout') <= 3) { $numAttempts = 2; } else { $numAttempts = 1; } $this->installErrorHandler(); for ($i = 0; $i < $numAttempts && !$this->mConn; $i++) { if ($i > 1) { usleep(1000); } if ($this->mFlags & DBO_PERSISTENT) { $this->mConn = mysql_pconnect($realServer, $user, $password); } else { # Create a new connection... $this->mConn = mysql_connect($realServer, $user, $password, true); } if ($this->mConn === false) { #$iplus = $i + 1; #wfLogDBError("Connect loop error $iplus of $max ($server): " . mysql_errno() . " - " . mysql_error()."\n"); } } $phpError = $this->restoreErrorHandler(); # Always log connection errors if (!$this->mConn) { $error = $this->lastError(); if (!$error) { $error = $phpError; } wfLogDBError("Error connecting to {$this->mServer}: {$error}\n"); wfDebug("DB connection error\n"); wfDebug("Server: {$server}, User: {$user}, Password: "******"..., error: " . mysql_error() . "\n"); $success = false; } wfProfileOut("dbconnect-{$server}"); if ($dbName != '' && $this->mConn !== false) { $success = @mysql_select_db($dbName, $this->mConn); if (!$success) { $error = "Error selecting database {$dbName} on server {$this->mServer} " . "from client host " . wfHostname() . "\n"; wfLogDBError(" Error selecting database {$dbName} on server {$this->mServer} \n"); wfDebug($error); } } else { # Delay USE query $success = (bool) $this->mConn; } if ($success) { $version = $this->getServerVersion(); if (version_compare($version, '4.1') >= 0) { // Tell the server we're communicating with it in UTF-8. // This may engage various charset conversions. global $wgDBmysql5; if ($wgDBmysql5) { $this->query('SET NAMES utf8', __METHOD__); } // Turn off strict mode $this->query("SET sql_mode = ''", __METHOD__); } // Turn off strict mode if it is on } else { $this->reportConnectionError($phpError); } $this->mOpened = $success; wfProfileOut(__METHOD__); return $success; }
/** * @return bool|float */ protected function getLagFromPtHeartbeat() { $masterInfo = $this->getMasterServerInfo(); if (!$masterInfo) { wfLogDBError("Unable to query master of {db_server} for server ID", $this->getLogContext(array('method' => __METHOD__))); return false; // could not get master server ID } list($time, $nowUnix) = $this->getHeartbeatData($masterInfo['serverId']); if ($time !== null) { // @time is in ISO format like "2015-09-25T16:48:10.000510" $dateTime = new DateTime($time, new DateTimeZone('UTC')); $timeUnix = (int) $dateTime->format('U') + $dateTime->format('u') / 1000000.0; return max($nowUnix - $timeUnix, 0.0); } wfLogDBError("Unable to find pt-heartbeat row for {db_server}", $this->getLogContext(array('method' => __METHOD__))); return false; }
/** * @param $error * @param $errno * @param $sql * @param string $fname * @param bool $tempIgnore */ function reportQueryError($error, $errno, $sql, $fname, $tempIgnore = false) { global $wgCommandLineMode, $wgFullyInitialised, $wgColorErrors; # Ignore errors during error handling to avoid infinite recursion $ignore = $this->ignoreErrors(true); ++$this->mErrorCount; if ($ignore || $tempIgnore) { wfDebug("SQL ERROR (ignored): {$error}\n"); $this->ignoreErrors($ignore); } else { $sql1line = str_replace("\n", "\\n", $sql); wfLogDBError("{$fname}\t{$this->mServer}\t{$errno}\t{$error}\t{$sql1line}\n"); wfDebug("SQL ERROR: " . $error . "\n"); throw new DBQueryError($this, $error, $errno, $sql, $fname); } }
/** * @param string $server * @param string $user * @param string $password * @param string $dbName * @throws Exception|DBConnectionError * @return bool */ function open($server, $user, $password, $dbName) { global $wgAllDBsAreLocalhost, $wgSQLMode; # Close/unset connection handle $this->close(); # Debugging hack -- fake cluster $realServer = $wgAllDBsAreLocalhost ? 'localhost' : $server; $this->mServer = $server; $this->mUser = $user; $this->mPassword = $password; $this->mDBname = $dbName; $this->installErrorHandler(); try { $this->mConn = $this->mysqlConnect($realServer); } catch (Exception $ex) { $this->restoreErrorHandler(); throw $ex; } $error = $this->restoreErrorHandler(); # Always log connection errors if (!$this->mConn) { if (!$error) { $error = $this->lastError(); } wfLogDBError("Error connecting to {db_server}: {error}", $this->getLogContext(array('method' => __METHOD__, 'error' => $error))); wfDebug("DB connection error\n" . "Server: {$server}, User: {$user}, Password: "******"..., error: " . $error . "\n"); $this->reportConnectionError($error); } if ($dbName != '') { MediaWiki\suppressWarnings(); $success = $this->selectDB($dbName); MediaWiki\restoreWarnings(); if (!$success) { wfLogDBError("Error selecting database {db_name} on server {db_server}", $this->getLogContext(array('method' => __METHOD__))); wfDebug("Error selecting database {$dbName} on server {$this->mServer} " . "from client host " . wfHostname() . "\n"); $this->reportConnectionError("Error selecting database {$dbName}"); } } // Tell the server what we're communicating with if (!$this->connectInitCharset()) { $this->reportConnectionError("Error setting character set"); } // Abstract over any insane MySQL defaults $set = array('group_concat_max_len = 262144'); // Set SQL mode, default is turning them all off, can be overridden or skipped with null if (is_string($wgSQLMode)) { $set[] = 'sql_mode = ' . $this->addQuotes($wgSQLMode); } // Set any custom settings defined by site config // (e.g. https://dev.mysql.com/doc/refman/4.1/en/innodb-parameters.html) foreach ($this->mSessionVars as $var => $val) { // Escape strings but not numbers to avoid MySQL complaining if (!is_int($val) && !is_float($val)) { $val = $this->addQuotes($val); } $set[] = $this->addIdentifierQuotes($var) . ' = ' . $val; } if ($set) { // Use doQuery() to avoid opening implicit transactions (DBO_TRX) $success = $this->doQuery('SET ' . implode(', ', $set)); if (!$success) { wfLogDBError('Error setting MySQL variables on server {db_server} (check $wgSQLMode)', $this->getLogContext(array('method' => __METHOD__))); $this->reportConnectionError('Error setting MySQL variables on server {db_server} (check $wgSQLMode)'); } } $this->mOpened = true; return true; }
/** * @return bool|float */ protected function getLagFromPtHeartbeat() { $options = $this->lagDetectionOptions; if (isset($options['conds'])) { // Best method for multi-DC setups: use logical channel names $data = $this->getHeartbeatData($options['conds']); } else { // Standard method: use master server ID (works with stock pt-heartbeat) $masterInfo = $this->getMasterServerInfo(); if (!$masterInfo) { wfLogDBError("Unable to query master of {db_server} for server ID", $this->getLogContext(['method' => __METHOD__])); return false; // could not get master server ID } $conds = ['server_id' => intval($masterInfo['serverId'])]; $data = $this->getHeartbeatData($conds); } list($time, $nowUnix) = $data; if ($time !== null) { // @time is in ISO format like "2015-09-25T16:48:10.000510" $dateTime = new DateTime($time, new DateTimeZone('UTC')); $timeUnix = (int) $dateTime->format('U') + $dateTime->format('u') / 1000000.0; return max($nowUnix - $timeUnix, 0.0); } wfLogDBError("Unable to find pt-heartbeat row for {db_server}", $this->getLogContext(['method' => __METHOD__])); return false; }