WARN_DATA_TRUNCATEDの出力の仕組み

またまた改造版MySQLでの話。

TableへのデータのINSERT時に、実際にはtruncateが発生していない(SELECT * FROM t1で確認可能)のに、"Data truncated for ..."のwarningsが出るパタンを発見。utf8なセッションからutf8以外のテーブルかつTEXT型のカラムへINSERTするとき。つまりucs2変換の時に、なぜかwarning発生コードを踏んでしまっているらしい。

で、以下解決までのメモ(の予定)。

"Data truncated for ..."というエラーメッセージ文字列は、sql/share/errmsg.txtに書いてあるけれども、コード内からはマクロが代わりに使われている。マクロは、

./mysqld_error.h:#define WARN_DATA_TRUNCATED 1265

こんな感じでinclude/mysqld_error.hにある。1265番というのは"show warnings"で得られるのでそこから引ける。

WARN_DATA_TRUNCATEDに触っているのは、sql/field.cc、sql/field_conv.cc、sql/my_decimal.ccなどなど。

ちょこちょこ見ていると、warningsを出すためには、Field::set_warning関数を使うのがMySQLの定跡っぽい雰囲気が伺えた。となると次はgdbの出番。

というわけで数分後、さっそく良い断面図が取れましたよ。

Breakpoint 1, Field::set_warning (this=0x8a94950, level=WARN_LEVEL_WARN, code=1265, cuted_increment=1) at field.cc:9142
(gdb) bt
#0  Field::set_warning (this=0x8a94950, level=WARN_LEVEL_WARN, code=1265, cuted_increment=1) at field.cc:9142
#1  0x081b0274 in report_data_too_long (field=0x8a94950) at field.cc:5920
#2  0x081a8db2 in Field_blob::store (this=0x8a94950, from=0x8acbe68 "ã\201\202", length=3, cs=0x867e8c0) at field.cc:7030
#3  0x0813bb8e in Item_string::save_in_field (this=0x8acbe70, field=0x8a94950, no_conversions=false) at item.cc:4340
#4  0x0820438d in fill_record (thd=0x8a93340, ptr=0x8a94934, values=@0x8acbe58, ignore_errors=false) at sql_base.cc:5119
#5  0x08204d61 in fill_record_n_invoke_before_triggers (thd=0x8a93340, ptr=0x8a94930, values=@0x8acbe58, ignore_errors=false, triggers=0x0, event=TRG_EVENT_INSERT) at sql_base.cc:5157
#6  0x0823defa in mysql_insert (thd=0x8a93340, table_list=0x8acbcb0, fields=@0x8a93880, values_list=@0x8a938a4, update_fields=@0x8a93898, update_values=@0x8a9388c, duplic=DUP_ERROR, ignore=false)
    at sql_insert.cc:558
#7  0x081dc672 in mysql_execute_command (thd=0x8a93340) at sql_parse.cc:3405
#8  0x081e1aee in mysql_parse (thd=0x8a93340, inBuf=0x8acbc28 "insert into t1 values (\"ã\201\202\")", length=29) at sql_parse.cc:5842
#9  0x081e3c0b in dispatch_command (command=COM_QUERY, thd=0x8a93340, packet=0x8ac3bf9 "insert into t1 values (\"ã\201\202\")", packet_length=30) at sql_parse.cc:1777
#10 0x081e4f23 in do_command (thd=0x8a93340) at sql_parse.cc:1559
#11 0x081e5e59 in handle_one_connection (arg=0x8a93340) at sql_parse.cc:1190
#12 0xa7f61240 in start_thread () from /lib/tls/i686/cmov/libpthread.so.0
#13 0xa7bc632e in clone () from /lib/tls/i686/cmov/libc.so.6

ちらちらステップ実行すると、どこの分岐からwarnings処理に入ったかが明らかに。Field_blob::store関数の最後の方。

  if (copy_length < length)
  {
    report_data_too_long(this);
    return 2;
  }

ん〜、なぜにField_blob。Field_textって無いんだっけ!?

ちなみに"あ"という1文字をINSERTしたとき、copy_lengthは2でlengthは3だった。こっから先はちと時間がかかりそう。とりあえず公式バイナリと比べてみるか。 オリジナルのソース配布版と比較します。

あうあう。公式ソースでも、公式バイナリでもwarningsでるよ。まいった〜。



というわけで、再現手順をしっかり作った後、某ルートで開発元に相談したら、バグ認定でました。
http://bugs.mysql.com/bug.php?id=25815

でもって、既に修正パッチがソースツリーにcommitされました。
http://lists.mysql.com/commits/18702

今回は対応めちゃ早かったねぇ〜>MySQLの中の人 すごいすごい!

# changesetを見るとやはりbarさんですね。ロシアチームばんざーいですな^^