pkg using sqlite
Table of Contents
lcm
仲間内で雑談してたら、ある人が怪談めいた事を言った。最近台所から変な声 が聞こえると。よく耳を澄ませたら、電池切れそうと、1分間隔ぐらいで繰り 返してた。天井からね。何年も前に取り付けた火災報知器が発声元だったとか。 ちゃんと設計されてるね。まあ、あたりまえか。
そこから話が発展して、たて続けに、冷蔵庫やら車が壊れて(ついでに自分の 体も)往生したなん てのに話題が移った。
そこで、オイラーが発言。壊れるのはしょうがないけど、もっとバラけて壊れ てほしい。某社の製品なら、狙った通りにこわれるタイマーが内蔵できるんじゃ ねぇ。
そう言えば、素数セミなんてのがいたよな。同時発生で問題をおこさないよう に、素数年周期で大発生。これで、カブる事が少なくなる。えと、最小公倍 数だったな。
scheme@(guile-user)> (lcm 12 10) $1 = 60 scheme@(guile-user)> (lcm 11 13 17) $2 = 2431
最初の例は、還暦。干支は「十二支」と「十干」の組み合わせから成っていて、 60年で一巡する。次は、3種類の素数セミが、同時発生する周期。
どういう原理だったけな? Gauche-0.9.12/src/libnum.scm 思わず調べちゃったぞ。
(define-in-module scheme (lcm . args) (define (lcm2 u v) (let1 g ((with-module gauche.internal %gcd) u v) (if (zero? u) 0 (* (quotient u g) v)))) (define (recn arg args) (if (null? args) arg (recn (lcm2 arg (car args)) (cdr args)))) (let1 args (map (^[arg] (unless (integer? arg) (error "integer required, but got" arg)) (abs arg)) args) (cond [(null? args) 1] [(null? (cdr args)) (car args)] [else (recn (car args) (cdr args))])))
普通は人は、こちらへ。 Pythonで最小公倍数、最大公約数を計算する
B木
前回からやってるsqliteの核心にBツリーってのがある。DISK等の記憶媒体に データを保存するのに適した構造を実現するのに都合がよい。
手元の資料を漁ったら、1990年代の雑誌、Cマガジンに連載されたものを纏め たものと、奥村先生のアルゴリズム辞典が出てきた。
#define M 2 /* 1ページのデータ数の上限の半分 */ typedef int keytype; /* 探索のキーの型 */ typedef struct page { /* ページの定義 */ int n; /* データ数 */ keytype key[2 * M]; /* キー */ struct page *branch[2 * M + 1]; /* 他ページへのポインタ */ } *pageptr; /* {\tt pageptr} はページへのポインタの型 */
まずは、構造の定義。そして、少し実行してからgdbで検査。
挿入 In, 検索 Sn, 削除 Dn (n:整数) ? i33 reg'd ((.22.33.50.)54(.60.67.70.)) (gdb) p *root $6 = { n = 1, key = {54, 0, 0, 0}, branch = {0x4e665e80, 0x4e6263c0, 0x0, 0x0, 0x0} (gdb) p *root.branch[0] $7 = { n = 3, key = {22, 33, 50, 60}, branch = {0x0, 0x0, 0x0, 0x0, 0x0} } (gdb) p *root.branch[1] $8 = { n = 3, key = {60, 67, 70, 0}, branch = {0x0, 0x0, 0x0, 0x0, 0x0} }
insert 65 (.65.) 91 (.65.91.) 44 (.44.65.91.) 56 (.44.56.65.91.) 42 ((.42.44.)56(.65.91.)) 73 ((.42.44.)56(.65.73.91.)) 82 ((.42.44.)56(.65.73.82.91.)) 60 ((.42.44.)56(.60.65.)73(.82.91.)) 18 ((.18.42.44.)56(.60.65.)73(.82.91.)) 25 ((.18.25.42.44.)56(.60.65.)73(.82.91.)) 79 ((.18.25.42.44.)56(.60.65.)73(.79.82.91.)) 78 ((.18.25.42.44.)56(.60.65.)73(.78.79.82.91.)) 3 ((.3.18.)25(.42.44.)56(.60.65.)73(.78.79.82.91.)) 66 ((.3.18.)25(.42.44.)56(.60.65.66.)73(.78.79.82.91.)) 97 ((.3.18.)25(.42.44.)56(.60.65.66.)73(.78.79.)82(.91.97.)) 47 ((.3.18.)25(.42.44.47.)56(.60.65.66.)73(.78.79.)82(.91.97.)) delete 65 ((.3.18.)25(.42.44.47.)56(.60.66.)73(.78.79.)82(.91.97.)) 91 ((.3.18.)25(.42.44.47.)56(.60.66.)73(.78.79.82.97.)) 44 ((.3.18.)25(.42.47.)56(.60.66.)73(.78.79.82.97.)) 56 ((.3.18.)25(.42.47.60.66.)73(.78.79.82.97.)) 42 ((.3.18.)25(.47.60.66.)73(.78.79.82.97.)) 73 ((.3.18.)25(.47.60.66.)78(.79.82.97.)) 82 ((.3.18.)25(.47.60.66.)78(.79.97.)) 60 ((.3.18.)25(.47.66.)78(.79.97.)) 18 ((.3.25.47.66.)78(.79.97.)) 25 ((.3.47.66.)78(.79.97.)) 79 ((.3.47.)66(.78.97.)) 78 (.3.47.66.97.) 3 (.47.66.97.) 66 (.47.97.) 97 (.47.) 47 .
これB木の成長と衰退の様子の現場中継。左の数字はランダムに発生させたkey 値だ。右 側の一見consっぽいのは、もとえ、括弧で囲まれたものは、一つのpage構造体 のkey値になる。
4個のキーまでは収納できるけど、それ以上になると別のページを作ってそち らに逃すようになってる。ページ内で整列。逃した場合も整列される様にされ てるのが味噌だ。
real sqlite3
btree.[ch]が、自動作成されるみたい。ヘッダーの方を(本体に比べて短かい という理由で)覗いてみる。
** Field usage summary: ** ** Table BTrees Index Btrees ** ** pKey always NULL encoded key ** nKey the ROWID length of pKey ** pData data not used ** aMem not used decomposed key value ** nMem not used entries in aMem ** nData length of pData not used ** nZero extra zeros after pData not used ** ** This object is used to pass information into sqlite3BtreeInsert(). struct BtreePayload { const void *pKey; /* Key content for indexes. NULL for tables */ sqlite3_int64 nKey; /* Size of pKey for indexes. PRIMARY KEY for tabs */ const void *pData; /* Data for tables. */ sqlite3_value *aMem; /* First of nMem value in the unpacked pKey */ u16 nMem; /* Number of aMem[] value. Might be zero */ int nData; /* Size of pData. 0 if none. */ int nZero; /* Extra zero data appended after pData,nData */ };
テーブルで使う場合とインディクスで使う場合と、大谷みたいな起用のされか たをするんだな。
本体のbtree.c
#define NN 1 /* Number of neighbors on either side of pPage */ #define NB 3 /* (NN*2+1): Total pages involved in the balance */ typedef struct CellArray CellArray; struct CellArray { int nCell; /* Number of cells in apCell[] */ MemPage *pRef; /* Reference page */ u8 **apCell; /* All cells begin balanced */ u16 *szCell; /* Local size of all cells in apCell[] */ u8 *apEnd[NB*2]; /* MemPage.aDataEnd values */ int ixNx[NB*2]; /* Index of at which we move to the next apEnd[] */ };
なんか、奥村先生っぽい構造が出てきたな。
pkg by sqlite3
sqliteの使い所が前回の説明にあった。個人でチマチマ使うのにはベスト・マッ チだそうだ。そんな訳なんで、FreeBSDでの利用を探ってみる。できれば、 sqliteを呼出ている現場を押さえてみたい。本当の実例ね。
/var/db
DBと言うか、データの集積場所が、こちらだ。関係がありそうな奴を見る。
[sakae@fb /var/db]$ ls -l pkg/ total 104580 -rw-r--r-- 1 root wheel 158 Mar 10 06:17 FreeBSD.meta -rw-r--r-- 1 root wheel 50585600 Mar 12 05:22 local.sqlite -rw-r--r-- 1 root wheel 48918528 Mar 10 06:17 repo-FreeBSD.sqlite -rw-r--r-- 1 root wheel 0 Mar 12 05:22 repo-FreeBSD.sqlite-journal -r--r--r-- 1 root wheel 7445436 Feb 23 05:57 vuln.xml [sakae@fb /var/db]$ file pkg/local.sqlite pkg/local.sqlite: SQLite 3.x database, user version 36, last written using SQLite version 3040000, file counter 5799, database pages 12350, 1st free page 8232, free pages 1047, cookie 0x58, schema 4, UTF-8, version-valid-for 5799
ファイル名でsqliteと名乗っているので、詐称してないか確認。確かに本物だ な。local.sqliteは多分、今このマシンにインストールされてるパッケージの 情報だろう。もう一つは、FreeBSDプロジェクトが提供してるパッケージの情 報だろう。ついでに、vuln.xmlも調べてみると、セキュリティー勧告用のデー タっぽかった。
これでpkgがsqlite3を使っている事が確定。次は、どう使っているかだな。
find source
それには、ソースを見るのが一番。/usr/src/usr.sbin/pkgに突入する。きっ
と、 sqlite3_open
とかが、記載されているはず。
が、予想は大外れ。man pkgして列挙される、 pkg-info
とかの片鱗すら見
付からない。あるのは、
static const char confirmation_message[] = "The package management tool is not yet installed on your system.\n" "Do you want to fetch and install it now? [y/N]: ";
とか、bootstrapって言うような文字列。そうか、本物をインストールするた めのコマンドなんだな。pkg自体がpkgになってるとな。 usr/posts/ports-mgmt 経由で、 freebsd/pkg at github に、行きついた。
実体は、
[sakae@fb ~]$ ls -l /usr/local/sbin/pkg* -rwxr-xr-x 1 root wheel 2695004 Jan 3 10:11 /usr/local/sbin/pkg* -rwxr-xr-x 1 root wheel 21973592 Jan 3 10:11 /usr/local/sbin/pkg-static* -rwxr-xr-x 1 root wheel 21803548 Jul 1 2021 /usr/local/sbin/pkg-static.pkgsave*
pkgなんでOSのリリースとは非同期でアップデート出来るとな。
[sakae@fb ~]$ ldd /usr/local/sbin/pkg /usr/local/sbin/pkg: libelf.so.2 => /lib/libelf.so.2 (0x206a8000) libjail.so.1 => /lib/libjail.so.1 (0x206c4000) libssl.so.111 => /usr/lib/libssl.so.111 (0x206cc000) libcrypto.so.111 => /lib/libcrypto.so.111 (0x2074d000) libarchive.so.7 => /usr/lib/libarchive.so.7 (0x209bd000) libbz2.so.4 => /usr/lib/libbz2.so.4 (0x20a89000) libz.so.6 => /lib/libz.so.6 (0x20a9d000) liblzma.so.5 => /usr/lib/liblzma.so.5 (0x20ab7000) libprivatezstd.so.5 => /usr/lib/libprivatezstd.so.5 (0x20ae1000) libm.so.5 => /lib/libm.so.5 (0x20b91000) libutil.so.9 => /lib/libutil.so.9 (0x20bcd000) libmd.so.6 => /lib/libmd.so.6 (0x20be4000) libthr.so.3 => /lib/libthr.so.3 (0x20c08000) libc.so.7 => /lib/libc.so.7 (0x20c32000) libbsdxml.so.4 => /lib/libbsdxml.so.4 (0x20e04000)
あれ? libsqliteを利用してないの? いや多分埋没してるんでしょう。完全 に埋没させると、スタチィク版になるんだな。こうなると、 一応、証拠を固めておくか。
[sakae@fb ~]$ strings /usr/local/sbin/pkg | grep sqlite3_open sqlite3_open_v2 sqlite3_open
watch
後は、心おきなく、ソースを散策する。src/main.cはpkgコマンドのメイン。 installとかsearchとかは、サブコマンドになる。んで、例として、info.cを 題材にしてみる。
このファイル中には、 sqlite3_xxx
なんてのは一切ない。レールの作りのよう
にインピーダンス・マッチングしてるんだな。そのかわりに、
ret = pkgdb_access(PKGDB_MODE_READ, PKGDB_DB_LOCAL); ret = pkgdb_open(&db, PKGDB_DEFAULT); if ((it = pkgdb_query(db, pkgname, match)) == NULL) {
これを頼りに探してみると、
[sakae@fb ~/pkg/libpkg]$ grep pkgdb_open *.c pkgdb.c:pkgdb_open(struct pkgdb **db_p, pkgdb_t type) pkgdb.c: return (pkgdb_open_all(db_p, type, NULL)); pkgdb.c:pkgdb_open_repos(struct pkgdb *db, const char *reponame) pkgdb.c:pkgdb_open_all(struct pkgdb **db_p, pkgdb_t type, const char *reponame) pkgdb.c: ret = pkgdb_open_repos(db, reponame); pkgdb.c: * Used both in the shell and pkgdb_open [sakae@fb ~/pkg/libpkg]$ wc pkgdb* 3031 8887 74076 pkgdb.c 1318 3532 31602 pkgdb_iterator.c 523 2040 15609 pkgdb_query.c 4872 14459 121287 total
pkgdb.c
prepare_sql(sqlite3 *s, const char *sql) { int ret; sqlite3_stmt *stmt; ret = sqlite3_prepare_v2(s, sql, strlen(sql), &stmt, NULL); if (ret != SQLITE_OK) { ERROR_SQLITE(s, sql); return (NULL); } return (stmt); }
一枚皮をかぶせるとORMな手法になるのかな。オブジェクト関係マッピング
pkgdb_init(sqlite3 *sdb) { const char sql[] = "" "PRAGMA journal_mode = TRUNCATE;" "PRAGMA synchronous = FULL;" "BEGIN;" "CREATE TABLE packages (" "id INTEGER PRIMARY KEY," "origin TEXT NOT NULL," "name TEXT NOT NULL," : return (sql_exec(sdb, sql, DBVERSION)); }
そんな事より、正体見たり。後は、これらのファイルを丁寧に見てくダ ケーーーーです。
[USERS2] = { NULL, "INSERT INTO pkg_users(package_id, user_id) " "VALUES (?1, (SELECT id FROM users WHERE name = ?2))", "IT", },
これ、SQLでの値を引っ張ってきて埋め込みの手法か。
pkg shell
恐しい事に、pkgコマンドからsqlite3を呼び出せるようになってる。十分に注 意して使えって但し書きがついてるけどね。
[sakae@fb ~]$ pkg shell SQLite version 3.40.0 2022-11-16 12:10:08 Enter ".help" for usage hints. sqlite> .tables annotation pkg_annotation pkg_requires categories pkg_categories pkg_script config_files pkg_conflicts pkg_shlibs deps pkg_directories pkg_shlibs_provided directories pkg_groups pkg_shlibs_required files pkg_licenses pkg_users groups pkg_lock provides licenses pkg_lock_pid requires lua_script pkg_lua_script script mtree pkg_option shlibs option pkg_option_default users option_desc pkg_option_desc packages pkg_provides
色々なテーブルがあるな。
sqlite> .mode table sqlite> select * from users; +----+------------+ | id | name | +----+------------+ | 1 | _tss | | 2 | messagebus | | 3 | avahi | | 4 | polkitd | | 5 | cups | | 6 | colord | | 7 | webcamd | | 8 | git_daemon | | 9 | pulse | | 10 | mysql | +----+------------+
pkgの導入に伴なって新らしいユーザーが作成されましたとな。
sqlite> select * from script limit 1; +-----------+----------------------------------------------------------+ | script_id | script | +-----------+----------------------------------------------------------+ | 1 | if ! /usr/sbin/service ldconfig restart >/dev/null; then | | | if [ -z "${INSTALL_AS_USER}" ]; then | | | exit 1 | | | fi | | | fi | +-----------+----------------------------------------------------------+
スクリプトまでDBに入れてあるのはいいけど、雑誌や本のようにパラパラ出来ないのが欠点。 世はディジタルな時代で電子本が大流行だけど、オイラーは普通の本の方が好きだ。
other cmd using sqlite
便利なsqliteは、あちこちで利用されてるだろう。2つの側面から調べてみる。
on FreeBSD
まずは、OS関係者が使っていないか。ソースをガサ入れ。
[sakae@fb /usr/src]$ find . -name '*.c' | xargs grep -l sqlite3_ ./contrib/wpa/hostapd/hlr_auc_gw.c ./contrib/wpa/src/radius/radius_server.c ./contrib/wpa/src/eap_server/eap_sim_db.c ./contrib/wpa/src/ap/eap_user_db.c ./contrib/wpa/src/ap/ieee802_1x.c ./contrib/wpa/src/ap/hostapd.c ./contrib/sqlite3/sqlite3.c ./contrib/sqlite3/tea/generic/tclsqlite3.c ./contrib/sqlite3/shell.c ./contrib/apr-util/dbd/apr_dbd_sqlite3.c ./contrib/apr-util/dbd/apr_dbd.c ./contrib/subversion/subversion/libsvn_subr/sqlite.c ./contrib/subversion/subversion/libsvn_subr/sqlite3wrapper.c ./crypto/heimdal/lib/hdb/hdb-sqlite.c ./crypto/heimdal/lib/krb5/scache.c
wpaは、無線LAN方面かな。あとは良く知らないなあ。
on pkg
色々なパッケージが利用してないか?
[sakae@fb ~]$ pkg info -r sqlite3-3.40.1,1 sqlite3-3.40.1,1: nss-3.88.1 sqlitebrowser-3.12.1_7 py39-sqlite3-3.9.16_7 tracker3-3.1.2_6 grilo-plugins-0.3.15 webkit2-gtk3-2.34.6_4 gnupg-2.3.8 tracker-2.3.4_9 libsoup-2.74.3 libsoup3-3.2.2 colord-1.4.6 py38-sqlite3-3.8.16_7 gom-0.4
案外、知らないパッケージが使っていた。このうち、自分が意思を持っていれ たのは、sqlitebrowserだけだ。知らないうちに、汚染されちゃってる訳ね。