summaryrefslogtreecommitdiff
path: root/includes
diff options
context:
space:
mode:
authorDries Buytaert <dries@buytaert.net>2010-04-07 15:07:59 +0000
committerDries Buytaert <dries@buytaert.net>2010-04-07 15:07:59 +0000
commitdde5c67ba041dc65588377808b1943fdd3b57bf6 (patch)
tree133c901b2517a88d36060da686dd95903e84d079 /includes
parent626e64025eb85faf819b9d17298df505e9d0526a (diff)
downloadbrdo-dde5c67ba041dc65588377808b1943fdd3b57bf6.tar.gz
brdo-dde5c67ba041dc65588377808b1943fdd3b57bf6.tar.bz2
- Patch #302327 by Josh Waihi, noahb, Crell, hswong3i: support cross-schema/database prefixing like we claim to.
Diffstat (limited to 'includes')
-rw-r--r--includes/database/database.inc20
-rw-r--r--includes/database/mysql/schema.inc36
-rw-r--r--includes/database/pgsql/schema.inc25
-rw-r--r--includes/database/schema.inc71
-rw-r--r--includes/database/sqlite/schema.inc28
5 files changed, 139 insertions, 41 deletions
diff --git a/includes/database/database.inc b/includes/database/database.inc
index a85489a5b..aa4cc671f 100644
--- a/includes/database/database.inc
+++ b/includes/database/database.inc
@@ -384,6 +384,26 @@ abstract class DatabaseConnection extends PDO {
}
/**
+ * Find the prefix for a table.
+ *
+ * This function is for when you want to know the prefix of 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;
+ }
+
+ /**
* Prepares a query string and returns 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 93c547eea..8a9d0176e 100644
--- a/includes/database/mysql/schema.inc
+++ b/includes/database/mysql/schema.inc
@@ -25,6 +25,26 @@ 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') {
+ $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
@@ -35,16 +55,11 @@ class DatabaseSchema_mysql extends DatabaseSchema {
protected function buildTableNameCondition($table_name, $operator = '=') {
$info = $this->connection->getConnectionOptions();
- if (strpos($table_name, '.')) {
- list($schema, $table_name) = explode('.', $table_name);
- }
- else {
- $schema = $info['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;
}
@@ -273,7 +288,8 @@ class DatabaseSchema_mysql extends DatabaseSchema {
throw new DatabaseSchemaObjectExistsException(t("Cannot rename %table to %table_new: table %table_new already exists.", array('%table' => $table, '%table_new' => $new_name)));
}
- $this->connection->query('ALTER TABLE {' . $table . '} RENAME TO {' . $new_name . '}');
+ $info = $this->getPrefixInfo($new_name);
+ return $this->connection->query('ALTER TABLE {' . $table . '} RENAME TO `' . $info['table'] . '`');
}
public function dropTable($table) {
@@ -446,7 +462,7 @@ class DatabaseSchema_mysql extends DatabaseSchema {
* 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);
diff --git a/includes/database/pgsql/schema.inc b/includes/database/pgsql/schema.inc
index fcff331fb..5ababf623 100644
--- a/includes/database/pgsql/schema.inc
+++ b/includes/database/pgsql/schema.inc
@@ -95,7 +95,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) . ')';
}
}
@@ -297,7 +297,9 @@ class DatabaseSchema_pgsql extends DatabaseSchema {
}
// Now rename the table.
- $this->connection->query('ALTER TABLE {' . $table . '} RENAME TO {' . $new_name . '}');
+ // Ensure the new table name does not include schema syntax.
+ $prefixInfo = $this->getPrefixInfo($new_name);
+ $this->connection->query('ALTER TABLE {' . $table . '} RENAME TO ' . $prefixInfo['table']);
}
public function dropTable($table) {
@@ -409,7 +411,7 @@ class DatabaseSchema_pgsql extends DatabaseSchema {
return FALSE;
}
- $this->connection->query('ALTER TABLE {' . $table . '} DROP CONSTRAINT {' . $table . '}_pkey');
+ $this->connection->query('ALTER TABLE {' . $table . '} DROP CONSTRAINT ' . $this->prefixNonTable($table, 'pkey'));
return TRUE;
}
@@ -421,8 +423,7 @@ class DatabaseSchema_pgsql extends DatabaseSchema {
throw new DatabaseSchemaObjectExistsException(t("Cannot add unique key %name to table %table: unique key already exists.", array('%table' => $table, '%name' => $name)));
}
- $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) . ')');
}
public function dropUniqueKey($table, $name) {
@@ -430,8 +431,7 @@ class DatabaseSchema_pgsql extends DatabaseSchema {
return FALSE;
}
- $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') . '"');
return TRUE;
}
@@ -451,8 +451,7 @@ class DatabaseSchema_pgsql extends DatabaseSchema {
return FALSE;
}
- $name = '{' . $table . '}_' . $name . '_idx';
- $this->connection->query('DROP INDEX ' . $name);
+ $this->connection->query('DROP INDEX ' . $this->prefixNonTable($table, $name, 'idx'));
return TRUE;
}
@@ -495,7 +494,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;
}
@@ -520,13 +519,13 @@ class DatabaseSchema_pgsql extends DatabaseSchema {
* 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)) {
- return $this->connection->query('SELECT col_description(oid, attnum) FROM pg_class, pg_attribute WHERE attrelid = oid AND relname = ? AND attname = ?', array($table, $column))->fetchField();
+ return $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();
}
else {
- return $this->connection->query('SELECT obj_description(oid, ?) FROM pg_class WHERE relname = ?', array('pg_class', $table))->fetchField();
+ return $this->connection->query('SELECT obj_description(oid, ?) FROM pg_class WHERE relname = ?', array('pg_class', $info['table']))->fetchField();
}
}
}
diff --git a/includes/database/schema.inc b/includes/database/schema.inc
index ab2a95a03..5302a6d22 100644
--- a/includes/database/schema.inc
+++ b/includes/database/schema.inc
@@ -150,6 +150,16 @@ abstract class DatabaseSchema implements QueryPlaceholderInterface {
*/
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;
@@ -160,6 +170,48 @@ abstract class DatabaseSchema implements QueryPlaceholderInterface {
}
/**
+ * 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 the prefix contains a period in it, then that means the prefix also
+ // contains a schema reference in which case we will change the schema key
+ // to the value before the period in the prefix. Everything after the dot
+ // will be prefixed onto the front of the table.
+ if ($pos = strpos($info['prefix'], '.') !== FALSE) {
+ // Grab everything before the period.
+ $info['schema'] = substr($info['prefix'], 0, $pos);
+ // Grab everything after the dot, and prefix on to the table.
+ $info['table'] = substr($info['prefix'], ++$pos) . $table;
+ }
+ else {
+ $info['table'] = $info['prefix'] . $table;
+ }
+ return $info;
+ }
+
+ /**
+ * Create names for indexes, primary keys and constraints.
+ *
+ * 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
@@ -175,7 +227,7 @@ abstract class DatabaseSchema implements QueryPlaceholderInterface {
* 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.
*
@@ -185,18 +237,13 @@ abstract class DatabaseSchema implements QueryPlaceholderInterface {
protected function buildTableNameCondition($table_name, $operator = '=') {
$info = $this->connection->getConnectionOptions();
- // 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['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;
}
@@ -210,7 +257,7 @@ abstract class DatabaseSchema implements QueryPlaceholderInterface {
* TRUE if the given table exists, otherwise FALSE.
*/
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
// concatenation for conditionals like this however, we
@@ -253,7 +300,7 @@ abstract class DatabaseSchema implements QueryPlaceholderInterface {
* TRUE if the given column exists, otherwise FALSE.
*/
public function fieldExists($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
diff --git a/includes/database/sqlite/schema.inc b/includes/database/sqlite/schema.inc
index 894d51c63..f8f18eb74 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();
@@ -45,14 +50,19 @@ class DatabaseSchema_sqlite extends DatabaseSchema {
*/
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;
@@ -228,7 +238,13 @@ class DatabaseSchema_sqlite extends DatabaseSchema {
$schema = $this->introspectSchema($table);
- $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 braces 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']);
// Drop the indexes, there is no RENAME INDEX command in SQLite.
if (!empty($schema['unique keys'])) {
@@ -445,7 +461,7 @@ class DatabaseSchema_sqlite extends DatabaseSchema {
return FALSE;
}
- $this->connection->query('DROP INDEX ' . '{' . $table . '}_' . $name);
+ $this->connection->query('DROP INDEX ' . $this->prefixNonTable($table, $name));
return TRUE;
}
@@ -469,7 +485,7 @@ class DatabaseSchema_sqlite extends DatabaseSchema {
return FALSE;
}
- $this->connection->query('DROP INDEX ' . '{' . $table . '}_' . $name);
+ $this->connection->query('DROP INDEX ' . $this->prefixNonTable($table, $name));
return TRUE;
}