Ответ 1
-
Нужно всегда проверять возвращаемые значения функций SQLite, чтобы убедиться, что они выполнены успешно, поэтому использование оператора
if
является наиболее предпочтительным. И если это не удалось, можно было бы вызватьsqlite3_errmsg()
чтобы получить описание ошибки в строке C. -
Можно использовать
sqlite3_prepare_v2
(вместоsqlite3_exec
) в любой ситуации, в которой:-
один возвращает данные и поэтому вызывает
sqlite3_step
за которым следует одна или несколько функцийsqlite3_column_xxx
, повторяя этот процесс для каждой строки данных; или же -
один привязывает значения к
?
заполнители в SQL сsqlite3_bind_xxx
.
Из вышесказанного можно сделать вывод, что использовать
sqlite3_exec
только в том случае, если (а) строка SQL не имеет параметров; и (б) SQL не возвращает никаких данных.sqlite3_exec
проще, но его следует использовать только в этих конкретных ситуациях.Пожалуйста, обратите внимание: что касается
?
Заполнители очень важны: следует избегать создания операторов SQL вручную (например, с помощьюstringWithFormat
или Swift с интерполяцией строк), особенно если вводимые значения включают ввод для конечного пользователя. Например, если вы вызываетеsqlite3_exec
с операторомINSERT
,UPDATE
илиDELETE
который был создан с использованием пользовательского ввода (например, вставка некоторого значения, предоставленного пользователем, в базу данных), у вас есть реальная возможность возникновения проблем, связанных с неэкранированными кавычками и escape-символы и т.д. Один также подвергается атакам 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, что упростит ее до:
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 вы можете видеть, что для правильной работы требуется небольшая работа. Вокруг этого интерфейса есть тонкая оболочка Objective-C под названием FMDB, которая не только упрощает взаимодействие с базой данных SQLite, но и немного более надежна.