むかしやってた映画のブログ( WordPress )のデータをこのブログに取り込もうとしたら、 mysqldump しといたデータが文字化けして読めなかった。 映画ブログのデータベースの文字コードは utf-8 にしてたけど、 Dreamhost の MySQL サーバーの defualt-character-set がおそらく latin1 になっていたと考えられ、 mysqldump するときに utf8 のデータが latin1 として export されてしまい文字化けを起こしていたっぽい。

vim で開いて e ++enc=utf8 してみたり、 nkr で変換しようと試みたり、ググって出てきた Perl のスクリプトで変換しようと試してみたけど直らなかった。この変換は MySQL 自身にやらせないとダメなのではと思い、以下を試したがダメ。

  • [x] latin1 で DB 作成し latin1 で import 、 utf8 で export
  • [x] latin1 で DB 作成し utf8 で import 、 utf8 で export

結局、 Stack Overflow にあった以下の方法で解決できた。

http://stackoverflow.com/a/18138899/1153050

一旦文字化けしたまま utf-8 の文字列として import し、以下の SQL を流す。

UPDATE [table_name] SET [column_name] = CONVERT(BINARY CONVERT([column_name] USING latin1) USING utf8);

やっぱり MySQL で化けたデータは MySQL で直すしかないみたいだった。

公開している Lokka の DB (MySQL) のデータを、手もとの Lokka の DB (SQLite) に取り込むときに毎回やり方を忘れるので書いておきます。

今回は MySQL to Sqlite converter という gist のコメント欄に書いてある以下の方法を試した。

sudo gem install sequel
sudo gem install sqlite3
sudo gem install mysql
rbenv rehash
sequel mysql://user:password@host/database -C sqlite://db.sqlite

これでデータを MySQL から SQLite に変換できた。Lokka をローカルで起動してアクセスしてみるが記事が表示されない。DB の中にはちゃんとデータが入ってるし、管理画面から記事一覧を表示するとちゃんと記事が表示される。どうも Post.published[] を返すみたい。

手でクエリを投げてみると

