From 7914ad0f3f9b29d78e7321ad9addbff99de48a4f Mon Sep 17 00:00:00 2001
From: Steven Wittens <steven@10.no-reply.drupal.org>
Date: Sat, 21 Jan 2006 01:42:52 +0000
Subject: [PATCH] - #40515: Ensure UTF-8 character set on the database side
 (and include upgrade path for incorrectly set up databases)

---
 INSTALL.pgsql.txt            |   1 +
 database/database.mysql      | 177 ++++++++++++++++++++++-------------
 database/database.pgsql      |   2 +-
 database/updates.inc         | 142 ++++++++++++++++++++++++++++
 includes/database.mysql.inc  |  12 ++-
 includes/database.mysqli.inc |   8 ++
 includes/database.pgsql.inc  |  10 ++
 includes/install.inc         |   4 +-
 modules/system.module        |   8 +-
 modules/system/system.module |   8 +-
 update.php                   |  32 ++++---
 11 files changed, 321 insertions(+), 83 deletions(-)

diff --git a/INSTALL.pgsql.txt b/INSTALL.pgsql.txt
index 88ba13d00228..700111608691 100644
--- a/INSTALL.pgsql.txt
+++ b/INSTALL.pgsql.txt
@@ -41,6 +41,7 @@ INSTALLATION AND CONFIGURATION
      createdb --encoding=UNICODE --owner=username databasename
 
    If everything works correctly, you'll see a "CREATE DATABASE" notice.
+   Note that the database must be created with UTF-8 (Unicode) encoding.
 
 3. LOAD THE DRUPAL DATABASE SCHEMA
 
diff --git a/database/database.mysql b/database/database.mysql
index 635ae5046490..cb6917c3b606 100644
--- a/database/database.mysql
+++ b/database/database.mysql
@@ -1,19 +1,14 @@
--- MySQL dump 8.22
---
--- Host: localhost    Database: drupal_devel
--- Server version  3.23.52-nt
-
 --
 -- Table structure for table 'access'
 --
