? drupal-512962-D6.patch Index: includes/menu.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/menu.inc,v retrieving revision 1.255.2.32 diff -u -p -r1.255.2.32 menu.inc --- includes/menu.inc 6 Oct 2009 11:59:05 -0000 1.255.2.32 +++ includes/menu.inc 22 Jan 2010 00:01:17 -0000 @@ -579,7 +579,7 @@ function _menu_translate(&$router_item, $router_item['href'] = implode('/', $link_map); $router_item['options'] = array(); _menu_check_access($router_item, $map); - + // For performance, don't localize an item the user can't access. if ($router_item['access']) { _menu_item_localize($router_item, $map); @@ -1880,7 +1880,7 @@ function _menu_delete_item($item, $force * - plid The mlid of the parent. * - router_path The path of the relevant router item. * @return - * The mlid of the saved menu link, or FALSE if the menu link could not be + * The mlid of the saved menu link, or FALSE if the menu link could not be * saved. */ function menu_link_save(&$item) { @@ -2342,8 +2342,33 @@ function _menu_router_build($callbacks) watchdog('php', 'Menu router rebuild failed - some paths may not work correctly.', array(), WATCHDOG_ERROR); return array(); } - // Delete the existing router since we have some data to replace it. - db_query('DELETE FROM {menu_router}'); + + // Prep for multi-insert + $data = array(); + $chunks = 0; + $loop_counter = 0; + if (stristr($db_type, 'pgsql')) { + // Set Max Packet size to 16MB if using postgreSQL. + $max_packet = 16777216; + } + else { + // Get maximum packet size for mysql + $max_packet = db_fetch_array(db_query("SHOW VARIABLES WHERE Variable_name = 'max_allowed_packet'")); + // default to 1/2 MB + $max_packet = (int)$max_packet['Value'] > 524288 ? (int)$max_packet['Value'] : 524288; + + // Get bulk insert buffer size + $insert_buffer_size = db_fetch_array(db_query("SHOW VARIABLES WHERE Variable_name = 'bulk_insert_buffer_size'")); + // default to 1/2 MB + $insert_buffer_size = (int)$insert_buffer_size['Value'] > 524288 ? (int)$insert_buffer_size['Value'] : 524288; + + // Set max + $max_packet = $max_packet > $insert_buffer_size ? $insert_buffer_size : $max_packet; + } + // Assume 50 Kb is max size of each insert + $max_insert = 51200; + $chunk_size = 0; + // Apply inheritance rules. foreach ($menu as $path => $v) { $item = &$menu[$path]; @@ -2425,22 +2450,72 @@ function _menu_router_build($callbacks) } $title_arguments = $item['title arguments'] ? serialize($item['title arguments']) : ''; - db_query("INSERT INTO {menu_router} - (path, load_functions, to_arg_functions, access_callback, - access_arguments, page_callback, page_arguments, fit, - number_parts, tab_parent, tab_root, - title, title_callback, title_arguments, - type, block_callback, description, position, weight, file) - VALUES ('%s', '%s', '%s', '%s', - '%s', '%s', '%s', %d, - %d, '%s', '%s', - '%s', '%s', '%s', - %d, '%s', '%s', '%s', %d, '%s')", - $path, $item['load_functions'], $item['to_arg_functions'], $item['access callback'], - serialize($item['access arguments']), $item['page callback'], serialize($item['page arguments']), $item['_fit'], - $item['_number_parts'], $item['tab_parent'], $item['tab_root'], - $item['title'], $item['title callback'], $title_arguments, - $item['type'], $item['block callback'], $item['description'], $item['position'], $item['weight'], $item['include file']); + $access_arguments = serialize($item['access arguments']); + $page_arguments = serialize($item['page arguments']); + + $data[$chunks][] = $path; + $data[$chunks][] = $item['load_functions']; + $data[$chunks][] = $item['to_arg_functions']; + $data[$chunks][] = $item['access callback']; + $data[$chunks][] = $access_arguments; + $data[$chunks][] = $item['page callback']; + $data[$chunks][] = $page_arguments; + $data[$chunks][] = $item['_fit']; + $data[$chunks][] = $item['_number_parts']; + $data[$chunks][] = $item['tab_parent']; + $data[$chunks][] = $item['tab_root']; + $data[$chunks][] = $item['title']; + $data[$chunks][] = $item['title callback']; + $data[$chunks][] = $title_arguments; + $data[$chunks][] = $item['type']; + $data[$chunks][] = $item['block callback']; + $data[$chunks][] = $item['description']; + $data[$chunks][] = $item['position']; + $data[$chunks][] = $item['weight']; + $data[$chunks][] = $item['include file']; + // Assume 255 for each field except for text fields. + $chunk_size += 14*255; + $chunk_size += mb_strlen($item['load_functions'], '8bit'); + $chunk_size += mb_strlen($item['to_arg_functions'], '8bit'); + $chunk_size += mb_strlen($access_arguments, '8bit'); + $chunk_size += mb_strlen($page_arguments, '8bit'); + $chunk_size += mb_strlen($item['description'], '8bit'); + $chunk_size += mb_strlen($item['include file'], '8bit'); + + $loop_counter++; + if ($chunk_size + $max_insert > $max_packet) { + $chunks++; + $loop_counter = 0; + $chunk_size = 0; + } + } + + // Delete the existing router since we have some data to replace it. + db_query('DELETE FROM {menu_router}'); + // Run the multi insert + foreach ($data as $values) { + db_insert_multi('menu_router', array( + 'path' => "'%s'", + 'load_functions' => "'%s'", + 'to_arg_functions' => "'%s'", + 'access_callback' => "'%s'", + 'access_arguments' => "'%s'", + 'page_callback' => "'%s'", + 'page_arguments' => "'%s'", + 'fit' => "%d", + 'number_parts' => "%d", + 'tab_parent' => "'%s'", + 'tab_root' => "'%s'", + 'title' => "'%s'", + 'title_callback' => "'%s'", + 'title_arguments' => "'%s'", + 'type' => "%d", + 'block_callback' => "'%s'", + 'description' => "'%s'", + 'position' => "'%s'", + 'weight' => "%d", + 'file' => "'%s'", + ), $values); } // Sort the masks so they are in order of descending fit, and store them. $masks = array_keys($masks); @@ -2451,6 +2526,56 @@ function _menu_router_build($callbacks) } /** + * Insert many records into the database. + * + * NOTE Be aware of the MySQL's max_packet_size variable. + * + * @param $table + * The name of the table. + * @param $fields array + * key: field name + * value: db_query placeholders; like %d or '%s' + * @param $values + * array of values you wish to be inserted. If you have 3 fields then the + * array should be structured like + * array($field_1_value_A, $field_2_value_A, $field_3_value_A, + * $field_1_value_B, $field_2_value_B, $field_3_value_B); + * @param $suppress + * bool. TRUE to suppress db_query errors + * @return + * returns db_query() result. + */ +function db_insert_multi($table, $fields, $data, $suppress = FALSE) { + + if ((count($data) % count($fields)) != 0) { + watchdog('db_insert_multi', 'Number of fields in the fields array do not match the number of fields in the data array', array(), WATCHDOG_ERROR); + return FALSE; + } + // Build the fields part of this query + $field_names = implode(', ', array_keys($fields)); + + // Get the number of rows that will be inserted + $rows = count($data)/count($fields); + // Build the values placeholders string. + $values = '(' . implode(', ', $fields) . ')'; + $placeholders = $values; + // Add the rest of the placeholders + for ($i = 1; $i < $rows; $i++) { + $placeholders .= ', ' . $values; + } + + // Glue query together + $query = "INSERT INTO {" . $table . "} ($field_names) VALUES $placeholders"; + // Run the query + if ($suppress) { + return @db_query($query, $data); + } + else { + return db_query($query, $data); + } +} + +/** * Returns TRUE if a path is external (e.g. http://example.com). */ function menu_path_is_external($path) {