=== modified file 'includes/actions.inc'
--- includes/actions.inc	2009-07-20 18:51:31 +0000
+++ includes/actions.inc	2009-07-21 04:01:33 +0000
@@ -324,7 +324,7 @@ function actions_save($function, $type, 
   // aid is the callback for singleton actions so we need to keep a separate
   // table for numeric aids.
   if (!$aid) {
-    $aid = db_insert('actions_aid')->useDefaults(array('aid'))->execute();
+    $aid = db_next_id('actions');
   }
 
   db_merge('actions')

=== modified file 'includes/database/database.inc'
--- includes/database/database.inc	2009-07-21 01:56:35 +0000
+++ includes/database/database.inc	2009-07-21 04:27:16 +0000
@@ -1046,6 +1046,45 @@ abstract class DatabaseConnection extend
   public function commit() {
     throw new ExplicitTransactionsNotSupportedException();
   }
+
+  /**
+   * Retrieves an unique id from a given sequence.
+   *
+   *  Use this function if for some reason you can't use a serial field.
+   *
+   * @param $name
+   *   The name of the sequence.
+   * @param $existing_id
+   *   After a database import, it might be that the sequences table is behind,
+   *   so by passing in the maximum existing id, it can be assured that we
+   *   never issue the same id.
+   * @return
+   *   An integer number larger than any number returned by earlier calls with
+   *   the same $name and also larger than the $existing_id if one was passed
+   *   in.
+   */
+  public function nextId($name, $existing_id = 0) {
+    $transaction = $this->startTransaction();
+    $current_value = $this
+      ->query('SELECT value FROM {sequences} WHERE name = :name', array(':name' => $name))
+      ->fetchField();
+    $new_value = max($current_value, $existing_id) + 1;
+    // As we start the sequence with 1, if $current_value is 0 then the
+    // sequence does not exist so we know already whether we want an INSERT or
+    // an UPDATE.
+    if ($current_value) {
+      // 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.
+      $this->query('UPDATE {sequences} SET value = :value WHERE name = :name', array(':name' => $name, ':value' => $new_value));
+    }
+    else {
+      $this->query('INSERT INTO {sequences} (name, value) VALUES (:name, :value)', array(':name' => $name, ':value' => $new_value));
+    }
+    return $new_value;
+  }
 }
 
 /**
@@ -2071,6 +2110,26 @@ function db_driver() {
 }
 
 /**
+ * Retrieves an unique id from a given sequence.
+ *
+ * Use this function if for some reason you can't use a serial field,
+ * normally a serial field with db_last_insert_id is preferred.
+ *
+ * @param $name
+ *   The name of the sequence.
+ * @param $existing_id
+ *   After a database import, it might be that the sequences table is behind,
+ *   so by passing in a minimum id, it can be assured that we never issue the
+ *   same id.
+ * @return
+ *   An integer number larger than any number returned before for this
+ *   sequence.
+ */
+function db_next_id($name, $existing_id = 0) {
+  return Database::getConnection()->nextId($name, $existing_id);
+}
+
+/**
  * @} End of "defgroup database".
  */
 

=== modified file 'includes/database/mysql/database.inc'
--- includes/database/mysql/database.inc	2009-03-25 18:43:01 +0000
+++ includes/database/mysql/database.inc	2009-07-21 04:08:13 +0000
@@ -77,6 +77,28 @@ class DatabaseConnection_mysql extends D
     // (?<!text) is a negative look-behind (no need to rewrite queries that already use DISTINCT).
     return preg_replace('/(SELECT.*)(?:' . $table . '\.|\s)(?<!DISTINCT\()(?<!DISTINCT\(' . $table . '\.)' . $field . '(.*FROM )/AUsi', '\1 ' . $field_to_select . '\2', $query);
   }
+
+  public function nextId($name, $existing_id) {
+    if ($this->transactionSupport) {
+      return parent::nextId($name, $existing_id);
+    }
+    try {
+      // Try an INSERT first.
+      $this->query('INSERT INTO {sequences} (name, value) VALUES (:name, :value)', array(':name' => $name, ':value' => $existing_id));
+    }
+    catch (PDOException $e) {
+      // The most likely case for an exception is that the sequence already
+      // exists and that's exactly what we wanted to assure.
+    }
+    // LAST_INSERT_ID(x) stores x to be retrieved with LAST_INSERT_ID().
+    // This way there is no window for race condition between the UPDATE and
+    // the SELECT.
+    $this->query('UPDATE {sequences} SET value = LAST_INSERT_ID(GREATEST(:existing_id, value) + 1) WHERE name = :name', array(
+      ':existing_id' => $existing_id,
+      ':name' => $name,
+    ));
+    return $this->query('SELECT LAST_INSERT_ID()')->fetchField();
+  }
 }
 
 

