Patch #106559 by kbahey, Damien Tournoud, catch: skip looking up certain paths in drupal_lookup_path()

From: damz <damz@damz-dev.local>


---
 database/sqlite/database.inc |   15 ++++++++++++++
 database/sqlite/schema.inc   |   11 ++++++++---
 path.inc                     |   44 +++++++++++++++++++++++++++++++++++-------
 system/system.install        |   24 +++++++++++++++++++++++
 4 files changed, 84 insertions(+), 10 deletions(-)

diff --git includes/database/sqlite/database.inc includes/database/sqlite/database.inc
index 6d8166d..ebbe01e 100644
--- includes/database/sqlite/database.inc
+++ includes/database/sqlite/database.inc
@@ -39,6 +39,7 @@ class DatabaseConnection_sqlite extends DatabaseConnection {
     $this->sqliteCreateFunction('length', 'strlen', 1);
     $this->sqliteCreateFunction('concat', array($this, 'sqlFunctionConcat'));
     $this->sqliteCreateFunction('substring', array($this, 'sqlFunctionSubstring'), 3);
+    $this->sqliteCreateFunction('substring_index', array($this, 'sqlFunctionSubstringIndex'), 3);
     $this->sqliteCreateFunction('rand', array($this, 'sqlFunctionRand'));
   }
 
