sqlite3_bind_text用于可变数量的值。

huangapple go评论52阅读模式
英文:

sqlite3_bind_text for variable number of values

问题

以下是代码的中文翻译部分:

// 创建一个SQLite数据库
sqlite3 *db;
char *dbErrMsg;
int rc = sqlite3_open("test.dat", &db);
if (rc)
{
    std::cout << "无法打开数据库\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查询
C:\Users\James\code\sharedLikes\bin>sqlite3 test.dat
SQLite 版本 3.36.0 2021-06-18 18:36:39
输入 ".help" 获取使用提示。
sqlite> SELECT userid,likeid FROM like WHERE userid != 1 AND likeid IN (1,2);
2|2
3|1
4|1

输出如预期:

3 行记录

接下来,我使用C API运行相同的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 << " 行记录\n";

输出也是正确的:

3 行记录

但是,我真正想要做的是运行查询以查找不定数量的匹配项。所以,我尝试使用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 << " 行记录\n";

输出显示未找到任何行:

0 行记录

我使用调试器检查了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 << "无法打开数据库\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 << " 行记录\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 << " 行记录\n";
}

如何运行具有可变数量IN值的SELECT查询?

解决方案,根据@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 << " 行记录\n";

这将允许您运行具有可变数量IN值的SELECT查询。

英文:

I create a SQLite database with:

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

Then run a SQL select from the command line tool

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

All is working as expected.

Now I run the same select command using the C API

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

Output is good:

3 rows found

But what I really want to do is run queries looking for a variable number of matching likes. So I tried using sqlite3_bind_text like this

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

Output shows that no rows are found

0 rows found

I used the debugger to check that rc was always returned as 0 ( no errors ) ( In the real code rc is checked, but I have removed the checks for readability in this test/demo. )

Here is the complete code for the demo application

#include &lt;iostream&gt;
#include &quot;sqlite3.h&quot;
int main(int argc, char *argv[])
{
sqlite3 *db;
char *dbErrMsg;
int rc = sqlite3_open(&quot;test.dat&quot;, &amp;db);
if (rc)
{
std::cout &lt;&lt; &quot;cannot open database\n&quot;;
exit(2);
}
rc = sqlite3_exec(db,
&quot;CREATE TABLE IF NOT EXISTS like ( userid, likeid );&quot;,
0, 0, &amp;dbErrMsg);
rc = sqlite3_exec(db,
&quot;DELETE FROM like;&quot;,
0, 0, &amp;dbErrMsg);
rc = sqlite3_exec(db,
&quot;INSERT INTO like VALUES &quot;
&quot;(1,1),(1,2),&quot;
&quot;(2,2),(2,3),&quot;
&quot;(3,3),(3,1),&quot;
&quot;(4,3),(4,1);&quot;,
0, 0, &amp;dbErrMsg);
sqlite3_stmt *match;
rc = sqlite3_prepare_v2(db,
&quot;SELECT userid,likeid &quot;
&quot;FROM like &quot;
&quot;WHERE userid != 1 &quot;
&quot;AND likeid IN ( 1,2 );&quot;,
-1, &amp;match, 0);
int found = 0;
while ((rc = sqlite3_step(match)) == SQLITE_ROW)
{
found++;
}
sqlite3_reset(match);
std::cout &lt;&lt; found &lt;&lt; &quot; rows found\n&quot;;
sqlite3_stmt *match2;
rc = sqlite3_prepare_v2(db,
&quot;SELECT userid,likeid &quot;
&quot;FROM like &quot;
&quot;WHERE userid != ?1 &quot;
&quot;AND likeid IN ( ?2 );&quot;,
-1, &amp;match2, 0);
rc = sqlite3_bind_int( match2, 1, 1);
rc = sqlite3_bind_text( match2, 2, &quot;1,2&quot;, -1,0);
found = 0;
while ((rc = sqlite3_step(match2)) == SQLITE_ROW)
{
found++;
}
sqlite3_reset(match2);
std::cout &lt;&lt; found &lt;&lt; &quot; rows found\n&quot;;
}

How can I run a SELECT ... IN ( ) query with a variable number of IN values?


Solution, following suggestion by @SHR:

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

答案1

得分: 1

bind 可以用于绑定单个值。你不能在单个 bind 命令中绑定多个参数。

你的 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,2");

绑定必须防止 SQL 注入。如果你在参数中绑定了恶意代码会怎样?例如:而不是 &quot;1,2&quot;,我会放入 &quot;1,2); delete from like where userid not in (-1&quot;,这会允许受限制的用户删除整个数据库。当它只是一个单一参数时,你无法注入它。

英文:

bind can be used to bind a single value. you can't bind several arguments in a single bind command.

your bind creates the following query: (with a single argument inside the IN condition)

SELECT userid,likeid FROM like WHERE userid != 1 AND likeid IN ( &#39;1,2&#39; )

you can append it to the query string (or use a sprintf...), but you must bind each argument in a single bind method. you can't create a list with a different count of elements in a single prepared statement.

You can create the query like this: (without binding, or bind only userid)

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

The binding must prevent SQL injection. what if you bind something with malicious code in an argument? for example: instead of &quot;1,2&quot; I'll put &quot;1,2); delete from like where userid not in (-1&quot;, It allow a restricted user to delete your entire DB. you can't inject it when it is only a single argument.

huangapple
  • 本文由 发表于 2023年6月19日 22:57:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/76507838.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定