2012-04-12 29 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