summaryrefslogtreecommitdiff
path: root/includes
diff options
context:
space:
mode:
Diffstat (limited to 'includes')
-rw-r--r--includes/database/database.inc21
-rw-r--r--includes/database/pgsql/database.inc48
-rw-r--r--includes/database/sqlite/database.inc21
3 files changed, 70 insertions, 20 deletions
diff --git a/includes/database/database.inc b/includes/database/database.inc
index 7ab0078df..916ca4cc6 100644
--- a/includes/database/database.inc
+++ b/includes/database/database.inc
@@ -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);
}
/**
diff --git a/includes/database/pgsql/database.inc b/includes/database/pgsql/database.inc
index 8b60b957b..218d10865 100644
--- a/includes/database/pgsql/database.inc
+++ b/includes/database/pgsql/database.inc
@@ -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;
+ }
}
/**
diff --git a/includes/database/sqlite/database.inc b/includes/database/sqlite/database.inc
index e762a6ba7..eaeacb32d 100644
--- a/includes/database/sqlite/database.inc
+++ b/includes/database/sqlite/database.inc
@@ -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;
+ }
}
/**