Sqlite3 database in iPhone gets locked – how to avoid?

I have a query that performs a search on an Sqlite3 DB. It does nothing but read using a reader.
For each found match it calls a callback to the UI which updates a result view.

While this search is running, I hit a button in the UI which will perform some other action in a new thread. In the end it is supposed to remove the search controller’s view and show a new controller.

  • Have a dedicated UIViewController for each RootElement in MonoTouch.Dialog?
  • Does UIButton become disabled when its alpha is set to 0.0?
  • Getting java.net.MalformedURLException in MonoDeveloper
  • HttpListener with HTTPS on MonoTouch
  • Android Toast equivalent in iOS
  • Add view over tableview (UITableViewController)
  • However, at some point the the triggered action wants to write to the databse. And there it just hangs and eventually I will see an exception that the DB is locked.

    Interesting is also, that the search reader does not continue either, it is a deadlock.

    Do I have to open the database in some special way to support multithreaded usage? What would the constructor for the connection be?

    2 Solutions Collect From Internet About “Sqlite3 database in iPhone gets locked – how to avoid?”

    MonoTouch 5.1+ provides an API to let you select the threading model to be used with SQLite.

    SqliteConnection.SetConfig (SQLiteConfig.MultiThread);
    

    This maps to some of the connection options of SQLite library.

    UPDATE: If you’re using an earlier version of MonoTouch (e.g. between 4.2 and 5.0.x) you can use the binary attached to the bug report #652 (follow the instructions) or copy-paste the patch (p/invoke and enum) inside your own application.

    I’m not sure I interpret your description correctly, but the way you describe it, it sounds to me that your “reader” steps through the database row-by-row and every time it finds a result it does a callback to a callback function? Is this correct?

    If that is the case, you might repeatedly lock your DB, and your search will be slow.

    The right way is to extract all matches into a result set in one single query – once that query is completed the lock will be released and you have a result set from SQL that contains only the matching rows.

    You let SQLite create a result set like this by using a query of the type “SELECT * FROM tablename WHERE columnX LIKE ‘%searchstring%'”

    (or similar, depending on your search criteria)

    This will create a result set with all matches in the database and then release the database lock. Then you can step through the result and create objects and put into and NSArray that is connected to your UI view.

    NSArray retval = [NSMutableArray array];
    
    //Create a query
    NSString *query = [NSString stringWithFormat:@"SELECT * FROM %@ WHERE %@ LIKE %@", 
    tableName, columnName, searchString];
    
    sqlite3_stmt *statement;
    
    //Database locks here
    if (sqlite3_prepare_v2(_database, [query UTF8String], -1, &statement, nil) 
    == SQLITE_OK) {
        //Database should unlock here, the query is finished
        while (sqlite3_step(statement) == SQLITE_ROW) {
            char *nameChars = (char *) sqlite3_column_text(statement, 0);
            NSString *name = [NSString stringWithUTF8String:nameChars];
            SomeClass *info = [[SomeClass alloc] initWithName:name];
    
            /* Extract other columns and put in your object */
    
            [retval addObject:info];
            [info release];
        }
    sqlite3_finalize(statement);
    } else {
        NSLog(@"SQL-statement failed");
    }
    

    Doing this way there shouldn’t be a problem to write to the DB when it is necessary. Only perform new queries to the DB when it’s absolutely necessary, for example when your search criteria changed or the content in the DB has been updated.

    Do not run repeated queries to a DB that has not changed, or with unchanged search criteria.