Sqlite inserting issue in iOS - ios

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.

Related

How manage the Tab Bar Item

I have a problem with the tab bar controller. My application is composed of a tab bar controller with two views. In the first I insert a record into a sqlite database, and in the second there is a table view that contains all the database records. I would like that when I insert a new record in the db, the table view was updated. This happens only in the method viewDidLoad, namely only one time. Is there a way to know when the tab bar item changes and then update the table? I had tried to use the properties: create an object of the ViewController class, and access to the property from an other ViewController...but I couldn't.
Thanks
//Insert new record view
sqlite3_stmt *statement;
const char*dbpath=[databasePath UTF8String];
if(sqlite3_open(dbpath, &DB) == SQLITE_OK)
{
NSString *insertSQL=[NSString stringWithFormat:#"INSERT INTO Example(Id, name) VALUES (?,?)"];
const char *insert_stmt=[insertSQL UTF8String];
if(sqlite3_prepare(DB, insert_stmt, -1, &statement, NULL) == SQLITE_OK )
{
sqlite3_bind_text(statement, 1, [id UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(statement, 2, [name.text UTF8String], -1, SQLITE_TRANSIENT);
}
if(sqlite3_step(statement) == SQLITE_DONE)
{
NSLog(#"OK");
}
else
{
NSLog(#"Error");
}
sqlite3_finalize(statement);
sqlite3_close(DB);
}
else
{
NSLog(#"Error");
}
SecondViewController *svc=[SecondViewController alloc] initWithNibname:#"Second" bundle:nil];
[svc.tableView reloadData];
At the end of where you're inserting your record in the db, you could send out a notification that will alert SecondViewController
Start by replacing
SecondViewController *svc=[SecondViewController alloc] initWithNibname:#"Second" bundle:nil];
[svc.tableView reloadData];
With:
[[NSNotificationCenter defaultCenter] postNotificationName:#"DidUpdateDatabase" object:self];
And in viewDidLoad of SecondViewController add this block to listen for the changes:
[[NSNotificationCenter defaultCenter] addObserverForName:#"DidUpdateDatabase"
object:nil
queue:[NSOperationQueue currentQueue]
usingBlock:^(NSNotification *note) {
[self.tableView reloadData];
}];
This is called a notification pattern that allows objects to be decoupled from each other and still hold communication.
EDIT:
Another solution is in SecondViewController's viewDidAppear call [self.tableView reloadData]. This will cause it to reload every time the user goes to that view

Can't open SQLite database due out of memory error

I'm having an issue opening my sqlite database for an iPhone app I'm writing. I thought I followed the tutorials verbatim but for some reason I am getting an "Out of memory" error.
-(NSString *) filepath{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES);
return [[paths objectAtIndex:0] stringByAppendingPathComponent:#"db.sqlite"];
}
-(sqlite3*)openDB{
if(db == NULL){
sqlite3 *newDBConnection;
if(sqlite3_open([[self filepath] UTF8String], &newDBConnection) != SQLITE_OK){
sqlite3_close(db);
NSLog(#"%s SQL error '%s' (%1d)", __FUNCTION__, sqlite3_errmsg(db), sqlite3_errcode(db));
db = NULL;
}
else{
NSLog(#"db opened");
}
}
return db;
}
DB is an ivar and I am calling db = [self openDB]; in the initialization method.
The sqlite3_open is failing because you are using NSDocumentationDirectory instead of NSDocumentDirectory.
The reason you're receiving the "Out of memory" error is that sqlite3_open is updating newDBConnection, but the sqlite3_errmsg is trying to use db (which is still NULL). And whenever you call sqlite3_errmsg with a NULL for the sqlite3 pointer, SQLite somewhat confusingly returns an "Out of memory" message.
Also note that even if you fix the two above issues, note that you are calling sqlite3_errmsg after performing sqlite3_close. Make sure to get your error message before you call sqlite3_close.
E.g. I would suggest:
- (BOOL)openDB {
if (db == NULL) {
int rc;
if ((rc = sqlite3_open([[self filepath] UTF8String], &db)) != SQLITE_OK) {
NSLog(#"%s SQL error '%s' (%d)", __FUNCTION__, sqlite3_errmsg(db), rc);
sqlite3_close(db);
db = NULL;
return false; // open failed
} else {
NSLog(#"db opened");
return true; // open successful
}
}
return true; // already open
}
Note, a minor point, given that sqlite3_open returns the error code, I would just save that directly, rather than calling sqlite3_errcode to get the code that was just returned.

Crash :Critical failure: the LastResort font is unavailable in ios7

Iam inserting the contacts from the address book in to the sqlite3 database. for only some contacts my app is getting crashed at
sqlite3_finalize(statement)
this statement. and this is happening in ios7. The crash log is as follows
"Critical failure: the LastResort font is unavailable. in ios"
NSString *insertSQL = [NSString stringWithFormat:#"insert into vcards (some thing data)",(something data)];
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(database, insert_stmt,-1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
NSLog(#"Record inserted");
}
else {
NSLog(#"Record inserted failed");
}
sqlite3_finalize(statement);
sqlite3_close(database);
please any one suggest me how to fix this crash.
This has nothing to do with your contacts but to do with the fonts when transitioning from ios 6 to ios 7. Delete all the fonts in your project and then carefully add one by one to your .plist testing each time before adding the next until you find the one that is causing the problem.
Have a look at this post from someone who also had the same problem as you and the person's answer.
Person have the same problem as you

Running an iOS SQLite Updates

I'm using this to do an Update statement in a test iOS App, the log outputs 'Success' but theres no change in the database. Could anyone point me in the right direction? It'd be greatly appreciated.
I'm brand new to this so please forgive my probably appallingly written code.
#try {
NSFileManager *fileMgr = [NSFileManager defaultManager];
NSString *dbPath = [[[NSBundle mainBundle] resourcePath ]stringByAppendingPathComponent:#"chars.sqlite3"];
BOOL success = [fileMgr fileExistsAtPath:dbPath];
if(!success)
{
NSLog(#"Cannot locate database file '%#'.", dbPath);
}
if(!(sqlite3_open([dbPath UTF8String], &db) == SQLITE_OK))
{
NSLog(#"An error has occured: %s", sqlite3_errmsg(db));
}
const char *sql = "UPDATE characters SET level = 'testing' WHERE id='1'";
sqlite3_stmt *sqlStatement;
if(sqlite3_prepare(db, sql, -1, &sqlStatement, NULL) != SQLITE_OK)
{
NSLog(#"Problem with prepare statement: %s", sqlite3_errmsg(db));
}else{
if (sqlite3_step(sqlStatement) == SQLITE_DONE){
NSLog(#"Success");
}
}
sqlite3_finalize(sqlStatement);
}
#catch (NSException *exception) {
NSLog(#"Problem with prepare statement: %s", sqlite3_errmsg(db));
}
#finally {
sqlite3_close(db);
}
The problem is that you're opening the database from the bundle, which is read-only. You should
Check for the existence of the database in the Documents folder.
If not there, copy the database from the bundle to the Documents folder.
Now, open the database from the Documents folder.

Sqlite in iOS memory issues

I spent hours trying to fix that but I've just given up; I have no idea what's wrong.
In my app, I do nested SQL operations to set all my objects correctly. For some reason, sometimes the sqlite3 objects do not get release properly, causing the memory to go up the roof. I understand it is a problem with using correctly sql3_close and sql3_finalize. However, as you will see, I think I have used them correctly.
Here is the source of the problem:
- (NSArray *) getAllSessions {
if (sqlite3_open(dbPath, &db) == SQLITE_OK) {
if (sqlite3_prepare_v2(db, query_stmt, -1, &statement, NULL) == SQLITE_OK) {
while (sqlite3_step(statement) == SQLITE_ROW) {
//I found out that doing something like that
//toAdd.in_loc = [self getIndoorLocationWithId:[NSNumber numberWithInt:(int)sqlite3_column_int(statement, 6)]];
//messes the memory up all the time
//but doing that works OK:
NSNumber *_id = [[NSNumber alloc] initWithInt:(int) sqlite3_column_int(statement, 5)];
toAdd.out_loc = [self getOutdoorLocationWithId:_id];
[_id release];
//So I did the same with the second one, but this one messes memory up:
NSNumber *id2 = [[NSNumber alloc] initWithInt:(int)sqlite3_column_int(statement, 6)];
toAdd.in_loc = [self getIndoorLocationWithId:id2];
[id2 release];
}
sqlite3_finalize(statement);
}
sqlite3_close(db);
}
}
So here is the one that messes memory up:
- (IndoorLocation *) getIndoorLocationWithId:(NSNumber *) locId {
if (sqlite3_open([databasePath UTF8String], &db) == SQLITE_OK) {
if (sqlite3_prepare_v2(db, query_stmt, -1, &statement, NULL) == SQLITE_OK) {
while (sqlite3_step(statement) == SQLITE_ROW) {
//if I comment the thing below it works
NSNumber *_id = [[NSNumber alloc] initWithInt:(int) sqlite3_column_int(statement, 5)];
toReturn.outLoc = [self getOutdoorLocationWithId:_id];
[_id release];
}
sqlite3_finalize(statement);
}
sqlite3_close(db);
}
}
So in the one that messes memory up, I use exactly the same function as the first time (getOutdoorLocationwithId), in the same way but it doesn't work, sqlite3 objects don't get released properly.
I hope you understand my problem, this is driving me nuts!
In a single user app like this, there's no need to continually open and close. This is not like a server based app where you have connection pooling with multiple users where holding the connection can defeat the pool and hurt scalability. In a phone app open it and hold it open. close it when you're done. At a minimum, do that within a recursive call.
To make it worse, you're opening and closing within recursive calls - just leave it open.
Also:
You're not checking the return codes of finalize
You're not checking the return codes of close.
You're preparing (compiling) the statments but you're not saving them off - call reset on the saved statement and execute it again.
Consider using FMDB - it's a good wrapper.
BTW, here's a richer and more durable close but don't use it on every call. Close it when you're ending or your app is going into the background ... This is mine which is similar to what fmdb does.
- (void)close
{
if (_sqlite3)
{
ENInfo(#"closing");
[self clearStatementCache];
int rc = sqlite3_close(_sqlite3);
ENDebug(#"close rc=%d", rc);
if (rc == SQLITE_BUSY)
{
ENError(#"SQLITE_BUSY: not all statements cleanly finalized");
sqlite3_stmt *stmt;
while ((stmt = sqlite3_next_stmt(_sqlite3, 0x00)) != 0)
{
ENDebug(#"finalizing stmt");
sqlite3_finalize(stmt);
}
rc = sqlite3_close(_sqlite3);
}
if (rc != SQLITE_OK)
{
ENError(#"close not OK. rc=%d", rc);
}
_sqlite3 = NULL;
}
}
I met the same issue.
When i use and open FMDB, it works OK. but in other callbacks it failed and threw exception of "Closing leaked statement." Finally I found that's because i kept the pointer from [FMDatabase databaseWithPath:dbPath] and the pointer is a autorelease object. I fixed the issue by this:
FMDatabase *db = [[FMDatabase databaseWithPath:dbPath] retain];
and when you want to close the database:
db [close];
[db release];
db = nil;
By this, you don't always open and close database for each operation, the should be a manager object accounting for it. When the manager starts, the database is open always until the manager stops.

Resources