Open Source WEB

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)()


フィードバック:

Name:
Comment:

There is no comment.

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

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