includes/database/mysql/query.inc | 14 ++++++++ includes/database/query.inc | 64 +++++++++++++++++++------------------ modules/system/system.install | 2 - 3 files changed, 47 insertions(+), 33 deletions(-) diff --git includes/database/mysql/query.inc includes/database/mysql/query.inc index bf71ddc..0516876 100644 --- includes/database/mysql/query.inc +++ includes/database/mysql/query.inc @@ -89,6 +89,14 @@ class MergeQuery_mysql extends MergeQuery { public function execute() { + // ON DUPLICATE KEY UPDATE in MySQL doesn't behave the right way if there + // is more than one key field. If so, fall back to the slower generic + // version. When there is only a single key field, it behaves the same + // as the generic version but is faster and more atomic. + //if (count($this->keyFields) > 1) { + return parent::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"); @@ -144,6 +152,12 @@ class MergeQuery_mysql extends MergeQuery { public function __toString() { + // We need to fall back to the generic implementation for both execute() + // and __toString() separately. + //if (count($this->keyFields) > 1) { + return parent::__toString(); + //} + // Set defaults. if ($this->updateFields) { $update_fields = $this->updateFields; diff --git includes/database/query.inc includes/database/query.inc index e481cdf..50466b7 100644 --- includes/database/query.inc +++ includes/database/query.inc @@ -779,45 +779,47 @@ class MergeQuery extends Query { $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)->fetch()) { + 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. } diff --git modules/system/system.install modules/system/system.install index 0d73c64..0693dd5 100644 --- modules/system/system.install +++ modules/system/system.install @@ -1393,8 +1393,6 @@ function system_schema() { 'indexes' => array( 'timestamp' => array('timestamp'), 'uid' => array('uid'), - ), - 'unique keys' => array( 'ssid' => array('ssid'), ), 'foreign keys' => array(