#633678 by Josh Waihi, chx, Crell, David Strauss, and Damien Tournoud: Make sequence API work on non-MySQL databases.
parent
39f4d7245c
commit
37fcdbc67c
|
@ -1178,26 +1178,7 @@ abstract class DatabaseConnection extends PDO {
|
|||
* An integer number larger than any number returned by earlier calls and
|
||||
* also larger than the $existing_id if one was passed in.
|
||||
*/
|
||||
public function nextId($existing_id = 0) {
|
||||
$transaction = $this->startTransaction();
|
||||
// We can safely use literal queries here instead of the slower query
|
||||
// builder because if a given database breaks here then it can simply
|
||||
// override nextId. However, this is unlikely as we deal with short strings
|
||||
// and integers and no known databases require special handling for those
|
||||
// simple cases. If another transaction wants to write the same row, it will
|
||||
// wait until this transaction commits.
|
||||
$stmt = $this->query('UPDATE {sequences} SET value = GREATEST(value, :existing_id) + 1', array(
|
||||
':existing_id' => $existing_id,
|
||||
));
|
||||
if (!$stmt->rowCount()) {
|
||||
$this->query('INSERT INTO {sequences} (value) VALUES (:existing_id + 1)', array(
|
||||
':existing_id' => $existing_id,
|
||||
));
|
||||
}
|
||||
// The transaction gets committed when the transaction object gets destroyed
|
||||
// because it gets out of scope.
|
||||
return $new_value;
|
||||
}
|
||||
abstract public function nextId($existing_id = 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -11,6 +11,11 @@
|
|||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* The name by which to obtain a lock for retrive the next insert id.
|
||||
*/
|
||||
define('POSTGRESQL_NEXTID_LOCK', 1000);
|
||||
|
||||
class DatabaseConnection_pgsql extends DatabaseConnection {
|
||||
|
||||
public function __construct(array $connection_options = array()) {
|
||||
|
@ -127,6 +132,49 @@ class DatabaseConnection_pgsql extends DatabaseConnection {
|
|||
|
||||
return isset($specials[$operator]) ? $specials[$operator] : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrive a the next id in a sequence.
|
||||
*
|
||||
* PostgreSQL has built in sequences. We'll use these instead of inserting
|
||||
* and updating a sequences table.
|
||||
*/
|
||||
public function nextId($existing = 0) {
|
||||
|
||||
// Retrive the name of the sequence. This information cannot be cached
|
||||
// because the prefix may change, for example, like it does in simpletests.
|
||||
$sequence_name = $this->makeSequenceName('sequences', 'value');
|
||||
|
||||
// When PostgreSQL gets a value too small then it will lock the table,
|
||||
// retry the INSERT and if it's still too small then alter the sequence.
|
||||
$id = $this->query("SELECT nextval('" . $sequence_name . "')")->fetchField();
|
||||
if ($id > $existing) {
|
||||
return $id;
|
||||
}
|
||||
|
||||
// PostgreSQL advisory locks are simply locks to be used by an
|
||||
// application such as Drupal. This will prevent other Drupal proccesses
|
||||
// from altering the sequence while we are.
|
||||
$this->query("SELECT pg_advisory_lock(" . POSTGRESQL_NEXTID_LOCK . ")");
|
||||
|
||||
// While waiting to obtain the lock, the sequence may have been altered
|
||||
// so lets try again to obtain an adequate value.
|
||||
$id = $this->query("SELECT nextval('" . $sequence_name . "')")->fetchField();
|
||||
if ($id > $existing) {
|
||||
$this->query("SELECT pg_advisory_unlock(" . POSTGRESQL_NEXTID_LOCK . ")");
|
||||
return $id;
|
||||
}
|
||||
|
||||
// Reset the sequence to a higher value than the existing id.
|
||||
$this->query("ALTER SEQUENCE " . $sequence_name . " RESTART WITH " . ($existing + 1));
|
||||
|
||||
// Retrive the next id. We know this will be as high as we want it.
|
||||
$id = $this->query("SELECT nextval('" . $sequence_name . "')")->fetchField();
|
||||
|
||||
$this->query("SELECT pg_advisory_unlock(" . POSTGRESQL_NEXTID_LOCK . ")");
|
||||
|
||||
return $id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -167,6 +167,27 @@ class DatabaseConnection_sqlite extends DatabaseConnection {
|
|||
// DatabaseStatement_sqlite::execute() and cannot be cached.
|
||||
return $this->prepare($this->prefixTables($query));
|
||||
}
|
||||
|
||||
public function nextId($existing_id = 0) {
|
||||
$transaction = $this->startTransaction();
|
||||
// We can safely use literal queries here instead of the slower query
|
||||
// builder because if a given database breaks here then it can simply
|
||||
// override nextId. However, this is unlikely as we deal with short strings
|
||||
// and integers and no known databases require special handling for those
|
||||
// simple cases. If another transaction wants to write the same row, it will
|
||||
// wait until this transaction commits.
|
||||
$stmt = $this->query('UPDATE {sequences} SET value = GREATEST(value, :existing_id) + 1', array(
|
||||
':existing_id' => $existing_id,
|
||||
));
|
||||
if (!$stmt->rowCount()) {
|
||||
$this->query('INSERT INTO {sequences} (value) VALUES (:existing_id + 1)', array(
|
||||
':existing_id' => $existing_id,
|
||||
));
|
||||
}
|
||||
// The transaction gets committed when the transaction object gets destroyed
|
||||
// because it gets out of scope.
|
||||
return $new_value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue