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さんですね。ロシアチームばんざーいですな^^