sqlite3_prepareの引数と動作の相違(?)

ものすっごい、いまさらだけど、ポエニーで使っているDの自作SQLiteクラスのsqlite3_prepareのラッパーがやばい感じにバグってることが発覚した。
たまにでるアクセス違反やSQLのエラーはこいつが原因かも・・。
原因は、sqlite3_prepareが(俺が思っている)仕様どおりに動いていないという・・・。


# http://www.sqlite.org/capi3ref.html#sqlite3_prepare
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 */
);
The first argument "db" is an SQLite database handle.
The second argument "zSql" is the statement to be compiled,
encoded as either UTF-8 or UTF-16 (see above).
If the next argument, "nBytes", is less than zero,
then zSql is read up to the first nul terminator.
If "nBytes" is not less than zero,
then it is the length of the string zSql in bytes (not characters).

内容としては、SQLiteクラスは、zSqlへDの文字列(null terminateされていたり、いなかったり)のポインタを渡して、その有効バイト長をnBytesへ渡していた。それでOKだと思っていた。でも実際は、null terminateされてない場合に、nBytesで指定したバイト長より先までSQLite3ライブラリが読みにいってて、構文エラーになることがある。現在、公開しているポエニーでは、文字列結合のタイミングがよくて、ほとんどがnull terminateされている状態なので、滅多に起きない現象だが、今いじっている部分でnull terminateされていない確率が格段に増えて、本格的に発覚、というか、前々からここ(動的にSQL文作るあたり)を触るたびに変な動作して意味分からん!と思ってたのだが、よくなったので放置していた。

説明をみるかぎり、nBytesが0より小さい場合は、zSqlをnullまでみて、それ以外だとzSqlのバイト長(文字列長ではない?)と書いてあると思ったのに、SQLite3のソースコードを見ると、nBytesを無視して(sqlite3_prepareの中で一度も使っていない!)、zSqlをtokenize.cのsqlite3RunParser()へ渡してて、そこでnull terminate前提で動いている。
うぬぬぬぬ。