Index: wordpress/wp-includes/wp-db.php
===================================================================
--- wordpress/wp-includes/wp-db.php	(revision 6510)
+++ wordpress/wp-includes/wp-db.php	(working copy)
@@ -33,10 +33,11 @@
 	var $options;
 	var $postmeta;
 	var $usermeta;
+	var $user_role;
 	var $terms;
 	var $term_taxonomy;
 	var $term_relationships;
-	var $tables = array('users', 'usermeta', 'posts', 'categories', 'post2cat', 'comments', 'links', 'link2cat', 'options',
+	var $tables = array('users', 'usermeta', 'user_role', 'posts', 'categories', 'post2cat', 'comments', 'links', 'link2cat', 'options',
 			'postmeta', 'terms', 'term_taxonomy', 'term_relationships');
 	var $charset;
 	var $collate;
Index: wordpress/wp-includes/version.php
===================================================================
--- wordpress/wp-includes/version.php	(revision 6510)
+++ wordpress/wp-includes/version.php	(working copy)
@@ -3,6 +3,6 @@
 // This holds the version number in a separate file so we can bump it without cluttering the SVN
 
 $wp_version = '2.4-bleeding';
-$wp_db_version = 6124;
+$wp_db_version = 6506;
 
 ?>
Index: wordpress/wp-includes/capabilities.php
===================================================================
--- wordpress/wp-includes/capabilities.php	(revision 6510)
+++ wordpress/wp-includes/capabilities.php	(working copy)
@@ -178,13 +178,21 @@
 		if ( ! isset($wp_roles) )
 			$wp_roles = new WP_Roles();
 
-		//Filter out caps that are not role names and assign to $this->roles
+		//This handles the old usermeta capabilities if present
 		if(is_array($this->caps))
 			$this->roles = array_filter(array_keys($this->caps), array(&$wp_roles, 'is_role'));
+		
+		// Fetch roles from the user_role table
+		global $wpdb, $blog_id;
+		$user_id = intval($this->ID);
+		$roles = $wpdb->get_col("SELECT role FROM {$wpdb->user_role} WHERE blog_id = {$blog_id} AND user_id = {$user_id}");
+		if ($roles)
+			$this->roles = array_filter($roles, array(&$wp_roles, 'is_role'));
 
 		//Build $allcaps from role caps, overlay user's $caps
 		$this->allcaps = array();
 		foreach( (array) $this->roles as $role) {
+			$this->allcaps[$role] = true;
 			$role = $wp_roles->get_role($role);
 			$this->allcaps = array_merge($this->allcaps, $role->capabilities);
 		}
@@ -192,32 +200,38 @@
 	}
 
 	function add_role($role) {
+		if ( !is_role($role) )
+			return new WP_Error( 'invalid_user_role', sprintf(__('Invalid user role: %s'), $role) );
+			
 		$this->caps[$role] = true;
-		update_usermeta($this->ID, $this->cap_key, $this->caps);
+		global $wpdb, $blog_id;
+		$user_id = intval($this->ID);
+		$role = $wpdb->escape($role);
+		$wpdb->query( "INSERT INTO {$wpdb->user_role} (blog_id, user_id, role) VALUES ({$blog_id}, {$user_id}, '{$role}')" );
 		$this->get_role_caps();
 		$this->update_user_level_from_caps();
 	}
 
 	function remove_role($role) {
-		if ( empty($this->roles[$role]) || (count($this->roles) <= 1) )
-			return;
 		unset($this->caps[$role]);
-		update_usermeta($this->ID, $this->cap_key, $this->caps);
+		global $wpdb, $blog_id;
+		$user_id = intval($this->ID);
+		$role = $wpdb->escape($role);
+		$wpdb->query( "DELETE FROM {$wpdb->user_role} WHERE blog_id = {$blog_id} AND user_id = {$user_id} AND role = '{$role}'" );
 		$this->get_role_caps();
 	}
 
 	function set_role($role) {
-		foreach($this->roles as $oldrole)
-			unset($this->caps[$oldrole]);
+		foreach($this->roles as $oldrole) {
+			if ($oldrole != $role)
+				$this->remove_role($oldrole);
+		}
 		if ( !empty($role) ) {
-			$this->caps[$role] = true;
-			$this->roles = array($role => true);
+			if ( !in_array($role, $this->roles) )
+				$this->add_role($role);
 		} else {
 			$this->roles = false;
 		}
-		update_usermeta($this->ID, $this->cap_key, $this->caps);
-		$this->get_role_caps();
-		$this->update_user_level_from_caps();
 	}
 
 	function level_reduction($max, $item) {
@@ -259,7 +273,7 @@
 	function has_cap($cap) {
 		if ( is_numeric($cap) )
 			$cap = $this->translate_level_to_cap($cap);
-
+			
 		$args = array_slice(func_get_args(), 1);
 		$args = array_merge(array($cap, $this->ID), $args);
 		$caps = call_user_func_array('map_meta_cap', $args);
@@ -481,4 +495,13 @@
 	return $wp_roles->remove_role($role);
 }
 
+function is_role($role) {
+	global $wp_roles;
+
+	if ( ! isset($wp_roles) )
+		$wp_roles = new WP_Roles();
+
+	return $wp_roles->is_role($role);
+}
+
 ?>
Index: wordpress/wp-includes/user.php
===================================================================
--- wordpress/wp-includes/user.php	(revision 6510)
+++ wordpress/wp-includes/user.php	(working copy)
@@ -52,7 +52,7 @@
 	global $wpdb, $blog_id;
 	if ( empty($id) )
 		$id = (int) $blog_id;
-	$users = $wpdb->get_results( "SELECT user_id, user_login, display_name, user_email, meta_value FROM $wpdb->users, $wpdb->usermeta WHERE " . $wpdb->users . ".ID = " . $wpdb->usermeta . ".user_id AND meta_key = '" . $wpdb->prefix . "capabilities' ORDER BY {$wpdb->usermeta}.user_id" );
+	$users = $wpdb->get_results( "SELECT user_id, user_login, display_name, user_email, role FROM {$wpdb->users}, {$wpdb->user_role} WHERE {$wpdb->users}.ID = {$wpdb->user_role}.user_id GROUP BY user_id ORDER BY {$wpdb->user_role}.user_id" );
 	return $users;
 }
 
