SQLite の SQL 文の進捗状況取得

PSS を作っていて、SQL 文の実行の進捗状況を取得できればいいなと思っていた。

プログラムが SQL 文(特にSELECT)の進捗状況を取得し、ユーザに適切に提示することによって、ユーザは長い待ち時間を変化のない画面をただ黙って見ているだけでなく、プログレスバーのような UI 要素を見ながらティータイムを楽しむことができる。

↑と、なんとなく訳文っぽく書いてみたけど、要は PSS が使っている SQLite のデータベースは肥大化しまくりで、下手なクエリーを書こうものなら(書かざるを得ない局面もたくさんある)10秒近く待たされることもある。実際には複数のクエリーに分割するようなテクニックで、1(+α) 秒毎に 10 回表示するようにしたりしてるんだけど、それでも 1 秒は長すぎ。

そんなことを漠然といつも考えていたら SQLite に面白い構文があることを発見。

http://www.net-newbie.com/sqlite/lang.html#explain

ほう、EXPLAIN 文で OP コードに落とせるのね。

SQLiteSQL 文発行時に hook 関数をかませることができるのは知っていたんだけど、hook 関数の引数である「現在実行中の OP コード番号」をどう使えばいいのかさっぱり分からなかった。事前に SQL 文を実行し、OP コードの行数をカウントしておいて、実際の SQL 文発行時との比較で進捗を知るのか、とかわけわかんないこと考えてた。
なるほど EXPLAIN 文を使えばほぼコスト0で OP コード量を知ることができるわけね。
って、本当かなぁ?

sqlite_progress_handler

あたりが使えるのかなと思ってググったら下記のページが。

http://d.hatena.ne.jp/halts/20040317#p3

お、おれの日記かよ!w

ちょうどほぼ2年前にもこんな微妙にディープなことやろうとしてたのね。
このころのほうがまともな「開発裏話」を書いていたような気がする。

まともなやり方


int sqlite3_prepare(
sqlite3 *db, /* Database handle */
const char *zSql, /* SQL statement, UTF-8 encoded */
int nBytes, /* Length of zSql in bytes. */
sqlite3_stmt **ppStmt, /* OUT: Statement handle */
const char **pzTail /* OUT: Pointer to unused portion of zSql */
);

で OP コードに落とす。

SQLite 内部の OP コードテーブルのうち、実行開始アドレス ppStmt と終了アドレス pzTail が取得できるので、ppStmt を使って


int sqlite3_step(sqlite3_stmt*);

を呼び出す。

んで、…sqlite3_trace() と sqlite3_progress_handler() を組み合わせるんだろうけど面倒なので今日はこのへんで。

結局 sqlite3.h を読んだだけじゃんorz