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

Last argument overrides previous arguments in SQLite binding

本文关键字:参数 SQLite 覆盖 最后一个 绑定      更新时间:2023-10-16

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

我正在为 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。我已经用各种输入对其进行了调试;请考虑以下示例:

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 。我真的很感激能再看看这个问题。

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

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