Index: includes/bootstrap.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/bootstrap.inc,v
retrieving revision 1.96.2.3
diff -u -r1.96.2.3 bootstrap.inc
--- includes/bootstrap.inc	5 Jul 2006 05:07:53 -0000	1.96.2.3
+++ includes/bootstrap.inc	10 Aug 2006 06:33:01 -0000
@@ -281,10 +281,10 @@
 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();
+  db_query_replace(
+    array("DELETE FROM {variable} WHERE name = '%s'", $name),
+    array("INSERT INTO {variable} (name, value) VALUES ('%s', '%s')", $name, serialize($value))
+  );
 
   cache_clear_all('variables');
 
@@ -368,12 +368,10 @@
  *   A string containing HTTP header information for cached pages.
  */
 function cache_set($cid, $data, $expire = CACHE_PERMANENT, $headers = NULL) {
-  db_lock_table('cache');
-  db_query("UPDATE {cache} SET data = %b, created = %d, expire = %d, headers = '%s' WHERE cid = '%s'", $data, time(), $expire, $headers, $cid);
-  if (!db_affected_rows()) {
-    @db_query("INSERT INTO {cache} (cid, data, created, expire, headers) VALUES ('%s', %b, %d, %d, '%s')", $cid, $data, time(), $expire, $headers);
-  }
-  db_unlock_tables();
+  db_query_replace(
+    array("DELETE FROM {cache} WHERE cid = '%s'", $cid),
+    array("INSERT INTO {cache} (cid, data, created, expire, headers) VALUES ('%s', ". DB_BLOB_PLACEHOLDER .", %d, %d, '%s')", $cid, $data, time(), $expire, $headers)
+  );
 }
 
 /**
Index: includes/database.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/database.inc,v
retrieving revision 1.56
diff -u -r1.56 database.inc
--- includes/database.inc	27 Apr 2006 20:38:49 -0000	1.56
+++ includes/database.inc	10 Aug 2006 06:33:01 -0000
@@ -136,32 +136,6 @@
 }
 
 /**
- * Helper function for db_query().
- */
-function _db_query_callback($match, $init = FALSE) {
-  static $args = NULL;
-  if ($init) {
-    $args = $match;
-    return;
-  }
-
-  switch ($match[1]) {
-    case '%d': // We must use type casting to int to convert false/null/(true?)
-      return (int) array_shift($args); // We don't need db_escape_string as numbers are db-safe
-    case '%s':
-      return db_escape_string(array_shift($args));
-    case '%%':
-      return '%';
-    case '%f':
-      return (float) array_shift($args);
-    case '%b': // binary data
-      return db_encode_blob(array_shift($args));
-  }
-}
-
-define('DB_QUERY_REGEXP', '/(%d|%s|%%|%f|%b)/');
-
-/**
  * Runs a basic query in the active database.
  *
  * User-supplied arguments to the query should be passed in as separate
@@ -192,8 +166,8 @@
   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 = _db_query_sprintf($query, $args);
   return _db_query($query);
 }
 
@@ -209,8 +183,7 @@
   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 = _db_query_sprintf($query, $args);
   return _db_query($query, 1);
 }
 
Index: includes/database.mysql.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/database.mysql.inc,v
retrieving revision 1.52.2.1
diff -u -r1.52.2.1 database.mysql.inc
--- includes/database.mysql.inc	24 May 2006 20:47:13 -0000	1.52.2.1
+++ includes/database.mysql.inc	10 Aug 2006 06:33:01 -0000
@@ -11,6 +11,8 @@
  * @{
  */
 
