locked sqlite file on iPhone - iphone

Until now all my database access has been reading, but now I need to update (and after this insert)
I have a database containing 'shows' in the app directory (read-only) and that's ok for me (I don't want to copy it to the documents folder as it's rather big and I don't need to change stuff in it.
But I want the user to select some shows as his favorite. Therefore I created a database with a table 'favorite_shows' in the documents folder. It contains 4 fields:
ID (prim key)
show_id
is_favorite
remarks (currently not yet in use)
The user is able to toggle the 'is_favorite' status ONCE, after that I'm getting an error when trying to update:
SQLITE_BUSY 5 /* The database file is locked */
This is my code:
if (sqlite3_open([databasePath UTF8String],&database) == SQLITE_OK){
sqlStatement = "select * from favorite_shows WHERE (show_id = ?)";
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
sqlite3_bind_int(compiledStatement, 1, self.ID);
// search for a show
if(sqlite3_step(compiledStatement) == SQLITE_ROW) {
// if we can find one, toggle the status
favID = sqlite3_column_int(compiledStatement, 0); // we need the primary key to update it
isFav = (sqlite3_column_int(compiledStatement, 2) == 1); // let's store the favorite status
sqlStatement = "update favorite_shows SET is_favorite = ? WHERE (ID = ?)";
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
sqlite3_bind_int(compiledStatement, 1, !isFav );
sqlite3_bind_int(compiledStatement, 2, favID);
int error = sqlite3_step(compiledStatement);
if (SQLITE_DONE != error) {
NSLog(#"error while updating favorite status");
}
}
}
//else : no records found indicating that this show hasn't been a favorite yet, so insert one as favorite
sqlite3_finalize(compiledStatement);
sqlite3_close(database);
}
What is the reason that it's locked the second time ? Is there some other instruction to give besides :
sqlite3_finalize(compiledStatement);
sqlite3_close(database);
to close everything ?

EDIT:
The BUSY is a result of re-using compiledStatement without deleting the previously compiled statment. You need to free resources properly using finalize and close functions.
Refer to the docs here. http://sqlite.org/c3ref/stmt.html
const char * select = "select * from favorite_shows WHERE (show_id = ?)";
const char * update = "update favorite_shows SET is_favorite = ? WHERE (ID = ?)";
sqlite3_stmt *selectStmt;
sqlite3_stmt *updateStmt;
if (sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
if(sqlite3_prepare_v2(database, select, -1, &selectStmt, NULL) == SQLITE_OK) {
sqlite3_bind_int(selectStmt, 1, self.ID);
// search for a show
if(sqlite3_step(selectStmt) == SQLITE_ROW) {
// if we can find one, toggle the status
favID = sqlite3_column_int(selectStmt, 0); // we need the primary key to update it
isFav = (sqlite3_column_int(selectStmt, 2) == 1) ? 0 : 1; // Flip is_favorite value
sqlite3_finalize(selectStmt); // Delete the statement OR create a new one
if(sqlite3_prepare_v2(database, update, -1, &updateStmt, NULL) == SQLITE_OK) {
sqlite3_bind_int(updateStmt, 1, isFav );
sqlite3_bind_int(updateStmt, 2, favID);
int error = sqlite3_step(updateStmt);
if (SQLITE_DONE != error) {
NSLog(#"error while updating favorite status");
} else {
sqlite3_finalize(updateStmt);
}
}
} //else : no records found indicating that this show hasn't been a favorite yet, so insert one as favorite
}
sqlite3_close(database);
}

Related

sqlite3 select query hang in IOS6

I can receive the data from .sql file(sqlite) in iOS5 , But in ios6. The app is getting hang..
If the query is select * from Table then works perfectly
If query is select *from Table order by ID DESC then the app is getting hang
some times the app hang in (sqlite3_open([dbPath UTF8String],&db) == SQLITE_OK)
some times hangs in
while(sqlite3_step(compiledStatement) == SQLITE_ROW)
{
}
Finalize the statements and Close the connection properly each you perform an operation, then you should be fine with the sqlite. If the statements are not finalized then you get to states like SQLITE_BUSY,SQLITE_LOCKED which you need to handle. If the app is hanging that means your main thread is blocked. Following is a sample sqlite operation.
-(int)keyIdForImgId:(int)ImgId
{
#synchronized(self)
{
int keyId=0;
sqlite3 *database=nil;
if (sqlite3_open([[self getDBPath] UTF8String], &database) == SQLITE_OK) {
const char *sql = "SELECT keyId From SomeTableName WHERE imageId=?";
sqlite3_stmt *selectstmt=nil;
if(sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL) == SQLITE_OK) {
sqlite3_bind_int(selectstmt, 1, ImgId);
while(sqlite3_step(selectstmt) == SQLITE_ROW)
{
keyId = sqlite3_column_int(selectstmt, 0);
}
}
sqlite3_finalize(selectstmt);
sqlite3_close(database);
}
return keyId;
}
}

