MySQL41 の SJIS 環境のクライアントからの文字化けを防ぐ

SJIS 以外は SET NAMES クエリでの設定で解決するが、SJIS ではクエリでは解決できない。
 
結論からいうと、

mysql –default-character-set=sjis [ -u username [ -p ]] […]

のように、クライアントでの接続時に –default-character-set=sjis と明示してやるだけで解決する(SET NAMES .. は不要)。my.cnf の [mysql] の項目に sjis と設定してやってもよい。
 
PHP でも my.cnf を見ているらしい。PHP ではこれ以外の妥当な設定の手段がないもよう。
現在は、一部のコードでのみ対応したい場合は、自分でラッパ関数を作ってやるのがよさそう。PEAR::DB の prepared statement 構文を使う場合は ujis (EUC_JP)として通信させてやらないとうまくいかなかった。
 
詳しく書くと、DB_Common::prepare で呼び出している内部関数で非 UTF8 のつもりでエスケープ処理を行うため、事前エンコードをすると文字が壊れてしまうのが原因。EUC-JP だと、ASCII コードに互換性があるためエスケープ処理で文字が壊れないのでエンコード後のエスケープでも問題ない。
 

//
// 単純なサンプル。実用コードではありませんよ。
//
$con->query(‘SET NAMES ujis’);
echo dec( $con->getOne(enc(‘SELECT description FROM product WHERE name = ?’), enc(array(‘苛性ソーダ’))) ) ;

/**
 *
 */
function dec($val){
    return mb_convert_encoding($val, mb_internal_encoding(), ‘EUC-JP’);
}
/**
 * エンコード。クエリを実行する前の前処理。
 * @param string $val 変換対象の文字列
 * @return string DB クエリの文字コードに変換した文字列
 */
function enc($val){
    return mb_convert_encoding($val, ‘EUC-JP’);
    // $val は mb_internal_encoding() の文字コード。それ以外も
    // 受け付ける場合, 第三引数を ‘auto,UTF-8’ 等にしておく。
}

参考:
OSS Web – 接続キャラクタセットの変更
ここにあるとおり、show variables like ‘character\_set\_%’ では同じなのに、実際は内部的には違うのがなんとも。