sqlite3_bind_text用于可变数量的值

enxuqcxy  于 2023-06-23  发布在  SQLite
关注(0)|答案(1)|浏览(200)

我创建了一个SQLite数据库:

sqlite3 *db;
char *dbErrMsg;
int rc = sqlite3_open("test.dat", &db);
if (rc)
{
    std::cout << "cannot open database\n";
    exit(2);
}
rc = sqlite3_exec(db,
                  "CREATE TABLE IF NOT EXISTS like ( userid, likeid );",
                  0, 0, &dbErrMsg);
rc = sqlite3_exec(db,
                  "DELETE FROM like;",
                  0, 0, &dbErrMsg);
rc = sqlite3_exec(db,
                  "INSERT INTO like VALUES "
                  "(1,1),(1,2),"
                  "(2,2),(2,3),"
                  "(3,3),(3,1),"
                  "(4,3),(4,1);",
                  0, 0, &dbErrMsg);

然后从命令行工具运行SQL select

C:\Users\James\code\sharedLikes\bin>sqlite3 test.dat
SQLite version 3.36.0 2021-06-18 18:36:39
Enter ".help" for usage hints.
sqlite> SELECT userid,likeid FROM like WHERE userid != 1 AND likeid IN (1,2);
2|2
3|1
4|1

一切都按预期工作。
现在我使用CAPI运行相同的select命令

sqlite3_stmt *match;
rc = sqlite3_prepare_v2(db,
                        "SELECT userid,likeid "
                        "FROM like "
                        "WHERE userid != 1 "
                        "AND likeid IN ( 1,2 );",
                        -1, &match, 0);
int found = 0;
while ((rc = sqlite3_step(match)) == SQLITE_ROW)
{
    found++;
}
sqlite3_reset(match);
std::cout << found << " rows found\n";

输出良好:

3 rows found

但我真正想做的是运行查询,寻找可变数量的匹配喜欢。所以我试着像这样使用sqlite3_bind_text

sqlite3_stmt *match2;
rc = sqlite3_prepare_v2(db,
                        "SELECT userid,likeid "
                        "FROM like "
                        "WHERE userid != ?1 "
                        "AND likeid IN ( ?2 );",
                        -1, &match2, 0);
rc = sqlite3_bind_int( match2, 1, 1);
rc = sqlite3_bind_text( match2, 2, "1,2", -1,0);
found = 0;
while ((rc = sqlite3_step(match2)) == SQLITE_ROW)
{
    found++;
}
sqlite3_reset(match2);
std::cout << found << " rows found\n";

输出显示未找到任何行

0 rows found

我使用调试器检查rc是否总是返回为0(没有错误)(在真实代码中,rc是被检查的,但是我在这个测试/演示中删除了可读性检查。)
下面是演示应用程序的完整代码

#include <iostream>
#include "sqlite3.h"
int main(int argc, char *argv[])
{
    sqlite3 *db;
    char *dbErrMsg;
    int rc = sqlite3_open("test.dat", &db);
    if (rc)
    {
        std::cout << "cannot open database\n";
        exit(2);
    }
    rc = sqlite3_exec(db,
                      "CREATE TABLE IF NOT EXISTS like ( userid, likeid );",
                      0, 0, &dbErrMsg);
    rc = sqlite3_exec(db,
                      "DELETE FROM like;",
                      0, 0, &dbErrMsg);
    rc = sqlite3_exec(db,
                      "INSERT INTO like VALUES "
                      "(1,1),(1,2),"
                      "(2,2),(2,3),"
                      "(3,3),(3,1),"
                      "(4,3),(4,1);",
                      0, 0, &dbErrMsg);

    sqlite3_stmt *match;
    rc = sqlite3_prepare_v2(db,
                            "SELECT userid,likeid "
                            "FROM like "
                            "WHERE userid != 1 "
                            "AND likeid IN ( 1,2 );",
                            -1, &match, 0);
    int found = 0;
    while ((rc = sqlite3_step(match)) == SQLITE_ROW)
    {
        found++;
    }
    sqlite3_reset(match);
    std::cout << found << " rows found\n";

    sqlite3_stmt *match2;
    rc = sqlite3_prepare_v2(db,
                            "SELECT userid,likeid "
                            "FROM like "
                            "WHERE userid != ?1 "
                            "AND likeid IN ( ?2 );",
                            -1, &match2, 0);
    rc = sqlite3_bind_int( match2, 1, 1);
    rc = sqlite3_bind_text( match2, 2, "1,2", -1,0);
    found = 0;
    while ((rc = sqlite3_step(match2)) == SQLITE_ROW)
    {
        found++;
    }
    sqlite3_reset(match2);
    std::cout << found << " rows found\n";
}

如何运行SELECT... IN()查询是否包含可变数量的IN值?
解决方案,遵循@SHR的建议:

int owner = 1;
std::string ownerInterests = "1,2";
std::string query = "SELECT userid,likeid "
                    "FROM like "
                    "WHERE userid != " +
                    std::to_string(owner) +
                    " AND likeid IN ( " + ownerInterests + " );";
std::cout << query << "\n";
sqlite3_stmt *match3;
rc = sqlite3_prepare_v2(db, query.c_str(),
                        -1, &match3, 0);
found = 0;
while ((rc = sqlite3_step(match3)) == SQLITE_ROW)
{
    found++;
}
sqlite3_reset(match3);
std::cout << found << " rows found\n";
ecfdbz9o

ecfdbz9o1#

bind可以用来绑定单个值。你不能在一个绑定命令中绑定多个参数。
绑定将创建以下查询:(在IN条件内使用单个参数)

SELECT userid,likeid FROM like WHERE userid != 1 AND likeid IN ( '1,2' )

你可以将它附加到查询字符串中(或者使用sprintf ...),但是你必须在一个bind方法中绑定每个参数。你不能在一个准备好的语句中创建一个具有不同元素计数的列表。
您可以像这样创建查询:(不绑定,或仅绑定userid

char sql_buffer[256]
sprintf (sql_buffer, "SELECT userid,likeid FROM like WHERE userid != 1 AND likeid IN ( %s )", 1,"1,2");

绑定必须防止SQL注入。如果你在参数中绑定了带有恶意代码的东西怎么办?例如:而不是"1,2",我将使用"1,2); delete from like where userid not in (-1",它允许受限用户删除整个数据库。当它只有一个参数时,你不能注入它。

相关问题