﻿### Eclipse Workspace Patch 1.0
#P drupal-HEAD
Index: modules/system/system.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.test,v
retrieving revision 1.84
diff -u -r1.84 system.test
--- modules/system/system.test	13 Oct 2009 05:37:46 -0000	1.84
+++ modules/system/system.test	13 Oct 2009 22:56:20 -0000
@@ -24,7 +24,7 @@
    *   specified base table. Defaults to TRUE.
    */
   function assertTableCount($base_table, $count = TRUE) {
-    $tables = db_find_tables(Database::getConnection()->prefixTables('{' . $base_table . '}') . '%');
+    $tables = db_find_tables($base_table . '%');
 
     if ($count) {
       return $this->assertTrue($tables, t('Tables matching "@base_table" found.', array('@base_table' => $base_table)));
Index: includes/database/sqlite/schema.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/database/sqlite/schema.inc,v
retrieving revision 1.10
diff -u -r1.10 schema.inc
--- includes/database/sqlite/schema.inc	29 Sep 2009 15:13:54 -0000	1.10
+++ includes/database/sqlite/schema.inc	13 Oct 2009 22:56:20 -0000
@@ -14,6 +14,11 @@
 
 class DatabaseSchema_sqlite extends DatabaseSchema {
 
+  /**
+   * Override DatabaseSchema::$defaultSchema
+   */
+  protected $defaultSchema = 'main';
+
   public function tableExists($table) {
     // Don't use {} around sqlite_master table.
     return (bool) $this->connection->query("SELECT name FROM sqlite_master WHERE type = 'table' AND name LIKE '{" . $table . "}'", array(), array())->fetchField();
@@ -45,14 +50,19 @@
    */
   protected function createIndexSql($tablename, $schema) {
     $sql = array();
+    $info = $this->getPrefixInfo($tablename);
     if (!empty($schema['unique keys'])) {
       foreach ($schema['unique keys'] as $key => $fields) {
-        $sql[] = 'CREATE UNIQUE INDEX "{' . $tablename . '}_' . $key . '" ON {' . $tablename . '} (' . $this->createKeySql($fields) . "); \n";
+        // Normally we don't escape double quotes (we use single quotes) but
+        // describing the index name like this is faster and is readable.
+        $index = "\"{$info['schema']}\".\"{$info['table']}_$key\"";
+        $sql[] = 'CREATE UNIQUE INDEX ' . $index . ' ON ' . $info['table'] . ' (' . $this->createKeySql($fields) . "); \n";
       }
     }
     if (!empty($schema['indexes'])) {
-      foreach ($schema['indexes'] as $index => $fields) {
-        $sql[] = 'CREATE INDEX "{' . $tablename . '}_' . $index . '" ON {' . $tablename . '} (' . $this->createKeySql($fields) . "); \n";
+      foreach ($schema['indexes'] as $key => $fields) {
+        $index = "\"{$info['schema']}\".\"{$info['table']}_$key\"";
+        $sql[] = 'CREATE INDEX ' . $index . ' ON ' . $info['table'] . ' (' . $this->createKeySql($fields) . "); \n";
       }
     }
     return $sql;
@@ -220,7 +230,13 @@
   *   The new name for the table.
   */
   public function renameTable($table, $new_name) {
-  	$this->connection->query('ALTER TABLE {' . $table . '} RENAME TO {' . $new_name . '}');
+    // SQLite doesn't allow you to rename tables outside of the current 
+    // database. So the syntax '...RENAME TO database.table' would fail. 
+    // So we must determine the full table name here rather than surrounding
+    // the table with curly bracesi incase the db_prefix contains a reference
+    // to a database outside of our existsing database.
+    $info = $this->getPrefixInfo($new_name);
+  	$this->connection->query('ALTER TABLE {' . $table . '} RENAME TO {' . $info['table'] . '}');
   }
 
   /**
@@ -442,7 +458,7 @@
    *   The name of the index.
    */
   public function dropIndex($table, $name) {
-  	$this->connection->query('DROP INDEX ' . '{' . $table . '}_' . $name);
+  	$this->connection->query('DROP INDEX ' . $this->prefixNonTable($table, $name));
   }
 
   /**
@@ -472,7 +488,7 @@
    *   The name of the key.
    */
   public function dropUniqueKey($table, $name) {
-  	$this->connection->query('DROP INDEX ' . '{' . $table . '}_' . $name);
+  	$this->connection->query('DROP INDEX ' . $this->prefixNonTable($table, $name));
   }
 
   /**
Index: includes/database/database.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/database/database.inc,v
retrieving revision 1.80
diff -u -r1.80 database.inc
--- includes/database/database.inc	12 Oct 2009 02:00:04 -0000	1.80
+++ includes/database/database.inc	13 Oct 2009 22:56:20 -0000
@@ -427,7 +427,26 @@
   }
 
   /**
-   * Prepare a query string and return the prepared statement.
+   * Find the prefix for a table.
+   *
+   * This is not used in prefixTables due to performance reasons.
+   */
+  public function tablePrefix($table = 'default') {
+    global $db_prefix;
+    if (is_array($db_prefix)) {
+      if (isset($db_prefix[$table])) {
+        return $db_prefix[$table];
+      }
+      elseif (isset($db_prefix['default'])) {
+        return $db_prefix['default'];
+      }
+      return '';
+    }
+    return $db_prefix;
+  }
+
+  /**
+   *    * Prepare a query string and return the prepared statement.
    *
    * This method caches prepared statements, reusing them when
    * possible. It also prefixes tables names enclosed in curly-braces.
@@ -2427,4 +2446,4 @@
     include_once DRUPAL_ROOT . '/includes/install.inc';
     install_goto('install.php');
   }
-}
+}
\ No newline at end of file
Index: includes/database/schema.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/database/schema.inc,v
retrieving revision 1.22
diff -u -r1.22 schema.inc
--- includes/database/schema.inc	8 Oct 2009 02:44:54 -0000	1.22
+++ includes/database/schema.inc	13 Oct 2009 22:56:20 -0000
@@ -155,6 +155,15 @@
    */
   protected $placeholder = 0;
 
+   * Definition of prefixInfo array structure.
+   *
+   * Rather than redefining DatabaseSchema::getPrefixInfo() for each driver,
+   * by defining the defaultSchema variable only MySQL has to re-write the
+   * method.
+   *
+   * @see DatabaseSchema::getPrefixInfo()
+   */ 
+  protected $defaultSchema = 'public';
 
   public function __construct($connection) {
     $this->connection = $connection;
@@ -165,6 +174,42 @@
   }
 
   /**
+   * Get information about the table name and schema from the db_prefix.
+   *
+   * @param
+   *   Name of table to look prefix up for. Defaults to 'default' because thats
+   *   default key for db_prefix.
+   * @return
+   *   A keyed array with information about the schema, table name and prefix.
+   */
+  protected function getPrefixInfo($table = 'default') {
+    $info = array(
+      'schema' => $this->defaultSchema,
+      'prefix' => $this->connection->tablePrefix($table),
+    );
+    if (($pos = strpos($info['prefix'], '.')) !== FALSE) {
+      $info['schema'] = substr($info['prefix'], 0, $pos);
+      $info['table'] = substr($info['prefix'], ++$pos) . $table;
+    }
+    else {
+      $info['table'] = $info['prefix'] . $table;
+    }
+    return $info;
+  }
+
+  /**
+   * Create names for indexes, primary keys and contstraints.
+   *
+   * This prevents using {} around non-table names like indexes and keys.
+   */
+  function prefixNonTable($table) {
+    $args = func_get_args();
+    $info = $this->getPrefixInfo($table);
+    $args[0] = $info['table'];
+    return implode('_', $args);
+  }
+
+  /**
    * Build a condition to match a table name against a standard information_schema.
    *
    * The information_schema is a SQL standard that provides information about the
@@ -180,7 +225,7 @@
    * to make all the others work. For example see includes/databases/mysql/schema.inc.
    *
    * @param $table_name
-   *   The name of the table to explode.
+   *   The name of the table in question.
    * @param $operator
    *   The operator to apply on the 'table' part of the condition.
    * @return
@@ -189,18 +234,13 @@
   protected function buildTableNameCondition($table_name, $operator = '=') {
     $info = Database::getConnectionInfo();
 
-    // The table name may describe the schema eg. schema.table.
-    if (strpos($table_name, '.')) {
-      list($schema, $table_name) = explode('.', $table_name);
-    }
-    else {
-      $schema = 'public';
-    }
+    // Retrive the table name and schema
+    $table_info = $this->getPrefixInfo($table_name);
 
     $condition = new DatabaseCondition('AND');
     $condition->condition('table_catalog', $info['default']['database']);
-    $condition->condition('table_schema', $schema);
-    $condition->condition('table_name', $table_name, $operator);
+    $condition->condition('table_schema', $table_info['schema']);
+    $condition->condition('table_name', $table_info['table'], $operator);
     return $condition;
   }
 
@@ -213,7 +253,7 @@
    *   false is no table exists otherwise the actual table name.
    */
   public function tableExists($table) {
-    $condition = $this->buildTableNameCondition($this->connection->prefixTables('{' . $table . '}'));
+    $condition = $this->buildTableNameCondition($table);
     $condition->compile($this->connection, $this);
     // Normally, we would heartily discourage the use of string
     // concatination for conditionals like this however, we
@@ -247,7 +287,7 @@
    * Check if a column exists in the given table.
    */
   public function columnExists($table, $column) {
-    $condition = $this->buildTableNameCondition($this->connection->prefixTables('{' . $table . '}'));
+    $condition = $this->buildTableNameCondition($table);
     $condition->condition('column_name', $column);
     $condition->compile($this->connection, $this);
     // Normally, we would heartily discourage the use of string
Index: includes/database/mysql/schema.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/database/mysql/schema.inc,v
retrieving revision 1.27
diff -u -r1.27 schema.inc
--- includes/database/mysql/schema.inc	29 Sep 2009 15:13:54 -0000	1.27
+++ includes/database/mysql/schema.inc	13 Oct 2009 22:56:20 -0000
@@ -25,7 +25,29 @@
   const COMMENT_MAX_COLUMN = 255;
 
   /**
-   * Build a condition to match a table name against a standard information_schema.
+   * Get information about the table and database name from the db_prefix.
+   *
+   * @return
+   *   A keyed array with information about the database, table name and prefix.
+   */
+  protected function getPrefixInfo($table = 'default') {
+    $info = array(
+      'prefix' => $this->connection->tablePrefix($table),
+    );
+    if (($pos = strpos($info['prefix'], '.')) !== FALSE) {
+      $info['database'] = substr($info['prefix'], 0, $pos);
+      $info['table'] = substr($info['prefix'], ++$pos) . $table;
+    }
+    else {
+      $db_info = Database::getConnectionInfo();
+      $info['database'] = $db_info['default']['database'];
+      $info['table'] = $info['prefix'] . $table;
+    }
+    return $info;
+  }
+
+  /**
+    * Build a condition to match a table name against a standard information_schema.
    *
    * MySQL uses databases like schemas rather than catalogs so when we build
    * a condition to query the information_schema.tables, we set the default
@@ -35,16 +57,11 @@
   protected function buildTableNameCondition($table_name, $operator = '=') {
     $info = Database::getConnectionInfo();
 
-    if (strpos($table_name, '.')) {
-      list($schema, $table_name) = explode('.', $table_name);
-    }
-    else {
-      $schema = $info['default']['database'];
-    }
+    $table_info = $this->getPrefixInfo($table_name);
 
     $condition = new DatabaseCondition('AND');
-    $condition->condition('table_schema', $schema);
-    $condition->condition('table_name', $table_name, $operator);
+    $condition->condition('table_schema', $table_info['database']);
+    $condition->condition('table_name', $table_info['table'], $operator);
     return $condition;
   }
 
@@ -269,7 +286,8 @@
   }
 
   public function renameTable($table, $new_name) {
-  	$this->connection->query('ALTER TABLE {' . $table . '} RENAME TO {' . $new_name . '}');
+    $info = $this->getPrefixInfo($new_name);
+    $ret[] = update_sql('ALTER TABLE {' . $table . '} RENAME TO `' . $info['table'] . '`');
   }
 
   public function dropTable($table) {
@@ -367,7 +385,7 @@
    * Retrieve a table or column comment.
    */
   public function getComment($table, $column = NULL) {
-    $condition = $this->buildTableNameCondition($this->connection->prefixTables('{' . $table . '}'));
+    $condition = $this->buildTableNameCondition($table);
     if (isset($column)) {
       $condition->condition('column_name', $column);
       $condition->compile($this->connection, $this);
Index: includes/database/pgsql/schema.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/database/pgsql/schema.inc,v
retrieving revision 1.21
diff -u -r1.21 schema.inc
--- includes/database/pgsql/schema.inc	29 Sep 2009 15:13:54 -0000	1.21
+++ includes/database/pgsql/schema.inc	13 Oct 2009 22:56:20 -0000
@@ -95,7 +95,7 @@
     }
     if (isset($table['unique keys']) && is_array($table['unique keys'])) {
       foreach ($table['unique keys'] as $key_name => $key) {
-        $sql_keys[] = 'CONSTRAINT {' . $name . '}_' . $key_name . '_key UNIQUE (' . implode(', ', $key) . ')';
+        $sql_keys[] = 'CONSTRAINT ' . $this->prefixNonTable($name, $key_name, 'key') . ' UNIQUE (' . implode(', ', $key) . ')';
       }
     }
 
@@ -275,7 +275,12 @@
   }
 
   function renameTable($table, $new_name) {
-  	$this->connection->query('ALTER TABLE {' . $table . '} RENAME TO {' . $new_name . '}');
+    // The new name of a table doesn't need to be referenced by schema.
+    // In the situation where the db_prefix is something like schema.prefix,
+    // the query will fail. So we must figure this out here instead of wrapping
+    // the new column in curly braces.
+    $prefixInfo = $this->getPrefixInfo($new_name);
+  	$this->connection->query('ALTER TABLE {' . $table . '} RENAME TO {' . $prefixInfo['table'] . '}');
   }
 
   public function dropTable($table) {
@@ -395,7 +400,7 @@
    *   The table to be altered.
    */
   public function dropPrimaryKey($table) {
-  	$this->connection->query('ALTER TABLE {' . $table . '} DROP CONSTRAINT {' . $table . '}_pkey');
+  	$this->connection->query('ALTER TABLE {' . $table . '} DROP CONSTRAINT ' . $this->prefixNonTable($table, 'pkey'));
   }
 
   /**
@@ -409,8 +414,7 @@
    *   An array of field names.
    */
   function addUniqueKey($table, $name, $fields) {
-    $name = '{' . $table . '}_' . $name . '_key';
-    $this->connection->query('ALTER TABLE {' . $table . '} ADD CONSTRAINT "' . $name . '" UNIQUE (' . implode(',', $fields) . ')');
+    $this->connection->query('ALTER TABLE {' . $table . '} ADD CONSTRAINT "' . $this->prefixNonTable($table, $name, 'key') . '" UNIQUE (' . implode(',', $fields) . ')'); 
   }
 
   /**
@@ -422,8 +426,7 @@
    *   The name of the key.
    */
   public function dropUniqueKey($table, $name) {
-    $name = '{' . $table . '}_' . $name . '_key';
-    $this->connection->query('ALTER TABLE {' . $table . '} DROP CONSTRAINT "' . $name . '"');
+    $this->connection->query('ALTER TABLE {' . $table . '} DROP CONSTRAINT "' . $this->prefixNonTable($table, $name, 'key') . '"');
   }
 
   /**
@@ -449,8 +452,7 @@
    *   The name of the index.
    */
   public function dropIndex($table, $name) {
-    $name = '{' . $table . '}_' . $name . '_idx';
-    $this->connection->query('DROP INDEX ' . $name);
+    $this->connection->query('DROP INDEX ' . $this->prefixNonTable($table, $name, 'idx'));
   }
 
   /**
@@ -545,7 +547,7 @@
   }
 
   protected function _createIndexSql($table, $name, $fields) {
-    $query = 'CREATE INDEX "{' . $table . '}_' . $name . '_idx" ON {' . $table . '} (';
+    $query = 'CREATE INDEX "' . $this->prefixNonTable($table, $name, 'idx') . '" ON {' . $table . '} (';
     $query .= $this->_createKeySql($fields) . ')';
     return $query;
   }
@@ -570,11 +572,11 @@
    * Retrieve a table or column comment.
    */
   public function getComment($table, $column = NULL) {
-    $table = $this->connection->prefixTables('{' . $table . '}');
+    $info = $this->getPrefixInfo($table);
     // Don't use {} around pg_class, pg_attribute tables.
     if (isset($column)) {
-      $this->connection->query('SELECT col_description(oid, attnum) FROM pg_class, pg_attribute WHERE attrelid = oid AND relname = ? AND attname = ?', array($table, $column))->fetchField();
+      $this->connection->query('SELECT col_description(oid, attnum) FROM pg_class, pg_attribute WHERE attrelid = oid AND relname = ? AND attname = ?', array($info['table'], $column))->fetchField();      
     }
-    $this->connection->query('SELECT obj_description(oid, ?) FROM pg_class WHERE relname = ?', array('pg_class', $table))->fetchField();
+    $this->connection->query('SELECT obj_description(oid, ?) FROM pg_class WHERE relname = ?', array('pg_class', $info['table']))->fetchField();    
   }
 }
Index: sites/default/default.settings.php
===================================================================
RCS file: /cvs/drupal/drupal/sites/default/default.settings.php,v
retrieving revision 1.32
diff -u -r1.32 default.settings.php
--- sites/default/default.settings.php	9 Oct 2009 07:48:07 -0000	1.32
+++ sites/default/default.settings.php	13 Oct 2009 22:56:20 -0000
@@ -130,6 +130,21 @@
  *     'authmap'   => 'shared_',
  *   );
  *
+ * You can also use db_prefix as a reference to a schema/database. This maybe
+ * useful if your Drupal installation exists in a schema that is not the default
+ * or you want to access several databases from the same code base at the same time.
+ * Example:
+ *
+ *  $db_prefix = array(
+ *    'default' => 'main.',
+ *     'users'      => 'shared.',
+ *     'sessions'  => 'shared.',
+ *     'role'      => 'shared.',
+ *     'authmap'   => 'shared.',
+ *  );
+ *
+ * NOTE: MySQL and SQLite's definition of a schema is a database.
+ *
  * Database configuration format:
  *   $databases['default']['default'] = array(
  *     'driver' => 'mysql',
Index: INSTALL.pgsql.txt
===================================================================
RCS file: /cvs/drupal/drupal/INSTALL.pgsql.txt,v
retrieving revision 1.8
diff -u -r1.8 INSTALL.pgsql.txt
--- INSTALL.pgsql.txt	27 Jul 2009 19:42:54 -0000	1.8
+++ INSTALL.pgsql.txt	13 Oct 2009 22:56:19 -0000
@@ -26,3 +26,34 @@
      createdb --encoding=UTF8 --owner=username databasename
 
    If there are no errors then the command was successful
+
+3. CREATE A SCHEMA OR SCHEMAS (Optional advanced)
+
+  Drupal will run across different schemas within your database if you so wish.
+  By default, Drupal runs inside the 'public' schema but you can use $db_prefix
+  inside settings.php to define a schema for Drupal to inside of or specify tables
+  that are shared inside of a separate schema. Drupal will not create schemas for 
+  you, infact the user that Drupal runs as should not be allowed to. You'll need
+  execute the SQL below as a superuser (such as a postgres user) and replace
+  'drupaluser' with the username that Drupal uses to connect to PostgreSQL with
+  and replace schema_name with a schema name you wish to use such as 'shared':
+
+    CREATE SCHEMA schema_name AUTHORIZATION drupaluser;
+
+  Do this for as many schemas as you need. See default.settings.php for how to 
+  set which tables use which schemas.
+3. CREATE A SCHEMA OR SCHEMAS (Optional advanced)
+
+  Drupal will run across different schemas within your database if you so wish.
+  By default, Drupal runs inside the 'public' schema but you can use $db_prefix
+  inside settings.php to define a schema for Drupal to inside of or specify tables
+  that are shared inside of a separate schema. Drupal will not create schemas for 
+  you, infact the user that Drupal runs as should not be allowed to. You'll need
+  execute the SQL below as a superuser (such as a postgres user) and replace
+  'drupaluser' with the username that Drupal uses to connect to PostgreSQL with
+  and replace schema_name with a schema name you wish to use such as 'shared':
+
+    CREATE SCHEMA schema_name AUTHORIZATION drupaluser;
+
+  Do this for as many schemas as you need. See default.settings.php for how to 
+  set which tables use which schemas.
