=== modified file 'includes/database/mysql/query.inc'
--- includes/database/mysql/query.inc	2010-05-15 07:04:21 +0000
+++ includes/database/mysql/query.inc	2010-06-14 15:23:50 +0000
@@ -87,121 +87,19 @@ class InsertQuery_mysql extends InsertQu
   }
 }
 
-class MergeQuery_mysql extends MergeQuery {
-
-  public function execute() {
-
-    // A merge query without any key field is invalid.
-    if (count($this->keyFields) == 0) {
-      throw new InvalidMergeQueryException("You need to specify key fields before executing a merge query");
-    }
-
-    // Set defaults.
-    if ($this->updateFields) {
-      $update_fields = $this->updateFields;
-    }
-    else {
-      // When update fields are derived from insert fields, we don't need
-      // placeholders since we can tell MySQL to reuse insert supplied
-      // values using the VALUES(col_name) function.
-      $update_fields = array();
-    }
-
-    $insert_fields = $this->insertFields + $this->keyFields;
-
-    $max_placeholder = 0;
-    $values = array();
-    // We assume that the order here is the same as in __toString(). If that's
-    // not the case, then we have serious problems.
-    foreach ($insert_fields as $value) {
-      $values[':db_insert_placeholder_' . $max_placeholder++] = $value;
-    }
-
-    // Expressions take priority over literal fields, so we process those first
-    // and remove any literal fields that conflict.
-    foreach ($this->expressionFields as $field => $data) {
-      if (!empty($data['arguments'])) {
-        $values += $data['arguments'];
-      }
-      unset($update_fields[$field]);
-    }
-
-    // Because we filter $fields the same way here and in __toString(), the
-    // placeholders will all match up properly.
-    $max_placeholder = 0;
-    foreach ($update_fields as $field => $value) {
-      $values[':db_update_placeholder_' . ($max_placeholder++)] = $value;
-    }
-
-
-    // MySQL's INSERT ... ON DUPLICATE KEY UPDATE queries return 1
-    // (MergeQuery::STATUS_INSERT) for an INSERT operation or 2
-    // (MergeQuery::STATUS_UPDATE) for an UPDATE operation.
-    //
-    // @link http ://dev.mysql.com/doc/refman/5.0/en/mysql-affected-rows.html
-    $this->queryOptions['return'] = Database::RETURN_AFFECTED;
-    return $this->connection->query((string) $this, $values, $this->queryOptions);
-  }
-
-
+class TruncateQuery_mysql extends TruncateQuery {
   public function __toString() {
-    // Create a comments string to prepend to the query.
-    $comments = (!empty($this->comments)) ? '/* ' . implode('; ', $this->comments) . ' */ ' : '';
-
-    // Set defaults.
-    if ($this->updateFields) {
-      $update_fields = $this->updateFields;
+    // TRUNCATE is actually a DDL statement on MySQL, and DDL statements are
+    // not transactional, and result in an implicit COMMIT. When we are in a
+    // transaction, fallback to the slower, but transactional, DELETE.
+    if ($this->connection->inTransaction()) {
+      // Create a comments string to prepend to the query.
+      $comments = (!empty($this->comments)) ? '/* ' . implode('; ', $this->comments) . ' */ ' : '';
+      return $comments . 'DELETE FROM {' . $this->connection->escapeTable($this->table) . '}';
     }
     else {
-      $update_fields = $this->insertFields;
-      // If there are no exclude fields, this is a no-op.
-      foreach ($this->excludeFields as $exclude_field) {
-        unset($update_fields[$exclude_field]);
-      }
-    }
-
-    // If the merge query has no fields to update, add the first key as an
-    // update field so the query will not fail if a duplicate key is found.
-    if (!$update_fields && !$this->expressionFields) {
-      $update_fields = array_slice($this->keyFields, 0, 1, TRUE);
-    }
-
-    $insert_fields = $this->insertFields + $this->keyFields;
-
-    $query = $comments . 'INSERT INTO {' . $this->table . '} (' . implode(', ', array_keys($insert_fields)) . ') VALUES ';
-
-    $max_placeholder = 0;
-    $values = array();
-    // We don't need the $field, but this is a convenient way to count.
-    foreach ($insert_fields as $field) {
-      $values[] = ':db_insert_placeholder_' . $max_placeholder++;
-    }
-
-    $query .= '(' . implode(', ', $values) . ') ON DUPLICATE KEY UPDATE ';
-
-    // Expressions take priority over literal fields, so we process those first
-    // and remove any literal fields that conflict.
-    $max_placeholder = 0;
-    $update = array();
-    foreach ($this->expressionFields as $field => $data) {
-      $update[] = $field . '=' . $data['expression'];
-      unset($update_fields[$field]);
+      return parent::__toString();
     }
-
-    // Build update fields clauses based on caller supplied list, or derived
-    // from insert supplied values using the VALUES(col_name) function.
-    foreach ($update_fields as $field => $value) {
-      if ($this->updateFields) {
-        $update[] = ($field . '=:db_update_placeholder_' . $max_placeholder++);
-      }
-      else {
-        $update[] = ($field . '=VALUES(' . $field . ')');
-      }
-    }
-
-    $query .= implode(', ', $update);
-
-    return $query;
   }
 }
 

=== modified file 'includes/database/query.inc'
--- includes/database/query.inc	2010-06-02 19:39:17 +0000
+++ includes/database/query.inc	2010-06-14 15:23:50 +0000
@@ -811,49 +811,52 @@ class MergeQuery extends Query {
 
     // Manually check if the record already exists.
     $select = $this->connection->select($this->table);
+    $select->addExpression('1');
     foreach ($this->keyFields as $field => $value) {
       $select->condition($field, $value);
     }
 
-    $select = $select->countQuery();
-    $sql = (string) $select;
+    // Using SELECT FOR UPDATE syntax will lock the rows we want to attempt to update.
+    $sql = ((string) $select) . ' FOR UPDATE';
     $arguments = $select->getArguments();
-    $num_existing = $this->connection->query($sql, $arguments)->fetchField();
 
-
-    if ($num_existing) {
-      // If there is already an existing record, run an update query.
-
-      if ($this->updateFields) {
-        $update_fields = $this->updateFields;
-      }
-      else {
-        $update_fields = $this->insertFields;
-        // If there are no exclude fields, this is a no-op.
-        foreach ($this->excludeFields as $exclude_field) {
-          unset($update_fields[$exclude_field]);
+    try {
+      // If there are already existing records, run an update query.
+      if ($this->connection->query($sql, $arguments)->fetchField()) {
+        if ($this->updateFields) {
+          $update_fields = $this->updateFields;
         }
-      }
-      if ($update_fields || $this->expressionFields) {
-        // Only run the update if there are no fields or expressions to update.
-        $update = $this->connection->update($this->table, $this->queryOptions)->fields($update_fields);
-        foreach ($this->keyFields as $field => $value) {
-          $update->condition($field, $value);
+        else {
+          $update_fields = $this->insertFields;
+          // If there are no exclude fields, this is a no-op.
+          foreach ($this->excludeFields as $exclude_field) {
+            unset($update_fields[$exclude_field]);
+          }
         }
-        foreach ($this->expressionFields as $field => $expression) {
-          $update->expression($field, $expression['expression'], $expression['arguments']);
+        if ($update_fields || $this->expressionFields) {
+          // Only run the update if there are no fields or expressions to update.
+          $update = $this->connection->update($this->table, $this->queryOptions)->fields($update_fields);
+          foreach ($this->keyFields as $field => $value) {
+            $update->condition($field, $value);
+          }
+          foreach ($this->expressionFields as $field => $expression) {
+            $update->expression($field, $expression['expression'], $expression['arguments']);
+          }
+          $update->execute();
+          return MergeQuery::STATUS_UPDATE;
         }
-        $update->execute();
-        return MergeQuery::STATUS_UPDATE;
+      }
+      else {
+        // If there is no existing record, run an insert query.
+        $insert_fields = $this->insertFields + $this->keyFields;
+        $this->connection->insert($this->table, $this->queryOptions)->fields($insert_fields)->execute();
+        return MergeQuery::STATUS_INSERT;
       }
     }
-    else {
-      // If there is no existing record, run an insert query.
-      $insert_fields = $this->insertFields + $this->keyFields;
-      $this->connection->insert($this->table, $this->queryOptions)->fields($insert_fields)->execute();
-      return MergeQuery::STATUS_INSERT;
+    catch (Exception $e) {
+      $transaction->rollback();
+      return FALSE;
     }
-
     // Transaction commits here where $transaction looses scope.
   }
 

