#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
|
* An integer number larger than any number returned by earlier calls and
|
||||||
* also larger than the $existing_id if one was passed in.
|
* also larger than the $existing_id if one was passed in.
|
||||||
*/
|
*/
|
||||||
public function nextId($existing_id = 0) {
|
abstract 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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 {
|
class DatabaseConnection_pgsql extends DatabaseConnection {
|
||||||
|
|
||||||
public function __construct(array $connection_options = array()) {
|
public function __construct(array $connection_options = array()) {
|
||||||
|
@ -127,6 +132,49 @@ class DatabaseConnection_pgsql extends DatabaseConnection {
|
||||||
|
|
||||||
return isset($specials[$operator]) ? $specials[$operator] : NULL;
|
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.
|
// DatabaseStatement_sqlite::execute() and cannot be cached.
|
||||||
return $this->prepare($this->prefixTables($query));
|
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