From 3f2b287d7c6ce2f9ea798406e2458421fb4b0371 Mon Sep 17 00:00:00 2001 From: Steven Wittens Date: Wed, 1 Mar 2006 22:19:24 +0000 Subject: [PATCH] - #49501: Improve error reporting in the update system --- database/updates.inc | 51 +++---------------- misc/progress.js | 29 ++++++++--- misc/update.js | 6 ++- update.php | 119 +++++++++++++++++++++++++++++++++++++++---- 4 files changed, 143 insertions(+), 62 deletions(-) diff --git a/database/updates.inc b/database/updates.inc index 9847ee13645..bbbade5ec98 100644 --- a/database/updates.inc +++ b/database/updates.inc @@ -1458,8 +1458,9 @@ function system_update_169() { } } + // Note: 'access' table manually updated in update.php return _system_update_utf8(array( - 'access', 'accesslog', 'aggregator_category', + 'accesslog', 'aggregator_category', 'aggregator_category_feed', 'aggregator_category_item', 'aggregator_feed', 'aggregator_item', 'authmap', 'blocks', 'book', 'boxes', 'cache', 'comments', 'contact', @@ -1477,7 +1478,7 @@ function system_update_169() { } /** - * Converts tables to UTF-8 encoding. + * Converts a set of tables to UTF-8 encoding. * * This update is designed to be re-usable by contrib modules and is * used by system_update_169(). @@ -1513,51 +1514,11 @@ function _system_update_utf8($tables) { $_SESSION['update_utf8_total'] = count($tables); } - // Fetch remaining tables list + // Fetch remaining tables list and convert next table $list = &$_SESSION['update_utf8']; - $ret = array(); - - // Convert a table to UTF-8. - // We change all text columns to their correspending 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 - $types = array('char' => 'binary', - 'varchar' => 'varbinary', - 'tinytext' => 'tinyblob', - 'text' => 'blob', - 'mediumtext' => 'mediumblob', - 'longtext' => 'longblob'); - - // Get next table in list - $table = array_shift($list); - $convert_to_binary = array(); - $convert_to_utf8 = array(); - - // Set table default charset - $ret[] = update_sql('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 - $ret[] = update_sql('ALTER TABLE {'. $table .'} '. implode(', ', $convert_to_binary)); - // Convert binary columns to UTF-8 - $ret[] = update_sql('ALTER TABLE {'. $table .'} '. implode(', ', $convert_to_utf8)); - } + $ret = update_convert_table_utf8(array_shift($list)); + // Are we done? if (count($list) == 0) { unset($_SESSION['update_utf8']); diff --git a/misc/progress.js b/misc/progress.js index 84979d68fc0..776e2b63708 100644 --- a/misc/progress.js +++ b/misc/progress.js @@ -8,11 +8,12 @@ * e.g. pb = new progressBar('myProgressBar'); * some_element.appendChild(pb.element); */ -function progressBar(id, callback, method) { +function progressBar(id, updateCallback, method, errorCallback) { var pb = this; this.id = id; this.method = method ? method : HTTPGet; - this.callback = callback; + this.updateCallback = updateCallback; + this.errorCallback = errorCallback; this.element = document.createElement('div'); this.element.id = id; @@ -41,8 +42,8 @@ progressBar.prototype.setProgress = function (percentage, message) { divs[i].innerHTML = message; } } - if (this.callback) { - this.callback(percentage, message, this); + if (this.updateCallback) { + this.updateCallback(percentage, message, this); } } @@ -82,13 +83,13 @@ progressBar.prototype.sendPing = function () { */ progressBar.prototype.receivePing = function (string, xmlhttp, pb) { if (xmlhttp.status != 200) { - return alert('An HTTP error '+ xmlhttp.status +' occured.\n'+ pb.uri); + return pb.displayError('An HTTP error '+ xmlhttp.status +' occured.\n'+ pb.uri); } // Parse response var progress = parseJson(string); // Display errors if (progress.status == 0) { - alert(progress.data); + pb.displayError(progress.data); return; } @@ -97,3 +98,19 @@ progressBar.prototype.receivePing = function (string, xmlhttp, pb) { // Schedule next timer pb.timer = setTimeout(function() { pb.sendPing(); }, pb.delay); } + +/** + * Display errors on the page. + */ +progressBar.prototype.displayError = function (string) { + var error = document.createElement('div'); + error.className = 'error'; + error.appendChild(document.createTextNode(string)); + + this.element.style.display = 'none'; + this.element.parentNode.insertBefore(error, this.element); + + if (this.errorCallback) { + this.errorCallback(this); + } +} diff --git a/misc/update.js b/misc/update.js index 2586fbf505c..e595e4cb2da 100644 --- a/misc/update.js +++ b/misc/update.js @@ -12,7 +12,11 @@ if (isJsEnabled()) { } } - var progress = new progressBar('updateprogress', updateCallback, HTTPPost); + errorCallback = function (pb) { + window.location = window.location.href.split('op=')[0] +'op=error'; + } + + var progress = new progressBar('updateprogress', updateCallback, HTTPPost, errorCallback); progress.setProgress(-1, 'Starting updates'); $('progress').appendChild(progress.element); progress.startMonitoring('update.php?op=do_update', 0); diff --git a/update.php b/update.php index 7275b63e43e..20c3e20214b 100644 --- a/update.php +++ b/update.php @@ -420,6 +420,9 @@ function update_do_updates() { return array($percentage, isset($update['module']) ? 'Updating '. $update['module'] .' module' : 'Updating complete'); } +/** + * Perform updates for the JS version and return progress. + */ function update_do_update_page() { global $conf; @@ -430,24 +433,27 @@ function update_do_update_page() { return ''; } - // Any errors which happen would cause the result to not be parsed properly, - // so we need to supporess them. All errors are still logged. - if (!isset($conf['error_level'])) { - $conf['error_level'] = 0; - } - ini_set('display_errors', FALSE); - list($percentage, $message) = update_do_updates(); print drupal_to_js(array('status' => TRUE, 'percentage' => $percentage, 'message' => $message)); } +/** + * Perform updates for the non-JS version and return the status page. + */ function update_progress_page_nojs() { $new_op = 'do_update_nojs'; if ($_SERVER['REQUEST_METHOD'] == 'GET') { + // Store a fallback redirect in case of a fatal PHP error + ob_start(); + print ''; + list($percentage, $message) = update_do_updates(); if ($percentage == 100) { $new_op = 'finished'; } + + // Updates succesful, remove fallback + ob_end_clean(); } else { // This is the first page so return some output immediately. @@ -463,15 +469,23 @@ function update_progress_page_nojs() { return $output; } -function update_finished_page() { +function update_finished_page($success) { drupal_set_title('Drupal database update'); // NOTE: we can't use l() here because the URL would point to 'update.php?q=admin'. $links[] = 'main page'; $links[] = 'administration pages'; - $output = '

