Index: menu.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/menu.inc,v
retrieving revision 1.362
diff -u -r1.362 menu.inc
--- includes/menu.inc	11 Nov 2009 08:28:50 -0000	1.362
+++ includes/menu.inc	30 Nov 2009 13:50:33 -0000
@@ -3148,41 +3148,12 @@
  * Helper function to save data from menu_router_build() to the router table.
  */
 function _menu_router_save($menu, $masks) {
-  // Delete the existing router since we have some data to replace it.
-  db_delete('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',
-      'block_callback',
-      'description',
-      'position',
-      'weight',
-      'file',
-    ));
 
+  // determine new table contents
+  $rows_new = array();
   foreach ($menu as $path => $item) {
     // Fill in insert object values.
-    $insert->values(array(
+    $rows_new[$path] = array(
       'path' => $item['path'],
       'load_functions' => $item['load_functions'],
       'to_arg_functions' => $item['to_arg_functions'],
@@ -3209,14 +3180,80 @@
       'file' => $item['include file'],
     ));
   }
-  // Execute insert object.
-  $insert->execute();
+  
+  _menu_router_save_table_contents($rows_new, 'menu_router', 'path');
+  
   // Store the masks.
   variable_set('menu_masks', $masks);
 
   return $menu;
 }
 
+
+/**
+ * TODO:
+ * This can be added to the DB abstraction layer for reuse.
+ * Maybe the $primary_key param arg can be made more
+ * flexible to support multi-column primary keys.
+ */
+function _menu_router_save_table_contents($rows_new, $table_name, $primary_key) {
+  // compare with old table contents
+  $delete = array();
+  $update = array();
+  $insert = $rows_new;
+  $q = db_query("SELECT * FROM {$table_name}");
+  foreach ($q as $row) {
+    $row = (array)$row;
+    $primary = $row[$primary_key];
+    if (!isset($rows_new[$primary])) {
+      $delete[$primary] = true;
+    }
+    else {
+      $row_changes = array();
+      foreach ($row as $key => $value) {
+        if ($rows_new[$primary][$key] != $value) {
+          $row_changes[$key] = $rows_new[$primary][$key];
+        }
+      }
+      if (count($row_changes)) {
+        $update[$primary] = $row_changes;
+      }
+    }
+    unset($insert[$primary]);
+  }
+  
+  // execute
+  
+  // delete old rows
+  db_delete($table_name)
+    ->condition($primary_key, array_keys($delete), 'IN')
+    ->execute()
+  ;
+  
+  // update rows
+  foreach ($update as $primary => $row_changes) {
+    db_update($table_name)
+      ->fields($row_changes)
+      ->condition($primary_key, $primary)
+      ->execute()
+    ;
+  }
+  
+  // insert new rows
+  $db_insert = db_insert($table_name);
+  // prepare insert object: use keys of the first row.
+  foreach ($insert as $primary => $row) {
+    $db_insert->fields(array_keys($row));
+    break;
+  }
+  // insert values
+  foreach ($insert as $primary => $row) {
+    $db_insert->values(array_values($row));
+  }
+  $db_insert->execute();
+}
+
+
 /**
  * Checks whether the site is in maintenance mode.
  *

