13

I have written a query that fires on viewWillAppear. On coming back to that screen with a quick navigation is resulting in crash at sqlite3_prepare_v2.
What I am assuming is that the crash is due to the previous query not finalized.
I am calling the database operation using dispatch_async as I am first updating remote data and then saving it in database.
The code looks like this :

dispatch_queue_t myQueue = dispatch_queue_create("My Queue",NULL);

                 dispatch_async(myQueue, ^{
                    [self parseActivityResponse:data];  // Sqlite operation here
                     dispatch_async(dispatch_get_main_queue(), ^{
                         // Update the UI
                         if(loaderImageview)
                            [loaderImageview removeFromSuperview];
                         [tableActivities reloadData];

                     });
                 }); 

The query is as follows :

- (NSMutableArray*)GetAllInvitations:(NSString*)ActivityId
{
    NSMutableArray *arrInvitations = [[NSMutableArray alloc]init];

    sqlite3_stmt *statement = nil;

    @try {
        const char *sql = "SELECT * FROM Invitation where ActivityId = ?";

        if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) == SQLITE_OK) {

            sqlite3_bind_text(statement, 1, [ActivityId UTF8String], -1, SQLITE_TRANSIENT);
            while (sqlite3_step(statement) == SQLITE_ROW) {

                char *dbString;

                Invitation *invitation = [[Invitation alloc]init];

                dbString = (char *)sqlite3_column_text(statement, 0);
                NSString *Id = (dbString) ? [NSString stringWithUTF8String:dbString] : @"";
                [invitation setId:Id];

                dbString = (char *)sqlite3_column_text(statement, 1);
                NSString *childName = (dbString) ? [NSString stringWithUTF8String:dbString] : @"";
                [invitation setChildName:childName];

                dbString = (char *)sqlite3_column_text(statement, 2);
                NSString *response = (dbString) ? [NSString stringWithUTF8String:dbString] : @"";
                [invitation setResponse:response];

                dbString = (char *)sqlite3_column_text(statement, 3);
                NSString *invitationId = (dbString) ? [NSString stringWithUTF8String:dbString] : @"";
                [invitation setInvitationId:invitationId];

                dbString = (char *)sqlite3_column_text(statement, 4);
                NSString *isCoGaurdian = (dbString) ? [NSString stringWithUTF8String:dbString] : @"";
                [invitation setIsCoGaurdian:isCoGaurdian];

                dbString = (char *)sqlite3_column_text(statement, 5);
                NSString *activityId = (dbString) ? [NSString stringWithUTF8String:dbString] : @"";
                [invitation setActivityId:activityId];

                dbString = (char *)sqlite3_column_text(statement, 6);
                NSString *picture = (dbString) ? [NSString stringWithUTF8String:dbString] : @"";
                [invitation setPicture:picture];

                dbString = (char *)sqlite3_column_text(statement, 7);
                NSString *invitationUserId = (dbString) ? [NSString stringWithUTF8String:dbString] : @"";
                [invitation setInvitedUserId:invitationUserId];

                dbString = (char *)sqlite3_column_text(statement, 8);
                NSString *roleId = (dbString) ? [NSString stringWithUTF8String:dbString] : @"";
                [invitation setRoleId:roleId];

                dbString = (char *)sqlite3_column_text(statement, 9);
                NSString *status = (dbString) ? [NSString stringWithUTF8String:dbString] : @"";
                [invitation setStatus:status];

                [arrInvitations addObject:invitation];
            }
        }
        else
        {
            NSLog(@"Failed to create table: %s", sqlite3_errmsg(database));
        }


    }
    @catch (NSException *exception) {

        NSLog(@"Error in GetAllInvitations : %@", exception.description);
    }
    @finally {

        sqlite3_finalize(statement);
    }

    return arrInvitations;
}  


- (NSMutableArray*)GetAllActivities{

    NSMutableArray *arrProjects = [[NSMutableArray alloc]init];

    sqlite3_stmt *statement = nil;

    @try {
        const char *sql = "SELECT * FROM Activity";

        if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) == SQLITE_OK) {
            NSLog(@"success");
            while (sqlite3_step(statement) == SQLITE_ROW) {

                .........

                activity.arrInvitations = [self GetAllInvitations:activityId];

                [arrProjects addObject:activity];
            }
        }
        else
        {
            NSLog(@"Prepare-error #%i: %s", sqlite3_prepare_v2(database, sql, -1, &statement, NULL), sqlite3_errmsg(database));
        }
    }
    @catch (NSException *exception) {

        NSLog(@"Error in GetAllActivities : %@", exception.description);
    }
    @finally {

        sqlite3_finalize(statement);
    }

    return arrProjects;
}

