? sites/default/modules
? sites/default/settings.php
Index: includes/database/database.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/database/database.inc,v
retrieving revision 1.23
diff -u -p -r1.23 database.inc
--- includes/database/database.inc	8 Nov 2008 04:45:36 -0000	1.23
+++ includes/database/database.inc	9 Nov 2008 02:49:58 -0000
@@ -155,6 +155,67 @@ abstract class DatabaseConnection extend
    */
   protected $logger = NULL;
 
+  /**
+   * Drupal provides the following functions for portably generating SQL
+   * functions as strings to be merged into your SQL statements.
+   *
+   * Most implementation reference from ADOdb
+   *
+   * @link http://phplens.com/lens/adodb/tips_portable_sql.htm
+   */
+
+  /**
+   * String to use to quote identifiers and names.
+   */
+  protected $nameQuote = '"';
+
+  /**
+   * Default concat operator. Change to || for Oracle/Interbase.
+   */
+  protected $concat_operator = '+';
+
+  /**
+   * Uppercase function.
+   */
+  public function upperCase($field) {
+    return 'UPPER(' . $field . ')';
+  }
+
+  /**
+   * Random function.
+   */
+  public function random() {
+    return 'RAND()';
+  }
+
+  /**
+   * String length operator.
+   */
+  public function length($field) {
+    return 'LENGTH(' . $field . ')';
+  }
+
+  /**
+   * Substring operator.
+   */  
+  public function substr($field, $start, $length = NULL) {
+    return 'SUBSTR(' . implode(', ', array($field, $start, $length)) . ')';
+  }
+
+  /**
+   * Portably concatenate strings.
+   */
+  public function Concat($args) {
+    return implode($this->concat_operator, $args);
+  }
+
+  /**
+   * Returns a string that is the equivalent of MySQL IFNULL or Oracle NVL.
+   */
+  public function IfNull($expr1, $expr2) {
+    return " CASE WHEN $expr1 IS NULL THEN $expr2 ELSE $expr1 END ";
+  }
+
   function __construct($dsn, $username, $password, $driver_options = array()) {
     // Because the other methods don't seem to work right.
     $driver_options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION;
@@ -1332,6 +1393,154 @@ class DatabaseStatement extends PDOState
  */
 
 /**
+ * Return the portably concatenate strings of the SQL strtoupper function.
+ *
+ * Use this function with following syntax:
+ * @code
+ * db_query("SELECT " . db_strtoupper('name') . " FROM {system}");
+ * @endcode
+ *
+ * @param $field
+ *   Field for apply abstraction.
+ * @param $options
+ *   An array of options to control how the query operates.
+ * @return
+ *   Portably concatenate strings of the SQL strtoupper function.
+ */
+function db_strtoupper($field, $options = array()) {
+  if (empty($options['target'])) {
+    $options['target'] = 'default';
+  }
+  return Database::getActiveConnection($options['target'])->upperCase($field);
+}
+
+/**
+ * Return the SQL to generate a random number between 0.00 and 1.00.
+ *
+ * Use this function with following syntax:
+ * @code
+ * db_query("SELECT " . db_rand() . " AS num FROM {system}");
+ * @endcode
+ *
+ * @param $options
+ *   An array of options to control how the query operates.
+ * @return
+ *   The SQL to generate a random number between 0.00 and 1.00.
+ */
+function db_rand($options = array()) {
+  if (empty($options['target'])) {
+    $options['target'] = 'default';
+  }
+  return Database::getActiveConnection($options['target'])->random();
+}
+
+/**
+ * Return the portably concatenate strings of the SQL strlen function.
+ *
+ * Use this function with following syntax:
+ * @code
+ * db_query("SELECT " . db_strlen('name') . " FROM {system}");
+ * @endcode
+ *
+ * @param $field
+ *   Field for apply abstraction.
+ * @param $options
+ *   An array of options to control how the query operates.
+ * @return
+ *   Portably concatenate strings of the SQL strlen function.
+ */
+function db_strlen($field, $options = array()) {
+  if (empty($options['target'])) {
+    $options['target'] = 'default';
+  }
+  return Database::getActiveConnection($options['target'])->length($field);
+}
+
+/**
+ * Return the portably concatenate strings of the SQL substr function.
+ *
+ * Use this function with following syntax:
+ * @code
+ * db_query("SELECT " . db_substr('name', 1, 3) . " FROM {system}");
+ * @endcode
+ *
+ * Be ware: both $start and $length should input as string in order to keep
+ * its formatting, as db_substr() will bypass it directly. For example, to
+ * handle the following SQL with db_substr():
+ * @code
+ * SUBSTRING(thread, 1, (LENGTH(thread) - 1))
+ * @endcode
+ *
+ * We should use the following syntax:
+ * @code
+ * db_substr('thread', 1, db_strlen('thread') . ' - 1')
+ * @endcode
+ *
+ * @param $field
+ *   Field for apply abstraction.
+ * @param $start
+ *   Starting position.
+ * @param $length
+ *   Length of characters return.
+ * @param $options
+ *   An array of options to control how the query operates.
+ * @return
+ *   Portably concatenate strings of the SQL substr function.
+ */
+function db_substr($field, $start, $length = NULL, $options = array()) {
+  if (empty($options['target'])) {
+    $options['target'] = 'default';
+  }
+  return Database::getActiveConnection($options['target'])->substr($field, $start, $length);
+}
+
+/**
+ * Return the portably concatenate strings of the SQL strcat function.
+ *
+ * Use this function with following syntax:
+ * @code
+ * db_query("SELECT " . db_strcat("name", "module") . " FROM {system}");
+ * @endcode
+ *
+ * @param $args
+ *   An array of string parameters.
+ * @param $options
+ *   An array of options to control how the query operates.
+ * @return
+ *   Portably concatenate strings of the SQL strcat function.
+ */
+function db_strcat($args = array(), $options = array()) {
+  if (!is_array($args)) {
+    $args = func_get_args();
+    $options = array();
+  }
+  if (empty($options['target'])) {
+    $options['target'] = 'default';
+  }
+  return Database::getActiveConnection($options['target'])->Concat($args);
+}
+
+/**
+ * Returns a string that is the equivalent of MySQL IFNULL or Oracle NVL.
+ *
+ * Use this function with following syntax:
+ * @code
+ * db_query("SELECT " . db_if_null("name", "module") . " FROM {system}");
+ * @endcode
+ *
+ * @param $options
+ *   An array of options to control how the query operates.
+ * @return
+ *   If $expr1 is not NULL, returns $expr1; otherwise it returns $expr2.
+ */
+function db_if_null($expr1, $expr2, $options = array()) {
+  if (empty($options['target'])) {
+    $options['target'] = 'default';
+  }
+  return Database::getActiveConnection($options['target'])->IfNull($expr1, $expr2);
+}
+
+/**
  * Execute an arbitrary query string against the active database.
  *
  * Do not use this function for INSERT, UPDATE, or DELETE queries.  Those should
Index: includes/database/mysql/database.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/database/mysql/database.inc,v
retrieving revision 1.5
diff -u -p -r1.5 database.inc
--- includes/database/mysql/database.inc	8 Nov 2008 04:45:36 -0000	1.5
+++ includes/database/mysql/database.inc	9 Nov 2008 02:49:58 -0000
@@ -14,6 +14,16 @@
 class DatabaseConnection_mysql extends DatabaseConnection {
 
   protected $transactionSupport;
+  protected $nameQuote = '`';
+
+  public function Concat($args) {
+    $return = implode(', ', $args);
+    return (strlen($return) > 0) ? "CONCAT($return)" : '';
+  }
+
+  public function IfNull($expr1, $expr2) {
+    return " IFNULL($expr1, $expr2) ";
+  }
 
   public function __construct(Array $connection_options = array()) {
 
Index: includes/database/pgsql/database.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/database/pgsql/database.inc,v
retrieving revision 1.7
diff -u -p -r1.7 database.inc
--- includes/database/pgsql/database.inc	8 Nov 2008 04:45:36 -0000	1.7
+++ includes/database/pgsql/database.inc	9 Nov 2008 02:49:58 -0000
@@ -14,6 +14,19 @@
 class DatabaseConnection_pgsql extends DatabaseConnection {
 
   protected $transactionSupport;
+  protected $concat_operator = '||';
+
+  public function random() {
+    return 'RANDOM()';
+  }
+
+  public function substr($field, $start, $length = NULL) {
+    return 'SUBSTRING(' . implode(', ', array($field, $start, $length)) . ')';
+  }
+
+  public function IfNull($expr1, $expr2) {
+    return " COALESCE($expr1, $expr2) ";
+  }
 
   public function __construct(Array $connection_options = array()) {
 
Index: modules/comment/comment.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v
retrieving revision 1.660
diff -u -p -r1.660 comment.module
--- modules/comment/comment.module	1 Nov 2008 19:51:06 -0000	1.660
+++ modules/comment/comment.module	9 Nov 2008 02:49:58 -0000
@@ -365,10 +365,14 @@ function comment_new_page_count($num_com
   else {
     // Threaded comments.
     // Find the first thread with a new comment.
-    $result = db_query('(SELECT thread FROM {comments} WHERE nid = %d  AND status = 0 ORDER BY timestamp DESC LIMIT %d) ORDER BY SUBSTRING(thread, 1, (LENGTH(thread) - 1)) LIMIT 1', $node->nid, $new_replies);
+    // @todo: Revamp with DBTNG syntax.
+    $result = db_query('(SELECT thread FROM {comments} WHERE nid = %d  AND status = 0 ORDER BY timestamp DESC LIMIT %d) ORDER BY ' . db_substr('thread', 1, db_strlen('thread') . ' - 1') . ' LIMIT 1', $node->nid, $new_replies);
     $thread = substr(db_result($result), 0, -1);
-    $result_count = db_query("SELECT COUNT(*) FROM {comments} WHERE nid = %d AND status = 0 AND SUBSTRING(thread, 1, (LENGTH(thread) - 1)) < '" . $thread . "'", $node->nid);
-    $count = db_result($result_count);
+    $query = db_select('comments');
+    $query->condition('nid', $node->nid);
+    $query->condition('status', 0);
+    $query->condition(db_substr('thread', 1, db_strlen('thread') . ' - 1'), $thread, '<');
+    $count = $query->countQuery()->execute()->fetchField();
     $pageno =  $count / $comments_per_page;
   }
 
@@ -959,7 +963,8 @@ function comment_render($node, $cid = 0)
         // See comment above. Analysis reveals that this doesn't cost too
         // much. It scales much much better than having the whole comment
         // structure.
-        $query .= ' ORDER BY SUBSTRING(c.thread, 1, (LENGTH(c.thread) - 1))';
+        // @todo: Revamp with DBTNG syntax.
+        $query .= ' ORDER BY ' . db_substr('c.thread', 1, db_strlen('c.thread') . ' - 1');
       }
 
       $query = db_rewrite_sql($query, 'c', 'cid');
Index: modules/forum/forum.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/forum/forum.module,v
retrieving revision 1.472
diff -u -p -r1.472 forum.module
--- modules/forum/forum.module	2 Nov 2008 14:42:45 -0000	1.472
+++ modules/forum/forum.module	9 Nov 2008 02:49:58 -0000
@@ -586,7 +586,8 @@ function forum_get_forums($tid = 0) {
     // This query does not use full ANSI syntax since MySQL 3.x does not support
     // table1 INNER JOIN table2 INNER JOIN table3 ON table2_criteria ON table3_criteria
     // used to join node_comment_statistics to users.
-    $sql = "SELECT ncs.last_comment_timestamp, IF (ncs.last_comment_uid != 0, u2.name, ncs.last_comment_name) AS last_comment_name, ncs.last_comment_uid FROM {node} n INNER JOIN {users} u1 ON n.uid = u1.uid INNER JOIN {term_node} tn ON n.vid = tn.vid INNER JOIN {node_comment_statistics} ncs ON n.nid = ncs.nid INNER JOIN {users} u2 ON ncs.last_comment_uid=u2.uid WHERE n.status = 1 AND tn.tid = %d ORDER BY ncs.last_comment_timestamp DESC";
+    // @todo: Revamp with DBTNG syntax.
+    $sql = "SELECT ncs.last_comment_timestamp, " . db_if_null("ncs.last_comment_name", "u2.name") . " AS last_comment_name, ncs.last_comment_uid FROM {node} n INNER JOIN {users} u1 ON n.uid = u1.uid INNER JOIN {term_node} tn ON n.vid = tn.vid INNER JOIN {node_comment_statistics} ncs ON n.nid = ncs.nid INNER JOIN {users} u2 ON ncs.last_comment_uid=u2.uid WHERE n.status = 1 AND tn.tid = %d ORDER BY ncs.last_comment_timestamp DESC";
     $sql = db_rewrite_sql($sql);
     $topic = db_fetch_object(db_query_range($sql, $forum->tid, 0, 1));
 
@@ -632,7 +633,8 @@ function forum_get_topics($tid, $sortby,
     }
   }
 
-  $sql = db_rewrite_sql("SELECT n.nid, r.tid, n.title, n.type, n.sticky, u.name, u.uid, n.created AS timestamp, n.comment AS comment_mode, l.last_comment_timestamp, IF(l.last_comment_uid != 0, cu.name, l.last_comment_name) AS last_comment_name, l.last_comment_uid, l.comment_count AS num_comments, f.tid AS forum_tid FROM {node_comment_statistics} l INNER JOIN {node} n ON n.nid = l.nid INNER JOIN {users} cu ON l.last_comment_uid = cu.uid INNER JOIN {term_node} r ON n.vid = r.vid INNER JOIN {users} u ON n.uid = u.uid INNER JOIN {forum} f ON n.vid = f.vid WHERE n.status = 1 AND r.tid = %d");
+  // @todo: Revamp with DBTNG syntax.
+  $sql = db_rewrite_sql("SELECT n.nid, r.tid, n.title, n.type, n.sticky, u.name, u.uid, n.created AS timestamp, n.comment AS comment_mode, l.last_comment_timestamp, " . db_if_null("l.last_comment_name", "cu.name") . " AS last_comment_name, l.last_comment_uid, l.comment_count AS num_comments, f.tid AS forum_tid FROM {node_comment_statistics} l INNER JOIN {node} n ON n.nid = l.nid INNER JOIN {users} cu ON l.last_comment_uid = cu.uid INNER JOIN {term_node} r ON n.vid = r.vid INNER JOIN {users} u ON n.uid = u.uid INNER JOIN {forum} f ON n.vid = f.vid WHERE n.status = 1 AND r.tid = %d");
   $sql .= tablesort_sql($forum_topic_list_header, 'n.sticky DESC,');
   $sql .= ', n.created DESC';  // Always add a secondary sort order so that the news forum topics are on top.
 
Index: modules/locale/locale.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale/locale.module,v
retrieving revision 1.230
diff -u -p -r1.230 locale.module
--- modules/locale/locale.module	22 Oct 2008 19:39:36 -0000	1.230
+++ modules/locale/locale.module	9 Nov 2008 02:49:59 -0000
@@ -377,8 +377,15 @@ function locale($string = NULL, $langcod
         // Refresh database stored cache of translations for given language.
         // We only store short strings used in current version, to improve
         // performance and consume less memory.
-        $result = db_query("SELECT s.source, t.translation, t.language FROM {locales_source} s LEFT JOIN {locales_target} t ON s.lid = t.lid AND t.language = '%s' WHERE s.textgroup = 'default' AND s.version = '%s' AND LENGTH(s.source) < 75", $langcode, VERSION);
-        while ($data = db_fetch_object($result)) {
+        $query = db_select('locales_source', 's');
+        $query->leftJoin('locales_target', 't', 's.lid = t.lid AND t.language = :language', array(':language' => $langcode));
+        $query->fields('s', array('source'));
+        $query->fields('t', array('translation', 'language'));
+        $query->condition('s.textgroup', 'DEFAULT');
+        $query->condition('s.version', VERSION);
+        $query->condition(db_strlen('s.source'), '75', '<');
+        $result = $query->execute();
+        foreach ($result as $data) {
           $locale_t[$langcode][$data->source] = (empty($data->translation) ? TRUE : $data->translation);
         }
         cache_set('locale:' . $langcode, $locale_t[$langcode]);
Index: modules/statistics/statistics.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/statistics/statistics.admin.inc,v
retrieving revision 1.14
diff -u -p -r1.14 statistics.admin.inc
--- modules/statistics/statistics.admin.inc	13 Oct 2008 00:33:04 -0000	1.14
+++ modules/statistics/statistics.admin.inc	9 Nov 2008 02:49:59 -0000
@@ -83,7 +83,8 @@ function statistics_top_visitors() {
   );
 
   $sql = "SELECT COUNT(a.uid) AS hits, a.uid, u.name, a.hostname, SUM(a.timer) AS total, bl.iid FROM {accesslog} a LEFT JOIN {blocked_ips} bl ON a.hostname = bl.ip LEFT JOIN {users} u ON a.uid = u.uid GROUP BY a.hostname, a.uid, u.name, bl.iid" . tablesort_sql($header);
-  $sql_cnt = "SELECT COUNT(DISTINCT(CONCAT(uid, hostname))) FROM {accesslog}";
+  // @todo: Revamp with DBTNG syntax.
+  $sql_cnt = "SELECT COUNT(DISTINCT(" . db_strcat('uid', 'hostname') . ")) FROM {accesslog}";
   $result = pager_query($sql, 30, 0, $sql_cnt);
 
   $rows = array();
Index: modules/system/system.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.install,v
retrieving revision 1.278
diff -u -p -r1.278 system.install
--- modules/system/system.install	5 Nov 2008 14:28:04 -0000	1.278
+++ modules/system/system.install	9 Nov 2008 02:49:59 -0000
@@ -305,37 +305,6 @@ function system_install() {
     if (!db_result(db_query("SELECT COUNT(*) FROM pg_constraint WHERE conname = 'bigint_unsigned_check'"))) {
       db_query("CREATE DOMAIN bigint_unsigned bigint CHECK (VALUE >= 0)");
     }
-
-    // Create functions.
-    db_query('CREATE OR REPLACE FUNCTION "greatest"(numeric, numeric) RETURNS numeric AS
-      \'SELECT CASE WHEN (($1 > $2) OR ($2 IS NULL)) THEN $1 ELSE $2 END;\'
-      LANGUAGE \'sql\''
-    );
-    db_query('CREATE OR REPLACE FUNCTION "greatest"(numeric, numeric, numeric) RETURNS numeric AS
-      \'SELECT greatest($1, greatest($2, $3));\'
-      LANGUAGE \'sql\''
-    );
-    if (!db_result(db_query("SELECT COUNT(*) FROM pg_proc WHERE proname = 'rand'"))) {
-      db_query('CREATE OR REPLACE FUNCTION "rand"() RETURNS float AS
-        \'SELECT random();\'
-        LANGUAGE \'sql\''
-      );
-    }
-
-    if (!db_result(db_query("SELECT COUNT(*) FROM pg_proc WHERE proname = 'concat'"))) {
-      db_query('CREATE OR REPLACE FUNCTION "concat"(text, text) RETURNS text AS
-        \'SELECT $1 || $2;\'
-        LANGUAGE \'sql\''
-      );
-    }
-    db_query('CREATE OR REPLACE FUNCTION "if"(boolean, text, text) RETURNS text AS
-      \'SELECT CASE WHEN $1 THEN $2 ELSE $3 END;\'
-      LANGUAGE \'sql\''
-    );
-    db_query('CREATE OR REPLACE FUNCTION "if"(boolean, integer, integer) RETURNS integer AS
-      \'SELECT CASE WHEN $1 THEN $2 ELSE $3 END;\'
-      LANGUAGE \'sql\''
-    );
   }
 
   // Create tables.
@@ -3085,6 +3054,27 @@ function system_update_7011() {
 }
 
 /**
+ * Discontinue some legacy PostgreSQL custom function abstraction.
+ */
+function system_update_7012() {
+  $ret = array();
+  if (db_driver() == 'pgsql') {
+    // GREATEST is build-in supported since PostgreSQL 8.1 (our minimal
+    // requirement).
+    $ret[] = update_sql('DROP FUNCTION IF EXISTS "greatest"(numeric, numeric)');
+    $ret[] = update_sql('DROP FUNCTION IF EXISTS "greatest"(numeric, numeric, numeric)');
+    // RAND() is now abstract with db_rand().
+    $ret[] = update_sql('DROP FUNCTION IF EXISTS "rand"()');
+    // CONCAT() is now abstract with db_strcat().
+    $ret[] = update_sql('DROP FUNCTION IF EXISTS "concat"(text, text)');
+    // IF() is now replace with db_if_null.
+    $ret[] = update_sql('DROP FUNCTION IF EXISTS "if"(boolean, text, text)');
+    $ret[] = update_sql('DROP FUNCTION IF EXISTS "if"(boolean, integer, integer)');
+  }
+  return $ret;  
+}
+
+/**
  * @} End of "defgroup updates-6.x-to-7.x"
  * The next series of updates should start at 8000.
  */
