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だけだ。知らないうちに、汚染されちゃってる訳ね。