MySQLをlatin1のまま使ってて、Djangoからだと文字化けた話

表題の通りMySQL適当に使ってたってだけの話なんですが、MySQLってインストールすると、デフォルトで文字コードは軒並みlatin1になってるわけです。utf8にしててくれても良いんじゃねと思わなくもないです。

mysql> status;
--------------
mysql  Ver 14.14 Distrib 5.1.51, for apple-darwin10.3.0 (i386) using readline 5.1
...
Server characterset:    latin1
Db     characterset:    latin1
Client characterset:    latin1
Conn.  characterset:    latin1
...
--------------

で、この設定のままutf8な文字列を突っ込んでて、でも別にmysqlコマンドやらpythonのMySQLdbやらから読み書きしても文字化けしないし、まぁ良いかと思ってほったらかしてた訳です。そしたらDjangoから読もうとしたら文字化けしたので、なんでやねんと思って調べたのでメモしておきます。

ちょっと調べたところDjangoはデータベースに対してutf8のデータを要求するようです。そうするとMySQLは自分の中にlatin1の文字列が入っていると思い込んでいるので、格納されている文字列をlatin1からutf8に変換しようとする訳ですね。結果utf8で格納されている文字列に対して、無理矢理latin1からutf8への変換をかけて化けるという事でした。

なぜDjango以外のケースで化けなかったかというと、クライアントがlatin1のデータを要求したせいで、データベースで変換処理が走らず、結果的には化けないと。まぁタマタマ動いていただけですね。ダメダメです。

さて化けてた理由はわかりましたが、すでにlatin1なテーブルにutf8で数十万件ほどデータを突っ込んでしまっています。my.cnfなんかで慌ててutf8な設定にしても当然解決されませんし、テーブルの文字コードを変更しても化けたままです。しょうがないから変換スクリプトでも書くかと思っていると、MySQLのリファレンスに解決方法が書かれていました。抜粋したものが以下になります。

ALTER TABLE t1 CHANGE c1 c1 BLOB;
ALTER TABLE t1 CHANGE c1 c1 TEXT CHARACTER SET utf8;

リファレンスによるとテキストなカラムとblobなカラム間では変換処理は走らないようです。なのでblobを経由させると変換処理を発生させずに、MySQLにデータに対する認識を変更できるという事ですね。この場合はlatin1だと思われているutf8な文字列を、utf8として認識させ直すことができました。ちなみに僕は一度blobを介さずにutf8にalter tableして、バックアップを取ってなくて終わったと冷や汗をかきましたが、すがる思いでlatin1に再度alter tableしたら、元通りになりました。運がよかったのか、そういうものなのかは未検証です。

という訳で、文字コードlatin1のまま使って、やっちまった!と思ったけど、ALTER TABLE使えば何とかなったという話でした。まぁ最初から文字コードくらいちゃんと設定しとけという事ですね。

[mysqld]
default-character-set=utf8

Leave a Reply

Your email address will not be published. Required fields are marked *