dbsql
Table of Contents
dependency
ArchLinuxのdb5.3が、何につられて導入されたか? 専用のチェックコマンド が用意されてると思うんだけど、見つけられなかった 。ちょいと消極的かつ強引だけど、いきなりの暴挙に出る。
[sakae@arch ~]$ sudo pacman -R db5.3 checking dependencies... error: failed to prepare transaction (could not satisfy dependencies) :: removing db5.3 breaks dependency 'db5.3' required by perl
perlさんが必要としてるから、削除できませんと、拒否された。問題ないもの なら、
[sakae@arch ~]$ sudo pacman -R indent [sudo] password for sakae: checking dependencies... Packages (1) indent-2.2.13-1 Total Removed Size: 0.46 MiB :: Do you want to remove these packages? [Y/n] y :: Running pre-transaction hooks... (1/1) Removing old entries from the info directory file... :: Processing package changes... (1/1) removing indent [######################] 100% :: Running post-transaction hooks... (1/1) Arming ConditionNeedsUpdate...
こんな風に、淡々と処理が進むよ。安心したまえ。
c++ game
WindowsにWSLなウブンツを入れて、サンプルを動かす方針だけど、オイラーは、 OpenBSD上の、c++さ。
vbox$ c++ -O3 -std=c++17 00_MazeState.cpp
こんな風にコンパイルするらしい。コメントがちゃんとUTFになってるのは、 ウブのおかげか。 スタイルがオイラーの趣味ではないけど、ガマン。
盤面の図が多数掲載されているので非常にわかりやすい。タイマーを使って計 算を打ち切りにする方法が解説されててよかった。
Reading symbols from a.out... (gdb) b randomAction(MazeState const&) Breakpoint 1 at 0x6966: file 00_MazeState.cpp, line 127. (gdb) r Starting program: /tmp/sample_code/03_OnePlayerGame/a.out Breakpoint 1, randomAction (state=...) at 00_MazeState.cpp:127 127 auto legal_actions = state.legalActions(); (gdb) bt #0 randomAction (state=...) at 00_MazeState.cpp:127 #1 0x18eb3b06 in playGame (seed=121321) at 00_MazeState.cpp:141 #2 0x18eb3c2a in main () at 00_MazeState.cpp:147
gdbを走らせるのは、オイラーの性だな。
(gdb) set print pretty (gdb) p state $1 = (const State &) @0xcf7e9478: { static dx = {1, -1, 0, 0}, static dy = <same as static member of an already seen type>, points_ = {{4, 6, 1, 3}, {0, 0, 2, 0}, {7, 5, 6, 6}}, turn_ = 0, character_ = { y_ = 1, x_ = 1 }, game_score_ = 0 }
こういうデータなら、ちゃんと見えるけど、深い所の奴は不快な表示になるん だよなあ。やっぱり普通のC言語がいいな。
ガマンが臨界に逹っしたので整形しとく。リナで実施。
sakae@deb:~/src/gameAIsearch/03_OnePlayerGame$ for f in *.cpp > do > indent -kr $f > done
sakae@deb:~/src/gameAIsearch/03_OnePlayerGame$ wc 00_MazeState.cpp /tmp/00_MazeState.cpp 130 429 4450 00_MazeState.cpp 148 440 3874 /tmp/00_MazeState.cpp
無駄なブラケットだけの行が無くなって、コンパクトになった。
The Kernighan & Ritchie style is used throughout their well-known book "The C Programming Language". It is enabled with the ‘-kr’ option. The Kernighan & Ritchie style corresponds to the following set of op‐ tions: -nbad -bap -bbo -nbc -br -brs -c33 -cd33 -ncdb -ce -ci4 -cli0 -cp33 -cs -d0 -di1 -nfc1 -nfca -hnl -i4 -ip0 -l75 -lp -npcs -nprs -npsl -saf -sai -saw -nsc -nsob -nss -par
Berkeley indent だと、-orig か。気にいったスタイルが決定したら、 $HOME/.indent.proに指定しとく。こういう論争を嫌って、gofmtとかが用意さ れてるんだな。あれ、gofmtは流用できないのかな? ダメダわさ。ガチガチに チャックしてるよ。まあ、あたりまえ。
それより、深刻な問題が*BSDのindentに内在してる。コメントが文字化けする んだ。下記はサンプルの一部。
[sakae@fb /tmp/sample_code/03_OnePlayerGame]$ cat 00_MazeState.cpp | head -2 // 一人ゲームの例 // 1ターンに上下左右四方向のいずれかに1マスずつ進む。
そして、整形結果もダンプ
[sakae@fb /tmp/sample_code/03_OnePlayerGame]$ hd 00_MazeState.cpp |head -2 00000000 2f 2f 20 e4 b8 80 e4 ba ba e3 82 b2 e3 83 bc e3 |// .............| 00000010 83 a0 e3 81 ae e4 be 8b 0a 2f 2f 20 31 e3 82 bf |.........// 1...| [sakae@fb /tmp/sample_code/03_OnePlayerGame]$ hd aa |head -2 00000000 2f 2f 20 e4 b8 37 e4 ba ba e3 82 b2 e3 83 bc e3 |// ..7..........| 00000010 83 a0 e3 81 ae e4 be 8b 0a 2f 2f 31 20 e3 20 82 |.........//1 . .|
いずれindentの動作を検証してみよう。それより、参考資料をみる方が、楽し いな。
sys/time.h of Linux
上では、リナに負けてしまったので、今度はBSDの優位性を強調してみる。 前回、時間計測で、リナには定義されていないBSDだけのマクロを使ってしまっ た。同等がどの程度実装されてるか確認。
# define TIMEVAL_TO_TIMESPEC(tv, ts) { \ (ts)->tv_sec = (tv)->tv_sec; \ (ts)->tv_nsec = (tv)->tv_usec * 1000; \ } # define TIMESPEC_TO_TIMEVAL(tv, ts) { \ (tv)->tv_sec = (ts)->tv_sec; \ (tv)->tv_usec = (ts)->tv_nsec / 1000; \ } # define timersub(a, b, result) \ do { \ (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ if ((result)->tv_usec < 0) { \ --(result)->tv_sec; \ (result)->tv_usec += 1000000; \ } \ } while (0)
timeval,timespecの相互変換が有って、timevalの減算も有るのに、timespec 版の減算が未定義。抜けてるな。
dbsql
try run
今回は、こんなデータを与えてみる。オイラーのパソコン遍歴の一部だ。
create table pc(mypc text, year integer); insert into pc(mypc,year) values('MZ80K', 1979); insert into pc(mypc,year) values('FM-11', 1983); insert into pc(mypc,year) values('Macintosh SE', 1989);
起動する時、rlwrapをかませておくと、ヒストリーが使えて便利。
vbox$ rlwrap dbsql pc.db dbsql> .headers on dbsql> select * from pc; mypc|year MZ80K|1979 FM-11|1983 dbsql> insert into pc(mypc,year) values('Macintosh SE', 1989); dbsql> select * from pc where year > 1980; mypc|year FM-11|1983 Macintosh SE|1989 dbsql> select * from pc where year < 1980; mypc|year MZ80K|1979
vbox$ ls -l pc.db* -rw-r----- 1 sakae wheel 32768 Mar 25 15:42 pc.db pc.db-journal: total 4516 -rw-r----- 1 sakae wheel 3211264 Mar 25 15:42 __db.001 -rw-r----- 1 sakae wheel 163840 Mar 25 15:42 __db.002 -rw-r----- 1 sakae wheel 90112 Mar 25 15:42 __db.003 -rw-r----- 1 sakae wheel 25 Mar 25 15:42 __db.register -rw-r----- 1 sakae wheel 2097152 Mar 25 15:42 log.0000000001 -rw-r--r-- 1 sakae wheel 117 Mar 25 15:19 sql-errors.txt
なんとオマケっぽい、ジャーナルdirが作成されて、その中に、色々なものが 格納されてる。多分削除しちゃっても動くんではなかろうか? 動いた。
メタコマンド
dbsql> .explain on dbsql> select * from pc; mypc year ---- ------------- MZ80K 1979 FM-11 1983 Macintosh SE 1989
ヘッダー宜しく、列の説明が付いた。
dbsql> .stat :env: Statistics for environment Sat Mar 25 15:53:47 2023 Local time =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= Default database environment information: 0x120897 Magic number 0 Panic value 5.3.28 Environment version 9 Btree version 9 Hash version 1 Lock version 19 Log version 4 Queue version 2 Sequence version 1 Txn version : 0x3df06000 DB_ENV 4 ENV handle mutex [0/12 0% !Own] /tmp/pc.db-journal Home DB_CREATE, DB_FORCE, DB_INIT_LOCK, DB_INIT_LOG, DB_INIT_MPOOL, DB_INIT_TXN, DB_R DONLY, DB_THREAD, DB_TXN_NOSYNC Open flags 0660 Mode 56486 Pid cache !Set Lockfhp !Set Locker Set Internal recovery table 240 Number of recovery table slots : =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= Primary REGINFO information: Environment Region type 1 Region ID /tmp/pc.db-journal/__db.001 Region name 0x61030000 Region address : =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= Environment file handle information /tmp/pc.db-journal/__db.register file-handle.file name 0 file-handle.mutex [!Set] 0 file-handle.reference count : /tmp/pc.db-journal/__db.002 file-handle.file name 0 file-handle.mutex [!Set] 0 file-handle.reference count : /tmp/pc.db-journal/__db.003 file-handle.file name 0 file-handle.mutex [!Set] 0 file-handle.reference count : /tmp/pc.db-journal/../pc.db file-handle.file name 1095 file-handle.mutex [0/0 0% !Own] 4 file-handle.reference count :
この他に、.stat pc ってな具合に指定して、テーブルの情報も引きだせる。 意味はよく分からないけど。。
タイマーで実行時間も計測できます。
dbsql> .timer on dbsql> select count(*) from bld; 8155 CPU Time: user 0.060090 sys 0.087353 dbsql> select count(*) from bld; 8155 CPU Time: user 0.006173 sys 0.020307 dbsql> select avg(ppm) from bld; 58.1648068669528 CPU Time: user 0.008309 sys 0.023798
.import
.import FILE TABLE Import data from FILE into TABLE
こんなメタコマンドが用意されてる。ファイルの形式は何の説明もないけど、 多分あれでしょう。そして昔やったsqlite3に習ってみると。
dbsql> .import bld.csv bld Error: no such table: bld dbsql> create table bld(ymd text, hi text, low text, ppm text); dbsql> .import bld.csv bld Error: bld.csv line 1: expected 4 columns of data but found 1 dbsql> .import bldsp bld Error: bldsp line 1: expected 4 columns of data but found 1
カンマ区切りだ駄目ならスペース区切り、でも駄目だわさ。後はタブ区切り?
shell.c
1686 if( c=='i' && strncmp(azArg[0], "import", n)==0 && nArg==3 ){ 1687 char *zTable = azArg[2]; /* Insert data into this table */ 1688 char *zFile = azArg[1]; /* The file from which to extract data */ 1689 sqlite3_stmt *pStmt = NULL; /* A statement */
このあたりにBPかなあ、なんて思っていたら、こんな案内を発見。
".separator STRING Change separator used by output mode and .import\n"
やってみる。
dbsql> .separator ',' dbsql> .import bld.csv bld dbsql> select * from bld limit 3; 12010104,118,73,57 12010121,109,70,71 12010204,128,82,60
デケタ。
inside dbsql
やっぱりgdbだよな。 初回は、色々やるので大変。
#0 sqlite3VdbeExec (p=0x209cb0e8) at ../lang/sql/generated/sqlite3.c:53487 #1 0x0048e227 in sqlite3Step (p=0x209cb0e8) at ../lang/sql/generated/sqlite3.c:51781 #2 0x0048ddfd in sqlite3_step (pStmt=0x209cb0e8) at ../lang/sql/generated/sqlite3.c:51846 #3 0x00492dc8 in sqlite3_exec (db=0x20954008, zSql=0x20991cc8 "SELECT name, rootpage, sql FROM 'main'.sqlite_master ORDER BY rowid", xCallback=0x4c2ec0 <sqlite3InitCallback>, pArg=0xffbfda20, pzErrMsg=0x0) at ../lang/sql/generated/sqlite3.c:77454 : #18 0x00493d81 in sqlite3_prepare_v2 (db=0x20954008, zSql=0x20953100 "select * from pc;", nBytes=-1, ppStmt=0xffbfe09c, pzTail=0xffbfe094) at ../lang/sql/generated/sqlite3.c:80916 #19 0x0046e655 in shell_exec (db=0x20954008, zSql=0x20953100 "select * from pc;", xCallback=0x46ea20 <shell_callback>, pArg=0xffbfe1c8, pzErrMsg=0xffbfe14c) at ../lang/sql/sqlite/src/shell.c:1092 #20 0x0046fd86 in process_input (p=0xffbfe1c8, in=0x0) at ../lang/sql/sqlite/src/shell.c:2515 #21 0x0046aa8f in main (argc=2, argv=0xffbfeb3c) at ../lang/sql/sqlite/src/shell.c:2946
こちらが本筋。
#0 sqlite3VdbeExec (p=0x209cb2a8) at ../lang/sql/generated/sqlite3.c:53487 #1 0x0048e227 in sqlite3Step (p=0x209cb2a8) at ../lang/sql/generated/sqlite3.c:51781 #2 0x0048ddfd in sqlite3_step (pStmt=0x209cb2a8) at ../lang/sql/generated/sqlite3.c:51846 #3 0x0046e756 in shell_exec (db=0x20954008, zSql=0x20953100 "select * from pc;", xCallback=0x46ea20 <shell_callback>, pArg=0xffbfe1c8, pzErrMsg=0xffbfe14c) at ../lang/sql/sqlite/src/shell.c:1120 #4 0x0046fd86 in process_input (p=0xffbfe1c8, in=0x0) at ../lang/sql/sqlite/src/shell.c:2515 #5 0x0046aa8f in main (argc=2, argv=0xffbfeb3c) at ../lang/sql/sqlite/src/shell.c:2946
コンパイルのログから、dbsqlqが構築される様子をぬきだした。
ar cr libdb_sql-5.3.a sqlite3.o db185.o mut_tas.o mut_pthread.o ... libtool: link: cc -g -o dbsql -pthread shell.o libdb_sql-5.3.a -ldl -lpthread -pthread
核になるのは、リンクしてる物。何が違うかと調べたら。sqlite3のライブラ リィの有無だけだった。
[sakae@fb /tmp]$ diff DD SS 3c3,4 < libdb-5.3.a --- > libdb_sql-5.3.a > sqlite3.o
sqlite3.c/openDatabase()
/* ** Berkley DB customization! ** Register any Berkeley DB specific extension functions. */ add_sequence_functions(db); /* End Berkeley DB customization. */
独自の拡張をやってるんだな。
int add_sequence_functions(sqlite3 *db) { int rc; rc = sqlite3CreateFunc(db, "create_sequence", -1, SQLITE_ANY, 0, db_seq_create_func, 0, 0, 0); rc = sqlite3CreateFunc(db, "drop_sequence", -1, SQLITE_ANY, 0, db_seq_drop_func, 0, 0, 0); rc = sqlite3CreateFunc(db, "nextval", 1, SQLITE_ANY, 0, db_seq_nextval_func, 0, 0, 0); rc = sqlite3CreateFunc(db, "currval", 1, SQLITE_ANY, 0, db_seq_currval_func, 0, 0, 0); return (rc); }
パーサーまで手を入れているんで、大変な事ですよ。
何で調べたかと言うと、bdbは既に優れたB木のシステムを持っている。ならば、 これを有効利用して、パーサーまでの部分をsqlite3から借用したものと想像 してたからだ。
Architecture of SQLite そう、この図のバックエンド部をバークレーDBが担 当してると思っていたのさ。どうやら違ったわい。なるべくsqliteを活用しま しょって方針なのね。