? .buildpath
? .project
? .settings
? db_rewrite_and_tests-D6.3.patch
? db_rewrite_and_tests-D6.4.patch
? db_rewrite_and_tests-D6.5.patch
? db_rewrite_and_tests-D6.6.patch
? scripts/run-tests.sh
? sites/all/modules
? sites/default/files
? sites/default/settings.php
Index: includes/database.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/Attic/database.inc,v
retrieving revision 1.92.2.8
diff -u -p -r1.92.2.8 database.inc
--- includes/database.inc	14 Sep 2009 10:49:34 -0000	1.92.2.8
+++ includes/database.inc	7 Dec 2009 16:57:32 -0000
@@ -62,7 +62,7 @@ define('DB_ERROR', 'a515ac9c2796ca0e23ad
  *      query: the SQL query executed, passed through check_plain()
  */
 function update_sql($sql) {
-  $result = db_query($sql, true);
+  $result = db_query($sql, TRUE);
   return array('success' => $result !== FALSE, 'query' => check_plain($sql));
 }
 
Index: includes/database.mysql.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/Attic/database.mysql.inc,v
retrieving revision 1.89.2.1
diff -u -p -r1.89.2.1 database.mysql.inc
--- includes/database.mysql.inc	21 Jul 2009 08:52:29 -0000	1.89.2.1
+++ includes/database.mysql.inc	7 Dec 2009 16:57:32 -0000
@@ -365,7 +365,7 @@ function db_distinct_field($table, $fiel
   $matches = array();
   if (preg_match('/^SELECT(.*?)FROM(.*)/is', $query, $matches)) {
     $select = preg_replace(
-      '/((?:^|,)\s*)(?<!DISTINCT\()(?:'. $table .'\.)?'. $field .'(\s*(?:,|$))/is',
+      '/((?:^|,|\()\s*)(?<!DISTINCT\()(?:'. $table .'\.)?'. $field .'(\s*(?:,|\sAS|\)|$))/is',
       '\1'. $field_to_select .'\2', $matches[1], 1
     );
     
Index: includes/database.mysqli.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/Attic/database.mysqli.inc,v
retrieving revision 1.54.2.1
diff -u -p -r1.54.2.1 database.mysqli.inc
--- includes/database.mysqli.inc	21 Jul 2009 08:52:30 -0000	1.54.2.1
+++ includes/database.mysqli.inc	7 Dec 2009 16:57:32 -0000
@@ -367,11 +367,11 @@ function db_distinct_field($table, $fiel
   $matches = array();
   if (preg_match('/^SELECT(.*?)FROM(.*)/is', $query, $matches)) {
     $select = preg_replace(
-      '/((?:^|,)\s*)(?<!DISTINCT\()(?:'. $table .'\.)?'. $field .'(\s*(?:,|$))/is',
+      '/((?:^|,|\()\s*)(?<!DISTINCT\()(?:'. $table .'\.)?'. $field .'(\s*(?:,|\sAS|\)|$))/is',
       '\1'. $field_to_select .'\2', $matches[1], 1
     );
     
-    return 'SELECT'. $select .'FROM'.$matches[2];
+    return 'SELECT' . $select . 'FROM'.$matches[2];
   }
   return $query;
 }
Index: includes/database.pgsql.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/Attic/database.pgsql.inc,v
retrieving revision 1.68.2.7
diff -u -p -r1.68.2.7 database.pgsql.inc
--- includes/database.pgsql.inc	14 Sep 2009 10:49:34 -0000	1.68.2.7
+++ includes/database.pgsql.inc	7 Dec 2009 16:57:32 -0000
@@ -419,13 +419,70 @@ function db_check_setup() {
  * @return SQL query with the DISTINCT wrapper surrounding the given table.field.
  */
 function db_distinct_field($table, $field, $query) {
+  //var_dump($table, $field, $query);
+  // The first regex tries to avoids touching the query if we have a subquery
+  // The second regex avoids messing with the query if there is already a 
+  // DISTINCT on the whole query (to respect the function documentation).
+  // This may or may not be a good idea, but we leave it as is for now.
   if (!preg_match('/FROM\s+\S+\s+AS/si', $query)
-  && !preg_match('/DISTINCT\s+ON\s*\(\s*(' . $table . '\s*\.\s*)?' . $field . '\s*\)/si', $query)
-  && !preg_match('/DISTINCT[ (]' . $field . '/si', $query)
-  && preg_match('/(.*FROM\s+)(.*?\s)(\s*(WHERE|GROUP|HAVING|ORDER|LIMIT|FOR).*)/Asi', $query, $m)) {
-    $query = $m[1];
-    $query .= preg_replace('/([\{\w+\}]+)\s+(' . $table . ')\s/Usi', '(SELECT DISTINCT ON (' . $field . ') * FROM \1) \2 ', $m[2]);
-    $query .= $m[3];
+  && !preg_match('/DISTINCT\s+ON\s*\(\s*(' . $table . '\s*\.\s*)?' . $field . '\s*\)/si', $query)) {
+    $matches = array();
+    if (preg_match('/DISTINCT[ (]' . preg_quote($table .'.'. $field) . '/si', $query, $matches)) {
+      // We should replace DISTINCT(), which doesn't do anything different than
+      // DISTINCT on the whole query with a DISTINCT ON. ().  However, as 
+      // DISTINCT() never works as developers seem to understand it on either 
+      // MySql or PostgreSQL (at least in postgres 8.4.1 and MySQL 5.1.37), 
+      // this could cause unintended regressions, and there is no similar 
+      // solution for MySql anyway.  
+      // So we leave the query as is for now.
+      ;
+    }
+    else {
+      $qualified_primary_field = $table .'.'. $field;
+
+      $regex = '/^SELECT\s+(.*?)\s+FROM\s+(.*?)(ORDER BY\s+(.*?))?(LIMIT.*|OFFSET.*|\s*$)/is';
+      if (preg_match($regex, $query, $matches)) {
+        //$matches[1] is the select list
+        //$matches[2] is everything after FROM and before ORDER BY
+        //$matches[3] is the order by as a whole (if it exists)
+        //$matches[4] is the order by content (if it exists)
+        //$matches[5] is the rest of the query
+        //var_dump($matches);
+
+        // If there is an ORDER BY, modify it to make sure that the primary
+        // field is at the begining (Postgres must have the content of
+        // distinct on at the begining of the order by (if there is an ORDER
+        // BY)
+        if (!empty($matches[3])) {
+          $order_by_elements = preg_split  ('/\s*,\s*/', $matches[4]);
+          $key_found = FALSE;
+          foreach ($order_by_elements as $key => $value) {
+            if (strstr($value, $qualified_primary_field)) {
+              $key_found = $key;
+              break;
+            }
+          }
+          if ($key_found!==FALSE) {
+            $prepend_to_order_by = $order_by_elements[$key_found];
+            unset($order_by_elements[$key_found]);
+          }
+          else {
+            $prepend_to_order_by = $qualified_primary_field;
+          }
+          array_unshift($order_by_elements, $prepend_to_order_by);
+          $order_by = 'ORDER BY ' . implode(', ', $order_by_elements);
+        }
+        else {
+          $order_by = '';
+        }
+        $field_to_select = 'DISTINCT ON (' . $qualified_primary_field . ') ' . $qualified_primary_field;
+        $field_to_select_count = 'DISTINCT(' . $qualified_primary_field . ')';
+        $patterns = array('/' . preg_quote($qualified_primary_field) . '(?=\s|,|$)/', '/(?<=COUNT\()' . preg_quote($qualified_primary_field) . '(?=\))/');
+        $replacements = array($field_to_select, $field_to_select_count);
+        $select_list_replacement = preg_replace($patterns, $replacements, $matches[1]);
+        $query = preg_replace($regex, "SELECT $select_list_replacement FROM \\2$order_by\\5", $query);
+      }
+    }
   }
   return $query;
 }
Index: modules/system/tests/database.test
===================================================================
RCS file: modules/system/tests/database.test
diff -N modules/system/tests/database.test
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ modules/system/tests/database.test	7 Dec 2009 16:57:32 -0000
@@ -0,0 +1,216 @@
+<?php
+// $Id:  $
+/**
+ * @file
+ * Tests for testing the database abstraction layer in Drupal 6.
+ */
+
+class DatabaseTestCase extends DrupalWebTestCase {
+  /**
+   * Implementation of getInfo().
+   */
+  public static function getInfo() {
+    return array(
+      'name' => t('Database functionality'),
+      'description' => t('Exercise the database helper functions, this not not test the database but the functions that abstract the database.'),
+      'group' => t('Database'),
+    );
+  }
+
+
+  /**
+   * Test the db_distinct_field() function.
+   *
+   * See http://drupal.org/node/284392 for more information
+   */
+  function testDbDistinctField() {
+    global $db_type;
+    // Test both mysql nad mysqli with the same queries
+    if (strpos($db_type, 'mysql')!==FALSE) {
+    // These assertions are taken from:
+    // http://drupal.org/node/284392
+
+    $assertions[] = array(
+      'input' => array("table", "field", "SELECT table.field FROM table"),
+      'expected' => 'SELECT DISTINCT(table.field) FROM table',
+    );
+
+    $assertions[] = array(
+      'input' => array("table", "field", "SELECT table.field AS table_field FROM table"),
+      'expected' => 'SELECT DISTINCT(table.field) AS table_field FROM table',
+    );
+
+    $assertions[] = array(
+      'input' => array("table", "field", "SELECT DISTINCT(table.field) FROM table"),
+      'expected' => 'SELECT DISTINCT(table.field) FROM table',
+    );
+
+    $assertions[] = array(
+      'input' => array("table", "field", "SELECT DISTINCT(table.field) AS table_field FROM table"),
+      'expected' => 'SELECT DISTINCT(table.field) AS table_field FROM table',
+    );
+
+    $assertions[] = array(
+      'input' => array("table", "field", "SELECT table.field,table.field2 FROM table"),
+      'expected' => 'SELECT DISTINCT(table.field),table.field2 FROM table',
+    );
+
+    $assertions[] = array(
+      'input' => array("table", "field", "SELECT table.field AS table_field, table.field2 AS table_field2 FROM table"),
+      'expected' => 'SELECT DISTINCT(table.field) AS table_field, table.field2 AS table_field2 FROM table',
+    );
+
+    $assertions[] = array(
+      'input' => array("table", "field", "SELECT DISTINCT(table.field),table.field2 FROM table"),
+      'expected' => 'SELECT DISTINCT(table.field),table.field2 FROM table',
+    );
+
+    $assertions[] = array(
+      'input' => array("table", "field", "SELECT DISTINCT(table.field) AS table_field, table.field2 AS table_field2 FROM table"),
+      'expected' => 'SELECT DISTINCT(table.field) AS table_field, table.field2 AS table_field2 FROM table',
+    );
+
+    $assertions[] = array(
+      'input' => array("table", "field", "SELECT COUNT(table.field) FROM table"),
+      'expected' => 'SELECT COUNT(DISTINCT(table.field)) FROM table',
+    );
+
+    $assertions[] = array(
+      'input' => array("table", "field", "SELECT COUNT(table.field) AS table_field FROM table"),
+      'expected' => 'SELECT COUNT(DISTINCT(table.field)) AS table_field FROM table',
+    );
+
+    $assertions[] = array(
+      'input' => array("table", "field", "SELECT table.field1, COUNT(table.field), table.field2 FROM table"),
+      'expected' => 'SELECT table.field1, COUNT(DISTINCT(table.field)), table.field2 FROM table',
+    );
+
+    $assertions[] = array(
+      'input' => array("table", "field", "SELECT COUNT(DISTINCT(table.field)) FROM table"),
+      'expected' => 'SELECT COUNT(DISTINCT(table.field)) FROM table',
+    );
+    }
+    else if (strpos($db_type, 'pgsql')!==FALSE) {
+    //Equivalents of the MySql tests above
+    $assertions[] = array(
+      'input' => array("table", "field", "SELECT table.field FROM table"),
+      'expected' => 'SELECT DISTINCT ON (table.field) table.field FROM table',
+    );
+
+    $assertions[] = array(
+      'input' => array("table", "field", "SELECT table.field AS table_field FROM table"),
+      'expected' => 'SELECT DISTINCT ON (table.field) table.field AS table_field FROM table',
+    );
+
+    $assertions[] = array(
+      'input' => array("table", "field", "SELECT DISTINCT(table.field) FROM table"),
+      'expected' => 'SELECT DISTINCT(table.field) FROM table',
+    );
+
+    $assertions[] = array(
+      'input' => array("table", "field", "SELECT DISTINCT(table.field) AS table_field FROM table"),
+      'expected' => 'SELECT DISTINCT(table.field) AS table_field FROM table',
+    );
+
+    $assertions[] = array(
+      'input' => array("table", "field", "SELECT table.field,table.field2 FROM table"),
+      'expected' => 'SELECT DISTINCT ON (table.field) table.field,table.field2 FROM table',
+    );
+
+    $assertions[] = array(
+      'input' => array("table", "field", "SELECT table.field AS table_field, table.field2 AS table_field2 FROM table"),
+      'expected' => 'SELECT DISTINCT ON (table.field) table.field AS table_field, table.field2 AS table_field2 FROM table',
+    );
+
+    $assertions[] = array(
+      'input' => array("table", "field", "SELECT DISTINCT(table.field),table.field2 FROM table"),
+      'expected' => 'SELECT DISTINCT(table.field),table.field2 FROM table',
+    );
+
+    $assertions[] = array(
+      'input' => array("table", "field", "SELECT DISTINCT(table.field) AS table_field, table.field2 AS table_field2 FROM table"),
+      'expected' => 'SELECT DISTINCT(table.field) AS table_field, table.field2 AS table_field2 FROM table',
+    );
+
+    $assertions[] = array(
+      'input' => array("table", "field", "SELECT COUNT(table.field) FROM table"),
+      'expected' => 'SELECT COUNT(DISTINCT(table.field)) FROM table',
+    );
+
+    $assertions[] = array(
+      'input' => array("table", "field", "SELECT COUNT(table.field) AS table_field FROM table"),
+      'expected' => 'SELECT COUNT(DISTINCT(table.field)) AS table_field FROM table',
+    );
+
+    $assertions[] = array(
+      'input' => array("table", "field", "SELECT table.field1, COUNT(table.field), table.field2 FROM table"),
+      'expected' => 'SELECT table.field1, COUNT(DISTINCT(table.field)), table.field2 FROM table',
+    );
+
+    $assertions[] = array(
+      'input' => array("table", "field", "SELECT COUNT(DISTINCT(table.field)) FROM table"),
+      'expected' => 'SELECT COUNT(DISTINCT(table.field)) FROM table',
+    );
+    
+    //The following tests werify that ORDER BY (if present) is always preceeded by table.field and 
+    //that the order by isn't needlessly mangled
+    
+    $assertions[] = array(
+      'input' => array("table", "field", "SELECT table.field FROM table ORDER BY table.field"),
+      'expected' => 'SELECT DISTINCT ON (table.field) table.field FROM table ORDER BY table.field',
+    );
+    
+    $assertions[] = array(
+      'input' => array("table", "field", "SELECT table.field FROM table ORDER BY table.field DESC"),
+      'expected' => 'SELECT DISTINCT ON (table.field) table.field FROM table ORDER BY table.field DESC',
+    );
+        
+    $assertions[] = array(
+      'input' => array("table", "field", "SELECT table.field FROM table ORDER BY table.field, foo.bar"),
+      'expected' => 'SELECT DISTINCT ON (table.field) table.field FROM table ORDER BY table.field, foo.bar',
+    );
+
+    $assertions[] = array(
+      'input' => array("table", "field", "SELECT table.field FROM table ORDER BY foo.bar, table.field"),
+      'expected' => 'SELECT DISTINCT ON (table.field) table.field FROM table ORDER BY table.field, foo.bar',
+    );
+    
+    $assertions[] = array(
+      'input' => array("table", "field", "SELECT table.field FROM table ORDER BY foo.bar"),
+      'expected' => 'SELECT DISTINCT ON (table.field) table.field FROM table ORDER BY table.field, foo.bar',
+    );
+    
+    // Check that we don't accidentally chop anything off the end of the query
+    $assertions[] = array(
+      'input' => array("table", "field", "SELECT table.field FROM table ORDER BY foo.bar LIMIT 10"),
+      'expected' => 'SELECT DISTINCT ON (table.field) table.field FROM table ORDER BY table.field, foo.bar LIMIT 10',
+    );
+
+    $assertions[] = array(
+      'input' => array("table", "field", "SELECT table.field FROM table LIMIT 10"),
+      'expected' => 'SELECT DISTINCT ON (table.field) table.field FROM table LIMIT 10',
+    );
+    }
+    else {
+	$this->fail(t('Unknown database type: %database', array('%database' => $db_type)));
+    }
+
+    foreach ($assertions as $assert) {
+      $distinct_added = call_user_func_array('db_distinct_field', $assert['input']);
+      if (!$this->assertEqual(
+        $distinct_added,
+        $assert['expected'],
+        t('Add DISTINCT to %query', array('%query' => $assert['input'][2]))
+      )) {
+        $this->pass(t('Query was rewritten to: %query, expected query: %expected_query', array('%query' => $distinct_added, '%expected_query' => $assert['expected'])));
+      }
+    }
+
+
+
+
+
+  }
+
+
+}
