Why can't I update statement in SQLite? - iphone

Can anyone help me with this code snippet:
-(void) updateData:(NSString*)value1:(NSString*)value2
{
sqlite3* database;
databaseName = #"AppDB.sqlite";
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
int databaseReturnCode = sqlite3_open([databasePath UTF8String], &database);
if(databaseReturnCode == SQLITE_OK) {
sqlite3_stmt *updateStmt;
const char *sql = "update PersonalInfo Set FirstName = ?,LastName = ? Where id = '1'";
//sqlite3_prepare_v2(database, sql, -1, &updateStmt, NULL);
sqlite3_prepare(database, sql, -1, &updateStmt,nil);
sqlite3_bind_text(updateStmt, 1, [value1 UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(updateStmt, 2, [value2 UTF8String], -1, SQLITE_TRANSIENT);
printf( "Update PersonalInfo| error or not an error? : %s\n", sqlite3_errmsg(database) );
while(sqlite3_step(updateStmt) == SQLITE_ROW)
{
NSString *aFirstName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(updateStmt, 1)];
NSString *aLastName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(updateStmt, 2)];
ProfileInfo *profile = [[ProfileInfo alloc]initWithFirstName:(NSString*)aFirstName LastName:(NSString*)aLastName];
[personalInfo addObject:profile];
[profile release];
}
sqlite3_reset(updateStmt);
sqlite3_finalize(updateStmt);
}
}
sqlite3_close(database);
}
It doesn't enter the while loop.
But if I remove the while loop and
enclose the code in a try-catch block and I get this exception:
+[NSString stringWithUTF8String:]: NULL cString

An sqlite_step while-loop is inappropriate for an update statement.
From the documentation at sqlite.org:
sqlite3_step() This routine is used to evaluate a prepared statement
that has been previously created by the sqlite3_prepare() interface.
The statement is evaluated up to the point where the first row of
results are available. To advance to the second row of results, invoke
sqlite3_step() again. Continue invoking sqlite3_step() until the
statement is complete. Statements that do not return results (ex:
INSERT, UPDATE, or DELETE statements) run to completion on a single
call to sqlite3_step().
Check your return value of sqlite_step. It should be something like SQLITE_DONE rather than SQLITE_ROW.

Related

wrong loop iterations with nested db queries in ios .while Loop not running proper number of times

