Index: includes/bootstrap.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/bootstrap.inc,v retrieving revision 1.96.2.3 diff -u -r1.96.2.3 bootstrap.inc --- includes/bootstrap.inc 5 Jul 2006 05:07:53 -0000 1.96.2.3 +++ includes/bootstrap.inc 10 Aug 2006 06:33:01 -0000 @@ -281,10 +281,10 @@ 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'); @@ -368,12 +368,10 @@ * A string containing HTTP header information for cached pages. */ function cache_set($cid, $data, $expire = CACHE_PERMANENT, $headers = NULL) { - db_lock_table('cache'); - db_query("UPDATE {cache} SET data = %b, created = %d, expire = %d, headers = '%s' WHERE cid = '%s'", $data, time(), $expire, $headers, $cid); - if (!db_affected_rows()) { - @db_query("INSERT INTO {cache} (cid, data, created, expire, headers) VALUES ('%s', %b, %d, %d, '%s')", $cid, $data, time(), $expire, $headers); - } - db_unlock_tables(); + db_query_replace( + array("DELETE FROM {cache} WHERE cid = '%s'", $cid), + array("INSERT INTO {cache} (cid, data, created, expire, headers) VALUES ('%s', ". DB_BLOB_PLACEHOLDER .", %d, %d, '%s')", $cid, $data, time(), $expire, $headers) + ); } /** Index: includes/database.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/database.inc,v retrieving revision 1.56 diff -u -r1.56 database.inc --- includes/database.inc 27 Apr 2006 20:38:49 -0000 1.56 +++ includes/database.inc 10 Aug 2006 06:33:01 -0000 @@ -136,32 +136,6 @@ } /** - * Helper function for db_query(). - */ -function _db_query_callback($match, $init = FALSE) { - static $args = NULL; - if ($init) { - $args = $match; - return; - } - - switch ($match[1]) { - case '%d': // We must use type casting to int to convert false/null/(true?) - return (int) array_shift($args); // We don't need db_escape_string as numbers are db-safe - case '%s': - return db_escape_string(array_shift($args)); - case '%%': - return '%'; - case '%f': - return (float) array_shift($args); - case '%b': // binary data - return db_encode_blob(array_shift($args)); - } -} - -define('DB_QUERY_REGEXP', '/(%d|%s|%%|%f|%b)/'); - -/** * Runs a basic query in the active database. * * User-supplied arguments to the query should be passed in as separate @@ -192,8 +166,8 @@ if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax $args = $args[0]; } - _db_query_callback($args, TRUE); - $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query); + + $query = _db_query_sprintf($query, $args); return _db_query($query); } @@ -209,8 +183,7 @@ if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax $args = $args[0]; } - _db_query_callback($args, TRUE); - $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query); + $query = _db_query_sprintf($query, $args); return _db_query($query, 1); } Index: includes/database.mysql.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/database.mysql.inc,v retrieving revision 1.52.2.1 diff -u -r1.52.2.1 database.mysql.inc --- includes/database.mysql.inc 24 May 2006 20:47:13 -0000 1.52.2.1 +++ includes/database.mysql.inc 10 Aug 2006 06:33:01 -0000 @@ -11,6 +11,8 @@ * @{ */ +define('DB_BLOB_PLACEHOLDER', "'%s'"); + /** * Initialize a database connection. * @@ -195,21 +197,25 @@ } /** - * 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(); } /** @@ -220,6 +226,12 @@ return mysql_affected_rows($active_db); } +function _db_query_sprintf($query, $args) { + $args = array_map('db_escape_string', $args); + array_unshift($args, $query); + return call_user_func_array('sprintf', $args); +} + /** * Runs a limited-range query in the active database. * @@ -263,8 +275,7 @@ if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax $args = $args[0]; } - _db_query_callback($args, TRUE); - $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query); + $query = _db_query_sprintf($query, $args); $query .= ' LIMIT '. (int)$from .', '. (int)$count; return _db_query($query); } @@ -311,8 +322,7 @@ if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax $args = $args[0]; } - _db_query_callback($args, TRUE); - $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query); + $query = sprintf($query, $args); return _db_query($query); } @@ -364,7 +374,20 @@ } /** + * 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.17.2.1 diff -u -r1.17.2.1 database.mysqli.inc --- includes/database.mysqli.inc 24 May 2006 20:47:13 -0000 1.17.2.1 +++ includes/database.mysqli.inc 10 Aug 2006 06:33:41 -0000 @@ -15,6 +15,8 @@ * @{ */ +define('DB_BLOB_PLACEHOLDER', "'%s'"); + /** * Initialise a database connection. * @@ -205,13 +207,22 @@ * will be created. */ 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)); } /** @@ -222,6 +233,12 @@ return mysqli_affected_rows($active_db); } +function _db_query_sprintf($query, $args) { + $args = array_map('db_escape_string', $args); + array_unshift($args, $query); + return call_user_func_array('sprintf', $args); +} + /** * Runs a limited-range query in the active database. * @@ -265,8 +282,7 @@ if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax $args = $args[0]; } - _db_query_callback($args, TRUE); - $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query); + $query = _db_query_sprintf($query, $args); $query .= ' LIMIT '. (int)$from .', '. (int)$count; return _db_query($query); } @@ -313,8 +329,7 @@ if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax $args = $args[0]; } - _db_query_callback($args, TRUE); - $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query); + $query = _db_query_sprintf($query, $args); return _db_query($query); } @@ -366,6 +381,21 @@ } /** + * 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.29.2.1 diff -u -r1.29.2.1 database.pgsql.inc --- includes/database.pgsql.inc 24 May 2006 20:47:13 -0000 1.29.2.1 +++ includes/database.pgsql.inc 10 Aug 2006 06:33:01 -0000 @@ -11,6 +11,8 @@ * @{ */ +define('DB_BLOB_PLACEHOLDER', '%b'); + /** * Initialize a database connection. * @@ -201,6 +203,37 @@ } /** + * Helper function for db_query(). + */ +function _db_query_callback($match, $init = FALSE) { + static $args = NULL; + if ($init) { + $args = $match; + return; + } + + switch ($match[1]) { + case '%d': // We must use type casting to int to convert FALSE/NULL/(TRUE?) + return (int) array_shift($args); // We don't need db_escape_string as numbers are db-safe + case '%s': + return db_escape_string(array_shift($args)); + case '%%': + return '%'; + case '%f': + return (float) array_shift($args); + case '%b': // binary data + return db_encode_blob(array_shift($args)); + } +} + +define('DB_QUERY_REGEXP', '/(%d|%s|%%|%f|%b)/'); + +function _db_query_sprintf($query, $args) { + _db_query_callback($args, TRUE); + return preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query); +} + +/** * Runs a limited-range query in the active database. * * Use this as a substitute for db_query() when a subset of the query @@ -239,8 +272,7 @@ if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax $args = $args[0]; } - _db_query_callback($args, TRUE); - $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query); + $query = _db_query_sprintf($query, $args); $query .= ' LIMIT '. (int)$count .' OFFSET '. (int)$from; return _db_query($query); } @@ -353,6 +385,26 @@ } /** + * 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". */ Index: modules/block.module =================================================================== RCS file: /cvs/drupal/drupal/modules/Attic/block.module,v retrieving revision 1.206.2.2 diff -u -r1.206.2.2 block.module --- modules/block.module 2 Jul 2006 20:53:52 -0000 1.206.2.2 +++ modules/block.module 10 Aug 2006 06:53:10 -0000 @@ -152,6 +152,8 @@ db_query("DELETE FROM {blocks} WHERE theme = '%s'", $theme_key); + $insert_values = array(); + foreach (module_list() as $module) { $module_blocks = module_invoke($module, 'block', 'list'); if ($module_blocks) { @@ -179,13 +181,20 @@ } // Reinsert blocks into table - db_query("INSERT INTO {blocks} (module, delta, theme, status, weight, region, visibility, pages, custom, throttle) VALUES ('%s', '%s', '%s', %d, %d, '%s', %d, '%s', %d, %d)", - $block['module'], $block['delta'], $theme_key, $block['status'], $block['weight'], $block['region'], $block['visibility'], $block['pages'], $block['custom'], $block['throttle']); + $insert_values[] = _db_query_sprintf("('%s', '%s', '%s', %d, %d, '%s', %d, '%s', %d, %d)", array($block['module'], $block['delta'], $theme_key, $block['status'], $block['weight'], $block['region'], $block['visibility'], $block['pages'], $block['custom'], $block['throttle'])); $blocks[] = $block; } } } + + if (count($insert_values)) { + db_query_replace( + array("DELETE FROM {blocks} WHERE theme = '%s'", $theme_key), + array('INSERT INTO {blocks} (module, delta, theme, status, weight, region, visibility, pages, custom, throttle) VALUES '. implode(', ', $insert_values)) + ); + } + return $blocks; }