diff -Naur includes/common.inc includes/common.inc
--- includes/common.inc	2007-10-17 17:28:59.000000000 -0400
+++ includes/common.inc	2007-11-05 10:40:46.119384767 -0500
@@ -1288,6 +1288,7 @@
   if (variable_get('cache', 0)) {
     page_set_cache();
   }
+  drupal_lookup_path('cache');
 
   module_invoke_all('exit');
 }
diff -Naur drupal-5.3.orig/includes/path.inc drupal-5.3/includes/path.inc
--- includes/path.inc	2006-12-23 17:04:52.000000000 -0500
+++ includes/path.inc	2007-11-05 10:40:46.119384767 -0500
@@ -32,6 +32,7 @@
  *   - wipe: delete the alias cache.
  *   - alias: return an alias for a given Drupal system path (if one exists).
  *   - source: return the Drupal system URL for a path alias (if one exists).
+ *   - cache: (internal use only) save the maps into the cache_path table.
  * @param $path
  *   The path to investigate for corresponding aliases or system URLs.
  *
@@ -41,44 +42,89 @@
  */
 function drupal_lookup_path($action, $path = '') {
   // $map keys are Drupal paths and the values are the corresponding aliases
-  static $map = array(), $no_src = array();
-  static $count;
+  static
+    $map, $no_src,
+    $map_dirty = FALSE, $no_src_dirty = FALSE,
+    $count, $expire = 0;
+  global $base_root;
 
   // Use $count to avoid looking up paths in subsequent calls if there simply are no aliases
   if (!isset($count)) {
-    $count = db_result(db_query('SELECT COUNT(pid) FROM {url_alias}'));
+    $count = db_result(db_query('SELECT COUNT(*) FROM {url_alias}'));
   }
-
-  if ($action == 'wipe') {
-    $map = array();
-    $no_src = array();
+  if ($count == 0) {
+    return FALSE;
   }
-  elseif ($count > 0 && $path != '') {
-    if ($action == 'alias') {
-      if (isset($map[$path])) {
-        return $map[$path];
+
+  if (!isset($map)) {
+    $cache = cache_get('map_'. $base_root . request_uri(), 'cache_path');
+    if ($cache) {
+      $map = unserialize($cache->data);
+      $expire = $cache->expire;
       }
-      $alias = db_result(db_query("SELECT dst FROM {url_alias} WHERE src = '%s'", $path));
-      $map[$path] = $alias;
-      return $alias;
+    else {
+      $map = array();
+    }
+
+    $cache = cache_get('no_src_'. $base_root . request_uri(), 'cache_path');
+    if ($cache) {
+      $no_src = unserialize($cache->data);
+      $expire = $cache->expire;
+    }
+    else {
+      $no_src = array();
     }
-    // Check $no_src for this $path in case we've already determined that there
-    // isn't a path that has this alias
-    elseif ($action == 'source' && !isset($no_src[$path])) {
-      // Look for the value $path within the cached $map
-      if (!$src = array_search($path, $map)) {
-        if ($src = db_result(db_query("SELECT src FROM {url_alias} WHERE dst = '%s'", $path))) {
-          $map[$src] = $path;
+  }
+  switch ($action) {
+    case 'alias':
+      if ($count > 0 && $path) {
+        if (isset($map[$path])) {
+          return $map[$path];
+          }
+        if (!$alias = db_result(db_query("SELECT dst FROM {url_alias} WHERE src = '%s'", $path))) {
+          $alias = FALSE;
+          }
+        $map[$path] = $alias;
+        $map_dirty = TRUE;
+        return $alias;
         }
-        else {
-          // We can't record anything into $map because we do not have a valid
-          // index and there is no need because we have not learned anything
-          // about any Drupal path. Thus cache to $no_src.
-          $no_src[$path] = TRUE;
+      break;
+    case 'source':
+      // Check $no_src for this $path in case we've already determined that there
+      // isn't a path that has this alias
+      if ($count > 0 && $path && !isset($no_src[$path])) {
+        if (!$src = array_search($path, $map)) {
+          if ($src = db_result(db_query("SELECT src FROM {url_alias} WHERE dst = '%s'", $path))) {
+            $map[$src] = $path;
+            $map_dirty = TRUE;
+          }
+          else {
+            // We can't record anything into $map because we do not have a valid
+            // index and there is no need because we have not learned anything
+            // about any Drupal path. Thus cache to $no_src.
+            $no_src[$path] = TRUE;
+            $no_src_dirty = TRUE;
+            return FALSE;
+          }
         }
+        return $src;
       }
-      return $src;
-    }
+      break;
+    case 'wipe':
+      $map = array();
+      $no_src = array();
+      break;
+    case 'cache':
+      if (!$expire) {
+        $expire = time() + (60 * 60 * 24);
+      }
+      if ($map_dirty) {
+        cache_set('map_'. $base_root . request_uri(), 'cache_path', serialize($map), $expire);
+      }
+      if ($no_src_dirty) {
+        cache_set('no_src_' . $base_root . request_uri(), 'cache_path', serialize($no_src), $expire);
+      }
+      break;
   }
 
   return FALSE;
diff -Naur modules/block/block.module modules/block/block.module
--- modules/block/block.module	2007-09-12 03:49:35.000000000 -0400
+++ modules/block/block.module	2007-11-05 10:40:27.118301766 -0500
@@ -286,6 +286,7 @@
   }
   drupal_set_message(t('The block settings have been updated.'));
   cache_clear_all();
+  cache_clear_all('*', 'cache_advcache_block', TRUE);
 }
 
 /**
@@ -500,6 +501,7 @@
     module_invoke($form_values['module'], 'block', 'save', $form_values['delta'], $form_values);
     drupal_set_message(t('The block configuration has been saved.'));
     cache_clear_all();
+    cache_clear_all('*', 'cache_advcache_block', TRUE);
     return 'admin/build/block';
   }
 }
@@ -538,6 +540,7 @@
   db_query("DELETE FROM {blocks} WHERE module = 'block' AND delta = %d", $form_values['bid']);
   drupal_set_message(t('The block %name has been removed.', array('%name' => $form_values['info'])));
   cache_clear_all();
+  cache_clear_all('*', 'cache_advcache_block', TRUE);
   return 'admin/build/block';
 };
 
@@ -643,26 +646,39 @@
   static $blocks = array();
 
   if (!count($blocks)) {
-    $rids = array_keys($user->roles);
-    $placeholders = implode(',', array_fill(0, count($rids), '%d'));
-    $result = db_query("SELECT DISTINCT b.* FROM {blocks} b LEFT JOIN {blocks_roles} r ON b.module = r.module AND b.delta = r.delta WHERE b.theme = '%s' AND b.status = 1 AND (r.rid IN ($placeholders) OR r.rid IS NULL) ORDER BY b.region, b.weight, b.module", array_merge(array($theme_key), $rids));
-    while ($block = db_fetch_object($result)) {
-      if (!isset($blocks[$block->region])) {
-        $blocks[$block->region] = array();
-      }
-      // Use the user's block visibility setting, if necessary
-      if ($block->custom != 0) {
-        if ($user->uid && isset($user->block[$block->module][$block->delta])) {
-          $enabled = $user->block[$block->module][$block->delta];
+    $cache_key = $user->uid. '::'. $theme_key;
+    $cache = cache_get($cache_key, 'cache_advcache_block');
+    $enabled_blocks = unserialize($cache->data);
+    if (empty($enabled_blocks)) {
+      $enabled_blocks = array();
+      $rids = array_keys($user->roles);
+      $placeholders = implode(',', array_fill(0, count($rids), '%d'));
+      $result = db_query("SELECT DISTINCT b.* FROM {blocks} b LEFT JOIN {blocks_roles} r ON b.module = r.module AND b.delta = r.delta WHERE b.theme = '%s' AND b.status = 1 AND (r.rid IN ($placeholders) OR r.rid IS NULL) ORDER BY b.region, b.weight, b.module", array_merge(array($theme_key), $rids));
+      while ($block = db_fetch_object($result)) {
+        if (!isset($blocks[$block->region])) {
+          $blocks[$block->region] = array();
+        }
+        // Use the user's block visibility setting, if necessary
+        if ($block->custom != 0) {
+          if ($user->uid && isset($user->block[$block->module][$block->delta])) {
+            $enabled = $user->block[$block->module][$block->delta];
+          }
+          else {
+            $enabled = ($block->custom == 1);
+          }
+
         }
         else {
-          $enabled = ($block->custom == 1);
+          $enabled = TRUE;
+        }
+        if ($enabled) {
+          $enabled_blocks[] = $block;
         }
       }
-      else {
-        $enabled = TRUE;
-      }
+      cache_set($cache_key, 'cache_advcache_block', serialize($enabled_blocks));
+    }
 
+    foreach ($enabled_blocks as $block) {
       // Match path if necessary
       if ($block->pages) {
         if ($block->visibility < 2) {
@@ -686,7 +702,7 @@
         $page_match = TRUE;
       }
 
-      if ($enabled && $page_match) {
+      if ($page_match) {
         // Check the current throttle status and see if block should be displayed
         // based on server load.
         if (!($block->throttle && (module_invoke('throttle', 'status') > 0))) {
diff -Naur modules/comment/comment.module modules/comment/comment.module
--- modules/comment/comment.module	2007-10-17 17:29:00.000000000 -0400
+++ modules/comment/comment.module	2007-11-05 10:40:31.618558157 -0500
@@ -931,6 +931,16 @@
 function comment_render($node, $cid = 0) {
   global $user;
 
+  // If this is an authenticated user who only has one role and cannot admin-
+  // ister comments, look for a cached copy of the comment, or in the case
+  // of $cid = 0, the whole tree of comments.
+  $cache_key = 0;
+  if (!user_access('administer comments') && count($user->roles) === 1 && in_array('authenticated user', $user->roles)) {
+    // Must accommodate pagination
+    $page = isset($_GET['page']) ? $_GET['page'] : '';
+    $cache_key = 'nid-'. $node->nid. '::cid-'. $cid. '::'. $page;
+  }
+
   $output = '';
 
   if (user_access('access comments')) {
@@ -945,6 +955,12 @@
     $comments_per_page = _comment_get_display_setting('comments_per_page');
 
     if ($cid) {
+      if ($cache_key) {
+        $cache = cache_get($cache_key, 'cache_comment');
+        $comment = unserialize($cache->data);
+      }
+
+      if (!$comment) {
       // Single comment view.
       $query = 'SELECT c.cid, c.pid, c.nid, c.subject, c.comment, c.format, c.timestamp, c.name, c.mail, c.homepage, u.uid, u.name AS registered_name, u.picture, u.data, c.score, c.users, c.status FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d';
       $query_args = array($cid);
@@ -963,11 +979,26 @@
           $function = $module .'_link_alter';
           $function($node, $links);
         }
+        }
+      }
 
+      if ($comment) {
         $output .= theme('comment_view', $comment, $links);
       }
     }
     else {
+      if ($cache_key) {
+        if ($cache = cache_get($cache_key, 'cache_comment')) {
+          $comments = unserialize($cache->data);
+          $comment_count = count($comments);
+
+          // Get the pager
+          $cache = cache_get($cache_key. '::pager', 'cache_comment');
+          $pager = $cache->data;
+        }
+      }
+
+      if (empty($comments)) {
       // Multiple comment view
       $query_count = 'SELECT COUNT(*) FROM {comments} WHERE nid = %d';
       $query = 'SELECT c.cid as cid, c.pid, c.nid, c.subject, c.comment, c.format, c.timestamp, c.name, c.mail, c.homepage, u.uid, u.name AS registered_name, u.picture, u.data, c.score, c.users, c.thread, c.status FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.nid = %d';
@@ -1002,21 +1033,32 @@
           $query .= ' ORDER BY SUBSTRING(c.thread, 1, (LENGTH(c.thread) - 1))';
         }
       }
-
       // Start a form, for use with comment control.
       $result = pager_query($query, $comments_per_page, 0, $query_count, $query_args);
-      if (db_num_rows($result) && (variable_get('comment_controls', COMMENT_CONTROLS_HIDDEN) == COMMENT_CONTROLS_ABOVE || variable_get('comment_controls', COMMENT_CONTROLS_HIDDEN) == COMMENT_CONTROLS_ABOVE_BELOW)) {
+        $comment_count = db_num_rows($result);
+      }
+
+
+      if ($comment_count && (variable_get('comment_controls', COMMENT_CONTROLS_HIDDEN) == COMMENT_CONTROLS_ABOVE || variable_get('comment_controls', COMMENT_CONTROLS_HIDDEN) == COMMENT_CONTROLS_ABOVE_BELOW)) {
         $output .= drupal_get_form('comment_controls', $mode, $order, $comments_per_page);
       }
 
       $divs = 0;
       $last_depth = 0;
       drupal_add_css(drupal_get_path('module', 'comment') .'/comment.css');
+
+      if (empty($comments)) {
+        $comments = array();
       while ($comment = db_fetch_object($result)) {
         $comment = drupal_unpack($comment);
         $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
         $comment->depth = count(explode('.', $comment->thread)) - 1;
+          $comments[] = $comment;
+        }
+        cache_set($cache_key, 'cache_comment', serialize($comments));
+      }
 
+      foreach ($comments as $comment) {
         if ($mode == COMMENT_MODE_THREADED_COLLAPSED || $mode == COMMENT_MODE_THREADED_EXPANDED) {
           if ($comment->depth > $last_depth) {
             $divs++;
@@ -1048,9 +1090,15 @@
       for ($i = 0; $i < $divs; $i++) {
         $output .= '</div>';
       }
-      $output .= theme('pager', NULL, $comments_per_page, 0);
 
-      if (db_num_rows($result) && (variable_get('comment_controls', COMMENT_CONTROLS_HIDDEN) == COMMENT_CONTROLS_BELOW || variable_get('comment_controls', COMMENT_CONTROLS_HIDDEN) == COMMENT_CONTROLS_ABOVE_BELOW)) {
+      if (empty($pager)) {
+        $pager = theme('pager', NULL, $comments_per_page, 0);
+        cache_set($cache_key. '::pager', 'cache_comment', $pager);
+      }
+
+      $output .= $pager;
+
+      if ($comment_count && (variable_get('comment_controls', COMMENT_CONTROLS_HIDDEN) == COMMENT_CONTROLS_BELOW || variable_get('comment_controls', COMMENT_CONTROLS_HIDDEN) == COMMENT_CONTROLS_ABOVE_BELOW)) {
         $output .= drupal_get_form('comment_controls', $mode, $order, $comments_per_page);
       }
     }
@@ -2016,4 +2064,3 @@
 function vancode2int($c = '00') {
   return base_convert(substr($c, 1), 36, 10);
 }
-
diff -Naur modules/forum/forum.module modules/forum/forum.module
--- modules/forum/forum.module	2007-08-08 02:27:22.000000000 -0400
+++ modules/forum/forum.module	2007-11-05 10:40:35.618786244 -0500
@@ -709,6 +709,17 @@
  */
 function forum_get_forums($tid = 0) {
 
+  // This caching breaks forum access! The results of db_rewrite_sql will
+  // be cached, meaning the first user to load this node after a cache
+  // refresh will set the permissions for everyone. If you are using a module
+  // that does query rewriting on forum queries, don't use this patch.
+  // If you're not sure whether or not this is the case, don't use this patch!
+  $cache = cache_get('forums::'. $tid, 'cache_forum');
+  if ($cache) {
+    $forums = unserialize($cache->data);
+    return $forums;
+  }
+
   $forums = array();
   $_forums = taxonomy_get_tree(variable_get('forum_nav_vocabulary', ''), $tid);
 
@@ -754,6 +765,8 @@
     $forums[$forum->tid] = $forum;
   }
 
+  cache_set('forums::'. $tid, 'cache_forum', serialize($forums));
+
   return $forums;
 }
 
diff -Naur modules/node/node.module modules/node/node.module
--- modules/node/node.module	2007-09-29 19:41:28.000000000 -0400
+++ modules/node/node.module	2007-11-05 10:40:42.119156678 -0500
@@ -501,17 +501,31 @@
  *   A fully-populated node object.
  */
 function node_load($param = array(), $revision = NULL, $reset = NULL) {
+  global $user;
   static $nodes = array();
 
   if ($reset) {
     $nodes = array();
   }
 
-  $cachable = ($revision == NULL);
+  $cache_id = 0;
   $arguments = array();
   if (is_numeric($param)) {
-    if ($cachable && isset($nodes[$param])) {
-      return is_object($nodes[$param]) ? drupal_clone($nodes[$param]) : $nodes[$param];
+    if (module_exists('advcache')) {
+      $cache_id = ($revision == NULL) ? $param. '::'. advcache_array2int($user->roles) : 0;
+    }
+    if ($cache_id > 0) {
+      if (isset($nodes[$param])) {
+        return is_object($nodes[$param]) ? drupal_clone($nodes[$param]) : $nodes[$param];
+      }
+      if ($user->uid != 1) {
+        $cache = cache_get($cache_id, 'cache_node');
+        if ($cache) {
+          $cache = unserialize($cache->data);
+          $nodes[$param] = is_object($cache) ? drupal_clone($cache) : $cache;
+          return $nodes[$param];
+        }
+      }
     }
     $cond = 'n.nid = %d';
     $arguments[] = $param;
@@ -549,8 +563,13 @@
         $node->$key = $value;
       }
     }
-    if ($cachable) {
+    if ($cache_id > 0) {
       $nodes[$node->nid] = is_object($node) ? drupal_clone($node) : $node;
+      if ($user->uid != 1) {
+        if (!in_array($node->type, variable_get('advcache_node_exclude_types', array('poll')))) {
+          cache_set($cache_id, 'cache_node', serialize($nodes[$node->nid]));
+        }
+      }
     }
   }
 
@@ -1973,8 +1992,8 @@
   if (isset($node->body) && count(explode(' ', $node->body)) < $type->min_word_count) {
     form_set_error('body', t('The body of your @type is too short. You need at least %words words.', array('%words' => $type->min_word_count, '@type' => $type->name)));
   }
-
-  if (isset($node->nid) && (node_last_changed($node->nid) > $node->changed)) {
+  $changed = node_last_changed($node->nid);
+  if (isset($node->nid) && ($changed > $node->changed)) {
     form_set_error('changed', t('This content has been modified by another user, changes cannot be saved.'));
   }
 
@@ -2463,7 +2482,7 @@
 /**
  * Menu callback; view a single node.
  */
-function node_page_view($node, $cid = NULL) {
+function node_page_view($node, $cid = 0) {
   drupal_set_title(check_plain($node->title));
   return node_show($node, $cid);
 }
diff -Naur modules/path/path.module modules/path/path.module
--- modules/path/path.module	2007-05-20 20:52:28.000000000 -0400
+++ modules/path/path.module	2007-11-05 10:40:46.119384767 -0500
@@ -130,11 +130,9 @@
 function path_set_alias($path = NULL, $alias = NULL, $pid = NULL) {
   if ($path && !$alias) {
     db_query("DELETE FROM {url_alias} WHERE src = '%s'", $path);
-    drupal_clear_path_cache();
   }
   else if (!$path && $alias) {
     db_query("DELETE FROM {url_alias} WHERE dst = '%s'", $alias);
-    drupal_clear_path_cache();
   }
   else if ($path && $alias) {
     $path = urldecode($path);
@@ -145,7 +143,6 @@
     // We have an insert:
     if ($path_count == 0 && $alias_count == 0) {
       db_query("INSERT INTO {url_alias} (src, dst) VALUES ('%s', '%s')", $path, $alias);
-      drupal_clear_path_cache();
     }
     else if ($path_count >= 1 && $alias_count == 0) {
       if ($pid) {
@@ -154,11 +151,9 @@
       else {
         db_query("INSERT INTO {url_alias} (src, dst) VALUES ('%s', '%s')", $path, $alias);
       }
-      drupal_clear_path_cache();
     }
     else if ($path_count == 0 && $alias_count == 1) {
       db_query("UPDATE {url_alias} SET src = '%s' WHERE dst = '%s'", $path, $alias);
-      drupal_clear_path_cache();
     }
     else if ($path_count == 1 && $alias_count == 1) {
       // This will delete the path that alias was originally pointing to:
@@ -167,6 +162,10 @@
       path_set_alias($path, $alias);
     }
   }
+
+  // Clear the static and the database cache.
+  drupal_clear_path_cache();
+  cache_clear_all(NULL, 'cache_path');
 }
 
 /**
diff -Naur modules/search/search.module modules/search/search.module
--- modules/search/search.module	2007-07-26 15:16:48.000000000 -0400
+++ modules/search/search.module	2007-11-05 10:40:49.619583995 -0500
@@ -272,6 +272,8 @@
     // When re-indexing, keep link references
     db_query("DELETE FROM {search_index} WHERE sid = %d AND type = '%s'". ($reindex ? " AND fromsid = 0" : ''), $sid, $type);
   }
+  // Cached search results may no longer be valid.
+  cache_clear_all(NULL, 'cache_search');
 }
 
 /**
@@ -304,6 +306,34 @@
   foreach (module_list() as $module) {
     module_invoke($module, 'update_index');
   }
+
+  // Don't allow search result caching if any node access modules such as organic
+  // groups have placed viewing restrictions on any nodes. Even if you have had
+  // organic groups enabled and have since disabled it, search caching will
+  // be prevented. If you find yourself in this position, you can restore the
+  // original un-restricted state of node_access like this:
+  // TRUNCATE node_access;
+  // INSERT INTO node_access VALUES (0,0,'all',1,0,0);
+  $r1 = db_result(db_query("SELECT count(*) FROM {node_access}"));
+  $r2 = db_result(db_query("SELECT count(*) FROM {node_access} WHERE grant_view = 1"));
+  if (($r1 == 1) && ($r2 == 1)) {
+    // Recalculate the list of popular search terms
+    $tops = array();
+    $result = db_query("SELECT location, count(*) AS count FROM {watchdog} WHERE type = 'search' GROUP BY location HAVING count(*) > 2");
+    while ($row = db_fetch_object($result)) {
+      $tops[] = strtolower($row->location);
+    }
+    // Since we just potentially indexed new or updated content, clear the cache.
+    cache_clear_all(NULL, 'cache_search');
+
+    // Set the search_cache_queries to the new top queries.
+    cache_set('search_cache_queries', 'cache_search', serialize($tops));
+  }
+  else {
+    // No guarantee that the cache complies with node access restrictions, so
+    // wipe it.
+    cache_clear_all(NULL, 'cache_search');
+  }
 }
 
 /**
@@ -890,6 +920,7 @@
  * Menu callback; presents the search form and/or search results.
  */
 function search_view() {
+  global $base_root, $user;
   $type = arg(1);
 
   // Search form submits with POST but redirects to GET. This way we can keep
@@ -909,8 +940,29 @@
       // Log the search keys:
       watchdog('search', t('%keys (@type).', array('%keys' => $keys, '@type' => module_invoke($type, 'search', 'name'))), WATCHDOG_NOTICE, l(t('results'), 'search/'. $type .'/'. $keys));
 
-      // Collect the search results:
-      $results = search_data($keys, $type);
+      // Look to the cache to see if this is a popular (thus cached) query
+      $request_uri = strtolower($base_root . request_uri());
+      $results = NULL;
+      // Only cache results if the user is not an admin, is authenticated, has
+      // exactly one role, and the query is in the popular search queries array.
+      if ($cachable = ($user->uid > 1 && count($user->roles) === 1 && in_array('authenticated user', $user->roles))) {
+        $queries_cache = cache_get('search_cache_queries', 'cache_search');
+      }
+      if ($cachable && $queries_cache) {
+        $search_cache_queries = unserialize($queries_cache->data);
+        if (in_array($request_uri, $search_cache_queries)) {
+          if ($search_cache = cache_get($request_uri, 'cache_search')) {
+            $results = $search_cache->data;
+          }
+        }
+      }
+      if (empty($results)) {
+        // Collect the search results:
+        $results = search_data($keys, $type);
+        if ($cachable) {
+          cache_set($request_uri, 'cache_search', $results);
+        }
+      }
 
       if ($results) {
         $results = theme('box', t('Search results'), $results);
diff -Naur modules/taxonomy/taxonomy.module modules/taxonomy/taxonomy.module
--- modules/taxonomy/taxonomy.module	2007-09-12 03:49:36.000000000 -0400
+++ modules/taxonomy/taxonomy.module	2007-11-05 10:40:55.619926127 -0500
@@ -779,12 +779,25 @@
   static $terms;
 
   if (!isset($terms[$nid][$key])) {
-    $result = db_query(db_rewrite_sql('SELECT t.* FROM {term_node} r INNER JOIN {term_data} t ON r.tid = t.tid INNER JOIN {vocabulary} v ON t.vid = v.vid WHERE r.nid = %d ORDER BY v.weight, t.weight, t.name', 't', 'tid'), $nid);
-    $terms[$nid][$key] = array();
-    while ($term = db_fetch_object($result)) {
-      $terms[$nid][$key][$term->$key] = $term;
+    // This caching breaks taxonomy access! The results of db_rewrite_sql will
+    // be cached, meaning the first user to load this node after a cache
+    // refresh will set the permissions for everyone. If you are using a module
+    // that does query rewriting on taxonomy queries, don't use this patch.
+    // If you're not sure whether or not this is the case, don't use this patch!
+    $cache_key = 'node::'. $nid. '::'. $key;
+    if ($cache = cache_get($cache_key, 'cache_taxonomy')) {
+      $terms[$nid][$key] = unserialize($cache->data);
+    }
+    else {
+      $result = db_query(db_rewrite_sql('SELECT t.* FROM {term_node} r INNER JOIN {term_data} t ON r.tid = t.tid INNER JOIN {vocabulary} v ON t.vid = v.vid WHERE r.nid = %d ORDER BY v.weight, t.weight, t.name', 't', 'tid'), $nid);
+      $terms[$nid][$key] = array();
+      while ($term = db_fetch_object($result)) {
+        $terms[$nid][$key][$term->$key] = $term;
+      }
+      cache_set($cache_key, 'cache_taxonomy', serialize($terms[$nid][$key]));
     }
   }
+
   return $terms[$nid][$key];
 }
 
@@ -988,7 +1001,14 @@
  */
 function taxonomy_get_tree($vid, $parent = 0, $depth = -1, $max_depth = NULL) {
   static $children, $parents, $terms;
-
+  if ((0 === $parent) && (-1 === $depth)) {
+    if ($cache = cache_get('tree::'. $vid, 'cache_taxonomy')) {
+      return unserialize($cache->data);
+    }
+    else {
+      $cache_tree = TRUE;
+    }
+  }
   $depth++;
 
   // We cache trees, so it's not CPU-intensive to call get_tree() on a term
@@ -1022,6 +1042,10 @@
     }
   }
 
+  if ($cache_tree) {
+    cache_set('tree::'. $vid, 'cache_taxonomy', serialize($tree));
+  }
+
   return $tree ? $tree : array();
 }
 
@@ -1144,13 +1168,19 @@
   static $vocabularies = array();
 
   if (!array_key_exists($vid, $vocabularies)) {
-    $result = db_query('SELECT v.*, n.type FROM {vocabulary} v LEFT JOIN {vocabulary_node_types} n ON v.vid = n.vid WHERE v.vid = %d ORDER BY v.weight, v.name', $vid);
-    $node_types = array();
-    while ($voc = db_fetch_object($result)) {
-      $node_types[] = $voc->type;
-      unset($voc->type);
-      $voc->nodes = $node_types;
-      $vocabularies[$vid] = $voc;
+    if ($cache = cache_get('vocabulary::'. $vid, 'cache_taxonomy')) {
+      $vocabularies[$vid] = unserialize($cache->data);
+    }
+    else {
+      $result = db_query('SELECT v.*, n.type FROM {vocabulary} v LEFT JOIN {vocabulary_node_types} n ON v.vid = n.vid WHERE v.vid = %d ORDER BY v.weight, v.name', $vid);
+      $node_types = array();
+      while ($voc = db_fetch_object($result)) {
+        $node_types[] = $voc->type;
+        unset($voc->type);
+        $voc->nodes = $node_types;
+        $vocabularies[$vid] = $voc;
+        cache_set('vocabulary::'. $vid, 'cache_taxonomy', serialize($voc));
+      }
     }
   }
 
@@ -1170,7 +1200,13 @@
   static $terms = array();
 
   if (!isset($terms[$tid])) {
-    $terms[$tid] = db_fetch_object(db_query('SELECT * FROM {term_data} WHERE tid = %d', $tid));
+    if ($cache = cache_get('term::'. $tid, 'cache_taxonomy')) {
+      $terms[$tid] = unserialize($cache->data);
+    }
+    else {
+      $terms[$tid] = db_fetch_object(db_query('SELECT * FROM {term_data} WHERE tid = %d', $tid));
+      cache_set('term::'. $tid, 'cache_taxonomy', serialize($terms[$tid]));
+    }
   }
 
   return $terms[$tid];
@@ -1299,8 +1335,8 @@
 function taxonomy_nodeapi($node, $op, $arg = 0) {
   switch ($op) {
     case 'load':
-     $output['taxonomy'] = taxonomy_node_get_terms($node->nid);
-     return $output;
+      $output['taxonomy'] = taxonomy_node_get_terms($node->nid);
+      return $output;
     case 'insert':
       taxonomy_node_save($node->nid, $node->taxonomy);
       break;