The code runs fine if the commented portion remains commented.The while loop should run two times and it does cause there are only two rows in threads table.However,if i uncomment the commented code the while loop runs only one time which is the wrong output
sql = "select thread_id,timestamp from threads";
if(sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL) == SQLITE_OK) {
while(sqlite3_step(selectstmt) == SQLITE_ROW) {
NSString* abc = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 0)];
NSString* def = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 1)];
NSLog(#"%#",abc);
NSLog(#"%#",def);
NSString* threadid = abc;
NSString *sql2 = [NSString stringWithFormat:#"select * from my where t_id=\"%#\"",threadid];
**Commented code begin**
/* sqlite3_finalize(selectstmt);
sqlite3_open([path UTF8String], &database);
if(sqlite3_prepare_v2(database, [sql2 UTF8String], -1, &selectstmt2, NULL) == SQLITE_OK) {
int a = sqlite3_data_count(selectstmt2);
NSLog(#"%d",a);
if (a==1) {
sql2 = [NSString stringWithFormat:#"select timestamp from my where t_id=\"%#\"",threadid];
if(sqlite3_prepare_v2(database, [sql2 UTF8String], -1, &selectstmt2, NULL) == SQLITE_OK) {
while(sqlite3_step(selectstmt) == SQLITE_ROW) {
NSString* abc = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt2, 0)];
NSLog(#"%#",abc);
}
}
}
if(a==0) {
char* error;
sql2 = [NSString stringWithFormat:#"insert into my(t_id,time) values(\"%#\",\"%#\")",threadid,#"0"];
int i = sqlite3_exec(database, [sql2 UTF8String], NULL, NULL, &error) ;
NSLog(#"inserted");
sqlite3_finalize(selectstmt);
}
}*/
**Commented code end**
**program end**
You are finalizing the prepared statement, inside the same loop you are processing it in. Not surprisingly, the next time you attempt to use the statement, it fails. Note that, each of the calls you make to the statement, after it is has been finalized, returns an error code that you can check before proceeding with additional logic.

Inserting array of value in Sqlite3 i-phone

I am trying to insert a set of values in an sqlite table using a for loop. It is inserting only one set of value. I am posting here my code:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *databasePath = [documentsDirectory stringByAppendingPathComponent:#"myDatabase.sql"];
for(int i=0;i<[arr count];i++)
{
sqlite3 *database;
// Open the database from the users filessytem
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
NSLog(#"\n inserting data \n");
sqlite3_exec(database, [[NSString stringWithFormat:#"INSERT INTO AnswerConnect VALUES('%#')",[arr objectAtindex:i] ] UTF8String], NULL, NULL, NULL);
//sqlite3_finalize(compiledStatement);
sqlite3_close(database);
}
}
Thanks in advance.
You have to first prepare a sqlite statement to insert data in table.Try this :
sqlite3_stmt *statement = nil
const char *sql = "insert into tablename (col1,col2) Values( ?, ?)";
if(sqlite3_prepare_v2(database, sql, -1, &statement, NULL) != SQLITE_OK)
{
NSLog(#"Error while creating add statement. '%s'", sqlite3_errmsg(database));
}
for(int i=0;i<[arr count];i++)
{
sqlite3_bind_text(statement, 1,[[arr objectAtindex:i] UTF8String] , -1, SQLITE_TRANSIENT);
if(SQLITE_DONE != sqlite3_step(add_statement))
{
NSLog(#"Error while inserting result data. '%s'", sqlite3_errmsg(database));
}
//Reset the add statement.
sqlite3_reset(statement);
}
Don't do like that! Don't open/close SQLite connection in loop like that! Open handle to database outside from loop and than just use pointer on it. In this kind of request it's unsafe to insert format, because SQL statement may be compiled with some kind of injection code. Use sqlite3_stmt instead and bind values to it. Also if you compile only one instance of sqlite3_stmt and reuse it, this will give you better performance than compiling new statements all the time.
How many columns in each data set? Does it insert only one value from single data set like string?

library routine called out of sequence

I am trying to write into a DB in sqlite on the iphone , i want to enter 3 values into the DB . Given Below is my code but i am getting the following sqlite error message:
-(void) writeintoDatabase:(id)value1:(id)value2:(id)value3 {
sqlite3 *database;
const char *sql = "insert into UserData(firstName,lastName,userName) Values(?, ?, ?)";
sqlite3_stmt *insert_statement;
sqlite3_prepare_v2(database, sql, -1, &insert_statement, NULL);
sqlite3_bind_text(insert_statement, 1, [value1 UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(insert_statement, 2, [value1 UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(insert_statement, 3, [value1 UTF8String], -1, SQLITE_TRANSIENT);
NSInteger keyID;
printf( "could not prepare statemnt: %s\n", sqlite3_errmsg(database) );
keyID = sqlite3_last_insert_rowid(database);
sqlite3_reset(insert_statement);
}
Thanks mmcomb, Thanks Mat. From both your advices i have solved the problem completely.
Firstly I wasn't checking the DB path and then opening the
connection.
Secondly after having prepared the statement and bounding the
appropriate variables i had to call sqlite3_step before calling
sqlite3_last_insert_rowid.
Thirdly i needed to call sqlite3_finalize to free the resources.
Fourthly i needed to sqlite3_close the connection.
'library routine called out of sequence' error was due to not opening and closing the connection properly.
This is the right code:
NSInteger keyID; // in .h class
NSMutableArray *user;// in .h class
-(void) writeintoUserDatabase:(NSString*)value1:(NSString*)value2:(NSString*)value3
{
sqlite3* database;
databaseName = #"UserData";// create a db with this name and keep it in app resources folder
NSString *pathToDatabase;
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
pathToDatabase = [documentsDir stringByAppendingPathComponent:databaseName];
int databaseReturnCode = sqlite3_open([pathToDatabase UTF8String], &database);
NSLog(#"databaseReturnCode %d",databaseReturnCode);
if(databaseReturnCode == SQLITE_OK) {
const char *sql = "insert into UserData(firstName,lastName,userName) Values(?, ?, ?)";
sqlite3_stmt *insert_statement;
sqlite3_prepare_v2(database, sql, -1, &insert_statement, NULL);
sqlite3_bind_text(insert_statement, 1, [value0 UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(insert_statement, 2, [value1 UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(insert_statement, 3, [value2 UTF8String], -1, SQLITE_TRANSIENT);
printf( "is there an error?: %s\n", sqlite3_errmsg(database) );
while(sqlite3_step(insert_statement) == SQLITE_ROW)
{
NSString *aFirstName = [NSString stringWithUTF8String: (char*)sqlite3_column_text(insert_statement, 1)];
NSString *aLastName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(insert_statement, 2)];
NSString *aUserName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(insert_statement, 3)];
users = [[NSMutableArray alloc]initWithObjects:aFirstName,aLastName,aUserName,nil];
}
keyID = sqlite3_last_insert_rowid(database);
sqlite3_reset(insert_statement);
sqlite3_finalize(insert_statement);
}
sqlite3_close(database);
}
Your code is missing a call to the sqlite3_step. You need to call that after having prepared the statement and bound the appropriate variables, and before you call sqlite3_last_insert_rowid.
Check the documentation for sqlite3_step for details, and always check the error codes returned by this type of function.
Don't call sqlite3_errormsg if no error has occurred, its result is undefined in that case.
Finally, you need to call sqlite3_finalize on the statement if you're not keeping a handle to it somewhere. Otherwise you'll get resource leaks and eventually crash. sqlite3_reset does not free the resources, it just "cleans" the statement so you can re-run it with different bound values.
Before preparing your sqlite stament/query you need to open the connection to your database...
sqlite3* database;
NSString pathToDatabase = #"/blah/blah/database.db";
const char* pathToDatabaseUTF8 = [databasePath UTF8String];
databaseReturnCode = sqlite3_open(pathToDatabaseUTF8, &database);
See the sqlite documentation for the sqlite3_open method for more info.
Oh, and don't forget to close that connection...
int sqlite3_close(database);
In my case it was a the string contained some special characters like it was ( ' ) single colon i replaced it with (" ") a space and it start working. [[[items objectAtIndex:locBtn.tag]valueForKey:#"description"] stringByReplacingOccurrencesOfString:#"'" withString:#""]

iphone sqlite query crashes if no rows returned

I have the following function in my iPhone project which works great...unless the query returns nothing and then the app crashes. It is being a pain to debug with none of the breakpoints being activated at all!
I know this works as I pass in static stuff that is in the DB and it returns a value.
-(NSString *)getSomeText:(NSString *)toPass {
sqlite3 *database;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *databasePath = [documentsDirectory stringByAppendingPathComponent:#"sf.sqlite"];
int strLength = 0;
strLength = [toPass length];
if (strLength <3)
return #"Unknown";
NSString *MIDstr;
NSMutableString * toPass Copy = [NSMutableString stringWithString:toPass];
MIDstr = [toPassCopy substringWithRange:NSMakeRange(0, 3)];
// Open the database from the users filessytem
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
// Setup the SQL Statement and compile it for faster access
NSString *BaseSQL = [NSString stringWithFormat:#"select * from MIDS where MID = '%#'",MIDstr];
NSLog(BaseSQL);
const char *sqlStatement = [BaseSQL UTF8String];
//NSLog(BaseSQL);
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
// Loop through the results and add them to the feeds array
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
NSString *aName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];
NSString *returnString = [NSString stringWithFormat:#"%#",aName];
return returnString;
}
}
// Release the compiled statement from memory
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
}
A. if sqlite3_step does not return any rows, you crash because you have declared that you are returning a NSString, but when there are no rows you return nothing.
The caller will try to read a NSString from the stack and thus end up dereferencing garbage.
To quickly fix the problem, write:
sqlite3_close(database);
return nil;
}
and make sure the caller handles nil results.
B/ If you do have data, your code never gets to call sqlite3_finalize and sqlite3_close because you return early:
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
[..]
return returnString;
while (sqlite3_step(sqlstatement) == SQLITE_ROW )
{
//Your code goes here
}
sqlite3_finalize(sqlstatement);
sqlite3_close(databaseRefObj);
close the database and finalize your statement after the while loop this helped me out,

SQLite Out of Memory when preparing insert statement

I have a problem with my app it opens this database and selects rows from it ok,
Then when I want to add new rows using the following code and I always get the following problem at the execution of the prepare_V2.
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Error while creating add statement. 'out of memory''
code is .....
static sqlite3 *database = nil;
static sqlite3_stmt *addStmt = nil;
- (BOOL)addUserprofile {
addStmt = nil; // set to force open for testing
database = nil; // set to force creation of addstmt for testing
if (database == nil) { // first time then open database
NSString *databaseName = #"UserProfile.db";
// Use editable database paths
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
NSString *databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
NSLog(#"path = %#",databasePath);
NSLog(#"opening Database");
sqlite3 *database;
// Open the database from the users filessytem
if (sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
NSLog(#"Database Open");
}
else {
NSLog(#"Database did not open");
}
}
if(addStmt == nil) {
NSLog(#"Creating add stmt");
const char *sql = "INSERT INTO Profile (ProfileName) VALUES(?)";
if(sqlite3_prepare_v2(database, sql, -1, &addStmt, NULL) != SQLITE_OK) {
NSAssert1(0, #"** Error while creating add statement. '%s'", sqlite3_errmsg(database));
success = NO;
return success;
}
}
sqlite3_bind_text(addStmt, 1, [ProfileName UTF8String], -1, SQLITE_TRANSIENT);
Sometime, your database is being SQLITE_BUSY or SQLITE_LOCKED.
You can refer this framework to know how to do:
https://github.com/ccgus/fmdb
Good luck!:)