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 where my error lies - however my inserts are returning the SQLITE_MISUSE
error (code 21), with the message "out of Memory". Below are the steps I have taken to lead me to this insert.
First, the table creation:
NSString *sql = @"CREATE TABLE IF NOT EXISTS UsersTable (lastName TEXT,id TEXT PRIMARY KEY NOT NULL,picture BLOB,firstName TEXT,age TEXT,email TEXT,sex TEXT,height TEXT,weight TEXT)";
//create the database if it does not yet exist
NSFileManager *filemgr = [NSFileManager defaultManager];
if ([filemgr fileExistsAtPath: path ] == NO)
{
const char *dbpath = [path UTF8String];
//This was if (sqlite3_open(dbpath, &store) == SQLITE_OK) , but it has not made a difference.
if (sqlite3_open_v2(dbpath, &store, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL) == SQLITE_OK)
{
char *errMsg = NULL;
const char *sql_stmt = [sql UTF8String];
if (sqlite3_exec(store, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK)
{
NSLog(@"Failed to create table: %s", errMsg);
}
if (errMsg)
sqlite3_free(errMsg);
}
else
{
NSLog(@"Failed to open/create database");
}
}
Next, the insert (currently using the email address for the user ID):
INSERT INTO UsersTable (id,lastName,firstName,email) VALUES ("jsmith@foobar.com","Smith","John","jsmith@foobar.com")
I am using one selector for all database interactions, so the above text is passed here:
-(int)execSQL:(NSString *)statement
{
NSLog(@"%@",statement);
const char *insert_stmt = [statement UTF8String];
sqlite3_stmt *stmnt;
sqlite3_prepare_v2(store, insert_stmt, -1, &stmnt, NULL);
int result = sqlite3_step(stmnt);
sqlite3_finalize(stmnt);
if (result != SQLITE_OK)
{
NSLog(@"Error: %s", sqlite3_errmsg(store));//This prints "Error: out of memory"
}
return result;
}
What am I doing wrong?
You're not checking the value of the
sqlite3_prepare_v2
statement. If it's notSQLITE_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.
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 thesqlite3_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: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 usesqlite3_bind_xxx
functions to bind the values. Something like:You can then call this like so:
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.