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