sqlite3_bind_text用于可变数量的值。

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

sqlite3_bind_text for variable number of values

问题

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

  1. // 创建一个SQLite数据库
  2. sqlite3 *db;
  3. char *dbErrMsg;
  4. int rc = sqlite3_open("test.dat", &db);
  5. if (rc)
  6. {
  7. std::cout << "无法打开数据库\n";
  8. exit(2);
  9. }
  10. rc = sqlite3_exec(db,
  11. "CREATE TABLE IF NOT EXISTS like ( userid, likeid );",
  12. 0, 0, &dbErrMsg);
  13. rc = sqlite3_exec(db,
  14. "DELETE FROM like;",
  15. 0, 0, &dbErrMsg);
  16. rc = sqlite3_exec(db,
  17. "INSERT INTO like VALUES "
  18. "(1,1),(1,2),"
  19. "(2,2),(2,3),"
  20. "(3,3),(3,1),"
  21. "(4,3),(4,1);",
  22. 0, 0, &dbErrMsg);
  23. // 从命令行工具运行SQL查询
  24. C:\Users\James\code\sharedLikes\bin>sqlite3 test.dat
  25. SQLite 版本 3.36.0 2021-06-18 18:36:39
  26. 输入 ".help" 获取使用提示。
  27. sqlite> SELECT userid,likeid FROM like WHERE userid != 1 AND likeid IN (1,2);
  28. 2|2
  29. 3|1
  30. 4|1

输出如预期:

  1. 3 行记录

接下来,我使用C API运行相同的SELECT命令:

  1. sqlite3_stmt *match;
  2. rc = sqlite3_prepare_v2(db,
  3. "SELECT userid,likeid "
  4. "FROM like "
  5. "WHERE userid != 1 "
  6. "AND likeid IN ( 1,2 );",
  7. -1, &match, 0);
  8. int found = 0;
  9. while ((rc = sqlite3_step(match)) == SQLITE_ROW)
  10. {
  11. found++;
  12. }
  13. sqlite3_reset(match);
  14. std::cout << found << " 行记录\n";

输出也是正确的:

  1. 3 行记录

但是,我真正想要做的是运行查询以查找不定数量的匹配项。所以,我尝试使用sqlite3_bind_text,如下所示:

  1. sqlite3_stmt *match2;
  2. rc = sqlite3_prepare_v2(db,
  3. "SELECT userid,likeid "
  4. "FROM like "
  5. "WHERE userid != ?1 "
  6. "AND likeid IN ( ?2 );",
  7. -1, &match2, 0);
  8. rc = sqlite3_bind_int(match2, 1, 1);
  9. rc = sqlite3_bind_text(match2, 2, "1,2", -1, 0);
  10. found = 0;
  11. while ((rc = sqlite3_step(match2)) == SQLITE_ROW)
  12. {
  13. found++;
  14. }
  15. sqlite3_reset(match2);
  16. std::cout << found << " 行记录\n";

输出显示未找到任何行:

  1. 0 行记录

我使用调试器检查了rc始终返回为0(无错误)的情况(在实际代码中已检查rc,但在此测试/演示中删除了检查以提高可读性)。

这是完整的演示应用程序的代码:

  1. #include <iostream>
  2. #include "sqlite3.h"
  3. int main(int argc, char *argv[])
  4. {
  5. sqlite3 *db;
  6. char *dbErrMsg;
  7. int rc = sqlite3_open("test.dat", &db);
  8. if (rc)
  9. {
  10. std::cout << "无法打开数据库\n";
  11. exit(2);
  12. }
  13. rc = sqlite3_exec(db,
  14. "CREATE TABLE IF NOT EXISTS like ( userid, likeid );",
  15. 0, 0, &dbErrMsg);
  16. rc = sqlite3_exec(db,
  17. "DELETE FROM like;",
  18. 0, 0, &dbErrMsg);
  19. rc = sqlite3_exec(db,
  20. "INSERT INTO like VALUES "
  21. "(1,1),(1,2),"
  22. "(2,2),(2,3),"
  23. "(3,3),(3,1),"
  24. "(4,3),(4,1);",
  25. 0, 0, &dbErrMsg);
  26. sqlite3_stmt *match;
  27. rc = sqlite3_prepare_v2(db,
  28. "SELECT userid,likeid "
  29. "FROM like "
  30. "WHERE userid != 1 "
  31. "AND likeid IN ( 1,2 );",
  32. -1, &match, 0);
  33. int found = 0;
  34. while ((rc = sqlite3_step(match)) == SQLITE_ROW)
  35. {
  36. found++;
  37. }
  38. sqlite3_reset(match);
  39. std::cout << found << " 行记录\n";
  40. sqlite3_stmt *match2;
  41. rc = sqlite3_prepare_v2(db,
  42. "SELECT userid,likeid "
  43. "FROM like "
  44. "WHERE userid != ?1 "
  45. "AND likeid IN ( ?2 );",
  46. -1, &match2, 0);
  47. rc = sqlite3_bind_int(match2, 1, 1);
  48. rc = sqlite3_bind_text(match2, 2, "1,2", -1, 0);
  49. found = 0;
  50. while ((rc = sqlite3_step(match2)) == SQLITE_ROW)
  51. {
  52. found++;
  53. }
  54. sqlite3_reset(match2);
  55. std::cout << found << " 行记录\n";
  56. }

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

