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; を削除すると、累計(経過実行時間)データになるよ。


This year's Index

Home