=== modified file 'includes/common.inc' --- includes/common.inc 2008-05-16 01:23:31 +0000 +++ includes/common.inc 2008-05-24 21:00:09 +0000 @@ -1460,6 +1460,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(); === modified file 'includes/database.mysql-common.inc' --- includes/database.mysql-common.inc 2008-04-14 17:48:33 +0000 +++ includes/database.mysql-common.inc 2008-05-25 19:36:41 +0000 @@ -7,6 +7,11 @@ */ /** + * Indicates we are using MySQL. + */ +define('PRACTICAL_DATABASE', TRUE); + +/** * Runs a basic query in the active database. * * User-supplied arguments to the query should be passed in as separate === modified file 'includes/path.inc' --- includes/path.inc 2008-04-14 17:48:33 +0000 +++ includes/path.inc 2008-05-25 19:51:50 +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() { @@ -48,43 +63,105 @@ function drupal_lookup_path($action, $pa // $map is an array with language keys, holding arrays of Drupal paths to alias relations static $map = array(), $no_src = array(); + if (!module_exists('path')) { + return FALSE; + } + $path_language = $path_language ? $path_language : $language->language; + $strategy = variable_get('path_strategy', PATH_CACHE_NONE); + if ($strategy == PATH_CACHE_ALL) { + return drupal_lookup_path_cached($action, $path, $path_language); + } if ($action == 'wipe') { $map = array(); $no_src = array(); + return; } - elseif (module_exists('path') && $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; } === modified file 'modules/path/path.admin.inc' --- modules/path/path.admin.inc 2008-04-14 17:48:33 +0000 +++ modules/path/path.admin.inc 2008-05-25 19:36:04 +0000 @@ -239,3 +239,20 @@ function path_admin_filter_get_keys() { $path = explode('/', $_GET['q'], 5); return count($path) == 5 ? $path[4] : ''; } + +function path_admin_settings() { + $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 (defined('PRACTICAL_DATABASE')) { + $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 === added file 'modules/path/path.install' --- modules/path/path.install 1970-01-01 00:00:00 +0000 +++ modules/path/path.install 2008-05-25 19:55:52 +0000 @@ -0,0 +1,77 @@ + 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; + 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 === modified file 'modules/path/path.module' --- modules/path/path.module 2008-05-06 12:18:44 +0000 +++ modules/path/path.module 2008-05-25 19:51:24 +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; } @@ -213,3 +220,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}'); +}