libdb
Table of Contents
libdb
前回は*BSD の libc に組み込まれたDBをやった。リナはそれを、あっさり放 棄してしまってた。その代わりがあるはず。そう、それがlibdbという奴。す こし追跡してみる。
at Arch Linux
既にインストールされてた。何かのアプリが要求したんだろね。
[sakae@arch ~]$ pacman -Qi db5.3 Name : db5.3 Version : 5.3.28-2 Description : The Berkeley DB embedded database system v5.3 Architecture : x86_64 URL : https://www.oracle.com/technology/software/products/berkeley-db/index.html Depends On : gcc-libs sh : Build Date : Thu Dec 15 02:59:02 2022 Install Date : Mon Jan 2 14:58:02 2023
でも、ソースから入れるのがオイラーの流儀。FreeBSDで make fetch したも のを題材にしてみる。
[sakae@arch db-5.3.28]$ dist/configure --help | grep -- --enable --enable-mingw Build Berkeley DB for MinGW. --enable-sql_codegen Build the SQL-to-C code generation tool. : [sakae@arch build_unix]$ ../dist/configure --prefix=/home/sakae/MINE --enable-sql --enable-compat185
インストール時が、機能選定のチャンス。色々あったけど、欲ばらないでやっ てみる。
Libraries have been installed in: /home/sakae/MINE/lib If you ever happen to want to link against installed libraries in a given directory, LIBDIR, you must either use libtool, and specify the full pathname of the library, or use the `-LLIBDIR' flag during linking and do at least one of the following: - add LIBDIR to the `LD_LIBRARY_PATH' environment variable during execution - add LIBDIR to the `LD_RUN_PATH' environment variable during linking - use the `-Wl,-rpath -Wl,LIBDIR' linker flag - have your system administrator add LIBDIR to `/etc/ld.so.conf' See any operating system documentation about shared libraries for more information, such as the ld(1) and ld.so(8) manual pages.
インストール時に、こんな案内が出てきた。これで少しはサポート業務が減る かな。
try run
[sakae@arch c]$ cc ex_access.c -I/home/sakae/MINE/include -L/home/sakae/MINE/lib -ldb [sakae@arch c]$ ldd ./a.out linux-vdso.so.1 (0x00007fff3d3f1000) libdb-5.3.so => /home/sakae/MINE/lib/libdb-5.3.so (0x00007f1c43d46000) libc.so.6 => /usr/lib/libc.so.6 (0x00007f1c43b5f000) /lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007f1c43f26000)
試運転は、examples/cの中にあった物を利用。ソースを開くと、こういうご褒 美が有るので、パッケージになったバイナリーを入れちゃ駄目だよ。
[sakae@arch c]$ ./a.out input> foo input> bar input> exit bar : rab foo : oof [sakae@arch c]$ file access.db access.db: Berkeley DB (Btree, version 9, native byte-order)
ソースをざっと見してから実行。入力したキーを反転させたのを値として記憶 してる。
/* Create and initialize database object, open the database. */ if ((ret = db_create(&dbp, NULL, 0)) != 0) { if ((ret = dbp->open(dbp, NULL, database, NULL, DB_BTREE, DB_CREATE, 0664)) != 0) { /* Acquire a cursor for the database. */ if ((ret = dbp->cursor(dbp, NULL, &dbcp, 0)) != 0) { /* Walk through the database and print out the key/data pairs. */ while ((ret = dbcp->get(dbcp, &key, &data, DB_NEXT)) == 0)
骨子は、こんな感じか。DB系とカーソル系でハンドルが別になってるのね。
dbsql
カーソルで思いだした。sql系を作るように指示したんだった。できばえを /home/sakae/binで確認。
[sakae@arch bin]$ ls db_archive* db_dump* db_log_verify* db_replicate* db_upgrade* db_checkpoint* db_hotbackup* db_printlog* db_stat* db_verify* db_deadlock* db_load* db_recover* db_tuner* dbsql*
ライブラリィーと言いつつ、おまけが沢山ついてくるのね。
[sakae@arch bin]$ ldd dbsql linux-vdso.so.1 (0x00007ffd9fd8a000) libdb_sql-5.3.so => /home/sakae/MINE/lib/libdb_sql-5.3.so (0x00007fc3a1800000) libc.so.6 => /usr/lib/libc.so.6 (0x00007fc3a1619000) /lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007fc3a1bfd000)
dbsqlって、あのsqlite3の事? 半信半疑で、昔やったのをやってみる。
[sakae@arch tmp]$ /home/sakae/MINE/bin/dbsql doctor.db Berkeley DB 11g Release 2, library version 11.2.5.3.28: (September 9, 2013) Enter ".help" for instructions Enter SQL statements terminated with a ";" dbsql> CREATE TABLE Procs( proc_id INTEGER, anest_name VARCHAR(64), start_time TIMESTAMP, end_time TIMESTAMP ); INSERT INTO Procs VALUES( 10, 'Baker', '01-07-01 08:00', '01-07-01 11:00'); INSERT INTO Procs VALUES( 20, 'Baker', '01-07-01 09:00', '01-07-01 13:00'); INSERT INTO Procs VALUES( 30, 'Dow' , '01-07-01 09:00', '01-07-01 15:30'); INSERT INTO Procs VALUES( 40, 'Dow' , '01-07-01 08:00', '01-07-01 13:30'); INSERT INTO Procs VALUES( 50, 'Dow' , '01-07-01 10:00', '01-07-01 11:30'); INSERT INTO Procs VALUES( 60, 'Dow' , '01-07-01 12:30', '01-07-01 13:30'); INSERT INTO Procs VALUES( 70, 'Dow' , '01-07-01 13:30', '01-07-01 14:30'); INSERT INTO Procs VALUES( 80, 'Dow' , '01-07-01 18:00', '01-07-01 19:00'); ...> ...> ...> ...> dbsql> dbsql> dbsql> dbsql> dbsql> dbsql> dbsql> dbsql> dbsql> dbsql> .exit
確かにバークレーって主張してるし、SQL文も受付た。もう、10年も前のバー ジョンみたいだけど。
[sakae@arch tmp]$ file doctor.db doctor.db: Berkeley DB (Btree, version 9, native byte-order)
出来たものは、確かにバークレーだ。ええい、こうなったら、ちゃんとselect できるか確認。
dbsql> select * from Procs; 10|Baker|01-07-01 08:00|01-07-01 11:00 20|Baker|01-07-01 09:00|01-07-01 13:00 30|Dow|01-07-01 09:00|01-07-01 15:30 40|Dow|01-07-01 08:00|01-07-01 13:30 50|Dow|01-07-01 10:00|01-07-01 11:30 60|Dow|01-07-01 12:30|01-07-01 13:30 70|Dow|01-07-01 13:30|01-07-01 14:30 80|Dow|01-07-01 18:00|01-07-01 19:00
こういうのを、一石二鳥と言うんだろな。
at Debian
そんじゃ鞍替えしてみる。どんなのが有るか。
sakae@deb:~$ apt-cache search db5.3 db5.3-doc - Berkeley v5.3 Database Documentation [html] db5.3-sql-util - Berkeley v5.3 SQL Database Utilities db5.3-util - Berkeley v5.3 Database Utilities libdb5.3 - Berkeley v5.3 Database Libraries [runtime] libdb5.3++ - Berkeley v5.3 Database Libraries for C++ [runtime] libdb5.3++-dev - Berkeley v5.3 Database Libraries for C++ [development] libdb5.3-dbg - Berkeley v5.3 Database Libraries [debug] libdb5.3-dev - Berkeley v5.3 Database Libraries [development] libdb5.3-java - Berkeley v5.3 Database Libraries for Java libdb5.3-java-dev - Berkeley v5.3 Database Libraries for Java [development] libdb5.3-java-jni - Berkeley v5.3 Database Libraries for Java libdb5.3-sql - Berkeley v5.3 Database Libraries [SQL runtime] libdb5.3-sql-dev - Berkeley v5.3 Database Libraries [SQL development] libdb5.3-stl - Berkeley v5.3 Database Libraries [STL runtime] libdb5.3-stl-dev - Berkeley v5.3 Database Libraries [STL development] libdb5.3-tcl - Berkeley v5.3 Database Libraries for Tcl [module]
try run
既に入っていたので、お試し。
sakae@deb:/tmp/db-5.3.28/examples/c$ cc ex_btrec.c -ldb sakae@deb:/tmp/db-5.3.28/examples/c$ ./a.out ex_btrec: open ../test/tcl/wordlist: No such file or directory
ざっと見で、リンクが間違っていたので修正。../../test/tcl/wordlistが正
解。これから推測するに、過去には、 examples/ex_btree.c
だったのでしょう。
それが、色々な言語がサポートされたので、c言語用を作って階層をさげた。
それにサンプルを追従させなかった。絵に書いた様なミスですな。と、小姑の
ような嫌味を垂れています。
sakae@deb:/tmp/db-5.3.28/examples/c$ ./a.out ex_btrec: database contains 1000 records recno #> 33 k/d 0033barrow : worrab3300 next 0034approve : evorppa4300 retrieved recno: 34 recno #> ex_btrec: BDB1002 illegal record number of 0 ex_btrec: DBcursor->get: Invalid argument
なんと、今迄避けていたレコードの例ですな。ありがたく拝謁しましょ。
lang and old ver.
言語が出てきたので、しっかりとそれを覗いてみる。
sakae@deb:/tmp/db-5.3.28$ ls lang/ csharp/ cxx/ db185/ dbm/ hsearch/ java/ perl/ php_db4/ sql/ tcl/
色々な言語に対応したのね。だからリナは、オリジナルでlibcに入っていたの を捨てて、分離独立させたのか。まあ、不承不承で納得してあげよう。
興味は、db185/ ですよ。中には、昔のlibcに詰め込まれていた奴をエミュレー
トするものが格納されてた。昔のソースを再活用するには、 db_185.h
を使
えとな。確かめてみる。前回のコードだな。
sakae@deb:/tmp$ cc olddb.c -ldb olddb.c: In function ‘reg_kv’: olddb.c:19:25: warning: passing argument 2 of ‘db->put’ from incompatible pointer type [-Wincompatible-pointer-types] 19 | (void)(db->put)(db, &key, &data, 0); | ^~~~ | | | DBT * olddb.c:19:25: note: expected ‘DB_TXN *’ but argument is of type ‘DBT *’ olddb.c:19:14: error: too few arguments to function ‘db->put’ 19 | (void)(db->put)(db, &key, &data, 0); | ~~~^~~~~~ :
昔のコードだと、しっかりエラー。じゃ、魔法をかけるよ。db.h ->
db_185.h
に変更。
sakae@deb:/tmp$ cc olddb.c -ldb sakae@deb:/tmp$ ./a.out sakae@deb:/tmp$ file z.db z.db: Berkeley DB (Btree, version 9, native byte-order) sakae@deb:/tmp$ strings z.db < HACKING_HACKING > HACKING < hello_hello > hello < hogefuga_hogefuga > hogefuga
検証用のコードも、チョイの変更で無事に動いた。
sakae@deb:/tmp$ ldd a.out linux-gate.so.1 (0xb7fcd000) libdb-5.3.so => /lib/i386-linux-gnu/libdb-5.3.so (0xb7db6000) libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7bcd000) libpthread.so.0 => /lib/i386-linux-gnu/libpthread.so.0 (0xb7bab000) /lib/ld-linux.so.2 (0xb7fcf000)
ちゃんと古いDNAがリレーされてるんですなあ。 猿と人間のDNAの違いは、僅か1%ぐらいしか無いと言われるけど、この場合は どうなんだろう?
最初の奴はC言語しか理解しなかった。時代を経てマルチリンガルに進化して きた。最新のやつは、ぼらくるのダウンロードセンターから入手できるみたい だけど、登録しなきゃいけないみたいで、断念したよ。
db_stat
sakae@deb:/tmp$ db_stat -h db_stat: option requires an argument -- 'h' usage: db_stat -d file [-fN] [-h home] [-P password] [-s database] usage: db_stat [-cEelmrtVx] [-C Aclop] [-h home] [-L A] [-M A] [-P password] [-R A] [-X A] [-aNZ] sakae@deb:/tmp$ db_stat -d z.db Wed Mar 22 07:22:15 2023 Local time 53162 Btree magic number 9 Btree version number Little-endian Byte order Flags 2 Minimum keys per-page 4096 Underlying database page size 1007 Overflow key/data size 1 Number of levels in the tree 3 Number of unique keys in the tree 3 Number of data items in the tree 0 Number of tree internal pages 0 Number of bytes free in tree internal pages (0% ff) 1 Number of tree leaf pages 3950 Number of bytes free in tree leaf pages (3% ff) 0 Number of tree duplicate pages 0 Number of bytes free in tree duplicate pages (0% ff) 0 Number of tree overflow pages 0 Number of bytes free in tree overflow pages (0% ff) 0 Number of empty pages 0 Number of pages on the free list
こんな楽しいコマンドが用意されてると、アレをやってみたくなる。
import from OpenBSD
古い仕様でも動く事を発見したので、前回のものをコンパイル。
sakae@deb:/tmp$ cc olddb.c -ldb olddb.c: In function ‘main’: olddb.c:38:7: warning: implicit declaration of function ‘timespecsub’; did you mean ‘timespec_get’? [-Wimplicit-function-declaration] 38 | timespecsub(&stop, &start, &pst); | ^~~~~~~~~~~ | timespec_get /usr/bin/ld: /tmp/cc5oFoGc.o: in function `main': olddb.c:(.text+0x1b3): undefined reference to `timespecsub' collect2: error: ld returned 1 exit status
あろう事か、脇道でエラーだ。リナには、ないのか、そんな関数。代わりに提 案してきた関数も無いぞ。gccAI 全然イケテナイな。こういう時は、ChatGPT に相談して下さい。
bigdb.c:40:7: warning: implicit declaration of function 'timespecsub' is invalid in C99 [-Wimplicit-function-declaration] timespecsub(&stop, &start, &pst); ^ 1 warning generated. ld: error: undefined symbol: timespecsub >>> referenced by bigdb.c >>> /tmp/bigdb-b56d12.o:(main) cc: error: linker command failed with exit code 1 (use -v to see invocation)
OpenBSDのコンパイラーは、差し出まがしい事は言ってこないぞ。うそつきに は、十分にご注意あれ。
しょうがない、OpenBSDから輸入するか。で、どこで定義されてる? そんなの、 gdbでブレークさせて、ファイル名を提示させよう。が、全くヒットしない。 どゆ事? くまなくサーチしたら、 sys/sys/time.hで、みつかった。マクロで定義されてたんで、溶けて無くなっ ちゃってたのね。全くもう、早く気づけよ。コードは巻末に掲載。
DBの元種を、簡単にコマンド一発で作成。一応ランダム化しとく。数字の前後 に、qpを挟んでいるけど、QPの回し者ではありません。ただの気まぐれです。
sakae@deb:/tmp$ seq -f q%.6fp 0 0.000997 500 | sort -R >SEED sakae@deb:/tmp$ wc SEED 501505 501505 6409233 SEED sakae@deb:/tmp$ head -3 SEED q179.322414p q211.376961p q297.795924p
1マンコづつの、実行時間が得られる。
10000 0.070563349 20000 0.128923175 30000 0.141863290 : 480000 0.185838424 490000 0.185373366 500000 0.187459667
初回だけ、やけに速いな。後は段々と遅くなっていく。経過時間のグラフだと、 こういうのが読み取れないんで、いわゆる微分系がお勧め。
たまたま図書館から、"データ分析できない社員は いらない" というすさまじ い書名の本をかりてきた。流行のデータサイエンスかと思ったら違って、営業 向け。売上分析では、よくZチャートが利用されますなんて説明されてた。
3本グラフがZ字になる。横軸が月、縦軸が売上。月毎の売上グラフ。それの累 計グラフ。もう1本は、季節変動を除去するため、月毎にシフトさせた年間売 上。3本でZになる。このZの字型から、将来どうなるかうらなう。昔、亀の甲 羅を焼いて形をうらなうあれとそっくり。
斜め線が、弓型に窪んでいるか、お椀型になってるかって、以外に判定がむず かしい。累計を微分すると、はっきりするぞ。しつこいなあ。
出来あがった奴の情報。3レベルのツリーって事は、3回のアクセスで、データ に到達できるって訳だ。これぞ、B木。
sakae@deb:/tmp$ db_stat -d z.db Thu Mar 23 08:06:37 2023 Local time 53162 Btree magic number 9 Btree version number Little-endian Byte order Flags 2 Minimum keys per-page 4096 Underlying database page size 1007 Overflow key/data size 3 Number of levels in the tree 501505 Number of unique keys in the tree 501505 Number of data items in the tree 65 Number of tree internal pages 48876 Number of bytes free in tree internal pages (81% ff) 9775 Number of tree leaf pages 12M Number of bytes free in tree leaf pages (69% ff) :
code
#include <stdio.h> #include <string.h> #include <sys/types.h> #include <db_185.h> #include <fcntl.h> #include <limits.h> #include <time.h> #define timespecsub(tsp, usp, vsp) \ do { \ (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \ (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \ if ((vsp)->tv_nsec < 0) { \ (vsp)->tv_sec--; \ (vsp)->tv_nsec += 1000000000L; \ } \ } while (0) DB *db; void reg_kv(char *mykey) { char dst[128]; DBT key, data; snprintf(dst, sizeof dst, "< %s_%s >", mykey, mykey); key.data = (char *)mykey; key.size = strlen(mykey) + 1; data.data = (char *)dst; data.size = strlen(dst) + 1; (void)(db->put)(db, &key, &data, 0); } int main(){ char buf[128]; FILE *fp; int cnt=0; struct timespec pst, start, stop; db = dbopen("z.db", O_CREAT|O_RDWR|O_TRUNC, 0644, DB_BTREE, NULL); fp = fopen("./SEED", "r"); clock_gettime(CLOCK_MONOTONIC, &start); while(fgets(buf, sizeof buf, fp) != NULL){ buf[strlen(buf) - 1] = '\0'; reg_kv(buf); cnt++; if (cnt % 10000 == 0){ clock_gettime(CLOCK_MONOTONIC, &stop); timespecsub(&stop, &start, &pst); printf("%d %lld.%09ld\n", cnt, (long long)pst.tv_sec, pst.tv_nsec); start = stop; } } fclose(fp); (void)(db->close)(db); return 0; }
start = stop; を削除すると、累計(経過実行時間)データになるよ。