sqlite3_prepare_v2 / sqlite3_exec

Posted

技术标签:

【中文标题】sqlite3_prepare_v2 / sqlite3_exec【英文标题】: 【发布时间】:2015-02-07 15:03:20 【问题描述】:

关于 sqlite3 的几个问题:

1.何时需要使用第一种方法,何时使用另一种?他们之间有区别吗?

sqlite3_prepare_v2(_contactDB, sql_stmt_getIdRecepteur, -1, &sqlStatement, NULL);

if(sqlite3_prepare_v2(_contactDB, sql_stmt_getIdRecepteur, -1, &sqlStatement, NULL) == SQLITE_OK) 

2. 什么时候最适合使用 'sqlite3_exec' 而不是 'sqlite3_prepare_v2' ?

3.当需要使用第一个、第二个或第三个时:

while(sqlite3_step(sqlStatement) == SQLITE_ROW)
if(sqlite3_step(sqlStatement) == SQLITE_ROW)
if(sqlite3_step(sqlStatement) == SQLITE_DONE)

提前谢谢你

【问题讨论】:

【参考方案1】:

    应该经常检查 SQLite 函数的返回值,以确保它成功,因此使用 if 语句是非常优选的。如果失败,人们会调用sqlite3_errmsg() 来检索错误的 C 字符串描述。

    在以下任何情况下都可以使用sqlite3_prepare_v2(而不是sqlite3_exec):

    一个正在返回数据,因此将调用sqlite3_step,然后调用一个或多个sqlite3_column_xxx函数,对每一行数据重复该过程;或

    一个是使用sqlite3_bind_xxx将值绑定到SQL中的?占位符。

    从上面可以推断,只有当(a)SQL 字符串没有参数时,才会使用sqlite3_exec; (b) SQL 不返回任何数据。 sqlite3_exec 更简单,但只应在这些特定情况下使用。

    请注意:关于 ? 占位符的这一点非常重要:应避免手动构建 SQL 语句(例如,使用 stringWithFormat 或 Swift 字符串插值),尤其是在插入的值包含最终用户输入的情况下。例如,如果您使用用户输入创建的INSERTUPDATEDELETE 语句调用sqlite3_exec(例如,将用户提供的一些值插入到数据库中),您固有地冒着非常真实的可能性未转义的引号和转义符号等引起的问题。一个也暴露在SQL注入攻击中。

    例如,如果commentString 是作为用户输入的结果提供的,则不建议这样做:

    NSString *sql = [NSString stringWithFormat:@"INSERT INTO COMMENTS (COMMENT) VALUES ('%@')", commentString];
    if (sqlite3_exec(database, [sql UTF8String], NULL, NULL, NULL) != SQLITE_OK) 
        NSLog(@"Insert failure: %s", sqlite3_errmsg(database));
    
    

    相反,您应该:

    const char *sql = "INSERT INTO COMMENTS (COMMENT) VALUES (?)";
    if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) != SQLITE_OK) 
        NSLog(@"Prepare failure: %s", sqlite3_errmsg(database));
        return;
    
    if (sqlite3_bind_text(statement, 1, [commentString UTF8String], -1, SQLITE_TRANSIENT) != SQLITE_OK) 
        NSLog(@"Bind 1 failure: %s", sqlite3_errmsg(database));
        sqlite3_finalize(statement);
        return;
    
    if (sqlite3_step(statement) != SQLITE_DONE) 
        NSLog(@"Step failure: %s", sqlite3_errmsg(database));
    
    sqlite3_finalize(statement);
    

    注意,如果这个正确的实现感觉工作量太大,您可以使用FMDB library,这会将其简化为:

    if (![db executeUpdate:@"INSERT INTO COMMENTS (COMMENT) VALUES (?)", commentString]) 
        NSLog(@"Insert failure: %@", [db lastErrorMessage]);
    
    

    这提供了sqlite3_prepare_v2 方法的严谨性,但提供了sqlite3_exec 接口的简单性。

    当检索多行数据时,可以使用:

    while(sqlite3_step(sqlStatement) == SQLITE_ROW)  ... 
    

    或者,更好的是,如果您想进行正确的错误处理,您应该这样做:

    int rc;
    while ((rc = sqlite3_step(sqlStatement)) == SQLITE_ROW) 
        // process row here
    
    if (rc != SQLITE_DONE) 
         NSLog(@"Step failure: %s", sqlite3_errmsg(database));
    
    

    在检索单行数据时,会:

    if (sqlite3_step(sqlStatement) != SQLITE_ROW) 
        NSLog(@"Step failure: %s", sqlite3_errmsg(database));
    
    

    在执行不返回任何数据的 SQL 时,会:

    if (sqlite3_step(sqlStatement) != SQLITE_DONE) 
        NSLog(@"Step failure: %s", sqlite3_errmsg(database));
    
    

