Kort svar:
Du skal blot slette eller kommentere linjen nedenfor, og det vil altid fungere, uanset hvilken databasekodning der virkelig er i brug (utf8
, latin1
osv.):
$pdo->exec('SET CHARACTER SET utf8');
Langt svar:
Dette er ikke PDO-fejl, dette er MySQL-fejl.
Når den faktiske databasekodning er latin1
, men du bruger:
SET CHARACTER SET utf8
(eller omvendt:faktisk er utf8
, men du bruger latin1
- vigtig del er, at det er anderledes ), så vil MySQL, så vidt jeg kan se, forsøge at udføre tegnsætkonvertering for al trafik mellem klient og server (selv for BLOB
!).
Hvis du IKKE bruger SET CHARACTER SET
sætning, ud fra hvad jeg ser for scripts (PHP/PDO eller Perl/DBI) er forbindelsestegnsæt som standard sat til at være databasetegnsættet, og i så fald finder ingen implicit konvertering sted.
Det er klart, at denne automatiske konvertering er det, der dræber BLOB'er, som ikke ønsker, at nogen konvertering skal ske.
Jeg har testet dette på både PHP/PDO og Perl/DBI, og problemet er let reproducerbart:begge vil mislykkes, hvis du bruger database med latin1
kodning og brug af SET CHARACTER SET utf8
(eller omvendt).
Hvis du vil være fuldt ud UTF8
kompatibel, bør du ændre kodningen af din database ved hjælp af:
ALTER DATABASE mydb CHARSET utf8;
Med dette vil alt bruge UTF8
, og BLOB'er vil også fungere fint.
Den minimale fil, der forårsager dette korruptionsproblem, er blob.bin
med enkelt byte 0xFF
. På Linux kan du oprette denne testfil ved hjælp af printf
kommando:
printf "0xFF" > blob.bin
Test nu scripts, der gengiver problemet:
PHP-testkode:
<?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-testkode:
#!/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";