sql >> Base de Datos >  >> RDS >> Mysql

¿Se convierte un BLOB utilizando el conjunto de caracteres actual/predeterminado en MySQL?

Respuesta corta:

Simplemente elimine o comente la línea a continuación, y siempre funcionará, sin importar qué codificación de base de datos esté realmente en uso (utf8 , latin1 , etc.):

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

Respuesta larga:

Este no es un error de PDO, es un error de MySQL.

Cuando la codificación de la base de datos real es latin1 , pero usas:

SET CHARACTER SET utf8

(o viceversa:real es utf8 , pero usas latin1 - la parte importante es que es diferente ), entonces, por lo que sé, MySQL intentará realizar una conversión de juego de caracteres para todo el tráfico entre el cliente y el servidor (incluso para BLOB !).

Si NO usa SET CHARACTER SET declaración, por lo que veo para los scripts (PHP/PDO o Perl/DBI), el juego de caracteres de conexión por defecto está configurado para ser el juego de caracteres de la base de datos, y en ese caso no se lleva a cabo ninguna conversión implícita.

Obviamente, esta conversión automática es lo que elimina los BLOB, que no quieren que se produzca ninguna conversión.

He probado esto tanto en PHP/PDO como en Perl/DBI, y el problema es fácilmente reproducible:ambos fallarán si se usa la base de datos con latin1 codificando y usando SET CHARACTER SET utf8 (o viceversa).

Si quieres ser completamente UTF8 compatible, debe cambiar la codificación de su base de datos usando:

ALTER DATABASE mydb CHARSET utf8;

Con esto, todo estará usando UTF8 y los BLOB también funcionarán bien.

El archivo mínimo que causa este problema de corrupción es blob.bin con un solo byte 0xFF . En Linux, puede crear este archivo de prueba usando printf comando:

printf "0xFF" > blob.bin

Ahora, pruebe los scripts que reproducen el problema:

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