3

我知道这可能是一个非常愚蠢的错误,但我已经坐了几个小时盯着几行代码,浏览文档,我仍然不知道为什么会发生这种情况:

我正在为 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. SQLite C API 的文档说“最左边的 SQL 参数的索引为 1”,所以这不应该是问题。尽管如此,在数据库中查询的结果是:id: 1, name: 23, age: 23. 我真的很感激再看这个问题。

4

1 回答 1

3

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

如果您想sqlite3_bind_text自行管理数据的生命周期,请更改SQLITE_STATICSQLITE_TRANSIENT.

于 2012-04-12T22:46:53.797 回答