Open Source WEB

もう一度、足早にgdbを立ち上げるところからやってみよう。

shell$ gdb /usr/local/mysql/bin/mysql
GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)
Copyright 2003 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"...
(gdb)

ここで、いきなり mysql.cc の 2232行 にブレイクポイントを設定しよう。

(gdb) b mysql.cc:2232
Breakpoint 1 at 0x8054f62: file mysql.cc, line 2232.
(gdb)

コマンドで起動し、漢字として問題が発生するであろう半角カタカナを使ってみる。

(gdb) r -u root
Starting program: /usr/local/mysql/bin/mysql -u root
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 30 to server version: 5.0.10-beta-debug-log
 
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
 
mysql> SET NAMES eucjpms;
Query OK, 0 rows affected (0.00 sec)
 
mysql> select 'サカマチ';
+----------+
| サカマチ |
+----------+
 
Breakpoint 1, print_table_data (result=0x809c9f8) at mysql.cc:2232
2232            uint currlength= (uint) lengths[off];
(gdb)

指定した位置でちゃんと止まったようだ。

タイトルも半角カタカナが表示されるのだが、変になっている。

2232            uint currlength= (uint) lengths[off];
2233            uint numcells= charset_info->cset->numcells(charset_info,
2234                                                str, str + currlength);
2235            tee_fprintf(PAGER, num_flag[off] ? "%*s |" : " %-*s|",
2236                        maxlength + currlength - numcells, str);

ここからは、1行ずつ実行しながら結果を見ていこう。

2246    static void
(gdb) n
2233            uint numcells= charset_info->cset->numcells(charset_info,
(gdb) p currlength
$1 = 8
(gdb) n
2235            tee_fprintf(PAGER, num_flag[off] ? "%*s |" : " %-*s|",
(gdb) p numcells
$2 = 8
(gdb) p maxlength + currlength - numcells
$3 = 9

ということで、%*s の 可変幅の部分(*)は9になる。

(gdb) p num_flag[off]
$5 = false

なので、 書式は " %-*s|" の方が採用される。 普通のprintfと同じ動きをするのであれば、'サカマチ' は8バイトなので、 後に空白が1つ付く。前には元々1つついているので、 全体としては

空白1文字 + サカマチ + 空白1文字 + |

が出力される。したがって詰まる訳だ。

その後もnで1行毎に実行すると、以下のように、表が出力される様子が分かる。

(gdb) n
2220        for (uint off= 0; off < mysql_num_fields(result); off++)
(gdb) n
2239        (void) tee_fputs("\n", PAGER);
(gdb) n
| サカマチ |
87        inline const char *ptr() const { return Ptr; }
(gdb) n
+----------+
78        ~String() { free(); }
(gdb)

charset_infoはどうなっているか

文字列の表示幅を求めるところは

2233            uint numcells= charset_info->cset->numcells(charset_info,
2234                                                str, str + currlength);

となっていて、charset_infからポインタを辿った先にある関数で処理されている。 この charset_info の値を見てみよう。

(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /usr/local/mysql/bin/mysql -u root
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 31 to server version: 5.0.10-beta-debug-log
 
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
 
mysql> SET NAMES eucjpms;
Query OK, 0 rows affected (0.03 sec)
 
mysql> select 'サカマチ';
+----------+
| サカマチ |
+----------+
 
Breakpoint 1, print_table_data (result=0x809c9f8) at mysql.cc:2232
2232            uint currlength= (uint) lengths[off];
(gdb) p charset_info
$6 = (CHARSET_INFO *) 0x80766a0
(gdb)

charset_infoはポインタなので、中味を見るには * を付けないといけない。

(gdb) p *charset_info
$7 = {number = 8, primary_number = 0, binary_number = 0, state = 801,
  csname = 0x808c158 "latin1", name = 0x8090998 "latin1_swedish_ci",
  comment = 0x8090978 "ISO 8859-1 West European", tailoring = 0x0, ctype = 0x400bca20 "",
  to_lower = 0x400bcb40 "", to_upper = 0x400bcc40 "", sort_order = 0x400bcd40 "",
  contractions = 0x0, sort_order_big = 0x0, tab_to_uni = 0x400bce40, tab_from_uni = 0x0,
  caseinfo = 0x4012b960,
  state_map = 0x8086580  .....以下省略 ,
  ident_map = 0x8086680 "", strxfrm_multiply = 1, caseup_multiply = 1 '\001',
  casedn_multiply = 1 '\001', mbminlen = 1, mbmaxlen = 1, min_sort_char = 0,
  max_sort_char = 255, cset = 0x400bdc60, coll = 0x400be020}
(gdb)

SET NAMES eucjpms とやったにもかかわらず、chasetinfo->csname は "latin1" となっている。eucjpmpは反映されていない。

ということは、8バイトの文字列 'サカマチ' は、latin1の文字として解釈され、 きっとlatin1では、全ての文字は1バイトが1セルと計算されるので、 思った通り変になっているのであろう。


戻る:print_table_data()にブレークポイントを設定

次へ:オプション指定はcharset_infoに反映される


フィードバック:

Name:
Comment:

There is no comment.

このサイトは、 IPA の「平成15年度オープンソフトウエア活用基盤整備事業」 の委託事業として開発されたKahuaで試験的に運用しております。

Copyright (c) 2004-2007 株式会社タイムインターメディア About Us