Skip to content

Instantly share code, notes, and snippets.

@pokutuna
Last active March 21, 2017 00:54
Show Gist options
  • Save pokutuna/b48973f3edaeed0324f24baadbda790e to your computer and use it in GitHub Desktop.
Save pokutuna/b48973f3edaeed0324f24baadbda790e to your computer and use it in GitHub Desktop.
DBD::mysql 4.042
local.*/
cpanfile.*.snapshot

DBD::mysql 4.042

  • DBD::mysql 4.0414.042 の間で mysql_enable_utf8(mb4) 時の文字列の扱いに変化があった
    • 日本語を扱う上で影響が大きい
    • 正確には 4.0414.041_01 の差分 diff
  • 4.041 まで MySQL server へ送る際、バイナリ文字列はそのまま、テキスト文字列は encode_utf8 相当の処理を経由しよしなにしていた
  • 4.042 からはバイナリ文字列を渡しても encode_utf8 相当の処理が行われる?
    • Input bind parameters of binary types (SQL_BIT, SQL_BLOB, SQL_BINARY, SQL_VARBINARY and SQL_LONGVARBINARY) are not touched regardless of the mysql_enable_utf8 attribute state.

      • とあるが常に encode_utf8 されていそう
      • binary types なカラムを SELECT してきた値はバイナリのまま(従来より)

JSON を INSERT する時の扱い

  • アプリケーション内部では JSON はバイナリ文字列で保持したい
    • シリアライズした表現であることを期待している
    • そのままクライアントに返したり Redis 等別のミドルウェアに渡したりしたい
  • バイナリ型のカラムへの INSERT 時にバイナリ文字列を渡しても encode_utf8 相当の処理が行われるのはこまる
  • JSON は諦めて MySQL へ入れるところだけ decode_utf8 してしまってもよいが、普通にバイナリデータを入れたい時はどうするのか
  • テストコードを読んでいると binary types とは $sth->bind_param(1, $blob, DBI::SQL_BINARY) のような INSERT をさすようだ
    • DBI$sth->execute
      • If any arguments are given, then execute will effectively call "bind_param" for each value before executing the statement. Values bound in this way are usually treated as SQL_VARCHAR types unless the driver can determine the correct type (which is rare), or unless bind_param (or bind_param_inout) has already been used to specify the type.

    • determine the correct type できなかった場合 SQL_VARCHAR として扱われる
    • これによってバイナリ文字列の INSERT に失敗していそう
  • 多くのライブラリは execute を呼んでいる
    • 型を指定する方法あるのか? なさそう 😇
    • mysql_enable_utf8(mb4) をやめてしまうか? SELECT した値を都度 decode_utf8 することになる...