Index: wordpress/wp-admin/includes/schema.php
===================================================================
--- wordpress/wp-admin/includes/schema.php	(revision 6510)
+++ wordpress/wp-admin/includes/schema.php	(working copy)
@@ -143,7 +143,17 @@
   PRIMARY KEY  (umeta_id),
   KEY user_id (user_id),
   KEY meta_key (meta_key)
-) $charset_collate;";
+) $charset_collate;
+CREATE TABLE IF NOT EXISTS $wpdb->user_role (
+  user_role_id bigint(20) NOT NULL auto_increment,
+  blog_id bigint(20) NOT NULL default '0',
+  user_id bigint(20) NOT NULL default '0',
+  role varchar(32) NOT NULL default '',
+  PRIMARY KEY  (user_role_id),
+  KEY user_blog (blog_id,user_id),
+  KEY user_id (user_id)
+) $charset_collate;
+";
 
 function populate_options() {
 	global $wpdb, $wp_db_version;
Index: wordpress/wp-admin/includes/user.php
===================================================================
--- wordpress/wp-admin/includes/user.php	(revision 6510)
+++ wordpress/wp-admin/includes/user.php	(working copy)
@@ -266,6 +266,7 @@
 
 	$wpdb->query("DELETE FROM $wpdb->users WHERE ID = $id");
 	$wpdb->query("DELETE FROM $wpdb->usermeta WHERE user_id = '$id'");
+	$wpdb->query("DELETE FROM $wpdb->user_role WHERE user_id = '$id'");
 
 	wp_cache_delete($id, 'users');
 	wp_cache_delete($user->user_login, 'userlogins');
Index: wordpress/wp-admin/includes/upgrade.php
===================================================================
--- wordpress/wp-admin/includes/upgrade.php	(revision 6510)
+++ wordpress/wp-admin/includes/upgrade.php	(working copy)
@@ -197,6 +197,10 @@
 
 	if ( $wp_current_db_version < 6124 )
 		upgrade_230_old_tables();
+		
+	global $wpdb, $blog_id;
+	if ( $wp_current_db_version < 6506 )
+		upgrade_user_roles($wpdb->prefix, $blog_id);
 
 	maybe_disable_automattic_widgets();
 
@@ -715,7 +719,36 @@
 	$wpdb->query("UPDATE $wpdb->postmeta SET meta_key = '_wp_old_slug' WHERE meta_key = 'old_slug'");
 }
 
+function upgrade_user_roles($prefix, $blog_id) {
+	// convert the old capabilities usermeta into user_role rows
+	// old usermeta rows are removed
+	
+	global $wpdb;
+	$prefix = $wpdb->escape($prefix);
+	$blog_id = intval($blog_id);
+	$users = $wpdb->get_results( "SELECT umeta_id, user_id, meta_value FROM {$wpdb->usermeta} WHERE meta_key = '{$prefix}capabilities' ORDER BY {$wpdb->usermeta}.user_id" );
 
+	foreach ($users as $user) {
+		// find any usermeta capabilites that are roles, and insert them as user_role rows
+		$caps = unserialize($user->meta_value);
+		foreach ( $caps as $cap => $active ) {
+			if ( is_role($cap) ) {
+				if ($active) {
+					$role = strtolower($cap);
+					$wpdb->insert( $wpdb->user_role, array('blog_id' => $blog_id, 'user_id' => $user->user_id, 'role' => $role) );
+				}
+				unset( $caps[$cap] );
+			}
+
+		}
+
+		// write any remaining caps as usermeta capabilities
+		$newcaps = maybe_serialize($caps);
+		$wpdb->update( $wpdb->usermeta, array('meta_value' => $newcaps), array('umeta_id' => $user->umeta_id) );
+		wp_cache_delete($user->user_id, 'users');
+	}
+}
+
 // The functions we use to actually do stuff
 
 // General
