Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.776
diff -u -p -r1.776 common.inc
--- includes/common.inc	2 Jul 2008 19:36:52 -0000	1.776
+++ includes/common.inc	11 Jul 2008 07:33:31 -0000
@@ -1475,6 +1475,7 @@ function l($text, $path, $options = arra
  * react to the closing of the page by calling hook_exit().
  */
 function drupal_page_footer() {
+  drupal_lookup_path('footer');
 
   if (variable_get('cache', CACHE_DISABLED) != CACHE_DISABLED) {
     page_set_cache();
Index: includes/path.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/path.inc,v
retrieving revision 1.24
diff -u -p -r1.24 path.inc
--- includes/path.inc	24 Jun 2008 22:12:15 -0000	1.24
+++ includes/path.inc	11 Jul 2008 07:33:31 -0000
@@ -11,6 +11,21 @@
  */
 
 /**
+ * Do not cache anything.
+ */
+define('PATH_CACHE_NONE', 0);
+
+/**
+ * Cache everything.
+ */
+define('PATH_CACHE_ALL', 1);
+
+/**
+ * Cache the aliases on page that seem unchanging.
+ */
+define('PATH_CACHE_ADAPTIVE', 2);
+
+/**
  * Initialize the $_GET['q'] variable to the proper normal path.
  */
 function drupal_init_path() {
@@ -49,50 +64,105 @@ function drupal_lookup_path($action, $pa
   static $map = array(), $no_src = array(), $count;
 
   $path_language = $path_language ? $path_language : $language->language;
+  $strategy = variable_get('path_strategy', PATH_CACHE_NONE);
 
-  // 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}'));
+  if ($strategy == PATH_CACHE_ALL) {
+    return drupal_lookup_path_cached($action, $path, $path_language);
   }
-
   if ($action == 'wipe') {
     $map = array();
     $no_src = array();
+    return;
   }
-  elseif ($count > 0 && $path != '') {
-    if ($action == 'alias') {
-      if (isset($map[$path_language][$path])) {
-        return $map[$path_language][$path];
+  if ($action == 'footer' && $strategy == PATH_CACHE_ADAPTIVE) {
+    foreach ($map as $path_language => $entry) {
+      foreach ($entry as $src => $dst) {
+        // The new database layer will make this PostgreSQL and whatever
+        // compatible. Let's focus on functionality for now.
+        db_query("INSERT DELAYED INTO {path_alias_statistics_map} (page_path, language, src, dst) VALUES ('%s', '%s', '%s', '%s')", $_GET['q'], $path_language, $src, $dst);
       }
-      // Get the most fitting result falling back with alias without language
-      $alias = db_result(db_query("SELECT dst FROM {url_alias} WHERE src = '%s' AND language IN('%s', '') ORDER BY language DESC", $path, $path_language));
-      $map[$path_language][$path] = $alias;
-      return $alias;
     }
-    // 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_language][$path])) {
-      // Look for the value $path within the cached $map
-      $src = '';
-      if (!isset($map[$path_language]) || !($src = array_search($path, $map[$path_language]))) {
-        // Get the most fitting result falling back with alias without language
-        if ($src = db_result(db_query("SELECT src FROM {url_alias} WHERE dst = '%s' AND language IN('%s', '') ORDER BY language DESC", $path, $path_language))) {
-          $map[$path_language][$src] = $path;
-        }
-        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_language][$path] = TRUE;
+  }
+  elseif ($path != '') {
+    if (!isset($map[$path_language]) && $strategy == PATH_CACHE_ADAPTIVE) {
+      $cache = cache_get($_GET['q'] .':'. $path_language, 'cache_path');
+      if ($cache) {
+        $map[$path_language] = $cache->data;
+      }
+      else {
+        $map[$path_language] = array();
+        $result = db_query("SELECT src, dst FROM {path_alias_map} WHERE page_path = '%s' AND language = '%s'", $_GET['q'], $path_language);
+        while ($pair = db_fetch_array($result)) {
+          $map[$path_language][$pair['src']] = $pair['dst'];
         }
+        cache_set($_GET['q'] .':'. $path_language, $map[$path_language], 'cache_path');
       }
-      return $src;
+    }
+    switch ($action) {
+      case 'alias':
+        if (isset($map[$path_language][$path])) {
+          return $map[$path_language][$path];
+        }
+        // Get the most fitting result falling back with alias without language
+        $alias = db_result(db_query("SELECT dst FROM {url_alias} WHERE src = '%s' AND language IN ('%s', '') ORDER BY language DESC", $path, $path_language));       
+        $map[$path_language][$path] = $alias;
+        return $alias;
+      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 (!isset($no_src[$path_language][$path])) {
+          // Look for the value $path within the cached $map
+          $src = '';
+          if (!isset($map[$path_language]) || !($src = array_search($path, $map[$path_language]))) {
+            // Get the most fitting result falling back with alias without language
+            if ($src = db_result(db_query("SELECT src FROM {url_alias} WHERE dst = '%s' AND language IN ('%s', '') ORDER BY language DESC", $path, $path_language))) {
+              $map[$path_language][$src] = $path;
+            }
+            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_language][$path] = TRUE;
+            }
+          }
+          return $src;
+        }
+        break;
     }
   }
+  return FALSE;
+}
 
+function drupal_lookup_path_cached($action, $path, $path_language) {
+  static $map = array();
+  if ($action == 'wipe') {
+    $map = array();
+    return FALSE;
+  }
+  if (empty($map)) {
+    $cache = cache_get('path', 'cache_path');
+    if ($cache) {
+      $map = $cache->data;
+    }
+    else {
+      $result = db_query("SELECT src, dst, language FROM {url_alias}");
+      while ($url_alias = db_fetch_object($result)) {
+        $map[$url_alias->language][$url_alias->src] = $url_alias->dst;
+      }
+      cache_set('path', $map, 'cache_path');
+    }
+  }
+  if ($action == 'alias' && isset($map[$path_language][$path])) {
+    return $map[$path_language][$path];
+  }
+  if ($action == 'source' && ($src = array_search($path, $map[$path_language]))) {
+    return $src;
+  }
   return FALSE;
 }
 
+        
+
 /**
  * Given an internal Drupal path, return the alias set by the administrator.
  *
Index: modules/path/path.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/path/path.module,v
retrieving revision 1.144
diff -u -p -r1.144 path.module
--- modules/path/path.module	21 Jun 2008 18:22:38 -0000	1.144
+++ modules/path/path.module	11 Jul 2008 07:33:32 -0000
@@ -63,6 +63,13 @@ function path_menu() {
     'access arguments' => array('administer url aliases'),
     'type' => MENU_LOCAL_TASK,
   );
+  $items['admin/build/path/settings'] = array(
+    'title' => 'URL alias settings',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => 'path_admin_settings',
+    'access arguments' => array('administer url aliases'),
+    'type' => MENU_LOCAL_TASK,
+  );
 
   return $items;
 }
@@ -215,3 +222,10 @@ function path_perm() {
 function path_load($pid) {
   return db_fetch_array(db_query('SELECT * FROM {url_alias} WHERE pid = %d', $pid));
 }
+
+function path_cron() {
+  db_query_temporary('SELECT page_path, language, %f * MAX(c) AS cutoff FROM (SELECT page_path, language COUNT(*) AS c FROM path_alias_statistics_map GROUP BY page_path, language, src, dst) AS x GROUP BY page_path, language', variable_get('path_cutoff', .6), 'page_cutoff');
+  db_query('DELETE FROM {path_alias_map}');
+  db_query('INSERT INTO {path_alias_map} (page_path, language, src, dst, counter) SELECT page_path, language, src, dst, COUNT(*) FROM {path_alias_statistics_map} p GROUP BY page_path, language, path, alias HAVING COUNT(*) > (SELECT cutoff FROM page_cutoff pc WHERE pc.page_path = p.page_path AND pc.language = p.language)');
+  db_query('DELETE FROM {path_alias_statistics_map}');
+}
Index: modules/path/path.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/path/path.test,v
retrieving revision 1.2
diff -u -p -r1.2 path.test
--- modules/path/path.test	25 Apr 2008 18:26:02 -0000	1.2
+++ modules/path/path.test	11 Jul 2008 07:33:32 -0000
@@ -21,9 +21,36 @@ class PathTestCase extends DrupalWebTest
   }
 
   /**
-   * Test alias functionality through the admin interfaces.
+   * Test alias functionality through the admin interfaces for PATH_CACHE_NONE.
    */
-  function testAdminAlias() {
+  function testAdminAliasCacheNone() {
+    variable_set('path_strategy', PATH_CACHE_NONE);
+    $this->checkAdminAlias('Path Cache None');
+  }
+  
+  /**
+   * Test alias functionality through the admin interfaces for PATH_CACHE_ALL.
+   */
+  function testAdminAliasCacheAll() {
+    variable_set('path_strategy', PATH_CACHE_ALL);
+    $this->checkAdminAlias('Path Cache All');
+  }
+  
+  /**
+   * Test alias functionality through the admin interfaces for PATH_CACHE_ADAPTIVE.
+   */
+  function testAdminAliasCacheAdaptive() {
+    global $db_type;
+    if (strpos($db_type, 'mysql') !== FALSE) {
+      variable_set('path_strategy', PATH_CACHE_ADAPTIVE);
+      $this->checkAdminAlias('Path Cache Adaptive');
+    }
+  }
+  
+  /**
+   * Test alias functionality through the admin interfaces for a path caching type.
+   */
+  function checkAdminAlias($type) {
     // create test node
     $node1 = $this->createNode();
 
@@ -35,7 +62,7 @@ class PathTestCase extends DrupalWebTest
 
     // Confirm that the alias works.
     $this->drupalGet($edit['dst']);
-    $this->assertText($node1->title, 'Alias works.');
+    $this->assertText($node1->title, t('@type: Alias works.', array('@type' => $type)));
 
     // Change alias.
     $pid = $this->getPID($edit['dst']);
@@ -46,11 +73,11 @@ class PathTestCase extends DrupalWebTest
 
     // Confirm that the alias works.
     $this->drupalGet($edit['dst']);
-    $this->assertText($node1->title, 'Changed alias works.');
+    $this->assertText($node1->title, t('@type: Changed alias works.', array('@type' => $type)));
 
     // Confirm that previous alias no longer works.
     $this->drupalGet($previous);
-    $this->assertNoText($node1->title, 'Previous alias no longer works.');
+    $this->assertNoText($node1->title, t('@type: Previous alias no longer works.', array('@type' => $type)));
     $this->assertResponse(404);
 
     // Create second test node.
@@ -69,13 +96,40 @@ class PathTestCase extends DrupalWebTest
 
     // Confirm that the alias no longer works.
     $this->drupalGet($edit['dst']);
-    $this->assertNoText($node1->title, 'Alias was successfully deleted.');
+    $this->assertNoText($node1->title, t('@type: Alias was successfully deleted.', array('@type' => $type)));
+  }
+  
+  /**
+   *  Test alias functionality through the node interfaces for PATH_CACHE_NONE.
+   */
+  function testNodeAliasCacheNone() {
+    variable_set('path_strategy', PATH_CACHE_NONE);
+    $this->checkNodeAlias('Path Cache None');
+  }
+  
+  /**
+   *  Test alias functionality through the node interfaces for PATH_CACHE_ALL.
+   */
+  function testNodeAliasCacheAll() {
+    variable_set('path_strategy', PATH_CACHE_ALL);
+    $this->checkNodeAlias('Path Cache All');
+  }
+  
+  /**
+   *  Test alias functionality through the node interfaces for PATH_CACHE_ADAPTIVE.
+   */
+  function testNodeAliasCacheAdaptive() {
+    global $db_type;
+    if (strpos($db_type, 'mysql') !== FALSE) {
+      variable_set('path_strategy', PATH_CACHE_ADAPTIVE);
+      $this->checkNodeAlias('Path Cache Adaptive');
+    }
   }
 
   /**
    * Test alias functionality through the node interfaces.
    */
-  function testNodeAlias() {
+  function checkNodeAlias($type) {
     // Create test node.
     $node1 = $this->createNode();
 
@@ -86,7 +140,7 @@ class PathTestCase extends DrupalWebTest
 
     // Confirm that the alias works.
     $this->drupalGet($edit['path']);
-    $this->assertText($node1->title, 'Alias works.');
+    $this->assertText($node1->title, t('@type: Alias works.', array('@type' => $type)));
 
     // Change alias.
     $previous = $edit['path'];
@@ -95,11 +149,11 @@ class PathTestCase extends DrupalWebTest
 
     // Confirm that the alias works.
     $this->drupalGet($edit['path']);
-    $this->assertText($node1->title, 'Changed alias works.');
+    $this->assertText($node1->title, t('@type: Changed alias works.', array('@type' => $type)));
 
     // Make sure that previous alias no longer works.
     $this->drupalGet($previous);
-    $this->assertNoText($node1->title, 'Previous alias no longer works.');
+    $this->assertNoText($node1->title, t('@type: Previous alias no longer works.', array('@type' => $type)));
     $this->assertResponse(404);
 
     // Create second test node.
@@ -110,14 +164,14 @@ class PathTestCase extends DrupalWebTest
     $this->drupalPost('node/' . $node2->nid . '/edit', $edit, t('Save'));
 
     // Confirm that the alias didn't make a duplicate.
-    $this->assertText(t('The path is already in use.'), 'Attempt to moved alias was rejected.');
+    $this->assertText(t('The path is already in use.'), t('@type: Attempt to moved alias was rejected.', array('@type' => $type)));
 
     // Delete alias.
     $this->drupalPost('node/' . $node1->nid . '/edit', array('path' => ''), t('Save'));
 
     // Confirm that the alias no longer works.
     $this->drupalGet($edit['path']);
-    $this->assertNoText($node1->title, 'Alias was successfully deleted.');
+    $this->assertNoText($node1->title, t('@type: Alias was successfully deleted.', array('@type' => $type)));
   }
 
   function getPID($dst) {
Index: modules/path/path.install
===================================================================
RCS file: modules/path/path.install
diff -N modules/path/path.install
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ modules/path/path.install	11 Jul 2008 07:33:32 -0000
@@ -0,0 +1,128 @@
+<?php
+
+function path_install() {
+  // Create tables.
+  drupal_install_schema('path');
+}
+
+function path_schema() {
+  $table = array(
+    'fields' => array(
+      'page_path' => array(
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => FALSE,
+        'description' => t('Internal path to page visited (relative to Drupal root.)'),
+      ),
+      'src' => array(
+        'description' => t('The Drupal path this alias is for; e.g. node/12.'),
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'dst' => array(
+        'description' => t('The alias for this path; e.g. title-of-the-story.'),
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'language' => array(
+        'description' => t('The language this alias is for; if blank, the alias will be used for unknown languages. Each Drupal path can have an alias for each supported language.'),
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+    )
+  );
+  
+  $schema['path_alias_map'] = $table;
+  $schema['path_alias_statistics_map'] = $table;
+  
+  $schema['cache_path'] = array(
+    'description' => t('Generic cache table for caching things not separated out into their own tables. Contributed modules may also use this to store cached items.'),
+    'fields' => array(
+      'cid' => array(
+        'description' => t('Primary Key: Unique cache ID.'),
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'data' => array(
+        'description' => t('A collection of data to cache.'),
+        'type' => 'blob',
+        'not null' => FALSE,
+        'size' => 'big',
+      ),
+      'expire' => array(
+        'description' => t('A Unix timestamp indicating when the cache entry should expire, or 0 for never.'),
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'created' => array(
+        'description' => t('A Unix timestamp indicating when the cache entry was created.'),
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'headers' => array(
+        'description' => t('Any custom HTTP headers to be added to cached data.'),
+        'type' => 'text',
+        'not null' => FALSE,
+      ),
+      'serialized' => array(
+        'description' => t('A flag to indicate whether content is serialized (1) or not (0).'),
+        'type' => 'int',
+        'size' => 'small',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+    'indexes' => array(
+      'expire' => array('expire'),
+    ),
+    'primary key' => array('cid'),
+  );
+  
+  return $schema;
+}
+
+function path_update_7001() {
+  $table = array(
+    'page_path' => array(
+      'type' => 'varchar',
+      'length' => 255,
+      'not null' => FALSE,
+      'description' => t('Internal path to page visited (relative to Drupal root.)'),
+    ),
+    'src' => array(
+      'description' => t('The Drupal path this alias is for; e.g. node/12.'),
+      'type' => 'varchar',
+      'length' => 128,
+      'not null' => TRUE,
+      'default' => '',
+    ),
+    'dst' => array(
+      'description' => t('The alias for this path; e.g. title-of-the-story.'),
+      'type' => 'varchar',
+      'length' => 128,
+      'not null' => TRUE,
+      'default' => '',
+    ),
+    'language' => array(
+      'description' => t('The language this alias is for; if blank, the alias will be used for unknown languages. Each Drupal path can have an alias for each supported language.'),
+      'type' => 'varchar',
+      'length' => 12,
+      'not null' => TRUE,
+      'default' => '',
+    ),
+  );
+  $ret = array();
+  db_create_table($ret, 'path_alias_map', $table);
+  db_create_table($ret, 'path_alias_statistics_map', $table);
+  return $ret;
+}
\ No newline at end of file
Index: modules/path/path.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/path/path.admin.inc,v
retrieving revision 1.8
diff -u -p -r1.8 path.admin.inc
--- modules/path/path.admin.inc	14 Apr 2008 17:48:38 -0000	1.8
+++ modules/path/path.admin.inc	11 Jul 2008 07:33:32 -0000
@@ -239,3 +239,22 @@ function path_admin_filter_get_keys() {
   $path = explode('/', $_GET['q'], 5);
   return count($path) == 5 ? $path[4] : '';
 }
+
+function path_admin_settings() {
+  global $db_type;
+  
+  $options = array(
+    PATH_CACHE_NONE => t('Do not cache anything'),
+    PATH_CACHE_ALL => t('Cache all aliases (not recommended for a big number of aliases)'),
+  );
+  if (strpos($db_type, 'mysql') !== FALSE) {
+    $options[PATH_CACHE_ADAPTIVE] = t('Cache the aliases on page that seem unchanging.');
+  }
+  $form['path_strategy'] = array(
+    '#type' => 'radios',
+    '#title' => 'Caching',
+    '#options' => $options,
+    '#default_value' => variable_get('path_strategy', PATH_CACHE_NONE),
+  );
+  return system_settings_form($form);
+}
\ No newline at end of file
