WarningとErrorの出力方法
Warningの出力方法
sql/sql_error.hにMYSQL_ERRORクラスとpush_warning関数が定義されてます。
class MYSQL_ERROR: public Sql_alloc { public: enum enum_warning_level { WARN_LEVEL_NOTE, WARN_LEVEL_WARN, WARN_LEVEL_ERROR, WARN_LEVEL_END}; uint code; enum_warning_level level; char *msg; MYSQL_ERROR(THD *thd, uint code_arg, enum_warning_level level_arg, const char *msg_arg) :code(code_arg), level(level_arg) { if (msg_arg) set_msg(thd, msg_arg); } void set_msg(THD *thd, const char *msg_arg); }; MYSQL_ERROR *push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level, uint code, const char *msg);
warningを返すにはこれらのクラスと関数を使います。
レベルは3つあるようです。上記の定義だと"WARN_LEVEL_END"もenumに追加してありますが、これはenumの終端を表すために定義されているものだと思われます。
- WARN_LEVEL_NOTE
- WARN_LEVEL_WARN
- WARN_LEVEL_ERROR
このレベル指定は、show warnings等を実行した際に使用されます。
push_warning関数はsql/sql_error.ccを見たところ以下のな作りになっていました。
- OPTION_SQL_NOTEフラグが立っていない限り、WARN_LEVEL_NOTEのものは無視(nullを返しておしまい)。
- strict modeが有効な場合、WARN_LEVEL_WARNをWARN_LEVEL_ERRORに昇格させる。
- thd->warn_listの長さがmax_error_countに達している場合を除き、引数を用いてMYSQL_ERRORオブジェクトをnewしてthd->warn_listに追加。
- thd->warn_countとthd->total_warn_countをインクリメント
- newしたMYSQL_ERRORオブジェクトを返す。(生成してない場合にはnullを返す。)
push_warning関数の引数codeですが、これはinclude/mysqld_error.hに定義されているマクロを使います。
#define ER_ERROR_FIRST 1000 #define ER_HASHCHK 1000 #define ER_NISAMCHK 1001 #define ER_NO 1002 #define ER_YES 1003 ...
エラーコードと返されるエラーメッセージの情報は以下のリファレンスマニュアルに記述されています。
ただし、実際に返されるエラーメッセージ(警告メッセージ)はpush_warning関数の引数msgによって決まるので、注意しましょう。例えば引数msgに"hogehoge"を渡すとどんなエラーコードであってもメッセージは"hogehoge"になります。
上記リファレンスマニュアルの仕様に基づいたエラーメッセージを返すには、ERマクロを使用します。ERマクロはsql/unireg.hで以下のように定義されているマクロです。(clientではまた別のマクロ定義が使用されます。)
#define ER(X) errmesg[(X) - ER_ERROR_FIRST]
つまりerrmesg配列から引っ張ってくる、というものです。で、このerrmesg配列はどのように定義されているかというと実はソースコード上には無くて、mysqldの起動プロセス中に呼び出されるinit_errmessage関数がsql/share/errmsg.txtを読み込んで初期化しています。で、さらにこの辺りで国際化が行われてて、、、みたいな話につながります。
要するに、呼び出す側からするとER(エラーコード)でメッセージ文字列に展開されるということです。
ただ、以下のように書式付文字列のままなので、これだけだと不十分です。
Incorrect column name '%s'
そこで、my_snprintfとかで加工しているコード例は無いかどうかを探したところ、いくつかでてきました。
./sql/sql_table.cc: my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_KEY), ./sql/sql_table.cc: my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_KEY), ./sql/sql_table.cc: length= my_snprintf(buff, sizeof(buff), ER(ER_OPEN_AS_READONLY),
この例に従って書けば良さそうです。
■コード例
MYSQL_ERRMSG_SIZEの値は512です(include/mysql_com.h)。
char warn_buff[MYSQL_ERRMSG_SIZE]; my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_KEY), length); push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_TOO_LONG_KEY, warn_buff);
■補足
最初はpush_warning関数でWARN_LEVEL_ERRORを指定すればエラーとして処理されるのかと思いましたがそうではありませんでした。Errorはまた別の関数を使用する必要があります。
Errorの出力方法
Errorを出力するには、my_error関数を使用します。mysys/my_error.cに実装されています。
int my_error(int nr, myf MyFlags, ...);
こちらはかなり単純です。第1引数nrにはエラーコードを渡します。するとmy_error関数内部でエラーメッセージとの紐付けを行ってくれます。エラーメッセージが書式付文字列である場合には、当てはめたい値を第3引数以降を使用して渡します。
第2引数のMyFlagsはMySQLのユーティリティ関数("my_"というprefixが名前についている関数)ではよく見かける引数でここでは単純に"MYF(0)"を渡せばOKです。
■コード例
my_error(ER_UNKNOWN_COLLATION, MYF(0), buf);