Sqlite crash app - iphone

i am using this code to insert names to table,i have a problem that after 150 +/- names the app crash with this log:
Received memory warning. Level=1
Received memory warning. Level=2
this is the code,did i done something wrong?
if (sqlite3_open([dataPath UTF8String], &database) == SQLITE_OK) {
for (int i = 0 ; i < count; i++) {
sqlite3_stmt *insertStmt = nil;
NSString *name = [song valueForProperty:MPMediaItemPropertyTitle];
if(insertStmt == nil)
{
NSString *statement = [NSString stringWithFormat:#"INSERT INTO Songs (name) VALUES (?)"];
const char *insertSql = [statement UTF8String];
if(sqlite3_prepare_v2(database, insertSql, -1, &insertStmt, NULL) != SQLITE_OK){
NSLog(#"Error while creating insert statement.");
insertStmt = nil;
continue;
}
sqlite3_bind_text(insertStmt, 1, [name UTF8String], -1, SQLITE_TRANSIENT);
if(SQLITE_DONE != sqlite3_step(insertStmt)){
NSLog(#"Error while inserting data.");
insertStmt = nil;
continue;
}
else{}
sqlite3_reset(insertStmt);
insertStmt = nil;
}
[delegate IPodLibraryFinishEntity:self];
}
sqlite3_close(database);
}

Use instruments to check for memory loss due to retained but not leaked memory. The latter is unused memory that is still pointed to. Use Heapshot in the Allocations instrument on Instruments.
For HowTo use Heapshot to find memory creap, see: bbum blog
Basically there method is to run Instruments allocate tool, take a heapshot, run an intuition of your code and another heapshot repeating 3 or 4 times. This will indicate memory that is allocated and not released during the iterations.
To figure out the results disclose to see the individual allocations.
If you need to see where retains, releases and autoreleases occur for an object use instruments:
Run in instruments, in Allocations set "Record reference counts" on on (you have to stop recording to set the option). Cause the picker to run, stop recording, search for there ivar (datePickerView), drill down and you will be able to see where all retains, releases and autoreleases occurred.

Your code is not optimal. You should place all prepare methods before the loop.
if (sqlite3_open([dataPath UTF8String], &database) == SQLITE_OK) {
sqlite3_stmt *insertStmt = nil;
NSString *name = [song valueForProperty:MPMediaItemPropertyTitle];
if(insertStmt == nil) {
NSString *statement = [NSString stringWithFormat:#"INSERT INTO Songs (name) VALUES (?)"];
const char *insertSql = [statement UTF8String];
if(sqlite3_prepare_v2(database, insertSql, -1, &insertStmt, NULL) != SQLITE_OK){
NSLog(#"Error while creating insert statement.");
insertStmt = nil;
return;
}
}
for (int i = 0 ; i < count; i++) {
sqlite3_bind_text(insertStmt, 1, [name UTF8String], -1, SQLITE_TRANSIENT);
if(SQLITE_DONE != sqlite3_step(insertStmt)){
NSLog(#"Error while inserting data.");
continue;
}
else{}
sqlite3_clear_bindings(insertStmt); //release bindings
sqlite3_reset(insertStmt);
[delegate IPodLibraryFinishEntity:self];
}
sqlite3_close(database);
}

Before starting each Insert statement use sqlite3_open and after execution of query put sqlite3_close statement. So that it wont make database object busy anymore after each Insert query execution.

Related

iPhone SQLite Crash sqlite3_last_insert_rowid or sqlite3_close.

I'm having two crashes show up in my logs using the following code to insert into my SQLite DB - the code works perfectly most of the time however clearly there is a something wrong that's causing the crash.
It crashes on either sqlite3_last_insert_rowid or sqlite3_close with EXC_BAD_ACCESS (I hold a reference to the SMSDatabase and it's in a singleton not sure why it would be deallocated) and SIGABRT (memory issue?)
- (NSInteger)query:(NSString *)query {
NSInteger lastRowID = 0;
if (sqlite3_open([self.databasePath UTF8String], &smsDatabase) == SQLITE_OK)
{
sqlite3_stmt *statement = nil;
const char *sql = [query UTF8String];
if (sqlite3_prepare_v2(smsDatabase, sql, -1, &statement, NULL) == SQLITE_OK) {
if (sqlite3_step(statement) == SQLITE_DONE)
{
lastRowID = sqlite3_last_insert_rowid(smsDatabase);
}
}
sqlite3_finalize(statement);
}
sqlite3_close(smsDatabase);
return lastRowID;
}
Where am I going wrong?
sqlite3_close(smsDatabase); - isn't this outside your if statement, which means you are closing a database which may have never been opened ?

UITableViewController not always consistent with actual SQLite database until after restarting app

In my "viewWillAppear" callback I attempt to populate a UITableViewController with data from SQLite database for the user to see.
However, what I noticed was if I switch to another tab, commit a new row of data into SQLite and switch back to the UITableViewController it does not update with the new row I just added to the database. I have to quit out of the app completely and navigate back to the UITableViewController in order to see the new row reflected on the table view.
How do I get around this problem (i.e. how do I force always showing the very latest information in SQLite on the UITableViewController after switching back and forth a bunch of times?)
Would appreciate all / any advice.
Here is the code:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSLog(#"viewwillappear");
//[[self tableView] reloadData];
int rc=-1;
if (databasePath == nil) {
NSLog(#"database path is NIL. Trying to set it");
databaseName = #"mymemories.sqlite";
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
return;
}
rc = sqlite3_open([databasePath UTF8String], &database);
if(rc == SQLITE_OK) {
memoriesArray = [[NSMutableArray alloc]init ];
sqlite3_stmt *statement = nil;
NSString *fullQuery = #"SELECT * FROM memories";
const char *sql = [fullQuery UTF8String];
if(sqlite3_prepare_v2(database, sql, -1, &statement, NULL)!=SQLITE_OK)
NSAssert1(0, #"Error preparing statement '%s'", sqlite3_errmsg(database));
else
{
while(sqlite3_step(statement) == SQLITE_ROW)
{
NSString *place= [NSString stringWithUTF8String:(const char*)sqlite3_column_text(statement, 4)];
//[User setName:[NSString stringWithUTF8String:(const char*)sqlite3_column_text(statement, 1)]];
//[User setAge:[NSString stringWithUTF8String:(const char*)sqlite3_column_text(statement, 2)]];
[memoriesArray addObject:place];
//[currentUser release];
}
}
sqlite3_finalize(statement);
sqlite3_close(database);
}
}
Also in case this is relevant, here is the code that commits to the SQLite database:
NSData * blob = [NSData dataWithContentsOfURL:recordedTmpFile];
int rc=-1;
rc = sqlite3_open([databasePath UTF8String], &database);
if(rc == SQLITE_OK) {
sqlite3_exec(database, "BEGIN", 0, 0, 0);
NSLog(#"Connected To: %#",databasePath);
sqlite3_stmt *updStmt =nil;
const char *sql = "INSERT INTO memories (data,place) VALUES (?,?);";
rc = sqlite3_prepare_v2(database, sql, -1, &updStmt, NULL);
if(rc!= SQLITE_OK)
{
NSLog(#"Error while creating update statement:%#", sqlite3_errmsg(database));
}
sqlite3_bind_text( updStmt, 2, [[tags text] UTF8String], -1, SQLITE_TRANSIENT);
rc = sqlite3_bind_blob(updStmt, 1, [blob bytes], [blob length] , SQLITE_BLOB);
if((rc = sqlite3_step(updStmt)) != SQLITE_DONE)
{
NSLog(#"Error while updating: %#", sqlite3_errmsg(database));
sqlite3_reset(updStmt);
}
sqlite3_exec(database, "COMMIT", 0, 0, 0);
//rc = sqlite3_reset(updStmt);
sqlite3_close(database);
}
As an extension of an explanation of Darren's answer.
First off his answer is correct.
Secondly you need to use an NSMutableArray to ensure consistency, this is where you are going wrong by not updating it as you should
The steps you should be taking to ensure consistency are the following:
Loading Data into Table
In viewDidLoad, call your SQL statement and load it into your array
in viewWillAppear ensure that your array contains data, if not display a notice that no results were returned
Saving Data into Database
Update the change to the array (or datasource)
Update the Database with the updated datasource to ensure consistency
Update the table with one of the 4 UITableView reloading methods
Using the NSMArray to ensure consistency between updates and app loads is fairly common practise has be recommended to me in the past by fellow co workers with decades of experience.
Note:
You will need to synchronise the datasource to ensure that 1 thread is accessing it at any 1 time otherwise you will get a crash.
Assuming you read your SQL data into an array, then use this array to build the UITableView, when you add a record to your SQL database, you either need to also add it to the array used to build the table, or re-read the data from the database into the array.

Getting error while executing SQLite command from application

I have this simple function in my application :
-(NSMutableArray *)SelectProductID:(NSMutableArray *)arr
{
NSLog(#"----------------");
sqlite3_stmt *statement;
NSMutableArray *arrPordID = [[NSMutableArray alloc]init];
#try
{
//Get productID
for(NSString *strSubProductID in arr)
{
NSString *s = [NSString stringWithFormat:#"SELECT ProductID FROM SubProducttable where SubProductID=%#",strSubProductID];
const char *sql = [s cStringUsingEncoding:NSASCIIStringEncoding];
if (sqlite3_prepare_v2(database, [s cStringUsingEncoding:NSUTF8StringEncoding], -1, &statement, NULL) == SQLITE_OK) {
while (sqlite3_step(statement) == SQLITE_ROW){
char *dbString;
dbString = (char *)sqlite3_column_text(statement, 0);
NSString *pID = (dbString) ? [NSString stringWithUTF8String:dbString] : #"";
[arrPordID addObject:pID];
}
}
}
}
#catch (NSException *exception) {
#throw exception;
}
#finally {
sqlite3_finalize(statement);
}
return arrPordID;
}
I am encountering a strange problem here. When application reaches while (sqlite3_step(statement) == SQLITE_ROW){, loop is never entered. I don't know why. I executed the same query in SQLite manager (when application is not running). And I get result as a single one. The result I get is 2. But here I am getting nothing.
And yes, I always close the database in SQLite manager whenever I run my application. I have also cleaned the application, restarted XCode, and removed the application from simulator. But no success.
Also I saw a strange thing during debugging. While debugging, sqlite3_stmt *statement is always skipped. Is this the reason I am not getting any result?
Have you tried subproductId in single quotes?
NSString *s = [NSString stringWithFormat:#"SELECT ProductID FROM SubProducttable where SubProductID='%#'",strSubProductID];
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
// Setup the SQL Statement and compile it for faster access
const char *sqlQuery = #"SELECT ProductID FROM SubProducttable where SubProductID=%#",strSubProductID;
sqlite3_stmt *statement;
if(sqlite3_prepare_v2(database, statement, -1, &sqlQuery, NULL) == SQLITE_OK) {
while(sqlite3_step(sqlQuery ) == SQLITE_ROW) {
// Read the data and add to your array object
}
}
// Release the compiled statement from memory
sqlite3_finalize(statement);
}
sqlite3_close(database)

iphone: strange leak

Can anybody help me out why this piece of code is leaking and how can we handle it?
sqlite3 *database;
if (pickerList) {
self.pickerList=nil;
[pickerList release];
}
self.pickerList=[[NSMutableArray alloc] init];
NSString *dbPath = [self applicationDocumentsDirectory];
dbPath=[dbPath stringByAppendingPathComponent:#"database"];
dbPath=[dbPath stringByAppendingPathComponent:#"OFFENDERSDB.sqlite"];
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK) {
if (isAlertForViolationPicker) {
const char *sqlStatement = "SELECT * FROM VIOLATIONS_TBL";
sqlite3_stmt *compiledStatement;
if (sqlite3_prepare(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
while (sqlite3_step(compiledStatement) == SQLITE_ROW) {
NSString *recSTR=[[NSString alloc] initWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];
[self.pickerList addObject:recSTR];
[recSTR release];
recSTR=nil;
}
}
//[tempRowArray release];
sqlite3_finalize(compiledStatement);
//sqlite3_reset(compiledStatement);
sqlite3_close(database);
}
else {
const char *sqlStatement = "SELECT * FROM PLAN_TBL";
sqlite3_stmt *compiledStatement;
if (sqlite3_prepare(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
while (sqlite3_step(compiledStatement) == SQLITE_ROW) {
NSString *recSTR=[[NSString alloc] initWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];
[self.pickerList addObject:recSTR];
[recSTR release];
recSTR=nil;
}
}
sqlite3_finalize(compiledStatement);
sqlite3_close(database);
}
}
sqlite3_reset(compiledStatement);
recSTR is leaking in this case and I have tried all the below mentioned solutions but none worked (updated the code)
Thanx in advance
It looks as though you may be leaking pickerList. You have a pointer to pickerList which you then set to nil. Then you send a release message to this point (which is in effect a no-op). If you use:
if (pickerList)
{
[pickerList release];
self.pickerList=nil;
}
instead of your current code, do you fare any better? Without seeing more code it's hard to say, but you definitely want to release before you set the ivar to nil. (This said if you you've done #property (retain) UIPickerList *pickerList then self.pickerList = nil will release pickerList. If you've done this then your [pickerList release] call is redundant.)
You may well get a report of a recSTR leaking from instruments. But that doesn't mean that the issue isn't with pickerList. Looking at the code, it would not unlikely that recSTR is owned by an instance of pickerList that is hanging around because you've discarded the pointer to it and then sent a release message to nil. So you'll end up with a leak of recSTR and pickerList.

Bulk Insert to Sqlite3 causing Db Error: Library routine called out of sequence

I need to load some configuration data after app updates for my iphone app. I bundle in a file with a bunch of SQL statements (800+) to run on first launch. It looks like I may be painted in a corner now. If I run it on the main thread at startup, it take so long to run that the app crashes due to the startup taking too long. If I run it on a separate thread, I am getting database contention errors (Library routine called out of sequence). I think this is because the app is continuing to load and read the DB on the main thread.
Here is the method that receives the data form the CSV file and then loops through and writes to the DB.
Any ideas about how to either make this run faster on startup or run successfully without contention on a low priority background thread?
+ (void) updateDB:(NSString *)data {
NSArray *lineArray = [data componentsSeparatedByString:#"\n"];
if (sqlite3_open([[BIUtility getDBPath] UTF8String], &database) != SQLITE_OK) {
sqlite3_close(database);
NSLog(#"Failed to opendatabase in updateDB");
}
char *errorMsg;
for(int k = 0; k < [lineArray count]; k++){
NSString *loadSQLi = [lineArray objectAtIndex:k];
if (sqlite3_exec(database, [loadSQLi UTF8String], NULL, NULL, &errorMsg) != SQLITE_OK) {
NSLog(#"DB Error. '%s'", sqlite3_errmsg(database));
}
}
if(database) sqlite3_close(database);
}
You can make this faster by doing all inserts in a single transaction instead of doing the inserts in separate transactions as it happens by default in that code.
+ (void) updateDB:(NSString *)data {
NSArray *lineArray = [data componentsSeparatedByString:#"\n"];
if (sqlite3_open([[BIUtility getDBPath] UTF8String], &database) != SQLITE_OK) {
sqlite3_close(database);
NSLog(#"Failed to opendatabase in updateDB");
}
char *errorMsg;
execQuery(#"Begin Transaction");
for(int k = 0; k < [lineArray count]; k++){
NSString *loadSQLi = [lineArray objectAtIndex:k];
execQuery(loadSQLi);
}
execQuery(#"Commit");
if(database) sqlite3_close(database);
}
+ (void) execQuery:(NSString *)query {
char *errorMsg;
if (sqlite3_exec(database, [query UTF8String], NULL, NULL, &errorMsg) != SQLITE_OK) {
NSLog(#"DB Error. '%s'", sqlite3_errmsg(database));
}
}