@@ -83,6 +84,20 @@ class DatabaseConnection_sqlite extends DatabaseConnection {
   }
 
   /**
+   * SQLite compatibility implementation for the SUBSTRING_INDEX() SQL function.
+   */
+  public function sqlFunctionSubstringIndex($string, $delimiter, $count) {
+    $end = 0;
+    for ($i = 0; $i < $count; $i++) {
+      $end = strpos($string, $delimiter, $end + 1);
+      if ($end === FALSE) {
+        $end = strlen($string);
+      }
+    }
+    return substr($string, 0, $end);
+  }
+
+  /**
    * SQLite compatibility implementation for the RAND() SQL function.
    */
   public function sqlFunctionRand($seed = NULL) {
diff --git includes/database/sqlite/schema.inc includes/database/sqlite/schema.inc
index 3a4bac0..565a9cc 100644
--- includes/database/sqlite/schema.inc
+++ includes/database/sqlite/schema.inc
@@ -430,7 +430,10 @@ class DatabaseSchema_sqlite extends DatabaseSchema {
    */
   public function addIndex(&$ret, $table, $name, $fields) {
     $schema['indexes'][$name] = $fields;
-    $ret[] = update_sql($this->createIndexSql($table, $schema));
+    $statements = $this->createIndexSql($table, $schema);
+    foreach ($statements as $statement) {
+      $ret[] = update_sql($statement);
+    }
   }
 
   /**
@@ -461,8 +464,10 @@ class DatabaseSchema_sqlite extends DatabaseSchema {
    */
   public function addUniqueKey(&$ret, $table, $name, $fields) {
     $schema['unique keys'][$name] = $fields;
-    $ret[] = update_sql($this->createIndexSql($table, $schema));
-
+    $statements = $this->createIndexSql($table, $schema);
+    foreach ($statements as $statement) {
+      $ret[] = update_sql($statement);
+    }
   }
 
   /**
diff --git includes/path.inc includes/path.inc
index 017100e..96f3f69 100644
--- includes/path.inc
+++ includes/path.inc
@@ -48,31 +48,36 @@ function drupal_lookup_path($action, $path = '', $path_language = '') {
   // $map is an array with language keys, holding arrays of Drupal paths to alias relations
   $map = &drupal_static(__FUNCTION__, array());
   $no_src = &drupal_static(__FUNCTION__ . ':no_src', array());
-  $count = &drupal_static(__FUNCTION__ . ':count');
+  $whitelist = &drupal_static(__FUNCTION__ . ':whitelist');
   $system_paths = &drupal_static(__FUNCTION__ . ':system_paths');
   $no_aliases = &drupal_static(__FUNCTION__ . ':no_alias', array());
   $first_call = &drupal_static(__FUNCTION__ . ':first_call', TRUE);
 
-  $path_language = $path_language ? $path_language : $language->language;
-
-  // Use $count to avoid looking up paths in subsequent calls if there simply are no aliases
-  if (!isset($count)) {
-    $count = db_query('SELECT COUNT(pid) FROM {url_alias}')->fetchField();
+  // Retrieve the path alias whitelist.
+  if (!isset($whitelist)) {
+    if (!$whitelist = variable_get('path_alias_whitelist', NULL)) {
+      $whitelist = drupal_path_alias_whitelist_rebuild();
+    }
   }
 
+  $path_language = $path_language ? $path_language : $language->language;
+
   if ($action == 'wipe') {
     $map = array();
     $no_src = array();
     $count = NULL;
     $system_paths = array();
     $no_aliases = array();
+
+    $whitelist = drupal_path_alias_whitelist_rebuild();
   }
-  elseif ($count > 0 && $path != '') {
+  elseif ($whitelist && $path != '') {
     if ($action == 'alias') {
       // During the first call to drupal_lookup_path() per language, load the
       // expected system paths for the page from cache.
       if ($first_call) {
         $first_call = FALSE;
+
         $map[$path_language] = array();
         // Load system paths from cache.
         $cid = current_path();
@@ -93,6 +98,12 @@ function drupal_lookup_path($action, $path = '', $path_language = '') {
       if (isset($map[$path_language][$path])) {
         return $map[$path_language][$path];
       }
+      // Check the path whitelist, if the top_level part before the first /
+      // is not in the list, then there is no need to do anything further,
+      // it is not in the database.
+      elseif (!isset($whitelist[strtok($path, '/')])) {
+        return FALSE;
+      }
       // For system paths which were not cached, query aliases individually.
       else if (!isset($no_aliases[$path_language][$path])) {
         // Get the most fitting result falling back with alias without language
@@ -347,3 +358,22 @@ function drupal_match_path($path, $patterns) {
 function current_path() {
   return $_GET['q'];
 }
+
+/**
+ * Rebuild the path alias white list.
+ *
+ * @return
+ *   An array containing a white list of path aliases.
+ */
+function drupal_path_alias_whitelist_rebuild() {
+  // For each alias in the database, get the top level component of the system
+  // path it corresponds to. This is the portion of the path before the first /
+  // if present, otherwise the whole path itself.
+  $whitelist = array();
+  $result = db_query("SELECT SUBSTRING_INDEX(src, '/', 1) AS path FROM {url_alias} GROUP BY path");
+  foreach ($result as $row) {
+    $whitelist[$row->path] = TRUE;
+  }
+  variable_set('path_alias_whitelist', $whitelist);
+  return $whitelist;
+}
diff --git modules/system/system.install modules/system/system.install
index f0505aa..b290cab 100644
--- modules/system/system.install
+++ modules/system/system.install
@@ -339,6 +339,11 @@ function system_install() {
       \'SELECT CASE WHEN $1 THEN $2 ELSE $3 END;\'
       LANGUAGE \'sql\''
     );
+
+    db_query('CREATE OR REPLACE FUNCTION "substr_index"(text, text, integer) RETURNS text AS
+      \'SELECT array_to_string((string_to_array($1, $2)) [1:$3], $2);\'
+      LANGUAGE \'sql\''
+    );
   }
 
   // Create tables.
@@ -3496,6 +3501,25 @@ function system_update_7023() {
 }
 
 /**
+ * Add the substr_index() function to PostgreSQL.
+ *
+ * Note: this should go into the driver itself, but we have no support
+ * for driver-specific updates yet.
+ */
+function system_update_7024() {
+  $ret = array();
+
+  if (db_driver() == 'pgsql') {
+    $ret[] = update_sql('CREATE OR REPLACE FUNCTION "substr_index"(text, text, integer) RETURNS text AS
+      \'SELECT array_to_string((string_to_array($1, $2)) [1:$3], $2);\'
+      LANGUAGE \'sql\''
+    );
+  }
+
+  return $ret;
+}
+
+/**
  * @} End of "defgroup updates-6.x-to-7.x"
  * The next series of updates should start at 8000.
  */
