Risposta breve:
Basta eliminare o commentare la riga di seguito e funzionerà sempre, indipendentemente dalla codifica del database effettivamente in uso (utf8
, latin1
, ecc):
$pdo->exec('SET CHARACTER SET utf8');
Risposta lunga:
Questo non è un bug PDO, questo è un bug di MySQL.
Quando la codifica effettiva del database è latin1
, ma usi:
SET CHARACTER SET utf8
(o viceversa:actual è utf8
, ma usi latin1
- la parte importante è che è diverso ), quindi, per quanto ne so, MySQL proverà a eseguire la conversione del set di caratteri per tutto il traffico tra client e server (anche per BLOB
!).
Se NON usi SET CHARACTER SET
istruzione, da quello che vedo per gli script (PHP/PDO o Perl/DBI) il set di caratteri di connessione per impostazione predefinita è impostato per essere il set di caratteri del database e in tal caso non avviene alcuna conversione implicita.
Ovviamente, questa conversione automatica è ciò che uccide i BLOB, che non vogliono che avvenga alcuna conversione.
L'ho testato sia su PHP/PDO che su Perl/DBI e il problema è facilmente riproducibile:entrambi falliranno se si utilizza il database con latin1
codifica e usando SET CHARACTER SET utf8
(o viceversa).
Se vuoi essere completamente UTF8
compatibile, dovresti cambiare la codifica del tuo database usando:
ALTER DATABASE mydb CHARSET utf8;
Con questo, tutto utilizzerà UTF8
e anche i BLOB funzioneranno bene.
Il file minimo che causa questo problema di corruzione è blob.bin
con un singolo byte 0xFF
. Su Linux, puoi creare questo file di prova usando printf
comando:
printf "0xFF" > blob.bin
Ora, prova gli script che riproducono il problema:
Codice test 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;
}
}
}
?>
Codice di prova 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";