解决方案,根据@SHR的建议:

  1. int owner = 1;
  2. std::string ownerInterests = "1,2";
  3. std::string query = "SELECT userid,likeid "
  4. "FROM like "
  5. "WHERE userid != " +
  6. std::to_string(owner) +
  7. " AND likeid IN ( " + ownerInterests + " );";
  8. std::cout << query << "\n";
  9. sqlite3_stmt *match3;
  10. rc = sqlite3_prepare_v2(db, query.c_str(),
  11. -1, &match3, 0);
  12. found = 0;
  13. while ((rc = sqlite3_step(match3)) == SQLITE_ROW)
  14. {
  15. found++;
  16. }
  17. sqlite3_reset(match3);
  18. std::cout << found << " 行记录\n";

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

英文:

I create a SQLite database with:

  1. sqlite3 *db;
  2. char *dbErrMsg;
  3. int rc = sqlite3_open(&quot;test.dat&quot;, &amp;db);
  4. if (rc)
  5. {
  6. std::cout &lt;&lt; &quot;cannot open database\n&quot;;
  7. exit(2);
  8. }
  9. rc = sqlite3_exec(db,
  10. &quot;CREATE TABLE IF NOT EXISTS like ( userid, likeid );&quot;,
  11. 0, 0, &amp;dbErrMsg);
  12. rc = sqlite3_exec(db,
  13. &quot;DELETE FROM like;&quot;,
  14. 0, 0, &amp;dbErrMsg);
  15. rc = sqlite3_exec(db,
  16. &quot;INSERT INTO like VALUES &quot;
  17. &quot;(1,1),(1,2),&quot;
  18. &quot;(2,2),(2,3),&quot;
  19. &quot;(3,3),(3,1),&quot;
  20. &quot;(4,3),(4,1);&quot;,
  21. 0, 0, &amp;dbErrMsg);

Then run a SQL select from the command line tool

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

All is working as expected.

Now I run the same select command using the C API

  1. sqlite3_stmt *match;
  2. rc = sqlite3_prepare_v2(db,
  3. &quot;SELECT userid,likeid &quot;
  4. &quot;FROM like &quot;
  5. &quot;WHERE userid != 1 &quot;
  6. &quot;AND likeid IN ( 1,2 );&quot;,
  7. -1, &amp;match, 0);
  8. int found = 0;
  9. while ((rc = sqlite3_step(match)) == SQLITE_ROW)
  10. {
  11. found++;
  12. }
  13. sqlite3_reset(match);
  14. std::cout &lt;&lt; found &lt;&lt; &quot; rows found\n&quot;;

Output is good:

  1. 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

  1. sqlite3_stmt *match2;
  2. rc = sqlite3_prepare_v2(db,
  3. &quot;SELECT userid,likeid &quot;
  4. &quot;FROM like &quot;
  5. &quot;WHERE userid != ?1 &quot;
  6. &quot;AND likeid IN ( ?2 );&quot;,
  7. -1, &amp;match2, 0);
  8. rc = sqlite3_bind_int( match2, 1, 1);
  9. rc = sqlite3_bind_text( match2, 2, &quot;1,2&quot;, -1,0);
  10. found = 0;
  11. while ((rc = sqlite3_step(match2)) == SQLITE_ROW)
  12. {
  13. found++;
  14. }
  15. sqlite3_reset(match2);
  16. std::cout &lt;&lt; found &lt;&lt; &quot; rows found\n&quot;;

