Understanding MySQL Internalsを読む(7)

連載記事一覧は*こちら*にあります。

Core API一覧

メモリ割当て、文字列操作、ファイル管理などの処理はいくつものグループの内部APIによって行われています。ポータビリティを確保するため、MySQLのコードはCのライブラリの呼出を非常に控えめにしており、またC++のライブラリについてはまったく使用していません。

その代わりに、MySQLでは多くのUtility系のAPIを用意しています。それらすべてを説明することはできませんが、代表的なものをいくつかここで触れたいと思います。

関数プロトタイプ 定義ファイル 説明
gptr my_malloc(uint Size, myf MyFlags) mysys/my_malloc.c malloc関数に対するWrapper API。mysqldが使うグローバルバッファ、1クエリの処理よりも長いライフサイクルを持つオブジェクト、大きなメモリブロックの確保などのためにメモリブロックを割り当てる際に使用される。
void my_free(gptr ptr, myf MyFlags) mysys/my_malloc.c my_malloc関数で割り当てたメモリブロックを解放する。以前はmy_freeは関数でしたが、今はmy_no_flags_free関数へのエイリアスとして機能するマクロになっています。従ってこのAPIの実装を調べたい場合にはmysys/my_malloc.cのmy_free()ではなくmy_no_flags_free()を調べて下さい。とはいうもののmy_free()は現在もコード上の多くの個所で継続して使用されています。
gptr my_multi_malloc(myf myFlags, ...) mysys/mulalloc.c 複数のポインタを用いてメモリを割当て、各ポインタが大きなメモリブロック内の各位置を指すようにする。例えば以下のような例があるとする。
char *p1, *p2, *block;
if (!(block = my_multi_malloc(MYF (MY_WME), &p1, 10, &p2, 20, NULL))
goto err;
この時、ポインタblockは割当てメモリの先頭を差し、ポインタp1はそのメモリブロック無いで10バイト分予約したと個所、ポインタp2は同様に20バイト分を予約した個所を指すようになる。ここで割り当てられたメモリを使用した処理が終わった場合、my_free()で明示的にメモリを開放する必要がある。
void init_alloc_root(MEM_ROOT *mem_root, uint block_size, uint pre_alloc_size) mysys/my_alloc.c 引数mem_rootで指定されたメモリプールを初期化する。
gptr alloc_root(MEM_ROOT *mem_root, unsigned int Size) mysys_my_alloc.c 引数mem_rootで指定されたメモリプールからメモリを割り当てる。成功した場合には割当て先へのポインタ、失敗した場合には0が返される。
void free_root(MEM_ROOT *mem_root, myf MyFlags) mysys/my_alloc.c 引数mem_rootで指定されたメモリプールを解放する。
File my_open(const char *FileName, int Flags, myf MyFlags) mysys/my_open.c open関数に対するWrapper API。3番目の引数MyFlagsはMySQL API独自のフラグ。File型というのはintのエイリアス
int my_close(File fd, myf MyFlags) mysys/my_open.c close関数に対するWrapper API。2番目の引数MyFlagsはMySQL API独自のフラグ。
uint my_read(File Filedes, byte *Buffer, uint Count, myf MyFlags) mysys/my_read.c read関数を拡張したWrapper API。通常の読み込みの他、引数MyFlagasにMY_FULL_IOフラグが設定されている場合には引数Countで指定されたバイト数の読み込みが成功するまで読みつづける。
uint my_write(int Filedes, const byte *Buffer, uint Count, myf MyFlags) mysys/my_write.c write関数を拡張したWrapper API。引数Countで指定されたバイト数の書き込みが成功するまで書き込みを行う。引数MyFlagasにMY_WAIT_IF_FULLフラグが設定されている場合には、ディスク上に利用可能な領域(空き領域)ができるまで待ち、ディスクがいっぱいの時に単純にfailを返すということをしない。
int init_io_cache(IO_CACHE *info, File file, uint cachesize, enum cache_type type, my_off_t seek_offset, pbool use_async_io, myf cache_myflags) mysys/mf_iocache.c I/Oキャッシュ記述子を初期化する。I/Oキャッシュとは標準CライブラリのFILE構造体のようなもの。成功した場合に0を返し、失敗した場合に0以外の値を返す。
int my_b_read(register IO_CACHE *info, byte *Buffer, uint Count) mysys/mf_iocache.c 引数infoで指定されたI/Oキャッシュから読み込みを行う。my_b_read自身は_my_b_read関数のエイリアスとして使用されているマクロだが、要求を満たすのに十分なデータがバッファ上に存在しないしない時に呼ばれる。成功した場合に0を返し、失敗した場合には0以外の値を返す。
int my_b_write(register IO_CACHE *info, const byte *Buffer, uint Count) mysys/mf_iocache.c 引数infoで指定されたI/Oキャッシュに書き込みを行う。my_b_write自身は_my_b_write関数のエイリアスだが、要求を満たすために必要な領域がバッファ中に無く物理的に書き込みを行うような時に呼ばれる。成功すると0を返し、失敗すると0以外の値を返す。
int flush_io_cache(IO_CACHE *info) mysys/mf_iocache.c メモリバッファからファイル記述子へデータを書き込む。flush_io_cache()は実際にはmy_b_flush_io_cacheを呼び出すマクロとなっており、成功した場合に0を返し、失敗した場合には0以外の値を返す。
int end_io_cache(IO_CACHE *info) mysys/mf_iocache.c 引数infoで指定されたI/Oキャッシュをクローズし、必要な終了処理を全て行う。
my_string fn_format(my_string to, const char *name, const char *dir, const char *extension, uint flag) mysys/mf_format.c 拡張子に対応したファイルパスを作成する。引数はスラッシュ"/"を用いたUnixファイルパス形式である必要がある。Windowsでは結果を返す際にバックスラッシュに変換される。引数flagはファイルパスに関数する操作を制御するためのフラグ。例えばMY_RESOLVE_SYMLINKSが設定されている場合にはシンボリックリンクを辿って処理を行う。
my_bool hash_init(HASH *hash, CHARSET_INFO *charset, uint size, uint key_offset, uint key_length, hash_get_key get_key, void (*free_element)(void*)) mysys/hash.c hash記述子を初期化する。成功した場合に0、失敗した場合に0以外の値を返す。hash_init()は実際には_hash_init()へのマクロとなっている。レコードのhashキーを得るには、key_offsetとkey_lengthを設定するか、あるいはget_key関数ポインタを使用する。
gptr hash_search(HASH *hash, const byte *key, uint length) mysys/hash.c 引数で指定されたkeyに対応する最初のレコードを発見する。成功した場合にはそのレコードへのポインタを返す。失敗した場合には0を返す。
gptr hash_next(HASH *hash, const byte *key, uint length) mysys/hash.c 引数で指定されたkeyに対応する次のレコードを発見する。この関数はhash_search関数が呼ばれた後、引き続き対応するレコードを探索するために繰り返しよばれることを想定している。成功した場合にはそのレコードへのポインタを返す。失敗した場合には0を返す。
my_bool my_hash_insert(HASH *info, const byte *record) mysys/hash.c レコードへのポインタをhashに追加する。成功した場合には0、失敗した場合には0以外の値を返す。以前はhash_insertという名前の関数だったが、関数名(名前空間)の衝突が原因で名称変更を行った。
my_bool hash_delete(HASH *hash, byte *record) mysys/hash.c hashからレコードへのポインタを削除する。ポインタの値(キーの値ではない)が比較され、1行だけが削除されることに注意。
void hash_free(HASH *hash) mysys/hash.c hash記述子により指定されたhashを解放する。必要に応じてリソースの掃除も全て行う。
my_bool init_dynamic_array(DYNAMIC_ARRAY *array, uint element_size, uint init_alloc, uint alloc_increment) mysys/array.c 動的に拡張可能な配列の記述子を初期化する。hashとは違い、配列に対する操作は各要素に対して行われ、ポインタに対する操作ではないことに注意すること。従って要素のサイズを引数で指定する必要がある。
my_bool insert_dynamic(DYNAMIC_ARRAY *array, gptr element) mysys/array.c 動的配列に要素を追加する。第二引数が追加すべき要素へのポインタであることに注意。
void get_dynamic(DYNAMIC_ARRAY *array, gptr element, uint idx) mysys/array.c 引数idx番目の要素を動的配列から引数elementが示す先にコピーする。引数elementが指す先には要素をコピーするのに十分なメモリ領域が確保されていなければならない。
void delete_dynamic(DYNAMIC_ARRAY *array) mysys/array.c 動的配列が使用していたリソースと記述子を解放する。動的配列を使い終わったらこの関数を呼ぶ。
char *strmov(char *dst, const char *src) strings/strmov.c strcpy関数に似た関数。ただし、戻り値としてdstのnull終端文字へのポインタを返す。
char* strxmov(char *dst, char* src1, ...) strings/strxmov.c 第二引数およびそれ以降の全ての引数を連結して第一引数dstに格納する。dstにはnull終端文字が付与される。最後の引数はNullSでなければならない。dstのnull終端文字へのポインタが戻り値として返される。
int my_snprintf(char* to, size_t n, const char* fmt, ...) strings/my_vsnprintf.c snprintf標準関数の機能のうちの必要最小限の機能を実装した関数。snprintf標準関数は、書式付文字列を文字数制限付きで安全にバッファにコピーできる素晴らしい関数。しかしながらsnprintfは全てのプラットフォームで利用することはできない。my_snprintf関数はsnprintf関数が実装していないいくつかの機能も含んでいる。
gptr sql_alloc(uint Size) sql/thr_malloc.cc 現在のスレッド記述子のメモリプールからメモリを割り当てる。1つのクエリを処理する過程でのみ一時的に必要となるメモリは全てこの関数を通じてメモリを割り当てるべきである。このsql_alloc関数で割当てられたメモリブロックは、クエリの処理が完了すると解放されてしまうことに注意。従ってsql_alloc()で割り当てられた個々のメモリブロックは明示的に解放させる必要は無い。もし1つのクエリを処理する期間よりも長い期間に渡ってメモリを使用する場合、あるいは大きなメモリブロックを必要とする場合にはmy_malloc関数を使ってメモリを割当て、必要がなくなったら明示的にmy_free関数で解放する必要がある。
my_string ip_to_hostname(struct in_addr *in, uint *errors) sql/hostname.cc ホスト名の解決が行える場合、IPアドレス用の構造体からホスト名の文字列に変換を行う。ホスト名が解決できない場合には、IPアドレスを文字列に変換する。

これらを使うとMySQLのコーディングが楽になりそう。hashとか動的配列とかいいね。sql_allocもクエリ処理が終わったら自動的に解放してくれるのはいいね。sql_allocを使っておけばメモリリークの心配ないね。