Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Wird ein BLOB mit dem aktuellen/Standardzeichensatz in MySQL konvertiert?

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";