鱒身(Masu_mi)のブログ

知った事をメモする場所。

Sqlite入門

SQLite 入門してみた。

気になった関数メモ

気になった主要な関数を羅列する。

sqlite3_exec
DBとSQL文を受け取って実行する。shell_exec も同様にDBに対してSQLを実行する。ただしcallback が異なるとコメントに書かれていた。
shell_callback
シェルに向けて表示している。p->modeで表示モードを切り分けている。 だいぶいろいろなモード(Line, Explain, Column, Semi, List, Html, Tcl, Csv, Insert, Ascii)が存在する。
sqlite3Prepare
UTF-8で書かれたSQL文を実行ハンドラにコンパイルしている。
process_input
コマンド群の入力を受け付けている。特にone_input_line が1行文のコマンド入力を受け取っている。 そのためprocess_input ではINTERACTIVEモードでプロンプトを出し分けたりエラーハンドリングを特別あつかいするための処理を行ったりしていた。
do_meta_command
“.load” など”.” で始まるコマンドの実行を担っていた。
sqlite3RegisterGlobalFunctions
SQLで使われる関数を登録する。 関数には色々(FUNCTION, FUNCTION2, VFUNCTION, DFUNCTION, AGGREGATE, ..)な種類が色々ある。 sqlite3.c:11947 から各種定義と説明を読める。

したこと

動きの確認

configure && make をするとカレント以下にsqlite3 が作られた。実行バイナリ本体は.libs 以下にある。

gdb .libs/lt-sqlite3

よくわからないのでとりあえず、main関数, read/write システムコールの3つにブレイクポイントを設定する。

b main
catch syscall read
catch syscall write

あとは動かす。

r ./test.data 'CREATE TABLE test (id int, text string, author string);'
// なんか止まったらスタックトレースを確認
bt

関数の引数でも最適化で消えてたりするみたい。読み書きが行われるタイミングで

#0  0x00007ffff7ba1690 in sqlite3VdbeList (p=<optimized out>) at sqlite3.c:68078
#1  sqlite3Step (p=<optimized out>) at sqlite3.c:5998
#2  sqlite3_step (pStmt=<optimized out>) at sqlite3.c:6064
#3  0x000000000040791a in shell_exec (db=0x613080,
.....
#4  0x00000000004030a8 in main (argc=<optimized out>, argv=<optimized out>)
    at /home/vagrant/src/bld/../SQLite-bda77dda/src/shell.c:4798

関数の呼び出し関係を俯瞰する

下を行って呼び出し関係を終えるようにするとだいぶ楽になる。ただしgraphvizが必要となる。 Doxygenの設定ファイルで生成する項目をちゃんと決めないと欲しい情報が出てくれない。

doxygen -g
vim Doxygen # 下に守勢する
EXTRACT_STATIC = YES
HAVE_DOT       = YES
CALL_GRAPH     = YES
CALLER_GRAPH   = YES

doxygen Doxygen
python -m SimpleHTTPServer

だいたいしたみたいが画像が全ての関数について手に入れられるしブラウザ上で探索できるので便利。

process_input sqlite3Prepare

コードを読む

shell_execなどが文字列としてSQL文を受け取っているのでどこかでパースやコンパイルしているはずと当たりをつけて中を追う。 またchar配列のSQL文が格納されたzSql を引数に取るはずと想像して読むとsqlite3Prepare がみつかる。コメントにはコンパイルすると書かれている。

この関数の中でsqlite3RunParser が呼ばれており実際にコードを読むとパースしてた。 中で呼ばれているsqlite3RunParser が1文のパースを行うと思われた。

sqlite3RunParser./src/parse.c に書かれていて同ディレクトリにparse.y が存在するからコードジェネレートされていると予想した。

yaccを疑ったけれど動かない。lemon っぽい。試してみたら動いたので当たりの気配がした。調べたら正しかった。 LALR(1)に対応したパーサージェネレータらしい。lex + yacc よりオススメしている人もいるので調べてみても良さそう。

gcc -g -o lemon ./tool/lemon.c
./lemon ../SQLite-a721fc0d/src/parse.y

拡張を使ってみる(fts5)

SQLite は拡張モジュールが使える仕組みになっている。 v3.9.0 ではjson1 , fts5 などの拡張が追加されている。

https://www.sqlite.org/loadext.html#build を参考にビルド・ロードして使って見る。

gcc -g -lm -fPIC -shared fts4.c -o fts5.so
./sqlite3
>> .load fts5
>> CREATE VIRTUAL TABLE email USING fts5(sender, title, body);
>> INSERT INTO email ('masu_mi', 'test', 'test body');
>> INSERT INTO email ('masumi', 'test', 'find my iphone');
>> INSERT INTO email ('masumi', 'hoge', 'kick us');
>> SELECT * FROM email WHERE email MATCH 'kick';