#195416: Table prefixes are now per database connection, update SimpleTest to deal with this.

From: Damien Tournoud <damien@tournoud.net>


---

 bootstrap.inc                       |   37 +++++++++++
 common.inc                          |    6 +-
 database/database.inc               |  113 ++++++++++++++++++++++++++---------
 database/mysql/database.inc         |    2 +
 database/pgsql/database.inc         |    2 +
 database/sqlite/database.inc        |    2 +
 install.php                         |   27 +++-----
 simpletest/drupal_web_test_case.php |  114 ++++++++++++++++++-----------------
 default/default.settings.php        |   21 ++++--
 update.php                          |   14 +++-
 10 files changed, 223 insertions(+), 115 deletions(-)

diff --git includes/bootstrap.inc includes/bootstrap.inc
index 334ef64..7f163d6 100644
--- includes/bootstrap.inc
+++ includes/bootstrap.inc
@@ -457,7 +457,7 @@ function conf_init() {
   global $base_url, $base_path, $base_root;
 
   // Export the following settings.php variables to the global namespace
-  global $databases, $db_prefix, $cookie_domain, $conf, $installed_profile, $update_free_access, $db_url;
+  global $databases, $cookie_domain, $conf, $installed_profile, $update_free_access;
   $conf = array();
 
   if (file_exists(DRUPAL_ROOT . '/' . conf_path() . '/settings.php')) {
@@ -527,6 +527,41 @@ function conf_init() {
     ini_set('session.cookie_domain', $cookie_domain);
   }
   session_name('SESS' . md5($session_name));
+
+  // Use the simpletest database prefix passed by the user agent header.
+  if (preg_match("/^simpletest\d+$/", $_SERVER['HTTP_USER_AGENT'])) {
+    // Set the test run id for use in other parts of Drupal.
+    drupal_test_info(array('test_run_id' => $_SERVER['HTTP_USER_AGENT'], 'in_child_site' => TRUE));
+
+    foreach ($databases['default'] as $target => $value) {
+      // Extract the current default database prefix.
+      if (empty($value['prefix'])) {
+        $current_prefix = '';
+      }
+      else if (is_array($value['prefix'])) {
+        $current_prefix = $value['prefix']['default'];
+      }
+      else {
+        $current_prefix = $value['prefix'];
+      }
+
+      // Remove the current database prefix and replace it by our own.
+      $databases['default'][$target]['prefix'] = array(
+        'default' => $current_prefix . $_SERVER['HTTP_USER_AGENT'],
+      );
+    }
+  }
+}
+
+/**
+ * Retrieve (and optionally set) information about the current test being run.
+ */
+function drupal_test_info($value = FALSE) {
+  static $cache;
+  if ($value !== FALSE) {
+    $cache = $value;
+  }
+  return $cache;
 }
 
 /**
diff --git includes/common.inc includes/common.inc
index e380270..2945708 100644
--- includes/common.inc
+++ includes/common.inc
@@ -464,8 +464,6 @@ function drupal_access_denied() {
  *       A string containing the response body that was received.
  */
 function drupal_http_request($url, array $options = array()) {
-  global $db_prefix;
-
   $result = new stdClass();
 
   // Parse the URL and make sure we can handle the schema.
@@ -549,8 +547,8 @@ function drupal_http_request($url, array $options = array()) {
   // user-agent is used to ensure that multiple testing sessions running at the
   // same time won't interfere with each other as they would if the database
   // prefix were stored statically in a file or database variable.
-  if (preg_match("/simpletest\d+/", $db_prefix, $matches)) {
-    $options['headers']['User-Agent'] = $matches[0];
+  if ($test_info = drupal_test_info()) {
+    $options['headers']['User-Agent'] = $test_info['test_run_id'];
   }
 
   foreach ($options['headers'] as $name => $value) {
diff --git includes/database/database.inc includes/database/database.inc
index 94aad52..afb94ee 100644
--- includes/database/database.inc
+++ includes/database/database.inc
@@ -319,6 +319,22 @@ abstract class DatabaseConnection extends PDO {
    */
   protected $schema = NULL;
 
+  /**
+   * The default prefix used by this database connection.
+   *
+   * Separated from the other prefixes for performance reasons.
+   *
+   * @var string
+   */
+  protected $defaultPrefix = '';
+
+  /**
+   * The non-default prefixes used by this database connection.
+   *
+   * @var array
+   */
+  protected $prefixes = array();
+
   function __construct($dsn, $username, $password, $driver_options = array()) {
     // Because the other methods don't seem to work right.
     $driver_options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION;
@@ -392,6 +408,24 @@ abstract class DatabaseConnection extends PDO {
   }
 
   /**
+   * Preprocess the prefix used by this database connection.
+   *
+   * @param $prefix
+   *   The prefix, in any of the multiple forms documented in default.settings.php.
+   */
+  protected function setPrefix($prefix) {
+    if (is_array($prefix)) {
+      $this->defaultPrefix = isset($prefix['default']) ? $prefix['default'] : '';
+      unset($prefix['default']);
+      $this->prefixes = $prefix;
+    }
+    else {
+      $this->defaultPrefix = $prefix;
+      $this->prefixes = array();
+    }
+  }
+
+  /**
    * Append a database prefix to all tables in a query.
    *
    * Queries sent to Drupal should wrap all table names in curly brackets. This
@@ -405,27 +439,12 @@ abstract class DatabaseConnection extends PDO {
    *   The properly-prefixed string.
    */
   public function prefixTables($sql) {
-    global $db_prefix;
-
-    if (is_array($db_prefix)) {
-      if (array_key_exists('default', $db_prefix)) {
-        $tmp = $db_prefix;
-        unset($tmp['default']);
-        foreach ($tmp as $key => $val) {
-          $sql = strtr($sql, array('{' . $key . '}' => $val . $key));
-        }
-        return strtr($sql, array('{' => $db_prefix['default'] , '}' => ''));
-      }
-      else {
-        foreach ($db_prefix as $key => $val) {
-          $sql = strtr($sql, array('{' . $key . '}' => $val . $key));
-        }
-        return strtr($sql, array('{' => '' , '}' => ''));
-      }
-    }
-    else {
-      return strtr($sql, array('{' => $db_prefix , '}' => ''));
+    // Replace specific table prefixes first.
+    foreach ($this->prefixes as $key => $val) {
+      $sql = strtr($sql, array('{' . $key . '}' => $val . $key));
     }
+    // Then replace remaining tables with the default prefix.
+    return strtr($sql, array('{' => $this->defaultPrefix , '}' => ''));
   }
 
   /**
@@ -1240,6 +1259,20 @@ abstract class Database {
         if (empty($value['driver'])) {
           $databaseInfo[$index][$target] = $databaseInfo[$index][$target][mt_rand(0, count($databaseInfo[$index][$target]) - 1)];
         }
+
+        // Parse the prefix information.
+        if (!isset($databaseInfo[$index][$target]['prefix'])) {
+          // Default to an empty prefix.
+          $databaseInfo[$index][$target]['prefix'] = array(
+            'default' => '',
+          );
+        }
+        else if (!is_array($databaseInfo[$index][$target]['prefix'])) {
+          // Transform the flat form into an array form.
+          $databaseInfo[$index][$target]['prefix'] = array(
+            'default' => $databaseInfo[$index][$target]['prefix'],
+          );
+        }
       }
     }
 
@@ -1286,7 +1319,40 @@ abstract class Database {
     if (!empty(self::$databaseInfo[$key])) {
       return self::$databaseInfo[$key];
     }
+  }
 
+  /**
+   * Rename a connection and its corresponding connection information.
+   *
+   * @param $old_key
+   *   The old connection key.
+   * @param $new_key
+   *   The new connection key.
+   */
+  final public static function renameConnection($old_key, $new_key) {
+    if (empty(self::$databaseInfo)) {
+      self::parseConnectionInfo();
+    }
+
+    if (!empty(self::$databaseInfo[$old_key]) && empty(self::$databaseInfo[$new_key])) {
+      self::$databaseInfo[$new_key] = self::$databaseInfo[$old_key];
+      unset(self::$databaseInfo[$old_key]);
+      if (isset(self::$connections[$old_key])) {
+        self::$connections[$new_key] = self::$connections[$old_key];
+        unset(self::$connections[$old_key]);
+      }
+    }
+  }
+
+  /**
+   * Remove a connection and its corresponding connection information.
+   *
+   * @param $key
+   *   The connection key.
+   */
+  final public static function removeConnection($key) {
+    unset(self::$databaseInfo[$key]);
+    unset(self::$connections[$key]);
   }
 
   /**
@@ -1299,7 +1365,6 @@ abstract class Database {
    *   The database target to open.
    */
   final protected static function openConnection($key, $target) {
-    global $db_prefix;
 
     if (empty(self::$databaseInfo)) {
       self::parseConnectionInfo();
@@ -1326,12 +1391,6 @@ abstract class Database {
       if (!empty(self::$logs[$key])) {
         $new_connection->setLogger(self::$logs[$key]);
       }
-
-      // We need to pass around the simpletest database prefix in the request
-      // and we put that in the user_agent header.
-      if (preg_match("/^simpletest\d+$/", $_SERVER['HTTP_USER_AGENT'])) {
-        $db_prefix .= $_SERVER['HTTP_USER_AGENT'];
-      }
       return $new_connection;
     }
     catch (Exception $e) {
diff --git includes/database/mysql/database.inc includes/database/mysql/database.inc
index 47573a2..b0768f7 100644
--- includes/database/mysql/database.inc
+++ includes/database/mysql/database.inc
@@ -25,6 +25,8 @@ class DatabaseConnection_mysql extends DatabaseConnection {
       $connection_options['port'] = 3306;
     }
 
+    $this->setPrefix(isset($connection_options['prefix']) ? $connection_options['prefix'] : '');
+
     $dsn = 'mysql:host=' . $connection_options['host'] . ';port=' . $connection_options['port'] . ';dbname=' . $connection_options['database'];
     parent::__construct($dsn, $connection_options['username'], $connection_options['password'], array(
       // So we don't have to mess around with cursors and unbuffered queries by default.
diff --git includes/database/pgsql/database.inc includes/database/pgsql/database.inc
index 292eb89..7fbf458 100644
--- includes/database/pgsql/database.inc
+++ includes/database/pgsql/database.inc
@@ -26,6 +26,8 @@ class DatabaseConnection_pgsql extends DatabaseConnection {
       $connection_options['port'] = 5432;
     }
 
+    $this->setPrefix(isset($connection_options['prefix']) ? $connection_options['prefix'] : '');
+
     $dsn = 'pgsql:host=' . $connection_options['host'] . ' dbname=' . $connection_options['database'] . ' port=' . $connection_options['port'];
     parent::__construct($dsn, $connection_options['username'], $connection_options['password'], array(
       // Convert numeric values to strings when fetching.
diff --git includes/database/sqlite/database.inc includes/database/sqlite/database.inc
index 7b97951..9d3fa92 100644
--- includes/database/sqlite/database.inc
+++ includes/database/sqlite/database.inc
@@ -25,6 +25,8 @@ class DatabaseConnection_sqlite extends DatabaseConnection {
     // This driver defaults to transaction support, except if explicitly passed FALSE.
     $this->transactionSupport = !isset($connection_options['transactions']) || $connection_options['transactions'] !== FALSE;
 
+    $this->setPrefix(isset($connection_options['prefix']) ? $connection_options['prefix'] : '');
+
     parent::__construct('sqlite:'. $connection_options['database'], '', '', array(
       // Force column names to lower case.
       PDO::ATTR_CASE => PDO::CASE_LOWER,
diff --git install.php install.php
index 0f23ebe..acde279 100644
--- install.php
+++ install.php
@@ -115,9 +115,9 @@ function install_main() {
 
   // Tasks come after the database is set up
   if (!$task) {
-    global $db_url;
+    global $databases;
 
-    if (!$verify && !empty($db_url)) {
+    if (!$verify && !empty($databases)) {
       // Do not install over a configured settings.php.
       install_already_done_error();
     }
@@ -177,7 +177,7 @@ function install_verify_drupal() {
  * Verify existing settings.php
  */
 function install_verify_settings() {
-  global $db_prefix, $databases;
+  global $databases;
 
   // Verify existing settings (if any).
   if (!empty($databases)) {
@@ -200,7 +200,7 @@ function install_verify_settings() {
  * Configure and rewrite settings.php.
  */
 function install_change_settings($profile = 'default', $install_locale = '') {
-  global $databases, $db_prefix;
+  global $databases;
 
   $conf_path = './' . conf_path(FALSE, TRUE);
   $settings_file = $conf_path . '/settings.php';
@@ -306,13 +306,13 @@ function install_settings_form(&$form_state, $profile, $install_locale, $setting
     );
 
     // Table prefix
-    $db_prefix = ($profile == 'default') ? 'drupal_' : $profile . '_';
-    $form['advanced_options']['db_prefix'] = array(
+    $prefix = ($profile == 'default') ? 'drupal_' : $profile . '_';
+    $form['advanced_options']['prefix'] = array(
       '#type' => 'textfield',
       '#title' => st('Table prefix'),
       '#default_value' => '',
       '#size' => 45,
-      '#description' => st('If more than one application will be sharing this database, enter a table prefix such as %prefix for your @drupal site here.', array('@drupal' => drupal_install_profile_name(), '%prefix' => $db_prefix)),
+      '#description' => st('If more than one application will be sharing this database, enter a table prefix such as %prefix for your @drupal site here.', array('@drupal' => drupal_install_profile_name(), '%prefix' => $prefix)),
     );
 
     $form['save'] = array(
@@ -333,7 +333,6 @@ function install_settings_form(&$form_state, $profile, $install_locale, $setting
  * Form API validate for install_settings form.
  */
 function install_settings_form_validate($form, &$form_state) {
-  global $db_url;
   _install_settings_form_validate($form_state['values'], $form_state['values']['settings_file'], $form_state, $form);
 }
 
@@ -343,12 +342,12 @@ function install_settings_form_validate($form, &$form_state) {
 function _install_settings_form_validate($database, $settings_file, &$form_state, $form = NULL) {
   global $databases;
   // Verify the table prefix
-  if (!empty($database['prefix']) && is_string($database['prefix']) && !preg_match('/^[A-Za-z0-9_.]+$/', $database['dprefix'])) {
-    form_set_error('db_prefix', st('The database table prefix you have entered, %db_prefix, is invalid. The table prefix can only contain alphanumeric characters, periods, or underscores.', array('%db_prefix' => $db_prefix)), 'error');
+  if (!empty($database['prefix']) && is_string($database['prefix']) && !preg_match('/^[A-Za-z0-9_.]+$/', $database['prefix'])) {
+    form_set_error('prefix', st('The database table prefix you have entered, %prefix, is invalid. The table prefix can only contain alphanumeric characters, periods, or underscores.', array('%prefix' => $prefix)), 'error');
   }
 
   if (!empty($database['port']) && !is_numeric($database['port'])) {
-    form_set_error('db_port', st('Database port must be a number.'));
+    form_set_error('port', st('Database port must be a number.'));
   }
 
   // Check database type
@@ -382,16 +381,12 @@ function _install_settings_form_validate($database, $settings_file, &$form_state
 function install_settings_form_submit($form, &$form_state) {
   global $profile, $install_locale;
 
-  $database = array_intersect_key($form_state['values']['_database'], array_flip(array('driver', 'database', 'username', 'password', 'host', 'port')));
+  $database = array_intersect_key($form_state['values']['_database'], array_flip(array('driver', 'database', 'username', 'password', 'host', 'port', 'prefix')));
   // Update global settings array and save
   $settings['databases'] = array(
     'value'    => array('default' => array('default' => $database)),
     'required' => TRUE,
   );
-  $settings['db_prefix'] = array(
-    'value'    => $form_state['values']['db_prefix'],
-    'required' => TRUE,
-  );
   drupal_rewrite_settings($settings);
 
   // Continue to install profile step
diff --git modules/simpletest/drupal_web_test_case.php modules/simpletest/drupal_web_test_case.php
index 9372288..716fe00 100644
--- modules/simpletest/drupal_web_test_case.php
+++ modules/simpletest/drupal_web_test_case.php
@@ -78,11 +78,11 @@ class DrupalWebTestCase {
   protected $additionalCurlOptions = array();
 
   /**
-   * The original database prefix, before it was changed for testing purposes.
+   * The database prefix of this test run.
    *
    * @var string
    */
-  protected $originalPrefix = NULL;
+  protected $databasePrefix = NULL;
 
   /**
    * The original file directory, before it was changed for testing purposes.
@@ -150,8 +150,6 @@ class DrupalWebTestCase {
    *   is the caller function itself.
    */
   private function assert($status, $message = '', $group = 'Other', array $caller = NULL) {
-    global $db_prefix;
-
     // Convert boolean status to string status.
     if (is_bool($status)) {
       $status = $status ? 'pass' : 'fail';
@@ -165,10 +163,6 @@ class DrupalWebTestCase {
       $caller = $this->getAssertionCall();
     }
 
-    // Switch to non-testing database to store results in.
-    $current_db_prefix = $db_prefix;
-    $db_prefix = $this->originalPrefix;
-
     // Creation assertion array that can be displayed while tests are running.
     $this->assertions[] = $assertion = array(
       'test_id' => $this->testId,
@@ -182,10 +176,10 @@ class DrupalWebTestCase {
     );
 
     // Store assertion for display after the test has completed.
-    db_insert('simpletest')->fields($assertion)->execute();
-
-    // Return to testing prefix.
-    $db_prefix = $current_db_prefix;
+    Database::getConnection('default', 'simpletest_original_default')
+      ->insert('simpletest')
+      ->fields($assertion)
+      ->execute();
     return $status == 'pass' ? TRUE : FALSE;
   }
 
@@ -812,14 +806,26 @@ class DrupalWebTestCase {
    *   List of modules to enable for the duration of the test.
    */
   protected function setUp() {
-    global $db_prefix, $user;
+    global $user;
+
+    // Generate a temporary prefixed database to ensure that tests have a clean starting point.
+    $this->databasePrefix = 'simpletest' . mt_rand(1000, 1000000);
+
+    // Clone the current connection and replace the current prefix.
+    $connection_info = Database::getConnectionInfo('default');
+    Database::renameConnection('default', 'simpletest_original_default');
+    foreach ($connection_info as $target => $value) {
+      $connection_info[$target]['prefix'] = array(
+        'default' => $value['prefix']['default'] . $this->databasePrefix,
+      );
+    }
+    Database::addConnectionInfo('default', 'default', $connection_info['default']);
 
     // Store necessary current values before switching to prefixed database.
-    $this->originalPrefix = $db_prefix;
     $clean_url_original = variable_get('clean_url', 0);
 
-    // Generate temporary prefixed database to ensure that tests have a clean starting point.
-    $db_prefix = Database::getConnection()->prefixTables('{simpletest' . mt_rand(1000, 1000000) . '}');
+    // Set the simpletest id for use in other parts of Drupal.
+    drupal_test_info(array('test_run_id' => $this->databasePrefix, 'in_child_site' => FALSE));
 
     include_once DRUPAL_ROOT . '/includes/install.inc';
     drupal_install_system();
@@ -860,7 +866,7 @@ class DrupalWebTestCase {
 
     // Use temporary files directory with the same prefix as database.
     $this->originalFileDirectory = file_directory_path();
-    variable_set('file_directory_path', file_directory_path() . '/' . $db_prefix);
+    variable_set('file_directory_path', file_directory_path() . '/' . $this->databasePrefix);
     $directory = file_directory_path();
     file_check_directory($directory, FILE_CREATE_DIRECTORY); // Create the files directory.
     set_time_limit($this->timeLimit);
@@ -872,8 +878,10 @@ class DrupalWebTestCase {
    * setup a clean environment for the current test run.
    */
   protected function preloadRegistry() {
-    db_query('INSERT INTO {registry} SELECT * FROM ' . $this->originalPrefix . 'registry');
-    db_query('INSERT INTO {registry_file} SELECT * FROM ' . $this->originalPrefix . 'registry_file');
+    $original_connection = Database::getConnection('default', 'simpletest_original_default');
+    $this->pass('INSERT INTO {registry} SELECT * FROM ' . $original_connection->prefixTables('{registry}'));
+    db_query('INSERT INTO {registry} SELECT * FROM ' . $original_connection->prefixTables('{registry}'));
+    db_query('INSERT INTO {registry_file} SELECT * FROM ' . $original_connection->prefixTables('{registry_file}'));
   }
 
   /**
@@ -899,44 +907,44 @@ class DrupalWebTestCase {
    * and reset the database prefix.
    */
   protected function tearDown() {
-    global $db_prefix, $user;
-    if (preg_match('/simpletest\d+/', $db_prefix)) {
-      // Delete temporary files directory and reset files directory path.
-      file_unmanaged_delete_recursive(file_directory_path());
-      variable_set('file_directory_path', $this->originalFileDirectory);
-
-      // Remove all prefixed tables (all the tables in the schema).
-      $schema = drupal_get_schema(NULL, TRUE);
-      $ret = array();
-      foreach ($schema as $name => $table) {
-        db_drop_table($ret, $name);
-      }
+    global $user;
 
-      // Return the database prefix to the original.
-      $db_prefix = $this->originalPrefix;
+    // Delete temporary files directory and reset files directory path.
+    file_unmanaged_delete_recursive(file_directory_path());
+    variable_set('file_directory_path', $this->originalFileDirectory);
 
-      // Return the user to the original one.
-      $user = $this->originalUser;
-      drupal_save_session(TRUE);
+    // Remove all prefixed tables (all the tables in the schema).
+    $schema = drupal_get_schema(NULL, TRUE);
+    $ret = array();
+    foreach ($schema as $name => $table) {
+      db_drop_table($ret, $name);
+    }
 
-      // Ensure that internal logged in variable and cURL options are reset.
-      $this->isLoggedIn = FALSE;
-      $this->additionalCurlOptions = array();
+    // Get back to the original connection.
+    Database::removeConnection('default');
+    Database::renameConnection('simpletest_original_default', 'default');
 
-      // Reload module list and implementations to ensure that test module hooks
-      // aren't called after tests.
-      module_list(TRUE);
-      module_implements(MODULE_IMPLEMENTS_CLEAR_CACHE);
+    // Return the user to the original one.
+    $user = $this->originalUser;
+    drupal_save_session(TRUE);
 
-      // Reset the Field API.
-      field_cache_clear();
+    // Ensure that internal logged in variable and cURL options are reset.
+    $this->isLoggedIn = FALSE;
+    $this->additionalCurlOptions = array();
 
-      // Rebuild caches.
-      $this->refreshVariables();
+    // Reload module list and implementations to ensure that test module hooks
+    // aren't called after tests.
+    module_list(TRUE);
+    module_implements(MODULE_IMPLEMENTS_CLEAR_CACHE);
 
-      // Close the CURL handler.
-      $this->curlClose();
-    }
+    // Reset the Field API.
+    field_cache_clear();
+
+    // Rebuild caches.
+    $this->refreshVariables();
+
+    // Close the CURL handler.
+    $this->curlClose();
   }
 
   /**
@@ -947,7 +955,7 @@ class DrupalWebTestCase {
    * see the description of $curl_options among the properties.
    */
   protected function curlInitialize() {
-    global $base_url, $db_prefix;
+    global $base_url;
     if (!isset($this->curlHandle)) {
       $this->curlHandle = curl_init();
       $curl_options = $this->additionalCurlOptions + array(
@@ -959,10 +967,8 @@ class DrupalWebTestCase {
         CURLOPT_SSL_VERIFYPEER => FALSE,  // Required to make the tests run on https://
         CURLOPT_SSL_VERIFYHOST => FALSE,  // Required to make the tests run on https://
         CURLOPT_HEADERFUNCTION => array(&$this, 'curlHeaderCallback'),
+        CURLOPT_USERAGENT => $this->databasePrefix,
       );
-      if (preg_match('/simpletest\d+/', $db_prefix, $matches)) {
-        $curl_options[CURLOPT_USERAGENT] = $matches[0];
-      }
       if (!isset($curl_options[CURLOPT_USERPWD]) && ($auth = variable_get('simpletest_httpauth_username', ''))) {
         if ($pass = variable_get('simpletest_httpauth_pass', '')) {
           $auth .= ':' . $pass;
diff --git sites/default/default.settings.php sites/default/default.settings.php
index 8357c04..f563b92 100644
--- sites/default/default.settings.php
+++ sites/default/default.settings.php
@@ -61,6 +61,7 @@
  *   'password' => 'password',
  *   'host' => 'localhost',
  *   'port' => 3306,
+ *   'prefix' => 'myprefix_',
  * );
  *
  * The "driver" property indicates what Drupal database driver the 
@@ -105,30 +106,31 @@
  *   'username' => 'username',
  *   'password' => 'password',
  *   'host' => 'localhost',
+ *   'prefix' => 'main_',
  * );
  *
  * You can optionally set prefixes for some or all database table names
- * by using the $db_prefix setting. If a prefix is specified, the table
+ * by using the 'prefix' setting. If a prefix is specified, the table
  * name will be prepended with its value. Be sure to use valid database
  * characters only, usually alphanumeric and underscore. If no prefixes
  * are desired, leave it as an empty string ''.
  *
- * To have all database names prefixed, set $db_prefix as a string:
+ * To have all database names prefixed, set 'prefix' as a string:
  *
- *   $db_prefix = 'main_';
+ *   'prefix' => 'main_',
  *
- * To provide prefixes for specific tables, set $db_prefix as an array.
+ * To provide prefixes for specific tables, set 'prefix' as an array.
  * The array's keys are the table names and the values are the prefixes.
- * The 'default' element holds the prefix for any tables not specified
- * elsewhere in the array. Example:
+ * The 'default' element is mandatory and holds the prefix for any tables
+ * not specified elsewhere in the array. Example:
  *
- *   $db_prefix = array(
+ *   'prefix' = array(
  *     'default'   => 'main_',
  *     'users'      => 'shared_',
  *     'sessions'  => 'shared_',
  *     'role'      => 'shared_',
  *     'authmap'   => 'shared_',
- *   );
+ *   ),
  *
  * Database configuration format:
  *   $databases['default']['default'] = array(
@@ -137,6 +139,7 @@
  *     'username' => 'username',
  *     'password' => 'password',
  *     'host' => 'localhost',
+ *     'prefix' => '',
  *   );
  *   $databases['default']['default'] = array(
  *     'driver' => 'pgsql',
@@ -144,6 +147,7 @@
  *     'username' => 'username',
  *     'password' => 'password',
  *     'host' => 'localhost',
+ *     'prefix' => '',
  *   );
  *   $databases['default']['default'] = array(
  *     'driver' => 'sqlite',
@@ -151,7 +155,6 @@
  *   );
  */
 $databases = array();
-$db_prefix = '';
 
 /**
  * Access control for update.php script
diff --git update.php update.php
index 5773c47..31be162 100644
--- update.php
+++ update.php
@@ -625,12 +625,12 @@ function update_task_list($active = NULL) {
  * Check update requirements and report any errors.
  */
 function update_check_requirements() {
-  global $db_url, $databases;
+  global $databases;
   $requirements = array();
 
   // If we will rewrite the settings.php then we need to make sure it is
   // writeable.
-  if (empty($databases) && !empty($db_url) && is_string($db_url)) {
+  if (empty($databases)) {
     $requirements = install_check_requirements('', FALSE);
   }
   $warnings = FALSE;
@@ -659,7 +659,10 @@ function update_check_requirements() {
  * Converts Drupal 6 $db_url to Drupal 7 $databases array.
  */
 function update_check_d7_settings() {
-  global $db_url, $databases;
+  // Reinclude the settings.php, to get old $db_url and $db_prefix.
+  if (file_exists(DRUPAL_ROOT . '/' . conf_path() . '/settings.php')) {
+    include_once DRUPAL_ROOT . '/' . conf_path() . '/settings.php';
+  }
 
   if (empty($databases) && !empty($db_url) && is_string($db_url)) {
     $url = parse_url($db_url);
@@ -670,7 +673,10 @@ function update_check_d7_settings() {
     $databases['default']['default']['driver'] = $driver;
     $databases['default']['default']['database'] = substr($url['path'], 1);
     foreach (array('user' => 'username', 'pass' => 'password', 'host' => 'host', 'port' => 'port') as $old_key => $new_key) {
-      $databases['default']['default'][$new_key] =  isset($url[$old_key]) ? urldecode($url[$old_key]) : '';
+      $databases['default']['default'][$new_key] = isset($url[$old_key]) ? urldecode($url[$old_key]) : '';
+    }
+    if (!empty($db_prefix)) {
+      $databases['default']['default']['prefix'] = $db_prefix;
     }
     $conf_path = conf_path();
     file_put_contents($conf_path .'/settings.php', "\n" . '$databases = '. var_export($databases, TRUE) . ';', FILE_APPEND);
