2019/02/12

[c/c++][lmdb]mdb_cursor_get(MDB_NEXT)の位置

久々にLMDBだ。
といっても、記事にするのが久々なだけで、毎日使っている。


にも関わらず、まだ理解できていないことが多い。
今回は、mdb_cursor_get()でMDB_NEXTを指定した場合についてだ。


cursorを使う場合、だいたいこんな感じになるだろう。

01:     while ((retval = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
02:         const char *p_key = (const char *)key.mv_data;
03:         const char *p_data = (const char *)data.mv_data;
04:         if (p_key[4] == '5') {
05:             mdb_cursor_del(cursor, 0);
06:         }
07:         printf("key=%s, data=%s\n", p_key, p_data);
08:     }

サンプルもこんな感じで、while()でMDB_NEXTを使って全部取得するようになっていたと思う。


で、上のコードではmdb_cursor_del()を使ってみた。
取得したデータの一部が「5」だったら、それを消したい、というわけだ。
やってみると、ちゃんと対象のデータだけが削除された。


ならいいじゃないか、と思いそうだが、気になったのはMDB_NEXTだ。
私は今まで「取得した後に次へ移動する」という意味だと思っていたのだ。
LMDBのドキュメントには"Position at next data item"と書かれているので、次のデータ位置にポジションを移動する、と解釈したのだ。

しかし実際は、「5」のデータを取得できた後でmdb_cursor_del()すると「5」のデータが消えたので、そういうわけではないのだ。
だから、「次へ移動して取得」という意味になる。



じゃあ、cursorをオープンした直後はどうなんだ?
カーソルの位置は先頭にあるだろうから、2番目のデータから取得されそうではないか。
しかし、そんなことはないのはわかっている。
ならば、オープンした直後はどうなっているのだ?

01:     if ((retval = mdb_cursor_get(cursor, &key, &data, MDB_GET_CURRENT)) == 0) {
02:         const char *p_key = (const char *)key.mv_data;
03:         const char *p_data = (const char *)data.mv_data;
04:         printf("KEY=%s, DATA=%s\n", p_key, p_data);
05:     } else {
06:         fprintf(stderr, "ERR(%u): %s\n", __LINE__, mdb_strerror(retval));
07:     }

これをオープンした直後に実行したのだが、エラーになった。
Invalid argumentだそうだ。
mdb_cursor_get(MDB_NEXT)でwhile()ループを抜けた後に実行してみると、最後に取得したデータが取れた。


つまり、mdb_cursor_get()は、opの動作をした後にカーソル位置のデータを取得するのだ。
opの動作ができない場合はエラーになるし、cursorのオープン直後はまだカーソル位置が先頭にもなっていないからMDB_GET_CURRENTはエラーになるということか。


どうにも気になっていたのに、確認しないまま使っていたのよねぇ。。。
不安だったので、削除するときはmdb_del()の方を使っていたのだ。

0 件のコメント:

コメントを投稿

コメントありがとうございます。
スパムかもしれない、と私が思ったら、
申し訳ないですが勝手に削除することもあります。