sqlite3事务和exec调用

时间:2012-11-07 21:37:13

标签: c++ database sqlite

我想要插入表格中的整套数据。我试图让它插入/更新一切或回滚。我打算在一个事务中执行它,但我不确定 sql_exec()命令是否做了同样的事情。

我的目标是遍历列表。 根据主键从每次迭代中选择。

If result was found:
  append update to string;
else
  append insert to string;

然后在迭代循环之后,我会有一个巨大的字符串并说:

sql_exec(字符串); SQL_CLOSE(分贝);

我应该怎么做?我将在循环的每次迭代中执行此操作,但如果出现错误,我不会想到全局回滚。

1 个答案:

答案 0 :(得分:5)

不,你不应该将所有东西都附加到一个巨大的字符串中。如果这样做,您将需要分配一大堆内存,并且为每个单独的语句创建良好的错误消息将更加困难,因为您只会为整个字符串收到一个错误。为什么要花费所有这些努力,在SQLite只需要将其再次解析为单独的语句时构建一个大字符串?

相反,正如@Chad建议的那样,您应该在sqlite3_exec()语句中使用BEGIN,这将开始一个事务。然后依次sqlite3_exec()每个语句,最后sqlite3_exec() COMMITROLLBACK,具体取决于一切如何。 BEGIN语句将启动一个事务,之后执行的所有语句都将在该事务中,因此一起提交或回滚。这就是ACID中的“A”所代表的含义; Atomic,因为事务中的所有语句都将被提交或回滚,就像它们是单个原子操作一样。

此外,如果某些数据在每个语句中有所不同(例如从文件中读取),则可能不应使用sqlite3_exec()。如果你这样做,错误可能很容易给你留下SQL injection错误。例如,如果您通过附加字符串来构造查询,并且要插入char *str = "it's a string"之类的字符串,那么如果您没有正确引用它,那么您的语句可能会像INSERT INTO table VALUES ('it's a string');一样出现,这将是一个错误。或者,如果恶意某人可以将数据写入此文件,那么他们可能会导致您执行他们想要的任何SQL语句(imagine如果字符串是"'); DROP TABLE my_important_table; --")。您可能认为没有人会提供输入,但是如果有人将一个将SQL解析器混淆的字符放入字符串中,您仍会遇到意外问题。

相反,您应该使用sqlite3_prepare_v2()sqlite3_bind_...()(其中...是类型,例如intdoubletext)。为此,您使用char *query = "INSERT INTO table VALUES (?)"之类的语句,在其中用?替换您希望参数的位置,使用sqlite3_prepare_v2(db, query, -1, &stmt, NULL)进行准备,使用{{绑定参数1}},然后用sqlite3_step(stmt)执行语句。如果语句返回任何数据,您将获得sqlite3_bind_text(stmt, 1, str, -1, SQLITE_STATIC),并可以使用各种sqlite3_columne_...()函数访问数据。务必仔细阅读文档;我给出的一些示例参数可能需要根据您的使用方式进行更改。

是的,这比调用SQLITE_ROW更麻烦,但如果您的查询有从外部源(文件,用户输入)加载的任何数据,这是正确执行此操作的唯一方法。如果查询的整个文本包含在您的来源中,例如sqlite3_exec()sqlite3_exec()BEGIN语句,或预先编写的查询,则COMMIT可以调用没有任何部分来自您的程序之外,如果有任何意外的字符串可能进入,您只需要准备/绑定。

最后,您不需要查询数据库中是否存在某些内容,然后插入或更新它。您可以执行ROLLBACK查询,该查询将插入记录,或者使用匹配的主键替换一个记录,这相当于选择然后执行INSERT OR REPLACEINSERT,但更快更简单。有关详细信息,请参阅INSERT"on conflict"文档。