Một sẽ sử dụng sqlite3_prepare_v2
(thay vì sqlite3_exec
) trong mọi tình huống trong đó một trong hai:
ai trở về dữ liệu và do đó sẽ gọi sqlite3_step
Tiếp theo một hoặc nhiều sqlite3_column_xxx
chức năng, lặp đi lặp lại quá trình cho mỗi hàng dữ liệu; hoặc
một giá trị ràng buộc cho các đối tượng giữ chỗ ?
trong SQL với sqlite3_bind_xxx
.
Người ta có thể phỏng đoán từ trên chỉ sử dụng sqlite3_exec
khi (a) chuỗi SQL không có tham số; và (b) SQL không trả về bất kỳ dữ liệu nào. Các sqlite3_exec
là đơn giản, nhưng chỉ nên được sử dụng trong những tình huống cụ thể.
Xin lưu ý: Điểm liên quan đến phần giữ chỗ ?
rất quan trọng: Bạn nên tránh tạo câu lệnh SQL theo cách thủ công (ví dụ: với nội suy chuỗi stringWithFormat
hoặc Swift), đặc biệt nếu các giá trị được chèn vào bao gồm đầu vào của người dùng cuối. Ví dụ: nếu bạn gọi số sqlite3_exec
bằng câu hỏi INSERT
, UPDATE
hoặc DELETE
đã được tạo bằng cách sử dụng đầu vào của người dùng (ví dụ: chèn một số giá trị do người dùng cung cấp vào cơ sở dữ liệu), bạn có thể gặp phải các vấn đề phát sinh từ dấu ngoặc kép các biểu tượng thoát, v.v. Một cũng được tiếp xúc với các cuộc tấn công SQL injection.
Ví dụ, nếu commentString
được cung cấp như một kết quả của việc người dùng nhập vào, đây sẽ là không khôn ngoan:
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));
}
Thay vào đó, bạn nên:
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));
}
if (sqlite3_bind_text(statement, 1, [commentString UTF8String], -1, NULL) != SQLITE_OK) {
NSLog(@"Bind 1 failure: %s", sqlite3_errmsg(database));
}
if (sqlite3_step(statement) != SQLITE_DONE) {
NSLog(@"Step failure: %s", sqlite3_errmsg(database));
}
sqlite3_finalize(statement);
Lưu ý, nếu thực hiện đúng này có cảm giác như quá nhiều công việc, bạn có thể sử dụng số FMDB library, điều này sẽ đơn giản hóa nó thành:
if (![db executeUpdate:@"INSERT INTO COMMENTS (COMMENT) VALUES (?)", commentString]) {
NSLog(@"Insert failure: %@", [db lastErrorMessage]);
}
Điều này cung cấp độ cứng của phương pháp sqlite3_prepare_v2
, nhưng sự đơn giản của giao diện sqlite3_exec
.
Khi lấy nhiều hàng dữ liệu, người ta sẽ sử dụng:
while(sqlite3_step(sqlStatement) == SQLITE_ROW) { ... }
Hoặc tốt hơn, nếu bạn muốn làm việc xử lý lỗi thích hợp, bạn sẽ làm:
int rc;
while ((rc = sqlite3_step(sqlStatement)) == SQLITE_ROW) {
// process row here
}
if (rc != SQLITE_DONE) {
NSLog(@"Step failure: %s", sqlite3_errmsg(database));
}
Khi truy xuất một hàng dữ liệu, một sẽ:
if (sqlite3_step(sqlStatement) != SQLITE_ROW) {
NSLog(@"Step failure: %s", sqlite3_errmsg(database));
}
Khi thực hiện SQL sẽ không trả về bất kỳ dữ liệu, một sẽ:
if (sqlite3_step(sqlStatement) != SQLITE_DONE) {
NSLog(@"Step failure: %s", sqlite3_errmsg(database));
}
Chắc chắn, điều này nên có trong tài liệu! Cảm ơn bạn – Developer3000
BTW, có một chút [giới thiệu trên trang web SQLite.org] (https://www.sqlite.org/cintro.html), mặc dù nó không đi vào chi tiết khi chức năng tiện lợi, 'sqlite3_exec', là thích hợp, và khi nó không phải là. Họ để nó cho bạn để suy ra điều này. Nhưng nó thảo luận về giao diện cơ bản. – Rob
Điều cần biết. Cảm ơn nhiều – Developer3000