requires 'DBD::mysql', '== 4.041';
requires 'DBIx::Handler::Sunny';
requires 'DDP';
requires 'Encode';
requires 'JSON::XS';
requires 'DBD::mysql', '== 4.042';
requires 'DBIx::Handler::Sunny';
requires 'DDP';
requires 'Encode';
requires 'JSON::XS';
Installing modules using /Users/pokutuna/tmp/20170320.4UDqCS/4041/cpanfile
Complete! Modules were installed into /Users/pokutuna/tmp/20170320.4UDqCS/4041/local
Wide character in print at /Users/pokutuna/tmp/20170320.4UDqCS/4041/local/lib/perl5/Data/Printer.pm line 181.
{
    blob    "�����",
    id      1,
    text    "お姉ちゃん" (U),
    title   "4.041: Text"
}
Wide character in print at /Users/pokutuna/tmp/20170320.4UDqCS/4041/local/lib/perl5/Data/Printer.pm line 181.
{
    blob    "�����",
    id      2,
    text    "お姉ちゃん" (U),
    title   "4.041: Binary"
}
Wide character in print at /Users/pokutuna/tmp/20170320.4UDqCS/4041/local/lib/perl5/Data/Printer.pm line 181.
{
    blob    "{"key":"�����"}",
    id      3,
    text    "{"key":"お姉ちゃん"}" (U),
    title   "4.041: JSON(utf8=0)"
}
Wide character in print at /Users/pokutuna/tmp/20170320.4UDqCS/4041/local/lib/perl5/Data/Printer.pm line 181.
{
    blob    "{"key":"�����"}",
    id      4,
    text    "{"key":"お姉ちゃん"}" (U),
    title   "4.041: JSON(utf8=1)"
}
{
    blob   "",
    text   1
}
Installing modules using /Users/pokutuna/tmp/20170320.4UDqCS/4042/cpanfile
Complete! Modules were installed into /Users/pokutuna/tmp/20170320.4UDqCS/4042/local
Wide character in print at /Users/pokutuna/tmp/20170320.4UDqCS/4042/local/lib/perl5/Data/Printer.pm line 181.
{
    blob    "�����",
    id      5,
    text    "お姉ちゃん" (U),
    title   "4.042: Text"
}
{
    blob    "�����",
    id      6,
    text    "お姉ちゃん" (U),
    title   "4.042: Binary"
}
Wide character in print at /Users/pokutuna/tmp/20170320.4UDqCS/4042/local/lib/perl5/Data/Printer.pm line 181.
{
    blob    "{"key":"�����"}",
    id      7,
    text    "{"key":"お姉ちゃん"}" (U),
    title   "4.042: JSON(utf8=0)"
}
{
    blob    "{"key":"�����"}",
    id      8,
    text    "{"key":"お姉ちゃん"}" (U),
    title   "4.042: JSON(utf8=1)"
}
{
    blob   "",
    text   1
}
mysql> select * from dbd_mysql_test;
+----+---------------------+------------------------------------------+------------------------------------------+
| id | title               | text                                     | blob                                     |
+----+---------------------+------------------------------------------+------------------------------------------+
|  1 | 4.041: Text         | お姉ちゃん                               | お姉ちゃん                               |
|  2 | 4.041: Binary       | お姉ちゃん                               | お姉ちゃん                               |
|  3 | 4.041: JSON(utf8=0) | {"key":"お姉ちゃん"}                     | {"key":"お姉ちゃん"}                     |
|  4 | 4.041: JSON(utf8=1) | {"key":"お姉ちゃん"}                     | {"key":"お姉ちゃん"}                     |
|  5 | 4.042: Text         | お姉ちゃん                               | お姉ちゃん                               |
|  6 | 4.042: Binary       | �����                          | �����                          |
|  7 | 4.042: JSON(utf8=0) | {"key":"お姉ちゃん"}                     | {"key":"お姉ちゃん"}                     |
|  8 | 4.042: JSON(utf8=1) | {"key":"�����"}                | {"key":"�����"}                |
+----+---------------------+------------------------------------------+------------------------------------------+
8 rows in set (0.01 sec)
#!/bin/bash
mysql -uroot -e 'source table.sql'
for version in 4041 4042; do
carton install --cpanfile=cpanfile.$version --path=local.$version
PERL_CARTON_PATH=local.$version PERL_CARTON_CPANFILE=cpanfile.$version carton exec -- ./str.pl
done
#!/usr/bin/env perl
use strict;
use warnings;
use utf8;
use DBD::mysql;
use DBI;
use DDP { show_unicode => 1, use_prototypes => 0 };
use Encode qw(is_utf8 encode_utf8);
use JSON::XS;
my $dbh = DBI->connect(
'dbi:mysql:dbname=dbd_mysql_test;host=localhost',
'root',
'',
{
AutoCommit => 1,
RaiseError => 1,
PrintError => 0,
ShowErrorStatement => 1,
mysql_enable_utf8mb4 => 1,
},
);
sub insert {
my ($info, $value, $type) = @_;
my $version = $DBD::mysql::VERSION;
my $title = "$version: $info";
my $query = <<SQL;
INSERT INTO dbd_mysql_test
SET `title` = ?,
`text` = ?,
`blob` = ?
SQL
my $sth = $dbh->prepare($query);
$sth->bind_param(1, $title);
$sth->bind_param(2, $value, $type);
$sth->bind_param(3, $value, $type);
$sth->execute;
$sth->finish;
$dbh->selectrow_hashref('SELECT * FROM dbd_mysql_test ORDER BY id DESC LIMIT 1');
}
my $str = 'お姉ちゃん'; # Text string
my $json = JSON::XS->new->utf8(1)->canonical;
p insert(
'JSON(utf8=1) with SQL_VARCHAR',
$json->encode({ key => $str }),
DBI::SQL_VARCHAR,
);
p (my $a = insert(
'JSON(utf8=1) with SQL_BINARY',
$json->encode({ key => $str }),
DBI::SQL_BINARY,
));
p $json->decode($a->{blob});
#!/usr/bin/env perl
use strict;
use warnings;
use utf8;
use DBD::mysql;
use DBIx::Handler::Sunny;
use DDP { show_unicode => 1, use_prototypes => 0 };
use Encode qw(is_utf8 encode_utf8);
use JSON::XS;
my $db = DBIx::Handler::Sunny->connect(
'dbi:mysql:dbname=dbd_mysql_test;host=localhost',
'root',
'',
{
AutoCommit => 1,
RaiseError => 1,
PrintError => 0,
ShowErrorStatement => 1,
mysql_enable_utf8mb4 => 1,
},
);
my $str = 'お姉ちゃん'; # Text string
sub insert {
my ($info, $value) = @_;
my $version = $DBD::mysql::VERSION;
my $title = "$version: $info";
$db->query(<<SQL, { title => $title, value => $value });
INSERT INTO dbd_mysql_test
SET `title` = :title,
`text` = :value,
`blob` = :value
SQL
my $row = $db->select_row(
'SELECT * FROM dbd_mysql_test ORDER BY id DESC LIMIT 1'
);
return $row;
}
p (my $row = insert('Text', $str));
p insert('Binary', encode_utf8($str));
p insert(
'JSON(utf8=0)',
# utf8(0)->encode() returns text string
JSON::XS->new->utf8(0)->canonical->encode({ key => $str })
);
p insert(
'JSON(utf8=1)',
# utf8(1)->encode() returns binary string
JSON::XS->new->utf8(1)->canonical->encode({ key => $str })
);
p +{
text => is_utf8($row->{text}),
blob => is_utf8($row->{blob}),
};
CREATE DATABASE IF NOT EXISTS `dbd_mysql_test`;
USE `dbd_mysql_test`;
CREATE TABLE IF NOT EXISTS `dbd_mysql_test` (
`id` INT NOT NULL AUTO_INCREMENT,
`title` VARCHAR(100),
`text` TEXT,
`blob` BLOB,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment