rdbms: specify DB name and table prefix even for the local domain
[lhc/web/wiklou.git] / includes / libs / rdbms / loadbalancer / LoadBalancer.php
index eb288dd..b01b23f 100644 (file)
@@ -146,17 +146,10 @@ class LoadBalancer implements ILoadBalancer {
                        }
                }
 
-               $this->localDomain = isset( $params['localDomain'] )
+               $localDomain = isset( $params['localDomain'] )
                        ? DatabaseDomain::newFromId( $params['localDomain'] )
                        : DatabaseDomain::newUnspecified();
-               // In case a caller assumes that the domain ID is simply <db>-<prefix>, which is almost
-               // always true, gracefully handle the case when they fail to account for escaping.
-               if ( $this->localDomain->getTablePrefix() != '' ) {
-                       $this->localDomainIdAlias =
-                               $this->localDomain->getDatabase() . '-' . $this->localDomain->getTablePrefix();
-               } else {
-                       $this->localDomainIdAlias = $this->localDomain->getDatabase();
-               }
+               $this->setLocalDomain( $localDomain );
 
                $this->mWaitTimeout = isset( $params['waitTimeout'] ) ? $params['waitTimeout'] : 10;
 
@@ -232,7 +225,9 @@ class LoadBalancer implements ILoadBalancer {
                $this->host = isset( $params['hostname'] )
                        ? $params['hostname']
                        : ( gethostname() ?: 'unknown' );
-               $this->cliMode = isset( $params['cliMode'] ) ? $params['cliMode'] : PHP_SAPI === 'cli';
+               $this->cliMode = isset( $params['cliMode'] )
+                       ? $params['cliMode']
+                       : ( PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' );
                $this->agent = isset( $params['agent'] ) ? $params['agent'] : '';
 
                if ( isset( $params['chronologyProtector'] ) ) {
@@ -240,6 +235,17 @@ class LoadBalancer implements ILoadBalancer {
                }
        }
 
+       /**
+        * Get the local (and default) database domain ID of connection handles
+        *
+        * @see DatabaseDomain
+        * @return string Database domain ID; this specifies DB name, schema, and table prefix
+        * @since 1.31
+        */
+       public function getLocalDomainID() {
+               return $this->localDomain->getId();
+       }
+
        /**
         * Get a LoadMonitor instance
         *
@@ -645,12 +651,6 @@ class LoadBalancer implements ILoadBalancer {
                $oldConnsOpened = $this->connsOpened; // connections open now
 
                if ( $i == self::DB_MASTER ) {
-                       if ( $flags & self::CONN_NO_WRITE ) {
-                               throw new InvalidArgumentException(
-                                       'Cannot set CONN_NO_WRITE flag on master connection!'
-                               );
-                       }
-
                        $i = $this->getWriterIndex();
                } else {
                        # Try to find an available server in any the query groups (in order)
@@ -661,9 +661,6 @@ class LoadBalancer implements ILoadBalancer {
                                        break;
                                }
                        }
-
-                       // Request no-write connection, even if $i == $this->getWriterIndex().
-                       $flags |= self::CONN_NO_WRITE;
                }
 
                # Operation-based index
@@ -680,9 +677,6 @@ class LoadBalancer implements ILoadBalancer {
                                $this->reportConnectionError();
                                return null; // not reached
                        }
-
-                       // Request no-write connection, even if $i == $this->getWriterIndex().
-                       $flags |= self::CONN_NO_WRITE;
                }
 
                # Now we have an explicit index into the servers array
@@ -803,13 +797,6 @@ class LoadBalancer implements ILoadBalancer {
                // a) those are usually set to implicitly use transaction rounds via DBO_TRX
                // b) those must support the use of explicit transaction rounds via beginMasterChanges()
                $autoCommit = ( ( $flags & self::CONN_TRX_AUTO ) == self::CONN_TRX_AUTO );
-               $noWrite = ( ( $flags & self::CONN_NO_WRITE ) == self::CONN_NO_WRITE );
-
-               if ( $noWrite && $i === $this->getWriterIndex() ) {
-                       // We can't disable writes on the master connection!
-                       // TODO: Wrap the master connection, so write operations fail!
-                       $noWrite = false;
-               }
 
                if ( $domain !== false ) {
                        // Connection is to a foreign domain
@@ -826,9 +813,12 @@ class LoadBalancer implements ILoadBalancer {
                                // Open a new connection
                                $server = $this->mServers[$i];
                                $server['serverIndex'] = $i;
-                               $server['noWrite'] = $noWrite;
                                $server['autoCommitOnly'] = $autoCommit;
-                               $conn = $this->reallyOpenConnection( $server, false );
+                               if ( $this->localDomain->getDatabase() !== null ) {
+                                       // Use the local domain table prefix if the local domain is specified
+                                       $server['tablePrefix'] = $this->localDomain->getTablePrefix();
+                               }
+                               $conn = $this->reallyOpenConnection( $server, $this->localDomain->getDatabase() );
                                $host = $this->getServerName( $i );
                                if ( $conn->isOpen() ) {
                                        $this->connLogger->debug( "Connected to database $i at '$host'." );
@@ -883,13 +873,6 @@ class LoadBalancer implements ILoadBalancer {
                $dbName = $domainInstance->getDatabase();
                $prefix = $domainInstance->getTablePrefix();
                $autoCommit = ( ( $flags & self::CONN_TRX_AUTO ) == self::CONN_TRX_AUTO );
-               $noWrite = ( ( $flags & self::CONN_NO_WRITE ) == self::CONN_NO_WRITE );
-
-               if ( $noWrite && $i === $this->getWriterIndex() ) {
-                       // We can't disable writes on the master connection!
-                       // TODO: Wrap the master connection, so write operations fail!
-                       $noWrite = false;
-               }
 
                if ( $autoCommit ) {
                        $connFreeKey = self::KEY_FOREIGN_FREE_NOROUND;
@@ -913,8 +896,6 @@ class LoadBalancer implements ILoadBalancer {
                        // Reuse a free connection from another domain
                        $conn = reset( $this->mConns[$connFreeKey][$i] );
                        $oldDomain = key( $this->mConns[$connFreeKey][$i] );
-                       // The empty string as a DB name means "don't care".
-                       // DatabaseMysqlBase::open() already handle this on connection.
                        if ( strlen( $dbName ) && !$conn->selectDB( $dbName ) ) {
                                $this->mLastError = "Error selecting database '$dbName' on server " .
                                        $conn->getServer() . " from client host {$this->host}";
@@ -923,7 +904,8 @@ class LoadBalancer implements ILoadBalancer {
                        } else {
                                $conn->tablePrefix( $prefix );
                                unset( $this->mConns[$connFreeKey][$i][$oldDomain] );
-                               $this->mConns[$connInUseKey][$i][$domain] = $conn;
+                               // Note that if $domain is an empty string, getDomainID() might not match it
+                               $this->mConns[$connInUseKey][$i][$conn->getDomainId()] = $conn;
                                $this->connLogger->debug( __METHOD__ .
                                        ": reusing free connection from $oldDomain for $domain" );
                        }
@@ -937,15 +919,15 @@ class LoadBalancer implements ILoadBalancer {
                        $server['foreignPoolRefCount'] = 0;
                        $server['foreign'] = true;
                        $server['autoCommitOnly'] = $autoCommit;
-                       $server['noWrite'] = $noWrite;
                        $conn = $this->reallyOpenConnection( $server, $dbName );
                        if ( !$conn->isOpen() ) {
                                $this->connLogger->warning( __METHOD__ . ": connection error for $i/$domain" );
                                $this->errorConnection = $conn;
                                $conn = false;
                        } else {
-                               $conn->tablePrefix( $prefix );
-                               $this->mConns[$connInUseKey][$i][$domain] = $conn;
+                               $conn->tablePrefix( $prefix ); // as specified
+                               // Note that if $domain is an empty string, getDomainID() might not match it
+                               $this->mConns[$connInUseKey][$i][$conn->getDomainID()] = $conn;
                                $this->connLogger->debug( __METHOD__ . ": opened new connection for $i/$domain" );
                        }
                }
@@ -967,7 +949,7 @@ class LoadBalancer implements ILoadBalancer {
         * @return bool
         */
        private function isOpen( $index ) {
-               if ( !is_integer( $index ) ) {
+               if ( !is_int( $index ) ) {
                        return false;
                }
 
@@ -975,22 +957,22 @@ class LoadBalancer implements ILoadBalancer {
        }
 
        /**
-        * Really opens a connection. Uncached.
+        * Open a new network connection to a server (uncached)
+        *
         * Returns a Database object whether or not the connection was successful.
-        * @access private
         *
         * @param array $server
-        * @param string|bool $dbNameOverride Use "" to not select any database
+        * @param string|null $dbNameOverride Use "" to not select any database
         * @return Database
         * @throws DBAccessError
         * @throws InvalidArgumentException
         */
-       protected function reallyOpenConnection( array $server, $dbNameOverride = false ) {
+       protected function reallyOpenConnection( array $server, $dbNameOverride ) {
                if ( $this->disabled ) {
                        throw new DBAccessError();
                }
 
-               if ( $dbNameOverride !== false ) {
+               if ( $dbNameOverride !== null ) {
                        $server['dbname'] = $dbNameOverride;
                }
 
@@ -1108,26 +1090,6 @@ class LoadBalancer implements ILoadBalancer {
                return isset( $this->mServers[$i]['type'] ) ? $this->mServers[$i]['type'] : 'unknown';
        }
 
-       /**
-        * @deprecated Since 1.30, no alternative
-        */
-       public function getServerInfo( $i ) {
-               wfDeprecated( __METHOD__, '1.30' );
-               if ( isset( $this->mServers[$i] ) ) {
-                       return $this->mServers[$i];
-               } else {
-                       return false;
-               }
-       }
-
-       /**
-        * @deprecated Since 1.30, construct new object
-        */
-       public function setServerInfo( $i, array $serverInfo ) {
-               wfDeprecated( __METHOD__, '1.30' );
-               $this->mServers[$i] = $serverInfo;
-       }
-
        public function getMasterPos() {
                # If this entire request was served from a replica DB without opening a connection to the
                # master (however unlikely that may be), then we can fetch the position from the replica DB.
@@ -1726,17 +1688,35 @@ class LoadBalancer implements ILoadBalancer {
                                "Foreign domain connections are still in use ($domains)." );
                }
 
-               $this->localDomain = new DatabaseDomain(
+               $oldDomain = $this->localDomain->getId();
+               $this->setLocalDomain( new DatabaseDomain(
                        $this->localDomain->getDatabase(),
                        null,
                        $prefix
-               );
+               ) );
 
-               $this->forEachOpenConnection( function ( IDatabase $db ) use ( $prefix ) {
-                       $db->tablePrefix( $prefix );
+               $this->forEachOpenConnection( function ( IDatabase $db ) use ( $prefix, $oldDomain ) {
+                       if ( !$db->getLBInfo( 'foreign' ) ) {
+                               $db->tablePrefix( $prefix );
+                       }
                } );
        }
 
+       /**
+        * @param DatabaseDomain $domain
+        */
+       private function setLocalDomain( DatabaseDomain $domain ) {
+               $this->localDomain = $domain;
+               // In case a caller assumes that the domain ID is simply <db>-<prefix>, which is almost
+               // always true, gracefully handle the case when they fail to account for escaping.
+               if ( $this->localDomain->getTablePrefix() != '' ) {
+                       $this->localDomainIdAlias =
+                               $this->localDomain->getDatabase() . '-' . $this->localDomain->getTablePrefix();
+               } else {
+                       $this->localDomainIdAlias = $this->localDomain->getDatabase();
+               }
+       }
+
        /**
         * Make PHP ignore user aborts/disconnects until the returned
         * value leaves scope. This returns null and does nothing in CLI mode.