最后一个参数会覆盖SQLite绑定中的先前参数

时间:2012-04-12 21:09:39

标签: c++ sqlite c++11

我知道这可能是一个非常愚蠢的错误,但我几乎花了几个小时盯着几行代码,运行文档,我仍然无能为力,为什么会发生这种情况:

我正在为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_是工作数据库的句柄。我不确定,如果这个功能甚至与问题有关。作为麻烦制造者,我宁可考虑在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] 23The documentation of the SQLite C API说“最左边的SQL参数的索引为1”,所以这不应该是问题。但是,在数据库中,查询结果为:id: 1, name: 23, age: 23。我非常感谢你对这个问题的第二次看法。

1 个答案:

答案 0 :(得分:3)

除非为第五个参数传递SQLITE_TRANSIENT,否则sqlite3_bind_text不会复制您传入的字符串,并且在执行语句之前,您的字符串应该存在。在您的情况下,在调用sqlite3_bind_text之后立即销毁字符串,并且您只是在这里目睹了一种可能的未定义行为表现。

如果您希望sqlite3_bind_text自行管理数据的生命周期,请将SQLITE_STATIC更改为SQLITE_TRANSIENT