- Patch #342503 by Josh Waihi, Damien Tournoud et al: schema function findTables fails on PostgreSQL.

merge-requests/26/head
Dries Buytaert 2009-01-08 09:52:12 +00:00
parent a7b4bdef1d
commit 9af9e3de88
4 changed files with 108 additions and 39 deletions

View File

@ -380,14 +380,14 @@ abstract class DatabaseConnection extends PDO {
* Queries sent to Drupal should wrap all table names in curly brackets. This
* function searches for this syntax and adds Drupal's table prefix to all
* tables, allowing Drupal to coexist with other systems in the same database
* if necessary.
* and/or schema if necessary.
*
* @param $sql
* A string containing a partial or entire SQL query.
* @return
* The properly-prefixed string.
*/
protected function prefixTables($sql) {
public function prefixTables($sql) {
global $db_prefix;
if (is_array($db_prefix)) {
@ -428,7 +428,7 @@ abstract class DatabaseConnection extends PDO {
* A PDO prepared statement ready for its execute() method.
*/
protected function prepareQuery($query, $cache = TRUE) {
$query = self::prefixTables($query);
$query = $this->prefixTables($query);
if (empty($this->preparedStatements[$query])) {
// Call PDO::prepare.
$this->preparedStatements[$query] = parent::prepare($query);

View File

@ -14,14 +14,29 @@
class DatabaseSchema_mysql extends DatabaseSchema {
public function tableExists($table) {
return (bool) $this->connection->query("SHOW TABLES LIKE '{" . $table . "}'", array(), array())->fetchField();
}
/**
* 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
* database as the schema unless specified otherwise, and exclude table_catalog
* from the condition criteria.
*/
protected function buildTableNameCondition($table_name, $operator = '=') {
$info = Database::getConnectionInfo();
public function columnExists($table, $column) {
return (bool) $this->connection->query("SHOW COLUMNS FROM {" . $this->connection->escapeTable($table) . "} LIKE '" . $this->connection->escapeTable($column) . "'", array(), array())->fetchField();
}
if (strpos($table_name, '.')) {
list($schema, $table_name) = explode('.', $table_name);
}
else {
$schema = $info['default']['database'];
}
$condition = db_and()
->condition('table_schema', $schema)
->condition('table_name', $table_name, $operator);
return $condition;
}
/**
* Generate SQL to create a new table from a Drupal schema definition.

View File

@ -13,14 +13,6 @@
class DatabaseSchema_pgsql extends DatabaseSchema {
public function tableExists($table) {
return (bool) db_result(db_query("SELECT COUNT(*) FROM pg_class WHERE relname = '{" . db_escape_table($table) . "}'"));
}
public function columnExists($table, $column) {
return (bool) db_result(db_query("SELECT COUNT(pg_attribute.attname) FROM pg_class, pg_attribute WHERE pg_attribute.attrelid = pg_class.oid AND pg_class.relname = '{" . db_escape_table($table) . "}' AND attname = '" . db_escape_table($column) . "'"));
}
/**
* Generate SQL to create a new table from a Drupal schema definition.
*

View File

@ -125,14 +125,95 @@ abstract class DatabaseSchema {
}
/**
* Check if a table exists.
* 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
* database server and the databases, schemas, tables, columns and users within
* it. This makes information_schema a useful tool to use across the drupal
* database drivers and is used by a few different functions. The function below
* describes the conditions to be meet when querying information_schema.tables
* for drupal tables or information associated with drupal tables. Even though
* this is the standard method, not all databases follow standards and so this
* method should be overwritten by a database driver if the database provider
* uses alternate methods. Because information_schema.tables is used in a few
* different functions, a database driver will only need to override this function
* to make all the others work. For example see includes/databases/mysql/schema.inc.
*
* @param $table_name
* The name of the table to explode.
* @param $operator
* The operator to apply on the 'table' part of the condition.
* @return
* A DatabaseCondition object.
*/
abstract public function tableExists($table);
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';
}
$condition = db_and()
->condition('table_catalog', $info['default']['database'])
->condition('table_schema', $schema)
->condition('table_name', $table_name, $operator);
return $condition;
}
/**
* Check if a table exists.
*
* @param $table
* The name of the table in drupal (no prefixing).
* @return
* false is no table exists otherwise the actual table name.
*/
public function tableExists($table) {
$condition = $this->buildTableNameCondition($this->connection->prefixTables('{' . $table . '}'));
$condition->compile($this->connection);
// Normally, we would heartily discourage the use of string
// concatination for conditionals like this however, we
// couldn't use db_select() here because it would prefix
// information_schema.tables and the query would fail.
return db_query("SELECT table_name FROM information_schema.tables WHERE " . (string) $condition, $condition->arguments())->fetchField();
}
/**
* Find all tables that are like the specified base table name.
*
* @param $table_expression
* An SQL expression, for example "simpletest%" (without the quotes).
* BEWARE: this is not prefixed, the caller should take care of that.
* @return
* Array, both the keys and the values are the matching tables.
*/
public function findTables($table_expression) {
$condition = $this->buildTableNameCondition($table_expression, 'LIKE');
$condition->compile($this->connection);
// Normally, we would heartily discourage the use of string
// concatination for conditionals like this however, we
// couldn't use db_select() here because it would prefix
// information_schema.tables and the query would fail.
return db_query("SELECT table_name FROM information_schema.tables WHERE " . (string) $condition, $condition->arguments())->fetchAllKeyed(0, 0);
}
/**
* Check if a column exists in the given table.
*/
abstract public function columnExists($table, $column);
public function columnExists($table, $column) {
$condition = $this->buildTableNameCondition($this->connection->prefixTables('{' . $table . '}'));
$condition->condition('column_name', $column);
$condition->compile($this->connection);
// Normally, we would heartily discourage the use of string
// concatination for conditionals like this however, we
// couldn't use db_select() here because it would prefix
// information_schema.tables and the query would fail.
return db_query("SELECT column_name FROM information_schema.columns WHERE " . (string) $condition, $condition->arguments())->fetchAllKeyed(0, 0);
}
/**
* This maps a generic data type in combination with its data size
@ -404,25 +485,6 @@ abstract class DatabaseSchema {
}
return $ret;
}
/**
* Find all tables that are like the specified base table name.
*
* @param $table_expression
* An SQL expression, for example "simpletest%" (without the quotes).
* BEWARE: this is not prefixed, the caller should take care of that.
* @return
* Array, both the keys and the values are the matching tables.
*/
public function findTables($table_expression) {
global $db_prefix;
$info = Database::getConnectionInfo();
$result = db_query("SELECT table_name FROM information_schema.tables WHERE table_schema = :database AND table_name LIKE :table_name", array(
':database' => $info['default']['database'],
':table_name' => $table_expression,
));
return $result->fetchAllKeyed(0, 0);
}
}
/**