out of memory when reinstalling aplication - ios

I'm desperate.
I create an application using sqlite an it was working fine until I submit an update to the apple store.
when my application is reinstalled and I tried to open a db connection I got an "out of memory" issue.
I've tried lots of stuff to solve this issue, but until today I was unable to solve this.
I use the code below to open db's connection:
-(Boolean)openConectionSQLiteDb
{
if (sqlite3_open([[GeneralFunction getInfoFromUserDefault:#"dbpath"] UTF8String], &db) != SQLITE_OK)
{
NSLog(#"Failed to open database!");
NSLog(#"%s",sqlite3_errmsg(statement));
//[GeneralFunction setDb:[[DbInteraction alloc] init:[GeneralFunction getDbName]]];
{
NSLog(#"Failed to open database! >>%#",[GeneralFunction getInfoFromUserDefault:#"dbpath"]);
// NSLog(#"222%s",sqlite3_errmsg(statement));
}
sqlite3_finalize(statement);
return false;
}
else
{
sqlite3_exec(db, "PRAGMA foreign_keys = on", NULL, NULL, NULL);
return true;
}
return false;
}
And the code below is used to insert date on the database:
-(Boolean)insertOrReplace:(NSDictionary *)elementoToInsert inTable:(NSString *)tableName
{
if([self openConectionSQLiteDb])
{
NSString * insert = [self createInsertStringWith:elementoToInsert in:tableName];
insert = [insert stringByReplacingOccurrencesOfString:#"INSERT" withString:#"INSERT OR REPLACE "];
const char *insert_stmt = [insert UTF8String];
sqlite3_prepare_v2(db, insert_stmt,
-1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
sqlite3_finalize(statement);
sqlite3_close(db);
// NSLog(#"FECHOU");
return true;
}
NSLog(#"INSERT ERROR %s",sqlite3_errmsg(db));
sqlite3_finalize(statement);
}
sqlite3_close(db);
NSLog(#"FECHOU");
return false;
}
I'm also use this code to copy the Db file to a writable path when it necessary:
-(id)init:(NSString *) dbName
{
self = [super init];
nomeDb = dbName;
BOOL success;
NSFileManager *filemngr = [[NSFileManager alloc] init];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [paths objectAtIndex:0];
NSString *writableDbPath = [documentDirectory stringByAppendingPathComponent:nomeDb];
// NSLog(#"%#",[GeneralFunction getInfoFromUserDefault:#"dbpath"]);
NSString * lastPath =[GeneralFunction getInfoFromUserDefault:#"dbpath"];
success = [filemngr fileExistsAtPath:lastPath];
if(!success)
{
NSLog(#"Arquivo não existe nesse Directorio >%#",lastPath);
success = [filemngr fileExistsAtPath:writableDbPath];
if(!success)
{
NSLog(#"Arquivo não existe nesse Directorio >%#",writableDbPath);
NSString *defaultDbPath = [[[NSBundle mainBundle] resourcePath]stringByAppendingPathComponent:nomeDb];
success = [filemngr copyItemAtPath:defaultDbPath toPath:writableDbPath error:&error];
NSLog(#"Copiou Para :%#",writableDbPath);
if (!success)
{
NSLog(#"Arquivo não existe nesse Directorio pela seguanda vez >%#",writableDbPath);
//[status setText:[error localizedDescription]];
/* UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error!"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];*/
}
else
{
NSLog(#"Achou 2");
NSMutableDictionary * dic = [[NSMutableDictionary alloc] init];
// NSLog(#"%#",writableDbPath);
[dic setObject:writableDbPath forKey:#"dbpath"];
[GeneralFunction saveTeacherInfo:dic];
writablePath = writableDbPath;
}
}
else
{
NSLog(#"Banco esta em %#",writableDbPath);
}
}
else{
NSLog(#"Achou");
}
// [bd executeUpdate:#"PRAGMA foreign_keys=ON"];
return self;
}
So when I install the application for the first time, it works fine. But if I run the applications again (It happens when I submit an update too) I got the LOG error "Failed to open database out of memory".
Someone please help me, I'm almost getting fired.
PS: I'm sorry for my terrible English. ;D

sqlite reports "out of memory" errors incorrectly at times. Usually if this occurs after an app update, it's because you have a bug in your sqlite access code. If your database schema has changed between app versions, but you didn't make sure that your database calls are backwards-compatible, you will frequently see this error.
If your CREATE TABLE statements aren't followed by ALTER TABLE statements to bring your schema up to speed when the CREATE fails because the table already exists, you run the risk of making database calls that are not backwards-compatible.

YEAH GUYS, finally I could solve my issue.
It was happening because when I was checking if there was a Database already copied in my writable path, in the TRUE condition I forgot to update my internal path that refers to the DB. For that reason when I was trying to open a connection, it was returning me the error because I was looking for the database in wrong place.

Related

Implementation of NSOperationQueue by sending request & Storing response in Database parallely

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?

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.

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.

siphon calling doesn't work - pjsip

So I have a compiled and running Siphon app but it just won't make the calls.
I get:
registration error - default error message.
Full error is this:
15:04:02.032 pjsua_call.c Making call with acc #0 to sip:6476805821#voip5-2.acanac.com
15:04:02.032 pjsua_call.c .Unable to make call because account is not valid: Invalid operation (PJ_EINVALIDOP) [status=70013]
15:04:05.580 call.m Error making call: Invalid operation (PJ_EINVALIDOP) [status=70013]
But when I use the same account on a different SIP app, it works perfectly fine.
When pjsip calls sip_dial_with_uri(_sip_acc_id, [url UTF8String], &call_id);
_sip_acc_id is 0 since I believe it's the 0th account that's in the settings for siphon.
url is the correct phone number I'm trying to dial but shows something like:
sip:62304892#url.com
and call id is just a reference so I dunno if it's important.
When I look at other voip apps, they have a registration process. Where you enter you username, password, and sip server domain or ip.
For Siphon, this is done in the settings file. However, if "register or login" is done in Siphon's code or not, I'm not sure.
Could that be the problem?
This is the code that tries to make an actual call:
/** FIXME plutôt à mettre dans l'objet qui gère les appels **/
-(void) dialup:(NSString *)phoneNumber number:(BOOL)isNumber
{
pjsua_call_id call_id;
pj_status_t status;
NSString *number;
UInt32 hasMicro, size;
// Verify if microphone is available (perhaps we should verify in another place ?)
size = sizeof(hasMicro);
AudioSessionGetProperty(kAudioSessionProperty_AudioInputAvailable,
&size, &hasMicro);
/*if (!hasMicro)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"No Microphone Available", #"SiphonApp")
message:NSLocalizedString(#"Connect a microphone to phone", #"SiphonApp")
delegate:nil
cancelButtonTitle:NSLocalizedString(#"OK", #"SiphonApp")
otherButtonTitles:nil];
[alert show];
[alert release];
return;
}*/
if (isNumber)
number = [self normalizePhoneNumber:phoneNumber];
else
number = phoneNumber;
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"removeIntlPrefix"])
{
number = [number stringByReplacingOccurrencesOfString:#"+"
withString:#""
options:0
range:NSMakeRange(0,1)];
}
else
{
NSString *prefix = [[NSUserDefaults standardUserDefaults] stringForKey:
#"intlPrefix"];
if ([prefix length] > 0)
{
number = [number stringByReplacingOccurrencesOfString:#"+"
withString:prefix
options:0
range:NSMakeRange(0,1)];
}
}
// Manage pause symbol
NSArray * array = [number componentsSeparatedByString:#","];
[callViewController setDtmfCmd:#""];
if ([array count] > 1)
{
number = [array objectAtIndex:0];
[callViewController setDtmfCmd:[array objectAtIndex:1]];
}
if (!isConnected && [self wakeUpNetwork] == NO)
{
_phoneNumber = [[NSString stringWithString: number] retain];
if (isIpod)
{
UIAlertView *alertView = [[[UIAlertView alloc] initWithTitle:nil
message:NSLocalizedString(#"You must enable Wi-Fi or SIP account to place a call.",#"SiphonApp")
delegate:nil
cancelButtonTitle:NSLocalizedString(#"OK",#"SiphonApp")
otherButtonTitles:nil] autorelease];
[alertView show];
}
else
{
UIActionSheet *actionSheet = [[[UIActionSheet alloc] initWithTitle:NSLocalizedString(#"The SIP server is unreachable!",#"SiphonApp")
delegate:self
cancelButtonTitle:NSLocalizedString(#"Cancel",#"SiphonApp")
destructiveButtonTitle:nil
otherButtonTitles:NSLocalizedString(#"Cellular call",#"SiphonApp"),
nil] autorelease];
actionSheet.actionSheetStyle = UIActionSheetStyleDefault;
[actionSheet showInView: self.window];
}
return;
}
if ([self sipConnect])
{
NSRange range = [number rangeOfString:#"#"];
NSLog(#"%i", _sip_acc_id);
if (range.location != NSNotFound)
{
status = sip_dial_with_uri(_sip_acc_id, [[NSString stringWithFormat:#"sip:%#", number] UTF8String], &call_id);
}
else
status = sip_dial(_sip_acc_id, [number UTF8String], &call_id);
if (status != PJ_SUCCESS)
{
// FIXME
//[self displayStatus:status withTitle:nil];
const pj_str_t *str = pjsip_get_status_text(status);
NSString *msg = [[NSString alloc]
initWithBytes:str->ptr
length:str->slen
encoding:[NSString defaultCStringEncoding]];
[self displayError:msg withTitle:#"registration error"];
}
}
}
Also if anyone has a link to the Siphon app's code that's newer and maybe works better, I'd appreciate that as well.
More info:
in call.m file essentially this gets called:
status = pjsua_call_make_call(acc_id, &pj_uri, 0, NULL, NULL, call_id);
and here
acc_id = 0
pj_uri = char *-> "sip:6476805821#voip5-2.acanac.com"
pj_ssize_t -> 33
call_id = 803203976
I figured this out. Turns out, the siphon app wasn't registering the account.
The code below is important:
pj_status_t sip_connect(pj_pool_t *pool, pjsua_acc_id *acc_id)
{
// ID
acc_cfg.id.ptr = (char*) pj_pool_alloc(/*app_config.*/pool, PJSIP_MAX_URL_SIZE);
if (contactname && strlen(contactname))
acc_cfg.id.slen = pj_ansi_snprintf(acc_cfg.id.ptr, PJSIP_MAX_URL_SIZE,
"\"%s\"<sip:%s#%s>", contactname, uname, server);
else
acc_cfg.id.slen = pj_ansi_snprintf(acc_cfg.id.ptr, PJSIP_MAX_URL_SIZE,
"sip:%s#%s", uname, server);
if ((status = pjsua_verify_sip_url(acc_cfg.id.ptr)) != 0)
{
PJ_LOG(1,(THIS_FILE, "Error: invalid SIP URL '%s' in local id argument",
acc_cfg.id));
[app displayParameterError: #"Invalid value for username or server."];
return status;
}
// Registrar
acc_cfg.reg_uri.ptr = (char*) pj_pool_alloc(/*app_config.*/pool,
PJSIP_MAX_URL_SIZE);
acc_cfg.reg_uri.slen = pj_ansi_snprintf(acc_cfg.reg_uri.ptr,
PJSIP_MAX_URL_SIZE, "sip:%s", server);
if ((status = pjsua_verify_sip_url(acc_cfg.reg_uri.ptr)) != 0)
{
PJ_LOG(1,(THIS_FILE, "Error: invalid SIP URL '%s' in registrar argument",
acc_cfg.reg_uri));
[app displayParameterError: #"Invalid value for server parameter."];
return status;
}
...
more code here
...
}
This is where your account gets registered to a SIP server.
Make sure the sip_connect function gets called from the main application itself shown below:
/* */
- (BOOL)sipConnect
{
pj_status_t status;
if (![self sipStartup])
return FALSE;
//if ([self wakeUpNetwork] == NO)
// return NO;
NSLog(#"%i", _sip_acc_id);
//if (_sip_acc_id == PJSUA_INVALID_ID)
//{
self.networkActivityIndicatorVisible = YES;
if ((status = sip_connect(_app_config.pool, &_sip_acc_id)) != PJ_SUCCESS)
{
self.networkActivityIndicatorVisible = NO;
return FALSE;
}
//}
return TRUE;
}
in my case _sip_acc_id wasn't equal to PJSUA_INVALID_ID therefore sip_connect was never getting called.
Thanks for all of those who tried to solve it in their head? :)
You are unlikely to get any useful help unless you post a code snippet as well as error output (at minimum). More context, such as configuration info and relevant aspects of your network, will further improve your chances.
(I would have added this as a comment on the question, but don't yet have the required reputation.)

SQLcipher how to create cypted database?

I'm trying to follow this tutorial: http://sqlcipher.net/ios-tutorial/
I create a database called "sqlcipher.db" then I recreate this
When I execute this code:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSString *databasePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]
stringByAppendingPathComponent: #"encrypted.db"];
sqlite3 *db;
if (sqlite3_open([databasePath UTF8String], &db) == SQLITE_OK) {
const char* key = [#"secret" UTF8String];
int sqlite3_key(sqlite3 *db, const void *pKey, int nKey);
sqlite3_key(db, key, strlen(key));
if (sqlite3_exec(db, (const char*) "SELECT count(*) FROM t1;", NULL, NULL, NULL) == SQLITE_OK) {
// password is correct, or, database has been initialized
NSLog(#"Hello 1");
} else {
// incorrect password!
NSLog(#"Hello 2");
}
sqlite3_close(db);
} else {
NSLog(#"Hello 3");
}
}
It allways outs "Hello 2".
When I try to reproduce the steps to crate an encrypted db described here http://zetetic.net/blog/2009/12/29/how-to-encrypt-a-plaintext-sqlite-database-to-use-sqlcipher.html#disqus_thread I can't get it encrypted, I believe that it is beacause I am using sqlite3 mac command.
So I saw in the comments that S Lombardo says that I have to compile a command line sqlcipher executable but the link doesn't works.
How should I encrypt my database to use it with SQLcipher?
Did anyone have success using sqlicipher in iOS?
After one hour Googling i've found how to compile sqlcipher command line for OSX:
I hope this could help somebody.
https://groups.google.com/forum/#!msg/sqlcipher/bd1R13RpZHQ/SEPK8YrRt1gJ

Resources