UD/CRUD
Table of Contents
delete …
今FreeBSDのパッケージになってるrfcを更新してみたら、最新は、rfc9371だっ た。でも、古いものからの積み重ねになってんだよなあ。と言う事で、簡単な データベースを作成。これを肴にする。
sqlite3 test.db create table rfc(id integer,name text); insert into rfc(id,name) values(786,'mail'); insert into rfc(id,name) values(801,'TCP'); insert into rfc(id,name) values(1945,'HTTP');
前回は軽く触れたsqliteの動作ね。潜ってみたい。
#include <stdio.h> #include "sqlite3.h" int main(void){ sqlite3 *db; char *filename="test.db"; int rc; //result codes sqlite3_stmt *stmt; rc = sqlite3_open(filename, &db); rc = sqlite3_prepare_v2(db,"DELETE FROM rfc where id=2", -1, &stmt, 0); if(rc != SQLITE_OK){ printf("ERROR(%d) %s\n",rc, sqlite3_errmsg(db)); sqlite3_close(db); return 2; } rc = sqlite3_step(stmt); // exec delete if(rc != SQLITE_DONE){ printf("ERROR(%d) %s\n",rc,sqlite3_errmsg(db)); } rc = sqlite3_finalize(stmt); if(rc != SQLITE_OK){ printf("ERROR(%d) %s\n",rc,sqlite3_errmsg(db)); } sqlite3_close(db); return 0; }
CRUDのうちのdeleteをやる。有り難くコードを利用させて貰う。Uも貴重です よ。普通はCRだけの説明が多いですからね。
cc -g -O0 rfc_del.c -L`pwd`/.libs -lsqlite3
で、こんな風にコンパイルしたら、バイナリーは無事に作成されたけど、ライ ブラリーが見付からないと言う実行時エラー。訳ワカメだったの で、.libs/libsqlite3.so.8.6を、/usr/libに入れてしまった。 ライブラリィーは、/home/sakae/sqlite-autoconf-3410000 か ら、–enable-debug付きで 作ったものだ。
実行の前に説明を聞いておく。
sqlite> explain DELETE FROM rfc where id=2; addr opcode p1 p2 p3 p4 p5 comment ---- ------------- ---- ---- ---- ------------- -- ------------- 0 Init 0 12 0 0 Start at 12 1 Null 0 1 0 0 r[1]=NULL 2 OpenWrite 0 2 0 2 0 root=2 iDb=0; rfc 3 Explain 3 0 0 SCAN rfc 0 4 Rewind 0 11 0 0 5 Column 0 0 2 0 r[2]= cursor 0 column 0 6 Ne 3 10 2 BINARY-8 84 if r[2]!=r[3] goto 10 7 ReleaseReg 2 1 0 0 release r[2] mask 0 8 Rowid 0 4 0 0 r[4]=rfc.rowid 9 Delete 0 1 0 rfc 2 10 Next 0 5 0 1 11 Halt 0 0 0 0 12 Transaction 0 1 1 0 1 usesStmtJournal=0 13 TableLock 0 2 1 rfc 0 iDb=0 root=2 write=1 14 Integer 2 3 0 0 r[3]=2 15 Goto 0 1 0 0
説明ついでに、manから拾い出したのをのせておく。SQL文って、綺麗に清書し たのを沢山見るけど、一行野郎なんだね。
sqlite3 test.db 'select * from rfc;'
An Introduction To The SQLite C/C++ Interface まず、これを参照して全体 を把握する事だな。
try again in FreeBSD
FreeBSDで失敗して悔しいので再トライ。方針は、sqlite3系をちゃんとインス トールする。そして、禁断の設定をしてターゲットをコンパイル。
./configure --prefix=/home/sakae/MINE --enable-debug --enable-shared
こんな風にsqlite3を用意しる。自前のエリアに入れるんだな。
[sakae@fb ~/MINE/work]$ LD_LIBRARY_PATH=/home/sakae/MINE/lib cc -v -g -O0 rfc_del.c -I/home/sakae/MINE/include -L/home/sakae/MINE/lib -lsqlite3
ターゲットのコンパイル時にライブラリィーのエリアを指定。指定したからと 言って、-Lの指定は省略できない。
[sakae@fb ~/MINE/work]$ ldd a.out a.out: libsqlite3.so.0 => /home/sakae/MINE/lib/libsqlite3.so.0 (0x20443000) libc.so.7 => /lib/libc.so.7 (0x206aa000) libz.so.6 => /lib/libz.so.6 (0x2087c000) libm.so.5 => /lib/libm.so.5 (0x20896000) libthr.so.3 => /lib/libthr.so.3 (0x208d2000)
これで目的のエリアにあるライブラリィーがリンクされた。勿論ちゃんと機能 するよ。
[sakae@fb ~/MINE/work]$ gdb -q a.out Reading symbols from a.out... (gdb) b sqlite3_step Breakpoint 1 at 0x401ad0 (gdb) r Starting program: /usr/home/sakae/MINE/work/a.out Breakpoint 1, sqlite3_step (pStmt=0x20966b78) at sqlite3.c:88538 88538 int rc = SQLITE_OK; /* Result from sqlite3Step() */ (gdb) bt 3 #0 sqlite3_step (pStmt=0x20966b78) at sqlite3.c:88538 #1 0x204cfff6 in sqlite3_exec (db=0x20934008, zSql=0x20969ba8 "SELECT*FROM\"main\".sqlite_master ORDER BY rowid", xCallback=0x20541820 <sqlite3InitCallback>, pArg=0xffbfdee0, pzErrMsg=0x0) at sqlite3.c:131919 #2 0x20541655 in sqlite3InitOne (db=0x20934008, iDb=0, pzErrMsg=0xffbfe8e4, mFlags=0) at sqlite3.c:137529 (More stack frames follow...)
但しemacsからgdbを起動した場合、
(gdb) b sqlite3_step Breakpoint 1 at 0x401ad0 (gdb) r Starting program: /usr/home/sakae/MINE/work/a.out Breakpoint 1, 0x2047e616 in sqlite3_step () from /usr/local/lib/libsqlite3.so.0
と、なってしまう。
[sakae@fb ~/MINE/work]$ export LD_LIBRARY_PATH=/home/sakae/MINE/lib [sakae@fb ~/MINE/work]$ mg rfc_del.c
こんな風に、場所を広告した上でemacsを使っても、その広告は、ターゲット までは届かずに、/usr/local/lib/libsqlte3の方が使われてしまっていた。相 性悪いな。
gud: how to set the LDLIBRARYPATH before run?
むむ、こんなのが有るぞ。
try ArchLinux
普通にemacsからgdbを起動すると、
Reading symbols from sqlite3... This GDB supports auto-downloading debuginfo from the following URLs: <https://debuginfod.archlinux.org> Enable debuginfod for this session? (y or [n]) Please answer y or [n]. : Enable debuginfod for this session? (y or [n]) n Please answer y or [n].
山本リンダ風に、どうにも止まらないになってしまう。こんなの初めての経験。 これを脱するには、
M-x gud-gdb と起動すればいいらしい。
Run gud-gdb (like this): gdb --fullname sqlite3
以下は、そうやった時のセッション。
(gdb) b sqlite3_exec Breakpoint 1 at 0x1274cf: file sqlite3.c, line 131888. (gdb) r doctor.db 'select * from Procs;' Starting program: /home/sakae/sqlite-autoconf-3410000/sqlite3 doctor.db 'select * from Procs;' This GDB supports auto-downloading debuginfo from the following URLs: <https://debuginfod.archlinux.org> Enable debuginfod for this session? (y or [n]) n Debuginfod has been disabled. To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit. [Thread debugging using libthread_db enabled] Using host libthread_db library "/usr/lib/libthread_db.so.1". Breakpoint 1, sqlite3_exec (db=0x5555557d9518, zSql=0x5555557e8068 "SELECT*FROM\"main\".sqlite_master ORDER BY rowid", xCallback=0x55555568410c <sqlite3InitCallback>, pArg=0x7fffffffbfe0, pzErrMsg=0x0) at sqlite3.c:131888 (gdb)
案内にもあるように、set debuginfod enabled off を記述しておけとな。
sqlite3 script
.open test.db create table rfc(id integer,name text); insert into rfc(id,name) values(786,'mail'); insert into rfc(id,name) values(801,'TCP'); insert into rfc(id,name) values(1945,'HTTP'); .mode box .print 'orignl data' select * from rfc; .print 'update data for id:801' update rfc set name='TCP/IP' where id=801; .print 'delete data for mail' delete from rfc where id=786; .print 'after UD' select * from rfc;
こんな具合にドット・コマンドとSQL文の混合スクリプトを作成した。
[sakae@fb ~/MINE/work]$ sqlite3 SQLite version 3.40.1 2022-12-28 14:03:47 Enter ".help" for usage hints. Connected to a transient in-memory database. Use ".open FILENAME" to reopen on a persistent database. sqlite> .read ud.sql orignl data ┌──────┬──────┐ │ id │ name │ ├──────┼──────┤ │ 786 │ mail │ │ 801 │ TCP │ │ 1945 │ HTTP │ └──────┴──────┘ update data for id:801 delete data for mail after UD ┌──────┬────────┐ │ id │ name │ ├──────┼────────┤ │ 801 │ TCP/IP │ │ 1945 │ HTTP │ └──────┴────────┘ sqlite>
起動して、中からスクリプトを読み込み(=実行)してみた。出来の悪いアップ デートとデリートの実例。
sqlite> .read ud.sql Parse error near line 2: table rfc already exists create table rfc(id integer,name text); ^--- error here orignl data ┌──────┬────────┐ │ id │ name │ ├──────┼────────┤ │ 801 │ TCP/IP │ │ 1945 │ HTTP │ │ 786 │ mail │ │ 801 │ TCP │ │ 1945 │ HTTP │ └──────┴────────┘ :
再度実行すると、既にDBは作成済なので、エラーと言われる。でも、実行でき ちゃった。
そういう難点を解消するには、起動時に、テーブルをdropしてしまえば良い。 そんな設定ファイルを用意して、sqlite3 –init xx.sql のように使えばよい。
Command Line Shell For SQLite に詳しい説明があるぞ。
csv to sqlite3
上のCLIの説明を見ていたら、簡単にCSVからデータを輸入できるようだ。
sqlite> .import --csv --skip 1 --schema temp C:/work/somedata.csv tab1
一応説明を参照しておく。
sqlite> .help import .import FILE TABLE Import data from FILE into TABLE Options: --ascii Use \037 and \036 as column and row separators --csv Use , and \n as column and row separators --skip N Skip the first N rows of input --schema S Target table to be S.TABLE -v "Verbose" - increase auxiliary output Notes: * If TABLE does not exist, it is created. The first row of input determines the column names. * If neither --csv or --ascii are used, the input mode is derived from the ".mode" output mode * If FILE begins with "|" then it is a command that generates the input text.
オイラーが貯めこんでいる血圧データから、朝の最高血圧だけを、年月と共に 採取。その冒頭には、ヘッダーをつけておいた。
#! /bin/sh cat current.csv | awk -F, \ 'BEGIN{printf("yy,mm,hi\n")} {if ($1 % 100 < 12) {printf("%s,%s,%s\n", substr($1,1,2), substr($1,3,2), $2)} }'
これを走らせて、一時ファイルのz.csvなんてのにリダイレクト。それから saliteに輸入しる。
$ sqlite3 my.db SQLite version 3.41.0 2023-02-21 18:09:37 Enter ".help" for usage hints. sqlite> .import --csv z.csv bld sqlite> .mode table sqlite> select * from bld limit 3; +----+----+-----+ | yy | mm | hi | +----+----+-----+ | 12 | 01 | 118 | | 12 | 01 | 128 | | 12 | 01 | 122 | +----+----+-----+
冒頭部分をちょいと確認。
sqlite> select yy, avg(hi) from bld group by yy; +----+------------------+ | yy | avg(hi) | +----+------------------+ | 12 | 119.644444444444 | | 13 | 119.050279329609 | | 14 | 122.433701657459 | :
次は年毎に平均を出してみた。無駄に桁数が多いな。こういう時は丸めればい いのか。ついでに、タイトルもasを使って置き換えてみた。roundは組み込み の関数。avgは組み込みの集計(aggregate)関数に分類されてた。
sqlite> select yy, round(avg(hi),2) as avg from bld group by yy; +----+--------+ | yy | avg | +----+--------+ | 12 | 119.64 | | 13 | 119.05 | | 14 | 122.43 | :
sqlite> select yy,mm,round(avg(hi),2) as avg from bld where yy=23 group by mm; +----+----+--------+ | yy | mm | avg | +----+----+--------+ | 23 | 01 | 135.71 | | 23 | 02 | 136.75 | +----+----+--------+
月毎の平均を求めると、何も指定しなければ、最初の年で終了してしまう。そ んな訳で、年をwhere句で強制指定してみた。
CREATE VIEW yma(yy, mm, avg) AS select L.yy, L.mm, round(avg(L.hi),2) from bld as L, bld as R where L.yy = R.yy and L.mm = R.mm group by L.yy, R.mm;
次は、こんなビューを作成。普通にやるだけで十分なんだけど、SQLパズルの 復習って事で。自分でやってみると、よく原理が分った。 原テーブルに別名のLとRをつける。左のテーブルと右のテーブルだな。where 句で、左右のそれぞれの列が等しいって条件を加味してselectする。 その結果は、合成したようなものだから、VIEWで新しいテーブルとして登録。
結果を確認。
sqlite> select * from yma limit 9,6; +----+----+--------+ | yy | mm | avg | +----+----+--------+ | 12 | 10 | 119.81 | | 12 | 11 | 122.5 | | 12 | 12 | 124.67 | | 13 | 01 | 124.03 | | 13 | 02 | 119.79 | | 13 | 03 | 116.81 | +----+----+--------+
これの答合わせはpythonエリアにお住まいのう北極熊さんにお願いかな。
ああ、.import時に勝手に作成されちゃったテーブルは、どういう形だろ?
CREATE TABLE IF NOT EXISTS "bld"( "yy" TEXT, "mm" TEXT, "hi" TEXT);
思った通りテキスト属性だった。それに対して、平均を計算するなんて、よき に計らえって方式。古くはawkやperlの流れをくんでいるな。pythonみたいに ツンツンしてない、大らかな所が好感だな。
tools
The sqlite3analyzer.exe Utility Program
sqlite3_analyzer.exe my.db /** Disk-Space Utilization Report For my.db Page size in bytes................................ 4096 Pages in the whole file (measured)................ 18 Pages in the whole file (calculated).............. 18 Pages that store data............................. 18 100.0% Pages on the freelist (per header)................ 0 0.0% Pages on the freelist (calculated)................ 0 0.0% Pages of auto-vacuum overhead..................... 0 0.0% Number of tables in the database.................. 2 Number of indices................................. 0 Number of defined indices......................... 0 Number of implied indices......................... 0 Size of the file in bytes......................... 73728 Bytes of user payload stored...................... 44627 60.5% :
このツールはMINGW64に紛れこんでいた。リナとか*BSDには入っていなかった ぞ。ソースは何処にあるのだろう? Windowsだけが優遇されてるの?
sqlite3_analyzer.exe
sqlite3_analyzer.sh
こんなのが同梱されてるんだけ
どな。
$ pacman -Qi mingw-w64-x86_64-sqlite3 Name : mingw-w64-x86_64-sqlite3 Version : 3.41.0-1 Description : A C library that implements an SQL database engine (mingw-w64) Architecture : any URL : https://www.sqlite.org Licenses : PublicDomain Groups : None Provides : mingw-w64-x86_64-sqlite=3.41.0 mingw-w64-x86_64-sqlite-analyzer=3.41.0 Depends On : mingw-w64-x86_64-gcc-libs mingw-w64-x86_64-readline mingw-w64-x86_64-tcl mingw-w64-x86_64-zlib Optional Deps : None Required By : mingw-w64-x86_64-python Optional For : None Conflicts With : mingw-w64-x86_64-sqlite-analyzer Replaces : mingw-w64-x86_64-sqlite-analyzer Installed Size : 12.25 MiB Packager : CI (msys2/msys2-autobuild/91ab3435/4335856811) Build Date : Sun Mar 5 20:52:15 2023 Install Date : Mon Mar 6 15:12:42 2023 Install Reason : Installed as a dependency for another package Install Script : No Validated By : Signature
python君の朋友なのか。その親分はgdbだったりするんだけどね。
etc
Debuginfod service - Arch Linux
Debuginfod service - Arch Linux
(gdb) b main Breakpoint 1 at 0x52434: file shell.c, line 26946. (gdb) r Starting program: /tmp/sqlite-autoconf-3410000/sqlite3 Downloading separate debug info for /lib64/ld-linux-x86-64.so.2 Downloading separate debug info for system-supplied DSO at 0x7ffff7fc8000 Downloading separate debug info for /usr/lib/libedit.so.0 Downloading separate debug info for /usr/lib/libncursesw.so.6 Downloading separate debug info for /usr/lib/libz.so.1 Downloading separate debug info for /usr/lib/libm.so.6 Downloading separate debug info for /usr/lib/libc.so.6 [Thread debugging using libthread_db enabled] Using host libthread_db library "/usr/lib/libthread_db.so.1".
通常は使う事がない、デバッグ情報付きのライブラリィーを自動的にDLしてく れる、小粋なサービス。
[sakae@arch sqlite-autoconf-3410000]$ ldd sqlite3 linux-vdso.so.1 (0x00007ffc76ff8000) libedit.so.0 => /usr/lib/libedit.so.0 (0x00007fb2fa48a000) libncursesw.so.6 => /usr/lib/libncursesw.so.6 (0x00007fb2fa416000) libz.so.1 => /usr/lib/libz.so.1 (0x00007fb2fa3fc000) libm.so.6 => /usr/lib/libm.so.6 (0x00007fb2fa314000) libc.so.6 => /usr/lib/libc.so.6 (0x00007fb2fa12d000) /lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007fb2fa75a000)