Mysql
 sql >> Base de données >  >> RDS >> Mysql

Un BLOB est-il converti à l'aide du jeu de caractères actuel/par défaut dans MySQL ?

Réponse courte :

Supprimez ou commentez simplement la ligne ci-dessous, et cela fonctionnera toujours, quel que soit l'encodage de base de données réellement utilisé (utf8 , latin1 , etc.):

$pdo->exec('SET CHARACTER SET utf8');

Réponse longue :

Ce n'est pas un bogue PDO, c'est un bogue MySQL.

Lorsque l'encodage réel de la base de données est latin1 , mais vous utilisez :

SET CHARACTER SET utf8

(ou vice versa :la valeur réelle est utf8 , mais vous utilisez latin1 - l'important est qu'il soit différent ), puis, pour autant que je sache, MySQL essaiera d'effectuer la conversion du jeu de caractères pour tout le trafic entre le client et le serveur (même pour BLOB !).

Si vous N'UTILISEZ PAS SET CHARACTER SET , d'après ce que je vois pour les scripts (PHP/PDO ou Perl/DBI), le jeu de caractères de connexion par défaut est défini comme étant le jeu de caractères de la base de données, et dans ce cas, aucune conversion implicite n'a lieu.

Évidemment, cette conversion automatique est ce qui tue les BLOBs, qui ne veulent aucune conversion.

J'ai testé cela à la fois sur PHP/PDO et Perl/DBI, et le problème est facilement reproductible :les deux échoueront si vous utilisez la base de données avec latin1 encodage et utilisation de SET CHARACTER SET utf8 (ou vice versa).

Si vous voulez être entièrement UTF8 compatible, vous devez modifier l'encodage de votre base de données en utilisant :

ALTER DATABASE mydb CHARSET utf8;

Avec cela, tout utilisera UTF8 , et les BLOB fonctionneront également correctement.

Le fichier minimal qui cause ce problème de corruption est blob.bin avec un seul octet 0xFF . Sous Linux, vous pouvez créer ce fichier de test en utilisant printf commande :

printf "0xFF" > blob.bin

Maintenant, testez les scripts qui reproduisent le problème :

Code d'essai 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;
        }
    }
}
?>

Code de test 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";