I am writing an iOS application that directly accesses SQLite
. I have done this sort of thing many times on Android, so I\'m struggling to see
Your open routine is only creating/opening the database if the database doesn't exist. Your database probably already exists and thus your routine isn't even opening it.
Bottom line, if you try calling SQLite functions without opening the database, you will get the SQLITE_MISUSE
return code (which indicates that the SQLite functions were not called in the right order) and the sqlite3_errmsg
will return the cryptic "out of memory" error.
A couple of other, unrelated observations:
You really should check the return code of sqlite3_prepare
as well:
- (int)execSQL:(NSString *)statement
{
int result;
NSLog(@"%@",statement);
const char *insert_stmt = [statement UTF8String];
sqlite3_stmt *stmnt;
if ((result = sqlite3_prepare_v2(store, insert_stmt, -1, &stmnt, NULL)) != SQLITE_OK)
{
NSLog(@"%s: prepare failure '%s' (%d)", __FUNCTION__, sqlite3_errmsg(store), result);
return result;
}
if ((result = sqlite3_step(stmnt)) != SQLITE_DONE)
{
NSLog(@"%s: step failure: '%s' (%d)", __FUNCTION__, sqlite3_errmsg(store), result);
}
sqlite3_finalize(stmnt);
return result;
}
In my experience, many common development problems are related to the SQL itself, something that is identified by checking the return code of the sqlite3_prepare_v2
statement.
You really should not be building your SQL statement in a NSString
. You open yourself to SQL injection attacks or, considering the more benign situation, just a SQL errors if someone's name has a quotation mark in it, e.g. The "Destroyer"
. You should be using ?
placeholders and then use sqlite3_bind_xxx functions to bind the values. Something like:
- (int)insertIdentifier:(NSString *)identifier
lastName:(NSString *)lastName
firstName:(NSString *)firstName
email:(NSString *)email
{
int result;
const char *insert_stmt = "INSERT INTO UsersTable (id, lastName, firstName, email) VALUES (?, ?, ?, ?);";
sqlite3_stmt *stmnt;
if ((result = sqlite3_prepare_v2(store, insert_stmt, -1, &stmnt, NULL)) != SQLITE_OK)
{
NSLog(@"%s: prepare failure '%s' (%d)", __FUNCTION__, sqlite3_errmsg(store), result);
return result;
}
if ((result = sqlite3_bind_text(stmnt, 1, [identifier UTF8String], -1, NULL)) != SQLITE_OK)
{
NSLog(@"%s: bind #1 failure '%s' (%d)", __FUNCTION__, sqlite3_errmsg(store), result);
sqlite3_finalize(stmnt);
return result;
}
if ((result = sqlite3_bind_text(stmnt, 2, [lastName UTF8String], -1, NULL)) != SQLITE_OK)
{
NSLog(@"%s: bind #2 failure '%s' (%d)", __FUNCTION__, sqlite3_errmsg(store), result);
sqlite3_finalize(stmnt);
return result;
}
if ((result = sqlite3_bind_text(stmnt, 3, [firstName UTF8String], -1, NULL)) != SQLITE_OK)
{
NSLog(@"%s: bind #3 failure '%s' (%d)", __FUNCTION__, sqlite3_errmsg(store), result);
sqlite3_finalize(stmnt);
return result;
}
if ((result = sqlite3_bind_text(stmnt, 4, [email UTF8String], -1, NULL)) != SQLITE_OK)
{
NSLog(@"%s: bind #4 failure '%s' (%d)", __FUNCTION__, sqlite3_errmsg(store), result);
sqlite3_finalize(stmnt);
return result;
}
if ((result = sqlite3_step(stmnt)) != SQLITE_DONE)
{
NSLog(@"%s: step failure: '%s'", __FUNCTION__, sqlite3_errmsg(store));
}
sqlite3_finalize(stmnt);
return result;
}
You can then call this like so:
[self insertIdentifier:@"jsmith@foobar.com"
lastName:@"Smith"
firstName:@"John"
email:@"jsmith@foobar.com"];
As you can see, as you start writing code where you're appropriately checking each and every return value, binding each variable, etc., your SQLite code gets hairy awfully quickly. I'd suggest you contemplate looking at FMDB. It's a nice, thin wrapper around the SQLite functions, which greatly simplifies the exercise of writing SQLite code in Objective-C.
You're not checking the value of the sqlite3_prepare_v2
statement. If it's not SQLITE_OK
then there's an issue.
Also, does the database file already exist? If not, you need to create it or load it from the bundle.