sql >> Database >  >> RDS >> Mysql

Is een BLOB geconverteerd met behulp van de huidige/standaard tekenset in MySQL?

Kort antwoord:

Verwijder of becommentarieer de onderstaande regel en het zal altijd werken, ongeacht welke databasecodering echt in gebruik is (utf8 , latin1 , enz.):

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

Lang antwoord:

Dit is geen PDO-bug, dit is een MySQL-bug.

Wanneer de werkelijke databasecodering latin1 is , maar je gebruikt:

SET CHARACTER SET utf8

(of vice versa:actueel is utf8 , maar je gebruikt latin1 - belangrijk onderdeel is dat het anders . is ), dan zal MySQL, voor zover ik weet, proberen charset-conversie uit te voeren voor al het verkeer tussen client en server (zelfs voor BLOB !).

Als u SET CHARACTER SET NIET gebruikt statement, van wat ik zie voor scripts (PHP/PDO of Perl/DBI) is de verbindingstekenset standaard ingesteld op de databasetekenset en in dat geval vindt er geen impliciete conversie plaats.

Het is duidelijk dat deze automatische conversie BLOB's doodt, die niet willen dat er een conversie plaatsvindt.

Ik heb dit getest op zowel PHP/PDO als Perl/DBI, en het probleem is gemakkelijk reproduceerbaar:beide zullen mislukken als de database wordt gebruikt met latin1 coderen en gebruiken SET CHARACTER SET utf8 (of omgekeerd).

Als je volledig UTF8 wilt zijn compatibel is, moet u de codering van uw database wijzigen met:

ALTER DATABASE mydb CHARSET utf8;

Hiermee gebruikt alles UTF8 , en BLOB's werken ook prima.

Het minimale bestand dat dit corruptieprobleem veroorzaakt, is blob.bin met enkele byte 0xFF . Op Linux kunt u dit testbestand maken met printf commando:

printf "0xFF" > blob.bin

Test nu scripts die het probleem reproduceren:

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


  1. Hoe MySQL-databases in de opdrachtregel te importeren

  2. Hoe doe je dating wiskunde die het jaar negeert?

  3. MySQL join met where-clausule

  4. SQLiteAssetHelper:Kan de database niet openen om te schrijven (zal alleen-lezen proberen)