sqlite-3.5.5のOpcode
で、驚きのリリース文と書きましたが、どれほどの変化か調べてみました。簡単なSQLではあまり変わりません(当り前?)。
今まで
pushを暗黙に行う命令 pushを暗黙に行う命令 pop,popを暗黙に行う命令
だったのが、
命令。結果をレジスタ1に格納 命令。結果をレジスタ2に格納 命令。レジスタ1とレジスタ2を引数
になるだけです。
以下、sqlite-3.4.2とsqlite-3.5.5の比較です。
sqlite-3.4.2で、単純なselectをした結果は次のようになりました。単純化するために、カラムは全部文字列にしています。
sqlite> CREATE TABLE my (title text, author text, body text); sqlite> .explain sqlite> EXPLAIN SELECT title FROM my WHERE author = 'foo'; addr opcode p1 p2 p3 ---- -------------- ---------- ---------- --------------------------------- 0 Goto 0 13 1 Integer 0 0 2 OpenRead 0 6 3 SetNumColumns 0 2 4 Rewind 0 11 5 Column 0 1 6 String8 0 0 foo 7 Ne 353 10 collseq(BINARY) 8 Column 0 0 9 Callback 1 0 10 Next 0 5 11 Close 0 0 12 Halt 0 0 13 Transaction 0 0 14 VerifyCookie 0 10 15 Goto 0 1 16 Noop 0 0
以下、addrカラム値がnの行をn行目と呼びます。
初期化の部分を無視すると、ループを形成しているのは、5行目から10行目です。10行目のNextは、レコードがあれば5行目にジャンプ、無ければ次の行(Close)に進みます。 5行目は1番目のカラムの値をスタックに積みます。カラムは0ベースなので、この場合、authorカラムです。6行目は文字列"foo"をスタックに積みます。7行目のNeは、勘が良ければ分かると思いますが、not equalの略で、スタックからふたつの値をポップして比較します。not equalが成立すると、10行目に飛びます。10行目はNextなので、このジャンプはcontinue相当です。8行目のColumnは0番目のカラム(titleカラム)の値をスタックに積みます。9行目のCallback命令は、スタックから値をポップして、ユーザアプリのコールバック関数を呼びます。sqliteの結果セットは、レコード単位でコールバック関数が呼ばれます。
同じことをsqlite-3.5.5ですると次のようになりました。
sqlite> EXPLAIN SELECT title FROM my WHERE author = 'foo'; addr opcode p1 p2 p3 p4 p5 comment ---- ------------- ---- ---- ---- ------------- -- ------------- 0 Trace 0 0 0 explain select title from my where author = 'foo'; 00 1 Goto 0 13 0 00 2 OpenRead 0 6 0 00 3 SetNumColumns 0 2 0 00 4 Rewind 0 11 0 00 5 Column 0 1 1 00 6 String8 0 2 0 foo 00 7 Ne 2 10 1 collseq(BINARY) 69 8 Column 0 0 3 00 9 ResultRow 3 1 0 00 10 Next 0 5 0 00 11 Close 0 0 0 00 12 Halt 0 0 0 00 13 Transaction 0 0 0 00 14 VerifyCookie 0 10 0 00 15 TableLock 0 6 0 my 00 16 Goto 0 2 0 00
ループも含めてほとんど変わりません(Callbackの代わりにResultRowになった程度)。5行目と6行目は、それぞれスタックに積まずに、レジスタ1とレジスタ2(それぞれp2で指定されています)に値をセットします。そして、7行目のNeはp1とp3で指定されたレジスタ(つまり1と2)の値を比較して、(not equalが)成立すると(p2で指定された)10行目に飛びます。
インデックスが使われる場合も見てみます。
以下はsqlite-3.4.2の結果です。
sqlite> CREATE INDEX idx_my ON my(author); sqlite> EXPLAIN SELECT title FROM my WHERE author = 'foo'; addr opcode p1 p2 p3 ---- -------------- ---------- ---------- --------------------------------- 0 Goto 0 21 1 Integer 0 0 2 OpenRead 0 6 3 SetNumColumns 0 2 4 Integer 0 0 5 OpenRead 1 7 keyinfo(1,BINARY) 6 String8 0 0 foo 7 IsNull -1 18 8 MakeRecord 1 0 a 9 MemStore 0 0 10 MoveGe 1 18 11 MemLoad 0 0 12 IdxGE 1 18 + 13 IdxRowid 1 0 14 MoveGe 0 0 15 Column 0 0 16 Callback 1 0 17 Next 1 11 18 Close 0 0 19 Close 1 0 20 Halt 0 0 21 Transaction 0 0 22 VerifyCookie 0 11 23 Goto 0 1 24 Noop 0 0
OpenReadが2つ(2行目と4行目)あります。4行目のOpenReadはインデックスのオープンです。それぞれp1の値が、それぞれの識別子です(Unixのシステムコール風に言えば、ファイルディスクリプタ相当です)。
ループは11行目から17行目です。
10行目のMoveGeで、インデックス上の"foo"の最初の位置を指します。12行目のIdxGEはインデックス上の値と"foo"を比較して、一致しなければ(正確には名前から推測できるようにgreater than or equalの演算)、18行目に飛んでループを抜けます。14行目のMoveGeはrowid(レコードを一意に識別するid)を使って、テーブル内のポインタ(カーソル)を動かします。
以下はsqlite-3.5.5の結果です。
sqlite> EXPLAIN SELECT title FROM my WHERE author = 'foo'; addr opcode p1 p2 p3 p4 p5 commeaddr opcode p1 p2 p3 p4 p5 comment ---- ------------- ---- ---- ---- ------------- -- ------------- 0 Trace 0 0 0 explain select title from my where author = 'foo'; 00 1 Goto 0 19 0 00 2 OpenRead 0 6 0 00 3 SetNumColumns 0 2 0 00 4 OpenRead 1 7 0 keyinfo(1,BINARY) 00 5 SetNumColumns 1 2 0 00 6 String8 0 2 0 foo 00 7 IsNull 2 16 0 00 8 MakeRecord 2 1 1 ab 00 9 MoveGe 1 16 1 00 10 IdxGE 1 16 1 01 11 IdxRowid 1 5 0 00 12 MoveGe 0 0 5 00 13 Column 0 0 6 00 14 ResultRow 6 1 0 00 15 Next 1 10 0 00 16 Close 0 0 0 00 17 Close 1 0 0 00 18 Halt 0 0 0 00 19 Transaction 0 0 0 00 20 VerifyCookie 0 11 0 00 21 TableLock 0 6 0 my 00 22 Goto 0 2 0 00
ループは10行目から15行目です。
9行目のMoveGeで、インデックス上の"foo"の位置を指します。10行目のIdxGEはインデックス上の値と"foo"を比較して、一致しなければ、16行目に飛んでループを抜けます。12行目のMoveGeはrowidを使って、テーブル内のポインタ(カーソル)を動かします。
ほとんど変わりませんが、少し命令が減っています。sqlite-3.4.2にあったMemStoreとMemLoadが無くなっていますが、これらは元々レジスタを扱うような命令だったので、無くなっているようです。
参考URL
- Category(s)
- カテゴリなし
- The URL to Trackback this entry is:
- http://dev.ariel-networks.com/Members/inoue/sqlite-opcode/tbping