Index: includes/bootstrap.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/bootstrap.inc,v
retrieving revision 1.461
diff -u -p -r1.461 bootstrap.inc
--- includes/bootstrap.inc	1 Jan 2011 22:46:02 -0000	1.461
+++ includes/bootstrap.inc	3 Jan 2011 03:53:50 -0000
@@ -2630,6 +2630,50 @@ function ip_address() {
  */
 
 /**
+ * Get the schema definition of a table.
+ *
+ * The returned schema will include any modifications made by any
+ * module that implements hook_schema_alter().
+ *
+ * @param $table
+ *   The name of the table.
+ * @param $rebuild
+ *   If true, the schema will be rebuilt instead of retrieved from the cache.
+ *
+ * @return
+ *   The processed schema array for this table, or FALSE.
+ */
+function drupal_get_table_schema($table, $rebuild = FALSE) {
+  // Use the advanced drupal_static() pattern, since this is called very often.
+  static $drupal_static_fast;
+  if (!isset($drupal_static_fast)) {
+    $drupal_static_fast['schema'] = &drupal_static(__FUNCTION__);
+  }
+  $schema = &$drupal_static_fast['schema'];
+
+  if (!$rebuild && isset($schema[$table])) {
+    return $schema[$table];
+  }
+  elseif (!$rebuild && $cached = cache_get("schema:$table")) {
+    $schema[$table] = $cached->data;
+  }
+  else {
+    $full_schema = drupal_get_schema(NULL, $rebuild);
+    if ($full_schema) {
+      $schema = $full_schema;
+    }
+    if (isset($full_schema[$table]) && drupal_get_bootstrap_phase() == DRUPAL_BOOTSTRAP_FULL) {
+        cache_set("schema:$table", $full_schema[$table]);
+    }
+    else {
+      // Store FALSE to not query whole schema when no table.
+      $schema[$table] = FALSE;
+    }
+  }
+  return $schema[$table];
+}
+
+/**
  * Get the schema definition of a table, or the whole database schema.
  *
  * The returned schema will include any modifications made by any
@@ -2639,16 +2683,19 @@ function ip_address() {
  *   The name of the table. If not given, the schema of all tables is returned.
  * @param $rebuild
  *   If true, the schema will be rebuilt instead of retrieved from the cache.
+ *
+ * @return
+ *   When $table is NULL, returns the full schema array, when $table is set
+ *   returns the schema for the table specified. Or FALSE if none is found.
  */
 function drupal_get_schema($table = NULL, $rebuild = FALSE) {
-  static $schema = array();
-
-  if (empty($schema) || $rebuild) {
-    // Try to load the schema from cache.
+  if (isset($table)) {
+    return drupal_get_table_schema($table, $rebuild);
+  }
+  else {
     if (!$rebuild && $cached = cache_get('schema')) {
       $schema = $cached->data;
     }
-    // Otherwise, rebuild the schema cache.
     else {
       $schema = array();
       // Load the .install files to get hook_schema.
@@ -2680,20 +2727,26 @@ function drupal_get_schema($table = NULL
       // If the schema is empty, avoid saving it: some database engines require
       // the schema to perform queries, and this could lead to infinite loops.
       if (!empty($schema) && (drupal_get_bootstrap_phase() == DRUPAL_BOOTSTRAP_FULL)) {
+        // To avoid caching stale data, set the full schema cache prior to
+        // clearing the cache of individual tables.
+        // @todo: the schema cache can be very large, so could exceed the
+        // maximum storage size for certain cache backends. Consider
+        // alternatives to storing the item as a single array.
         cache_set('schema', $schema);
+        // Cleanup per table schema cache.
+        cache_clear_all('schema:', 'cache', TRUE);
+        // Refresh the static cache in drupal_get_table_schema() with the
+        // updated schema.
+        $static = &drupal_static('drupal_get_table_schema');
+        $static = $schema;
+      }
+      elseif (empty($schema)) {
+        // If no schema is found, return FALSE.
+        $schema = FALSE;
       }
     }
-  }
-
-  if (!isset($table)) {
     return $schema;
   }
-  elseif (isset($schema[$table])) {
-    return $schema[$table];
-  }
-  else {
-    return FALSE;
-  }
 }
 
 /**
Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.1282
diff -u -p -r1.1282 common.inc
--- includes/common.inc	1 Jan 2011 22:35:28 -0000	1.1282
+++ includes/common.inc	3 Jan 2011 03:53:52 -0000
@@ -6635,7 +6635,7 @@ function _drupal_schema_initialize(&$sch
  * @return An array of fields.
  **/
 function drupal_schema_fields_sql($table, $prefix = NULL) {
-  $schema = drupal_get_schema($table);
+  $schema = drupal_get_table_schema($table);
   $fields = array_keys($schema['fields']);
   if ($prefix) {
     $columns = array();
@@ -6681,7 +6681,7 @@ function drupal_write_record($table, &$r
     $primary_keys = array($primary_keys);
   }
 
-  $schema = drupal_get_schema($table);
+  $schema = drupal_get_table_schema($table);
   if (empty($schema)) {
     return FALSE;
   }
Index: includes/entity.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/entity.inc,v
retrieving revision 1.19
diff -u -p -r1.19 entity.inc
--- includes/entity.inc	30 Nov 2010 19:31:46 -0000	1.19
+++ includes/entity.inc	3 Jan 2011 03:53:53 -0000
@@ -1067,7 +1067,7 @@ class EntityFieldQuery {
       throw new EntityFieldQueryException(t('Entity %entity has no base table.', array('%entity' => $entity_type)));
     }
     $base_table = $entity_info['base table'];
-    $base_table_schema = drupal_get_schema($base_table);
+    $base_table_schema = drupal_get_table_schema($base_table);
     $select_query = db_select($base_table);
     $select_query->addExpression(':entity_type', 'entity_type', array(':entity_type' => $entity_type));
     // Process the property conditions.
Index: includes/install.core.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/install.core.inc,v
retrieving revision 1.48
diff -u -p -r1.48 install.core.inc
--- includes/install.core.inc	28 Dec 2010 18:17:27 -0000	1.48
+++ includes/install.core.inc	3 Jan 2011 03:53:53 -0000
@@ -1428,16 +1428,6 @@ function install_configure_form($form, &
   // Build menu to allow clean URL check.
   menu_rebuild();
 
-  // Cache a fully-built schema. This is necessary for any invocation of
-  // index.php because: (1) setting cache table entries requires schema
-  // information, (2) that occurs during bootstrap before any module are
-  // loaded, so (3) if there is no cached schema, drupal_get_schema() will
-  // try to generate one but with no loaded modules will return nothing.
-  //
-  // This logically could be done during the 'install_finished' task, but the
-  // clean URL check requires it now.
-  drupal_get_schema(NULL, TRUE);
-
   // Return the form.
   return _install_configure_form($form, $form_state, $install_state);
 }
Index: includes/menu.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/menu.inc,v
retrieving revision 1.429
diff -u -p -r1.429 menu.inc
--- includes/menu.inc	17 Dec 2010 01:08:15 -0000	1.429
+++ includes/menu.inc	3 Jan 2011 03:53:54 -0000
@@ -2376,14 +2376,16 @@ function menu_link_get_preferred($path =
     $query = db_select('menu_links', 'ml', array('fetch' => PDO::FETCH_ASSOC));
     $query->leftJoin('menu_router', 'm', 'm.path = ml.router_path');
     $query->fields('ml');
-    // Weight must be taken from {menu_links}, not {menu_router}.
-    $query->fields('m', array_diff(drupal_schema_fields_sql('menu_router'), array('weight')));
+    $query->fields('m');
     $query->condition('ml.menu_name', $menu_names, 'IN');
     $query->condition('ml.link_path', $path_candidates, 'IN');
+    // Weight must be taken from {menu_links}, not {menu_router}.
+    $query->addField('ml', 'weight', 'link_weight');
 
     // Sort candidates by link path and menu name.
     $candidates = array();
     foreach ($query->execute() as $candidate) {
+      $candidate['weight'] = $candidate['link_weight'];
       $candidates[$candidate['link_path']][$candidate['menu_name']] = $candidate;
     }
 
@@ -2502,10 +2504,12 @@ function menu_link_load($mlid) {
     $query = db_select('menu_links', 'ml');
     $query->leftJoin('menu_router', 'm', 'm.path = ml.router_path');
     $query->fields('ml');
-    // Weight should be taken from {menu_links}, not {menu_router}.
-    $query->fields('m', array_diff(drupal_schema_fields_sql('menu_router'), array('weight')));
+    $query->fields('m');
     $query->condition('ml.mlid', $mlid);
+    // Weight should be taken from {menu_links}, not {menu_router}.
+    $query->addField('ml', 'weight', 'link_weight');
     if ($item = $query->execute()->fetchAssoc()) {
+      $item['weight'] = $item['link_weight'];
       _menu_link_translate($item);
       return $item;
     }
Index: includes/module.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/module.inc,v
retrieving revision 1.209
diff -u -p -r1.209 module.inc
--- includes/module.inc	27 Nov 2010 20:41:38 -0000	1.209
+++ includes/module.inc	3 Jan 2011 03:53:55 -0000
@@ -421,14 +421,14 @@ function module_enable($module_list, $en
       _system_update_bootstrap_status();
       // Update the registry to include it.
       registry_update();
-      // Refresh the schema to include it.
-      drupal_get_schema(NULL, TRUE);
       // Clear entity cache.
       entity_info_cache_clear();
 
       // Now install the module if necessary.
       if (drupal_get_installed_schema_version($module, TRUE) == SCHEMA_UNINSTALLED) {
         drupal_install_schema($module);
+        // Refresh the schema to include it.
+        drupal_get_schema(NULL, TRUE);
 
         // Set the schema version to the number of the last update provided
         // by the module.
Index: modules/field/modules/field_sql_storage/field_sql_storage.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/modules/field_sql_storage/field_sql_storage.module,v
retrieving revision 1.58
diff -u -p -r1.58 field_sql_storage.module
--- modules/field/modules/field_sql_storage/field_sql_storage.module	21 Dec 2010 04:11:12 -0000	1.58
+++ modules/field/modules/field_sql_storage/field_sql_storage.module	3 Jan 2011 03:53:55 -0000
@@ -216,6 +216,7 @@ function field_sql_storage_field_storage
   foreach ($schema as $name => $table) {
     db_create_table($name, $table);
   }
+  // Cleans whole schema cache.
   drupal_get_schema(NULL, TRUE);
 }
 
@@ -273,6 +274,7 @@ function field_sql_storage_field_storage
       }
     }
   }
+  // Cleans whole schema cache.
   drupal_get_schema(NULL, TRUE);
 }
 
@@ -294,6 +296,7 @@ function field_sql_storage_field_storage
   $revision_new_table = _field_sql_storage_revision_tablename($field);
   db_rename_table($table, $new_table);
   db_rename_table($revision_table, $revision_new_table);
+  // Cleans whole schema cache.
   drupal_get_schema(NULL, TRUE);
 }
 
Index: modules/profile/profile.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/profile/profile.admin.inc,v
retrieving revision 1.44
diff -u -p -r1.44 profile.admin.inc
--- modules/profile/profile.admin.inc	28 Oct 2010 02:27:09 -0000	1.44
+++ modules/profile/profile.admin.inc	3 Jan 2011 03:53:55 -0000
@@ -314,7 +314,7 @@ function profile_field_form_validate($fo
     form_set_error('name', t('The specified form name contains one or more illegal characters. Spaces or any other special characters except dash (-) and underscore (_) are not allowed.'));
   }
 
-  $users_table = drupal_get_schema('users');
+  $users_table = drupal_get_table_schema('users');
   if (!empty($users_table['fields'][$form_state['values']['name']])) {
     form_set_error('name', t('The specified form name is reserved for use by Drupal.'));
   }
Index: modules/simpletest/drupal_web_test_case.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/drupal_web_test_case.php,v
retrieving revision 1.257
diff -u -p -r1.257 drupal_web_test_case.php
--- modules/simpletest/drupal_web_test_case.php	2 Jan 2011 23:54:05 -0000	1.257
+++ modules/simpletest/drupal_web_test_case.php	3 Jan 2011 03:53:56 -0000
@@ -1403,11 +1403,6 @@ class DrupalWebTestCase extends DrupalTe
     // Reset the list of enabled modules.
     module_list(TRUE);
 
-    // Reset cached schema for new database prefix. This must be done before
-    // drupal_flush_all_caches() so rebuilds can make use of the schema of
-    // modules enabled on the cURL side.
-    drupal_get_schema(NULL, TRUE);
-
     // Perform rebuilds and flush remaining caches.
     drupal_flush_all_caches();
 
@@ -1455,7 +1450,7 @@ class DrupalWebTestCase extends DrupalTe
     file_unmanaged_delete_recursive($this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10));
 
     // Remove all prefixed tables (all the tables in the schema).
-    $schema = drupal_get_schema(NULL, TRUE);
+    $schema = drupal_get_schema(NULL);
     foreach ($schema as $name => $table) {
       db_drop_table($name);
     }
Index: modules/user/user.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.module,v
retrieving revision 1.1229
diff -u -p -r1.1229 user.module
--- modules/user/user.module	28 Dec 2010 21:46:23 -0000	1.1229
+++ modules/user/user.module	3 Jan 2011 03:53:57 -0000
@@ -404,7 +404,7 @@ function user_load_by_name($name) {
 function user_save($account, $edit = array(), $category = 'account') {
   $transaction = db_transaction();
   try {
-    $table = drupal_get_schema('users');
+    $table = drupal_get_table_schema('users');
 
     if (!empty($edit['pass'])) {
       // Allow alternate password hashing schemes.
@@ -1225,7 +1225,7 @@ function user_account_form_validate($for
       // Move text value for user signature into 'signature'.
       $form_state['values']['signature'] = $form_state['values']['signature']['value'];
 
-      $user_schema = drupal_get_schema('users');
+      $user_schema = drupal_get_table_schema('users');
       if (drupal_strlen($form_state['values']['signature']) > $user_schema['fields']['signature']['length']) {
         form_set_error('signature', t('The signature is too long: it must be %max characters or less.', array('%max' => $user_schema['fields']['signature']['length'])));
       }
