Index: includes/database.mysql.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/database.mysql.inc,v
retrieving revision 1.52
diff -u -p -r1.52 database.mysql.inc
--- includes/database.mysql.inc	27 Apr 2006 20:38:49 -0000	1.52
+++ includes/database.mysql.inc	29 Apr 2006 03:04:38 -0000
@@ -364,6 +364,58 @@ function db_unlock_tables() {
 }
 
 /**
+ * Constructs a query fragment to split a database field according to a
+ * delimiter, and to fetch the specified substring.
+ *
+ * @param $string
+ *   The database field to be split.
+ * @param $delimiter
+ *   The string value to use as a delimiter.
+ * @param $field
+ *   The number of the field to return. Must be in the range 1 ... x, where x
+ *   is the number of fields in the string.
+ * @return
+ *   An SQL query fragment, suitable for forming part of a string that gets
+ *   passed to db_query().
+ */
+function db_sql_explode($string, $delimiter, $field) {
+  $sql = "SUBSTRING_INDEX($string, '$delimiter', $field)";
+  if ($field > 1) {
+    $start_field = 1 - $field;
+    $sql = "SUBSTRING_INDEX($sql, '$delimiter', $start_field)";
+  }
+
+  return $sql;
+}
+
+/**
+ * Constructs a query fragment to match a database field against a regular
+ * expression pattern.
+ *
+ * @param $field
+ *   The database field on which to make the match.
+ * @param $pattern
+ *   The pattern to use for matching. The pattern should conform to the
+ *   regular expression syntax for both MySQL and PostgreSQL (this should not
+ *   be a problem if the pattern adheres to PHP's PCRE syntax, but check the
+ *   manuals for both database systems if in doubt).
+ * @param $check_match
+ *   Boolean indicating whether to check for a positive or a negative match.
+ *   Default is TRUE (i.e. positive match).
+ * @return
+ *   An SQL query fragment, suitable for forming part of a string that gets
+ *   passed to db_query().
+ */
+function db_sql_preg_match($field, $pattern, $check_match = TRUE) {
+  $negate = '';
+  if (!$check_match) {
+    $negate = ' NOT';
+  }
+
+  return $field . $negate . " RLIKE '$pattern'";
+}
+
+/**
  * @} End of "ingroup database".
  */
 
Index: includes/database.mysqli.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/database.mysqli.inc,v
retrieving revision 1.17
diff -u -p -r1.17 database.mysqli.inc
--- includes/database.mysqli.inc	27 Apr 2006 20:38:49 -0000	1.17
+++ includes/database.mysqli.inc	29 Apr 2006 03:04:38 -0000
@@ -366,6 +366,58 @@ function db_unlock_tables() {
 }
 
 /**
+ * Constructs a query fragment to split a database field according to a
+ * delimiter, and to fetch the specified substring.
+ *
+ * @param $string
+ *   The database field to be split.
+ * @param $delimiter
+ *   The string value to use as a delimiter.
+ * @param $field
+ *   The number of the field to return. Must be in the range 1 ... x, where x
+ *   is the number of fields in the string.
+ * @return
+ *   An SQL query fragment, suitable for forming part of a string that gets
+ *   passed to db_query().
+ */
+function db_sql_explode($string, $delimiter, $field) {
+  $sql = "SUBSTRING_INDEX($string, '$delimiter', $field)";
+  if ($field > 1) {
+    $start_field = 1 - $field;
+    $sql = "SUBSTRING_INDEX($sql, '$delimiter', $start_field)";
+  }
+
+  return $sql;
+}
+
+/**
+ * Constructs a query fragment to match a database field against a regular
+ * expression pattern.
+ *
+ * @param $field
+ *   The database field on which to make the match.
+ * @param $pattern
+ *   The pattern to use for matching. The pattern should conform to the
+ *   regular expression syntax for both MySQL and PostgreSQL (this should not
+ *   be a problem if the pattern adheres to PHP's PCRE syntax, but check the
+ *   manuals for both database systems if in doubt).
+ * @param $check_match
+ *   Boolean indicating whether to check for a positive or a negative match.
+ *   Default is TRUE (i.e. positive match).
+ * @return
+ *   An SQL query fragment, suitable for forming part of a string that gets
+ *   passed to db_query().
+ */
+function db_sql_preg_match($field, $pattern, $check_match = TRUE) {
+  $negate = '';
+  if (!$check_match) {
+    $negate = ' NOT';
+  }
+
+  return $field . $negate . " RLIKE '$pattern'";
+}
+
+/**
  * @} End of "ingroup database".
  */
 
