Related
I am working with sqlite database in iOS.I have used CURD operation in my app.For example to insert data into the database i have used below code.
- (BOOL) saveData:(User *)user
{
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &database) == SQLITE_OK)
{
if([self getUserData:user.email] != nil)
{
[self updateUserData:user];
return YES;
}
else
{
NSString *insertSQL = [NSString stringWithFormat:
#"insert into users(userid,name,email,password,address,age,gender,phone,qualification,role,createddate,apiKey,priorityid,usertype) values (\"%#\",\"%#\",\"%#\",\"%#\",\"%#\",\"%#\",\"%#\",\"%#\",\"%#\",\"%#\",\"%#\",\"%#\",\"%#\",\"%#\",\"%#\")",user.userid,user.name,user.email,user.password,user.address,user.age,user.gender,user.phone,user.qualification,user.role,user.createddate,user.api_key,user.priority_id,user.user_type];
NSLog(#"%#",insertSQL);
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(database, insert_stmt,-1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
sqlite3_finalize(statement);
// sqlite3_close(database);
return YES;
}
else
{
NSLog(#"%serrr is ",sqlite3_errmsg(database));
//sqlite3_reset(statement);
sqlite3_close(database);
return NO;
}
}
}
//sqlite3_reset(statement);
sqlite3_close(database);
return NO;
}
I am getting memory issues in my app due to sqlite database.I am not able to understand what should be order of calling sqlite3_reset();,sqlite3_finalize(), sqlite3_close().Please tell in reference to my code so that i can resolve all memory issues in my app
EDIT:
- (BOOL) insertSeMapData:(Client *)client : (NSString *)userid : (NSString *)sendemailto : (NSString *)assignworkordersto
{
BOOL result=NO;
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &database) == SQLITE_OK)
{
InspectionMapDetails *inspMap = [self getSEMapData:client :userid];
if(inspMap != nil)
{
[self updateSEMapData:client :userid :inspMap : sendemailto : assignworkordersto];
result=YES;
}
else
{
const char *insert_stmt = "insert into map(inspid,inspectorid,clientid,status,createddate,sendemailreportto,assignworkordersto) values (?,?,?,?, datetime(),?,?)";
if (sqlite3_prepare_v2(database, insert_stmt,-1, &statement, NULL) == SQLITE_OK)
{
NSString *clientinspid=[NSString stringWithFormat:#"%#",client.inspid];
NSString *clientid=[NSString stringWithFormat:#"%#",client.clientid];
userid=[NSString stringWithFormat:#"%#",userid];
sqlite3_bind_text(statement, 1, [clientinspid UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(statement, 2, [userid UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(statement, 3, [clientid UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(statement, 4, [sendemailto UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(statement, 5, [assignworkordersto UTF8String], -1, SQLITE_TRANSIENT);
if (sqlite3_step(statement) == SQLITE_DONE)
{
result = YES;
}
sqlite3_finalize(statement);
}
else
{
NSLog(#"Unable to prepare statement: %s",sqlite3_errmsg(database));
}
sqlite3_close(database);
}
}
else
{
NSLog(#"Unable to open database: %s",sqlite3_errmsg(database));
}
return result;
}
Above function causing memory issue.Here Sqlite_open statement is causing a memory leak which i don't understand why?
There are simple guidelines, and to be blunt, your code doesn't follow any of them at all.
For each successful call to sqlite3_open you must call sqlite3_close.
For each successful call to sqlite3_prepare_v2 you must call sqlite3_finalize. Optionally, in between you may call sqlite3_reset zero or more times in cases where you reuse the prepared statement.
You should always check the result of calls to sqlite3_open and sqlite3_prepare_v2. If they fail, you should use sqlite3_errmsg to log what the problem was.
Do not build query strings using stringWithFormat. Instead, properly bind values to the query using appropriate calls to the various sqlite3_bind_xxx functions.
The code you posted violates all of these. You have plenty of cases where you don't close the database or finalize the prepared statement. And you are incorrectly building your query with stringWithFormat:.
Here's your code all fixed up. Note that I'm assuming all the values you are inserting are strings. Adjust accordingly for any non-string values.
- (BOOL) saveData:(User *)user
{
BOOL result = NO;
if([self getUserData:user.email] != nil)
{
[self updateUserData:user];
result = YES;
}
else
{
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &database) == SQLITE_OK)
{
const char *insert_stmt = "insert into users(userid,name,email,password,address,age,gender,phone,qualification,role,createddate,apiKey,priorityid,usertype) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
if (sqlite3_prepare_v2(database, insert_stmt,-1, &statement, NULL) == SQLITE_OK) {
sqlite3_bind_text(stmt, 1, [user.userid UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 2, [user.name UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 3, [user.email UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 4, [user.password UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 5, [user.address UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 6, [user.age UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 7, [user.gender UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 8, [user.phone UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 9, [user.qualification UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 10, [user.role UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 11, [user.createddate UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 12, [user.api_key UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 13, [user.priority_id UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 14, [user.user_type UTF8String], -1, SQLITE_TRANSIENT);
if (sqlite3_step(statement) == SQLITE_DONE)
{
result = YES;
}
sqlite3_finalize(statement);
}
else
{
NSLog(#"Unable to prepare statement: %s",sqlite3_errmsg(database));
}
sqlite3_close(database);
}
else
{
NSLog(#"Unable to open database: %s",sqlite3_errmsg(database));
}
}
return result;
}
Update based on the new edit:
Just like your original code, you violate a few of the rules. Your updated code doesn't quite follow the pattern I gave in my answer above.
The main issue now is that you open the database but you only close it under one of two possible code paths. If inspMap != nil you never close the database.
You really should refactor the code so you only open the database if inspMap is nil. I did something similar to your original code in my original answer.
////this method use for show data and that works
NSString *databasePath =[[[NSBundle mainBundle] bundlePath]stringByAppendingPathComponent:#"images.db"];
if(sqlite3_open([databasePath UTF8String], &db)==SQLITE_OK){
const char *sql = "SELECT * FROM IMAGEDATA";
sqlite3_stmt *sqlStatement;
if(sqlite3_prepare(db, sql, -1, &sqlStatement, NULL) != SQLITE_OK)
{
NSLog(#"Problem with prepare statement: %s", sqlite3_errmsg(db));
}
else{
while (sqlite3_step(sqlStatement)==SQLITE_ROW) {
NSData *imageData = [[NSData alloc] initWithBytes:sqlite3_column_blob(sqlStatement, 1) length: sqlite3_column_bytes(sqlStatement, 1)];
imagesfromdb =[UIImage imageWithData:imageData];
NSLog(#"IMAGE :::%#",imagesfromdb);
[databaseimage addObject:imagesfromdb];
//imageData is saved favicon from website.
}
}
}
/////this method use for insert data.
const char *dbpath = [databasePath UTF8String];
if(sqlite3_open(dbpath, &database)==SQLITE_OK)
{
NSData *imageData = UIImageJPEGRepresentation(chosenImage, 1);
const char* sqliteQuery = "INSERT INTO IMAGEDATA (NAME, IMAGE) VALUES (?, ?)";
//sqlite3_stmt* statement;
if( sqlite3_prepare_v2(database, sqliteQuery, -1, &statement, NULL) == SQLITE_OK )
{
sqlite3_bind_text(statement, 1, [fileName UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_blob(statement, 2, [imageData bytes], (int)[imageData length], SQLITE_TRANSIENT);
sqlite3_step(statement);
}
else NSLog( #"SaveBody: Failed from sqlite3_prepare_v2. Error is: %s", sqlite3_errmsg(database) );
// Finalize and close database.
sqlite3_finalize(statement);
}
this method not working i cant understand but this method show data until i close the app. but after that the data will be gone and didnt save in db file
All the assets inside mainBundle of your app sandbox have readonly property. That's why in your case you are able to read the data but not modify it. So you should first copy your database file to DocumentDirectory which has readwrite access, and then you will be able to perform the write operation in that db. Check this question to understand how to copy database file from mainBundle to DocumentDirectory.
How to copy sqlite database when application is launched in iOS?
You can follow this Tutorial as well to understand all steps
I am trying to update a table in my sqlite database within an iOS app. I have used this update statement before, and it worked.
The below statement is returning a sqlite commit value of 0, BUT all the fields in the row to be updated are set to "< null >".
I don't get an error message ...
What is wrong with the statement?
-(void) UpdateSMUser :(NSString *) tableName
withField1:(NSString *) field1
field1Value:(NSString *) field1Value
andField2:(NSString *) field2
field2Value:(NSString *) field2Value
andField3:(NSString *) field3
field3Value:(NSString *) field3Value
andField4:(NSString *) field4
field4Value:(NSString *) field4Value
{
[self openDB];
sqlite3_stmt *updateStmt;
NSString *sql = [NSString stringWithFormat:#"UPDATE SMUser SET UserID = ?, Profile = ?, SMReg = ?, Date = ? WHERE UserID = '%#'" ,field1Value];
if (sqlite3_prepare_v2(db, [sql UTF8String] , -1,
&updateStmt, NULL) == SQLITE_OK)
{
sqlite3_bind_text(updateStmt, 1, [field1Value UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(updateStmt, 2, [field2Value UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(updateStmt, 3, [field3Value UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(updateStmt, 4, [field4Value UTF8String], -1, SQLITE_TRANSIENT);
}
// [self openDB];
char* errmsg;
NSLog(#"sqlite commit %d",sqlite3_exec(db, [sql UTF8String], NULL, NULL, &errmsg));
sqlite3_exec(db, [sql UTF8String], NULL, NULL, &errmsg);
if(SQLITE_DONE != sqlite3_step(updateStmt)){
NSLog(#"Error while updating. %s", sqlite3_errmsg(db));
}
else{
// [self clearClick:nil];
}
sqlite3_finalize(updateStmt);
sqlite3_close(db);
}
Thank you very much in advance ... it is driving me crazy!
sqlite3_exec() is an (obsolete) wrapper around sqlite3_prepare*()/sqlite3_step()/sqlite3_finalize().
You cannot use sqlite3_exec() with parameters.
You still have the correct sqlite3_step() call in your program; just remove both sqlite3_exec() calls.
I have an application using sqlite where I'm checking if a particular record exists in table. If not, I am inserting data into table but while inserting it is giving me error code 5.
This is my code
-(int)addPayOptions:(NSString*)owner:(NSString*)bank:(NSString*)number:(NSString*)scheme:(NSString*)type:(NSString*)expiryDate:(NSString*)name
{
NSString *queryString=nil;
NSString *dbPath = [DBManager getdbpath];
int val =0;
#try {
queryString=[NSString stringWithFormat:#"SELECT * FROM PAYTABLE WHERE NAME='%#'",name];
if (sqlite3_open([dbPath UTF8String], &database)==SQLITE_OK)
{
int resp_code =sqlite3_prepare_v2(database, [queryString UTF8String], -1, &statement, NULL);
NSLog(#"%d",resp_code);
if (sqlite3_prepare_v2(database, [queryString UTF8String], -1, &statement, NULL)==SQLITE_OK)
{
if (sqlite3_step(statement)==SQLITE_ROW)
{
val=0;
return val;
sqlite3_reset(statement);
}
else
{
BOOL checkinserted =[self insertPayOptions:owner :bank :number :0:scheme:type:expiryDate:name];
if (checkinserted)
{
val=1;
return val;
}
else
{
val=0;
return val;
}
}
sqlite3_finalize(statement);
}
}
sqlite3_close(database);
}
#catch (NSException *exception) {
}
return val;
}
-(BOOL)insertPayOptions:(NSString*)owner:(NSString*)bank:(NSString*)number:(NSString*)defaultOption:(NSString*)scheme:(NSString*)type:(NSString*)expiryDate:(NSString*)name
{
const char *dbpath = [dbPath UTF8String];
BOOL check = '\0';
if (sqlite3_open([dbPath UTF8String], &database)==SQLITE_OK)
{
NSString *insertSQL = [NSString stringWithFormat:#"insert into PAYTABLE (OWNER,BANK,NUMBER,DEFAULT_OPTION,SCHEME,TYPE,EXPIRYDATE,NAME) VALUES('%#','%#','%#','%d','%#','%#','%#','%#')",owner,bank,number,0,scheme,type,expiryDate,name];
if(sqlite3_prepare_v2(database, [insertSQL UTF8String], -1, &statement, NULL)==SQLITE_OK)
{
NSLog(#"%d",sqlite3_step(statement));
if (sqlite3_step(statement)==SQLITE_DONE)
{
check=YES;
}
else
{
check =NO;
}
}
sqlite3_finalize(statement);
}
sqlite3_close(database);
return check;
}
A value of 5 is SQLITE_BUSY, which means that the database is currently busy doing something else (e.g., that a prior SQLite call was not properly finalized and closed).
Perhaps coincidentally, this method is a perfect example of precisely the sort of programming error that would cause this SQLITE_BUSY error. In your code, if the sqlite3_prepare_v2 statement succeeded, you will never get to your sqlite3_finalize or sqlite3_close statements because you return before getting to those finalize/close calls. Thus, the next time you call this method, it will tell you that the database is busy (because the prior call was never finalized and closed).
Be very, very wary of calling return inside your methods that perform SQLite calls, or if you do do employ return within the method, ensure that you call the appropriate sqlite3_finalize and sqlite3_close calls before returning. Or, better, refactor your code to minimize these return statements littered throughout the method, and it will be harder to make this mistake.
By the way, while this method conveniently illustrates the sort of programming error that would cause SQLITE_BUSY, it's not necessarily the case that this is the only situation in your code where you fail to finalize your statement or fail to close the database. You really should go through all of your SQLite related functions and consider each path of execution, and make sure that there's no possible way that sqlite3_finalize and sqlite3_close might fail to be called.
A couple of unrelated observations:
By the way, if a database call ever fails, it's often useful to log sqlite3_errmsg, which will be a little more informative regarding the precise nature of the error. For example, right now, if the prepare statement failed, you're silently returning, without any useful diagnostic information regarding any potential error.
You should avoid using try/catch blocks in Objective-C. While this is an important construct in other programming languages, in Objective-C you should do all of your error handling by looking at return codes (and where provided, examining NSError objects). In fact, since your catch block does nothing, you'll actually be obscuring any exceptions that take place, so it's even worse.
You should be very wary about using stringWithFormat to build your SQL statements. If the value included an apostrophe, your SQL would fail. Use sqlite3_bind_text instead, in conjunction with SQL that uses the ? placeholder.
The use of sqlite3_reset statement is unnecessary in this case. You only use sqlite3_reset when reusing a previously prepared sqlite3_stmt, which you are not doing here. You called sqlite3_reset after your return statement, so perhaps this is academic, but nonetheless, this sqlite3_reset call can be removed.
Hi following code works for me you can check,
NSString *querySQL = [NSString stringWithFormat: #"SELECT * FROM PAYTABLE WHERE NAME='%#'",name]];
int rows;
const char *query_stmt = [querySQL UTF8String];
if (sqlite3_prepare_v2(database, query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
sqlite3_step(statement);
rows = sqlite3_column_int(statement, 0);
sqlite3_finalize(statement);
sqlStatement = "insert into PAYTABLE (OWNER,BANK,NUMBER,DEFAULT_OPTION,SCHEME,TYPE,EXPIRYDATE,NAME) VALUES (?,?,?,?,?,?,?,?)";
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(billDeskDB, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK)
{
sqlite3_bind_text(compiledStatement, 1, [owner UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(compiledStatement, 2, [bank UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(compiledStatement, 3, [number UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(compiledStatement, 4, [#"0" UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(compiledStatement, 5, [scheme UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(compiledStatement, 6, [type UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(compiledStatement, 7, [expiryDate UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(compiledStatement, 8, [name UTF8String], -1, SQLITE_TRANSIENT);
}
}
I'm trying to updating the SQLite db on the iPhone with a dictionary returned by my server.
sqlite3_step return sqlite_ok but the database still remain empty.
Anyway inserted a log for a possible error after sqlite3_step and this return "database locked" so I think something is wrong with the sqlite function order in my code.
I've also verified that the dictionary contains data with a log inside the iteration and it gives me all I expect to find.
What's wrong here? Can someone help me?
Sorry for my bad english and thanks
Marco
//----- database update -----------------------------------------------------------------------------------------------------
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *dbPath = [documentsDirectory stringByAppendingPathComponent:#"DB.sqlite"];
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK) {
hasError= false;
const char *update_stmt="REPLACE INTO table VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?)";
sqlite3_stmt *compiledstatement;
if(sqlite3_prepare_v2(database,update_stmt , -1, &compiledstatement, NULL)==SQLITE_OK) {
for (NSDictionary *item in [update objectForKey:#"table1"]) {
sqlite3_bind_int(compiledstatement, 1,[[item objectForKey:#"a"]integerValue]);
sqlite3_bind_text(compiledstatement,2,[[item objectForKey:#"b"] UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(compiledstatement,3,[[item objectForKey:#"c"] UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(compiledstatement,4,[[item objectForKey:#"d"] UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_int(compiledstatement,5,[[item objectForKey:#"e"] integerValue]);
sqlite3_bind_double(compiledstatement,6,[[item objectForKey:#"f"] doubleValue]);
sqlite3_bind_text(compiledstatement,7,[[item objectForKey:#"g"] UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(compiledstatement,8,[[item objectForKey:#"s"] UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(compiledstatement,9,[[item objectForKey:#"g"] UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(compiledstatement,10,[[item objectForKey:#"u"] UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(compiledstatement,11,[[item objectForKey:#"y"] UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_int(compiledstatement,12,[[item objectForKey:#"n"] integerValue]);
sqlite3_bind_int(compiledstatement,13,[[item objectForKey:#"k"] integerValue]);
//sqlite3_bind_text(compiledstatement,2,[#"" UTF8String], -1, SQLITE_TRANSIENT);
if(!sqlite3_step(compiledstatement)==SQLITE_DONE) {hasError= true; NSLog(#"error (%s)", sqlite3_errmsg(database));} //error
NSLog(#"error (%s)", sqlite3_errmsg(database));
sqlite3_reset(compiledstatement);
}
sqlite3_finalize(compiledstatement);
}else { NSLog(#"prepare FAILED (%s)", sqlite3_errmsg(database));}
} else {NSLog(#"opening error");}
sqlite3_close(database);
EDIT:
I'm not able to understand why this code won't work!!! I've not find the final solution.
I always get "library routine called out of sequence" at COMMIT, why?
Isn't this sequence correct?
-open
-begin
-prepare
-cycle the dictionary(bind, step and reset; for every item in dictionary I must insert or replace a row)
-finalize
-commit
-close
If I remove the begin and commit exec I have the error "database is locked" on "step", when the code try to insert the first row...
it's a nightmare...
Thank you again for your help!!
//----- database update -----------------------------------------------------------------------------------------------------
const char *updateTags_stmt="REPLACE INTO tags VALUES(?,?,?)";
if (sqlite3_open_v2([dbPath UTF8String], &database,SQLITE_OPEN_READWRITE,NULL) != SQLITE_OK) {
sqlite3_close(database);return;
}
if (sqlite3_exec(database, "BEGIN", 0, 0, 0)!=SQLITE_OK) {sqlite3_close(database);return;}
sqlite3_stmt *compiledstatement;
if(sqlite3_prepare_v2(database,updateTags_stmt , -1, &compiledstatement, NULL)!=SQLITE_OK) {sqlite3_close(database);return;}
for (NSDictionary *item in [update objectForKey:#"tags"]) {
sqlite3_bind_int(compiledstatement,1,[[item objectForKey:#"tid"] integerValue]);
sqlite3_bind_text(compiledstatement,2,[[item objectForKey:#"categoria"] UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(compiledstatement,3,[[item objectForKey:#"tag"] UTF8String], -1, SQLITE_TRANSIENT);
if(sqlite3_step(compiledstatement)!=SQLITE_DONE) {
sqlite3_finalize(compiledstatement);
sqlite3_close(database);
return;
} else {if (sqlite3_reset(compiledstatement)!=SQLITE_OK){sqlite3_close(database);return;}}
}
if (sqlite3_finalize(compiledstatement)!=SQLITE_OK){sqlite3_close(database);return;}
if (sqlite3_exec(database, "COMMIT", NULL, NULL, 0)!=SQLITE_OK) {sqlite3_close(database);return;}
if (sqlite3_close(database)!=SQLITE_DONE){sqlite3_close(database);return;}
This code is wrong; it first inverts the return value of sqlite3_step, and then compares that value to SQLITE_DONE (which never succeeds because ! returns 0 or 1):
if(!sqlite3_step(compiledstatement)==SQLITE_DONE)
Do it correctly:
if (sqlite3_step(compiledstatement) != SQLITE_DONE)
Finally I've found the problem: there was another query on a different class in which then statement was not finalized so the DB still remains always open... two weeks for this lesson!! heheh sorry!