X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2Flibs%2Frdbms%2Fdatabase%2FDatabaseMysqlBase.php;h=454e0c2adeb6ed0b8131edaff05e331cb27b95cb;hb=99b65649c0f9b4ab1156cfc2ee50959d4b8a12c6;hp=bd2d274cc5e57ad34f16084c5b4a1531d9ccaa0a;hpb=963bff8cce84a627fd68f415b99ce6760e3748b8;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/libs/rdbms/database/DatabaseMysqlBase.php b/includes/libs/rdbms/database/DatabaseMysqlBase.php index bd2d274cc5..454e0c2ade 100644 --- a/includes/libs/rdbms/database/DatabaseMysqlBase.php +++ b/includes/libs/rdbms/database/DatabaseMysqlBase.php @@ -60,6 +60,8 @@ abstract class DatabaseMysqlBase extends Database { protected $sqlMode; /** @var bool Use experimental UTF-8 transmission encoding */ protected $utf8Mode; + /** @var bool|null */ + protected $defaultBigSelects = null; /** @var string|null */ private $serverVersion = null; @@ -126,14 +128,14 @@ abstract class DatabaseMysqlBase extends Database { # Close/unset connection handle $this->close(); - $this->mServer = $server; - $this->mUser = $user; - $this->mPassword = $password; - $this->mDBname = $dbName; + $this->server = $server; + $this->user = $user; + $this->password = $password; + $this->dbName = $dbName; $this->installErrorHandler(); try { - $this->mConn = $this->mysqlConnect( $this->mServer ); + $this->conn = $this->mysqlConnect( $this->server ); } catch ( Exception $ex ) { $this->restoreErrorHandler(); throw $ex; @@ -141,7 +143,7 @@ abstract class DatabaseMysqlBase extends Database { $error = $this->restoreErrorHandler(); # Always log connection errors - if ( !$this->mConn ) { + if ( !$this->conn ) { if ( !$error ) { $error = $this->lastError(); } @@ -171,7 +173,7 @@ abstract class DatabaseMysqlBase extends Database { ] ) ); $this->queryLogger->debug( - "Error selecting database $dbName on server {$this->mServer}" ); + "Error selecting database $dbName on server {$this->server}" ); $this->reportConnectionError( "Error selecting database $dbName" ); } @@ -190,7 +192,7 @@ abstract class DatabaseMysqlBase extends Database { } // 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 ) { + foreach ( $this->sessionVars as $var => $val ) { // Escape strings but not numbers to avoid MySQL complaining if ( !is_int( $val ) && !is_float( $val ) ) { $val = $this->addQuotes( $val ); @@ -213,7 +215,7 @@ abstract class DatabaseMysqlBase extends Database { } } - $this->mOpened = true; + $this->opened = true; return true; } @@ -465,10 +467,10 @@ abstract class DatabaseMysqlBase extends Database { * @return string */ public function lastError() { - if ( $this->mConn ) { + if ( $this->conn ) { # Even if it's non-zero, it can still be invalid Wikimedia\suppressWarnings(); - $error = $this->mysqlError( $this->mConn ); + $error = $this->mysqlError( $this->conn ); if ( !$error ) { $error = $this->mysqlError(); } @@ -477,7 +479,7 @@ abstract class DatabaseMysqlBase extends Database { $error = $this->mysqlError(); } if ( $error ) { - $error .= ' (' . $this->mServer . ')'; + $error .= ' (' . $this->server . ')'; } return $error; @@ -510,7 +512,8 @@ abstract class DatabaseMysqlBase extends Database { $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__, $insertOptions = [], $selectOptions = [], $selectJoinConds = [] ) { - if ( $this->insertSelectIsSafe === null ) { + $isSafe = in_array( 'NO_AUTO_COLUMNS', $insertOptions, true ); + if ( !$isSafe && $this->insertSelectIsSafe === null ) { // In MySQL, an INSERT SELECT is only replication safe with row-based // replication or if innodb_autoinc_lock_mode is 0. When those // conditions aren't met, use non-native mode. @@ -531,7 +534,7 @@ abstract class DatabaseMysqlBase extends Database { (int)$row->innodb_autoinc_lock_mode === 0; } - if ( !$this->insertSelectIsSafe ) { + if ( !$isSafe && !$this->insertSelectIsSafe ) { return $this->nonNativeInsertSelect( $destTable, $srcTable, @@ -594,7 +597,7 @@ abstract class DatabaseMysqlBase extends Database { list( $database, , $prefix, $table ) = $this->qualifiedTableComponents( $table ); $tableName = "{$prefix}{$table}"; - if ( isset( $this->mSessionTempTables[$tableName] ) ) { + if ( isset( $this->sessionTempTables[$tableName] ) ) { return true; // already known to exist and won't show in SHOW TABLES anyway } @@ -889,17 +892,21 @@ abstract class DatabaseMysqlBase extends Database { return 0; // already reached this point for sure } - $useGTID = ( $this->useGTIDs && $pos->gtids ); - // Call doQuery() directly, to avoid opening a transaction if DBO_TRX is set - if ( $useGTID ) { + if ( $pos->getGTIDs() ) { + // Ignore GTIDs from domains exclusive to the master DB (presumably inactive) + $rpos = $this->getReplicaPos(); + $gtidsWait = $rpos ? MySQLMasterPos::getCommonDomainGTIDs( $pos, $rpos ) : []; + if ( !$gtidsWait ) { + return -1; // $pos is from the wrong cluster? + } // Wait on the GTID set (MariaDB only) - $gtidArg = $this->addQuotes( implode( ',', $pos->gtids ) ); + $gtidArg = $this->addQuotes( implode( ',', $gtidsWait ) ); $res = $this->doQuery( "SELECT MASTER_GTID_WAIT($gtidArg, $timeout)" ); } else { // Wait on the binlog coordinates - $encFile = $this->addQuotes( $pos->file ); - $encPos = intval( $pos->pos ); + $encFile = $this->addQuotes( $pos->getLogFile() ); + $encPos = intval( $pos->pos[1] ); $res = $this->doQuery( "SELECT MASTER_POS_WAIT($encFile, $encPos, $timeout)" ); } @@ -912,7 +919,7 @@ abstract class DatabaseMysqlBase extends Database { // Result can be NULL (error), -1 (timeout), or 0+ per the MySQL manual $status = ( $row[0] !== null ) ? intval( $row[0] ) : null; if ( $status === null ) { - if ( !$useGTID ) { + if ( !$pos->getGTIDs() ) { // T126436: jobs programmed to wait on master positions might be referencing // binlogs with an old master hostname; this makes MASTER_POS_WAIT() return null. // Try to detect this case and treat the replica DB as having reached the given @@ -938,24 +945,26 @@ abstract class DatabaseMysqlBase extends Database { * @return MySQLMasterPos|bool */ public function getReplicaPos() { - $res = $this->query( 'SHOW SLAVE STATUS', __METHOD__ ); - $row = $this->fetchObject( $res ); + $now = microtime( true ); - if ( $row ) { - $pos = $row->Exec_Master_Log_Pos; - // Also fetch the last-applied GTID set (MariaDB) - if ( $this->useGTIDs ) { - $res = $this->query( "SHOW GLOBAL VARIABLES LIKE 'gtid_slave_pos'", __METHOD__ ); - $gtidRow = $this->fetchObject( $res ); - $gtidSet = $gtidRow ? $gtidRow->Value : ''; - } else { - $gtidSet = ''; + if ( $this->useGTIDs ) { + $res = $this->query( "SELECT @@global.gtid_slave_pos AS Value", __METHOD__ ); + $gtidRow = $this->fetchObject( $res ); + if ( $gtidRow && strlen( $gtidRow->Value ) ) { + return new MySQLMasterPos( $gtidRow->Value, $now ); } + } - return new MySQLMasterPos( $row->Relay_Master_Log_File, $pos, $gtidSet ); - } else { - return false; + $res = $this->query( 'SHOW SLAVE STATUS', __METHOD__ ); + $row = $this->fetchObject( $res ); + if ( $row && strlen( $row->Relay_Master_Log_File ) ) { + return new MySQLMasterPos( + "{$row->Relay_Master_Log_File}/{$row->Exec_Master_Log_Pos}", + $now + ); } + + return false; } /** @@ -964,23 +973,23 @@ abstract class DatabaseMysqlBase extends Database { * @return MySQLMasterPos|bool */ public function getMasterPos() { - $res = $this->query( 'SHOW MASTER STATUS', __METHOD__ ); - $row = $this->fetchObject( $res ); + $now = microtime( true ); - if ( $row ) { - // Also fetch the last-written GTID set (MariaDB) - if ( $this->useGTIDs ) { - $res = $this->query( "SHOW GLOBAL VARIABLES LIKE 'gtid_binlog_pos'", __METHOD__ ); - $gtidRow = $this->fetchObject( $res ); - $gtidSet = $gtidRow ? $gtidRow->Value : ''; - } else { - $gtidSet = ''; + if ( $this->useGTIDs ) { + $res = $this->query( "SELECT @@global.gtid_binlog_pos AS Value", __METHOD__ ); + $gtidRow = $this->fetchObject( $res ); + if ( $gtidRow && strlen( $gtidRow->Value ) ) { + return new MySQLMasterPos( $gtidRow->Value, $now ); } + } - return new MySQLMasterPos( $row->File, $row->Position, $gtidSet ); - } else { - return false; + $res = $this->query( 'SHOW MASTER STATUS', __METHOD__ ); + $row = $this->fetchObject( $res ); + if ( $row && strlen( $row->File ) ) { + return new MySQLMasterPos( "{$row->File}/{$row->Position}", $now ); } + + return false; } public function serverIsReadOnly() { @@ -1079,6 +1088,10 @@ abstract class DatabaseMysqlBase extends Database { * @since 1.20 */ public function lockIsFree( $lockName, $method ) { + if ( !parent::lockIsFree( $lockName, $method ) ) { + return false; // already held + } + $encName = $this->addQuotes( $this->makeLockName( $lockName ) ); $result = $this->query( "SELECT IS_FREE_LOCK($encName) AS lockstatus", $method ); $row = $this->fetchObject( $result ); @@ -1170,14 +1183,14 @@ abstract class DatabaseMysqlBase extends Database { */ public function setBigSelects( $value = true ) { if ( $value === 'default' ) { - if ( $this->mDefaultBigSelects === null ) { + if ( $this->defaultBigSelects === null ) { # Function hasn't been called before so it must already be set to the default return; } else { - $value = $this->mDefaultBigSelects; + $value = $this->defaultBigSelects; } - } elseif ( $this->mDefaultBigSelects === null ) { - $this->mDefaultBigSelects = + } elseif ( $this->defaultBigSelects === null ) { + $this->defaultBigSelects = (bool)$this->selectField( false, '@@sql_big_selects', '', __METHOD__ ); } $encValue = $value ? '1' : '0'; @@ -1376,7 +1389,7 @@ abstract class DatabaseMysqlBase extends Database { */ public function listViews( $prefix = null, $fname = __METHOD__ ) { // The name of the column containing the name of the VIEW - $propertyName = 'Tables_in_' . $this->mDBname; + $propertyName = 'Tables_in_' . $this->dbName; // Query for the VIEWS $res = $this->query( 'SHOW FULL TABLES WHERE TABLE_TYPE = "VIEW"' ); @@ -1445,6 +1458,11 @@ abstract class DatabaseMysqlBase extends Database { return $index; } } + + protected function isTransactableQuery( $sql ) { + return parent::isTransactableQuery( $sql ) && + !preg_match( '/^SELECT\s+(GET|RELEASE|IS_FREE)_LOCK\(/', $sql ); + } } class_alias( DatabaseMysqlBase::class, 'DatabaseMysqlBase' );