Perl + MySQL で cp932 を使っている際、'表' 等のように 0x5C が含まれる文字を INSERT するとエラーになる場合の対処方法
悲しい大人の事情で、MySQL に cp932 のデータを保存する事になったので、そのメモ。
環境
CREATE DATABASE foo CHARACTER SET cp932; GRANT SELECT, DELETE, UPDATE, INSERT ON foo.* TO bar@localhost IDENTIFIED BY 'baz'; CREATE TABLE zip_codes( id INTEGER AUTO_INCREMENT, zip_code CHAR(7) NOT NULL, state_id INTEGER NOT NULL, city VARCHAR(256) NOT NULL, street VARCHAR(256), office VARCHAR(256), modify_date TIMESTAMP, PRIMARY KEY (id), KEY (zip_code), KEY (state_id) ) TYPE=InnoDB;
相談された際の my.cnf の内容
$ cat /etc/my.cnf [mysqld] #..snip.. default-character-set=cp932 skip-character-set-client-handshake #..snip..
渡されたエラーの再現コード
$ cat ./test.csv 5180801,24,"伊賀市","平野見能"
$ cat ./test.pl use strict; use warnings; use Carp; use FindBin qw($Bin); use Path::Class; use Text::CSV_XS; use DBI; my $csv = Text::CSV_XS->new({binary=>1}); my $fh = file($Bin, 'test.csv')->openr or croak $!; my $dbh = DBI->connect('DBI:mysql:foo', 'bar', 'baz'); my $sth = $dbh->prepare('insert into zip_codes (zip_code, state_id, city, street) value (?, ?, ?, ?)'); while(my $line = $fh->getline) { $line =~ s/\n$//; $csv->parse($line) or croak $line; my @fields = $csv->fields; $sth->execute(@fields); }
$ perl test.pl DBD::mysql::st execute failed: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''平野見能\')' at line 1 at testz.pl line 19, <GEN0> line 1.
修正してみた
まずは、DBD::mysql が default-character-set を利用出来ていない気がしたので my.cnf を下記のように修正してみた。
$ cat /etc/my.cnf [mysqld] #..snip.. default-character-set=cp932 # skip-character-set-client-handshake #..snip.. [client] default-character-set=cp932
これで INSERT ができるようになった!と思ったら・・・文字化けた状態で INSERT されていた orz
とりあえず perl 側で my.cnf を指定してみる。
my $dbh = DBI->connect('DBI:mysql:foo;mysql_read_default_file=/etc/my.cnf', 'bar', 'baz');
これで正しく文字化けせずに INSERT できるようになった。