From a19acb219e249693357daeb037165eaf19a70b33 Mon Sep 17 00:00:00 2001
From: Dries Buytaert <dries@buytaert.net>
Date: Tue, 6 Dec 2005 09:25:22 +0000
Subject: [PATCH] - Patch #35924 by Neil: improved the update system.

---
 database/database.mysql      |   1 +
 database/database.pgsql      |   1 +
 database/updates.inc         | 401 +++++++------------------
 includes/database.mysql.inc  |   2 +-
 includes/database.mysqli.inc |   2 +-
 includes/database.pgsql.inc  |   2 +-
 includes/theme.inc           |  10 +
 misc/drupal.js               |   2 +-
 misc/maintenance.css         |  36 ++-
 misc/progress.js             |  14 +-
 misc/update.js               |  20 ++
 update.php                   | 557 +++++++++++++++++++++++++++++++----
 12 files changed, 679 insertions(+), 369 deletions(-)
 create mode 100644 misc/update.js

diff --git a/database/database.mysql b/database/database.mysql
index a5ecd1de4795..7be8d9ecd402 100644
--- a/database/database.mysql
+++ b/database/database.mysql
@@ -656,6 +656,7 @@ CREATE TABLE system (
   status int(2) NOT NULL default '0',
   throttle tinyint(1) DEFAULT '0' NOT NULL,
   bootstrap int(2) NOT NULL default '0',
+  schema_version smallint(2) unsigned NOT NULL,
   PRIMARY KEY (filename)
 ) TYPE=MyISAM;
 
diff --git a/database/database.pgsql b/database/database.pgsql
index 59ce317f30d4..5dfee4889fb6 100644
--- a/database/database.pgsql
+++ b/database/database.pgsql
@@ -650,6 +650,7 @@ CREATE TABLE system (
   status integer NOT NULL default '0',
   throttle smallint NOT NULL default '0',
   bootstrap integer NOT NULL default '0',
+  schema_version int2 NOT NULL CHECK (schema_version > 0),
   PRIMARY KEY (filename)
 );
 
diff --git a/database/updates.inc b/database/updates.inc
index e1523b94949c..0c06471c29e7 100644
--- a/database/updates.inc
+++ b/database/updates.inc
@@ -1,114 +1,17 @@
 <?php
 // $Id$
 
