Chat (Lingr.com)
Informaiton
Daily
Column
- MySQL日本語の旅(5/1)
- アクセス向上秘伝(5/9)
- 一風変ったHaskellλ門(6/13)
- SICP Answer Book (5/31) 問題3.26追加
Zope Solution
Extra
アーカイブ
OSS案内所
Site Info
関連リンク
MySQLは、オープンソースである。だから、最後は(最初からでもいいんだが)、 ソースリストを読んで確認することができる。 さらに、ソースコードを書き変えて、MySQLに提供することだって可能である。
オープンソースなんだから、ソースを読もう!
といことで、さっそくmysqlコマンドのソースリストを見てみよう。 ソースリストも、バイナリと同様にダウンロードすることができる。 各パージョンのダウンロードページの最後の方にソースのダウンロードがある。
ここでは、5.0.7 の Tarball である mysql-5.0.7-beta.tar.gz を元に説明する。 ダウンロード、展開などは省略する。
展開してできるディレクトリ mysql-5.0.7-beta の下に client というディレクトリがあり、その中に mysql.cc というのがある。 これが mysqlクライアントのソースであろう、たぶん。
mysql-5.0.7-beta/client/mysql.cc mysqlクライアントコマンドのソース
mysql.ccは3584行ある。ちょっと長いかも知れないが、 日頃使い慣れている mysqlクライアントコマンドのソースだから、 何とかなるかも知れない。
ということで、たらたらとプログラムを眺めていたら、 どうもテーブルを表示しているらしい関数 print_table_data() を見つけることができた。
2145 static void
2146 print_table_data(MYSQL_RES *result)
2147 {
2148 String separator(256);
2149 MYSQL_ROW cur;
2150 MYSQL_FIELD *field;
2151 bool *num_flag;
2152
2153 num_flag=(bool*) my_alloca(sizeof(bool)*mysql_num_fields(result));
2154 if (info_flag)
2155 {
2156 print_field_types(result);
2157 if (!mysql_num_rows(result))
2158 return;
2159 mysql_field_seek(result,0);
2160 }
2161 separator.copy("+",1,charset_info);
2162 while ((field = mysql_fetch_field(result)))
2163 {
2164 uint length= column_names ? field->name_length : 0;
2165 if (quick)
2166 length=max(length,field->length);
2167 else
2168 length=max(length,field->max_length);
2169 if (length < 4 && !IS_NOT_NULL(field->flags))
2170 length=4; // Room for "NULL"
2171 field->max_length=length+1;
2172 separator.fill(separator.length()+length+2,'-');
2173 separator.append('+');
2174 }
2175 separator.append('\0'); // End marker for \0
2176 tee_puts((char*) separator.ptr(), PAGER);
2177 if (column_names)
2178 {
2179 mysql_field_seek(result,0);
2180 (void) tee_fputs("|", PAGER);
2181 for (uint off=0; (field = mysql_fetch_field(result)) ; off++)
2182 {
2183 tee_fprintf(PAGER, " %-*s|",(int) min(field->max_length,
2184 MAX_COLUMN_LENGTH),
2185 field->name);
2186 num_flag[off]= IS_NUM(field->type);
2187 }
2188 (void) tee_fputs("\n", PAGER);
2189 tee_puts((char*) separator.ptr(), PAGER);
2190 }
2191
2192 while ((cur= mysql_fetch_row(result)))
2193 {
2194 ulong *lengths= mysql_fetch_lengths(result);
2195 (void) tee_fputs("|", PAGER);
2196 mysql_field_seek(result, 0);
2197 for (uint off= 0; off < mysql_num_fields(result); off++)
2198 {
2199 const char *str= cur[off] ? cur[off] : "NULL";
2200 field= mysql_fetch_field(result);
2201 uint maxlength= field->max_length;
2202 if (maxlength > MAX_COLUMN_LENGTH)
2203 {
2204 tee_fputs(str, PAGER);
2205 tee_fputs(" |", PAGER);
2206 }
2207 else
2208 {
2209 uint currlength= (uint) lengths[off];
2210 uint numcells= charset_info->cset->numcells(charset_info,
2211 str, str + currlength);
2212 tee_fprintf(PAGER, num_flag[off] ? "%*s |" : " %-*s|",
2213 maxlength + currlength - numcells, str);
2214 }
2215 }
2216 (void) tee_fputs("\n", PAGER);
2217 }
2218 tee_puts((char*) separator.ptr(), PAGER);
2219 my_afree((gptr) num_flag);
2220 }
さて、この関数と、実際のテーブルの表示内容を比べてみよう。
テーブルは、
+---------+------------+----------------------------+ | post | name | yomi | +---------+------------+----------------------------+ | 1620824 | 揚場町 | アゲバチョウ | | 1620848 | 市谷鷹匠町 | イチガヤタカジョウマチ | | 1620802 | 改代町 | カイタイチョウ | | 1600002 | 坂町 | サカマチ | +---------+------------+----------------------------+
のような形であるが、
+---------+------------+----------------------------+
を組み立てているのが
2161 separator.copy("+",1,charset_info);
2162 while ((field = mysql_fetch_field(result)))
2163 {
2164 uint length= column_names ? field->name_length : 0;
2165 if (quick)
2166 length=max(length,field->length);
2167 else
2168 length=max(length,field->max_length);
2169 if (length < 4 && !IS_NOT_NULL(field->flags))
2170 length=4; // Room for "NULL"
2171 field->max_length=length+1;
2172 separator.fill(separator.length()+length+2,'-');
2173 separator.append('+');
2174 }
2175 separator.append('\0'); // End marker for \0
である。でき上がったセパレータを利用するのが、
2176 tee_puts((char*) separator.ptr(), PAGER); 2189 tee_puts((char*) separator.ptr(), PAGER); 2218 tee_puts((char*) separator.ptr(), PAGER);
である。
すると、タイトル部分(カラム名)は以下で作っている。
2179 mysql_field_seek(result,0);
2180 (void) tee_fputs("|", PAGER);
2181 for (uint off=0; (field = mysql_fetch_field(result)) ; off++)
2182 {
2183 tee_fprintf(PAGER, " %-*s|",(int) min(field->max_length,
2184 MAX_COLUMN_LENGTH),
2185 field->name);
2186 num_flag[off]= IS_NUM(field->type);
2187 }
2188 (void) tee_fputs("\n", PAGER);
そして、レコード数だけくり返しているのが以下である。
2192 while ((cur= mysql_fetch_row(result)))
2193 {
2194 ulong *lengths= mysql_fetch_lengths(result);
2195 (void) tee_fputs("|", PAGER);
2196 mysql_field_seek(result, 0);
2197 for (uint off= 0; off < mysql_num_fields(result); off++)
2198 {
2199 const char *str= cur[off] ? cur[off] : "NULL";
2200 field= mysql_fetch_field(result);
2201 uint maxlength= field->max_length;
2202 if (maxlength > MAX_COLUMN_LENGTH)
2203 {
2204 tee_fputs(str, PAGER);
2205 tee_fputs(" |", PAGER);
2206 }
2207 else
2208 {
2209 uint currlength= (uint) lengths[off];
2210 uint numcells= charset_info->cset->numcells(charset_info,
2211 str, str + currlength);
2212 tee_fprintf(PAGER, num_flag[off] ? "%*s |" : " %-*s|",
2213 maxlength + currlength - numcells, str);
2214 }
2215 }
2216 (void) tee_fputs("\n", PAGER);
2217 }
ここまで分れば、縦線が揃ったり、揃わなかったりする原因を突き止めるには、 あと一歩である。
起動時にデフォルトキャラクタセットを指定した場合に限っての話だが、 色々なキャラクタセットで揃ったということは、 文字幅が管理されている証拠である。
もう、上の関数の中で文字幅を調整している場所は分かりますよね。
戻る:eucjpms,cp932,utf8混在のREPLACEは
次へ:文字幅を求める関数へのポインタ(*numcells)()
フィードバック:
There is no comment.