薄いブログ

技術の雑多なことを書く場所

MySQLのエラー番号はどこに定義されているのか

背景

Go のアプリケーションで MySQL のエラー番号によって処理を分岐させたいことがあり、調査していたらドキュメントには記載されていた。 (例: ER_DUP_ENTRY(1062) をハンドリングしたい)

Go から参照しやすいように定数で提供されているものはないかと調査したが見つけることができなかった。

プログラムから参照しやすい形で存在しているだろうと思い、https://github.com/mysql/mysql-server を検索したが見つからなかった。

調査

ソースコードの中からは定数として参照はされているが定義は見つからない。

どうやら comp_err というコマンドによってビルド時に生成していることがわかった。

comp_err は MySQL 8.0.19 より前は errmsg-utf8.txt、以降は messages_to_error_log.txt と messages_to_clients.txt の情報からエラーを生成していると書かれている。

確かにそれらのファイルはソースコードの中からシンボル名を検索していたときに引っかかっていたがどこにもエラー番号がなかったので無視していた。

つまり comp_err 内部の何らかのロジックによってエラー番号が決められているだろうということでロジックを調査した。

comp_err は設定ファイルから

  • languages
  • default-language
  • start-error-number
  • ER_、WARN_、(OBSOLETE_ER_、OBSOLETE_WARN_)
  • (reserved-error-section)

から始まる行とエラーに付随するメッセージを解釈する。括弧がついているものは 8 系から追加された要素である。

languages は言語名の long name と short name の紐付け、文字コードの紐付けを定義する。

default-language は対象の言語のエラーメッセージがなかったときにどの言語のエラーメッセージを使うかを short name で指定する。基本的には eng。

start-error-number によって使用するエラー番号の先頭が決められ、それ以降に現れたエラーのシンボルから順に番号が割り振られていく。 start-error-number は複数回現れることがある。

ER_、WARN_ から始まる行はエラーの定義を行う。SQL State と ODBC State が存在するものに関してはエラーの名前の後に空白区切りで書かれている。

タブや空白から始まる行はエラーに紐づくメッセージの定義を行う。言語名の short name と書式文字列が書かれている。 サポートしている言語がエラーによってまちまちで eng しか定義されていないものも多い。

reserved-error-section はエラー番号として予約済みで使ってはいけない範囲を指定する。

github.com

実際に中身を確認するとより理解が深まる。

結論

MySQL のエラー番号は comp_err によって errmsg-utf8.txt、または messages_to_error_log.txt と messages_to_clients.txt で定義されたエラーに対して採番したものが mysqld_error.h に定義されている。

余談1

背景で書いてあるとおりGoから定数として参照したい。 すでにリリース済みのMySQLのソースの中の mysqld_error.h から生成すればよいが、せっかく調べたので comp_err の再実装を行い Go のパッケージを生成した。

github.com

GitHub に上がっている errmsg-utf8.txt や messages_to_clients.txt から自動的に生成するようになっている。

余談2

MySQL のエラーのサポート言語を集計してみた。

MySQL 5.7

// english(eng) 1123
// german(ger) 661
// swedish(swe) 331
// japanese(jpn) 292
// portuguese(por) 289
// spanish(spa) 286
// russian(rus) 268
// dutch(nla) 256
// ukrainian(ukr) 243
// italian(ita) 231
// serbian(serbian) 219
// french(fre) 212
// danish(dan) 208
// estonian(est) 206
// czech(cze) 192
// hungarian(hun) 183
// romanian(rum) 174
// korean(kor) 160
// greek(greek) 141
// slovak(slo) 138
// norwegian-ny(norwegian-ny) 128
// norwegian(nor) 126
// polish(pol) 121
// bulgarian(bgn) 3
// (bg) 3

1位は英語で圧倒的、2位がドイツ語なのが個人的には意外に感じた。 3位はスウェーデン語、4位が日本語という結果だった。 最下位はブルガリア語で3なのだが明らかにミスのようなものが3つあり、半分しか使えていないのが残念な気持ちになった。

MySQL 8.0

// english(eng) 1672
// german(ger) 661
// swedish(swe) 331
// japanese(jpn) 292
// portuguese(por) 289
// spanish(spa) 286
// russian(rus) 267
// dutch(nla) 256
// ukrainian(ukr) 243
// italian(ita) 231
// serbian(serbian) 219
// french(fre) 212
// danish(dan) 208
// estonian(est) 206
// czech(cze) 192
// hungarian(hun) 183
// romanian(rum) 174
// korean(kor) 160
// greek(greek) 141
// slovak(slo) 138
// polish(pol) 136
// norwegian-ny(norwegian-ny) 128
// norwegian(nor) 126
// bulgarian(bgn) 8

8 系においても順位は変わらないがブルガリア語のミスが修正されており安心した。