Kurze Antwort:
Einfach Zeile darunter löschen oder auskommentieren, und es funktioniert immer, egal welche Datenbankkodierung tatsächlich verwendet wird (utf8
, latin1
, usw.):
$pdo->exec('SET CHARACTER SET utf8');
Lange Antwort:
Das ist kein PDO-Bug, das ist ein MySQL-Bug.
Wenn die tatsächliche Datenbankcodierung latin1
ist , aber Sie verwenden:
SET CHARACTER SET utf8
(oder umgekehrt:aktuell ist utf8
, aber Sie verwenden latin1
- wichtiger Teil ist, dass es anders ist ), dann versucht MySQL, soweit ich das beurteilen kann, eine Zeichensatzkonvertierung für den gesamten Datenverkehr zwischen Client und Server durchzuführen (selbst für BLOB
!).
Wenn Sie SET CHARACTER SET
NICHT verwenden Anweisung, von dem, was ich für Skripte (PHP/PDO oder Perl/DBI) sehe, ist der Verbindungszeichensatz standardmäßig auf den Datenbankzeichensatz eingestellt, und in diesem Fall findet keine implizite Konvertierung statt.
Offensichtlich tötet diese automatische Konvertierung BLOBs, die keine Konvertierung wünschen.
Ich habe dies sowohl auf PHP/PDO als auch auf Perl/DBI getestet, und das Problem ist leicht reproduzierbar:Beide schlagen fehl, wenn die Datenbank mit latin1
verwendet wird Codierung und Verwendung von SET CHARACTER SET utf8
(oder umgekehrt).
Wenn Sie vollständig UTF8
sein möchten kompatibel, sollten Sie die Kodierung Ihrer Datenbank ändern mit:
ALTER DATABASE mydb CHARSET utf8;
Damit wird alles UTF8
verwenden , und BLOBs funktionieren ebenfalls einwandfrei.
Die minimale Datei, die dieses Beschädigungsproblem verursacht, ist blob.bin
mit Einzelbyte 0xFF
. Unter Linux können Sie diese Testdatei mit printf
erstellen Befehl:
printf "0xFF" > blob.bin
Testen Sie nun Skripte, die das Problem reproduzieren:
PHP-Testcode:
<?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-Testcode:
#!/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";