Early Bird Registration for DrupalCon Portland 2024 is open! Register by 23:59 PST on 31 March 2024, to get $100 off your ticket.
menu_execute_active_handler() is smart enough to rebuild the menus if the menu_mask variable isn't already set due to some race condition.
// Rebuild if we know it's needed, or if the menu masks are missing which
// occurs rarely, likely due to a race condition of multiple rebuilds.
if (variable_get('menu_rebuild_needed', FALSE) || !variable_get('menu_masks', array())) {
menu_rebuild();
}
However, earlier in the page generation process we might have already died from the call chain
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
=> _drupal_bootstrap_full();
=> menu_set_custom_theme();
=> menu_get_custom_theme(TRUE);
=> menu_get_item();
This throws errors like
PDOException: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ')) ORDER BY fit DESC LIMIT 0, 1' at line 2: SELECT menu_router.* FROM {menu_router} menu_router WHERE (path IN ()) ORDER BY fit DESC LIMIT 0, 1; Array ( ) in menu_get_item() (line 404 of C:\wamp\www\drupal7\includes\menu.inc).
(as seen in #619542: Malformed theme .info files break menu_router generation from a different cause).
Comment | File | Size | Author |
---|---|---|---|
#5 | repro1026474.zip | 768 bytes | quartsize |
#2 | 1026474-menu-rebuild-2-D7.patch | 1.46 KB | quartsize |
Comments
Comment #1
bfroehle CreditAttribution: bfroehle commentedThe solution is likely to be adding something like
in the appropriate place which may be immediately before the
menu_get_item()
call inmenu_get_custom_theme()
, or perhaps inmenu_get_ancestors()
(which is the only function that actually uses the 'menu_masks' variable).Comment #2
quartsize CreditAttribution: quartsize commentedIssues for historical perspective: #261148: menu_masks variable is empty (race condition) and #202955: Access denied after install - menu_rebuild() calls.
My inclination is toward menu_get_ancestors(), since this issue has has the potential to manifest itself in any situation where menu_get_ancestors() is called, either directly, as it is a public function, or via menu_get_item(), and the menu needs (re)built (see also #1069922: Boost installation fails when using installation profile to install). Moreover, as witnessed by menu_get_item(), callers do not seem to expect an empty array as a possible return value from menu_get_ancestors() (menu_get_item() will generate an invalid SQL query). The trouble with doing that is that menu_get_ancestors() may be called indirectly by menu_rebuild() itself, causing an infinite loop, and if stale data is okay in some situations, it's a waste of cycles.
I have a patch which tries to avoid the former by checking for the lock. I don't like it because I really want to know if menu_rebuild() is higher in the call stack, not if it's running in another thread, but in the interest of helping this issue along, I'm attaching it.
Comment #4
sunLacking clear steps to reproduce this issue, this is a task.
Comment #5
quartsize CreditAttribution: quartsize commentedsites/all/modules
.dependencies[] = repro1026474
toprofiles/minimal/minimal.info
.when Drupal goes to enable
repro1026474
.The Minimal profile is used because the Standard includes
menu
as dependency, the first action of which when enabling is to callmenu_rebuild()
. The fundamental issue is that it is possible formenu_get_item()
to be called before themenu_masks
are built, butmenu_get_item()
assumes, viamenu_get_ancestors()
, that the masks are not in need of building any time it is called.Comment #6
sunTo safe others from the hazzle of downloading and extracting, here's the actual and only code of that module:
Comment #7
catchThis is a duplicate of #898634: install_drupal(): call to install_display_output() results in SQL syntax error.