diff --git a/INSTALL.pgsql.txt b/INSTALL.pgsql.txt index f5f276e..57178bb 100644 --- a/INSTALL.pgsql.txt +++ b/INSTALL.pgsql.txt @@ -26,3 +26,19 @@ Note that the database must be created with UTF-8 (Unicode) encoding. 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. diff --git a/includes/database/database.inc b/includes/database/database.inc index 946a495..6937adc 100644 --- a/includes/database/database.inc +++ b/includes/database/database.inc @@ -426,6 +426,25 @@ abstract class DatabaseConnection extends PDO { } /** + * 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 isset($db_prefix['default']); + } + return ''; + } + return $db_prefix; + } + + /** * Prepare a query string and return the prepared statement. * * This method caches prepared statements, reusing them when diff --git a/includes/database/mysql/schema.inc b/includes/database/mysql/schema.inc index 16f3fd5..13de91a 100644 --- a/includes/database/mysql/schema.inc +++ b/includes/database/mysql/schema.inc @@ -25,6 +25,29 @@ class DatabaseSchema_mysql extends DatabaseSchema { const COMMENT_MAX_COLUMN = 255; /** + * 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') { + $connectionInfo = Database::getConnectionInfo(); + $info = array( + // Assume the default database. + 'database' => $connectionInfo[$this->connection->getTarget()]['database'], + '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 { + $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 @@ -265,7 +288,7 @@ class DatabaseSchema_mysql extends DatabaseSchema { } public function renameTable(&$ret, $table, $new_name) { - $ret[] = update_sql('ALTER TABLE {' . $table . '} RENAME TO {' . $new_name . '}'); + $ret[] = update_sql('ALTER TABLE {' . $table . '} RENAME TO `' . $this->getPrefixInfo($new_name, 'table') . '`'); } public function dropTable(&$ret, $table) { diff --git a/includes/database/pgsql/schema.inc b/includes/database/pgsql/schema.inc index 61080c0..b4614ed 100644 --- a/includes/database/pgsql/schema.inc +++ b/includes/database/pgsql/schema.inc @@ -68,7 +68,7 @@ class DatabaseSchema_pgsql extends DatabaseSchema { } return $this->tableInformation[$key]; } - + /** * Generate SQL to create a new table from a Drupal schema definition. * @@ -91,7 +91,7 @@ class DatabaseSchema_pgsql extends DatabaseSchema { } 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) . ')'; } } @@ -277,7 +277,12 @@ class DatabaseSchema_pgsql extends DatabaseSchema { * The new name for the table. */ function renameTable(&$ret, $table, $new_name) { - $ret[] = update_sql('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); + $ret[] = update_sql('ALTER TABLE {' . $table . '} RENAME TO "' . $prefixInfo['table'] . '"'); } /** @@ -418,7 +423,7 @@ class DatabaseSchema_pgsql extends DatabaseSchema { * The table to be altered. */ public function dropPrimaryKey(&$ret, $table) { - $ret[] = update_sql('ALTER TABLE {' . $table . '} DROP CONSTRAINT {' . $table . '}_pkey'); + $ret[] = update_sql('ALTER TABLE {' . $table . '} DROP CONSTRAINT ' . $this->_prefixNonTable($table, 'pkey')); } /** @@ -434,8 +439,7 @@ class DatabaseSchema_pgsql extends DatabaseSchema { * An array of field names. */ function addUniqueKey(&$ret, $table, $name, $fields) { - $name = '{' . $table . '}_' . $name . '_key'; - $ret[] = update_sql('ALTER TABLE {' . $table . '} ADD CONSTRAINT "' . $name . '" UNIQUE (' . implode(',', $fields) . ')'); + $ret[] = update_sql('ALTER TABLE {' . $table . '} ADD CONSTRAINT "' . $this->_prefixNonTable($table, $name, 'key') . '" UNIQUE (' . implode(',', $fields) . ')'); } /** @@ -449,8 +453,7 @@ class DatabaseSchema_pgsql extends DatabaseSchema { * The name of the key. */ public function dropUniqueKey(&$ret, $table, $name) { - $name = '{' . $table . '}_' . $name . '_key'; - $ret[] = update_sql('ALTER TABLE {' . $table . '} DROP CONSTRAINT "' . $name . '"'); + $ret[] = update_sql('ALTER TABLE {' . $table . '} DROP CONSTRAINT "' . $this->_prefixNonTable($table, $name, 'key') . '"'); } /** @@ -480,8 +483,7 @@ class DatabaseSchema_pgsql extends DatabaseSchema { * The name of the index. */ public function dropIndex(&$ret, $table, $name) { - $name = '{' . $table . '}_' . $name . '_idx'; - $ret[] = update_sql('DROP INDEX ' . $name); + $ret[] = update_sql('DROP INDEX ' . $this->_prefixNonTable($table, $name, 'idx')); } /** @@ -578,7 +580,7 @@ class DatabaseSchema_pgsql extends DatabaseSchema { } 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; } @@ -600,10 +602,19 @@ class DatabaseSchema_pgsql extends DatabaseSchema { } /** + * Create names for Indexes, Primary Keys and Contstraints + */ + function _prefixNonTable($table) { + $args = func_get_args(); + $args[0] = $this->getPrefixInfo($table, 'table'); + return implode('_', $args); + } + + /** * Retrieve a table or column comment. */ public function getComment($table, $column = NULL) { - $table = $this->connection->prefixTables('{' . $table . '}'); + $table = $this->getPrefixInfo($table, 'table'); // Don't use {} around pg_class, pg_attribute tables. if (isset($column)) { return db_query('SELECT col_description(oid, attnum) FROM pg_class, pg_attribute WHERE attrelid = oid AND relname = ? AND attname = ?', array($table, $column))->fetchField(); diff --git a/includes/database/schema.inc b/includes/database/schema.inc index 442fedd..6531d47 100644 --- a/includes/database/schema.inc +++ b/includes/database/schema.inc @@ -150,11 +150,54 @@ abstract class DatabaseSchema { protected $connection; + /** + * 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; } /** + * Get information about the table name and schema from the db_prefix. + * + * @return + * A keyed array with information about the schema, table name and prefix. + */ + protected function getPrefixInfo($table = 'default', $key = NULL) { + $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 $key ? $info[$key] : $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(); + $args[0] = $this->getPrefixInfo($table, '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 diff --git a/includes/database/sqlite/schema.inc b/includes/database/sqlite/schema.inc index d32ef98..5b78940 100644 --- a/includes/database/sqlite/schema.inc +++ b/includes/database/sqlite/schema.inc @@ -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(); @@ -47,12 +52,12 @@ class DatabaseSchema_sqlite extends DatabaseSchema { $sql = array(); if (!empty($schema['unique keys'])) { foreach ($schema['unique keys'] as $key => $fields) { - $sql[] = 'CREATE UNIQUE INDEX "{' . $tablename . '}_' . $key . '" ON {' . $tablename . '} (' . $this->createKeySql($fields) . "); \n"; + $sql[] = 'CREATE UNIQUE INDEX "' . $this->_prefixNonTable($tablename, $key) . '" ON {' . $tablename . '} (' . $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"; + $sql[] = 'CREATE INDEX "' . $this->_prefixNonTable($tablename, $key) . '" ON {' . $tablename . '} (' . $this->createKeySql($fields) . "); \n"; } } return $sql; @@ -218,7 +223,11 @@ class DatabaseSchema_sqlite extends DatabaseSchema { * The new name for the table. */ public function renameTable(&$ret, $table, $new_name) { - $ret[] = update_sql('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. + $ret[] = update_sql('ALTER TABLE {' . $table . '} RENAME TO ' . $this->getPrefixInfo($new_name, 'table')); } /** @@ -447,7 +456,7 @@ class DatabaseSchema_sqlite extends DatabaseSchema { * The name of the index. */ public function dropIndex(&$ret, $table, $name) { - $ret[] = update_sql('DROP INDEX ' . '{' . $table . '}_' . $name); + $ret[] = update_sql('DROP INDEX ' . $this->_prefixNonTable($table, $name)); } /** @@ -481,7 +490,7 @@ class DatabaseSchema_sqlite extends DatabaseSchema { * The name of the key. */ public function dropUniqueKey(&$ret, $table, $name) { - $ret[] = update_sql('DROP INDEX ' . '{' . $table . '}_' . $name); + $ret[] = update_sql('DROP INDEX ' . $this->_prefixNonTable($table, $name)); } /** diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php index 7e3ded1..6dc2c25 100644 --- a/sites/default/default.settings.php +++ b/sites/default/default.settings.php @@ -130,6 +130,17 @@ * 'authmap' => 'shared_', * ); * + * For PostgreSQL, you can also specify a schema as a part of the + * prefix if the schema's already exist, for example: + * + * $db_prefix = array( + * 'default' => 'main.', + * 'users' => 'shared.', + * 'sessions' => 'shared.', + * 'role' => 'shared.', + * 'authmap' => 'shared.', + * ); + * * Database configuration format: * $databases['default']['default'] = array( * 'driver' => 'mysql',