sql >> データベース >  >> RDS >> Mysql

MySQLの現在/デフォルトの文字セットを使用してBLOBが変換されますか?

    簡単な回答:

    以下の行を削除またはコメントアウトするだけで、実際に使用されているデータベースエンコーディングに関係なく、常に機能します(utf8latin1 、など):

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

    長い答え:

    これはPDOのバグではなく、MySQLのバグです。

    実際のデータベースエンコーディングがlatin1の場合 、ただし使用するもの:

    SET CHARACTER SET utf8
    

    (またはその逆:実際はutf8 、ただし、latin1を使用します -重要な部分は、それが異なるということです )、その後、私が知る限り、MySQLはクライアントとサーバー間のすべてのトラフィックに対して(BLOBの場合でも)文字セット変換を実行しようとします。 !)。

    SET CHARACTER SETを使用しない場合 ステートメント、スクリプト(PHP/PDOまたはPerl/DBI)で見たものから、接続文字セットはデフォルトでデータベース文字セットに設定されており、その場合、暗黙的な変換は行われません。

    明らかに、この自動変換は、変換が発生することを望まないBLOBを強制終了するものです。

    これをPHP/PDOとPerl/DBIの両方でテストしましたが、問題は簡単に再現できます。latin1でデータベースを使用すると、どちらも失敗します。 SET CHARACTER SET utf8のエンコードと使用 (またはその逆)。

    完全にUTF8になりたい場合 互換性がある場合は、次を使用してデータベースのエンコーディングを変更する必要があります:

    ALTER DATABASE mydb CHARSET utf8;
    

    これにより、すべてがUTF8を使用するようになります 、およびBLOBも正常に機能します。

    この破損の問題を引き起こす最小限のファイルはblob.binです。 1バイトの0xFF 。 Linuxでは、printfを使用してこのテストファイルを作成できます。 コマンド:

    printf "0xFF" > blob.bin
    

    次に、問題を再現するスクリプトをテストします。

    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;
            }
        }
    }
    ?>
    

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


    1. RDSデータベースのローカルレプリカ

    2. さまざまなカーソルオプションがどのような影響を与える可能性がありますか?

    3. MySQLまたはMariaDBデータベースをSQLインジェクションから保護する方法:パート1

    4. sqlalchemyを使用してpostgresqlに接続中にエラーが発生しました