+define('DB_BLOB_PLACEHOLDER', "'%s'");
+
 /**
  * Initialize a database connection.
  *
@@ -195,21 +197,25 @@
 }
 
 /**
- * Return a new unique ID in the given sequence.
- *
- * For compatibility reasons, Drupal does not use auto-numbered fields in its
- * database tables. Instead, this function is used to return a new unique ID
- * of the type requested. If necessary, a new sequence with the given name
- * will be created.
+ * Return a new unique ID.
  */
 function db_next_id($name) {
-  $name = db_prefix_tables($name);
-  db_query('LOCK TABLES {sequences} WRITE');
-  $id = db_result(db_query("SELECT id FROM {sequences} WHERE name = '%s'", $name)) + 1;
-  db_query("REPLACE INTO {sequences} VALUES ('%s', %d)", $name, $id);
-  db_query('UNLOCK TABLES');
-
-  return $id;
+  // Prefix any curly-bracket "{table}" names
+   $name = db_prefix_tables($name);
+  // Assume initially that the sequence already exists.  Attempt to update it atomically.
+  // LAST_INSERT_ID lets us get its value later. IGNORE suppresses update failures.
+  db_query("UPDATE IGNORE {sequences} SET id=LAST_INSERT_ID(id + 1) WHERE name = '%s'", $name);
+  // Check whether that worked
+  if (!db_affected_rows()) {
+    // Updated failed, so the sequence doesn't exist yet.
+    // In either case, try to create a new sequence starting from zero
+    // IGNORE suppresses insert error if e.g. a concurrent process got there first.
+    db_query("INSERT IGNORE INTO {sequences} VALUES ('%s', %d)", $name, 0);
+    // Sequence now exists (unless some database error occurred to prevent it)
+    // Retry update, allowing for possible concurrent process. It should work this time.
+    db_query("UPDATE {sequences} SET id = LAST_INSERT_ID(id + 1) WHERE name = '%s'", $name);
+  }
+  return mysql_insert_id();
 }
 
 /**
@@ -220,6 +226,12 @@
   return mysql_affected_rows($active_db);
 }
 
+function _db_query_sprintf($query, $args) {
+  $args = array_map('db_escape_string', $args);
+  array_unshift($args, $query);
+  return call_user_func_array('sprintf', $args);
+}
+
 /**
  * Runs a limited-range query in the active database.
  *
@@ -263,8 +275,7 @@
   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 = _db_query_sprintf($query, $args);
   $query .= ' LIMIT '. (int)$from .', '. (int)$count;
   return _db_query($query);
 }
@@ -311,8 +322,7 @@
   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 = sprintf($query, $args);
   return _db_query($query);
 }
 
@@ -364,7 +374,20 @@
 }
 
 /**
+ * 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.17.2.1
diff -u -r1.17.2.1 database.mysqli.inc
--- includes/database.mysqli.inc	24 May 2006 20:47:13 -0000	1.17.2.1
+++ includes/database.mysqli.inc	10 Aug 2006 06:33:41 -0000
@@ -15,6 +15,8 @@
  * @{
  */
 
+define('DB_BLOB_PLACEHOLDER', "'%s'");
+
 /**
  * Initialise a database connection.
  *
@@ -205,13 +207,22 @@
  * will be created.
  */
 function db_next_id($name) {
-  $name = db_prefix_tables($name);
-  db_query('LOCK TABLES {sequences} WRITE');
-  $id = db_result(db_query("SELECT id FROM {sequences} WHERE name = '%s'", $name)) + 1;
-  db_query("REPLACE INTO {sequences} VALUES ('%s', %d)", $name, $id);
-  db_query('UNLOCK TABLES');
-
-  return $id;
+  // Prefix any curly-bracket "{table}" names
+   $name = db_prefix_tables($name);
+  // Assume initially that the sequence already exists.  Attempt to update it atomically.
+  // LAST_INSERT_ID lets us get its value later. IGNORE suppresses update failures.
+  db_query("UPDATE IGNORE {sequences} SET id = LAST_INSERT_ID(id + 1) WHERE name = '%s'", $name);
+  // Check whether that worked
+  if (!db_affected_rows()) {
+    // Updated failed, so the sequence doesn't exist yet.
+    // In either case, try to create a new sequence starting from zero
+    // IGNORE suppresses insert error if e.g. a concurrent process got there first.
+    db_query("INSERT IGNORE INTO {sequences} VALUES ('%s', %d)", $name, 0);
+    // Sequence now exists (unless some database error occurred to prevent it)
+    // Retry update, allowing for possible concurrent process. It should work this time.
+    db_query("UPDATE {sequences} SET id = LAST_INSERT_ID(id + 1) WHERE name = '%s'", $name);
+  }
+  return db_result(db_query("SELECT LAST_INSERT_ID() FROM {sequences} WHERE name = '%s'", $name));
 }
 
 /**
@@ -222,6 +233,12 @@
   return mysqli_affected_rows($active_db);
 }
 
+function _db_query_sprintf($query, $args) {
+  $args = array_map('db_escape_string', $args);
+  array_unshift($args, $query);
+  return call_user_func_array('sprintf', $args);
+}
+
 /**
  * Runs a limited-range query in the active database.
  *
@@ -265,8 +282,7 @@
   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 = _db_query_sprintf($query, $args);
   $query .= ' LIMIT '. (int)$from .', '. (int)$count;
   return _db_query($query);
 }
@@ -313,8 +329,7 @@
   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 = _db_query_sprintf($query, $args);
   return _db_query($query);
 }
 
@@ -366,6 +381,21 @@
 }
 
 /**
+ * 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.29.2.1
diff -u -r1.29.2.1 database.pgsql.inc
--- includes/database.pgsql.inc	24 May 2006 20:47:13 -0000	1.29.2.1
+++ includes/database.pgsql.inc	10 Aug 2006 06:33:01 -0000
@@ -11,6 +11,8 @@
  * @{
  */
 