I am calling GetAllActivities from viewWillAppear :

-(void) viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self reloadData];
}

-(void)reloadData
{
    [self getRemoteActivities:^(BOOL finished) {
        if(finished){
            if([TGProjectHandler hasExistingSession])
            {
                [self getActivities];

            }
            else
                [self login];   
        }
    }];
}

Your thoughts and solution ?

Nitish
  • 13,845
  • 28
  • 135
  • 263
  • 1
    So when the app crashes (or gets stuck), do you have sqlite3 calls on the same database connection from multiple threads? If that's the case there is possibly a rather simple solution. Also - do you have any info about the crash from the debug console? – Artal Jul 22 '15 at 21:11
  • 1
    @Artal : Database calls being in a singleton class, I think yes they share the same database connection. Nothing appears on the console. – Nitish Jul 23 '15 at 05:42
  • 1
    and are the calls being made by different threads at the same time? – Artal Jul 23 '15 at 05:46
  • 1
    @Artal : By the same thread. – Nitish Jul 23 '15 at 06:21
  • 1
    Okay, I thought I might have a direction in case you had multiple threads accessing the DB. Sorry.. – Artal Jul 23 '15 at 08:16
  • try to initialise sqlite db with SQLITE_CONFIG_SERIALIZED – naresh Jul 24 '15 at 06:08
  • Where is the `getAllActivities` method being called? Also, be sure to call `[super viewWillAppear:animated];`. – ChrisHaze Jul 26 '15 at 11:17
  • @ChrisHaze : It's called from viewWillAppear. I have called [super viewWillAppear:animated]; I have updated code in my question. Please check. – Nitish Jul 26 '15 at 15:20
  • @Nitish : make sure all the methods have completed before dismissing the, what I am guessing is a loading page, view controller. If they aren't, it will result in the viewWillAppear being called again. – ChrisHaze Jul 26 '15 at 17:35
  • @Nitish checkout the answer posted on [this](http://stackoverflow.com/questions/16071503/how-to-tell-when-uitableview-has-completed-reloaddata) tableview reloaddata question. – ChrisHaze Jul 26 '15 at 17:38

3 Answers3

6

Create a serial queue with:

dispatch_queue_t myQueue = dispatch_queue_create("My Queue", DISPATCH_QUEUE_SERIAL);

and that should manage sequential access to the database.

trojanfoe
  • 120,358
  • 21
  • 212
  • 242
  • 1
    This delayed the crash to few more navigations but then I again received the crash. – Nitish Jul 16 '15 at 09:04
  • @Nitish Hmmm OK. You have no error reporting in your sqlite3 code. Please update it and see if it gives you any clues. If it doesn't I would like to see the stacktrace. – trojanfoe Jul 16 '15 at 09:09
  • Done so but the error is not gettable. It is stuck at sqlite3_prepare_v2. I have updated my code in the question. – Nitish Jul 16 '15 at 10:12
  • OK looking better; two comments however: That method isn't being called anywhere and you can get rid of the Objective-C exception handler. That won't play a part and Objective-C exceptions are pretty much fatal. However bottom line; I don't know why it's crashing. – trojanfoe Jul 16 '15 at 10:16
  • I am now able to at least come closer to the issue. Not getting the reason though. This crash comes only in case of child tables. I have now added the code from where GetllInvitation is called. Please check. – Nitish Jul 16 '15 at 10:18
  • It is being called at activity.arrInvitations = [self GetAllInvitations:activityId]; – Nitish Jul 16 '15 at 10:26
4

One thing i notice is in viewWillAppear you call method getActivities but you paste a body of GetAllActivities. It's kind of a long shot but you sure you call your GetAllActivities method properly? And from where GetAllInvitations method get called?

Jakub
  • 13,712
  • 17
  • 82
  • 139
0

Be sure to transmit the good sqlite3 * (and check it out). That's how I solved the problem