SQLite コードジェネレート箇所を探した
sqlite3_prepare_v2()
でプリペアドステートメント(バイトコード)が生成されている。
バイトコードの生成過程を調べた。
tl;dr
振り返ると普通のコード生成だった。
- プリペアドステートメントはsqlite3_prepare_v2 で生成される
- パースはLALR(1)構文解析器が利用される
- 構文解析器ジェネレーターはLemonを用いている
- 構文解析は sqlite3Parser にトークンを渡す形で実行される
- cmd 非終端記号の還元時にバイトコードは生成される
- 他の非終端記号の還元時は中間情報をノードに格納する
- OpCodeはBTreeを操作する命令になっている
詳細
sqlite3_prepare_v2
sqlite3_prepare_v2
はsqlite3Prepare
(in prepare.c
)を呼びプリペアドステートメントを取得する。
その中ではsqlite3RunParser
(in token.c
)を通して構文解析器が呼ばれている。
sqlite3Prepare
は大雑把に以下の様な流れでバイトコード格納場所(sqlite3_stmt* ppStmt
)へ作成されたバイトコード(pParse->pVdbe
)を代入している。
EXPLAIN
用の処理はバイトコードを表示しているのでsqlite3RunParser
の中でバイトコード生成をしている事が予想される。
|
|
sqlite3RunParser
sqlite3RunParser は構文解析器を駆動する。
lemenパーサジェネレータで生成されているのでサンプルと基本的な流れは同じ。
sqlite3GetToken
でトークンを取得しsqlite3Parser
(in parse.c
, sqliteInt.h
)に渡している。
気になった事は
- 最後に 0 でParserを呼び出す理由がわからない
- パーサーに任さずにToken種別で分岐する理由がわからない
|
|
sqlite3GetToken
は典型的な字句解析器で特に変わったところはない。
sqlite3Parser
はLR系のためシフト・還元の繰返しで実装されている。
yy_find_shift_action()
で状態マシンが次に取るアクションナンバーが返却される。
アクションナンバーによりシフト(yy_shift
)・還元(yy_reduce
)を行う。
バイトコード生成箇所
yy_reduce
でcmdに還元する際にアクションとしてバイトコードが生成される。
バイトコード生成関数はpVdbe
を操作する事でコードを格納している。
この様な関数はbuild.c
,select.c
といったソース内に記述されている。
cmd以外の還元アクションで中間情報を記録している。parse.y
でコード生成は理解できる。
- ASTの各ノードは種別ごとに別の型を使う
- cmd の還元時にコード生成を行う
- 他の非終端記号の還元時には親ノードで利用する情報をノードに格納する
例えば、SQL関数の実行に関係しそうな部分(状態番号196, 197)では、
sqlite3ExprFunction
が呼ばれてpExpr
に値が格納されたりしている。
OpCodeはBTreeを操作する命令で構成されている。
BTreeのインタフェースを確認してEXPLAIN
を使えば概要を掴める。
具体例) SELECT 文の場合
|
|