Index: includes/database.mysql.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/database.mysql.inc,v retrieving revision 1.56 diff -u -r1.56 database.mysql.inc --- includes/database.mysql.inc 31 Jul 2006 19:24:16 -0000 1.56 +++ includes/database.mysql.inc 6 Aug 2006 11:23:13 -0000 @@ -209,15 +209,51 @@ * database tables. Instead, this function is used to return a new unique ID * of the type requested. If necessary, a new sequence with the given name * will be created. + * + * Usage example: + * @code + * // Attempt to get the next id + * $id = db_next_id('{example}_xid'); + * if (!$id) { + * // Failed to get an ID, so take a suitable error action + * return false; + * } + * // Got the id OK, so insert the record + * db_query("INSERT INTO {example} (xid, data) VALUES (%d, '%s')", $id, $data); + * @endcode + * + * Drupal convention is to prefix the table names using the '{'...'}' curly brackets, + * although since the sequence table itself is prefixed this is not strictly necessary. + * + * @param $name + * Sequence name to use. Will be prefixed using db_prefix_tables() + * @return + * The next unique ID, or FALSE if error */ function db_next_id($name) { + // Prefix any curly-bracket "{table}" names $name = db_prefix_tables($name); - db_query('LOCK TABLES {sequences} WRITE'); - $id = db_result(db_query("SELECT id FROM {sequences} WHERE name = '%s'", $name)) + 1; - db_query("REPLACE INTO {sequences} VALUES ('%s', %d)", $name, $id); - db_query('UNLOCK TABLES'); + // Assume initially that the sequence already exists. Attempt to update it atomically. + // LAST_INSERT_ID lets us get its value later. IGNORE suppresses update failures. + db_query("UPDATE IGNORE {sequences} SET id=LAST_INSERT_ID(id+1) WHERE name = '%s'", $name); + // Check whether that worked + if (!db_affected_rows()) { + // Updated failed, so (a) sequence doesn't exist yet, or (b) some database error + // In either case, try to create a new sequence starting from zero + // IGNORE suppresses insert error if e.g. a concurrent process got there first. + db_query("INSERT IGNORE INTO {sequences} VALUES ('%s', %d)", $name, 0); + // Sequence now exists (unless some database error occurred to prevent it) + // Retry update, allowing for possible concurrent process. It should work this time. + $ok = db_query("UPDATE {sequences} SET id=LAST_INSERT_ID(id+1) WHERE name = '%s'", $name); + // Make sure it worked. + if (!$ok) { + // Update failed, so we failed to get an id. Tell the caller something's wrong. + return false; + } // end if [failed to get a sequence id] + } // end if [sequence needs creating] - return $id; + // If we get here, the increment using LAST_INSERT_ID was successful, so return its value. + return mysql_insert_id(); } /**