Updates were attempted. If you see no failures below, you may proceed happily to the administration pages. Otherwise, you may need to update your database manually. All errors have been logged.

'; + + if ($success) { + $output = '

Updates were attempted. If you see no failures below, you may proceed happily to the administration pages. Otherwise, you may need to update your database manually. All errors have been logged.

'; + } + else { + $output = '

The update process did not complete. All errors have been logged. You may need to check the watchdog table manually.'; + } + if ($GLOBALS['access_check'] == FALSE) { $output .= "

Reminder: don't forget to set the \$access_check value at the top of update.php back to TRUE."; } + $output .= theme('item_list', $links); // Output a list of queries executed @@ -546,8 +560,89 @@ function update_fix_system_table() { } } +// This code may be removed later. It is part of the Drupal 4.6 to 4.7 migration. +function update_fix_access_table() { + if (variable_get('update_access_fixed', FALSE)) { + return; + } + + switch ($GLOBALS['db_type']) { + // Only for MySQL 4.1+ + case 'mysqli': + break; + case 'mysql': + if (version_compare(mysql_get_server_info($GLOBALS['active_db']), '4.1.0', '<')) { + return; + } + break; + case 'pgsql': + return; + } + + // Convert access table to UTF-8 if needed. + $result = db_fetch_array(db_query('SHOW CREATE TABLE `access`')); + if (!preg_match('/utf8/i', array_pop($result))) { + update_convert_table_utf8('access'); + } + + // Don't run again + variable_set('update_access_fixed', TRUE); +} + +/** + * Convert a single MySQL table to UTF-8. + * + * We change all text columns to their correspending 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 update_convert_table_utf8($table) { + $ret = array(); + $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 + $ret[] = update_sql('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 + $ret[] = update_sql('ALTER TABLE {'. $table .'} '. implode(', ', $convert_to_binary)); + // Convert binary columns to UTF-8 + $ret[] = update_sql('ALTER TABLE {'. $table .'} '. implode(', ', $convert_to_utf8)); + } + return $ret; +} + +// Some unavoidable errors happen because the database is not yet up to date. +// We suppress them to avoid confusion. All errors are still logged. +ini_set('display_errors', FALSE); + include_once './includes/bootstrap.inc'; update_fix_system_table(); +update_fix_access_table(); drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); drupal_maintenance_theme(); @@ -567,7 +662,11 @@ if (($access_check == FALSE) || ($user->uid == 1)) { break; case 'finished': - $output = update_finished_page(); + $output = update_finished_page(true); + break; + + case 'error': + $output = update_finished_page(false); break; case 'do_update':