-
 CREATE TABLE access (
   aid tinyint(10) NOT NULL auto_increment,
   mask varchar(255) NOT NULL default '',
   type varchar(255) NOT NULL default '',
   status tinyint(2) NOT NULL default '0',
   PRIMARY KEY (aid)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'accesslog'
@@ -31,7 +26,8 @@ CREATE TABLE accesslog (
   timestamp int(11) unsigned NOT NULL default '0',
   KEY accesslog_timestamp (timestamp),
   PRIMARY KEY (aid)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'aggregator_category'
@@ -44,7 +40,8 @@ CREATE TABLE aggregator_category (
   block tinyint(2) NOT NULL default '0',
   PRIMARY KEY (cid),
   UNIQUE KEY title (title)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'aggregator_category_feed'
@@ -54,7 +51,8 @@ CREATE TABLE aggregator_category_feed (
   fid int(10) NOT NULL default '0',
   cid int(10) NOT NULL default '0',
   PRIMARY KEY (fid,cid)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'aggregator_category_item'
@@ -64,7 +62,8 @@ CREATE TABLE aggregator_category_item (
   iid int(10) NOT NULL default '0',
   cid int(10) NOT NULL default '0',
   PRIMARY KEY (iid,cid)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'aggregator_feed'
@@ -85,7 +84,8 @@ CREATE TABLE aggregator_feed (
   PRIMARY KEY (fid),
   UNIQUE KEY link (url),
   UNIQUE KEY title (title)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'aggregator_item'
@@ -100,7 +100,8 @@ CREATE TABLE aggregator_item (
   description longtext NOT NULL,
   timestamp int(11) default NULL,
   PRIMARY KEY (iid)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'authmap'
@@ -113,7 +114,8 @@ CREATE TABLE authmap (
   module varchar(128) NOT NULL default '',
   PRIMARY KEY (aid),
   UNIQUE KEY authname (authname)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'blocks'
@@ -130,7 +132,8 @@ CREATE TABLE blocks (
   throttle tinyint(1) DEFAULT '0' NOT NULL,
   visibility tinyint(1) DEFAULT '0' NOT NULL,
   pages text DEFAULT '' NOT NULL
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'book'
@@ -144,7 +147,8 @@ CREATE TABLE book (
   PRIMARY KEY (vid),
   KEY nid (nid),
   KEY parent (parent)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'boxes'
@@ -158,7 +162,8 @@ CREATE TABLE boxes (
   format int(4) NOT NULL default '0',
   PRIMARY KEY (bid),
   UNIQUE KEY info (info)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'cache'
@@ -172,7 +177,8 @@ CREATE TABLE cache (
   headers text,
   PRIMARY KEY (cid),
   INDEX expire (expire)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'comments'
@@ -197,7 +203,8 @@ CREATE TABLE comments (
   homepage varchar(255) default NULL,
   PRIMARY KEY (cid),
   KEY lid (nid)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structre for table 'contact'
@@ -212,7 +219,8 @@ CREATE TABLE contact (
   selected tinyint(1) NOT NULL default '0',
   PRIMARY KEY (cid),
   UNIQUE KEY category (category)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structre for table 'node_comment_statistics'
@@ -226,7 +234,8 @@ CREATE TABLE node_comment_statistics (
   comment_count int(10) unsigned NOT NULL default '0',
   PRIMARY KEY (nid),
   KEY node_comment_timestamp (last_comment_timestamp)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'client'
@@ -245,7 +254,8 @@ CREATE TABLE client (
   created int(11) NOT NULL default '0',
   changed int(11) NOT NULL default '0',
   PRIMARY KEY (cid)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'client_system'
@@ -256,7 +266,8 @@ CREATE TABLE client_system (
   name varchar(255) NOT NULL default '',
   type varchar(255) NOT NULL default '',
   PRIMARY KEY (cid,name)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'files'
@@ -274,7 +285,8 @@ CREATE TABLE files (
   list tinyint(1) unsigned NOT NULL default '0',
   KEY vid (vid),
   KEY fid (fid)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'filter_formats'
@@ -286,7 +298,8 @@ CREATE TABLE filter_formats (
   roles varchar(255) NOT NULL default '',
   cache tinyint(2) NOT NULL default '0',
   PRIMARY KEY (format)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'filters'
@@ -298,7 +311,8 @@ CREATE TABLE filters (
   delta tinyint(2) DEFAULT '0' NOT NULL,
   weight tinyint(2) DEFAULT '0' NOT NULL,
   INDEX (weight)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'flood'
@@ -308,7 +322,8 @@ CREATE TABLE flood (
   event varchar(64) NOT NULL default '',
   hostname varchar(128) NOT NULL default '',
   timestamp int(11) NOT NULL default '0'
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'forum'
@@ -321,7 +336,8 @@ CREATE TABLE forum (
   PRIMARY KEY (nid),
   KEY vid (vid),
   KEY tid (tid)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'history'
@@ -332,7 +348,8 @@ CREATE TABLE history (
   nid int(10) NOT NULL default '0',
   timestamp int(11) NOT NULL default '0',
   PRIMARY KEY (uid,nid)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'locales_meta'
@@ -346,7 +363,8 @@ CREATE TABLE locales_meta (
   plurals int(1) NOT NULL default '0',
   formula varchar(128) NOT NULL default '',
   PRIMARY KEY (locale)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'locales_source'
@@ -357,7 +375,8 @@ CREATE TABLE locales_source (
   location varchar(255) NOT NULL default '',
   source blob NOT NULL,
   PRIMARY KEY (lid)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'locales_target'
@@ -373,7 +392,8 @@ CREATE TABLE locales_target (
   KEY lang (locale),
   KEY plid (plid),
   KEY plural (plural)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'menu'
@@ -388,7 +408,8 @@ CREATE TABLE menu (
   weight tinyint(4) NOT NULL default '0',
   type int(2) unsigned NOT NULL default '0',
   PRIMARY KEY (mid)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'node'
@@ -418,7 +439,8 @@ CREATE TABLE node (
   KEY node_created (created),
   KEY node_changed (changed),
   KEY node_status_type (status, type, nid)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table `node_access`
@@ -432,7 +454,8 @@ CREATE TABLE node_access (
   grant_update tinyint(1) unsigned NOT NULL default '0',
   grant_delete tinyint(1) unsigned NOT NULL default '0',
   PRIMARY KEY (nid,gid,realm)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'node_revisions'
@@ -450,7 +473,8 @@ CREATE TABLE node_revisions (
   format int(4) NOT NULL default '0',
   PRIMARY KEY  (nid,vid),
   KEY uid (uid)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'profile_fields'
@@ -472,7 +496,8 @@ CREATE TABLE profile_fields (
   KEY category (category),
   UNIQUE KEY name (name),
   PRIMARY KEY (fid)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'profile_values'
@@ -484,7 +509,8 @@ CREATE TABLE profile_values (
   value text,
   KEY uid (uid),
   KEY fid (fid)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 
 --
@@ -498,7 +524,8 @@ CREATE TABLE url_alias (
   PRIMARY KEY (pid),
   UNIQUE KEY dst (dst),
   KEY src (src)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'permission'
@@ -509,7 +536,8 @@ CREATE TABLE permission (
   perm longtext,
   tid int(10) unsigned NOT NULL default '0',
   KEY rid (rid)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'poll'
@@ -520,7 +548,8 @@ CREATE TABLE poll (
   runtime int(10) NOT NULL default '0',
   active int(2) unsigned NOT NULL default '0',
   PRIMARY KEY (nid)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'poll_votes'
@@ -533,7 +562,8 @@ CREATE TABLE poll_votes (
   INDEX (nid),
   INDEX (uid),
   INDEX (hostname)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'poll_choices'
@@ -547,7 +577,8 @@ CREATE TABLE poll_choices (
   chorder int(2) NOT NULL default '0',
   PRIMARY KEY (chid),
   KEY nid (nid)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'role'
@@ -558,7 +589,8 @@ CREATE TABLE role (
   name varchar(32) NOT NULL default '',
   PRIMARY KEY (rid),
   UNIQUE KEY name (name)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'search_dataset'
@@ -568,7 +600,8 @@ CREATE TABLE search_dataset (
   type varchar(16) default NULL,
   data longtext NOT NULL,
   KEY sid_type (sid, type)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'search_index'
@@ -584,7 +617,8 @@ CREATE TABLE search_index (
   KEY sid_type (sid, type),
   KEY from_sid_type (fromsid, fromtype),
   KEY word (word)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'search_total'
@@ -594,7 +628,8 @@ CREATE TABLE search_total (
   word varchar(50) NOT NULL default '',
   count float default NULL,
   PRIMARY KEY (word)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'sessions'
@@ -611,7 +646,8 @@ CREATE TABLE sessions (
   KEY uid (uid),
   PRIMARY KEY (sid),
   KEY timestamp (timestamp)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'sequences'
@@ -621,7 +657,8 @@ CREATE TABLE sequences (
   name varchar(255) NOT NULL default '',
   id int(10) unsigned NOT NULL default '0',
   PRIMARY KEY (name)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'node_counter'
@@ -636,7 +673,8 @@ CREATE TABLE node_counter (
   KEY totalcount (totalcount),
   KEY daycount (daycount),
   KEY timestamp (timestamp)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'system'
@@ -650,11 +688,12 @@ 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 default 0,
+  schema_version smallint(3) NOT NULL default -1,
   weight int(2) NOT NULL default '0',
   PRIMARY KEY (filename),
   KEY (weight)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'term_data'
@@ -668,7 +707,8 @@ CREATE TABLE term_data (
   weight tinyint(4) NOT NULL default '0',
   PRIMARY KEY (tid),
   KEY vid (vid)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'term_hierarchy'
@@ -680,7 +720,8 @@ CREATE TABLE term_hierarchy (
   KEY tid (tid),
   KEY parent (parent),
   PRIMARY KEY (tid, parent)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'term_node'
@@ -692,7 +733,8 @@ CREATE TABLE term_node (
   KEY nid (nid),
   KEY tid (tid),
   PRIMARY KEY (tid,nid)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'term_relation'
@@ -703,7 +745,8 @@ CREATE TABLE term_relation (
   tid2 int(10) unsigned NOT NULL default '0',
   KEY tid1 (tid1),
   KEY tid2 (tid2)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'term_synonym'
@@ -714,7 +757,8 @@ CREATE TABLE term_synonym (
   name varchar(255) NOT NULL default '',
   KEY tid (tid),
   KEY name (name(3))
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'users'
@@ -742,7 +786,8 @@ CREATE TABLE users (
   PRIMARY KEY (uid),
   UNIQUE KEY name (name),
   KEY access (access)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'users_roles'
@@ -752,7 +797,8 @@ CREATE TABLE users_roles (
   uid int(10) unsigned NOT NULL default '0',
   rid int(10) unsigned NOT NULL default '0',
   PRIMARY KEY (uid, rid)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'variable'
@@ -762,7 +808,8 @@ CREATE TABLE variable (
   name varchar(48) NOT NULL default '',
   value longtext NOT NULL,
   PRIMARY KEY (name)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'vocabulary'
@@ -781,7 +828,8 @@ CREATE TABLE vocabulary (
   module varchar(255) NOT NULL default '',
   weight tinyint(4) NOT NULL default '0',
   PRIMARY KEY (vid)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'vocabulary_node_types'
@@ -791,7 +839,8 @@ CREATE TABLE vocabulary_node_types (
   vid int(10) unsigned NOT NULL DEFAULT '0',
   type varchar(32) NOT NULL DEFAULT '',
   PRIMARY KEY (vid, type)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Table structure for table 'watchdog'
@@ -809,7 +858,8 @@ CREATE TABLE watchdog (
   hostname varchar(128) NOT NULL default '',
   timestamp int(11) NOT NULL default '0',
   PRIMARY KEY (wid)
-) TYPE=MyISAM;
+) TYPE=MyISAM
+/*!40100 DEFAULT CHARACTER SET utf8 */ ;
 
 --
 -- Insert some default values
@@ -865,3 +915,4 @@ INSERT INTO variable (name, value) VALUES ('node_options_forum', 'a:1:{i:0;s:6:"
 INSERT INTO menu VALUES (2, 0, '', 'Primary links', '', 0, 115);
 INSERT INTO variable VALUES ('menu_primary_menu', 'i:2;');
 INSERT INTO variable VALUES ('menu_secondary_menu', 'i:2;');
+
diff --git a/database/database.pgsql b/database/database.pgsql
index 728f99962ef9..f9b5827f7d35 100644
--- a/database/database.pgsql
+++ b/database/database.pgsql
@@ -646,7 +646,7 @@ CREATE TABLE system (
   status integer NOT NULL default '0',
   throttle smallint NOT NULL default '0',
   bootstrap integer NOT NULL default '0',
-  schema_version smallint NOT NULL default 0,
+  schema_version smallint NOT NULL default -1,
   weight smallint NOT NULL default 0,
   PRIMARY KEY (filename)
 );
diff --git a/database/updates.inc b/database/updates.inc
index eaf1b5c849e1..c82f445f5756 100644
--- a/database/updates.inc
+++ b/database/updates.inc
@@ -1437,3 +1437,145 @@ function system_update_168() {
 
   return $ret;
 }
+
+function system_update_169() {
+  // Warn PGSQL admins if their database is set up incorrectly
+  if ($GLOBALS['db_type'] == 'pgsql') {
+    $encoding = db_result(db_query('SHOW server_encoding'));
+    if (!in_array(strtolower($encoding), array('unicode', 'utf8'))) {
+      $msg = 'Your PostgreSQL database is set up with the wrong character encoding ('. $encoding .'). It is possible it will not work as expected. It is advised to recreate it with UTF-8/Unicode encoding. More information can be found in the <a href="http://www.postgresql.org/docs/7.4/interactive/multibyte.html">PostgreSQL documentation</a>.';
+      watchdog('php', $msg, WATCHDOG_WARNING);
+      drupal_set_message($msg, 'status');
+    }
+  }
+
+  return _system_update_utf8(array(
+    'access', 'accesslog', 'aggregator_category',
+    'aggregator_category_feed', 'aggregator_category_item',
+    'aggregator_feed', 'aggregator_item', 'authmap', 'blocks',
+    'book', 'boxes', 'cache', 'comments', 'contact',
+    'node_comment_statistics', 'client', 'client_system', 'files',
+    'filter_formats', 'filters', 'flood', 'forum', 'history',
+    'locales_meta', 'locales_source', 'locales_target', 'menu',
+    'moderation_filters', 'moderation_roles', 'moderation_votes',
+    'node', 'node_access', 'node_revisions', 'profile_fields',
+    'profile_values', 'url_alias', 'permission', 'poll', 'poll_votes',
+    'poll_choices', 'role', 'search_dataset', 'search_index',
+    'search_total', 'sessions', 'sequences', 'node_counter',
+    'system', 'term_data', 'term_hierarchy', 'term_node',
+    'term_relation', 'term_synonym', 'users', 'users_roles', 'variable',
+    'vocabulary', 'vocabulary_node_types', 'watchdog'
+  ));
+}
+
+/**
+ * Converts tables to UTF-8 encoding.
+ * 
+ * This update is designed to be re-usable by contrib modules and is
+ * used by system_update_169().
+ */
+function _system_update_utf8($tables) {
+  // Are we starting this update for the first time?
+  if (!isset($_SESSION['update_utf8'])) {
+    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 array();
+        }
+        break;
+      case 'pgsql':
+        return array();
+    }
+
+    // See if database uses UTF-8 already
+    $url = parse_url($GLOBALS['db_url']);
+    $db_name = substr($url['path'], 1);
+    $create = array_pop(db_fetch_array(db_query('SHOW CREATE DATABASE `%s`', $db_name)));
+    if (preg_match('/utf8/i', $create)) {
+      return array();
+    }
+
+    // Make list of tables to convert
+    $_SESSION['update_utf8'] = $tables;
+    // Keep track of total for progress bar
+    $_SESSION['update_utf8_total'] = count($tables);
+  }
+
+  // Fetch remaining tables list
+  $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));
+  }
+
+  // Are we done?
+  if (count($list) == 0) {
+    unset($_SESSION['update_utf8']);
+    unset($_SESSION['update_utf8_total']);
+    return $ret;
+  }
+
+  // Progress percentage
+  $ret['#finished'] = 1 - (count($list) / $_SESSION['update_utf8_total']);
+  return $ret;
+}
+
+function system_update_170() {
+  if (!variable_get('update_170_done', false)) {
+    switch ($GLOBALS['db_type']) {
+      case 'pgsql':
+        $ret = array();
+        db_change_column($ret, 'system', 'schema_version', 'schema_version', 'smallint', array('not null' => TRUE, 'default' => -1));
+        break;
+
+      case 'mysql':
+      case 'mysqli':
+        db_query('ALTER TABLE {system} CHANGE schema_version schema_version smallint(3) not null default -1');
+        break;
+    }
+    // Set schema version -1 (uninstalled) for disabled modules (only affects contrib).
+    db_query('UPDATE {system} SET schema_version = -1 WHERE status = 0 AND schema_version = 0');
+  }
+  return array();
+}
diff --git a/includes/database.mysql.inc b/includes/database.mysql.inc
index c41e00494bbe..445a798167c1 100644
--- a/includes/database.mysql.inc
+++ b/includes/database.mysql.inc
@@ -62,7 +62,7 @@ function db_connect($url) {
   if (!mysql_select_db(substr($url['path'], 1))) {
     drupal_maintenance_theme();
     drupal_set_title('Unable to select database');
-    print theme('maintenance_page', '<p>We were able to connect to the MySQL database server (which means your username and password is okay) but not able to select the database.</p>
+    print theme('maintenance_page', '<p>We were able to connect to the MySQL database server (which means your username and password are okay) but not able to select the database.</p>
 <p>The MySQL error was: '. theme('placeholder', mysql_error($connection)) .'.</p>
 <p>Currently, the database is '. theme('placeholder', substr($url['path'], 1)) .'. The username is '. theme('placeholder', $url['user']) .' and the database server is '. theme('placeholder', $url['host']) .'.</p>
 <ul>
@@ -74,6 +74,16 @@ function db_connect($url) {
     exit;
   }
 
+  /* On MySQL 4.1 and later, force UTF-8 */
+  if (version_compare(mysql_get_server_info(), '4.1.0', '>=')) {
+    mysql_query('SET NAMES "utf8"', $connection);
+    mysql_query('SET collation_connection="utf8_general_ci"', $connection);
+    mysql_query('SET collation_server="utf8_general_ci"', $connection);
+    mysql_query('SET character_set_client="utf8"', $connection);
+    mysql_query('SET character_set_connection="utf8"', $connection);
+    mysql_query('SET character_set_results="utf8"', $connection);
+    mysql_query('SET character_set_server="utf8"', $connection);
+  }
   return $connection;
 }
 
diff --git a/includes/database.mysqli.inc b/includes/database.mysqli.inc
index 8645b7378ebd..3fba6092f921 100644
--- a/includes/database.mysqli.inc
+++ b/includes/database.mysqli.inc
@@ -71,6 +71,14 @@ function db_connect($url) {
     exit;
   }
 
+  /* Force UTF-8 */
+  mysqli_query($connection, 'SET NAMES "utf8"');
+  mysqli_query($connection, 'SET collation_connection="utf8_general_ci"');
+  mysqli_query($connection, 'SET collation_server="utf8_general_ci"');
+  mysqli_query($connection, 'SET character_set_client="utf8"');
+  mysqli_query($connection, 'SET character_set_connection="utf8"');
+  mysqli_query($connection, 'SET character_set_results="utf8"');
+  mysqli_query($connection, 'SET character_set_server="utf8"');
 
   /**
    * from: http://bugs.php.net/bug.php?id=33772
diff --git a/includes/database.pgsql.inc b/includes/database.pgsql.inc
index 89e6b9d7bff1..8375c6366232 100644
--- a/includes/database.pgsql.inc
+++ b/includes/database.pgsql.inc
@@ -334,6 +334,16 @@ function db_unlock_tables() {
   db_query('COMMIT');
 }
 
+/**
+ * Verify if the database is set up correctly.
+ */
+function db_check_setup() {
+  $encoding = db_result(db_query('SHOW server_encoding'));
+  if (!in_array(strtolower($encoding), array('unicode', 'utf8'))) {
+    drupal_set_message(t('Your PostgreSQL database is set up with the wrong character encoding (%encoding). It is possibile it will not work as expected. It is advised to recreate it with UTF-8/Unicode encoding. More information can be found in the <a href="%url">PostgreSQL documentation</a>.', array('%encoding' => $encoding, '%url' => 'http://www.postgresql.org/docs/7.4/interactive/multibyte.html')), 'status');
+  }
+}
+
 /**
  * @} End of "ingroup database".
  */
diff --git a/includes/install.inc b/includes/install.inc
index c62039091e3f..b9db13c97056 100644
--- a/includes/install.inc
+++ b/includes/install.inc
@@ -1,8 +1,8 @@
 <?php
 // $Id$
 
-define('SCHEMA', 0);
-define('SCHEMA_MIN', 1);
+define('SCHEMA_UNINSTALLED', -1);
+define('SCHEMA_INSTALLED', 0);
 
 
 // The system module (Drupal core) is currently a special case
diff --git a/modules/system.module b/modules/system.module
index 2f3a41385289..bf2250d79d62 100644
--- a/modules/system.module
+++ b/modules/system.module
@@ -472,6 +472,10 @@ function system_view_general() {
   $form['cron'] = array('#type' => 'fieldset', '#title' => t('Cron jobs'), '#collapsible' => TRUE, '#collapsed' => TRUE);
   $form['cron'] = array_merge($form['cron'], system_cron_settings());
 
+  // Check database setup if necessary
+  if (function_exists('db_check_setup') && empty($_POST)) {
+    db_check_setup();
+  }
   return $form;
 }
 
@@ -1010,8 +1014,8 @@ function system_modules_submit($form_id, $edit) {
   foreach ($new_modules as $module) {
     // Set the installed schema version for newly-enabled modules
     $versions = drupal_get_schema_versions($module);
-    if ($versions !== FALSE && drupal_get_installed_schema_version($module) == 0) {
-      drupal_set_installed_schema_version($module, max($versions));
+    if (drupal_get_installed_schema_version($module) == SCHEMA_UNINSTALLED) {
+      drupal_set_installed_schema_version($module, $versions ? max($versions) : SCHEMA_INSTALLED);
     }
   }
 
diff --git a/modules/system/system.module b/modules/system/system.module
index 2f3a41385289..bf2250d79d62 100644
--- a/modules/system/system.module
+++ b/modules/system/system.module
@@ -472,6 +472,10 @@ function system_view_general() {
   $form['cron'] = array('#type' => 'fieldset', '#title' => t('Cron jobs'), '#collapsible' => TRUE, '#collapsed' => TRUE);
   $form['cron'] = array_merge($form['cron'], system_cron_settings());
 
+  // Check database setup if necessary
+  if (function_exists('db_check_setup') && empty($_POST)) {
+    db_check_setup();
+  }
   return $form;
 }
 
@@ -1010,8 +1014,8 @@ function system_modules_submit($form_id, $edit) {
   foreach ($new_modules as $module) {
     // Set the installed schema version for newly-enabled modules
     $versions = drupal_get_schema_versions($module);
-    if ($versions !== FALSE && drupal_get_installed_schema_version($module) == 0) {
-      drupal_set_installed_schema_version($module, max($versions));
+    if (drupal_get_installed_schema_version($module) == SCHEMA_UNINSTALLED) {
+      drupal_set_installed_schema_version($module, $versions ? max($versions) : SCHEMA_INSTALLED);
     }
   }
 
diff --git a/update.php b/update.php
index 1e929e2320ac..136058f5af41 100644
--- a/update.php
+++ b/update.php
@@ -132,7 +132,7 @@ function db_change_column(&$ret, $table, $column, $column_new, $type, $attribute
 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
+    // temporary variables to prevent the updates from being executed twice and
     // throwing errors.
     switch ($update_start) {
       case '2005-04-14':
@@ -151,6 +151,9 @@ function update_fix_schema_version() {
         break;
 
     }
+    // The schema_version column (added below) was changed during 4.7beta.
+    // Update_170 is only for those beta users.
+    variable_set('update_170_done', TRUE);
 
     $sql_updates = array(
       '2004-10-31: first update since Drupal 4.5.0 release' => 110,
@@ -174,18 +177,22 @@ function update_fix_schema_version() {
       '2005-11-14' => 154, '2005-11-27' => 155, '2005-12-03' => 156,
     );
 
+    // Add schema version column
     switch ($GLOBALS['db_type']) {
       case 'pgsql':
         $ret = array();
-        db_add_column($ret, 'system', 'schema_version', 'smallint', array('not null' => TRUE, 'default' => 0));
+        db_add_column($ret, 'system', 'schema_version', 'smallint', array('not null' => TRUE, 'default' => -1));
         break;
 
       case 'mysql':
       case 'mysqli':
-        db_query('ALTER TABLE {system} ADD schema_version smallint(2) unsigned not null default 0');
+        db_query('ALTER TABLE {system} ADD schema_version smallint(3) not null default -1');
         break;
     }
+    // Set all enabled (contrib) modules to schema version 0 (installed)
+    db_query('UPDATE {system} SET schema_version = 0 WHERE status = 1');
 
+    // Set schema version for core
     drupal_set_installed_schema_version('system', $sql_updates[$update_start]);
     variable_del('update_start');
   }
@@ -276,7 +283,7 @@ function update_fix_watchdog() {
 function update_data($module, $number) {
   $ret = module_invoke($module, 'update_'. $number);
   // Assume the update finished unless the update results indicate otherwise.
-  $finished = TRUE;
+  $finished = 1;
   if (isset($ret['#finished'])) {
     $finished = $ret['#finished'];
     unset($ret['#finished']);
@@ -294,7 +301,7 @@ function update_data($module, $number) {
   }
   $_SESSION['update_results'][$module][$number] = array_merge($_SESSION['update_results'][$module][$number], $ret);
 
-  if ($finished) {
+  if ($finished == 1) {
     // Update the installed version
     drupal_set_installed_schema_version($module, $number);
   }
@@ -332,11 +339,11 @@ function update_selection_page() {
   $form['has_js'] = array(
     '#type' => 'hidden',
     '#default_value' => FALSE,
-    '#attributes' => array('id' => 'edit-has_js')
+    '#attributes' => array('id' => 'edit-has_js'),
   );
   $form['submit'] = array(
     '#type' => 'submit',
-    '#value' => 'Update'
+    '#value' => 'Update',
   );
 
   drupal_set_title('Drupal database update');
@@ -375,7 +382,7 @@ function update_progress_page() {
 
   drupal_set_title('Updating');
   $output = '<div id="progress"></div>';
-  $output .= '<p>Updating your site will take a few seconds.</p>';
+  $output .= '<p>Please wait while your site is being updated.</p>';
   return $output;
 }
 
@@ -389,9 +396,10 @@ function update_progress_page() {
 function update_do_updates() {
   while (($update = reset($_SESSION['update_remaining']))) {
     $update_finished = update_data($update['module'], $update['version']);
-    if ($update_finished) {
+    if ($update_finished == 1) {
       // Dequeue the completed update.
       unset($_SESSION['update_remaining'][key($_SESSION['update_remaining'])]);
+      $update_finished = 0; // Make sure this step isn't counted double
     }
     if (timer_read('page') > 1000) {
       break;
@@ -399,12 +407,12 @@ function update_do_updates() {
   }
 
   if ($_SESSION['update_total']) {
-    $percent = floor(($_SESSION['update_total'] - count($_SESSION['update_remaining'])) / $_SESSION['update_total'] * 100);
+    $percent = floor(($_SESSION['update_total'] - count($_SESSION['update_remaining']) + $update_finished) / $_SESSION['update_total'] * 100);
   }
   else {
     $percent = 100;
   }
-  return array($percent, 'Updating '. $update['module'] .' module');
+  return array($percent, isset($update['module']) ? 'Updating '. $update['module'] .' module' : 'Updating complete');
 }
 
 function update_do_update_page() {
@@ -438,7 +446,7 @@ function update_progress_page_nojs() {
   else {
     // This is the first page so return some output immediately.
     $percent = 0;
-    $message = 'Starting updates...';
+    $message = 'Starting updates';
   }
 
   drupal_set_html_head('<meta http-equiv="Refresh" content="0; URL=update.php?op='. $new_op .'">');
-- 
GitLab