簡単な回答:
以下の行を削除またはコメントアウトするだけで、実際に使用されているデータベースエンコーディングに関係なく、常に機能します(utf8
、latin1
、など):
$pdo->exec('SET CHARACTER SET utf8');
長い答え:
これはPDOのバグではなく、MySQLのバグです。
実際のデータベースエンコーディングがlatin1
の場合 、ただし使用するもの:
SET CHARACTER SET utf8
(またはその逆:実際はutf8
、ただし、latin1
を使用します -重要な部分は、それが異なるということです )、その後、私が知る限り、MySQLはクライアントとサーバー間のすべてのトラフィックに対して(BLOB
の場合でも)文字セット変換を実行しようとします。 !)。
SET CHARACTER SET
を使用しない場合 ステートメント、スクリプト(PHP/PDOまたはPerl/DBI)で見たものから、接続文字セットはデフォルトでデータベース文字セットに設定されており、その場合、暗黙的な変換は行われません。
明らかに、この自動変換は、変換が発生することを望まないBLOBを強制終了するものです。
これをPHP/PDOとPerl/DBIの両方でテストしましたが、問題は簡単に再現できます。latin1
でデータベースを使用すると、どちらも失敗します。 SET CHARACTER SET utf8
のエンコードと使用 (またはその逆)。
完全にUTF8
になりたい場合 互換性がある場合は、次を使用してデータベースのエンコーディングを変更する必要があります:
ALTER DATABASE mydb CHARSET utf8;
これにより、すべてがUTF8
を使用するようになります 、およびBLOBも正常に機能します。
この破損の問題を引き起こす最小限のファイルはblob.bin
です。 1バイトの0xFF
。 Linuxでは、printf
を使用してこのテストファイルを作成できます。 コマンド:
printf "0xFF" > blob.bin
次に、問題を再現するスクリプトをテストします。
PHPテストコード:
<?php
$dbh = new PDO("mysql:host=127.0.0.1;dbname=test");
# If database encoding is NOT utf8, uncomment to break it:
# $dbh->exec("SET CHARACTER SET utf8");
$blob1 = file_get_contents("blob.bin");
$sth = $dbh->prepare(
"INSERT INTO pdo_blob (the_blob) VALUES(:the_blob)"
);
$sth->bindParam(":the_blob", $blob1, PDO::PARAM_LOB);
$sth->execute();
$sth = $dbh->prepare(
"SELECT the_blob FROM pdo_blob ORDER BY id DESC LIMIT 1"
);
$sth->execute();
$blob2 = null;
$sth->bindColumn(1, $blob2, PDO::PARAM_LOB);
$sth->fetch();
if ($blob1 == $blob2) {
echo "Equal\n";
} else {
echo "Not equal\n";
$arr1 = str_split($blob1);
$arr2 = str_split($blob2);
$i=0;
for ($i=0; $i<count($arr1); $i++) {
if ($arr1[$i] != $arr2[$i]) {
echo "First diff: " . dechex(ord($arr1[$i])) . " != "
. dechex(ord($arr2[$i])) . "\n";
break;
}
}
}
?>
Perlテストコード:
#!/usr/bin/perl -w
use strict;
use DBI qw(:sql_types);
my $dbh = DBI->connect("dbi:mysql:host=127.0.0.1;dbname=test");
# If database encoding is NOT utf8, uncomment to break it:
# $dbh->do("SET CHARACTER SET utf8");
open FILE, "blob.bin";
binmode FILE;
read(FILE, my $blob1, 100000000);
close FILE;
my $sth = $dbh->prepare(
"INSERT INTO pdo_blob (the_blob) VALUES(?)"
);
$sth->bind_param(1, $blob1, SQL_BLOB);
$sth->execute();
my ($blob2) = $dbh->selectrow_array(
"SELECT the_blob FROM pdo_blob ORDER BY id DESC LIMIT 1"
);
print ($blob1 eq $blob2 ? "Equal" : "Not equal") , "\n";