Index: includes/bootstrap.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/bootstrap.inc,v retrieving revision 1.121 diff -u -p -r1.121 bootstrap.inc --- includes/bootstrap.inc 31 Aug 2006 18:40:04 -0000 1.121 +++ includes/bootstrap.inc 31 Aug 2006 21:33:35 -0000 @@ -287,10 +287,10 @@ function variable_get($name, $default) { function variable_set($name, $value) { global $conf; - db_lock_table('variable'); - db_query("DELETE FROM {variable} WHERE name = '%s'", $name); - db_query("INSERT INTO {variable} (name, value) VALUES ('%s', '%s')", $name, serialize($value)); - db_unlock_tables(); + db_query_replace( + array("DELETE FROM {variable} WHERE name = '%s'", $name), + array("INSERT INTO {variable} (name, value) VALUES ('%s', '%s')", $name, serialize($value)) + ); cache_clear_all('variables', 'cache'); Index: includes/cache.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/cache.inc,v retrieving revision 1.4 diff -u -p -r1.4 cache.inc --- includes/cache.inc 30 Aug 2006 08:46:16 -0000 1.4 +++ includes/cache.inc 31 Aug 2006 21:33:35 -0000 @@ -91,12 +91,10 @@ function cache_get($key, $table = 'cache * A string containing HTTP header information for cached pages. */ function cache_set($cid, $table = 'cache', $data, $expire = CACHE_PERMANENT, $headers = NULL) { - db_lock_table($table); - db_query("UPDATE {%s} SET data = '%s', created = %d, expire = %d, headers = '%s' WHERE cid = '%s'", $table, $data, time(), $expire, $headers, $cid); - if (!db_affected_rows()) { - @db_query("INSERT INTO {%s} (cid, data, created, expire, headers) VALUES ('%s', '%s', %d, %d, '%s')", $table, $cid, $data, time(), $expire, $headers); - } - db_unlock_tables(); + db_query_replace( + array("DELETE FROM {%s} WHERE cid = '%s'", $table, $cid), + array("INSERT INTO {%s} (cid, data, created, expire, headers) VALUES ('%s', %b, %d, %d, '%s')", $table, $cid, $data, time(), $expire, $headers) + ); } /** Index: includes/database.mysql.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/database.mysql.inc,v retrieving revision 1.56 diff -u -p -r1.56 database.mysql.inc --- includes/database.mysql.inc 31 Jul 2006 19:24:16 -0000 1.56 +++ includes/database.mysql.inc 31 Aug 2006 21:33:35 -0000 @@ -203,21 +203,25 @@ function db_error() { } /** - * Return a new unique ID in the given sequence. - * - * For compatibility reasons, Drupal does not use auto-numbered fields in its - * 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. + * Return a new unique ID. */ function db_next_id($name) { - $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'); - - return $id; + // Prefix any curly-bracket "{table}" names + $name = db_prefix_tables($name); + // 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 the sequence doesn't exist yet. + // 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. + db_query("UPDATE {sequences} SET id = LAST_INSERT_ID(id + 1) WHERE name = '%s'", $name); + } + return mysql_insert_id(); } /** @@ -379,7 +383,20 @@ function db_table_exists($table) { } /** + * Perform a replace query. + * + * @param $delete + * An array, where the first value is a query which deletes one row, + * subsequent values are arguments. Same syntax as of db_query. + * @param + * An array, where the first value is a query which inserts the new row, + * subsequent values are arguments. Same syntax as of db_query. + */ +function db_query_replace($delete, $insert) { + $insert_query = array_shift($insert); + db_query(preg_replace('/^INSERT /', 'REPLACE ', $insert_query), $insert); +} + +/** * @} End of "ingroup database". */ - - Index: includes/database.mysqli.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/database.mysqli.inc,v retrieving revision 1.20 diff -u -p -r1.20 database.mysqli.inc --- includes/database.mysqli.inc 26 Jul 2006 07:16:08 -0000 1.20 +++ includes/database.mysqli.inc 31 Aug 2006 21:33:35 -0000 @@ -198,21 +198,25 @@ function db_error() { } /** - * Return a new unique ID in the given sequence. - * - * For compatibility reasons, Drupal does not use auto-numbered fields in its - * 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. + * Return a new unique ID. */ function db_next_id($name) { - $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'); - - return $id; + // Prefix any curly-bracket "{table}" names + $name = db_prefix_tables($name); + // 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 the sequence doesn't exist yet. + // 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. + db_query("UPDATE {sequences} SET id = LAST_INSERT_ID(id + 1) WHERE name = '%s'", $name); + } + return db_result(db_query("SELECT LAST_INSERT_ID() FROM {sequences} WHERE name = '%s'", $name)); } /** @@ -374,6 +378,21 @@ function db_table_exists($table) { } /** + * Perform a replace query. + * + * @param $delete + * An array, where the first value is a query which deletes one row, + * subsequent values are arguments. Same syntax as of db_query. + * @param + * An array, where the first value is a query which inserts the new row, + * subsequent values are arguments. Same syntax as of db_query. + */ +function db_query_replace($delete, $insert) { + $insert_query = array_shift($insert); + db_query(preg_replace('/^INSERT /', 'REPLACE ', $insert_query), $insert); +} + +/** * @} End of "ingroup database". */ Index: includes/database.pgsql.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/database.pgsql.inc,v retrieving revision 1.34 diff -u -p -r1.34 database.pgsql.inc --- includes/database.pgsql.inc 18 Aug 2006 12:16:57 -0000 1.34 +++ includes/database.pgsql.inc 31 Aug 2006 21:33:35 -0000 @@ -370,6 +370,26 @@ function db_check_setup() { } /** + * Perform a replace query. + * + * @param $delete + * An array, where the first value is a query which deletes one row, + * subsequent values are arguments. Same syntax as of db_query. + * @param + * An array, where the first value is a query which inserts the new row, + * subsequent values are arguments. Same syntax as of db_query. + */ +function db_query_replace($delete, $insert) { + $delete_query = array_shift($delete); + db_query('BEGIN'); + db_query(preg_replace('/^DELETE /', 'SELECT ', $delete_query .' FOR UPDATE'), $delete); + db_query($delete_query, $delete); + $insert_query = array_shift($insert); + db_query($insert_query, $insert); + db_query('COMMIT'); +} + +/** * @} End of "ingroup database". */