Index: includes/bootstrap.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/bootstrap.inc,v
retrieving revision 1.145.2.6
diff -u -p -r1.145.2.6 bootstrap.inc
--- includes/bootstrap.inc	26 Jul 2007 19:16:45 -0000	1.145.2.6
+++ includes/bootstrap.inc	5 Aug 2007 16:44:47 -0000
@@ -234,7 +234,7 @@ function drupal_unset_globals() {
  * session name correctly.
  */
 function conf_init() {
-  global $base_url, $base_path, $base_root;
+  global $db_url, $db_slave_url, $db_prefix, $base_url, $base_path, $base_root, $conf, $installed_profile;
 
   // Export the following settings.php variables to the global namespace
   global $db_url, $db_prefix, $cookie_domain, $conf, $installed_profile;
@@ -382,14 +382,14 @@ function drupal_get_filename($type, $nam
 function variable_init($conf = array()) {
   // NOTE: caching the variables improves performance by 20% when serving cached pages.
   if ($cached = cache_get('variables', 'cache')) {
-    $variables = unserialize($cached->data);
+    $variables = $cached->data;
   }
   else {
     $result = db_query('SELECT * FROM {variable}');
     while ($variable = db_fetch_object($result)) {
       $variables[$variable->name] = unserialize($variable->value);
     }
-    cache_set('variables', 'cache', serialize($variables));
+    cache_set('variables', 'cache', $variables);
   }
 
   foreach ($conf as $name => $value) {
@@ -427,14 +427,13 @@ function variable_get($name, $default) {
 function variable_set($name, $value) {
   global $conf;
 
-  db_lock_table('variable');
-  db_query("DELETE FROM {variable} WHERE name = '%s'", $name);
-  db_query("INSERT INTO {variable} (name, value) VALUES ('%s', '%s')", $name, serialize($value));
-  db_unlock_tables();
-
-  cache_clear_all('variables', 'cache');
+  db_query_replace(
+    array("DELETE FROM {variable} WHERE name = '%s'", $name),
+    array("INSERT INTO {variable} (name, value) VALUES ('%s', '%s')", $name, serialize($value))
+  );
 
   $conf[$name] = $value;
+  cache_set('variables', 'cache', $conf);
 }
 
 /**
Index: includes/database.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/database.inc,v
retrieving revision 1.62.2.4
diff -u -p -r1.62.2.4 database.inc
--- includes/database.inc	12 Jul 2007 06:25:47 -0000	1.62.2.4
+++ includes/database.inc	5 Aug 2007 16:44:47 -0000
@@ -99,16 +99,35 @@ function db_prefix_tables($sql) {
  * @return the name of the previously active database or FALSE if non was found.
  */
 function db_set_active($name = 'default') {
-  global $db_url, $db_type, $active_db;
-  static $db_conns;
+  global $db_url, $db_slave_url, $db_type, $active_db, $active_slave_db;
+  static $db_conns, $db_slave_conns;
 
   if (!isset($db_conns[$name])) {
-    // Initiate a new connection, using the named DB URL specified.
+    // If the $name doesn't have a corresponding URL, use the default.
+    if (!isset($db_url[$name])) {
+      $name = 'default';
+    }
+    
+    // Initiate a new connection, using the specifed DB URL.
     if (is_array($db_url)) {
-      $connect_url = array_key_exists($name, $db_url) ? $db_url[$name] : $db_url['default'];
+      $connect_url = $db_url[$name];
+      if (is_array($db_slave_url[$name])) {
+        $slave_index = mt_rand(0, count($db_slave_url[$name]));
+        $slave_connect_url = $db_slave_url[$name][$slave_index];
+      }
+      else {
+        $slave_connect_url = $db_slave_url[$name];        
+      }
     }
     else {
       $connect_url = $db_url;
+      if (is_array($db_slave_url)) {
+        $slave_index = mt_rand(0, count($db_slave_url) - 1);
+        $slave_connect_url = $db_slave_url[$slave_index];
+      }
+      else {
+        $slave_connect_url = $db_slave_url;        
+      }
     }
 
     $db_type = substr($connect_url, 0, strpos($connect_url, '://'));
@@ -126,11 +145,20 @@ function db_set_active($name = 'default'
     }
 
     $db_conns[$name] = db_connect($connect_url);
+    if (!empty($slave_connect_url)) {
+      $db_slave_conns[$name] = db_connect($slave_connect_url); 
+    }
   }
 
   $previous_db = $active_db;
-  // Set the active connection.
+  // Set the active connections.
   $active_db = $db_conns[$name];
+  if (isset($db_slave_conns[$name])) {
+    $active_slave_db = $db_slave_conns[$name];
+  }
+  else {
+    unset($active_slave_db);
+  }
 
   return array_search($previous_db, $db_conns);
 }
@@ -201,6 +229,42 @@ function db_query($query) {
 }
 
 /**
+ * Runs a basic query in the active slave database.
+ *
+ * User-supplied arguments to the query should be passed in as separate
+ * parameters so that they can be properly escaped to avoid SQL injection
+ * attacks.
+ *
+ * @param $query
+ *   A string containing an SQL query.
+ * @param ...
+ *   A variable number of arguments which are substituted into the query
+ *   using printf() syntax. Instead of a variable number of query arguments,
+ *   you may also pass a single array containing the query arguments.
+
+ *   Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose
+ *   in '') and %%.
+ *
+ *   NOTE: using this syntax will cast NULL and FALSE values to decimal 0,
+ *   and TRUE values to decimal 1.
+ *
+ * @return
+ *   A database query result resource, or FALSE if the query was not
+ *   executed correctly.
+ */
+function db_query_slave($query) {
+  $args = func_get_args();
+  array_shift($args);
+  $query = db_prefix_tables($query);
+  if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
+    $args = $args[0];
+  }
+  _db_query_callback($args, TRUE);
+  $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query);
+  return _db_query($query, FALSE, TRUE);
+}
+
+/**
  * Helper function for db_rewrite_sql.
  *
  * Collects JOIN and WHERE statements via hook_db_rewrite_sql().
Index: includes/database.mysql.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/database.mysql.inc,v
retrieving revision 1.66.2.1
diff -u -p -r1.66.2.1 database.mysql.inc
--- includes/database.mysql.inc	22 Jan 2007 02:20:50 -0000	1.66.2.1
+++ includes/database.mysql.inc	5 Aug 2007 16:44:47 -0000
@@ -142,23 +142,30 @@ function db_connect($url) {
 /**
  * Helper function for db_query().
  */
-function _db_query($query, $debug = 0) {
-  global $active_db, $queries;
+function _db_query($query, $debug = 0, $slave = FALSE) {
+  global $active_db, $active_slave_db, $queries;
 
   if (variable_get('dev_query', 0)) {
     list($usec, $sec) = explode(' ', microtime());
     $timer = (float)$usec + (float)$sec;
   }
 
-  $result = mysql_query($query, $active_db);
-
+  $sent_to_slave = FALSE;
+  if (isset($active_slave_db) && $slave) {
+    $result = mysql_query($query, $active_slave_db);
+    $sent_to_slave = TRUE;
+  }
+  else {
+    $result = mysql_query($query, $active_db);
+  }
+  
   if (variable_get('dev_query', 0)) {
     $bt = debug_backtrace();
     $query = $bt[2]['function'] . "\n" . $query;
     list($usec, $sec) = explode(' ', microtime());
     $stop = (float)$usec + (float)$sec;
     $diff = $stop - $timer;
-    $queries[] = array($query, $diff);
+    $queries[] = array($query, $diff, $sent_to_slave);
   }
 
   if ($debug) {
@@ -321,6 +328,50 @@ function db_query_range($query) {
 }
 
 /**
+ * Runs a limited-range query in the active slave database.
+ *
+ * Use this as a substitute for db_query_slave() when a subset of the query is to be
+ * returned.
+ * User-supplied arguments to the query should be passed in as separate parameters
+ * so that they can be properly escaped to avoid SQL injection attacks.
+ *
+ * @param $query
+ *   A string containing an SQL query.
+ * @param ...
+ *   A variable number of arguments which are substituted into the query
+ *   using printf() syntax. The query arguments can be enclosed in one
+ *   array instead.
+ *   Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose
+ *   in '') and %%.
+ *
+ *   NOTE: using this syntax will cast NULL and FALSE values to decimal 0,
+ *   and TRUE values to decimal 1.
+ *
+ * @param $from
+ *   The first result row to return.
+ * @param $count
+ *   The maximum number of result rows to return.
+ * @return
+ *   A database query result resource, or FALSE if the query was not executed
+ *   correctly.
+ */
+function db_query_range_slave($query) {
+  $args = func_get_args();
+  $count = array_pop($args);
+  $from = array_pop($args);
+  array_shift($args);
+
+  $query = db_prefix_tables($query);
+  if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
+    $args = $args[0];
+  }
+  _db_query_callback($args, TRUE);
+  $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query);
+  $query .= ' LIMIT '. (int)$from .', '. (int)$count;
+  return _db_query($query, FALSE, TRUE);
+}
+
+/**
  * Runs a SELECT query and stores its results in a temporary table.
  *
  * Use this as a substitute for db_query() when the results need to stored
@@ -358,13 +409,13 @@ function db_query_temporary($query) {
   $tablename = array_pop($args);
   array_shift($args);
 
-  $query = preg_replace('/^SELECT/i', 'CREATE TEMPORARY TABLE '. $tablename .' SELECT', db_prefix_tables($query));
+  $query = preg_replace('/^SELECT/i', 'CREATE TEMPORARY TABLE '. $tablename .'  Engine=HEAP SELECT', db_prefix_tables($query));
   if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
     $args = $args[0];
   }
   _db_query_callback($args, TRUE);
   $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query);
-  return _db_query($query);
+  return _db_query($query, FALSE, TRUE);
 }
 
 /**
@@ -439,7 +490,20 @@ function db_distinct_field($table, $fiel
 }
 
 /**
+ * Perform a replace query.
+ *
+ * @param $delete
+ *   An array, where the first value is a query which deletes one row,
+ *   subsequent values are arguments. Same syntax as of db_query.
+ * @param
+ *   An array, where the first value is a query which inserts the new row,
+ *   subsequent values are arguments. Same syntax as of db_query.
+ */
+function db_query_replace($delete, $insert) {
+  $insert_query = array_shift($insert);
+  db_query(preg_replace('/^INSERT /', 'REPLACE ', $insert_query), $insert);
+}
+
+/**
  * @} End of "ingroup database".
  */
-
-
Index: includes/database.mysqli.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/database.mysqli.inc,v
retrieving revision 1.32
diff -u -p -r1.32 database.mysqli.inc
--- includes/database.mysqli.inc	27 Dec 2006 22:50:09 -0000	1.32
+++ includes/database.mysqli.inc	5 Aug 2007 16:44:47 -0000
@@ -121,15 +121,22 @@ function db_connect($url) {
 /**
  * Helper function for db_query().
  */
-function _db_query($query, $debug = 0) {
-  global $active_db, $queries;
+function _db_query($query, $debug = 0, $slave = FALSE) {
+  global $active_db, $active_slave_db, $queries;
 
   if (variable_get('dev_query', 0)) {
     list($usec, $sec) = explode(' ', microtime());
     $timer = (float)$usec + (float)$sec;
   }
 
-  $result = mysqli_query($active_db, $query);
+  $sent_to_slave = FALSE;
+  if (isset($active_slave_db) && $slave) {
+    $result = mysqli_query($active_slave_db, $query);
+    $sent_to_slave = TRUE;
+  }
+  else {
+    $result = mysqli_query($active_db, $query);
+  }
 
   if (variable_get('dev_query', 0)) {
     $bt = debug_backtrace();
@@ -137,7 +144,7 @@ function _db_query($query, $debug = 0) {
     list($usec, $sec) = explode(' ', microtime());
     $stop = (float)$usec + (float)$sec;
     $diff = $stop - $timer;
-    $queries[] = array($query, $diff);
+    $queries[] = array($query, $diff, $sent_to_slave);
   }
 
   if ($debug) {
@@ -301,6 +308,50 @@ function db_query_range($query) {
 }
 
 /**
+ * Runs a limited-range query in the active slave database.
+ *
+ * Use this as a substitute for db_query_slave() when a subset of the query is to be
+ * returned.
+ * User-supplied arguments to the query should be passed in as separate parameters
+ * so that they can be properly escaped to avoid SQL injection attacks.
+ *
+ * @param $query
+ *   A string containing an SQL query.
+ * @param ...
+ *   A variable number of arguments which are substituted into the query
+ *   using printf() syntax. The query arguments can be enclosed in one
+ *   array instead.
+ *   Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose
+ *   in '') and %%.
+ *
+ *   NOTE: using this syntax will cast NULL and FALSE values to decimal 0,
+ *   and TRUE values to decimal 1.
+ *
+ * @param $from
+ *   The first result row to return.
+ * @param $count
+ *   The maximum number of result rows to return.
+ * @return
+ *   A database query result resource, or FALSE if the query was not executed
+ *   correctly.
+ */
+function db_query_range_slave($query) {
+  $args = func_get_args();
+  $count = array_pop($args);
+  $from = array_pop($args);
+  array_shift($args);
+
+  $query = db_prefix_tables($query);
+  if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
+    $args = $args[0];
+  }
+  _db_query_callback($args, TRUE);
+  $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query);
+  $query .= ' LIMIT '. (int)$from .', '. (int)$count;
+  return _db_query($query, FALSE, TRUE);
+}
+
+/**
  * Runs a SELECT query and stores its results in a temporary table.
  *
  * Use this as a substitute for db_query() when the results need to stored
@@ -419,6 +470,21 @@ function db_distinct_field($table, $fiel
 }
 
 /**
+ * Perform a replace query.
+ *
+ * @param $delete
+ *   An array, where the first value is a query which deletes one row,
+ *   subsequent values are arguments. Same syntax as of db_query.
+ * @param
+ *   An array, where the first value is a query which inserts the new row,
+ *   subsequent values are arguments. Same syntax as of db_query.
+ */
+function db_query_replace($delete, $insert) {
+  $insert_query = array_shift($insert);
+  db_query(preg_replace('/^INSERT /', 'REPLACE ', $insert_query), $insert);
+}
+
+/**
  * @} End of "ingroup database".
  */
 
Index: includes/database.pgsql.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/database.pgsql.inc,v
retrieving revision 1.43
diff -u -p -r1.43 database.pgsql.inc
--- includes/database.pgsql.inc	27 Dec 2006 22:13:56 -0000	1.43
+++ includes/database.pgsql.inc	5 Aug 2007 16:44:47 -0000
@@ -114,15 +114,22 @@ function db_connect($url) {
 /**
  * Helper function for db_query().
  */
-function _db_query($query, $debug = 0) {
-  global $active_db, $last_result, $queries;
+function _db_query($query, $debug = 0, $slave = FALSE) {
+  global $active_db, $active_slave_db, $last_result, $queries;
 
   if (variable_get('dev_query', 0)) {
     list($usec, $sec) = explode(' ', microtime());
     $timer = (float)$usec + (float)$sec;
   }
 
-  $last_result = pg_query($active_db, $query);
+  $sent_to_slave = FALSE;
+  if (isset($active_slave_db) && $slave) {
+    $last_result = pg_query($active_slave_db, $query);
+    $sent_to_slave = TRUE;
+  }
+  else {
+    $last_result = pg_query($active_db, $query);
+  }
 
   if (variable_get('dev_query', 0)) {
     $bt = debug_backtrace();
@@ -130,7 +137,7 @@ function _db_query($query, $debug = 0) {
     list($usec, $sec) = explode(' ', microtime());
     $stop = (float)$usec + (float)$sec;
     $diff = $stop - $timer;
-    $queries[] = array($query, $diff);
+    $queries[] = array($query, $diff, $sent_to_slave);
   }
 
   if ($debug) {
@@ -291,6 +298,51 @@ function db_query_range($query) {
 }
 
 /**
+ * Runs a limited-range query in the active slave database.
+ *
+ * Use this as a substitute for db_query_slave() when a subset of the query
+ * is to be returned.
+ * User-supplied arguments to the query should be passed in as separate
+ * parameters so that they can be properly escaped to avoid SQL injection
+ * attacks.
+ *
+ * @param $query
+ *   A string containing an SQL query.
+ * @param ...
+ *   A variable number of arguments which are substituted into the query
+ *   using printf() syntax. Instead of a variable number of query arguments,
+ *   you may also pass a single array containing the query arguments.
+ *   Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose
+ *   in '') and %%.
+ *
+ *   NOTE: using this syntax will cast NULL and FALSE values to decimal 0,
+ *   and TRUE values to decimal 1.
+ *
+ * @param $from
+ *   The first result row to return.
+ * @param $count
+ *   The maximum number of result rows to return.
+ * @return
+ *   A database query result resource, or FALSE if the query was not executed
+ *   correctly.
+ */
+function db_query_range_slave($query) {
+  $args = func_get_args();
+  $count = array_pop($args);
+  $from = array_pop($args);
+  array_shift($args);
+
+  $query = db_prefix_tables($query);
+  if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
+    $args = $args[0];
+  }
+  _db_query_callback($args, TRUE);
+  $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query);
+  $query .= ' LIMIT '. (int)$count .' OFFSET '. (int)$from;
+  return _db_query($query, FALSE, TRUE);
+}
+
+/**
  * Runs a SELECT query and stores its results in a temporary table.
  *
  * Use this as a substitute for db_query() when the results need to stored
Index: includes/pager.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/pager.inc,v
retrieving revision 1.59
diff -u -p -r1.59 pager.inc
--- includes/pager.inc	15 Oct 2006 19:57:05 -0000	1.59
+++ includes/pager.inc	5 Aug 2007 16:44:47 -0000
@@ -69,10 +69,10 @@ function pager_query($query, $limit = 10
   $pager_page_array = explode(',', $page);
 
   // We calculate the total of pages as ceil(items / limit).
-  $pager_total_items[$element] = db_result(db_query($count_query, $args));
+  $pager_total_items[$element] = db_result(db_query_slave($count_query, $args));
   $pager_total[$element] = ceil($pager_total_items[$element] / $limit);
   $pager_page_array[$element] = max(0, min((int)$pager_page_array[$element], ((int)$pager_total[$element]) - 1));
-  return db_query_range($query, $args, $pager_page_array[$element] * $limit, $limit);
+  return db_query_range_slave($query, $args, $pager_page_array[$element] * $limit, $limit);
 }
 
 /**
Index: modules/search/search.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/search/search.module,v
retrieving revision 1.209.2.5
diff -u -p -r1.209.2.5 search.module
--- modules/search/search.module	26 Jul 2007 19:16:48 -0000	1.209.2.5
+++ modules/search/search.module	5 Aug 2007 16:44:47 -0000
@@ -852,7 +852,7 @@ function do_search($keywords, $type, $jo
   $result = db_query_temporary("SELECT i.type, i.sid, SUM(i.score * t.count) AS relevance, COUNT(*) AS matches FROM {search_index} i INNER JOIN {search_total} t ON i.word = t.word $join1 WHERE $conditions GROUP BY i.type, i.sid HAVING COUNT(*) >= %d", $arguments, 'temp_search_sids');
 
   // Calculate maximum relevance, to normalize it
-  $normalize = db_result(db_query('SELECT MAX(relevance) FROM temp_search_sids'));
+  $normalize = db_result(db_query_slave('SELECT MAX(relevance) FROM temp_search_sids'));
   if (!$normalize) {
     return array();
   }
@@ -862,7 +862,7 @@ function do_search($keywords, $type, $jo
   $conditions = '('. $query[0] .')';
   $arguments = array_merge($arguments2, $query[1]);
   $result = db_query_temporary("SELECT i.type, i.sid, $select2 FROM temp_search_sids i INNER JOIN {search_dataset} d ON i.sid = d.sid AND i.type = d.type $join2 WHERE $conditions $sort_parameters", $arguments, 'temp_search_results');
-  if (($count = db_result(db_query('SELECT COUNT(*) FROM temp_search_results'))) == 0) {
+  if (($count = db_result(db_query_slave('SELECT COUNT(*) FROM temp_search_results'))) == 0) {
     return array();
   }
   $count_query = "SELECT $count";