sqlite> select * from entries order by created_at desc limit 1;
             id = 960
        user_id = 1
    category_id = 5
           slug = eye-pro-is-shit
          title = Eye-Fi Pro を買ったけどクソだった
           body = [hitode909さんの日記](http://hitode909.hatenablog.com/entry/2013/05/24/093749 "hitode909の日記")...
           type = Post
          draft = 0
     created_at = 2013-10-06 07:28:00.000000
     updated_at = 2013-10-06 13:11:23.000000
frozen_tag_list =
         markup = redcarpet

draft = 0 なので表示されそうなもんなんだけど、よくよく調べてみると SQLite には Boolean 型がなくて、DataMapper は SQLite に Boolean 型のデータを保存するときは t/f の文字列で保存するみたい。これは盲点だった。

結局 draft = 0draft = 'f' に変えてあげればすべての記事を表示できるようになった。

一月くらい前から Lokka の master ブランチを自分のブログ用のブランチに merge してサイトをデプロイすると謎の白画面が出るようになっていて困っていた。現象は極めて謎で、ローカルの開発環境(RACK_ENV='development')では見られず、本番(RACK_ENV='production')だけで発生した。HTTP ステータスコードは 1054 が返ってきたりする。なんか変な gem でも入れてしまったかなと休みの日に動作検証したりしていたんだけどついぞ分からなかった。

SQL 弱者なので気がついてなかったんだけど、2月15日の commit で Site モデルに新しいフィールドが追加されていた(Add default markup in admin/site/edit · 2243dc5 · lokka/lokka ※この機能便利すね)。なので bundle exec rake db:migrate しないといけなかったわけだった。ローカルで動いている開発環境(データベースは SQLite)では Migration なんて行ってないんだけどエラーは出なかった。本番は MySQL で動いていて、こちらでだけエラーが出るようだった。

しかしいざ migrate しようとすると失敗する。

bundle exec rake db:migrate
Upgrading Database...
rake aborted!
Invalid default value for 'updated_at'

のようなエラーが出る。updated_at のデフォルト値がおかしいらしい。このときのテーブルの構造を見てみると以下のような感じだった。

mysql> desc entries;
+-----------------+--------------+------+-----+---------------------+-----------------------------+
| Field           | Type         | Null | Key | Default             | Extra                       |
+-----------------+--------------+------+-----+---------------------+-----------------------------+
| id              | int(11)      | NO   | PRI | NULL                | auto_increment              |
| user_id         | int(11)      | YES  |     | NULL                |                             |
| category_id     | int(11)      | YES  |     | NULL                |                             |
| slug            | varchar(255) | YES  |     | NULL                |                             |
| title           | varchar(255) | YES  |     | NULL                |                             |
| body            | text         | YES  |     | NULL                |                             |
| type            | text         | NO   |     | NULL                |                             |
| draft           | tinyint(1)   | YES  |     | 0                   |                             |
| created_at      | timestamp    | NO   |     | 0000-00-00 00:00:00 |                             |
| updated_at      | timestamp    | NO   |     | CURRENT_TIMESTAMP   | on update CURRENT_TIMESTAMP |
| frozen_tag_list | text         | YES  |     | NULL                |                             |
| markup          | varchar(255) | YES  |     | NULL                |                             |
+-----------------+--------------+------+-----+---------------------+-----------------------------+

調べてみたところ MySQL の Mode が NO_ZERO_DATE になっている場合、MySQL は timestamp 型のフィールドのデフォルト値に 0000-00-00 00:00:00 みたいな値を設定することを許さないらしい。 mysql - Invalid default value for 'create_date' timestamp field - Stack Overflow

検証用に別にテーブルを用意して bundle exec rake db:setup してみたところ、以下のような構造のテーブルができた。

mysql> desc entries;
+-----------------+------------------+------+-----+---------+----------------+
| Field           | Type             | Null | Key | Default | Extra          |
+-----------------+------------------+------+-----+---------+----------------+
| id              | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| user_id         | int(11)          | YES  |     | NULL    |                |
| category_id     | int(11)          | YES  |     | NULL    |                |
| slug            | varchar(255)     | YES  |     | NULL    |                |
| title           | varchar(255)     | YES  |     | NULL    |                |
| body            | text             | YES  |     | NULL    |                |
| markup          | varchar(255)     | YES  |     | NULL    |                |
| type            | varchar(50)      | NO   |     | NULL    |                |
| draft           | tinyint(1)       | YES  |     | 0       |                |
| created_at      | datetime         | YES  |     | NULL    |                |
| updated_at      | datetime         | YES  |     | NULL    |                |
| frozen_tag_list | text             | YES  |     | NULL    |                |
+-----------------+------------------+------+-----+---------+----------------+

created_atupdated_atdatetime 型になるらしい。なので以下のような ALTER 文を実行した。

mysql> alter table entries modify column created_at datetime, modify column updated_at datetime;
mysql> desc entries;
+-----------------+--------------+------+-----+---------+----------------+
| Field           | Type         | Null | Key | Default | Extra          |
+-----------------+--------------+------+-----+---------+----------------+
| id              | int(11)      | NO   | PRI | NULL    | auto_increment |
| user_id         | int(11)      | YES  |     | NULL    |                |
| category_id     | int(11)      | YES  |     | NULL    |                |
| slug            | varchar(255) | YES  |     | NULL    |                |
| title           | varchar(255) | YES  |     | NULL    |                |
| body            | text         | YES  |     | NULL    |                |
| type            | text         | NO   |     | NULL    |                |
| draft           | tinyint(1)   | YES  |     | 0       |                |
| created_at      | datetime     | YES  |     | NULL    |                |
| updated_at      | datetime     | YES  |     | NULL    |                |
| frozen_tag_list | text         | YES  |     | NULL    |                |
| markup          | varchar(255) | YES  |     | NULL    |                |
+-----------------+--------------+------+-----+---------+----------------+

これで最新のコードをデプロイしても真っ白画面になることはなくなった。以前遭遇した更新時に created_at の値が更新されてしまう問題 もフィールドの型が timestamp だったのが原因なのだと思う。SQLite から MySQL への移行は一筋縄では行かないことが分かった。

DataMapper はソースコード内の記述内容から動的に Migration を行えるけど、ActiveRecord みたいに $APP_ROOT/db/ ディレクトリに Migration ファイルを作ってくれたりしないので DB スキーマの変更が必要なことに気がつきにくい。便利だけど不便な感じがする。Rails で $APP_ROOT/db/ 以下にアホみたいにファイルが出来ていくの嫌だと思っていたけど、スキーマ変更に気がつかずコードをデプロイしてウェブアプリケーション停止みたいな自体は防げると思った。

ブログ書こうと思ってパソコン開いて「ついでに最新版の変更を取り込むか」とかやるとデプロイできなくなったりして書きたかった記事が書けず残念な感じになる。はてなブログでブログ書いてて画面が真っ白になったらひとでくんさんに不具合報告して直してもらえば良いので楽だと思う。