$value) { if ($key == 'default') { $prefix = $value; } else { $exceptions[] = $value . $key; } } } else { $prefix = $db_prefix; } $prefix_length = strlen($prefix); // Get all tables from database, and also sort these into 'drupal' // and 'other' groups. $tables = array('all' => array(), 'drupal' => array(), 'other' => array()); $result = db_query('SHOW TABLES'); while ($table = db_fetch_array($result)) { $table = array_values($table); $table = $table[0]; $tables['all'][] = $table; if (!$prefix_length || in_array($table, $exceptions) || ($prefix_length && substr($table, 0, $prefix_length) == $prefix)) { $tables['drupal'][] = $table; } else { $tables['other'][] = $table; } } return $tables; } /** * Convert a single MySQL table to UTF-8. * * (This function is a modified copy from update.php) * NOTE: The queries are missing curly braces around table names * intentionally! This allows us to use real table names as * discovered in database, including possibly non-Drupal tables. * update_sql() is avoided to allow running this script across * various Drupal versions, where location of that function differs. * * We change all text columns to their corresponding binary type, * then back to text, but with a UTF-8 character set. * See: http://dev.mysql.com/doc/refman/4.1/en/charset-conversion.html */ function mysqlfix_convert_table_utf8($table) { $types = array('char' => 'binary', 'varchar' => 'varbinary', 'tinytext' => 'tinyblob', 'text' => 'blob', 'mediumtext' => 'mediumblob', 'longtext' => 'longblob'); // Get next table in list $convert_to_binary = array(); $convert_to_utf8 = array(); // Set table default charset db_query('ALTER TABLE '. $table .' DEFAULT CHARACTER SET utf8'); // Find out which columns need converting and build SQL statements $result = db_query('SHOW FULL COLUMNS FROM '. $table); while ($column = db_fetch_array($result)) { list($type) = explode('(', $column['Type']); if (isset($types[$type])) { $names = 'CHANGE `'. $column['Field'] .'` `'. $column['Field'] .'` '; $attributes = ' DEFAULT '. ($column['Default'] == 'NULL' ? 'NULL ' : "'". db_escape_string($column['Default']) ."' ") . ($column['Null'] == 'YES' ? 'NULL' : 'NOT NULL'); $convert_to_binary[] = $names . preg_replace('/'. $type .'/i', $types[$type], $column['Type']) . $attributes; $convert_to_utf8[] = $names . $column['Type'] .' CHARACTER SET utf8'. $attributes; } } if (count($convert_to_binary)) { // Convert text columns to binary db_query('ALTER TABLE '. $table .' '. implode(', ', $convert_to_binary)); // Convert binary columns to UTF-8 db_query('ALTER TABLE '. $table .' '. implode(', ', $convert_to_utf8)); } } /** * Start the actual converting process. It may need quite a while, * so we do it in multiple page-requests to avoid php timeout problems. * Note that this script is supposed to work across 4.7.x to 6.x Drupal * versions, and on unhealthy database, so we can't use the regular * Batch API here. */ function mysqlfix_convert_start($tables) { // Set the necessary session data. $_SESSION['mysqlfix_unprocessed'] = $tables; $_SESSION['mysqlfix_processed'] = array(); // Execute the first batch. return mysqlfix_progress(); } /** * Execute one batch of processing, and redirect to the next. */ function mysqlfix_progress() { drupal_set_title('Processing'); $new_op = 'progress'; // Error handling: if PHP dies, it will output whatever is in the output // buffer, followed by the error message. ob_start(); $fallback = '
An unrecoverable error has occurred. You can find the error message below. It is advised to copy it to the clipboard for reference.
'; print theme('maintenance_page', $fallback); // Process tables for two seconds. while ($table = reset($_SESSION['mysqlfix_unprocessed'])) { mysqlfix_convert_table_utf8($table); unset($_SESSION['mysqlfix_unprocessed'][key($_SESSION['mysqlfix_unprocessed'])]); $_SESSION['mysqlfix_processed'][] = $table; if (timer_read('page') > 2000) { break; } } // Show progress and check whether we are done or not. if (empty($_SESSION['mysqlfix_unprocessed'])) { $percentage = 100; $new_op = 'finished'; } else { $remaining = count($_SESSION['mysqlfix_unprocessed']); $total = $remaining + count($_SESSION['mysqlfix_processed']); $percentage = floor(($total - $remaining) / $total * 100); } $output = 'Please wait. Done: '. $percentage . '%
'; // Batch successful; remove fallback ob_end_clean(); // Redirect to next batch. drupal_set_html_head(''); return $output; } /** * The final page confirming success. */ function mysqlfix_final_page() { drupal_set_title('Finished'); $output = 'The selected set of tables was processed. Please check, whether your database is correct now, either through your favorite database administration tool, or simply by visiting the site. If there are still problems, please read the relevant documentation page for more help.
'; $output .= 'The following tables were processed:
If you modified the access_check flag at the top of mysqlfix.php, or update_free_access inside settings.php, to access this page, you should modify it back now, to avoid security risks. You may also remove mysqlfix.php entirely now, because it's no more needed.
"; return $output; } /** * The Access denied page. */ function mysqlfix_access_denied_page() { drupal_set_title('Access denied'); return 'Access denied. You are not authorized to access this page. Please log in as the admin user (the first user you created). If you cannot log in, you will have to edit mysqlfix.php or settings.php (6.x) to bypass this access check, in the same way as with update.php. Please refer to UPGRADE.txt for more information about access to update scripts on your current Drupal version.
You are using either MySQL version 4.0 and lower, or some entirely different database type. This script is only useful after migration to MySQL 4.1 and higher; there is nothing to do on your configuration.
Operation declined.
'; } /** * The initial page. */ function mysqlfix_main_page($tables) { drupal_set_title('MySQL 4.0-4.1 migration'); $output = 'This is a helper script allowing you to fix your MySQL database after your MySQL server has been upgraded from version 4.0 (or lower) to 4.1 (or higher). Typically, if weird characters (such as "ÅÃÄ") appear in your contents after the upgrade, you might need to use this script. But however, there are quite a few different problems that may happen, especially if you moved your database to an other server.
'; $output .= 'So, before proceeding, you should ensure that:
The following tables were found in your database, and identified as Drupal tables. Please note, that this identification may be inaccurate, if you are running applications other than Drupal on the same database without proper table prefixes. Being that the case, you should check the list very carefully, before proceeding.
'; $output .= ''. implode(', ', $tables['drupal']) .'
Click the button below to process the listed Drupal tables.
'; $output .= ''; } if (!empty($tables['other'])) { $output .= "Additionally, the following tables were found in your database. These tables most probably belong to other applications running on the same database, alongside with Drupal. If you are sure that all these applications also use the utf-8 encoding, like Drupal do, you may process these tables too, and so fix the migration problems for all the applications at once. Please note, that this identification may be inaccurate, so it's entirely possible that these are in fact Drupal tables. Please check the list carefully, before proceeding.
"; $output .= ''. implode(', ', $tables['other']) .'
Click the button below to process the listed non-Drupal tables.
'; $output .= ''; $output .= 'Or you may click this button, to process all the tables found in your database (both Drupal and non-Drupal).
'; $output .= ''; } $output .= "If the options above are insufficient, especially if you found the offered table sets to be wrong, or you want to pick tables manually for some other reason, please click the button below.
"; $output .= ''; return $output; } /** * The manual tables-selection form. * * We're not using regular Forms API here, because this script * is supposed to work across various Drupal versions, and on * unhealthy database. */ function mysqlfix_choose_page($tables) { drupal_set_title('Choose tables'); $output = 'The list below shows all tables found in your database. Please check the boxes for tables you want to process. The tables believed to be Drupal tables are checked by default.
'; $output .= ''; return $output; } /** * The start point of main script */ // Initialize Drupal in a safe way, if possible. include_once './includes/bootstrap.inc'; drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE); $update_mode = TRUE; drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); timer_start('page'); drupal_maintenance_theme(); // Access check similar to update.php (both 5.x and 6.x ways). if ((!isset($access_check) || $access_check !== FALSE) && empty($update_free_access) && $user->uid != 1) { $output = mysqlfix_access_denied_page(); } else { // Check whether the database is currently MySQL 4.1+ if ($db_type != 'mysqli' && ($db_type != 'mysql' || version_compare(mysql_get_server_info($active_db), '4.1.0', '<'))) { $output = mysqlfix_wrong_db_page(); } else { // Build/execute various possible pages. $op = empty($_GET['op']) ? '' : $_GET['op']; switch ($op) { case 'convert:drupal': $tables = mysqlfix_get_tables(); $output = mysqlfix_convert_start($tables['drupal']); break; case 'convert:other': $tables = mysqlfix_get_tables(); $output = mysqlfix_convert_start($tables['other']); break; case 'convert:all': $tables = mysqlfix_get_tables(); $output = mysqlfix_convert_start($tables['all']); break; case 'convert:custom': if (isset($_POST['tables']) && is_array($_POST['tables'])) { $output = mysqlfix_convert_start(array_values($_POST['tables'])); break; } case 'choose': $tables = mysqlfix_get_tables(); $output = mysqlfix_choose_page($tables); break; case 'progress': $output = mysqlfix_progress(); break; case 'finished': $output = mysqlfix_final_page(); break; default: $tables = mysqlfix_get_tables(); $output = mysqlfix_main_page($tables); break; } } } // Show the resulting page to the user. if (!empty($output)) { print theme('maintenance_page', $output); }