Output shows that no rows are found

  1. 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

  1. #include &lt;iostream&gt;
  2. #include &quot;sqlite3.h&quot;
  3. int main(int argc, char *argv[])
  4. {
  5. sqlite3 *db;
  6. char *dbErrMsg;
  7. int rc = sqlite3_open(&quot;test.dat&quot;, &amp;db);
  8. if (rc)
  9. {
  10. std::cout &lt;&lt; &quot;cannot open database\n&quot;;
  11. exit(2);
  12. }
  13. rc = sqlite3_exec(db,
  14. &quot;CREATE TABLE IF NOT EXISTS like ( userid, likeid );&quot;,
  15. 0, 0, &amp;dbErrMsg);
  16. rc = sqlite3_exec(db,
  17. &quot;DELETE FROM like;&quot;,
  18. 0, 0, &amp;dbErrMsg);
  19. rc = sqlite3_exec(db,
  20. &quot;INSERT INTO like VALUES &quot;
  21. &quot;(1,1),(1,2),&quot;
  22. &quot;(2,2),(2,3),&quot;
  23. &quot;(3,3),(3,1),&quot;
  24. &quot;(4,3),(4,1);&quot;,
  25. 0, 0, &amp;dbErrMsg);
  26. sqlite3_stmt *match;
  27. rc = sqlite3_prepare_v2(db,
  28. &quot;SELECT userid,likeid &quot;
  29. &quot;FROM like &quot;
  30. &quot;WHERE userid != 1 &quot;
  31. &quot;AND likeid IN ( 1,2 );&quot;,
  32. -1, &amp;match, 0);
  33. int found = 0;
  34. while ((rc = sqlite3_step(match)) == SQLITE_ROW)
  35. {
  36. found++;
  37. }
  38. sqlite3_reset(match);
  39. std::cout &lt;&lt; found &lt;&lt; &quot; rows found\n&quot;;
  40. sqlite3_stmt *match2;
  41. rc = sqlite3_prepare_v2(db,
  42. &quot;SELECT userid,likeid &quot;
  43. &quot;FROM like &quot;
  44. &quot;WHERE userid != ?1 &quot;
  45. &quot;AND likeid IN ( ?2 );&quot;,
  46. -1, &amp;match2, 0);
  47. rc = sqlite3_bind_int( match2, 1, 1);
  48. rc = sqlite3_bind_text( match2, 2, &quot;1,2&quot;, -1,0);
  49. found = 0;
  50. while ((rc = sqlite3_step(match2)) == SQLITE_ROW)
  51. {
  52. found++;
  53. }
  54. sqlite3_reset(match2);
  55. std::cout &lt;&lt; found &lt;&lt; &quot; rows found\n&quot;;
  56. }

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


Solution, following suggestion by @SHR:

  1. int owner = 1;
  2. std::string ownerInterests = &quot;1,2&quot;;
  3. std::string query = &quot;SELECT userid,likeid &quot;
  4. &quot;FROM like &quot;
  5. &quot;WHERE userid != &quot; +
  6. std::to_string(owner) +
  7. &quot; AND likeid IN ( &quot; + ownerInterests + &quot; );&quot;;
  8. std::cout &lt;&lt; query &lt;&lt; &quot;\n&quot;;
  9. sqlite3_stmt *match3;
  10. rc = sqlite3_prepare_v2(db, query.c_str(),
  11. -1, &amp;match3, 0);
  12. found = 0;
  13. while ((rc = sqlite3_step(match3)) == SQLITE_ROW)
  14. {
  15. found++;
  16. }
  17. sqlite3_reset(match3);
  18. std::cout &lt;&lt; found &lt;&lt; &quot; rows found\n&quot;;

答案1

得分: 1

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

你的 bind 创建了以下查询:(在 IN 条件中有一个单一参数)

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

你可以将其附加到查询字符串(或使用 sprintf ...),但你必须在单个 bind 方法中绑定每个参数。你不能在单个准备好的语句中创建具有不同元素数量的列表。

你可以像这样创建查询:(不绑定,或仅绑定 userid

  1. char sql_buffer[256];
  2. 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)

  1. 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)

  1. char sql_buffer[256]
  2. 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:

确定