From f80c6184276793e60cd67ef0bad39c2c1914e10e Mon Sep 17 00:00:00 2001
From: Dries Buytaert <dries@buytaert.net>
Date: Sun, 11 Jan 2009 10:57:20 +0000
Subject: [PATCH] - Patch #349500 by Damien Tournoud et al: made
 db_query_temporary() generate its own temporary table names.

---
 includes/database/database.inc                | 34 +++++++++++++------
 includes/database/mysql/database.inc          |  6 ++--
 includes/database/pgsql/database.inc          |  6 ++--
 includes/database/sqlite/database.inc         |  6 ++--
 modules/simpletest/tests/database_test.module | 12 ++++---
 modules/simpletest/tests/database_test.test   | 26 ++++++++++++--
 6 files changed, 65 insertions(+), 25 deletions(-)

diff --git a/includes/database/database.inc b/includes/database/database.inc
index 2ca7747ac960..4f97afd40d4b 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 b03c03ca3e35..281bcfa0927a 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 477845053624..6e39bb23275d 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 e62ed5d1b8c8..bf0c1625e9bb 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 25631e0c7792..573075078ffe 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 5df638346ae4..8451f83f1e8f 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.'));
   }
 }
 
-- 
GitLab