当使用 SQLite C 接口时,您可以看到它需要一些工作才能正确完成。在这个接口周围有一个名为 FMDB 的精简 Objective-C 包装器,它不仅简化了与 SQLite 数据库的交互,而且更加健壮。

【讨论】:

当然,这应该在文档中!谢谢 顺便说一句,有一点 introduction on the SQLite.org site,虽然它没有详细说明便利功能 sqlite3_exec 何时合适,何时不合适。他们留给你推断这一点。但它确实讨论了基本界面。 很高兴知道。非常感谢 sqlite3_bind_text 在后台线程中运行所有这些时,我应该使用SQLITE_TRANSIENT 吗? @Rob /@任何人。我在谷歌搜索时找到了this。 @LalKrishna - 是的,我们应该使用SQLITE_TRANSIENT。我已经相应地更新了这个。仅供参考,它与背景或前景无关:这是您是否希望 SQLite 使用复制语义的问题。十分之九,您确实想使用SQLITE_TRANSIENT。正如文档所说,“SQLITE_TRANSIENT 值意味着内容可能会在不久的将来发生变化,并且 SQLite 应该在返回之前制作自己的内容私有副本。”【参考方案2】:

对于问题1,在大多数情况下,您需要验证结果是否等于SQLITE_OK,以确保您的命令运行成功。 (SQLITE_OKint 类型**)。因此,首选第二种。

对于问题 2,函数 sqlite3_exec 用于运行任何不返回数据的命令,包括更新、插入和删除。从数据库中检索数据稍微有点复杂。 sqlite3_prepare_v2 函数可用于 SELECT(在SQL 中)。通常,create table经常使用第一个。

对于问题3,嗯,while是针对loop,而if是针对condition 。一般来说,如果你从db中检索dada,你需要一个loop来遍历*return array**。如果你插入一个数据到db(例如),你可以使用SQLITE_DONE来检查你的操作。

顺便说一句,核心数据在大多数情况下在 ios 中是首选。

【讨论】:

感谢您的回答。【参考方案3】:

我刚刚找到的 #2 的较晚答案:当 sql_stmt_getIdRecepteur 实际上包含多个 SQL 语句时,使用 sqlite3_exec(或在循环中使用 sqlite3_prepare_v2)。来自docs for sqlite3_prepare_v2

这些例程只编译 zSql 中的第一条语句,因此 *pzTail 指向未编译的部分。

sqlite3_exec 包含一个内部循环,该循环多次调用sqlite3_prepare_v2,直到编译整个输入字符串。如果不使用sqlite3_exec,并且一个字符串中有多个SQL语句,则需要检查sqlite3_prepare_v2中的pzTail返回值。

【讨论】:

以上是关于sqlite3_prepare_v2 / sqlite3_exec的主要内容,如果未能解决你的问题,请参考以下文章

sqlite3_prepare_v2 不会返回花费太长时间(慢)和使用内存

SQLite sqlite3_prepare_v2没有错误,但也没有

C++ sqlite3使用sqlite3_prepare_v2接口报错,求大神解答

如何修复 Xcode 中的“_sqlite3_prepare_v3”链接器错误

详细sqli-labs(1-65)通关讲解

sqlite3接口API函数备注