Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Um BLOB é convertido usando o charset atual/padrão no MySQL?

Resposta curta:


Simplesmente exclua ou comente a linha abaixo e sempre funcionará, não importa qual codificação de banco de dados esteja realmente em uso (utf8 , latin1 , etc):
$pdo->exec('SET CHARACTER SET utf8');

Resposta longa:


Este não é um bug do PDO, este é um bug do MySQL.

Quando a codificação real do banco de dados é latin1 , mas você usa:
SET CHARACTER SET utf8

(ou vice-versa:real é utf8 , mas você usa latin1 - parte importante é que é diferente ), então, até onde eu sei, o MySQL tentará realizar a conversão de charset para todo o tráfego entre cliente e servidor (mesmo para BLOB !).

Se você NÃO usar SET CHARACTER SET declaração, pelo que vejo para scripts (PHP/PDO ou Perl/DBI), o conjunto de caracteres de conexão por padrão é definido como o conjunto de caracteres do banco de dados e, nesse caso, nenhuma conversão implícita ocorre.

Obviamente, essa conversão automática é o que mata os BLOBs, que não querem que nenhuma conversão aconteça.

Eu testei isso em PHP/PDO e Perl/DBI, e o problema é facilmente reproduzível:ambos falharão se estiver usando banco de dados com latin1 codificação e usando SET CHARACTER SET utf8 (ou vice-versa).

Se você deseja ser totalmente UTF8 compatível, você deve alterar a codificação do seu banco de dados usando:
ALTER DATABASE mydb CHARSET utf8;

Com isso, tudo estará usando UTF8 , e BLOBs também funcionarão bem.

O arquivo mínimo que causa esse problema de corrupção é blob.bin com byte único 0xFF . No Linux, você pode criar este arquivo de teste usando printf comando:
printf "0xFF" > blob.bin

Agora, teste os scripts que reproduzem o problema:

Código de teste 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;
        }
    }
}
?>

Código de teste 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";