我需要在SQLite数据库中保存html字符串。运行insert
查询时,我在HTML文件的style
标记附近出现语法错误。
以下是代码:
-(BOOL)insertAttendees{
sqlite3_stmt *statement;
NSString *insertSQL;
BOOL var=NO;
if (sqlite3_open(dbpath, &db) == SQLITE_OK)
{
//work only for the 1st event
for (int i=0; i<[attendeeCount[0]integerValue];i++)
{
insertSQL = [NSString stringWithFormat:@"INSERT INTO ATTENDEE (A_NAME,A_IMAGE,A_EMAIL,A_PHONE,A_BIO) VALUES (\"%@\",\"%@\", \"%@\",\"%@\",\"%@\")",arrayOf_AName[0][i],arrayOf_AImage[0][i],arrayOf_AEmail[0][i],arrayOf_APhone[0][i],arrayOf_ABio[0][i]];
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(db, insert_stmt,-1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
var=YES;
}
else
{
NSLog(@"sqlite insertion error %s", sqlite3_errmsg(db));
var=NO;
}
sqlite3_finalize(statement);
}
sqlite3_close(db);
return var;
}
return var;
}
答案 0 :(得分:0)
这里有很多问题:
问题的根源在于您使用stringWithFormat
构建SQL,这是您不应该做的。如果您的值中包含引号(例如可能在HTML中的style
标记附近),则您的SQLite代码将失败。相反,你应该:
您的SQL应该使用?
占位符(注意,SQL中也没有引号):
const char *insert_stmt = "INSERT INTO ATTENDEE (A_NAME,A_IMAGE,A_EMAIL,A_PHONE,A_BIO) VALUES (?,?,?,?,?)";
然后,您应该使用以下内容绑定值:
if (sqlite3_bind_text(statement, 1, [string UTF8String], -1, SQLITE_TRANSIENT) != SQLITE_OK) {
NSLog(@"%s: bind column 1 failed: %s", __FUNCTION__, sqlite3_errmsg(db));
}
其中string
是您要插入的NSString
值,而第二个参数(1
,在上例中)是?
占位符的索引你绑定到文本值(最左边的占位符的索引为1)。
只有在您为五个sqlite3_bind_xxxx()
占位符中的每一个调用?
后,才可以通过调用sqlite3_step()
等来继续
第二个问题是您没有检查sqlite3_prepare_v2
是否成功。这很重要,因为如果在prepare语句失败后立即调用sqlite3_errmsg
,您将看到的有意义的错误消息现在已丢失。您无视任何潜在的准备错误,无论如何继续sqlite3_step
,因此有意义的错误消息将被替换为有效(但隐密地)表示您在没有先成功调用{{sqlite3_step
的情况下调用sqlite3_prepare_v2
的错误消息1}}。
因此,请检查以确保sqlite3_prepare_v2
成功,如果失败,请在执行任何其他SQLite调用之前立即检查sqlite3_errmsg
。
解决上述两个问题后,您可能会考虑优化代码。值得注意的是,如果您在所有BEGIN TRANSACTION
语句之前执行INSERT
,而在完成所有插入操作后执行COMMIT TRANSACTION
,则会更快。如果您只插入几条记录,则可能无法观察到,但如果插入大量记录,性能提升可能会非常惊人。
作为进一步的优化,让我们假设您解决了我的上述观点(特别是,消除了stringWithFormat
的SQL并使用了?
占位符)并且你有类似下面的伪代码(但显然检查所有的sqlite3函数返回值并正确处理错误):
sqlite3_exec("begin transaction");
for (i = 0; i < whatever; i++) {
sqlite3_prepare(...);
sqlite3_bind(...);
sqlite3_bind(...);
sqlite3_bind(...);
sqlite3_bind(...);
sqlite3_bind(...);
sqlite3_step();
sqlite3_finalize();
}
sqlite3_exec("commit transaction");
不是多次准备相同的SQL,而是可以准备一次,绑定值,执行步骤,然后重置绑定,以便再次执行:
sqlite3_exec("begin transaction");
sqlite3_prepare(...);
for (i = 0; i < whatever; i++) {
sqlite3_bind(...);
sqlite3_bind(...);
sqlite3_bind(...);
sqlite3_bind(...);
sqlite3_bind(...);
sqlite3_step();
sqlite3_reset();
}
sqlite3_finalize();
sqlite3_exec("commit transaction");
就个人而言,我建议你专注于上面的第1点和第2点,只有在你解决了基本问题时,你应该考虑第3点和第4点的优化。确保在你需要优化之前让它工作它
最后,如果我没有指出你真的应该考虑围绕SQLite C接口的精彩的第三方FMDB Objective-C包装器,那将是我的疏忽。当您编写正确的SQLite代码,即绑定每个值,检查每个返回代码等时,它会很快变得难以处理。 FMDB极大地简化了您的代码。
答案 1 :(得分:0)
如果您确实想在sqlite数据库中插入HTML,请在执行查询之前将所有"
替换为\"
。
(你没有提到你是否要替换特殊字符。)
假设你正在这样做..
"SELECT * FROM table WHERE someColume = <div style="width:25px"></div>"
在 style =“之后它会失败,因为sqlite会尝试执行"SELECT * FROM table WHERE someColume = <div style="
,
但如果您将"
替换为\"
,那么您的最终查询将如下所示 -
"SELECT * FROM table WHERE someColume = <div style=\"width:25px\"></div>"
祝你好运。