Index: includes/database.pgsql.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/database.pgsql.inc,v
retrieving revision 1.29
diff -u -p -r1.29 database.pgsql.inc
--- includes/database.pgsql.inc	27 Apr 2006 20:38:49 -0000	1.29
+++ includes/database.pgsql.inc	29 Apr 2006 03:04:39 -0000
@@ -353,6 +353,52 @@ function db_check_setup() {
 }
 
 /**
+ * Constructs a query fragment to split a database field according to a
+ * delimiter, and to fetch the specified substring.
+ *
+ * @param $string
+ *   The database field to be split.
+ * @param $delimiter
+ *   The string value to use as a delimiter.
+ * @param $field
+ *   The number of the field to return. Must be in the range 1 ... x, where x
+ *   is the number of fields in the string.
+ * @return
+ *   An SQL query fragment, suitable for forming part of a string that gets
+ *   passed to db_query().
+ */
+function db_sql_explode($string, $delimiter, $field) {
+  return "SPLIT_PART($string, '$delimiter', $field)";
+}
+
+/**
+ * Constructs a query fragment to match a database field against a regular
+ * expression pattern.
+ *
+ * @param $field
+ *   The database field on which to make the match.
+ * @param $pattern
+ *   The pattern to use for matching. The pattern should conform to the
+ *   regular expression syntax for both MySQL and PostgreSQL (this should not
+ *   be a problem if the pattern adheres to PHP's PCRE syntax, but check the
+ *   manuals for both database systems if in doubt).
+ * @param $check_match
+ *   Boolean indicating whether to check for a positive or a negative match.
+ *   Default is TRUE (i.e. positive match).
+ * @return
+ *   An SQL query fragment, suitable for forming part of a string that gets
+ *   passed to db_query().
+ */
+function db_sql_preg_match($field, $pattern, $check_match = TRUE) {
+  $negate = ' ';
+  if (!$check_match) {
+    $negate .= '!';
+  }
+
+  return $field . $negate . "~ '$pattern'";
+}
+
+/**
  * @} End of "ingroup database".
  */
 
Index: modules/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node.module,v
retrieving revision 1.641
diff -u -p -r1.641 node.module
--- modules/node.module	27 Apr 2006 22:20:51 -0000	1.641
+++ modules/node.module	29 Apr 2006 03:04:41 -0000
@@ -2408,14 +2408,74 @@ function node_access_view_all_nodes() {
 }
 
 /**
+ * Generate an SQL join clause for use in fetching a menu item.
+ *
+ * @param $menu_alias
+ *   If the menu table has been given an SQL alias other than the default
+ *   "m", that must be passed here.
+ * @param $node_alias
+ *   If the node table has been given an SQL alias other than the default
+ *   "n", that must be passed here.
+ * @param $node_access_alias
+ *   If the node_access table has been given an SQL alias other than the default
+ *   "na", that must be passed here.
+ * @return
+ *   An SQL join clause.
+ */
+function _node_access_menu_join_sql($menu_alias = 'm', $node_alias = 'n', $node_access_alias = 'na') {
+  if (user_access('administer nodes')) {
+    return '';
+  }
+
+  $path_nid = db_sql_explode($menu_alias . '.path', '/', 2);
+
+  return 'LEFT JOIN {node} '. $node_alias .' ON '. $path_nid .' = '. $node_alias .'.nid LEFT JOIN {node_access} '. $node_access_alias .' ON '. $node_alias .'.nid = '. $node_access_alias .'.nid';
+}
+
+/**
+ * Generate an SQL where clause for use in fetching a node listing.
+ *
+ * @param $op
+ *   The operation that must be allowed to return a node.
+ * @param $menu_alias
+ *   If the menu table has been given an SQL alias other than the default
+ *   "m", that must be passed here.
+ * @param $node_alias
+ *   If the node table has been given an SQL alias other than the default
+ *   "n", that must be passed here.
+ * @param $node_access_alias
+ *   If the node_access table has been given an SQL alias other than the default
+ *   "na", that must be passed here.
+ * @return
+ *   An SQL where clause.
+ */
+function _node_access_menu_where_sql($op = 'view', $menu_alias = 'm', $node_alias = 'n', $node_access_alias = 'na', $uid = NULL) {
+  if (user_access('administer nodes')) {
+    return;
+  }
+
+  $not_node_path = db_sql_preg_match($menu_alias . '.path', '^node/[0-9]+$', FALSE);
+
+  return $node_alias .'.nid IS NULL OR '. $not_node_path .' OR ('. _node_access_where_sql($op, $node_access_alias, $uid) .')';
+}
+
+/**
  * Implementation of hook_db_rewrite_sql
  */
 function node_db_rewrite_sql($query, $primary_table, $primary_field) {
-  if ($primary_field == 'nid' && !node_access_view_all_nodes()) {
-    $return['join'] = _node_access_join_sql($primary_table);
-    $return['where'] = _node_access_where_sql();
-    $return['distinct'] = 1;
-    return $return;
+  if (!node_access_view_all_nodes()) {
+    if ($primary_field == 'nid') {
+      $return['join'] = _node_access_join_sql($primary_table);
+      $return['where'] = _node_access_where_sql();
+      $return['distinct'] = 1;
+      return $return;
+    }
+    else if ($primary_field == 'mid') {
+      $return['join'] = _node_access_menu_join_sql($primary_table);
+      $return['where'] = _node_access_menu_where_sql();
+      $return['distinct'] = 1;
+      return $return;
+    }
   }
 }
 
