commit 0ad4d8131398f3db9be32d7e9884b324c43565af
Author: Daniel Black <daniel.black@openquery.com.au>
Date:   Mon Mar 31 08:38:13 2014 +1100

    Issue #2229013 by danblack: Wrap _menu_router_save in a transaction so a) menu items still exist while committing b) the table still exists

diff --git a/includes/menu.inc b/includes/menu.inc
index fa5a71e..b237d6d 100644
--- a/includes/menu.inc
+++ b/includes/menu.inc
@@ -3759,6 +3759,7 @@ function _menu_router_build($callbacks) {
     }
   }
 
+
   // Sort the masks so they are in order of descending fit.
   $masks = array_keys($masks);
   rsort($masks);
@@ -3771,75 +3772,83 @@ function _menu_router_build($callbacks) {
  */
 function _menu_router_save($menu, $masks) {
   // Delete the existing router since we have some data to replace it.
-  db_truncate('menu_router')->execute();
-
-  // Prepare insert object.
-  $insert = db_insert('menu_router')
-    ->fields(array(
-      'path',
-      'load_functions',
-      'to_arg_functions',
-      'access_callback',
-      'access_arguments',
-      'page_callback',
-      'page_arguments',
-      'delivery_callback',
-      'fit',
-      'number_parts',
-      'context',
-      'tab_parent',
-      'tab_root',
-      'title',
-      'title_callback',
-      'title_arguments',
-      'theme_callback',
-      'theme_arguments',
-      'type',
-      'description',
-      'position',
-      'weight',
-      'include_file',
-    ));
-
-  $num_records = 0;
 
-  foreach ($menu as $path => $item) {
-    // Fill in insert object values.
-    $insert->values(array(
-      'path' => $item['path'],
-      'load_functions' => $item['load_functions'],
-      'to_arg_functions' => $item['to_arg_functions'],
-      'access_callback' => $item['access callback'],
-      'access_arguments' => serialize($item['access arguments']),
-      'page_callback' => $item['page callback'],
-      'page_arguments' => serialize($item['page arguments']),
-      'delivery_callback' => $item['delivery callback'],
-      'fit' => $item['_fit'],
-      'number_parts' => $item['_number_parts'],
-      'context' => $item['context'],
-      'tab_parent' => $item['tab_parent'],
-      'tab_root' => $item['tab_root'],
-      'title' => $item['title'],
-      'title_callback' => $item['title callback'],
-      'title_arguments' => ($item['title arguments'] ? serialize($item['title arguments']) : ''),
-      'theme_callback' => $item['theme callback'],
-      'theme_arguments' => serialize($item['theme arguments']),
-      'type' => $item['type'],
-      'description' => $item['description'],
-      'position' => $item['position'],
-      'weight' => $item['weight'],
-      'include_file' => $item['include file'],
-    ));
+  $txn = db_transaction();
+  try {
+    db_delete('menu_router')->execute();
 
-    // Execute in batches to avoid the memory overhead of all of those records
-    // in the query object.
-    if (++$num_records == 20) {
-      $insert->execute();
-      $num_records = 0;
+    // Prepare insert object.
+    $insert = db_insert('menu_router')
+      ->fields(array(
+        'path',
+        'load_functions',
+        'to_arg_functions',
+        'access_callback',
+        'access_arguments',
+        'page_callback',
+        'page_arguments',
+        'delivery_callback',
+        'fit',
+        'number_parts',
+        'context',
+        'tab_parent',
+        'tab_root',
+        'title',
+        'title_callback',
+        'title_arguments',
+        'theme_callback',
+        'theme_arguments',
+        'type',
+        'description',
+        'position',
+        'weight',
+        'include_file',
+      ));
+
+    $num_records = 0;
+
+    foreach ($menu as $path => $item) {
+      // Fill in insert object values.
+      $insert->values(array(
+        'path' => $item['path'],
+        'load_functions' => $item['load_functions'],
+        'to_arg_functions' => $item['to_arg_functions'],
+        'access_callback' => $item['access callback'],
+        'access_arguments' => serialize($item['access arguments']),
+        'page_callback' => $item['page callback'],
+        'page_arguments' => serialize($item['page arguments']),
+        'delivery_callback' => $item['delivery callback'],
+        'fit' => $item['_fit'],
+        'number_parts' => $item['_number_parts'],
+        'context' => $item['context'],
+        'tab_parent' => $item['tab_parent'],
+        'tab_root' => $item['tab_root'],
+        'title' => $item['title'],
+        'title_callback' => $item['title callback'],
+        'title_arguments' => ($item['title arguments'] ? serialize($item['title arguments']) : ''),
+        'theme_callback' => $item['theme callback'],
+        'theme_arguments' => serialize($item['theme arguments']),
+        'type' => $item['type'],
+        'description' => $item['description'],
+        'position' => $item['position'],
+        'weight' => $item['weight'],
+        'include_file' => $item['include file'],
+      ));
+
+      // Execute in batches to avoid the memory overhead of all of those records
+      // in the query object.
+      if (++$num_records == 20) {
+        $insert->execute();
+        $num_records = 0;
+      }
     }
+    // Insert any remaining records.
+    $insert->execute();
+  }
+  catch (Exception $e) {
+    $txn->rollback();
+    throw $e;
   }
-  // Insert any remaining records.
-  $insert->execute();
   // Store the masks.
   variable_set('menu_masks', $masks);
 
