Merge "Ensure abort link parsing on xmlish tag in link title position"
[lhc/web/wiklou.git] / includes / libs / rdbms / database / DatabaseMysqlBase.php
index 3085276..1e845e8 100644 (file)
@@ -67,6 +67,8 @@ abstract class DatabaseMysqlBase extends Database {
        private $serverVersion = null;
        /** @var bool|null */
        private $insertSelectIsSafe = null;
+       /** @var stdClass|null */
+       private $replicationInfoRow = null;
 
        /**
         * Additional $params include:
@@ -508,19 +510,32 @@ abstract class DatabaseMysqlBase extends Database {
                return $this->nativeReplace( $table, $rows, $fname );
        }
 
-       protected function nativeInsertSelect(
-               $destTable, $srcTable, $varMap, $conds,
-               $fname = __METHOD__, $insertOptions = [], $selectOptions = [], $selectJoinConds = []
-       ) {
-               if ( $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.
-                       // While we could try to determine if the insert is safe anyway by
-                       // checking if the target table has an auto-increment column that
-                       // isn't set in $varMap, that seems unlikely to be worth the extra
-                       // complexity.
-                       $row = $this->selectRow(
+       protected function isInsertSelectSafe( array $insertOptions, array $selectOptions ) {
+               $row = $this->getReplicationSafetyInfo();
+               // For row-based-replication, the resulting changes will be relayed, not the query
+               if ( $row->binlog_format === 'ROW' ) {
+                       return true;
+               }
+               // LIMIT requires ORDER BY on a unique key or it is non-deterministic
+               if ( isset( $selectOptions['LIMIT'] ) ) {
+                       return false;
+               }
+               // 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.
+               // While we could try to determine if the insert is safe anyway by
+               // checking if the target table has an auto-increment column that
+               // isn't set in $varMap, that seems unlikely to be worth the extra
+               // complexity.
+               return ( (int)$row->innodb_autoinc_lock_mode === 0 );
+       }
+
+       /**
+        * @return stdClass Process cached row
+        */
+       private function getReplicationSafetyInfo() {
+               if ( $this->replicationInfoRow === null ) {
+                       $this->replicationInfoRow = $this->selectRow(
                                false,
                                [
                                        'innodb_autoinc_lock_mode' => '@@innodb_autoinc_lock_mode',
@@ -529,33 +544,9 @@ abstract class DatabaseMysqlBase extends Database {
                                [],
                                __METHOD__
                        );
-                       $this->insertSelectIsSafe = $row->binlog_format === 'ROW' ||
-                               (int)$row->innodb_autoinc_lock_mode === 0;
-               }
-
-               if ( !$this->insertSelectIsSafe ) {
-                       return $this->nonNativeInsertSelect(
-                               $destTable,
-                               $srcTable,
-                               $varMap,
-                               $conds,
-                               $fname,
-                               $insertOptions,
-                               $selectOptions,
-                               $selectJoinConds
-                       );
-               } else {
-                       return parent::nativeInsertSelect(
-                               $destTable,
-                               $srcTable,
-                               $varMap,
-                               $conds,
-                               $fname,
-                               $insertOptions,
-                               $selectOptions,
-                               $selectJoinConds
-                       );
                }
+
+               return $this->replicationInfoRow;
        }
 
        /**
@@ -892,9 +883,15 @@ abstract class DatabaseMysqlBase extends Database {
                }
 
                // Call doQuery() directly, to avoid opening a transaction if DBO_TRX is set
-               if ( $pos->gtids ) {
+               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
@@ -912,7 +909,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 ( !$pos->gtids ) {
+                       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