2012-04-12 8 views
2

私はおそらくそれは非常にばかげた間違いであることは知っていますが、私は文字通り数行のコードを見て、文書を通していました。 :最後の引数はSQLiteバインディングの前の引数をオーバーライドします

私はC++ 11でSQLite C API用の便利なラッパークラスを作成しています。データベースへの接続が確立されると、関数sqlは文を準備し、必要に応じて引数をバインドし、後で実行します。準備と実行はうまくいきますが、1つの引数をバインドするのはうまくいきますが、複数の引数をバインドする場合は、すべての値が常にリストの最後の引数に設定されます。

sql関数は次のようになります。

template <typename... Args> 
bool sql(const std::string &query, const Args &... args) 
{ 
    sql_statement call; 
    if(sqlite3_prepare_v2(database_, query.data(), -1, &call.statement, nullptr) 
     != SQLITE_OK) 
    { 
     return false; 
    } 

    if(!bind(call.statement, 1, args...)) { 
     return false; 
    } 

    return sqlite3_step(call.statement) == SQLITE_DONE; 
} 

sql_statementは、それがスコープ外に実行したときに文を解放するが、何もしないsqlite3_stmtのラッパーの構造体です。 database_は、作業データベースのハンドルです。私はこの機能が問題と何か関係があっても、私は確信していません。トラブルメーカーとしてsql関数内で呼び出されるbind関数です。ここにあります:

template <typename T, typename... Args> 
bool bind(sqlite3_stmt *statement, int current, const T &first, const Args &... args) 
{ 
    std::stringstream ss; 
    ss << first; 
    if(sqlite3_bind_text(statement, current, 
     ss.str().data(), ss.str().length(), SQLITE_STATIC) != SQLITE_OK) 
    { 
     return false; 
    } 
    return bind(statement, current+1, args...); 
} 
bool bind(sqlite3_stmt *, int) { return true; } 

エラーはないことに注意してください。この関数はtrueを返します。私はこれをさまざまな入力でデバッグしました。この例を考えてみます。

if(!db.sql("INSERT INTO test (name, age) VALUES (?, ?);", "nijansen", 23)) { 
    std::cerr << db.error() << std::endl; 
    return 1; 
} 

私はbindの関数呼び出し、期待通りに再帰が機能するので、それは機能の値[1] nijansen[2] 23を渡すをデバッグするとき。 The documentation of the SQLite C APIは "一番左のSQLパラメータは1のインデックスを持っている"と言っているので、問題ではないはずです。それでも、データベースでは、クエリの結果はid: 1, name: 23, age: 23です。私はこの問題をもう一度見ていただきたいと思います。

答えて

2

sqlite3_bind_textは、渡された文字列のコピーを作成せず、文が実行されるまで文字列が生存することが期待されます。あなたのケースでは、sqlite3_bind_textが呼び出された直後に文字列が破棄され、ここでは未定義の動作の可能性を目撃しています。

sqlite3_bind_textでデータの有効期間を独自に管理する場合は、SQLITE_STATICSQLITE_TRANSIENTに変更してください。

関連する問題