Sqlite finalise and Db locking issue

I am using the below function in my app,and i have started using the sq-lite recently and i would like to get your opinion that i am going with that correctly or not.
Since i am facing db locked issue in my app when searched i found that i need to use sqlite3 finalise statement.
what i am not sure is do i need to place one finalise statement for each sqlite3 prepare statement
Please let me know
- ( BOOL ) addNewCate:(NSString*)dbPath:(NSString*)title:(NSString*)tierOneID:(NSString*)tierTwoID{
BOOL returnVal = NO;
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK)
{
const char *sql = "insert into storyboard_phrases(phrase) Values(?)";
sqlite3_stmt *addStmt;
if(sqlite3_prepare_v2(database, sql, -1, &addStmt, NULL) == SQLITE_OK){
sqlite3_bind_text(addStmt, 1, [title UTF8String], -1, SQLITE_TRANSIENT);
}
if(sqlite3_step(addStmt) != SQLITE_DONE ) {
NSLog( #"Error: %s", sqlite3_errmsg(database) );
} else {
NSLog( #"Insert into row id = %d", sqlite3_last_insert_rowid(database));
int ph_id = sqlite3_last_insert_rowid(database);
int sub_category_id = [tierTwoID intValue];
int main_category_id = [tierOneID intValue];
addStmt = nil;
sql = "insert into phrase_reference(phrase_id, sub_category_id,main_category_id) Values(?,?,?)";
if(sqlite3_prepare_v2(database, sql, -1, &addStmt, NULL) == SQLITE_OK){
sqlite3_bind_int(addStmt, 1, ph_id);
sqlite3_bind_int(addStmt, 2, sub_category_id);
sqlite3_bind_int(addStmt, 3, main_category_id);
}
if(sqlite3_step(addStmt) != SQLITE_DONE ) {
NSLog( #"Error: %s", sqlite3_errmsg(database) );
} else {
NSLog( #"Insert into row id = %d", sqlite3_last_insert_rowid(database));
returnVal = YES;
}
}
sqlite3_finalize(addStmt);
}
sqlite3_close(database);
return returnVal;
}
hii you get the locked issues in log right, that means your database is open and you doing some changes in that database so close database and try again to run application and insert in table...
hope this will help you..

SQLite update statement performed without error, but table row not updated?

I cant for the life of me see what it could be? Any Advice? Thanks
(void)setRegId: (NSInteger) _regId
{
NSInteger r_id = _regId;
NSInteger row = 1;
ret = sqlite3_open(dbName, &database);
if (ret != SQLITE_OK)
{
printf("Error opening the db: %s\n", sqlite3_errmsg(database));
sqlite3_close(database);
return;
}
sqlite3_stmt *compiledStatement = nil;
if (compiledStatement == nil) {
const char *sql = "UPDATE RegDB Set id = ? Where rowindex = ?";
if (sqlite3_prepare_v2(database, sql, -1, &compiledStatement, NULL) != SQLITE_OK)
{
NSLog(#"Error: failed to prepare compiledStatement with message '%s'.",sqlite3_errmsg(database));
return;
}
}
sqlite3_bind_int(compiledStatement, 1, r_id);
sqlite3_bind_int(compiledStatement, 2, row);
int success = sqlite3_step(compiledStatement);
if (success == SQLITE_ERROR){
NSLog(#"Error: failed to update compiledStatement with message '%s'.", sqlite3_errmsg(database));
}
sqlite3_finalize(compiledStatement);
if(sqlite3_close(database) != SQLITE_OK)
{
NSLog(#"Error: failed to closed DB with message '%s'.",sqlite3_errmsg(database));
}
}
You need to check that you are both sending the correct update statement, and that you are correctly reading the result.
Try logging your final statement (or at least the variables r_id and row). Use sqlite3 or SQLiteManager with Firefox to check changes in the database.

iphone: delete row from sqlite database

I have tried many ways to achieve this biut unable to delete row from sqlite, please help me to correct following piece of code
dbPath = [self applicationDocumentsDirectory];
NSLog(#"%#", dbPath);
dbPath=[dbPath stringByAppendingPathComponent:#"database"];
dbPath=[dbPath stringByAppendingPathComponent:#"OFFENDERSDB.sqlite"];
NSLog(#"database path --> %#", dbPath);
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK) {
const char *sqlStatement = ("DELETE * from OFFENDERS_LIST where ID=%d ",2);
sqlite3_stmt *compiledStatement;
if (sqlite3_prepare(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
NSLog(#"offender deleted");
}
sqlite3_finalize(compiledStatement);
}
Thanx in advance
have you tried using:
sqlite3_exec(database, sqlStatement...
with your DELETE statement?
ALSO.... it's DELETE FROM.... not DELETE * FROM....
Snagged the following code from another place out on the internet for a more complete example...
sqlite3 *db;
int rc;
rc = sqlite3_open( "C:\\MyDatabase", &db );
if ( rc )
{
sqlite3_close(db);
}
else //Database connection opened successfuly
{
char *zErrMsg = 0;
rc = sqlite3_exec( db, "DELETE FROM yourTable", NULL, NULL, &zErrMsg );
if( rc != SQLITE_OK )
{
sqlite3_free( zErrMsg );
}
sqlite3_close(db);
}

sqlite3 int problem

I've completely given up on this, so if a moderator happens to come by, deleting would be great.
It doesn't hurt to leave this question up, but CoreData is so much nicer, you know?
I have a sqlite database for handling the contents of a table. It's great and everything (much easier than other options I looked at), but I'm having trouble with ints. The first time I go to edit an item after launching the app, the field for the int is empty. Re-entering works fine, it saves and appears in the table, but the next edit (without reopening the app) sets the second item's int to that of the first.
i.e., A(1) resets to A(0). I fix it (A(1)), but then B(2) becomes B(1) as soon as I load the edit view. Fix it (B(2)) or not (B(1)), C(3) will then have the same (#) as B.
I still can't figure out what's causing it. Changing the int to a string (edit database column and every relevant file in the app) would certainly work, but that's a whole lot of unnecessary work just to make it slower and easier to break.
edit:
CREATE TABLE "items" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" VARCHAR, "need" INTEGER DEFAULT 0, "notes" TEXT)
- (void)updateItemAtIndexPath:(NSIndexPath *)path {
Item *i = (Item *)[items objectAtIndex:path.row];
int ret;
const char *sql = "update items set name = ?, need = ?, notes = ? where id = ?;";
if (!updStmt) { // build update statement
if ((ret = sqlite3_prepare_v2(database, sql, -1, &updStmt, NULL)) != SQLITE_OK) {
NSAssert1(0, #"Error building statement to update items [%s]", sqlite3_errmsg(database));
}
}
// bind values to statement
NSString *s = i.name;
if (s == NULL) s = #"";
sqlite3_bind_text(updStmt, 1, [s UTF8String], -1, SQLITE_TRANSIENT);
NSInteger n = i.need;
sqlite3_bind_int(updStmt, 2, n);
s = i.notes;
if (s == NULL) s = #"";
sqlite3_bind_text(updStmt, 3, [s UTF8String], -1, SQLITE_TRANSIENT);
n = i.itemid;
sqlite3_bind_int(updStmt, 4, n);
// now execute sql statement
if (sqlite3_step(updStmt) != SQLITE_DONE) {
NSAssert1(0, #"Error updating values [%s]", sqlite3_errmsg(database));
}
// now reset bound statement to original state
sqlite3_reset(updStmt);
}
- (void)insertItem:(Item *)item {
int ret;
const char *sql = "insert into items (name, need, notes) values (?, ?, ?);";
if (!insStmt) { // first insert - build statement
if ((ret = sqlite3_prepare_v2(database, sql, -1, &insStmt, NULL)) != SQLITE_OK) {
NSAssert1(0, #"Error building statement to insert item [%s]", sqlite3_errmsg(database));
}
}
// bind values
NSString *s = item.name;
if (s == NULL) s = #"";
sqlite3_bind_text(insStmt, 1, [s UTF8String], -1, SQLITE_TRANSIENT);
NSInteger n = item.need;
sqlite3_bind_int(insStmt, 2, n);
s = item.notes;
if (s == NULL) s = #"";
sqlite3_bind_text(insStmt, 3, [s UTF8String], -1, SQLITE_TRANSIENT);
// execute sql statement
if (sqlite3_step(insStmt) != SQLITE_DONE) {
NSAssert1(0, #"Error inserting item [%s]", sqlite3_errmsg(database));
}
// reset bound statement to original state
sqlite3_reset(insStmt);
[self readItems]; // refresh array
}
Instead of using sqlite3_bind_text and sqlite3_bind_int, I would try to construct the query string from the various values and use sqlite3_exec to run it. Let's call that a tryout towards a solution.
Example (warning, untested!!):
- (void)updateItemAtIndexPath:(NSIndexPath *)path {
Item *i = (Item *)[items objectAtIndex:path.row];
// validate values
NSString *name = i.name;
if (name == NULL) name = #"";
[name stringByReplacingOccurrencesOfString:#"'"
withString:#"''"];
NSInteger need = i.need;
NSString *notes = i.notes;
if (notes == NULL) notes = #"";
[notes stringByReplacingOccurrencesOfString:#"'"
withString:#"''"];
NSInteger itemid = i.itemid;
NSString *sql = [NSString stringWithFormat:
#"update items set name = '%#', need = %#, notes = '%#' where id = %#;",
name, need, notes, itemid];
// now execute sql statement
if (sqlite3_exec(database, [sql UTF8String], NULL, NULL, NULL) != SQLITE_DONE) {
NSAssert1(0, #"Error updating values [%s]", sqlite3_errmsg(database));
}
}