+define('DB_BLOB_PLACEHOLDER', '%b');
+
 /**
  * Initialize a database connection.
  *
@@ -201,6 +203,37 @@
 }
 
 /**
+ * Helper function for db_query().
+ */
+function _db_query_callback($match, $init = FALSE) {
+  static $args = NULL;
+  if ($init) {
+    $args = $match;
+    return;
+  }
+
+  switch ($match[1]) {
+    case '%d': // We must use type casting to int to convert FALSE/NULL/(TRUE?)
+      return (int) array_shift($args); // We don't need db_escape_string as numbers are db-safe
+    case '%s':
+      return db_escape_string(array_shift($args));
+    case '%%':
+      return '%';
+    case '%f':
+      return (float) array_shift($args);
+    case '%b': // binary data
+      return db_encode_blob(array_shift($args));
+  }
+}
+
+define('DB_QUERY_REGEXP', '/(%d|%s|%%|%f|%b)/');
+
+function _db_query_sprintf($query, $args) {
+  _db_query_callback($args, TRUE);
+  return preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query);
+}
+
+/**
  * Runs a limited-range query in the active database.
  *
  * Use this as a substitute for db_query() when a subset of the query
@@ -239,8 +272,7 @@
   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 = _db_query_sprintf($query, $args);
   $query .= ' LIMIT '. (int)$count .' OFFSET '. (int)$from;
   return _db_query($query);
 }
@@ -353,6 +385,26 @@
 }
 
 /**
+ * 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) {
+  $delete_query = array_shift($delete);
+  db_query('BEGIN');
+  db_query(preg_replace('/^DELETE /', 'SELECT ', $delete_query . ' FOR UPDATE'), $delete);
+  db_query($delete_query, $delete);
+  $insert_query = array_shift($insert);
+  db_query($insert_query, $insert);
+  db_query('COMMIT');
+}
+
+/**
  * @} End of "ingroup database".
  */
 
Index: modules/block.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/Attic/block.module,v
retrieving revision 1.206.2.2
diff -u -r1.206.2.2 block.module
--- modules/block.module	2 Jul 2006 20:53:52 -0000	1.206.2.2
+++ modules/block.module	10 Aug 2006 06:58:26 -0000
@@ -150,7 +150,7 @@
     $old_blocks[$old_block->module][$old_block->delta] = $old_block;
   }
 
-  db_query("DELETE FROM {blocks} WHERE theme = '%s'", $theme_key);
+  $insert_values = array();
 
   foreach (module_list() as $module) {
     $module_blocks = module_invoke($module, 'block', 'list');
@@ -179,13 +179,20 @@
         }
 
         // Reinsert blocks into table
-        db_query("INSERT INTO {blocks} (module, delta, theme, status, weight, region, visibility, pages, custom, throttle) VALUES ('%s', '%s', '%s', %d, %d, '%s', %d, '%s', %d, %d)",
-          $block['module'], $block['delta'], $theme_key, $block['status'], $block['weight'], $block['region'], $block['visibility'], $block['pages'], $block['custom'], $block['throttle']);
+        $insert_values[] = _db_query_sprintf("('%s', '%s', '%s', %d, %d, '%s', %d, '%s', %d, %d)", array($block['module'], $block['delta'], $theme_key, $block['status'], $block['weight'], $block['region'], $block['visibility'], $block['pages'], $block['custom'], $block['throttle']));
         $blocks[] = $block;
       }
     }
   }
+  
+  if (count($insert_values)) {
+    db_query_replace(
+      array("DELETE FROM {blocks} WHERE theme = '%s'", $theme_key),
+      array('INSERT INTO {blocks} (module, delta, theme, status, weight, region, visibility, pages, custom, throttle) VALUES '. implode(', ', $insert_values))
+    );
+  }
 
+  
   return $blocks;
 }
 