=== modified file 'modules/simpletest/tests/database_test.test'
--- modules/simpletest/tests/database_test.test	2009-07-21 01:56:35 +0000
+++ modules/simpletest/tests/database_test.test	2009-07-21 04:01:33 +0000
@@ -2762,3 +2762,30 @@ class DatabaseTransactionTestCase extend
     }
   }
 }
+
+/**
+ * Check the sequences API.
+ */
+class DatabaseNextIdCase extends DrupalWebTestCase {
+  function getInfo() {
+    return array(
+      'name' => t('Sequences API'),
+      'description' => t('Test the secondary sequences API.'),
+      'group' => t('Database'),
+    );
+  }
+
+  /**
+   * Test that the sequences API work.
+   */
+  function testDbNextId() {
+    $first = db_next_id('test');
+    $second = db_next_id('test');
+    // We can test for exact increase in here because we know there is no
+    // other process operating on these tables -- normally we could only
+    // expect $second > $first.
+    $this->assertEqual($first + 1, $second, t('The second call from a sequence provides a number increased by one.'));
+    $result = db_next_id('test', 1000);
+    $this->assertEqual($result, 1001, t('Sequence provides a larger number than the existing ID.'));
+  }
+}

=== modified file 'modules/system/system.install'
--- modules/system/system.install	2009-07-20 18:51:31 +0000
+++ modules/system/system.install	2009-07-21 04:27:16 +0000
@@ -355,21 +355,10 @@ function system_install() {
   // Load system theme data appropriately.
   system_get_theme_data();
 
-  // Inserting uid 0 here confuses MySQL -- the next user might be created as
-  // uid 2 which is not what we want. So we insert the first user here, the
-  // anonymous user. uid is 1 here for now, but very soon it will be changed
-  // to 0.
-  db_query("INSERT INTO {users} (name, mail) VALUES('%s', '%s')", '', '');
+  db_query("INSERT INTO {users} (uid, name, mail) VALUES (0, '%s', '%s')", '', '');
   // We need some placeholders here as name and mail are uniques and data is
-  // presumed to be a serialized array. Install will change uid 1 immediately
-  // anyways. So we insert the superuser here, the uid is 2 here for now, but
-  // very soon it will be changed to 1.
-  db_query("INSERT INTO {users} (name, mail, created, status, data) VALUES('%s', '%s', %d, %d, '%s')", 'placeholder-for-uid-1', 'placeholder-for-uid-1', REQUEST_TIME, 1, serialize(array()));
-  // This sets the above two users uid 0 (anonymous). We avoid an explicit 0
-  // otherwise MySQL might insert the next auto_increment value.
-  db_query("UPDATE {users} SET uid = uid - uid WHERE name = '%s'", '');
-  // This sets uid 1 (superuser). We skip uid 2 but that's not a big problem.
-  db_query("UPDATE {users} SET uid = 1 WHERE name = '%s'", 'placeholder-for-uid-1');
+  // presumed to be a serialized array. 
+  db_query("INSERT INTO {users} (uid, name, mail, created, status, data) VALUES (1, '%s', '%s', %d, %d, '%s')", 'placeholder-for-uid-1', 'placeholder-for-uid-1', REQUEST_TIME, 1, serialize(array()));
 
   // Built-in roles.
   $rid_anonymous = db_insert('role')
@@ -585,19 +574,6 @@ function system_schema() {
     'primary key' => array('aid'),
   );
 
-  $schema['actions_aid'] = array(
-    'description' => 'Stores action IDs for non-default actions.',
-    'fields' => array(
-      'aid' => array(
-        'description' => 'Primary Key: Unique actions ID.',
-        'type' => 'serial',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-      ),
-    ),
-    'primary key' => array('aid'),
-  );
-
   $schema['batch'] = array(
     'description' => 'Stores details about batches (processes that run in multiple HTTP requests).',
     'fields' => array(
@@ -1207,18 +1183,6 @@ function system_schema() {
     ),
   );
 
-  $schema['queue_consumer_id'] = array(
-    'description' => 'Stores queue consumer IDs, used to auto-increment the consumer ID so that a unique consumer ID is used.',
-    'fields' => array(
-      'consumer_id'  => array(
-        'type' => 'serial',
-        'not null' => TRUE,
-        'description' => 'Primary Key: Unique consumer ID used to make sure only one consumer gets one item.',
-      ),
-    ),
-    'primary key' => array('consumer_id'),
-  );
-
   $schema['registry'] = array(
     'description' => "Each record is a function, class, or interface name and the file it is in.",
     'fields' => array(
@@ -1288,6 +1252,26 @@ function system_schema() {
     'primary key' => array('filename'),
   );
 
+  $schema['sequences'] = array(
+    'description' => 'Stores IDs.',
+    'fields' => array(
+      'value' => array(
+        'description' => 'The .',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+      ),
+      'name' => array(
+        'description' => "Primary key: A session ID. The value is generated by PHP's Session API.",
+        'type' => 'varchar',
+        'length' => 64,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+    ),
+    'primary key' => array('name'),
+  );
+
   $schema['sessions'] = array(
     'description' => "Drupal's session handlers read and write into the sessions table. Each record represents a user session, either anonymous or authenticated.",
     'fields' => array(
@@ -2241,6 +2225,23 @@ function system_update_7029() {
 }
 
 /**
+ * Reuse the actions_aid table as sequences.
+ */
+function system_update_7030() {
+  $ret = array();
+  db_drop_primary_key($ret, 'actions_aid');
+  db_change_field($ret, 'actions_aid', 'aid', 'sid', array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE), array('primary key' => array('sid')));
+  db_rename_table($ret, 'actions_aid', 'sequences');
+  $max_sid = db_query('SELECT MAX(sid) FROM {sequences}')->fetchField();
+  db_delete('sequences')->condition('sid', $max_sid, '<');
+  $max_uid = db_query('SELECT MAX(uid) FROM {users}')->fetchField();
+  if ($max_uid > $max_sid) {
+    db_update('sequences')->fields(array('sid' => $max_uid))->execute();
+  }
+  return $ret;
+}
+
+/**
  * @} End of "defgroup updates-6.x-to-7.x"
  * The next series of updates should start at 8000.
  */

=== modified file 'modules/system/system.queue.inc'
--- modules/system/system.queue.inc	2009-05-24 17:39:30 +0000
+++ modules/system/system.queue.inc	2009-07-21 04:01:33 +0000
@@ -195,9 +195,7 @@ class SystemQueue implements DrupalQueue
 
   public function claimItem($lease_time = 30) {
     if (!isset($this->consumerId)) {
-      $this->consumerId = db_insert('queue_consumer_id')
-        ->useDefaults(array('consumer_id'))
-        ->execute();
+      $this->consumerId = db_next_id('queue_consumer_id');
     }
     // Claim an item by updating its consumer_id and expire fields. If claim
     // is not successful another thread may have claimed the item in the

=== modified file 'modules/user/user.install'
--- modules/user/user.install	2009-06-01 22:07:08 +0000
+++ modules/user/user.install	2009-07-21 04:01:33 +0000
@@ -102,10 +102,11 @@ function user_schema() {
     'description' => 'Stores user data.',
     'fields' => array(
       'uid' => array(
-        'type' => 'serial',
+        'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
         'description' => 'Primary Key: Unique user ID.',
+        'default' => 0,
       ),
       'name' => array(
         'type' => 'varchar',

=== modified file 'modules/user/user.module'
--- modules/user/user.module	2009-07-15 17:40:17 +0000
+++ modules/user/user.module	2009-07-21 04:27:16 +0000
@@ -376,6 +376,7 @@ function user_save($account, $edit = arr
   if (!isset($account->is_new)) {
     $account->is_new = empty($account->uid);
   }
+
   if (is_object($account) && !$account->is_new) {
     user_module_invoke('update', $edit, $account, $category);
     $data = unserialize(db_query('SELECT data FROM {users} WHERE uid = :uid', array(':uid' => $account->uid))->fetchField());
@@ -486,6 +487,11 @@ function user_save($account, $edit = arr
     user_module_invoke('after_update', $edit, $user, $category);
   }
   else {
+    // Allow 'uid' to be set by the caller. There is no danger of writing an
+    // existing user as drupal_write_record will do an INSERT.
+    if (empty($edit['uid'])) {
+      $edit['uid'] = db_next_id('users', db_query('SELECT MAX(uid) FROM {users}')->fetchField());
+    }
     // Allow 'created' to be set by the caller.
     if (!isset($edit['created'])) {
       $edit['created'] = REQUEST_TIME;

