diff --git includes/database/query.inc includes/database/query.inc index 2e775be..7289121 100644 --- includes/database/query.inc +++ includes/database/query.inc @@ -773,52 +773,43 @@ class MergeQuery extends Query { // Wrap multiple queries in a transaction, if the database supports it. $transaction = $this->connection->startTransaction(); - // Manually check if the record already exists. - $select = $this->connection->select($this->table); - foreach ($this->keyFields as $field => $value) { - $select->condition($field, $value); + // We want to reduce the number of queries to the minimum so that the + // behaviour is as close to atomic as possible. Because our database layer + // supports savepoints we can try to insert first, catch the failure if any + // and do the update instead with out breaking any transaction we maybe in. + try { + $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(); } - $select = $select->countQuery(); - $sql = (string) $select; - $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; + // Should the insert fail, we will assume it is because the record already + // exists so lets attempt to update the record instead. + 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]); } - 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 ($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); } - 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; + foreach ($this->expressionFields as $field => $expression) { + $update->expression($field, $expression['expression'], $expression['arguments']); } + $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; - } - - // Transaction commits here where $transaction looses scope. + throw new InvalidMergeQueryException("Unexpected action occured. Merge query did not insert or update a record."); } public function __toString() {