我知道这可能是一个非常愚蠢的错误,但我几乎花了几个小时盯着几行代码,运行文档,我仍然无能为力,为什么会发生这种情况:
我正在为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_statement
是sqlite3_stmt
的包装结构,它只会在超出范围时释放语句。 database_
是工作数据库的句柄。我不确定,如果这个功能甚至与问题有关。作为麻烦制造者,我宁可考虑在bind
函数中调用的sql
函数。这是:
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
。我非常感谢你对这个问题的第二次看法。
答案 0 :(得分:3)
除非为第五个参数传递SQLITE_TRANSIENT
,否则sqlite3_bind_text
不会复制您传入的字符串,并且在执行语句之前,您的字符串应该存在。在您的情况下,在调用sqlite3_bind_text
之后立即销毁字符串,并且您只是在这里目睹了一种可能的未定义行为表现。
如果您希望sqlite3_bind_text
自行管理数据的生命周期,请将SQLITE_STATIC
更改为SQLITE_TRANSIENT
。