I am trying to update some values to my database using async task but I get this error:
Values not inserted. Error: SQL logic error or missing database
Here is the code I use to update the db:
- (void)persistResponseData:(NSDictionary *)data database:(DataBaseHandler *)database completion:(void (^)(NSError *error))completion {
NSDictionary *contentData = [data objectForKey:#"contentdata"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Process Contents
NSArray *contents = [contentData objectForKey:#"contents"];
if (contents != nil) {
[self storeData:contents inTable:#"contents" database:database withCustomIdentifier:nil];
}
});
}
The error occurs on this line of code with error code EXC_BAD_ACCESS:
if (sqlite3_prepare_v2(_database, [query UTF8String], -1, &statement, nil)==SQLITE_OK)
which is called when I try to confirm if a record exists or not, inside the storeData function.
If I remove the asynchronous part
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
then the code runs without any problem and the database gets updated.
Any idea how to resolve this ?
Related
I have implemented NSOPERATIONQUEUE By sending request and i can able to download response from server but the problem is i cant able to parallely process the sqlite to insert/update the response,i am getting Database Locked Error.So Can anyone suggest me a solution to work with database for insertion/updation of my downloaded response??
I have created a NSOperation Class as PageLoadOperation and i will pass request 7 times to that NSOperation class and add it to queue as below
PageLoadOperation *pageLoad=[[PageLoadOperation alloc]initWithRequest:theRequest];
[queue addOperation:pageLoad];
In Main i can able to send request by below NSURLConnection
[NSURLConnection sendAsynchronousRequest:theRequest
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
[self openConnection];
[self doSomethingWithData:data];
[self closeConnection];
}];
and in doSomethingWithData method i will insert/update the response by converting data to NSString I have separately created method for opening the database connection and once insertion/updation completed i will closeconnection by following methods
-(BOOL)openConnection
{
database=nil;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
documentsDir = [paths objectAtIndex:0];
if(sqlite3_open([[documentsDir stringByAppendingPathComponent:#"DataStore"] UTF8String], &database)==SQLITE_OK)
{
NSLog(#"DB Connection Established successfully");
return YES;
}
else
{
NSLog(#"DB not connected");
return NO;
}
}
-(BOOL)closeConnection
{
NSLog(#"%#",documentsDir);
if(sqlite3_close(database)==SQLITE_OK)
{
NSLog(#"DB Connection closed successfully");
return YES;
}
else
{
NSLog(#"Error Failed to close DB because %s",sqlite3_errmsg(database));
return NO;
}
}
My Insertion Method is :
- (void) addToDatabase :(NSString*) sqlstmt :(NSArray *)params
{
sqlite3_stmt *addStmt;
if(sqlite3_prepare_v2(database, [sqlstmt UTF8String], -1, &addStmt, NULL) != SQLITE_OK)
NSAssert1(0, #"Error while creating add statement. '%s'", sqlite3_errmsg(database));
else
{
for (i=1; i<=[params count]; i++)
{
sqlite3_bind_text(addStmt, i,[[params objectAtIndex:i-1] UTF8String], -1, SQLITE_TRANSIENT);
}
}
if(SQLITE_DONE != sqlite3_step(addStmt))
NSAssert1(0, #"Error while inserting data. '%s'", sqlite3_errmsg(database));
else
NSLog(#"%lld",sqlite3_last_insert_rowid(database));
sqlite3_finalize(addStmt);
}
While adding to database i am getting database locked error because while one response is open the database and inserting into database parallely another response try to open the database connection,so how can i resolve this problem?
I need to store user doubleTap count in db. Its working fine.
If i am doing very fast double tapping to insert, issues will appear (database is locked, no such table: and out of memory). How to handle that issue?
Thanks in Advance
I tried this, FYI:
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(void)
{
#try {
if([self openAppDatabase] == YES) //If DB open success...
{
sqlite3_stmt *statement = nil;
NSString *queryString = [NSString stringWithFormat:#"INSERT INTO %# (%#,%#) values (?,?)",Tables_Count,lkt_TO,ttt_TIME];
BOOL prepareStatementResult = sqlite3_prepare_v2(databaseObj, [queryString UTF8String], -1, &statement, NULL);
if(prepareStatementResult == SQLITE_OK) {
sqlite3_bind_text(statement, 1, [contactNo UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(statement, 2, [sentDate UTF8String], -1, SQLITE_TRANSIENT);
if (sqlite3_step(statement) != SQLITE_DONE) {
DLog(#"Can't insert ---- Error");
}
}
else {
// In the database cannot be opened then show the error message on the debugger.
DLog(#"%s", sqlite3_errmsg(databaseObj));
}
// Release the compiled statement from memory.
sqlite3_finalize(statement);
}
[self closeAppDatabase]; //If DB not opened will close it here...
}
#catch (NSException *exception) {
DLog(#"#Exception --- %#",exception.reason);
}
}
The dispatch queue you are using is a concurrent queue, not a series queue. The result is that too many requests are being made to the dB at a time. You should be ok if you change your code to use dispatch_barrier_async but I've never used it on system queues.
Additionally, you're code could use some cleanup. The try is unnecessary since this code does not throw exceptions. Contrary to Java, Objective Chicago very rarely throws exceptions.
Lastly, you may consider using a Objective C SQLite wrapper, like FMDatabase. It cleans things up quite a bit.
I've created this method in my iOS to call to FMDatabaseQueue:
-(void) enqueueSelectStatement:(NSString*)selectStatement withArguments:(NSArray*)args {
NSLog(#"Checkpoint 1");
[self.dbQueue inDatabase:^(FMDatabase *db) {
FMResultSet *result = [db executeQuery:selectStatement withArgumentsInArray:args];
NSLog(#"Checkpoint 2");
}];
NSLog(#"Checkpoint 3");
}
When I call this method with:
NSString *query = #"SELECT * FROM mytable WHERE id = ?";
[self enqueueSelectStatement:query withArguments:#[1]];
I get Checkpoint 1 in my terminal output, but nothing else. The app does not crash and continues to run, but the method/block apparently dies after Checkpoint 1
Is the method just stopping, and if so, why?
I download images from Parse, and sometimes the fetch result get more errores than images get. That's the code I use to download the images:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
*PFQuery *query = [PFQuery queryWithClassName:albumToLoad];
NSArray *objectsPFFounded=[NSArray arrayWithArray:[query findObjects]];
int count= [objectsPFFounded count];
int i=0;
while (i<count){
PFFile *userImageFile = [objectsPFFounded objectAtIndex:i][#"imageFile"];
[userImageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
if (!error) {
if (!self.imagesGallery) {
self.imagesGallery = [NSMutableArray arrayWithObject:[UIImage imageWithData:imageData]];
}else{
[self.imagesGallery addObject:[UIImage imageWithData:imageData]];
}
}
}];
i++;
}
dispatch_async(dispatch_get_main_queue(), ^{
[self updateCollectionViewWhenBackgroundDone];
});
});
I get as well this error from console:
Could not save HTTP response body to /var/mobile/Applications/D96AFE9B-17EF-4630-8608-423D721394F3/Library/Caches/Parse/PFFileCache/578df836-a12c-4e70-b389-033efa647ee5-28.jpg: Error Domain=NSCocoaErrorDomain Code=516 "The operation couldn’t be completed. (Cocoa error 516.)" UserInfo=0x16ed58a0 {NSSourceFilePathErrorKey=/private/var/mobile/Applications/D96AFE9B-17EF-4630-8608-423D721394F3/tmp/PFHTTPCommand0x16ddd220, NSUserStringVariant=( Move ),
NSFilePath=/private/var/mobile/Applications/D96AFE9B-17EF-4630-8608-423D721394F3/tmp/PFHTTPCommand0x16ddd220, NSDestinationFilePath=/var/mobile/Applications/D96AFE9B-17EF-4630-8608-423D721394F3/Library/Caches/Parse/PFFileCache/578df836-a12c-4e70-b389-033efa647ee5-28.jpg, NSUnderlyingError=0x16ee8350 "The operation couldn’t be completed. File exists"}
Have I to delete temp directory or cache directory before do the objects fetching? How can I solve this?
Thanks
Cocoa error 516 means that a file already exists. In your case, it seems that the image file already exists in the local cache.
You can try to check with isDataAvailable() first before you call getDataInBackgroundWithBlock.
Also, I believe this is warning should be a bug in parse. Be sure you are using the latest version of the parse framework.
In my case, the error occurred in the following case:
I loaded a PFFile the first time. It was loaded from the server, since it was not yet cached. During loading, caching was apparently going on.
While the file was still loading, it was requested again in a different context. Since caching was not yet completed, a server request was done again. This 2nd server request apparently initiated again to cache the file, and iOS complained with error 516 (file exists). The 2nd server request terminated thus with error without returning the file data. I consider this as a Parse bug.
The workaround I used was to store which file requests are under way, and to delay a new request to the same file a little to give the system a chance to complete writing the cache file. This worked.
A better way, of course, were to queue the requests for the same file so that further requests are only executed if the 1st one has completed, so that these requests are served from the cache.
I figured it out. I was trying to reload my Collection View in a incorrect thread. That's how looks my final code. It works like a charm
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
PFQuery *query = [PFQuery queryWithClassName:albumToLoad];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error){
if (objects.count > 0){
for (PFObject *eachObject in objects){
PFFile *userImageFile = [eachObject objectForKey:#"imageFile"];
PFImageView *imageViewParse=[[PFImageView alloc] init];
imageViewParse.file=userImageFile;
[imageViewParse loadInBackground:^(UIImage *image, NSError *error) {
if (!error){
if (!self.imagesGallery) {
self.imagesGallery = [NSMutableArray arrayWithObject:image];
}else{
[self.imagesGallery addObject:image];
}
if ([self.imagesGallery count] == objects.count){
dispatch_async(dispatch_get_main_queue(), ^{
[self updateCollectionViewWhenBackgroundDone];
});
}
}
}];
}
}
}
}];
});
I am developing an app that communicates with a server, and saves to core data.
in appDelegate, I started my singleton object and called useDocument to initialize UIManagedDocument:
- (void)useDocument
{
if (![[NSFileManager defaultManager] fileExistsAtPath:self.database.fileURL.path]){
[self.database saveToURL:self.database.fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:nil];
} else if (self.database.documentState == UIDocumentStateClosed){
[self.database openWithCompletionHandler:nil];
}
}
heres the code upon receiving data from server:
- (void)downloadCompletedWithData: (NSData *)data item: (TPDownloadItem *)finishedItem;
{
// parse data and update core
NSError *error;
id dataObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
if (dataObject && !error){
dispatch_async(dispatch_get_main_queue(), ^{
[User createUserWithInfo:dataObject inManagedContext:self.database.managedObjectContext];
});
}
}
heres the code that writes to core data:
+ (id)createUserWithInfo: (NSDictionary *)userInfo inManagedContext: (NSManagedObjectContext *)aContext
{
NSError *error;
User *aUser = [NSEntityDescription insertNewObjectForEntityForName:ENTITY_NAME inManagedObjectContext:aContext];
NSString *userID = [userInfo objectForKey:USER_ID];
aUser.userID = userID;
aUser.effective = [NSNumber numberWithBool:[[userInfo objectForKey:USER_EFFECTIVE] boolValue]];
aUser.job = [userInfo objectForKey:USER_JOB];
aUser.gender = [userInfo objectForKey:USER_GENDER];
return aUser;
}
The problem is that whenever core data tries to auto save (waited for about 30 sec) the app crashes with the following error:
I tried to call "useDocument" and "createUser" in the main thread using GCD, also override UIDocument "contentsForType" to make sure it happens right after autosave, document state is normal before it saves, and persistence store file is also created.
Does anyone encounter similar situation? any help is very much appreciated!!
managed context is not thread safe, but you can have as many managed contexts as you like, that might have something to do with this. Perhaps remove the dispatch async, you don't need it as you are doing the code on main thread.
I make a background managed context in a data manager class, where I do all my writes and when I need to, I listen do a notification name MANAGEDCONTEXTDIDSAVE and reload if I need to.
Works like a charm.
the problem is sort of fixed, I had to turn off and reset my phone to get it to work...
but still don't know why this could happen