Benutzer: Gast
Eintrag: Typo3-Upgrade DB-Charset-Problem

Typo3-Upgrade DB-Charset-Problem

von: marcel
Kreise: Typo3

Früher hat man Typo3 mit einem Western-Europe-Charset installiert und die Datenbank/Tabellen liefen normalerweise unter latin1_swedish oder ähnliches.
Dann kam die Zeit als alles auf UTF8 umgestellt wurde. Da konnte man den Charset bei der Datenbankkommunikation noch ändern (db_init SET NAMES latin1 oder ähnlich).
Nun hat man ein ernstes Problem, wenn man mit alten Installationen weiter upgraden will. Die Datenbank benutzt einen Charset, die Kommunikation mit Typo3 ein anderer und im Endeffekt soll alles UTF8 sein.

Mit folgendem SQL kann man Zeichen in den einzelnen Feldern in der Datenbank eventuell zurechtrücken und zu UTF8 konvertieren:

Code: SQL
UPDATE my_table SET my_field = CONVERT(CAST(CONVERT(my_field USING latin1) AS BINARY) USING utf8);

Durch das DB-Compare und den Upgrade-Prozess erhält man wahrscheinlich Tabellen mit unterschiedlichen Zeichensätzen. Abfragen über Tabellen mit unterschiedlichen Zeichensätzen führen dann zu einem Fehler, weil diese unterschiedlich sortiert werden (COLLATION).

Mit folgendem SQL kann man die Tabellen vereinheitlichen:

Code: SQL
ALTER TABLE my_table CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;

Das Ganze notiere ich hier natürlich ohne Garantie und wenn man mit der Datenbank spielt sollte man immer vorher eine Sicherung machen (und am Besten auch testen, ob man die Sicherung zurückspielen kann).
Ich notiere es in erster Linie hier für mich selbst, weil ich das schon des öfteren suchte. Eventuell hilft es euch aber auch weiter.
Anregungen und Korrekturen gerne bitte per Mail oder unten als Kommentar.

Größe: 3584 Bytes
Typ: text/php

Hier ein kleines Script, um ein Array mit Tabellen und Feldern zu definieren, damit man nicht jeden CONVERT einzeln durchführen muss. Wie immer: keine Garantie ;-)
Achtung, bitte nicht daran stören: der Code ist quick’n‘dirty – musste schnell gehen.

db_to_utf8.php

Code: PHP
<?php
echo str_repeat( ' ', 256 );
?>
<html>
<head>
<title>Converter for old typo3-databases...</title>
</head>
<body>
<?php
die( "we are closed." );

// get the configuration for database-access (or define in class-call)
require_once ('localconf.obsolete.php');

$converter = new convertFields( $typo_db_host, $typo_db_username, $typo_db_password, $typo_db );

/*
 * uncomment or define tables and fields to process for
 * don't convert fields twice by calling this script twice!
 */
//$converter->addFields( "pages", "title, keywords, description" );
//$converter->addFields( "tt_content", "header, subheader, bodytext, altText, titleText, longdescURL" );
//$converter->addFields( "fe_users", "username, name, first_name, middle_name, last_name, address, city, company" );


// do it
$converter->convert();
class convertFields {
	/**
	 * Tables and fields which should be converted.
	 * Use $this->addFields() to define some.
	 * @var array
	 */
	protected $tables;
	
	/**
	 * The connection to the database
   * @var resource
   */
	protected $db;
	
	/**
   * @var array
   */
	protected $charset = array (
			'source' => "latin1",
			'target' => 'utf8' 
	);
	
	/**
   * @return void
   */
	public function __construct($database_host, $database_username, $database_password, $database) {
		$this->db = mysql_connect( $database_host, $database_username, $database_password, TRUE );
		if (! is_resource( $this->db )) {
			die( 'Could not connect to db!: ' . mysql_error() );
		}
		
		if (mysql_select_db( $database, $this->db ) === FALSE) {
			die( 'Could not select database!: ' . mysql_error() );
		}
	}
	
	/**
   * @return void
   */
	public function __destruct() {
		if (is_resource( $this->db )) {
			mysql_close( $this->db );
		}
	}
	
	/**
   * @param string $charset
   * @return void
   */
	public function setCharsetSource($charset) {
		$this->charset['source'] = $charset;
	}
	
	/**
   * @param string $charset
   * @return void
   */
	public function setCharsetTarget($charset) {
		$this->charset['target'] = $charset;
	}
	
	/**
   * @param string $table
   * @param string $fields Commaseparated list of fields
   * @return void
   */
	public function addFields($table, $fields) {
		$fieldsArr = explode( ",", $fields );
		$tmpArr = array ();
		foreach ( $fieldsArr as $field ) {
			if (trim( $field )) {
				$tmpArr[] = $field;
			}
		}
		
		if (is_array( $this->tables[$table] )) {
			$this->tables[$table] = array_merge( $this->tables[$table], $tmpArr );
		} else {
			$this->tables[$table] = $tmpArr;
		}
		
		$this->tables[$table] = array_unique( $this->tables[$table] );
	}
	
	/**
	 * call this to do the magick
	 * @return void
	 */
	public function convert() {
		if (! count( $this->tables )) {
			die ("There are no tables defined!");
		}
		
		foreach ( $this->tables as $table => $fields ) {
			echo "<br />processing table " . $table;
			
			if (! is_array( $fields )) {
				echo "<br /> - there are no fields defined: " . $field;
				continue;
			}
			
			foreach ( $fields as $field ) {
				echo "<br /> - field: " . $field;
				
				$sql = "UPDATE " . $table . " SET " . $field . " = CONVERT(CAST(CONVERT(" . $field . " USING " . $this->charset['source'] . ") AS BINARY) USING " . $this->charset['target'] . ")";
				#echo "<br />" . $sql;
				

				$db_res = mysql_query( $sql, $this->db );
				if (! is_resource( $db_res ) && mysql_errno( $this->db ) != 0) {
					echo " :: Could not execute query!: " . mysql_error( $this->db ) . " SQL: " . $sql;
				} else {
					echo " :: done";
				}
			}
			
			ob_flush();
			flush();
		}
	}
}

?>
</body>
</html>
Es gibt noch keine Kommentare.