diff --git a/includes/database/database.inc b/includes/database/database.inc index 2ca7747ac960ca4b7948939aafeb3653967967d0..4f97afd40d4b13cae351e013ef3607d91e7dadc5 100644 --- a/includes/database/database.inc +++ b/includes/database/database.inc @@ -295,6 +295,13 @@ abstract class DatabaseConnection extends PDO { */ protected $transactionalDDLSupport = FALSE; + /** + * An index used to generate unique temporary table names. + * + * @var integer + */ + protected $temporaryNameIndex = 0; + /** * The schema object for this connection. * @@ -909,6 +916,16 @@ public function popTransaction() { */ abstract public function queryRange($query, array $args, $from, $count, array $options = array()); + /** + * Generate a temporary table name. + * + * @return + * A table name. + */ + protected function generateTemporaryTableName() { + return "db_temporary_" . $this->temporaryNameIndex++; + } + /** * Runs a SELECT query and stores its results in a temporary table. * @@ -925,17 +942,13 @@ abstract public function queryRange($query, array $args, $from, $count, array $o * A string containing a normal SELECT SQL query. * @param $args * An array of values to substitute into the query at placeholder markers. - * @param $tablename - * The name of the temporary table to select into. This name will not be - * prefixed as there is no risk of collision. * @param $options * An associative array of options to control how the query is run. See * the documentation for DatabaseConnection::defaultOptions() for details. * @return - * A database query result resource, or FALSE if the query was not executed - * correctly. + * The name of the temporary table. */ - abstract function queryTemporary($query, array $args, $tablename, array $options = array()); + abstract function queryTemporary($query, array $args, array $options = array()); /** * Returns the type of database driver. @@ -1788,20 +1801,19 @@ function db_query_range($query, $args, $from = 0, $count = 0, $options = array() * placeholders, this is an associative array in any order. If the query uses * unnamed placeholders (?), this is an indexed array and the order must match * the order of placeholders in the query string. - * @param $tablename - * The name of the temporary table to select into. This name will not be - * prefixed as there is no risk of collision. * @param $options * An array of options to control how the query operates. + * @return + * The name of the temporary table. */ -function db_query_temporary($query, $args, $tablename, $options = array()) { +function db_query_temporary($query, $args, $options = array()) { if (!is_array($args)) { $args = func_get_args(); array_shift($args); } list($query, $args, $options) = _db_query_process_args($query, $args, $options); - return Database::getActiveConnection($options['target'])->queryTemporary($query, $args, $tablename, $options); + return Database::getActiveConnection($options['target'])->queryTemporary($query, $args, $options); } /** diff --git a/includes/database/mysql/database.inc b/includes/database/mysql/database.inc index b03c03ca3e3531da8df75a5ab17b0cf0089e7512..281bcfa0927a82b392120c768ed8c279fbf00567 100644 --- a/includes/database/mysql/database.inc +++ b/includes/database/mysql/database.inc @@ -50,8 +50,10 @@ public function queryRange($query, array $args, $from, $count, array $options = return $this->query($query . ' LIMIT ' . $from . ', ' . $count, $args, $options); } - public function queryTemporary($query, array $args, $tablename, array $options = array()) { - return $this->query(preg_replace('/^SELECT/i', 'CREATE TEMPORARY TABLE ' . $tablename . ' Engine=MEMORY SELECT', $query), $args, $options); + public function queryTemporary($query, array $args, array $options = array()) { + $tablename = $this->generateTemporaryTableName(); + $this->query(preg_replace('/^SELECT/i', 'CREATE TEMPORARY TABLE {' . $tablename . '} Engine=MEMORY SELECT', $query), $args, $options); + return $tablename; } public function driver() { diff --git a/includes/database/pgsql/database.inc b/includes/database/pgsql/database.inc index 4778450536249af8074c821390487445bff128a6..6e39bb23275d56046a6853681c1f38a78ce0b715 100644 --- a/includes/database/pgsql/database.inc +++ b/includes/database/pgsql/database.inc @@ -82,8 +82,10 @@ public function queryRange($query, array $args, $from, $count, array $options = return $this->query($query . ' LIMIT ' . $count . ' OFFSET ' . $from, $args, $options); } - public function queryTemporary($query, array $args, $tablename, array $options = array()) { - return $this->query(preg_replace('/^SELECT/i', 'CREATE TEMPORARY TABLE ' . $tablename . ' AS SELECT', $query), $args, $options); + public function queryTemporary($query, array $args, array $options = array()) { + $tablename = $this->generateTemporaryTableName(); + $this->query(preg_replace('/^SELECT/i', 'CREATE TEMPORARY TABLE {' . $tablename . '} AS SELECT', $query), $args, $options); + return $tablename; } public function driver() { diff --git a/includes/database/sqlite/database.inc b/includes/database/sqlite/database.inc index e62ed5d1b8c829527138b584f18a38a35a7a30bc..bf0c1625e9bb8dc1b51ed1320f1ac5404d4e4484 100644 --- a/includes/database/sqlite/database.inc +++ b/includes/database/sqlite/database.inc @@ -123,8 +123,10 @@ public function queryRange($query, array $args, $from, $count, array $options = return $this->query($query . ' LIMIT ' . $from . ', ' . $count, $args, $options); } - public function queryTemporary($query, array $args, $tablename, array $options = array()) { - return $this->query(preg_replace('/^SELECT/i', 'CREATE TEMPORARY TABLE ' . $tablename . ' AS SELECT', $query), $args, $options); + public function queryTemporary($query, array $args, array $options = array()) { + $tablename = $this->generateTemporaryTableName(); + $this->query(preg_replace('/^SELECT/i', 'CREATE TEMPORARY TABLE {' . $tablename . '} AS SELECT', $query), $args, $options); + return $tablename; } public function driver() { diff --git a/modules/simpletest/tests/database_test.module b/modules/simpletest/tests/database_test.module index 25631e0c77928e083c824c50367ceb915f432d98..573075078ffed1fd8dd4e8d596eede1f61b28330 100644 --- a/modules/simpletest/tests/database_test.module +++ b/modules/simpletest/tests/database_test.module @@ -44,7 +44,7 @@ function database_test_query_database_test_alter_remove_range_alter(QueryAlterab * Implementation of hook_menu(). */ function database_test_menu() { - $items['database_test_db_query_temporary'] = array( + $items['database_test/db_query_temporary'] = array( 'access callback' => TRUE, 'page callback' => 'database_test_db_query_temporary', ); @@ -52,15 +52,17 @@ function database_test_menu() { } /** - * Run a db_query_temporary and print the number of rows in the resulting table. + * Run a db_query_temporary and output the table name and its number of rows. * * We need to test that the table created is temporary, so we run it here, in a * separate menu callback request; After this request is done, the temporary * table should automatically dropped. */ function database_test_db_query_temporary() { - db_query_temporary('SELECT status FROM {system}', array(), 'temporary'); - print db_query('SELECT COUNT(*) FROM temporary')->fetchField(); + $table_name = db_query_temporary('SELECT status FROM {system}', array()); + drupal_json(array( + 'table_name' => $table_name, + 'row_count' => db_select($table_name)->countQuery()->execute()->fetchField(), + )); exit; } - diff --git a/modules/simpletest/tests/database_test.test b/modules/simpletest/tests/database_test.test index 5df638346ae4568cefde675ffbaf13bb287f5348..8451f83f1e8f4d73de9a93bfd7b50a73e6299660 100644 --- a/modules/simpletest/tests/database_test.test +++ b/modules/simpletest/tests/database_test.test @@ -1933,13 +1933,33 @@ class DatabaseTemporaryQueryTestCase extends DrupalWebTestCase { parent::setUp('database_test'); } + /** + * Return the number of rows of a table. + */ + function countTableRows($table_name) { + return db_select($table_name)->countQuery()->execute()->fetchField(); + } + /** * Confirm that temporary tables work and are limited to one request. */ function testTemporaryQuery() { - $this->drupalGet('database_test_db_query_temporary'); - $this->assertEqual(db_query('SELECT COUNT(*) FROM {system}')->fetchField(), $this->drupalGetContent(), t('The temporary table exists and contains the correct amount of rows.')); - $this->assertFalse(db_table_exists('temporary'), t('The temporary table is, indeed, temporary.')); + $this->drupalGet('database_test/db_query_temporary'); + $data = json_decode($this->drupalGetContent()); + if ($data) { + $this->assertEqual($this->countTableRows("system"), $data->row_count, t('The temporary table contains the correct amount of rows.')); + $this->assertFalse(db_table_exists($data->table_name), t('The temporary table is, indeed, temporary.')); + } + else { + $this->fail(t("The creation of the temporary table failed.")); + } + + // Now try to run two db_query_temporary() in the same request. + $table_name_system = db_query_temporary('SELECT status FROM {system}', array()); + $table_name_users = db_query_temporary('SELECT uid FROM {users}', array()); + + $this->assertEqual($this->countTableRows($table_name_system), $this->countTableRows("system"), t('A temporary table was created successfully in this request.')); + $this->assertEqual($this->countTableRows($table_name_users), $this->countTableRows("users"), t('A second temporary table was created successfully in this request.')); } }