-/**
- * @file
- * All incremental database updates performed between Drupal releases.
- *
- * For all updates after 147, please use the following syntax:
- *
- * function update_N() {
- *   $ret = array();
- *
- *   switch ($GLOBALS['db_type']) {
- *     case 'pgsql':
- *       // PostgreSQL code goes here
- *       break;
- *     case 'mysql':
- *     case 'mysqli':
- *       // MySQL code goes here
- *       break;
- *   }
- *
- *   return $ret;
- * }
- *
- *
- * A quick guide to mysql2postgres conversion. Usually (but not allways!) you will use following sql statements:
- *
- * - Adding a key (an index):
- * mysql: ALTER TABLE {$table} ADD KEY $column ($column)
- * pgsql: CREATE INDEX {$table}_$column_idx ON {$table}($column)  // Please note the _idx "extension"
- *
- * - Adding a primary key:
- * mysql: ALTER TABLE {$table} ADD PRIMARY KEY $column ($column)
- * pgsql: ALTER TABLE {$table} ADD PRIMARY KEY ($column)
- *
- * - Dropping a primary key:
- * mysql: ALTER TABLE {$table} DROP PRIMARY KEY
- * pgsql: ALTER TABLE {$table} DROP CONSTRAINT {$table}_pkey
- *
- * - Dropping a column:
- * mysql: ALTER TABLE {$table} DROP $column
- * pgsql: ALTER TABLE {$table} RENAME $column TO $column_old  // For compatibility reasons we don't drop columns but rename them
- *
- * - Dropping an index:
- * mysql: ALTER TABLE {$table} DROP INDEX $index
- * pgsql: DROP INDEX {$table}_$column_idx                            // When index was defined by CREATE INDEX
- * pgsql: ALTER TABLE {$table} DROP CONSTRAINT {$table}_$column_key  // In case of UNIQUE($column)
- *
- * - Adding a column: (an example)
- * mysql: $ret = update_sql("ALTER TABLE {vocabulary} ADD tags tinyint(3) unsigned default '0' NOT NULL");
- * pgsql: db_add_column($ret, 'vocabulary', 'tags', 'smallint', array('default' => 0, 'not null' => TRUE));
- *
- * - Changing a column: (an example):
- * mysql: $ret[] = update_sql("ALTER TABLE {locales_source} CHANGE location location varchar(255) NOT NULL default ''");
- * pgsql: db_change_column($ret, 'locales_source', 'location', 'location', 'varchar(255)', array('not null' => TRUE, 'default' => "''"));
- *
- */
-
-
-// Define the various updates in an array("date : comment" => "function");
-$sql_updates = array(
-  "2004-10-31: first update since Drupal 4.5.0 release" => "update_110",
-  "2004-11-07" => "update_111",
-  "2004-11-15" => "update_112",
-  "2004-11-28" => "update_113",
-  "2004-12-05" => "update_114",
-  "2005-01-07" => "update_115",
-  "2005-01-14" => "update_116",
-  "2005-01-18" => "update_117",
-  "2005-01-19" => "update_118",
-  "2005-01-20" => "update_119",
-  "2005-01-25" => "update_120",
-  "2005-01-26" => "update_121",
-  "2005-01-27" => "update_122",
-  "2005-01-28" => "update_123",
-  "2005-02-11" => "update_124",
-  "2005-02-23" => "update_125",
-  "2005-03-03" => "update_126",
-  "2005-03-18" => "update_127",
-  "2005-03-21" => "update_128",
-  "2005-04-08: first update since Drupal 4.6.0 release" => "update_129",
-  "2005-04-10" => "update_130",
-  "2005-04-11" => "update_131",
-  "2005-04-14" => "update_132",
-  "2005-04-24" => "update_133",
-  "2005-04-30" => "update_134",
-  "2005-05-06" => "update_135",
-  "2005-05-08" => "update_136",
-  "2005-05-09" => "update_137",
-  "2005-05-10" => "update_138",
-  "2005-05-11" => "update_139",
-  "2005-05-12" => "update_140",
-  "2005-05-22" => "update_141",
-  "2005-07-29" => "update_142",
-  "2005-07-30" => "update_143",
-  "2005-08-08" => "update_144",
-  "2005-08-15" => "update_145",
-  "2005-08-25" => "update_146",
-  "2005-09-07" => "update_147",
-  "2005-09-18" => "update_148",
-  "2005-09-27" => "update_149",
-  "2005-10-15" => "update_150",
-  "2005-10-23" => "update_151",
-  "2005-10-28" => "update_152",
-  "2005-11-03" => "update_153",
-  "2005-11-14" => "update_154",
-  "2005-11-27" => "update_155",
-); // Please leave trailing , in the last line
-
-function update_110() {
+function system_version($type) {
+  switch ($type) {
+    case SCHEMA:
+      return 155;
+
+    case SCHEMA_MIN:
+      return 110;
+  }
+}
+
+function system_update_110() {
   $ret = array();
 
   // TODO: needs PGSQL version
@@ -192,7 +95,7 @@ function update_110() {
   return $ret;
 }
 
-function update_111() {
+function system_update_111() {
   $ret = array();
 
   $ret[] = update_sql("DELETE FROM {variable} WHERE name LIKE 'throttle_%'");
@@ -207,7 +110,7 @@ function update_111() {
   return $ret;
 }
 
-function update_112() {
+function system_update_112() {
   $ret = array();
 
   if ($GLOBALS['db_type'] == 'mysql') {
@@ -228,7 +131,7 @@ function update_112() {
   return $ret;
 }
 
-function update_113() {
+function system_update_113() {
   $ret = array();
 
   if ($GLOBALS['db_type'] == 'mysql') {
@@ -259,7 +162,7 @@ function update_113() {
   return $ret;
 }
 
-function update_114() {
+function system_update_114() {
   $ret = array();
   if ($GLOBALS['db_type'] == 'mysql') {
     $ret[] = update_sql("CREATE TABLE {queue} (
@@ -313,7 +216,7 @@ function update_114() {
   return $ret;
 }
 
-function update_115() {
+function system_update_115() {
   $ret = array();
   if ($GLOBALS['db_type'] == 'mysql') {
     $ret[] = update_sql("ALTER TABLE {watchdog} ADD severity tinyint(3) unsigned NOT NULL default '0'");
@@ -327,11 +230,11 @@ function update_115() {
   return $ret;
 }
 
-function update_116() {
+function system_update_116() {
   return array(update_sql("DELETE FROM {system} WHERE name = 'admin'"));
 }
 
-function update_117() {
+function system_update_117() {
   $ret = array();
   if ($GLOBALS['db_type'] == 'mysql') {
     $ret[] = update_sql("CREATE TABLE {vocabulary_node_types} (
@@ -348,7 +251,7 @@ function update_117() {
   return $ret;
 }
 
-function update_118() {
+function system_update_118() {
   $ret = array();
   $node_types = array();
   $result = db_query('SELECT vid, nodes FROM {vocabulary}');
@@ -366,7 +269,7 @@ function update_118() {
   return $ret;
 }
 
-function update_119() {
+function system_update_119() {
   $ret = array();
 
   foreach (node_get_types() as $type => $name) {
@@ -397,7 +300,7 @@ function update_119() {
   return $ret;
 }
 
-function update_120() {
+function system_update_120() {
   $ret = array();
 
   // Rewrite old URL aliases.  Works for both PostgreSQL and MySQL
@@ -413,7 +316,7 @@ function update_120() {
   return $ret;
 }
 
-function update_121() {
+function system_update_121() {
   $ret = array();
 
   // Remove the unused page table.
@@ -422,7 +325,7 @@ function update_121() {
   return $ret;
 }
 
-function update_122() {
+function system_update_122() {
 
   $ret = array();
   $ret[] = update_sql("ALTER TABLE {blocks} ADD types text");
@@ -430,7 +333,7 @@ function update_122() {
 
 }
 
-function update_123() {
+function system_update_123() {
   $ret = array();
 
   if ($GLOBALS['db_type'] == 'mysql') {
@@ -452,7 +355,7 @@ function update_123() {
   return $ret;
 }
 
-function update_124() {
+function system_update_124() {
   $ret = array();
 
   if ($GLOBALS['db_type'] == 'mysql') {
@@ -500,7 +403,7 @@ function update_124() {
   return $ret;
 }
 
-function update_125() {
+function system_update_125() {
   // Postgres only update.
   $ret = array();
 
@@ -519,7 +422,7 @@ function update_125() {
   return $ret;
 }
 
-function update_126() {
+function system_update_126() {
   variable_set('forum_block_num_0', variable_get('forum_block_num', 5));
   variable_set('forum_block_num_1', variable_get('forum_block_num', 5));
   variable_del('forum_block_num');
@@ -527,7 +430,7 @@ function update_126() {
   return array();
 }
 
-function update_127() {
+function system_update_127() {
   $ret = array();
   if ($GLOBALS['db_type'] == 'pgsql') {
     $ret[] = update_sql("ALTER TABLE {poll} RENAME voters TO polled");
@@ -538,7 +441,7 @@ function update_127() {
   return $ret;
 }
 
-function update_128() {
+function system_update_128() {
   $ret = array();
 
   if ($GLOBALS['db_type'] == 'mysql') {
@@ -551,7 +454,7 @@ function update_128() {
   return $ret;
 }
 
-function update_129() {
+function system_update_129() {
   $ret = array();
 
   if ($GLOBALS['db_type'] == 'mysql') {
@@ -564,18 +467,16 @@ function update_129() {
   return $ret;
 }
 
-function update_130() {
+function system_update_130() {
   $ret = array();
-  if ($GLOBALS['db_type'] == 'mysql') {
-    $ret[] = update_sql("ALTER TABLE {sessions} ADD cache int(11) NOT NULL default '0'");
-  }
-  elseif ($GLOBALS['db_type'] == 'pgsql') {
-    db_add_column($ret, 'sessions', 'cache', 'int', array('default' => 0, 'not null' => TRUE));
-  }
+
+  // This update has been moved to update_fix_sessions in update.php because it
+  // is needed for the basic functioning of the update script.
+
   return $ret;
 }
 
-function update_131() {
+function system_update_131() {
   $ret = array();
 
   if ($GLOBALS['db_type'] == 'mysql') {
@@ -589,30 +490,34 @@ function update_131() {
   return $ret;
 }
 
-function update_132() {
+function system_update_132() {
   /**
    * PostgreSQL only update.
    */
   $ret = array();
 
-  if ($GLOBALS['db_type'] == 'pgsql') {
-    $ret[] = update_sql('DROP TABLE {search_total}');
-    $ret[] = update_sql("CREATE TABLE {search_total} (
-              word varchar(50) NOT NULL default '',
-              count float default NULL)");
-    $ret[] = update_sql('CREATE INDEX {search_total}_word_idx ON {search_total}(word)');
+  if (variable_get('update_132_done', FALSE)) {
+    if ($GLOBALS['db_type'] == 'pgsql') {
+      $ret[] = update_sql('DROP TABLE {search_total}');
+      $ret[] = update_sql("CREATE TABLE {search_total} (
+        word varchar(50) NOT NULL default '',
+             count float default NULL)");
+      $ret[] = update_sql('CREATE INDEX {search_total}_word_idx ON {search_total}(word)');
+
+      /**
+       * Wipe the search index
+       */
+      include_once './modules/search.module';
+      search_wipe();
+    }
 
-    /**
-     * Wipe the search index
-     */
-    include_once './modules/search.module';
-    search_wipe();
+    variable_del('update_132_done');
   }
 
   return $ret;
 }
 
-function update_133() {
+function system_update_133() {
   $ret = array();
 
   if ($GLOBALS['db_type'] == 'mysql') {
@@ -633,7 +538,7 @@ function update_133() {
   return $ret;
 }
 
-function update_134() {
+function system_update_134() {
   $ret = array();
   if ($GLOBALS['db_type'] == 'mysql') {
     $ret[] = update_sql('ALTER TABLE {blocks} DROP types');
@@ -644,16 +549,20 @@ function update_134() {
   return $ret;
 }
 
-function update_135() {
-  $result = db_query("SELECT delta FROM {blocks} WHERE module = 'aggregator'");
-  while ($block = db_fetch_object($result)) {
-    list($type, $id) = explode(':', $block->delta);
-    db_query("UPDATE {blocks} SET delta = '%s' WHERE module = 'aggregator' AND delta = '%s'", $type .'-'. $id, $block->delta);
+function system_update_135() {
+  if (variable_get('update_135_done', FALSE)) {
+    $result = db_query("SELECT delta FROM {blocks} WHERE module = 'aggregator'");
+    while ($block = db_fetch_object($result)) {
+      list($type, $id) = explode(':', $block->delta);
+      db_query("UPDATE {blocks} SET delta = '%s' WHERE module = 'aggregator' AND delta = '%s'", $type .'-'. $id, $block->delta);
+    }
+
+    variable_del('update_135_done');
   }
   return array();
 }
 
-function update_136() {
+function system_update_136() {
   $ret = array();
 
   switch ($GLOBALS['db_type']) {
@@ -673,26 +582,30 @@ function update_136() {
   return $ret;
 }
 
-function update_137() {
+function system_update_137() {
   $ret = array();
 
-  if ($GLOBALS['db_type'] == 'mysql') {
-    $ret[] = update_sql("ALTER TABLE {locales_source} CHANGE location location varchar(255) NOT NULL default ''");
-  }
-  elseif ($GLOBALS['db_type'] == 'pgsql') {
-    db_change_column($ret, 'locales_source', 'location', 'location', 'varchar(255)', array('not null' => TRUE, 'default' => "''"));
+  if (variable_get('update_137_done', FALSE)) {
+    if ($GLOBALS['db_type'] == 'mysql') {
+      $ret[] = update_sql("ALTER TABLE {locales_source} CHANGE location location varchar(255) NOT NULL default ''");
+    }
+    elseif ($GLOBALS['db_type'] == 'pgsql') {
+      db_change_column($ret, 'locales_source', 'location', 'location', 'varchar(255)', array('not null' => TRUE, 'default' => "''"));
+    }
+    variable_del('update_137_done');
   }
+
   return $ret;
 }
 
-function update_138() {
+function system_update_138() {
   $ret = array();
   // duplicate of update_97 which never got into the default database.* files.
   $ret[] = update_sql("INSERT INTO {url_alias} (src, dst) VALUES ('node/feed', 'rss.xml')");
   return $ret;
 }
 
-function update_139() {
+function system_update_139() {
   $ret = array();
   switch ($GLOBALS['db_type']) {
     case 'pgsql':
@@ -707,7 +620,7 @@ function update_139() {
   return $ret;
 }
 
-function update_140() {
+function system_update_140() {
   $ret = array();
 
   if ($GLOBALS['db_type'] == 'mysql') {
@@ -719,7 +632,7 @@ function update_140() {
   return $ret;
 }
 
-function update_141() {
+function system_update_141() {
   $ret = array();
 
   variable_del('upload_maxsize_total');
@@ -727,21 +640,16 @@ function update_141() {
   return $ret;
 }
 
-function update_142() {
+function system_update_142() {
   $ret = array();
-  switch ($GLOBALS['db_type']) {
-    case 'pgsql':
-      db_add_column($ret, 'watchdog', 'referer', 'varchar(128)', array('not null' => TRUE, 'default' => "''"));
-      break;
-    case 'mysql':
-    case 'mysqli':
-      $ret[] = update_sql("ALTER TABLE {watchdog} ADD COLUMN referer varchar(128) NOT NULL");
-      break;
-  }
+
+  // This update has been moved to update_fix_sessions in update.php because it
+  // is needed for the basic functioning of the update script.
+
   return $ret;
 }
 
-function update_143() {
+function system_update_143() {
   $ret = array();
 
   if ($GLOBALS['db_type'] == 'mysql') {
@@ -760,7 +668,7 @@ function update_143() {
   return $ret;
 }
 
-function update_144() {
+function system_update_144() {
   $ret = array();
   if ($GLOBALS['db_type'] == 'mysql') {
     $ret[] = update_sql("ALTER TABLE {node} CHANGE type type VARCHAR(32) NOT NULL");
@@ -777,7 +685,7 @@ function update_144() {
   return $ret;
 }
 
-function update_145() {
+function system_update_145() {
   $default_theme = variable_get('theme_default', 'bluemarine');
   $ret = array();
 
@@ -809,7 +717,7 @@ function update_145() {
   return $ret;
 }
 
-function update_146() {
+function system_update_146() {
   $ret = array();
 
   if ($GLOBALS['db_type'] == 'mysql') {
@@ -917,7 +825,7 @@ function update_146() {
   return $ret;
 }
 
-function update_147() {
+function system_update_147() {
   $ret = array();
 
   // this update is mysql only, pgsql should get it right in the first try.
@@ -928,7 +836,7 @@ function update_147() {
   return $ret;
 }
 
-function update_148() {
+function system_update_148() {
   $ret = array();
 
   // Add support for tracking users' session ids (useful for tracking anon users)
@@ -940,12 +848,12 @@ function update_148() {
     case 'mysqli':
       $ret[] = update_sql("ALTER TABLE {accesslog} ADD sid varchar(32) NOT NULL default ''");
       break;
- }
+  }
 
   return $ret;
 }
 
-function update_149() {
+function system_update_149() {
   $ret = array();
 
   switch ($GLOBALS['db_type']) {
@@ -963,7 +871,7 @@ function update_149() {
   return $ret;
 }
 
-function update_150() {
+function system_update_150() {
   $ret = array();
 
   $ret[] = update_sql("DELETE FROM {variable} WHERE name = 'node_cron_last'");
@@ -1032,7 +940,7 @@ function update_150() {
   return $ret;
 }
 
-function update_151() {
+function system_update_151() {
   $ret = array();
 
   $ts = variable_get('theme_settings', null);
@@ -1109,7 +1017,7 @@ function update_151() {
   return $ret;
 }
 
-function update_152() {
+function system_update_152() {
   $ret = array();
 
   // Postgresql only update
@@ -1125,7 +1033,7 @@ function update_152() {
   return $ret;
 }
 
-function update_153(){
+function system_update_153(){
   $ret = array();
   switch ($GLOBALS['db_type']) {
     case 'pgsql':
@@ -1145,7 +1053,7 @@ function update_153(){
   return $ret;
 }
 
-function update_154() {
+function system_update_154() {
   $ret = array();
   switch ($GLOBALS['db_type']) {
     case 'pgsql':
@@ -1161,7 +1069,7 @@ function update_154() {
   return $ret;
 }
 
-function update_155() {
+function system_update_155() {
   $ret = array();
 
   // Postgresql only update
@@ -1186,114 +1094,9 @@ function update_155() {
   return $ret;
 }
 
-
-/**
- * Adds a column to a database. Uses syntax appropriate for PostgreSQL.
- * Saves result of SQL commands in $ret array.
- *
- * Note: when you add a column with NOT NULL and you are not sure if there are rows in table already,
- *  you MUST also add DEFAULT. Otherwise PostgreSQL won't work if the table is not empty. If NOT NULL and
- *  DEFAULT is set the Postgresql version will set values of the added column in old rows to the DEFAULT value.
- *
- * @param $ret
- *  Array to which results will be added.
- * @param $table
- *  Name of the table, without {}
- * @param $column
- *  Name of the column
- * @param $type
- *  Type of column
- * @param $attributes
- *  Additional optional attributes. Recognized atributes:
- *    - not null    => TRUE/FALSE
- *    - default     => NULL/FALSE/value (with or without '', it wont' be added)
- * @return
- *  nothing, but modifies $ret parametr.
- */
-function db_add_column(&$ret, $table, $column, $type, $attributes = array()) {
-  if (array_key_exists('not null', $attributes) and $attributes['not null']) {
-    $not_null = 'NOT NULL';
-  }
-  if (array_key_exists('default', $attributes)) {
-    if (is_null($attributes['default'])) {
-      $default_val = 'NULL';
-      $default = 'default NULL';
-    }
-    elseif ($attributes['default'] === FALSE) {
-      $default = '';
-    }
-    else {
-      $default_val = "$attributes[default]";
-      $default = "default $attributes[default]";
-    }
-  }
-
-  $ret[] = update_sql("ALTER TABLE {". $table ."} ADD $column $type");
-  if ($default) { $ret[] = update_sql("ALTER TABLE {". $table ."} ALTER $column SET $default"); }
-  if ($not_null) {
-    if ($default) { $ret[] = update_sql("UPDATE {". $table ."} SET $column = $default_val"); }
-    $ret[] = update_sql("ALTER TABLE {". $table ."} ALTER $column SET NOT NULL");
-  }
-}
-
-/**
- * Changes a column definition. Uses syntax appropriate for PostgreSQL.
- * Saves result of SQL commands in $ret array.
- *
- * @param $ret
- *  Array to which results will be added.
- * @param $table
- *  Name of the table, without {}
- * @param $column
- *  Name of the column to change
- * @param $column_new
- *  New name for the column (set to the same as $column if you don't want to change the name)
- * @param $type
- *  Type of column
- * @param $attributes
- *  Additional optional attributes. Recognized atributes:
- *    - not null    => TRUE/FALSE
- *    - default     => NULL/FALSE/value (with or without '', it wont' be added)
- * @return
- *  nothing, but modifies $ret parametr.
- */
-function db_change_column(&$ret, $table, $column, $column_new, $type, $attributes = array()) {
-  if (array_key_exists('not null', $attributes) and $attributes['not null']) {
-    $not_null = 'NOT NULL';
-  }
-  if (array_key_exists('default', $attributes)) {
-    if (is_null($attributes['default'])) {
-      $default_val = 'NULL';
-      $default = 'default NULL';
-    }
-    elseif ($attributes['default'] === FALSE) {
-      $default = '';
-    }
-    else {
-      $default_val = "$attributes[default]";
-      $default = "default $attributes[default]";
-    }
-  }
-
-  $ret[] = update_sql("ALTER TABLE {". $table ."} RENAME $column TO ". $column ."_old");
-  $ret[] = update_sql("ALTER TABLE {". $table ."} ADD $column_new $type");
-  $ret[] = update_sql("UPDATE {". $table ."} SET $column_new = ". $column ."_old");
-  if ($default) { $ret[] = update_sql("ALTER TABLE {". $table ."} ALTER $column_new SET $default"); }
-  if ($not_null) { $ret[] = update_sql("ALTER TABLE {". $table ."} ALTER $column_new SET NOT NULL"); }
-  // We don't drop columns for now
-  // $ret[] = update_sql("ALTER TABLE {". $table ."} DROP ". $column ."_old");
-}
-
-
-function update_sql($sql) {
-  $edit = $_POST["edit"];
-  $result = db_query($sql);
-  if ($result) {
-    return array('1', check_plain($sql) ."\n<span class=\"success\">OK</span>\n");
-  }
-  else {
-    return array('0', check_plain($sql) ."\n<span class=\"failure\">FAILED</span>\n");
-  }
+function system_update_156() {
+  $ret = array();
+  $ret[] = update_sql("DELETE FROM {cache}");
+  system_themes();
+  return array();
 }
-
-?>
diff --git a/includes/database.mysql.inc b/includes/database.mysql.inc
index a58c9f1d5bc6..04af5ea0ac9a 100644
--- a/includes/database.mysql.inc
+++ b/includes/database.mysql.inc
@@ -105,7 +105,7 @@ function _db_query($query, $debug = 0) {
     return $result;
   }
   else {
-    trigger_error(check_plain(mysql_error() ."\nquery: ". $query), E_USER_ERROR);
+    trigger_error(check_plain(mysql_error() ."\nquery: ". $query), E_USER_WARNING);
     return FALSE;
   }
 }
diff --git a/includes/database.mysqli.inc b/includes/database.mysqli.inc
index ed80b70caf20..8645b7378ebd 100644
--- a/includes/database.mysqli.inc
+++ b/includes/database.mysqli.inc
@@ -113,7 +113,7 @@ function _db_query($query, $debug = 0) {
     return $result;
   }
   else {
-    trigger_error(check_plain(mysqli_error($active_db) ."\nquery: ". $query), E_USER_ERROR);
+    trigger_error(check_plain(mysqli_error($active_db) ."\nquery: ". $query), E_USER_WARNING);
     return FALSE;
   }
 }
diff --git a/includes/database.pgsql.inc b/includes/database.pgsql.inc
index 3c2f8f710d15..28d5efd7a426 100644
--- a/includes/database.pgsql.inc
+++ b/includes/database.pgsql.inc
@@ -92,7 +92,7 @@ function _db_query($query, $debug = 0) {
     return $last_result;
   }
   else {
-    trigger_error(check_plain(pg_last_error() ."\nquery: ". $query), E_USER_ERROR);
+    trigger_error(check_plain(pg_last_error() ."\nquery: ". $query), E_USER_WARNING);
     return FALSE;
   }
 }
diff --git a/includes/theme.inc b/includes/theme.inc
index c7f58585344b..279015b7118d 100644
--- a/includes/theme.inc
+++ b/includes/theme.inc
@@ -974,6 +974,16 @@ function theme_username($object) {
   return $output;
 }
 
+function theme_progress_bar($percent, $message) {
+  $output = '<div id="progress" class="progress">';
+  $output .= '<div class="percentage">'. $percent .'%</div>';
+  $output .= '<div class="status">'. $message .'</div>';
+  $output .= '<div class="bar"><div class="filled" style="width: '. $percent .'%"></div></div>';
+  $output .= '</div>';
+
+  return $output;
+}
+
 /**
  * @} End of "defgroup themeable".
  */
diff --git a/misc/drupal.js b/misc/drupal.js
index c2ee5233218a..f1af13be1849 100644
--- a/misc/drupal.js
+++ b/misc/drupal.js
@@ -67,7 +67,7 @@ function HTTPGet(uri, callbackFunction, callbackParameter) {
 /**
  * Creates an HTTP POST request and sends the response to the callback function
  */
-function HTTPPost(uri, object, callbackFunction, callbackParameter) {
+function HTTPPost(uri, callbackFunction, callbackParameter, object) {
   var xmlHttp = new XMLHttpRequest();
   var bAsync = true;
   if (!callbackFunction) {
diff --git a/misc/maintenance.css b/misc/maintenance.css
index 8c6a5bbe6fe2..2292550ce507 100644
--- a/misc/maintenance.css
+++ b/misc/maintenance.css
@@ -9,8 +9,13 @@ body {
 }
 h1 {
   margin: 1.6em 0 1.1em 0;
+}
+h1, h2, h3, h4, h5, h6 {
   font-family: sans-serif;
 }
+ul {
+  margin: 0;
+}
 :link {
   color: #0073ba;
   font-weight: bold;
@@ -20,13 +25,34 @@ h1 {
   font-weight: bold;
 }
 
+div.messages {
+  border: 1px solid #ddd;
+  padding: 0.4em;
+  margin-top: 1em;
+}
+
+div.error {
+  border: 1px solid #daa;
+}
+
 /* Update styles */
-h3.update {
-  font-size: 1em;
+#update-results {
+  margin-top: 3em;
+  padding: 0.25em;
+  border: 1px solid #ccc;
+  background: #eee;
+  font-size: smaller;
+}
+#update-results h2 {
+  margin-top: 0.25em;
+}
+#update-results h4 {
+  margin-bottom: 0.25em;
 }
-pre.update span.success {
-  color: #6bb521;
+#update-results li.none {
+  color: #888;
+  font-style: italic;
 }
-pre.update span.failure {
+#update-results li.failure strong {
   color: #b63300;
 }
diff --git a/misc/progress.js b/misc/progress.js
index a2de44e80711..b19cc08dbc8f 100644
--- a/misc/progress.js
+++ b/misc/progress.js
@@ -2,12 +2,17 @@
  * A progressbar object. Initialized with the given id. Must be inserted into
  * the DOM afterwards through progressBar.element.
  *
+ * method is the function which will perform the HTTP request to get the
+ * progress bar status. Either HTTPGet or HTTPPost.
+ *
  * e.g. pb = new progressBar('myProgressBar');
  *      some_element.appendChild(pb.element);
  */
-function progressBar(id) {
+function progressBar(id, callback, method) {
   var pb = this;
   this.id = id;
+  this.method = method ? method : HTTPGet;
+  this.callback = callback;
 
   this.element = document.createElement('div');
   this.element.id = id;
@@ -36,6 +41,9 @@ progressBar.prototype.setProgress = function (percentage, status) {
       divs[i].innerHTML = status;
     }
   }
+  if (this.callback) {
+    this.callback(percentage, status);
+  }
 }
 
 /**
@@ -61,14 +69,14 @@ progressBar.prototype.sendPing = function () {
   if (this.timer) {
     clearTimeout(this.timer);
   }
-  HTTPGet(this.uri, this.receivePing, this);
+  this.method(this.uri, this.receivePing, this);
 }
 
 /**
  * HTTP callback function. Passes data back to the progressbar and sets a new
  * timer for the next ping.
  */
-progressBar.prototype.receivePing = function(string, xmlhttp, pb) {
+progressBar.prototype.receivePing = function (string, xmlhttp, pb) {
   if (xmlhttp.status != 200) {
     return alert('An HTTP error '+ xmlhttp.status +' occured.\n'+ pb.uri);
   }
diff --git a/misc/update.js b/misc/update.js
new file mode 100644
index 000000000000..e4358d269172
--- /dev/null
+++ b/misc/update.js
@@ -0,0 +1,20 @@
+if (isJsEnabled()) {
+  addLoadEvent(function() {
+    if ($('edit-has_js')) {
+      $('edit-has_js').value = 1;
+    }
+
+    if ($('progress')) {
+      updateCallback = function (progress, status) {
+        if (progress == 100) {
+          window.location = window.location.href.split('op=')[0] +'op=finished';
+        }
+      }
+
+      this.progress = new progressBar('updateprogress', updateCallback, HTTPPost);
+      this.progress.setProgress(-1, 'Starting updates...');
+      $('progress').appendChild(this.progress.element);
+      this.progress.startMonitoring('update.php?op=do_update', 0);
+    }
+  });
+}
diff --git a/update.php b/update.php
index 9f9269b4a978..a76587ce79d2 100644
--- a/update.php
+++ b/update.php
@@ -17,49 +17,347 @@
 // Enforce access checking?
 $access_check = TRUE;
 
-if (!ini_get("safe_mode")) {
-  set_time_limit(180);
+
+define('SCHEMA', 0);
+define('SCHEMA_MIN', 1);
+
+/**
+ * Includes install files.
+ */
+function update_include_install_files() {
+  // The system module (Drupal core) is currently a special case
+  include_once './database/updates.inc';
+
+  foreach (module_list() as $module) {
+    $install_file = './'. drupal_get_path('module', $module) .'/'. $module .'.install';
+    if (is_file($install_file)) {
+      include_once $install_file;
+    }
+  }
 }
 
-include_once './database/updates.inc';
+function update_sql($sql) {
+  $result = db_query($sql);
+  return array('success' => $result !== FALSE, 'query' => check_plain($sql));
+}
 
-function update_data($start) {
-  global $sql_updates;
-  $output = '';
-  $sql_updates = array_slice($sql_updates, ($start-- ? $start : 0));
-  foreach ($sql_updates as $date => $func) {
-    $output .= '<h3 class="update">'. $date .'</h3><pre class="update">';
-    $ret = $func();
-    foreach ($ret as $return) {
-      $output .= $return[1];
+/**
+ * Adds a column to a database. Uses syntax appropriate for PostgreSQL.
+ * Saves result of SQL commands in $ret array.
+ *
+ * Note: when you add a column with NOT NULL and you are not sure if there are
+ * rows in table already, you MUST also add DEFAULT. Otherwise PostgreSQL won't
+ * work if the table is not empty. If NOT NULL and DEFAULT is set the
+ * PostgreSQL version will set values of the added column in old rows to the
+ * DEFAULT value.
+ *
+ * @param $ret
+ *  Array to which results will be added.
+ * @param $table
+ *  Name of the table, without {}
+ * @param $column
+ *  Name of the column
+ * @param $type
+ *  Type of column
+ * @param $attributes
+ *  Additional optional attributes. Recognized atributes:
+ *    - not null    => TRUE/FALSE
+ *    - default     => NULL/FALSE/value (with or without '', it wont' be added)
+ * @return
+ *  nothing, but modifies $ret parametr.
+ */
+function db_add_column(&$ret, $table, $column, $type, $attributes = array()) {
+  if (array_key_exists('not null', $attributes) and $attributes['not null']) {
+    $not_null = 'NOT NULL';
+  }
+  if (array_key_exists('default', $attributes)) {
+    if (is_null($attributes['default'])) {
+      $default_val = 'NULL';
+      $default = 'default NULL';
+    }
+    elseif ($attributes['default'] === FALSE) {
+      $default = '';
+    }
+    else {
+      $default_val = "$attributes[default]";
+      $default = "default $attributes[default]";
     }
-    variable_set("update_start", $date);
-    $output .= "</pre>\n";
   }
-  db_query('DELETE FROM {cache}');
-  return $output;
+
+  $ret[] = update_sql("ALTER TABLE {". $table ."} ADD $column $type");
+  if ($default) { $ret[] = update_sql("ALTER TABLE {". $table ."} ALTER $column SET $default"); }
+  if ($not_null) {
+    if ($default) { $ret[] = update_sql("UPDATE {". $table ."} SET $column = $default_val"); }
+    $ret[] = update_sql("ALTER TABLE {". $table ."} ALTER $column SET NOT NULL");
+  }
 }
 
-function update_selection_page() {
-  global $sql_updates;
+/**
+ * Changes a column definition. Uses syntax appropriate for PostgreSQL.
+ * Saves result of SQL commands in $ret array.
+ *
+ * @param $ret
+ *  Array to which results will be added.
+ * @param $table
+ *  Name of the table, without {}
+ * @param $column
+ *  Name of the column to change
+ * @param $column_new
+ *  New name for the column (set to the same as $column if you don't want to change the name)
+ * @param $type
+ *  Type of column
+ * @param $attributes
+ *  Additional optional attributes. Recognized atributes:
+ *    - not null    => TRUE/FALSE
+ *    - default     => NULL/FALSE/value (with or without '', it wont' be added)
+ * @return
+ *  nothing, but modifies $ret parametr.
+ */
+function db_change_column(&$ret, $table, $column, $column_new, $type, $attributes = array()) {
+  if (array_key_exists('not null', $attributes) and $attributes['not null']) {
+    $not_null = 'NOT NULL';
+  }
+  if (array_key_exists('default', $attributes)) {
+    if (is_null($attributes['default'])) {
+      $default_val = 'NULL';
+      $default = 'default NULL';
+    }
+    elseif ($attributes['default'] === FALSE) {
+      $default = '';
+    }
+    else {
+      $default_val = "$attributes[default]";
+      $default = "default $attributes[default]";
+    }
+  }
+
+  $ret[] = update_sql("ALTER TABLE {". $table ."} RENAME $column TO ". $column ."_old");
+  $ret[] = update_sql("ALTER TABLE {". $table ."} ADD $column_new $type");
+  $ret[] = update_sql("UPDATE {". $table ."} SET $column_new = ". $column ."_old");
+  if ($default) { $ret[] = update_sql("ALTER TABLE {". $table ."} ALTER $column_new SET $default"); }
+  if ($not_null) { $ret[] = update_sql("ALTER TABLE {". $table ."} ALTER $column_new SET NOT NULL"); }
+  // We don't drop columns for now
+  // $ret[] = update_sql("ALTER TABLE {". $table ."} DROP ". $column ."_old");
+}
+
+/**
+ * If the schema version for Drupal core is stored in the the variables table
+ * (4.6.x and earlier) move it to the schema_version column of the system
+ * table.
+ *
+ * This function may be removed when update 156 is removed, which is the last
+ * update in the 4.6 to 4.7 migration.
+ */
+function update_fix_schema_version() {
+  if ($update_start = variable_get('update_start', FALSE)) {
+    // Some updates were made to the 4.6 branch and 4.7 branch. This sets
+    // temporary variables to provent the updates from being executed twice and
+    // throwing errors.
+    switch ($update_start) {
+      case '2005-04-14':
+        variable_set('update_132_done', TRUE);
+        break;
+
+      case '2005-05-06':
+        variable_set('update_132_done', TRUE);
+        variable_set('update_135_done', TRUE);
+        break;
+
+      case '2005-05-07':
+        variable_set('update_132_done', TRUE);
+        variable_set('update_135_done', TRUE);
+        variable_set('update_137_done', TRUE);
+        break;
+
+    }
+
+    $sql_updates = array(
+      '2004-10-31: first update since Drupal 4.5.0 release' => 110,
+      '2004-11-07' => 111, '2004-11-15' => 112, '2004-11-28' => 113,
+      '2004-12-05' => 114, '2005-01-07' => 115, '2005-01-14' => 116,
+      '2005-01-18' => 117, '2005-01-19' => 118, '2005-01-20' => 119,
+      '2005-01-25' => 120, '2005-01-26' => 121, '2005-01-27' => 122,
+      '2005-01-28' => 123, '2005-02-11' => 124, '2005-02-23' => 125,
+      '2005-03-03' => 126, '2005-03-18' => 127, '2005-03-21' => 128,
+      // The following three updates were made on the 4.6 branch
+      '2005-04-14' => 129, '2005-05-06' => 129, '2005-05-07' => 129,
+      '2005-04-08: first update since Drupal 4.6.0 release' => 129,
+      '2005-04-10' => 130, '2005-04-11' => 131, '2005-04-14' => 132,
+      '2005-04-24' => 133, '2005-04-30' => 134, '2005-05-06' => 135,
+      '2005-05-08' => 136, '2005-05-09' => 137, '2005-05-10' => 138,
+      '2005-05-11' => 139, '2005-05-12' => 140, '2005-05-22' => 141,
+      '2005-07-29' => 142, '2005-07-30' => 143, '2005-08-08' => 144,
+      '2005-08-15' => 145, '2005-08-25' => 146, '2005-09-07' => 147,
+      '2005-09-18' => 148, '2005-09-27' => 149, '2005-10-15' => 150,
+      '2005-10-23' => 151, '2005-10-28' => 152, '2005-11-03' => 153,
+      '2005-11-14' => 154, '2005-11-27' => 155, '2005-12-03' => 156,
+    );
+
+    switch ($GLOBALS['db_type']) {
+      case 'pgsql':
+        $ret = array();
+        db_add_column($ret, 'system', 'schema_version', 'int2', array('not null' => TRUE));
+        break;
+
+      case 'mysql':
+      case 'mysqli':
+        db_query('ALTER TABLE {system} ADD schema_version smallint(2) unsigned not null');
+        break;
+    }
+
+    update_set_installed_version('system', $sql_updates[$update_start]);
+    variable_del('update_start');
+  }
+}
+
+/**
+ * System update 130 changes the sessions table, which breaks the update
+ * script's ability to use session variables. This changes the table
+ * appropriately.
+ *
+ * This code, including the 'update_sessions_fixed' variable, may be removed
+ * when update 130 is removed. It is part of the Drupal 4.6 to 4.7 migration.
+ */
+function update_fix_sessions() {
+  $ret = array();
+
+  if (update_get_installed_version('system') < 130 && !variable_get('update_sessions_fixed', FALSE)) {
+    if ($GLOBALS['db_type'] == 'mysql') {
+      db_query("ALTER TABLE {sessions} ADD cache int(11) NOT NULL default '0' AFTER timestamp");
+    }
+    elseif ($GLOBALS['db_type'] == 'pgsql') {
+      db_add_column($ret, 'sessions', 'cache', 'int', array('default' => 0, 'not null' => TRUE));
+    }
+
+    variable_set('update_sessions_fixed', TRUE);
+  }
+}
+
+/**
+ * System update 142 changes the watchdog table, which breaks the update
+ * script's ability to use logging. This changes the table appropriately.
+ *
+ * This code, including the 'update_watchdog_fixed' variable,  may be removed
+ * when update 142 is removed. It is part of the Drupal 4.6 to 4.7 migration.
+ */
+function update_fix_watchdog() {
+  if (update_get_installed_version('system') < 142 && !variable_get('update_watchdog_fixed', FALSE)) {
+    switch ($GLOBALS['db_type']) {
+      case 'pgsql':
+        db_add_column($ret, 'watchdog', 'referer', 'varchar(128)', array('not null' => TRUE, 'default' => "''"));
+        break;
+      case 'mysql':
+      case 'mysqli':
+        $ret[] = db_query("ALTER TABLE {watchdog} ADD COLUMN referer varchar(128) NOT NULL");
+        break;
+    }
+
+    variable_set('update_watchdog_fixed', TRUE);
+  }
+}
+
+function update_data($module, $number) {
+  $ret = module_invoke($module, 'update_'. $number);
+
+  // Save the query and results for display by update_finished_page().
+  if (!isset($_SESSION['update_results'])) {
+    $_SESSION['update_results'] = array();
+  }
+  else if (!isset($_SESSION['update_results'][$module])) {
+    $_SESSION['update_results'][$module] = array();
+  }
+  $_SESSION['update_results'][$module][$number] = $ret;
+
+  // Update the installed version
+  update_set_installed_version($module, $number);
+}
+
+/**
+ * Returns an array of availiable schema versions for a module.
+ *
+ * @param $module
+ *   A module name.
+ * @return
+ *   If the module has updates, an array of available updates. Otherwise,
+ *   FALSE.
+ */
+function update_get_versions($module) {
+  if (!($max = module_invoke($module, 'version', SCHEMA))) {
+    return FALSE;
+  }
+  if (!($min = module_invoke($module, 'version', SCHEMA_MIN))) {
+    $min = 1;
+  }
+  return range($min, $max);
+}
+
+/**
+ * Returns the currently installed schema version for a module.
+ *
+ * @param $module
+ *   A module name.
+ * @return
+ *   The currently installed schema version.
+ */
+function update_get_installed_version($module, $reset = FALSE) {
+  static $versions;
 
-  $start = variable_get("update_start", 0);
-  $i = 1;
-  foreach ($sql_updates as $date => $sql) {
-    $dates[$i++] = $date;
-    if ($date == $start) {
-      $selected = $i;
+  if ($reset) {
+    unset($versions);
+  }
+
+  if (!$versions) {
+    $versions = array();
+    $result = db_query("SELECT name, schema_version FROM {system} WHERE type = 'module'");
+    while ($row = db_fetch_object($result)) {
+      $versions[$row->name] = $row->schema_version;
     }
   }
-  $dates[$i] = "No updates available";
 
-  // make update form and output it.
+  return $versions[$module];
+}
+
+/**
+ * Update the installed version information for a module.
+ *
+ * @param $module
+ *   A module name.
+ * @param $version
+ *   The new schema version.
+ */
+function update_set_installed_version($module, $version) {
+  db_query("UPDATE {system} SET schema_version = %d WHERE name = '%s'", $version, $module);
+}
+
+function update_selection_page() {
+  $output = '<p>'. t('The version of Drupal you are updating from has been automatically detected. You can select a different version, but you should not need to.') .'</p>';
+  $output .= '<p>'. t('Click Update to start the update process.') .'</p>';
+
+  $form = array();
   $form['start'] = array(
-    '#type' => 'select',
-    '#title' => t('Perform updates from'),
-    '#default_value' => (isset($selected) ? $selected : -1),
-    '#options' => $dates,
-    '#description' => t('This defaults to the first available update since the last update you performed.')
+    '#tree' => TRUE,
+    '#type' => 'fieldset',
+    '#title' => 'Select versions',
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+  );
+  foreach (module_list() as $module) {
+    if (module_hook($module, 'version')) {
+      $updates = drupal_map_assoc(update_get_versions($module));
+      $updates[] = 'No updates available';
+
+      $form['start'][$module] = array(
+        '#type' => 'select',
+        '#title' => t('%name module', array('%name' => $module)),
+        '#default_value' => array_search(update_get_installed_version($module), $updates) + 1,
+        '#options' => $updates,
+      );
+    }
+  }
+
+  $form['has_js'] = array(
+    '#type' => 'hidden',
+    '#default_value' => FALSE
   );
   $form['submit'] = array(
     '#type' => 'submit',
@@ -67,21 +365,150 @@ function update_selection_page() {
   );
 
   drupal_set_title('Drupal database update');
-  return drupal_get_form('update_script_selection_form', $form);
+  drupal_add_js('misc/update.js');
+  $output .= drupal_get_form('update_script_selection_form', $form);
+
+  return $output;
+}
+
+function update_update_page() {
+  // Set the installed version so updates start at the correct place.
+  $_SESSION['update_remaining'] = array();
+  foreach ($_POST['edit']['start'] as $module => $version) {
+    update_set_installed_version($module, $version - 1);
+    $max_version = max(update_get_versions($module));
+    if ($version <= $max_version) {
+      foreach (range($version, $max_version) as $update) {
+        $_SESSION['update_remaining'][] = array('module' => $module, 'version' => $update);
+      }
+    }
+  }
+  // Keep track of total number of updates
+  $_SESSION['update_total'] = count($_SESSION['update_remaining']);
+
+  if ($_POST['edit']['has_js']) {
+    return update_progress_page();
+  }
+  else {
+    return update_progress_page_nojs();
+  }
+}
+
+function update_progress_page() {
+  drupal_add_js('misc/progress.js');
+  drupal_add_js('misc/update.js');
+
+  drupal_set_title('Updating');
+  $output = '<div id="progress"></div>';
+  $output .= '<p>Updating your site will take a few seconds.</p>';
+  return $output;
 }
 
+/**
+ * Perform updates for one second or until finished.
+ *
+ * @return
+ *   An array indicating the status after doing updates. The first element is
+ *   the overall percent finished. The second element is a status message.
+ */
 function update_do_updates() {
-  $edit = $_POST['edit'];
+  foreach ($_SESSION['update_remaining'] as $key => $update) {
+    update_data($update['module'], $update['version']);
+    unset($_SESSION['update_remaining'][$key]);
+    if (timer_read('page') > 1000) {
+      break;
+    }
+  }
+
+  if ($_SESSION['update_total']) {
+    $percent = floor(($_SESSION['update_total'] - count($_SESSION['update_remaining'])) / $_SESSION['update_total'] * 100);
+  }
+  else {
+    $percent = 100;
+  }
+  return array($percent, 'Updating '. $update['module'] .' module');
+}
+
+function update_do_update_page() {
+  global $conf;
+
+  // HTTP Post required
+  if ($_SERVER['REQUEST_METHOD'] != 'POST') {
+    drupal_set_message('HTTP Post is required.', 'error');
+    drupal_set_title('Error');
+    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);
+
+  print implode('|', update_do_updates());
+}
+
+function update_progress_page_nojs() {
+  $new_op = 'do_update_nojs';
+  if ($_SERVER['REQUEST_METHOD'] == 'GET') {
+    list($percent, $message) = update_do_updates();
+    if ($percent == 100) {
+      $new_op = 'finished';
+    }
+  }
+  else {
+    // This is the first page so return some output immediately.
+    $percent = 0;
+    $message = 'Starting updates...';
+  }
+
+  drupal_set_html_head('<meta http-equiv="Refresh" content="0; URL=update.php?op='. $new_op .'">');
+  drupal_set_title('Updating');
+  $output = theme('progress_bar', $percent, $message);
+  $output .= '<p>Updating your site will take a few seconds.</p>';
+
+  return $output;
+}
+
+function update_finished_page() {
   drupal_set_title('Drupal database update');
   // NOTE: we can't use l() here because the URL would point to 'update.php?q=admin'.
-  $links[] = "<a href=\"index.php\">main page</a>";
-  $links[] = "<a href=\"index.php?q=admin\">administration pages</a>";
-  $output = theme('item_list', $links);
-  $output .= update_data($edit['start']);
-  $output .= '<p>Updates were attempted. If you see no failures above, you may proceed happily to the <a href="index.php?q=admin">administration pages</a>. Otherwise, you may need to update your database manually.</p>';
+  $links[] = '<a href="">main page</a>';
+  $links[] = '<a href="?q=admin">administration pages</a>';
+  $output = '<p>Updates were attempted. If you see no failures below, you may proceed happily to the <a href="?q=admin">administration pages</a>. Otherwise, you may need to update your database manually. All errors have been <a href="?q=admin/logs">logged</a>.</p>';
   if ($GLOBALS['access_check'] == FALSE) {
     $output .= "<p><strong>Reminder: don't forget to set the <code>\$access_check</code> value at the top of <code>update.php</code> back to <code>TRUE</code>.</strong>";
   }
+  $output .= theme('item_list', $links);
+
+  // Output a list of queries executed
+  if ($_SESSION['update_results']) {
+    $output .= '<div id="update-results">';
+    $output .= '<h2>The following queries were executed</h2>';
+    foreach ($_SESSION['update_results'] as $module => $updates) {
+      $output .= '<h3>'. $module .' module</h3>';
+      foreach ($updates as $number => $queries) {
+        $output .= '<h4>Update #'. $number .'</h4>';
+        $output .= '<ul>';
+        foreach ($queries as $query) {
+          if ($query['success']) {
+            $output .= '<li class="success">'. $query['query'] .'</li>';
+          }
+          else {
+            $output .= '<li class="failure"><strong>Failed:</strong> '. $query['query'] .'</li>';
+          }
+        }
+        if (!count($queries)) {
+          $output .= '<li class="none">No queries</li>';
+        }
+        $output .= '</ul>';
+      }
+    }
+    $output .= '</div>';
+    unset($_SESSION['update_results']);
+  }
+
   return $output;
 }
 
@@ -90,7 +517,7 @@ function update_info_page() {
   $output = "<ol>\n";
   $output .= "<li>Use this script to <strong>upgrade an existing Drupal installation</strong>. You don't need this script when installing Drupal from scratch.</li>";
   $output .= "<li>Before doing anything, backup your database. This process will change your database and its values, and some things might get lost.</li>\n";
-  $output .= "<li>Update your Drupal sources, check the notes below and <a href=\"update.php?op=update\">run the database upgrade script</a>. Don't upgrade your database twice as it may cause problems.</li>\n";
+  $output .= "<li>Update your Drupal sources, check the notes below and <a href=\"update.php?op=selection\">run the database upgrade script</a>. Don't upgrade your database twice as it may cause problems.</li>\n";
   $output .= "<li>Go through the various administration pages to change the existing and new settings to your liking.</li>\n";
   $output .= "</ol>";
   $output .= '<p>For more help, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.</p>';
@@ -109,34 +536,48 @@ function update_access_denied_page() {
 }
 
 include_once './includes/bootstrap.inc';
+drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
 drupal_maintenance_theme();
 
-if (isset($_GET["op"])) {
-  drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
+// Access check:
+if (($access_check == FALSE) || ($user->uid == 1)) {
+  update_include_install_files();
 
-  // Access check:
-  if (($access_check == 0) || ($user->uid == 1)) {
-    $op = isset($_POST['op']) ? $_POST['op'] : '';
-    switch ($op) {
-      case t('Update'):
-        $output = update_do_updates();
-        break;
+  update_fix_schema_version();
+  update_fix_sessions();
+  update_fix_watchdog();
 
-      default:
-        $output = update_selection_page();
-        break;
-    }
-  }
-  else {
-    $output = update_access_denied_page();
+  $op = isset($_REQUEST['op']) ? $_REQUEST['op'] : '';
+  switch ($op) {
+    case 'Update':
+      $output = update_update_page();
+      break;
+
+    case 'finished':
+      $output = update_finished_page();
+      break;
+
+    case 'do_update':
+      $output = update_do_update_page();
+      break;
+
+    case 'do_update_nojs':
+      $output = update_progress_page_nojs();
+      break;
+
+    case 'selection':
+      $output = update_selection_page();
+      break;
+
+    default:
+      $output = update_info_page();
+      break;
   }
 }
 else {
-  drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE);
-  $output = update_info_page();
+  $output = update_access_denied_page();
 }
 
 if (isset($output)) {
   print theme('maintenance_page', $output);
 }
-
-- 
GitLab