在以下任何情况下都可以使用sqlite3_prepare_v2(而不是sqlite3_exec):
从上面可以推断,只有当(a)SQL 字符串没有参数时,才会使用sqlite3_exec; (b) SQL 不返回任何数据。 sqlite3_exec 更简单,但只应在这些特定情况下使用。
请注意:关于 ? 占位符的这一点非常重要:应避免手动构建 SQL 语句(例如,使用 stringWithFormat 或 Swift 字符串插值),尤其是在插入的值包含最终用户输入的情况下。例如,如果您使用用户输入创建的INSERT、UPDATE 或DELETE 语句调用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));
}