SQLITE database locked during transaction - objective-c

I have been trying to figure this out for days now but I cannot figure out what is going on. In my app I want to be able to delete an item, and related items from a tableView. So when the delete button in the TableView is pushed the following method is called.
- (BOOL) deleteDuik:(MOODuik *) duik
{
int diveid = [duik duikId];
NSLog(#"Begin Delete");
sqlite3 *db = [self openDatabase];
sqlite3_stmt * deleteStmt = nil;
BOOL succes = TRUE;
#try{
if (db != NULL) {
sqlite3_exec(db, "BEGIN TRANSACTION", 0, 0, 0);
//NSString *sqlString = [NSString stringWithFormat:#"delete from duik where duikid = %d", diveid];
const char *sql = "delete from duik where duikid = ?";
if(sqlite3_prepare_v2(db, sql, -1, &deleteStmt, NULL) == SQLITE_OK)
{
sqlite3_bind_int(deleteStmt, 1, diveid);
sqlite3_step(deleteStmt);
sqlite3_finalize(deleteStmt);
deleteStmt = nil;
sql = "delete from waarneming where duikid = ?";
if(sqlite3_prepare_v2(db, sql, -1, &deleteStmt, NULL) == SQLITE_OK)
{
sqlite3_bind_int(deleteStmt, 1, diveid);
sqlite3_step(deleteStmt);
sqlite3_finalize(deleteStmt);
}
}
} else
{
NSLog(#"Failed to connect to db");
succes = FALSE;
}
if(succes && ( [duik backendid] == 0 || [self deleteDuikServerSide:[duik backendid]]))
{
NSLog(#"End Delete");
if(sqlite3_exec(db, "COMMIT TRANSACTION", 0, 0, 0) !=SQLITE_OK) NSLog(#"SQL Error: %s",sqlite3_errmsg(db));
} else{
NSLog(#"Failed to delete remote copy");
sqlite3_exec(db, "ROLLBACK TRANSACTION", 0, 0, 0);
succes = false;
}
} #catch (NSException *e) {
NSLog(#"Failed to delete: %#", [e description]);
sqlite3_exec(db, "ROLLBACK TRANSACTION", 0, 0, 0);
} #finally {
#try {
sqlite3_finalize(deleteStmt);
sqlite3_close(db);
}
#catch (NSException *exception) {
//REally nothing to do here
}
sqlite3_close(db);
}
return succes;
}
When I try to run this, it keeps telling me the database is locked, yet I have no idea why it is locked. I have checked to see if other actions require the DB at the same time but I cannot find any. Also, if I add a row from the same tableView, I use the same construction with a transaction and that one DOES work. I have tried everything I could come up with, rewritten this chunk of code at least 10 times but so far no luck.
Is there anyone who can tell me what I am doing wrong here?

Related

sqlite3_step(statement) == SQLITE_DONE statement not return yes

all time saatment not return no.
-(BOOL)updateProductTable:(NSString *)productid column_shop_Product_quantity:(NSString *) productquantity{
NSLog(#"%#",productid);
NSLog(#"%#",productquantity);
const char *dbpath = [databasepath UTF8String];
if (sqlite3_open(dbpath, &database) == SQLITE_OK)
{
NSString *updateSQL = [NSString stringWithFormat:#"UPDATE ShopProduct set column_shop_Product_quantity=%# WHERE column_shop_Product_id=%#",productquantity,productid];
NSLog(#"%#",updateSQL);
const char *update_stmt = [updateSQL UTF8String];
if (sqlite3_prepare_v2(database, update_stmt, -1, &statement, nil) == SQLITE_OK)
{
if (sqlite3_step(statement) == SQLITE_DONE)
{
sqlite3_reset(statement);
return YES;
}
else {
return NO;
}
sqlite3_finalize(statement);
}
}
sqlite3_close(database);
return NO;
}
There are several issues with your code:
Never bind values to a query using stringWithFormat:.
You don't close the database in most cases.
You don't finalize the prepared statement in most cases.
You should add more error logging to determine the cause of any issues.
Why is the product quantity passed as a string instead of a number?
You should use sqlite3_open_v2.
Use local variables for the database and prepared statement.
Here is your code updated for all of these issues:
-(BOOL)updateProductTable:(NSString *)productid column_shop_Product_quantity:(NSString *)productquantity {
NSLog(#"%#",productid);
NSLog(#"%#",productquantity);
BOOL res = NO;
sqlite3 *database = NULL;
const char *dbpath = [databasepath UTF8String];
if (sqlite3_open_v2(dbpath, &database, SQLITE_OPEN_READWRITE, NULL) == SQLITE_OK)
{
const char *updateSQL = "UPDATE ShopProduct set column_shop_Product_quantity=? WHERE column_shop_Product_id=?";
sqlite3_stmt *statement = NULL;
if (sqlite3_prepare_v2(database, updateSQL, -1, &statement, NULL) == SQLITE_OK)
{
sqlite3_bind_int(statement, 0, [productquantity intValue]);
sqlite3_bind_text(statement, 1, [productid UTF8String], -1, SQLITE_TRANSIENT);
if (sqlite3_step(statement) == SQLITE_DONE) {
res = YES;
} else {
NSLog(#"Unable to update data: %s", sqlite3_errmsg(database));
}
sqlite3_finalize(statement);
} else {
NSLog(#"Unable to prepare statement: %s", sqlite3_errmsg(database));
}
sqlite3_close(database);
} else {
NSLog(#"Unable to open database at %#: %s", databasepath, sqlite3_errmsg(database));
}
return res;
}

Sqlite DB Locked

-(void)insertQuery:(NSString *)query{
sqlite3_stmt *selectstmt;
// Create a sqlite object.
sqlite3 *database;
// Set the database file path.
NSString *databasePath = [self.documentsDirectory stringByAppendingPathComponent:self.databaseFilename];
if (sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
//*************** insert value in database******************************\\
const char *sql = [query UTF8String];
sqlite3_prepare_v2(database,sql, -1, &selectstmt, NULL);
if(sqlite3_step(selectstmt)==SQLITE_DONE)
{
NSLog(#"insert successfully");
}
else
{
NSLog(#"insert not successfully");
NSLog(#"DB Error: %s", sqlite3_errmsg(database));
}
sqlite3_finalize(selectstmt);
}
sqlite3_close(database);
}
Using above code trying to fire insert query but sometime it works and most of the time i get DB locked error.
For helping hands Thanks in advance.
http://www.icodeblog.com/2011/11/04/simple-sqlite-database-interaction-using-fmdb/
Use this link and manage your Database with this using FMDB. I also faced this problem 5 months back and used this and my problem resolved 100%.
https://github.com/ccgus/fmdb
You are not closing the connection properly.
if (sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
//*************** insert value in database******************************\\
const char *sql = [query UTF8String];
sqlite3_prepare_v2(database,sql, -1, &selectstmt, NULL);
if(sqlite3_step(selectstmt)==SQLITE_DONE)
{
NSLog(#"insert successfully");
}
else
{
NSLog(#"insert not successfully");
NSLog(#"DB Error: %s", sqlite3_errmsg(database));
}
sqlite3_finalize(selectstmt);
sqlite3_close(database);
}
Database lock usually happens when you are writing something to database or you have left some other connection open. Check you other code for lose connections.
Try to check for SQLITE_OK for sqlite3_prepare_v2 because database may be busy and may return SQLITE_BUSY for step statement. Further explained here. Try following code:
if (sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
//*************** insert value in database******************************\\
const char *sql = [query UTF8String];
if(sqlite3_prepare_v2(database,sql, -1, &selectstmt, NULL)==SQLITE_OK)
{
if(sqlite3_step(selectstmt)==SQLITE_DONE)
{
NSLog(#"insert successfully");
}
else
{
NSLog(#"insert not successfully");
NSLog(#"DB Error: %s", sqlite3_errmsg(database));
}
sqlite3_finalize(selectstmt);
}
sqlite3_close(database);
}

Using sqlite3_exec to do transactions in Objective-c and not having desired effect

Top of the morning to'ya!
I'm currently developing a iOS app in Objective-c and I have a local database that I allow the user to update over the internet.
The database can change rather drastically in only a few short days and thus could all have been reduced to a few lines where before there were hundred of thousands (unlikely but still).
I'm using sqlite and trying to perform a transactional delete and not getting any errors while running the following code, but it isn't having the desired effect i.e the lines that should be deleted aren't being removed!
I have ran select queries on the db before, while and after but always find the data that should've been deleted.
Here is the code
#try {
if(sqlite3_open([dbPath UTF8String], &db) == SQLITE_OK){
//Db exists and can be open
sqlite3_exec(db, "BEGIN EXCLUSIVE TRANSACTION", 0, 0, 0);
sqlite3_stmt *stmt;
const char *query = [[NSString stringWithFormat:#"DELETE FROM %#
WHERE ? = '?'", table] UTF8String];
if(sqlite3_prepare_v2(db, query, -1, &stmt, NULL)== SQLITE_OK)
{
//integer i = 0
//PrimaryKey contains k primaryKeys
for(int i=0; i<[primaryKey count]; i++){
//i = j < k
sqlite3_bind_text(stmt, 1, (const char *)[parameters UTF8String]
, [parameters lengthOfBytesUsingEncoding:NSUTF8StringEncoding], SQLITE_STATIC);
sqlite3_bind_text(stmt, 2,
(const char *)[[primaryKey objectAtIndex:i] UTF8String]
, [[primaryKey objectAtIndex:i] lengthOfBytesUsingEncoding:NSUTF8StringEncoding ]
, SQLITE_STATIC);
if (sqlite3_step(stmt) != SQLITE_DONE){
NSLog(#"Delete commit failed. Error %s", sqlite3_errmsg(db));
return NO;
}
if(sqlite3_reset(stmt)!= SQLITE_OK){
NSLog(#"SQL error %s", sqlite3_errmsg(db));
return NO;
}
}
//i = k
}
if(sqlite3_exec(db, "COMMIT TRANSACTION", 0, 0, 0) != SQLITE_OK){
NSLog(#"SQL error %s", sqlite3_errmsg(db));
return NO;
}
sqlite3_finalize(stmt);
sqlite3_close(db);
return YES;
}
NSLog(#"SQL error %s", sqlite3_errmsg(db));
sqlite3_close(db);
return NO;
}
#catch (NSException *exception) {
NSLog(#"%#", [exception reason]);
return NO;
}
Any help or tips from those more used to using transactions in sqlite would be most appreciated.
You are not checking the result of
sqlite3_exec(db, "BEGIN EXCLUSIVE TRANSACTION", 0, 0, 0);
There are extra quotes in
WHERE ? = '?'
You don't need those quotes with sqlite3_bind_text
I suspect you are trying to bind a column name for parameter 1. You cannot do this in SQLite. sqlite3_prepare_v2 needs to know the column names; your query is just doing a compare of the name of the column against parameter 2.

memory leak in sqlite3 in ios

Its location based application and application crash after 5-6
Minutes. When I run the app using performance tools, leaks, I keep
finding that all of my classes leak memory. i am calling this
function second by second. Please properly close my database or
sqlite3. Thanks.
- (void)locationUpdate:(CLLocation *)location {
for (int i=0; i<[arrayAlerts count]; i++) {
Msg=[DatabaseManager GetSelectedMessage:[NSString stringWithFormat:#"select text_msg from TextMsgTemplate where msg_id=%d",ObjAlertInfo.msgID]];
}
in Databasemanger
+(NSString *)GetSelectedMessage:(NSString *)SQL
{
NSString *msg;//=[[[NSString alloc]init] autorelease];
msg=nil;
sqlite3_stmt *dataset=nil;
dataset=[DatabaseManager getMessages:SQL];
while ((sqlite3_step(dataset)==SQLITE_ROW) ) {
msg=[NSString stringWithUTF8String:(char *)sqlite3_column_text(dataset, 0)];
}
sqlite3_finalize(dataset);
dataset = nil;
return msg;
}
+(sqlite3_stmt *)getMessages:(NSString *)SQL
{
sqlite3_stmt *dataset=nil;
sqlite3_stmt *finaldataset=nil;
if (sqlite3_open([[self getdatabasePath] UTF8String], &database) == SQLITE_OK ) {
if (sqlite3_prepare_v2(database, [SQL UTF8String], -1, &dataset, NULL) != SQLITE_OK)
{
NSLog(#"error get message");
return nil;
}else{
finaldataset= dataset;
}
}
sqlite3_close(database);
return finaldataset;
}

iOS SQLite FMDB Transactions.. Correct usage?

I'm just going to try out using transactions with the FMDB SQLite iOS wrapper.
The documentation is a little vague on transactions but from having a quick look at some functions I have come up with the following logic:
[fmdb beginTransaction];
// Run the following query
BOOL res1 = [fmdb executeUpdate:#"query1"];
BOOL res2 = [fmdb executeUpdate:#"query2"];
if(!res1 || !res2) [fmdb rollback];
else [fmdb commit];
You could also use FMDatabaseQueue to handle your transactions, which is part of fmdb:
[queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
[db executeUpdate:#"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]];
[db executeUpdate:#"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]];
[db executeUpdate:#"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]];
if (whoopsSomethingWrongHappened) {
*rollback = YES;
return;
}
// etc…
[db executeUpdate:#"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:4]];
}];
Documentation
I wouldn't try to do the second update if the first failed.
bool ret = false;
[fmdb beginTransaction];
ret = [fmdb executeUpdate:#"query1"];
if (ret)
{
ret = [fmdb executeUpdate:#"query2"];
if (!ret)
{
// report error 2
}
}
if(ret)
{
if (![fmdb commit])
{
// panic!
}
}
else
{
if (![fmdb rollback])
{
// panic!
}
}
For paranoid robustness you should have a try ... catch block in case anything throws an exception. If you do, you can use it to your advantage.
[fmdb beginTransaction];
#try
{
if (![fmdb executeUpdate:#"query1"])
{
// report error
#throw someExcpetion;
}
if (![fmdb executeUpdate:#"query2"])
{
// report error
#throw someExcpetion;
}
[fmdb commit]
}
#catch(NSException* e)
{
[fmdb rollback];
// rethrow if not one of the two exceptions above
}
Swift way:
let queue = FMDatabaseQueue(path: databaseURL.path!)
queue.inTransaction() {
db, rollback in
result = db.executeUpdate("INSERT INTO client VALUES (NULL, ?)", client.name ?? "")
if result {
client.ID = Int(db.lastInsertRowId())
} else {
rollback.initialize(true)
print("\(__FUNCTION__) insert into table failed: \(db.lastErrorMessage())")
}
}
queue.close()
It seems like a valid usage scenario, to which I might add outputting the values of -lastErrorMessage and -lastErrorCode before you perform a rollback, so that you get a sense of what exactly went wrong.
Better yet, make those calls after each -executeUpdate, so you'll know if an error occured after each statement:
[fmdb beginTransaction];
// Run the following query
BOOL res1 = [fmdb executeUpdate:#"query1"];
if (!res1) {
NSLog(#"Error %d - %#", [fmdb lastErrorMessage], [fmdb lastErrorCode]);
}
BOOL res2 = [fmdb executeUpdate:#"query2"];
if (!res2) {
NSLog(#"Error %d - %#", [fmdb lastErrorMessage], [fmdb lastErrorCode]);
}
if(!res1 || !res2) [fmdb rollback];
else [fmdb commit];