2012-04-12 30 views
2

我知道這可能是一個非常愚蠢的錯誤,但我幾乎一直坐在幾個小時的盯着幾行代碼,通過文檔運行,我仍然無能爲力,爲什麼會發生這種情況:最後一個參數覆蓋了SQLite綁定中的以前的參數

我正在爲C++ 11中的SQLite C API編寫一個便捷包裝類。建立與數據庫的連接時,功能sql準備一個語句,必要時將參數綁定到該語句,然後執行它。準備和執行工作正常,綁定一個參數工作正常,但如果我想綁定多個參數,所有值將始終設置爲列表中的最後一個參數。

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_statementsqlite3_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。我用各種輸入來調試它;當我調試的bind函數調用,遞歸正常工作,所以它傳遞函數的值[1] nijansen[2] 23

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

:考慮這個例子。 The documentation of the SQLite C API說「最左邊的SQL參數的索引是1」,所以不應該是問題。但是,在數據庫中查詢的結果是:id: 1, name: 23, age: 23。我真的很感謝在這個問題上的第二次看。

回答

2

除非爲第五個參數傳遞SQLITE_TRANSIENT,否則sqlite3_bind_text不會生成您傳入的字符串的副本,並且您的字符串預計將一直存在,直到執行語句。在你的情況下,在調用sqlite3_bind_text之後,字符串會立即被銷燬,而你只是在這裏目睹一個未定義行爲的可能表現。

如果您希望sqlite3_bind_text自行管理數據的生命週期,請將SQLITE_STATIC更改爲SQLITE_TRANSIENT